The following code is an alternative to the usual style (using decorators) pyglet makes use of.
Can anyone explain how the on_draw() method is called here?
import pyglet
class HelloWorldWindow(pyglet.window.Window):
def __init__(self):
super(HelloWorldWindow, self).__init__()
self.label = pyglet.text.Label('Hello, world!')
def on_draw(self):
self.clear()
self.label.draw()
if __name__ == '__main__':
window = HelloWorldWindow()
pyglet.app.run()
The code written using decorators can be found here.
You can just dig through the source to find the answer.
The EventLoop class (you use it by pyglet.app.run()) dispatches the on_draw event regulary.
From the source:
Calling run begins the application event loop, which processes
operating system events, calls pyglet.clock.tick to call scheduled
functions and calls pyglet.window.Window.on_draw and
pyglet.window.Window.flip to update window contents.
The Window class subscripes to this event:
BaseWindow.register_event_type('on_draw')
So by subclassing Window, you ensure your on_draw method gets called.
Look at the programming guide for an example of how the event system of pyglet works.
Related
I am developing a QGIS Plugins, complex plugin with various classes and methods that call other methods.
My plugin works fine, but I'm loking to run some class method in the background (something like jobs in JAVA) to avoid frozen GUI. I mean, I have a class, this class has a method and this method I would like run in background
I tried with QThread,QTask, threading, QProcess, etc, but I did not find what I want to do.
Some idea could help me? Some plugin does works with background process? Some plugin example?
Thanks
I have been using QTask in order to run heavy processes in the background.
The useful ressources I am using are these ones :
https://gis.stackexchange.com/questions/94477/how-to-use-threads-in-pyqgis-mainly-to-keep-ui-active
https://github.com/qgis/QGIS/pull/3004
You have to specifically create a new Class that inherits from QgsTask and call it in the run() method of your GUI Plugin.
In my case this is what I have :
class MyPlugin:
"""QGIS Plugin Implementation.
... __init__ method, tr method, initGui method, ...
"""
def run(self):
"""Run method that performs the work after clicking 'OK' in the Plugin GUI"""
task = MyTask('UserFriendlyTaskName', **props)
QgsApplication.taskManager().addTask( task )
class MyTask(QgsTask):
def __init__(self, task_name, **props):
QgsTask.__init__(self, desc)
# do w/e with the props
def run(self):
# call your heavy processes
See this .gif below for the UI rendering in QGIS3, there is no freezing and I can still move on the map and edit data while the plugin is running.
QGIS GUI Launch Background Process GIF
Hope this answer finds you in time !
If i understand correctly, you want to refresh the UI to avoid QGIS freezing.
This is what worked for me:
from PyQt5.QtWidgets import QApplication
QApplication.processEvents()
This will refresh the UI of QGIS.
You probably have to call it multiple time, for example after every iteration in a for loop.
thanks you for your answer
what I would to do is run a method in background without freezing QGIS GUI
a example of my code
class Example:
def __init__(self):
code
self.createBotton()
def createButton(self):
Here I create my button
self.okbutton.clicked.connect(self.pressOkButton)
def pressOkButton(self):
here I call other methods
self.methodA()
self.methodB()
def methodA(self):
code
def methodB(self):
code
my question is--> how can i run 'pressOkButton' method in background? When i executed this method i can not ineract with QGIS GUI until finish method
NOTE:consider that it is a class that belongs to my plugin so this class is instantiated when starting the plugin
A part of what I am doing, needs me to have background music in a tkinter game I created long time ago. I am using playsound.playsound{ Docs Here } to play music . I could use any other tool if needed to achieve what I intend like pyglet.media or pygame.mixer.
As the actual program was about 1k lines, I have tried adding an MCVE below.
Desired behavior & issue
The BGM (Background Music) should start when the app is launched - of course alongside a GUI, I would have a button to stop the BGM OR more preferably pause/play - I do think I need to use anything other than playsound.playsound for pause/play behavior.
The issue:: I can't seem to figure out how to make that communication between both the threads so that I can stop the music from playing or perhaps terminate the thread playing BGM - I could create a new thread when needed.
What I Have Tried
First up, I created two classes for GUI and BGM, each inheriting from threading.Thread - overridden the constructor and run() methods to do what I intend. Like this -
import tkinter as tk
import threading
from playsound import playsound
class BGM(threading.Thread):
def __init__(self, stopSignal):
threading.Thread.__init__(self)
self.stopSignal = stopSignal
def run(self):
while not self.stopSignal:
playsound('LoL.mp3') # to make this function play asynchronously, pass in False as second arg.
class GUI(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.root = tk.Tk()
def run(self):
tk.Button(self.root, text='stop').pack()
if __name__ == '__main__':
guiThread = GUI()
guiThread.start()
musicThread = BGM(0)
musicThread.start()
guiThread.root.mainloop()
But as you can see, it will just continue to play the song infinitely as long as the app is alive. There is no communication ( or active sync ) between GUI ( the button to stop song ) and BGM thread.
Now I tried using threading.Event() objects. The official docs on threading.Event() quotes:
This is one of the simplest mechanisms for communication between
threads: one thread signals an event and other threads wait for it.
An event object manages an internal flag that can be set to true with
the set() method and reset to false with the clear() method. The
wait() method blocks until the flag is true.
I'm not sure if this is useful for me to achieve what I intend but using threading.Event() did not do anything for me (or maybe I wasn't able to make use of it properly)
Now
What would you suggest to do to implement a BGM thread which can be stopped ( or paused/played preferably ) with a button(s) in the GUI thread.
Thank You For Any help Or Suggestions :)
I'm trying to understand how the cycle of my "main.py" works. It's based on examples found on the net, about the PySide and Qt Designer, to implement a Python GUI.
The code is:
#***********************************#
# Python Libraries #
#***********************************#
from PySide.QtCore import *
from PySide.QtGui import *
import sys
import time
#***********************************#
# Python files #
#***********************************#
import Gui
from server import *
class MainDialog(QDialog, Gui.Ui_TCPServer):
def __init__(self, parent=None):
super(MainDialog, self).__init__(parent)
self.setupUi(self)
self.connect(self.ConnectBt, SIGNAL("clicked()"), self.ConnectBt_clicked)
self.connect(self.QuitBt, SIGNAL("clicked()"), self.QuitBt_clicked)
self.connect(self.DisconnectBt, SIGNAL("clicked()"), self.DisconnectBt_clicked)
print "NOW HERE\r\n"
def ConnectBt_clicked(self):
self.ConnectBt.setText("Connecting...")
self.server_connect()
print "THEN HERE\r\n"
def QuitBt_clicked(self):
self.close()
def DisconnectBt_clicked(self):
self.ConnectBt.setText("Connect")
self.server_off = ChronoRequestHandler()
self.server_off.finish()
def server_connect(self):
self.server_on = ServerStart()
self.server_on.try_connect()
if __name__ == '__main__':
app = QApplication(sys.argv)
form = MainDialog()
print "HERE\r\n"
form.show()
app.exec_()
print "END\r\n"
When I call the "main.py" I get a print of "NOW HERE" and "THEN HERE". When I press the 'ConnectBt', I get the print of "THEN HERE".
But and after this, where the cycle remains? Does it returns to init, if so, shouldn't I get again the print of "NOW HERE"? Does it returns to main, if so, shouldn't I get the print of "HERE"? Please explain me...
When I press the 'QuitBt' I get the print of "END"... I'm confused!
Thanks.
I think you should get more clear on how object programming and events work.
In the last if-statement (the code on the bottom that runs when you call your script from e.g. terminal) you create an app object instance of QApplication.
After that you create form, instance of MainDialog which is the class you define above (inheriting methods, properties, etc from two classes, QDialog, Gui.Ui_TCPServer).
By doing
form = MainDialog()
you run __init__, print "NOW HERE" and go out of that method. Please check what __init__ does in Python. why-do-we-use-init-in-python-classes
Before the end you call the exec() method of the app instance. This contains a loop so that your interface gathers and processes events. See the documentation on QApplication.exec() below.
When you press the 'ConnectBt' button you call the ConnectBt_clicked() method, which does stuff (connects with the server) and prints "THEN HERE".
In the same way, when you press QuitBt you call QuitBt_clicked(), which closes the connection and lets the code print "END".
I also suggest you read more documentation about the classes you are using. They will explain how come that the different buttons are "linked"/have as callbacks the methods ConnectBt_clicked(), def QuitBt_clicked(), and DisconnectBt_clicked(). The mechanisms by which the buttons trigger these callbacks is kind of implicit in the code implemented in those classes.
QApplication Class Reference: exec_
int QApplication.exec_ ()
Enters the main event loop and waits until exit() is called, then
returns the value that was set to exit() (which is 0 if exit() is
called via quit()).
It is necessary to call this function to start event handling. The
main event loop receives events from the window system and dispatches
these to the application widgets.
Generally, no user interaction can take place before calling exec().
As a special case, modal widgets like QMessageBox can be used before
calling exec(), because modal widgets call exec() to start a local
event loop.
To make your application perform idle processing, i.e., executing a
special function whenever there are no pending events, use a QTimer
with 0 timeout. More advanced idle processing schemes can be achieved
using processEvents().
We recommend that you connect clean-up code to the aboutToQuit()
signal, instead of putting it in your application's main() function.
This is because, on some platforms the QApplication.exec() call may
not return. For example, on the Windows platform, when the user logs
off, the system terminates the process after Qt closes all top-level
windows. Hence, there is no guarantee that the application will have
time to exit its event loop and execute code at the end of the main()
function, after the QApplication.exec() call.
See also quitOnLastWindowClosed, quit(), exit(), processEvents(), and
QCoreApplication.exec().
I spent longer than I'd care to admit think of a suitable 'question' heading for this topic, as my issue is somewhat hard to articulate.
Here is a quick summary of the situation:
I'm writing a basic GUI with Python 3.4 and PySide
I'm using QFileSystemWatcher to monitor a particular file
When the file is changed, QFileSystemWatcher calls a method, which in turn calls a method within a PySide Class
All of the above seems to be working perfectly, except the GUI-specific actions detailed in the PySide Class method aren't being executed (I'll explain in more detail below).
Example code:
#Establishing the PySide GUI Class
class GUI(QMainWindow, Ui_GUI):
def __init__(self, parent=None)
super(GUI, self).__init__(parent)
self.setupUi(self)
QtCore.QObject.connect(self.Button, QtCore.SIGNAL("clicked()"), self.Run)
def Run(self):
print("1")
self.treeWidget1.clear()
self.treeWidget2.clear()
print("2")
self.label1.setText("Text 1")
self.label2.setText("Text 2")
print("3")
for y in range(0, 5):
self.treeWidget1.resizeColumnsToContents()
print("Finished")
#Establish the file monitoring mechanism, *outside* the PySide class
def FileChanged():
Script = GUI()
Script.Run()
Paths = ['path/to/file']
Watch = QtCore.QFileSystemWatcher(Paths)
Watch.fileChanged.connect(FileChanged)
#Setting up the GUI
if __name__ == '__main__':
app = QApplication(sys.argv)
showGUI = GUI()
showGUI.show()
app.exec_()
As I mentioned above, the above code doesn't return any errors. When I change the file (listed in the path), FileChanged does indeed call the Run() method from the GUI class. However, it won't actually do any of the 'stuff', it will only execute the print commands in between the 'stuff'.
If I then click on the 'Button' in the GUI, it will execute Run() correctly, and properly execute all the 'stuff'.
My question: is there something I'm missing here? If it's calling the method correctly, and is able to execute the various 'print' commands, why is it not executing the actual 'stuff'?
Thanks!
EDIT 1: I've removed the -do stuff- tags and put in some example code. All the 'stuff' code relates to updating various PySide QLabels, QTreeWidgets, etc.
EDIT 2: I forget the () at the end of the treeWidget clear commands.
The Script object created in the FileChanged function has local scope, and will be garbage-collected as soon as the function returns.
If the Run slot gets called when the signal fires, it will carry out all of the changes correctly, but you won't get to see any of those changes, because Script will be deleted before it is ever shown.
In order to for the example script to begin to make any sense, it would need to be re-arranged to something like this:
#Setting up the GUI
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
showGUI = GUI()
#Establish the file monitoring mechanism, *outside* the PySide class
def FileChanged():
showGUI.Run()
Paths = ['path/to/file']
Watch = QtCore.QFileSystemWatcher(Paths)
Watch.fileChanged.connect(FileChanged)
showGUI.show()
app.exec_()
Of course, it's possible that your real code is nothing like the example in your question (which has numerous other errors preventing it from being run), and so this might be no help. But if that is the case, you will have to post a fully working, self-contained example that properly demonstrates the problems you are having.
I would like to run pyglet in a separate thread so that I can implement a user interface for input without being blocked by pyglet.app.run().
import pyglet
class Window(pyglet.window.Window):
def __init__(self):
pyglet.window.Window.__init__(self, visible=True)
self.push_handlers(on_draw=self.on_draw)
self.im = pyglet.resource.image('image.jpg')
pyglet.app.run()
def on_draw(self):
self.clear()
import threading
class Thread(threading.Thread):
def run(self):
w = Window()
Running
Window()
works fine. However, running
t = Thread()
t.start()
results in Segmentation fault (core dumped), which is caused by the call to pyglet.resource.image(). Omitting that call eliminates the problem.
Specifically, what is causing this problem, and how can I correct it? More generally, what is the recommended way to render a window using pyglet while allowing for other program execution? Is there a better way to do this?