QStateMachine: Difference between QEvent and Signal? - python

I'm currently porting a small application from PyGTK to PySide which sits in your systray and periodically checks a server for updates.
When I initially wrote this, I used a custom state model to represent its behaviour:
init: initial state
check: check server for updates
disconnected: no connection to server (wait for some time to try again)
error: server reported an error (user needs to check settings before proceeding)
idle: wait for POLLING INTERVAL seconds before checking again
quit
I since discovered that Qt has QStateMachine, which seems perfect for this type of structure. However, I was not able to apply the examples satisfyingly to my problem!
In particular:
In the context of QStateMachine, what is the difference between Signal and QEvent?
How do I define a conditional transition, i.e. on error go to... ?
Should program logic happen in Transition.onTransition() or in QState.onEnter()?
Any pointers are appreciated muchly!

Ok. Pyside.QtCore.Signal is Signals & Slots derivative.
As for your questions
I'd say there is no difference in QEvent and Signal in context of QStateMachine (although QEvent and Signals & Slots are totally different concepts). Depending on your needs you can trigger transition with QEvent or Signal. See QAbstactTransition for the list of out of the box transitions:
Inherited by: QSignalTransition, QEventTransition,
QMouseEventTransition, QKeyEventTransition
Again depending on what happens inside your application your error may be either signal from QObject or you can send (post) custom QEvent. You'll need to implement your custom QEvent and/or custom QEventTransition to trigger transition only on your events.
And again it depends:) Transition is the glue. It has knowledge about source and destination states. So I'd put only preparatory code inside onTransition() and state initialization code inside onEnter(). Also it seems wrong for me to put code that changes state inside onTransition() like in example you've shown:
def onTransition(self, e):
x = e.arguments()[0]
fac = self.fact.fac
self.fact.fac = x * fac
self.fact.x = x - 1
but as you can see it works well.
NB: If you have iOS experience then UIStoryboardSegue is analogue for transition. Mainly it is used to pass data between UIView's i.e. UI states.

Related

Running background code in Maya as a Daemon, scriptJob

I want a Maya camera orbiting a mesh while user chooses what to do with that mesh on my own script interface.
So i have a while orbiting the camera, but no way to interact with user interface while this happens.
There is anyway to "share", or part the focus in second's decims to be able to interact with user interface?
I tried with cmds.evaldeferred(), but no luck...
Technically, Maya widgets interface is immediately available when script finishes the job...
I'm trying to finish the job with one single camera little orbit, and relaunch it with mouse movement event... Time changing event... But no way how to do that... Would be like a daemon running in the background... No idea how I could reach something like that....
Some code:
import maya.cmds as cmds
#declares global variable
global orbitCam
#just something to see in the scene
cmds.polyCube()
#function to stop camera orbit
def stopOrbiting():
global orbitCam
orbitCam = False
#simplest ui
cmds.window("testWindow")
cmds.formlayout("flo", parent="testWindow")
#button that calls stopOrbit function
cmds.button("pushMeIfYouCan", parent="flo", label="pushMeIfYouCan", c="stopOrbiting()")
cmds.showWindow("testWindow")
#condition for the while
orbitCam=True
#while itself
while orbitCam:
cmds.orbit("persp", ra=(0.2,0.1))
Any way to be able to push the button -and interact with widgets- while camera orbits?
Well, based on solution provided by mhlester in the link I posted under the question (and considering a great answer too by theodox, as a wonderful alternative, thank you both, guys), I'm gonna aswer my own question and provide another new detailed explanation perhaps can help somebody in the future.
First of all, Maya's "scriptjob" command utility puts in memory a script (or a function), haves a trigger to start executing that function, and the condition to Maya to execute that function once registered and triggered, is that Maya must to be doing exactly nothing, so, Maya must be idle.
Being that way, Maya never collapses or crashes or hangs by a scriptJob. ScriptJob is basically a... kind of "daemon" working in the background.
The good thing, is that is possible to limit the maximum number of times per second, so you can even maintain your system fresh and working if you want to care it about.
Once a scriptJob is launched (or registered in Maya memory), it can be killed to never runs again, and obviously, can be registered again if we want to start working again.
Exists an internal scriptJobs list inside of Maya, where you can find the scriptjob if you have forgotten to store that scriptJob in a variable, where you can find the "scriptJob ID", just an integer number you can use to kill the job.
After the first theory, let's see some code now:
This line will register a new daemon (maya scriptjob), its reference will be stored in "activeDaemon" variable, it will call a function called "camDaemon" with some arguments:
activeDaemon = registerDaemon(0.04, BackgroundFunction, ArgumentsForBackgroundFunction)
The point here is not just launch the daemon with conditions, besides, to store the daemon id to be able to kill it with:
cmds.scriptJob(kill=activeDaemon)
If we forgotten to store the daemon (scriptjob) id, we can use this command to watch a list with all scriptJobs of Maya:
cmds.scriptJob(listJobs=True)
We would need to find the last ids in the list with the event "idle", and probably we could hit and finished stopping our daemon killing it.
Now let's see "registerDaemon" function (all the absolute credit for MhLester):
def registerDaemon(seconds, backgroundFunction, *args, **kwargs):
def updateNow():
now = time.time()
if now - updateNow.then > seconds:
updateNow.then = now
backgroundFunction(*args, **kwargs)
updateNow.then = time.time()
return cmds.scriptJob(event=("idle", updateNow))
registerDaemon defines an internal function (updateNow) that calls "automatically" at the backgroundFunction we pass as argument, including this "backgroundFunction" arguments. Just that. A function A that defines a function B that calls a function C. And it calls it every quantity of seconds we specify at register the daemon with the first argument, so if we want it executed 25 times per second, 1seg/25fps = 0.04 would be the right value.
To have this function working, we must import the module time in our script with:
import time
Finally it returns the scriptJob id at the same time it registers it in Maya memory, registering with the event "idle" and with the function (B) that calls the function (C) we really want executed in the background. This way we can control the temperatures of the machine not overwhelming the cpu unnecessary. Very complete last line, brilliant, at least, for me.
At this point no other thing left than just register the function we want to have running the background in the Maya memory (take the code you want in the background, put inside a function and execute it one time in Script Editor)
def backgroundFunction():
print "this message shows in idle time by a daemon (Maya scriptjob)"
Of course there are much more in the documentation (Maya scriptJob documentation), as a list of events or conditions, but the base is this.

