I learned how to implement ttk calendar from this post Python Tkinter ttk calendar
All I want is including two ttk calendars button in my GUI interface. One for the 'arrival date' button and another for the 'return date' button. However, after I tried to include these two buttons in my GUI window, my GUI window becomes very laggy and slow to be loaded and it sometimes even freezes. Can someone give me some suggestion on what is the problem here?
Based on the link above, I tried to include on ttk calendar button first and everything works fine and my GUI window works smoothly. However, as long as I have two ttk calendar buttons being included, the whole GUI window is very laggy.
# from stackoverflow
# https://stackoverflow.com/questions/48298195/python-tkinter-ttk-calendar
class MyDateEntry(DateEntry):
def __init__(self, master=None, **kw):
DateEntry.__init__(self, master=master, **kw)
# add black border around drop-down calendar
self._top_cal.configure(bg='black', bd=1)
# add label displaying today's date below
tk.Label(self._top_cal, bg='gray90', anchor='w',
text='Today: %s' % date.today().strftime('%x')).pack(fill='x')
...
...
...
# first button
ttk.Label(self.frame_entry_left_col, text='Arrival Date:').grid(row=6, column=0, padx=5, pady=(5, 0), sticky=tk.W)
self.fldArrivalDate = MyDateEntry(self.color, master=self.frame_entry_left_col, font=("Calibri", 8), background=self.color.secondary, width=17, selectmode='day')
self.fldArrivalDate.grid(row=7, column=0, padx=5, pady=(0, 6))
# second button
ttk.Label(self.frame_entry_left_col, text='Return Date:').grid(row=8, column=0, padx=5, pady=(5, 0), sticky=tk.W)
self.fldReturnDate = MyDateEntry(self.color, master=self.frame_entry_left_col, font=("Calibri", 8), width=17, selectmode='day')
self.fldReturnDate.grid(row=9, column=0, padx=5, pady=(0, 6))
I expect both buttons can work smoothly.
I don't reproduce your slowdown on Windows with Python 3.7. Your example is not really complete enough so what I actually tested is below.
A possible missing cause of the slowup is if you have conflicting pack and grid geometry management in a single frame. We can't see that from the code provided. In the below example you could pack the text widget and see if that causes a problem. In the version of Tkinter with Python 3.7 doing both packa nd grid in the same frame will raise an error but depending on the version of Tk you are using this might not occur on Python 2.7 and at one point this caused a lockup in Tk as the geometry managers fought against each other.
# https://stackoverflow.com/q/56811713/291641
#
# pip install tkcalendar
import sys
import tkinter as tk
import tkinter.ttk as ttk
from tkcalendar import DateEntry
from datetime import date
class MyDateEntry(DateEntry):
def __init__(self, master=None, **kw):
DateEntry.__init__(self, master=master, **kw)
# add black border around drop-down calendar
self._top_cal.configure(bg='black', bd=1)
# add label displaying today's date below
tk.Label(self._top_cal, bg='gray90', anchor='w',
text='Today: %s' % date.today().strftime('%x')).pack(fill='x')
def main(args=None):
root = tk.Tk()
frame = ttk.Frame(root)
row = 0
for name in ['Arrival' , 'Departure']:
label = ttk.Label(frame, text=name + ': ')
cal = MyDateEntry(master=frame, width=17, selectmode='day')
label.grid(row=row, column=0, sticky='news')
cal.grid(row=row, column=1, sticky='news')
row += 1
text = tk.Text(frame)
text.grid(row=row, columnspan=2, sticky='news')
frame.grid_rowconfigure(row, weight=1)
frame.grid_columnconfigure(1, weight=1)
frame.grid(row=0, column=0, sticky='news')
root.grid_rowconfigure(0, weight=1)
root.grid_columnconfigure(0, weight=1)
root.mainloop()
return 0
if __name__ == '__main__':
sys.exit(main(sys.argv[1:]))
Related
I'm working on numercial simulations in Abaqus 2020 and coded a GUI with Tkinter.
My problem is, that while running the Tkinter code everything works well unitl the start of the mainloop. Then both windows of Tkinter and Abaqus freeze. When I first close the Abaqus window, the Tkinter Window works correctly. I tried sth with threads but wihtout success..
Setup: Windows 10, Abaqus 2020, Python 2.7
here you find a minimal example:
import Tkinter as tk
from Tkinter import *
# Creating master Tkinter window
window = tk.Tk()
window.title('TKINTER-mini')
# window.attributes("-fullscreen", True)
width=1200
window.geometry("1800x1000")
# Frames
frame0 = Frame(window, width=width)
label_Titel = tk.Label(master=frame0, text ='Test', font=('Aerial 15 bold'))
label_Titel.grid(row=0, column = 0)
frame0.grid(row=0, column=2, padx=20, pady=20, ipadx = 20, ipady=20)
window.mainloop()
# window.update()
I'm looking forward to get some help. Thank you in advance!
Greets JBKingPile
I tried
#1
def __init__(self):
import threading
threading.Thread.__init__(self)
def shutdown_ttk_repeat(self):
self.mainroot.eval('::ttk::CancelRepeat')
self.mainroot.destroy()
def __init__(self, parent):
self.mainroot = parent
# self.mainroot.protocol("WM_DELETE_WINDOW", self.shutdown_ttk_repeat)
# self.tabpage()
self.root.after(1000, self.refresh())
self.root.mainloop()
#2
try:
# Tk will crash if pythonw.exe has an XP .manifest
# file and the root has is not destroyed explicitly.
# If the problem is ever fixed in Tk, the explicit
# destroy can go.
try:
gui = gui(window)
window.mainloop()
finally:
window.destroy()
except KeyboardInterrupt:
pass
#3
if __name__ == "__main__":
app = simpleapp_tk(window)
window.mainloop()
How can I do in tkinter something similar to a messagebox, but that doesn't have any buttons, including the close, minimize and maximize buttons on top?
I want it to be in a different window (like a message box), and to be able to update the text and close it only within the code.
Is there a way to do something like that?
Make your own. For example:
try: #python3 imports
import tkinter as tk
except ImportError: #python3 failed, try python2 imports
import Tkinter as tk
class Popup(tk.Toplevel):
"""modal window requires a master"""
def __init__(self, master, **kwargs):
tk.Toplevel.__init__(self, master, **kwargs)
self.overrideredirect(True)
self.geometry('300x200+500+500') # set the position and size of the popup
lbl = tk.Label(self, text="Please wait for other players to join ... ")
lbl.place(relx=.5, rely=.5, anchor='c')
# The following commands keep the popup on top.
# Remove these if you want a program with 2 responding windows.
# These commands must be at the end of __init__
self.transient(master) # set to be on top of the main window
self.grab_set() # hijack all commands from the master (clicks on the main window are ignored)
### demo usage:
def open_popup():
root.popup = Popup(root)
# close the popup in 2 seconds
root.after(2000, close_popup)
def close_popup():
root.popup.destroy()
root = tk.Tk()
btn = tk.Button(root, text='Open Modal Window', command=open_popup)
btn.pack()
root.mainloop()
Shortened the code to minimum for the question. I want to toggle between images to apply on the canvas using radiobuttons. The code will only apply the images if I enter the image name, (ex. tkimg2) into the 'stamp' event. The radiobuttons are not selecting the images, nor does the link work if I enter the image name in picture=[]. Do you know why the image name is sufficient in one location and not the other and why the radiobuttons don't work? Thank you for any help
from Tkinter import *
import PIL
from PIL import ImageTk, Image
import random
import os.path
root = Tk()
shapes = []
#load 2 images for stamping
__dir__ = os.path.dirname(os.path.abspath(__file__))
filename = os.path.join(__dir__, 'balloon.jpg')
img = PIL.Image.open(filename)
tkimg=PIL.ImageTk.PhotoImage(img)
filename2 = os.path.join(__dir__, 'bird1.jpg')
img2 = PIL.Image.open(filename2)
tkimg2=PIL.ImageTk.PhotoImage(img2)
picture =[]
image=picture
# A Radiobutton to toggle between images
radio = [0]*2
v = IntVar()
def call():
if int(float(str(v.get())))==1:
picture=tkimg
else:
picture=tkimg2
Label(root, text ="Select an image to place.").grid(row=1, column=0,
columnspan=5, sticky=S)
R1=Radiobutton(root, text="Bird 1", variable=v, value=1, command=call)
R1.grid(row=2, column=0, sticky=N+E)
R1.select()
R2=Radiobutton(root, text="Bird 2", variable=v, value=2, command=call)
R2.grid(row=2, column=1, sticky=N+E)
# A canvas for mouse events and image drawing
canvas = Canvas(root, height=1000, width=1000, bg='#2EEAFF')
canvas.grid(column=5, row=0, rowspan=4, sticky=W)
# Bind a function to the left mouse button down event.
def stamp(event):
canvas.create_image(event.x,event.y,image)
canvas.bind('<ButtonPress-1>', stamp)
# Enter event loop
root.mainloop()
I removed most of the code not relevant to the question and changed the images to ordinary PhotoImages for simplicity. Also I changed the first positioning of the image on the canvas.
If you want to assign a value to a variable inside a function you'll have to make it global or it will not work. The variable will be defined in the local function scope and will be garbage collected when the function ends.
I don't think you can change an image on a canvas by updating the variable you used to create it. That's how a StringVar functions.
As the image is the only widget on the canvas I delete ALL items and then create a new image when I toggle images.
Also: I use Python 3.6 so I spell tkinter without the capital T.
from tkinter import *
root = Tk()
#load 2 images for stamping
tkimg = PhotoImage(file='test.gif') # Test image
tkimg2 = PhotoImage(file='tesu.gif') # Test image
# A Radiobutton to toggle between images
v = IntVar()
def call():
canvas.delete(ALL)
if v.get() == 1:
canvas.create_image((2, 2), image=tkimg, anchor=NW)
else:
canvas.create_image((2, 2), image=tkimg2, anchor=NW)
Label(root, text ="Select an image to place.").grid(row=1, column=0, columnspan=5, sticky=S)
R1=Radiobutton(root, text="Bird 1", variable=v, value=1, command=call)
R1.grid(row=2, column=0, sticky=N+E)
R1.select()
R2=Radiobutton(root, text="Bird 2", variable=v, value=2, command=call)
R2.grid(row=2, column=1, sticky=N+E)
# A canvas for mouse events and image drawing
canvas = Canvas(root, height=200, width=200, bg='#2EEAFF')
canvas.grid(column=5, row=0, rowspan=4, sticky=W)
canvas.create_image((2, 2), image=tkimg, anchor=NW)
# Enter event loop
root.mainloop()
I want each tab to come from it's own class (classes are in their own files - I am just testing the first one for now).
Here is what I tried:
tab1.py
from Tkinter import *
import Tkinter as tk
class Tab(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self, master)
fr = Frame(self).pack()
Label(fr, text="one", bg='red', bd=2).pack()
Label(fr, text="two", bg='yellow', bd=2).pack()
if __name__ == '__main__':
root = Tk()
frame = Frame(root).pack()
Tab(frame)
Button(frame, text='only if class', command=root.destroy).pack()
mainloop()
noteBook.py
from Tkinter import *
from ttk import *
from tab1 import Tab
root = Tk()
note = Notebook(root)
main_frame = Frame(note)
button1 = Button(main_frame, text='test').pack()
#tab1 = Tab(note)
tab1 = Frame(note)
tab2 = Frame(note)
tab3 = Frame(note)
Tab(tab1)
Button(tab1, text='Exit', command=root.destroy).pack()
note.add(tab1, text = "Tab One", compound=TOP)
note.add(tab2, text = "Tab Two")
note.add(tab3, text = "Tab Three")
note.pack()
root.mainloop()
exit()
run with:
python2.7 noteBook.py
The problem is that the content of tab1.py does not appear within the first tab, it instead appears within the frame that contains the whole noteBook.
Also when running tab1.py directly with python2.7 noteBook.py I need it to behave properly meaning from what it has now it should show just the tab with an extra button from the if __name___... part.
I have come accros multiple examples but only found one that was what I want but it had no working solution and it was for python3 - I would like python2. python3 question with no working answer Thanks.
The problem is this line of code:
fr = Frame(self).pack()
When you do the above, fr is None because .pack() returns None (because x().y() returns the value of y()). Later, you do this:
Label(fr, text="one", bg='red', bd=2).pack()
Since fr is None, the label is created in the root window.
Unrelated to the problem, here's some advice: you are creating too many frames. You don't need fr inside of Tab, and you don't need tab1, tab2, or tab3
Here's all you need for Tab:
class Tab(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self, master, background="pink")
Label(self, text="one", bg='red', bd=2).pack()
Label(self, text="two", bg='yellow', bd=2).pack()
To add it to the notebook, you just need two lines:
tab1 = Tab(note)
note.add(tab1, text = "Tab One", compound=TOP)
This works perfectly and just for fun I've illustrated the populating of tabs 2 and 3 althought I just reused the same class for simplicity here. The goal was to be able to run the tabs directly to view them alone during developpement without having to run the whole thing every time.
noteBook.py
from Tkinter import *
from ttk import *
from tab1 import Tab
root = Tk()
note = Notebook(root)
main_frame = Frame(note)
button1 = Button(main_frame, text='test').pack()
tab1 = Frame(note)
tab2 = Frame(note)
tab3 = Frame(note)
Tab(tab1)
Tab(tab2)
Tab(tab3)
Button(tab1, text='Exit', command=root.destroy).pack()
note.add(tab1, text = "Tab One", compound=TOP)
note.add(tab2, text = "Tab Two")
note.add(tab3, text = "Tab Three")
note.pack()
root.mainloop()
exit()
tab1.py
import Tkinter as tk
class Tab(tk.Frame):
def __init__(self, parent_widget):
tk.Frame.__init__(self, parent_widget)
self.fr = tk.Frame(parent_widget, width=200, height=200, bg='pink', bd=2)
tk.Label(self.fr, text="one", bg='red', bd=2).pack()
tk.Label(self.fr, text="two", bg='yellow', bd=2).pack()
self.fr.pack() # this packing must be done after 2 above packings
if __name__ == '__main__':
root = tk.Tk() # the app window
main_frame = tk.Frame(root, height=200, width=200, bg='blue', bd=2) # main frame
Tab(main_frame) # instatiate Tab(), sending main_frame as the parent_widget
tk.Button(main_frame, text='only if class', command=root.destroy).pack()
main_frame.pack() # display main frame on window
tk.mainloop()
So I was doing this program and noticed that both my buttons look initially like this
And after I run my program for some time the second button changes it appearance to this
When does this happen?
Here's my code :/ in case I am doing something that should not be done. I am doing this in python 2.7.8 in IDLE.
import time
import Tkinter as tk
from Tkinter import StringVar
import threading
global root
root = tk.Tk()
x = tk.StringVar()
x.set('false')
def xval(*args):
try:
for i in range(0,9):
global x
print x.get()
if x.get()== 'false' :
print "x=false %d time"%i
time.sleep(1)
else:
print "waiting"
root.update()
except:
pass
def stop(event):
resume_btn.configure(state="normal")
global x
x.set('true')
print "execution stopped:%s"%x
def start(event):
global x
x.set('false')
print "execution started:%s"%x
xval()
root.title("GUI-Data Retrieval")
th = threading.Event()
t = threading.Thread(target=xval,args=(th,))
t.deamon=True
t.start()
x_btn = tk.Button(root, text="Stop", background="Snow", width=20, relief="raised")
x_btn.grid(row=0, column=4, sticky="W", padx=20, pady=5)
x_btn.bind('<Button-1>',stop)
resume_btn = tk.Button(root, text="Start", background="Snow", width=20, relief="raised")
resume_btn.configure(state="disabled")
resume_btn.grid(row=0, column=6, sticky="W", padx=20, pady=5)
resume_btn.bind('<Button-1>',start)
root.mainloop()
The problem is that your binding is handled before the default bindings. It is the default bindings that change the appearance of the button when it is clicked on. You are disabling the button on a click, preventing the default behavior from resetting the appearance of the button when you release the mouse button.
Unless there's a specific reason to do otherwise, you should use the command attribute of the button widget rather than try to create your own bindings.