Beispiel #1
0
    def sig_on_key(self, keyseq_str):
        key_sequence = QKeySequence(keyseq_str)
        if key_sequence.isEmpty():
            raise RuntimeError("invalid key binding: '%s'" % keyseq_str)

        shortcut = QShortcut(key_sequence, self.editormap_widget)
        signal = Signal()

        def on_key(*args):
            pos = self.editormap_widget.mapFromGlobal(QCursor.pos())
            # pos = self.gc_state.screen2world(Point.from_qt(pos))
            signal(pos.x(), pos.y())

        shortcut.activated.connect(on_key)

        return signal
Beispiel #2
0
    def setValue(self, value):
        """Sets the value and validates, returns True if OK.

        Returns False if validation fails but the old value is OK,
        or if the value is unchanged.
        Raises a ValueError if validation fails without an old value.
        Arguments:
            value -- a key string value to set
        """
        key = QKeySequence(value)
        if value and key.isEmpty():
            if self.value == None:
                raise ValueError
            return False
        if value != self.value:
            self.value = key
            return True
        return False
class KeySequenceButton(QPushButton):
    
    def __init__(self, parent=None):
        super(KeySequenceButton, self).__init__(parent)
        self.setIcon(icons.get("configure"))
        self._modifierlessAllowed = False
        self._seq = QKeySequence()
        self._timer = QTimer()
        self._timer.setSingleShot(True)
        self._isrecording = False
        self.clicked.connect(self.startRecording)
        self._timer.timeout.connect(self.doneRecording)

    def setKeySequence(self, seq):
        self._seq = seq
        self.updateDisplay()

    def keySequence(self):
        if self._isrecording:
            self.doneRecording()
        return self._seq
    
    def updateDisplay(self):
        if self._isrecording:
            s = self._recseq.toString(QKeySequence.NativeText).replace('&', '&&')
            if self._modifiers:
                if s: s += ","
                s += QKeySequence(self._modifiers).toString(QKeySequence.NativeText)
            elif self._recseq.isEmpty():
                s = _("Input")
            s += " ..."
        else:
            s = self._seq.toString(QKeySequence.NativeText).replace('&', '&&')
        self.setText(s)

    def isRecording(self):
        return self._isrecording
        
    def event(self, ev):
        if self._isrecording:
            # prevent Qt from special casing Tab and Backtab
            if ev.type() == QEvent.KeyPress:
                self.keyPressEvent(ev)
                return True
        return super(KeySequenceButton, self).event(ev)
        
    def keyPressEvent(self, ev):
        if not self._isrecording:
            return super(KeySequenceButton, self).keyPressEvent(ev)
        if ev.isAutoRepeat():
            return
        modifiers = int(ev.modifiers() & (Qt.SHIFT | Qt.CTRL | Qt.ALT | Qt.META))
        ev.accept()
        
        key = ev.key()
        # check if key is a modifier or a character key without modifier (and if that is allowed)
        if (
            # don't append the key if the key is -1 (garbage) or a modifier ...
            key not in (-1, Qt.Key_AltGr, Qt.Key_Shift, Qt.Key_Control,
                            Qt.Key_Alt, Qt.Key_Meta, Qt.Key_Menu)
            # or if this is the first key and without modifier and modifierless keys are not allowed
            and (self._modifierlessAllowed
                 or self._recseq.count() > 0
                 or modifiers & ~Qt.SHIFT
                 or not ev.text()
                 or (modifiers & Qt.SHIFT
                     and key in (Qt.Key_Return, Qt.Key_Space, Qt.Key_Tab, Qt.Key_Backtab,
                                 Qt.Key_Backspace, Qt.Key_Delete, Qt.Key_Escape)))):
            # change Shift+Backtab into Shift+Tab
            if key == Qt.Key_Backtab and modifiers & Qt.SHIFT:
                key = Qt.Key_Tab | modifiers
            # remove the Shift modifier if it doesn't make sense
