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.
Related
I've tried to create a software. In this software there is a menu widget with function button that open functions e widgets. But, I've noticed, to keep going on script, it's necessary to close the master window (menu).
I've created an example to you understand my problem.
from tkinter import *
#Create Fuction that open new fuctions and widget
def test():
#Open a new widget
def fun_test_1():
top_level = Tk()
def test1():
top_level1 = Toplevel()
top_level1.title('new1')
top_level1.mainloop()
Button(top_level, text='test1',command=test1).pack()
top_level.mainloop()
fun_test_1()
#Before, open the second widget
def fun_test_2():
print('def fun_test_2(): works!')
top_level = Tk()
def test1():
top_level1 = Toplevel()
top_level1.title('new1')
top_level1.mainloop()
Button(top_level, text='Button', command=test1).pack()
top_level.mainloop()
fun_test_2()
root = Tk()
root.title('MASTER')
Button(root, text='Button',command=test).pack()
root.mainloop()
So, I need that fun_test_2() be called without close the root widget
And all functions i've tried to change Tk() to Toplevel() and Toplevel() to Tk().
The problem is you calling mainloop more than once, and for creating more than one instance of Tk. When you call mainloop, it won't return until that window has been destroyed. That's a fundamental aspect of how tkinter works.
The solution is to not create more than one instance of Tk and to not call mainloop more than once. If you need multiple windows, create instances of Toplevel. And again, only call mainloop once in total, not once per window.
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?
I am making a GUI that had the Welcome page and the main page. The purpose is to let user agree on the welcome page, the welcome page is dismissed and the main page will show up for further step. However, the icon in the taskbar only shows up in the welcome page, when we click into the main window the icon is disappeared and the app appeared to be a minimized window on the bottom left corner in the screen.
The starting page and main window layout is appear like this.
class welcome_window(QtWidgets.QMainWindow):
def __init__(self,parent = None):
super(welcome_window, self).__init__(parent)
self.confirm_button = QtWidgets.QPushButton('Yes')
self.confirm_button.clicked.connect(self.startup)
Main_layout = QtWidgets.QHBoxLayout()
Main_layout.addWidget(self.confirm_button)
self.main.setLayout(Main_layout)
def startup(self):
self.close()
dialog = Main_window(self)
self.dialogs.append(dialog)
dialog.show()
class Main_window(QtWidgets.QMainWindow):
def __init__(self,parent = None):
super(Main_window, self).__init__(parent)
self.setGeometry(50, 50, 1500, 850)
# here is all the step for later operation
def main():
app = QtWidgets.QApplication(sys.argv)
main = welcome_window()
main.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
I expected that if the icon located in the taskbar could always stay on, it would be great for my GUI. Thank you all.
First of all, the MRE you gave is not reproducible. When I tried to run it it just didn't work. In this case you had a simple issue so I could just guess what was intended, but when you get more complicated problems people might not be able to help you. So in the future please make sure that we can just copy-paste-execute your code.
The reason that the main window disappears is that it's a member of the Welcome window. When you close the Welcome window, the corresponding python object will deleted and therefore Python will no longer have a reference to the main window. The main window object will be garbage-collected and all kinds of strange things might happen (I would expect it to just disappear).
The solution is to have a reference to the main window that stays valid until the program closes. This can be done by defining it in the main function (and then giving it as a parameter to the Welcome window). Like this...
import sys
from PyQt5 import QtWidgets
# Use a QWidget if you don't need toolbars.
class welcome_window(QtWidgets.QWidget):
def __init__(self, main_window=None, parent = None):
super(welcome_window, self).__init__(parent)
self.main_window = main_window
self.confirm_button = QtWidgets.QPushButton('Yes')
self.confirm_button.clicked.connect(self.startup)
main_layout = QtWidgets.QHBoxLayout() # use lower case for variable names
main_layout.addWidget(self.confirm_button)
self.setLayout(main_layout)
def startup(self):
self.main_window.show()
self.close()
class Main_window(QtWidgets.QMainWindow):
def __init__(self,parent = None):
super(Main_window, self).__init__(parent)
self.setGeometry(50, 50, 1500, 850)
# here is all the step for later operation
# Don't use self.setLayout on a QMainWindow,
# use a central widget and set a layout on that.
self.main_widget = QtWidgets.QWidget()
self.setCentralWidget(self.main_widget)
main_layout = QtWidgets.QHBoxLayout()
self.main_widget.setLayout(main_layout)
main_layout.addWidget(QtWidgets.QLabel("Hello"))
def main():
app = QtWidgets.QApplication(sys.argv)
main = Main_window()
welcome = welcome_window(main_window=main)
welcome.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Some more tips. Don't use setLayout on a QMainWindow. Use a central widget and add your widgets to the layout of the central widget. The layout of the main window is for toolbars and such. See: https://doc.qt.io/qt-5/qmainwindow.html#qt-main-window-framework
Just use a QWidget if you want a simple window without toolbars (like your welcome window),
Best to use lower case for variable names and upper case for class names. E.g. I renamed Main_layout to main_layout. Look at the difference in syntax highlighting by Stack Overflow above.
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.
I'm realtively new to python and am making a GUI app that does a lot of file i/o and processing. To complete this i would like to get a confirmation box to pop-up when the user commits and actions. From this when clicking 'yes' the app then runs the i/o and displays a progress bar.
From other threads on here I have gotten as far as reading about the requirement to create an addtional thread to take on one of these processes (for example Tkinter: ProgressBar with indeterminate duration and Python Tkinter indeterminate progress bar not running have been very helpful).
However, I'm getting a little lost because I'm not activating the threaded process from the Main() function. So I'm still getting lost in how, and where, I should be creating the progress bar and passing of the i/o process to another thread (reading in a csv file here).
Here is my code and I would be very grateful for any help anyone can give me:
import tkinter as tk
import tkinter.messagebox as messagebox
import csv
import tkinter.ttk as ttk
import threading
class ReadIn(tk.Frame):
def __init__(self, parent, *args, **kwargs):
tk.Frame.__init__(self, parent, *args, **kwargs)
self.parent = parent
self.initUI()
def initUI(self):
self.parent.title("Read in file and display progress")
self.pack(fill=tk.BOTH, expand=True)
self.TestBtn = tk.Button(self.parent, text="Do Something", command=lambda: self.confirm_pb())
self.TestBtn.pack()
def confirm_pb(self):
result = messagebox.askyesno("Confirm Action", "Are you sure you want to?")
if result:
self.handle_stuff()
def handle_stuff(self):
nf = threading.Thread(target=self.import_csv)
nf.start()
self.Pbar()
nf.join()
def Pbar(self):
self.popup = tk.Tk()
self.popup.title('Loading file')
self.label = tk.Label(self.popup, text="Please wait until the file is created")
self.progressbar = ttk.Progressbar(self.popup, orient=tk.HORIZONTAL, length=200,
mode='indeterminate')
self.progressbar.pack(padx=10, pady=10)
self.label.pack()
self.progressbar.start(50)
def import_csv(self):
print("Opening File")
with open('csv.csv', newline='') as inp_csv:
reader = csv.reader(inp_csv)
for i, row in enumerate(reader):
# write something to check it reading
print("Reading Row " + str(i))
def main():
root = tk.Tk() # create a Tk root window
App = ReadIn(root)
root.geometry('400x300+760+450')
App.mainloop() # starts the mainloop
if __name__ == '__main__':
main()
The statement nf.join() in function handle_stuff() will block tkinter's main loop to show the progress bar window. Try modify handle_stuff() as below:
def handle_stuff(self):
nf = threading.Thread(target=self.import_csv)
nf.start()
self.Pbar()
#nf.join() # don't call join() as it will block tkinter's mainloop()
while nf.is_alive():
self.update() # update the progress bar window
self.popup.destroy()