Tkinter - Deleting widgets of a Canvas when calling Misc.lower() method - python

I'm just working on a GUI with tkinter in python. But I'm having some issues here. I'm creating a button that once it's clicked it will go back a page (Misc.lower(canvas2) and Misc.lift(canvas1)). Although i created a window for the button on the self.canvas2, the buttons is still there when the canvas is lowered.
Here is my code:
from tkinter import *
class Application:
def __init__(self):
self.window = Tk()
self.window.geometry("1280x720")
self.canvas1 = Canvas(self.window, highlightthickness=0, bg="#1b1b1b")
self.canvas2 = Canvas(self.window, highlightthickness=0, bg="red2")
self.initButtons()
self.window.mainloop()
def initButtons(self, *args):
buttons = {}
self.buttonList = []
buttons['backButtons'] = Button(self.window, bd=0, activebackground="#1b1b1b", bg="#1b1b1b", text='Back', fg="#ffffff")
self.buttonList.append(buttons['backButtons'])
self.initSecondWindow()
def initFirstWindow(self, *args):
Misc.lower(self.canvas2)
self.canvas1.place(relwidth=1, relheight=1)
def initSecondWindow(self, *args):
backButton = self.buttonList[0]
backButton.config(command=self.initFirstWindow)
self.canvas2.place(relwidth=1, relheight=1)
self.canvas2.create_window(640, 360, window=backButton)
Application()
I read a bit about canvas.delete(), but I'm not sure how it works.
Thanks in advance!

Not sure why you would just want to hide it though, then how would the users redirect to the previous page? Is this what you wanted?
from tkinter import *
class Application:
def __init__(self):
# self.backButton = None
self.window = Tk()
self.window.geometry("1280x720")
self.canvas1 = Canvas(self.window, highlightthickness=0, bg="#1b1b1b")
self.canvas2 = Canvas(self.window, highlightthickness=0, bg="red2")
self.initButtons()
self.window.mainloop()
def initButtons(self, *args):
buttons = {}
self.buttonList = []
buttons['backButtons'] = Button(self.window, bd=0, activebackground="#1b1b1b", bg="#1b1b1b", text='Back', fg="#ffffff")
self.buttonList.append(buttons['backButtons'])
self.initSecondWindow()
def initFirstWindow(self, *args):
Misc.lower(self.canvas2)
self.canvas2.delete(self.res)
self.canvas1.place(relwidth=1, relheight=1)
def initSecondWindow(self, *args):
backButton = self.buttonList[0]
backButton.config(command=self.initFirstWindow)
self.canvas2.place(relwidth=1, relheight=1)
self.res = self.canvas2.create_window(640, 360, window=backButton)
Application()

Related

How to get variables from toplevel window to main window?

