OOP / PySide question

475 views
Skip to first unread message

olheiros

unread,
Feb 10, 2014, 6:21:46 AM2/10/14
to python_in...@googlegroups.com
Hi all.
I am taking my first steps at object oriented, and i'm
trying to build a tool to aid me in the lighting process.

i want to list all my Vray lights in a QTableWidget and some of its attributes
so i can change them more easily.

I have made a base class with all the methods of the Tool UI and its working fine.

I also created another class Light() with all the Light parameters and methods.
The thing is i wanted (if possible) to instantiate as many Light() objects as there are in the scene
so i can refer to them in the UI when changing values. But this has to be dynamic, i mean.

hope you see what i mean. All the examples i have seen are static instantiation like a = light()

in the following example i can only reach to one object. In this case the last one to be created.

Maybe it's my methodology that is wrong in the first place. How would you go about this
Thanks in advance.

Best
Ric

-----------------------------------------------------------------------------

import pymel.core as pm

class lightObject():

    def __init__(self,name):

        self.name = name

        self.color = (255,255,255)

    def __repr__(self):

        return self.name



lights = pm.ls(shapes=1)

for light in lights:

    if light.nodeType() == "VRayLightRectShape" or light.nodeType() == "VRayLightDomeShape":

    lightNode = lightObject("%s" %light)






Marcus Ottosson

unread,
Feb 10, 2014, 6:44:40 AM2/10/14
to python_in...@googlegroups.com
By "dynamic" you may be referring to having your GUI refresh whenever your scene changes.

What may be simpler, at least as a start, may be to rely on a refresh on first launch of your GUI, or alternatively a refresh button within your GUI that the user could press whenever something has changed. It depends on how long you expect the GUI to be running at each run.

To refresh would mean to run a loop over all lights, pm.ls(type='VRayLightRectShape') and instantiate lightObject() from there (which by the way should have upper-case formatting - see PEP08)

Although I have to say, running a QTableWidget as a first excercise in OO is rather intense. You would probably have to composite lightObject within a QTableWidgetItem and override whatever method it has that deals with displaying and editing its corresponding field, either by monkey-patching or prior subclassing.

Have a look here for some more hints


Best,
Marcus


--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_m...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/045c727c-ac97-4fbf-8ceb-a89d240d6f86%40googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.



--
Marcus Ottosson
konstr...@gmail.com

olheiros

unread,
Feb 10, 2014, 7:05:34 AM2/10/14
to python_in...@googlegroups.com
Hi Marcus thank you for your help.
By dynamic i meant creating automatic instancing instead of by hand writing -> newLight = Light()
if i want to refer to a specific light object they are all newLight. Unless i can get it with __str__ or __repr__ have to investigate it aswell.

the model view programming might be what i need to look at / learn first.

Thank you very much
best
Ric
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_maya+unsub...@googlegroups.com.



--
Marcus Ottosson
konstr...@gmail.com

Marcus Ottosson

unread,
Feb 10, 2014, 7:39:16 AM2/10/14
to python_in...@googlegroups.com
The link I gave you is about what you are attempting, but in a slightly more low-level way. The QTableWidget is actually a simplification of the QTableView and QAbstractItemModel which you will see referenced on that page.


To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_m...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/96c133e4-5cce-4d78-93d2-3854dad6c7ca%40googlegroups.com.

For more options, visit https://groups.google.com/groups/opt_out.



--
Marcus Ottosson
konstr...@gmail.com

olheiros

unread,
Feb 10, 2014, 2:02:04 PM2/10/14
to python_in...@googlegroups.com

Thanks Marcus. Your info was very useful.
I have taken it into a point where i can store the attributes in string format
on QTableView using the QtCore.QAbstractTableModel and it updates really nice.
But...

from what i have gathered untill now is that with the roles available i can only get text in it. while
my attributes have float, boolean and tuples values. The only way i have found to display them was to force
the value returned to str().

It must possible to read other data types i'm sure !! but how?
I wanted to fill my cells with spin and comboBoxes instead of the text. So i can edited them.

Any help is very much appretiated.

best regards
Ricardo Viana



snippet bellow:
----------------------------------------------------------
def data (self, index, role):

     if role == QtCore.Qt.DisplayRole:

     row = index.row()

     column = index.column()

     value = self.__lights[row][column]

     return str(value)