#            elif (Qt.Key_Exclam <= key <= Qt.Key_At
#                  or Qt.Key_Z < key <= 0x0ff):
#                key = key | (modifiers & ~Qt.SHIFT)
            else:
                key = key | modifiers
            
            # append max. 4 keystrokes
            if self._recseq.count() < 4:
                l = list(self._recseq)
                l.append(key)
                self._recseq = QKeySequence(*l)
        
        self._modifiers = modifiers
        self.controlTimer()
        self.updateDisplay()
        
    def keyReleaseEvent(self, ev):
        if not self._isrecording:
            return super(KeySequenceButton, self).keyReleaseEvent(ev)
        modifiers = int(ev.modifiers() & (Qt.SHIFT | Qt.CTRL | Qt.ALT | Qt.META))
        ev.accept()
        
        self._modifiers = modifiers
        self.controlTimer()
        self.updateDisplay()
    
    def hideEvent(self, ev):
        if self._isrecording:
            self.cancelRecording()
        super(KeySequenceButton, self).hideEvent(ev)
        
    def controlTimer(self):
        if self._modifiers or self._recseq.isEmpty():
            self._timer.stop()
        else:
            self._timer.start(600)
    
    def startRecording(self):
        self.setFocus(True) # because of QTBUG 17810
        self.setDown(True)
        self.setStyleSheet("text-align: left;")
        self._isrecording = True
        self._recseq = QKeySequence()
        self._modifiers = int(QApplication.keyboardModifiers() & (Qt.SHIFT | Qt.CTRL | Qt.ALT | Qt.META))
        self.grabKeyboard()
        self.updateDisplay()
        
    def doneRecording(self):
        self._seq = self._recseq
        self.cancelRecording()
        self.clearFocus()
        self.parentWidget().keySequenceChanged.emit(self.parentWidget().num())
        
    def cancelRecording(self):
        if not self._isrecording:
            return
        self.setDown(False)
        self.setStyleSheet(None)
        self._isrecording = False
        self.releaseKeyboard()
        self.updateDisplay()
class KeySequenceButton(QPushButton):
    def __init__(self, parent=None):
        super(KeySequenceButton, self).__init__(parent)
        self.setIcon(icons.get("configure"))
        self._modifierlessAllowed = False
        self._seq = QKeySequence()
        self._timer = QTimer()
        self._timer.setSingleShot(True)
        self._isrecording = False
        self.clicked.connect(self.startRecording)
        self._timer.timeout.connect(self.doneRecording)

    def setKeySequence(self, seq):
        self._seq = seq
        self.updateDisplay()

    def keySequence(self):
        if self._isrecording:
            self.doneRecording()
        return self._seq

    def updateDisplay(self):
        if self._isrecording:
            s = self._recseq.toString(QKeySequence.NativeText).replace(
                '&', '&&')
            if self._modifiers:
                if s: s += ","
                s += QKeySequence(self._modifiers).toString(
                    QKeySequence.NativeText)
            elif self._recseq.isEmpty():
                s = _("Input")
            s += " ..."
        else:
            s = self._seq.toString(QKeySequence.NativeText).replace('&', '&&')
        self.setText(s)

    def isRecording(self):
        return self._isrecording

    def event(self, ev):
        if self._isrecording:
            # prevent Qt from special casing Tab and Backtab
            if ev.type() == QEvent.KeyPress:
                self.keyPressEvent(ev)
                return True
        return super(KeySequenceButton, self).event(ev)

    def keyPressEvent(self, ev):
        if not self._isrecording:
            return super(KeySequenceButton, self).keyPressEvent(ev)
        if ev.isAutoRepeat():
            return
        modifiers = int(ev.modifiers()
                        & (Qt.SHIFT | Qt.CTRL | Qt.ALT | Qt.META))
        ev.accept()

        key = ev.key()
        # check if key is a modifier or a character key without modifier (and if that is allowed)
        if (
                # don't append the key if the key is -1 (garbage) or a modifier ...
                key not in (-1, Qt.Key_AltGr, Qt.Key_Shift, Qt.Key_Control,
                            Qt.Key_Alt, Qt.Key_Meta, Qt.Key_Menu)
                # or if this is the first key and without modifier and modifierless keys are not allowed
                and (self._modifierlessAllowed or self._recseq.count() > 0
                     or modifiers & ~Qt.SHIFT or not ev.text() or
                     (modifiers & Qt.SHIFT and key in
                      (Qt.Key_Return, Qt.Key_Space, Qt.Key_Tab, Qt.Key_Backtab,
                       Qt.Key_Backspace, Qt.Key_Delete, Qt.Key_Escape)))):
            # change Shift+Backtab into Shift+Tab
            if key == Qt.Key_Backtab and modifiers & Qt.SHIFT:
                key = Qt.Key_Tab | modifiers
            # remove the Shift modifier if it doesn't make sense
