Wired that ttk Label textvariable can't be displayed in class properly - python-3.x

I am totally newbie of Python. However i have to use python 3 to do some projects. I am really confused by ttk.Label with textvariable. Below is my codes.
from tkinter import *
from tkinter import ttk
class new_label:
def __init__(self, master):
self.master = master
self.label_var = StringVar()
ttk.Label(self.master, text="iii").grid(row=0, sticky = "w")
self.create_label()
def create_label(self):
self.l1 = ttk.Label(f,
textvariable = self.label_var,
foreground = "red",)
self.l1.grid(row=1)
self.label_var.set("First Label")
print(self.l1.cget("text"))
r=Tk()
r.title("My Label Update")
f=ttk.Frame(r)
f.grid(row=0)
new_label(f)
r.mainloop()
In my codes I add a print and it can print the text well. However the text can't display out and there is no any message of any errors. I do need someone to help for this issue.
Thank you very much in advance.

You aren't keeping a reference so the instance of new_label, so the python garbage collector is collecting it. The ttk widgets are particularly sensitive to the use of garbage-collected StringVar instances.
The simple solution is to keep a reference to your instance of new_label:
x = new_label(f)

Related

Python scrollable frame and canvas window sizing

I have been trying to find a way to size a frame inside of a canvas window for quite a while to no avail. I finally came across some posts that helped me begin to understand the problem, and eventually dug up a post that gave the solution below:
import tkinter as tk
def onCanvasConfigure(e):
canvas.itemconfig('frame', height=canvas.winfo_height(), width=canvas.winfo_width())
root=tk.Tk()
canvas = tk.Canvas(root, background="blue")
frame = tk.Frame(canvas, background="red")
canvas.pack(expand=True, fill="both")
canvas.create_window((0,0), window=frame, anchor="nw", tags="frame")
canvas.bind("<Configure>", onCanvasConfigure)
root.mainloop()
This completely solves my problem....if I don't have the GUI in a function, which I need to. I have multiple different GUI's that would need to implement this solution. I have come across other solutions that use OOP, but I haven't yet wrapped my head around OOP. I've also found a way to make the above code work inside of a program myself:
import tkinter as tk
def onCanvasConfigure(e):
canvas.itemconfig('frame', height=canvas.winfo_height(), width=canvas.winfo_width())
def test():
window=tk.Tk()
global canvas
canvas = tk.Canvas(master=window)
frame=tk.Frame(master=canvas, background='red')
canvas.pack(expand=True, fill=tk.BOTH)
canvas.create_window((0,0), window=frame, anchor=tk.NW, tags = 'frame')
canvas.bind("<Configure>", onCanvasConfigure)
root.mainloop()
test()
However, this requires the use of a global variable, which I would rather avoid. Is there anything I'm missing that would help me resize the frame inside of the canvas window? If you have any pointers to where I might even find this information that would also be helpful.
The event object that is passed in has a reference to the widget that received the event. So, just replace your global canvas with e.widget, or initialize a local variable named canvas:
def onCanvasConfigure(e):
canvas = e.widget
canvas.itemconfig('frame', height=canvas.winfo_height(), width=canvas.winfo_width())
If it helps, here's an object-oriented version of your application code. Other than the implementation differences, it should behave the same way as the functional version.
import tkinter as tk
class App(tk.Tk): # create a base app class that inherits from Tk
def __init__(self):
super().__init__() # initialize Tk
self.canvas = tk.Canvas(master=self)
self.frame = tk.Frame(master=self.canvas, background='red')
self.canvas.pack(expand=True, fill=tk.BOTH)
self.canvas.create_window(
(0,0),
window=self.frame,
anchor=tk.NW,
tags='frame',
)
self.canvas.bind('<Configure>', self.on_canvas_configure)
def on_canvas_configure(self, event):
self.canvas.itemconfig(
'frame',
height=self.canvas.winfo_height(),
width=self.canvas.winfo_width(),
)
if __name__ == '__main__':
root = App() # instantiate the App class
root.mainloop() # start the app
Since everything here is contained within the App class, you can avoid globals (thanks to self!)

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.

python tkinter update content of a label when a file is opened

