I'm using Tkinter as GUI for my program, but as I see, many programs don't have standard look as Tkinter does. By standard look I mean standard title bar, borders, etc.
For example, Tkinter's title bar:
vs GitHub's title bar:
See how they have their own custom exit, resize and minimize buttons? Is it possible to achieve that look using Tkinter?
Thanks in advance! :)
Yes it's possible. You can use the overrideredirect() method on the root window to kill the title bar and the default geometry settings. After that, you need to rebuild all those methods from scratch to set it back up like you want. Here's a small working example with minimal functionality:
root = Tk()
def move_window(event):
root.geometry('+{0}+{1}'.format(event.x_root, event.y_root))
root.overrideredirect(True) # turns off title bar, geometry
root.geometry('400x100+200+200') # set new geometry
# make a frame for the title bar
title_bar = Frame(root, bg='white', relief='raised', bd=2)
# put a close button on the title bar
close_button = Button(title_bar, text='X', command=root.destroy)
# a canvas for the main area of the window
window = Canvas(root, bg='black')
# pack the widgets
title_bar.pack(expand=1, fill=X)
close_button.pack(side=RIGHT)
window.pack(expand=1, fill=BOTH)
# bind title bar motion to the move window function
title_bar.bind('<B1-Motion>', move_window)
root.mainloop()
Most will know there is an error when using the 'move_window' method used above; I found a fix that gets the exact position of the mouse and moves with that rather than from the corner:
def get_pos(event):
xwin = app.winfo_x()
ywin = app.winfo_y()
startx = event.x_root
starty = event.y_root
ywin = ywin - starty
xwin = xwin - startx
def move_window(event):
app.geometry("400x400" + '+{0}+{1}'.format(event.x_root + xwin, event.y_root + ywin))
startx = event.x_root
starty = event.y_root
app.TopFrame.bind('<B1-Motion>', move_window)
app.TopFrame.bind('<Button-1>', get_pos)
I found a way of making the title bar black using ctypes: (win11 only)
Tkinter dark title bar example:
import ctypes as ct
def dark_title_bar(window):
"""
MORE INFO:
https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute
"""
window.update()
DWMWA_USE_IMMERSIVE_DARK_MODE = 20
set_window_attribute = ct.windll.dwmapi.DwmSetWindowAttribute
get_parent = ct.windll.user32.GetParent
hwnd = get_parent(window.winfo_id())
rendering_policy = DWMWA_USE_IMMERSIVE_DARK_MODE
value = 2
value = ct.c_int(value)
set_window_attribute(hwnd, rendering_policy, ct.byref(value),
ct.sizeof(value))
I searched almost a year for a solution!
These are the modifications that I have made using python 3.7.2
from tkinter import *
root = Tk()
root.overrideredirect(True) # turns off title bar, geometry
root.geometry('400x100+200+200') # set new geometry
# make a frame for the title bar
title_bar = Frame(root, bg='#2e2e2e', relief='raised', bd=2,highlightthickness=0)
# put a close button on the title bar
close_button = Button(title_bar, text='X', command= root.destroy,bg = "#2e2e2e",padx = 2,pady = 2,activebackground='red',bd = 0,font="bold",fg='white',highlightthickness=0)
# a canvas for the main area of the window
window = Canvas(root, bg='#2e2e2e',highlightthickness=0)
# pack the widgets
title_bar.pack(expand=1, fill=X)
close_button.pack(side=RIGHT)
window.pack(expand=1, fill=BOTH)
xwin=None
ywin=None
# bind title bar motion to the move window function
def move_window(event):
root.geometry('+{0}+{1}'.format(event.x_root, event.y_root))
def change_on_hovering(event):
global close_button
close_button['bg']='red'
def return_to_normalstate(event):
global close_button
close_button['bg']='#2e2e2e'
title_bar.bind('<B1-Motion>', move_window)
close_button.bind('<Enter>',change_on_hovering)
close_button.bind('<Leave>',return_to_normalstate)
root.mainloop()
Explanation:
We use bd(border thickness)=0 to remove the borders from the the button
Then we bind the <Enter> event to a function which
changes the foreground color.
And to return to its original state we bind the <Leave> event to another function
Initial State
Change in state after hovering mouse cursor over it
Note : The cursor is not visible because my screen capture software somehow removed it
In python3.5.2 I had to make some modifications to get this to work:
#custom title bar for tkinter
from tkinter import Tk, Frame, Button, Canvas
root = Tk()
def move_window(event):
root.geometry('+{0}+{1}'.format(event.x_root, event.y_root))
root.overrideredirect(True) # turns off title bar, geometry
root.geometry('400x100+200+200') # set new geometry
# make a frame for the title bar
title_bar = Frame(root, bg='white', relief='raised', bd=2)
# put a close button on the title bar
close_button = Button(title_bar, text='Close this Window', command=root.destroy)
# a canvas for the main area of the window
window = Canvas(root, bg='black')
# pack the widgets
title_bar.pack(expand=1, fill="x")
close_button.pack(side="right")
window.pack(expand=1, fill="both")
# bind title bar motion to the move window function
title_bar.bind('<B1-Motion>', move_window)
root.mainloop()
enter image description herehere you go updated to python 3.8
and new variable for title bar background and for
main content background and title name add
and new background + clear some indention errors
from tkinter import *
root = Tk()
# turns off title bar, geometry
root.overrideredirect(True)
# set new geometry
root.geometry('400x100+200+200')
# set background color of title bar
back_ground = "#2c2c2c"
# set background of window
content_color = "#ffffff"
# make a frame for the title bar
title_bar = Frame(root, bg=back_ground, relief='raised', bd=1, highlightcolor=back_ground,highlightthickness=0)
# put a close button on the title bar
close_button = Button(title_bar, text='x', command=root.destroy,bg=back_ground, padx=5, pady=2, activebackground="red", bd=0, font="bold", fg='white', activeforeground="white", highlightthickness=0)
# window title
title_window = "Title Name"
title_name = Label(title_bar, text=title_window, bg=back_ground, fg="white")
# a canvas for the main area of the window
window = Canvas(root, bg="white", highlightthickness=0)
# pack the widgets
title_bar.pack(expand=1, fill=X)
title_name.pack(side=LEFT)
close_button.pack(side=RIGHT)
window.pack(expand=1, fill=BOTH)
x_axis = None
y_axis = None
# bind title bar motion to the move window function
def move_window(event):
root.geometry('+{0}+{1}'.format(event.x_root, event.y_root))
def change_on_hovering(event):
global close_button
close_button['bg'] = 'red'
def return_to_normal_state(event):
global close_button
close_button['bg'] = back_ground
title_bar.bind('<B1-Motion>', move_window)
close_button.bind('<Enter>', change_on_hovering)
close_button.bind('<Leave>', return_to_normal_state)
root.mainloop()
This is how it looksI think this is the easy one. I used it on one of my project , due to some reasons from client side, client was not able to make any payment for that project and a lot of time got wasted.
from cProfile import label
from tkinter import *
import ttkbootstrap as ttk
from ttkbootstrap import Style
from tkinter import messagebox as m_box
win = Tk()
win.overrideredirect(True)
win.attributes("-topmost", True)
win.geometry("600x300+300+300")
win.resizable(1, 1)
style = Style("cyborg")
# ============================================================= Title bar Header
def ext():
exit()
def minim():
win.overrideredirect(0)
win.wm_state("iconic")
win.overrideredirect(1)
def about():
pass
m_box.showinfo("About", "Developer: Vivek phogat\nemail: Chaudharyvivekphogat#outlook.com")
#------------------------------- Header section
Header_frame = ttk.Label(win)
info_label = ttk.Label(Header_frame,text="Customized title bar enjoy :-)",bootstyle="WARNING",font=("Comic Sans MS", 15))
info_label.pack(padx=5, pady=5,side = LEFT)
win_close_btn1 = ttk.Button(Header_frame, text=" X ", command=(win.destroy), bootstyle="danger")
win_close_btn1.pack( side = RIGHT,anchor= NE)
Min_but = ttk.Button(Header_frame, text=" _ ", command=minim, bootstyle="light")
Min_but.pack( side = RIGHT,anchor= NE)
about_btn1 = ttk.Button(Header_frame, text=" a ", command=about, bootstyle="secondary")
about_btn1.pack(side = RIGHT,anchor= NE)
Header_frame.pack(fill=X)
#-------------------------------
#title bar get position.
def get_pos(event):
global xwin
global ywin
xwin = event.x
ywin = event.y
#title bar drag functon.
def drag(event):
win.geometry(f"+{event.x_root - xwin}+{event.y_root - ywin}")
Header_frame.bind("<B1-Motion>", drag)
Header_frame.bind("<Button-1>", get_pos)
info_label.bind("<B1-Motion>", drag)
info_label.bind("<Button-1>", get_pos)
Footer_frame = ttk.Frame(win)
label = ttk.Label(Footer_frame, text= " Hope you all are doing good enjoy the code.",bootstyle="WARNING",font=("Comic Sans MS", 20)).pack()
Footer_frame.pack(expand= TRUE )
win.mainloop()
ok i left the old answer if some need it.
it is a recreated from scratch custom taskbar that assemble windows 10 bar
from tkinter import *
window = Tk()
bg = "#f5f6f7"
title_window = "test app"
class app:
def __init__(self, main):
self.main = main
self.main.configure(bg=bg)
self.main.overrideredirect(True)
self.main.geometry('230x130')
self.main.resizable(width=False, height=False)
self.top_bar = Frame(main,bg=bg, cursor="sizing")
self.top_bar.pack(fill=X)
self.title_txt = Label(self.top_bar, text=title_window ,bg=bg)
self.title_txt.pack(side="left", padx=3)
close_btn = Button(self.top_bar,text="x", cursor="arrow", bg=bg, fg="black", highlightthickness=0,activebackground="red", activeforeground="white",bd=0, command=self.main.quit)
close_btn.pack(side="right")
bottom_bar = Frame(main, bg=bg)
bottom_bar.pack()
label_scr = Label(bottom_bar, text="label 1", padx=100, pady=5, bg=bg)
label_scr.grid(row=0, column=0, columnspan=3)
button_scr = Button(bottom_bar, text="Button1", bg=bg, bd=0)
button_scr.grid(row=2, column=0, columnspan=3, pady=3)
button2_scr = Button(bottom_bar, text="Button2", bg=bg,bd=0)
button2_scr.grid(row=3, column=0, columnspan=3, pady=3)
def move_window(event):
window.geometry(f"+{event.x_root}+{event.y_root}")
execution = app(window)
execution.top_bar.bind('<B1-Motion>', move_window)
execution.title_txt.bind('<B1-Motion>', move_window)
window.mainloop()
This is for me the best form:
import tkinter as tk
from turtle import title, width
root= tk.Tk()
root.title('Civ IV select songs')
canvas1 = tk.Canvas(root, width = 300, height = 600)
canvas1.pack()
here is my one
from tkinter import *
import windll
class CustomTitle():
"""
Ex:
root = Tk()
titleBar = CustomTitle(root,title_text = 'Hello,World!' , bg = "#000000" , fg = '#ffffff')
titleBar.resizeable = True
titleBar.packBar()
root.mainloop()
Note:
Try to Give Color value in Hex and the 3rd car should be number
#7a4e7a
↑ (this one)
"""
resizeable = True
font_style = ('Candara',13)
def __init__(self,win,title_text='Custom Title Bar',bg='#ffffff',fg="#000000"):
# deactivating main title bar
self._win = win
win.title(title_text)
# props
self.bg = bg
self._maximized = False
self._win_width = win.winfo_width()
self._win_height = win.winfo_height()
self._scr_width = win.winfo_screenwidth()
self._scr_height = win.winfo_screenheight()
self._addWidget(title_text,bg,fg)
def packBar(self):
self._title_bar.pack(fill=X)
self._checkAbility()
self._win.overrideredirect(1)
self._finilize()
def _checkAbility(self):
if not self.resizeable:
self._maximize_btn.config(state=DISABLED)
else:
self._resizey_widget.pack(side=BOTTOM,ipadx=.1,fill=X)
self._resizex_widget.pack(side=RIGHT,ipadx=.1,fill=Y)
def _maximize_win(self):
if not self._maximized:
self._past_size = root.geometry()
self._win.geometry(f"{self._scr_width}x{self._scr_height}+{0}+{0}")
self._maximize_btn.config(text = '🗗')
else:
self._win.geometry(self._past_size)
self._maximize_btn.config(text = '🗖')
self._maximized = not self._maximized
def _minimize(self):
Minimize = win32gui.GetForegroundWindow()
win32gui.ShowWindow(Minimize, win32con.SW_MINIMIZE)
def _setIconToTaskBar(self,mainWindow):
GWL_EXSTYLE = -20
WS_EX_APPWINDOW = 0x00040000
WS_EX_TOOLWINDOW = 0x00000080
# Magic
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)
mainWindow.wm_withdraw()
mainWindow.after(10, mainWindow.wm_deiconify)
def _addWidget(self,title_text,bg,fg):
self._title_bar = Frame(self._win,bd=1,bg=bg)
self._title_text = Label(self._title_bar,text=title_text,bg=bg,fg=fg,font=self.font_style)
self._title_text.pack(side=LEFT,padx=4,pady=3)
self._title_text.bind("<B1-Motion>",self._drag)
self._close_btn = Button(self._title_bar,text = '×',bd=0,bg=bg,fg=fg,width=3,font=self.font_style,command=self._win.destroy)
self._close_btn.pack(side=RIGHT,fill=Y)
self._maximize_btn = Button(self._title_bar,text="🗖",bd=0,bg=bg,fg=fg,width=3,font=self.font_style,command=self._maximize_win)
self._maximize_btn.pack(side=RIGHT,fill=Y)
self._minimize_btn = Button(self._title_bar,text="_",bd=0,bg=bg,fg=fg,width=3,font=self.font_style,command=self._minimize)
self._minimize_btn.pack(side=RIGHT,fill=Y)
self._title_bar.bind('<Button-1>', self._drag)
self._resizex_widget = Frame(self._win,cursor='sb_h_double_arrow')
self._resizex_widget.bind("<B1-Motion>",self._resizex)
self._resizey_widget = Frame(self._win,cursor='sb_v_double_arrow')
self._resizey_widget.bind("<B1-Motion>",self._resizey)
self._hover_effect()
def _hover_effect(self):
try:
num = int(self.bg[3]) - 1
newbg = self.bg.replace(self.bg[3],str(num))
except:
newbg = "#c7ebe8"
def change_bg(which_one,bg = newbg):
which_one.config(bg=bg)
def restore_bg(which_one):
which_one.config(bg=self.bg)
self._maximize_btn.bind('<Enter>',lambda event: change_bg(self._maximize_btn))
self._maximize_btn.bind('<Leave>',lambda event: restore_bg(self._maximize_btn))
self._minimize_btn.bind('<Enter>',lambda event: change_bg(self._minimize_btn))
self._minimize_btn.bind('<Leave>',lambda event: restore_bg(self._minimize_btn))
self._close_btn.bind('<Enter>',lambda event: change_bg(self._close_btn,bg='#db2730'))
self._close_btn.bind('<Leave>',lambda event: restore_bg(self._close_btn))
def _finilize(self):
self._win.after(10, lambda: self._setIconToTaskBar(self._win))
def _drag(self,event):
xwin = root.winfo_x()
ywin = root.winfo_y()
startx = event.x_root
starty = event.y_root
ywin = ywin - starty
xwin = xwin - startx
def _move_window(event): # runs when window is dragged
root.geometry(f'+{event.x_root + xwin}+{event.y_root + ywin}')
def _release_window(event): # runs when window is released
root.config(cursor="arrow")
self._title_bar.bind('<B1-Motion>', _move_window)
self._title_bar.bind('<ButtonRelease-1>', _release_window)
self._title_text.bind('<B1-Motion>', _move_window)
self._title_text.bind('<ButtonRelease-1>', _release_window)
def _resizex(self,event):
xwin = root.winfo_x()
difference = (event.x_root - xwin) - root.winfo_width()
if root.winfo_width() > 150 : # 150 is the minimum width for the window
try:
root.geometry(f"{ root.winfo_width() + difference }x{ root.winfo_height() }")
except:
pass
else:
if difference > 0: # so the window can't be too small (150x150)
try:
root.geometry(f"{ root.winfo_width() + difference }x{ root.winfo_height() }")
except:
pass
def _resizey(self,event):
ywin = root.winfo_y()
difference = (event.y_root - ywin) - root.winfo_height()
if root.winfo_height() > 150: # 150 is the minimum height for the window
try:
root.geometry(f"{ root.winfo_width() }x{ root.winfo_height() + difference}")
except:
pass
else:
if difference > 0: # so the window can't be too small (150x150)
try:
root.geometry(f"{ root.winfo_width() }x{ root.winfo_height() + difference}")
except:
pass
We have to change the DWMWindowAttributes for the tkinter window with ctypes which is under windll.dwmapi.DwmSetWindowAttribute
For changing the title bar color, we can use DWMWA_CAPTION_COLOR which is set as 35 (attribute number) for windows 11.
Here is an example:
import tkinter
from ctypes import windll, byref, sizeof, c_int
root = tkinter.Tk()
root.title("Tkinter Window")
root.update()
HWND = windll.user32.GetParent(root.winfo_id()) # the window we want to change
# These attributes are for windows 11
DWMWA_CAPTION_COLOR = 35
DWMWA_TITLE_COLOR = 36
COLOR_1 = 0x0000FFFF # color should be in hex order: 0x00bbggrr
COLOR_2 = 0x00008000
windll.dwmapi.DwmSetWindowAttribute(HWND, DWMWA_CAPTION_COLOR, byref(c_int(COLOR_1)), sizeof(c_int))
windll.dwmapi.DwmSetWindowAttribute(HWND, DWMWA_TITLE_COLOR, byref(c_int(COLOR_2)), sizeof(c_int))
root.mainloop()
more info about the color api used here:
https://learn.microsoft.com/en-us/windows/win32/gdi/colorref
Related
Hi guys i'm having trouble with a scrollbar, in the photo you can see the grey box that is a simple listbox, how to put the scrollbar in that red specific position, and not in the right or bottom end of the screen just like te normal scollbar? Thanks!
adding code:
from tkinter import *
from ctypes import windll
def inserisci_spesa():
global lista_spese
global testo_nuova_spesa
if testo_nuova_spesa.get() != "":
lista_spese.insert(0,testo_nuova_spesa.get())
def invio_aggiungi_spesa(e):
window.bind("<Return>",illumina_aggiungi_spesa)
def illumina_aggiungi_spesa(e):
bottone_inserisci_spesa.config(bg="#052b4d")
window.after(200,illumina_aggiungi_spesa2)
def illumina_aggiungi_spesa2():
bottone_inserisci_spesa.config(bg="#1e476b")
window.after(0,inserisci_spesa)
def invio_descrivi_spesa(e):
window.bind("<Return>",illumina_descrivi_spesa)
def illumina_descrivi_spesa(e):
global bottone_inserisci_descrizione
bottone_inserisci_descrizione.config(bg="#052b4d")
window.after(200,illumina_descrivi_spesa2)
def illumina_descrivi_spesa2():
global bottone_inserisci_descrizione
bottone_inserisci_descrizione.config(bg="#1e476b")
windll.shcore.SetProcessDpiAwareness(1)
window = Tk()
frame = Frame (window)
frame.pack(padx=150, pady=150)
window.geometry("1500x770")
window.title ("Gestione spese")
window.call('wm', 'iconphoto', window._w, PhotoImage(file="trasparente.png"))
sfondo = PhotoImage(file="soldi.png")
etichetta_sfondo = Label(window,image=sfondo)
etichetta_sfondo.place(x=0,y=0)
testo_nuova_spesa = Entry(window,borderwidth=5,font=("Ink Free",20),width=9,bg="#f2f2f2")
testo_nuova_spesa.place(x=36,y=80)
testo_nuova_spesa.bind("<FocusIn>",invio_aggiungi_spesa)
descrizione_testo_nuova_spesa = Label(window,text="Nuova spesa",bg="#64d981",font=("Ink Free",19),relief="solid",borderwidth=1)
descrizione_testo_nuova_spesa.place(x=40,y=28)
testo_descrivi_spesa = Entry(window,borderwidth=5,font=("Ink Free",20),width=22,bg="#f2f2f2")
testo_descrivi_spesa.place(x=300,y=80)
testo_descrivi_spesa.bind("<FocusIn>",invio_descrivi_spesa)
descrizione_testo_descrivi_spesa = Label(window,text="Descrizione",bg="#64d981",font=("Ink Free",19),relief="solid",borderwidth=1)
descrizione_testo_descrivi_spesa.place(x=304.5,y=28)
bottone_inserisci_spesa = Button(window,text="Inserisci",font=("Ink Free",15),bg="#1e476b",fg="white",activebackground="#052b4d",activeforeground="white",command=inserisci_spesa)
bottone_inserisci_spesa.place(x=36,y=140)
bottone_inserisci_descrizione = Button(window,text="Inserisci",font=("Ink Free",15),bg="#1e476b",fg="white",activebackground="#052b4d",activeforeground="white")
bottone_inserisci_descrizione.place(x=300,y=140)
lista_spese = Listbox(frame)
lista_spese.pack(side=LEFT)
lista_spese.configure(font=('Courier 20 '), width=21, height=9, bg="#4a4a4a", fg="#dedede",relief="solid",borderwidth=4)
etichetta_lista_spese = Label(window,text="Lista delle spese",bg="#64d981",font=("Ink Free",19),relief="solid",borderwidth=1)
etichetta_lista_spese.place(x=720,y=270)
scrollbar = Scrollbar(frame,command=lista_spese.yview)
scrollbar.pack(side=RIGHT,fill=Y)
lista_spese.config(yscrollcommand=scrollbar.set)
window.mainloop()
Simply put both (Listbox and Scrollbar) in the same Frame to group them.
Minimal working code
import tkinter as tk
root = tk.Tk()
root['bg'] = '#ff8080'
# - create -
frame = tk.Frame(root)
frame.pack(padx=150, pady=150)
listbox = tk.Listbox(frame)
listbox.pack(side='left', fill='both', expand=True)
scrollbar = tk.Scrollbar(frame, orient='vertical', command=listbox.yview)
scrollbar.pack(side='right', fill='y')
#scrollbar.pack(side='left', fill='y') # `left` also works
listbox.config(yscrollcommand=scrollbar.set)
# - add some values to listbox for scrolling -
for i in range(50):
listbox.insert('end', str(i))
# - start -
root.mainloop()
Result:
EDIT:
You may also use Frame to create own widget ScrolledListbox and then you can reuse it many times.
import tkinter as tk
class ScrolledListbox(tk.Frame):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.listbox = tk.Listbox(self)
self.listbox.pack(side='left', fill='both', expand=True)
self.scrollbar = tk.Scrollbar(self, orient='vertical', command=self.listbox.yview)
self.scrollbar.pack(side='right', fill='y')
#self.scrollbar.pack(side='left', fill='y') # `left` also works
self.listbox.config(yscrollcommand=self.scrollbar.set)
# - main -
root = tk.Tk()
root['bg'] = '#ff8080'
# - create -
lb1 = ScrolledListbox(root)
lb1.pack(side='left', fill='both', expand=True, padx=25, pady=25)
lb2 = ScrolledListbox(root)
lb2.pack(side='left', fill='both', expand=True, padx=25, pady=25)
lb3 = ScrolledListbox(root)
lb3.pack(side='left', fill='both', expand=True, padx=25, pady=25)
# - add some values to listbox for scrolling -
for i in range(50):
lb1.listbox.insert('end', str(i))
lb2.listbox.insert('end', str(i+100))
lb3.listbox.insert('end', str(i+200))
# - start -
root.mainloop()
This is to be a desktop application for opening multiple small databases and running queries on them. So far I've written some code for opening forms as necessary. Is this a good way to do it? Also - the code shown opens two copies of each form - what am I doing wrong? It's my first attempt at Python and I'm a rudimentary programmer so simple answers would be of most help please. TIA (Python 3.9.6)
link_1.py
from tkinter import Tk
import link_2
root = Tk()
class ClassLink_1:
#if another function in the class is called, the __init__ function is run at start up
def __init__(self):
print("in link_1 __init__ instruction")
#the call_function function can be called at start up, or not, and will act accordingly
def call_function(self):
print("in call_function")
#the line below is run at start up whether or not another function in the class is called
print("in link_1")
root.withdraw() #hides the blank form at start up
#if __name__ == "__main__":
#the line below shows the link_2 form, whether or not the if __name__==__main__ condition is used as its condition
link_2.ClassLink_2(root).__init__(root)
#link_3.ClassLink_3(root).__init__(root)
#the line below runs call_function on start up to print text
ClassLink_1().call_function()
root.mainloop()
link_2.py
from tkinter import Tk, Button
from tkinter import * #for Toplevel
import link_3
root = Tk()
class ClassLink_2:
def __init__(self, master):
self.openNewWindow()
def openNewWindow(self):
newWindow = Toplevel(root) #creates a top level widget with the parent root (first parameter)
newWindow.title("Title opened from link_1")
newWindow.geometry("500x500")
label = Label(newWindow, text ="Opened from link_1").grid(row=1, column=1)
self.add_button = Button(newWindow, text="in ClassLink_2", command= self.do_add)
self.add_button.grid(row=3, column=1)
def do_add(self):
print("button pressed")
link_3.ClassLink_3(root).__init__(root)
root.withdraw() #hides the blank form at start up
link_3.py
from tkinter import Tk, Button
from tkinter import * #for Toplevel
root = Tk()
class ClassLink_3:
def __init__(self, master):
self.openNewWindow()
def openNewWindow(self):
newWindow = Toplevel(root) #creates a top level widget with the parent root (first parameter)
newWindow.title("Title opened from link_2")
newWindow.geometry("500x500")
label = Label(newWindow, text ="Opened from link_2").grid(row=1, column=1)
self.add_button = Button(newWindow, text="in ClassLink_3", command= self.do_add)
self.add_button.grid(row=3, column=1)
def do_add(self):
print("button pressed")
# link_4.ClassLink_4(root).__init__(root) this file has not yet been made
root.withdraw() #hides the blank form at start up
This is a proposed solution, can be expanded as needed. Constructive suggestions for improvement of the structure or code would be appreciated. TIA. I've left in the details in case they are of use to anyone.
link_1
from tkinter import Tk
import link_2
root = Tk()
class ClassLink_1:
def __init__(self):
print("in link_1 __init__ instruction")
root.withdraw() #hides the blank form at start up
link_2.ClassLink_2(root).openNewWindow(0)
root.mainloop()
link_2
from tkinter import Tk, Button
from tkinter import *
import link_3
root = Tk()
class ClassLink_2:
root.withdraw() #hides the blank form at start up
class_var_1 = 0
inst_var_1 = 0
def __init__(self, incoming_inst_var_1):
pass
def openNewWindow(self, inst_var_1_to_open):
newWindow = Toplevel(root)
newWindow.title("Title opened from link_1")
newWindow.geometry("500x500")
label = Label(newWindow, text ="Opened from link_1").grid(row=1, column=1)
self.add_button = Button(newWindow, text="in ClassLink_2", command= self.do_add)
self.add_button.grid(row=3, column=1)
self.add_button = Button(newWindow, text="increment class_var_1", command= self.inc_class_var_1)
self.add_button.grid(row=5, column=1)
self.inst_var_1 = inst_var_1_to_open
def do_add(self):
print("button pressed")
link_3.ClassLink_3(root).openNewWindow(0)
def inc_class_var_1(self):
ClassLink_2.class_var_1 += 2
self.inst_var_1 += 1
print("self.class_var_1 = " + (str)(self.class_var_1))
print("self.inst_var_1 = " + (str)(self.inst_var_1))
link_3
from tkinter import Tk, Button
from tkinter import *
from tkinter.ttk import Combobox
import tkinter.scrolledtext as tkscrolled
# import link_4 <filename of next form>
root = Tk()
class ClassLink_3:
class_var_1 = 0
inst_var_1 = 0
# ------------------------------- START CLASS CONTROLS -----------------------------
root.withdraw()
def __init__(self, incoming_inst_var_1):
pass
def openNewWindow(self, inst_var_1_to_open):
new_window = Toplevel(root)
self.widget_factory(new_window)
self.inst_var_1 = inst_var_1_to_open
def do_add(self):
print("button pressed")
# link_4.ClassLink_4(root).openNewWindow(0) # <filename of next form>
# ---------------------------------- END CLASS CONTROLS -----------------------------
# -------------------------------------- START CALCS --------------------------------------
def inc_class_var_1(self):
ClassLink_3.class_var_1 += 2
self.inst_var_1 += 1
print("self.class_var_1 = " + (str)(self.class_var_1))
print("self.inst_var_1 = " + (str)(self.inst_var_1))
# ---------------------------------------- END CALCS --------------------------------------
# ---------------------------------------- START FACTORY SHOPS-----------------------------------------
def widget_factory(self, new_window):
self.shop_window(new_window)
self.shop_labels(new_window)
self.shop_buttons(new_window)
self.shop_menu(new_window)
self.shop_listbox(new_window)
self.shop_combobox(new_window)
self.shop_radiobuttons(new_window)
self.shop_checkbuttons(new_window)
self.shop_entries(new_window)
self.shop_canvas(new_window)
self.shop_scale(new_window)
self.shop_scrollbars(new_window)
self.shop_textbox(new_window)
self.shop_menu(new_window)
pass
# ------------------------
def shop_window(self, new_window):
new_window.title("Title opened from link_2")
new_window.geometry("800x900")
def shop_labels(self, new_window):
self.label_1 = Label(new_window, text ="Opened from link_2").grid(row=1, column=1)
def shop_buttons(self, new_window):
self.button_1 = Button(new_window, text="in ClassLink_3", command= self.do_add)
self.button_1.grid(row=3, column=1)
self.button_2 = Button(new_window, text="increment class_var_1", command= self.inc_class_var_1)
self.button_2.grid(row=5, column=1)
def shop_listbox(self, new_window):
data=("one", "two", "three", "four")
self.listbox_1 = Listbox(new_window, height=5, selectmode='multiple')
for num in data:
self.listbox_1.insert(END,num)
self.listbox_1.grid(row=7, column=1)
def shop_combobox(self, new_window):
data=("one", "two", "three", "four")
self.combobox_1 = Combobox(new_window, values=data)
self.combobox_1.grid(row=8, column=3)
def shop_radiobuttons(self, new_window):
var_1 = IntVar()
var_1.set(1)
self.rad_btn_1 = Radiobutton(new_window, text="male", variable=var_1, value=1)
self.rad_btn_2 = Radiobutton(new_window, text="female", variable=var_1, value=2)
self.rad_btn_1.grid(row=9, column=1)
self.rad_btn_2.grid(row=9, column=2)
def shop_checkbuttons(self, new_window):
var_1 = IntVar()
var_2 = IntVar()
self.checkbtn_1 = Checkbutton(new_window, text = "Cricket", variable = var_1)
self.checkbtn_2 = Checkbutton(new_window, text = "Tennis", variable = var_2)
self.checkbtn_1.grid(row=10, column=1)
self.checkbtn_2.grid(row=10, column=2)
def shop_entries(self, new_window):
self.entry_1 = Entry(new_window, width = 20)
self.entry_1.insert(0,'Username')
self.entry_1.grid(row= 11, column=2)
self.entry_2 = Entry(new_window, width = 15)
self.entry_2.insert(0,'password')
self.entry_2.grid(row= 12, column=2)
#might want to place on a frame, see example https://coderslegacy.com/python/list-of-tkinter-widgets/
def shop_canvas(self, new_window):
canvas = Canvas(new_window, bg= 'white', width = 260,height = 260) #this is the background for the figure
# left and top of figure is from [x from left, y from top] of canvas right and bottom of figure from [x from left, y from top] of canvas
coordinates = 20, 50, 210, 230
arc = canvas.create_arc(coordinates, start=0, extent=250, fill="blue") #start is from E, extent is in degrees CCW
arc = canvas.create_arc(coordinates, start=250, extent=50, fill="red")
arc = canvas.create_arc(coordinates, start=300, extent=60, fill="yellow")
canvas.grid(row=2, column=1)
def shop_scale(self, new_window):
self.scale_1 = Scale(new_window, from_=0, to=10, orient=VERTICAL)
self.scale_1.grid(row=15, column=2)
self.scale_2 = Scale(new_window, from_=0, to=10, orient = HORIZONTAL)
self.scale_2.grid(row=15, column=3)
def shop_scrollbars(self, new_window):
self.scroll_vert = Scrollbar(new_window)
self.scroll_vert.grid(row=19, column=3)
self.listbox_3 = Listbox(new_window, yscrollcommand = self.scroll_vert.set )
for line in range(1, 100):
self.listbox_3.insert(END, "Number " + str(line))
self.listbox_3.grid(row=19 , column=2)
self.scroll_vert.config(command = self.listbox_3.yview)
def shop_textbox(self, new_window):
#make a frame with parent new_window
self.frame_textbox_1 = Frame(new_window)
self.frame_textbox_1.grid(row=1, column=5)
#make the textbox with parent frame
self.textbox_1 = Text(self.frame_textbox_1)
self.textbox_1.config(wrap=NONE, width=15, height=8) #width, height are characters x rows permitted wrap values should be WORD CHAR, or NONE
#make the X scrollbar with parent frame
self.scroll_text_horiz = Scrollbar(self.frame_textbox_1, orient="horizontal")
self.scroll_text_horiz.config(command = self.textbox_1.xview)
#make the Y scrollbar with parent frame
self.scroll_text_vert = Scrollbar(self.frame_textbox_1, orient="vertical")
self.scroll_text_vert.config(command = self.textbox_1.yview)
#pack the scrollbars before the texbox to allow them to fill the frame width and height
self.scroll_text_horiz.pack(side=BOTTOM, fill=X)
self.scroll_text_vert.pack(side=RIGHT, fill=Y)
self.textbox_1.pack(fill="y")
#connect the scrollbars to the textbox
self.textbox_1["xscrollcommand"] = self.scroll_text_horiz.set
self.textbox_1["yscrollcommand"] = self.scroll_text_vert.set
message = "the quick brown fox"
self.textbox_1.insert(1.1, message)
# ----------------------------------------------
def shop_menu(self, new_window):
print("in shop menu1")
menubar = Menu(new_window)
print("in shop_menu2")
file = Menu(menubar, tearoff=0)
file.add_command(label="New")
file.add_command(label="Open")
file.add_command(label="Save")
file.add_command(label="Save as...")
file.add_command(label="Close")
file.add_separator()
file.add_command(label="Exit", command=new_window.quit)
menubar.add_cascade(label="File", menu=file)
edit = Menu(menubar, tearoff=0)
edit.add_command(label="Undo")
edit.add_separator()
edit.add_command(label="Cut")
edit.add_command(label="Copy")
edit.add_command(label="Paste")
edit.add_command(label="Delete")
edit.add_command(label="Select All")
menubar.add_cascade(label="Edit", menu=edit)
help = Menu(menubar, tearoff=0)
help.add_command(label="About")
menubar.add_cascade(label="Help", menu=help)
new_window.config(menu=menubar)
print("in shop menu3")
# --------------------------------------- END FACTORY SHOPS---------------------------------------
I tried searching for a way to add elevation to my Tkinter window but was unable to do so. I have removed the title bar from my window (and hence it is a custom one). Is there any direct/in-direct way to increase the elevation of the window/add some shadow effect for the same? (Basically the background color of the frame seems to get merged with the actual background on the screen)
Program Code:
from tkinter import *
from PIL import Image, ImageTk
class MyLabel(Label):
def __init__(self, master, filename):
im = Image.open(filename)
seq = []
try:
while 1:
seq.append(im.copy())
im.seek(len(seq))# skip to next frame
except EOFError:
pass # we're done
try:
self.delay = im.info['duration']
except KeyError:
self.delay = 100
first = seq[0].convert('RGBA')
self.frames = [ImageTk.PhotoImage(first)]
Label.__init__(self, master, image=self.frames[0])
self.config(borderwidth=0, highlightthickness=0)
temp = seq[0]
for image in seq[1:]:
temp.paste(image)
frame = temp.convert('RGBA')
self.frames.append(ImageTk.PhotoImage(frame))
self.idx = 0
self.cancel = self.after(self.delay, self.play)
def play(self):
self.config(image=self.frames[self.idx])
self.idx += 1
if self.idx == len(self.frames):
self.idx = 0
self.cancel = self.after(self.delay, self.play)
root=Tk(relief='raised')
root.overrideredirect(True) # turns off title bar, geometry
# make a frame for the title bar
title_bar = Frame(root, bg='#181818', bd=2, highlightthickness=0)
# put a close button on the title bar
close_button = Button(title_bar, text=' X ', command=root.destroy, bg="#181818", padx=2, pady=2, activebackground='red', bd=0, font="bold", fg='white', highlightthickness=0)
# a canvas for the main area of the window
window = Canvas(root, bg='#181818', highlightthickness=0)
# pack the widgets
title_bar.pack(expand=1, fill=X)
close_button.pack(side=RIGHT)
window.pack(expand=1, fill=BOTH)
xwin=None
ywin=None
# bind title bar motion to the move window function
def move_window(event):
root.geometry('+{0}+{1}'.format(event.x_root, event.y_root))
def change_on_hovering(event):
global close_button
close_button['bg']='red'
def return_to_normalstate(event):
global close_button
close_button['bg']='#2e2e2e'
title_bar.bind('<B1-Motion>', move_window)
close_button.bind('<Enter>',change_on_hovering)
close_button.bind('<Leave>',return_to_normalstate)
anim = MyLabel(window, 'test.gif')
anim.pack()
root.mainloop()
def stop_it():
anim.after_cancel(anim.cancel)
root.mainloop()
test.gif
Output:
As we can see here in the output, there is barely any distinction between the background and the window as it would have been without root.overrideredirect(True).
I am referring to this shadow/elevation that you can see beside the window in the below image (the background is basically the rest of the screen).
I have a button that adds a DateEntry widget to a scrollable frame. Unfortunately, the frame is at the bottom of the window, so after adding a few rows, the dropdown calendar hides behind the task bar. Is there any way to change it, so the calendar opens above the field, rather than below?
Here is some test code
from tkinter import *
from tkinter import ttk
from tkcalendar import Calendar, DateEntry
master = Tk()
global rowNumForShiftReport
rowNumForShiftReport=0
shiftDateWidgetList=[]
def myfunction(event):
canvas2.configure(scrollregion=canvas2.bbox("all"), width=100, height=100)
def addEntry2():
global rowNumForShiftReport
rowNumForShiftReport = rowNumForShiftReport + 1
shiftDateWidgetList.append(DateEntry(frame2, state='readonly', width=15))
shiftDateWidgetList[-1].grid(row=rowNumForShiftReport, column=0)
rowNumForShiftReport+1
master.geometry('400x400')
btn_addField2 = ttk.Button(master, text="Add Entry",command=addEntry2)
btn_addField2.grid(row=0, column=1)
#lotFrame2 = Frame(master)
actualLabelFrame=ttk.LabelFrame(master, text="Shift Report", height=300, width=300)
actualLabelFrame.grid(row=0, column=0)
canvas2 = Canvas(actualLabelFrame,width=160)
frame2 = Frame(canvas2,width=160)
frame2.bind("<Configure>", myfunction)
canvas2.create_window((0, 0), window=frame2, anchor='nw')
scrollBar2 = ttk.Scrollbar(actualLabelFrame, orient="vertical", command=canvas2.yview)
canvas2.configure(yscrollcommand=scrollBar2.set)
scrollBar2.grid(row=0, column=2, sticky=N + S)
canvas2.grid(row=0, column=1)
mainloop()
You can achieve the goal by extending DateEntry and override its drop_down() function as below:
class MyDateEntry(DateEntry):
def drop_down(self):
"""Display or withdraw the drop-down calendar depending on its current state."""
if self._calendar.winfo_ismapped():
self._top_cal.withdraw()
else:
self._validate_date()
date = self.parse_date(self.get())
x = self.winfo_rootx()
y = self.winfo_rooty() + self.winfo_height()
if self.winfo_toplevel().attributes('-topmost'):
self._top_cal.attributes('-topmost', True)
else:
self._top_cal.attributes('-topmost', False)
# - patch begin: make sure the drop-down calendar is visible
if x+self._top_cal.winfo_width() > self.winfo_screenwidth():
x = self.winfo_screenwidth() - self._top_cal.winfo_width()
if y+self._top_cal.winfo_height() > self.winfo_screenheight()-30:
y = self.winfo_rooty() - self._top_cal.winfo_height()
# - patch end
self._top_cal.geometry('+%i+%i' % (x, y))
self._top_cal.deiconify()
self._calendar.focus_set()
self._calendar.selection_set(date)
Then replace all DateEntry(...) by MyDateEntry(...) in your code.
Note that it is based on tkcalendar v1.6.1.
I'm trying to use tkinter's buttons to control a turtle in another graphics window, but the buttons don't function
I was trying to use turtle graphics and Tkinter to make a simple program that controls a turtle with buttons. However, it seems that only one button is actually running a function, and even that is producing an error. Here is the code I wrote:
from tkinter import *
import turtle
global x
global y
x = 1
y = 1
i = 0
root = Tk()
wn = turtle.Screen()
bob = turtle.Turtle()
bob.up()
def goUp(event):
y=y+5
bob.goto(x,y)
def goDown(event):
y=y-5
bob.goto(x,y)
def goRight(event):
x=x+5
bob.goto(x,y)
def goLeft(event):
x=x-5
bob.goto(x,y)
topFrame = Frame(root)
topFrame.pack()
middleFrame = Frame(root)
middleFrame.pack()
bottomFrame = Frame(root)
bottomFrame.pack()
button1 = Button(topFrame, text = "Up", fg="red")
button2 = Button(middleFrame, text = "Left", fg="red")
button3 = Button(middleFrame, text = "Right", fg="red")
button4 = Button(bottomFrame, text = "Down", fg="red")
button1.bind("<Button-1>", goUp)
button2.bind("<Button-2>", goLeft)
button3.bind("<Button-3>", goRight)
button4.bind("<Button-4>", goDown)
button1.pack()
button2.pack(side=LEFT)
button3.pack(side=RIGHT)
button4.pack()
root.mainloop()
The TKinter Button class accepts a command option where you can pass in the function that gets called when a button is pressed. You also need to access the global x and y variables inside your methods.
Try this code:
from tkinter import *
import turtle
global x
global y
x = 1
y = 1
i = 0
root = Tk()
wn = turtle.Screen()
bob = turtle.Turtle()
bob.up()
def goUp():
global x, y
y=y+5
bob.goto(x,y)
def goDown():
global x, y
y=y-5
bob.goto(x,y)
def goRight():
global x, y
x=x+5
bob.goto(x,y)
def goLeft():
global x, y
x=x-5
bob.goto(x,y)
topFrame = Frame(root)
topFrame.pack()
middleFrame = Frame(root)
middleFrame.pack()
bottomFrame = Frame(root)
bottomFrame.pack()
button1 = Button(topFrame, text = "Up", fg="red", command=goUp)
button2 = Button(middleFrame, text = "Left", fg="red", command=goLeft)
button3 = Button(middleFrame, text = "Right", fg="red", command=goRight)
button4 = Button(bottomFrame, text = "Down", fg="red", command=goDown)
button1.pack()
button2.pack(side=LEFT)
button3.pack(side=RIGHT)
button4.pack()
root.mainloop()
Turtle's Turtle and Screen classes are generally used when turtle.py is used standalone. When turtle, which is implemented atop tkinter, is used embedded inside tkinter, we generally use the RawTurtle and TurtleScreen classes instead. You'll find more about this in the turtle documentation
You also should review one of the many online Python global keyword tutorials as you're not using it correctly. Fortunately, we don't need it to implement this particular program.
Here's a rework of your code, as a single window implementation, that illustrates the above:
import tkinter as tk
from turtle import RawTurtle, TurtleScreen
def goUp():
bob.sety(bob.ycor() + 5)
def goDown():
bob.sety(bob.ycor() - 5)
def goRight():
bob.setx(bob.xcor() + 5)
def goLeft():
bob.setx(bob.xcor() - 5)
root = tk.Tk()
canvas = tk.Canvas(root, width=500, height=500)
canvas.pack()
wn = TurtleScreen(canvas)
bob = RawTurtle(wn, shape="turtle")
bob.penup()
topFrame = tk.Frame(root)
topFrame.pack()
middleFrame = tk.Frame(root)
middleFrame.pack()
bottomFrame = tk.Frame(root)
bottomFrame.pack()
tk.Button(topFrame, text="Up", fg="red", command=goUp).pack()
tk.Button(middleFrame, text="Left", fg="red", command=goLeft).pack(side=tk.LEFT)
tk.Button(middleFrame, text="Right", fg="red", command=goRight).pack(side=tk.RIGHT)
tk.Button(bottomFrame, text="Down", fg="red", command=goDown).pack()
wn.mainloop()