I made a program that runs from a basic GUI. The GUI has one button which starts a long-ish process, and I want to show what's going on for the user to read live.
This is the GUI part of my code, I tried to apply solutions that I read here, which mostly included threading and sys.stdout but couldn't make it work. Now it shows the stdouts but at the end of the process, which is pointless.
class Toplevel1:
def init(self, top=None):
'''This class configures and populates the toplevel window. top is the toplevel containing window.'''
top.geometry("656x362+393+114")
top.minsize(120, 1)
top.maxsize(1364, 749)
top.resizable(1, 1)
top.title("AUGENRE")
top.configure(background="#efe352")
top.configure(highlightbackground="#d9d9d9")
top.configure(highlightcolor="black")
self.top = top
archivo_abierto = tk.StringVar()
archivo_destino = tk.StringVar()
def redirector(inputStr):
self.Text1.insert(INSERT, inputStr)
def buscarVideo():
archivo_abierto.set(askopenfilename(title='Seleccione archivo de video'))
def buscarDir():
archivo_destino.set(askdirectory(title= 'Seleccione carpeta de salida'))
sys.stdout.write = redirector
self.Button1 = tk.Button(self.top, command= lambda: buscarVideo())
self.Button1.place(relx=0.043, rely=0.182, height=24, width=117)
self.Button1.configure(activebackground="beige")
self.Button1.configure(activeforeground="black")
self.Button1.configure(background="#d9d9d9")
self.Button1.configure(compound='left')
self.Button1.configure(disabledforeground="#a3a3a3")
self.Button1.configure(foreground="#000000")
self.Button1.configure(highlightbackground="#d9d9d9")
self.Button1.configure(highlightcolor="black")
self.Button1.configure(pady="0")
self.Button1.configure(text='''Seleccionar video''')
self.Entry1 = tk.Entry(self.top, textvariable=archivo_abierto)
self.Entry1.place(relx=0.236, rely=0.185, height=20, relwidth=0.631)
self.Entry1.configure(background="white")
self.Entry1.configure(disabledforeground="#a3a3a3")
self.Entry1.configure(font="TkFixedFont")
self.Entry1.configure(foreground="#000000")
self.Entry1.configure(highlightbackground="#d9d9d9")
self.Entry1.configure(highlightcolor="black")
self.Entry1.configure(insertbackground="black")
self.Entry1.configure(selectbackground="#c4c4c4")
self.Entry1.configure(selectforeground="black")
self.Button1_1 = tk.Button(self.top, command= lambda: buscarDir())
self.Button1_1.place(relx=0.043, rely=0.351, height=24, width=117)
self.Button1_1.configure(activebackground="beige")
self.Button1_1.configure(activeforeground="black")
self.Button1_1.configure(background="#d9d9d9")
self.Button1_1.configure(compound='left')
self.Button1_1.configure(disabledforeground="#a3a3a3")
self.Button1_1.configure(foreground="#000000")
self.Button1_1.configure(highlightbackground="#d9d9d9")
self.Button1_1.configure(highlightcolor="black")
self.Button1_1.configure(pady="0")
self.Button1_1.configure(text='''Seleccionar destino''')
self.Entry1_1 = tk.Entry(self.top, textvariable=archivo_destino)
self.Entry1_1.place(relx=0.238, rely=0.356, height=20, relwidth=0.631)
self.Entry1_1.configure(background="white")
self.Entry1_1.configure(disabledforeground="#a3a3a3")
self.Entry1_1.configure(font="TkFixedFont")
self.Entry1_1.configure(foreground="#000000")
self.Entry1_1.configure(highlightbackground="#d9d9d9")
self.Entry1_1.configure(highlightcolor="black")
self.Entry1_1.configure(insertbackground="black")
self.Entry1_1.configure(selectbackground="#c4c4c4")
self.Entry1_1.configure(selectforeground="black")
self.Button2 = tk.Button(self.top, command= lambda: main_process(
time='timegps',
fRate=0.1,
inputP=str(archivo_abierto.get()),
exifPath='exiftool.exe',
ffmpegPath='ffmpeg.exe',
outputD=str(archivo_destino.get())
)
)
self.Button2.place(relx=0.274, rely=0.497, height=64, width=286)
self.Button2.configure(activebackground="beige")
self.Button2.configure(activeforeground="black")
self.Button2.configure(background="#d9d9d9")
self.Button2.configure(compound='left')
self.Button2.configure(disabledforeground="#a3a3a3")
self.Button2.configure(foreground="#000000")
self.Button2.configure(highlightbackground="#d9d9d9")
self.Button2.configure(highlightcolor="black")
self.Button2.configure(pady="0")
self.Button2.configure(text='''Generar referencias gráficas''')
self.Text1 = tk.Text(self.top)
self.Text1.place(relx=0.076, rely=0.757, relheight=0.149, relwidth=0.829)
self.Text1.configure(font="TkTextFont")
self.Text1.configure(foreground="black")
self.Text1.configure(highlightbackground="#d9d9d9")
self.Text1.configure(highlightcolor="black")
self.Text1.configure(insertbackground="black")
self.Text1.configure(selectbackground="#c4c4c4")
self.Text1.configure(selectforeground="black")
self.Text1.configure(wrap="word")
def start_up():
gui_support.main()
if name == 'main':
gui_support.main()
And this is the gui_support:
import sys
import tkinter as tk
import tkinter.ttk as ttk
from tkinter.constants import *
import gui
debug = True # False to eliminate debug printing from callback functions.
def main(*args):
'''Main entry point for the application.'''
global root
root = tk.Tk()
root.protocol( 'WM_DELETE_WINDOW' , root.destroy)
'''Creates a toplevel widget'''
global _top1, _w1
_top1 = root
_w1 = gui.Toplevel1(_top1)
root.mainloop()
if __name__ == '__main__':
gui.start_up()
Tried StdOut redirector object with threading, but I'm not sure of how to implement it.
Related
My problem is, that then i open a second window with a button in the first window, the spinbox in the second window does not give an output. If i start the second one individually, it works as expectet. I used the pygub designer to design the windows.
Here is my Code from the first (main) window:
import tkinter.ttk as ttk
from tkinter import messagebox
import Test2
class NewprojectApp:
def __init__(self, master=None):
# build ui
toplevel1 = tk.Tk() if master is None else tk.Toplevel(master)
toplevel1.configure(height=500, width=500)
entry1 = ttk.Entry(toplevel1)
self.Entrytest1 = tk.StringVar()
entry1.configure(textvariable=self.Entrytest1)
entry1.pack(padx=10, pady=10, side="top")
spinbox1 = ttk.Spinbox(toplevel1)
self.Spinbox1 = tk.IntVar()
spinbox1.configure(from_=0, increment=1, textvariable=self.Spinbox1, to=10)
spinbox1.pack(padx=10, pady=10, side="top")
button2 = ttk.Button(toplevel1)
button2.configure(text="Neuesfenster")
button2.pack(padx=10, pady=10, side="top")
button2.bind("<ButtonPress>", self.ereignisbehandler, add="")
# Main widget
self.mainwindow = toplevel1
def run(self):
self.mainwindow.mainloop()
def ereignisbehandler(self, event=None):
messagebox.showinfo("Test", self.Spinbox1.get())
Test2.secondwindow()
if __name__ == "__main__":
app = NewprojectApp()
app.run()
And the second window:
import tkinter as tk
import tkinter.ttk as ttk
from tkinter import messagebox
class NewprojectApp2:
def __init__(self, master=None):
# build ui
toplevel2 = tk.Tk() if master is None else tk.Toplevel(master)
toplevel2.configure(height=500, width=500)
entry2 = ttk.Entry(toplevel2)
self.Entrytest1 = tk.StringVar()
entry2.configure(textvariable=self.Entrytest1)
entry2.pack(padx=10, pady=10, side="top")
spinbox2 = ttk.Spinbox(toplevel2)
self.Spinbox2 = tk.IntVar()
spinbox2.configure(from_=0, increment=1, state="normal", textvariable=self.Spinbox2, to=10)
spinbox2.pack(padx=10, pady=10, side="top")
button4 = ttk.Button(toplevel2)
button4.configure(text="Neuesfenster")
button4.pack(padx=10, pady=10, side="top")
button4.bind("<ButtonPress>", self.ereignisbehandler, add="")
# Main widget
self.mainwindow = toplevel2
def run(self):
self.mainwindow.mainloop()
def ereignisbehandler(self, event=None):
messagebox.showinfo("Test", self.Spinbox2.get())
if __name__ == "__main__":
app = NewprojectApp2()
app.run()
def secondwindow():
app = NewprojectApp2()
app.run()
Thank you for helping!
So basically i want to make this GUI where you can insert text and then hit the go button ad it would do the feature. (example: "Stopwatch" and then after you hit enter it would start a stopwatch).
import tkinter as tk
import math
import time
root = tk.Tk()
root.geometry()
root.attributes("-fullscreen", True)
exit_button = tk.Button(root, text = "Exit", command = root.destroy)
exit_button.place(x=1506, y=0)
main_entry = root.Entry(root, )
root.mainloop()
So basically I want the answer in main entry to be taken and do the action asked in the text insertion.
Here i wrote an example on how to use the Entry and the Button widgets in Tkitner:
from tkinter import *
from tkinter.messagebox import showerror
import random
class App(Tk):
def __init__(self):
super(App, self).__init__()
self.title('Get a random integer')
self.geometry('400x250')
Label(self, text='Get a random number from A to B').pack(pady=20)
self.a_value = Entry(self, width=10)
self.a_value.insert(END, 'A')
self.a_value.pack(pady=5)
self.b_value = Entry(self, width=10)
self.b_value.insert(END, 'B')
self.b_value.pack(pady=5)
self.btn = Button(self, text='Get Random', command=self.get_rand)
self.btn.pack(pady=5)
self.out_label = Label(self)
self.out_label.pack(pady=10)
self.bind('<Return>', self.get_rand)
def get_rand(self, event=None):
a = self.a_value.get()
b = self.b_value.get()
try:
a = int(a)
b = int(b)
self.out_label.configure(text=f'{random.randint(a, b)}')
self.update()
except Exception as error:
showerror(title='ERROR', message=f'{error}')
if __name__ == '__main__':
myapp = App()
myapp.mainloop()
Sorry for any stupidity or ignorance on my part but I am new to Python, and coding in general. I have been trying to get a UI working for a game I'm going to make as a way of teaching myself python I have a main window and a few button widgets in this.
The Play game button opens a second window (for the game itself) and hides the root window with the .withdraw method. This works perfectly. Inside the game window I have another button which I would like to destroy the game window and bring the user back to the root menu window that I have withdrawn. This seems to work except every time I call it,it creates an new, duplicate set of widgets in the window so I end up with multiple sets.
I'll post my full code at the bottom but here are what I believe are the relevant parts.
A tkinter Button calls this inside the parent class (the main window). This works fine.
def playGame(self): #Start the main game window
self.master.withdraw()
gameWindow()
I'm using the bellow method inside of the child class to destroy the game window and then call a method in the parent class to bring back the earlier withdrawn window
def exitMenu(self):
self.g.destroy()
UI(root).showMenu()
this works except it duplicates the widgets each time resulting in this being shown:
screen capture of result
Bellow is all my code, thank you so much for any help.
import tkinter as tk
import PIL
from Config import *
root = tk.Tk()
class UI(): #Main Menu
def __init__(self, master):
#Create Main Menu Window
self.master = master
self.master.title("Monopoly")
self.master.wm_iconbitmap('icons\Monopoly-Icon.ico')
self.master.geometry((resolution))
#Menu Buttons
self.label = tk.Label(master, text= 'Welcome to Monopoly! PLACEHOLDER')
self.playButton = tk.Button(master, text= 'Play Game', command= self.playGame)
self.settingsButton = tk.Button(master, text= 'settings', command= self.settings)
self.exitButton = tk.Button(master, text= 'Exit', command= self.exitGame)
self.label.grid(columnspan=2)
self.playButton.grid(column=0)
self.settingsButton.grid(column=0)
self.exitButton.grid(column=0)
def settings(self): #Opens Settings Window
s = tk.Toplevel()
s.title('Settings')
s.wm_iconbitmap('icons\Monopoly-Icon.ico')
s.geometry((resolution))
self.master.withdraw()
resLabel = tk.Label(s, text= 'Resolution')
resOption = tk.OptionMenu(s, resolution, *resList)
resLabel.grid(column=0,row=0)
resOption.grid(column=0, row=4)
def showMenu(self): #Bring back menu windwow
self.master.deiconify()
def exitGame(self): #Exit Game Method
root.destroy()
def playGame(self): #Start the main game window
self.master.withdraw()
gameWindow()
class gameWindow(UI):
def __init__(self):
self.g = tk.Toplevel()
self.g.title('Monopoly')
self.g.wm_iconbitmap('icons\Monopoly-Icon.ico')
self.g.geometry((resolution))
self.menuButton = tk.Button(self.g, text= 'Main Menu', command= self.exitMenu)
self.menuButton.grid(column=0,row=0)
def exitMenu(self):
self.g.destroy()
UI(root).showMenu()
mainMenu = UI(root)
root.mainloop()
You should pass the parent class, UI, to the child class gameWindow, then you can use the showMenu() method of the parent class in the child class.
Code for showing the main window from the child window will be:
self.mainUI.showMenu() instead of UI(root).showMenu()
Child class becomes:
class gameWindow(UI):
def __init__(self, mainUI): # Make the main window a parameter/attribute
self.mainUI = mainUI # Make the main window a parameter/attribute
self.g = tk.Toplevel()
self.g.title('Monopoly')
self.g.wm_iconbitmap('icons\Monopoly-Icon.ico')
self.g.geometry((resolution))
self.menuButton = tk.Button(self.g, text= 'Main Menu', command= self.exitMenu)
self.menuButton.grid(column=0,row=0)
def exitMenu(self):
self.g.destroy()
self.mainUI.showMenu()
gameWindow now requires one argument, so you pass it the main UI as self
def playGame(self): #Start the main game window
self.master.withdraw()
gameWindow(self)
You can read more about hiding/showing window here
Here is the full code:
import tkinter as tk
import PIL
from Config import *
root = tk.Tk()
class UI(): #Main Menu
def __init__(self, master):
#Create Main Menu Window
self.master = master
self.master.title("Monopoly")
self.master.wm_iconbitmap('icons\Monopoly-Icon.ico')
self.master.geometry((resolution))
#Menu Buttons
self.label = tk.Label(master, text= 'Welcome to Monopoly! PLACEHOLDER')
self.playButton = tk.Button(master, text= 'Play Game', command= self.playGame)
self.settingsButton = tk.Button(master, text= 'settings', command= self.settings)
self.exitButton = tk.Button(master, text= 'Exit', command= self.exitGame)
self.label.grid(columnspan=2)
self.playButton.grid(column=0)
self.settingsButton.grid(column=0)
self.exitButton.grid(column=0)
def settings(self): #Opens Settings Window
s = tk.Toplevel()
s.title('Settings')
s.wm_iconbitmap('icons\Monopoly-Icon.ico')
s.geometry((resolution))
self.master.withdraw()
resLabel = tk.Label(s, text= 'Resolution')
resOption = tk.OptionMenu(s, resolution, *resList)
resLabel.grid(column=0,row=0)
resOption.grid(column=0, row=4)
def showMenu(self): #Bring back menu windwow
self.master.deiconify()
def exitGame(self): #Exit Game Method
root.destroy()
def playGame(self): #Start the main game window
self.master.withdraw()
gameWindow(self)
class gameWindow(UI):
def __init__(self, mainUI): # Make the main window a parameter/attribute
self.mainUI = mainUI # Make the main window a parameter/attribute
self.g = tk.Toplevel()
self.g.title('Monopoly')
self.g.wm_iconbitmap('icons\Monopoly-Icon.ico')
self.g.geometry((resolution))
self.menuButton = tk.Button(self.g, text= 'Main Menu', command= self.exitMenu)
self.menuButton.grid(column=0,row=0)
def exitMenu(self):
self.g.destroy()
self.mainUI.showMenu()
mainMenu = UI(root)
root.mainloop()
I am creating 2 window in my program and i am using two class, since the code is complex, i separate it in 2 different python file. After i imported the second window file, how can i make sure it open without having this error which show in this picture
The original result should look like this after the new window button clicked:
Coding for Main Window:
from tkinter import *
import classGUIProgram
class Window(Tk):
def __init__(self, parent):
Tk.__init__(self, parent)
self.parent = parent
self.initialize()
def initialize(self):
self.geometry("600x400+30+30")
self.wButton = Button(self, text='newWindow', command = self.OnButtonClick)
self.wButton.pack()
def OnButtonClick(classGUIProgram):
classGUIProgram.top = Toplevel()
master = Tk()
b = classGUIProgram.HappyButton(master)
master.mainloop()
if __name__ == "__main__":
window = Window(None)
window.title("title")
window.mainloop()
Coding for Second Window:
from tkinter import *
class HappyButton:
def __init__(self, master):
frame = Frame(master)
frame.pack()
self.printButton = Button(frame, text="Print message", command=self.printMessage)
self.printButton.pack(side=LEFT)
self.quitButton = Button(frame, text="Quit", command= quit)
self.quitButton.pack(side=LEFT)
self.downloadHistoryCB=Checkbutton(frame, text="Download History")
self.downloadHistoryCB.pack(side=LEFT)
def printMessage(self):
print("Wow this actually worked!")
master = Tk()
b = HappyButton(master)
master.mainloop()
You're creating extra Tk windows. Here is an example of using Toplevel widgets and another file.
mainWindow.py
import tkinter as tk
import secondWindow as sW
class MainWindow(tk.Tk):
def __init__(self):
super().__init__()
self.title("Main Window")
self.geometry("600x400+30+30")
tk.Button(self, text = "New Window", command = self.new_window).pack()
tk.Button(self, text = "Close Window", command = self.close).pack()
self._second_window = None
def new_window(self):
# This prevents multiple clicks opening multiple windows
if self._second_window is not None:
return
self._second_window = sW.SubWindow(self)
def close(self):
# Destory the 2nd window and reset the value to None
if self._second_window is not None:
self._second_window.destroy()
self._second_window = None
if __name__ == '__main__':
window = MainWindow()
window.mainloop()
secondWindow.py
import tkinter as tk
class SubWindow(tk.Toplevel):
def __init__(self, master):
super().__init__(master)
self.title("Sub Window")
self.geometry("400x300+30+30")
# Change what happens when you click the X button
# This is done so changes also reflect in the main window class
self.protocol('WM_DELETE_WINDOW', master.close)
tk.Button(self, text = "Print", command = self.printMessage).pack()
def printMessage(self):
print("Wow this actually worked!")
When using another file be sure to not have any global code you don't want running. Your classes don't have to inherit from Tk and Toplevel, this is just an example. But you need to ensure you only ever have one instance of Tk otherwise you get the behaviour you encountered
so I am making an application that takes notes(similar to Windows Sticky Notes). Since I need to display multiple notes simultaneously, I have used a class which inherits from Thread and also creates a tkinter window. The problem is that my windows do not open simultaneously. The second opens up after the first is closed. Here is the code. What am I doing wrong? Is there another method that I can use? [For now I am just displaying notes I have hard-coded.]
from tkinter import *
from threading import Thread
class Note(Thread):
nid = 0
title = ""
message = ""
def __init__(self, nid, title, message):
Thread.__init__(self)
self.nid = nid
self.title = title
self.message = message
def display_note_gui(self):
'''Tkinter to create a note gui window with parameters '''
window = Tk()
window.title(self.title)
window.geometry("200x200")
window.configure(background="#BAD0EF")
title = Entry(relief=FLAT, bg="#BAD0EF", bd=0)
title.pack(side=TOP)
scrollBar = Scrollbar(window, takefocus=0, width=20)
textArea = Text(window, height=4, width=1000, bg="#BAD0EF", font=("Times", "14"))
scrollBar.pack(side=RIGHT, fill=Y)
textArea.pack(side=LEFT, fill=Y)
scrollBar.config(command=textArea.yview)
textArea.config(yscrollcommand=scrollBar.set)
textArea.insert(END, self.message)
window.mainloop()
def run(self):
self.display_note_gui()
new_note1 = Note(0, "Hello", "Hi, how are you?")
new_note1.start()
new_note1.join()
new_note2 = Note(1, "2", "How's everyone else?")
new_note2.start()
new_note2.join()
If all you need is multiple note windows then you definitely don't need threads. Tkinter is quite capable of managing dozens or hundreds of open windows.
Just create instances of Toplevel for every window except the root window. Here's a somewhat over-engineered example:
import Tkinter as tk
class Notepad(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.text = tk.Text(self, wrap="word")
self.vsb = tk.Scrollbar(self, orient="vertical", comman=self.text.yview)
self.text.configure(yscrollcommand=self.vsb.set)
self.vsb.pack(side="right", fill="y")
self.text.pack(side="left", fill="both", expand=True)
def main():
root = tk.Tk()
Notepad(root).pack(fill="both", expand=True)
for i in range(5):
top = tk.Toplevel(root)
Notepad(top).pack(fill="both", expand=True)
root.mainloop()
if __name__ == "__main__":
main()
Instead of subclassing Thread just subclass Toplevel, a top level in tkinter is a separate window in the same application which sounds like exactly what you are trying to accomplish:
from tkinter import *
#from threading import Thread #no longer needed
class Note(Toplevel):
nid = 0
#title = "" #this would block the method to override the current title
message = ""
def __init__(self, master, nid, title, message):
Toplevel.__init__(self,master)
self.nid = nid
self.title(title) #since toplevel widgets define a method called title you can't store it as an attribute
self.message = message
self.display_note_gui() #maybe just leave that code part of the __init__?
def display_note_gui(self):
'''Tkinter to create a note gui window with parameters '''
#no window, just self
self.geometry("200x200")
self.configure(background="#BAD0EF")
#pass self as the parent to all the child widgets instead of window
title = Entry(self,relief=FLAT, bg="#BAD0EF", bd=0)
title.pack(side=TOP)
scrollBar = Scrollbar(self, takefocus=0, width=20)
textArea = Text(self, height=4, width=1000, bg="#BAD0EF", font=("Times", "14"))
scrollBar.pack(side=RIGHT, fill=Y)
textArea.pack(side=LEFT, fill=Y)
scrollBar.config(command=textArea.yview)
textArea.config(yscrollcommand=scrollBar.set)
textArea.insert(END, self.message)
#self.mainloop() #leave this to the root window
def run(self):
self.display_note_gui()
root = Tk()
root.withdraw() #hide the root so that only the notes will be visible
new_note1 = Note(root, 0, "Hello", "Hi, how are you?")
#new_note1.start()
#new_note1.join()
new_note2 = Note(root, 1, "2", "How's everyone else?")
#new_note2.start()
#new_note2.join()
root.mainloop() #still call mainloop on the root
note that instead of storing the title as an attribute you can call self.title() to get the current title of the window and self.title("new title") to change it.