[PySide] QStateMachine and updating a progressbar

271 views
Skip to first unread message

Markus Hubig

unread,
Mar 16, 2011, 7:27:59 AM3/16/11
to pys...@lists.openbossa.org
Hi @all!

I'm new to to GUI programming and this is surely a simple newbie
question ;-)
I've created a small GUI with just one Button and a progessbar. If one
presses the Button a QStateMachine is created and started and performs
a
rather long running process.

Now, what I wanna do is to update a progessbar every time I'm entering
a new State. The Problem is that I'm running thru my StateMachine but
the progessbar is updated only _after_ the QStateMachine is finished!

So what I'm missing here?

Here is a snipplet of my code:

class MainWindow(QMainWindow, Ui_mainWindow):

def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.setupUi(self)
# Start the QStateMashine by pressing the Button
self.pushButton.clicked.connect(self.state_mashine)

def state_mashine(self):
self.progressBar_4.setValue(0)
machine = QStateMachine(self)

prepare = QState()
prepare.entered.connect(self.prepare_dut())

flash = QState()
flash.entered.connect(self.flash_dut())

config = QState()
config.entered.connect(self.config_dut())

finish = QFinalState()
finish.entered.connect(self.finish_dut())

prepare.addTransition(flash)
flash.addTransition(config)
config.addTransition(finish)

machine.addState(prepare)
machine.addState(flash)
machine.addState(config)
machine.addState(finish)
machine.setInitialState(prepare)
machine.start()

def prepare_dut(self):
self.progressBar_4.setValue(33)
print "State 1: Prepare DUT"
time.sleep(1)

def flash_dut(self):
self.progressBar_4.setValue(66)
print "State 2: Flash DUT"
time.sleep(1)

def config_dut(self):
self.progressBar_4.setValue(80)
print "State 3: Config DUT"
time.sleep(1)

def finish_dut(self):
self.progressBar_4.setValue(100)
print "State 4: DUT finished!"
time.sleep(1)

if __name__ == '__main__':
app = QApplication(sys.argv)
frame = MainWindow()
frame.show()
app.exec_()
_______________________________________________
PySide mailing list
PyS...@lists.pyside.org
http://lists.pyside.org/listinfo/pyside

Hugo Parente Lima

unread,
Mar 16, 2011, 4:56:35 PM3/16/11
to pys...@lists.pyside.org, pys...@lists.openbossa.org, Markus Hubig
On Wednesday 16 March 2011 08:27:59 Markus Hubig wrote:
> Hi @all!
>
> I'm new to to GUI programming and this is surely a simple newbie
> question ;-)
> I've created a small GUI with just one Button and a progessbar. If one
> presses the Button a QStateMachine is created and started and performs
> a
> rather long running process.
>
> Now, what I wanna do is to update a progessbar every time I'm entering
> a new State. The Problem is that I'm running thru my StateMachine but
> the progessbar is updated only _after_ the QStateMachine is finished!
>
> So what I'm missing here?

You need to give Qt a chance to process the events, when you call
machine.start() the machine starts, change states and until it finishes Qt
doesn't process any event, so the GUI isn't updated.

There's two possibilities to fix this:

- Put your state machine code into a separated thread.
- Call QCoreApplication.processEvents() on each slot used by your
QStateMachine.

this was my two cents.

--
Hugo Parente Lima
INdT - Instituto Nokia de Tecnologia

signature.asc

Darren Dale

unread,
Mar 16, 2011, 5:00:40 PM3/16/11
to Hugo Parente Lima, pys...@lists.openbossa.org, pys...@lists.pyside.org, Markus Hubig

Although, you need to take care that processEvents() will never be
called while actually processing events.

Markus Hubig

unread,
Mar 17, 2011, 6:23:32 AM3/17/11
to pys...@lists.openbossa.org
On 16 Mrz., 21:56, Hugo Parente Lima <hugo.l...@openbossa.org> wrote:
> On Wednesday 16 March 2011 08:27:59 Markus Hubig wrote:
>
> > Now, what I wanna do is to update a progessbar every time I'm entering
> > a new State. The Problem is that I'm running thru my StateMachine but
> > the progessbar is updated only _after_ the QStateMachine is finished!
>
> > So what I'm missing here?
>
> You need to give Qt a chance to process the events, when you call
> machine.start() the machine starts, change states and until it finishes Qt
> doesn't process any event, so the GUI isn't updated.
>
> There's two possibilities to fix this:
>
> - Put your state machine code into a separated thread.

Thanks for this tip!

Now I've putted my QStateMachine into a separate thread, as you
suggested,
and it works ... mostly. Now if I start my application I get two error
messages:

1. QMetaObject::connectSlotsByName: No matching signal for
on_progress()
2. QObject::setParent: Cannot set parent, new parent is in a different
thread

Msg 1 appears when I start the application. Msg 2 when I press the
start Button.
But the application is running fine. While msg 1 is surely something
related to
the multi inheritance in my application but what's the course of Msg
2?

Hope someone has a tip for me ... ;-)

- Markus

--------------------8<----schnipp---------------------------------

import time
import threading
import platform

from PySide import QtCore
from PySide import QtGui

from ui_trimeibt import Ui_mainWindow

