示例#1
0
class GUIHandler:

    screenwidth = 0
    screenheight = 0
    windowwidth = 0
    windowheight = 0

    ## Initializes the GUI setup of the program.
    #
    def __init__(self,game):

        assert type(game) is Game
        self.root = Tk()
        self.root.title('SET')
        self.root.resizable(0,0)
        self.root.withdraw()
        GUIHandler.screenwidth = self.root.winfo_screenwidth()
        GUIHandler.screenheight = self.root.winfo_screenheight()
        if GUIHandler.screenwidth < 1024 or GUIHandler.screenheight < 768:
            showerror("Resolution Error","Your screen's resolution is likely not the best choice to run this game. Minimum resolution for this game is at least 1024x768.")
            raise ResolutionError(GUIHandler.screenwidth,GUIHandler.screenheight)
        GUIHandler.windowwidth = GUIHandler.screenwidth // 3
        GUIHandler.windowheight = GUIHandler.screenheight // 1.5
        self.buttonField = None
        self.checkButtonField = None
        self.Game = game
        self.Field = game.field
        assert self.Game
        assert self.Field

        self.root.geometry('%dx%d+%d+%d' % (GUIHandler.windowwidth,
                                          GUIHandler.windowheight,
                                          self.root.winfo_screenwidth()/8,
                                          self.root.winfo_screenheight()/8))

        menu = Menu(self.root)

        gamemenu = Menu(menu,tearoff=0)
        gamemenu.add_command(label='New Game',command=lambda:self.startNewGame(),accelerator="F2")
        gamemenu.add_command(label='Leaderboards',command=lambda:showinfo("Not implemented","Feature not implemented...yet."))
        gamemenu.add_command(label='Exit',command=lambda:self.root.destroy(),accelerator="Alt-F4")

        menu.add_cascade(label='Game',menu=gamemenu)

        settingmenu = Menu(menu,tearoff=0)

        gamedifficulty = Menu(settingmenu,tearoff=0)
        gamedifficulty.add_radiobutton(label='Beginner',command=lambda :self.changeGameDifficulty(Difficulty.BEGINNER),accelerator="B")
        gamedifficulty.add_radiobutton(label='Novice',command=lambda :self.changeGameDifficulty(Difficulty.NOVICE),accelerator="N")
        gamedifficulty.add_radiobutton(label='Advanced',command=lambda :self.changeGameDifficulty(Difficulty.ADVANCED),accelerator="A")

        settingmenu.add_cascade(label='Game Difficulty',menu=gamedifficulty)

        timedmode = Menu(settingmenu,tearoff=0)
        timedmode.add_radiobutton(label='On',command=lambda:showinfo("Not implemented","Feature not implemented...yet."))
        timedmode.add_radiobutton(label='Off',command=lambda:showinfo("Not implemented","Feature not implemented...yet."))
        
        settingmenu.add_cascade(label='Timed Mode',menu=timedmode)

        timeddifficulty = Menu(settingmenu,tearoff=0)
        timeddifficulty.add_radiobutton(label='Easy',accelerator="E")
        timeddifficulty.add_radiobutton(label='Medium',accelerator="M")
        timeddifficulty.add_radiobutton(label='Hard',accelerator="H")

        settingmenu.add_cascade(label='Timed Difficulty',menu=timeddifficulty)
        menu.add_cascade(label='Settings',menu=settingmenu)

        helpmenu = Menu(menu,tearoff=0)
        helpmenu.add_command(label='About SET',command=lambda:showinfo("Not implemented","Feature not implemented...yet."))

        menu.add_cascade(label='Help',menu=helpmenu)

        self.root.config(menu=menu)

        self.root.bind('<F2>',lambda e:gamemenu.invoke(0))
        self.root.bind('L',lambda e:gamemenu.invoke(1))
        self.root.bind('<Alt-F4>',lambda e:gamemenu.invoke(2))
        self.root.bind('b',lambda e:gamedifficulty.invoke(Difficulty.BEGINNER))
        self.root.bind('n',lambda e:gamedifficulty.invoke(Difficulty.NOVICE))
        self.root.bind('a',lambda e:gamedifficulty.invoke(Difficulty.ADVANCED))

        self.remainderLabel = Label(self.root,text="There are %d set(s) remaining on the board." % self.Game.numSetsRemaining(),bg="white",relief=Tkinter.RAISED,font=('Helvetica',12))
        self.remainderLabel.place(x=(GUIHandler.windowwidth-self.remainderLabel.winfo_reqwidth())//2,y=3*GUIHandler.windowheight//4)

        timer = Label(self.root,text="Untimed Mode",bg="green",relief=Tkinter.RAISED,font=('Helvetica',12))
        timer.place(x=(GUIHandler.windowwidth-timer.winfo_reqwidth())//2,y=3*GUIHandler.windowheight//4.5)

        hintbutton = Button(text="Hint, please!",font=("Helvetica",12),command=lambda :self.getHint())
        hintbutton.place(x=(GUIHandler.windowwidth-hintbutton.winfo_reqwidth())//2,y=3*GUIHandler.windowheight//3.5)

        self.userSetsCreated = Toplevel(self.root)
        self.userSetsHeight = 0
        self.userSetsCreated.title("Sets Created")
        self.userSetsCreated.geometry("%dx%d+%d+%d" % (Card.pixelWidth*3,
                                               self.userSetsCreated.winfo_screenheight(),
                                               self.root.winfo_pointerx()+self.userSetsCreated.winfo_reqwidth(),0))
        self.userSetsCreated.protocol("WM_DELETE_WINDOW",0)
        self.userSetsCreated.resizable(0,0)
        self.root.focus_set()

        self.updateCardsOnField(self.Field)

    ## Process the card location that the user picked. The function calls the game instance's addCardChoice method
    # to get a callback on the result of adding the card choice.
    def processCardChoice(self,i):

        cbutton = self.checkButtonField[i//self.Field.cols()][i%self.Field.cols()]
        cbutton.select()
        result = self.Game.addCardChoice(i)

        if result == 3:
            cbutton.deselect()
            return
        elif not result:
            return

        if result == 2: #Case 1: 3 choices were made and the Game object verified them as a set.
            showinfo("Good Job!","You made a set!")
            self.remainderLabel.config(text="There are %d set(s) remaining on the board." % self.Game.numSetsRemaining())
            x = 0
            for h in map(Card.getCardImgNumber,[self.Field[i//self.Field.cols()][i%self.Field.cols()] for i in self.Game.setsMadeSoFar[-1]]):
                pic = PhotoImage(file='../media/%d.gif'%h)
                label = Label(self.userSetsCreated,image=pic)
                label.image = pic
                label.place(x=x,y=self.userSetsHeight)
                x+=Card.pixelWidth
            self.userSetsHeight+=Card.pixelHeight
            if not self.Game.numSetsRemaining():
                showinfo("WINNER!","Congratulations! You found all sets!")
                self.disableButtons()
        elif result == 1: #Case 2: 3 choices were made but the user has made this set before in the game.
            showinfo("Repeat","You've already made this set")
        elif type(result) == tuple: #Case #3: 3 choices were made but they didn't form a set.
            assert len(result) == 2
            showinfo("Not a set","Not a set because 2 are %s and 1 is %s" % (result[0],result[1]))
        for i in self.checkButtonField:
            for j in i:
                j.deselect()

    def disableButtons(self):
        for i in self.buttonField:
            for j in i:
                j.config(state=Tkinter.DISABLED)
        for i in self.checkButtonField:
            for j in i:
                j.config(state=Tkinter.DISABLED)

    #Hard Coded lists of button widgets because python won't let me generate each of the Button's command
    #feature correctly via list comprehension.

    def setupNoviceField(self):

        self._destroyAllButtons() if self.buttonField else None

        self.buttonField = [[Button(self.root,command=lambda :self.processCardChoice(0)),
                             Button(self.root,command=lambda :self.processCardChoice(1)),
                             Button(self.root,command=lambda :self.processCardChoice(2))],
                            [Button(self.root,command=lambda :self.processCardChoice(3)),
                             Button(self.root,command=lambda :self.processCardChoice(4)),
                             Button(self.root,command=lambda :self.processCardChoice(5))],
                            [Button(self.root,command=lambda :self.processCardChoice(6)),
                             Button(self.root,command=lambda :self.processCardChoice(7)),
                             Button(self.root,command=lambda :self.processCardChoice(8))]]

        self.checkButtonField = [[Checkbutton(self.root,text='1',command=lambda :self.processCardChoice(0)),
                                  Checkbutton(self.root,text='2',command=lambda :self.processCardChoice(1)),
                                  Checkbutton(self.root,text='3',command=lambda :self.processCardChoice(2))],
                                 [Checkbutton(self.root,text='4',command=lambda :self.processCardChoice(3)),
                                  Checkbutton(self.root,text='5',command=lambda :self.processCardChoice(4)),
                                  Checkbutton(self.root,text='6',command=lambda :self.processCardChoice(5))],
                                 [Checkbutton(self.root,text='7',command=lambda :self.processCardChoice(6)),
                                  Checkbutton(self.root,text='8',command=lambda :self.processCardChoice(7)),
                                  Checkbutton(self.root,text='9',command=lambda :self.processCardChoice(8))]]

    #See the comment above setup Novice _Field.

    def setupAdvancedField(self):

        self._destroyAllButtons() if self.buttonField else None

        self.buttonField = [[Button(self.root,command=lambda :self.processCardChoice(0)),
                             Button(self.root,command=lambda :self.processCardChoice(1)),
                             Button(self.root,command=lambda :self.processCardChoice(2)),
                             Button(self.root,command=lambda :self.processCardChoice(3))],
                            [Button(self.root,command=lambda :self.processCardChoice(4)),
                             Button(self.root,command=lambda :self.processCardChoice(5)),
                             Button(self.root,command=lambda :self.processCardChoice(6)),
                             Button(self.root,command=lambda :self.processCardChoice(7))],
                            [Button(self.root,command=lambda :self.processCardChoice(8)),
                             Button(self.root,command=lambda :self.processCardChoice(9)),
                             Button(self.root,command=lambda :self.processCardChoice(10)),
                             Button(self.root,command=lambda :self.processCardChoice(11))]]

        self.checkButtonField = [[Checkbutton(self.root,text='1',command=lambda :self.processCardChoice(0)),
                                  Checkbutton(self.root,text='2',command=lambda :self.processCardChoice(1)),
                                  Checkbutton(self.root,text='3',command=lambda :self.processCardChoice(2)),
                                  Checkbutton(self.root,text='4',command=lambda :self.processCardChoice(3))],
                                 [Checkbutton(self.root,text='5',command=lambda :self.processCardChoice(4)),
                                  Checkbutton(self.root,text='6',command=lambda :self.processCardChoice(5)),
                                  Checkbutton(self.root,text='7',command=lambda :self.processCardChoice(6)),
                                  Checkbutton(self.root,text='8',command=lambda :self.processCardChoice(7))],
                                 [Checkbutton(self.root,text='9',command=lambda :self.processCardChoice(8)),
                                  Checkbutton(self.root,text='10',command=lambda :self.processCardChoice(9)),
                                  Checkbutton(self.root,text='11',command=lambda :self.processCardChoice(10)),
                                  Checkbutton(self.root,text='12',command=lambda :self.processCardChoice(11))]]

    def updateCardsOnField(self,cardField):

        rows = cardField.rows()
        cols = cardField.cols()

        if cols == 3:
            self.setupNoviceField()
        elif cols == 4:
            self.setupAdvancedField()
        else:
            raise IndexError("CardField argument has illegal number of columns")

        assert len(self.buttonField) == rows
        assert len(self.buttonField[0]) == cols

        spacing = (GUIHandler.windowwidth-(cols * Card.pixelWidth) - 5) / (cols-1)
        x,y = 0,0
        cbwidth = self.checkButtonField[0][0].winfo_reqwidth()
        for i in xrange(rows):
            for j in xrange(cols):
                pic = PhotoImage(file='../media/%s.gif' % str(cardField[i][j].getCardImgNumber()))
                self.buttonField[i][j].config(image=pic)
                self.buttonField[i][j].image = pic
                self.buttonField[i][j].place(x=x,y=y)
                self.checkButtonField[i][j].place(x=x + Card.pixelWidth//2 - cbwidth//4,y=(y+Card.pixelHeight+10))
                x += Card.pixelWidth + spacing
            y += Card.pixelHeight + 40
            x = 0

    def startNewGame(self):

        self.userSetsHeight = 0
        [i.destroy() for i in self.userSetsCreated.children.values()]
        self.Game.resetGame()
        self.updateCardsOnField(self.Game.field)
        self.remainderLabel.config(text="There are %d set(s) remaining on the board." % self.Game.numSetsTotal)
        #print map(lambda ls:map(lambda x:x+1,ls),self.Game.setsListTotal)

    def _destroyAllButtons(self):

        for i in self.buttonField:
            for j in i:
                j.destroy()
        for i in self.checkButtonField:
            for j in i:
                j.destroy()

    def changeGameDifficulty(self,difficulty):

        if self.Game.changeGameDifficulty(difficulty):
            self.startNewGame()

    def getHint(self):
        result = self.Game.callHint()
        if result == -3:
            showinfo("One set remains","You cannot use hints in finding the last set.")
        elif result == -2:
            showinfo("Game's over","Game has ended. Start a new game if you wish.")
        elif result == -1:
            showinfo("No more hints","Sorry. You are out of hints to spare.")
        else:
            showinfo("Your Hint","Pick Card #%d"%(result+1))

    def run(self):
        self.root.deiconify()
        self.root.mainloop()