-------------------------------------------------------------
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_maya+unsubscribe@googlegroups.com.



--
Marcus Ottosson
konstr...@gmail.com

--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_maya+unsub...@googlegroups.com.



--
Marcus Ottosson
konstr...@gmail.com

Justin Israel

unread,
Feb 10, 2014, 3:10:01 PM2/10/14
to python_in...@googlegroups.com
Hey,

Your QtCore.Qt.DisplayRole is just, as it says, for displaying the value, which would be a string. You can actually store any custom amount of roles you want by using:

thisRole = QtCore.Qt.UserRole 
thatRole = QtCore.Qt.UserRole + 1

So that means you can easily store complex objects in one role, an id in another, etc.

For advanced control over data types and editing operations, you can create a custom QitemDelegate, and use model.setItemDelegateForColumn() for a column to handle a specific type with validation. Like being able to show a spin box when editing a numeric value.




To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_m...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/131b9998-c464-43be-9d49-ad28de22bc8e%40googlegroups.com.

olheiros

unread,
Feb 12, 2014, 1:38:53 PM2/12/14
to python_in...@googlegroups.com
Thanks Justin and Marcus.

I have been looking at the MVC videos on youtube to sort of understand it, And its really powerfull.

I have taken it to a point where i can display the default widgets for editing using openPersistentEditor() on the columns i want.

but ran into another issue, sorry if im bothering you guys too much. the thing is boolean values retrieved result in having the ComboBox as the default editor
but i wanted checkBoxes instead (checked=True, unchecked=False).
I managed to display them with the corrected checked values but can only edit with the comboBoxes.

Any ideas?

I have looked into QItemDelegate and for now it seems a bit too much sand for my wagon.
Have to investigate it better. MVC was already a big step for me.

you can see pic of what i mean attached.

here is the code:

https://gist.github.com/RicardoViana/8961047



--
Marcus Ottosson
konstr...@gmail.com

--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_maya+unsub...@googlegroups.com.
Lister.png

Marcus Ottosson

unread,
Feb 12, 2014, 2:35:40 PM2/12/14
to python_in...@googlegroups.com
I think the delegate would be the only way to go, that is what decides to put the combobox there.


To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_m...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/0b4e7ac6-6528-4735-8e66-f7784b22efc5%40googlegroups.com.

For more options, visit https://groups.google.com/groups/opt_out.



--
Marcus Ottosson
konstr...@gmail.com

Justin Israel

unread,
Feb 12, 2014, 2:53:42 PM2/12/14
to python_in...@googlegroups.com
Actually I am surprised you found QItemDelegate to be too much, given the fact that you have already dived into probably one of the most complicated areas of Qt, and made your own QAbstract* model subclass. You should implement 4 functions of the QItemDelegate to do what you want, I think:

This is when the delegate wants a new instance of an editor. So if you made a subclass called QCheckBoxDelegate, then you would want to return a new QCheckBox instance in that method

This will be called when the checkbox needs to have its value set from the model. It will hand you the instance of the checkbox, and the particular QModelIndex, so you can retrieve the right value and set it on the widget.

Editing has finished and the model wants the new value from the checkbox. It hands you a reference to the checkbox, the model, and the index so you can grab the bool off the checkbox and set it back on the model

Gives you the opportunity to properly size your editor widget, from the style options opt.rect

So then you would just set this custom delegate for the column in your view. 

There is a C++ example of all of these here:

As far as a persistentEditor goes though...I can't remember exactly, but I think it will call your setModelData() when the cell loses focus, even though the editor stays up. 




olheiros

unread,
Feb 14, 2014, 8:12:56 AM2/14/14
to python_in...@googlegroups.com

Hi again fellas.

After trying to connect all the dots, i think i figured out how QItemDelegate works.

here is the semi-final product.
https://gist.github.com/RicardoViana/8961047

it does almost everything except for the color selection, and for that i have an example
from Justin here "ColorPot()"  that should do the trick.

The only things that still intrigues me are:
-> is there an alignment method to use on the cells of the QtableView?
-> Why the result of QCheckBox.checkState() is returning 0 or 2 instead of  0(unchecked) and 1(checked)?
-> How to get the selected row in the QTableView ?

