maemo.org - Talk

maemo.org - Talk (https://talk.maemo.org/index.php)
-   Development (https://talk.maemo.org/forumdisplay.php?f=13)
-   -   PyQt Chaning multiple Objects attributes at once (https://talk.maemo.org/showthread.php?t=46653)

mikec 2010-03-06 16:11

PyQt Changing multiple Objects attributes at once
 
Hi Guys

I have over 100 QpushButtons in a gridlayout, and I want to set the toggle state of all of them to the same state.

Any clues how I can do this short of writing this statement 100 times
self.pushButton1.setChecked(0)
.
.
.
self.pushButton100.setChecked(0)


Cheers

Mike C

hartti 2010-03-07 05:54

Re: PyQt Changing multiple Objects attributes at once
 
Maybe (most likely?) I am missing something obvious, but why don't you use array of push buttons and iterate through them in a loop?

Hartti

mikec 2010-03-07 08:35

Re: PyQt Changing multiple Objects attributes at once
 
Quote:

Originally Posted by hartti (Post 558432)
Maybe (most likely?) I am missing something obvious, but why don't you use array of push buttons and iterate through them in a loop?

Hartti

that is exactly what i want to do, how does one refer to the object names in a loop? the object names have been given by qt designer.

also i thought there might be a more clever way, ie changing the
property of all instances of QpushButton.

thanks in advance

pelago 2010-03-07 10:29

Re: PyQt Changing multiple Objects attributes at once
 
Quote:

Originally Posted by mikec (Post 558509)
the object names have been given by qt designer.

I'm not an expert, but it might be that creating these buttons one-by-one in Qt Designer wasn't the best thing to do. If you create them in code instead, in an array as mentioned, you should be able to control them easier.

attila77 2010-03-07 10:44

Re: PyQt Chaning multiple Objects attributes at once
 
Pelago is right. However, since you already did it the other way, you're probably looking for the eval function, something like

eval("pushButton%s.setChecked(0)" % i)

mikec 2010-03-07 11:10

Re: PyQt Chaning multiple Objects attributes at once
 
Thanks Atilla and Pelago

I will give the eval function a try, sounds like exec() might also be useful.

Is there no way to globally set QpushButton attributes?
for instance I can change the style of QpushButtons globally with a single method

attila77 2010-03-07 15:27

Re: PyQt Chaning multiple Objects attributes at once
 
Quote:

Originally Posted by mikec (Post 558603)
Thanks Atilla and Pelago

I will give the eval function a try, sounds like exec() might also be useful.

Is there no way to globally set QpushButton attributes?
for instance I can change the style of QpushButtons globally with a single method

Style or attribute ? 'Cause those are two very different things :) If you maintain parent-child relations or a wrapper class you could of course make things easier.

mikec 2010-03-09 19:32

Re: PyQt Chaning multiple Objects attributes at once
 
ok just to close close this thread off.

Could not get Eval() to work but Exec() worked well , but I decided to rewrite the app using an array of buttons populated from within the application. This was non trivial cause of python arrays peculiarities but here is the code snippet for future ref.

This creates an 8x16 array of buttons in an array declared inside the Qt MainWindow Class (this avoids the use of globals, don't even go there :D)

Code:

#Here is the MainWindow Class with an array called ledArray declared.
class MainWindow(QMainWindow, Ui_MainWindow):
    ledArray=[]
    def __init__(self, parent = None):
          QMainWindow.__init__(self, parent)
          self.setupUi(self)

Here is the initialization routine to create the array of buttons in the Main.py and inserts them into the UI created by Qt Designer, where I had placed a grid layout in the main window to accept my buttons, all pre-styled to look nice.

Code:

def initled():       
        for r in range(8):
            MainWindow.ledArray.append([])
            for c in range(16):
                    MainWindow.ledArray[r].append(QtGui.QPushButton())
                    MainWindow.ledArray[r][c].setCheckable(True)
                    MainWindow.ledArray[r][c].setMinimumSize(QtCore.QSize(45, 45))
                    ui.gridLayout.addWidget(MainWindow.ledArray[r][c], r, c)

And I can change all attributes at once now with loop.

Code:

for r in range(8):
            for c in range(16):
                self.ledArray[r][c].setChecked(self.ledState)

and here is the result

http://farm3.static.flickr.com/2799/...2bf5432a_o.png

Mike C

noobmonkey 2010-03-09 19:37

Re: PyQt Chaning multiple Objects attributes at once
 
Quote:

Originally Posted by mikec (Post 561623)
ok just to close close this thread off.

Could not get Eval() to work but Exec() worked well , but I decided to rewrite the app using an array of buttons populated from within the application. This was non trivial cause of python arrays peculiarities but here is the code snippet for future ref.

This creates an 8x16 array of buttons in an array declared inside the Qt MainWindow Class (this avoids the use of globals, don't even go three :D)

Code:

#Here is the MainWindow Class with an array called ledArray declared.
class MainWindow(QMainWindow, Ui_MainWindow):
    ledArray=[]
    def __init__(self, parent = None):
          QMainWindow.__init__(self, parent)
          self.setupUi(self)

Here is the initialization routine to create the array of buttons in the Main.py and inserts them into the UI created by Qt Designer, where I had placed a grid layout in the main window to accept my buttons, all pre-styled to look nice.

Code:

def initled():       
        for r in range(8):
            MainWindow.ledArray.append([])
            for c in range(16):
                    MainWindow.ledArray[r].append(QtGui.QPushButton())
                    MainWindow.ledArray[r][c].setCheckable(True)
                    MainWindow.ledArray[r][c].setMinimumSize(QtCore.QSize(45, 45))
                    ui.gridLayout.addWidget(MainWindow.ledArray[r][c], r, c)

And I can change all attributes at once now with loop.

Code:

for r in range(8):
            for c in range(16):
                self.ledArray[r][c].setChecked(self.ledState)

and here is the result

http://farm3.static.flickr.com/2799/...2bf5432a_o.png

Mike C

that rocks!!!!!

attila77 2010-03-12 12:06

Re: PyQt Chaning multiple Objects attributes at once
 
Quote:

Originally Posted by mikec (Post 561623)
Code:

def initled():       
        for r in range(8):
            MainWindow.ledArray.append([])
            for c in range(16):
                    MainWindow.ledArray[r].append(QtGui.QPushButton())
                    MainWindow.ledArray[r][c].setCheckable(True)
                    MainWindow.ledArray[r][c].setMinimumSize(QtCore.QSize(45, 45))
                    ui.gridLayout.addWidget(MainWindow.ledArray[r][c], r, c)


Great stuff ! Two notes, though (not as discouragement, just as advices):

Avoid things like MainWindow.ledArray[r].append(QtGui.QPushButton())

The problem is that Python handles Qt object lifetimes/references differently than C++ and this (calling a constructor as a parameter for another function) can cause all sorts of nastiness if you're not careful. I know it's uglier to allocate a local variable for this, but you'll understand when you meet the first segfaults :)

The other note is python style - when dealing arrays it's recommended to use the 'in' operator. Faster, IMHO prettier and less error-prone than index based stuff. So, something like:

Code:

# c-ish
for r in range(8):
            for c in range(16):
                self.ledArray[r][c].setChecked(self.ledState)

# pythonic
for row in self.ledArray:
            for led in row:
                led.setChecked(self.ledState)

See ? :)

pelago 2010-03-12 12:23

Re: PyQt Chaning multiple Objects attributes at once
 
Quote:

Originally Posted by attila77 (Post 564953)
Avoid things like MainWindow.ledArray[r].append(QtGui.QPushButton())

Hi, I'm not the OP but I'm trying to learn PyQt. Do you mean that
Code:

tempPushButton = QtGui.QPushButton()
MainWindow.ledArray[r].append(tempPushButton)

is the thing to do? Is there a reference as to why the first form is incorrect?

attila77 2010-03-12 12:59

Re: PyQt Chaning multiple Objects attributes at once
 
Quote:

Originally Posted by pelago (Post 564973)
Hi, I'm not the OP but I'm trying to learn PyQt. Do you mean that
Code:

tempPushButton = QtGui.QPushButton()
MainWindow.ledArray[r].append(tempPushButton)

is the thing to do? Is there a reference as to why the first form is incorrect?

It's not incorrect, just might cause trouble on particular combination of methods and classes :) AFAIK the problem stems from two sources - from the way Python garbage collection works and from the problem that it does not necessarily know about object pointer ownership transfers than happen on Qt's level. Can't find anything descriptive on google right now, so you'll have to take my word for it :) Or remember this and when your first segfault appears, try the suggested change above and then ponder why it fixes the problem :)

mikec 2010-03-12 19:35

Re: PyQt Chaning multiple Objects attributes at once
 
Quote:

Originally Posted by attila77 (Post 564953)
Great stuff ! Two notes, though (not as discouragement, just as advices):

Avoid things like MainWindow.ledArray[r].append(QtGui.QPushButton())

The problem is that Python handles Qt object lifetimes/references differently than C++ and this (calling a constructor as a parameter for another function) can cause all sorts of nastiness if you're not careful. I know it's uglier to allocate a local variable for this, but you'll understand when you meet the first segfaults :)

The other note is python style - when dealing arrays it's recommended to use the 'in' operator. Faster, IMHO prettier and less error-prone than index based stuff. So, something like:

Code:

# c-ish
for r in range(8):
            for c in range(16):
                self.ledArray[r][c].setChecked(self.ledState)

# pythonic
for row in self.ledArray:
            for led in row:
                led.setChecked(self.ledState)

See ? :)


The Pythonic way rocks, so much easier when you know how :D

aspidites 2010-03-13 09:20

Re: PyQt Chaning multiple Objects attributes at once
 
I assume list comprehensions aren't used to keep things simple/terse?

mikec 2010-03-13 09:34

Re: PyQt Chaning multiple Objects attributes at once
 
Quote:

Originally Posted by pelago (Post 564973)
Hi, I'm not the OP but I'm trying to learn PyQt. Do you mean that
Code:

tempPushButton = QtGui.QPushButton()
MainWindow.ledArray[r].append(tempPushButton)

is the thing to do? Is there a reference as to why the first form is incorrect?

Just tried this, and it does not work. you end up appending the same pushbutton into the array, rather than an instance of the qpushbutton. This has got me curious

attila77 2010-03-13 10:51

Re: PyQt Chaning multiple Objects attributes at once
 
Quote:

Originally Posted by mikec (Post 565877)
Just tried this, and it does not work. you end up appending the same pushbutton into the array, rather than an instance of the qpushbutton. This has got me curious

That's what I get when I don't post a full example just a hint. In python, all those objects are addressed by reference. If I understand correctly what you did, in C terms - you are appending the tempPushbutton *pointer address* to the list, not it's values, so whenever you change it, you changed all the values :)

I can already hear the question - but then I need to create an array and append all of them... yuck, that's no better than the original ! But before I address that valid concern, let's let's dig a little deeper into the topic of Python-style garbage collection. Take a look at this:

Code:

// C++
col = new QColor();
col = new QColor();

# Python
col = QtGui.QColor()
col = QtGui.QColor()

If you think the two are equivalent, you're wrong :) The point is that in case of C++, the first created object is still there after the second line, even though it is no longer directly addressable. In Python, however, the events of the second line mean the object will be garbage collected as there are no references left to it ! Now, to curve back to the original problem, we can solve this by increasing the reference count by hierarchical means, by specifying an explicit parent object.

This also explains why you don't get a segfault in your original example - by calling the layout's addWidget before the QPushButton you created a reference that prevents garbage collection. Without this, if you came back to it after the original QPushButton pointer went out of scope (or the reference disappeared for whatever ownership reason), you would get a big fat segfault because (unlike in C++) the underlying object is no longer there. So the alternative solution to local variables (which are ugly if you have many objects) is to specify parents in the constructor, creating the necessary reference to avoid garbage collection (i.e. mylist.append(QPushButton(parent))).

Usually you create enough references inadvertently (like here with addWidget, or QObject voodoo that happens behind the scenes) for garbage collection/ownership not to mess you up, but sooner or later, especially in applications with multiple intertwined classes, you *will* run into segfaults or weirdness because of this. Seems we got into deep water here, but the idea is not to get surprised when you meet your first segfaults and know where to look for trouble spots :)

attila77 2010-03-13 10:58

Re: PyQt Chaning multiple Objects attributes at once
 
Quote:

Originally Posted by aspidites (Post 565867)
I assume list comprehensions aren't used to keep things simple/terse?

Yes, the whole thing could have been written as a one-liner with list comprehension, but that is a bit scary (and sounds backwards) for many python newbies. First things first :)

EDIT: just for the sake of completeness:
Code:

[[led.setChecked(self.ledState) for led in row] for row in self.ledArray]


All times are GMT. The time now is 22:24.

vBulletin® Version 3.8.8