Python Tkinter AutoResizing scrolling Canvas in window - python

I create a simple application interface in python with tkinter.
[you can run the code]
It has 2 frames inside [self.CON] frame.
Frame one is for header [self.HeaderFrame] and second frame [self.CFrame] is a dynamically populated list inside scrolling canvas [self.Content].
Since the number of items in scrolling canvas [self.Content] will change, I need to resize the canvas each time list was populated or window resized.
from tkinter import *
import tkinter.font as font
from PIL import Image, ImageTk
class wmGUI (object):
def __init__ (self,tittle,itemnum):
self.tittle=tittle
self.GUI = wmWINDOW(self.tittle,itemnum)
class wmWINDOW(Tk):
windowwidth,windowheight,sliderresizer = 250,600,100
def __init__ (self,tittle,itemnum):
super().__init__()
WW = self.winfo_screenwidth()
WH = self.winfo_screenheight()
print('WW : %d & WH : %d' % (WW,WH))
geometry = '{width}x{height}+{pos_x}+{pos_y}'.format(height=WH, width=self.windowwidth, pos_y=0, pos_x=WW-self.windowwidth-20)
self.geometry(geometry)
self.configure(background='grey')
self.title("GUI")
self.itemnum=itemnum
self.resizable(width=False, height=True)
self.attributes('-topmost',True)
self.CreateMenus()
self.Myfont= font.Font(family="Arial", size=8, weight=font.NORMAL)
self.Myfont2 = font.Font(family="Helvetica", size=9, weight=font.BOLD)
self.Myfont3 = font.Font(family="Helvetica", size=7, weight=font.BOLD)
self.CON=Frame(self,width=self.windowwidth,height=WH)
self.MakeHeader()
self.MakeContent()
self.CON.pack(fill=BOTH, expand=YES)
self.RUN()
def MakeHeader (self):
self.HeaderFrame= Frame(self.CON, width=self.windowwidth,height=60, background='Blue',borderwidth=0 )
self.HeaderFrame.propagate(1)
hImage = Image.open("images/header.jpg")
headerImage=ImageTk.PhotoImage(hImage, width= self.windowwidth, height=60)
self.headerLabel=Label(self.HeaderFrame,image=headerImage, width= self.windowwidth, height=60, borderwidth=0, highlightthickness=0)
self.headerLabel.image=headerImage
self.headerLabel.pack( )
self.HeaderFrame.pack( padx=0,pady=0,ipadx=0,ipady=0)
def MakeContent (self):
self.CFrame = Frame(self.CON, width=self.windowwidth,bg="cyan")
self.Content = Canvas(self.CFrame, width=self.windowwidth-15, bg="green",borderwidth=0, highlightthickness=0)
self.ContentFrame = Frame(self.Content, bg="#EBEBEB",height=100)
self.Content.create_window(0, 0, window=self.ContentFrame, anchor='nw')
self.ContentFrame.bind("<Configure>", self.on_change)
self.Items()
self.CScrollbar = Scrollbar(self.CFrame, orient=VERTICAL , width=15)
self.CScrollbar.config(command=self.Content.yview)
self.Content.config(yscrollcommand=self.CScrollbar.set)
self.CScrollbar.grid(row=0, column=1, sticky="ns")
self.Content.grid(row=0, column=0, sticky="nsew")
self.CFrame.pack(fill=BOTH, expand=YES)
self.Content.configure(height=self.CON.winfo_reqheight() - 60)
def on_change(self,event):
self.Content.configure(scrollregion=self.Content.bbox("all"))
def Items(self):
for n in range(self.itemnum):
LabelFrame(self.ContentFrame, text='Example Item {}'.format(n) , font=self.Myfont3, bg="yellow", fg="black",width=self.windowwidth,height=40).grid()
def RUN (self):
self.mainloop()
def CreateMenus(self):
#MAIN MENUBAR
MenuBar = Menu(self) #Creates Menu bar
self.config(menu=MenuBar)
# FILE MENU
FileMenu=Menu(MenuBar,tearoff=0)
FileMenu.add_command(label="Open",command=None) #command=bewritten
FileMenu.add_command(label="Close",command=None)
FileMenu.add_separator()
FileMenu.add_command(label="Reset", accelerator="Ctrl + Shift + r")
FileMenu.add_separator()
FileMenu.add_command(label="Quit", command= None,accelerator="Ctrl q")
MenuBar.add_cascade(label="File",menu=FileMenu)
WMGUI=wmGUI('My Window',50)
I assume to resize the canvas [self.Content] I should retrieve the window inner height. But I can not find a method for it.
How can i do this ?

