Tkinter Label class not appearing when used within a class - python-3.x

I am creating a basic GUI with multiple, but similar, label structures. However, when I created a class to help minimize the text, and placed it within a label frame, the label frame does not appear. This only happens when I use the class within a class, and if I use the regular label and label frame classes everything works out well. I'm trying to figure out as to why this is the case.
My code:
main.py
from tkinter import *
def main():
main_window = Tk()
app = First(main_window)
main_window.mainloop()
class GPULabel(Label):
def __init__(self, master, varText):
varText = varText
super().__init__()
self["text"] = varText
self["anchor"] = "w"
self["width"] = 25
class First:
def __init__(self, root):
self.root = root
self.root.title('First Window')
self.myFrame = LabelFrame(self.root, text="frame")
self.myFrame.pack()
label1 = GPULabel(self.myFrame, "label")
lable1.pack()
if __name__ == '__main__'
main()
This opens a window but it is completely empty. However, if I swap to a regular Label(self.myFrame...) then the window pops up correctly. Why is that? And is there a way to make my original method work?

Related

How to open two modules in one window, Python Tkinter

I have a question: i am tossing the code from 2ch files, i have already lost ideas. Calling fileA.py opens a window for me with two buttons exit and start. Exit works but when I click start I need to open the second window fileB.pt. (I want both windows to open in one window) Seemingly works only problem I have is it doesn't open "window on window" but "docks underneath" and I have the effect of two windows open :/. Please help, thank you in advance:) Python 3.10
fileA.py
import tkinter as tk
from GUI.module.scale_of_img import get_scale
class FirstPage:
def __init__(self, root):
self.root = root
def get_settings(self):
# Window settings
self.root.title('....')
self.root.resizable(False, False)
self.root.geometry("1038x900")
if __name__ == '__main__':
first = FirstPage(tk.Tk())
first.get_run_first_page()
fileB.py
import tkinter as tk
"importy..."
''' The second side of the application '''
class SecondPage:
def __init__(self, root=None):
self.root = root
self.my_canvas = tk.Canvas(self.root, width=1038, height=678)
self.my_canvas.pack(fill="both", expand=True)
if __name__ == '__main__':
second = SecondPage(tk.Tk())
second.get_run()
in order to put two "windows" in the same "window" you need to put all items inside a Frame, which is basically a container than you can simply pack when you want everything to show and unpack when you want everything in it to disapear.
all items in the first window will be children of a frame and all items in the second window will be children of another frame, and to switch you just need to call pack_forget() on one and pack() on another.
for the first file
class FirstPage:
def __init__(self, root):
self.root = root
self.frame = tk.Frame(root)
self.frame.pack(expand=True)
def get_picture(self):
# all items inside this window must be children of self.frame
self.my_canvas = tk.Canvas(self.frame, width=1038, height=500)
...
def get_second_page(self):
from GUI.module.second_page import SecondPage
self.frame.pack_forget() # to hide first page
# self.frame.destroy() # if you are never brining it back
SecondPage(self.root).get_run()
and for the second file
class SecondPage:
def __init__(self, root=None):
self.root = root
self.frame = tk.Frame(root) # new frame
self.frame.pack(expand=True)
self.my_canvas = tk.Canvas(self.frame, width=1038, height=678)
self.my_canvas.pack(fill="both", expand=True)
def get_button(self):
# Add buttons
# all here should be children of self.frame now
button1 = tk.Button(self.frame, text="...", )
...
you could destroy the first frame when you switch over to save some resources if you don't intend to return to it ever again, but the difference in memory is negligible.
assuming what you want is another Tk window to open, you shouldn't give it the same root, instead use an instance of Toplevel
from tkinter import Toplevel
# class definition here
def get_second_page(self):
from GUI.module.second_page import SecondPage
SecondPage(Toplevel(self.root)).get_run()
passing the Toplevel as a child of self.root is necessary, but note that the two windows have different roots.
Edit: turns out this wasn't what the OP ment by "window on window" -_-, but it am keeping it here for other readers.

Tkinter gives me a second window

