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?
Related
Despite I am using Thread class in my long-term function, my GUI is freezing in PyQt5 and I couldn't solve the problem.
I am using separate py files in this process.
welding.py
`
python
from threading import Threading
class Weld:
def __init__(self):
self.power_supply = PowerSupply()
self.ui_object = ui_object
# Some code
run_cycle_threader = Thread(target = self.power_supply_run_cycle,
args = (self.ui_object,..some args..,self.simulation_mode,))
run_cycle_threader.start()
#some code
`
And at the other module like following:
power_supply.py
class PowerSupply:
def run_cycle(ui, someargs...,simulation_mode):
#some code interacting with physical device
#some code which changes GUI elements like Labels, ProgressBars and button colors
At other Threading objects I don't face with a problem but this function freezing GUI (for example prevents hovering of buttons)
I have tried to use QTimer class but I got same result.
My complete code is here:
https://github.com/emreisler/weld-redesign
When I call
self.client = ThreadedClient()
in my Python program, I get the error
"RuntimeError: main thread is not in main loop"
I have already done some googling, but I am making an error somehow ... Can someone please help me out?
Full error:
Exception in thread Thread-1:
Traceback (most recent call last):
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/threading.py", line 530, in __bootstrap_inner
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/threading.py", line 483, in run
File "/Users/Wim/Bird Swarm/bird_swarm.py", line 156, in workerGuiThread
self.root.after(200, self.workerGuiThread)
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-tk/Tkinter.py", line 501, in after
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-tk/Tkinter.py", line 1098, in _register
RuntimeError: main thread is not in main loop
Classes:
class ThreadedClient(object):
def __init__(self):
self.queue = Queue.Queue( )
self.gui = GuiPart(self.queue, self.endApplication)
self.root = self.gui.getRoot()
self.running = True
self.GuiThread = threading.Thread(target=self.workerGuiThread)
self.GuiThread.start()
def workerGuiThread(self):
while self.running:
self.root.after(200, self.workerGuiThread)
self.gui.processIncoming( )
def endApplication(self):
self.running = False
def tc_TekenVogel(self,vogel):
self.queue.put(vogel)
class GuiPart(object):
def __init__(self, queue, endCommand):
self.queue = queue
self.root = Tkinter.Tk()
Tkinter.Canvas(self.root,width=g_groottescherm,height=g_groottescherm).pack()
Tkinter.Button(self.root, text="Move 1 tick", command=self.doSomething).pack()
self.vogelcords = {} #register of bird and their corresponding coordinates
def getRoot(self):
return self.root
def doSomething():
pass #button action
def processIncoming(self):
while self.queue.qsize( ):
try:
msg = self.queue.get(0)
try:
vogel = msg
l = vogel.geeflocatie()
if self.vogelcords.has_key(vogel):
cirkel = self.vogelcords[vogel]
self.gcanvas.coords(cirkel,l.geefx()-g_groottevogel,l.geefy()-g_groottevogel,l.geefx()+g_groottevogel,l.geefy()+g_groottevogel)
else:
cirkel = self.gcanvas.create_oval(l.geefx()-g_groottevogel,l.geefy()-g_groottevogel,l.geefx()+g_groottevogel,l.geefy()+g_groottevogel,fill='red',outline='black',width=1)
self.vogelcords[vogel] = cirkel
self.gcanvas.update()
except:
print('Failed, was van het type %' % type(msg))
except Queue.Empty:
pass
You're running your main GUI loop in a thread besides the main thread. You cannot do this.
The docs mention offhandedly in a few places that Tkinter is not quite thread safe, but as far as I know, never quite come out and say that you can only talk to Tk from the main thread. The reason is that the truth is somewhat complicated. Tkinter itself is thread-safe, but it's hard to use in a multithreaded way. The closest to official documentation on this seems to be this page:
Q. Is there an alternative to Tkinter that is thread safe?
Tkinter?
Just run all UI code in the main thread, and let the writers write to a Queue object…
(The sample code given isn't great, but it's enough to figure out what they're suggesting and do things properly.)
There actually is a thread-safe alternative to Tkinter, mtTkinter. And its docs actually explain the situation pretty well:
Although Tkinter is technically thread-safe (assuming Tk is built with --enable-threads), practically speaking there are still problems when used in multithreaded Python applications. The problems stem from the fact that the _tkinter module attempts to gain control of the main thread via a polling technique when processing calls from other threads.
I believe this is exactly what you're seeing: your Tkinter code in Thread-1 is trying to peek into the main thread to find the main loop, and it's not there.
So, here are some options:
Do what the Tkinter docs recommend and use TkInter from the main thread. Possibly by moving your current main thread code into a worker thread.
If you're using some other library that wants to take over the main thread (e.g., twisted), it may have a way to integrate with Tkinter, in which case you should use that.
Use mkTkinter to solve the problem.
Also, while I didn't find any exact duplicates of this question, there are a number of related questions on SO. See this question, this answer, and many more for more information.
I know this is late, but I set my thread to a Daemon, and no exception was raised:
t = threading.Thread(target=your_func)
t.setDaemon(True)
t.start()
I found a way to solve it.
it might look like a joke but you just should add
plt.switch_backend('agg')
Since all this did help my problem but did not solve it completely here is an additional thing to keep in mind:
In my case I started off importing the pyplot library in many threads and using it there. After moving all the library calls to my main thread I still got that error.
I did get rid of it by removing all import statements of that library in other files used in other threads. Even if they did not use the library the same error was caused by it.
from tkinter import *
from threading import Thread
from time import sleep
from random import randint
class GUI():
def __init__(self):
self.root = Tk()
self.root.geometry("200x200")
self.btn = Button(self.root,text="lauch")
self.btn.pack(expand=True)
self.btn.config(command=self.action)
def run(self):
self.root.mainloop()
def add(self,string,buffer):
while self.txt:
msg = str(randint(1,100))+string+"\n"
self.txt.insert(END,msg)
sleep(0.5)
def reset_lbl(self):
self.txt = None
self.second.destroy()
def action(self):
self.second = Toplevel()
self.second.geometry("100x100")
self.txt = Text(self.second)
self.txt.pack(expand=True,fill="both")
self.t = Thread(target=self.add,args=("new",None))
self.t.setDaemon(True)
self.t.start()
self.second.protocol("WM_DELETE_WINDOW",self.reset_lbl)
a = GUI()
a.run()
maybe this example would help someone.
Write it at the end:
root.mainloop()
Of course, in place of root should be the name of your Tk object if it is not root.
You cannot modify your main GIU from another thread
you need to send event to the main GUI in order to avoid exceptions
Use window.write_event_value instead, this method allows you to send events from your threads
you can take a look a this too: window.perform_long_operation
I know this question was asked a long time ago, but I wanted to tell you how I solved it. In my case, I have a program that sends and receives messages through the serial port and uses the TKinter library.
If I do:
while (True):
#more code here
window.update_idletasks()
window.update()
The code crashes when a thread tries to access a tkinter function.
But, if I do this:
window.mainloop()
All the threads execute normaly.
Hope this helps someone.
I have created a relatively complex PyQt program and am trying to implement threads so that when the program encounters a part of the program which is particularly CPU intensive, the GUI will remain refreshed and responsive throughout. Sadly though, I am having some difficulties with the threading.
I am using Python 2.7 for reasons that I don't believe to be relevant.
Anyway, the entire program runs within one class and calls upon a PyQt designer .ui file in order to display the actual GUI. When a particular button is pressed, in order to shred a file, it calls a function within that class that then starts a thread using the 'thread' module, yes, outdated, I know. The shredding function that is then called from this commences the shredding of the file. Throughout the shredding of the file, the actual shredding function interacts and adds bits to the GUI in order to keep the user up to date on what is happening.
During the execution of the function the GUI continues to be refreshed, however it does become a little laggy, I can cope with that. However, when that function is complete, instead of smoothly continuing and allowing the user to keep using the program, the program throws a complete hissy fit and simply just stops working and has to be closed.
Hopefully someone can assist me here. I would greatly appreciate as much detail as possible as I have been searching around for a way to cope with this for a good number of weeks now.
I am using PyQt4.
Here's a simple demo of threading in pyqt5. Qt has it's own threading class that works pretty well.
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import pyqtSignal
import sys
import time
class TheBoss(QtWidgets.QWidget):
def __init__(self, parent=None):
super(TheBoss, self).__init__(parent)
self.resize(300,200)
self.VL = QtWidgets.QVBoxLayout(self)
self.label = QtWidgets.QLabel()
self.VL.addWidget(self.label)
self.logger = Logger()
self.logger.sec_signal.connect(self.label.setText)
self.logger.start()
def closeEvent(self,event):
self.logger.terminate()
class Logger(QtCore.QThread):
sec_signal = pyqtSignal(str)
def __init__(self, parent=None):
super(Logger, self).__init__(parent)
self.current_time = 0
self.go = True
def run(self):
#this is a special fxn that's called with the start() fxn
while self.go:
time.sleep(1)
self.sec_signal.emit(str(self.current_time))
self.current_time += 1
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
app.setApplicationName("Thread Example")
window = TheBoss()
window.show()
sys.exit(app.exec_())
my code has thread, but when i close the gui, it still works on background. how can i stop threads? is there something stop(), close()?
i dont use signal, slots? Must i use this?
from PyQt4 import QtGui, QtCore
import sys
import time
import threading
class Main(QtGui.QMainWindow):
def __init__(self, parent=None):
super(Main, self).__init__(parent)
self.kac_ders=QtGui.QComboBox()
self.bilgi_cek=QtGui.QPushButton("Save")
self.text=QtGui.QLineEdit()
self.widgetlayout=QtGui.QFormLayout()
self.widgetlar=QtGui.QWidget()
self.widgetlar.setLayout(self.widgetlayout)
self.bilgiler=QtGui.QTextBrowser()
self.bilgi_cek.clicked.connect(self.on_testLoop)
self.scrollArea = QtGui.QScrollArea()
self.scrollArea.setWidgetResizable(True)
self.scrollArea.setWidget(self.widgetlar)
self.analayout=QtGui.QVBoxLayout()
self.analayout.addWidget(self.text)
self.analayout.addWidget(self.bilgi_cek)
self.analayout.addWidget(self.bilgiler)
self.centralWidget=QtGui.QWidget()
self.centralWidget.setLayout(self.analayout)
self.setCentralWidget(self.centralWidget)
def on_testLoop(self):
self.c_thread=threading.Thread(target=self.kontenjan_ara)
self.c_thread.start()
def kontenjan_ara(self):
while(1):
self.bilgiler.append(self.text.text())
time.sleep(10)
app = QtGui.QApplication(sys.argv)
myWidget = Main()
myWidget.show()
app.exec_()
A few things:
You shouldn't be calling GUI code from outside the main thread. GUI elements are not thread-safe. self.kontenjan_ara updates and reads from GUI elements, it shouldn't be the target of your thread.
In almost all cases, you should use QThreads instead of python threads. They integrate nicely with the event and signal system in Qt.
If you just want to run something every few seconds, you can use a QTimer
def __init__(self, parent=None):
...
self.timer = QTimer(self)
self.timer.timeout.connect(self.kontenjan_ara)
self.timer.start(10000)
def kontenjan_ara(self):
self.bilgiler.append(self.text.text())
If your thread operations are more computationally complex you can create a worker thread and pass data between the worker thread and the main GUI thread using signals.
class Worker(QObject):
work_finished = QtCore.pyqtSignal(object)
#QtCore.pyqtSlot()
def do_work(self):
data = 'Text'
while True:
# Do something with data and pass back to main thread
data = data + 'text'
self.work_finished.emit(data)
time.sleep(10)
class MyWidget(QtGui.QWidget):
def __init__(self, ...)
...
self.worker = Worker()
self.thread = QtCore.QThread(self)
self.worker.work_finished.connect(self.on_finished)
self.worker.moveToThread(self.thread)
self.thread.started.connect(self.worker.do_work)
self.thread.start()
#QtCore.pyqtSlot(object)
def on_finished(self, data):
self.bilgiler.append(data)
...
Qt will automatically kill all the subthreads when the main thread exits the event loop.
I chose to rewrite a bit this answer, because I had failed to properly look at the problem's context. As the other answers and comments tell, you code lacks thread-safety.
The best way to fix this is to try to really think "in threads", to restrict yourself to only use objects living in the same thread, or functions that are known as "threadsafe".
Throwing in some signals and slots will help, but maybe you want to think back a bit to your original problem. In your current code, each time a button is pressed, a new thread in launched, that will, every 10 seconds, do 2 things :
- Read some text from self.text
- Append it to self.bilgiler
Both of these operations are non-threadsafe, and must be called from the thread that owns these objects (the main thread). You want to make the worker threads "schedule & wait" the read & append oeprations, instead of simply "executing" them.
I recommend using the other answer (the thread halting problem is automatically fixed by using proper QThreads that integrate well with Qt's event loop), which would make you use a cleaner approach, more integrated with Qt.
You may also want to rethink your problem, because maybe there is a simpler approach to your problem, for example : not spawning threads each time bilgi_cek is clicked, or using Queue objects so that your worker is completely agnostic of your GUI, and only interact with it using threadsafe objects.
Good luck, sorry if I caused any confusion. My original answer is still available here. I think it would be wise to mark the other answer as the valid answer for this question.
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.