示例#1
0
 def preferencesHandler(self, event):
     """
     Displays a dialog with preferences for changing
     card backs and the background image.
     """
     card_backs = self.dealer.board.getCardBacks()
     background_img = self.gui.backgroundFilename
     preferences = {'Card Back': card_backs, 'Background': background_img}
     self.prefsDialog = PreferencesDialog(prefs=preferences)
     
     self.prefsDialog.setUploadCardBackBtnHandler(self.prefsDialog.uploadCardBackHandler)
     self.prefsDialog.setUploadBackgroundBtnHandler(self.prefsDialog.uploadBackgroundHandler)
     
     self.prefsDialog.setOKButtonHandler(self.preferences_OK)
     self.prefsDialog.setCancelButtonHandler(self.preferences_CANCEL)
     
     self.prefsDialog.CenterOnParent()
     self.prefsDialog.ShowModal()
示例#2
0
class Control(object):
    
    """
    
    The Control class sends commands to the model to change the state
    of the Solitaire game, and to the view to update the visual aspect
    of the game.
    
    """

    def __init__(self):
        """
        Initializes a new Control for the Solitaire game.
        Sets all event handlers for the GUI.
        """
        app = wx.App(False)
        
        # Seconds elapsed since the epoch until when the application 
        # is launched. For use in the Status Bar.
        self.current = time.time()
        self.preferences = DEFAULT_PREFERENCES
        
        # Create Model and View instances.
        self.dealer = Dealer()
        self.player = self.dealer.player
        self.options = self.player.options
        self.gui = Frame()
        
        # Start the game by dealing the cards.
        self.dealer.deal()
        self.resetSrcMove()
        self.resetDestMove()
        self.drag_image = None
        
        # Set all event handlers for the Frame, the main Panel,
        # the PilePanels, the MenuBar, and the StatusBar.
        self.setHandlers()
        
        # Show the Frame and run the app.
        self.gui.Show()
        app.MainLoop()
    
    def setHandlers(self):
        """
        Set all event handlers for the Frame, the main Panel,
        the PilePanels, the MenuBar, and the StatusBar.
        """
        self.setFrameHandlers()
        self.setPilePanelHandlers()
        self.setMenuBarHandlers()
        self.setStatusBarHandlers()
    
    def setFrameHandlers(self):
        """
        Sets all event handlers for the Frame.
        """
        self.gui.setOnPaintHandler(self.frameOnPaint)
        self.gui.setOnSizeHandler(self.frameOnRepaint)
        self.gui.setPanelOnPaint(self.panelOnPaint)
        self.gui.setOnCharHandler(self.charPressedHandler)
        self.gui.setOnLeftUpHandler(self.frameOnLeftUp)
        self.gui.setPanelOnLeftUp(self.frameOnLeftUp)
        self.gui.setPanelOnRightDown(self.frameOnRightDown)
        self.gui.setOnMotionHandler(self.frameOnMotion)
    
    def setPilePanelHandlers(self):
        """
        Sets the event handlers for the PilePanels.
        """
        # Set all event handlers for the Stock panel.
        self.gui.stock_panel.setOnPaintHandler(self.pilePanelOnPaint)
        self.gui.stock_panel.setOnLeftDownHandler(self.stockOnLeftDown)
        
        # Set all event handlers for the Waste panel.
        self.gui.waste_panel.setOnPaintHandler(self.pilePanelOnPaint)
        self.gui.waste_panel.setOnLeftDownHandler(self.pilePanelOnLeftDown)
        self.gui.waste_panel.setOnLeftUpHandler(self.pilePanelOnLeftUp)
        self.gui.waste_panel.setOnLeftDoubleHandler(self.pilePanelOnRightDown)
        self.gui.waste_panel.setOnRightDownHandler(self.pilePanelOnRightDown)
        
        # Set all event handlers for the Foundation panels.
        for suit in Suit.SUITS:
            fpanel = self.gui.foundation_panels[suit]
            fpanel.setOnPaintHandler(self.pilePanelOnPaint)
            fpanel.setOnLeftDownHandler(self.pilePanelOnLeftDown)
            fpanel.setOnLeftUpHandler(self.pilePanelOnLeftUp)
        
        # Set all event handlers for the Tableau panels.
        for tab in self.gui.tableau_panels:
            tab.setOnPaintHandler(self.pilePanelOnPaint)
            tab.setOnLeftDownHandler(self.pilePanelOnLeftDown)
            tab.setOnLeftUpHandler(self.pilePanelOnLeftUp)
            tab.setOnLeftDoubleHandler(self.pilePanelOnRightDown)
            tab.setOnRightDownHandler(self.pilePanelOnRightDown)
    
    def setMenuBarHandlers(self):
        """
        Sets all event handlers for the MenuBar.
        """
        # Set all event handlers for the Game menu items.
        self.gui.setNewGameHandler(self.newGameHandler)
        self.gui.setUndoHandler(self.undoMenuHandler)
        self.gui.setHintMenuHandler(self.hint)
        self.gui.setStatisticsHandler(self.statisticsHandler)
        self.gui.setOptionsHandler(self.optionsHandler)
        self.gui.setPreferencesHandler(self.preferencesHandler)
        self.gui.setChangeUserHandler(self.changeUserHandler)
        self.gui.setExitHandler(self.closewindow)
        
        # Set all event handlers for the Help menu items.
        self.gui.setHowToPlayHandler(self.howtoplaywindow)
        self.gui.setAboutHandler(self.aboutwindow)
    
    def setStatusBarHandlers(self):
        """
        Initializes the StatusBar by setting the event handler
        for the timer and redirecting standard output to the
        fourth field of the StatusBar.
        """
        # Initialize the timer in the status bar, and
        # set its handler.
        self.gui.setTimerHandler(self.start_timer)
        
        # Redirect stdout to the 4th StatusBar Field.
        redirection = RedirectText(self.gui.GetStatusBar())
        sys.stdout = redirection

    def frameOnPaint(self, event):
        """
        Paints the background image of the Frame, and
        calls Update() on each of the panels to update
        the cards they contain.        
        """
        # Create a new Device Context for drawing
        # graphics; AutoBuffered to prevent flickering.
        dc = wx.AutoBufferedPaintDC(self.gui)
        
        # Scale the background image to fit the size of the Frame.
        # Draw the new background image, and update all pile Panels.
        self.gui.paintBackground(dc)
        del dc
        self.gui.updateChildren()
    
    def frameOnRepaint(self, event):
        """
        Repaints the background image of the Frame on a resize
        event.
        """
        self.gui.Layout()
        self.gui.Refresh(eraseBackground=False)
    
    def frameOnLeftUp(self, event):
        """
        The event handler for when the left mouse 
        button is released on the Frame.
        """
        (x, y) = event.GetPosition()
        pLeft, pTop = self.gui.panel.GetPosition()
        for panel in self.gui.panels:
            l, t, w, h = panel.GetRect()
            (left, top) = (l + pLeft, t + pTop)
            (right, bottom) = (left + w, top + h)
            if x >= left and x <= right and y <= bottom and y >= top:
                self.pileFinishMove(panel)
                break
        if (self.clickedPile is not None and 
            self.srcPile is not None and 
            self.destPile is None):
            self.dealer.push_pile(self.clickedPile, self.srcPile, self.destPile)
            self.srcPanel.Refresh()
            self.resetSrcMove()
            self.resetDestMove()
        self.endDrag()
    
    def frameOnRightDown(self, event):
        """
        The event handler for when the right mouse 
        button is pressed on the Frame.
        """
        if self.dealer.can_autocomplete():
            self.autocomplete()
    
    def frameOnMotion(self, event):
        """
        The event handler for when the mouse is moving
        on the Frame.
        """
        if self.drag_image is not None and event.LeftIsDown():
            self.drag_image.Show()
            (x, y) = event.GetPosition()
            self.drag_image.Move((x, y))
            return

    def panelOnPaint(self, event):
        """
        Paints the background image of the Panel.        
        """
        # Create a new Device Context for drawing
        # graphics; AutoBuffered to prevent flickering.
        dc = wx.AutoBufferedPaintDC(self.gui.panel)
        newbg = self.gui.scaleBackground()
        dc.DrawBitmap(newbg.GetSubBitmap(self.gui.panel.GetRect()), 0, 0)

    def pilePanelOnPaint(self, event):
        """
        Paints the Cards in the Pile associated with
        a PilePanel.
        """
        panel = event.GetEventObject()
        pile = self.getPile(panel)
        
        # Create a new Device Context for drawing
        # graphics; AutoBuffered to prevent flickering.
        dc = wx.AutoBufferedPaintDC(panel)
        panel.paintPile(dc, self.gui.scaleBackground(), pile)
    
    def pilePanelOnLeftDown(self, event):
        """
        The event handler for when the left mouse 
        button is pressed on a PilePanel.
        """
        (x, y), panel, pile = self.startMove(event)
        
        if panel.inClickableRange(x, y):
            card_idx = panel.findCardAt(x, y)
            if card_idx is not None:
                coords = panel.get_card_coord(x, y)
                self.dragPos = (coords[0], coords[1])
                self.setSrcMove(pile[card_idx], panel, pile)
                self.srcPanel.Refresh()
                self.beginDrag(event)

    def stockOnLeftDown(self, event):
        """
        The event handler for when the left mouse 
        button is pressed on the Stock Panel.
        """
        (x, y), spanel, spile = self.startMove(event)
        self.setSrcMove(None, spanel, spile)
        
        wpanel = self.gui.waste_panel
        wpile = self.getPile(wpanel)
        self.setDestMove(wpanel, wpile)
        
        if spanel.inClickableRange(x, y):
        
            # Draw cards from the Stock, and tell the Waste Panel 
            # how many cards to fan.
            self.dealer.draw()
            wpanel.squared = max(len(wpile) - 3, wpanel.squared, 0)
        
            self.endMove()

    def pilePanelOnLeftUp(self, event):
        """
        The event handler for when the left mouse 
        button is released on a PilePanel.
        """
        # Find out which pile the mouse was released on.
        _, panel, pile = self.startMove(event)
            
        if self.srcPanel is None:
            return
        
        # Tell the dealer to make the move.
        self.setDestMove(panel, pile)
        self.dealer.push_pile(self.clickedPile, self.srcPile, self.destPile)
        self.endMove()
        self.endDrag()
    
    def pileFinishMove(self, panel):
        """
        """
        pile = self.getPile(panel)
        if self.srcPanel is None:
            return
        
        # Tell the dealer to make the move.
        self.setDestMove(panel, pile)
        self.dealer.push_pile(self.clickedPile, self.srcPile, self.destPile)
        self.endMove()
        self.endDrag()
    
    def pilePanelOnRightDown(self, event):
        """
        Pushes the card on top of a pile
        to its respective Foundation pile, if
        it is suitable.
        """
        (x, y), panel, pile = self.startMove(event)
        
        if (pile.is_empty() or not 
            panel.inClickableRange(x, y)):
            event.Skip()
            return
        
        # Grab the card that was clicked.
        card_idx = panel.findCardAt(x, y)
        self.setSrcMove(pile[card_idx], panel, pile)
        
        # Find the appropriate foundation pile.
        card_suit = self.clickedCard.suit
        fpanel = self.gui.foundation_panels[card_suit]
        fpile = self.getPile(fpanel)
        
        self.setDestMove(fpanel, fpile)
        
        # Make the move.
        self.dealer.push_pile(self.clickedPile, self.srcPile, self.destPile)
        self.endMove()

    def pilePanelOnMotion(self, event):
        """
        The event handler for when the mouse is moving on
        a Pile Panel.
        """
        if self.drag_image is not None and event.LeftIsDown():
            (x, y) = event.GetPosition()
            self.drag_image.Move((x, y))
            return
        event.Skip()
    
    def startMove(self, event):
        """
        Sets the variables for the beginning of a move.
        """
        (x, y) = event.GetPosition()
        panel = event.GetEventObject()
        pile = self.getPile(panel)
        self.gui.SetStatusText('', 4)
        return (x, y), panel, pile

    def doMove(self):
        """
        """
        pass
    
    def endMove(self):
        """
        Refreshes the Panels where cards were moved,
        updates moves and score, and resets the
        source/destination variables. Checks if the player
        won.
        """
        # Mark the source and destination Panels as 'dirty' so
        # that they will be repainted on the next paint event.
        self.srcPanel.Refresh()
        self.destPanel.Refresh()
        
        # Update the Status Bar fields.
        self.set_moves_text()
        self.set_score_text()
        
        # Reset the pile variables and check if the Player won.
        self.resetSrcMove()
        self.resetDestMove()
        self.checkEndGame()
    
    def beginDrag(self, event):
        """
        Begins dragging the image representing the
        pile the player popped.
        """
        (x, y) = event.GetPosition()
        if self.clickedPile is not None:
            pileBMP = self.gui.pileBMP(self.clickedPile)
            self.drag_image = wx.DragImage(pileBMP)
            xPos, yPos = self.dragPos
            hotspot = (x - xPos, y - yPos)
            self.drag_image.BeginDrag(hotspot, self.gui)
            self.drag_image.Hide()
            self.gui.Update()

    def endDrag(self):
        """
        Ends the drag.
        """
        if self.drag_image is not None:
            self.drag_image.Hide()
            self.drag_image.EndDrag()
            self.gui.Update()
            self.drag_image = None
    
    def getPile(self, panel):
        """
        Gets the Pile associated with a Pile Panel.
        """
        name = panel.GetName()
        if name == 'Waste':
            return self.dealer.board.waste
        if name == 'Stock':
            return self.dealer.board.stock
        if name in Suit.SUITS:
            return self.dealer.board.foundations[name]
        if int(name) in range(0,7):
            return self.dealer.board.tableaux[int(name)]
    
    def getPanel(self, pile):
        """
        Gets the Panel associated with a Pile.
        """
        name = pile.name
        if name == 'Waste':
            return self.gui.waste_panel
        if name == 'Stock':
            return self.gui.stock_panel
        if name in Suit.SUITS:
            return self.gui.foundation_panels[name]
        if name.find('Tableau') >= 0:
            return self.gui.tableau_panels[pile.id_]
    
    def setSrcMove(self, clickedCard, srcPanel, srcPile):
        """
        Sets the source variables.
        """
        self.clickedCard = clickedCard
        self.srcPanel = srcPanel
        self.srcPile = srcPile
        self.clickedPile = self.dealer.pop_pile(self.clickedCard, self.srcPile)
    
    def setDestMove(self, destPanel, destPile):
        """
        Sets the destination variables.
        """
        self.destPanel = destPanel
        self.destPile = destPile
    
    def resetSrcMove(self):
        """
        Sets the source Panel, source Pile, and clicked
        card to None.
        """
        self.srcPanel = None
        self.srcPile = None
        self.clickedCard = None
        self.clickedPile = None
    
    def resetDestMove(self):
        """
        Sets the destination Panel and destination Pile
        to None.
        """
        self.destPanel = None
        self.destPile = None

    def start_timer(self, event):
        """
        Sets the text in the GUI's StatusBar Time Field to the
        number of seconds elapsed since the application was
        launched.
        """
        t = "Time:\t" + str(int(time.time() - self.current))
        self.gui.SetStatusText(t, 1)
    
    def stop_timer(self):
        """
        Stops the timer and updates the current time
        so that a new game can begin.
        """
        self.gui.timer.Stop()

    def set_score_text(self):
        """
        Sets the text in the GUI's status bar Score field to the
        score of the current game.
        """
        self.gui.setScoreText(self.player.current_score)
    
    def set_moves_text(self):
        """
        Sets the text in the Moves StatusBar Field to be
        the player's current number of moves.
        """
        self.gui.setMovesText(self.player.current_moves)
    
    def set_player_data(self):
        """
        Sets fields in the StatusBar relevant to the Player's
        data.
        """
        self.player.current_time = self.gui.getTimeText()
        self.player.current_moves = self.gui.getMovesText()
        self.player.current_score = self.gui.getScoreText()

    def autocomplete(self):
        """
        Finishes the game by moving all cards to their
        Foundation piles.
        """
        piles = []
        piles.append(self.dealer.board.waste)
        piles.extend(self.dealer.board.tableaux)
        
        all_empty = self.dealer.all_empty()
        while not all_empty:
            for pile in piles:
                if pile.is_empty():
                    continue
                
                # Grab the card at the top of the pile.
                card = pile.peek()
                panel = self.getPanel(pile)
                self.setSrcMove(card, panel, pile)
                self.gui.SetStatusText('', 4)
                self.srcPanel.Refresh()
                
                # Send it to its respective Foundation pile.
                fpanel = self.gui.foundation_panels[card.suit]
                fpile = self.dealer.board.foundations[card.suit]
                self.setDestMove(fpanel, fpile)
                self.dealer.push_pile(self.clickedPile, self.srcPile, self.destPile)
                self.endMove()
                
                # Update the panels.
                panel.Update()
                fpanel.Update()
            all_empty = self.dealer.all_empty()

    def checkEndGame(self):
        """
        If the player has completed all four foundation panels,
        end the game.
        """
        if self.dealer.player_won():
            self.gui.updatePilePanels()
            winDialog = wx.MessageDialog(self.gui, 'You win!', 'Congratulations!', wx.OK)
            winDialog.CenterOnParent()
            winDialog.Show()
            self.gui.paintBouncingCards()
            self.endGame()
    
    def endGame(self):
        """
        Stops the timer, sends the current game information
        to the player to be uploaded to the database.
        """
        self.stop_timer()
        self.set_player_data()
        self.gui.refreshChildren()
        if self.dealer.player_won():
            self.player.end_game(win=1)
        else:
            self.player.end_game(win=0)
    
    def newGameHandler(self, event):
        """
        Opens a dialog message asking if the user wants
        to start a new game.
        """
        idx = wx.MessageBox(message="Start a new game?", 
                            caption='New Game', 
                            style=wx.YES_NO|wx.NO_DEFAULT|wx.ICON_QUESTION|wx.CENTER_FRAME, 
                            parent=self.gui)
        if idx == wx.YES:
            self.endGame()
            self.startNewGame()
    
    def startNewGame(self):
        """
        Initializes the model and view for a new game.
        """
        self.dealer.new_game()
        self.gui.resetStatusText()
        self.current = time.time()
        self.gui.timer.Start()
        
        dc = wx.ClientDC(self.gui)
        self.gui.paintBackground(dc)
        for panel in self.gui.panels:
            panel.clearCardCoords()
            panel.Refresh()
    
    def undoMove(self):
        """
        Undoes the last move.
        """
        if self.dealer.undo() == False:
            return
        self.set_score_text()
        self.set_moves_text()
        self.gui.refreshPilePanels(False)

    def undoMenuHandler(self, event):
        """
        Undoes the last move when 'Undo' is selected
        from the MenuBar.
        """
        self.undoMove()
    
    def hint(self, event):
        """
        Prints out the next possible move in the StatusBar.
        """
        self.dealer.next_move()
    
    def charPressedHandler(self, event):
        """
        The handler for when either of H or Ctrl+Z are
        pressed.
        """
        keyCode = event.GetKeyCode()
        controlDown = event.CmdDown()
        
        # Handle when H is pressed - Display a Hint.
        if keyCode == ord('H') and not controlDown:
            return self.hint(event)
        
        # Handle when Ctrl+Z is pressed - Undo the last move.
        elif controlDown and keyCode == ord('Z'):
            return self.undoMove()
        event.Skip()

    def statisticsHandler(self, event):
        """
        Displays a dialog with statistics for a user.
        """
        self.statsDialog = StatisticsDialog(self.player)
        self.statsDialog.set_OKhandler(self.statistics_OK)
        self.statsDialog.set_ResetHandler(self.statistics_Reset)
        self.statsDialog.CenterOnParent()
        self.statsDialog.ShowModal()
    
    def optionsHandler(self, event):
        """
        Displays options to the user, including the type of draw,
        the scoring, and whether the game is timed.
        """
        self.optionsDialog = OptionsDialog(self.player, self.options)
        self.optionsDialog.set_OKhandler(self.options_OK)
        self.optionsDialog.set_cancelHandler(self.options_cancel)
        self.optionsDialog.CenterOnParent()
        self.optionsDialog.ShowModal()
    
    def preferencesHandler(self, event):
        """
        Displays a dialog with preferences for changing
        card backs and the background image.
        """
        card_backs = self.dealer.board.getCardBacks()
        background_img = self.gui.backgroundFilename
        preferences = {'Card Back': card_backs, 'Background': background_img}
        self.prefsDialog = PreferencesDialog(prefs=preferences)
        
        self.prefsDialog.setUploadCardBackBtnHandler(self.prefsDialog.uploadCardBackHandler)
        self.prefsDialog.setUploadBackgroundBtnHandler(self.prefsDialog.uploadBackgroundHandler)
        
        self.prefsDialog.setOKButtonHandler(self.preferences_OK)
        self.prefsDialog.setCancelButtonHandler(self.preferences_CANCEL)
        
        self.prefsDialog.CenterOnParent()
        self.prefsDialog.ShowModal()

    def preferences_OK(self, event):
        """
        Changes the card backs and background image
        if the user changed the preferences.
        """
        newCardBack = self.prefsDialog.newCardBack
        newBackground = self.prefsDialog.newBackground
        self.changeCardBacks(newCardBack)
        self.changeBackgroundImage(newBackground)
        self.prefsDialog.Destroy()
    
    def preferences_CANCEL(self, event):
        """
        Closes the PreferencesDialog.
        """
        self.prefsDialog.Destroy()
    
    def changeUserHandler(self, event):
        """
        Displays a dialog to the user to change the
        current Player based on the entered username.
        """
        usrDialog = wx.TextEntryDialog(self.gui, 'What is your username?', 
                                       'Change Username', self.player.username)
        usrDialog.ShowModal()
        new_username = usrDialog.GetValue()
        self.dealer.change_player(new_username)
        self.player = self.dealer.player
        self.startNewGame()
    
    def changeCardBacks(self, filename):
        """
        Updates the card back images and refreshes the
        PilePanels.
        """
        self.dealer.board.setCardBacks(filename)
        self.gui.refreshPilePanels()
    
    def changeBackgroundImage(self, filename):
        """
        Updates the background image and refreshes
        the Frame and all Panels.
        """
        self.gui.changeBackgroundImage(filename)

    def closewindow(self, event):
        """
        Stops the timer and closes the window.
        """
        self.endGame()
        self.player.db.close_connection()
        if self.optionsDialog is not None: 
            self.optionsDialog.Destroy()
        if self.statisticsHandler is not None: 
            self.statisticsHandler.Destroy()
        self.gui.Destroy()
        self.gui.Close()
    
    def options_onDrawSelection(self, event):
        """
        Update the options for this game.
        """
        self.optionsDialog.selectedOptions['draw'] = self.optionsDialog.drawRB.GetSelection()
    
    def options_OK(self, event):
        """
        When the OK button is pressed in the Options Dialog,
        the user is prompted to begin a new game if any
        options were changed.
        """
        updatedOptions = self.optionsDialog.selectedOptions
        updatedDraw = updatedOptions['draw']
        updatedScoring = updatedOptions['scoring']
        currDraw = self.player.options['draw']
        currScoring = self.player.options['scoring']
        if updatedDraw != currDraw:
            idx = wx.MessageBox(message="Changing the type of draw requires starting a new game.\n\
            The current game will be counted as a loss.\n\
            Would you like to start a new game?", 
                                caption='New Game', 
                                style=wx.YES_NO|wx.NO_DEFAULT|wx.ICON_QUESTION, 
                                parent=None)
            if idx == wx.NO:
                self.optionsDialog.drawRB.SetSelection(self.options['draw'])
            else:
                self.player.update_options(updatedOptions)
                self.options = self.player.options
                self.endGame()
                self.startNewGame()
        if updatedScoring != currScoring:
            idx = wx.MessageBox(message="Changing the scoring requires starting a new game.\n\
            The current game will be counted as a loss.\n\
            Would you like to start a new game?", 
                                caption='New Game', 
                                style=wx.YES_NO|wx.NO_DEFAULT|wx.ICON_QUESTION, 
                                parent=None)
            if idx == wx.NO:
                self.optionsDialog.scoringRB.SetSelection(self.options['scoring'])
                
        else:
            self.optionsDialog.Destroy()
    
    def options_cancel(self, event):
        """
        When the Cancel button is pressed in the Options Dialog,
        the Dialog window is destroyed, and the changed
        options are not saved.
        """
        self.optionsDialog.Destroy()

    def statistics_OK(self, event):
        """
        Pressing the OK button on the statisticsHandler dialog simply
        closes the window.
        """
        self.statsDialog.Destroy()
    
    def statistics_Reset(self, event):
        """
        Pressing the Reset button on the Statistics dialog
        resets the statisticsHandler for the user and closes
        the window.
        """
        self.player.reset_stats()
        self.statsDialog.Destroy()
    
    def aboutwindow(self, event):
        """
        Displays information about this version of solitaire, 
        and Copyright/license information.
        """
        description = """Solitaire is a virtual card game for the Windows 
operating system. Features include comprehensive 
statisticsHandler, a beautiful graphical user interface, and 
changing appearance.
"""
        licence = """Solitaire is free software; you may\nredistribute and/or modify it."""
        info = wx.AboutDialogInfo()
        info.SetIcon(wx.Icon(ABOUT_ICON_PATH, wx.BITMAP_TYPE_PNG))
        info.SetName('Solitaire')
        info.SetVersion('1.0')
        info.SetCopyright('(C) 2013 Elizabeth Jakubowski')
        info.SetLicence(licence)
        info.AddDeveloper('Elizabeth Jakubowski')
        info.AddDocWriter('Elizabeth Jakubowski')
        info.SetDescription(description)
        wx.AboutBox(info)
    
    def howtoplaywindow(self, event):
        """
        A Dialog for how to play Solitaire and the rules.
        """
        self.howToPlay = HowToPlayDialog()
        self.howToPlay.CenterOnParent()
        self.howToPlay.Show()