I am writing code for a tkinter gui using a class, however I notice that when I run there is a second window besides the main one I made. I've tried a number of things but they either break the code or the window is black. See code below.
import tkinter as gui
class loginWindow(gui.Frame):
def __init__(self):
super(loginWindow, self).__init__()
self.logUI()
def logUI(self):
self.mainWindow = gui.Tk()
self.mainWindow.title("GLSC IT Inventory")
self.mainWindow.minsize(400, 150)
self.mainWindow.maxsize(400, 150)
self.mainWindow.geometry("400x150")
self.greet_label = gui.Label(self.mainWindow, text="Welcome!!!")
self.greet_label.place(x=180, y=5)
self.uname_label = gui.Label(self.mainWindow, text="Username:")
self.uname_label.place(x=10, y=24)
self.uname_input = gui.StringVar()
self.uname_field = gui.Entry(self.mainWindow, bd=4, textvariable=self.uname_input)
self.uname_field.place(x=80, y=25, width=160)
self.pwd_label = gui.Label(self.mainWindow, text="Password:")
self.pwd_label.place(x=10, y=54)
self.pwd_input = gui.StringVar()
self.pwd_field = gui.Entry(self.mainWindow, bd=4, textvariable=self.pwd_input, show="\u2022")
self.pwd_field.place(x=80, y=55, width=160)
self.login_button = gui.Button(self.mainWindow, text="Login", command=None)
self.login_button.place(x=180, y=95)
my_app = loginWindow()
my_app.mainloop()
When you create instance of loginWindow(), an instance of Tk() is required but there is none, so it will be created implicitly for you.
Then another instance of Tk() is created inside logUI(). So there are two instances of Tk().
One way to fix it is loginWindow not inherited from Frame:
class loginWindow:
def __init__(self):
self.logUI()
def logUI(self):
...
# add for calling tkinter.mainloop()
def mainloop(self):
self.mainWindow.mainloop()

How can I create a multiple buttons in tkinter that change the background image when hovering over them?

I am coding a GUI for a Mathematics Formula Calculator. I want to create multiple buttons that change the background image when hovering over them, and I don't really know how to go about doing that...
I have already tried creating a class for the button itself so that I can modify the behaviour of it, it did not work...
import tkinter as tk
class HoverButton(tk.Button):
def __init__(self, master, **kw):
tk.Button.__init__(self, master=master,**kw)
self.defaultbackground = tk.PhotoImage(file = "GeometricBackground.png")
self.currentbackground = tk.PhotoImage(file = "")
self.bind("<Enter>", self.on_enter)
self.bind("<Leave>", self.on_leave)
def on_enter(self, currentbackground):
image = tk.Label(self, image = currentbackground)
image.pack()
def on_leave(self):
image = tk.Label(self, image = self.defaultbackground)
image.pack()
root = tk.Tk()
classButton = HoverButton(root, currentbackground = "MainMenu.png")
classButton.grid()
root.mainloop()
I was really hoping this would cut it, but I got this error message when it executed:
_tkinter.TclError: unknown option "-currentbackground"
Any help would be appreciated :)
There are several issues with your code:
The error you get is because you are trying to pass the currentbackground option to your HoverButton but given the way your class is defined:
def __init__(self, master, **kw):
tk.Button.__init__(self, master=master,**kw)
currentbackground ends into the kw dictionary you pass in argument to the standard tkinter Button class which has no currentbackground option, hence the unknown option error. To fix it, you can define the options specific to your class like
def __init__(self, master, defaultbackground="", currentbackground="", **kw):
tk.Button.__init__(self, master=master, **kw)
so that defaultbackground and currentbackground won't end in the kw dictionary.
When an event occurs, the function you bound to this event is executed with one argument, the "event" object that contains information about the event (like the pointer coordinates, the widget in which the event happened ...) so you need to add this argument when you define on_enter() and on_leave().
You are creating a label to put the image inside then packing this label in the button. This is overly complicated (and probably will result in the button not reacting to click events). The button class has an image option to set the background image of the button, so you can change the image with button.configure(image=<image>).
Inserting all those changes in the code gives
import tkinter as tk
class HoverButton(tk.Button):
def __init__(self, master, defaultbackground="GeometricBackground.png", currentbackground="", **kw):
tk.Button.__init__(self, master=master, **kw)
self.defaultbackground = tk.PhotoImage(file=defaultbackground)
self.currentbackground = tk.PhotoImage(file=currentbackground)
self.configure(image=self.defaultbackground)
self.bind("<Enter>", self.on_enter)
self.bind("<Leave>", self.on_leave)
def on_enter(self, event):
self.configure(image=self.currentbackground)
def on_leave(self, event):
self.configure(image=self.defaultbackground)
root = tk.Tk()
classButton = HoverButton(root, currentbackground="MainMenu.png")
classButton.grid()
root.mainloop()

Python Tkinter GUI File Menu Not Displaying though GUI is operational

