tkinter access widget on a different window - python

So I'm fairly new to the tkiner programming and trying my hands at a GUI with more than one window wich should scan Wifi-Hotspots and show me a list of them. I've copied an example from Switch between two frames in tkinter to get the different windows
I have a Main Menu to enable Monitor Mode for the Wifi-Card and to start the scan. I'm calling another frame with a Listbox to show the results.
My problem now is, that the function startScan(self) is called within StartPage, while the Listbox is in PageOne. How can I adress it there and add entrys to it?
class PyWiFi(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
# the container is where we'll stack a bunch of frames
# on top of each other, then the one we want visible
# will be raised above the others
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, PageTwo):
frame = F(container, self)
self.frames[F] = frame
# put all of the pages in the same location;
# the one on the top of the stacking order
# will be the one that is visible.
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(StartPage)
def show_frame(self, c):
'''Show a frame for the given class'''
frame = self.frames[c]
frame.tkraise()
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = tk.Label(self, text="Main Menu", font=TITLE_FONT)
label.pack(side="top", fill="x", pady=10)
self.var = tk.IntVar()
monCheck = tk.Checkbutton(self, text="Monitor Mode", variable=self.var, command=self.monSwitch)
scanButton = tk.Button(self, text="Start Scan", command=self.startScan)
quitButton = tk.Button(self, text="Quit", command=self.master.quit)
monCheck.pack()
scanButton.pack()
quitButton.pack()
def monSwitch(self):
if(self.var.get()):
print "Monitor Modus an"
check_call(["airmon-ng", "start", "wlan0"])
else:
print "Monitor Modus aus"
check_call(["airmon-ng", "stop", "mon0"])
def startScan(self):
print "Scan gestartet"
app.show_frame(PageOne)
output=check_output('iwlist wlan0 scan | grep -E "Channel:|ESSID:"', shell=True)
netze = output.split()
print netze
for i in range(0,(len(netze)/2)-1):
string = netze[2*i]+" "+netze[2*i+1]
app.frames[PageOne].netzList.insert(END, string)
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = tk.Label(self, text="Scanergebnisse", font=TITLE_FONT)
label.pack(side="top", fill="x", pady=10)
menuButton = tk.Button(self, text="Menu", command=lambda: controller.show_frame(StartPage))
quitButton = tk.Button(self, text="Quit", command=self.master.quit)
button = tk.Button(self, text="P2", command=lambda: controller.show_frame(PageTwo))
netzList = tk.Listbox(self, width=30)
netzList.pack()
quitButton.pack(side=LEFT)
menuButton.pack(side=LEFT)
button.pack(side=LEFT)
class PageTwo(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = tk.Label(self, text="This is page 2", font=TITLE_FONT)
label.pack(side="top", fill="x", pady=10)
button = tk.Button(self, text="Go to the start page",
command=lambda: controller.show_frame(StartPage))
button.pack()
if __name__ == "__main__":
app = PyWiFi()
app.resizable(0, 0)
app.geometry("320x240")
app.mainloop()

netzlist and all other widgets defined in PageOne are local to __init__. You should name them self.netzlist etc. to be able to reference them as class attributes of PageOne later on.

Related

Is it possible to center a frame in the middle of the screen in Python Tkinter?

I am a high school student coding in Python and I have a very simple GUI application, and my program is different from others in that it doesn't use normal Tkinter windows. I used frames so that my code would flow more, but now I want to know is it possible to make each frame pop up in the center of my screen and not off to the top left. I am still new to this so let me know if you need anything more. Thank you <3
Here is my code:
import tkinter as tk
from tkinter import font as tkfont
LARGE_FONT = ("Verdana", 12)
class SampleApp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.title_font = tkfont.Font(family='Helvetica', size=18, weight="bold", slant="italic")
# the container is where we'll stack a bunch of frames
# on top of each other, then the one we want visible
# will be raised above the others
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):
page_name = F.__name__
frame = F(parent=container, controller=self)
self.frames[page_name] = frame
# put all of the pages in the same location;
# the one on the top of the stacking order
# will be the one that is visible.
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame("StartPage")
def show_frame(self, page_name):
'''Show a frame for the given page name'''
frame = self.frames[page_name]
frame.tkraise()
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="This is the start page", font=controller.title_font)
label.pack(side="top", fill="x", pady=10)
button1 = tk.Button(self, text="Go to Page One",
command=lambda: controller.show_frame("PageOne"))
button2 = tk.Button(self, text="Go to Page Two",
command=lambda: controller.show_frame("PageTwo"))
button1.pack()
button2.pack()
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="This is page 1", font=controller.title_font)
label.pack(side="top", fill="x", pady=10)
button = tk.Button(self, text="Go to the start page",
command=lambda: controller.show_frame("StartPage"))
button.pack()
if __name__ == "__main__":
app = SampleApp()
app.mainloop()

