Exit Python RFID Thread - python

I have started a small project and am currently stuck on a problem.
I have written a small GUI in Tkinter which opens another thread when the button is pressed in order to read the ID of an RFID chip. This is then returned to the GUI via a queue object and displayed there by the label. This works so far.
Now I would like to implement that the thread is terminated when no RFID chip has been detected for 10 seconds or the thread still exists after this time. I have tried to interrupt this with an event as follows but without success.
Does anyone have an idea how this could work?
Thanks already.
#!/usr/bin/env python3
import tkinter as tk
import queue
import threading
import time
import RPi.GPIO as GPIO
from mfrc522 import SimpleMFRC522
class GUI:
def __init__(self, master):
self.master = master
self.test_button = tk.Button(self.master, command=self.tb_click)
self.test_button.configure(text="Start", background="Grey", padx=50)
self.test_button.pack(side='top')
self.test_label = tk.Label(self.master, text="test")
self.test_label.pack(side='bottom')
def tb_click(self):
self.stop = threading.Event()
self.queue = queue.Queue( )
thread = Rfid(self.queue, self.stop)
thread.start()
self.master.after(100, self.process_queue)
self.master.after(10000, self.terminate)
def process_queue(self):
try:
msg = self.queue.get(0)
#print(str(msg))
self.test_label.configure(text=str(msg))
except queue.Empty:
self.master.after(100, self.process_queue)
def terminate(self):
if thread.is_alive():
self.stop.set()
class Rfid(threading.Thread):
def __init__(self, queue, stop):
threading.Thread.__init__(self)
self.queue = queue
self.stop = stop
def run(self):
while True:
if self.stop.isSet():
print("abgeschalten")
break
reader = SimpleMFRC522()
id, text = reader.read()
print(str(id))
self.queue.put(str(text))
GPIO.cleanup()
root = tk.Tk()
root.title("Test")
main_ui = GUI(root)
root.mainloop()

Related

Close a Looping Thread make the script freezes

