示例#1
0
class QuizClient():
    """
    The client represents the application itself and controls the changes to the
    view, as propagated by the models.
    It also reacts to user events.
    """

    def __init__(self):
        self.ui = Application(self._onBtnPress, self._cleanup)
        self.activeQuestion = None
        self._answerLocked = True
        self._initUmundo()
        self._scoreboard = Scoreboard(self)
        self._leader = Leader(self, self._scoreboard)

        # After(!) everything is initialized => register handlers
        self._publisher.setGreeter(self._greeter)
        self._subscriber.setReceiver(self._receiver)

    def _initUmundo(self):
        # Explicit references to umundo objects are required!
        self._greeter = QuizGreeter(self)
        self._receiver = QuizReceiver(self)

        self._publisher = umundo.Publisher(config.QUESTION_CHANNEL)
        self._subscriber = umundo.Subscriber(config.QUESTION_CHANNEL)

        self._node = umundo.Node()
        self._node.addPublisher(self._publisher)
        self._node.addSubscriber(self._subscriber)

        self._disc = umundo.Discovery(umundo.Discovery.MDNS)
        self._disc.add(self._node)

    def _cleanup(self):
        self._disc = None
        self._node = None
        self._subscriber = None
        self._publisher = None
        self._greeter = None
        self._receiver = None

    def _onBtnPress(self, pressedBtn):
        if self._answerLocked:
            return

        mapping = {
            Application.BTN_A: 0,
            Application.BTN_B: 1,
            Application.BTN_C: 2,
            Application.BTN_D: 3,
        }
        answer = Answer(self.ui, self.activeQuestion, mapping[pressedBtn])
        correctBtn = next(k for k,v in mapping.items() if v == int(self.activeQuestion.getCorrectAnswer()))

        self.ui.highlightBtn(correctBtn, None if pressedBtn == correctBtn else pressedBtn)
        self._answerLocked = True
        self.send(answer.toDict(), True)

    def _toMsg(self, kvMap):
        msg = umundo.Message()

        for k in kvMap:
            msg.putMeta(str(k), str(kvMap[k]))

        return msg

    def send(self, kvMap, dispatchToSelf=False):
        """
        Expects a dictonary which is then serialized and published as umundo message.

        kvMap -- The dictonary key-value pairs. Only string keys and string
            values are allowed. Each pair is attached as meta data to the message.
        dispatchToSelf -- Whether to simulate the receiving of the sent message.
        """
        # print("Send message " + kvMap["type"])
        message = self._toMsg(kvMap)
        self._publisher.send(message)

        if dispatchToSelf:
            self.dispatch(message)

    def hasSubscribers(self):
        """Whether there are any subscribers (players) besides ourselves."""
        return self._publisher.waitForSubscribers(0) > 0

    def onSubscriber(self, pub, subStub):
        """
        Gets called whenever a new player is participating.
        We respond via a welcome message to announce him our username.
        """
        self.send({
            "type": config.Message.WELCOME,
            "username": self.ui.username,
        })

    def onSubscriberLeave(self, pub, subStub):
        pass

    def onQuestion(self, msg):
        """
        Gets called when a new question is received.
        The received question is then displayed in the GUI.
        """
        self._answerLocked = False
        self.activeQuestion = Question.fromMsg(msg)
        self.ui.updateQuestion(self.activeQuestion)

    def dispatch(self, msg):
        """Delegate incoming messages to the appropriate handler functions"""
        # print ("Dispatch " + msg.getMeta("type"))

        mapping = {
            config.Message.HEARTBEAT: self._leader.dispatchHeartbeat,
            config.Message.PRIORITY: self._leader.dispatchPriority,
            config.Message.ANSWER: self._leader.dispatchAnswer,
            config.Message.WELCOME: self._scoreboard.dispatchWelcome,
            config.Message.SCORES: self._scoreboard.dispatchScores,
            config.Message.QUESTION: self.onQuestion,
        }

        msgType = msg.getMeta("type")
        if msgType in mapping:
            mapping[msgType](msg)
        else:
            print("Unknown message type: ", msgType)

    def start(self):
        """Starts the eventloop of the GUI"""
        self.ui.schedule(500, self._leader.tick)
        self.ui.run()