Smooth frame-switch and window resize in Tkinter

Using the methodology described in this post, I've created a multi-frame application. Without resizing the main window, the transitions from one frame to the next are very smooth, as all of the widgets are pre-loaded upon initializing the application.
However, many of my frames have different sizes, so ideally I would like to be able to set the geometry of the main window according to the requested size of the frame. I've used two methods thus far to achieve this, both working successfully:
Using frame.grid_remove() on the top frame when switching to another, and then frame.grid() on the new top frame, as described in this post.
Using the incoming frame's size attributes, frame.winfo_reqwidth() and frame.winfo_reqheight(), and then calling the master window's geometry(f'{width}x{height}') method to set the size.
However, my issue is that this transition, in both cases, is not smooth, and causes visual disruptions when switching frames, including leaving widgets blacked out until the mouse is moved again. I would like to make the resizing process less visually disruptive for my end-users.
import tkinter as tk, tkinter.ttk as ttk
class Application(tk.Tk):
def __init__(self):
super().__init__()
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, PageTwo):
page_name = F.__name__
frame = F(parent=container, controller=self)
self.frames[page_name] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(StartPage)
def show_frame(self, page_name):
'''Show a frame for the given page name'''
frame = self.frames[page_name]
frame.tkraise()
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="This is the start page", font=controller.title_font)
label.pack(side="top", fill="x", pady=10)
button1 = tk.Button(self, text="Go to Page One",
command=lambda: controller.show_frame(PageOne))
button2 = tk.Button(self, text="Go to Page Two",
command=lambda: controller.show_frame(PageTwo))
button1.pack()
button2.pack()
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="This is page 1", font=controller.title_font)
label.pack(side="top", fill="x", pady=10)
button = tk.Button(self, text="Go to the start page",
command=lambda: controller.show_frame(StartPage))
button.pack()
class PageTwo(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="This is page 2", font=controller.title_font)
label.pack(side="top", fill="x", pady=10)
button = tk.Button(self, text="Go to the start page",
command=lambda: controller.show_frame(StartPage))
button.pack()
The different versions of the Application method show_frame I have tried to use are:
1.
class Application(tk.Tk):
def show_frame(self, page_name, frame_removed):
frame = self.frames[page_name]
frame_removed.grid_remove()
frame.grid(sticky="nswe")
class StartPage(ttk.Frame):
def __init__(self, parent, controller):
...
button1 = ttk.Button(self, text="Go to page one",
command=lambda: controller.show_frame(PageOne, self))
button2 = ttk.Button(self, text="Go to page two",
command=lambda: controller.show_frame(PageTwo, self))
# all pages have been modified in the same manner as this
class Application(tk.Tk):
def show_frame(self, page_name):
frame = self.frames[page_name]
width = str(frame.winfo_reqwidth())
height = str(frame.winfo_reqheight())
if width != "1":
self.geometry(f'{width}x{height}')
frame.tkraise()

How to swap between pages / windows in Tkinter

