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
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()
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