Missing menu in Tkinter GUI - python-3.x

I have developed a script for image analysis and would like to wrap it with a GUI. I have decided to use tkinter. I have created a class which when I try to initilise is missing a menu that I have scripted for. I am slightly confused as to why my script is not working, I am new to tkinter and also to classes, which adds to the confusion :) Any help as to why my script is not working would be greatly appreciated, thank you!
import tkinter as tk
#from tkinter import filedialog
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("Lifespan Data Analyser")
self.pack(fill=BOTH, expand=1)
menu = Menu(self.master)
self.master.config(menu=menu)
file = Menu(menu)
file.add_command(label='Exit', command = self.frame.quit)
menu.add_cascade(label ='File', menu=file)
browse = Menu(menu)
browse.add_command(label='Browse', command=self.browse_button)
menu.add_cascade(label='Start',menu=browse)
def browse_button(self):
global file_path
# Allow user to select a directory and store it in global var
file_path = filedialog.askdirectory()
file_path=str(file_path)
print(file_path)
root = Tk()
root.geometry("400x300")
app = Window(root)
root.mainloop()

When I try to add the "Exit" item to the File menu I get the error:
AttributeError: 'Window' object has no attribute 'frame'
which makes sense as there is no attribute "frame". I'm thinking this maybe works better:
file.add_command(label='Exit', command=self.master.destroy)

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.

How to enter a file name into your graphics window and then read that file and display the file in a graphics window using a file menu

I'm trying to enter a file name into your graphics window and then read that file and display the file in a graphics window using a file menu. When I press new another window opens to ask for a name and displays it in the main window, but I can't get the new window to open and do the rest. I have the TopLevel to open a new window, but I get NameError: name 'TopLevel' is not defined and can't continue from there. What can I do to make it work?
from tkinter import Tk, Frame, Menu
from tkinter.ttk import *
from tkinter import filedialog as fd
class Example(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.parent = parent
self.initUI()
def initUI(self):
self.parent.title("Simple menu")
menubar = Menu(self.parent)
self.parent.config(menu=menubar)
fileMenu = Menu(menubar)
fileMenu.add_command(label="New", command=self.onNew)
menubar.add_cascade(label="File", menu=fileMenu)
def onNew(self):
print("do New")
top = TopLevel()
Label(self, text='Enter Your Name').grid(row=0)
e1 = Entry(self)
e1.grid(row=0, column=1)
def main():
root = Tk()
root.geometry("250x150+300+300")
app = Example(root)
root.mainloop()
if __name__ == '__main__':
main()
The problem is simply that you're not defining or importing anything named Toplevel. The way you're importing tkinter makes this an easy problem to have.
My recommendation is to remove these statements:
from tkinter import Tk, Frame, Menu
from tkinter.ttk import *
... and replace them with these:
import tkinter as tk
from tkinter import ttk
From then on, you have access to almost everything in the tkinter and ttk packages. You simply need to add a tk. or ttk. prefix to everything you use from those packages.
This keeps global namespace pollution at a minimum (ie: you only add two names to the global namespace), and makes your code more self-documenting.
class Example(tk.Frame):
def __init__(self, parent):
ttk.Frame.__init__(self, parent)
self.parent = parent
self.initUI()
def initUI(self):
self.parent.title("Simple menu")
menubar = tk.Menu(self.parent)
self.parent.config(menu=menubar)
fileMenu = tk.Menu(menubar)
fileMenu.add_command(label="New", command=self.onNew)
menubar.add_cascade(label="File", menu=fileMenu)
def onNew(self):
print("do New")
top = tk.TopLevel()
ttk.Label(self, text='Enter Your Name').grid(row=0)
e1 = ttk.Entry(self)
e1.grid(row=0, column=1)
def main():
root = tk.Tk()
root.geometry("250x150+300+300")
app = Example(root)
root.mainloop()

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)

Python3 class function definition confusion about NameError

I'm new to programming and this is my first post on the site. I'm sure I'm making a dumb mistake, but I'd really appreciate a push in the right direction. I'm trying to make a calculator, and want to make a function that produces a Button object for numbers. When I try to run this I get the error:
'NameError: name 'num_but_gen' is not defined'
Here is the code:
from tkinter import *
WINDOW_HEIGHT = 300
WINDOW_WIDTH = 325
class Window(Frame):
def __init__(self, master = None):
Frame.__init__(self, master)
self.master = master
self.init_window()
def num_but_gen(self, disp, xloc=0, yloc=0, wid=0, hei=0):
self.Button(text='{}'.format(disp),height=hei, width=wid)
self.place(x=xloc, y=yloc)
def init_window(self):
self.master.title('Calculator')
self.pack(fill=BOTH, expand=1)
Button1 = num_but_gen('1', xloc=0, yloc=200, wid=40, hei=40)
root = Tk()
app = Window(root)
root.geometry("{}x{}".format(WINDOW_WIDTH,WINDOW_HEIGHT))
root.mainloop()
Any help would be greatly appreciated! Also bonus points to anyone with suggestions on how to better phrase my question titles in future posts.
jasonharper is right, you need to add self in front of num_but_gen, but there are other problems in your code.
In num_but_gen:
your window class does not have a Button attribute, so you need to remove self. in front of Button
it is not the Window instance but the button that you want to place
you don't need to use text='{}'.format(disp), text=disp does the same.
In init_window:
you store the result of num_but_gen in a variable, but this function returns nothing so that's useless (and capitalized names should not be used for variables, but for class names only)
the width option of a button displaying text is in letters, not in pixels and its height option is in text lines, so wid=40, hei=40 will create a very big button. If you want to set the button size in pixels, you can do it through the place method instead.
Here is the corresponding code:
import tkinter as tk
WINDOW_HEIGHT = 300
WINDOW_WIDTH = 325
class Window(tk.Frame):
def __init__(self, master = None):
tk.Frame.__init__(self, master)
self.master = master
self.init_window()
def num_but_gen(self, disp, xloc=0, yloc=0, wid=0, hei=0):
button = tk.Button(self, text=disp)
button.place(x=xloc, y=yloc, height=hei, width=wid)
def init_window(self):
self.master.title('Calculator')
self.pack(fill=tk.BOTH, expand=1)
self.num_but_gen('1', xloc=0, yloc=200, wid=40, hei=40)
root = tk.Tk()
app = Window(root)
root.geometry("{}x{}".format(WINDOW_WIDTH,WINDOW_HEIGHT))
root.mainloop()

Resources