0

Suppose I have two functions which when called opens up a Frame:

Eg:

from tkinter import *
win = Tk()

class MyFrame(Frame):
    def __init__(self, master, **kwargs):
        Frame.__init__(self, master,
        bg='white' ,**kwargs)

def FuncOne():
    frameone = MyFrame(win)
    frameone.pack()

    lbl = Label(frameone, text="ABC", font='"Serif" 247' , width=win.winfo_screenwidth(), height=win.winfo_screenheight())
    lbl.pack(fill=BOTH, expand=True)


def FuncTwo():
    frametwo = MyFrame(win)
    frametwo.pack()

    lbl = Label(frametwo, text="ABC", font='"Serif" 200', width=win.winfo_screenwidth(), height=win.winfo_screenheight())
    lbl.pack()


win.mainloop()

How do I call FuncOne and FuncTwo which opens a separate frame each time by using a side arrow key and destroy the frame in FuncOne while calling FuncTwo and vice versa?

  • Does this answer your question? [How to call a function whenever a key is pressed in python](https://stackoverflow.com/questions/19707578/how-to-call-a-function-whenever-a-key-is-pressed-in-python) – Sergey Ronin Aug 02 '20 at 16:41
  • This is talking about creating a button and binding it to a main window. I wanted to directly bind the key to a function without creating a button. – Sakar Subedi Aug 02 '20 at 17:06

3 Answers3

1

The following code provides 2 functions that will create a instance of your MyFrame class. In this function we will check if the list is empty with if actual_frame: returns False when its empty and create an instances of your class. On the bottom of this function wie store this instance in the list actual_frame. If you run now another time a function if actual_frame: returns True and it will destroy this instances, clear the list and build a new instance.

from tkinter import *
win = Tk()
class MyFrame(Frame):
    def __init__(self, master, **kwargs):
        Frame.__init__(self, master,
        bg='red' ,**kwargs)
actual_frame=[]
def FuncOne(event):
    if actual_frame:
        actual_frame[0].destroy()
        actual_frame.clear()
        
    frameone = MyFrame(win)
    frameone.grid(column=0,row=0)
    lbl = Label(frameone, text="ABC", font='"Serif" 24' , width=10, height=15)
    lbl.pack(side=TOP,fill=BOTH, expand=True)
    
    actual_frame.append(frameone)

def FuncTwo(event):
    if actual_frame:
        actual_frame[0].destroy()
        actual_frame.clear()
    
    frametwo = MyFrame(win)
    frametwo.grid(column=0,row=0)
    lbl = Label(frametwo, text="XYZ", font='"Serif" 20', width=10,height=15)
    lbl.pack(side=TOP, fill=BOTH, expand=True)
    
    actual_frame.append(frametwo)

win.bind('<Left>', FuncOne)
win.bind('<Right>', FuncTwo)

win.mainloop()

Update In this update I hade made another list in that we store all the functions you like to have. The keys we use here to cycle through the list with a little help oft itertools.cycle. We call in our new binded function the function that is next or once before in the function list.

from tkinter import *
from itertools import cycle

win = Tk()
class MyFrame(Frame):
    def __init__(self, master, **kwargs):
        Frame.__init__(self, master,
        bg='red' ,**kwargs)

my_funcs=[]
actual_frame=[]

def FuncOne():
    if actual_frame:
        actual_frame[0].destroy()
        actual_frame.clear()
        
    frameone = MyFrame(win)
    frameone.grid(column=0,row=0)
    lbl = Label(frameone, text="ABC", font='"Serif" 24' , width=10, height=15)
    lbl.pack(side=TOP,fill=BOTH, expand=True)
    
    actual_frame.append(frameone)

def FuncTwo():
    if actual_frame:
        actual_frame[0].destroy()
        actual_frame.clear()
    
    frametwo = MyFrame(win)
    frametwo.grid(column=0,row=0)
    lbl = Label(frametwo, text="XYZ", font='"Serif" 20', width=10,height=15)
    lbl.pack(side=TOP, fill=BOTH, expand=True)
    
    actual_frame.append(frametwo)

def FuncThree():
    if actual_frame:
        actual_frame[0].destroy()
        actual_frame.clear()
    
    framethree = MyFrame(win)
    framethree.grid(column=0,row=0)
    lbl = Label(framethree, text="DEF", font='"Serif" 20', width=10,height=15)
    lbl.pack(side=TOP, fill=BOTH, expand=True)
    
    actual_frame.append(framethree)
    
my_funcs.append(FuncOne)
my_funcs.append(FuncTwo)
my_funcs.append(FuncThree)

cycle_of_funcs = cycle(my_funcs)

def right_way(event):
    func = next(cycle_of_funcs)
    func()
def left_way(event):
    leng = len(my_funcs)
    for function in range(leng-1):
        func = next(cycle_of_funcs)
    func()
win.bind('<Left>', left_way)
win.bind('<Right>', right_way)

win.mainloop()

I just want to leave a comment here that I think it isnt the best way of doing this, but still a possible way. Just to make sure on this answer is space for a OOP solutions [click]

Thingamabobs
  • 7,274
  • 5
  • 21
  • 54
  • Thanks a lot !! This Worked ! I still need to figure out and learn more about how this worked. If you don't mind will you also let me know what if there are more than 20 functions like these and we had to switch between each frames with the help of arrow keys and destroy the previous frame each time we move to the next. – Sakar Subedi Aug 02 '20 at 18:05
  • Great ! This should now keep me on a right track while writing any tkinter GUI. Stackoverflow tells me not to thank on a comment but it would be really rude if I do not as my reputation doesn't allow to show my upvotes. :) Thanks !! – Sakar Subedi Aug 02 '20 at 18:58
  • 1
    @Sakar I don't think it is the best practice but it depends on what you want to do. The solid solution that I am using more or less too is linked on the bottom of my answer. It's also possible to switch between Frames, which is a really cool idea, with a architecture like in the link. Make sure you read it. If there are questions we are here to help. – Thingamabobs Aug 02 '20 at 19:06
  • 1
    I did go through the link that you provided and have bookmarked it for future reference. Thanks Again ! – Sakar Subedi Aug 02 '20 at 19:15
  • 1
    @SakarSubedi take a quick look on this link and the few comments in the answer https://stackoverflow.com/questions/62619867/layering-graphical-interfaces-on-top-of-each-other/62620295#62620295 – Thingamabobs Aug 03 '20 at 14:19
  • 1
    Thanks ! I did something similar with my current project but with classes. I made two classes one for the frame and the other for the Toplevel frame. Slowing its getting easier for me to structure my program with two different class. – Sakar Subedi Aug 03 '20 at 21:05
0

Here are the binds for the arrow keys:

win.bind('<left>', function)
win.bind('<right>', function2) 

As for getting rid of each frame, you need someway to figure out whether the other frame exists, and if it does then destroy it.

frameone = None # Initialize it as nothing
frametwo = None # Initialize it as nothing

def FuncOne():
    global frameone, frametwo # make it accessible
    ...
    if frametwo != None:
        # Get rid of frametwo
        frametwo.destroy()
        frametwo = None 
    ...
    frameone.pack()

def FuncTwo():
    global frameone, frametwo # make it accessible
    ...
    if frameone != None:
        # Get rid of frameone
        frameone.destroy()
        frameone= None 
    ...
    frametwo.pack()

Basically, what it does is that it sets frameone and frametwo to None. Then, when a certain function is called, it sets frameone/frametwo to a frame object. Then, when the other function is called, it sees that the other frame is not none, so it destroys it and makes it none.

  • win.bind('Right', FuncTwo) only works when I am in the main window (win). It does not work when I am inside the frame of FuncTwo to get back to FuncOne by win.bind('Left', FuncOne) – Sakar Subedi Aug 02 '20 at 17:04
  • Then you can do frameone.bind(, FuncTwo) or frametwo.bind(, FuncOne) when you create the frame. – Mannan Bhardwaj Aug 02 '20 at 17:16
  • I did try frameone.bind too but it is neither throwing me any error nor taking me to the next frame. – Sakar Subedi Aug 02 '20 at 17:27
0

I made a picker function which is an event. There is also a frame count which tells the picker which one should it start. At the start of your function I destroy the other window. Here is my code:

from tkinter import *
win = Tk()
frame = 1



class MyFrame(Frame):
    def __init__(self, master, **kwargs):
        Frame.__init__(self, master,
        bg='white' ,**kwargs)

def FuncOne():
    global frameone
    try:
        frametwo.destroy()
    except:
        pass

    frameone = MyFrame(win)
    frameone.pack()

    lbl = Label(frameone, text="ABC", font='"Serif" 247' , width=win.winfo_screenwidth(), height=win.winfo_screenheight())
    lbl.pack(fill=BOTH, expand=True)


def FuncTwo():
    global frametwo
    try:
        frameone.destroy()
    except:
        pass

    frametwo = MyFrame(win)
    frametwo.pack()

    lbl = Label(frametwo, text="ABC", font='"Serif" 200', width=win.winfo_screenwidth(), height=win.winfo_screenheight())
    lbl.pack()
def picker(event):
    global frame
    if frame == 1:
        frame = 2
        FuncOne()

    else:
        frame = 1
        FuncTwo()
win.bind('<Left>', picker)
win.bind('<Right>', picker)
win.mainloop()
Wrench56
  • 290
  • 2
  • 14