Tkinter gives me a second window - python-3.x

I am writing code for a tkinter gui using a class, however I notice that when I run there is a second window besides the main one I made. I've tried a number of things but they either break the code or the window is black. See code below.
import tkinter as gui
class loginWindow(gui.Frame):
def __init__(self):
super(loginWindow, self).__init__()
self.logUI()
def logUI(self):
self.mainWindow = gui.Tk()
self.mainWindow.title("GLSC IT Inventory")
self.mainWindow.minsize(400, 150)
self.mainWindow.maxsize(400, 150)
self.mainWindow.geometry("400x150")
self.greet_label = gui.Label(self.mainWindow, text="Welcome!!!")
self.greet_label.place(x=180, y=5)
self.uname_label = gui.Label(self.mainWindow, text="Username:")
self.uname_label.place(x=10, y=24)
self.uname_input = gui.StringVar()
self.uname_field = gui.Entry(self.mainWindow, bd=4, textvariable=self.uname_input)
self.uname_field.place(x=80, y=25, width=160)
self.pwd_label = gui.Label(self.mainWindow, text="Password:")
self.pwd_label.place(x=10, y=54)
self.pwd_input = gui.StringVar()
self.pwd_field = gui.Entry(self.mainWindow, bd=4, textvariable=self.pwd_input, show="\u2022")
self.pwd_field.place(x=80, y=55, width=160)
self.login_button = gui.Button(self.mainWindow, text="Login", command=None)
self.login_button.place(x=180, y=95)
my_app = loginWindow()
my_app.mainloop()

When you create instance of loginWindow(), an instance of Tk() is required but there is none, so it will be created implicitly for you.
Then another instance of Tk() is created inside logUI(). So there are two instances of Tk().
One way to fix it is loginWindow not inherited from Frame:
class loginWindow:
def __init__(self):
self.logUI()
def logUI(self):
...
# add for calling tkinter.mainloop()
def mainloop(self):
self.mainWindow.mainloop()

Related

Tkinter Label class not appearing when used within a class

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?

Retrieve choosen value of a ttk.combobox

I am creating a user interface where there is a first window that ask the user to choose a parameter within a list of choices. Here is an MWE :
from tkinter import *
import tkinter.ttk as ttk
Port=''
root = Tk()
PortCOM = ttk.Combobox(root, values=[1,2,3,4,5,6])
PortCOM.bind("<<ComboboxSelected>>",Port)
PortCOM.pack ()
root.mainloop()
print(Port)
So i already tried that but also :
Port = PortCOM.get
Port = PortCOM.cget
With that last try, i get that error message :
<bound method Misc.cget of <tkinter.ttk.Combobox object .!combobox>>
For example, if the user choose the value '4' in my list of values, i want it to be stored in the variable 'Port'.
You do not need a bind to track the variable. You can do this with a StringVar.
That said you cannot just call Port = PortCOM.get in the global as the code is initiated and expect to get anything. 1st issue with that the correct syntax is Port = PortCOM.get() with parenthesis. 2nd you are calling get() at init and thus the only possible value would be an empty string if not an error.
The next issue I see is the bind() this is not doing what you think it is doing. The bind is used to call a function not to update a variable directly.
The correct use of Combobox is to use a textvariable with an IntVar() or StringVar() depending on the values and then use get() on that var when you need it in a function.
from tkinter import *
import tkinter.ttk as ttk
root = Tk()
textVar = StringVar(root)
textVar.set('')
PortCOM = ttk.Combobox(root, textvariable=textVar, values=[1, 2, 3, 4, 5, 6])
PortCOM.pack()
def print_value():
print(textVar.get())
Button(root, text='Print Value', command=print_value).pack()
root.mainloop()
If you really want to use bind() for some reason like to have the selection immediately do something when selected then try this instead.
Make sure the bind call is after the function used to do something with your combobox.
from tkinter import *
import tkinter.ttk as ttk
root = Tk()
textVar = StringVar(root)
textVar.set('')
PortCOM = ttk.Combobox(root, textvariable=textVar, values=[1, 2, 3, 4, 5, 6])
PortCOM.pack()
# Note the _ in the argument section of the function.
# A bind will send an event to the function selected unless you use a lambda.
# so to deal with events we don't care about we have a few options.
# We can use an underscore to just except any argument and do nothing with it.
# We could also do event=None but typically I only use that when a function might use the event variable but not always.
def print_value(_):
print(textVar.get())
print(PortCOM.get())
PortCOM.bind("<<ComboboxSelected>>", print_value)
root.mainloop()
And if you don't want extra button you can just do this
from tkinter import *
import tkinter.ttk as ttk
Port=''
root = Tk()
def set_port(_):
global Port
Port = PortCOM.get()
print(Port)
PortCOM = ttk.Combobox(root, values=[1,2,3,4,5,6])
PortCOM.bind("<<ComboboxSelected>>", set_port)
PortCOM.pack ()
root.mainloop()
Look at this OO example to use all the power of the combo ;)
#!/usr/bin/python3
import tkinter as tk
from tkinter import ttk
from tkinter import messagebox
class Main(ttk.Frame):
def __init__(self, parent):
super().__init__()
self.parent = parent
self.values = ('Apple','Banana','Orange','Grapes','Watermelon','Plum','Strawberries','Pear')
self.init_ui()
def init_ui(self):
self.pack(fill=tk.BOTH, expand=1)
f = ttk.Frame()
ttk.Label(f, text = "Combobox").pack()
self.cbCombo = ttk.Combobox(f,state='readonly',values=self.values)
self.cbCombo.pack()
w = ttk.Frame()
ttk.Button(w, text="Callback", command=self.on_callback).pack()
ttk.Button(w, text="Reset", command=self.on_reset).pack()
ttk.Button(w, text="Set", command=self.on_set).pack()
ttk.Button(w, text="Close", command=self.on_close).pack()
f.pack(side=tk.LEFT, fill=tk.BOTH, expand=1)
w.pack(side=tk.RIGHT, fill=tk.BOTH, expand=1)
def on_callback(self,):
if self.cbCombo.current() != -1:
msg = "You have selected:\n{0}".format(self.cbCombo.get())
else:
msg = "You did not select anything"
messagebox.showinfo(self.parent.title(), msg)
def on_reset(self):
self.cbCombo.set('')
def on_set(self):
self.cbCombo.current(0)
def on_close(self):
self.parent.on_exit()
class App(tk.Tk):
"""Start here"""
def __init__(self):
super().__init__()
self.protocol("WM_DELETE_WINDOW", self.on_exit)
self.set_title()
self.set_style()
frame = Main(self,)
frame.pack(fill=tk.BOTH, expand=1)
def set_style(self):
self.style = ttk.Style()
#('winnative', 'clam', 'alt', 'default', 'classic', 'vista', 'xpnative')
self.style.theme_use("clam")
def set_title(self):
s = "{0}".format('Simple App')
self.title(s)
def on_exit(self):
"""Close all"""
if messagebox.askokcancel("Simple App", "Do you want to quit?", parent=self):
self.destroy()
if __name__ == '__main__':
app = App()
app.mainloop()

