How to create Thread in GUI-application? - python

Problem:
I set up a image downloading GUI-application(tkinter) through unsplash API.
Every thing is working on. Not any error but when I run application so It's shows 'Not responding' cause Image downloading in background. How can I create Two threads. one will handle tkinter GUI and second will handle background image downloading process.
My code:
from tkinter import *
import requests
import os, pprint
from time import sleep
from threading import *
from urllib.request import urlretrieve
class Gui(Tk):
def __init__(self):
self.do_splash_call = False
Tk.__init__(self)
self.title('unsplash image download')
self.geometry('900x500')
# main canvas
main = Canvas(self, bg='#0099e6')
main.place(relx=0, rely=0, relwidth=1, relheight=1)
# frame1 for upper left
name_frame = Frame(main)
name_frame.place(relx=.1, rely=.1)
# name input and label
name_label = Label(name_frame, text='Image Name:')
name_label.pack(side=LEFT, pady=3)
self.name = Entry(name_frame, bd=0, font=2, width=23)
self.name.pack(side=RIGHT, pady=3)
# frame2 for upper right
page_frame = Frame(main)
page_frame.place(relx=.5, rely=.1, relwidth=.3)
# landing page
Label(page_frame, text='Landing page:').pack(side=LEFT, pady=3)
self.page = Spinbox(page_frame, from_=1, to=50, width=30)
self.page.pack(side=RIGHT, pady=3)
# frame3 for down left
size_frame = Frame(main)
size_frame.place(relx=.1, rely=.2)
# name input and label
size_label = Label(size_frame, text='Android, pc, tablet:')
size_label.pack(side=LEFT, pady=3)
self.size = Entry(size_frame, bd=0, font=2)
self.size.pack(side=RIGHT, pady=3)
# button
download = Button(main, bd=1, font=4, text='Download', width=10, command=lambda :self.splash())
download.place(relx=.6, rely=.2)
# output
self.output = Text(main, bd=2)
self.output.place(relx=0, rely=.5, relheight=1, relwidth=1)
# path to save images
save_frame = Frame(main)
save_frame.place(relx=.1, rely=.3, relwidth=.7)
Label(save_frame, text='Save:').pack(side=LEFT, padx=5)
self.save = Entry(save_frame, bd=1)
self.save.place(relx=.1, relwidth=1)
# insert path for save images
self.save.insert(0, str('c:\\users\\abc\\Desktop\\photos'))
def path(self):
if os.path.exists(self.save.get()):
os.chdir(self.save.get())
else:
os.mkdir(self.save.get())
os.chdir(self.save.get())
def splash(self):
self.do_splash_call = True
self.call_splash()
def call_splash(self):
if self.do_splash_call:
# getting input from user
image_name = self.name.get()
pages = self.page.get()
size = self.size.get()
# set the size of photos
if size == 'pc' or 'desktop':
size = 1080
orientation = 'landscape'
elif size == 'hd' or 'full hd' or 'clear' or 'normal' or 'HD':
size = 1500
orientation = 'landscape'
elif size == 'android' or 'mini' or 'mobile':
size = 400
orientation = 'portrait'
else:
size = 1500
orientation = 'landscape'
try:
api = f'''https://api.unsplash.com/photos/search?query=
{image_name}&resolution={size}&orientation=
{orientation}&client_id=API Key
&page={pages}&w=1500&dpi=2'''
res = requests.get(api).json()
for i in range(2):
url = res[i]['links']['download']
name_of_image = str(res[i]['alt_description'])
img_name = '_'.join(name_of_image[:40].split(' '))
# create folder for download images
self.path()
# print current images downloading
self.output.insert(INSERT, '\nDownloading img.... %s.png' % img_name)
self.output.tag_add('fine', '1.0', '3.0')
self.output.tag_config('fine', background='lightGreen', foreground='#196619')
# Downloading image in pc
urlretrieve(url, '%s.png' % img_name)
except Exception as e:
self.output.insert(INSERT, e)
self.output.tag_add('error', '1.0', '3.0')
self.output.tag_config('error', background='#ff4d4d', foreground='black')
print(e)
if __name__ == '__main__':
app = Gui()
app.mainloop()
I except:
There is a class and class inside there is method called splash() so How can I create Thread, one for constructor and second for splash() method ?