I have tried googling and everyone seems to have structured their code completely differently. I understand the base level tkinter, however I do not understand how people are using classes and def's to swap pages. How can I swap from my main window to my second one? (and not open the second one after the main is closed)
import tkinter as tk
main = tk.Tk()
main.title("Main Program")
firstlabel = tk.Label(main, text="This is a program!")
firstlabel.pack()
main.mainloop()
second = tk.Tk()
second.title("Second Program")
firstlabel = tk.Label(second, text="This is another program!")
firstlabel.pack()
second.mainloop()
EDIT: (solution)
import tkinter as tk
class Application(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, 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="Start Page", font=("Consolas", 30))
label.pack(pady=10,padx=10)
button = tk.Button(self, text="Visit Page 1", command=lambda: controller.show_frame(PageOne))
button.pack()
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = tk.Label(self, text="Page One!!!", font=("Consolas", 30))
label.pack(pady=10,padx=10)
button1 = tk.Button(self, text="Back to Home", command=lambda: controller.show_frame(StartPage))
button1.pack()
app = Application()
app.mainloop()
One way do this is by clearing everything (every widget), with this function:
def clear(app):
# Delete everything else in app
widget_list = app.winfo_children()
for item in widget_list:
if item.winfo_children():
widget_list.extend(item.winfo_children())
for item in widget_list:
item.pack_forget()
And then puting in the new window you want to swap to (every window should hav its own frame to make it simpler).
Try this::::
tk.Toplevel(main)

Raising a Tkinter frame from a separate function

Apologies if the title is a bit broad.
I am creating a Tkinter app with multiple pages and I'm using this piece of code in order to do so.
Each page is a frame and frames are raised by calling the 'show_frame' function. I have no problem with switching between pages using buttons, however I want to run a certain function and change pages if a condition is met.
Below is an example:
class SampleApp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.title_font = tkfont.Font(family='Helvetica', size=18, weight="bold", slant="italic")
# the container is where we'll stack a bunch of frames
# on top of each other, then the one we want visible
# will be raised above the others
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, PageTwo):
page_name = F.__name__
frame = F(parent=container, controller=self)
self.frames[page_name] = frame
# put all of the pages in the same location;
# the one on the top of the stacking order
# will be the one that is visible.
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame("StartPage")
def show_frame(self, page_name):
'''Show a frame for the given page name'''
frame = self.frames[page_name]
frame.tkraise()
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="This is the start page", font=controller.title_font)
label.pack(side="top", fill="x", pady=10)
button1 = tk.Button(self, text="Go to Page One",
command=lambda: controller.show_frame("PageOne"))
button2 = tk.Button(self, text="Go to Page Two",
command=lambda: controller.show_frame("PageTwo"))
button1.pack()
button2.pack()
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="This is page 1", font=controller.title_font)
label.pack(side="top", fill="x", pady=10)
button = tk.Button(self, text="Go to the start page",
command=lambda: controller.show_frame("StartPage"))
button.pack()
def doSomething():
...
if x == y:
"RAISE PAGE ONE"
if __name__ == "__main__":
app = SampleApp()
app.mainloop()
As you can see, the doSomething function is outside of any class. How would I go about raising PageOne from this function.
The easiest answer would be to make doSomething() a method of SampleApp. You would then have access to the method show_frame. However, your question seems to imply this isn't an option.
Failing this, I would advise passing app to the function, and then calling app.show_frame("PageName").
If you don't want to pass a pointer to app as an argument, then you could pass doSomething the function app.show_frame as foo, and then call foo("PageName") when you want the page to be shown.
E.g.
def doSomething(foo):
'''some code'''
foo("page name")
doSomething(app.show_frame)

Add picture with ImageTk for Multiple Windows/Frames in Tkinter GUI with Python

