Domanda

I'm creating a basic rock-paper-scissors game to get familiar with python and tkinter. I wanted to create a gui and a logic class to separate the two. However, I can't seem to find a code layout that's both valid in python and makes sense to me.

I want the Gui class to only know about the widgets and update them.

class Gui:      
    def setup(self):
        root = Tk.Tk()
        root.geometry("370x170")
        root.resizable(width=False, height=False)
        root.title("Rock, Paper, Scissors")
        root.iconbitmap("Play.ico")

        rock_button = Tk.Button(root, text="Rock", command=rock_clicked)
        rock_button.place(width=100, height=30, x=10, y=30)

        paper_button = Tk.Button(root, text="Paper", command=paper_clicked)
        paper_button.place(width=100, height=30, x=10, y=70)

        scissors_button = Tk.Button(root, text="Scissors", command=scissors_clicked)
        scissors_button.place(width=100, height=30, x=10, y=110)

        score_font = font.Font(family="Helvetica", size=20)

        own_score_lbl = Tk.Label(root, text="0", relief=Tk.RIDGE, font=score_font)
        own_score_lbl.place(width=50, height=110, x=120, y=30)

        ai_score_lbl = Tk.Label(root, text="0", relief=Tk.RIDGE, font=score_font)
        ai_score_lbl.place(width=50, height=110, x=200, y=30)

        ai_choice = Tk.Label(root, relief=Tk.RIDGE)
        ai_choice.place(width=100, height=110, x=260, y=30)

        root.mainloop()

gui = Gui()
gui.setup()

In other languages, I'm used to having a logic member variable in the gui class, and vice versa. That doesn't work here. The click handler functions can't be members of the logic class, becuase of the self argument. So I tried to declare them module-level, and call methods of the logic class from those, that didn't work out either.

Ideally, after a click event, I would expect a logic class method to be called, which then makes it's calculations, and calls an appropriate gui method back i.e. set_label_text().

How can I accomplish this with an OO design?

È stato utile?

Soluzione

I am definitely not an Tkinter expert, this is my first Tkinter app.

But here is my proposal how to use Python class inheritance to organize your solution.

The code runs

import Tkinter
import tkFont as font

class Gui(Tkinter.Tk):      
    def __init__(self, logic):
        Tkinter.Tk.__init__(self)

        self.logic = logic

        self.geometry("370x170")
        self.resizable(width=False, height=False)

        rock_button = Tkinter.Button(self, text="Rock", command=self.rock_clicked)
        rock_button.place(width=100, height=30, x=10, y=30)

        paper_button = Tkinter.Button(self, text="Paper", command=self.paper_clicked)
        paper_button.place(width=100, height=30, x=10, y=70)

        scissors_button = Tkinter.Button(self, text="Scissors", command=self.scissors_clicked)
        scissors_button.place(width=100, height=30, x=10, y=110)

        score_font = font.Font(family="Helvetica", size=20)

        own_score_lbl = Tkinter.Label(self, text="0", relief=Tkinter.RIDGE, font=score_font)
        own_score_lbl.place(width=50, height=110, x=120, y=30)

        ai_score_lbl = Tkinter.Label(self, text="0", relief=Tkinter.RIDGE, font=score_font)
        ai_score_lbl.place(width=50, height=110, x=200, y=30)

        ai_choice = Tkinter.Label(self, relief=Tkinter.RIDGE)
        ai_choice.place(width=100, height=110, x=260, y=30)

        self.render_title()

    def render_title(self):
        logic = self.logic
        templ = "Rock({logic.rock_counter}), Paper({logic.paper_counter}), Scissors({logic.scissors_counter})"
        title = templ.format(logic=logic)
        self.title(title)

    def rock_clicked(self):
        self.logic.play_rock()
        self.render_title()

    def paper_clicked(self):
        self.logic.play_paper()
        self.render_title()

    def scissors_clicked(self):
        self.logic.play_scissors()
        self.render_title()

class GameLogic():
    def __init__(self):
        self.rock_counter = 0
        self.paper_counter = 0
        self.scissors_counter = 0

    def play_rock(self):
        self.rock_counter += 1

    def play_paper(self):
        self.paper_counter += 1

    def play_scissors(self):
        self.scissors_counter += 1

logic = GameLogic()
game = Gui(logic)
game.mainloop()

Gui Inheriting from Tkinter.Tk

  • we get all the methods from Tk, incl. mainloop

Using Gui __init__ constructor

First, we ask our parent class to initialize, this is the Tkinter.Tk.init(self)

Then we refer to former root as self.

Adding logic to Gui

Logic is implemented as independent class, it knows noting about frontend, it only expects calls to it's methods.

We instantiate logic in advance and pass it to Gui constructore.

There must be a contract, what methods and properties is Logic going to provide.

Using Logic from Gui

When Gui finds out, there is some logic related event, it can call methods of logic.

After changes of the logic, it is often needed (withing Gui method) to rerender something.

Start that up

This shall be the patter to follow:

  1. instantiate logic
  2. create gui, passing the logic in
  3. let it run

translated to Python:

logic = GameLogic()
game = Gui(logic)
game.mainloop()
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top