# Licensed under GPLv3 # Written by Sebastian Lohff (seba@someserver.de) # http://seba-geek.de/projects/seopardy/ from __future__ import print_function from PySide import QtCore, QtGui from player import Player, nobodyColor from windows import QuestionWindow, EditAnswersWindow, PlayerStartWindow, VictoryWindow, DoubleJeopardyWindow from gamestate import QuestionAnswers class SeopardyGame(QtGui.QWidget): def __init__(self, questions, gamestate, inputs, parent=None): super(SeopardyGame, self).__init__(parent) self.questions = questions self.gamestate = gamestate self.inputs = inputs #self.players = [Player.gen_player(i, parent=self) for i in range(1, 4)] self.players = self.gamestate.get_players() if len(self.players) == 0: for i in range(3): self.add_player() self.currentPlayer = None self._inOtherWindow = False self._createGui() self.setWindowTitle("Seopardy - Board") self.showFullScreen() # initiate starting the game! if self.gamestate.get_state() == "start": QtCore.QCoreApplication.postEvent(self, GameStartEvent()) def add_player(self, addToGui=False): self.players.append(Player.gen_player(len(self.players)+1, self)) if addToGui: if len(self.players) > 1: self.playerBar.addStretch() self.playerBar.addWidget(self.players[-1]) def _createGui(self): """ Create the board from questions. """ layout = QtGui.QVBoxLayout() headerLayout = QtGui.QHBoxLayout() self.header = QtGui.QLabel(self.questions.get_title()) self.header.setStyleSheet("QLabel { font-size: 20px; }") headerLayout.addWidget(self.header) headerLayout.setSizeConstraint(QtGui.QLayout.SetMinimumSize) layout.addWidget(self.header, alignment=QtCore.Qt.AlignCenter) # create board #board = QtGui.QGridLayout(6, len(self.questions.get_sections())) self.board = QtGui.QGridLayout() self.board.setSizeConstraint(QtGui.QLayout.SetMaximumSize) for i, sec in enumerate(self.questions.get_sections()): seclabel = QtGui.QLabel(sec, alignment=QtCore.Qt.AlignCenter) seclabel.setStyleSheet("QLabel { font-size: 30px; }") self.board.addWidget(seclabel, 0, i) for j in range(5): b = QtGui.QPushButton(str((j+1)*100)) b.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding) b.setAutoDefault(False) #b.setStyleSheet("QPushButton { font-size: 60px; }") b.installEventFilter(FocusKeyGrabber(i+1, j+1, self)) b.clicked.connect(lambda sec=sec, j=j: self.go_to_question(sec, j+1)) self.board.addWidget(b, j+1, i) self._restyle_button(sec, j+1, self.gamestate.get_answers(sec, j+1)) layout.addLayout(self.board) # create player bar self.playerBar = QtGui.QHBoxLayout() for i, player in enumerate(self.players): self.playerBar.addWidget(player) if i != len(self.players)-1: self.playerBar.addStretch() layout.addLayout(self.playerBar) self.setLayout(layout) def setFocusToQuestion(self, secno, number): if secno < 1 or secno > len(self.questions.get_sections()) or number > 5 or number < 1: return self.board.itemAtPosition(number, secno-1).widget().setFocus() def event(self, e): if e.type() == GameStartEvent.eventType: # start window if not self._inOtherWindow: self._inOtherWindow = True x = PlayerStartWindow(self.players, self) for inp in self.inputs: inp.buttonEvent.connect(x.playerButtonPress) x.buzzersOpen.connect(inp.buzzersOpen) x.playerGotQuestion.connect(inp.playerGotQuestion) x.exec_() for inp in self.inputs: inp.buttonEvent.disconnect(x.playerButtonPress) x.buzzersOpen.disconnect(inp.buzzersOpen) x.playerGotQuestion.disconnect(inp.playerGotQuestion) self._inOtherWindow = False self.gamestate.set_state("playing") self.gamestate.save() return True else: return super(SeopardyGame, self).event(e) def closeEvent(self, event): if not self._inOtherWindow: event.accept() else: event.ignore() def keyPressEvent(self, e): if e.key() == QtCore.Qt.Key_Escape: self.close() elif e.key() == QtCore.Qt.Key_R: x = PlayerStartWindow(self.players, self) self._inOtherWindow = True x.exec_() self._inOtherWindow = False elif e.key() == QtCore.Qt.Key_V: self.show_victory_window() elif e.key() == QtCore.Qt.Key_E: if self._inOtherWindow: return # find current question section = None qno = None found = False for secno, section in enumerate(self.questions.get_sections(), 0): for qno in range(1, 6): btn = self.board.itemAtPosition(qno, secno).widget() if btn == self.focusWidget(): found = True break if found: break if not found: return self.edit_question_answers(section, qno) def edit_question_answers(self, section, number): self._inOtherWindow = True answers = self.gamestate.get_answers(section, number) if answers is None: answers = QuestionAnswers(section, number) editAnswersWin = EditAnswersWindow(self.players, answers, self) editAnswersWin.exec_() self._inOtherWindow = False oldAnswers = self.gamestate.get_answers(section, number) newAnswers = editAnswersWin.get_new_answers() if oldAnswers: self._set_player_points(oldAnswers, rollback=True) self._set_player_points(newAnswers) self.gamestate.set_answers(section, number, newAnswers) self._restyle_button(section, number, newAnswers) self.gamestate.save() def show_victory_window(self): self._inOtherWindow = True victoryWindow = VictoryWindow(self.players, self) victoryWindow.exec_() self._inOtherWindow = False def go_to_question(self, section, number): if self._inOtherWindow: return self._inOtherWindow = True answers = self.gamestate.get_answers(section, number) wasAnswered = (answers is not None and answers.is_answered()) question = self.questions.get_question(section, number) dj = None if not wasAnswered and question["Double-Jeopardy"]: dwin = DoubleJeopardyWindow(self.players, number*100, self.currentPlayer, parent=self) dwin.exec_() dj = (dwin.get_player(), dwin.get_chosen_points()) qwin = QuestionWindow(self.players, section, number, question, answers, dj, self) for inp in self.inputs: inp.buttonEvent.connect(qwin.playerButtonPress) qwin.buzzersOpen.connect(inp.buzzersOpen) qwin.playerGotQuestion.connect(inp.playerGotQuestion) qwin.showFullScreen() qwin.exec_() for inp in self.inputs: inp.buttonEvent.disconnect(qwin.playerButtonPress) qwin.buzzersOpen.disconnect(inp.buzzersOpen) qwin.playerGotQuestion.disconnect(inp.playerGotQuestion) self._inOtherWindow = False # add points to players if not wasAnswered: answers = qwin.get_answers() self.gamestate.set_answers(section, number, answers) self._set_player_points(answers) if answers.get_winner(): self.currentPlayer = answers.get_winner() # restyle the button self._restyle_button(section, number, answers) # safe current gamestate self.gamestate.save() # check if we are done foundUnanswered = False for sec in self.questions.get_sections(): for j in range(5): boardAnswers = self.gamestate.get_answers(sec, j+1) if boardAnswers is None or not answers.is_answered(): foundUnanswered = True if foundUnanswered: break if not foundUnanswered: self.show_victory_window() def _set_player_points(self, answers, rollback=False): for i, (player, correct) in enumerate(answers.get_tries()): prefix = 1 if rollback: prefix *= -1 if not correct: prefix *= -1 points = 0 if i == 0 and answers.get_dj_points() is not None: points = answers.get_dj_points() else: points = answers.points() player.add_points(prefix*points) def _restyle_button(self, sec, qno, answers): secno = self.questions.get_number_from_section(sec) btn = self.board.itemAtPosition(qno, secno-1).widget() btnstr = "" btncolor = None if answers is None or not answers.is_answered(): btn.setText(str(qno*100)) btn.setStyleSheet("QPushButton { font-size: 60px; }") return # restrict number of tries shown numTries = 4 if answers.got_answered() else 3 tries = answers.get_tries() if len(tries) > numTries: btnstr = "...\n" first = True for player, correct in answers.get_tries()[-numTries:]: if correct: btncolor = player.color prefix = "+" else: prefix = "-" dj = " (D)" if first and answers.get_dj_points() is not None else "" btnstr += "%s%s%s\n" % (prefix, player.name, dj) first = False if not answers.got_answered(): btnstr += "+nobody" btncolor = nobodyColor btn.setStyleSheet("QPushButton { background-color: %s; color: white; font-size: 20px; border: none; }" % (btncolor.name(),)) btn.setText(btnstr.strip()) class FocusKeyGrabber(QtCore.QObject): def __init__(self, secno, number, parent, *args, **kwargs): super(FocusKeyGrabber, self).__init__(parent, *args, **kwargs) self._secno = secno self._number = number self._parent = parent def eventFilter(self, obj, e): if e.type() == QtCore.QEvent.KeyPress: if e.key() == QtCore.Qt.Key.Key_Left: self._parent.setFocusToQuestion(self._secno-1, self._number) return True elif e.key() == QtCore.Qt.Key.Key_Right: self._parent.setFocusToQuestion(self._secno+1, self._number) return True elif e.key() == QtCore.Qt.Key.Key_Up: self._parent.setFocusToQuestion(self._secno, self._number-1) return True elif e.key() == QtCore.Qt.Key.Key_Down: self._parent.setFocusToQuestion(self._secno, self._number+1) return True return QtCore.QObject.eventFilter(self, obj, e) class GameStartEvent(QtCore.QEvent): eventType = QtCore.QEvent.Type(QtCore.QEvent.registerEventType()) def __init__(self): super(GameStartEvent, self).__init__(self.eventType)