I found the solution . The example here really helps..
LINK
The format of frames in the window was like following
[Frame CON] -[Frame Header] -[Frame Content] -[Content Canvas] > [/Frame CON]
I binded on_change function to self.CON ; container frame since the container frame will change each time CON content changed.
self.CON.bind("<Configure>", self.on_change)
I changed on_change function to :
def on_change(self,event):
self.Content.configure(scrollregion=self.Content.bbox("all"))
self.Content.configure(height=self.CON.winfo_height()-60)
So on an event, on_change event gets the height of CON container frame and applies height-60 to canvas each time CON changed.
This really worked for the scrollbar to be resize when content created...
Also added an event handler to the root window ..This will resize the scrollbar when the window size changes.
self.bind('<Configure>', self.on_change)
Seems like everything works fine now ...

Related

How to insert scrollbar into canvas filled with buttons?

I'm trying to insert a scrollbar into a canvas filled with buttons (where, buttons can be switched to clickable text as well).
With the code below, the window seems OK.
However, when I uncomment the lines with the scrollbar, the window geometry scrambles.
Can anybody give a hand to figure out what's wrong with the code?
System: Windows 10, python 3.9.6.
import tkinter as tk
class tWindow(tk.Tk):
frame = None
canvas = None
scrollbar = None
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.geometry("640x480+50+50")
self.setup_frame()
self.setup_canvas()
def setup_frame(self):
self.frame = tk.Frame(master=self, width=640, height=480)
self.frame.configure(bg="#003163")
self.frame.place(x=0, y=0)
def setup_canvas(self):
self.update()
self.canvas = tk.Canvas(master=self.frame, bg="#006331",
width=int(self.frame.winfo_width() / 4), height=self.frame.winfo_height())
self.canvas.place(x=0, y=0)
self.update()
# self.scrollbar = tk.Scrollbar(master=self.canvas, orient=tk.VERTICAL)
# self.scrollbar.pack(side=tk.RIGHT, fill=tk.BOTH, expand=tk.TRUE)
# self.canvas.configure(yscrollcommand=self.scrollbar.set)
# self.scrollbar.configure(command=self.canvas.yview)
# self.update()
tmp_pos = 0
for i in range(20):
btn_tmp = tk.Button(master=self.canvas, text=f"testing testing testing testing testing {i:02} ...",
justify=tk.LEFT, wraplength=self.canvas.winfo_width(), bg="#00c0c0", fg="#000000")
btn_tmp.place(x=0, y=tmp_pos)
self.update()
tmp_pos = btn_tmp.winfo_y() + btn_tmp.winfo_height()
def main():
app = tWindow()
app.mainloop()
if __name__ == "__main__":
main()
See comments in code as additional to the link I have provided. Also see why update is considered harmfull and how do I organize my tkinter gui for more details.
import tkinter as tk
class tWindow(tk.Tk):
frame = None
canvas = None
scrollbar = None
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.geometry("640x480+50+50")
self.setup_frame()
self.setup_canvas()
def setup_frame(self):
self.frame = tk.Frame(master=self, width=640, height=480,bg="#003163")
self.frame.pack(side='left',fill='both',expand=True)##pack left
## self.frame.place(x=0, y=0) ##place requiers too think to much
def setup_canvas(self):
## self.update() ##update is harmfull
self.canvas = tk.Canvas(master=self.frame, bg='orange',#"#006331",
width=int(self.frame.winfo_reqwidth() / 4), height=self.frame.winfo_reqheight())
##use reqwidth/hight to avoid the use of update.
##It takes the requested size instead the actual
self.canvas.pack(side='left')
self.canvas_frame = tk.Frame(self.canvas,bg="#006331")
##frame to pack buttons in canvas
self.canvas.create_window((0,0), window=self.canvas_frame, anchor="nw")
##show frame in canvas
self.scrollbar = tk.Scrollbar(master=self.frame, orient=tk.VERTICAL)
self.scrollbar.pack(side='left',fill='y')
self.canvas.configure(yscrollcommand=self.scrollbar.set)
self.scrollbar.configure(command=self.canvas.yview)
self.canvas_frame.bind('<Configure>',self.oncanvasconfigure)
##bind event configure to update the scrollregion
for i in range(20):
btn_tmp = tk.Button(master=self.canvas_frame, text=f"testing testing testing testing testing {i:02} ...",
justify=tk.LEFT, wraplength=self.canvas.winfo_reqwidth(), bg="#00c0c0", fg="#000000")
btn_tmp.pack()
def oncanvasconfigure(self,event):
self.canvas.configure(scrollregion=self.canvas.bbox("all"))
def main():
app = tWindow()
app.mainloop()
if __name__ == "__main__":
main()