Unable to executing multiple functions using multiprocessing module from a TKinter GUI

Hi I have a small GUI which has a button using which two functions must be executed in different processors. In reality these two functions are heavy calculations. I do not want to use multi threading. I want them to run on 2 different processors. When I try to execute the button, another instance of the GUI gets created and it says
File "C:\Python3.7\lib\multiprocessing\reduction.py", line 60, in dump.
ForkingPickler(file, protocol).dump(obj)
TypeError: can't pickle _tkinter.tkapp objects
My code is as follows.
from multiprocessing import Process
from tkinter import Button, Tk, Frame
class GUI(Frame):
def __init__(self):
super().__init__()
self.button = Button(self, text="Start", command=self.execute)
self.button.pack()
self.pack()
def F1(self):
print("Hello")
def F2(self):
print("World")
def execute(self):
self.P1 = Process(target = self.F1)
self.P2 = Process(target = self.F2)
self.P1.start()
self.P2.start()
self.P1.join()
self.P2.join()
Root = Tk()
Software = GUI()
Root.mainloop()
Please click here
The problem lies with pickling tkinter widgets. You simply cannot do it, as the Tcl interpreter does not understand the python pickle format.
Coming to your code, I tried the following and it prints as expected:
from multiprocessing import Process
from tkinter import Button, Tk, Frame
class GUI(Frame):
def __init__(self):
super().__init__()
self.button = Button(self, text="Start", command=self.execute)
self.button.pack()
self.pack()
#staticmethod
def F1():
print("Hello")
#staticmethod
def F2():
print("World")
def execute(self):
self.P1 = Process(target = GUI.F1())
self.P2 = Process(target = GUI.F2())
self.P1.start()
self.P2.start()
self.P1.join()
self.P2.join()
Root = Tk()
Software = GUI()
Root.mainloop()

Delivering data between instances of different classes in tkinter