Example with thread and other changes or comments in code because there are mistakes.
I didn't change from tkinter import * because it would need more work. On some systems thread may not have access to GUI so self.output in call_spash may have problem but I didn't change it - too many works.
By the way: PEP 8 -- Style Guide for Python Code
from tkinter import * # PEP8 - don't use *
#import tkinter as tk
import requests
import os
import pprint # PEP8 - imports in separated lines
from time import sleep
# from threading import * # PEP8 - don't use *
import threading
from urllib.request import urlretrieve
class Gui(Tk):
def __init__(self):
#Tk.__init__(self)
super().__init__() # instead of Tk.__init__(self)
self.title('unsplash image download')
self.geometry('900x500')
self.do_splash_call = False
self.thread = None
# main canvas
main = Canvas(self, bg='#0099e6')
main.place(relx=0, rely=0, relwidth=1, relheight=1)
# frame1 for upper left
name_frame = Frame(main)
name_frame.place(relx=.1, rely=.1)
# name input and label
name_label = Label(name_frame, text='Image Name:')
name_label.pack(side=LEFT, pady=3)
self.name = Entry(name_frame, bd=0, font=2, width=23)
self.name.pack(side=RIGHT, pady=3)
# frame2 for upper right
page_frame = Frame(main)
page_frame.place(relx=.5, rely=.1, relwidth=.3)
# landing page
Label(page_frame, text='Landing page:').pack(side=LEFT, pady=3)
self.page = Spinbox(page_frame, from_=1, to=50, width=30)
self.page.pack(side=RIGHT, pady=3)
# frame3 for down left
size_frame = Frame(main)
size_frame.place(relx=.1, rely=.2)
# name input and label
size_label = Label(size_frame, text='Android, pc, tablet:')
size_label.pack(side=LEFT, pady=3)
self.size = Entry(size_frame, bd=0, font=2)
self.size.pack(side=RIGHT, pady=3)
# button
#download = Button(main, bd=1, font=4, text='Download', width=10, command=lambda :self.splash())
download = Button(main, bd=1, font=4, text='Download', width=10, command=self.splash) # no need lambda
download.place(relx=.6, rely=.2)
# output
self.output = Text(main, bd=2)
self.output.place(relx=0, rely=.5, relheight=1, relwidth=1)
# path to save images
save_frame = Frame(main)
save_frame.place(relx=.1, rely=.3, relwidth=.7)
Label(save_frame, text='Save:').pack(side=LEFT, padx=5)
self.save = Entry(save_frame, bd=1)
self.save.place(relx=.1, relwidth=1)
# insert path for save images
#self.save.insert(0, str('c:\\users\\abc\\Desktop\\photos'))
self.save.insert(0, 'c:\\users\\abc\\Desktop\\photos') # it is already string
def path(self):
# you could use `val = self.save.get()` once and later use `val`
#if os.path.exists(self.save.get()):
# os.chdir(self.save.get())
#else:
# os.mkdir(self.save.get())
# os.chdir(self.save.get())
val = self.save.get()
if not os.path.exists(val):
#os.mkdir(val) # it makes only first dir in path `dir1/dir2/dir3`
os.makedirs(val) # it makes all dirs in path `dir1/dir2/dir3`
#os.makedirs(val, exist_ok=True) # Python 3.7+, it doesn't need `os.path.exists`
os.chdir(val)
def splash(self):
#if not self.thread or not self.thread.is_alive(): # method without `self.do_splash_call`
if not self.do_splash_call:
print('[DEBUG] run splash')
self.do_splash_call = True
# getting input from user
image_name = self.name.get()
pages = self.page.get()
size = self.size.get()
self.thread = threading.Thread(target=self.call_splash, args=(image_name, pages, size) )
self.thread.start()
def call_splash(self, image_name, pages, size):
# set the size of photos
#if size == 'pc' or 'desktop': # wrong
#if size == 'pc' or size = 'desktop': # good
if size in ('pc', 'desktop'): # good
size = 1080
orientation = 'landscape'
#elif size == 'hd' or 'full hd' or 'clear' or 'normal' or 'HD': # wrong
elif size in ('hd', 'full hd', 'clear', 'normal', 'HD'):
size = 1500
orientation = 'landscape'
#elif size == 'android' or 'mini' or 'mobile': # wrong
elif size in ('android', 'mini', 'mobile'):
size = 400
orientation = 'portrait'
else:
size = 1500
orientation = 'landscape'
try:
api = f'''https://api.unsplash.com/photos/search?query=
{image_name}&resolution={size}&orientation=
{orientation}&client_id=API Key
&page={pages}&w=1500&dpi=2'''
res = requests.get(api).json()
print('[DEBUG] res:', res)
for i in range(2):
url = res[i]['links']['download']
name_of_image = str(res[i]['alt_description'])
img_name = '_'.join(name_of_image[:40].split(' '))
# create folder for download images
self.path()
# print current images downloading
self.output.insert(INSERT, '\nDownloading img.... %s.png' % img_name)
self.output.tag_add('fine', '1.0', '3.0')
self.output.tag_config('fine', background='lightGreen', foreground='#196619')
# Downloading image in pc
urlretrieve(url, '%s.png' % img_name)
except Exception as e:
self.output.insert('end', e)
self.output.tag_add('error', '1.0', '3.0')
self.output.tag_config('error', background='#ff4d4d', foreground='black') # you could define it once
print('[DEBUG] e:', e)
self.do_splash_call = False
if __name__ == '__main__':
app = Gui()
app.mainloop()

Related

Setting frames on tkinter with cef

