I am trying to build a real time project where the status gets updated every second, so some part of code repeats continuously. when i want to change the information which has to be get updated i will just click on new button which gives me the first window where i can update the new information. but by doing so gives me the following error. if i use after() instead of threading, the error wont be there but the output window gets hanged.Please help me with the idea to resolve this. Thank you.
Exception in thread Thread-2:
Traceback (most recent call last):
File "C:\Program Files (x86)\Microsoft Visual Studio\Shared\Python37_64\lib\threading.py", line 926, in _bootstrap_inner
self.run()
File "C:\Program Files (x86)\Microsoft Visual Studio\Shared\Python37_64\lib\threading.py", line 1177, in run
self.function(*self.args, **self.kwargs)
File "C:/Users/Desktop/Tool/t.py", line 47, in ae
self.treeview.insert('', 'end',image=self._img, value=(a))
File "C:\Program Files (x86)\Microsoft Visual Studio\Shared\Python37_64\lib\tkinter\ttk.py", line 1370, in insert
res = self.tk.call(self._w, "insert", parent, index, *opts)
_tkinter.TclError: invalid command name ".!treeview"
Code where i have problem with:
def aaa(self):
num_threads = 5 * multiprocessing.cpu_count()
p = multiprocessing.dummy.Pool(num_threads)
p.map(self.ping_func, [x for x in Demo2.t1])
self.process_incoming()
#threading.Timer(1.0, self.aaa).start()-this gives the error while pressing new button and updating information
self.master.after(100, self.aaa) #it hangs the output window
Sample code:
import multiprocessing.dummy
import multiprocessing
import os
import socket
import sys
import subprocess
import re
import time
import threading
import tkinter.messagebox
from tkinter import ttk
import queue
from tkinter import *
class Demo1: #window 1
data=[]
def __init__(self, master):
self.master = master
self.t=tkinter.Text(self.master,height=20,width=50)
self.t.grid(row=1, column=1)
self.button = tkinter.Button(self.master,height=3,width=10, text="OK", command = self.new_window)
self.button.grid(row=2,column=1)
def new_window(self):
self.inputValue=self.t.get("1.0",'end-1c')
Demo1.data=self.inputValue.split("\n")
self.master.destroy() # close the current window
self.master = tkinter.Tk() # create another Tk instance
self.app = Demo2(self.master) # create Demo2 window
self.master.mainloop()
class Demo2: #window 2
value = []
display = []
num=0
def __init__(self, master):
self.master = master
self.queue = queue.Queue()
Demo2.value = Demo1.data
self.button = tkinter.Button(self.master,height=2,width=11, text="new",command=self.new).place(x=0,y=0)
self.label = tkinter.Label(self.master, text="monitor", font=("Arial",20)).grid(row=0, columnspan=3)
cols = ('aa','bb')
self.treeview = ttk.Treeview(self.master, columns=cols)
for col in cols:
self.treeview.heading(col, text=col)
self.treeview.column(col,minwidth=0,width=170)
self.treeview.grid(row=1, column=0)
self._img=tkinter.PhotoImage(file="green1.gif")
self.aaa()
def aaa(self):
num_threads = 5 * multiprocessing.cpu_count()
p = multiprocessing.dummy.Pool(num_threads)
p.map(self.ping_func, [x for x in Demo2.value])
self.process_incoming()
#threading.Timer(1.0, self.aaa).start()
self.master.after(100, self.aaa)
def ping_func(self,ip): #Ping every ip and append the result
ping_result = []
pingCmd = "ping -n 1 -w 1000 " + ip
childStdout = os.popen(pingCmd)
result = (childStdout.readlines())
childStdout.close()
ping_result.append(ip)
if(any('Reply from' in i for i in result)):
ping_result.append("success")
else:
ping_result.append("failed")
self.queue.put(ping_result) #Thread value to queue
def process_incoming(self): #add the ping result to treeview
while self.queue.qsize():
try:
if Demo2.num<len(Demo1.data):
self._img=tkinter.PhotoImage(file="green1.gif")
self._img1=tkinter.PhotoImage(file="red.gif")
msg = self.queue.get_nowait()
Demo2.display.append(msg) #adding queue value to variable(display)
if(len(Demo2.display)==len(Demo1.data)):
self.treeview.insert("","end",values=(0,0,0,0,0))
self.treeview.delete(*self.treeview.get_children())
for i,(a,b) in enumerate(Demo2.display):
if(Demo2.display[i][1]=='success' ):
self.treeview.insert('', 'end',image=self._img, value=(a,b))
else:
self.treeview.insert('', 'end',image=self._img1, value=(a,b))
Demo2.num=Demo2.num+1
Demo2.display.clear()
else:
Demo2.display.clear()
Demo2.num=0
except queue.Empty: # Shouldn't happen.
pass
def periodic_call(self):
self.master.after(200, self.periodic_call) # checking its contents periodically
self.process_incoming()
if not self.running:
import sys
sys.exit(1)
def new(self):
self.master.destroy() # close the current window
self.master = tkinter.Tk() # create another Tk instance
self.app = Demo1(self.master) # create Demo2 window
self.master.mainloop()
def main():
root = tkinter.Tk()
app = Demo1(root)
root.mainloop()
if __name__ == '__main__':
main()
Main problem is that ping -w 1000 need a lot of time to run but Pool.map() waits for all results. You could even run results = p.map(...) without queue (but with return result) but it could also block tkinter
You may use map_async() to run it without waiting for results.
I also use starmap (or rather starmap_async()) to send two arguments ip, queue to every process.
I also made other changes - ie. rename variables, move some code to __init__ to create only once (images, Pool, Queue). I also send list of IP to other window as argument Window2(master, data) and back Window1(master, data) - so I can edit this list.
BTW: because I run on Linux so I changed arguments for ping and check different text to test if it get answer.
import os
import multiprocessing.dummy
import queue
import tkinter as tk
import tkinter.ttk as ttk
# --- classes ---
class Window1:
def __init__(self, master, data=None):
self.master = master
self.data = data
if self.data is None:
self.data = []
self.text = tk.Text(self.master, height=20, width=50)
self.text.grid(row=1, column=1)
self.button = tk.Button(self.master, height=3, width=10, text="OK", command=self.new_window)
self.button.grid(row=2, column=1)
# put self.data in Text
#for item in self.data:
# self.text.insert('end', item + '\n')
self.text.insert('end', '\n'.join(self.data))
def new_window(self):
text = self.text.get('1.0', 'end')
# remove empty lines
self.data = [item.strip() for item in text.split("\n") if item.strip()]
self.master.destroy()
root = tk.Tk()
Window2(root, self.data)
root.mainloop()
class Window2:
def __init__(self, master, data):
self.master = master
# keep list
self.data = data
# create dictionary for results
self.results = {ip: [ip, '???'] for ip in self.data}
self.button = tk.Button(self.master, height=2, width=11, text='New', command=self.new)
self.button.grid(row=0, column=0, sticky='w')
self.label = tk.Label(self.master, text='monitor', font=("Arial", 20))
self.label.grid(row=0, column=1)
cols = ('IP','Result')
self.treeview = ttk.Treeview(self.master, columns=cols)
for col in cols:
self.treeview.heading(col, text=col)
self.treeview.column(col,minwidth=0,width=170)
self.treeview.grid(row=1, column=0, columnspan=3)
# create only once
self._image_green = None # tk.PhotoImage(file="green1.gif")
self._image_red = None # tk.PhotoImage(file="red.gif")
# to reduce number of processes for small `data`
n = min(5, len(self.data))
# create only once
self.queue = queue.Queue()
self.num_threads = n * multiprocessing.cpu_count()
self.p = multiprocessing.dummy.Pool(self.num_threads)
# to stop `after()`
self.running = True
# run first time
self.update_treeview() # to display it before running processes
self.run_processes()
self.processes_incoming() # first create window to display it faster
def run_processes(self):
if self.running:
self.p.starmap_async(self.ping, [(ip, self.queue) for ip in self.data])
self.after_ID2 = self.master.after(500, self.run_processes)
def ping(self, ip, queue):
#print('start ping:', ip)
#cmd = 'ping -n 1 -w 3 ' + ip
cmd = 'ping -w 1 ' + ip # Linux
child_stdout = os.popen(cmd)
result = child_stdout.readlines()
child_stdout.close()
#print('end ping:', ip)
#if any('Reply from' in line for line in result):
if any('bytes from' in line for line in result): # Linux
value = [ip, 'success']
else:
value = [ip, 'failed']
queue.put(value)
def update_treeview(self):
self.treeview.delete(*self.treeview.get_children())
for ip in self.data:
ip, value = self.results[ip]
if value == 'success':
image = self._image_green
elif value == 'failed':
image = self._image_red
else:
image = None
#self.treeview.insert('', 'end', image=image, value=(ip, valueb))
self.treeview.insert('', 'end', value=(ip, value))
def processes_incoming(self):
if self.running:
# get all from queue
new_values = False
while self.queue.qsize():
#while not self.queue.empty:
data = self.queue.get_nowait()
ip, value = data
self.results[ip] = data
new_values = True
# update only if new values
if new_values:
self.update_treeview()
# repeate after 100ms
self.after_ID1 = self.master.after(100, self.processes_incoming)
def new(self):
# to stop all `after()`
self.running = False
self.master.after_cancel(self.after_ID1)
self.master.after_cancel(self.after_ID2)
self.master.destroy()
root = tk.Tk()
Window1(root, self.data)
root.mainloop()
# --- functions ---
def main():
examples = [
'127.0.0.1', # localhost
'10.0.0.1', # IP in local network
'192.168.0.1', # IP in local network
'8.8.8.8', # Google DNS
'8.8.4.4', # Google DNS
]
root = tk.Tk()
Window1(root, examples)
root.mainloop()
# --- main ---
if __name__ == '__main__':
main()
Related
Sorry for the confusing title basically what I'm trying to figure out how to import a frame from one script into another. I'm not sure how I would call it since it has so many functions. Here are my two scripts:
File Name - wrapper:
import tkinter as tk
import workingcatch
from tkinter import *
root = tk.Tk()
outputframe = LabelFrame(master=root, width=800, height=700) #where i want to import the script
outputframe.pack(side=LEFT, padx=10,pady=10)
root.geometry('1280x720')
root.mainloop()
File Name - workingcatch:
from subprocess import Popen, PIPE, STDOUT
from threading import Thread
import tkinter as tk
import logging
import time
import sys
info = logging.getLogger(__name__).info
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(message)s")
platform = windows = 'mswin'
# define dummy subprocess to generate some output
cmd = [sys.executable or "python", "-u", "-c", """
import itertools, time
for i in itertools.count():
print(i)
time.sleep(0.5)
exit()
""", "exit"]
class OStream(Thread):
enable_print = True
def handel_line(self, line):
if platform == windows:
# Windows uses: "\r\n" instead of "\n" for new lines.
line = line.replace(b"\r\n", b"\n")
if self.enable_print:
info("got: %r", line)
if self.stream_print is not None:
self.stream_print(line)
def stop(self):
self.alive = False
def run(self):
while self.alive:
for s in self.ostrams:
line = s.stdout.readline()
if line:
self.handel_line(line)
time.sleep(0.2)
info("OStream Exit")
def pipe_proc(self, stream):
self.ostrams.append(stream)
def stream_callback(self, func):
self.stream_print = func
def __init__(self):
self.ostrams = []
self.alive = True
self.stream_print = None
Thread.__init__(self)
class Scrolable_Frame(tk.Frame):
def get(self):
return self.interior
def __init__(self, master):
tk.Frame.__init__(self, master)
self.scroll = tk.Scrollbar(self)
self.scroll.pack(side=tk.RIGHT, fill=tk.Y)
self.canvas = tk.Canvas(
self, bd=0, highlightthickness=0,
yscrollcommand=self.scroll.set)
self.canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=tk.TRUE)
self.scroll.config(command=self.canvas.yview)
self.canvas.xview_moveto(0)
self.canvas.yview_moveto(0)
self.interior = tk.Frame(self.canvas)
interior_id = self.canvas.create_window(
0, 0, window=self.interior, anchor=tk.NW
)
def _configure_interior(_):
size = (self.interior.winfo_reqwidth(), self.interior.winfo_reqheight())
self.canvas.config(scrollregion="0 0 %s %s" % size)
if self.interior.winfo_reqwidth() != self.canvas.winfo_width():
self.canvas.config(width=self.interior.winfo_reqwidth())
self.interior.bind('<Configure>', _configure_interior)
def _configure_canvas(_):
if self.interior.winfo_reqwidth() != self.canvas.winfo_width():
self.canvas.itemconfigure(interior_id, width=self.canvas.winfo_width())
self.canvas.bind('<Configure>', _configure_canvas)
def _on_mousewheel(event):
self.canvas.yview_scroll(int(-1 * (event.delta / 120)), 'units')
self.canvas.bind_all("<MouseWheel>", _on_mousewheel)
class ShowProcessOutputDemo(tk.Tk):
def __init__(self):
"""Start subprocess, make GUI widgets."""
tk.Tk.__init__(self)
self.geometry('300x200+500+300')
self.protocol("WM_DELETE_WINDOW", self.stop)
self.proc = Popen(cmd, stdout=PIPE, stderr=STDOUT)
self.ostream = OStream()
self.ostream.pipe_proc(self.proc)
self.ostream.stream_callback(self.add_label)
self.ostream.start()
self.exit_button = tk.Button(
self, text="Stop subprocess", command=self.stop
)
self.exit_button.pack(pady=20)
self.scolable_frame = Scrolable_Frame(self)
self.scolable_frame.pack(
expand=True, fill=tk.BOTH, pady=20, padx=20
)
def add_label(self, line):
line_text = 'OStream line content: {0}'.format(line[:-1].decode())
tk.Label(
self.scolable_frame.get(), text=line_text
).pack(anchor=tk.CENTER, expand=True, fill=tk.X)
def stop(self):
"""Stop subprocess and quit GUI."""
self.ostream.stop()
self.proc.kill()
self.proc.stdout.close()
self.proc.wait(timeout=2)
info("GUI Exit")
self.quit()
if __name__ == '__main__':
app = ShowProcessOutputDemo()
app.mainloop()
So what I'm trying to do is get the output from "workingcatch" into 'wrapper's frame.
Thanks for reading
I have question about how to call some variables, in this case from the method inside exportCsv that belongs to back_Gui class. I want to use the variables self.msg_ and self.opstat in the method __update from class _Gui to stop the reproduction of the gift and promt out the window to save the file. When I run the code and press the button it iterates in an infinite loop because the variable is not passing to. Also, I try to aggregate some threading to not to freeze the window when the button is pressed. Any solution?.
Also, I think these variables I mention would be inside the try but what happens in the except?. Do I have to create more variables to avoid infinite looping on the gift?
from tkinter import *
import tkinter as tk
from tkinter import filedialog
import sqlite3
import pandas as pd
from PIL import Image, ImageTk
import time
import threading
class back_Gui: #Superclass
'''Class that handle the data'''
def __init__(self, db_name = 'database.db'):
self.db_name = db_name
self.msg_ = None
self.opstat = None
.
.
.
def exportCSV(self):
df_to = self.df
try:
export_file = filedialog.asksaveasfilename(defaultextension='.csv')
df_to.to_csv(export_file, index=False, header=True)
except:
pass
#These are the variables that I need
self.msg_ = "Done."
self.opstat = -1
class _Gui(back_Gui): #Subclass
def __init__(self, window):
'''Gui of the windw tk '''
self.wind = window #child
super().__init__()
self.text_font = ('Helvetica', '10', 'bold')
self.Button_(self.wind)
def Button_(self, wind):
"""Run the gift while the csv is being generated"""
#Button
b1 = Button(self.wind, text="random",
font=self.text_font,
command=self.job_genCsv,
).grid(row=1, column=5, padx=5, pady=5 ,sticky=W)
def frame_maps(self, wind):
'''Frame Containter'''
self.frame = LabelFrame(self.wind, text='Hail Hydra', font=self.text_font, height = 500, width = 1300, bd=4)
self.frame.grid(row=2, columnspan=20, sticky=W+E)#, columnspan=3, padx=25, pady=25)
# create the canvas, size in pixels
self.canvas = Canvas(self.frame, width=1300, height=500, bg='white')
# pack the canvas into a frame/form
self.canvas.grid(row=0, columnspan=20, sticky=N, ipadx=20, ipady=20)#
# load the .gif image file
#Here it has to be use the self because is local variable
self.current_image_number = 0
file="cain.gif"
info = Image.open(file)
self.frames = info.n_frames
self.gif1 = [PhotoImage(file=file, format=f"gif -index {i}") for i in range(self.frames)]
def __update(self):
#self.job_genCsv()
''' update the gift '''
self.frame.update_idletasks()#update
if self.opstat >= 0.0:
#msg = self.image_on_canvas #
# next image
self.current_image_number += 1
# return to first image
if self.current_image_number == self.frames: #len(self.images):
self.current_image_number = 0
# change image on canvas
self.canvas.itemconfig(self.update, image=self.gif1[self.current_image_number])
_ = threading.Timer(0, self.__update).start()
print("loop")
else:
if self.msg_ == "Done.":
self.update = self.canvas.create_image(250, 50, anchor=NW, image=self.gif1[self.current_image_number])
del self.msg_
#control variable restablished
self.opstat = 0
print("ends")
def job_genCsv(self):
'''Runs his job and call frame_maps '''
self.frame_maps(self)
_ = threading.Timer(0, self.exportCSV).start()
_ = threading.Timer(0, self.__update).start()
if __name__ == '__main__':
window = Tk()
application = _Gui(window)
window.mainloop()
I'm getting this error:
File "C:\Users\Documents\run.py", line 214, in __update
if self.opstat >= 0.0:
AttributeError: '_Gui' object has no attribute 'opstat'
I've searched the whole internet to answer my problem, but nobody seems to have the same one: I'm trying to update my tkinter GUI dynamically from a subprocess output, which works fine, if I'm starting my GUI inside eclipse. BUT if I'm running it in the file explorer or in visual studio, the 'stdout.readline' command waits, until the subprocess is finished. Only then the complete output is printed to my textarea... I am working with a thread and I've tried 2 ways: one is shown in 'App.py', the other one is threading the 'read_update' method instead (and not using 'reader_thread' and 'update' methods).
Interesting sidenote: the sys.argv command in Test.py does not return my string "var_test". Can anyone tell me why?
My Classes are:
GUI.py
import Tkinter as tk
from App import App
if __name__ == '__main__':
root = tk.Tk()
app = App(root)
root.protocol("WM_DELETE_WINDOW", app.quit)
root.mainloop()
App.py
#App.py
import Tkinter as tk
import tkFont as tkfont
import subprocess
from subprocess import Popen
from subprocess import PIPE
from itertools import islice
from threading import Thread
from ttk import Scrollbar
from Tkinter import *
from Queue import Queue, Empty
class App():
def __init__(self, root):
self.root = root
self.root.title_font = tkfont.Font(family = "Helvetica", size = 18, weight = "bold", slant = "italic")
Grid.columnconfigure(self.root, 5, weight = 1)
button_ok = tk.Button(self.root, text = "OK", width = 10, command = lambda: self.on_okay())
button_ok.grid(row = 7, column = 0, padx = (20,0), pady = 10, sticky = W)
xscrollbar = Scrollbar(self.root, orient=HORIZONTAL)
xscrollbar.grid(row=8, column=1, columnspan=4, sticky=E + W)
yscrollbar = Scrollbar(self.root, orient=VERTICAL)
yscrollbar.grid(row=8, column=5, sticky=N + S)
self.textarea = Text(self.root, wrap=NONE, bd=0,
xscrollcommand=xscrollbar.set,
yscrollcommand=yscrollbar.set)
self.textarea.grid(row=8, column=1, columnspan=4, rowspan=1,
padx=0, sticky=E + W + S + N)
def on_okay(self):
self.textarea.delete("1.0", END)
exec_path = r"\Test.py" #insert location of Test.py
self.process = subprocess.Popen([exec_path, 'var_test'], shell = True, stdout = subprocess.PIPE, stderr = subprocess.STDOUT)
self.q = Queue(maxsize = 1024)
t = Thread(target=self.reader_thread, args=[self.q])
t.daemon = True
t.start()
self.update(self.q)
def reader_thread(self, q):
try:
with self.process.stdout as pipe:
for line in iter(pipe.readline, b''):
q.put(line)
finally:
q.put(None)
def update(self, q):
for line in self.iter_except(q.get_nowait, Empty):
if line is None:
#self.quit()
return
else:
self.textarea.insert(INSERT, line)
self.textarea.yview(END)
break
self.root.after(40, self.update, q)
def iter_except(self, function, exception):
try:
while True:
yield function()
except exception:
return
def read_update(self):
while True:
line = self.process.stdout.readline()
if line == "" and self.process.poll() != None:
break
elif line == "":
pass
else:
self.textarea.insert(INSERT, line)
self.textarea.yview(END)
self.textarea.update_idletasks()
def quit(self):
try:
self.process.kill()
except AttributeError:
pass
finally:
self.root.destroy()
Test.py
import sys
from time import sleep
var = sys.argv
print var
for i in range(1, 10):
print i
print "finished printing numbers"
sleep(10)
print "finished"
Thank you for your help! I'm pretty desperate 'cause I've been trying to solve this problems for many hours now...
use sys.stdout.flush() after print
I'm looking for elegant way to display log lines on screen while the script is running.
from time import sleep
from threading import Thread
import tkinter as tk
class WaitGuiPrallel(Thread):
def __init__(self, TXT='Wait!', ttl='Logs'):
self.txt = TXT
Thread.__init__(self)
self.ttl = ttl
self.start() # This is starting the self.run()
def run(self):
self.root = tk.Tk()
self.root.attributes("-topmost", True)
self.root.title(self.ttl)
self.label = tk.Label(self.root, text=self.txt, font=("Helvetica", 20))
self.label.pack()
self.Location()
self.root.mainloop()
def Exit(self):
self.root.quit()
def Location(self):
w = 500 # width for the Tk root
h = 150 # height for the Tk root
ws = self.root.winfo_screenwidth() # width of the screen
self.root.geometry('%dx%d+%d+%d' % (w, h, ws - w - 20, 10))
def Update(self, newText):
self.txt1 = newText
self.label.destroy()
self.label = tk.Label(self.root, text=self.txt1,
font=("Helvetica", 12))
self.label.pack()
self.root.update()
Wait = WaitGuiPrallel(TXT='Wait! Do not touch mouse or keyboard')
sleep(2)
for t in range(5):
sleep(1)
Wait.Update(newText='Log line %s' % t)
Wait.Update(newText='Done!')
sleep(1)
Wait.Exit()
The current script got few issues:
it is not elegant - there must be a better way
it has problems when updated from different Threads
Sometime running it twice from Spyder+IPython is not possible (IPython freeze)
Tkinter doesn't really play well with threads. Using a StringVar is a little more thread friendly than other methods (in my experience). Here's how to do that plus a couple other fixes:
from time import sleep
from threading import Thread
import tkinter as tk
class WaitGuiPrallel(Thread):
def __init__(self, TXT='Wait!', ttl='Logs'):
Thread.__init__(self)
self.txt = TXT
self.ttl = ttl # what's this for?
self.daemon = True # this thread will terminate when the main thread terminates
self.start() # This is starting the self.run()
def run(self):
self.root = tk.Tk()
self.root.attributes("-topmost", True)
self.root.title(self.ttl)
self.txt = tk.StringVar(value=self.txt)
self.label = tk.Label(self.root, textvariable=self.txt, font=("Helvetica", 20))
self.label.pack()
self.Location()
self.root.mainloop()
def Location(self):
w = 500 # width for the Tk root
h = 150 # height for the Tk root
ws = self.root.winfo_screenwidth() # width of the screen
self.root.geometry('%dx%d+%d+%d' % (w, h, ws - w - 20, 10))
Wait = WaitGuiPrallel(TXT='Wait! Do not touch mouse or keyboard')
sleep(2)
for t in range(5):
sleep(1)
Wait.txt.set('Log line %s' % t)
Wait.txt.set('Done!')
sleep(1)
If you have multiple threads calling this then I would consider using a Queue and a third thread to monitor the queue.
I want to have a function run continuously within a tkinter GUI. I have attached some shell code:
#!/usr/bin/env python3
import tkinter as tk
from time import sleep
import os
import sys
class Application(Frame):
def __init__(self, master):
super(Application, self).__init__(master)
self.grid()
self.create_widgets()
def create_widgets(self):
......
root = Tk()
def run_continously:
... -- calls sleep -- ...
root.after(3000, run_continuously)
root.geometry('%dx%d+%d+%d' % (w, h, x, y))
app = Application(root)
root.after(3000, run_continuously)
root.mainloop()
When running the GUI it tends to run the 'run_continuously' function once and the GUI freezes up. I suspect from poking around that this is due to the sleep function (which I call in the run_continuously function)
How would I go about implementing the 'run_continuously' function in a very simple thread to get around this issue? Would running the function in a thread even get me around the problem? The 'run_continuously' function does not need to interact at all with the Application class. I want it to run simply in the background and stop when the mainloop is finished.
Here is the end of the code:
def run_continuously(quit_flag):
print("in it")
if not quit_flag:
GPIO.output(DIR_PIN, True)
for i in range(steps):
print("in loop")
GPIO.output(STEP_PIN, True)
sleep(sDelay)
GPIO.output(STEP_PIN, False)
sleep(sDelay)
sleep(wait_time)
GPIO.output(DIR_PIN, False)
for i in range(steps):
GPIO.output(STEP_PIN, True)
sleep(sDelay)
GPIO.output(STEP_PIN, False)
sleep(sDelay)
print("run motor")
root.after(1000, run_continuously(quit_flag,))
#=================================================================
# main
#=================================================================
root = Tk() # Create the GUI root object
press1 = StringVar()
press2 = StringVar()
x = 275
y = 50
w = 580
h = 250
root.geometry('%dx%d+%d+%d' % (w, h, x, y))
app = Application(root) # Create the root application window
quit_flag = False
root.after(0, app.read_pressure)
motor_thread = threading.Thread(target=run_continuously, args=(quit_flag,)).start()
root.mainloop()
quit_flag=True
motor_thread.join()
This is a minimal, complete, and verifiable example that exits cleanly if the 'QUIT' button is pressed or Ctrl-C is pressed:
from Tkinter import *
import multiprocessing
import threading
import time
import logging
class Application(Frame):
def create_widgets(self):
self.quit_button = Button(self)
self.quit_button['text'] = 'QUIT'
self.quit_button['fg'] = 'red'
self.quit_button['command'] = self.quit
self.quit_button.pack({'side': 'left'})
def __init__(self, master=None):
Frame.__init__(self, master)
self.quit_button = None
self.pack()
self.create_widgets()
self.poll()
def poll(self):
"""
This method is required to allow the mainloop to receive keyboard
interrupts when the frame does not have the focus
"""
self.master.after(250, self.poll)
def worker_function(quit_flag):
counter = 0
while not quit_flag.value:
counter += 1
logging.info("Work # %d" % counter)
time.sleep(1.0)
format = '%(levelname)s: %(filename)s: %(lineno)d: %(message)s'
logging.basicConfig(level=logging.DEBUG, format=format)
root = Tk()
app = Application(master=root)
quit_flag = multiprocessing.Value('i', int(False))
worker_thread = threading.Thread(target=worker_function, args=(quit_flag,))
worker_thread.start()
logging.info("quit_flag.value = %s" % bool(quit_flag.value))
try:
app.mainloop()
except KeyboardInterrupt:
logging.info("Keyboard interrupt")
quit_flag.value = True
logging.info("quit_flag.value = %s" % bool(quit_flag.value))
worker_thread.join()