Tkinter GUI Freeze even after running background process in separate Thread - python

UI is Freezing even after running the background process in separate Thread.
Db2Input = c1.fetchone() is taking few seconds to fetch the data from table as you can see it in the screenshot and I have recursive call to fetchData.
I thought Separate Thread won't have any interference with main Thread since I am not using any GUI element in FetchData() Function. Any suggestions...
from tkinter import *
import pyodbc
import connect
from datetime import datetime
import threading as th
class appLayout(Tk):
def __init__(self):
Tk.__init__(self)
self.masterPane = PanedWindow(self,bd=0,sashwidth =0 )
self.leftPane = Frame(self.masterPane,bg = 'orange',height = 400,width = 200,relief = 'raised')
self.masterPane.add(self.leftPane)
self.rightPane = Frame(self.masterPane,bg = 'blue',height =400,width =500)
self.masterPane.add(self.rightPane)
self.masterPane.pack(fill = 'both',expand = True)
self.AddButton()
def on_enter(self,e):
e.widget['background'] = 'green'
def on_leave(self,e):
e.widget['background'] = 'orange'
def AddButton(self):
btn1 = Button(self.leftPane, text = 'Dashboard ', bg ='orange',relief ='flat',
bd =0,padx =10,pady=7,font ='Arial',fg='white',activebackground = 'green',command =self.SubmitThread)
btn2 = Button(self.leftPane, text = 'Dashboard ', bg ='orange',relief ='flat',
bd =0,padx =10,pady=7,font ='Arial',fg='white')
btn3 = Button(self.leftPane, text = 'Dashboard ', bg ='orange',relief ='flat',
bd =0,padx =10,pady=7,font ='Arial',fg='white')
btn1.grid(row =2,column =0,sticky ='nwe',ipady=5)
btn2.grid(row =3,column =0,sticky ='nwe',ipady=5)
btn3.grid(row =4,column =0,sticky ='nwe',ipady=5)
btn1.bind("<Enter>", self.on_enter)
btn1.bind("<Leave>", self.on_leave)
btn2.bind("<Enter>", self.on_enter)
btn2.bind("<Leave>", self.on_leave)
btn3.bind("<Enter>", self.on_enter)
btn3.bind("<Leave>", self.on_leave)
def SubmitThread(self):
th.Thread(target = self.fetchData).start()
def fetchData(self):
connection = connect.connect('ADMIN','USER','Password')
AS400Connection = connection.ToAS400Database()
c1 = AS400Connection.cursor()
sqlstatement = ("SELECT * FROM (SELECT * FROM TABLE(QSYS2.JOB_INFO(JOB_STATUS_FILTER => '*OUTQ',\
JOB_USER_FILTER =>'{}') )) WHERE JOB_TYPE ='BCH' AND JOB_NAME LIKE '%{}%'\
AND JOB_E00002 > '{}' ORDER BY JOB_E00002".format('USER','TEST','2021-06-11-10.25.00'))
print("Before Execution",datetime.now())
c1.execute(sqlstatement)
print("Before fetch",datetime.now())
**Db2Input = c1.fetchone()**
print("After fetch",datetime.now())
c1.close()
self.after(3000,self.fetchData)
app = appLayout()
app.mainloop()
enter image description here

Related

Threading tkinter serial reading function

