I have programmed a 2600 lines application which worked great when running from the IDE. Now I have created an executable via Pyinstaller and now the GUI does not start. The application starts and disappears quickly. I do not get any errors (anymore, solved them), however this problem remains. I think it has to do with missing the mainloop() in my application, which I don't know how to apply in this particular case. Usually its like this:
root = tk.Tk()
root.mainloop()
In my case I created a class for my window adding a menubar and label as statusbar (the latter not shown in my code below). This makes me assigning this class being a Tk() to main_window. Where do I put the mainloop() without getting an error?
I already tried:
main_window.mainloop()
since main_window is the window where all frames put on to, but then I get the following error in the IDE:
main_window.mainloop() AttributeError: 'AppWindow' object has no attribute 'mainloop'
How do I apply the mainloop() into my application without getting the above mentioned error? Or how do I get my GUI to be working in a different manner? Both answers are welcome.
Here is the necessary code to know:
import tkinter as tk
class AppWindow():
def __init__(self, master):
self.master = master
master.title("Basic Application")
master.geometry("1060x680")
master.grid_propagate(False)
#create drop down menu
self.menubar = tk.Menu(master) # main menubar
#Add filemenu
self.filemenu = tk.Menu(self.menubar, tearoff=0) #sub menu
self.filemenu.add_separator() #create a bar in the menu
self.filemenu.add_command(label="Quit", command=master.destroy) #Add submenu item
self.menubar.add_cascade(label="File", menu=self.filemenu) #Add submenu to menubar
self.master.config(menu=self.menubar) #Show menu
class FrameOne(tk.Frame):
def __init__(self, parent):
super().__init__()
self["borderwidth"]=5
self["relief"]="ridge"
self.create_widgets() #Function which creates all widgets
self.position_widgets() #Function which position all widgets
def create_widgets(self): #Function which creates all widgets
pass
def position_widgets(self): #Function which position all widgets
pass
#Create a window as defined in the AppWindow class
main_window = AppWindow(tk.Tk())
#Create a Frame as defined in class FrameOne
first_frame = FrameOne(main_window)
first_frame.grid(row=0, column=0) #Positioning Frame on Window
main_window.mainloop() #THIS PROVIDES AN ERROR | GUI DOES NOT START WITHOUT
mainloop is a method on the tkinter root window and on tkinter itself. Like the error says, it's not a method of your AppWindow class.
In your case you should do it like this:
root = tk.Tk()
main_window = AppWindow(root)
root.mainloop()
Related
I am trying to call a new window by pushing the button of an existing window. The original window should close when the new window is being created. When I push the button the new window will show up as expected but additionally a blank window will appear. Using tk.Tk() or tk.Toplevel() will lead to the same result.
Later in the program I want to destroy the created window again. When using tk.Tk() closing the blank window by mouse will create an error "application has been destroyed" when the destroy() method gets applicated to the new window.
import tkinter as tk
from tkinter import messagebox
def main():
root = tk.Tk()
root.title("Hauptmenü")
Menue = MainMenue(root)
Menue.pack()
root.mainloop()
class MainMenue(tk.Frame):
def __init__(self, parent):
super().__init__(parent)
self.button_rennen = tk.Button(self, text="New Window", width=20, command=self.call_bet)
self.button_rennen.pack()
def call_bet(self):
self.destroy()
root2 = tk.Tk()
Bet = BetFrame(root2)
Bet.pack()
class BetFrame(tk.Frame):
def __init__(self, parent):
super().__init__(parent)
self.button = tk.Button(text="Wette platzieren",
command=self.question)
self.button.pack()
def question(self):
dialog = tk.messagebox.askokcancel(message="Destroy Window?")
if dialog is True:
self.destroy()
main()
I am creating a new class for every new window since the original program should return some variables.
I know that there are already many questions to this topic but for me none of these seemed quite to fit and helped to find the solution for my problem. I am grateful for any help!
Look at this:
import tkinter as tk
from tkinter import messagebox
class MainMenue(tk.Frame):
def __init__(self, parent):
super().__init__(parent)
self.button_rennen = tk.Button(self, text="New Window", width=20,
command=self.call_bet)
self.button_rennen.pack()
def call_bet(self):
# `self.master` is the window
Bet = BetFrame(self.master)
Bet.pack()
self.destroy()
class BetFrame(tk.Frame):
def __init__(self, parent):
super().__init__(parent)
# You need to tell the button that its master should be this BetFrame
# If you don't it will assume you mean the window so
# `self.destroy()` isn't going to destroy the button.
self.button = tk.Button(self, text="Wette platzieren", command=self.question)
self.button.pack()
def question(self):
dialog = tk.messagebox.askokcancel(message="Destroy Window?")
if dialog is True:
self.destroy()
def main():
root = tk.Tk()
Menue = MainMenue(root)
Menue.pack()
root.mainloop()
main()
Your code is great but it was 2 mistakes:
Instead of creating a new window is it better to reuse the old one.
When you create your button in your BetFrame class, you don't pass anything for the master argument (the first argument). That is why the button isn't destroyed when you destroy the BetFrame object using self.destroy()
In my real software I have a main menu (the main window) and others ones (toplevel windows) that the user can open using some widgets placed in the main one. it seems work, the main window can open the other toplevel windows, but I saw a really big issue. when I open more than one window for the same menu, all of them, except the last one, lost always the information placed in their widgets (in my case, Entry and ComboBox widgets). let's start with a simple example:
from tkinter import *
from tkinter import ttk
class MainWindow:
def __init__(self):
# load a "SecondWindow" object:
self.obj=SecondWindow(self)
# main window's gui:
self.parent=Tk()
self.parent.geometry("300x280+360+200")
self.parent.title("main window")
self.parent.configure(background="#f0f0f0")
self.OkButton=ttk.Button(self.parent, text="open the second window", width=26, command=lambda:self.obj.GUI())
self.OkButton.place(x=20, y=20)
self.parent.mainloop()
class SecondWindow:
def __init__(self, mw):
self.mw=mw
def GUI(self):
self.window=Toplevel(self.mw.parent)
self.window.geometry("300x180+360+200")
self.window.title("second window")
self.window.configure(background="#f0f0f0")
self.MenuSV=StringVar()
self.MenuSV.set("test test test")
self.MenuComboBox=ttk.Combobox(self.window, state="readonly", values=("ciao", "hola", "hello", "Salut"), textvariable=self.MenuSV)
self.MenuComboBox.place(x=20, y=20)
self.window.mainloop()
# start the program:
if __name__ == "__main__":
my_gui=MainWindow()
this code works like my real software. before to open the main window, a SecondWindow object is loaded (his main component is the GUI function). when you open the second window for just one time (using the SeconWindow object loaded before), it's ok, no issues, but if you open another one, the first one lost the information placed in his widgets. why?
I really don't understand this weird behaviour. how can I solve the issue?
Since you created only one instance of SecondWindow() inside MainWindow, so whenever GUI() of SecondWindow is executed, self.MenuSV will be reassigned another instance of StringVar(), so the previously created instance of StringVar() has no variable reference to it and it is garbage collected.
You can either create new instance of SecondWindow() whenever the button is clicked:
class MainWindow:
def __init__(self):
# load a "SecondWindow" object:
#self.obj=SecondWindow(self)
# main window's gui:
self.parent=Tk()
self.parent.geometry("300x280+360+200")
self.parent.title("main window")
self.parent.configure(background="#f0f0f0")
self.OkButton=ttk.Button(self.parent, text="open the second window", width=26,
command=lambda:SecondWindow(self).GUI()) # create new instance of SecondWindow here
self.OkButton.place(x=20, y=20)
self.parent.mainloop()
Or keep a reference of self.MenuSV inside SecondWindow:
class SecondWindow:
def __init__(self, mw):
self.mw=mw
def GUI(self):
self.window=Toplevel(self.mw.parent)
self.window.geometry("300x180+360+200")
self.window.title("second window")
self.window.configure(background="#f0f0f0")
self.MenuSV=StringVar()
self.MenuSV.set("test test test")
self.MenuComboBox=ttk.Combobox(self.window, state="readonly", values=("ciao", "hola", "hello", "Salut"), textvariable=self.MenuSV)
self.MenuComboBox.place(x=20, y=20)
self.MenuComboBox.MenuSV = self.MenuSV # keep a reference
self.window.mainloop()
I am working on code in tkinter python v3.7 where I want to open a new window which has same functionalities like original window. How can I do that?
While searching for solution I came across function naming Toplevel which creates new tkinter window. But this new window is completely new, It doesn't have functionalities(Button, geometry size in my case) which were provided in original one.
from tkinter import *
class TextPad:
def new_window(self):
top = Toplevel()
def __init__(self, master):
master.title('Text Pad')
master.geometry('400x400')
self.button = Button(master, text='Press',
command=self.new_window)
self.button.pack()
root = Tk()
t = TextPad(root)
root.mainloop()
My original window has geometry size of '400x400' and It has 'button', I want to open a new window having this functionalities.
I assume you want two (or more) windows at the same time.
If you want identical window then use again TextPad()
but this time use Toplevel() instead of Tk()
def new_window(self):
top = TextPad(Toplevel())
and don't run second mainloop()
If you want replace first window by new window - to have alwasy only one window - then you would have to destroy old window and then create new one using TextPad() with Tk() and mainloop()
But it would need to use self.master instead of master to have access to master in method new_window
from tkinter import *
class TextPad:
def new_window(self):
self.master.destroy()
root = Tk()
t = TextPad(root)
root.mainloop()
def __init__(self, master):
self.master = master
self.master.title('Text Pad')
self.master.geometry('400x400')
self.button = Button(self.master, text='Press',
command=self.new_window)
self.button.pack()
root = Tk()
t = TextPad(root)
root.mainloop()
I have a Python Program that opens a Toplevel window which is working I just wanted to know if there is an option to set the Toplevel window window to be active once it has been opened because at the moment it is still showing the parent window as the active window after opening it.
The python code (Python 3.4.1)
from tkinter import *
class cl_gui:
def __init__(self, master):
master.title("DataBox")
menu = Menu(master)
master.config(menu=menu)
menu_users = Menu(menu, tearoff=0)
menu.add_cascade(label="Users", menu=menu_users)
menu_users.add_command(label="View", command=self.f_openUsers)
def f_openUsers(self):
top = Toplevel()
top.title("Users")
root = Tk()
app = cl_gui(root)
root.mainloop()
You can set focus onto the new Toplevel widget as follows:
def f_openUsers(self):
top = Toplevel()
top.title("Users")
top.focus_set() # <- add this line
See e.g. this handy tkinter guide.
I'm trying to make a GUI using Tkinter and have come to implementing a menu bar. I've looked at a few tutorials and written some code for it, but a menu bar never seems to appear - just a blank frame with a white background. This doesn't just happen for my code though; on copying and pasting the code of one of the aforementioned tutorials into a new script, the same behaviour is exhibited.
I'd appreciate it if anyone could shed any light on what's causing this. My system is OS X 10.5, Python 2.7, Tk 8.4. Here's the code from the tutorial that doesn't appear to work:
#!/usr/local/bin/python2.7
from Tkinter import *
from ttk import *
class App(Frame):
def __init__(self):
Frame.__init__(self)
self.master.geometry('400x300')
self.master.title(__file__)
self.pack()
self.menu = Menu(tearoff=False)
self.master.config(menu = self.menu)
fm = self.file_menu = None
fm = Menu(self.menu, tearoff=False)
self.menu.add_cascade(label='File', menu = fm)
fm.add_command(label='Say Hello', command = self.say_hello)
fm.add_separator()
fm.add_command(label='Quit', command = self.quit)
self.mainloop()
def say_hello(self, *e):
self.label = Label(self.master, text='Hello there!')
self.label.pack(anchor=CENTER, fill=NONE, expand=YES, side=LEFT)
if __name__ == '__main__':
App()
and my code is here:
from Tkinter import *
class App(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
parent.title("Cluedo Solver 1.0")
menubar = Menu(root)
menubar.add_command(label="File")
menubar.add_command(label="Quit", command=root.quit())
root.config(menu=menubar)
root=Tk()
root.geometry("300x250+300+300")
app=App(root)
root.mainloop()
Based on some comments you made to one of the answers, you are apparently running this on a Macintosh. The code works fine, but the menu appears in the mac menubar rather than on the window like it does on Windows and Linux. So, there's nothing wrong with your code as far as the menubar is concerned.
Code with Explanation
From personal experience, I have found that it is usually easier to manage all widgets in a widgets method. That is what I did here, and it worked. Also, instead of parent, I used master. I will now walk you through the code step-by-step.
from Tkinter import *
We import Tkinter (GUI stuff)
class App(Frame):
We create a class called App, which is the Frame where widgets are held.
def __init__(self, master):
Frame.__init__(self, master)
self.grid()
self.widgets()
We create a method called __init__. This initializes the class, and runs another method called widgets.
def widgets(self):
menubar = Menu(root)
menubar.add_command(label="File")
menubar.add_command(label="Quit", command=root.quit())
root.config(menu=menubar)
We create the widgets method. This is where the widget, menubar is added. If we were to create anymore widgets, they would also be here.
root=Tk()
root.title("Menubar")
app=App(root)
root.mainloop()
Lastly, we give the entire window some properties. We give it a title, Menubar, and run the App class. lastly, we start the GUI's mainloop with root.mainloop.
I am trying the code as above, but all I get is "Python" on the macOS menubar and it's usual pulldown. tkinter just doesn't seem to work menus on macOS 10.14.1
I think what is happening is that there are mac specific fixups that are changing the event codes so certain menu items will end up under the Python menu item instead of where expected, I saw some of this in my own experiments. When I expanded my code and used some of the reserved FILE event codes instead of the standard ones, things worked better.
#!/Library/Frameworks/Python.framework/Versions/3.7/bin/python3.7
# -*- coding: utf-8 -*-# -*- coding: utf-8 -*-
from tkinter import *
class App(Frame):
def __init__(self, master):
Frame.__init__(self, master)
self.grid()
self.widgets()
def widgets(self):
menubar = Menu(root)
menubar.add_command(label = 'File')
menubar.add_command(label = 'quit', command = root.quit())
root.config(menu = menubar)
root = Tk()
root.title('Menubar')
app = App(root)
root.mainloop()
Check your mac menu bar if you are doing any GUI that involves menubar which you will like to view or test. Its subtle and you may think you code is not to working. Click on the app(in this case python window), it will show a drop down menubar.
guys to solve the problem
look the file part that's where the menubar for mac is located