# Licensed under GPLv3 # Written by Sebastian Lohff (seba@someserver.de) # http://seba-geek.de/projects/seopardy/ from PySide import QtGui, QtCore import copy from gamestate import QuestionAnswers from music import MusicBox from video import VideoPlayer from player import Player, ButtonEvent, nobodyColor class QuestionWindow(QtGui.QDialog): buzzersOpen = QtCore.Signal(bool) playerGotQuestion = QtCore.Signal(int) def __init__(self, players, section, qnumber, question, answers=None, dj=None, parent=None): super(QuestionWindow, self).__init__(parent) self.players = players self.section = section self.qnumber = qnumber self.question = question self.dj = dj self._windowSetup = False self._videoPlayer = None if answers is not None: self.answers = answers else: self.answers = QuestionAnswers(self.section, self.qnumber) if not self.answers.is_answered(): if not self._question_has_audio(): MusicBox.play_music("questionSong") if self.dj: self.answers.set_dj_points(self.dj[1]) playerNo = self.players.index(self.dj[0])+1 QtCore.QCoreApplication.postEvent(self, ButtonEvent(playerNo)) self._setupGui() self.setWindowTitle("Seopardy - %s - %d" % (section, qnumber*100)) self._inWindow = False if self.question["Type"] == "Music": tag = "%s-%s" % (self.section, self.qnumber) MusicBox.add_music(tag, self.question["Question"]) MusicBox.play_music(tag) elif self.question["Type"] == "Video": self._videoPlayer.play() def get_answers(self): return self.answers def closeEvent(self, event): if not self._inWindow: self.buzzersOpen.emit(False) MusicBox.stop_music() if self._videoPlayer: self._videoPlayer.stop() event.accept() else: event.ignore() def _mkQuestionLabel(self, text, monospaced=False): question = QtGui.QLabel(text, alignment=QtCore.Qt.AlignCenter) question.setWordWrap(True) extra = "" if monospaced: extra += "font-family: monospace;" question.setStyleSheet("QLabel { font-size: 40px; %s }" % (extra,)) return question def _setupGui(self): self.layout = QtGui.QVBoxLayout() seclabel = QtGui.QLabel(self.section) seclabel.setStyleSheet("QLabel { font-size: 30px; }") self.layout.addWidget(seclabel, alignment=QtCore.Qt.AlignCenter) self.layout.addStretch() qlabel = None stretch = 0 if self.question["Type"] == "Text": qlabel = self._mkQuestionLabel(self.question["Question"]) elif self.question["Type"] == "Music": qlabel = self._mkQuestionLabel("Listen...") elif self.question["Type"] == "Image": qlabel = QtGui.QLabel() pixmap = QtGui.QPixmap() pixmap.loadFromData(open(self.question["Question"]).read()) qlabel.setPixmap(pixmap) qlabel.setAlignment(QtCore.Qt.AlignCenter) elif self.question["Type"] == "Code": # to have this a) aligned but b) centered we "cheat" a bit with the spaces... code = self.question["Question"].split("\n") code = map(lambda x: x.replace("\t", " "), code) maxLineLen = max(map(len, code)) code = map(lambda x: x + ((maxLineLen-len(x))*" "), code) qlabel = self._mkQuestionLabel("
"+"\n".join(code)+"
", monospaced=True) elif self.question["Type"] == "Video": self._videoPlayer = VideoPlayer(self.question["Question"], self.question["Audio"]) qlabel = self._videoPlayer.get_widget() stretch = 1 else: raise ValueError("%s is an unknown type for section %s question name %s" % (self.question["Type"], self.section, self.question["Name"])) self.layout.addWidget(qlabel, stretch=stretch) self.layout.addStretch() self.setLayout(self.layout) def event(self, e): if e.type() == ButtonEvent.eventType: if e.get_playerno() <= len(self.players) and not self._inWindow: done = False if self.question["Type"] == "Music": MusicBox.stop_music() elif self.question["Type"] == "Video": self._videoPlayer.pause() self._inWindow = True player = self.players[e.get_playerno()-1] qawin = QuestionAnswerWindow(player, self) self.playerGotQuestion.emit(e.get_playerno()) res = qawin.exec_() if res == QuestionAnswerWindow.CORRECT: self.answers.add_try(player, correct=True) self._inWindow = False done = True self.close() elif res == QuestionAnswerWindow.WRONG: self.answers.add_try(player, correct=False) self._inWindow = False if not done: self.buzzersOpen.emit(True) if self.question["Type"] == "Music": # resume music if question was not answered MusicBox.play_music("%s-%s" % (self.section, self.qnumber)) elif self.question["Type"] == "Video": # resume video if question was not answered self._videoPlayer.play() elif e.get_playerno() > len(self.players) and not self._inWindow: # unknown player! to not confuse certain devices we send a buttons open event self.buzzersOpen.emit(True) print("unknown player", e.get_playerno()) return True else: ret = super(QuestionWindow, self).event(e) # we want to do this after the window has focus for the first time if not self._windowSetup and e.type() == QtCore.QEvent.FocusIn: if not self.answers.is_answered(): self.buzzersOpen.emit(True) self._windowSetup = True return ret def keyPressEvent(self, e): if e.key() == QtCore.Qt.Key_Escape: if not self.answers.is_answered(): self.answers.nobody_knew() self.close() elif not self.answers.is_answered() and e.key() >= ord('1') and e.key() <= ord(str(len(self.players))): QtCore.QCoreApplication.postEvent(self, ButtonEvent(int(chr(e.key())))) @QtCore.Slot(int) def playerButtonPress(self, no): QtCore.QCoreApplication.postEvent(self, ButtonEvent(int(no))) def _question_has_audio(self): return self.question["Type"] == "Music" or (self.question["Type"] == "Video" and self.question["Audio"]) class QuestionAnswerWindow(QtGui.QDialog): CORRECT = 1 WRONG = 2 OOPS = 3 def __init__(self, player, parent=None): super(QuestionAnswerWindow, self).__init__(parent) self.player = player self._setupGui() self.setWindowTitle("Seopardy - Answer by Player %s" % (self.player.name,)) # move window to bottom right of screen g = QtGui.QApplication.desktop().screenGeometry() self.show() cPos = self.rect() self.move(g.width() - cPos.width(), g.height() - cPos.height()) def _setupGui(self): self.layout = QtGui.QVBoxLayout() self.plabel = QtGui.QLabel(self.player.name) self.plabel.setStyleSheet("QLabel { font-size: 60px; }") self.layout.addWidget(self.plabel, alignment=QtCore.Qt.AlignCenter) btnbox = QtGui.QHBoxLayout() right = QtGui.QPushButton("Correct") right.clicked.connect(lambda: self.done(self.CORRECT)) btnbox.addWidget(right) wrong = QtGui.QPushButton("Wrong") wrong.clicked.connect(lambda: self.done(self.WRONG)) btnbox.addWidget(wrong) btnbox.addStretch() oops = QtGui.QPushButton("Oops") oops.clicked.connect(lambda: self.done(self.OOPS)) btnbox.addWidget(oops) self.layout.addLayout(btnbox) self.setLayout(self.layout) class EditAnswersWindow(QtGui.QDialog): def __init__(self, players, answers, parent=None): super(EditAnswersWindow, self).__init__(parent) self._players = players self._answers = answers self._nobody = not answers.got_answered() self._dj_points = answers.get_dj_points() self._in_window = False if self._answers: self._tries = copy.copy(self._answers.get_tries()) else: self._tries = [] self._setupGui() self.setWindowTitle("Seopardy - Edit Answers") self.installEventFilter(KeyGrabber((QtCore.Qt.Key.Key_Left, QtCore.Qt.Key.Key_Right), self)) def get_new_answers(self): qa = QuestionAnswers(self._answers.section, self._answers.qnumber, dj_points=self._dj_points) rightPlayer = None for player, correct in self._tries: if correct: rightPlayer = player else: qa.add_try(player, correct) if rightPlayer: qa.add_try(rightPlayer, True) elif self._nobody: qa.nobody_knew() return qa def closeEvent(self, event): if not self._in_window: event.accept() else: event.ignore() def _setupGui(self): self.layout = QtGui.QVBoxLayout() self.qlabel = QtGui.QLabel("Questions") self.qlabel.setStyleSheet("QLabel { font-size: 30px; }") self.layout.addWidget(self.qlabel, alignment=QtCore.Qt.AlignCenter) self.answerlayout = QtGui.QVBoxLayout() self._resetAllButtons() self.layout.addLayout(self.answerlayout) self.xlabel = QtGui.QLabel("+/- right/wrong, d delete, a add, arrows change player\ne edit double jeopardy") self.xlabel.setStyleSheet("QLabel { font-size: 15px; }") self.layout.addWidget(self.xlabel) self.setLayout(self.layout) def keyPressEvent(self, e): if e.key() == QtCore.Qt.Key_Escape: self.close() elif e.key() == QtCore.Qt.Key_E: # edit double jeopardy if self._dj_points is not None and len(self._tries) > 0 and not self._in_window: self._in_window = True dwin = DoubleJeopardyWindow(self._players, self._answers.qnumber*100, self._tries[0][0], current_points=self._dj_points, parent=self) dwin.exec_() self._dj_points = dwin.get_chosen_points() self._tries[0] = (dwin.get_player(), self._tries[0][1]) self._in_window = False else: currFocus = self._getCurrFocus() if currFocus is None and e.key() != QtCore.Qt.Key_A: return nothingDone = False if e.key() == QtCore.Qt.Key_Plus: self._nobody = False for i, (player, correct) in enumerate(self._tries): self._tries[i] = (player, i==currFocus) if currFocus == len(self._tries): self._nobody = True elif e.key() == QtCore.Qt.Key_Minus: if currFocus == len(self._tries): if len(self._tries) == 0: self._nobody = False else: if self._tries[currFocus][1]: # give nobody the score for this one self._nobody = True self._tries[currFocus] = (self._tries[currFocus][0], False) elif e.key() == QtCore.Qt.Key_A: self._tries.append((self._players[0], False)) self._makeNobodyConsistent() currFocus = len(self._tries) - 1 elif e.key() == QtCore.Qt.Key_D and currFocus < len(self._tries): del self._tries[currFocus] elif e.key() == QtCore.Qt.Key_Left and currFocus < len(self._players): playlen = len(self._players) prevPlayer = self._players[(self._players.index(self._tries[currFocus][0])-1+playlen)%playlen] self._tries[currFocus] = (prevPlayer, self._tries[currFocus][1]) elif e.key() == QtCore.Qt.Key_Right and currFocus < len(self._tries): playlen = len(self._players) nextPlayer = self._players[(self._players.index(self._tries[currFocus][0])+1+playlen)%playlen] self._tries[currFocus] = (nextPlayer, self._tries[currFocus][1]) else: super(EditAnswersWindow, self).keyPressEvent(e) nothingDone = True if not nothingDone: self._resetAllButtons() self.updateGeometry() # reset focus #print("setting focus to", min(currFocus, self.answerlayout.count()), self.answerlayout.itemAt(min(currFocus, self.answerlayout.count())).widget().text(), self.answerlayout.itemAt(min(currFocus, self.answerlayout.count())).widget().hasFocus()) self.answerlayout.itemAt(min(currFocus, self.answerlayout.count())).widget().setFocus(QtCore.Qt.FocusReason.NoFocusReason) #print("setting focus to", min(currFocus, self.answerlayout.count()), self.answerlayout.itemAt(min(currFocus, self.answerlayout.count())).widget().text(), self.answerlayout.itemAt(min(currFocus, self.answerlayout.count())).widget().hasFocus()) #print(self.focusWidget()) #if self.focusWidget(): # print("widget", self.focusWidget().text()) def _makeNobodyConsistent(self): if len(self._tries) > 0: someoneKnew = any([c for p, c in self._tries]) self._nobody = not someoneKnew def _getCurrFocus(self): if self.focusWidget() is not None: for i in range(self.answerlayout.count()): if self.answerlayout.itemAt(i).widget() == self.focusWidget(): return i return None def _resetAllButtons(self): f = self.answerlayout.takeAt(0) while f is not None: f.widget().deleteLater() f = self.answerlayout.takeAt(0) # add buttons first = True for player, correct in self._tries: self._addAnswerButton(player, correct, first) first = False # add the nobody button self._addAnswerButton(Player("nobody", nobodyColor), self._nobody) def _addAnswerButton(self, player, correct=False, first=False): prefix = "+" if correct else "-" dj = " (D)" if first and self._dj_points is not None else "" btn = QtGui.QPushButton("\n%s%s%s\n" % (prefix, player.name, dj), self) btn.setStyleSheet("QPushButton { background-color: %s; color: white; font-size: 20px; border: none; }" % (player.color.name(),)) btn.installEventFilter(KeyGrabber((QtCore.Qt.Key.Key_Left, QtCore.Qt.Key.Key_Right), self)) self.answerlayout.addWidget(btn) class KeyGrabber(QtCore.QObject): def __init__(self, keys, parent, *args, **kwargs): super(KeyGrabber, self).__init__(parent, *args, **kwargs) self._keys = keys self._parent = parent def eventFilter(self, obj, e): if e.type() == QtCore.QEvent.KeyPress: if e.key() in self._keys: self._parent.keyPressEvent(e) return True return QtCore.QObject.eventFilter(self, obj, e) class PlayerStartWindow(QtGui.QDialog): buzzersOpen = QtCore.Signal(bool) playerGotQuestion = QtCore.Signal(int) def __init__(self, players, parent): super(PlayerStartWindow, self).__init__(parent) self._players = players self._parent = parent self._playerLineEdits = [] self._windowSetup = False self._setupGui() self.setWindowTitle("Seopardy - Start") self.show() MusicBox.play_music("startSong") # center window g = QtGui.QApplication.desktop().screenGeometry() cPos = self.rect() self.move(g.width()/2 - cPos.width()/2, g.height()/2 - cPos.height()/2) def event(self, e): if e.type() == ButtonEvent.eventType: if e.get_playerno() <= len(self._players)+1: if e.get_playerno() == len(self._players)+1: # add a new player self._parent.add_player(addToGui=True) self._guiAddPlayer(self._players[-1]) layoutItem = self.playerGrid.itemAtPosition(e.get_playerno(), 1) if layoutItem: widget = layoutItem.widget() widget.selectAll() widget.setFocus() self.playerGotQuestion.emit(e.get_playerno()) return True else: ret = super(PlayerStartWindow, self).event(e) if not self._windowSetup and e.type() == QtCore.QEvent.WindowActivate: self.buzzersOpen.emit(True) self._windowSetup = True return ret def closeEvent(self, event): MusicBox.stop_music() self.buzzersOpen.emit(False) event.accept() def keyPressEvent(self, e): # needed to circumvent dialogclosing when enter is pressed if e.key() == QtCore.Qt.Key_Escape: self.close() elif e.key() == QtCore.Qt.Key_K: # FIXME: Remove this QtCore.QCoreApplication.postEvent(self, ButtonEvent(1)) def _setupGui(self): self.layout = QtGui.QVBoxLayout() self.playerGrid = QtGui.QGridLayout() for player in self._players: self._guiAddPlayer(player) self.layout.addLayout(self.playerGrid) startbtn = QtGui.QPushButton("\nStart\n", self) startbtn.setStyleSheet("QPushButton { font-size: 40px; }") startbtn.clicked.connect(self.close) self.layout.addWidget(startbtn) self.setLayout(self.layout) def _guiAddPlayer(self, player): row = self.playerGrid.rowCount() label = QtGui.QLabel("Player %d" % (row,), self) label.setStyleSheet("QLabel { font-size: 40px; background-color: %s; color: white; }" % (player.color.name(),)) self.playerGrid.addWidget(label, row, 0) edit = QtGui.QLineEdit(player.name, self) edit.setStyleSheet("QLineEdit { font-size: 40px; }") edit.editingFinished.connect(lambda widget=edit, player=player: self._playerDoneEditing(widget, player)) self.playerGrid.addWidget(edit, row, 1) def _playerDoneEditing(self, widget, player): player.change_name(widget.text()) self.buzzersOpen.emit(True) @QtCore.Slot(int) def playerButtonPress(self, no): QtCore.QCoreApplication.postEvent(self, ButtonEvent(int(no))) class VictoryWindow(QtGui.QDialog): def __init__(self, players, parent): super(VictoryWindow, self).__init__(parent) self._players = players self._parent = parent self._setupGui() self.setWindowTitle("Seopardy - Victory") MusicBox.play_music("closingSong") def _setupGui(self): self.layout = QtGui.QVBoxLayout() self.playerGrid = QtGui.QGridLayout() winPlayers = sorted(self._players, key=lambda p: p.points, reverse=True) for i, player in enumerate(winPlayers): label = QtGui.QLabel(player.name, self) fsize = 60 if i == 0 else 25 label.setStyleSheet("QLabel { font-size: %dpx; background-color: %s; color: white; }" % (fsize, player.color.name(),)) self.playerGrid.addWidget(label, i, 0) points = QtGui.QLabel(str(player.points), self) points.setStyleSheet("QLabel { font-size: %dpx; }" % (fsize,)) self.playerGrid.addWidget(points, i, 1, alignment=QtCore.Qt.AlignRight) self.layout.addLayout(self.playerGrid) self.setLayout(self.layout) def keyPressEvent(self, e): if e.key() == QtCore.Qt.Key_Escape: self.close() def closeEvent(self, event): MusicBox.stop_music() event.accept() class DoubleJeopardyWindow(QtGui.QDialog): def __init__(self, player, points, current_player=None, current_points=None, parent=None): super(DoubleJeopardyWindow, self).__init__(parent) self._player = player self._currentPlayer = current_player self._points = points if current_points: self._currentPoints = current_points else: self._currentPoints = points self._setupGui() self.setWindowTitle("Seopardy - Double Jeopardy") # move window to bottom right of screen g = QtGui.QApplication.desktop().screenGeometry() self.show() cPos = self.rect() self.move(g.width() - cPos.width(), g.height() - cPos.height()) def get_chosen_points(self): return self._currentPoints def get_player(self): return self._currentPlayer def _setupGui(self): self.layout = QtGui.QVBoxLayout() header = QtGui.QLabel("Double Jeopardy", self) header.setStyleSheet("QLabel { font-size: 50px; }") self.layout.addWidget(header, alignment=QtCore.Qt.AlignCenter) self.pbtn = QtGui.QPushButton("", self) self._stylePlayerButton() self.pbtn.installEventFilter(KeyGrabber((QtCore.Qt.Key.Key_Left, QtCore.Qt.Key.Key_Right), self)) self.layout.addWidget(self.pbtn) self.pointsbtn = QtGui.QPushButton("% 5s" % self._currentPoints) self.pointsbtn.setStyleSheet("QPushButton { font-size: 50px; border: none; }") self.pointsbtn.installEventFilter(KeyGrabber((QtCore.Qt.Key.Key_Left, QtCore.Qt.Key.Key_Right), self)) self.layout.addWidget(self.pointsbtn, alignment=QtCore.Qt.AlignCenter) self.pointsbtn.setFocus() start = QtGui.QPushButton("Done", self) start.clicked.connect(self.closeIfNotNobody) self.layout.addWidget(start) self.setLayout(self.layout) def closeIfNotNobody(self): if self.get_player() is not None: self.close() def keyPressEvent(self, e): if e.key() == QtCore.Qt.Key_Escape or e.key() == QtCore.Qt.Key_Enter: self.closeIfNotNobody() elif e.key() == QtCore.Qt.Key_Left or e.key() == QtCore.Qt.Key_Right: if self.focusWidget() == self.pbtn: if e.key() == QtCore.Qt.Key_Right: if self._currentPlayer == None: self._currentPlayer = self._player[0] elif self._currentPlayer == self._player[-1]: self._currentPlayer = None else: self._currentPlayer = self._player[self._player.index(self._currentPlayer)+1] elif e.key() == QtCore.Qt.Key_Left: if self._currentPlayer == None: self._currentPlayer = self._player[-1] elif self._currentPlayer == self._player[0]: self._currentPlayer = None else: self._currentPlayer = self._player[self._player.index(self._currentPlayer)-1] self._stylePlayerButton() elif self.focusWidget() == self.pointsbtn: if e.key() == QtCore.Qt.Key_Right and self._currentPoints < self._points*2: self._currentPoints += 50 elif e.key() == QtCore.Qt.Key_Left and self._currentPoints > self._points/2: self._currentPoints += -50 self.pointsbtn.setText("% 4s" % self._currentPoints) def _stylePlayerButton(self): player = None color = None if self._currentPlayer == None: player = "nobody" color = nobodyColor else: player = self._currentPlayer.name color = self._currentPlayer.color self.pbtn.setText(player) self.pbtn.setStyleSheet("QPushButton { background-color: %s; color: white; font-size: 50px; border: none; }" % (color.name(),))