I'm making an application that reads serial data coming from the sensors on an arduino board. Problems arise when trying to use the matplotlib.animation class to make a live graph of said data. The GUI widgets become unresponsive when the plotting is taking place. As far as i've understood, making the serial reading process run on its own thread could potentially solve the issue. I'm having trouble understanding how this could be made so that it is compatible with the FuncAnimation-subclass.
def read_serial_data(port, bauds=9600):
s = serial.Serial(port, bauds)
line = s.readline()[0:-2]
return line
def getPorts():
return [port.device for port in serial.tools.list_ports.comports(include_links=False)]
class GUI():
def __init__(self):
self.root = Tk.Tk()
self._fig = plt.figure()
self.root.title('Measurement Dashboard')
self.root.state('normal')
self.root.config(background='#ffffff')
self._canvas = FigureCanvasTkAgg(self._fig, self.root)
self._canvas.get_tk_widget().grid(column = 1, row = 1)
self._canvas.draw()
self._animate = None
self._ax = self._fig.add_subplot(111)
self._ax.yaxis.grid(True, color = 'black', linestyle='--')
self._ax.xaxis.grid(True, color = 'black', linestyle='--')
self._ax.set_xlabel('time')
self._ax.set_ylabel('CO2')
self.filename = Tk.StringVar()
self.entry = ttk.Entry(self.root, textvariable = self.filename)
self.entry.grid(column = 2, row = 2)
self.info_var = Tk.StringVar()
self.info_entry = ttk.Entry(self.root, textvariable = self.info_var)
self.info_entry.grid(column = 2, row = 3)
self.port = Tk.StringVar()
self.ports = getPorts()
self._cb = ttk.Combobox(self.root, textvariable= self.port, values = self.ports)
self._cb.grid(column = 2, row = 1)
self.start_button = Tk.Button(self.root, text = 'Start', command = self.plot)
self.start_button.grid(column = 1, row = 2)
self.save_button = Tk.Button(self.root, text = 'Save info', command = self.save_info)
self.save_button.grid(column = 2, row = 4)
def save_info(self):
global info
info = self.info_var.get()
def start(self):
self.root.mainloop()
def plot(self):
if self._animate is None:
self.scope = Scope(self._ax, self.filename.get())
self._canvas.draw_idle()
self._animate = animation.FuncAnimation(self._fig, self.scope.animate, frames = self.update, interval=2000, blit=False)
def update(self):
line = read_serial_data(self.port.get())
data = line.decode('utf-8')
yield data
time = datetime.now()
duration = time - start_time
measurement = {'time': time, 'dur': duration.seconds, 'CO2': data, 'info': info}
write_csv_line(self.filename.get(), measurement)
self.root.after(10000, self.update)
if __name__ == "__main__":
gui = GUI()
gui.start()
thread = Thread(target=read_serial_data,args=(gui.port,))
thread.start()
You don't really need another thread but can do this using non-blocking IO on th eserial port and make use of the Tkinter after call to manage the poll interval. pyserial provices inWaiting to test if the device has input waiting to be read. If there are bytes waiting, read just those.
Here is an example reader class to read lines from a serial device and post them to the application handler method once a complete line is read.
class Reader(Serial):
def __init__(self, *args, **kwargs):
self.buffer = ''
super(Reader, self).__init__(*args, **kwargs)
def monitor(self, w, interval=10):
n = self.inWaiting()
if n != 0:
data = self.read(n).decode('ascii')
self.buffer = self.buffer + data
ndx = self.buffer.find("\n")
if ndx != -1:
line = self.buffer[:ndx+1]
self.buffer = self.buffer[ndx+1:]
w.after_idle(lambda: w.parse_input(line))
w.after(interval, lambda: self.monitor(w, interval))
Used like:
app = <tkinter application class instance>
reader = Reader(port, baudrate, timeout=0)
reader.flushInput()
reader.monitor(app, 10)
app.mainloop()
In this case, it will call a parse_input method on the app instance whenever a line is read (delimited by a newline).
If you decide to use a thread, then you need a Queue to pass the data to the Tkinter UI thread and must ensure you don't call Tkinter methods from the worker thread.

Python: parallel processing and tkinter