In attach is the latest update

best regards and A big THANK YOU. i have been learning so much with you guys!!

Ric

--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_maya+unsub...@googlegroups.com.



--
Marcus Ottosson
konstr...@gmail.com

--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_maya+unsub...@googlegroups.com.

Marcus Ottosson

unread,
Feb 14, 2014, 11:08:49 AM2/14/14
to python_in...@googlegroups.com
Wow, great job! It certainly looks like you have got the delegate under control.


-> is there an alignment method to use on the cells of the QtableView?

Yes, but not in the QTableView, that would be the responsibility of the model. As part of its data() method, one of the role asked for be an alignment role of sorts, for which you can return a Qt.AlignLeft etc like usual.

You could possibly subclass the view to take control over this, I am not sure.
 
-> Why the result of QCheckBox.checkState() is returning 0 or 2 instead of  0(unchecked) and 1(checked)?

Because the checkbox can have three states - off (0), half-on (1) and full-on (2).
 
-> How to get the selected row in the QTableView ?

Have a look at the QItemSelectionModel, there should be one within your QTableView.

Best,
Marcus

Justin Israel

unread,
Feb 14, 2014, 2:48:20 PM2/14/14
to python_in...@googlegroups.com
Ya nice work!

For the checkbox, if you are only concerned with on/off, then use isChecked(). Like Marcus said, checkState() would be relevant if you were also considering half checked and you would want to use the constants to test it instead of int/bool

A couple notes on your gist:

  • 34: I would recommend not limiting it to a specific column in this method. The table view has a way to set the delegate for a given column. This way you can reuse this delegate in multiple places, regardless of its intended column. It would just care about the type of data/state it is going to represent.
  • 44: You can actually get the data right off the QModelIndex:  index.data(QtCore.Qt.CheckStateRole) These kinds of things can actually save you a lot of calls, since in the Qt model/view world, you have to expect that your methods will get called a bazillion times a second. Its not really relevant for performance in this particular method since it does get called often, but its a good thing to know when you are inside something like data() which is called insanely often.
  • 46 and 195: Don't compare the CheckStateRole against an int. Use the constants:  QtCore.Qt.Checked
  • 98: Move your list of columns that you define every time into a private constant set for your class:  self._checkBoxColumns = set([4,5,6,7,8])
    That way you can test membership quickly, without creating the list every time, and have a nice place in your constructor to set those.
  • 154: Are you sure this is correct to create and return a widget for the EditRole? Although I don't see it really being used anywhere.

But ya, props on figuring it out. Like I said, you are delving into one of the most complex areas of Qt ;-)


--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_m...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/CAFRtmOD%3D7nqvQDg8Zcf2SxgLiMqJSLi-QgvLQS3dJEooG79u%2BQ%40mail.gmail.com.

Ricardo Viana

unread,
Feb 14, 2014, 4:19:42 PM2/14/14
to python_in...@googlegroups.com
wow guys thanks so much. specially for your valuable time. i will get those fixes done for sure. and try to implement new ones like change one entire column with same value and select the light by changing the row selected. but one step at a time.
have a nice weekend guys. 
best
Ricardo Viana

For more options, visit https://groups.google.com/groups/opt_out.


--
Sent from Mobile

Ricardo Viana

unread,
Feb 19, 2014, 6:30:26 AM2/19/14
to python_in...@googlegroups.com


Hi again. back with some more questions!!
i have fixed a few things and got the alignment right (textAlign actually),
but the checkBoxes don´t align.

https://gist.github.com/RicardoViana/8961047

i followed this link, it seems to be a workaround
http://www.qtcentre.org/threads/19157-QTableView-checkbox-center-with-stylesheet?p=181413#post181413
but its C++ and i loose myself a bit in there since i don´t know c++.
Has anyone got it working in Python/PyQT/Pyside ? I guess the same principles apply.

thanks guys
best
Ricardo

Marcus Ottosson

unread,
Feb 19, 2014, 7:12:22 AM2/19/14
to python_in...@googlegroups.com
I would imagine that the checkbox that you assign is trying to fill up the full space given to it, and for some reason the space seem left-aligned. 