Can someone help with this?
I have my page defined with frames but when i run the code it only shows the browser window.
This issue is a continuation from Load CEF in labelframe
Its basicly a window with links and a browser window on the right
import ctypes
import platform
import sys
import tkinter as tk
from cefpython3 import cefpython as cef
from tkinter import ttk
import tkinter as tk
import requests
from bs4 import BeautifulSoup
import webbrowser
import tkinterweb
global urlset
urlset="https://www.google.com"
# platforms
WINDOWS = platform.system() == 'Windows'
LINUX = platform.system() == 'Linux'
MAC = platform.system() == 'Darwin'
class BrowserFrame(tk.Frame):
def __init__(self, master=None, **kw):
super().__init__(master, **kw)
self.browser = None
self.bind('<Configure>', self.on_configure)
def open(self, url):
if self.browser:
self.browser.LoadUrl(url)
# inside BrowserFrame
def on_configure(self, event):
if self.browser is None:
self.browser = cef.CreateBrowserSync(cef_winfo,url=urlset)
def get_window_handle(self):
if MAC:
from AppKit import NSApp
import objc
return objc.pyobjc_id(NSApp.windows()[-1].contentView())
elif self.winfo_id() > 0:
return self.winfo_id()
else:
raise Exception('Could not obtain window handle!')
def on_configure(self, event):
if self.browser is None:
# create the browser and embed it in current frame
rect = [0, 0, self.winfo_width(), self.winfo_height()]
cef_winfo = cef.WindowInfo()
win_id = self.get_window_handle()
cef_winfo.SetAsChild(win_id, rect)
self.browser = cef.CreateBrowserSync(cef_winfo, url=urlset)
# start the browser handling loop
self.cef_loop()
# resize the browser
if WINDOWS:
ctypes.windll.user32.SetWindowPos(
self.browser.GetWindowHandle(), 0,
0, 0, event.width, event.height, 0x0002)
elif LINUX:
self.browser.SetBounds(0, 0, event.width, event.height)
def cef_loop(self):
cef.MessageLoopWork()
self.after(10, self.cef_loop)
def main():
sys.excepthook = cef.ExceptHook # To shutdown all CEF processes on error
root = tk.Tk()
w, h = root.winfo_screenwidth(), root.winfo_screenheight()
root.geometry("%dx%d+0+0" % (w, h))
root.title('Test')
settings = {}
if MAC:
settings["external_message_pump"] = True
cef.Initialize(settings=settings)
container = ttk.Frame(root)
canvas = tk.Canvas(container)
scrollbar_x = ttk.Scrollbar(container, orient="horizontal", command=canvas.xview)
scrollbar_y = ttk.Scrollbar(container, orient="vertical", command=canvas.yview)
scrollable_frame = ttk.Frame(canvas)
scrollable_frame.bind("<Configure>", lambda e: canvas.configure(scrollregion=canvas.bbox("all")))
canvas.create_window((0, 0), window=scrollable_frame, anchor="nw")
canvas.configure(xscrollcommand=scrollbar_x.set)
canvas.configure(yscrollcommand=scrollbar_y.set)
stathomelib = tk.LabelFrame(scrollable_frame, text="Library", padx=5, pady=5, highlightbackground="black",
highlightthickness=2)
stathomelib.grid(row=3, column=0, columnspan=2, sticky="NW", pady=2)
stathomedoc = tk.LabelFrame(scrollable_frame, text="Documentation", padx=5, pady=5, highlightbackground="black",
highlightthickness=2)
stathomedoc.grid(row=3, column=2, columnspan=2, sticky="NW", pady=2)
home_browser = tk.LabelFrame(scrollable_frame, text="", padx=5, pady=5, highlightbackground="black", highlightthickness=2)
home_browser.grid(row=0, column=99, rowspan=99, sticky='ne', pady=2)
valido_browser = tk.IntVar()
valido_browser.set(False)
checkbrowser = tk.Checkbutton(home_browser, fg="blue", selectcolor="red", text="Open in Browser",variable=valido_browser)
checkbrowser.grid(row=0, column=0, columnspan=9, sticky="wn")
# specify initial width and height
browser = BrowserFrame(home_browser, width=1200, height=700)
browser.grid(row=2, column=0, columnspan=9, sticky="wn")
def choose_browser(urlbook):
if valido_browser.get() == 1:
webbrowser.open(urlbook)
else:
browser.open(urlbook)
global varLnkOrder
varLnkOrder = ""
varLbook = open("Confs/bookmarks.txt", "r").readlines()
try:
for line in varLbook:
undef = ""
if ":stathomelib" in line:
varLnkOrder = stathomelib
taman = 43
c = 0
i = 0
elif ":stathomedoc" in line:
varLnkOrder = stathomedoc
taman = 43
c = 0
i = 0
if len(line) > 1:
titul, urlbook = line.split('<=>')
if len(titul) > 1:
link1 = tk.Label(varLnkOrder, width=taman, text=titul + str(undef), justify="left", anchor="center",
fg="blue", cursor="draft_large")
link1.grid(row=i, column=c, sticky="n", pady=2)
link1.bind("<Button-1>", lambda e, urlbook=urlbook: choose_browser(urlbook.rstrip()))
i += 1
line == ""
except Exception as e:
from main import log_error
log_error(e, "Bookmark_Builder")
container.pack(expand=1, fill="both")
scrollbar_x.pack(side="bottom", fill="x")
scrollbar_y.pack(side="right", fill="y")
canvas.pack(side="bottom", fill="both", expand=True)
root.mainloop()
if __name__ == '__main__':
main()
bookmarks.txt
<=>:stathomelib
aaaaswefdee<=>ryweryfgertgjhslkrghkjreshjglkjhlksghklrhsklrhklegrthuyug
aaaaswefdee<=>ryweryfgertgjhslkrghkjreshjglkjhlksghklrhsklrhklegrthuyug/
aaaaswefdee<=>ryweryfgertgjhslkrghkjreshjglkjhlksghklrhsklrhklegrth
<=>:stathomedoc Wiki Teltonika
Commands<=>https://wiki.teltonika-gps.com/view/FMB_SMS/GPRS_Commands
Lucene Query Syntax(LogTrail
Search)<=>http://www.lucenetutorial.com/lucene-query-syntax.html
aaaaswefdee<=>ryweryfgertgjhslkrghkjreshjglkjhlksghklrhsklrhklegrthuyug/
aaaaswefdee<=>ryweryfgertgjhslkrghkjreshjglkjhlksghklrhsklrhklegrthuyug/
aaaaswefdee<=>ryweryfgertgjhslkrghkjreshjglkjhlksghklrhsklrhklegrthuyug/
<=>:stathomeTools
aaaaswefdee<=>ryweryfgertgjhslkrghkjreshjglkjhlksghklrhsklrhklegrthuyug/
aaaaswefdee<=>ryweryfgertgjhslkrghkjreshjglkjhlksghklrhsklrhklegrthuyug/
Note that updating urlset will not load the URL in the CEF browser automatically. You need to create a function in BrowserFrame to load an URL:
# inside BrowserFrame
def open(self, url):
if self.browser:
self.browser.LoadUrl(url)
And you need to load a default URL when creating the CEF browser:
# inside BrowserFrame
def on_configure(self, event):
if self.browser is None:
...
self.browser = cef.CreateBrowserSync(cef_winfo,
url="https://www.google.com")
...
Then update main() as below:
def main():
...
# specify initial width and height
browser = BrowserFrame(home_browser, width=1200, height=700)
browser.grid(row=2, column=0, columnspan=9, sticky="wn")
def choose_browser(urlbook):
if valido_browser.get() == 1:
webbrowser.open(urlbook)
else:
browser.open(urlbook)
# not necessary to call choose_browser() here
#choose_browser("http://www.google.com")
...
It is not recommended to import same module like below:
from tkinter import *
import tkinter as tk
Better use the later only.