QApplication and main window connection

Considering a very basic HelloWorld PyQt5 application like:
app = QApplication(sys.argv)
window = QWidget()
window.setWindowTitle('PyQt5 app')
window.setGeometry(100, 100, 280, 80)
window.move(60, 15)
helloMsg = QLabel('<h1>Hello World!</h1>', parent=window)
helloMsg.move(60, 15)
window.show()
sys.exit(app.exec_())
It constructs a QApplication, a parent-less QWidget becoming the main window, adds a QLabel and shows it.
My question is: how does the QApplication know about the main window?
There is nothing in this code connecting the two.
Perhaps it is a naive question but just looking at this, it seems like magic.
How is the main window's paint event added to the application's event queue without telling so in the source code ? How does the QApplication instance know what is going to be added below in the source code?
tl;dr
There's no "magic" involved: sub-modules can access their "main" modules, and each module of Qt can know if a QApplication instance is running.
Long version
I think that's an interesting question, especially for those who are not that into low level programming. For instance, I've always given the QApplication as some sort of a "cartesian" assumption: «it exists».
As a premise, I'm not going to give you a very technical and low-level explanation: I don't have enough skills to do so (and I really welcome any other answer or edit to this), but I'm assuming that's not what you're looking for.
[Almost] technically speaking, you've to remember that Qt - and PyQt along with it - is an environment (the exact term is framework). As such, each one of its sub elements (classes, and eventually instances of them) "know" about that environment.
QApplication (and its base classes QGuiApplication and QCoreApplication) is a class that is internally accessible from any "sub" Qt module.
It's something like the builtin types (str, int, bool, etc.) that are accessible to any module. For example, the os.path is a python module that you can import as standalone, but it knows what the main os module is, and each function of os.path actually uses some part of that main module.
Like most frameworks, Qt has what is called called an event loop, which is usually run as soon as you call Q[*]Application.exec(). An event loop is something that generally blocks itself waiting for something to happen (an event) and eventually react to it.
Whenever a Qt class needs it, it internally calls the Q[*]Application.instance() method to ensure that an instance of the application is running, meaning that an event loop is active and running. For example Qt widgets need that to be able to show the interface and interact with it: tell the operating system that a new window has been created, therefore it has to be drawn on the screen, so the OS will say "ok, let's show it" by sending Qt an event requesting the drawing, then Qt will "send" that event to that window that will finally draw itself by telling Qt how it's being painted; finally Qt will "tell" the OS what's going to be shown. At the same time, that window might need to know if some keyboard or mouse event has been sent to it and react in some way.
You can see this in the Qt sources: whenever a new QWidget is created, it ensures that a QApplication exists by calling QCoreApplication.instance().
The same happens for other Qt objects that require an application event loop running. This is the case of QTimer (that doesn't require a graphical interface, but has to interface with the system for correct timing) and QPixmap (which needs to know about the graphical environment to correctly show its image), but in some specific cases it also depends on the platform (for example, creation of a QIcon on MacOS requires a running event loop, while that's not necessary on Linux and Windows).
So, finally, that's what (roughly) happens when you run your code:
# create an application instance; at this point the loop is not "running"
# (but that might be enough to let know most classes about the current system
# environment, such as available desktop geometries or cursor position)
app = QApplication(sys.argv)
# create a widget; an application exists and the widget can "begin" to create its
# interface using the information provided by it, like the system default font
# (it's actually a bit more complicated due to cross-platform issues, but let's
# ignore those things now)
window = QWidget()
window.setWindowTitle('PyQt5 app')
window.setGeometry(100, 100, 280, 80)
window.move(60, 15)
helloMsg = QLabel('<h1>Hello World!</h1>', parent=window)
helloMsg.move(60, 15)
# "ask Qt to prepare" the window that is going to be shown; at this point the
# widget's window is not shown yet even if it's "flagged as shown" to Qt, meaning
# that "window.isVisible()" will return True even if it's not actually visible yet
window.show()
# start the event loop by running app.exec(); sys.exit will just "wait" for the
# application to return its value as soon as it actually exits, while in the
# meantime the "exec" function will run its loop almost as a "while True" cycle
# would do; at this point the loop will start telling the OS that a new window
# has to be mapped and wait from the system to tell what to do: it will probably
# "answer" that it's ok to show that window, then Qt will tell back the widget
# that it can go on by "polishing" (use the current style and app info to finally
# "fix" its size) and begin drawing itself, then Qt will give back those drawing
# information allowing the OS to actually "paint" it on the screen; then it will
# be probably waiting for some user (keyboard/mouse) interaction, but the event
# loop might also tell the OS that the window is willing to close itself (as a
# consequence of a QTimer calling "widget.close", for instance) which could
# possibly end with ending the whole event loop, which is the case of
# https://doc.qt.io/qt-5/qguiapplication.html#quitOnLastWindowClosed-prop
# which would also cause the application to, finally, return "0" to sys.exit()
sys.exit(app.exec_())

