[SOLVED] user interaction from widget

Discussion forum for C++ and script developers who are using the QCAD development platform or who are looking to contribute to QCAD (translations, documentation, etc).

Moderator: andrew

Forum rules

Always indicate your operating system and QCAD version.

Attach drawing files, scripts and screenshots.

Post one question per topic.

Post Reply
smf
Premier Member
Posts: 177
Joined: Tue Feb 28, 2012 1:05 pm

[SOLVED] user interaction from widget

Post by smf » Sun Dec 21, 2014 9:41 pm

Hi,

for the model railroad scripts currently I'm working on a widget that should insert a track in the drawing at an user specified position (similar to BlockInsert, but the track must be connected to an existing track and therefore rotated automatically). The existing widgets like BlockList widget or LayerList widget connect to other scripts through button.setDefaultAction(RGUIAction...), but after the coordinate is picked, I need to have a reference to the scope (this) of the current widget to get some more parameters.

Is there an elegant way of letting the user pick a coordinate / entity / checked block and then starting another script and exchange parameters with it?

Thanks in advance, best regards
Stefan
Last edited by smf on Tue Dec 23, 2014 5:15 pm, edited 1 time in total.

smf
Premier Member
Posts: 177
Joined: Tue Feb 28, 2012 1:05 pm

Re: user interaction from widget

Post by smf » Mon Dec 22, 2014 2:26 pm

...now I'm trying with a global variable and access like Block.getActiveBlockId - this really seems to be the intended way to do things like this. I will report after success or failure. :wink:

smf
Premier Member
Posts: 177
Joined: Tue Feb 28, 2012 1:05 pm

Re: user interaction from widget

Post by smf » Tue Dec 23, 2014 1:58 pm

I'm very sorry, but I'm trying for hours now and never succeed in handing out a simple reference to an instance created by another script. :( The global variable thing didn't worked out, I always get the unmodified initialization of my "global" variable. So I tried to replicate the BlockListWidget-AddBlock-duo. But this didn't worked out either.

First: attaching an action to the toolbutton of my widget, I tried:

Code: Select all

  var saction = RGuiAction.getByScriptFile("scripts/QCTrack/TrackWidgetInsert/TrackWidgetInsert.js");
  qDebug("saction: ", saction); //always returns null although script existing and working if started by shortcut
But this always returns null (file exists, and using the shortcut "QB" defined there is working). Replacing the getbyScriptFile-parameter by the AddBlock.js, it returns a RGuiAction. But why?

Second: Trying to access the list of type RTrackListQt inside TrackListWidget, I always get a reference to RListWidget - which is the parent of RTrackListQt not what I need to access the required variables.

Code: Select all

  var trackList = EAction.getMainWindow().findChild("TrackList");
  qDebug("trackList: ", trackList); //always returns a RListWidget instead of RTrackListQt
I'm very used to Java which is completely different in terms of classes, instances and references to them. So any suggestion is welcome, ECMA is driving me crazy. :(

User avatar
andrew
Site Admin
Posts: 9037
Joined: Fri Mar 30, 2007 6:07 am

Re: user interaction from widget

Post by andrew » Tue Dec 23, 2014 2:36 pm

There are several ways to share information between different scripts or between widgets, etc:

- If the information is application wide and persistent, it can be stored in the QCAD3.ini file, in this case, use RSettings:
RSettings.setValue("Key/SubKey", value);
value = RSettings.getValue("Key/SubKey", defaultValue);
- If the information is application wide and not persistent, one way is to attach it to a QObject, for example the main window or any other widget that might be more appropriate:
RMainWindowQt.getMainWindow().setProperty("MyUniqueName", 7);
value = RMainWindowQt.getMainWindow().property("MyUniqueName");
- If the information is tied to a document, store it as part of the document:
document.setVariable("Key", value);
value = document.getVariable("Key", defaultValue);
- If the information is tied to a drawing object (layer, entity, block, ...), attach it to the object:
object.setCustomProperty("QCAD", "Key", value);
value = object.getCustomProperty("QCAD", "Key", defaultValue);
Every other piece of information created by a script as a local or global variable is limited to the current script engine.
There is one script engine running per document and one global script engine which runs global scripts such as the init functions to add tools to menus or create widgets (property editor, library browser, layer list, etc.).
What you are experiencing is likely a problem of accessing information from different script engines. Your widget is presumably created by the global script engine at the start of QCAD. Your action, however, is run by the script engine that belongs to the current document.

If your widget needs to store information that is not stored inside a child widget (such as a text in a QLineEdit would be), you can attach this information to your widget using the setProperty() method of the QObject class as outlined above. This information will be stored on the C++ level inside your QWidget object. Any script in any context can then access this information by looking up the widget (e.g. RMainWindowQt.getMainWindow().findChild("MyObjectName")) and using the property() method to access the user defined property.

Your ECMAScript class might have member variables. This works fine for everything that is limited to one script engine (i.e. only concerns an action or a widget). If you design your class with getter / setter functions, you can simply rewrite those to use QObject::setProperty(), QObject::property:
MyWidget.setMyValue = function(value) {
    var widget = RMainWindowQt.getMainWindow().findChild("MyWidget");
    widget.setProperty("MyProperty", value);
};

MyWidget.getMyValue = function() {
    var widget = RMainWindowQt.getMainWindow().findChild("MyWidget");
    return widget.property("MyProperty");
};

smf
Premier Member
Posts: 177
Joined: Tue Feb 28, 2012 1:05 pm

Re: user interaction from widget

Post by smf » Tue Dec 23, 2014 2:48 pm

wow. e_surprised Many thanks to this explanation! :) This different-script-engine-thing indeed answers my problems. :) I will try to adopt your suggestions and report later. :)

