This program works and displays server latency on a small canvas, but because it takes the program time to ping the server and display the ping def display():, it is not possible to drag the window class WindowDraggable():, until the subprocess has finished, and thus there is lag when dragging the window. Can this lag be resolved with mutil-threading so the window can be dragged smoothly?
from tkinter import *
from PIL import ImageTk, Image
import subprocess
import _thread
host = "141.101.115.212" #host IP address
root = Tk()
root.overrideredirect(1)
im = Image.open("image.png")
width, height = im.size
canvas = Canvas(root, width=width, height=height)
canvas.pack()
image = ImageTk.PhotoImage(file="image.png")
canvas.create_image(0, 0, image=image, anchor=NW)
text = canvas.create_text(125, 75, anchor=CENTER)
def display():
global text
#Launches 'command' windowless and waits until finished; finds ping
suinfo = subprocess.STARTUPINFO()
suinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
x = subprocess.Popen(["ping.exe", "141.101.115.212"], stdout=subprocess.PIPE, startupinfo=suinfo)
#find latency with regex
x = str(x.communicate()[0])
lhs, rhs = x.split("Average = ")
lhs, rhs = rhs.split("\\", 1)
lhs, rhs = lhs.split("m")
if int(lhs) > 999:
lhs = "999" + "ms"
latency = lhs
canvas.itemconfig(text, text=latency, width=width)
canvas.itemconfig(text, font=("courier", 25, "bold"))
canvas.itemconfig(text, fill="white")
root.after(1000, display)
class WindowDraggable():
def __init__(self, label):
self.label = label
label.bind('<ButtonPress-1>', self.StartMove)
label.bind('<ButtonRelease-1>', self.StopMove)
label.bind('<B1-Motion>', self.OnMotion)
def StartMove(self, event):
self.x = event.x
self.y = event.y
def StopMove(self, event):
self.x = None
self.y = None
def OnMotion(self,event):
x = (event.x_root - self.x - self.label.winfo_rootx() + self.label.winfo_rootx())
y = (event.y_root - self.y - self.label.winfo_rooty() + self.label.winfo_rooty())
root.geometry("+%s+%s" % (x, y))
label = Label(root, text='drag me')
WindowDraggable(label)
label.pack()
#_thread.start_new_thread( print_time, ("Thread-2", 4, ) )
root.after(0, display())
root.mainloop()
Rather than try to fight Tkinter's builtin loop/threading, use it:
def wait_for_it(proc):
proc.poll()
if proc.returncode is None: # subprocess hasn't finished yet
root.after(100, lambda: wait_for_it(proc)) # register a callback for 100ms
else:
display(proc.communicate()[0])
def display(x):
lhs, rhs = x.split("Average = ")
# the rest of your code goes here...
# instead of root.after(0, display)
wait_for_it(subprocess.Popen(['ping', 'google.com']))
As a slight aside, I highly recommend pasting your code on the Code Review Stack Exchange to get some style pointers and help simplifying it.
Related
I am creating a painting application and I want to save my drawing on canvas widget as png file on my computer. This is my code:
from tkinter import *
from tkinter.filedialog import *
from functools import partial
from tkinter import Menu
from tkinter import filedialog,messagebox
from PIL import Image
from tkinter.colorchooser import askcolor
import pyscreenshot as ImageGrab
import pyautogui
class PaintingApp:
x=y=None
def __init__(self,window):
self.window = window
self.upper_frame = Frame(window)
self.upper_frame.grid(row=0,column=0, padx=10, pady=5,sticky="ew")
self.lower_frame = Frame(window)
self.lower_frame.grid(row=2, column=0, padx=10, pady=5,sticky="ew")
self.canvas= Canvas(self.lower_frame,width=500,height=530,bg="white")
self.canvas.grid()
self.objects = [] #objects on canvas
self.pen_size = 2
self.pcolor = "black"
self.pen = Button(self.upper_frame,text="Pen",command=partial(self.pen_draw,thickness=self.pen_size))
self.pen.grid(row=0,column=3,padx=(10,160))
self.bg = Button(self.upper_frame,text="Background",command= self.bgcolor) #change bg color
self.bg.grid(row=2,column=1,padx=(100,10))
self.upper_menu()
self.canvas.bind("<Button-1>", self.get_x_and_y)
self.canvas.bind("<B1-Motion>", lambda event, b=self.pen_size: self.pen_draw(b,event))
self.im = None
def save_pic(self,event=None):
file = asksaveasfilename(defaultextension=".png")
x = self.canvas.winfo_rootx() + self.canvas.winfo_x()
y = self.canvas.winfo_rooty() + self.canvas.winfo_y()
x1 = x + self.canvas.winfo_width()
y1 = y + self.canvas.winfo_height()
self.im=ImageGrab.grab(bbox=(x,y,x1,y1))
self.im.save(file[19:])
def pen_color(self,color):
self.pcolor= color
def get_x_and_y(self,event):
global x,y
x, y = event.x, event.y
def pen_draw(self,thickness,event=None):
global x,y
self.canvas.bind("<Button-1>", self.get_x_and_y) # Bind to pen_draw function
self.canvas.bind("<B1-Motion>", lambda event, b=self.pen_size: self.pen_draw(b,event))
if event != None:
self.objects.append(self.canvas.create_line((x, y, event.x, event.y), fill=self.pcolor,width=self.pen_size,capstyle=ROUND,smooth=True))
x, y = event.x, event.y
def upper_menu(self):
self.menubar = Menu(window)
self.menu1 = Menu(self.menubar, tearoff=0)
self.menu1.add_command(label="Save pic", command=self.save_pic)
self.menu1.add_separator()
self.menu1.add_command(label="Exit", command=window.destroy)
self.menubar.add_cascade(label="Settings", menu=self.menu1)
self.menu2 = Menu(self.menubar, tearoff=0)
self.menu2.add_command(label="Open pic")
self.menubar.add_cascade(label="Image", menu=self.menu2)
self.window.config(menu=self.menubar)
def bgcolor(self):
chosen_color = askcolor(color=self.canvas["bg"])[1]
self.canvas.configure(bg=chosen_color)
window = Tk()
window.geometry("500x450")
p = PaintingApp(window)
window.mainloop()
Now I have tried many many codes but it won't work. The code I provided above saves an all black picture which does not make any sense. I have tried using the module pyautogui as well but I still get the same result.
def save_pic(self,event=None):
file = asksaveasfilename(defaultextension=".png")
x = self.canvas.winfo_rootx() + self.canvas.winfo_x()
y = self.canvas.winfo_rooty() + self.canvas.winfo_y()
x1 = x + self.canvas.winfo_width()
y1 = y + self.canvas.winfo_height()
self.im=pyautogui.screenshot(region=(x,y,x1,y1))
self.im.save(file[19:])
If you use a screen shot library, you should wait until the backend UI framework(X11 in your case) finishes the drawing. Also you can use the PIL.ImageGrab.grab() in Pillow instead of pyscreenshot.
So do like this.(I fixed several errors in the original example, such as an incorrect path.)
...
from PIL import ImageGrab
...
class PaintingApp:
...
def save_pic(self,event=None):
file = asksaveasfilename(defaultextension=".png")
def grab_and_save():
x = self.canvas.winfo_rootx()
y = self.canvas.winfo_rooty()
x1 = x + self.canvas.winfo_width()
y1 = y + self.canvas.winfo_height()
self.im = ImageGrab.grab(bbox=(x,y,x1,y1))
self.im.save(file)
self.window.update()
self.window.after(1000, grab_and_save) # This waits 1000ms.
...
...
A better method will be to install Tkimg and export the bitmap on the canvas directly, but it will need some hard work. You can start with an unmaintained project, python-tkimg.
I am trying to use the code at Python Tkinter rotate image animation with the following change:
Instead of rotating the canvas endlessly, I want a rotation of "turn" degrees which is randomly decided using randint() function. However, after turning by this angle, the tkinter window disappears and an error is raised. How can I make the following code work.
From my intermediate level knowledge of Python, I can see that the "yield" statement is putting a generator to work.
You can use any image in place of "0.png" in my code. I am using Python 3.9.6. Thanks in advance. The following is the code I am trying to get to work.
from tkinter import *
from PIL import ImageTk, Image
from time import sleep
from random import randint
class SimpleApp(object):
def __init__(self, master, filename):
self.master = master
self.filename = filename
self.canvas = Canvas(master, bg="black", width=500, height=500)
self.canvas.pack()
self.update = self.draw().__next__
master.after(100, self.update)
def draw(self):
image = Image.open(self.filename)
angle = 0
turn = randint(30, 390)
for i in range(turn):
tkimage = ImageTk.PhotoImage(image.rotate(angle))
canvas_obj = self.canvas.create_image(250, 250, image=tkimage)
self.master.after_idle(self.update)
yield
self.canvas.delete(canvas_obj)
angle = (angle - 1) % 360
sleep(.01)
win = Tk()
app = SimpleApp(win, '0.png')
win.mainloop()
After last yield it exits function draw in normal way and then __next__() can't run it again and it raises StopIteration and this makes problem. Normally when it is used in for-loop then it catchs StopIteration. Or if you run it with next() then you can also catch StopIteration but in this example it is problem.
I would do it without yield. I would split it in two functions: draw() to set default values at start, and rotate() to update image.
import tkinter as tk
from PIL import ImageTk, Image
from time import sleep
from random import randint
class SimpleApp(object):
def __init__(self, master, filename):
self.master = master
self.filename = filename
self.canvas = tk.Canvas(master, bg="black", width=500, height=500)
self.canvas.pack()
self.draw()
def draw(self):
self.image = Image.open(self.filename)
self.angle = 0
self.turn = randint(30, 360)
self.canvas_obj = None
self.master.after(100, self.rotate)
def rotate(self):
# it will remove image after last move
#if self.canvas_obj:
# self.canvas.delete(self.canvas_obj)
if self.turn > 0:
# it will NOT remove image after last move
if self.canvas_obj:
self.canvas.delete(self.canvas_obj)
self.tkimage = ImageTk.PhotoImage(self.image.rotate(self.angle))
self.canvas_obj = self.canvas.create_image(250, 250, image=self.tkimage)
self.angle = (self.angle - 1) % 360
self.turn -= 1
self.master.after_idle(self.rotate)
win = tk.Tk()
app = SimpleApp(win, 'lenna.png')
win.mainloop()
lenna.png - (Wikipedia Lenna)
What I'm trying to achieve is a Tkinter window, using overridedirect, that show up in your taskbar. You can minimize it like a normal window, and bring it back up like a normal window. The problem that I'm having right now is that I've achieved adding it to the taskbar, but for some reason when you minimize it and then bring it back, it's no longer on the taskbar.
# Imports
from tkinter import *
from ctypes import windll
# Some WindowsOS styles, required for task bar integration
GWL_EXSTYLE = -20
WS_EX_APPWINDOW = 0x00040000
WS_EX_TOOLWINDOW = 0x00000080
def set_appwindow(mainWindow):
# Honestly forgot what most of this stuff does. I think it's so that you can see
# the program in the task bar while using overridedirect. Most of it is taken
# from a post I found on stackoverflow.
hwnd = windll.user32.GetParent(mainWindow.winfo_id())
stylew = windll.user32.GetWindowLongW(hwnd, GWL_EXSTYLE)
stylew = stylew & ~WS_EX_TOOLWINDOW
stylew = stylew | WS_EX_APPWINDOW
res = windll.user32.SetWindowLongW(hwnd, GWL_EXSTYLE, stylew)
# re-assert the new window style
mainWindow.wm_withdraw()
mainWindow.after(10, lambda: mainWindow.wm_deiconify())
def main():
global mainWindow
# Default window configuration
mainWindow = Tk()
mainWindow.geometry('800x400')
mainWindow.resizable(width=False, height=False)
mainWindow.overrideredirect(True)
mainWindow.after(10, lambda: set_appwindow(mainWindow))
def exitGUI():
mainWindow.destroy()
def minimizeGUI():
mainWindow.state('withdrawn')
mainWindow.overrideredirect(False)
mainWindow.state('iconic')
def frameMapped(event=None):
mainWindow.overrideredirect(True)
mainWindow.state("normal")
mainWindow.iconbitmap("ANALOG.ico")
exitButton = Button(mainWindow, text='', bg='#212121', fg='#35DAFF',
command=exitGUI)
minimizeButton = Button(mainWindow, text='', bg="#212121", fg='#35DAFF',
command=minimizeGUI)
exitButton.place(x=780, y=0)
minimizeButton.place(x=759, y=0)
mainWindow.bind("<Map>", frameMapped) # This brings back the window
mainWindow.mainloop() # Window Loop
if __name__ == '__main__':
main()
Okay guys, I finally figured it out.
# Imports
from tkinter import *
from ctypes import windll
# Some WindowsOS styles, required for task bar integration
GWL_EXSTYLE = -20
WS_EX_APPWINDOW = 0x00040000
WS_EX_TOOLWINDOW = 0x00000080
lastClickX = 0
lastClickY = 0
def SaveLastClickPos(event):
global lastClickX, lastClickY
lastClickX = event.x
lastClickY = event.y
def Dragging(event):
x, y = event.x - lastClickX + mainWindow.winfo_x(), event.y - lastClickY + mainWindow.winfo_y()
mainWindow.geometry("+%s+%s" % (x , y))
def set_appwindow(mainWindow):
# Honestly forgot what most of this stuff does. I think it's so that you can see
# the program in the task bar while using overridedirect. Most of it is taken
# from a post I found on stackoverflow.
hwnd = windll.user32.GetParent(mainWindow.winfo_id())
stylew = windll.user32.GetWindowLongW(hwnd, GWL_EXSTYLE)
stylew = stylew & ~WS_EX_TOOLWINDOW
stylew = stylew | WS_EX_APPWINDOW
res = windll.user32.SetWindowLongW(hwnd, GWL_EXSTYLE, stylew)
# re-assert the new window style
mainWindow.wm_withdraw()
mainWindow.after(10, lambda: mainWindow.wm_deiconify())
def main():
global mainWindow, z
# Default window configuration
mainWindow = Tk()
mainWindow.geometry('800x400')
mainWindow.resizable(width=False, height=False)
mainWindow.overrideredirect(True)
mainWindow.after(10, lambda: set_appwindow(mainWindow))
mainWindow.bind('<Button-1>', SaveLastClickPos)
mainWindow.bind('<B1-Motion>', Dragging)
z = 0
def exitGUI():
mainWindow.destroy()
def minimizeGUI():
global z
mainWindow.state('withdrawn')
mainWindow.overrideredirect(False)
mainWindow.state('iconic')
z = 1
def frameMapped(event=None):
global z
mainWindow.overrideredirect(True)
mainWindow.iconbitmap("ANAL_OG.ico")
if z == 1:
set_appwindow(mainWindow)
z = 0
exitButton = Button(mainWindow, text='', bg='#212121', fg='#35DAFF',
command=exitGUI)
minimizeButton = Button(mainWindow, text='', bg="#212121", fg='#35DAFF',
command=minimizeGUI)
exitButton.place(x=780, y=0)
minimizeButton.place(x=759, y=0)
mainWindow.bind("<Map>", frameMapped) # This brings back the window
mainWindow.mainloop() # Window Loop
if __name__ == '__main__':
main()
Thanks for all your suggestions!
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
I have this simple Tkinter Custom Window. I am a beginner and only learnt tkinter a few months ago. I have no experience in real software development. So, I would like to know if the way it was coded is acceptable? I know that when i say acceptable , it could mean a lot of things. I just want to know what are the things i should improve in my coding style & the way i think?
import Tkinter as tk
''' Creating Tkinter Tk instance '''
class Application(tk.Tk):
def __init__(self,*args,**kwargs):
tk.Tk.__init__(self,*args,**kwargs)
self.bind("<ButtonPress-1>", self.StartMove)
self.bind("<ButtonRelease-1>", self.StopMove)
self.bind("<B1-Motion>", self.OnMotion)
self.Init()
self.Layout()
self.AddButtons()
''' Setting Main Tk window size & styles '''
def Init(self):
self.geometry("1280x700+0+0")
self.overrideredirect(True)
self['background'] = '#201F29'
self['highlightthickness'] = 2
self['relief'] = 'groove'
'''Layout of the Tk window'''
def Layout(self):
self.exitmenu = tk.Frame(self)
self.exitmenu.place(x = 1217, y = 0)
self.container = tk.Frame(self,width = 1268,height = 648 , relief = 'flat',bd = 0)
self.container.place(x = 5,y = 40)
''' Adding Exit button and Minimize button to the Tk window'''
def AddButtons(self):
self.minibutton = tk.Button(self.exitmenu,text = '0',font=('webdings',8,'bold'),relief = 'flat' , command = self.minimize )
self.minibutton.pack(side = 'left')
self.exitbutton = tk.Button(self.exitmenu,text = 'r',font=('webdings',8),relief = 'flat' ,bg = '#DB6B5A', command = self.destroy )
self.exitbutton.pack(side = 'left')
def minimize(self):
self.overrideredirect(False)
self.wm_state('iconic')
self.overrideredirect(True)
'''Methods for moving window frame'''
def StartMove(self, event):
self.x = event.x
self.y = event.y
def StopMove(self, event):
self.x = None
self.y = None
def OnMotion(self, event):
x1 = self.x
y1 = self.y
x2 = event.x
y2 = event.y
deltax = x2 - x1
deltay = y2 - y1
a = self.winfo_x() + deltax
b = self.winfo_y() + deltay
self.geometry("+%s+%s" % (a, b))
def Main():
app = Application()
app.mainloop()
if __name__ == "__main__":
Main()
Read PEP-8 Install and run one or all of PEP8 checker, pyFlakes, pyChecker, pylint.
The first thing that stands out is that docstrings are supposed to be within the function rather than before it - then they become a part of the function code and can be accessed by help.