Reputation: 495
I have a two tkinter canvas which needs to change color based on the data which I receive from other module. basically 0,1 Ex: canvas1 to black and canvas2 to green if 1 is received and viceversa if 0 is received.
I have used multiprocessing queue technique to receive the data, but when I tried to apply the changes it is not updating? I assume there is something with self
.
Here is my code snippet: main.py
import multiprocessing
from udpsocket import *
from ui import *
if __name__ == "__main__":
queue = multiprocessing.Queue()
ui = multiprocessing.Process(target=uiMain, args=(queue,))
ui.daemon = True
ui.start()
udpMain(queue)
udpsocket.py:
import time
import struct
import socket
import ui
MYPORT = 51506
MYGROUP_4 = '225.0.0.1'
MYTTL = 1 # Increase to reach other networks
def udpMain(queue):
app = udpsocket(queue)
class udpsocket():
def __init__(self,queue):
print('UDP Socket started')
group = MYGROUP_4
self.receiver('225.0.0.1',queue)
def receiver(self,group,queue):
print('Receiver')
addrinfo = socket.getaddrinfo(group, None)[0]
# Create a socket
s = socket.socket(addrinfo[0], socket.SOCK_DGRAM)
#.... reuse address, binding, add membership
# loop, send data to ui
while True:
data, sender = s.recvfrom(1500)
while data[-1:] == '\0': data = data[:-1] # Strip trailing \0's
print (str(sender) + ' ' + str(ord(data)))
queue.put(ord(data))
ui.do_something(queue)
ui.py:
from tkinter import *
from tkinter import ttk
import multiprocessing
def uiMain(queue):
app = MainWindow()
app.mainloop()
class MainWindow(Frame):
def __init__(self):
Frame.__init__(self)
self.master.title("Test")
self.master.minsize(330, 400)
self.grid(sticky=E+W+N+S)
modeFrame = Frame(self)
modeFrame.pack(side="top", fill="x")
self.canvas1 = Canvas(modeFrame, height=25, width=25)
self.canvas1.create_oval(5, 5, 20, 20, fill="black", tags="SetupLight")
self.canvas1.pack(side="left")
self.canvas2 = Canvas(modeFrame, height=25, width=25)
self.canvas2.create_oval(5, 5, 20, 20, fill="black", tags="RunLight")
self.canvas2.pack(side="left")
def changeLight(self,value):
print('change light ' + str(value))
if(value):
self.canvas1.itemconfig("SetupLight", fill="black")
self.canvas2.itemconfig("RunLight", fill="green")
else:
self.canvas1.itemconfig("SetupLight", fill="green")
self.canvas2.itemconfig("RunLight", fill="black")
def do_something(queue):
t = MainWindow()
MainWindow.changeLight(t,queue.get()) #is this way of calling is correct??
Note: I tried modifying modeFrame
to self
while creating Canvas
, but there is nothing could happen
I understood from the below link tkinter canvas not updating color that I was creating MainWindow() again and again and that is the reason that canvas was not changing the color. I need an implementation which could help me in changing the colors with the usecase scenerio
Upvotes: 0
Views: 394
Reputation: 2202
As you mentioned yourself already, you are creating instances of MainWindow
class all over in your do_something
.
Second, the do_something
function call is a bit strange for me.
I would prefer
def do_something(queue):
t = MainWindow()
t.changeLight(queue.get())
It might be arguable if this is a more pythonic way or not but I think in almost all tutorials, how-to's and example code you will see it like I mentioned it.
Last time I implemented something like this, I took a different approach.
I started Threads from the GUI, passed a queue to it and let the threads handle traffic.
The GUI updated cyclic (every 100ms) where it checked the queue for items and based on what was inside the queue it updated the GUI.
The threads where started again every time the update finished. (The application itself was a Session Watchdog for a Server including localization of the Users inside the threads)
So as some implementation Advice I would start like the following:
import Tkinter as tk
import Queue
class MainWindow(tk.Frame):
""" This is the GUI
It starts threads and updates the GUI
this is needed as tkinter GUI updates inside the Main Thread
"""
def __init__(self, *args, **kwargs):
# Here some initial action takes place
# ...
self.queue = Queue()
self.start_threads()
self.__running = True
self.update_gui()
def start_threads(self):
""" Here we start the threads
for ease of reading only one right now
thread_handler_class is a class performing
tasks and appending results to a queue
"""
thread = thread_handler_class(self.queue)
def update_gui(self):
""" Update the UI with Information from the queue """
if not self.queue.empty():
# we need to check for emptiness first
# otherwise we get exceptions if empty
while self.queue.qsize() > 0:
# handle the data
# self.queue.get() automatically reduces
# qsize return value next round so if no
# new elements are added to the queue,
# it will go to zero
data = self.queue.get()
# do stuff here
# ...
# Set up the cyclic task
if self.__running:
self.after(100, self.update_gui)
# if neccessary we can also restart the threads
# if they have a determined runtime
# self.after(100, self.start_threads)
if __name__ == "__main__":
APP = MainWindow()
APP.mainloop()
Upvotes: 1