Example #1
0
    def __init__ (self, surface):
        "Creates the dialog."
        # call superclass
        Widget.__init__ (self)
        
        # create the widget manager so that it manages our canvas and then paints out its stuff on
        # the root window
        self.wm = WidgetManager (surface, self)

	self.surface = surface

        # register ourselves as a paintable object to the widget manager. This should insure that we
        # get repainted too
        self.wm.register ( self )
        
        # create all widgets
        self.createWidgets ()

        # flags used for indicating what we've done
        self.state = REJECTED

        # no background
        self.background = None
        self.tile = 0
Example #2
0
    def __init__ (self):
        "Creates the dialog."

        # create a new surface matching the display
        self.surface = sdl.surface_new ( 0, properties.window_size_x, properties.window_size_y,
                                         properties.window_depth, (0,0,0,0) ).convert_display () 
        
        # load the fonts too
        self.titlefont = pygame.font.Font  ( properties.titlefont, 24 )
        self.smallfont = pygame.font.Font  ( properties.textfont, 16 )
        self.tinyfont  = pygame.font.Font  ( properties.tinyfont, 12 )


        # create the labels for the rebel and union units
#        rebellabels = self.createLabels ( organization.REBEL )
#        unionlabels = self.createLabels ( organization.UNION )

        # loop over all lables we got
#        for index in range ( len (rebellabels) ):
#            # get the current label
#            indent, labels = rebellabels[index]
#
#            # do we have one or more labels?
#            if len (labels) == 1:
#                # we have only one label, it is a highlevel organization of some kind. Get the label
#                label = labels [0]
#                
#                # blit the label
#                self.surface.blit ( label, (0, 0, label.get_width(), label.get_height()),
#                                    (50 + indent * 50, 100 + index * label.get_height(), label.get_width(),
#                                     label.get_height() ) )
#            else:
#                # it is a tuple of labels for a unit
#                name, type, men, guns = labels
#
#                # blit the labels
#                self.surface.blit ( name, (0, 0, name.get_width(), name.get_height()),
#                                    (50 + indent * 50, 100 + index * name.get_height(), name.get_width(),
#                                     name.get_height() ) )
#                self.surface.blit ( type, (0, 0, type.get_width(), type.get_height()),
#                                    (100 + indent * 50, 100 + index * type.get_height(), type.get_width(),
#                                     type.get_height() ) )
#                self.surface.blit ( men, (0, 0, men.get_width(), men.get_height()),
#                                    (150 + indent * 50, 100 + index * men.get_height(), men.get_width(),
#                                     men.get_height() ) )
#                self.surface.blit ( guns, (0, 0, guns.get_width(), guns.get_height()),
#                                    (200 + indent * 50, 100 + index * guns.get_height(), guns.get_width(),
#                                     guns.get_height() ) )
#
        # create the widget manager
        self.wm = WidgetManager (self.surface)

        # create all widgets
        self.createWidgets ()
            
                
        # blit out or full surface to the main surface
#        scenario.sdl.blit ( self.surface, (0, 0, properties.window_size_x, properties.window_size_y )
#                            (0, 0, properties.window_size_x, properties.window_size_y ) )

        # update the whole screen
        scenario.sdl.update_rect ( (0, 0, properties.window_size_x, properties.window_size_y ) )
Example #3
0
class InfoUnits:
    """
    This class is used as a dialog for showing the 'order of battle', i.e. which units participate
    in the battle from the rebel and union sides. The full organizations are shown along with all
    available stats for all units.
    """

    def __init__ (self):
        "Creates the dialog."

        # create a new surface matching the display
        self.surface = sdl.surface_new ( 0, properties.window_size_x, properties.window_size_y,
                                         properties.window_depth, (0,0,0,0) ).convert_display () 
        
        # load the fonts too
        self.titlefont = pygame.font.Font  ( properties.titlefont, 24 )
        self.smallfont = pygame.font.Font  ( properties.textfont, 16 )
        self.tinyfont  = pygame.font.Font  ( properties.tinyfont, 12 )


        # create the labels for the rebel and union units
#        rebellabels = self.createLabels ( organization.REBEL )
#        unionlabels = self.createLabels ( organization.UNION )

        # loop over all lables we got
#        for index in range ( len (rebellabels) ):
#            # get the current label
#            indent, labels = rebellabels[index]
#
#            # do we have one or more labels?
#            if len (labels) == 1:
#                # we have only one label, it is a highlevel organization of some kind. Get the label
#                label = labels [0]
#                
#                # blit the label
#                self.surface.blit ( label, (0, 0, label.get_width(), label.get_height()),
#                                    (50 + indent * 50, 100 + index * label.get_height(), label.get_width(),
#                                     label.get_height() ) )
#            else:
#                # it is a tuple of labels for a unit
#                name, type, men, guns = labels
#
#                # blit the labels
#                self.surface.blit ( name, (0, 0, name.get_width(), name.get_height()),
#                                    (50 + indent * 50, 100 + index * name.get_height(), name.get_width(),
#                                     name.get_height() ) )
#                self.surface.blit ( type, (0, 0, type.get_width(), type.get_height()),
#                                    (100 + indent * 50, 100 + index * type.get_height(), type.get_width(),
#                                     type.get_height() ) )
#                self.surface.blit ( men, (0, 0, men.get_width(), men.get_height()),
#                                    (150 + indent * 50, 100 + index * men.get_height(), men.get_width(),
#                                     men.get_height() ) )
#                self.surface.blit ( guns, (0, 0, guns.get_width(), guns.get_height()),
#                                    (200 + indent * 50, 100 + index * guns.get_height(), guns.get_width(),
#                                     guns.get_height() ) )
#
        # create the widget manager
        self.wm = WidgetManager (self.surface)

        # create all widgets
        self.createWidgets ()
            
                
        # blit out or full surface to the main surface