I have a code that gets keyinputs and show it on a entrybox in the toplevel window of the main window. In the main window I have a listbox and I am wishing to get the keyinput that is shown on the entrybox to be enlisted when the confirm button is pressed on toplevel.
I tried several ways to get evt.keysym to my listbox but all failed.
class EntryBox(tk.Entry):
def __init__(self, master, cnf = {}, **kw):
kw = tk._cnfmerge((kw, cnf))
kw['justify'] = kw.get('justify', 'center')
kw['width'] = 15
kw['state'] = 'readonly'
super(EntryBox, self).__init__(master=master, **kw)
self.unbind_class('Entry', '<BackSpace>')
self.unbind_class('Entry', '<Key>')
self.bind_class(self, '<Key>', self._display)
def _display(self, evt):
self['state'] = 'normal'
self.delete('0', 'end')
self.insert('0', str(evt.keysym))
self['state'] = 'readonly'
class Keyboard:
def __init__(self):
self.kb = tk.Toplevel()
kb_frame = ttk.Frame(self.kb)
kb_frame.grid(column=0, row=1, pady=(7, 19))
ttk.Label(kb_frame, text='Enter Key').grid(column=0, row=0, pady=4)
entry = EntryBox(kb_frame)
entry.grid(column=0, row=1)
# Confirm button
self.co_button = ttk.Button(self.kb, text='Confirm')
self.co_button.grid(column=0, row=2)
class Main:
def __init__(self):
self.win = tk.Tk()
# listbox
lb_frame = tk.Frame(self.win)
lb_frame.grid(column=0, row=0)
scrollbar = tk.Scrollbar(lb_frame, orient='vertical')
scrollbar.grid(column=1, row=0, sticky='NS', pady=(12, 4))
listbox = tk.Listbox(lb_frame, selectmode='extended', width=25,
height=16,
yscrollcommand=scrollbar.set, activestyle='none')
listbox.grid(column=0, row=0, sticky='NSEW', padx=(6, 0), pady=(12, 4))
scrollbar.config(command=listbox.yview)
# button to open toplevel
bt_frame = ttk.Frame(self.win)
bt_frame.grid(column=2, row=0, rowspan=2)
self.kb_button = ttk.Button(bt_frame, text='KeyBoard', command=KeyBoard)
self.kb_button.grid(column=0, row=0)
main = Main()
main.win.mainloop()
To get values from one class to another class you've to link them. Inheriting the Widgets directly to the class will help you a lot in establishing a connection between Tk() window and Toplevel() Window.
One more thing when a Keyboard window is already opened disable the button by configure state = 'disabled' so the user won't open another one by mistake and when Keyboard window is closed re-enable the button by state = 'normal'.
Here is the complete code:
import tkinter as tk
import tkinter.ttk as ttk
class EntryBox(tk.Entry):
def __init__(self, master, cnf = {}, **kw):
kw = tk._cnfmerge((kw, cnf))
kw['justify'] = kw.get('justify', 'center')
kw['width'] = 15
kw['state'] = 'readonly'
super(EntryBox, self).__init__(master=master, **kw)
self.bind_class(self, '<Key>', self._display)
def _display(self, evt):
self['state'] = 'normal'
self.delete('0', 'end')
self.insert('0', str(evt.keysym))
self['state'] = 'readonly'
class Keyboard(tk.Toplevel):
def __init__(self, master=None, cnf={}, **kw):
super(Keyboard, self).__init__(master=master, cnf=cnf, **kw)
self.master = master
kb_frame = ttk.Frame(self)
kb_frame.grid(column=0, row=1, pady=(7, 19))
ttk.Label(kb_frame, text='Enter Key').grid(column=0, row=0, pady=4)
self.entry = EntryBox(kb_frame)
self.entry.grid(column=0, row=1)
# This protocol calls the function when clicked on 'x' on titlebar
self.protocol("WM_DELETE_WINDOW", self.Destroy)
# Confirm button
self.co_button = ttk.Button(self, text='Confirm', command=self.on_press)
self.co_button.grid(column=0, row=2)
def on_press(self):
key = self.entry.get()
# Condition to not have duplicate values, If you want to have duplicate values remove the condition
if key not in self.master.lb.get('0', 'end') and key:
# Insert the Key to the listbox of mainwindow
self.master.lb.insert('end', key)
def Destroy(self):
# Enable the button
self.master.kb_button['state'] = 'normal'
# Then destroy the window
self.destroy()
class Main(tk.Tk):
def __init__(self):
super(Main, self).__init__()
bt_frame = ttk.Frame(self)
bt_frame.grid(column=2, row=0, rowspan=2)
self.kb_button = ttk.Button(bt_frame, text='KeyBoard', command=self.on_press)
self.kb_button.grid(column=0, row=0)
self.lb = tk.Listbox(bt_frame)
self.lb.grid(column=0, row=1)
def on_press(self):
# Creating a toplevel window and passed self as master parameter
self.Kb = Keyboard(self)
# Disabled the button
self.kb_button['state'] = 'disabled'
main = Main()
main.mainloop()

How do I use the The Tkinter Menu Widget?