I'm relatively new to Python and I'm sure this is an error with the structure of my code, but I cannot seem to get the filemenu to display in my GUI. Can someone tell me what errors I have made with the filemenu inclusion? Also, I am sorry, but the spacing after copying and pasting is a little off. The class indentation level is proper on my side. I am using Python 3.71
Any other comments on better or more Pythonic ways to accomplish what I have here are also welcome and thank you for your help in advance!
from tkinter import *
from tkinter import ttk
import tkinter.scrolledtext as tkst
import os
import tkinter as tk
from functools import partial
from PIL import Image, ImageTk
class UserGui(tk.Tk):
def __init__(self,parent):
self.parent=parent
self.widgets()
def widgets(self):
self.parent.configure(bg='white')
self.frame1_style = ttk.Style()
self.frame1_style.configure('My.TFrame', background='white')
self.frame2_style = ttk.Style()
self.frame2_style.configure('My2.TFrame',background='white')
self.parent.title("TGUI")
self.frame1 = ttk.Frame(self.parent, style='My.TFrame') #Creating Total Window Frame 1
self.frame1.grid(row=0, column=0, sticky=(N, S, E, W))
self.frame2 = ttk.Frame(self.parent, width=100, height=20, style='My2.TFrame')
self.frame2.grid(row=0, column=6, padx=20, pady=5)
#Menu Creation
self.menu1 = tk.Menu(self.parent, tearoff=0)
self.parent.config(menu=self.menu1)
self.fileMenu = tk.Menu(self.menu1, tearoff=0)
self.fileMenu.add_command(label="Open", command=self.donothing)
self.fileMenu.add_command(label="Save", command=self.donothing)
self.fileMenu.add_separator()
self.fileMenu.add_command(label="Exit", command=self.parent.quit)
self.fileMenu.add_cascade(label="File", menu=self.menu1)
self.editMenu = tk.Menu(self.menu1, tearoff=0)
self.editMenu.add_command(label="Cut", command=self.donothing)
self.editMenu.add_command(label="Copy", command=self.donothing)
self.editMenu.add_command(label="Paste", command=self.donothing)
self.editMenu.add_cascade(label="Edit", menu=self.menu1)
def donothing(self):
filewin = Toplevel(self.parent)
button = Button(filewin, text="Do nothing button")
button.pack()
def main():
root=tk.Tk()
ug=UserGui(root)
root.mainloop()
if __name__ == '__main__':
main()
Edit 1,2,3: I have corrected the add_cascade option for menu with menu=self.menu1 and I still do not have a file menu displaying.
EDIT: I'm sorry I didn't notice the Python-3 tag in time, it's all the same except when inherriting you would call super().__init__ instead of the Frame.__init__ directly. That would make it more Py3-like. Even so, this should still work.
Weirdly, pushing the menu.config down to the run function worked for me - even though it looks like it should work the way you did it.
def main():
root=tk.Tk()
ug=UserGui(root)
root.config(menu=ug.fileMenu)
root.mainloop()
if __name__ == '__main__':
main()
Oterwise there are some things you can work on to make it more OOP like and readable. THis is how I usually handle making GUIs. The idea is to split the GUI's into Frames that then do simmilar things. I.e. your app could have left and right Frame where the RightFrame would hold the textbox ad the left Frame would actually have 2 sub frames - one for the names and dropdowns and the other for the buttons. That way each individual functionality is handled by the Frames themselves and it's not all in one giant class, the elements in those Frames are placed relative to the Frame's grid itself, while all the Frames are placed in the MainFrame's grid. This lets you split a lot of code into modules as well and helps with maintainability.
The sub-frames emit "global" events (events bothering other frames) by propagating them through the MainFrame, that's why they all carry a self.parent - their parent frame, and a self.root - the MainFrame. The MainFrame is also the Frame in which I like to put something like self.data which itself is a class on its own (outside Tkinter) that handles all the data input/output and logic so that you don't clutter the GUI code logic with data calculations and logic. Ideally the Data class would handle data errors and GUI would only then have to handle any errors in logic (such as selecting two impossible-to-combine options from the dropdown menus.
from tkinter import *
from tkinter import ttk
class SubFrame(Frame):
def __init__(self, parent, text="Top Right"):
Frame.__init__(self)
self.pack()
self.parent = parent
self.root = parent.root
self.label=Label(self, text=text).pack()
class RightFrame(Frame):
def __init__(self, parent):
Frame.__init__(self, relief=RAISED, borderwidth=1)
self.pack(side=RIGHT, fill=BOTH, expand=1)
self.root = parent
self.label = Label(self, text="Right Frame").pack()
class LeftFrame(Frame):
def __init__(self, parent):
Frame.__init__(self, relief=RAISED, borderwidth=1)
self.pack(side=LEFT, fill=BOTH, expand=1)
self.root = parent
self.label = Label(self, text="Left Frame").pack()
#again Frames which would have a parent class RightFrame and root MainFrame
self.subFrame1 = SubFrame(self)
self.subFrame2 = SubFrame(self, text="Top Right SubFrame 2")
class MainFrame(Tk):
def __init__(self):
Tk.__init__(self)
self.geometry("1100x600")
self.title("Working Example")
self.leftFrame = LeftFrame(self)
self.rightFrame = RightFrame(self)
#self.data = MagicalDataHandlingClass()
def run():
app = MainFrame()
app.mainloop()
EDIT answer to comments that are too long to fit
The call to Frame.__init__(...) is made because the class definition looks like class LeftFrame(Frame). Usually to declare a class what you would write is just class LeftFrame. When you add the bit in the () what is happening is called inheritance. When you inherit from a class (called parent), your class (called child) inherits all of the methods and attributes of parent. But much like you have to initialize your class to get an object, i.e. lf = LeftFrame(...) the parent class has to be initialized too. In Python this initialization is done by calling the special dunder __init__(...) function. So that call to Frame.__init__(...) happens because you need to tell the parent class what are all the values it needs to work properly. In Python 3 however it is recommended that instead of instantiating the parent by name like that you use the super function like super().__init__(....). This happens for a lot of complicated reasons most of which you probably don't have to worry about for a while yet (such as what if you inherit from multiple classes at the same time, what if you inherit from a class that inherited from a different one, etc...). I wouldn't try to feel overwhelmed by understanding the complete power of super() if you're just starting because 99% of the time in Python 3 just doing super().__init__(...) will do exactly what you want even if you don't understand. If you feel like getting in over your head Raymond Hettinger has a good writeup of Super is Super and why exactly it's so much better than old way.
I will post this answer for completeness considering #JasonHarper has not copied it to an answer format and I want others to be able to benefit from the post.
The key was the object that I was calling the add_cascade on the child Menu widget object instead of the main Menu widget object called self.menu1. The key was changing:
self.fileMenu.add_cascade(label="File", menu=self.menu1)
to :
self.menu1.add_cascade(label="File", menu=self.fileMenu)
This was the proper way of adding the fileMenu Menu object to the total Menu widget object of self.menu1.

Is there a way to stop a tkinter class object from opening until the button is opened

I have a little problem with my code. I am writing multiples classes with different GUI interfaces as a project. However, every time I import those classes the GUI window automatically opens the window and I want the window to open only when a button is clicked.
from FinalProject import addFlight
from FinalProject import reserveFlight
class ex:
def __init__(self,win):
self.win = win
...
...
def mainButtons(self):
look = Button(self.win, text="Add New Flight",command=lambda: self.reserveMenu(1))
look.place(relx="0.2", rely="0.3")
res = Button(self.win, text="Book A Flight",command=lambda: self.reserveMenu(2))
res.place(relx="0.4", rely="0.3")
...
...
def reserveMenu(self, options):
if options == 1:
self.flight = Toplevel(self.win)
self.flMenu = addFlight.AddFlights(self.flight)
self.flMenu.addingFlight()
# call(["python","addFlight.py"])
if options == 2:
pass
# self.flight = Toplevel(self.win)
# self.flMenu = reserveFlight.ReserveFlights(self.flight)
# self.flMenu.reserve()
# call(["python","reserveFlight.py"])
...
...
The "reserveMenu" function works fine but is there way to suppress those import statements or at least prevent the windows from opening until the button is clicked.
I know there are other methods of opening my python code but this HAS to be done using CLASSES. Trust me I have found way easier methods of doing this. FYI, there is more code but I only copied the more important parts.
Instead of using a method you could define your reserve option windows as classes, ReserveAdd, ReserveBook, that inherit from tkinter.Toplevel. And all a button would do is to call them. Here's an example:
import tkinter as tk
root = tk.Tk()
class ReserveAdd(tk.Toplevel):
def __init__(self, master):
super().__init__(master)
self.master = master
tk.Label(self, text="This is ReserveAdd window.").pack()
class ReserveBook(tk.Toplevel):
def __init__(self, master):
super().__init__(master)
self.master = master
tk.Label(self, text="This is ReserveBook window.").pack()
def res_one():
ReserveAdd(root)
def res_two():
ReserveBook(root)
tk.Button(root, text="Reserve Option 1", command=res_one).pack()
tk.Button(root, text="Reserve Option 2", command=res_two).pack()
root.mainloop()
In the above example Reserve Option 1 calls an instance of ReserveAdd class whereas Reserve Option 2 calls an instance of a ReserveBook class.
I'd define a single method for buttons but that's not exactly the scope here.

Resources