I am trying to implement a piano game. The rules are simple: a note is played and after 5 seconds the answer is shown. The problem is that I want the user to be able to play the notes while the program waits for those 5 seconds. Right now the program creates a new GUI window when the the gamemode's process starts.
Gamemode class:
from multiprocessing import Process
import random
import time
class Practice(Process):
def __init__(self, keys, rounds = 5):
super(Process, self).__init__()
self.rounds = rounds
self.keys = keys
def run(self):
for i in range(3):
answer = self.__random_key()
answer.play()
time.sleep(3)
print("woke up")
self.show_answer()
def show_answer(self, answer):
print(f"Answer is: {answer.name}")
def __random_key(self):
return self.keys[random.randint(0, len(self.keys) - 1)]
piano key class:
import asyncio
import copy
import time
from tkinter import Button
from pygame import mixer
class PianoKey():
def __init__(self, master, note = None, octave = None, color = None):
self.master = master
self.note = note
self.color = color
self.octave = octave
self.name = f"{note}{octave}"
self.button = Button(self.master, bg = color, command = lambda : self.play())
def get_piano_button(self):
return self.button
def empty_copy(self):
button = self.button
self.button = None
result = copy.copy(self)
self.button = button
def change_color(self, color):
self.button.config(bg = color)
def play(self):
path = "PATH_OF_NOTES_FOLDER"
print(f"played {self.name}")
# mixer.music.load("B3.mp3")
# mixer.music.play()
Piano class and subclasses:
from core.GUI.gui_piece import GUIPiece, GUITypes
from core.GUI.piano_key import PianoKey
from core.game_modes.Practice import Practice
class Piano(GUIPiece):
def __init__(self, master, relx, rely, relwidth, relheight, background = "White", octaves = 3):
super().__init__(master, relx, rely, relwidth, relheight, GUITypes.FRAME, background)
self.keys = []
self.initiate_keys(octaves)
self.mode = Practice(self.keys)
def get_piano(self):
return self.gui
def initiate_keys(self, octaves):
white_width = 1/(octaves * 7)
self.initiate_key(octaves=octaves, keys=["C", "D", "E", "F", "G", "A", "B"], color="White", positions=[0,1,2,3,4,5,6], width = white_width, relxwidth = white_width, minus_starting = 0, relh = 1)
black_width = white_width * 0.7
self.initiate_key(octaves=octaves, keys=["Cb", "Db", "Fb", "Gb", "Ab"], color="Black", positions=[1,2,4,5,6], width = black_width, relxwidth = white_width, minus_starting= black_width/2, relh = 0.6)
def start_practice(self):
self.mode.start()
# t = threading.Thread(target=self.mode.play())
# print("now going in for loop")
# t.start()
def empty_keys_copy(self):
return [key.empty_copy() for key in self.keys]
def initiate_key(self, octaves, keys, color, positions, width, relxwidth, minus_starting, relh):
for o in range(octaves):
octave = o * 7
for index in range(len(positions)):
relx = relxwidth * (octave + positions[index])- minus_starting
key = PianoKey(self.get_piano(), keys[index], o, color)
self.keys.append(key)
key.get_piano_button().place(relx=relx, rely=0, relwidth= width, relheight = relh)
def show_answer(self):
print(f"Answer is: {self.answer.name}")
self.answer.change_color('Red')
class GUITypes(Enum):
LABEL = 0,
FRAME = 1,
CHECKBUTTON = 2,
BUTTON = 3
class GUIPiece():
def __init__(self, master, relx, rely, relwidth, relheight, type, background = 'Grey', parent = None, text = ""):
self.master = master
self.relx = relx
self.rely = rely
self.relwidth = relwidth
self.relheight = relheight
if type == GUITypes.LABEL:
self.gui = Label(bg = background)
elif type == GUITypes.FRAME:
self.gui = Frame(bg=background)
elif type == GUITypes.CHECKBUTTON:
self.gui = Checkbutton(bg=background)
elif type == GUITypes.BUTTON:
self.gui = Button(bg=background)
else:
raise AttributeError("Please select a GUIType.")
if text != "":
self.set_text(text)
#adapt the relative sizes
if parent != None:
self.relx *= parent.relwidth
self.rely *= parent.relheight
self.relwidth = relwidth * parent.relwidth
self.relheight = relheight * parent.relheight
def get_gui(self):
return self.gui
def set_text(self, text):
self.get_gui().config(text = text)
def set_gui(self, gui):
self.gui = gui
def place(self):
self.gui.place(relx = self.relx, rely = self.rely, relwidth = self.relwidth, relheight = self.relheight)
def get_placements(self):
return self.relx, self.rely, self.relwidth, self.relheight
class GUIButton(GUIPiece):
def __init__(self, master, relx, rely, relwidth, relheight, background = "White", parent = None, text = ""):
super().__init__(master, relx, rely, relwidth, relheight, GUITypes.BUTTON, background, parent = parent, text = text)
main:
from tkinterdnd2 import *
from tkinter import *
from core.GUI.pian import Piano
from core.GUI.top_menu import TopMenu
from core.GUI.top_menu_parts.top_menu_pieces import GUIButton
from core.configurations import PIANO_RELX, PIANO_RELY, PIANO_RELWIDTH, PIANO_RELHEIGHT, TOPMENU_RELHEIGHT
from core.game_modes.Practice import Practice
root = Tk()
# set window title
root.wm_title("Tkinter window")
root.geometry("1600x700")
p = Piano(root, relx=PIANO_RELX, rely=PIANO_RELY, relwidth=PIANO_RELWIDTH, relheight=PIANO_RELHEIGHT, background="White")
p.place()
# t = TopMenu(root, relx=0, rely=0, relwidth=1, relheight=TOPMENU_RELHEIGHT, background="Pink")
start = GUIButton(root, relx=0, rely=0, relwidth=1, relheight=TOPMENU_RELHEIGHT, text="practice")
start.place()
# t.place()
mode = Practice(p.keys)
start.get_gui().config(command = lambda: p.start_practice())
root.mainloop()
Use the root.after() method. It can delay like time.sleep() but can also call a function when the timeout finishes. In this way, the user can play the piano notes while the program is waiting. Here is a tutorial regarding root.after().