Again: many thanks! :D

smf
Premier Member
Posts: 177
Joined: Tue Feb 28, 2012 1:05 pm

Re: user interaction from widget

Post by smf » Tue Dec 23, 2014 4:00 pm

Hm, either this is not working the way I need it or I'm too stupid. :? The required information is not persistent and does not belong to a document, so I thought it would be best to give the widget-property a try. Unfortunately, it seems like the property is copied, so it can not hold a reference which is requested once and reflects changes of the original object.

Perhaps I try to explain what I want to do so one can see what I am missing. I have a widget displayed together with block list and layer list, depending on the tracks defined in the current document. There the user can select a track (similar to a block) and per track a "connection end" (1, 2, 3, ... depending on the track). There is an "add" button that shell add the currently selected track (at an user picked coordinate, this part is not yet working), and there is a "rotate" button that rotates the currently selected track (i.e. changes the connection end).

The "add" function as well as the "rotate" function can be activated by shortcuts - this is not needed hardly, but I couldn't find a way to let the user pick a coordinate from within the TrackListWidget which made me try to "outsource" the adding to another script. Therefore I need to get the currently selected track and the connection end and possibly need to change a spin box.

If there's a simple way to let the user pick a coordinate similar to other actions with setState, escapeEvent, pickEntity... this would be my favorite, but I couldn't get this run so I tried to adopt the BlockList/AddBlock-behavior. The data exchange there is done through Block.getActiveBlockId, but there only the currently selected item.text is used - I need more which would require my ECMA-added fields to be copied.
Attachments
TrackWidget.png
Screenshot of TrackWidget
TrackWidget.png (11.85 KiB) Viewed 13225 times

User avatar
andrew
Site Admin
Posts: 9037
Joined: Fri Mar 30, 2007 6:07 am

Re: user interaction from widget

Post by andrew » Tue Dec 23, 2014 4:09 pm

smf wrote:Hm, either this is not working the way I need it or I'm too stupid. :? The required information is not persistent and does not belong to a document, so I thought it would be best to give the widget-property a try. Unfortunately, it seems like the property is copied, so it can not hold a reference which is requested once and reflects changes of the original object.
Don't store a reference to your ECMAScript object. You cannot (never) access an ECMAScript object created in one engine from another engine. What you can do is copy values back and forth using widget properties (ints, strings, doubles, etc.).

smf
Premier Member
Posts: 177
Joined: Tue Feb 28, 2012 1:05 pm

Re: user interaction from widget

Post by smf » Tue Dec 23, 2014 4:22 pm

Many thanks for the fast and clear answer. :)

So my idea is kind of dead. :( If I can not access a widget from a shortcut-script, I will have no chance to modify e.g. a specific spinbox inside this widget.

User avatar
andrew
Site Admin
Posts: 9037
Joined: Fri Mar 30, 2007 6:07 am

Re: user interaction from widget

Post by andrew » Tue Dec 23, 2014 4:43 pm

smf wrote:So my idea is kind of dead. :( If I can not access a widget from a shortcut-script, I will have no chance to modify e.g. a specific spinbox inside this widget.
You can access all your widgets anytime from anywhere using their objectName. What you cannot access are references to ECMAScript objects.

Example 1:
var w = new QLineEdit();
w.objectName = "MyLineEdit";
You can access this QLineEdit anytime from any script in any context for as long as it exists:
var w = RMainWindowQt.getMainWindow().findChild("MyLineEdit");
Example 2:
var w = new MyScriptClassDerivedFromQWidget();
w.objectName = "MyScriptClassObject";
Although you can also get a reference to the widget created here from anywhere just like above, that reference will be of type QWidget (not of type MyScriptClassDerivedFromQWidget). In this case your widget object lives in C++ up to QWidget. However, the rest of your object that is defined in ECMAScript lives in the script engine it was created in. So when accessing this widget from another script engine, you loose everything that is not defined in QWidget. Any custom information you want to access would have to be attached to the QWidget using properties.

smf
Premier Member
Posts: 177
Joined: Tue Feb 28, 2012 1:05 pm

Re: user interaction from widget

Post by smf » Tue Dec 23, 2014 5:09 pm

In this case your widget object lives in C++ up to QWidget. However, the rest of your object that is defined in ECMAScript lives in the script engine it was created in. So when accessing this widget from another script engine, you loose everything that is not defined in QWidget.
Thanks for clarification, that is what I understood of your last post. The access of the QWidget (even QListWidget) did work, the access of the ECMA added variables did not - and this is not because I did something wrong, it is caused by multiple script engines. I would have spent even more days on "how do I get those variables". :)

Many thanks for your responses, so I have to think of a completely other solution.

smf
Premier Member
Posts: 177
Joined: Tue Feb 28, 2012 1:05 pm

Re: [SOLVED] user interaction from widget

Post by smf » Tue Dec 23, 2014 5:20 pm

...now added the current version of QCTrack here:
viewtopic.php?f=107&t=3172

The plan for the here discussed widget was to replace the option "consecutive extension", which at the moment first asks for a track end to extend and then always shows a dialog by which track the existing track is to be extended.

Post Reply

Return to “QCAD Programming, Script Programming and Contributing”