I'm working on a project using National Instruments boards to do data acquistion. I have functional C codes for doing the tasks, but would like to use Python, so the GUI programming is less painful. In my C-code, I use the API call setTimer, which raises a WM_TIMER event at regular intervals. Is there a similar, mechanism in a Tk loop? I tried using the following code.
def DAQ(self):
if self.do_DAQ:
result = self.myDAQ.getData()
currTime = time.time() - self.start_time
self.time_label.config(text="{:.1f} seconds".format(currTime))
self.volt_label.config(text="{:.4f} volts".format(result))
self.time_data[self.i] = currTime
self.volt_data[self.i] = result
self.i += 1
self.after(1962, self.DAQ)
The magic "1962" in the after() was determined by trial and error to give about a 2 second delay, but the time slices drift depending on what else is in the queue. Is there a way I can do this so my time slices are more accurate? Specifically, can I force Tk to do my DAQ event before other things in the queue?
Here's a sort of quickie example of what I'm talking about in my comment:
import Tkinter as tk
import threading
import random
import time
from Queue import Queue, Empty
root = tk.Tk()
time_label = tk.Label(root, text='<unknown> seconds')
volt_label = tk.Label(root, text='<unknown> volts')
time_label.pack()
volt_label.pack()
def DAQ(q):
while True:
q.put((time.time(), random.randrange(100)))
time.sleep(2)
def update_data(queue, root):
try:
timestamp, volts = queue.get_nowait()
except Empty:
pass
else:
time_label.config(text='{:.1f} seconds'.format(timestamp))
volt_label.config(text='{:.4f} volts'.format(volts))
root.after(100, update_data, queue, root)
data_queue = Queue()
t = threading.Thread(target=DAQ, args=(data_queue,))
t.daemon = True
t.start()
update_data(data_queue, root)
root.mainloop()
Obviously the above DAQ() function is just a stand-in for the real thing. The point is, as #ballsdotballs suggested in their answer, you can sample at whatever rate you want in your DAQ thread, add the values to a queue, and then update the GUI at a more appropriate rate.
I actually do NIDAQmx with Python using PyDAQmx. We take data at 20kHz (by setting the clock timer on the NI board, and streaming the data to a file in chunks of 2000 at 10hz).
I would highly recommend separating your GUI process from your data acquisition process if temporal precision is important.
If you are just wanting to log the data every 2 seconds, you could set your sample clock on your NIDAQ to something like 1000, buffer size 1000, and use an AutoRegisterEveryNSamplesEvent callback to write the last index of data for every other buffer (which should be every two seconds) to a file or pipe it to your GUI process. This will ensure that your processing queue for your GUI won't affect the precision that your data is sampled with.
Related
I'm trying to run some code inside of a GUI, where I run a function after I get a few text inputs. however the function I am trying to run is actually really complicated, so when it runs, it makes the entire gui freeze up for 10-15 seconds before continuing.
How can I make it so that when I hit the run button, it doesn't freeze up the entire GUI waiting for the function to complete?
I do understand that there is a way to make functions threaded, however, I don't know how to implement that?
An example of how I can wrap a function to make it a threaded one would be great.
The code below gives an example of the problem that I am dealing with.
import PySimpleGUI as sg
import time
def simple_gui():
layout = [ [sg.T('try clicking "do something" and move the window')],
[sg.Button('do something'), sg.Button('Exit')] ]
w = sg.Window('test', layout)
while True:
events, values = w.read()
if events == 'do something':
# If you hit the button "do something":
# run a function that takes 30 seconds to complete.
time.sleep(30)
if events == sg.WIN_CLOSED or events == 'Exit':
break
w.close()
simple_gui()
There are a number of examples of how you can use threads to perform "long operations" using PySimpleGUI. You'll find the demo programs located at http://Demos.PySimpleGUI.org . There are at least 6 sample programs labeled as being multi-threaded examples.
Some are also available on Trinket to run online. This one shows how to run a thread and then wait for it to complete:
https://pysimplegui.trinket.io/demo-programs#/multi-threaded/multi-threaded-long-task-simple
Here's the code that you'll find there.
#!/usr/bin/python3
import threading
import time
import PySimpleGUI as sg
"""
DESIGN PATTERN - Multithreaded Long Tasks GUI using shared global variables
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. Communicating is done (carefully) using global variables
There are 2 ways "progress" is being reported to the user.
You can simulate the 2 different scenarios that happen with worker threads.
1. If a the amount of time is known ahead of time or the work can be broken down into countable units, then a progress bar is used.
2. If a task is one long chunk of time that cannot be broken down into smaller units, then an animated GIF is shown that spins as
long as the task is running.
"""
total = 100 # number of units that are used with the progress bar
message = '' # used by thread to send back a message to the main thread
progress = 0 # current progress up to a maximum of "total"
def long_operation_thread(seconds):
"""
A worker thread that communicates with the GUI through a global message variable
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
"""
global message, progress
print('Thread started - will sleep for {} seconds'.format(seconds))
for i in range(int(seconds * 10)):
time.sleep(.1) # sleep for a while
progress += total / (seconds * 10)
message = f'*** The thread says.... "I am finished" ***'
def the_gui():
"""
Starts and executes the GUI
Reads data from a global variable and displays
Returns when the user exits / closes the window
"""
global message, progress
sg.theme('Light Brown 3')
layout = [[sg.Text('Long task to perform example')],
[sg.Output(size=(80, 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.CBox('ONE chunk, cannot break apart', key='-ONE CHUNK-')],
[sg.Text('Work progress'), sg.ProgressBar(total, size=(20, 20), orientation='h', key='-PROG-')],
[sg.Button('Click Me'), sg.Button('Exit')], ]
window = sg.Window('Multithreaded Demonstration Window', layout)
thread = None
# --------------------- EVENT LOOP ---------------------
while True:
event, values = window.read(timeout=100)
if event in (None, 'Exit'):
break
elif event.startswith('Do') and not thread:
print('Thread Starting! Long work....sending value of {} seconds'.format(float(values['-SECONDS-'])))
thread = threading.Thread(target=long_operation_thread, args=(float(values['-SECONDS-']),), daemon=True)
thread.start()
elif event == 'Click Me':
print('Your GUI is alive and well')
if thread: # If thread is running
if values['-ONE CHUNK-']: # If one big operation, show an animated GIF
sg.popup_animated(sg.DEFAULT_BASE64_LOADING_GIF, background_color='white', transparent_color='white', time_between_frames=100)
else: # Not one big operation, so update a progress bar instead
window['-PROG-'].update_bar(progress, total)
thread.join(timeout=0)
if not thread.is_alive(): # the thread finished
print(f'message = {message}')
sg.popup_animated(None) # stop animination in case one is running
thread, message, progress = None, '', 0 # reset variables for next run
window['-PROG-'].update_bar(0,0) # clear the progress bar
window.close()
if __name__ == '__main__':
the_gui()
print('Exiting Program')
So I'm trying to create a timer or sorts, the start button starts the timer, i need to stop the timer with the stop button and then record the time... I can't seem to figure out how to stop the timer function once its started. Ive tried if statements, disconnect(), and many others...
I realize that there is a timer in qt already but I'm trying to figure it out this way. thanks.
import sys
import time
from PyQt4 import QtCore, QtGui, uic
form_class = uic.loadUiType("/Users/Home/Desktop/Timer/timer.ui")[0]
class MyWindowClass(QtGui.QMainWindow, form_class):
def __init__(self, parent=None):
QtGui.QMainWindow.__init__(self, parent)
self.setupUi(self)
self.startButton.clicked.connect(self.timer)
def timer(self):
timeL = 0
self.stopButton.clicked.connect(False)
while True:
self.timeView.display(timeL)
timeL = timeL + 1
time.sleep(1)
app.processEvents()
app = QtGui.QApplication(sys.argv)
myWindow = MyWindowClass(None)
myWindow.show()
app.exec_()
TL;DR: You're after QElapsedTimer, not QTimer.
The operating system is already measuring the passage of time for you. You won't do any better job at it yourself.
"I'm trying to figure it out this way" - it will be less accurate than using QElapsedTimer because you're assuming that exactly as much time has passed as you wished to sleep for. This is almost never true: the actual amount of time that passed is different than the argument to sleep. Worse yet, the errors are usually of a systematic kind too, so your time accumulation will have a bias and will get less accurate as time passes. So, don't do it. It makes no sense. Perhaps you're not telling us exactly what you're trying to do: if you're asking about a particular solution that doesn't work, it helps to say what problem the solution is supposedly to. Why are you trying to figure it out this (wrong) way?
There are conceptually three different kinds of timers in Qt:
QElapsedTimer is like a stopwatch: it is an interface to the operating system's way of measuring the passage of time. That's the class you should be using to measure how much time has passed between the button clicks.
QTime is like a wall clock: you can ask it what time it is through currentTime(), and take difference between two readings of time to obtain elapsed time. Use this class only if you need to know the absolute time, otherwise QElapsedTimer will offer better resolution for elapsed time measurements.
QTimer is a source of timeouts: it is a way to periodically call your code. This is not meant to be used in measuring time, but merely to let your code run periodically, e.g. when you wish to refresh screen display, or implement a metronome that beeps periodically. There are no guarantees that your code will be called on time, and no guarantees that some ticks won't be missed. You want it guaranteed, you need to write a kernel driver, no way around that.
Below is a complete example using PyQt4, for Python 3.5. It uses QElapsedTimer to measure the passage of time between button presses, and QTimer to keep the time display refreshed.
#!/usr/bin/env python
#https://github.com/KubaO/stackoverflown/tree/master/questions/interval-timer-38036583
# -*- coding: utf-8 -*-
import sys
from PyQt4 import QtCore, QtGui
if __name__ == "__main__":
running = False
app = QtGui.QApplication(sys.argv)
w = QtGui.QWidget()
layout = QtGui.QVBoxLayout(w)
label = QtGui.QLabel()
button = QtGui.QPushButton('Start')
timer = QtCore.QElapsedTimer()
updateTimer = QtCore.QTimer()
layout.addWidget(label)
layout.addWidget(button)
def onTimeout():
label.setText('Elapsed: {0}ms'.format(timer.elapsed()))
def onClicked():
global running
if running:
onTimeout()
updateTimer.stop()
button.setText('Start')
else:
timer.start()
updateTimer.start()
button.setText('Stop')
running = not running
updateTimer.setInterval(1000/25) # ~25fps update rate
updateTimer.timeout.connect(onTimeout)
button.clicked.connect(onClicked)
w.show()
sys.exit(app.exec_())
Use a flag to control the loop. Then reset the flag in the slot connected to the stop button:
self.startButton.clicked.connect(self.timer)
self.stopButton.clicked.connect(self.stop)
def stop(self):
self._timer_flag = False
def timer(self):
timeL = 0
self._timer_flag = True
while self._timer_flag:
self.timeView.display(timeL)
timeL = timeL + 1
time.sleep(1)
app.processEvents()
A QTimer is better, because there is no lag in updating the ui. But you can improve your example by using an inner loop to call processEvents more frequently:
def timer(self):
timeL = 0
self._timer_flag = True
while self._timer_flag:
self.timeView.display(timeL)
timeL = timeL + 1
for _ in range(10):
# process events for up to 50 milliseconds
app.processEvents(QtCore.QEventLoop.AllEvents, 50)
time.sleep(0.1)
Sounds like you want to use QTimer on a QThread.
This answer should give you everything you need.
https://stackoverflow.com/a/18960953/5757280
I'm trying to learn how to use the thread module. I followed along with the instructions here: http://effbot.org/zone/tkinter-threads.htm
My hope is the test script will:
Print out the "count" every two seconds
Show a pop-up dialog window (also every 2 seconds)
The pop-ups should be allowed to accumulate (if I don't click "OK" for a while, there should be
multiple pop-ups)
However, when I run this script it will freeze the main window and after a while crash. I think I'm not implementing the thread module correctly.
Could someone please have a look and point out what I'm doing wrong?
Here is what I've tried so far:
from Tkinter import *
import thread
import Queue
import time
class TestApp:
def __init__(self, parent):
self.super_Parent = parent
self.main_container = Frame(parent)
self.main_container.pack()
self.top_frame = Frame(self.main_container)
self.top_frame.pack(side=TOP)
self.bottom_frame = Frame(self.main_container)
self.bottom_frame.pack(side=TOP)
self.text_box = Text(self.top_frame)
self.text_box.config(height=20, width=20)
self.text_box.pack()
self.queue = Queue.Queue()
self.update_me()
def show_popup(self):
self.my_popup = Toplevel(self.main_container)
self.my_popup.geometry('100x100')
self.popup_label = Label(self.my_popup, text="Hello!")
self.popup_label.pack(side=TOP)
self.pop_button = Button(self.my_popup, text="OK", command=self.my_popup.destroy)
self.pop_button.pack(side=TOP)
def write(self, line):
self.queue.put(line)
def update_me(self):
try:
while 1:
line = self.queue.get_nowait()
if line is None:
self.text_box.delete(1.0, END)
else:
self.text_box.insert(END, str(line))
self.text_box.see(END)
self.text_box.update_idletasks()
except Queue.Empty:
pass
self.text_box.after(100, self.update_me)
def pipeToWidget(input, widget):
widget.write(input)
def start_thread():
thread.start_new(start_test, (widget,))
def start_test(widget):
count = 0
while True:
pipeToWidget(str(count) + "\n", widget)
count += 1
time.sleep(2)
widget.show_popup()
root = Tk()
widget = TestApp(root)
start_button = Button(widget.bottom_frame, command=start_thread)
start_button.configure(text="Start Test")
start_button.pack(side=LEFT)
root.title("Testing Thread Module")
root.mainloop()
I can't reproduce your problem, but I can see why it would happen.
You're using the queue to pass messages from the background thread to the main thread for updating text_box, which is correct. But you're also calling widget.show_popup() from the background thread, which means it creates and displays a new Toplevel in the background thread. That's not correct.
All UI code must run in the same thread—not all UI code for each top-level window, all UI code period. On some platforms, you may get away with running each window in its own thread (or even free-threading everything), but that isn't supposed to work, and definitely will crash or do improper things on some platforms. (Also, that single UI thread has to be the initial thread on some platforms, but that isn't relevant here.)
So, to fix this, you need to do the same dance for creating the popups that you do for updating the textbox.
The obvious way to do that is to move the widget.show_popup() to the loop in update_me(). If you want it to happen 2 seconds after the textbox updates, just add self.top_frame.after(2000, self.show_popup) to the method.
But I'm guessing you're trying to teach yourself how to have multiple independent updating mechanisms, so telling you "just use a single update queue for everything" may not be a good answer. In that case, just create two queues, and a separate update method servicing each queue. Then, do your pipeToWidget, sleep 2 seconds, then pipeToPopup.
Another way around this is to use mtTkinter. It basically does exactly what you're doing, but makes it automatic, pushing each actual Tk GUI call onto a queue to be run later by the main loop. Of course your objects themselves have to be thread-safe, and this also means that you have to deal with the GUI calls from one thread getting interleaved with calls from another thread. But as long as neither of those is a problem (and they don't seem to be in your case), it's like magic.
If you want to know why this is freezing and/or crashing for you on Win7 and not for me on OS X 10.8… well, you really need to look into a mess of Tcl, C, and Python code, and also at how each thing is built. And, unless it's something simple (like your Tk build isn't free-threaded), it wouldn't tell you much anyway. The code isn't supposed to work, and if it seems to work for me… that probably just means it would work every time until the most important demo of my career, at which point it would fail.
How to run multiple processes in python without multithreading? For example consider the following problem:-
We have to make a Gui,which has a start button which starts a function(say, prints all integers) and there is a stop button, such that clicking it stops the function.
How to do this in Tkinter?
Then you need to bind the Button widget with the function which starts the working thread. For example:
import time
import threading
import Tkinter as tk
class App():
def __init__(self, root):
self.button = tk.Button(root)
self.button.pack()
self._resetbutton()
def _resetbutton(self):
self.running = False
self.button.config(text="Start", command=self.startthread)
def startthread(self):
self.running = True
newthread = threading.Thread(target=self.printints)
newthread.start()
self.button.config(text="Stop", command=self._resetbutton)
def printints(self):
x = 0
while self.running:
print(x)
x += 1
time.sleep(1) # Simulate harder task
With the self.running approach, you can end the thread gracefully only by changing its value. Note that the use of multiple threads serves to avoid blocking the GUI while printints is being executed.
I have read this previous question and I suppose why you explicitly asked here for a solution without multithreading. In Tkinter this solution can be used in a scenario where the other threads have to communicate with the GUI part. For example: filling a progressbar while some images are being rendered.
However, as it has been pointed in the comments, this approach is too complex for just printing numbers.
Here you can find a lot of information and more examples about Tkinter.
Edit:
Since your new question has been closed, I'll change the code here to clarify the last point.
Did you try to used multiprocessing module? Seems to be the one you're looking for.
This question already has an answer here:
Tkinter locks Python when an icon is loaded and tk.mainloop is in a thread
(1 answer)
Closed 7 months ago.
I am new to GUI programming and I want to write a Python program with tkinter. All I want it to do is run a simple function in the background that can be influenced through the GUI.
The function counts from 0 to infinity until a button is pressed. At least that is what I want it to do. But I have no idea how I can run this function in the background, because the mainloop() of tkinter has control all the time. And if I start the function in an endless loop, the mainloop() cannot be executed and the GUI is dead.
I would like to return control back to the mainloop() after each cycle, but how can I get the control back from the mainloop() to the runapp-function without a user-triggered event?
Here is some sample code that kills the GUI:
from Tkinter import *
class App:
def __init__(self, master):
frame = Frame(master)
frame.pack()
self.button = Button(frame, text="START", command=self.runapp)
self.button.pack(side=LEFT)
self.hi_there = Button(frame, text="RESTART", command=self.restart)
self.hi_there.pack(side=LEFT)
self.runapp()
def restart(self):
print "Now we are restarting..."
def runapp(self):
counter = 0
while (1):
counter =+ 1
time.sleep(0.1)
Event based programming is conceptually simple. Just imagine that at the end of your program file is a simple infinite loop:
while <we have not been told to exit>:
<pull an event off of the queue>
<process the event>
So, all you need to do to run some small task continually is break it down into bite-sized pieces and place those pieces on the event queue. Each time through the loop the next iteration of your calculation will be performed automatically.
You can place objects on the event queue with the after method. So, create a method that increments the number, then reschedules itself to run a few milliseconds later. It would look something like:
def add_one(self):
self.counter += 1
self.after(1000, self.add_one)
The above will update the counter once a second. When your program initializes you call it once, and from then after it causes itself to be called again and again, etc.
This method only works if you can break your large problem (in your case "count forever") into small steps ("add one"). If you are doing something like a slow database query or huge computation this technique won't necessarily work.
You will find the answer in this other question Tkinter locks python when Icon loaded and tk.mainloop in a thread.
In a nutshell, you need to have two threads, one for tkinter and one for the background task.
Try to understand this example : clock updating in backgroud, and updating GUI ( no need for 2 threads ).
# use Tkinter to show a digital clock
# tested with Python24 vegaseat 10sep2006
from Tkinter import *
import time
root = Tk()
time1 = ''
clock = Label(root, font=('times', 20, 'bold'), bg='green')
clock.pack(fill=BOTH, expand=1)
def tick():
global time1
# get the current local time from the PC
time2 = time.strftime('%H:%M:%S')
# if time string has changed, update it
if time2 != time1:
time1 = time2
clock.config(text=time2)
# calls itself every 200 milliseconds
# to update the time display as needed
# could use >200 ms, but display gets jerky
clock.after(200, tick)
tick()
root.mainloop( )
credits: link to site
I don't have sufficient reputation to comment on Bryan Oakley's answer (which I found to be very effective in my program), so I'll add my experience here. I've found that depending on how long your background function takes to run, and how precise you want the time interval to be, it can be better to put self.after call at the beginning of the recurring function. In Bryan's example, that would look like
def add_one(self):
self.after(1000, self.add_one)
self.counter += 1
Doing it this way ensures that the interval of time is respected exactly, negating any interval drift that might occur if your function takes a long time.
If you don't want to be away from those threads, I would like to give one suggestion for your GUI-
Place the function for your GUI just before the root.mainloop() statement.
Example-
root = tk.Tk()
.
.
graphicsfunction() #function for triggering the graphics or any other background
#function
root.mainloop()
Please up vote if you like.