Python tkinter frames (personal project)


#1

I am struggling with tkinter and using different frames for different views, here is what i have so far:

import tkinter as tk
from pokemon_data import *

def partial(f, v):
    return lambda: f(v)

class PokemonGame:
    ''' controller for managiging frames '''
    def __init__(self, master):
        self.master = master
        self.frames = {}
        for F in (StartGame,ShowPokemon,PokemonDetail):
            frame = F(parent=master, controller=self)
            self.frames[F.__name__] = frame
            frame.grid(row=0, column=0, sticky="nsew")
        ''' rais the first frame '''
        self.raise_frame("StartGame")
    def get_page(self, classname):
        '''Returns an instance of a page given it's class name as a string'''
        for page in self.frames.values():
            if str(page.__class__.__name__) == classname:
                return page
        return Non

    def raise_frame(self,page_name):
        ''' raise a frame '''
        frame = self.frames[page_name]
        frame.tkraise()

    def lower_frame(self,page_name):
        ''' lower a frame '''
        frame = self.frames[page_name]
        frame.lower()

class StartGame(tk.Frame):
  ''' introduction screen of the game '''
  def __init__(self, parent, controller):
    super(StartGame,self).__init__(parent)
    self.controller = controller
    tk.Label(self, text="welcome to the world of pokemon").grid(row=0,column=0)
    tk.Label(self, text="You have to beat the water gym").grid(row=1,column=0)
    tk.Label(self, text="the gym choices the following pokemon first: " + gym_pokemon.name).grid(row=2, column=0)
    tk.Button(self, text="next", command=lambda: controller.raise_frame("ShowPokemon")).grid(row=3, column=0)


class ShowPokemon(tk.Frame):
  ''' main frame, overview of all pokemon '''
  def __init__(self, parent, controller):
    super(ShowPokemon,self).__init__(parent)
    self.controller = controller
    for row, pokemon in enumerate(reinier):
      pokemon_label = tk.Label(self, text=pokemon.name + "\nhp: " + str(pokemon.hp) + "/" + str(pokemon.max_hp))
      pokemon_label.grid(row=row,column=0)
      info_button = tk.Button(self, text="view info", command=partial(self.switch,pokemon))
      info_button.grid(row=row,column=1)

  def switch(self, pokemon):
    self.controller.raise_frame("PokemonDetail")

class PokemonDetail(tk.Frame):
    ''' detail view of pokemon '''
    def __init__(self, parent, controller):
        super(PokemonDetail,self).__init__(parent)
        self.controller = controller
        tk.Button(self, text="back", command=lambda: controller.lower_frame("PokemonDetail")).grid()

def main():
    root = tk.Tk()
    app = PokemonGame(root)
    root.mainloop()
main()

but for example for PokemonDetail, i have no idea how to include a label with the pokemon name? The frame is already there, did i choice a wrong approach to this problem altogether?


#2

That's two problems, creating the label and acquiring the name
I'm guessing you mean the latter?
Where's the name information in your program?
Anything preventing you from sending it as an argument when creating the detail?


#3

true, two problems.

Yep, in the ShowPokemon class there is a button:

info_button = tk.Button(self, text="view info", command=partial(self.switch,pokemon))

which contains the pokemon (which is instance, pokename.name would give me the name of the pokemon)

this command should both raise PokemonDetail frame and create a label on this frame. I am currently trying a different approach. (i couldn't get this to work)

i tried with kwargs, this seemed possible (i believe succeeded, but discarded this changes), i don't remember why


#4

You should still be importing partial, not implementing it


#5

Okay, did that :slight_smile: thank you :slight_smile:

I will just mess around with it further, see if i can get it to work

+ from functools import partial
-  def partial(f, v):
-     return lambda: f(v)

#6

Could you have a lambda that both creates it and calls switch?
I guess that would be a regular function, since that's two actions


#7

command is what the button execute:

command=partial(self.switch,pokemon)

so this will call switch, which is a method:

def switch(self, pokemon):
    test = self.controller.raise_frame("PokemonDetail")

then switch method calls raise_frame, so i guess it must be possible to let the switch call raise_frame and create? But the problem i am facing is then: How do i add buttons to an already created frame?

Oh bad naming, switch doesn't actually do the switching, raise_frame does.


#8

That's crazy talk

As is that.

You're leaving the kind of things that you would be saying in program logic and you instead use meta-programming


#9

Is that a problem of having a reference to the frame, or of how that's done in tkinter?


#10

Maybe switch shouldn't exist at all? It's just an alias to something else

raise/lower frame also a bit questionable (I think it would be more clear to write what they are doing instead of calling them, and they are both one-liners)

Maybe that self.frames dict is something you can use instead of messing with the dunder names? (__*__)


#11

oh, i looked at stackoverflow to see what i could find:


i am not entirely sure yet, still trying to puzzle everything together, maybe i am in over my head.

The problem seems to be that PokemonDetail class inherits from tk.Frame,so then if you look at how a button is added:

tk.Button(self

it is added to self, i don't understand this inheritance well enough i am afraid


#12

Wouldn't you do something akin to:

detail = PokemonDetail()
# .. 5 minutes later:
tk.Button(detail, text="blah", ...

#13

hm... interesting, i will try, and also see to get a better handle on how self is related to the frames

As always ionatan, you are really helpful :slight_smile: