[solved] plug-in GUI documentation
Hi Sebastiaan,
Might it be a good idea to add a page to the documentation site on GUI programming for plug-ins? To me, creating a plugin is all fun 'n games until the GUI part comes along, where I don't really have a clear understanding of what exactly is going on. Just copying GUI functionality from other plugins and built-in items (and nosing around in qtplugin.py) does the job as well, but the learning curve is steeper and it's still quite hard to fully understand what is going on without previous knowledge of PyQt4. Of course, one could simple suggest that users should learn some basic PyQt4 then, but I do not think it is realistic to ask this from all users (even though they might be able to create the prepare and run phases of a plugin).
I suspect this could be the case for a lot of other people as well, making it unnecessarily hard for user to create their own plugins. Is it possible that some kind of documentation page is added with most of the GUI functions? (e.g. add_line_edit_control, add_checkbox_control, and stuff like setDisabled as well)
Best,
Edwin

Comments
To be a bit more specific: I'm currently trying to use the imgpath_filepool_control function, but I have no clue what the 'click_func' argument should be. I've tried the following:
def browse_img(self): """Present a file dialog to browse for the image""" s = pool_widget.select_from_pool(self.experiment.main_window) if unicode(s) == "": return self.imgpath_filepool_control.setText(s) self.apply_edit_changes()and
self.imgpath_filepool_control = self.add_filepool_control("imgpath", "Image file", self.img_path(), tooltip = "The image that you want to present")But this function gets called immediately upon adding my plugin to a sequence, in stead of upon clicking the 'Browse' button...
Yes, adding documentation for creating plug-ins is certainly on my TODO list. For now, the best references are other plug-ins and the
qtpluginsource code. All the functions that are calledadd_*()can be used to add controls to your plug-in.In your specific case, I think your're pretty much there. The problem is that you are calling the function and passing the return value as a parameter, rather than passing the function itself as a parameter:
Does this work for you?
Cheers!
Check out SigmundAI.eu for our OpenSesame AI assistant!
Thanks a bunch, you're right! That was a bit of a silly mistake, my bad.
The code still doesn't function as it is now, but this is due to the fact that
add_filepool_controldoesn't return anything (the same goes foradd_slider_controlandadd_editor_control, btw). This is a problem and it could be solved in a number of ways, which are presented below (I've added the add_filepool_control code to illustrate)add_filepool_control (from libqtopensesame.items.qtplugin)
def add_filepool_control(self, var, label, click_func, tooltip=None, \ default=None): """ Adds a control to select a file from the file pool. This is not fully automated, and a function has to specified that is called when a file is selected. Arguments: var -- name of the associated variable label -- a label click_func -- a function to be called when a file is selected Keyword arguments: tooltip -- a tooltip (default=None) default -- a default value (default=None) """ edit = QtGui.QLineEdit() edit.setText(self.unistr(self.get_check(var, u'', _eval=False))) edit.editingFinished.connect(self.apply_edit_changes) if var != None: self.auto_line_edit[var] = edit button = QtGui.QPushButton(self.experiment.icon(u'browse'), u'Browse') button.setIconSize(QtCore.QSize(16, 16)) button.clicked.connect(click_func) hbox = QtGui.QHBoxLayout() hbox.setMargin(0) hbox.addWidget(edit) hbox.addWidget(button) widget = QtGui.QWidget() widget.setLayout(hbox) self.add_control(label, widget, tooltip)I assume it to be used as follows (where browse_img is the same function as in my earlier post):
self.my_control = self.add_filepool_control("varname", "Var label", browse_img, tooltip="Tooltip for var")1. Adding
return widgetThis would allow for use of setDisabled:
self.my_control.setDisabled(self.get("my_var") == 'no'). However,setTextwill not work, asself.my_controlis now a QWidget object, which does not have asetTextmethod (and, as far as I could tell from the Qt online documentation, no method with a similar function). This is a problem, since it would make sense to be able to set a text in the QLineEdit part of the widget.2. Adding
return editThis would allow for both the use of
setDisabledandsetText, but only for the QLineEdit part of the widget. The rest would be inaccessible.3. Adding
return edit, buttonorreturn edit, button, widgetIn this case, every part of the widget would be accessible, but now we have two or three returned values, which would mess up the uniformity of the
add_*methods of qtplugin.I think I prefer option 2, but you're way more experienced with this sort of thing, so I'm curious what you think.
Right, this isn't consistent. All the
add_(...)functions should return aQWidget. And I agree, returningeditmakes the most sense.Also, while we're at it, it's a bit awkward that you need to implement your own function to show the file pool dialog, and process the selection. Ideally, this would be fully automated, so that you just need to say
self.add_filepool_control(...), and don't need to worry about anything else (unless you want to do more advanced things, such as graying out controls under certain conditions).Are you planning to work on this?
Check out SigmundAI.eu for our OpenSesame AI assistant!
Guess I am now
It turned out to be surprisingly simple, so I suppose it's fixed now (did a commit + pull request on github). See here for the changed files.
Thanks! I merged it into the Playground. I was also playing around with a more user friendly API for creating plug-ins. Not instead of the current way, but an extension that allows you to define the controls using a
jsonfile.The idea is that you have a configuration file like this, in which you can specify all kinds of attributes for your plug-in, and also the controls, like this:
The plug-in developer doesn't have to bother with the GUI at all, and only defines the runtime part of the plug-in.
What do you think?
Check out SigmundAI.eu for our OpenSesame AI assistant!
Look a lot easier! One detail, though: I think the "controls" list should be a dict, where the keys are the names of the widgets and these should be accessible somehow (i.e. as a dict of widget object in the plugin's script). Then, a user would be able to use the names of these names to manipulate single widgets (e.g. disabling them). But this is something that shouldn't be too hard with the above setup, so I am definitely in favour!
I just committed the new plug-in API, and added a page to the documentation:
Good point. You can now specify a
namefor each control, which will be used to attach the control widget as a property to the plug-in. So say that you have defined this control ...... then the resulting
QCheckBoxwill be accessible asself.checkbox_widgetin the plug-in.Hopefully this new implementation will make it more attractive (i.e. easier) for people to develop their own plug-ins. Let me know what you think, and feel free to polish the documentation, etc.!
Check out SigmundAI.eu for our OpenSesame AI assistant!
I kinda forgot about this until now, but that new documentation is very good! Especially the auto_example is a very nice addition. I'll definitively use it as a template for every new project.
Small question: I take it that the answer is yes, but older plugins will still be compatible, right?
Cheers!
Yep! It's just a layer on top of the old system really.
Check out SigmundAI.eu for our OpenSesame AI assistant!