I am trying to create a mutiple-frames GUI. The code I am using is working fine for this purpose. However, when I add some pictures in some frames, the GUI appears in two windows: one with normal functionalities and arrangement; one with nothing inside it. If I close either of them, both of them are closed.
I use Python 2.7.
Would anybody please explain to me what happened?
Edit: I know the reason now. It is because I use tk.Toplevel instead of tk.Tk. With tk.Tk, I have no problem with double windows but I cannot get my pictures shown. Any help?
Here is my code:
import tkinter as tk # python3
#import Tkinter as tk # python
from PIL import ImageTk, Image
TITLE_FONT = ("Helvetica", 18, "bold")
img = Image.open('arrow.png')
class SampleApp(tk.Toplevel):
def __init__(self, *args, **kwargs):
tk.Toplevel.__init__(self, *args, **kwargs)
# the container is where we'll stack a bunch of frames
# on top of each other, then the one we want visible
# will be raised above the others
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.arrow = ImageTk.PhotoImage(img)
self.frames = {}
for F in (StartPage, PageOne, PageTwo):
page_name = F.__name__
frame = F(parent=container, controller=self)
self.frames[page_name] = frame
# put all of the pages in the same location;
# the one on the top of the stacking order
# will be the one that is visible.
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame("StartPage")
def show_frame(self, page_name):
'''Show a frame for the given page name'''
frame = self.frames[page_name]
frame.tkraise()
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="This is the start page", font=TITLE_FONT)
# label.pack(side="top", fill="x", pady=10)
label.grid(row=0)
arrow1 = tk.Label(self, image = self.controller.arrow)
arrow2 = tk.Label(self, image = self.controller.arrow)
arrow1.grid(row=1,column=0)
arrow2.grid(row=2,column=0)
button1 = tk.Button(self, text="Go to Page One",
command=lambda: controller.show_frame("PageOne"))
button2 = tk.Button(self, text="Go to Page Two",
command=lambda: controller.show_frame("PageTwo"))
button1.grid(row=1,column=1)
button2.grid(row=2,column=1)
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="This is page 1", font=TITLE_FONT)
label.pack(side="top", fill="x", pady=10)
label.grid(row=0)
button = tk.Button(self, text="Go to the start page",
command=lambda: controller.show_frame("StartPage"))
button.grid(row=1,column=1)
class PageTwo(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="This is page 2", font=TITLE_FONT)
label.pack(side="top", fill="x", pady=10)
button = tk.Button(self, text="Go to the start page",
command=lambda: controller.show_frame("StartPage"))
button.pack()
if __name__ == "__main__":
app = SampleApp()
app.mainloop()
You forgot to include the image=... option into your label in PageOne and PageTwo. I have added it. ;)
Also, removed the Toplevel. Alternatively, you can still use Toplevel as you did and hide the root=tk.Tk() window after it has been created.
#import tkinter as tk # python3
import Tkinter as tk # python
from PIL import ImageTk, Image
TITLE_FONT = ("Helvetica", 18, "bold")
img = Image.open('arrow.png')
class SampleApp(tk.Frame): #change to tk.Frame
def __init__(self, parent, *args, **kwargs): #added parent
tk.Frame.__init__(self, parent, *args, **kwargs) #added parent
# the container is where we'll stack a bunch of frames
# on top of each other, then the one we want visible
# will be raised above the others
container = tk.Frame(parent) #changed self to parent
container.pack(side="top", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.arrow = ImageTk.PhotoImage(img)
self.frames = {}
for F in (StartPage, PageOne, PageTwo):
page_name = F.__name__
frame = F(parent=container, controller=self)
self.frames[page_name] = frame
# put all of the pages in the same location;
# the one on the top of the stacking order
# will be the one that is visible.
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame("StartPage")
def show_frame(self, page_name):
'''Show a frame for the given page name'''
frame = self.frames[page_name]
frame.tkraise()
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="This is the start page", font=TITLE_FONT)
# label.pack(side="top", fill="x", pady=10)
label.grid(row=0)
arrow1 = tk.Label(self, image = self.controller.arrow)
arrow2 = tk.Label(self, image = self.controller.arrow)
arrow1.grid(row=1,column=0)
arrow2.grid(row=2,column=0)
button1 = tk.Button(self, text="Go to Page One",
command=lambda: controller.show_frame("PageOne"))
button2 = tk.Button(self, text="Go to Page Two",
command=lambda: controller.show_frame("PageTwo"))
button1.grid(row=1,column=1)
button2.grid(row=2,column=1)
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="This is page 1", font=TITLE_FONT,
image = self.controller.arrow) # Added
label.pack(side="top", fill="x", pady=10)
label.grid(row=0)
button = tk.Button(self, text="Go to the start page",
command=lambda: controller.show_frame("StartPage"))
button.grid(row=1,column=1)
class PageTwo(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="This is page 2", font=TITLE_FONT,
image = self.controller.arrow) # Added
label.pack(side="top", fill="x", pady=10)
button = tk.Button(self, text="Go to the start page",
command=lambda: controller.show_frame("StartPage"))
button.pack()
if __name__ == "__main__":
root = tk.Tk() #Added
app = SampleApp(root) #added root
root.mainloop()

Categories