def __init__(self, parent, context, name, sequence, shortcuts): super(ShortcutEditor, self).__init__(parent) self.context = context self.npressed = 0 self.keys = set() self.key_modifiers = set() self.key_non_modifiers = list() self.key_text = list() self.sequence = sequence self.new_sequence = None self.edit_state = True self.shortcuts = shortcuts # Widgets self.label_info = QLabel(_("Press the new shortcut and select 'Ok': \n" "(Press 'Tab' once to switch focus between " "shortcut entry and buttons)")) self.label_current_sequence = QLabel(_("Current shortcut:")) self.text_current_sequence = QLabel(sequence) self.label_new_sequence = QLabel(_("New shortcut:")) self.text_new_sequence = CustomLineEdit(self) self.text_new_sequence.setPlaceholderText(sequence) self.helper_button = HelperToolButton() bbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) self.button_ok = bbox.button(QDialogButtonBox.Ok) self.button_cancel = bbox.button(QDialogButtonBox.Cancel) # Setup widgets self.setWindowTitle(_('Shortcut: {0}').format(name)) self.button_ok.setFocusPolicy(Qt.NoFocus) self.button_ok.setEnabled(False) self.button_cancel.setFocusPolicy(Qt.NoFocus) self.helper_button.setToolTip('') self.helper_button.setFocusPolicy(Qt.NoFocus) style = """ QToolButton { margin:1px; border: 0px solid grey; padding:0px; border-radius: 0px; }""" self.helper_button.setStyleSheet(style) # Layout spacing = 24 layout_sequence = QGridLayout() layout_sequence.addWidget(self.label_info, 0, 0, 1, 3) layout_sequence.addItem(QSpacerItem(spacing, spacing), 1, 0, 1, 2) layout_sequence.addWidget(self.label_current_sequence, 2, 0) layout_sequence.addWidget(self.text_current_sequence, 2, 2) layout_sequence.addWidget(self.label_new_sequence, 3, 0) layout_sequence.addWidget(self.helper_button, 3, 1) layout_sequence.addWidget(self.text_new_sequence, 3, 2) layout = QVBoxLayout() layout.addLayout(layout_sequence) layout.addSpacing(spacing) layout.addWidget(bbox) self.setLayout(layout) # Signals bbox.accepted.connect(self.accept) bbox.rejected.connect(self.reject)
class ShortcutEditor(QDialog): """A dialog for entering key sequences.""" def __init__(self, parent, context, name, sequence, shortcuts): super(ShortcutEditor, self).__init__(parent) self.context = context self.npressed = 0 self.keys = set() self.key_modifiers = set() self.key_non_modifiers = list() self.key_text = list() self.sequence = sequence self.new_sequence = None self.edit_state = True self.shortcuts = shortcuts # Widgets self.label_info = QLabel(_("Press the new shortcut and select 'Ok': \n" "(Press 'Tab' once to switch focus between " "shortcut entry and buttons)")) self.label_current_sequence = QLabel(_("Current shortcut:")) self.text_current_sequence = QLabel(sequence) self.label_new_sequence = QLabel(_("New shortcut:")) self.text_new_sequence = CustomLineEdit(self) self.text_new_sequence.setPlaceholderText(sequence) self.helper_button = HelperToolButton() bbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) self.button_ok = bbox.button(QDialogButtonBox.Ok) self.button_cancel = bbox.button(QDialogButtonBox.Cancel) # Setup widgets self.setWindowTitle(_('Shortcut: {0}').format(name)) self.button_ok.setFocusPolicy(Qt.NoFocus) self.button_ok.setEnabled(False) self.button_cancel.setFocusPolicy(Qt.NoFocus) self.helper_button.setToolTip('') self.helper_button.setFocusPolicy(Qt.NoFocus) style = """ QToolButton { margin:1px; border: 0px solid grey; padding:0px; border-radius: 0px; }""" self.helper_button.setStyleSheet(style) # Layout spacing = 24 layout_sequence = QGridLayout() layout_sequence.addWidget(self.label_info, 0, 0, 1, 3) layout_sequence.addItem(QSpacerItem(spacing, spacing), 1, 0, 1, 2) layout_sequence.addWidget(self.label_current_sequence, 2, 0) layout_sequence.addWidget(self.text_current_sequence, 2, 2) layout_sequence.addWidget(self.label_new_sequence, 3, 0) layout_sequence.addWidget(self.helper_button, 3, 1) layout_sequence.addWidget(self.text_new_sequence, 3, 2) layout = QVBoxLayout() layout.addLayout(layout_sequence) layout.addSpacing(spacing) layout.addWidget(bbox) self.setLayout(layout) # Signals bbox.accepted.connect(self.accept) bbox.rejected.connect(self.reject) def keyPressEvent(self, e): """Qt override""" key = e.key() self.npressed += 1 self.key_non_modifiers.append(key) self.key_modifiers.add(key) self.key_text.append(e.text()) debug_print('key %s, npressed: %s' % (key, self.npressed)) if key == Qt.Key_unknown: return # The user clicked just and only the special keys # Ctrl, Shift, Alt, Meta. if (key == Qt.Key_Control or key == Qt.Key_Shift or key == Qt.Key_Alt or key == Qt.Key_Meta): return # Check if valid keys if key not in VALID_KEYS: return modifiers = e.modifiers() if modifiers & Qt.ShiftModifier: key += Qt.SHIFT if modifiers & Qt.ControlModifier: key += Qt.CTRL if sys.platform == 'darwin': self.npressed -= 1 debug_print('decrementing') if modifiers & Qt.AltModifier: key += Qt.ALT if modifiers & Qt.MetaModifier: key += Qt.META self.keys.add(key) def toggle_state(self): """Switch between shortcut entry and Accept/Cancel shortcut mode.""" self.edit_state = not self.edit_state if not self.edit_state: self.text_new_sequence.setEnabled(False) if self.button_ok.isEnabled(): self.button_ok.setFocus() else: self.button_cancel.setFocus() else: self.text_new_sequence.setEnabled(True) self.text_new_sequence.setFocus() def nonedit_keyrelease(self, e): """Key release event for non-edit state.""" key = e.key() if key == Qt.Key_Escape: # self.close() pass if key in [Qt.Key_Left, Qt.Key_Right, Qt.Key_Up, Qt.Key_Down]: if self.button_ok.hasFocus(): self.button_cancel.setFocus() else: self.button_ok.setFocus() def keyReleaseEvent(self, e): """Qt override""" self.npressed -= 1 if self.npressed <= 0: if len(self.keys) == 1 and (list(self.keys)[0] == Qt.Key_Tab): self.toggle_state() if not self.edit_state: self.nonedit_keyrelease(e) else: debug_print('keys: {}'.format(self.keys)) if self.keys: self.validate_sequence() self.keys = set() self.key_modifiers = set() self.key_non_modifiers = list() self.key_text = list() self.npressed = 0 def check_conflicts(self): """Check shortcuts for conflicts""" conflicts = [] for index, shortcut in enumerate(self.shortcuts): sequence = str(shortcut.key) if sequence == self.new_sequence and \ (shortcut.context == self.context or shortcut.context == '_' or self.context == '_'): conflicts.append(shortcut) return conflicts def update_warning(self, reset=False): """ """ conflicts = self.check_conflicts() if reset: conflicts = [] widget = self.helper_button if conflicts: tip_title = _('The new entered shorcut conflicts with:') + '\n' tip_body = '' for s in conflicts: tip_body += ' - {0}: {1}\n'.format(s.context, s.name) tip = '{0}{1}'.format(tip_title, tip_body) widget.setIcon(get_std_icon('MessageBoxWarning')) widget.setToolTip(tip) QToolTip.showText(widget.mapToGlobal(QPoint(0, 5)), tip) else: widget.setToolTip('') QToolTip.hideText() widget.setIcon(get_std_icon('DialogApplyButton')) def set_sequence(self, sequence): """Set the new shortcut and update buttons.""" if self.sequence != sequence: self.button_ok.setEnabled(True) reset = False else: self.button_ok.setEnabled(False) reset = True self.text_new_sequence.setText(sequence) self.new_sequence = sequence self.update_warning(reset=reset) def validate_sequence(self): """Provide additional checks for accepting or rejecting shortcuts.""" for mod in MODIFIERS: non_mod = set(self.key_non_modifiers) non_mod.discard(mod) if mod in self.key_non_modifiers: self.key_non_modifiers.remove(mod) self.key_modifiers = self.key_modifiers - non_mod while u'' in self.key_text: self.key_text.remove(u'') self.key_text = [k.upper() for k in self.key_text] # Fix Backtab, Tab issue if os.name == 'nt': if Qt.Key_Backtab in self.key_non_modifiers: idx = self.key_non_modifiers.index(Qt.Key_Backtab) self.key_non_modifiers[idx] = Qt.Key_Tab if len(self.key_modifiers) == 0: # Filter single key allowed if self.key_non_modifiers[0] not in VALID_SINGLE_KEYS: return # Filter elif len(self.key_non_modifiers) > 1: return # QKeySequence accepts a maximum of 4 different sequences if len(self.keys) > 4: # If the user enters more than 4, take the first 4 only self.keys = set([0, 1, 2, 3]) keys = [] for i in range(len(self.keys)): key_seq = 0 for m in self.key_modifiers: key_seq += MODIFIERS[m] key_seq += self.key_non_modifiers[i] keys.append(key_seq) sequence = QKeySequence(*keys) self.set_sequence(sequence.toString())