Introduction
I have been made some script with threading inside of it to kill exe's for certain amount of time. The threading seems working fine but when im going to close the script, it freezes. My script has a "button" to execute and exit using TKinter
Problematic
The script become freezes and the thread stil going on the background. The thread contain loops for 30-60 seconds (killing exe's). It seems the script cant close properly because of the thread still going on and on.
Here's the thread class :
# Global Variable #
runBroke='taskkill /F /im RuntimeBroker.exe'
#Thread Class #
class Killizer(object):
def __init__(self):
self.thread1 = None
self.stop_threads = Event()
def killer(self):
while not self.stop_threads.is_set():
def kill():
os.system(runBroke)
timer()
def timer():
time.sleep(60)
kill()
kill()
def exe(self):
self.stop_threads.clear()
self.thread1 = Thread(target = self.killer)
self.thread1.start()
def shut(self):
self.stop_threads.set()
self.thread1.join()
self.thread1 = None
How to properly close or enforce to exit that thread using TKinter Button?
You could use after making a timer to kill the application:
import time, os
import tkinter as tk
runBroke='taskkill /F /im RuntimeBroker.exe'
class KillingApp():
def __init__(self):
self.root = tk.Tk()
self.label = tk.Label(self.root, text="")
self.label.pack()
self.kill()
self.root.mainloop()
def _kill(self):
print("running: taskkill")
os.system(runBroke)
def kill(self):
now = time.strftime("%H:%M:%S")
self.label.configure(text="Last kill time: %s" % now)
self._kill()
# idle 5 seconds
self.root.after(5000, self.kill)
app=KillingApp()
use ... at the end
# Global Variable #
runBroke='taskkill /F /im RuntimeBroker.exe'
#Thread Class #
class Killizer(object):
def __init__(self):
self.thread1 = None
self.stop_threads = Event()
def killer(self):
while not self.stop_threads.is_set():
def kill():
os.system(runBroke)
timer()
def timer():
time.sleep(60)
kill()
kill()
def exe(self):
self.stop_threads.clear()
self.thread1 = Thread(target = self.killer)
self.thread1.start()
def shut(self):
self.stop_threads.set()
self.thread1.join()
self.thread1 = None
it will close it all cleanly

Thread doesn't log into tkinter window if it is in an imported file

I have a tkinter app running alongside two different threads that are logging into it using a queue.
One of the threads is in the same code file as the tkinter app. The other one is imported from another file, even thought their code is similar. What I verify is that only the thread defined in the same file manages to write into the UI. Do you know why this happens?
The code for the main file is:
import time
import queue
import threading
import tkinter as tk
from tkinter.scrolledtext import ScrolledText
from tkinter import ttk
import logging
from logging.handlers import QueueHandler
from foo import ImportedLoggerThread
logger = logging.getLogger(__name__)
class LoggerThread(threading.Thread):
def __init__(self):
super().__init__()
self._stop_event = threading.Event()
def run(self):
logger.debug('LoggerThread: running')
i = 0
while not self._stop_event.is_set():
logger.info("LoggerThread: iteration %d" % i)
i += 1
time.sleep(1)
def stop(self):
self._stop_event.set()
class LoggingWindow:
def __init__(self, frame):
self.frame = frame
self.scrolled_text = ScrolledText(frame, height=12)
self.scrolled_text.pack()
self.log_queue = queue.Queue()
self.queue_handler = QueueHandler(self.log_queue)
logger.addHandler(self.queue_handler)
# start polling
self.frame.after(100, self.poll_log_queue)
def write(self, record):
msg = self.queue_handler.format(record)
self.scrolled_text.insert(tk.END, msg + '\n')
# Scroll to the bottom
self.scrolled_text.yview(tk.END)
def poll_log_queue(self):
# Poll every 100ms
while True:
try:
record = self.log_queue.get(block=False)
except queue.Empty:
break
else:
self.write(record)
self.frame.after(100, self.poll_log_queue)
class App:
def __init__(self, root):
self.root = root
frame = ttk.Labelframe(text="Log")
frame.pack()
self.console = LoggingWindow(frame)
self.th = LoggerThread()
self.th.start()
self.imported = ImportedLoggerThread()
self.imported.start()
self.root.protocol('WM_DELETE_WINDOW', self.quit)
def quit(self):
self.th.stop()
self.imported.stop()
self.root.destroy()
def main():
logging.basicConfig(level=logging.DEBUG)
root = tk.Tk()
app = App(root)
app.root.mainloop()
if __name__ == '__main__':
main()
and for the second file foo.py:
import threading
import logging
import time
logger = logging.getLogger(__name__)
class ImportedLoggerThread(threading.Thread):
def __init__(self):
super().__init__()
self._stop_event = threading.Event()
def run(self):
logger.debug('Imported: running')
i = 0
while not self._stop_event.is_set():
logger.info("Imported: iteration %d" % i)
i += 1
time.sleep(2)
def stop(self):
self._stop_event.set()
Thanks in advance!
You define 2 logger instances in your files (logger = logging.getLogger(__name__)) and it causes your issue. If you use the same logger instance, it should work. It means in your case, you should pass the logger instance from your main file to the imported module (foo.py). Please see below the fixed foo.py and the fixed App class in the main file.
foo.py:
import threading
import time
class ImportedLoggerThread(threading.Thread):
def __init__(self, my_logger):
super().__init__()
self._stop_event = threading.Event()
self.my_logger = my_logger # Should be passed from caller side.
def run(self):
self.my_logger.debug('Imported: running')
i = 0
while not self._stop_event.is_set():
self.my_logger.info("Imported: iteration %d" % i)
i += 1
time.sleep(2)
def stop(self):
self._stop_event.set()
As you can see above the "imported" module uses a getting logger (It should comes from the "main" file)
App class:
class App:
def __init__(self, root):
self.root = root
frame = ttk.Labelframe(text="Log")
frame.pack()
self.console = LoggingWindow(frame)
self.th = LoggerThread()
self.th.start()
self.imported = ImportedLoggerThread(my_logger=logger) # Should be passed the defined logger instance.
self.imported.start()
self.root.protocol('WM_DELETE_WINDOW', self.quit)
def quit(self):
self.th.stop()
self.imported.stop()
self.root.destroy()
As you can see in the App class, the defined logger instance is passed to the imported ImportedLoggerThread class.
Output:
>>> python3 test.py
DEBUG:__main__:LoggerThread: running
DEBUG:__main__:Imported: running
INFO:__main__:LoggerThread: iteration 0
INFO:__main__:Imported: iteration 0
INFO:__main__:LoggerThread: iteration 1
INFO:__main__:Imported: iteration 1
INFO:__main__:LoggerThread: iteration 2
GUI:

Pausing a Thread When GPIO Input Interrupt Triggers using Event.set() Inside a Thread Class Method

Good day everyone, Im trying to call a method inside a thread class which should set an event flag inside that thread, resulting in the thread to stop running while the event is set. The current code kind of works, and calls the function, but the event flag does not seem to trigger inside the thread.
The thread is responsible for an operation to run when a button is pressed on the GUI, but it shouldn't run if the event is set.
A minimal version of my code:
import threading
import time
GPIO.setup(18, GPIO.IN, pull_up_down=GPIO.PUD.UP)
global run = 0
class Pump_Thread(threading.Thread):
def __init__(self, interval=0.5):
super(Pump_Thread, self).__init__()
self._stop_event = threading.Event()
self.interval = interval
Pump_threads = threading.Thread(target=self.run, daemon=True)
Pump_threads.start()
def stop(self):
self._stop_event.set()
print("Things have stopped yo...")
def resume(self):
self._stop_event.clear()
print("And Now Things will resume")
def run(self)
while not self._stop_event.is_set():
if (run == 1):
#doing some stuff when bit set#
print("Yeah Yeah, I'm running")
class Page1(Page):
def __init__(self, *args, **kwargs):
#Some Initializing methods to load buttons and graphics
self.start_btn=tk.Button(self,height=32,width =80,command=self.Start)
self.start_btn.place(x=50, y=50)
self.reset_btn=tk.Button(self,height=32,width =80,command=self.Reset)
self.reset_btn.place(x=50, y=80)
def Start(self):
global run
run = 1 #<------Set Bit for code to run
def Reset(self):
d = Pump_Thread()
d.resume() #<-----Run Method inside thread
class Monitor_Thread(threading.Thread):
def __init__(self, interval=0.5):
self.interval = interval
Monitor_Threads = threading.Thread(target=self.Monitor_Thread, daemon=True)
Monitor_Threads.start()
def run(self)
while True:
if Condition == True:
d = Pump_Thread()
d.stop() #<-----Run Method inside thread
class Interrupt_Class(page):
def Input(self):
d = Pump_Thread()
d.stop() #<-----Run Method inside thread
GPIO.add_event_detect(18, GPIO.FALLING, callback=Input, bouncetime=300)
class MainView(tk.Frame):
def __init__(self, *args, **kwargs):
tk.Frame.__init__(self, *args, *kwargs)
super().__init__()
p1 = Page1(self) #Other Pages Added here with navigation buttons to raise the page to the front of the GUI#
p1.show()
if __name__ == "__main__":
root = tk.TK()
main = MainView(root)
main.pack(side="top", fill="both", expand=True)
root.wm_geometry("400x400")
root.attributes("-fullscreen", False)
Thread1 = Pump_Thread()
Thread2 = Monitor_Thread()
root.mainloop()
When the interrupt is triggered, "Things have stopped yo..." is printed, so the method is called, but the process still starts when the GUI Button pressed, meaning the event didn't set. What would be the reason for this?
You seem to have many mistakes piled on top of one another.
You need to look at the many examples on stackoverflow of how to create and manage threads.
You might try something like this:
class Pump_Thread(threading.Thread):
def __init__(self, interval=0.5):
super().__init__()
self._stop_event = threading.Event()
self.interval = interval
def stop(self):
self._stop_event.set()
print("Things have stopped yo...")
def resume(self):
self._stop_event.clear()
print("And Now Things will resume")
def run(self)
while not self._stop_event.is_set():
print("Yeah Yeah, I'm running")
# other parts elided
if __name__ == "__main__":
root = tk.TK()
main = MainView(root)
Thread1 = Pump_Thread()
Thread1.start()
but now all other places which need to start and stop Pump_Thread need access to Thread1. So you should pass this into objects that are created.
class Interrupt_Class(page):
def __init__(self, pump):
self.pump = pump
def Input(self):
self.pump.stop() #<-----Run Method inside thread
ic = Interrupt_Class(Thread1)
You are calling:
d = Pump_Thread()
each time before calling: d.stop() which only makes that thread stop.
How is Pump_Thread instantiated and where do you store the reference to it?
In fact, you don't show how any of your classes are instantiated.

Tkinter text entry with pyHook hangs GUI window

I have a Tkinter GUI application that I need to enter text in. I cannot assume that the application will have focus, so I implemented pyHook, keylogger-style.
When the GUI window does not have focus, text entry works just fine and the StringVar updates correctly. When the GUI window does have focus and I try to enter text, the whole thing crashes.
i.e., if I click on the console window or anything else after launching the program, text entry works. If I try entering text immediately (the GUI starts with focus), or I refocus the window at any point and enter text, it crashes.
What's going on?
Below is a minimal complete verifiable example to demonstrate what I mean:
from Tkinter import *
import threading
import time
try:
import pythoncom, pyHook
except ImportError:
print 'The pythoncom or pyHook modules are not installed.'
# main gui box
class TestingGUI:
def __init__(self, root):
self.root = root
self.root.title('TestingGUI')
self.search = StringVar()
self.searchbox = Label(root, textvariable=self.search)
self.searchbox.grid()
def ButtonPress(self, scancode, ascii):
self.search.set(ascii)
root = Tk()
TestingGUI = TestingGUI(root)
def keypressed(event):
key = chr(event.Ascii)
threading.Thread(target=TestingGUI.ButtonPress, args=(event.ScanCode,key)).start()
return True
def startlogger():
obj = pyHook.HookManager()
obj.KeyDown = keypressed
obj.HookKeyboard()
pythoncom.PumpMessages()
# need this to run at the same time
logger = threading.Thread(target=startlogger)
# quits on main program exit
logger.daemon = True
logger.start()
# main gui loop
root.mainloop()
I modified the source code given in the question (and the other one) so that the pyHook
related callback function sends keyboard event related data to a
queue. The way the GUI object is notified about the event may look
needlessly complicated. Trying to call root.event_generate in
keypressed seemed to hang. Also the set method of
threading.Event seemed to cause trouble when called in
keypressed.
The context where keypressed is called, is probably behind the
trouble.
from Tkinter import *
import threading
import pythoncom, pyHook
from multiprocessing import Pipe
import Queue
import functools
class TestingGUI:
def __init__(self, root, queue, quitfun):
self.root = root
self.root.title('TestingGUI')
self.queue = queue
self.quitfun = quitfun
self.button = Button(root, text="Withdraw", command=self.hide)
self.button.grid()
self.search = StringVar()
self.searchbox = Label(root, textvariable=self.search)
self.searchbox.grid()
self.root.bind('<<pyHookKeyDown>>', self.on_pyhook)
self.root.protocol("WM_DELETE_WINDOW", self.on_quit)
self.hiding = False
def hide(self):
if not self.hiding:
print 'hiding'
self.root.withdraw()
# instead of time.sleep + self.root.deiconify()
self.root.after(2000, self.unhide)
self.hiding = True
def unhide(self):
self.root.deiconify()
self.hiding = False
def on_quit(self):
self.quitfun()
self.root.destroy()
def on_pyhook(self, event):
if not queue.empty():
scancode, ascii = queue.get()
print scancode, ascii
if scancode == 82:
self.hide()
self.search.set(ascii)
root = Tk()
pread, pwrite = Pipe(duplex=False)
queue = Queue.Queue()
def quitfun():
pwrite.send('quit')
TestingGUI = TestingGUI(root, queue, quitfun)
def hook_loop(root, pipe):
while 1:
msg = pipe.recv()
if type(msg) is str and msg == 'quit':
print 'exiting hook_loop'
break
root.event_generate('<<pyHookKeyDown>>', when='tail')
# functools.partial puts arguments in this order
def keypressed(pipe, queue, event):
queue.put((event.ScanCode, chr(event.Ascii)))
pipe.send(1)
return True
t = threading.Thread(target=hook_loop, args=(root, pread))
t.start()
hm = pyHook.HookManager()
hm.HookKeyboard()
hm.KeyDown = functools.partial(keypressed, pwrite, queue)
try:
root.mainloop()
except KeyboardInterrupt:
quit_event.set()

Python&PyGTK: Transport data to threading

I would like to transfer data to threading class, but I can't get what is wrong. The code below is from this question and I changed it a little.
This is a code:
import gtk, gobject, threading, time
gobject.threads_init()
class T(threading.Thread):
pause = threading.Event()
stop = False
def start(self, data, *args):
super(T, self).start()
def run(self, data):
while not self.stop:
self.pause.wait()
gobject.idle_add(lambda *a : self.rungui(data))
time.sleep(0.1)
def rungui(self, data):
print "printed"
print data
thread = T()
class Start:
def toggle_thread(self, data=None, *args):
if not thread.is_alive():
thread.start(data)
thread.pause.set()
self.button.set_label('Pause Thread')
return
if thread.pause.is_set():
thread.pause.clear()
self.button.set_label('Resume Thread')
else:
thread.pause.set()
self.button.set_label('Pause Thread')
def __init__(self):
thread = T()
window = gtk.Window()
self.button = gtk.ToggleButton('Start Thread')
data = 3
self.button.connect('toggled', lambda *a : self.start(data), None)
window.add(self.button)
self.button.show()
window.show()
def start(self, data=None):
self.toggle_thread(data)
def main(self):
gtk.main()
if __name__ == "__main__":
start = Start()
start.main()
What do I have to correct to get threading fully working?
Don`t work with gtk out of gui thread. That about:
gobject.idle_add(self.rungui)
Example at your link work fine, but need system kill command for termination.
And super() can`t bring arguments to run() function.
My threads initialization looks like this:
class VirtService(threading.Thread):
def __init__(self, queue):
threading.Thread.__init__(self)
self.queue = queue
def thread_loop(self):
while self.queue.qsize():
data_command = self.queue_get()
...
queue = Queue()
if __name__ == '__main__':
gobject.threads_init()
vs = VirtService(queue)
And you may use Queue for data translation to both directions. You may use also queue for command. In non-graphical thread create c++ poll() analog through Queue.qet(), and in gui thread queue.put()

Categories