Linked data from database to a button tkinter

I am working on a project on tkinter python.
This is how my graphic interface looks like:
And I have this database.txt:
ChickenCurry,Rice,Curry,Chicken,0.ppm
ChocolateCake,Chocolate,Flour,Sugar,Eggs,1.ppm
BolognesePasta,Pasta,Beef,TomatoeSauce,Cheese,2.ppm
Really simple. 0.ppm, 1.ppm and 2.ppm are the name of 3 images, the first one is the image of chicken curry the second one of chocolate and the last one of bolognese pasta.
My project: I would like to display the image of the chicken dish when I am clicking on the button ChickenCurry, the image of the chocolate cake when I am clicking on the chocolate cake, etc...
Here is my code:
import sys
from tkinter import *
import tkinter as tk
from PIL import Image
class Application(tk.Frame):
x = 2
def __init__(self, param = None, i = None, master=None):
super().__init__(master)
self.master = master
self.pack()
self.create_widgets()
def create_widgets(self):
if (self.x == 2):
param = "coucou"
self.hi_there = tk.Label(self)
self.hi_there["text"] = param
#self.hi_there["command"] = self.say_hi
self.hi_there.pack(side="top")
self.quit = tk.Button(self, text="QUIT", fg="red",
command=self.master.destroy)
self.quit.pack(side="bottom")
# Opening file in read format
File = open('data.txt',"r")
if(File == None):
print("File Not Found..")
else:
while(True):
# extracting data from records
record = File.readline()
if (record == ''): break
data = record.split(',')
print('Name of the dish:', data[0])
self.hi_there = tk.Button(self)
self.hi_there["text"] = data[0]
self.hi_there["command"] = self.photoOfTheDish
self.hi_there.pack(side="top")
# printing each record's data in organised form
for i in range(1, len(data)-1):
print('Ingredients:',data[i])
self.hi_there = tk.Label(self)
self.hi_there["text"] = data[i]
self.hi_there.pack(side="top")
File.close()
def photoOfTheDish(self):
novi = Toplevel()
self.canvas = Canvas(novi, width = 1500, height = 1000)
self.canvas.pack(expand = YES, fill = BOTH)
File = open('data.txt',"r")
with open('data.txt') as f:
record = File.readline()
data = record.split(',')
gif1 = PhotoImage(file = data[-1].rstrip('\n'))
#image not visual
self.canvas.create_image(50, 10, image = gif1, anchor = NW)
#assigned the gif1 to the canvas object
self.canvas.gif1 = gif1
root = tk.Tk()
root.geometry("5000x2000")
app = Application(master=root)
app.mainloop()
My issue is whatever the button I am clicking on, it's always the image corresponding to "0.ppm" which is displaying. I don't know how to link the button to his set of value from the database.
Inside photoOfTheDish(), you open data.txt and read only the first line to get the image filename. Therefore you always get the 0.ppm.
You can use lambda to pass the image filename to photoOfTheDish() when creating the buttons:
def create_widgets(self):
...
else:
while True:
...
# extracting data from records
record = File.readline().rstrip() # strip out the trailing newline
...
# pass image filename to callback
self.hi_there["command"] = lambda image=data[-1]: self.photoOfTheDish(image)
...
...
def photoOfTheDish(self, image):
novi = Toplevel()
self.canvas = Canvas(novi, width = 1500, height = 1000)
self.canvas.pack(expand = YES, fill = BOTH)
self.canvas.gif1 = PhotoImage(file=image)
self.canvas.create_image(50, 10, image=self.canvas.gif1, anchor=NW)

