class MemoryDisplay(threading.Thread): "Displays timer and memory images. Handles barcode input (barcode scanner is a HID)." # taken from http://learn.adafruit.com/pi-video-output-using-pygame/pygame-drawing-functions def __init__(self, ledstrip, noReturn=False): "Ininitializes a new pygame screen using the framebuffer" threading.Thread.__init__(self) if DEBUG: logging.info("DEBUG MODE") self.screen = pygame.display.set_mode((640, 480)) else: # Based on "Python GUI in Linux frame buffer" # http://www.karoltomala.com/blog/?p=679 dispNo = os.getenv("DISPLAY") if dispNo: logging.info("I'm running under X display = %s" % dispNo) # Check which frame buffer drivers are available # Start with fbcon since directfb hangs with composite output drivers = ["fbcon", "directfb", "svgalib"] found = False for driver in drivers: # Make sure that SDL_VIDEODRIVER is set if not os.getenv("SDL_VIDEODRIVER"): os.putenv("SDL_VIDEODRIVER", driver) try: pygame.display.init() except pygame.error: logging.warning("Driver: %s failed." % driver) continue found = True break if not found: logging.critical("No suitable video driver found! Are you root?") raise Exception("No suitable video driver found! Are you root?") size = (pygame.display.Info().current_w, pygame.display.Info().current_h) logging.info("Framebuffer size: %d x %d" % (size[0], size[1])) self.screen = pygame.display.set_mode(size, pygame.FULLSCREEN) self.clock = pygame.time.Clock() self.playTime = 0 self.noReturn = noReturn self.runGame = True self.inGame = False self.lastAction = datetime.now() - timedelta(seconds=30) self.validGame = True self.lastHighscore = [] self.barcode = "" self.lastImg = None self.white = (255, 255, 255) self.black = (0, 0, 0) self.red = (255, 0, 0) self.green = (0, 255, 0) self.ledstrip = ledstrip self.memory = Memory(self) # preload images gameImgs = ["byhickerspace.png", "qrcode.png"] self.preloadedImgs = {} for imgPath in gameImgs: self.preloadedImgs[imgPath] = pygame.image.load(imgPath).convert() for imgPath in self.memory.images: img = pygame.image.load(imgPath).convert() self.preloadedImgs[imgPath] = self.scalePercentage(img, 1) # Clear the screen to start self.screen.fill((0, 0, 0)) # Initialise font support pygame.font.init() # hide mouse pointer pygame.mouse.set_visible(False) # Render the screen pygame.display.update() self.endGame() def __del__(self): "Destructor to make sure pygame shuts down, etc." def scalePercentage(self, surface, perc): screenRect = self.screen.get_rect() surface = pygame.transform.scale(surface, (int(perc*screenRect.width), int(perc*screenRect.height))) return surface def showImg(self, img, firstMove): "Takes image path and displays the image." if not self.inGame: return self.screen.fill((0, 0, 0)) self.screen.blit(self.preloadedImgs[img], (0, 25)) if firstMove: self.lastImg = img else: secondary = pygame.image.load(self.lastImg).convert() secondary = self.scalePercentage(secondary, 0.25) self.screen.blit(secondary, (self.screen.get_rect().width-secondary.get_rect().width, 25)) pygame.display.update() def newGame(self): "Resets the timer and clears the screen." self.inGame = True self.validGame = True self.playTime = 0 # clear incomplete barcodes self.barcode = "" # clear the screen to start self.screen.fill((0, 0, 0)) # instructions font = pygame.font.Font(None, 35) instruction = font.render("Start scanning!", True, (255, 255, 255), (0, 0, 0)) self.blitCenterX(instruction, 100) pygame.display.update() def endGame(self, valid=True): self.inGame = False self.validGame = valid def handleKey(self): "Handles keyboard input: barcodes, RETURN and ESCAPE" events = pygame.event.get() for event in events: if event.type == pygame.KEYDOWN: if event.key == pygame.K_RETURN: # input complete logging.info("Barcode %s received." % self.barcode) self.memory.scan(self.barcode) self.barcode = "" self.lastAction = datetime.now() elif event.key == pygame.K_ESCAPE: # exit logging.info("Exitting.. (Escape pressed)") self.close() elif event.key <= 127: self.barcode += chr(event.key) # if barcode scanner does not send return after barcode defaultLength = len(self.memory.preset_barcodes.keys()[0]) if self.noReturn and len(self.barcode) == defaultLength: returnEvent = pygame.event.Event(pygame.KEYDOWN, key=pygame.K_RETURN) pygame.event.post(returnEvent) def close(self): self.runGame = False # stop other threads self.ledstrip.close() def blitCenterX(self, surface, y): pos = surface.get_rect() pos.centerx = self.screen.get_rect().centerx pos.y = y self.screen.blit(surface, pos) def highscore(self): self.ledstrip.animation(self.memory.barcodepositions.values()) # clear screen self.screen.fill(self.black) #titleFont = pygame.font.Font(None, 60) #titleSurface = titleFont.render("Barcode Memory", True, white, black) #self.blitCenterX(titleSurface, 160) font = pygame.font.Font(None, 32) if self.playTime > 0 and self.validGame: time_ = str(timedelta(seconds=self.playTime))[2:] personal = font.render("Congratulations, your score: %s" % time_, True, self.red, self.black) self.blitCenterX(personal, 185) pos = 230 rank = 1 for score in self.memory.loadHighscore()[:8]: delta, playTime = score scoreline = font.render("#%d %s %s" % (rank, str(delta)[2:], playTime.strftime("%Y-%m-%d %H:%M")), True, self.white, self.black) self.blitCenterX(scoreline, pos) pos += 25 rank += 1 self.blitCenterX(self.preloadedImgs["byhickerspace.png"], 0) self.blitCenterX(self.preloadedImgs["qrcode.png"], 600) pygame.display.update() def run(self): # initialize font font = pygame.font.Font(None, 40) count = 0 while self.runGame: # stop after 30 seconds of inactivity if self.inGame and self.lastAction < datetime.now() - timedelta(seconds=30): logging.info("Returned to menu due to 30 seconds of inactivity.") self.endGame(False) if self.inGame: # show timer timerSurface = font.render("Timer: %s" % str(timedelta(seconds=self.playTime))[2:], True, (255, 255, 255), (0, 0, 0)) # blit text self.screen.blit(timerSurface, (0, 0)) pygame.display.update() if count % 10 == 0: self.playTime += 1 else: if count == 3: self.blitCenterX(font.render("Scan NEW GAME to play!", True, self.green, self.black), 440) # update display pygame.display.update() elif count == 10: self.blitCenterX(font.render("Scan NEW GAME to play!", True, self.black, self.black), 440) # update display pygame.display.update() if count % 10 == 0: count = 0 self.handleKey() # 10 fps self.clock.tick(10) count += 1 # max play time exceeded self.close() logging.info("Display thread closed.")