Beginner to tkinder and the version of python is 3.6.
I'm trying to make a python GUI for data processing.
There're several instances of different classes designed for different jobs.
When the data is processed with function click_emd in EMDFrame class, it should be drawn in PreviewFrame class which uses matplotlib to show the data. However i have no idea how to pass the data between two classes.
I've searched similar questions but they didn't work.
Now I'm thinking about two possible solutions.
One is to find a way to pass data_processed to previewframe.cplot in emdframe.click_emd.
Another is to make full use MainPage class. It can acquire data_processed from one class and call the cplot in another class to drawn. But how can MainPage get noticed once the data_processed is generated?
Got confused and don't know what to do. Really appreciate for your patience.
There are two py code file.
Mainframe.py :
import tkinter as tk
from view import * # initiate the subframes
class MainPage():
def __init__(self, master=None):
self.root = master
root.geometry('%dx%d' % (800, 600))
self.createPage()
def createPage(self):
self.emdPage = EMDFrame(root) # subframe
self.emdPage.place(x=20, y=300)
self.preview = PreviewFrame(root)
self.preview.place(x=340, y=20)
self.aboutPage = AboutFrame(root)
menubar = Menu(root)
menubar.add_command(label='EMD', command=self.emd)
menubar.add_command(label='About', command=self.aboutDisp)
root['menu'] = menubar # set menu
def emd(self):
self.emdPage.place(x=20, y=300)
self.preview.place(x=340, y=20)
self.aboutPage.place_forget()
def aboutDisp(self):
self.emdPage.place_forget()
self.preview.place_forget()
self.aboutPage.place(x=20, y=300)
def plot_update(self, data, t):
self.preview.cplot(data, t)
root = tk.Tk()
root.title("Demo")
app = MainPage(root)
root.mainloop()
view.py :
from tkinter import *
from pyhht.emd import EMD
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import numpy as np
data = []
class EMDFrame(LabelFrame):
def __init__(self, master=None):
LabelFrame.__init__(self, master, width=300, height=250, text='EMD')
self.root = master
self.createPage()
def createPage(self):
b = Button(self, text='EMD', width=20, height=2, command=self.click_emd)
b.place(x=40, y=160, anchor='nw', width=80, height=40)
def click_emd(self):
global data
decomposer = EMD(data)
data_processed = decomposer.decompose()
# Deliever data_processed to PreviewFrame's cplot function
class PreviewFrame(LabelFrame):
def __init__(self, master=None):
LabelFrame.__init__(self, master, width=440, height=530, text='Preview')
self.root = master
self.createPage()
def cplot(self, data, t):
f = Figure(figsize=(4, 4.8), dpi=100)
a = f.add_subplot(111)
a.plot(t, data)
canvas = FigureCanvasTkAgg(f, master=self)
canvas.draw()
canvas.get_tk_widget().place(x=20, y=0)
def createPage(self):
t = np.arange(0.0, 3, 0.01)
s = np.sin(2 * np.pi * t)
self.cplot(s,t)
class AboutFrame(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
self.root = master
self.createPage()
def createPage(self):
Label(self, text='About').pack()
You aren't passing data between classes, you are passing data between two instances of classes.
In order for one instance to send data to another instance of a class, it needs to know about it. So in MainFrame.py we pass the reference of the preview class to the emdPage.
def createPage(self):
self.preview = PreviewFrame(root)
self.preview.place(x=340, y=20)
self.emdPage = EMDFrame(root,self.preview) # subframe
self.emdPage.place(x=20, y=300)
self.aboutPage = AboutFrame(root)
Then in the other view.py, update the class to keep track of the preview reference and then use it once the data has been processed.
class EMDFrame(LabelFrame):
def __init__(self, preview, master=None):
LabelFrame.__init__(self, master, width=300, height=250, text='EMD')
self.preview = preview
self.root = master
self.createPage()
def createPage(self):
b = Button(self, text='EMD', width=20, height=2, command=self.click_emd)
b.place(x=40, y=160, anchor='nw', width=80, height=40)
def click_emd(self):
global data
decomposer = EMD(data)
data_processed = decomposer.decompose()
# Deliever data_processed to PreviewFrame's cplot function
self.preview.cplot(.....)
(Code example is untested and incomplete but should show the principle)
(There is probably a better way to do it thought the MainPage class but this would involve a bit more code.)

why does my tk tabbed for loop have an error of "tabs[upper_tabs] = self.tab TypeError: unhashable type: 'list'"

I can create the 2 tabs individually, and I am trying to make it expendable by using a for loop. I get an error that says
tabs[upper_tabs] = self.tab
TypeError: unhashable type: 'list'
I am assuming it's how I reference the dictionary. Would you please help me understand and correct the error.
import tkinter as tk
from tkinter import ttk
upper_tabs = ["Final", "Requests"]
tabs = {}
class Application(ttk.Frame): #inherent from frame.
def __init__(self, parent):
tk.Frame.__init__(self, parent, bg="ivory2")
self.parent = parent
self.pack()
self.tabControl = ttk.Notebook(self, width="900", height= "350") #
Create Tab Control
for names in upper_tabs:
self.tab=ttk.Frame(self.tabControl)# Create a tab
self.tabControl.add(self.tab, text=names) # Add the tab
tabs[names] = self.tab
self.tabControl.pack(expand=1, fill="both") # Pack to make visible
self.grid()
def main():
root = tk.Tk()
root.title("class basic window")
root.geometry("1200x600")
root.config(background="LightBlue4")
app = Application(root)
root.mainloop()
if __name__ == '__main__':
main()
I don't quite understand what you want to do, but to remove the error and make the program look like this...
(Scaled down)
... is to change the line tabs[upper_tabs] = self.tab to tabs[tuple(upper_tabs)] = self.tab.
This works because a tuple cannot change, and it is, therefore, possible to use it as a dictionary key, which a list can't be. For more on python dictionaries, see this page.

Resources