I don't get how to add the The Tkinter Menu Widget with my code I tried the code on a website but it won't work. It think im just being silly but please help. Just for the record I have looked at other stack overflow solutions but they did't work.
import pyautogui
import time
import tkinter as tk
class Coordinates():
replayBtn = (100,350)
class YourGUI(tk.Tk):
def __init__(self):
# inherit tkinter's window methods
tk.Tk.__init__(self)
#Enter X field and label ⬇
tk.Label(self, text="ENTER X:").grid(row=0, column=3)
self.inputX = tk.Entry(self)
self.inputX.grid(row=0, column=1)
#Enter Y field and label ⬇
tk.Label(self, text="ENTER Y:").grid(row=0, column=0)
self.inputY = tk.Entry(self)
self.inputY.grid(row=0, column=4)
# Start Button ⬇
tk.Button(self, text="start", command=self.do_conversion).grid(row=3, column=0, columnspan=2)
# close button ⬇
tk.Button(self, text="exit!", command=self.EXITME).grid(row=4, column=0, columnspan=2)
def EXITME(self):
exit(0) # crashed prog so it closes
# strtoint("crashmE!")
def do_conversion(self):
y = self.inputY.get()
x = self.inputX.get()
running = True
try:
x = int(x)
y = int(y)
except:
print("Invalid point")
exit(0)
# strtoint("crashmE!")
while running:
pyautogui.click(x, y)
if __name__ == '__main__':
your_gui = YourGUI()
your_gui.title('Macro Clicker') # Set title
your_gui.iconbitmap('favicon.ico') # Set icon
your_gui.resizable(False, False)
your_gui.mainloop()
time.sleep(0)
Sorry to say that but if you aren't lazy u will find over thousend examples about tkinter menu.
Even your code has nothing to do with tkinter.menu!?
Here's an example of mine. The "Info" menu is empty just to show you how create additional menus.
from tkinter import Tk, Frame, Menu
class Gui(Frame):
def __init__(self, master):
self.master = master
Frame.__init__(self, self.master) #main container
self.grid()
self.create_menu()
def create_menu(self):
self.menubar = Menu(self.master)
self.theme = Menu(self.menubar, tearoff=0)
self.menubar.add_cascade(label="View", menu=self.theme)
self.menubar.add_command(label="Info", command=self.passing)
self.views = Menu(self.theme, tearoff=0)
self.theme.add_cascade(label="Themes", menu=self.views)
self.views.add_command(label="Default", command=self.passing)
self.views.add_command(label="Red", command=self.passing)
self.views.add_command(label="Blue", command=self.passing)
self.views.add_command(label="Random", command=self.passing)
self.master.configure(menu=self.menubar)
def passing(self):
print("Use your brain before asking questions on stackoverflow or use google if your brain is slow")
if __name__ == "__main__":
root = Tk()
root.geometry("300x200")
my_gui = Gui(root)
root.mainloop()

How to refresh the tkinter Frame and listBox after performing a specific action?

