Posts: 9
Threads: 2
Joined: Jun 2020
Hello,
I have created a Tkinter GUI script which monitor a PLC unit's I/O's in real time.
Therefore , i created a Modbus TCP/IP communication to the PLC client so i receive the digital input register as a list of 30 elements.
In order to monitor those digital inputs and update their status i created LED symbols that turn green if the status is True and turn red if the status changes to False.
In order to change the status (acts like real time) i used .after() methods inside multiple functions that executes them every 0.5 second
The problem takes place when i adds more Led widgets like that which bound to one of the input register.
I think that the multiple usage of .after() method caused the problem , so as many bound widgets as i add , the more the gui gets slower and even stuck at some point.
Here is an example of a function with that method that i wrote:
class dig_in_rec():
def __init__(self, canvas , index, x1,y1,x2,y2 ,label, img1 , img2):
self.canvas = canvas
self.index = index
self.x1 = x1
self.y1 = y1
self.x2 = x2
self.y2 = y2
self.img1 = img1
self.img2 = img2
self.label=label
def rec_create(self):
in_arr = md.rd_dig_in(0, 30)
self.label.place(x=self.x1, y=self.y1)
if in_arr[self.index] == True:
self.label.config(image=self.img1)
elif in_arr[self.index] == False:
self.label.config(image=self.img2)
self.canvas.after(500, self.rec_create)
Posts: 2,166
Threads: 35
Joined: Sep 2016
Does any of these methods have loops, pauses, long-running code, that would block the GUI main loop? if so they will need to run in a separate thread.
Posts: 6,195
Threads: 16
Joined: Feb 2020
Are you calling md.rd_dig_in for each lamp? If you have 30 digital inputs that is 30 Modbus reads just to update each lamp once. Why not read the digital inputs once and then update all the lamps at the same time?
Posts: 9
Threads: 2
Joined: Jun 2020
how should i update them at once?
Posts: 6,195
Threads: 16
Joined: Feb 2020
I mean read all the input values and then update all the lamps. I think you are reading all the input values and update one lamp, repeat for each lamp. It doesn't take long to set 30 images, so the poor performance must be from reading the Modbus.
You should also have your lamps remember their state and only change the image when the associated digital input value changes. Not only will that be faster, but it gets rid of any redraw flicker.
class DigitalInputLamp():
def __init__(self, x1, y1, x2, y2, label, img1 , img2):
self.x1 = x1
self.y1 = y1
self.x2 = x2
self.y2 = y2
# Are images same for all lamps? Make a class variable?
self.images = [img1, img2]
self.label = label
# Placing widget should probably happen outside the class
# when the widget is created
self.label.place(x=self.x1, y=self.y1)
self.state = 0
self.label.config(image=self.images[self.state))
def show_state(self, state):
if state != self.state:
self.state = state
self.label.config(image=self.images[self.state))
def update_lamps(lamps, period, canvas):
digital_inputs = md.rd_dig_in(0, len(lamps))
for i, lamp in enumerate(lamps)
lamp.show_state(digital_inputs[i])
canvas.after(period, lamps)
# Make a bunch of lamps and start updating
lamps = []
for I in range(number_of_lamps)
lamps.append(DigitalInputLamp…
...
update_lamps(lamps, 500, canvas)
Posts: 9
Threads: 2
Joined: Jun 2020
Thanks for the response ill try that
Posts: 9
Threads: 2
Joined: Jun 2020
Jun-15-2020, 11:49 AM
(This post was last modified: Jun-15-2020, 11:49 AM by Omer.)
When i run this code:
import Modbus_PLC as md
from tkinter import *
class DigitalInputLamp():
def __init__(self, x1, y1, label, img1, img2):
self.x1 = x1
self.y1 = y1
# Are images same for all lamps? Make a class variable?
self.images = [img1, img2]
self.label = label
# Placing widget should probably happen outside the class
# when the widget is created
self.label.place(x=self.x1, y=self.y1)
self.state = 0
self.label.config(image=self.images[self.state])
def show_state(self, state):
if state != self.state:
self.state = state
self.label.config(image=self.images[self.state])
#############################################################
def Main_Page():
root1 = Tk()
root1.title("GUI App")
canvas44 = Canvas(root1, height=512, width=898)
canvas44.pack()
img_butt8 = PhotoImage(file='C:\\Users\\Omer\\Desktop\\Green_led1.png')
img_butt9 = PhotoImage(file='C:\\Users\\Omer\\Desktop\\Red_led1.png')
label1 = Label(canvas44)
label2 = Label(canvas44)
label3 = Label(canvas44)
label4 = Label(canvas44)
label5 = Label(canvas44)
label_list=[label1,label2,label3,label4,label5]
# Make a bunch of lamps and start updating
lamps = [DigitalInputLamp(i*80,10,label_list[i], img_butt8,img_butt9) for i in range(5)]
update_lamps(lamps, 500, canvas44)
print(lamps)
root1.mainloop()
def update_lamps(lamps, period, canvas):
digital_inputs = md.rd_dig_in(0, len(lamps))
for i, lamp in enumerate(lamps):
lamp.show_state(digital_inputs[i])
canvas.after(period, lamps)
Main_Page() Main_Page()[/python]
I get lamp.show_state as NoneType object:
Output: Traceback (most recent call last):
File "C:/Users/Omer/test.py", line 57, in <module>
Main_Page()
File "C:/Users/Omer/test.py", line 45, in Main_Page
update_lamps(lamps, 500, canvas44)
File "C:/Users/Omer/test.py", line 53, in update_lamps
lamp.show_state(digital_inputs[i])
TypeError: 'NoneType' object is not subscriptable
Posts: 2,166
Threads: 35
Joined: Sep 2016
The line
digital_inputs = md.rd_dig_in(0, len(lamps)) is returning None not the iterable you are expecting.
Posts: 9
Threads: 2
Joined: Jun 2020
yes , i just dont understand why...it supposed to read digital input through modbus tcp/ip
Posts: 6,195
Threads: 16
Joined: Feb 2020
What does the rd_dig_in code look like? I cannot find the Modbus_PLC package.
|