#        scenario.sdl.blit ( self.surface, (0, 0, properties.window_size_x, properties.window_size_y )
#                            (0, 0, properties.window_size_x, properties.window_size_y ) )

        # update the whole screen
        scenario.sdl.update_rect ( (0, 0, properties.window_size_x, properties.window_size_y ) )


    def createWidgets (self):
        "Creates all widgets for the dialog."
        # create the buttons for the next and previous buttons
        self.wm.register ( Button ( properties.path_dialogs + "butt-back-moff.png",
                                    properties.path_dialogs + "butt-back-mover.png",
                                    (850, 30 ), {widget.MOUSEBUTTONUP : self.prevScreen } ) )

        # labels
        self.wm.register ( Label (self.titlefont, "Scenario order of battle ", (10,10), color=(255, 0, 0)) )

        unionlabel = Label (self.smallfont, "Union brigades", (50, 80), color=(255, 255, 0) )
        self.wm.register ( unionlabel )

        # create the label summarizing the units of the union side
        unionlabel = self.createSummary ( organization.UNION, 200, 83 )
        self.wm.register ( unionlabel )
        
        # create labels for all union brigades and store the coordinate of the last used y-position
        lasty = self.createBrigadeLabels ( organization.UNION, 80 + unionlabel.getHeight () + 30 );

        
        rebellabel = Label (self.smallfont, "Rebel brigades", (50, lasty + 30), color=(255, 255, 0))
        self.wm.register ( rebellabel )

        # create the label summarizing the units of the rebel side
        rebellabel = self.createSummary ( organization.REBEL, 200, lasty + 30 + 3 )
        self.wm.register ( rebellabel )

        # create labels for all rebel brigades
        unionBrigades = self.createBrigadeLabels ( organization.REBEL,
                                                   lasty + 30 + rebellabel.getHeight () + 30 );


    def createBrigadeLabels (self, owner, starty):
        """Creates labels for all brigades owned by 'owner'."""
        index = starty

        # loop over all brigades owned by the correct player
        for brigade in scenario.brigades [owner].values ():

            # render a label and register it
            label =  Label (self.smallfont, brigade.getName (), (150, index),
                            color=(255, 0, 255))

            # register label so that it gets managed
            self.wm.register ( label )

            # also create a label with info about the brigade
            # increment the index
            index += label.getHeight () + 20

        # we're done, return the last index we used
        return index
    

    def createSummary (self, owner, x, y):
        "Creates a summary label for all the units of the specified owner."

        companies = []
        
        # first get all companies of all the brigades
        for brigade in scenario.brigades [ owner ].values ():
            companies = companies + brigade.getCompanies ()

        # now set some counters
        counts = { unit.INFANTRY: 0,
                   unit.CAVALRY: 0,
                   unit.ARTILLERY: 0 }
        guns = 0

        # loop over all companies
        for company in companies:
            # add the men to the proper counter
            counts [company.getType ()] += company.getMen ()
            guns += company.getGuns ()

        # now create a suitable label
        labeltext  = "infantry: " + str (counts[unit.INFANTRY]) + ", cavalry: " + str (counts[unit.CAVALRY])
        labeltext += ", artillery (men/guns): " + str (counts[unit.ARTILLERY]) + "/" + str (guns)

        # create and return the label
        return Label ( self.tinyfont, labeltext, (x,y), color=(255, 255, 255))
        

    def run (self):
        """Executes the dialog and runs its internal loop until a key is pressed. When that happens
        this dialog is terminated and the method returns."""

        # loop forever
        while 1:
            # get next event
            event = sdl.events_wait ()

            # see wether the widget manager wants to handle it
            if event != -1:
                # handle event and get the return code that tells us wehter it was handled or not
                returncode = self.wm.handle ( event )

                # is the event loop done?
                if returncode == widget.DONE:
                    return


    def prevScreen (self, event):
        """Callback triggered when the user clicks the 'Previous' button. This method simply cancels
        the event loop for this dialog's widget manager. Basically this dialog quits."""
        # we're done
        return widget.DONE
        # loop forever
        while 1:
            # get next event
            event = sdl.events_wait ()

            # see wether the widegt manager wants to handle it
            if event != -1 and self.wm.handle ( event ):
                # it's handled, nothing to see here folks
                continue

            # did we gert anything?
            if event != -1 and event.get_type () == sdl.KEYDOWN:
                # we're done here, go away
                return
Example #4
0
from tkinter import *
from window_manager import Window
from widget_manager import WidgetManager

# Define window
root = Tk()
root.resizable(False, False)
root.geometry('800x350')
# Initialise ui definitions class
wind = Window(root)
clr_mgr = WidgetManager(root)
# Run application
root.mainloop()
Example #5
0
    def __init__(self, screen):
        self.sel = selectors.DefaultSelector()
        self.screen = screen

        self.is_running = False

        #curses.raw() # disable special keys and stuff (such as ctrl-c)
        self.screen.nodelay(True)  # disable blocking on getch()
        curses.curs_set(False)  # hide cursor
        curses.mousemask(curses.ALL_MOUSE_EVENTS)  # enable mouse interaction
        curses.nonl()  # don't translate KEY_RETURN into '\r\n'

        self.screen.clear()

        # create server for remote control
        self.mi = ManagementInterface(1234, self.sel)
        log.info("local address: {}".format(self.mi.get_local_addresses()))

        self.mi.register_handler('quit', self.exit_app_by_packet)
        self.mi.register_handler('reset', lambda _: self.reset())
        self.mi.register_handler('show_popup', self.show_popup_from_packet)
        self.mi.register_handler('ping', lambda _: None)  # dummy command

        self.widget_mgr = WidgetManager(self)

        # register key handler
        self.sel.register(sys.stdin, selectors.EVENT_READ, self.handle_input)

        with open('puzzle.cfg', 'r') as puzzle_cfg:
            cfg = json.load(puzzle_cfg)
            log.info("using configuration: {}".format(cfg))
            h, w = screen.getmaxyx()
            self.puzzle = Crossword(self, Vector(10, 1), Vector(w - 10, h - 2),
                                    cfg)
            self.widget_mgr.add(self.puzzle)
            self.widget_mgr.focus = self.puzzle

        self.puzzle.resize_to_contents()

        # center in middle of screen (also take progress bar into account)
        ps = self.puzzle.size()
        self.puzzle.move(Vector(int((w - ps.x) / 2), int((h - ps.y + 4) / 2)))

        self.progress_bar = ProgressBar(self,
                                        self.puzzle.pos() - Vector(0, 4),
                                        Vector(self.puzzle.size().x, 4),
                                        'Lösungsfortschritt:')
        self.puzzle.progress_bar = self.progress_bar
        self.puzzle.solved_callback = self.on_crossword_solved

        self.door_panel = DoorPanel(self)
        self.door_panel.center_in(self.screen)
        self.widget_mgr.add(self.door_panel)
        self.door_panel.visible = False
        #self.widget_mgr.focus = self.door_panel

        self.shooting_range = ShootingRange(self)
        self.widget_mgr.add(self.shooting_range)
        self.sel.register(self.shooting_range.target.shots_queue_available,
                          selectors.EVENT_READ, self.handle_shot)
        self.widget_mgr.focus = self.shooting_range
        """
Example #6
0
class Application:
    def __init__(self, screen):
        self.sel = selectors.DefaultSelector()
        self.screen = screen

        self.is_running = False

        #curses.raw() # disable special keys and stuff (such as ctrl-c)
        self.screen.nodelay(True)  # disable blocking on getch()
        curses.curs_set(False)  # hide cursor
        curses.mousemask(curses.ALL_MOUSE_EVENTS)  # enable mouse interaction
        curses.nonl()  # don't translate KEY_RETURN into '\r\n'

        self.screen.clear()

        # create server for remote control
        self.mi = ManagementInterface(1234, self.sel)
        log.info("local address: {}".format(self.mi.get_local_addresses()))

        self.mi.register_handler('quit', self.exit_app_by_packet)
        self.mi.register_handler('reset', lambda _: self.reset())
        self.mi.register_handler('show_popup', self.show_popup_from_packet)
        self.mi.register_handler('ping', lambda _: None)  # dummy command

        self.widget_mgr = WidgetManager(self)

        # register key handler
        self.sel.register(sys.stdin, selectors.EVENT_READ, self.handle_input)

        with open('puzzle.cfg', 'r') as puzzle_cfg:
            cfg = json.load(puzzle_cfg)
            log.info("using configuration: {}".format(cfg))
            h, w = screen.getmaxyx()
            self.puzzle = Crossword(self, Vector(10, 1), Vector(w - 10, h - 2),
                                    cfg)
            self.widget_mgr.add(self.puzzle)
            self.widget_mgr.focus = self.puzzle

        self.puzzle.resize_to_contents()

        # center in middle of screen (also take progress bar into account)
        ps = self.puzzle.size()
        self.puzzle.move(Vector(int((w - ps.x) / 2), int((h - ps.y + 4) / 2)))

        self.progress_bar = ProgressBar(self,
                                        self.puzzle.pos() - Vector(0, 4),
                                        Vector(self.puzzle.size().x, 4),
                                        'Lösungsfortschritt:')
        self.puzzle.progress_bar = self.progress_bar
        self.puzzle.solved_callback = self.on_crossword_solved

        self.door_panel = DoorPanel(self)
        self.door_panel.center_in(self.screen)
        self.widget_mgr.add(self.door_panel)
        self.door_panel.visible = False
        #self.widget_mgr.focus = self.door_panel

        self.shooting_range = ShootingRange(self)
        self.widget_mgr.add(self.shooting_range)
        self.sel.register(self.shooting_range.target.shots_queue_available,
                          selectors.EVENT_READ, self.handle_shot)
        self.widget_mgr.focus = self.shooting_range
        """
        self.widget_mgr.show_popup('Dies ist der Titel',
                "asdfa sfasdfdsaf;dsa kfsa;dkfjdsa;if jsa;ifjsa dfijdsfoisdhaf " +'%'*40+ " uhsaif usahd end of first line\nsecond line here\nand a third " + "#"*250,
                lambda b: log.info('selected {}'.format(b)), ['foo', 'bar', 'baz'])
                """

    def __del__(self):
        self.exit()

    def handle_input(self, stdin):
        k = self.screen.getch()
        if k >= 0:
            if k == curses.KEY_F1 or k == curses.KEY_F2:
                self.widget_mgr.show_single_popup(
                    'Hilfe',
                    'TODO: Hier sollte wohl etwas Hilfe zum Puzzle (bzw. einfach zur Bedienung) hinkommen.'
                )
            elif k == curses.KEY_F12:
                self.show_about()
            elif k == curses.KEY_F11 or k == curses.KEY_F9:
                self.show_admin_screen()
            else:
                if not self.widget_mgr.handle_input(k):
                    log.info("unhandled key '{}'".format(k))

    def handle_shot(self, _):
        self.shooting_range.target.shots_queue_available.clear()
        while not self.shooting_range.target.shots_queue.empty():
            self.shooting_range.handle_shot(
                self.shooting_range.target.shots_queue.get())

    def show_about(self):
        self.widget_mgr.show_single_popup(
            'Kreuzworträtsel', """Geschrieben von Samuel Bryner