I have two listboxes on tkinter, what I'd like to do is double-click the item in one listbox, add it to the list, and have it show up in the other listbox. Currently, the adding to list part is working, but it's not showing up in the other listbox for some reason.
import tkinter as tk
testList = ["dog", "cat", "orange"]
newList = []
class SampleApp(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self._frame = HomePage
class HomePage(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self, master)
self.config(width=300, bg='white', height=500, relief='sunken', borderwidth=2)
self.reportNames = tk.StringVar(value=tuple(testList))
self.lbox = tk.Listbox(self, listvariable=self.reportNames, height=15, width=50, borderwidth=2)
self.lbox.pack()
self.lbox.bind('<Double-1>', lambda x: addButton.invoke())
addButton = tk.Button(self, text='Select', command=self.selection)
addButton.pack()
self.testNames = tk.StringVar(value=newList)
self.lbox2 = tk.Listbox(self, listvariable=self.testNames, height=15, width=50, borderwidth=2)
self.lbox2.pack()
def selection(self):
addThis = self.lbox.selection_get()
print(self.lbox.selection_get())
newList.append(addThis)
print(newList)
if __name__ == "__main__":
global app
app = SampleApp()
sidebar = HomePage(app)
sidebar.pack(expand=False, fill='both', side='left', anchor='nw')
app.geometry("1200x700")
app.mainloop()
Your StringVar testNames won't track changes made to newList, you need to update the StringVar every time newList changes.
def selection(self):
addThis = self.lbox.selection_get()
print(self.lbox.selection_get())
newList.append(addThis)
print(newList)
# update StringVar
self.testNames.set(newList)

How to make Tkinter button to be placed in particular position?

I am new to python so I was trying to make a GUI, in that I have to place a button in a particular position.
I tried using self.nxt_form.place(x=200,y=100) instead of self.nxt_form.pack().
But the button disappeared and only the frame appeared when it ran. Can you tell me how to place the button in a particular position?
Here is the code:
import tkinter as tk
class Main_form:
def __init__(self, root,title="Simulated MTBF"):
self.root = root
self.frame = tk.Frame(self.root)
"""Button nxt_form which moves to next form"""
self.nxt_form = tk.Button(self.frame, text = 'Next Form', width = 25,command = self.new_window)
self.nxt_form.pack()
self.frame.pack()
"""command to open new window by clicking Button """
def new_window(self):
self.newWindow = tk.Toplevel(self.root)
self.app = Demo2(self.newWindow)
class Demo2:
def __init__(self, root):
self.root = root
self.frame = tk.Frame(self.root)
self.quitButton = tk.Button(self.frame, text = 'Quit', width = 25, command = self.close_windows)
self.quitButton.pack()
self.frame.pack()
def close_windows(self):
self.root.destroy()
def main():
root = tk.Tk()
app = Main_form(root)
root.mainloop()
if __name__ == '__main__':
main()
when i am using tkinter i used column and row to position objects
self.btn = tk.Button(self, text = "button")
self.btn.grid(row = 1, column = 1)
EDIT - expanded on information in response to comment (below)
I would make an label and change its width and height to make the spacing you need (note im a beginer at python as well so this is probly a bad way but it works)
from tkinter import *
import tkinter as tk
from tkinter.ttk import Combobox,Treeview,Scrollbar
class MainMenu(Frame):
def __init__(self, master):
""" Initialize the frame. """
super(MainMenu, self).__init__(master)
self.grid()
self.create_GUI()
def create_GUI(self):
frame1 = tk.LabelFrame(self, text="frame1", width=300, height=130, bd=5)
frame1.grid(row=0, column=0, columnspan=3, padx=8)
#the frame is not needed but it is a good thing to use as can group
#parts of your interface together
self.text1 = Entry(frame1)
#note if you were not using frames would just put self here
self.text1.grid(row = 1, column = 0)
self.text2 = Label(frame1, text = "",height = 10)
self.text2.grid(row = 2 , column = 0)
self.text3 = Entry(frame1)
self.text3.grid(row = 3, column = 0)
root = Tk()
root.title("hi")
root.geometry("500x500")
root.configure(bg="white")
app = MainMenu(root)
root.mainloop()
Also note that you can not use pack and grid together what you could do is group your objects in different frames then use grid in one frame and pack in a different frame. I personally prefer to use grid to pack as it gives you more control over your object then pack does

Lift and raise a Canvas over a Canvas in tkinter

I'm creating a game, and I am using tkinter to build the GUI.
In this game, I want the user to start with a window, filled by a Canvas, with an image as background, and some buttons in some sort of item windows. And this Canvas is the home Canvas of the game.
The problem is that I want the user to click on a Button to land on other Canvas, who will host other things like the map or others buttons for others actions. Actually, I would like to superimpose several canvas (as in the Z-index method), and put a canvas on the top of the list, when I want it (if I click on a button for example).
I already searched and I had to change my mind several times, and now I really don't know how to do it.
I find the following code here on Stack Overflow, but it is coded for Python 2 (I think), and I'm starting coding in Python 3, so I am not able to translate it to Python 3 and solve my problem.
import Tkinter as tk
class SampleApp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.frame = tk.Frame(self)
self.frame.pack(side="top", fill="both", expand=True)
self.label = tk.Label(self, text="Hello, world")
button1 = tk.Button(self, text="Click to hide label",
command=self.hide_label)
button2 = tk.Button(self, text="Click to show label",
command=self.show_label)
self.label.pack(in_=self.frame)
button1.pack(in_=self.frame)
button2.pack(in_=self.frame)
def show_label(self, event=None):
self.label.lift(self.frame)
def hide_label(self, event=None):
self.label.lower(self.frame)
if __name__ == "__main__":
app = SampleApp()
app.mainloop()
My code using grid :
from tkinter import *
fenetre = Tk()
fenetre.title(my game)
# acceuil
# variable acceuil
largeur = 700
hauteur = 430
BG_acceuil = PhotoImage(file="BG_acceuil.gif")
acceuil = Canvas(fenetre, width=largeur, height=hauteur)
acceuil.create_image(0, 0, anchor=NW, image=BG_acceuil)
acceuil.grid(row=0)
acceuil.pack()
# fond
fond = PhotoImage(file="BG_acceuil.gif")
acceuil2 = Canvas(fenetre, width=largeur, height=hauteur)
acceuil2.create_image(0, 0, anchor=NW, image=fond)
acceuil2.pack()
# variable bt_jouer
x0 = 80
y0 = 230
class hide_me():
def hide_me(event, widget, pos):
widget.grid_forget()
def show_me(event, widget, pos):
widget.grid(row=pos)
# Boutton jouer
BT_jouer = Button(acceuil, text="Jouer", command=hide_me())
BT_jouer.configure(width=10, activebackground="#33B5E5", relief=GROOVE)
BT_jouer_window = acceuil.create_window(x0, y0, window=BT_jouer,)
BT_jouer.bind('<Button-1>', lambda event: hide_me(event, BT_jouer, 1))
BT_jouer.grid(row=1)
# Bouton règle
BT_regle = Button(acceuil2, text="Règles", command=fenetre.destroy)
BT_regle.configure(width=10, activebackground="#33B5E5", relief=FLAT, bd=0)
BT_regle_window = acceuil2.create_window(x0, y0 + 50, window=BT_regle)
# Boutton quitter
BT_quit = Button(acceuil, text="Quitter", command=fenetre.destroy)
BT_quit.configure(width=10, activebackground="#33B5E5", relief=FLAT)
BT_quit_window = acceuil.create_window(x0, y0 + 100, window=BT_quit)
fenetre.mainloop()
The answer is very easy: To convert to Python3, change Tkinter to tkinter, and it works!
import tkinter as tk
class SampleApp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.frame = tk.Frame(self)
self.frame.pack(side="top", fill="both", expand=True)
self.label = tk.Label(self, text="Hello, world")
button1 = tk.Button(self, text="Click to hide label",
command=self.hide_label)
button2 = tk.Button(self, text="Click to show label",
command=self.show_label)
self.label.pack(in_=self.frame)
button1.pack(in_=self.frame)
button2.pack(in_=self.frame)
def show_label(self, event=None):
self.label.lift(self.frame)
def hide_label(self, event=None):
self.label.lower(self.frame)
def main():
app = SampleApp()
app.mainloop()
return 0
if __name__ == '__main__':
main()
Note: You are not really hiding the label - it still occupies space on the canvas. The following code, from this entry, really removes the item. It can then be recalled with a pack() call:
from Tkinter import *
def hide_me(event):
event.widget.pack_forget()
root = Tk()
btn=Button(root, text="Click")
btn.bind('<Button-1>', hide_me)
btn.pack()
btn2=Button(root, text="Click too")
btn2.bind('<Button-1>', hide_me)
btn2.pack()
root.mainloop()
I did some testing, and made an equivalent program to yours... The only problem is that the unhidden widget is always packed at the end:
from tkinter import *
def hide_me(event, widget):
widget.pack_forget()
def show_me(event, widget):
widget.pack()
root = Tk()
lbl = Label(root, text="Victim")
btn = Button(root, text="Hide the victim")
btn.bind('<Button-1>', lambda event: hide_me(event, lbl))
btn.pack()
btn2 = Button(root, text="Show the victim")
btn2.bind('<Button-1>', lambda event: show_me(event, lbl))
btn2.pack()
lbl.pack()
root.mainloop()
A better version uses the grid() packer. Here you can actually restore the 'forgotten' widget to its original position. Only slightly more complicated :)
from tkinter import *
def hide_me(event, widget, pos):
widget.grid_forget()
def show_me(event, widget, pos):
widget.grid(row = pos)
root = Tk()
lbl = Label(root, text="Victim")
lbl.grid(row = 0)
btn = Button(root, text="Hide the victim")
btn.bind('<Button-1>', lambda event: hide_me(event, lbl, 0))
btn.grid(row = 1)
btn2 = Button(root, text="Show the victim")
btn2.bind('<Button-1>', lambda event: show_me(event, lbl, 0))
btn2.grid(row = 2)
root.mainloop()
EDIT: Another observation from the comments: Bryan Oakley commented that if you use .grid_remove() instead of .grid_forget(), then the coordinates will not be lost, and a simple .grid() will restore the widget at its location.

Categories