I'm currently programming a GUI using tkinter and Python 3.
My problem here is i made a Label with which i want to display the path of a file i opened via the askopenfilename() method and this path is not "generated" when i start the program, obviously, so the Label is empty which makes sense but i don't know how to fix it.
I'm gonna put the needed code below (I'm going to cut unnecessary code for this question):
import tkinter as tk
class Graphicaluserinterface(tk.Frame):
def __init__(self,master=None):
super().__init__(master)
self.grid()
self.fileopenname=tk.StringVar()
self.menubar = tk.Menu(self)
self.create_widgets()
def create_widgets(self):
self.inputpathdisplay = tk.Label(self,textvariable=self.fileopenname,bg="white",width=30)
self.inputpathdisplay.grid(row=1,column=8,columnspan=3,sticky = "W")
def fileopening(self):
from tkinter.filedialog import askopenfilename
self.fileopenname = askopenfilename(filetypes = [("binary files","*.bin*"),("all files","*.*")])
root = tk.Tk()
app = Graphicaluserinterface(master=root)
root.config(menu=app.menubar)
app.mainloop()
I read about using update_idletasks(). If this is correct in my case how would i go about implementing it here?
Right now you are doing self.fileopenname = askopenfilename() and this will redefine self.fileopenname as a string instead of a StringVar(). To correct this you need to set the value of StringVar with set().
That said you should also define all your imports at the top of your code instead of in your function.
import tkinter as tk
from tkinter.filedialog import askopenfilename
class Graphicaluserinterface(tk.Frame):
def __init__(self,master=None):
super().__init__(master)
self.grid()
self.fileopenname=tk.StringVar()
self.menubar = tk.Menu(self)
self.inputpathdisplay = tk.Label(self, textvariable=self.fileopenname, bg="white")
self.inputpathdisplay.grid(row=1,column=8,columnspan=3,sticky = "W")
self.fileopening()
def fileopening(self):
self.fileopenname.set(askopenfilename(filetypes = [("binary files","*.bin*"),("all files","*.*")]))
root = tk.Tk()
app = Graphicaluserinterface(master=root)
root.config(menu=app.menubar)
app.mainloop()

python3 tkinter Entry() cannot select text field until I click outside app window once

I've written a very simple app with python3, tkinter, but am seeing some strange behaviour with Entry(). I'm new to tkinter and python.
import os
from tkinter import Tk, Entry, filedialog
class MyGUI:
def __init__(self,master):
self.master = master
self.date_entry = Entry(master)
self.date_entry.pack()
self.date_entry.insert(0,"test")
self.master.mainloop()
root = Tk()
root.directory = os.path.abspath(filedialog.askdirectory())
my_gui = MyGUI(root)
When I run this code, the second to last line is what is causing the following problem:
When I try to edit the "test" text I cannot select it (no cursor or anything). However, if I click once away from the app (e.g. desktop) I can then edit it.
Does anyone know what the problem could be?
I was wondering if it's to do with a new app window being created by the filedialog, but I couldn't find an answer.
Thanks for your replies!
After testing this odd behavior a bit it appear as though as long as you add a button to get the directory the issue goes away.
I find it odd however and I will see if I can find anything that could explain why tkinter is acting like this.
This code should work for you:
import tkinter as tk
from tkinter import filedialog
class MyGUI(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.date_entry = tk.Entry(self)
self.date_entry.pack()
self.date_entry.insert(0, "test")
self.directory = ""
tk.Button(self, text="Get Directory", command=self.get_directory).pack()
def get_directory(self):
self.directory = filedialog.askdirectory()
MyGUI().mainloop()
UPDATE:
I have recently learned that adding update_idletasks() before the filedialog will fix the focus issue.
Updated code:
import os
from tkinter import Tk, Entry, filedialog
class MyGUI:
def __init__(self,master):
self.master = master
self.date_entry = Entry(master)
self.date_entry.pack()
self.date_entry.insert(0,"test")
self.master.mainloop()
root = Tk()
root.update_idletasks() # fix focus issue.
root.directory = os.path.abspath(filedialog.askdirectory())
my_gui = MyGUI(root)

global name "IntVar" not defined

I defined a class and variables inside that class.
I preceded all the variable names with self.varname.
I don't know what is wrong, but I keep getting "global name 'IntVar' not defined" when trying to run the script
This is part of the code:
import tkinter as tk
from tkinter import Frame, Button, Label
import time
import random
class fragal(tk.Frame):
def __init__(self, master=None):
tk.Frame.__init__(self, master)
self.grid()
self.mymaster = master
self.primes = []
self.cntr1a = 0
self.cntr1b = 0
self.cntr2a = 0
self.cntr2b = 0
self.count = IntVar()
self.count.set(0)
self.time_remaining = IntVar()
self.time_remaining.set(0)
self.gamearray = []
self.equation = StringVar()
self.equation.set("")
Please help! I have several versions of my program and this version was
working until I started changing the main() loop.
IntVar is defined in Tkinter. Because you're importing Tkinter "as tk", you need to prefix IntVar with tk.:
self.count = tk.IntVar()
You also do not need this line: from tkinter import Frame, Button, Label You aren't directly using any of those classes. Plus, you're already importing all of tkinter so you don't need to also import just parts of tkinter.
You have not imported IntVar from tkinter that's why it is undefined. You should impoprt it first by writing this from tkinter import IntVar. Now use your IntVar() and you will not face this problem again.

Resources