Diese Software ist frei verfügbar unter der GPL. Quellcode unter
https://github.com/iliis/crossword
""")

    def show_admin_screen(self):
        self.widget_mgr.show_single_popup(
            'Admin',
            'Serial Port: {}\n\n'.format(self.shooting_range.target.ser.name) +
            'Local Address:\n{}\n'.format('\n'.join(
                ' - {}'.format(a) for a in self.mi.get_local_addresses())) +
            'Local Port: {}\n'.format(self.mi.port) +
            'Remote Control Connections:\n{}\n'.format('\n'.join(
                ' - {}'.format(c.getpeername()) for c in self.mi.connections)),
            callback=self._admin_screen_cb,
            buttons=['CLOSE', 'AUTOFILL', 'RESET ALL', 'EXIT APP'])

    def _admin_screen_cb(self, button):
        if button == 'EXIT APP':
            log.info("Exiting application through admin panel.")
            self.exit()
        elif button == 'RESET ALL':
            self.reset()
        elif button == 'AUTOFILL':
            self.puzzle.autofill()

    def exit_app_by_packet(self, packet):
        log.info("Exiting application trough remote command.")
        self.exit()

    def exit(self):
        self.is_running = False
        self.shooting_range.target.is_running = False

    def show_popup_from_packet(self, packet):
        if not 'title' in packet or not 'text' in packet:
            raise ValueError(
                "Invalid command: missing 'title' or 'text' field.")

        if 'buttons' in packet:
            buttons = packet['buttons']
        else:
            buttons = ['OK']

        self.widget_mgr.show_single_popup(packet['title'],
                                          packet['text'],
                                          buttons=buttons)

    def on_crossword_solved(self, _):
        self.puzzle.visible = False
        self.door_panel.visible = True
        self.widget_mgr.focus = self.door_panel

    def reset(self):
        log.info("Resetting application!")
        self.screen.clear()
        self.puzzle.reset()

        self.door_panel.reset()
        self.door_panel.visible = False

        self.widget_mgr.focus = self.puzzle

    def run(self):
        #ser.write(b'crossword running')
        self.is_running = True

        while self.is_running:
            self.widget_mgr.render()
            events = self.sel.select()

            for key, mask in events:
                callback = key.data
                callback(key.fileobj)
Example #7
0
class Dialog(Widget):
    """
    This class is used as a baseclass for dialogs. A dialog is a window with widgets that occupies
    the whole or part of the screen. Currently only whole screen dialogs are supported. This class
    contains the main widget manager to which widgets are added. Subclasses could override the
    method createWidgets() to create their own widgets, but that is not mandatory.

    It also contains the main event loop which is started using the method run(). The event loop
    handles events from all registered widgets and calls their callbacks.

    A dialog can have a background. It is set using setBackground() and can be either painted once
    or tiled over the entire screen.
    """

    # define a shared cache
    cache = {}

    def __init__ (self, surface):
        "Creates the dialog."
        # call superclass
        Widget.__init__ (self)
        
        # create the widget manager so that it manages our canvas and then paints out its stuff on
        # the root window
        self.wm = WidgetManager (surface, self)

	self.surface = surface

        # register ourselves as a paintable object to the widget manager. This should insure that we
        # get repainted too
        self.wm.register ( self )
        
        # create all widgets
        self.createWidgets ()

        # flags used for indicating what we've done
        self.state = REJECTED

        # no background
        self.background = None
        self.tile = 0

        
    def createWidgets (self):
        "Creates all widgets for the dialog. Override in subclasses."
        pass


    def setBackground (self, image, tile=0):
        """Sets a background for the entire dialog. The background is loaded from the passed
        'filename'. It is painted either only once or tiled, depending on the setting for 'tile'. A
        value of 1 will tile the image, any other value will draw the background once centered."""

        self.background = image 
            
        # store the tiling value
        self.tile = tile

        # we're dirty now
        self.dirty = 1
        

    def getWidth (self):
        "Returns the width of the dialog."
        return 0


    def getHeight (self):
        "Returns the height of the widget."
        return 0


    def getGeometry (self):
        """Returns the geometry of the widget. This is a tuple containing the x, y, width and height
        of the dialog. Not currently meaningful."""
        return ( self.position [0], self.position [1], 0, 0 )


    def isInside (self,position):
        """Checks wether the passed point is inside the dialog. Returns 1 if inside and 0 if
        outside. A point on the border of the widget is considered to be inside. Currently always
        returns 0."""
        # not inside
        return 0


    def paint (self, destination, force=0):
        """Method that paints the dialog. This method will simply blit out the background if one has
        been set. Override if custom painting is needed."""
        # are we dirty or not?
        if self.background == None or ( self.dirty == 0 and force == 0 ):
            # not dirty, or no background nothing to do here
            return 0

        # get the dimensions of the background
        width, height = self.background.get_size () 
        
        # should we tile or not?
        if self.tile:
            # perform tiling of the background
            for y in range ( self.surface.get_height() / height + 1 ):
                for x in range ( self.surface.get_width() / width + 1 ):
                    # what height should be used?
                    if y * height > self.surface.get_height:
                        # use only the missing part
                        heighty = (( y + 1 ) * height) - self.surface.get_height()

                    else:
                        # full height of icon
                        heighty = height

                    # what width should be used?
                    if x * width > self.surface.get_width():
                        # use only the missing part
                        widthx = (( x + 1 ) * width) - self.surface.get_width()

                    else:
                        # full width of icon
                        widthx = width

                        # blit it all out
                        destination.blit ( self.background, (x * width, y * height ) )
                        
        else:
            # no tiling, just blurt it out once
            destination.blit ( self.background, self.position )

        self.dirty = 0
        
        # we did something, make sure the widget manager knows that
        return 1



    def run (self):
        """Executes the dialog and runs its internal loop until a callback returns widget.DONE. When
        that dialog is terminated this method also returns.""" 

        # loop forever
        while 1:
            # repaint the stuff if needed
            self.wm.paint ()

            # get next event
            event = pygame.event.wait()

            # see wether the widget manager wants to handle it
            if event != -1:
                # handle event and get the return code that tells us wehter it was handled or not
                returncode = self.wm.handle ( event )

                # is the event loop done?
                if returncode == widget.DONE:
                    # disable the timer
                    self.disableTimer ()

                    return self.state

    def handleEvent(self, event):
		# repaint the stuff if needed
 		self.wm.paint ()

		# see wether the widget manager wants to handle it
		if event != -1:
                	# handle event and get the return code that tells us wehter it was handled or not
                	returncode = self.wm.handle ( event )

			# is the event loop done?
			if returncode == widget.DONE:
				# disable the timer
				self.disableTimer ()

				return self.state
			else:
				return -1
		else:
			return -1


    def accept (self):
        """Accepts the dialog. Will close it and return from it's event loop with the return status
        'dialog.ACCEPTED'."""
        # we're accepting the dialog
        self.state = ACCEPTED

        return widget.DONE


    def reject (self, trigger, event):
        """Accepts the dialog. Will close it and return from it's event loop with the return status
        'dialog.REJECTED'."""
        # we're cancelling the dialog
        self.state = REJECTED
        
        return widget.DONE


    def messagebox (self, message):
        
        # failed to init network connection, show a messagebox
        messagebox.Messagebox ( message )

        # repaint the stuff if needed
        self.wm.paint (force=1, clear=1)


    def enableTimer (self, ms):
        """Enables timer events. Calling this method will make the method timer() get called every
        'ms' milliseconds. Call disableTimer() to disable the timer."""
        # just call the method, make no checks
        pygame.time.set_timer ( USEREVENT, ms )
            

    def disableTimer (self):
        """Disables timer events."""
        # just call the method, make no checks
        pygame.time.set_timer ( USEREVENT, 0 )

        # remove all old stale events that may have been left in the queue
        pygame.event.get ( [USEREVENT] )
   

    def timer (self):
        """Callback triggered when the dialog has enabled timers and a timer fires. This should be
        overridden by subclasses to provide the needed code."""
        # by default we're handled
        return widget.HANDLED
Example #8
0
    def __init__(self, screen):
        self.sel = selectors.DefaultSelector()
        self.screen = screen

        self.is_running = False

        # optionally pass different configuration file (useful for debugging)
        if len(sys.argv) > 1:
            app_cfg_path = sys.argv[1]
        else:
            app_cfg_path = os.path.join(
                os.path.dirname(os.path.realpath(__file__)), 'app.cfg')

        with open(app_cfg_path, 'r') as app_cfg:
            self.cfg = json.load(app_cfg)
            log.info("using application configuration: {}".format(self.cfg))

            if not self.cfg['cheats']:
                # ignore CTRL+Z/V keybinding
                log.info("disabling SIGINT")
                signal.signal(signal.SIGINT, signal.SIG_IGN)

        #curses.raw() # disable special keys and stuff (such as ctrl-c)
        self.screen.nodelay(True)  # disable blocking on getch()
        curses.curs_set(False)  # hide cursor
        curses.mousemask(curses.ALL_MOUSE_EVENTS)  # enable mouse interaction
        curses.nonl()  # don't translate KEY_RETURN into '\r\n'

        self.screen.clear()

        # make sure display is big enough
        H, W = self.screen.getmaxyx()
        log.info("Screen Size: {} cols by {} rows".format(W, H))

        if W < 102 or H < 39:
            raise Exception(
                "Screen is too small: {} rows by {} columns, but we need at least 39 by 102.\n"
                .format(H, W) +
                "Try a smaller font size or increasing the resolution of your terminal emulator."
            )

        # create server for remote control
        self.mi = ManagementInterface(1234, self.sel)
        log.info("local address: {}".format(self.mi.get_local_addresses()))

        self.mi.register_handler('quit', self.exit_app_by_packet)
        self.mi.register_handler('shutdown', self.shutdown)
        self.mi.register_handler('reset', lambda _: self.reset())
        self.mi.register_handler('show_popup', self.show_popup_from_packet)
        self.mi.register_handler('ping', lambda _: None)  # dummy command
        self.mi.register_handler('set_time',
                                 lambda p: self.set_timeout(p['timeout']))
        self.mi.register_handler('show_shooting_range',
                                 lambda _: self.show_shooting_range())
        self.mi.register_handler(
            'get_time',
            lambda p: self.mi.reply_success(p, self.remaining_time()))
        self.mi.register_handler('restore_saved_state',
                                 self.restore_backup_by_packet)
        self.mi.register_handler('memory_dump', lambda p: self.memory_dump())
        self.mi.register_handler('wakeup', lambda p: self.wakeup_screen())

        self.mi.new_connection_handler = self.new_connection_handler

        self.widget_mgr = WidgetManager(self)

        self.TIMEOUT = self.cfg["game_timeout"]  #60*60
        self.timeout_timer = WaitableTimer(self.sel, self.TIMEOUT,
                                           self.on_timeout)
        self.set_timeout(self.TIMEOUT)

        # register key handler
        self.sel.register(sys.stdin, selectors.EVENT_READ, self.handle_input)

        puzzle_filename = os.path.join(
            os.path.dirname(os.path.realpath(__file__)), 'puzzle.cfg')
        with open(puzzle_filename, 'r', encoding="utf-8") as puzzle_cfg:
            pcfg = json.load(puzzle_cfg)
            log.info("using puzzle configuration: {}".format(pcfg))
            h, w = screen.getmaxyx()
            self.puzzle = Crossword(self, Vector(10, 1), Vector(w - 10, h - 2),
                                    pcfg)
            self.widget_mgr.show(self.puzzle)

        self.puzzle.resize_to_contents()
        H, W = self.puzzle.screen.getmaxyx()
        log.info("Puzzle Size: {} x {}".format(W, H))

        # center in middle of screen (also take progress bar into account)
        ps = self.puzzle.size()
        try:
            self.puzzle.move(
                Vector(int((w - ps.x) / 2), int((h - ps.y + 6) / 2)))
        except Exception as e:
            raise Exception(
                "Failed to move curses window. This can happen if your screen is too small!\nMaybe you forgot to switch to fullscreen?"
            )

        self.progress_bar = ProgressBar(self,
                                        self.puzzle.pos() - Vector(0, 5),
                                        Vector(self.puzzle.size().x, 5),
                                        'Lösungsfortschritt:')
        self.puzzle.progress_bar = self.progress_bar
        self.puzzle.solved_callback = self.on_crossword_solved

        self.door_panel = DoorPanel(self)
        self.door_panel.center_in(self.screen)
        self.door_panel.visible = False
        #self.widget_mgr.add(self.door_panel)

        self.shooting_range = ShootingRange(self)
        self.shooting_range.first_shot_callback = self.show_shooting_range
        self.shooting_range.closed_callback = self.on_shooting_range_closed

        if self.shooting_range.target is None:
            self.widget_mgr.show_popup(
                "Hallo", "Konnte keine Verbindung zum Reddot-Target aufbauen. "
                "Schiessstand ist deaktiviert.")
        else:
            self.sel.register(self.shooting_range.target.shots_queue_available,
                              selectors.EVENT_READ, self.handle_shot)
            self.sel.register(self.shooting_range.target.has_raised_exception,
                              selectors.EVENT_READ,
                              self.handle_exception_in_reddot_target)

        self.final_screen = FinalScreen(self)

        self.check_for_backup()

        self.periodic_backup_timer = WaitableTimer(self.sel, 10,
                                                   self.on_periodic_backup)
        self.periodic_backup_timer.start()
Example #9
0
class Application:
    def __init__(self, screen):
        self.sel = selectors.DefaultSelector()
        self.screen = screen

        self.is_running = False

        # optionally pass different configuration file (useful for debugging)
        if len(sys.argv) > 1:
            app_cfg_path = sys.argv[1]
        else:
            app_cfg_path = os.path.join(
                os.path.dirname(os.path.realpath(__file__)), 'app.cfg')

        with open(app_cfg_path, 'r') as app_cfg:
            self.cfg = json.load(app_cfg)
            log.info("using application configuration: {}".format(self.cfg))

            if not self.cfg['cheats']:
                # ignore CTRL+Z/V keybinding
                log.info("disabling SIGINT")
                signal.signal(signal.SIGINT, signal.SIG_IGN)

        #curses.raw() # disable special keys and stuff (such as ctrl-c)
        self.screen.nodelay(True)  # disable blocking on getch()
        curses.curs_set(False)  # hide cursor
        curses.mousemask(curses.ALL_MOUSE_EVENTS)  # enable mouse interaction
        curses.nonl()  # don't translate KEY_RETURN into '\r\n'

        self.screen.clear()

        # make sure display is big enough
        H, W = self.screen.getmaxyx()
        log.info("Screen Size: {} cols by {} rows".format(W, H))

        if W < 102 or H < 39:
            raise Exception(
                "Screen is too small: {} rows by {} columns, but we need at least 39 by 102.\n"
                .format(H, W) +
                "Try a smaller font size or increasing the resolution of your terminal emulator."
            )

        # create server for remote control
        self.mi = ManagementInterface(1234, self.sel)
        log.info("local address: {}".format(self.mi.get_local_addresses()))

        self.mi.register_handler('quit', self.exit_app_by_packet)
        self.mi.register_handler('shutdown', self.shutdown)
        self.mi.register_handler('reset', lambda _: self.reset())
        self.mi.register_handler('show_popup', self.show_popup_from_packet)
        self.mi.register_handler('ping', lambda _: None)  # dummy command
        self.mi.register_handler('set_time',
                                 lambda p: self.set_timeout(p['timeout']))
        self.mi.register_handler('show_shooting_range',
                                 lambda _: self.show_shooting_range())
        self.mi.register_handler(
            'get_time',
            lambda p: self.mi.reply_success(p, self.remaining_time()))
        self.mi.register_handler('restore_saved_state',
                                 self.restore_backup_by_packet)
        self.mi.register_handler('memory_dump', lambda p: self.memory_dump())
        self.mi.register_handler('wakeup', lambda p: self.wakeup_screen())

        self.mi.new_connection_handler = self.new_connection_handler

        self.widget_mgr = WidgetManager(self)

        self.TIMEOUT = self.cfg["game_timeout"]  #60*60
        self.timeout_timer = WaitableTimer(self.sel, self.TIMEOUT,
                                           self.on_timeout)
        self.set_timeout(self.TIMEOUT)

        # register key handler
        self.sel.register(sys.stdin, selectors.EVENT_READ, self.handle_input)

        puzzle_filename = os.path.join(
            os.path.dirname(os.path.realpath(__file__)), 'puzzle.cfg')
        with open(puzzle_filename, 'r', encoding="utf-8") as puzzle_cfg:
            pcfg = json.load(puzzle_cfg)
            log.info("using puzzle configuration: {}".format(pcfg))
            h, w = screen.getmaxyx()
            self.puzzle = Crossword(self, Vector(10, 1), Vector(w - 10, h - 2),
                                    pcfg)
            self.widget_mgr.show(self.puzzle)

        self.puzzle.resize_to_contents()
        H, W = self.puzzle.screen.getmaxyx()
        log.info("Puzzle Size: {} x {}".format(W, H))

        # center in middle of screen (also take progress bar into account)
        ps = self.puzzle.size()
        try:
            self.puzzle.move(
                Vector(int((w - ps.x) / 2), int((h - ps.y + 6) / 2)))
        except Exception as e:
            raise Exception(
                "Failed to move curses window. This can happen if your screen is too small!\nMaybe you forgot to switch to fullscreen?"
            )

        self.progress_bar = ProgressBar(self,
                                        self.puzzle.pos() - Vector(0, 5),
                                        Vector(self.puzzle.size().x, 5),
                                        'Lösungsfortschritt:')
        self.puzzle.progress_bar = self.progress_bar
        self.puzzle.solved_callback = self.on_crossword_solved

        self.door_panel = DoorPanel(self)
        self.door_panel.center_in(self.screen)
        self.door_panel.visible = False
        #self.widget_mgr.add(self.door_panel)

        self.shooting_range = ShootingRange(self)
        self.shooting_range.first_shot_callback = self.show_shooting_range
        self.shooting_range.closed_callback = self.on_shooting_range_closed

        if self.shooting_range.target is None:
            self.widget_mgr.show_popup(
                "Hallo", "Konnte keine Verbindung zum Reddot-Target aufbauen. "
                "Schiessstand ist deaktiviert.")
        else:
            self.sel.register(self.shooting_range.target.shots_queue_available,
                              selectors.EVENT_READ, self.handle_shot)
            self.sel.register(self.shooting_range.target.has_raised_exception,
                              selectors.EVENT_READ,
                              self.handle_exception_in_reddot_target)

        self.final_screen = FinalScreen(self)

        self.check_for_backup()

        self.periodic_backup_timer = WaitableTimer(self.sel, 10,
                                                   self.on_periodic_backup)
        self.periodic_backup_timer.start()

    def __del__(
        self
    ):  # TODO: this should be implemented using a context manager ('with')
        self.mi.delete()
        del self.mi
        self.exit()

    def set_timeout(self, seconds):
        if seconds <= 0:
            log.warn(
                "set timeout called with invalid timeout '{}', setting timeout to one second."
                .format(seconds))
            seconds = 1  # netagive or zero timeouts are not allowed
        self.TIMEOUT = seconds
        self.time_ends = time.time() + self.TIMEOUT
        self.timeout_timer.reset(
            self.TIMEOUT
        )  # stop any running timer (if any) and set new timeout
        self.timeout_timer.start()

    def on_timeout(self):
        log.info("main application timeout!")
        self.mi.send_packet({
            'event': 'main_timeout',
        })

        self.widget_mgr.remove(self.puzzle)
        self.widget_mgr.remove(self.shooting_range)
        self.widget_mgr.remove(self.door_panel)
        self.widget_mgr.show(self.final_screen)

        self.screen.clear()
        self.screen.refresh()
        self.widget_mgr.show_popup(
            "Zeit Abgelaufen",
            "Ihre Zeit ist leider rum. Bitte begeben Sie sich zum Ausgang.\nFreundlichst, Ihre Spielleitung"
        )
        self.widget_mgr.render()

        self.clear_backup()

    def show_static_crossword(self):
        log.info("showing crossword again")
        self.widget_mgr.remove_all()
        self.widget_mgr.show(self.puzzle)

    def handle_input(self, stdin):
        k = self.screen.get_wch()
        if k == curses.KEY_F1:
            self.show_help()
        elif k == curses.KEY_F12:
            self.show_about()
        elif k == curses.KEY_F9:
            if self.cfg["cheats"]:
                self.show_admin_screen()
            else:
                self.widget_mgr.show_input("Management", "Passwort:",
                                           self.show_admin_screen, True)
        else:
            if not self.widget_mgr.handle_input(k):
                log.info("unhandled key '{}'".format(k))

    def handle_shot(self, _):
        self.shooting_range.target.shots_queue_available.clear()
        while not self.shooting_range.target.shots_queue.empty():
            self.shooting_range.handle_shot(
                self.shooting_range.target.shots_queue.get())

    def show_about(self):
        self.widget_mgr.show_popup(
            'Kreuzworträtsel', """Geschrieben von Samuel Bryner:

Diese Software ist frei verfügbar unter der GPL. Quellcode unter
https://github.com/iliis/crossword
""")

    def show_admin_screen(self, pw=None):
        if pw == self.cfg["admin_pw"] or self.cfg["cheats"]:

            ser_port = "not connected"
            if self.shooting_range.target is not None:
                ser_port = self.shooting_range.target.ser.name

            self.widget_mgr.show_popup(
                'Admin',
                'Version {}\nLast Update: {}\n\n'.format(
                    get_version(), get_version_date()) +
                'Serial Port: {}\n\n'.format(ser_port) +
                'Local Address:\n{}\n'.format('\n'.join(
                    ' - {}'.format(a)
                    for a in self.mi.get_local_addresses())) +
                'Local Port: {}\n\n'.format(self.mi.port) +
                'Remote Control Connections:\n{}\n'.format('\n'.join(
                    ' - {}'.format(c.getpeername())
                    for c in self.mi.connections)),
                callback=self._admin_screen_cb,
                buttons=[
                    'CLOSE', 'LOAD BACKUP', 'AUTOFILL', 'SHOW SRANGE',
                    'RESET ALL', 'EXIT APP'
                ])
        else:
            self.widget_mgr.show_popup(
                'Passwort Falsch',
                'Die Management-Konsole ist nur für die Spielleitung gedacht, sorry.'
            )

    def _admin_screen_cb(self, button):
        if button == 'EXIT APP':
            log.info("Exiting application through admin panel.")
            self.exit()
        elif button == 'RESET ALL':
            self.reset()
        elif button == 'AUTOFILL':
            self.puzzle.autofill()
        elif button == 'SHOW SRANGE':
            self.show_shooting_range()
        elif button == 'LOAD BACKUP':
            if not self.restore_backup():
                self.widget_mgr.show_popup(
                    'Backup wiederhestellen fehlgeschlagen',
                    'Konnte kein Backup laden. Kontrollier das log für weitere Fehler. Gibt es überhaupt ein Backup?'
                )
        elif button == 'CLOSE':
            pass  # just ignore
        else:
            raise ValueError("Unknown button pressed in admin panel: '" +
                             str(button) + "'.")

    def exit_app_by_packet(self, packet):
        log.info("Exiting application trough remote command.")
        self.exit()

    def exit(self):
        self.is_running = False
        # if something fails, shooting_range is not yet created, thus we
        # trigger another exception when trying to set this variable. To
        # protect against this, let's check if the member variable exists
        # before accessing it
        if hasattr(
                self,
                'shooting_range') and self.shooting_range.target is not None:
            self.shooting_range.target.is_running = False

        self.backup_state()
        self.mi.close()
        self.mi = None

    def show_popup_from_packet(self, packet):
        if not 'title' in packet or not 'text' in packet:
            raise ValueError(
                "Invalid command: missing 'title' or 'text' field.")

        if 'buttons' in packet:
            buttons = packet['buttons']
        else:
            buttons = ['OK']

        self.widget_mgr.show_popup(packet['title'],
                                   packet['text'],
                                   buttons=buttons)

    def on_crossword_solved(self, _):
        self.widget_mgr.remove(self.puzzle)
        self.widget_mgr.show(self.door_panel)
        self.backup_state()

    def show_shooting_range(self):
        if self.shooting_range.target is not None:
            if self.shooting_range.state == ShootingRangeState.NOT_WORKING:
                raise Exception(
                    "Cannot start shooting range: Target is not working.")
            else:
                self.widget_mgr.show(self.shooting_range)

    def on_shooting_range_closed(self, _):
        self.widget_mgr.remove(self.shooting_range)

        # convert bonus point to time
        self.set_timeout(self.remaining_time_in_seconds() +
                         self.shooting_range.points_to_bonus_time())

        self.mi.send_packet({
            'event':
            'shooting_range_timeout',
            'bonus_time':
            self.shooting_range.points_to_bonus_time(),
            'total_points':
            self.shooting_range.total_points(),
            'remaining_time':
            self.remaining_time_in_seconds()
        })
        self.backup_state()

    def handle_exception_in_reddot_target(self, _):
        self.shooting_range.target.has_raised_exception.clear()
        exc, info = self.shooting_range.target.cached_exception
        raise exc from None

    def remaining_time_in_seconds(self):
        return max(math.ceil(self.time_ends - time.time()), 0)

    def remaining_time(self):
        return time_format(self.remaining_time_in_seconds())

    def shutdown(self, packet):
        log.info("Shutting down PC!!")
        subprocess.call(["sudo", "halt"])

    def show_help(self):
        self.widget_mgr.show_popup(
            'Hilfe',
            'Für jeden Verbrecher gibt es ein Rätsel. Löse die Rätsel und trage die Lösungsworte hier im Kreuzworträtsel ein um auszubrechen. '
            'Pfeiltasten auf der Tastatur benutzen zum navigieren. Sobald alles korrekt ausgefüllt ist erscheint die Türsteuerung. '
            'Bei Fragen oder wenn Ihr einen Tipp braucht einfach mit dem Telefon anrufen: Kurbeln und Höhrer ans Ohr halten.'
        )

    def reset(self):
        log.info("Resetting application!")
        self.screen.clear()

        self.puzzle.reset()
        self.door_panel.reset()
        self.shooting_range.reset()

        self.widget_mgr.remove_all()
        self.TIMEOUT = self.cfg["game_timeout"]
        self.set_timeout(self.TIMEOUT)

        self.widget_mgr.show(self.puzzle)

    def run(self):
        #ser.write(b'crossword running')
        self.is_running = True

        while self.is_running:
            self.widget_mgr.render()
            events = self.sel.select()

            for key, mask in events:
                callback = key.data
                callback(key.fileobj)

    def on_periodic_backup(self):
        self.backup_state()
        self.periodic_backup_timer.reset()
        self.periodic_backup_timer.start()

    def backup_state(self):
        state = {
            # Q: store this as an absolute timestamp, so time continues?
            # A: No, don't count time it took to restart app, that's not the players fault
            'time_remaining': self.remaining_time_in_seconds(),
            # TODO: puzzle state backup/restore should be handled in Crossword class itself
            'puzzle_input':
            [''.join(line) for line in self.puzzle.puzzle_input],
            'puzzle_solved': self.door_panel.visible
        }

        with open('state_backup.cfg', 'w') as state_file:
            json.dump(state, state_file)
            log.info("wrote state to backup file: {}".format(state))

    def clear_backup(self):
        if os.path.exists("state_backup.cfg"):
            log.info("deleting state backup")
            os.remove("state_backup.cfg")

    def load_backup(self):
        if not os.path.isfile('state_backup.cfg'):
            log.error("cannot restore state from backup: file does not exist")
            return None

        try:
            with open('state_backup.cfg', 'r') as state_file:
                return json.load(state_file)
        except:
            return None

    def restore_backup_by_packet(self, packet):
        if self.restore_backup():
            return self.mi.reply_success(packet)
        else:
            return self.mi.reply_failure(
                packet, "Could not restore state. Maybe there is no backup?")

    def restore_backup(self):
        state = self.load_backup()
        if state is None:
            return False

        # don't restore a completed game
        if state["time_remaining"] <= 0:
            return False

        self.set_timeout(state["time_remaining"])

        if state["puzzle_solved"]:
            self.widget_mgr.remove(self.puzzle)
            self.widget_mgr.show(self.door_panel)
            self.widget_mgr.render(clear=True)
        else:
            # TODO: puzzle state backup/restore should be handled in Crossword class itself
            self.puzzle.puzzle_input = [[c for c in line]
                                        for line in state["puzzle_input"]]
            self.puzzle.notify_state_update('restore')

        log.warning("Restored state from backup. Remaining time: {}".format(
            self.remaining_time_in_seconds()))
        self.mi.send_packet({
            'event': 'backup_restored',
            'remaining_time': self.remaining_time_in_seconds(),
        })
        return True

    def is_backup_different(self):
        """
        Check if there is anything in the backup to be interesting.
        """
        if not os.path.exists('state_backup.cfg'):
            return False  # no backup -> nothing interesting

        backup = self.load_backup()
        if not backup:
            log.error(
                "Failed to load backup even though file exists?! This should not happen."
            )
            return False

        if backup["time_remaining"] <= 0:
            return False

        if backup["puzzle_solved"]:
            return True

        # check crossword puzzle
        for line_backup, line_puzzle in zip(backup["puzzle_input"],
                                            self.puzzle.puzzle_input):
            if [c for c in line_backup] != line_puzzle:
                return True

        # nothing to restore from backup
        return False

    def check_for_backup(self):
        # check if backup contains anything interesting
        if self.is_backup_different():
            # if so, ask if you want to restore it
            self.widget_mgr.show_popup(
                'Wiederherstellen?',
                'Offenbar wurde das Spiel unterbrochen und nicht korrekt zu Ende gespielt.\n'
                'Dies sollte nicht passieren, bitte melden Sie diesen Vorfall der Spielleitung.\n\n'
                'Möchten Sie Ihren den vorherigen Zustand wieder rekonstruieren? Andernfalls müssen Sie mit dem Kreuzworträtsel von vorne beginnen.',
                callback=self._check_backup_popup_cb,
                buttons=['WIEDERHERSTELLEN', 'NEUES SPIEL'])

    def _check_backup_popup_cb(self, button):
        if button == 'WIEDERHERSTELLEN':
            self.restore_backup()

    def new_connection_handler(self, connection):
        # notify newly connected clients about the current state of things
        # (these will notify *all* clients, but that shouldn't be a problem)
        self.widget_mgr.send_stack_update(force=True)
        self.puzzle.notify_state_update("new_connection")

    def get_screen_width(self):
        H, W = self.screen.getmaxyx()
        return W

    # from https://stackoverflow.com/a/9567831
    def memory_dump(self):
        log.info("collecting garbage...")
        gc.collect()
        log.info("dumping memory to 'memory_dump.pickle'...")
        xs = []
        for obj in gc.get_objects():
            i = id(obj)
            size = sys.getsizeof(obj, 0)
            #    referrers = [id(o) for o in gc.get_referrers(obj) if hasattr(o, '__class__')]
            referents = [
                id(o) for o in gc.get_referents(obj)
                if hasattr(o, '__class__')
            ]

            cls = None
            if hasattr(obj, '__class__'):
                cls = str(obj.__class__)
            xs.append({
                'id': i,
                'class': cls,
                'str': str(obj),
                'size': size,
                'referents': referents
            })

        with open('memory_dump.pickle', 'wb') as dump:
            pickle.dump(xs, dump, pickle.HIGHEST_PROTOCOL)
        log.info("memory dump complete")

    def wakeup_screen(self):
        log.info("sending key to wakeup screen")
        subprocess.check_call(["xdotool", "key", "Up"])
Example #10
0
    def __init__(self):
        "Creates the dialog."

        # create a new surface matching the display
        self.surface = sdl.surface_new(
            (properties.window_size_x, properties.window_size_y), 1)
        self.surface = self.surface.convert()

        # load the fonts too
        self.titlefont = pygame.font.Font(properties.titlefont, 24)

        # create the static labels
        self.title = self.titlefont.render("Scenario map ", 1, (255, 0, 0))

        # blit 'em
        self.surface.blit(self.title, (10, 10))

        # get the map
        map = scenario.map

        # get the hexes of the map
        hexes = scenario.map.getHexes()

        # create a new surface for it. It must contain the entire map (2x2 pixels per hex) and be a
        # little wider as every even row is adjusted half a hex to the right
        self.minimap = sdl.surface_new(
            (map.getXsize() * 4 + 1, map.getYsize() * 3 + 1), 1)

        # loop over the hexes in the map
        for y in range(map.getYsize()):
            for x in range(map.getXsize()):
                # get the hex for that position
                hex = hexes[x][y]

                # get the miniicon for the hex
                icon = hex.getMiniIcon()

                # is this an odd or even row?
                if y % 2:
                    # odd
                    self.minimap.blit(icon, (x * 4 + 2, y * 3))
                else:
                    # even
                    self.minimap.blit(icon, (x * 4, y * 3))

        # calculate the x position for the map
        xpos = properties.window_size_x / 2 - (self.minimap.get_size()[0] / 2)

        # blit out the created surface
        self.surface.blit(self.minimap, (xpos, self.title.get_size()[1] + 40))

        # create the widget manager
        self.wm = WidgetManager(self.surface)

        # create all widgets
        self.createWidgets()

        # blit out or full surface to the main surface
        scenario.sdl.blit(self.surface, (0, 0))

        # update the whole screen
        scenario.sdl.update()
Example #11
0
class InfoMap:
    """
    This class is used as a dialog for.
    """
    def __init__(self):
        "Creates the dialog."

        # create a new surface matching the display
        self.surface = sdl.surface_new(
            (properties.window_size_x, properties.window_size_y), 1)
        self.surface = self.surface.convert()

        # load the fonts too
        self.titlefont = pygame.font.Font(properties.titlefont, 24)

        # create the static labels
        self.title = self.titlefont.render("Scenario map ", 1, (255, 0, 0))

        # blit 'em
        self.surface.blit(self.title, (10, 10))

        # get the map
        map = scenario.map

        # get the hexes of the map
        hexes = scenario.map.getHexes()

        # create a new surface for it. It must contain the entire map (2x2 pixels per hex) and be a
        # little wider as every even row is adjusted half a hex to the right
        self.minimap = sdl.surface_new(
            (map.getXsize() * 4 + 1, map.getYsize() * 3 + 1), 1)

        # loop over the hexes in the map
        for y in range(map.getYsize()):
            for x in range(map.getXsize()):
                # get the hex for that position
                hex = hexes[x][y]

                # get the miniicon for the hex
                icon = hex.getMiniIcon()

                # is this an odd or even row?
                if y % 2:
                    # odd
                    self.minimap.blit(icon, (x * 4 + 2, y * 3))
                else:
                    # even
                    self.minimap.blit(icon, (x * 4, y * 3))

        # calculate the x position for the map
        xpos = properties.window_size_x / 2 - (self.minimap.get_size()[0] / 2)

        # blit out the created surface
        self.surface.blit(self.minimap, (xpos, self.title.get_size()[1] + 40))

        # create the widget manager
        self.wm = WidgetManager(self.surface)

        # create all widgets
        self.createWidgets()

        # blit out or full surface to the main surface
        scenario.sdl.blit(self.surface, (0, 0))

        # update the whole screen
        scenario.sdl.update()
        #pygame.display.update ()

    def createWidgets(self):
        "Creates all widgets for the dialog."
        # create the buttons for the next and previous buttons
        self.wm.register(
            Button(properties.path_dialogs + "butt-back-moff.png",
                   properties.path_dialogs + "butt-back-mover.png", (284, 650),
                   {widget.MOUSEBUTTONUP: self.prevScreen}))
        self.wm.register(
            Button(properties.path_dialogs + "butt-forward-moff.png",
                   properties.path_dialogs + "butt-forward-mover.png",
                   (528, 650), {widget.MOUSEBUTTONUP: self.nextScreen}))

    def run(self):
        """Executes the dialog and runs its internal loop until a key is pressed. When that happens
        shows the dialog InfoUnits. When that dialog is terminated this method also returns."""

        # loop forever
        while 1:
            # get next event
            event = pygame.event.wait()

            # see wether the widget manager wants to handle it
            if event != -1:
                # handle event and get the return code that tells us wehter it was handled or not
                returncode = self.wm.handle(event)

                # is the event loop done?
                if returncode == widget.DONE:
                    return

    def nextScreen(self, event):
        """Callback triggered when the user clicks the 'Next' button. Shows the 'Order of battle'
        dialog. After the dialog has been shown this dialog is repainted."""

        # show the dialog
        InfoUnits().run()

        # blit out or full surface to the main surface
        scenario.sdl.blit(self.surface, (0, 0))

        # update the whole screen
        scenario.sdl.update()
        #pygame.display.update ()

        # we've handled the event
        return widget.HANDLED

    def prevScreen(self, event):
        """Callback triggered when the user clicks the 'Previous' button. This method simply cancels
        the event loop for this dialog's widget manager. Basically this dialog quits."""
        # we're done
        return widget.DONE