kar
kar

Reputation: 495

tkinter canvas color not changing

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

Answers (1)

R4PH43L
R4PH43L

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

Related Questions