If you were to composite the checkbox within another QWidget, put the checkbox in a layout for the QWidget, and use layout.setAlignment(Qt.AlignCenter | Qt.AlignCenter) the QWidget should fill up the full space, whilst the checkbox should land in the center of the QWidget. Don't forget to remove contentsMargins of the layout as well. layout.setContentsMargins(0, 0, 0, 0)

A neat trick to debug these scenarios is to assign a temporary in-code stylesheet.
my_widget.setObjectName('DebugTest')
my_widget.setStylesheet("#DebugTest {background-color: blue}")

Example delegate method:

checkbox = QCheckBox()
container = QWidget()
layout = QHBoxLayout(container)
layout.addWidget(checkbox)
layout.setAlignment(Qt.AlignCenter | Qt.AlignCenter)
layout.setContentsMargins(0, 0, 0, 0)
# Here is where you return your container, rather than the checkbox directly.
return container

Edit: After having actually looked at the example you posted, this is the way he does it too. :)



--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_m...@googlegroups.com.

For more options, visit https://groups.google.com/groups/opt_out.



--
Marcus Ottosson
konstr...@gmail.com

Ricardo Viana

unread,
Feb 20, 2014, 5:51:01 AM2/20/14
to python_in...@googlegroups.com
Hi Marcus thank you for the help.

I have managed to align everything now. widgets all centered. but now my model doesnt update with the
data after i check the checkboxes, all the rest still working.

code here:

It seems my setModelData function doesn´t work. it doesn't spit any errors so i can trace it.
the checkboxes update nicely from the model so my guess is my input is not reaching the setData on the model.


def setModelData(self, editor, model, index):


     model.setData(index,self.checkBox.isChecked())



this works.

def setEditorData (self, editor, index):


     getValue = index.model().data(index,QtCore.Qt.EditRole)

     if getValue == True:

         self.checkBox.setCheckState(QtCore.Qt.Checked)

         self.container.setStyleSheet("background-color:#347545")

     else:

         self.checkBox.setCheckState(QtCore.Qt.Unchecked)

         self.container.setStyleSheet("background-color:#7F2627")


thank you

best
Ricardo


Another thing im confused is if i should still use the checkStateRole or EditRole for the checkboxes

Justin Israel

unread,
Feb 20, 2014, 1:41:48 PM2/20/14
to python_in...@googlegroups.com

It is probably because when its not a persistent editor, the view triggers the completion of the edit as each cell loses focus and the editor goes away. But when you are using persistent editors, the widget is always there. The delegate/model never see any interaction. Only the check box is seeing it.

You should see if it fixes the problem by connecting a signal from the check box to commitData() on the delegate.

http://qt-project.org/doc/qt-4.8/qabstractitemdelegate.html#commitData

In your createEditor() after you make a new check box...

from functools import partial
...
checkbox.toggled.connect(partial(self.commitData, self))

That will tell the delegate that the checkbox has changed and the state should be committed to the model.
I used a partial because we needed to wrap the editor attribute into the callback. The toggle value from the signal will just get ignored.

--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_m...@googlegroups.com.

Ricardo Viana

unread,
Feb 20, 2014, 1:51:06 PM2/20/14
to python_in...@googlegroups.com
it errors me out

# TypeError: native Qt signal is not callable
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_maya+unsub...@googlegroups.com.

Ricardo Viana

unread,
Feb 20, 2014, 2:09:26 PM2/20/14
to python_in...@googlegroups.com
Its strange because i thought that could be a pyside thing but in the docs

they document it as a method which takes the editor widget.

PySide.QtGui.QAbstractItemDelegate.commitData(editor)
Parameters:editorPySide.QtGui.QWidget




pyside docs



Justin Israel

unread,
Feb 20, 2014, 7:31:44 PM2/20/14
to python_in...@googlegroups.com

What version of Qt are you using? I typed that from my phone :-)

--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_m...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/a0983908-8bdf-4039-9259-05555f1e78fc%40googlegroups.com.

Justin Israel

unread,
Feb 20, 2014, 7:48:24 PM2/20/14
to python_in...@googlegroups.com
Sorry, I completely overlooked my mistake because of my food coma from lunch...

It should really be more like:

    checkbox.toggled.connect(partial(self.commitData.emit, self))

Normally when you connect a signal to another signal, Qt will see it and automatically hook them up properly. But because I was using partial() here as a wrapper, it was literally trying to call the signal like a function. It needed the explicit emit() to be wrapped.
Message has been deleted
Message has been deleted