#            elif (Qt.Key_Exclam <= key <= Qt.Key_At
#                  or Qt.Key_Z < key <= 0x0ff):
#                key = key | (modifiers & ~Qt.SHIFT)
            else:
                key = key | modifiers

            # append max. 4 keystrokes
            if self._recseq.count() < 4:
                l = list(self._recseq)
                l.append(key)
                self._recseq = QKeySequence(*l)

        self._modifiers = modifiers
        self.controlTimer()
        self.updateDisplay()

    def keyReleaseEvent(self, ev):
        if not self._isrecording:
            return super(KeySequenceButton, self).keyReleaseEvent(ev)
        modifiers = int(ev.modifiers()
                        & (Qt.SHIFT | Qt.CTRL | Qt.ALT | Qt.META))
        ev.accept()

        self._modifiers = modifiers
        self.controlTimer()
        self.updateDisplay()

    def hideEvent(self, ev):
        if self._isrecording:
            self.cancelRecording()
        super(KeySequenceButton, self).hideEvent(ev)

    def controlTimer(self):
        if self._modifiers or self._recseq.isEmpty():
            self._timer.stop()
        else:
            self._timer.start(600)

    def startRecording(self):
        self.setFocus(True)  # because of QTBUG 17810
        self.setDown(True)
        self.setStyleSheet("text-align: left;")
        self._isrecording = True
        self._recseq = QKeySequence()
        self._modifiers = int(QApplication.keyboardModifiers()
                              & (Qt.SHIFT | Qt.CTRL | Qt.ALT | Qt.META))
        self.grabKeyboard()
        self.updateDisplay()

    def doneRecording(self):
        self._seq = self._recseq
        self.cancelRecording()
        self.clearFocus()
        self.parentWidget().keySequenceChanged.emit(self.parentWidget().num())

    def cancelRecording(self):
        if not self._isrecording:
            return
        self.setDown(False)
        self.setStyleSheet(None)
        self._isrecording = False
        self.releaseKeyboard()
        self.updateDisplay()