can I create/push custom event in Tkinter python?

can I create/push custom event in Tkinter python?
in the code https://stackoverflow.com/a/10575137/933882
the redraw callback is still invoked on a fixed schedule, every 100 milliseconds etc.
but is it possible to trigger the callback immediately as the external serial data is available ? from usage point of view it's probably not much different, but the mechanisms are very different.
thanks

Infinite background while loop in PyQt

I want to write a QT program in Python and it is supposed to continuously look for updates. Just like
while(True):
doSomething()
would do in a console based program.
Is there a specific slot which I can connect to for this purpose?
It depends from what you want to achieve.
QThread is one solution.
You also can solve this by using a QTimer with a timeout of zero. Such a timer executes
the connected slot whenever nothing other needs to be done by the event loop (like some background task).
Depending from what you do also a QTimer with a decent timeout can be the solution (like polling for an event).
As I said, it depends from what you want to do.
Use QThread to create loop what your want. Your can find example in here, here and a lot of in google search result. QThread i subclass in QObject so your can connect just like QWidget old-style or new-style signal.

PyQt beforeInsert() signal

I'm trying to use the beforeInsert() signal emitted by the QSqlRelationalTableModel in PyQt. However, even after I make edits to the model, the slot connected to the signal does't run. When exactly is the beforeInsert() signal emitted? Could someone show a working example of the use of beforeInsert(), or beforeUpdate() for that matter?
The documentation says "This signal is emitted by insertRowIntoTable() before a new row is inserted into the currently active database table". So it sounds like the signal is only emitted when insertRowIntoTable() is called, and not for any other type of model edit.

Categories

Resources