Tkinter buttons getting hidden behind other frames

I am trying to create a battlemap for dnd (picture) with adjustable grid and movable enemy/creature tokens. The idea is to drag one of the token from the right onto the map on the left.
The window is made of 3 frames. The frame for the map, the frame for the "new map" button and slider. And then frame for the tokens, which are buttons tiled using button.grid()
I found a drag and drop system here that I'm using to drag the tokens. However, when I bring them over the map, they go behind it and you can't see them (I know they go behind because they can be partially visible between the two frames). Is there any way to bring them to the front?
import tkinter as tk
class DragManager():
def add_dragable(self, widget):
widget.bind("<ButtonPress-1>", self.on_start)
widget.bind("<B1-Motion>", self.on_drag)
widget.bind("<ButtonRelease-1>", self.on_drop)
widget.configure(cursor="hand1")
def on_start(self, event):
# you could use this method to create a floating window
# that represents what is being dragged.
pass
def on_drag(self, event):
# you could use this method to move a floating window that
# represents what you're dragging
event.widget.place(x=event.x_root + event.x, y= event.y_root + event.y)
#when button is dropped, create a new one where this one originally was
def on_drop(self, event):
# find the widget under the cursor
x,y = event.widget.winfo_pointerxy()
target = event.widget.winfo_containing(x,y)
try:
target.configure(image=event.widget.cget("image"))
except:
pass
if x > window.winfo_screenwidth() - 200:
del event.widget
return
if not event.widget.pure:
return
button = tk.Button(master=entity_select_frame, text = "dragable", borderwidth=1, compound="top")
#avoiding garbage collection
button.gridx = event.widget.gridx
button.gridy = event.widget.gridy
button.grid(row = event.widget.gridx, column = event.widget.gridy)
button.grid()
button.pure = True
dnd.add_dragable(button)
window = tk.Tk()
window.geometry("1000x800")
map_frame = tk.Frame()
controls_frame = tk.Frame(width=200, borderwidth=1, relief=tk.RAISED)
tk.Label(master=controls_frame, text="controls here").pack()
entity_select_frame = tk.Frame(width=200, relief=tk.RAISED, borderwidth=1)
dnd = DragManager()
button = tk.Button(master=entity_select_frame, text = "dragable", borderwidth=1)
button.gridx = 0
button.gridy = 0
button.grid(row = 0, column = 0)
button.pure = True
dnd.add_dragable(button)
map_frame.pack(fill=tk.BOTH, side=tk.LEFT, expand=True)
controls_frame.pack(fill=tk.BOTH)
entity_select_frame.pack(fill=tk.BOTH)
window.mainloop()
I played around a little bit and used stuff from this post. I did not structure it as a class and I used the picture frame as my root-frame and put the control-frame inside that. I'm not sure how this would be best combined with your "draw-grid", "token" functionalities etc., however I hope it helps. I did not find a way to drag widgets across frames though (tried to set a new master for the button, recreate it after dropping it etc.). Get the image used in my code from here.
from tkinter import Tk, Frame, Label, Button, Canvas, font
from tkinter import ttk
from PIL import Image, ImageTk
root = Tk()
""" ####################### Configuration parameters ###################### """
image_file_path = "Island_AngelaMaps-1024x768.jpg"
resize_img = False # set to True if you want to resize the image > window size
resize_to = (600, 600) # resolution to rescale image to
""" ####################### Drag and drop functionality ################### """
def make_draggable(widget):
widget.bind("<Button-1>", on_drag_start)
widget.bind("<B1-Motion>", on_drag_motion)
def on_drag_start(event):
widget = event.widget
widget._drag_start_x = event.x
widget._drag_start_y = event.y
def on_drag_motion(event):
widget = event.widget
x = widget.winfo_x() - widget._drag_start_x + event.x
y = widget.winfo_y() - widget._drag_start_y + event.y
widget.place(x=x, y=y)
""" ################################# Layout ############################## """
# picture frame with picture as background
picture_frame = Frame(root)
picture_frame.pack(side="left", anchor="w", fill="both", expand=True)
# load the image
if resize_img:
img = ImageTk.PhotoImage(Image.open(image_file_path).resize(resize_to, Image.ANTIALIAS))
else:
img = ImageTk.PhotoImage(Image.open(image_file_path))
# create canvas, set canvas background to the image
canvas = Canvas(picture_frame, width=img.width(), height=img.height())
canvas.pack(side="left")
canvas.background = img # Keep a reference in case this code is put in a function.
bg = canvas.create_image(0, 0, anchor="nw", image=img)
# subframe inside picture frame for controls
ctrl_subframe = Frame(picture_frame)
ctrl_subframe.pack(side="right", anchor="n")
# separator between picture and controls, inside picture frame
ttk.Separator(picture_frame, orient="vertical").pack(side="right", fill="y")
# underlined label 'Controls' in subframe
ctrl_header = Label(ctrl_subframe, text="Controls", font=("Arial", 10, "bold"))
f = font.Font(ctrl_header, ctrl_header.cget("font"))
f.configure(underline=True)
ctrl_header.configure(font=f)
ctrl_header.pack(side="top", pady=2)
# update window to get proper sizes from widgets
root.update()
# a draggable button, placed below ctrl_header
# (based on X of ctrl_subframe and height of ctrl_header, plus padding)
drag_button = Button(picture_frame, text="Drag me", bg="green", width=6)
drag_button.place(x=ctrl_subframe.winfo_x()+2, y=ctrl_header.winfo_height()+10)
make_draggable(drag_button)
""" ################################ Mainloop ############################# """
root.mainloop()