Beispiel #5
0
class KeySequenceEditor(QQuickItem):

    _logger = _module_logger.getChild('KeySequenceEditor')

    ##############################################

    def __init__(self, parent):

        super().__init__(parent)

        self._default_sequence = QKeySequence()  # default sequence
        self._edited_sequence = QKeySequence()  # current/edited sequence
        self._new_sequence = QKeySequence()  # customised sequence

        self._reset_pressed_keys()

    ##############################################

    def _reset_pressed_keys(self):
        self._logger.info('Clearing pressed keys')
        self._pressed_keys = []

    ##############################################

    default_sequence_changed = Signal()

    @Property(str, notify=default_sequence_changed)
    def default_sequence(self):
        return self._default_sequence.toString()

    ##############################################

    @default_sequence.setter
    def default_sequence(self, default_sequence):

        if default_sequence != self._default_sequence.toString():
            self._default_sequence = QKeySequence(default_sequence,
                                                  QKeySequence.PortableText)
            self._set_edited_sequence('')  # Fixme: why reset ???
            self.new_sequence = ''
            self.default_sequence_changed.emit()
            # This might not always be the case, I'm just lazy.
            self.is_customised_changed.emit()
            self.display_sequence_changed.emit()

    ##############################################

    new_sequence_changed = Signal()

    @Property(str, notify=new_sequence_changed)
    def new_sequence(self):
        return self._new_sequence.toString()

    @new_sequence.setter
    def new_sequence(self, new_sequence):

        if new_sequence != self._new_sequence.toString():
            self._new_sequence = QKeySequence(new_sequence,
                                              QKeySequence.PortableText)
            self._logger.info('Set new sequence to {}'.format(
                self._new_sequence.toString()))
            self.new_sequence_changed.emit()
            self.is_customised_changed.emit()
            self.display_sequence_changed.emit()

    ##############################################

    display_sequence_changed = Signal()

    @Property(str, notify=display_sequence_changed)
    def display_sequence(self):
        """Text to show in the sequence editor"""

        if self.hasActiveFocus():
            # we are editing the sequence
            sequence = self._edited_sequence
        elif self._new_sequence.isEmpty():
            # no new sequence
            sequence = self._default_sequence
        else:
            sequence = self._new_sequence

        return sequence.toString()

    ##############################################

    is_customised_changed = Signal()

    @Property(bool, notify=is_customised_changed)
    def is_customised(self):
        """Flag to indicate a new valid sequence is set"""
        return not self._new_sequence.isEmpty(
        ) and self._new_sequence != self._default_sequence

    ##############################################

    @Slot()
    def reset(self):
        """Reset the sequence to the default one"""
        self._set_edited_sequence(self.default_sequence)
        self.new_sequence = self.default_sequence
        self._reset_pressed_keys()

    ##############################################

    def _set_edited_sequence(self, edited_sequence=''):
        """Update the edited sequence.

        emit *is_customised* and *display_sequence*
        """

        if edited_sequence != self._edited_sequence.toString():
            self._edited_sequence = QKeySequence(edited_sequence,
                                                 QKeySequence.PortableText)
            self._logger.info('Edited sequence changed to {}'.format(
                self._edited_sequence.toString()))
            self.is_customised_changed.emit()
            self.display_sequence_changed.emit()

    ##############################################

    def keyPressEvent(self, event):
        """Handler when a key is pressed.

        * use Escape to leave the control
        * use Enter to accept the sequence

        """

        if event.key() == Qt.Key_Escape:
            self.setFocus(False)

        elif event.key() == Qt.Key_Return:
            self._accept()

        elif not event.isAutoRepeat():
            modifiers = 0
            # event.modifiers().testFlag(...)
            if event.modifiers() & Qt.ControlModifier:
                modifiers |= Qt.CTRL
            if event.modifiers() & Qt.ShiftModifier:
                modifiers |= Qt.SHIFT
            if event.modifiers() & Qt.AltModifier:
                modifiers |= Qt.ALT
            if event.modifiers() & Qt.MetaModifier:
                modifiers |= Qt.META

            if Qt.Key_Shift <= event.key() <= Qt.Key_Meta:
                self._logger.info(
                    'Only modifiers were pressed ({} / {} / {} ignoring'.
                    format(event.text(), _key_name(event.key()),
                           QKeySequence(event.key()).toString()))

            else:
                self._pressed_keys.append(event.key() | modifiers)
                self._logger.info(
                    'Adding key {} / {} / {} with modifiers ({}) to pressed keys'
                    .format(
                        event.text(),
                        _key_name(event.key()),
                        QKeySequence(event.key()).toString(),
                        # UnicodeEncodeError: 'utf-8' codec can't encode character '\udc21' in position 188: surrogates not allowed
                        QKeySequence(self._pressed_keys[-1]).toString(),
                    ))

                sequence = QKeySequence(*self._pressed_keys)  # up to 4 keys
                self._set_edited_sequence(sequence.toString())

                if len(self._pressed_keys) == 4:
                    # That was the last key out of four possible keys end it here.
                    self._accept()

        event.accept()

    ##############################################

    def keyReleaseEvent(self, event):
        event.accept()

    ##############################################

    def focusInEvent(self, event):
        event.accept()
        # The text displaying the shortcut should be cleared when editing begins.
        self.display_sequence_changed.emit()

    ##############################################

    def focusOutEvent(self, event):
        event.accept()
        self._cancel()

    ##############################################

    def _accept(self):
        """Update *new_sequence* if the input is valid."""

        self._logger.info('Attempting to accept input...')

        # If there hasn't been anything new successfully entered yet, check against the original
        # sequence, otherwise check against the latest successfully entered sequence.
        # Note: is_customised() assumes that an empty sequence isn't possible we might want to account
        # for this in the future.
        if ((self._edited_sequence != self._default_sequence)
                or (self.is_customised
                    and self._edited_sequence != self._new_sequence)):
            if self._validate(self._edited_sequence):
                self._logger.info('Input valid')
                self.new_sequence = self._edited_sequence.toString()
            else:
                self._logger.info('Input invalid')
                self._cancel()
        else:
            self._logger.info('Nothing has changed in the input')
            # Nothing's changed.

        self._reset_pressed_keys()
        self.setFocus(False)

    ##############################################

    def _cancel(self):

        self._reset_pressed_keys()
        if self._edited_sequence.isEmpty():
            # If the edited sequence is empty, setting it to an empty string
            # obviously won't change anything, and it will return early.
            # We need the display sequence to update though, so call it here.
            self.display_sequence_changed.emit()
        else:
            self._set_edited_sequence('')

    ##############################################

    def _validate(self, sequence):
        """Method to validate the new sequence"""

        self._logger.info('Validating key sequence {} ...'.format(
            sequence.toString()))
        valid = True  # False
        # do some checks
        return valid