OpenCV Video with Tkinter and threading Crash

I'm trying to make a multithreaded program with Python, OpenCV, and Tkinter.
My program has some general point.
Load Video from file
Create 2 thread
1st thread to fetch frames from capture object and put it to python Queue
2nd thread to get the frames from Queue
At last, if possible, start and stop the capture object
However, my script seems to behave weirdly. Sometimes it can finish playing video until the end, but sometimes it also crash at some point of the video. Here is what I've got so far.
from Tkinter import Tk, Text
from Tkinter import PhotoImage
from ttk import Frame, Scrollbar, Button, Label
from PIL import Image, ImageTk
import cv
import time
import Queue
import threading
def writeToLog(log, msg):
numlines = log.index('end - 1 line').split('.')[0]
if log.index('end-1c')!='1.0': log.insert('end', '\n')
log.insert('end', msg)
log.see('end')
def GetIplImageMode(img):
orientation = 1 if img.origin == 0 else -1
mode_list = {(1, cv.IPL_DEPTH_8U) : ("L", "L", 1),\
(3, cv.IPL_DEPTH_8U) : ("BGR", "RGB", 3),\
(1, cv.IPL_DEPTH_32F) : ("F", "F", 4)}
key = (img.nChannels, img.depth)
modes = mode_list[key]
return [modes[0], modes[1], orientation]
def IplImage2PIL(img, mode):
return Image.fromstring(mode[1], (img.width, img.height),\
img.tostring(), "raw", mode[0],\
img.width * img.channels,\
mode[2])
def ResizePILImage(pil, width = 260, height = 180):
return pil.resize((width, height), Image.ANTIALIAS)
def PIL2TkImage(pil):
return ImageTk.PhotoImage(pil)
def setImageLabelFromIplImage(label, img_ipl):
mode = GetIplImageMode(img_ipl)
img_pil = IplImage2PIL(img_ipl, mode)
img_resized = ResizePILImage(img_pil)
img_tk = PIL2TkImage(img_resized)
label.configure(image = img_tk)
label.image = img_tk
def setImageLabelFromFile(label, szFileName):
img_ipl = cv.LoadImage(szFileName)
setImageLabelFromIplImage(label, img_ipl)
def mat_from_ipl(ipl):
return cv.GetMat(ipl)
def ipl_from_mat(mat):
ipl = cv.CreateImageHeader((mat.width, mat.height),\
cv.IPL_DEPTH_8U, mat.channels)
cv.SetData(ipl, mat.tostring())
return ipl
class asdf(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.pack(fill='both', expand=True)
self.parent = parent
self.variables()
self.ui()
def variables(self):
self.ctr = 0
self.fps = 0
self.video = None
self.image = None
self.putProc = None
self.getProc = None
self.isRunning = False
self.queue = Queue.Queue()
def ui(self):
f1 = Frame(self)
frm1 = Frame(f1)
self.lbl1 = Label(frm1, image=None)
setImageLabelFromFile(self.lbl1, '../image.bmp')
self.txt1 = Text(frm1, width=30, height=8)
sb1 = Scrollbar(frm1, orient='vertical', command=self.txt1.yview)
self.txt1.configure(yscrollcommand = sb1.set)
self.lbl1.pack()
self.txt1.pack(side='left')
sb1.pack(side='left', fill='y')
frm1.pack(side='left')
frm2 = Frame(f1)
self.lbl2 = Label(frm2, image=None)
setImageLabelFromFile(self.lbl2, '../image.bmp')
self.txt2 = Text(frm2, width=30, height=8)
sb2 = Scrollbar(frm2, orient='vertical', command=self.txt2.yview)
self.txt2.configure(yscrollcommand = sb2.set)
self.lbl2.pack()
self.txt2.pack(side='left')
sb2.pack(side='left', fill='y')
frm2.pack(side='left')
f1.pack()
f2 = Frame(self)
Button(f2, text='Run', command=self.run).pack(side='left')
Button(f2, text='Stop', command=self.stop).pack(side='left')
f2.pack()
def put_to_queue(self):
while self.isRunning:
self.ctr = self.ctr + 1
self.image = cv.QueryFrame(self.video)
time.sleep(1 / self.fps)
try:
writeToLog(self.txt1, '\nPut to queue .. %d' % (self.ctr))
temp1 = cv.CloneImage(self.image)
setImageLabelFromIplImage(self.lbl1, temp1)
temp2 = mat_from_ipl(temp1)
self.queue.put([self.ctr, temp2])
except:
writeToLog(self.txt1, '\nReach end of video ..')
break
def get_from_queue(self):
while self.isRunning:
from_queue = self.queue.get()
self.ctr_fr = from_queue[0]
if self.ctr_fr == self.ctr: time.sleep(30 / self.fps)
temp1 = ipl_from_mat(from_queue[1])
setImageLabelFromIplImage(self.lbl2, temp1)
writeToLog(self.txt2, '\nGet from queue .. %d' % (self.ctr_fr))
time.sleep(1 / self.fps)
def run(self):
self.isRunning = True
self.video = cv.CreateFileCapture('../video.avi')
self.fps = cv.GetCaptureProperty(self.video, cv.CV_CAP_PROP_FPS)
writeToLog(self.txt1, '\nStart put_queue ..')
self.putProc = threading.Thread(target=self.put_to_queue)
self.putProc.start()
time.sleep(1)
writeToLog(self.txt2, '\nStart get_queue ..')
self.getProc = threading.Thread(target=self.get_from_queue)
self.getProc.start()
def stop(self):
self.isRunning = False
if self.putProc.isAlive():
self.putProc._Thread__stop()
writeToLog(self.txt1, '\nputProc still alive, stopping ..')
self.putProc = None
if self.getProc.isAlive():
self.getProc._Thread__stop()
writeToLog(self.txt2, '\ngetProc still alive, stopping ..')
self.getProc = None
self.ctr_fr = 0
self.ctr = 0
if __name__ == '__main__':
root = Tk()
c = asdf(root)
root.mainloop()
Am I doing it wrong?
Any ideas will be very appreciated.
Thanks

How to use transform from PIL in Python

That's my code and I get the error message:
... return getattr(self.tk, attr)
AttributeError: temp_pic"...
I need to program two buttons: [zoom in] and [zoom out].
If you have any better ideas for doing that, please, just say it.
I'm going to use this image to develop maps through graphs (structure)
from Tkinter import *
from PIL import Image, ImageTk, ImageDraw, ImageOps, ImageEnhance
bairro = "botafogo.jpg"
class Painel(Tk):
def __init__(self):
Tk.__init__(self) #create ui
self.zoom = Frame(self)
self.zoom.pack()
self.zin = Button(self.zoom, command = self.zoom_in, text = "Zoom In")
self.zin.pack()
self.zout = Button(self.zoom, command = self.zoom_out, text = "Zoom Out")
self.zout.pack()
self.c = Canvas(self, bd=0, highlightthickness=0, width=100, height=100)
self.c.pack(fill='both', expand=1)
self.main_pic = Image.open(bairro) #load image
self.main_pic.thumbnail((800, 600))
self.tkphoto = ImageTk.PhotoImage(self.main_pic)
self.canvasItem = self.c.create_image(0, 0, anchor='nw', image = self.tkphoto)
self.c.config(width = self.main_pic.size[0], height = self.main_pic.size[1])
self.temp = self.main_pic.copy() # 'working' image
def update_painel(self):
self.tkphoto = ImageTk.PhotoImage(self.temp_pic)
self.c.itemconfigure(self.canvasItem, image = self.tkphoto)
def zoom_in(self):
self.temp_pic = self.temp_pic.transform( ( self.temp_pic.size[0]/2,
self.temp_pic.size[0]/2
),
Image.EXTEND,
( 0, 0, self.temp_pic[0], self.temp_pic[1]
)
)
self.update_painel()
def zoom_out(self):
self.temp_pic = self.main_pic
self.update_painel()
app = Painel()
app.mainloop()
a deep-copy instruction shall read
self.temp_pic = self.main_pic.copy() # 'working' image
instead of
self.temp = self.main_pic.copy() # 'working' image**

maya python print state of checkbox?

I'm trying to get the state of a checkbox in a maya UI using python. I was wondering if someone would help me. currently when the user hits the Distribute button, it calls a function which should print the true/false state of the 'x' checkbox.
import maya.cmds as cmds
class createMyLayoutCls(object):
def __init__(self, *args):
pass
def show(self):
self.createMyLayout()
def createMyLayout(self):
#check to see if our window exists
if cmds.window('utility', exists = True):
cmds.deleteUI('utility')
# create our window
self.window = cmds.window('utility', widthHeight = (200, 200), title = 'Distribute', resizeToFitChildren=1, sizeable = False)
cmds.setParent(menu=True)
# create a main layout
mainLayout = cmds.columnLayout(w = 200, h = 200, cw = 10, rs = 8, co = ['both',2])
# X Control
self.xAxis = cmds.checkBox('X')
# Distribute Button
btnDistribute = cmds.button(label = 'Distribute', width = 200, height = 40, c = self.GetSelectedNodes)
# show window
cmds.showWindow(self.window)
def GetSelectedNodes(self,*args):
cal = cmds.checkBox(self['X'],q = True, v = True)
print cal
b_cls = createMyLayoutCls()
b_cls.show()
you need to pass the checkbox's name into the call to checkBox in GetSelectedNodes:
def GetSelectedNodes(self,*args):
cal = cmds.checkBox(self.xAxis,q = True, v = True)
print cal

Categories