How do I make python tkinter widgets appear on the right side of a frame and grow left?

Current behavior, adding LabelFrame widgets (with their own label and picture children) into another Labelframe named "Test putting buffs..." The LabelFrame widgets are being added into the left side of the LabelFrame and "grow" to the right. Wanted behavior is for the widgets to appear on the right side of the frame and "grow" left.
Cannot seem to get there with anchor or sticky settings. How can this "grow-left" be done and still preserve the ability to sort by name or time remaining?
Current behavior gif:
Wanted behavior (mocked up with paint):
Code (took out the image stuff so files aren't needed to run):
import tkinter as tk
from tkinter import ttk
from PIL import ImageTk, Image
import time
class MainFrame(tk.Frame):
def __init__(self, container):
super().__init__(container)
self.grid(column=0, row=0)
self.buffs_list_frames = []
self.button1 = ttk.Button(self)
self.button1['text'] = "Simulate frame list appended True Strike"
self.button1['command'] = lambda: self.buffs_frame_list_is_appended(["True Strike", time.time() + 9])
self.button1.grid(column=0, row=0)
self.button2 = ttk.Button(self)
self.button2['text'] = "Simulate frame list appended Bulls"
self.button2['command'] = lambda: self.buffs_frame_list_is_appended(["Bulls", time.time() + 1080])
self.button2.grid(column=0, row=1)
self.button0 = ttk.Button(self)
self.button0['text'] = "Simulate frame list appended Endurance"
self.button0['command'] = lambda: self.buffs_frame_list_is_appended(["Endurance", time.time() + 1080])
self.button0.grid(column=0, row=2)
self.button3 = ttk.Button(self)
self.button3['text'] = "Simulate frame list put into .grid() and displayed"
self.button3['command'] = lambda: self.buffs_display_nicely()
self.button3.grid(column=0, row=3)
self.button4 = ttk.Button(self)
self.button4['text'] = "START loops of time passing"
self.button4['command'] = lambda: self.buffs_loop_time_passing()
self.button4.grid(column=0, row=4)
self.test_label_frame = ttk.LabelFrame(self, text="Testing putting buffs into a frame with grid")
self.test_label_frame.grid(column=1, row=0)
def buffs_loop_time_passing(self):
for x in self.buffs_list_frames:
x.buff_timer.set(f"{x.buff_birthday - time.time():.1f}s")
if x.buff_birthday < time.time() + 6:
x['background'] = 'red'
if x.buff_birthday < time.time():
self.buffs_list_frames.remove(x)
x.destroy()
self.after(1000, self.buffs_loop_time_passing)
def buffs_frame_list_is_appended(self, added_buff):
""" makes the buff frame and adds to the list of frame widgets
"""
self.buff_frame = tk.LabelFrame(self.test_label_frame, borderwidth=1, text=added_buff[0][0:4], labelanchor="n")
# self.buff_frame.buff_image_reference = ImageTk.PhotoImage(Image.open(added_buff[2]))
# self.buff_frame.buff_image_label = ttk.Label(self.buff_frame, image=self.buff_frame.buff_image_reference)
# self.buff_frame.buff_image_label.image_keep = self.buff_frame.buff_image_reference
# self.buff_frame.buff_image_label.grid(column=0, row=0)
self.buff_frame.buff_birthday = added_buff[1]
self.buff_frame.buff_timer = tk.StringVar()
self.buff_frame.buff_timer.set(f"{self.buff_frame.buff_birthday - time.time():.1f}s")
self.buff_frame.buff_label = ttk.Label(self.buff_frame, textvariable=self.buff_frame.buff_timer)
self.buff_frame.buff_label.grid(column=0, row=1)
self.buffs_list_frames.append(self.buff_frame)
self.buffs_display_nicely()
def buffs_display_nicely(self):
""" takes the list of frames, sorts by name, and .grids()s them into the test frame
"""
self.buffs_list_frames = sorted(self.buffs_list_frames, key=lambda z: z['text'])
print(f"sorted? {self.buffs_list_frames}")
j = 0
for x in self.buffs_list_frames:
x.grid(row=0, column=j)
j += 1
class App(tk.Tk):
def __init__(self):
super().__init__()
# configure the root window
self.title('NWN Buff Watcher')
self.geometry('300x50')
if __name__ == "__main__":
app = App()
main_frame = MainFrame(app)
app.mainloop()
Try this (using #acw1668's suggestion):
import tkinter as tk
from tkinter import ttk
from PIL import ImageTk, Image
import time
class MainFrame(tk.Frame):
def __init__(self, container):
super().__init__(container)
self.buffs_list_frames = []
self.buttons_frame = tk.Frame(self)
self.buttons_frame.pack(fill="both", side="left")
self.button1 = ttk.Button(self.buttons_frame, text="Simulate frame list appended True Strike",
command=lambda: self.buffs_frame_list_is_appended("True Strike", 9))
self.button1.grid(column=0, row=0)
self.button2 = ttk.Button(self.buttons_frame, text="Simulate frame list appended Bulls",
command=lambda: self.buffs_frame_list_is_appended("Bulls", 1080))
self.button2.grid(column=0, row=1)
self.button0 = ttk.Button(self.buttons_frame, text="Simulate frame list appended Endurance",
command=lambda: self.buffs_frame_list_is_appended("Endurance", 1080))
self.button0.grid(column=0, row=2)
#self.button3 = ttk.Button(self.buttons_frame, text="Order items", command=self.order_items)
#self.button3.grid(column=0, row=3)
self.button4 = ttk.Button(self.buttons_frame, text="START loops of time passing",
command=self.buffs_loop_time_passing)
self.button4.grid(column=0, row=4)
self.test_label_frame = ttk.LabelFrame(self, text="Testing putting buffs into a frame with grid")
self.test_label_frame.pack(side="right")
def buffs_loop_time_passing(self):
for x in self.buffs_list_frames:
x.buff_timer.set(f"{x.buff_birthday - time.time():.1f}s")
time_now = time.time()
if x.buff_birthday < time_now + 6:
x.config(bg="red")
if x.buff_birthday < time_now:
self.buffs_list_frames.remove(x)
x.destroy()
self.after(100, self.buffs_loop_time_passing)
def buffs_frame_list_is_appended(self, added_buff, time_alive):
""" makes the buff frame and adds to the list of frame widgets
"""
buff_frame = tk.LabelFrame(self.test_label_frame, borderwidth=1,
text=added_buff[:4], labelanchor="n")
# buff_frame.buff_image_reference = ImageTk.PhotoImage(Image.open(added_buff[2]), master=self)
# buff_frame.buff_image_label = ttk.Label(buff_frame, image=buff_frame.buff_image_reference)
# buff_frame.buff_image_label.image_keep = buff_frame.buff_image_reference
# buff_frame.buff_image_label.grid(column=0, row=0)
buff_frame.buff_birthday = time.time() + time_alive
buff_frame.buff_timer = tk.StringVar(master=self)
buff_frame.buff_timer.set(f"{buff_frame.buff_birthday - time.time():.1f}s")
buff_frame.buff_label = ttk.Label(buff_frame,
textvariable=buff_frame.buff_timer)
buff_frame.buff_label.grid(column=0, row=1)
self.buffs_list_frames.append(buff_frame)
self.order_items()
def order_items(self):
self.buffs_list_frames = sorted(self.buffs_list_frames, key=lambda z: z['text'], reverse=True)
for x in self.buffs_list_frames:
x.pack_forget()
x.pack(side="right")
class App(tk.Tk):
def __init__(self):
super().__init__()
# configure the root window
self.title("NWN Buff Watcher")
# self.geometry("300x50")
if __name__ == "__main__":
app = App()
main_frame = MainFrame(app)
main_frame.pack()
app.mainloop()
I made a few changes to your code. The main thing is that I removed buffs_display_nicely and instead added buff_frame.pack(side="right"). The side="right" tells tkinter to add the widgets from the right to the left.
Also you can improve your code by a lot if you add a class instead of using buff_frame.buff_birthday, buff_frame.buff_timer, ... Also it will make your life a lot easier

How to cancel or pause a urllib request in python

So I have this program which requests a file from the web and the user can download it. I am using urllib.request and tkinter for my program. The problem is that when the user hits the 'Download' button there is no pause or cancel until the file gets downloaded and the program freezes too. I really want to create a pause or a cancel button, but I do not know how and I want to eliminate the freezing of the program. Should I use another library like 'requests'? Or should I try threading? Can someone guide me through this?
My code(BTW if you know any way to improve my program I would appreciate it a lot if you shared it with me):
from tkinter import *
from tkinter import font as tkFont
import random
import urllib.request
import requests
from tqdm import tqdm
from tqdm.auto import tqdm
def printsth():
print("Yay it works! ")
def main_menu():
root = Tk()
# the top menu
num = IntVar()
# var = IntVar()
menu = Menu(root)
root.config(menu=menu)
submenu = Menu(menu)
menu.add_cascade(label="Settings", menu=submenu)
def custom_op():
custom = Tk()
custom.mainloop()
submenu.add_command(label="Customization ", command=custom_op)
def settings_op():
set_win = Tk()
set_win.mainloop()
submenu.add_command(label="Settings ", command=settings_op)
submenu.add_separator()
submenu.add_command(label="Exit", command=root.destroy)
# the edit menu
editmenu = Menu(menu)
menu.add_cascade(label="Edit", menu=editmenu)
editmenu.add_command(label="Redo...", command=printsth)
# the tool bar
toolbar = Frame(root, bg="light gray")
insert_button = Button(toolbar, text="Insert an image", command=printsth)
insert_button.pack(side=LEFT, padx=2, pady=2)
print_button = Button(toolbar, text="Print", command=printsth)
print_button.pack(side=LEFT, padx=2, pady=2)
toolbar.pack(side=TOP, fill=X)
# the download function
def download_image():
global formatname
if num.get() == 1:
name = random.randrange(1, 100000)
else:
name = str(name_entry.get())
formatname = str(format_entry.get())
'''if var.get() == 1:
operator = str(url_entry.get())
formatname = '.' + operator[-3] + operator[-2] + operator[-1]
else:
pass'''
fullname = str(name) + formatname
url = str(url_entry.get())
fw = open('file-size.txt', 'w')
file_size = int(requests.head(url, headers={'accept-encoding': ''}).headers['Content-Length'])
fw.write(str(file_size))
fw.close()
path = str(output_entry.get()) + "\\"
urllib.request.urlretrieve(url, path.replace("\\", "\\\\") + fullname)
# the status bar
status_bar = Label(root, text="Downloading...", bd=1, relief=SUNKEN, anchor=W)
status_bar.pack(side=BOTTOM, fill=X)
# the download frame
body_frame = Frame(root, bg="light blue")
download_button = Button(body_frame, text="Download! ", command=download_image, border=3, width=20, height=5)
download_design = tkFont.Font(size=12, slant='italic')
download_button['font'] = download_design
download_button.pack(side=LEFT, pady=5, padx=5)
body_frame.pack(side=LEFT, fill=Y)
# the main interaction menu
inter_frame = Frame(root)
url_entry = Entry(inter_frame)
label = Label(inter_frame, text="Enter the image URL: ")
file_format = Label(inter_frame, text="Choose your file format: ")
format_entry = Entry(inter_frame)
file_name = Label(inter_frame, text="File's name: ")
name_entry = Entry(inter_frame)
check_name = Checkbutton(inter_frame, text="Give a random name", variable=num)
# check_format = Checkbutton(inter_frame, text="Download with default format", variable=var)
output_path = Label(inter_frame, text="Choose output path: ")
output_entry = Entry(inter_frame)
file_name.pack(anchor=CENTER, expand=1)
name_entry.pack(anchor=CENTER, expand=1)
check_name.pack(anchor=CENTER, expand=1)
label.pack(anchor=CENTER, expand=1)
url_entry.pack(anchor=CENTER, expand=1)
file_format.pack(anchor=CENTER, expand=1)
format_entry.pack(anchor=CENTER, expand=1)
# check_format.pack(anchor=CENTER)
output_path.pack(anchor=CENTER, expand=1)
output_entry.pack(anchor=CENTER, expand=1)
inter_frame.pack(expand=1)
root.mainloop()
# the end!
main_menu()
You can use reporthook option of urllib.request.urlretrieve() to associate a callback and abort the download by raising exception inside the callback:
downloading = False # flag to indicate whether download is active
def download_progress(count, blksize, filesize):
nonlocal downloading
if downloading:
downloaded = count * blksize
print('downloaded %s / %s' % (downloaded, filesize))
root.update() # let user interact with the GUI
else:
# user selects to abort the download
raise Exception('download aborted!')
# the download function
def download_image():
global formatname
nonlocal downloading
if downloading:
downloading = False
return
download_button.config(text='Stop!') # let user to click the button to abort download
downloading = True
if num.get() == 1:
name = random.randrange(1, 100000)
else:
name = str(name_entry.get())
formatname = str(format_entry.get())
'''if var.get() == 1:
operator = str(url_entry.get())
formatname = '.' + operator[-3] + operator[-2] + operator[-1]
else:
pass'''
fullname = str(name) + formatname
url = str(url_entry.get())
fw = open('file-size.txt', 'w')
file_size = int(requests.head(url, headers={'accept-encoding': ''}).headers['Content-Length'])
fw.write(str(file_size))
fw.close()
path = str(output_entry.get()) + "\\"
try:
urllib.request.urlretrieve(url, path.replace("\\", "\\\\")+fullname, download_progress) # added reporthook callback
except Exception as e:
print(e) # download aborted
else:
print('done')
download_button.config(text='Download!') # resume download button
The text of download_button is changed to Stop! after it is clicked so that user can click it again to abort the download. When the download is aborted/completed, its text is changed back to "Download!".

having trouble in setting up a calendar in tkinter

i have a small ui programm and i need to display a calendar in it or a date picker . (NOTE : userid and password is root")**
i have tried this code :
from Tkinter import *
from PIL import Image, ImageTk
import ttkcalendar
class App():
def __init__(self):
pass
root = Tk()
root.configure(bg='black')
root.attributes('-alpha', 0.0)
def mainWindow(self):
self.root.geometry('{}x{}'.format(600,400))
self.root.attributes('-alpha', 1)
self.root.configure(bg='#404040')
self.ttkcal = ttkcalendar.Calendar(self.root,firstweekday=calendar.SUNDAY)
self.ttkcal.pack()
self.root.wm_title("time sheet management system")
# Create the toolbar as a frame
self.toolbar = Frame(self.root, borderwidth=1, relief='raised')
self.toolbar.configure( bg = '#838383')
# Load all the images first as PNGs and use ImageTk to convert
# them to usable Tkinter images.
self.img1 = Image.open('NewIcon.png')
self.useImg1 = ImageTk.PhotoImage(self.img1)
self.img2 = Image.open('LoadIcon.png')
self.useImg2 = ImageTk.PhotoImage(self.img2)
self.img3 = Image.open('SaveIcon.png')
self.useImg3 = ImageTk.PhotoImage(self.img3)
# Set up all the buttons for use on the toolbars.
newBtn = Button(self.toolbar, image=self.useImg1, command=self.callback)
newBtn.pack(side=LEFT, fill=X)
loadBtn = Button(self.toolbar, image=self.useImg2, command=self.callback)
loadBtn.pack(side=LEFT, fill=X)
saveBtn = Button(self.toolbar, image=self.useImg3, command=self.callback)
saveBtn.pack(side=LEFT, fill=X)
# Add the toolbar
self.toolbar.pack(side=TOP, fill=X)
"""
#create a frame
self.infoArea= Frame(self.root, borderwidth=2,height=40,width=100, relief='raised',bg='red')"""
self.root.mainloop()
"""
# Set up a Text box and scroll bar.
self.scrollbar = Scrollbar(self.root)
self.scrollbar.pack(side=RIGHT, fill=Y)
self.text = Text(self.root)
self.text.pack()
self.text.config(yscrollcommand=self.scrollbar.set)
self.scrollbar.config(command=self.text.yview)"""
def loginClick(self):
self.userid = self.txt1.get()
self.password = self.txt2.get()
print self.userid,self.password
if self.password == 'root' and self.userid == 'root':
self.wi.destroy()
self.mainWindow()
def callback(self):
print "A button was pressed"
def login(self):
self.wi = Toplevel (self.root)
#self.wi.geometry('{}x{}'.format(200,200))
self.wi.configure(bg='#404040')
self.wi.overrideredirect(1)
self.wi.update_idletasks()
self.w = self.wi.winfo_screenwidth()
self.h = self.wi.winfo_screenheight()
self.size = tuple(int(_) for _ in self.wi.geometry().split('+')[0].split('x'))
self.x = self.w/2 - self.size[0]/2
self.y = self.h/2 - self.size[1]/2
self.wi.geometry("%dx%d+%d+%d" % (self.size + (self.x, self.y)))
self.wi.geometry('{}x{}'.format(400,200))
self.frame1 = Frame ( self.wi,bg='#404040' )
self.frame1.pack(side=BOTTOM,fill=X)
self.butt1 = Button(self.frame1,text= "Cancel" , bg = '#838383',fg='white',command = self.wi.destroy)
self.butt1.pack(side=RIGHT,padx=1)
self.butt2 = Button(self.frame1,text= "Login" ,bg = '#838383',fg='white',command = self.loginClick)
self.butt2.pack(side=RIGHT)
"""self.frame2 = Frame ( self.wi,bg='green' )
self.frame2.pack(side=BOTTOM,fill=BOTH)"""
self.lab1 = Label (self.wi,text='UserID',bg='#404040',fg='white')
#self.lab1.pack(side=LEFT,padx=60,pady=20)
self.lab1.place(x=60,y=20)
self.lab2 = Label (self.wi,text='Password',bg='#404040',fg='white')
#self.lab1.pack(side=LEFT,padx=60,pady=20)
self.lab2.place(x=60,y=60)
self.txt1 = Entry( self.wi)
self.txt1.place(x=140,y=20)
self.txt2 = Entry( self.wi,show='*')
self.txt2.place(x=140,y=60)
"""
self.label = Label(self.wi,text="UserID",bg='#838383',fg='white')
self.label.place("""
self.wi.mainloop()
if __name__ == "__main__":
a = App()
#a.root.mainloop()
a.login()
but it is giving error that "name calendar not defined". how can i solve this ?
or is there any other way to implement a calendar or date picker in tkinter.
The code is using calendar module, but it is not importing the module.
self.ttkcal = ttkcalendar.Calendar(self.root, firstweekday=calendar.SUNDAY)
^^^^^^^^^^^^^^^
Simply adding following import statement will solve the problem.
import calendar

Categories