Way to avoid having multiple windows when using Messagebox (or alternative to it)?

I'm trying to write my first GUI based python program using Tkinter. I have created a base window which holds the menu bar with a couple of options.
One of the options is for a standard 'About' box. When I call the about section with
helpMainMenu.add_command(label="About", command=self.aboutProgram)
It opens the message box, but in a fresh window so there are now two windows showing on the taskbar.
Is there any way to stop it opening a new window and use the main one instead, or is there a better way to do it?
The full code is below
#! /usr/bin/python3
from tkinter import *
from tkinter import messagebox
import datetime
timeNow = datetime.datetime.now()
writeYear = 2020 # Enter the year you started writing the program
lineFeed = "\n"
programTitle = "Basic Menu"
programVersion = "Version 1.0.0"
programmerName = " Name (email#gmail.com)"
if timeNow.year > writeYear:
programAuthor = "©" + str(writeYear) + "-" + str(timeNow.year) + programmerName
else:
programAuthor = "©" + str(writeYear) + programmerName
aboutMessage = programTitle + lineFeed + programVersion + lineFeed + programAuthor
class Window(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
self.master = master
self.init_window()
def init_window(self):
self.master.title("{} ({})".format(programTitle, programVersion))
self.pack(fill=BOTH, expand=1)
menu = Menu(self.master)
self.master.config(menu=menu)
fileMainMenu = Menu(menu, tearoff=0) #Create the File menu container
fileMainMenu.add_command(label="Exit", command=self.programExit) # File menu option
menu.add_cascade(label="File", menu=fileMainMenu)
helpMainMenu = Menu(menu, tearoff=0) #Create the Help menu container
helpMainMenu.add_command(label="About", command=self.aboutProgram)
menu.add_cascade(label="Help", menu=helpMainMenu)
def programExit(self):
exitMsgBox = messagebox.askquestion ("Exit Application","Are you sure you want to exit the application",icon = "warning")
if exitMsgBox == "yes":
root.destroy()
exit()
def aboutProgram(self):
messagebox.showinfo("About","About the application", icon = "info")
root = Tk() # root window created. Here, that would be the only window, but
windowHeight = int(root.winfo_screenheight()/100*75) # Set the main window height to 75% of the screen height
windowWidth = int(root.winfo_screenwidth()/100*75) # Set the main window width to 75% of the screen width
screenWidth = int(root.winfo_screenwidth())
screenHeight = int(root.winfo_screenheight())
positionRight = int(root.winfo_screenwidth()/2 - windowWidth/2) # Get the screen width and divide by 2, then minus the result of 'windowWidth' divided by 2
positionDown = int(root.winfo_screenheight()/2 - windowHeight/2) # Get the screen height and divide by 2, then minus the result of 'windowHeight' divided by 2
root.geometry("{}x{}+{}+{}".format(windowWidth, windowHeight, positionRight, positionDown)) # Positions the window in the center of the page.
app = Window(root)
root.mainloop()
Python Version 3.7.3
tkinter.TkVersion 8.6
The simplest way would be to create a new frame for the "about" page, and then overlay it on top of the main window with place -- one of the few times when place is superior to grid and pack.
You should also do a "grab" on the frame so that all events are funneled to the frame and its children. With a grab, while the popup is visible you can't interact with the widgets on the main window.
Here's a quick example:
def aboutProgram(self):
# create the frame with a message and a button
# which destroys the window
aboutFrame = Frame(self.master, bd=2, relief="groove")
label = Label(aboutFrame, text="About the application...")
button = Button(aboutFrame, text="Ok", command=aboutFrame.destroy)
label.pack(side="top", padx=20, pady=20)
button.pack(side="bottom", pady=20)
# overlay the "about" page on top of the root window
aboutFrame.place(relx=.5, rely=.5, anchor="c")
# force all events to go to the popup
aboutFrame.grab_set()
If you want to completely hide the contents of the main window, you can change the place arguments to fill the window:
aboutFrame.place(x=0, y=0, anchor="nw", relwidth=1.0, relheight=1.0)

performing operations on images uploaded by tkinter

I am new to Tkinter and python. I am trying to upload two images and then perform some operations on them. The problem is that the Window class is loading all at once or the code in running parallel, so the images uploaded after that have been already assigned to None since they were uploaded later in the ScrollableFrame class and did not have a value earlier.
from tkinter import *
from tkinter import filedialog
from PIL import Image, ImageTk, ImageOps
import os
# ************************
# Scrollable Frame Class
# ************************
row=0
column=0
imagePaths = []
#Class to generate a frame to add to the GUI with vertical and horizontal scroll bars
class ScrollableFrame(Frame):
#The Constructor method for the class
def __init__(self, parent , *args, **kw):
Frame.__init__(self, parent, *args, **kw)
#Defining the position of the frame grid
self.grid(row = row , column = column)
self.image = None
self.imageFile = None
#Defining the vertical scroll bar
vscrollbar = Scrollbar(self, orient=VERTICAL)
vscrollbar.grid(row=row, column=column+1, sticky=N+S)
#Defining the horizontal scroll bar
hscrollbar = Scrollbar(self, orient = 'horizontal')
hscrollbar.grid(row=row+1, column=column, sticky=E+W)
#Defining the canvas to put the scroll bars on
canvas = Canvas(self, bd=0, highlightthickness=0, yscrollcommand=vscrollbar.set, xscrollcommand=hscrollbar.set)
canvas.grid(row=row, column=column, sticky = N+S+E+W)
canvas.config( width=800, height = 800 )
#Defining the scrolling commands (vertically and horizontally )
vscrollbar.config(command=canvas.yview)
hscrollbar.config(command=canvas.xview)
#Defining the scroll region where the scrolling is active
canvas.config(scrollregion= (0,0,1280,1024))
self.canvas = canvas
def openImage(self):
#Getting the path of the image
imageFile = filedialog.askopenfilename(initialdir=os.getcwd(),title="Select BMP File",filetypes=[("BMP Files",("*.bmp",".png",".jpg",".jpeg",".tif",".tiff"))])
#Assigning the image value to this frame object
self.imageFile = imageFile
if not imageFile:
return
def showImage(self):
#Getting the path of the image
imageFile = filedialog.askopenfilename(initialdir=os.getcwd(),title="Select BMP File",filetypes=[("BMP Files",("*.bmp",".png",".jpg",".jpeg",".tif",".tiff"))])
#Assigning the image value to this frame object
self.imageFile = imageFile
if not imageFile:
return
#Checking for the extension of the image
filename, file_extension = os.path.splitext(imageFile)
#If it is a .bmp, this means that it is an HD image, where we can directly display it
if file_extension == '.bmp':
imageToDisplay = Image.open(imageFile)
#border = (0, 0, 0, 66) #Decide on the area you want to crop in terms of no. pixels: left, up, right, bottom
#ImageOps.crop(imageToDisplay, border)
img = ImageTk.PhotoImage(imageToDisplay)
self.image = img
#print ("Done conversion")
self.canvas.create_image(row, column, image=self.image, anchor=NW)
class Window(Frame):
def __init__(self, master=None):
global row, column,imagePaths
Frame.__init__(self, master)
self.master = master
self.pos = []
self.master.title("BMP Image GUI")
self.pack(fill=BOTH, expand=1)
self.label = Label(self, text="Instructions: \n 1. Open the HD image. \n 2. Open the EBSD image. \n 3. Open the Color Map image.", anchor=W, justify=LEFT)
self.label.place(x=1640, y=0)
menu = Menu(self.master)
self.master.config(menu=menu)
self.frame1 = ScrollableFrame(self)
row=0
column=1
self.frame2 = ScrollableFrame(self)
# File Bar
file = Menu(menu)
file.add_command(label="Open HD image", command=self.frame1.showImage)
img = Image.open("original.bmp")
HD = self.frame2.imageFile
file.add_command(label="Open EBSD image", command=self.frame2.openImage)
EBSD = self.frame2.imageFile
print (HD)
print (EBSD)
root = tk.Tk()
root.geometry("%dx%d" % (1670, 1024))
root.title("BMP Image GUI")
app = Window(root)
app.pack(fill=tk.BOTH, expand=1)
#print (HD)
root.mainloop()
So printing the HD and EBSD images is giving None. What I am aiming to to make them get the actual value assigned after the upload.
This is a lot of code and it doesn't run. It also has a few problems. When you are coding complex applications it is best to do it one little piece at a time or you'll have problems finding the problems. Here are a few:
Don't use global variables in an object oriented application. The names row, column and imagePaths should belong to either of the two classes.
The menu doesn't work because you have not implemented it correctly:
file = Menu(menu)
menu.add_cascade(label='File', menu=file) # You need this for it to work
file.add_command(label="Open HD image", command=self.frame1.showImage)
# etc...
You are packing app twice, once in it's __init__() function and once after it's been created (in the global scope).
The scrollable frames are packed in front of the Label with instructions so you can't see it.
Try fixing these problems by writing components, and when each component works then combine them. If there is a problem with any of the components, or if everything works but for one thing, come back here and we will be able to give you a better answer.

Switch between multiple windows in Tkinter

I have created few windows using Tkinter. I need help in the implementation of switching from one window to another when the button has been clicked.
All windows that are created should have the same size.
And also I want to clear existing window data and show next window data.
If you want to have multiple windows opened and want to switch between each window with all of their widgets intact then I don't think destroying a window each time you switch is a good idea instead you can try to withdraw and deiconify the windows.
I've created something like this which can switch between windows and maintain the same geometry of the previous window as you said.
import tkinter as tk
class Window(tk.Toplevel):
# List to keep the reference of all the toplevel windows
_info_pages = []
def __init__(self, master=None, cnf={}, **kw):
kw = tk._cnfmerge( (cnf,kw) )
width = kw.pop('width', master.winfo_width()) # 250x250 will be the standard size of the window
height = kw.pop('height', master.winfo_height())
title = kw.pop('title', 'Win %s' %(len(self._info_pages)+1) )
super(Window, self).__init__(master=master, cnf=cnf, **kw)
for i in self._info_pages: i.wm_withdraw() # Hide the previous windows
if self._info_pages and width == master.winfo_width():
self.wm_geometry(self._info_pages[-1].winfo_geometry())
else:
self.wm_geometry("%dx%d+%d+%d" % (width, height,
master.winfo_rootx()+master.winfo_width(), master.winfo_rooty()))
self._info_pages.append(self)
self.title(title)
self.B1 = tk.Button(self, text='◀ Prev', padx=5, command=self.switch_to_prev)
self.B1.place(relx=0, rely=1, anchor='sw')
self.B2 = tk.Button(self, text='Next ▶', padx=5, command=self.switch_to_next)
self.B2.place(relx=1, rely=1, anchor='se')
self.enable_disable_button()
def enable_disable_button(self):
"""Enable and disable the buttons accordingly if there is no window."""
for i in self._info_pages:
if i == self._info_pages[0]: i.B1['state'] = 'disabled'
else: i.B1['state'] = 'normal'
if i == self._info_pages[-1]: i.B2['state'] = 'disabled'
else: i.B2['state'] = 'normal'
def switch_to_prev(self):
"""Switch to the previous window"""
index = self._info_pages.index(self)
if index != 0:
for i in self._info_pages:
i.wm_withdraw()
self._info_pages[index-1].geometry(self.winfo_geometry())
self._info_pages[index-1].wm_deiconify()
def switch_to_next(self):
"""Switch to the next window"""
index = self._info_pages.index(self)
if index+1 != len(self._info_pages):
for i in self._info_pages:
i.wm_withdraw()
self._info_pages[index+1].geometry(self.winfo_geometry())
self._info_pages[index+1].wm_deiconify()
def destroy(self):
"""if a window is destroyed this will open the last window in the list"""
self._info_pages.remove(self)
if self._info_pages:
self._info_pages[-1].geometry(self.winfo_geometry())
self._info_pages[-1].wm_deiconify()
self.enable_disable_button()
return super().destroy()
# This is just a demo
if __name__ == '__main__':
import random as rnd
root = tk.Tk()
root.geometry('250x250')
root.title("I'm the main window")
colorlist = ['beige','bisque','black','blanchedalmond','blue','blueviolet',
'burlywood', 'cadetblue','chartreuse','chocolate' ]
def create_window():
Window(root, bg=rnd.choice(colorlist))
tk.Button(root, text='Create Window', command=create_window).pack()
root.mainloop()

Categories

Resources