def connectWidgets(self):
     self.speedHSlider.valueChanged.connect(self.speedChangedAction);
     self.startPushButton.clicked.connect(self.startAction);
     self.stopPushButton.clicked.connect(self.stopAction);
     self.simultaneousLettersComboBox.currentIndexChanged.connect(self.maxNumSimultaneousFloatersAction);
     
     CommChannel.getSignal('MorseChallengerSignals.focusLostInadvertently').connect(self.reassertWindowFocus);
 def onTransition(self, eventEnumConstant):
     super(TimeReadingStateTransition, self).onTransition(eventEnumConstant);
     now = time.time();
     transitionTime = self.timeSettingTransition.transitionTime;
     if (now - transitionTime) > GestureButton.FLICK_TIME_THRESHOLD:        
         #print "Too slow"
         pass
     else:
         #print "Fast enough"
         if self.timeSettingTransition.flickDirection == FlickDirection.NORTH:
             CommChannel.getSignal('GestureSignals.flickSig').emit(self.gestureButtonObj, FlickDirection.NORTH);
         elif self.timeSettingTransition.flickDirection == FlickDirection.SOUTH:
             CommChannel.getSignal('GestureSignals.flickSig').emit(self.gestureButtonObj, FlickDirection.SOUTH);
         elif self.timeSettingTransition.flickDirection == FlickDirection.WEST:
             CommChannel.getSignal('GestureSignals.flickSig').emit(self.gestureButtonObj, FlickDirection.WEST);
         elif self.timeSettingTransition.flickDirection == FlickDirection.EAST:
             CommChannel.getSignal('GestureSignals.flickSig').emit(self.gestureButtonObj, FlickDirection.EAST);
             
     # Signal that this transition is done determining whether 
     # a ReEntry event was quick enough to be a flick. 
     # The timeout signal for this 0-delay timer will cause a 
     # transition to the Entered state. (Note: In the PySide version
     # current at this writing, neither custom events nor custom signals
     # seem to work for triggering QEventTransaction or QSignalTransaction,
     # respectively, therefore this hack using a 0-delay timer.): 
     self.gestureButtonObj.flickDetectDoneTrigger.start(0);
 def onTransition(self, wrappedEventObj):
     '''
     First entry to button from an idle state (as opposed to
     from a re-entry (i.e. flick activity) state.
     @param wrappedEventObj: Entry event
     @type wrappedEventObj: QWrappedEvent
     '''
     try:
         super(HotEntryTransition, self).onTransition(wrappedEventObj);
     except TypeError:
         # Printing this will Segfault. Though this branch
         # should not execute if Qt passes in the properly typed wrappedEventObj:
         # It is supposed to be a QEvent, but sometimes comes as QListWidgetItem:
         #print "Type error in HotEntryXition: expected wrappedEventObj, got QListWidgetItem: " + str(wrappedEventObj.text())
         pass
     CommChannel.getSignal('GestureSignals.buttonEnteredSig').emit(self.gestureButtonObj);
 def connectWidgets(self):
     '''
     Connect signals and button slots to their handlers. 
     '''
     CommChannel.getSignal('GestureSignals.flickSig').connect(self.handleButtonFlicks);
     CommChannel.getSignal('GestureSignals.buttonEnteredSig').connect(self.handleButtonEntered);
     CommChannel.getSignal('GestureSignals.buttonExitedSig').connect(self.handleButtonExited);
     self.eraseWordButton.clicked.connect(self.handleEraseWordButton);
     self.addWordButton.clicked.connect(self.handleSaveWordButton);
     self.crossOutTimer.timeout.connect(self.handleCrossoutTimeout);
     # Number pad:
     for button in set([self.numPad1, self.numPad2, self.numPad3, self.numPad4, self.numPad5, 
                       self.numPad6, self.numPad7, self.numPad8, self.numPad9, self.numPad0]):
         button.clicked.connect(partial(self.handleNumPad, button));
     
     # Special characters:
     for button in set([self.commaButton, self.colonButton, self.questionButton, self.atButton, self.leftParenButton, 
                       self.periodButton, self.backspaceButton, self.slashButton, self.spaceButton, self.rightParenButton]):
         button.clicked.connect(partial(self.handleSpecialChars, button));
         
     # Gesture button clicks (switching between dialpad and letter mode:
     for buttonObj in self.letterButtons:
         buttonObj.clicked.connect(partial(self.handleGestureButtonClick, buttonObj));
     
     # Add word dialog box capitalization checkbox state changed:    
     self.addWordDialog.capitalizeCheckbox.stateChanged.connect(self.handleAddDictWordCapitalizeStateChanged);
     # Add word dialog box OK or Cancel botton clicked:
     self.addWordDialog.cancelButton.clicked.connect(partial(self.handleAddDictWordOK_Cancel, self.addWordDialog.cancelButton));
     self.addWordDialog.addWordButton.clicked.connect(partial(self.handleAddDictWordOK_Cancel, self.addWordDialog.addWordButton));
     
     # CopyAll button:
     self.copyButton.clicked.connect(self.handleCopyAll);
    def moveLetters(self):
        self.cyclesSinceLastLaunch += 1;
        # Did floater detonate during previous timeout?:
        detonatedFloaters = self.floatersDetonated.keys();
        for detonatedFloater in detonatedFloaters:
            self.floatersDetonated[detonatedFloater] += 1;
            if self.floatersDetonated[detonatedFloater] > self.DETONATION_VISIBLE_CYCLES:
                self.decommissionFloater(detonatedFloater);
                del self.floatersDetonated[detonatedFloater];
        
        remainingFloaters = self.floatersDetonated.keys();
        for floaterLabel in self.floatersInUse:
            if floaterLabel in remainingFloaters:
                continue;
            geo = floaterLabel.geometry();
            newY = geo.y() + self.PIXELS_TO_MOVE_PER_TIMEOUT;
            savedHeight = geo.height();
            if newY > self.height():
                newY = self.height(); #********
                self.detonate(floaterLabel);
            geo.setY(newY)
            geo.setHeight(savedHeight);
            floaterLabel.setGeometry(geo);
            
        # Done advancing each floater. Is it time to start a new floater?
        numFloatersToLaunch = self.maxNumFloaters - len(self.floatersInUse) + len(self.floatersDetonated);   
        if numFloatersToLaunch > 0:
             # Use the following commented lines if you want the launching of
             # new floaters to be random, e.g. between 2 and 10 timeout intervals:
#            if self.cyclesSinceLastLaunch > random.randint(2,10):
#                self.launchFloaters(self.maxNumFloaters - len(self.floatersInUse));
             self.launchFloaters(numFloatersToLaunch);
        # Launching floaters deactivates the main window, thereby losing the keyboard focus.
        # We can't seem to reassert that focus within this timer interrupt service routine.
        # So, issue a signal that will do it after return:
        CommChannel.getSignal('MorseChallengerSignals.focusLostInadvertently').emit();
    def __init__(self):
        super(MorseChallenger,self).__init__();
        
        self.PIXELS_TO_MOVE_PER_TIMEOUT = 3
        self.ABSOLUTE_MAX_NUM_FLOATERS  = 10;
        self.FLOATER_POINT_SIZE = 20;
        self.LETTER_CHECKBOX_NUM_COLS = 6;
        # Number of timeout cycles that detonated
        # letters stay visible:
        self.DETONATION_VISIBLE_CYCLES = 4;

        self.maxNumFloaters = 1;        
        self.lettersToUse = set();
        self.floatersAvailable = set();
        self.floatersInUse = set();
        self.bookeepingAccessLock = threading.Lock();
        # Floaters that detonated. Values are number of timeouts
        # the detonation was visible:
        self.floatersDetonated = {};
        # Used to launch new floaters at random times:
        self.cyclesSinceLastLaunch = 0;
        
        self.dialogService = DialogService();        
        self.optionsFilePath = os.path.join(os.getenv('HOME'), '.morser/morseChallenger.cfg');

        # Load UI for Morse Challenger:
        relPathQtCreatorFileMainWin = "qt_files/morseChallenger/morseChallenger.ui";
        qtCreatorXMLFilePath = self.findFile(relPathQtCreatorFileMainWin);
        if qtCreatorXMLFilePath is None:
            raise ValueError("Can't find QtCreator user interface file %s" % relPathQtCreatorFileMainWin);
        # Make QtCreator generated UI a child of this instance:
        loadUi(qtCreatorXMLFilePath, self);
        self.windowTitle = "Morse Challenger";
        self.setWindowTitle(self.windowTitle);

        self.iconDir = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'icons')
        self.explosionPixMap = QPixmap(os.path.join(self.iconDir, 'explosion.png'));        

        CommChannel.registerSignals(MorseChallengerSignals);

        self.connectWidgets();
        
        self.generateLetterCheckBoxes();
        self.simultaneousLettersComboBox.addItems(['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']);
        self.generateFloaters();
        
        self.setFocusPolicy(Qt.ClickFocus);
        
        self.letterMoveTimer = QTimer();
        self.letterMoveTimer.setInterval(self.timerIntervalFromSpeedSlider()); # milliseconds
        self.letterMoveTimer.setSingleShot(False);
        self.letterMoveTimer.timeout.connect(self.moveLetters);
        
        # Bring options from config file:
        self.setOptions();
        
        # Styling:
        self.createColors();
        self.setStyleSheet("QWidget{background-color: %s}" % self.lightBlueColor.name());

        self.show();
 def startTiming(self):
     self.resetAll();
     self.currentlyTiming = True;
     self.timerCocked = True;
     self.morseWin.blinkCrosshair(doBlink=True)
     CommChannel.getSignal('MorseInputSignals.letterDone').connect(self.newLetter);
 def onTransition(self, wrappedEventObj):
     super(HotExitTransition, self).onTransition(wrappedEventObj);
     CommChannel.getSignal('GestureSignals.buttonExitedSig').emit(self.gestureButtonObj); 
    def initSignals(self):
        #*******************
#        self.signals = GestureSignals();        
#        CommChannel.getInstance().registerSignals(self.signals);
        #CommChannel.getInstance().registerSignals(GestureSignals);
        CommChannel.registerSignals(GestureSignals);
        self.gestureButtonObj.flickDetectDoneTrigger.start(0);
        
# --------------------------  Testing ---------------------------            
        
if __name__ == "__main__":
    
    @QtCore.Slot(QPushButton)
    def ackButtonEntered(buttonObj):
        print "Button entered."
        
    @QtCore.Slot(QPushButton)
    def ackButtonExited(buttonObj):
        print "Button exited."
        
    @QtCore.Slot(QPushButton,int)
    def ackButtonFlicked(buttonObj, direction):
        print "Button flicked: %s." % FlickDirection.toString(direction);
        

    app = QApplication(sys.argv);
    b = GestureButton("Foo");
    b.setFixedSize(200,250);
    enteredSig = CommChannel.getSignal('GestureSignals.buttonEnteredSig'); 
    enteredSig.connect(ackButtonEntered);
    CommChannel.getSignal('GestureSignals.buttonExitedSig').connect(ackButtonExited);
    CommChannel.getSignal('GestureSignals.flickSig').connect(ackButtonFlicked);
    b.show()
    app.exec_();
    sys.exit();