I'm trying to put a python3 program that controls my pool with 5 queues running in Main into a Tkinter GUI but tkinter blocks the queues. How do I get the functions to work in background with the interface always on top???
I'm NOT an expert!!
This is what I#m trying to put into a GUI
Screen shot of the GUI
My main problem is to run the temperature read out , that runs continuosly, display the temperatures in a live window and at the same time not to block everything else. I've been able to run the temperatures (code snipet below) and I can run thr GUI (see screen shot) but both together ??????
your help would be much apreciated!
Update: I'Ve now managed to get a live window displaying temperatures and time but that is still blocken the other funtions , e.g. filter on\off times motion detection etc.
`if __name__ == '__main__':
setup()
queue = Queue()
filtime = Thread(target=filtime, args=(queue,))
filtime.start()
get_motion = Thread(target=get_motion, args=(queue,))
get_motion.start()
gettemps = Thread(target=gettemps, args=(queue,))
gettemps.start()
Frostcheck = Thread(target=Frostcheck, args=(queue,))
Frostcheck.start()
try:
filtime.join()
get_motion.join()
gettemps.join()
Frostcheck.join()
except KeyboardInterrupt:
print("Program Halted")
Log('Program Halted')
GPIO.cleanup()
exit() `
Related
As a bit of a background, I picked up a project from another developer who wrote the entire as one big file that runs from top to bottom.
The manager wants to display images / videos on the screen at certain points during the scripts execution and that disappear during other points of execution.
I have worked with Tk extensively, but I am still limited by the lack of thread safeness and its blocking mainloop() function.
Is there a way in python to display images / videos asynchronously/ in a non blocking way, while the main thread still executes? In the context of this project, the API would have to be something similar to what is below.
if __name__ == "__main__":
gui = load_gui()
gui.display_image("doing_math.png") #doesn't block
for i in range(0, 500):
print(str(i))
gui.display_image("math_complete.mp4")#doesn't block
sleep(2)
gui.clear_display()
currently I am using trying to use Tk like displayed below. but it is limited by the mainloop() blocking the rest of the scripts execution.
class MediaGui(tk.Tk):
def __init__(self):
self.mainloop()
...
gui = MediaGui() #this line blocks because of mainloop
gui.load_image("image.png")
Try this :
from concurrent.futures import ThreadPoolExecutor
gui = load_gui()
def run_io_tasks_in_parallel(tasks):
with ThreadPoolExecutor() as executor:
running_tasks = [executor.submit(task) for task in tasks]
for running_task in running_tasks:
running_task.result()
run_io_tasks_in_parallel([
lambda: gui.display_image("doing_math.png"),
lambda: gui.display_image("math_complete.mp4"),
])
I have code that is running through PySimpleGUI that executes as expected but the problem is the GUI appears as not responding when running through the code. I want it to appear as its running without showing 'Not Responding' in the header of the window.
I believe this is because of having an Infinite Loop of While True: but this is for the purpose of PySimpleGUI to run.
Here is a snippet of my loop:
while True:
event, values = window.Read()
#print(event, values)
some_functions()
window.close()
I expect to have the window run normal when a button is pressed without going into not responding after few seconds, it is working as in running the code as expected but I want to hide the appearance of not responding somehow.
[ EDIT Nov 2021 ]
There's a rather large problem with using StackOverflow to get answers for projects that are ACTIVE and thus constantly evolving, hopefully in ways that improve the package.
Stack Overflow answers, at best, offer solutions in a snapshot in time.
There's the awful problem that even at the time written, some of the answers are not written by experts in that package.
When an expert answers the question if the package evolves, the answer can become both not applicable or worst sends the user down a path that is doomed to fail, or wastes time solving a problem that's been solved in a more elegant way.
In this particular situation, PySimpleGUI has evolved to include a simple wrapper for the Thread standard library calls. Why? Because someone entirely new to programming that hits this long-operation problem won't have the ability to grasp threading, but yet will still encounter this very real problem, perhaps on their very first GUI.
In Aug 2021 a new method was added to the Window object.... Window.perform_long_operation which removes the burden entirely from a new user.
perform_long_operation is in the PySimpleGUI Cookbook, in the eCookbook, in demo programs and can remove this significant roadblock for a newcomer and enable them to get on with solving their original problem rather than getting mired in a topic they are not prepared to tackle in their first 2 weeks of programming, ever.
I don't have the ability to scour StackOverflow for my previous answers and update them. I urge PySimpleGUI users to follow the support steps outlined in the documentation. It's one reason I rarely come here and answer questions. Support is provided elsewhere.
On to the now outdated answer....
The problem isn't the loop. The problem is your functions that you're calling after you call Read. If those functions didn't take so long, you wouldn't see the Not Responding message. Take out that call and your problem goes away, right? But, of course you can't take out that call because that's the stuff you need to do in your GUI.
There's a Demo Program written for this very purpose.
Any time you're taking so much "time" that the operating system is complaining it means you're taking too much.
It's a classic GUI problem that exists in all GUI SDKs. In the PySimpleGUI Event Loop or in callbacks from other frameworks, you'll get these kinds of OS warnings that your program has stopped working.
Because PySimpleGUI is tkinter based, you can't simply spin out the GUI as another thread. tkinter doesn't like to be anything but the main thread. So, it's your portion of the work that needs to be run as a separate thread so that the GUI continues to be given CPU time and the OS will thus stop complaining.
Last time I posted only a link on SO I was scolded, so I'm going to paste in the code here for the demo program. You can use this as a design pattern for your code. As mentioned in the comments you can run it online to see how it works by running the code on repl.it.
import queue
import threading
import time
# This program has been tested on all flavors of PySimpleGUI and it works with no problems at all
# To try something other than tkinter version, just comment out the first import and uncomment the one you want
import PySimpleGUI as sg
# import PySimpleGUIQt as sg
# import PySimpleGUIWx as sg
# import PySimpleGUIWeb as sg
"""
DESIGN PATTERN - Multithreaded Long Tasks GUI
Presents one method for running long-running operations in a PySimpleGUI environment.
The PySimpleGUI code, and thus the underlying GUI framework, runs as the primary, main thread
The "long work" is contained in the thread that is being started.
A queue.Queue is used by the threads to communicate with main GUI code
The PySimpleGUI code is structured just like a typical PySimpleGUI program. A layout defined,
a Window is created, and an event loop is executed.
What's different is that within this otherwise normal PySimpleGUI Event Loop, there is a check for items
in the Queue. If there are items found, process them by making GUI changes, and continue.
This design pattern works for all of the flavors of PySimpleGUI including the Web and also repl.it
You'll find a repl.it version here: https://repl.it/#PySimpleGUI/Async-With-Queue-Communicationspy
"""
def long_operation_thread(seconds, gui_queue):
"""
A worker thread that communicates with the GUI through a queue
This thread can block for as long as it wants and the GUI will not be affected
:param seconds: (int) How long to sleep, the ultimate blocking call
:param gui_queue: (queue.Queue) Queue to communicate back to GUI that task is completed
:return:
"""
print('Starting thread - will sleep for {} seconds'.format(seconds))
time.sleep(seconds) # sleep for a while
gui_queue.put('** Done **') # put a message into queue for GUI
###### ## ## ####
## ## ## ## ##
## ## ## ##
## #### ## ## ##
## ## ## ## ##
## ## ## ## ##
###### ####### ####
def the_gui():
"""
Starts and executes the GUI
Reads data from a Queue and displays the data to the window
Returns when the user exits / closes the window
"""
gui_queue = queue.Queue() # queue used to communicate between the gui and the threads
layout = [[sg.Text('Long task to perform example')],
[sg.Output(size=(70, 12))],
[sg.Text('Number of seconds your task will take'),sg.Input(key='_SECONDS_', size=(5,1)), sg.Button('Do Long Task', bind_return_key=True)],
[sg.Button('Click Me'), sg.Button('Exit')], ]
window = sg.Window('Multithreaded Window').Layout(layout)
# --------------------- EVENT LOOP ---------------------
while True:
event, values = window.Read(timeout=100) # wait for up to 100 ms for a GUI event
if event is None or event == 'Exit':
break
elif event.startswith('Do'):
try:
seconds = int(values['_SECONDS_'])
print('Starting thread to do long work....sending value of {} seconds'.format(seconds))
threading.Thread(target=long_operation_thread, args=(seconds , gui_queue,), daemon=True).start()
except Exception as e:
print('Error starting work thread. Did you input a valid # of seconds? You entered: %s' % values['_SECONDS_'])
elif event == 'Click Me':
print('Your GUI is alive and well')
# --------------- Check for incoming messages from threads ---------------
try:
message = gui_queue.get_nowait()
except queue.Empty: # get_nowait() will get exception when Queue is empty
message = None # break from the loop if no more messages are queued up
# if message received from queue, display the message in the Window
if message:
print('Got a message back from the thread: ', message)
# if user exits the window, then close the window and exit the GUI func
window.Close()
## ## ### #### ## ##
### ### ## ## ## ### ##
#### #### ## ## ## #### ##
## ### ## ## ## ## ## ## ##
## ## ######### ## ## ####
## ## ## ## ## ## ###
## ## ## ## #### ## ##
if __name__ == '__main__':
the_gui()
print('Exiting Program')
I'm using PyQt for Python, and am building a gui. I had a problem a few weeks ago where I had a function outside of the gui module modifying widgets within the gui (advanced progress bar, updating strings, etc.), and those modifications were not reflected in the gui until the function that had made the changes finished running.
The solution to this was to simply call app.processEvents() after doing whatever modifications I wanted, which would immediately update the graphics of the window.
But now I am wondering, is there a way to do this everytime the window is brought forward?
Let's say I have called a function that will be modifying the progress bar, but this function takes quite a while to run. Inbetween calling the function, and the progress bar modification, the app processes no events. So, it during this time, I pull up a Chrome window (or anything else), and then close it, my gui window is blank, just gray, until app.processEvents() is called again.
Is ther functionality in PyQt that allows me to detect whenever the window is brought to the front of all current windows?
You should look into QThread.
Threads allow you to run long, complicated tasks in a worker thread while a background thread keeps the GUI responsive, such as updating a QProgressBar, ensuring it responds to motion events.
The basic idea is this:
# load modules
import time
from PySide import QtCore, QtGui
# APPLICATION STUFF
# -----------------
APP = QtGui.QApplication([])
# THREADS
# -------
class WorkerThread(QtCore.QThread):
'''Does the work'''
def __init__(self):
super(WorkerThread, self).__init__()
self.running = True
def run(self):
'''This starts the thread on the start() call'''
# this goes over 1000 numbers, at 10 a second, will take
# 100 seconds to complete, over a minute
for i in range(1000):
print(i)
time.sleep(0.1)
self.running = False
class BackgroundThread(QtCore.QThread):
'''Keeps the main loop responsive'''
def __init__(self, worker):
super(BackgroundThread, self).__init__()
self.worker = worker
def run(self):
'''This starts the thread on the start() call'''
while self.worker.running:
APP.processEvents()
print("Updating the main loop")
time.sleep(0.1)
# MAIN
# ----
def main():
# make threads
worker = WorkerThread()
background = BackgroundThread(worker)
# start the threads
worker.start()
background.start()
# wait until done
worker.wait()
if __name__ == '__main__':
main()
The output you get is something like this, showing how it takes turns at doing the long calculation and updating the main loop:
0
Updating the main loop
1
Updating the main loop
2
Updating the main loop
3
Updating the main loop
4
Updating the main loop
5
Updating the main loop
6
Updating the main loop
Updating the main loop7
8
Updating the main loop
9
This along with a QFocusEvent override should allow you to do whatever you wish. But it's better to separate updating the GUI and running your desired long thread.
As for overriding the QFocusEvent you can do something as follows:
def focusInEvent(self, event):
event.accept()
# insert your code here
And if you choose to implement threads to avoid GUI blocking, you should read about the basics of threading (as threads have a lot of nuances unless you know about their potential pitfalls).
I'm currently writing a GUI for rvplayer that shall enable artists to automatically render dailies with slate and burn-in information. The GUI is written with PySide and scripted in Python 2.7. My problem is that upon calling my process and updating my QProgressBar with the stdout the GUI freezes. I know that this is a common problem and that it can probably be solved with processEvents() somehow, but I know far too little about threading and process loops to get my head around this issue. Since my code is a little lengthy already, here's the part that causes the issue:
def rv(self, args):
p = subprocess.Popen(["C:/Program Files/Tweak/RV-4.0.10-64/bin/rvio_hw.exe"]+[x for x in args], stdout=subprocess.PIPE)
while True:
line = p.stdout.readline()
if line != "":
progressStr=re.search(r"([0-9]+.[0-9]+%)", line.rstrip())
if progressStr == None:
print line.rstrip()
else:
progressInt=int(float(re.sub("[^0123456789\.]", "", progressStr.group())))
self.prog_QProgressBar.setValue(progressInt)
print progressStr.group()
else:
break
and here is the part that starts my QApplication:
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
finalForm = MainWindow()
finalForm.show()
sys.exit(app.exec_())
I'm calling the function rv upon pressing a button and although the progress bar keeps updating normally, the window starts to get nonresponsive after some time. I do not understand at which point I could use app.processEvents() to tell my QApplication to run the process in a separate thread or in the background.
Since it looks like you're not using threads, it may be that all that's required is to call processEvents after updating the progress bar, like this:
self.prog_QProgressBar.setValue(progressInt)
QtGui.qApp.processEvents()
However, the effectiveness of this may depend on how long it takes the process to produce the output. All that the processEvents call does is to immediately handle any pending events (e.g. widget painting, mouse clicks, etc) that are currently in the application's event queue. In between those calls, the GUI will continue to freeze (i.e. the executing code is not run in a separate thread or in the background, as you suggested). So the extent to which this technique can keep the GUI responsive depends on how frequently processEvents can be called within the rv() method.
The issue is that it's not as if your app is frozen, but Windows thinks that the app is frozen as it's ignoring events (mouse over, click etc etc), so Windows, in its wisdom, gives you that dialogue.
You need to start the thread off after the show() and then run the processEvents function, and obviously only call sys.exit once your thread has finished.
okay so now i am almost finished with my little project with some bits left, that's running my background task and then showing my GUI.
class myGUIApp:
def __init()__:
....
def createwidgets():
....
if __name__ == "__main__":
import myBackgroundTasks
x = myBackgroundTasks()
x.startbackground1() <----- this is background task that doesn't need user interaction
x.startbackground2() <----- this is background task that doesn't need user interaction
MainWindow = myGUIApp()
MainWindow.show() <---- this is Pyside GUI
The issue is this, the GUI doesn't "show" until my 2 background tasks are finished, which can take quite some time as they are doing I/O jobs and grabber files from the internet. How should i go about this? Using python's multithread (inside the background task, i am also using multithreading)? Qthread? or multiprocessing module? or others? thanks for answering.
You could put it on a thread. Since the Qt gui runs in its own thread this is efficient use. Use a queue to pass back the results of x. The only trick is where and when do you need x? If you need it inside your gui then the best thing to do is use the gui's after method, if it has one, or whatever it's equivalent is. The point is you don't hog up all the resources continuously checking the queue for the output. If you put a while loop inside your gui, it will probably cause the gui to freeze.
from threading import Thread
from Queue import Queue
class myGUIApp:
def __init()__:
....
def createwidgets():
....
if __name__ == "__main__":
import myBackgroundTasks
QUEUE = Queue()
def queue_fun(q):
x = myBackgroundTasks()
x.startbackground1() <----- this is background task that doesn't need user interaction
x.startbackground2() <----- this is background task that doesn't need user interaction
q.put(x)
THREAD = Thread(target=queue_fun, args=QUEUE)
THREAD.start()
MainWindow = myGUIApp()
MainWindow.show() <---- this is Pyside GUI
# if you can wait until after mainloop terminates to get x, put this here
while THREAD.is_alive()
try:
x = QUEUE.get_nowait()
except Queue.Empty:
continue
# if you need this inside Pyside, then you should put this inside Pyside,
# but don't use while loop, use Qt after function and call self.wait
def wait(self):
try:
x = QUEUE.get_nowait()
except Queue.Empty:
self.after(5, self.wait)