Ricardo Viana

unread,
Feb 21, 2014, 7:47:03 AM2/21/14
to python_in...@googlegroups.com

Hi Justin

i'm using standard Maya 2014 Pyside. guess is Qt4.8
and i get another error now:

# TypeError: commitData(QWidget*) only accepts 1 arguments, 3 given!


this is my full method in case something else is messing it:

    def createEditor(self, parent , option, index):

self.container = QtGui.QWidget(parent)
layout = QtGui.QHBoxLayout()
layout.setAlignment(QtCore.Qt.AlignCenter | QtCore.Qt.AlignCenter)
layout.setContentsMargins(0, 0, 0, 0)
self.container.setLayout(layout)
self.checkBox = QtGui.QCheckBox(parent)
self.checkBox.toggled.connect(partial(self.commitData.emit, self))
layout.addWidget(self.checkBox)

Justin Israel

unread,
Feb 21, 2014, 2:36:02 PM2/21/14
to python_in...@googlegroups.com
It's trying to pass the toggled bool value as an extra argument. Probably should be more like this:

def createEditor(self, parent , option, index):
    container = QtGui.QWidget(parent)
    layout = QtGui.QHBoxLayout()
    layout.setAlignment(QtCore.Qt.AlignCenter | QtCore.Qt.AlignCenter)
    layout.setContentsMargins(0, 0, 0, 0)
    container.setLayout(layout)
    checkBox = QtGui.QCheckBox(parent)
    checkBox.toggled.connect(partial(self._checkBoxToggled, checkBox))
    layout.addWidget(checkBox)
    container.checkBox = checkBox
    return container

def _checkBoxToggled(self, cbeckBox, *args, **kwargs):
    self.commitData.emit(checkBox)

In addition to just pointing it at a custom slot, I have also changed your configuration a bit. I don't think the editor widget should be built and stored as members of the QItemDelegate. The view expects to use a single instance as a controller for more than just one cell in your table. That means you would be constantly replacing those references with newly created one every time it wants to create an editor. The interface methods tell you what editor widget you should be operating on, as a parameter. So that means setEditorData() and setModelData() should be receiving your container as the parameter and you would have to access your checkbox as:   container.checkBox





--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_m...@googlegroups.com.

Marcus Ottosson

unread,
Feb 21, 2014, 2:52:54 PM2/21/14
to python_in...@googlegroups.com
Just to note, I would re-consider whether or not you need the Model/View framework for this task at all. A simple widget per row, with a widget per column, might make life easier for you.

The QAbstractItemModel and View are designed to handle thousands, if not hundreds of thousands of items, which is why there are so many abstractions, like the delegate and selection model. If you're working with tens or hundreds, I would recommend going with plain widgets.



For more options, visit https://groups.google.com/groups/opt_out.



--
Marcus Ottosson
konstr...@gmail.com

Justin Israel

unread,
Feb 21, 2014, 3:14:01 PM2/21/14
to python_in...@googlegroups.com

Marcus makes a good point. Depending on how big your data set is, it can be easier to just use a TableWidget

Ricardo Viana

unread,
Feb 22, 2014, 12:56:46 PM2/22/14
to python_in...@googlegroups.com
Yeah you guys are right. i should probably stick with a QTableWidget.

In the beginning i was thinking of creating a QHBoxLayout for each light
w/ a widget for each attr inside.
And append each to a central widget.
would instantiate a custom class with all the functionality i needed for
every light in the scene.
What you more experienced guys think about this idea of design ?

But then i would loose those handy header labels
("diffuse","specular",etc) and have to duplicate labels for
every light.


the thing is i learned a lot from this process. And maybe it's not
necessary to handle
tons of data, but the neat thing about using the model is that if i
change some attr in the attribute
editor, it immediately updates the data on my table, as soon as i have
focus on it. And the other way
i need always a refresh button.

I got another older version that has the functionality, just doesn't
have the nice centered checkboxes.
And it doesn't even use delegates. Just by using CheckStateRole.

https://gist.github.com/RicardoViana/8961047


have a nice weekend all, and again thank you for all the mentoring.
best
Ricardo




Reply all
Reply to author
Forward
0 new messages