__version__ = '0.0.1'

class MainWindow(QtGui.QMainWindow, Ui_mainWindow):

def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.setupUi(self)

self._progress = 0
self._running = False
self.startButton.clicked.connect(self._start_machine)
self.on_progress.connect(self._update_progressbar)

def _update_progressbar(self):
self.progressBar_DUT1.setValue(self.progress)

def _reset_progressbar(self):
print "RESET"
self.progressBar_DUT1.reset()

def _state_machine(self):
machine = QtCore.QStateMachine(self)
machine.finished.connect(self._reset_progressbar)

prepare = QtCore.QState()
prepare.entered.connect(self.prepare_dut())
flash = QtCore.QState()
flash.entered.connect(self.flash_dut())
config = QtCore.QState()
config.entered.connect(self.config_dut())
finish = QtCore.QFinalState()
finish.entered.connect(self.finish_dut())

prepare.addTransition(flash)
flash.addTransition(config)
config.addTransition(finish)

machine.addState(prepare)
machine.addState(flash)
machine.addState(config)
machine.addState(finish)
machine.setInitialState(prepare)
machine.start()

def prepare_dut(self):


print "State 1: Prepare DUT"

self.progress = 20
time.sleep(1)

def flash_dut(self):


print "State 2: Flash DUT"

self.progress = 50
time.sleep(1)

def config_dut(self):


print "State 3: Config DUT"

self.progress = 70
time.sleep(1)

def finish_dut(self):


print "State 4: DUT finished!"

self.progress = 100
time.sleep(1)

@QtCore.Slot()
def _start_machine(self):
if not self._running:
self._running = True
thread = threading.Thread(target=self._state_machine)
thread.start()

def _get_progress(self):
return self._progress

def _set_progress(self, progress):
self._progress = progress
self.on_progress.emit()

on_progress = QtCore.Signal()
progress = QtCore.Property(int, _get_progress, _set_progress,
notify=on_progress)

if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)


frame = MainWindow()
frame.show()
app.exec_()


--------------------8<----schnapp---------------------------------

Thomas Perl

unread,
Mar 17, 2011, 6:29:44 AM3/17/11
to Markus Hubig, pys...@lists.openbossa.org
Hi Markus,

2011/3/17 Markus Hubig <mhu...@imko.de>:


> 2. QObject::setParent: Cannot set parent, new parent is in a different
> thread
>
> Msg 1 appears when I start the application. Msg 2 when I press the
> start Button.
> But the application is running fine. While msg 1 is surely something
> related to
> the multi inheritance in my application but what's the course of Msg
> 2?

You pass "self" as the first parameter in this part of your code:

>    def _state_machine(self):
>        machine = QtCore.QStateMachine(self)

And "_state_machine" is run in a different thread (the one you
created; the other one is the implicit main thread of execution of the
application). Message 2 is pretty clear there. To avoid the error,
don't set the QStateMachine as child (by not passing "self" as the
first parameter). If you do this, however, the state machine doesn't
become a child of the object (obviously), but you need to keep a
reference to it so it doesn't get destroyed when it falls out of
scope. You can do this by having in the __init__ method of your
MainWindow:

self._machine = None

And then in the _state_machine function:

self._machine = QtCore.QStateMachine()

This avoids the second error message.

HTH.
Thomas

Markus Hubig

unread,
Mar 17, 2011, 8:18:07 AM3/17/11
to pys...@lists.openbossa.org

Yeah! I finally got it working without errors! ;-)

--------------------8<----schnipp
(trime.py)------------------------------
import platform

from PySide import QtCore
from PySide import QtGui

from ui_trimeibt import Ui_mainWindow
from statemachine import StateMachine

__version__ = '0.0.1'

class MainWindow(QtGui.QMainWindow, Ui_mainWindow):

def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.setupUi(self)
self._progress = 0
self._running = False
self.startButton.clicked.connect(self._start_machine)

self.machine = StateMachine()
self.machine.on_progress.connect(self._update_progressbar)
self.machine.finished.connect(self._reset_progressbar)

def _update_progressbar(self):
self.progressBar_DUT1.setValue(self.machine.progress)

def _reset_progressbar(self):
print "RESET"
self.progressBar_DUT1.reset()

def _start_machine(self):
self.machine.start()

if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
frame = MainWindow()
frame.show()
app.exec_()

--------------------8<----schnapp (trime.py)
---------------------------------

--------------------8<----schnipp
(statemachine.py)---------------------------
import time
import platform

from PySide import QtCore
from PySide import QtGui

class StateMachine(QtCore.QThread):
def __init__(self, parent=None):
super(StateMachine, self).__init__(parent)
self._progress = 0

def run(self):
machine = QtCore.QStateMachine()

prepare.addTransition(flash)
flash.addTransition(config)
config.addTransition(finish)

def _get_progress(self):
return self._progress

def _set_progress(self, progress):
self._progress = progress
self.on_progress.emit()

on_progress = QtCore.Signal()
progress = QtCore.Property(int, _get_progress, _set_progress,
notify=on_progress)

--------------------8<----schnapp
(statemachine.py)---------------------------

Reply all
Reply to author
Forward
0 new messages