Tkinter Application showing blank screen while running - python

When I try to execute my Tkinter application created in python, it is giving me a blank application window. No Buttons/Labels are displaying. What may be the Issue ?
Codes are as follows:
import tkinter as tk
from tkinter import ttk
LARGE_FONT=("Verdana", 18)
class VNMSapp(tk.Tk):
def __int__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
container=tk.Frame(self)
container.pack(side="top", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames= []
for F in (StartPage, AdminPage):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(StartPage)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = tk.Label(text="THIS IS HOME PAGE", font=LARGE_FONT)
label.pack()
btn1 = tk.Button(self, text="Enter ADMIN PAGE",
command=lambda: controller.show_frame(AdminPage))
btn1.pack()
class AdminPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = tk.Label(text="THIS IS ADMIN PAGE", font=LARGE_FONT)
label.pack()
btn1 = tk.Button(self, text="Enter HOME PAGE",
command=lambda: controller.show_frame(StartPage))
btn1.pack()
app = VNMSapp()
app.mainloop()
It is not giving me any error also.

There is a typo in the definition of the __init__() method for class VNMSapp:
def __int__(self, *args, **kwargs):
should be
def __init__(self, *args, **kwargs):
As a result your __init__() method is not being called, so your widgets are not created.
Once you correct that you will find an additional problem where you are using a list, but I think that you meant to use a dictionary:
self.frames= []
...
self.frames[F] = frame
the second line will fail raising a TypeError exception because list indices must be integers, which frame objects are not.
Fix that by initialising self.frames to an empty dict:
self.frames= {}

Related

Create a list or dictionary of Tkinter buttons to use in different frames

So I have my Tkinter application that consist of multiple frame
All these multiple frames contain the same basic structure of many buttons; the only difference is that the buttons have a different bg on each page.
In my actual project, these buttons contain so many options, and so having to write the same basic code each time for all pages makes my code look unnecessarily long.
So I'm thinking: Is there a way to put all these buttons into a dictionary or list, and pack them onto each separate frame? (Bear in mind the button will need to inherit the bg variable from the specific frame.)
I've created a minimal example to illustrate what I mean:
import tkinter as tk
from tkinter import *
listt = []
self = None
bg_colour_for_this_frame = None
button1 = Button(self,text="Button 1",bg=bg_colour_for_this_frame,fg='white')
button2 = Button(self,text="Button 2",bg=bg_colour_for_this_frame,fg='blue')
button3 = Button(self,text="Button 3",bg=bg_colour_for_this_frame,fg='orange')
listt.append(button1)
listt.append(button2)
listt.append(button3)
class Tkinter(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
container = tk.Frame(self)
container.pack(side="top", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
for F in (StartPage, SecondPage):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(StartPage)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
frame.winfo_toplevel().geometry("860x864")
frame.configure(bg='#000000')
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
Button(self,text='SecondPage',command=lambda:controller.show_frame(SecondPage)).pack()
for s in listt:
s.pack()
class SecondPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
Button(self,text='StartPage',command=lambda:controller.show_frame(StartPage)).pack()
for s in listt:
s.pack()
app = Tkinter()
app.mainloop()
Or maybe, instead of having a list, use a dictionary:
listt = {'button1':'Button[root,text="Button 1",bg=bg_colour_for_this_frame,fg="white"]',
'button2':'Button[root,text="Button 2",bg=bg_colour_for_this_frame,fg="red"]',
'button3':'Button[root,text="Button 3",bg=bg_colour_for_this_frame,fg="blue"]',
}
I get the error:
s.pack()
AttributeError: 'str' object has no attribute 'pack'
Since you can't create the Buttons before the page they're on exists, It would be simpler to make a function and call it during the initialization of each of the page classes — like the make_buttons() shown below:
import tkinter as tk
from tkinter import *
# Button options for all pages.
BTN_OPTS = [dict(text="Button 1", fg='white'),
dict(text="Button 2", fg='blue'),
dict(text="Button 3", fg='orange')]
def make_buttons(parent, bg_colour):
return [Button(parent, bg=bg_colour, **opts) for opts in BTN_OPTS]
class Tkinter(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
container = tk.Frame(self)
container.pack(side="top", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
for F in (StartPage, SecondPage):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(StartPage)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
frame.winfo_toplevel().geometry("860x864")
frame.configure(bg='#000000')
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
Button(self, text='SecondPage',
command=lambda: controller.show_frame(SecondPage)).pack()
for btn in make_buttons(self, 'Pink'):
btn.pack()
class SecondPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
Button(self, text='StartPage',
command=lambda: controller.show_frame(StartPage)).pack()
for btn in make_buttons(self, 'green'):
btn.pack()
app = Tkinter()
app.mainloop()
A more sophisticated and object-oriented approach would be to define a base class for all page classes that had a method in it something like the function above, and then derive the concrete subclasses from that allowing them just inherit the method. It also gets rid of the global data because the button options are now in a (base) class attribute.
Here's a runnable example of how it could be done that way. Note: it requires Python 3.6+ because it uses object.__init_subclass__() which was added in that version:
import tkinter as tk
class Tkinter(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
container = tk.Frame(self)
container.pack(side="top", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
for F in (StartPage, SecondPage):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(StartPage)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
frame.winfo_toplevel().geometry("860x864")
frame.configure(bg='#000000')
class BasePage(tk.Frame):
# Button options common to all pages.
BTN_OPTS = [dict(text="Button 1", fg='white'),
dict(text="Button 2", fg='blue'),
dict(text="Button 3", fg='orange')]
#classmethod
def __init_subclass__(cls, /, bg_color, **kwargs):
super().__init_subclass__(**kwargs)
cls.bg_color = bg_color
def __init__(self, parent, controller, text, command):
super().__init__(parent)
tk.Button(self, text=text, command=command).pack() # Next page button.
for btn in (tk.Button(self, bg=self.bg_color, **opts) for opts in self.BTN_OPTS):
btn.pack()
class StartPage(BasePage, bg_color='pink'):
def __init__(self, parent, controller):
super().__init__(parent, controller, text='SecondPage',
command=lambda: controller.show_frame(SecondPage))
class SecondPage(BasePage, bg_color='green'):
def __init__(self, parent, controller):
super().__init__(parent, controller, text='StartPage',
command=lambda: controller.show_frame(StartPage))
app = Tkinter()
app.mainloop()

Automatically updating a tkinter label that isn't directly accessible

I want to create a simple GUI for a constant data that comes through Serial. I decided to use tkinter. The value reading is updated and should be shown in a label. I created separate classes for the container and the other pages. I defined the container as such:
class Gui(Tk):
def __init__(self, *args, **kwargs):
Tk.__init__(self, *args, **kwargs)
container = Frame(self)
container.pack(side="top", fill = "both", expand = TRUE)
container.grid_rowconfigure(0, weight = 1)
self.frames={}
for F in (StartPage, PageOne):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row = 0, column = 0, sticky = "nsew")
frame.UpdateMe()
self.show_frame(StartPage)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
And the page showing the label:
class PageOne(Frame):
def __init__(self, parent, controller):
Frame.__init__(self,parent)
global reading
self.label1text = StringVar()
self.label1 = Label(self, textvariable = label1text)
self.label1.pack()
button1 = Button (self, text = "Show Start Page", command = lambda: controller.show_frame(StartPage))
button1.pack()
self.label1text.set(reading)
def UpdateMe(self):
global reading
self.lable1text.set(reading)
Now, to initialize the GUI:
root = Gui()
root.mainloop()
However, since mainloop() is blocking, any argument coming after that wouldn't be executed; I could get around that with update and update_idletasks. However, I still don't know how I could call the function UpdateMe() inside PageOne()when I only created an instantiation of the Gui(). Is there a way for me to solve this or remediate my understanding of classes and object programming?
Since you cannot create StringVar without initializing Tk() (for your case, it is Gui()), so you need to create the reading variable inside Gui() and PageOne.label1 uses it as its textvariable. Below is an example based on your code:
from tkinter import *
from random import randint
class Gui(Tk):
def __init__(self, *args, **kwargs):
Tk.__init__(self, *args, **kwargs)
container = Frame(self)
container.pack(side="top", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.reading = StringVar() # create the StringVar for PageOne
self.frames = {}
for F in (StartPage, PageOne):
frame = F(container, self)
frame.grid(row=0, column=0, sticky="nsew")
self.frames[F] = frame
self.show_frame(StartPage)
def show_frame(self, cont):
self.frames[cont].tkraise()
class StartPage(Frame):
def __init__(self, parent, controller):
Frame.__init__(self, parent)
button1 = Button (self, text="Show Page 1", command=lambda: controller.show_frame(PageOne))
button1.pack(fill="both", expand=True)
class PageOne(Frame):
def __init__(self, parent, controller):
Frame.__init__(self, parent)
self.label1 = Label(self, textvariable=controller.reading) # refer to Gui.reading StringVar
self.label1.pack(fill='x')
button1 = Button (self, text="Show Start Page", command=lambda: controller.show_frame(StartPage))
button1.pack(fill='x')
# use .after() to simulate the update of reading variable periodically
def update_reading():
app.reading.set(randint(0, 10000))
print('reading:', app.reading.get())
app.after(1000, update_reading)
app = Gui()
update_reading() # start the simulation task of updating reading variable
app.mainloop()
Note that I have created a function update_reading() to simulate the update of reading variable periodically using after() function.

Parameter 'event' value is not used

I'm developing a program in python 2.7 and have run into an issue on Pycharm. In the program, I want the user to be able to navigate through different screens by either clicking buttons, or by pressing the "enter" key. I tried implementing it into my program, and it works but Pycharm is giving the error
import Tkinter as Tk
class MemoryGameApp(Tk.Tk):
def __init__(self, *args, **kwargs):
Tk.Tk.__init__(self, *args, **kwargs)
Tk.Tk.wm_title(self, "2 screens")
container = Tk.Frame(self)
container.pack(side="top", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
for F in (StartPage, PageTwo):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(StartPage)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
class StartPage(Tk.Frame):
def __init__(self, parent, controller):
Tk.Frame.__init__(self, parent)
label = Tk.Label(self, text="Page 1")
label.pack()
global button
button = Tk.Button(self, text="Next", width=5, command=lambda: controller.show_frame(PageTwo))
button.pack(pady=100, padx=100)
button.focus_set()
def press_enter(event):
controller.show_frame(PageTwo)
button2.focus_set()
button.bind("<Return>", press_enter)
class PageTwo(Tk.Frame):
def __init__(self, parent, controller):
Tk.Frame.__init__(self, parent)
label_title2 = Tk.Label(self, text="Page 2")
label_title2.pack()
global button2
button2 = Tk.Button(self, text="Back", width=5, command=lambda: controller.show_frame(StartPage))
button2.pack(pady=100, padx=100)
def press_enter(event):
controller.show_frame(StartPage)
button.focus_set()
button2.bind("<Return>", press_enter)
app = MemoryGameApp()
app.mainloop()
It has given the error in def press_enter(event):
It claims that event is not used, but if I remove it from the program, the program does not function correctly
I know that it works when it is implemented, I'm just interested in seeing if there's anyway that I can remove this issue.
Thanks
To remove this warning, you could replace the argument 'event' by '_', or just do something with the object, like printing it to console.
Even though the argument of the function seems useless, it cannot be removed: It belongs to the signature of a valid event function.
It is sufficient to change event to _event.

Python call multiple commands in a funktion by pressing a button

I have a problem with my code:
I want that the OK-button (tkinter) in a popup-window (exp_Name) makes three things when it gets pressed:
First it should store a string from the Entry (in the popup-window) in a global variable
exp_name = entry.get()
Second it should open another window
app.show_frame(PageOne)
Third it should close the popup-window
popup.destroy
I tried to call a function, but I have no idea how I should close the popup window in a separate function. And also how I should store the Entry in a global variable in this separate function.
Thanks a lot!!
Here the whole code:
import tkinter as tk
from tkinter import ttk
global exp_name
exp_name = "Hello"
def exp_Name():
popup = tk.Tk()
popup.geometry("800x400")
label=ttk.Label(popup, text="Enter Something:")
label.pack(side="top", fill="x", pady=10)
entry = ttk.Entry(popup)
entry.pack()
##Here is the problem...
OK_btn=ttk.Button(popup, text="OK", command= lambda: [app.show_frame(PageOne),exp_name = entry.get(), popup.destroy])
OK_btn.pack()
Close_btn=ttk.Button(popup, text="Close",command = popup.destroy)
Close_btn.pack()
popup.mainloop()
class Steppermovementsapp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
tk.Tk.iconbitmap(self, default="images/Icon_fly.ico")
tk.Tk.wm_title(self, "Drosophila ADF apparatus")
container = tk.Frame(self)
container.pack(side="top", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames={}
for F in (StartPage, PageOne):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(StartPage)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
exp_btn = ttk.Button(self, text="Start experiment",command=exp_Name)
exp_btn.pack()
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label =ttk.Label(self, text=exp_name)
label.pack()
app = Steppermovementsapp()
app.geometry("{0}x{1}".format(app.winfo_screenwidth(),app.winfo_screenheight()))
app.mainloop()
This is how I tried it with a function:
import tkinter as tk
from tkinter import ttk
global exp_name
exp_name = "Hello"
class sidefunctions(tk.Tk):
def func_three(self):
app.show_frame(PageOne)
exp_name = self.entry.get()
popup.destroy
def exp_Name(self):
self.popup = tk.Tk()
self.popup.geometry("800x400")
self.label=ttk.Label(self,text="Enter Something:")
self.label.pack(side="top", fill="x", pady=10)
self.entry = ttk.Entry(self)
self.entry.pack()
self.OK_btn=ttk.Button(self,text="OK", command= self.func_three)
self.OK_btn.pack()
self.Close_btn=ttk.Button(self,text="Close",command =
self.popup.destroy)
self.Close_btn.pack()
self.popup.mainloop()
class Steppermovementsapp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
tk.Tk.iconbitmap(self, default="images/Icon_fly.ico")
tk.Tk.wm_title(self, "Drosophila ADF apparatus")
container = tk.Frame(self)
container.pack(side="top", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames={}
for F in (StartPage, PageOne):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(StartPage)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
exp_btn = ttk.Button(self, text="Start experiment",command=sidefunctions().exp_Name)
exp_btn.pack()
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label =ttk.Label(self, text=exp_name)
label.pack()
app = Steppermovementsapp()
app.geometry("{0}x{1}".format(app.winfo_screenwidth(),app.winfo_screenheight()))
app.mainloop()

Switching Frames In PyQt4

I would like to know how I would go about doing the following in PyQt4:
import tkinter as tk
LARGE_FONT= ("Verdana", 12)
class SeaofBTCapp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
container = tk.Frame(self)
container.pack(side="top", fill="both", expand = True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
for F in (StartPage, PageOne):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(StartPage)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self,parent)
label = tk.Label(self, text="Home", font=LARGE_FONT)
label.pack(pady=10,padx=10)
btn = tk.Button(self, text="Page One", command= lambda: controller.show_frame(PageOne))
btn.pack()
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self,parent)
label = tk.Label(self, text="Page One", font=LARGE_FONT)
label.pack(pady=10,padx=10)
btn = tk.Button(self, text="Home", command= lambda: controller.show_frame(StartPage))
btn.pack()
app = SeaofBTCapp()
app.mainloop()
I am going to have multiple pages and would like to have it easily defined. I am still very new to python coding and I'm still learning how PyQt4 works but my project will have atleast 4 pages that I need to switch between and I don't want the code to be too long by defining the same thing on every page's class. All help will be appreciated. Thank you in advance.
Take a look at QStackedWidget.
From the docs:
The QStackedWidget class provides a stack of widgets where only one widget is visible at a time.
Depending on what you are doing exactly, QWizard may be suited even better.
Both also exist for Qt4, but you should use Qt5 if you can.

Categories