def __init__(self, action, collection, name): QTreeWidgetItem.__init__(self) self.collection = collection self.name = name self.setIcon(0, action.icon()) self.setText(0, actioncollection.removeAccels(action.text())) self._shortcuts = {}
def __init__(self, dialog): super(Shortcuts, self).__init__(dialog) layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) self.setLayout(layout) self.scheme = SchemeSelector(self) layout.addWidget(self.scheme) self.tree = QTreeWidget(self) self.tree.setHeaderLabels([_("Command"), _("Shortcut")]) self.tree.setRootIsDecorated(False) self.tree.setColumnCount(2) self.tree.setAllColumnsShowFocus(True) self.tree.setAnimated(True) layout.addWidget(self.tree) self.edit = QPushButton(icons.get("configure-shortcuts"), '') layout.addWidget(self.edit) # signals self.scheme.currentChanged.connect(self.slotSchemeChanged) self.scheme.changed.connect(self.changed) self.tree.currentItemChanged.connect(self.slotCurrentItemChanged) self.tree.itemDoubleClicked.connect(self.editCurrentItem) self.edit.clicked.connect(self.editCurrentItem) # make a dict of all actions with the actions as key and the names as # value, with the collection prepended (for loading/saving) win = dialog.parent() allactions = {} for collection in actioncollectionmanager.manager(win).actionCollections(): for name, action in collection.actions().items(): allactions[action] = (collection, name) # keep a list of actions not in the menu structure left = allactions.keys() def childactions(menu): for a in menu.actions(): if a.menu(): for a in childactions(a.menu()): yield a elif a in left: yield a left.remove(a) # present the actions nicely ordered as in the menus for a in win.menuBar().actions(): menuitem = QTreeWidgetItem() menu = a.menu() text = actioncollection.removeAccels(a.text()) for a in childactions(menu): menuitem.addChild(ShortcutItem(a, *allactions[a])) if menuitem.childCount(): menuitem.setText(0, _("Menu {name}:").format(name=text)) self.tree.addTopLevelItem(menuitem) menuitem.setExpanded(True) menuitem.setFlags(Qt.ItemIsEnabled) # disable selection # sort leftover actions left.sort(key=lambda i: i.text()) # show actions that are left, grouped by collection titlegroups = {} for a in left[:]: # copy collection, name = allactions[a] if collection.title(): titlegroups.setdefault(collection.title(), []).append(a) left.remove(a) for title in sorted(titlegroups): item = QTreeWidgetItem(["{0}:".format(title)]) for a in titlegroups[title]: item.addChild(ShortcutItem(a, *allactions[a])) self.tree.addTopLevelItem(item) item.setExpanded(True) item.setFlags(Qt.ItemIsEnabled) # disable selection # show other actions that were not in the menus item = QTreeWidgetItem([_("Other commands:")]) for a in left: if a.text() and not a.menu(): item.addChild(ShortcutItem(a, *allactions[a])) if item.childCount(): self.tree.addTopLevelItem(item) item.setExpanded(True) item.setFlags(Qt.ItemIsEnabled) # disable selection item = self.tree.topLevelItem(0).child(0) if _lastaction: # find the previously selected item for i in self.items(): if i.name == _lastaction: item = i break self.tree.setCurrentItem(item) self.tree.resizeColumnToContents(0)
def editAction(self, parent, action, default=None, skip=None): """Edits the keyboard shortcut for a single action. Returns True if editing was Ok, False if cancelled. parent is the widget to show the dialog above. default gives None or a list with QKeySequence objects that are the default shortcut. Use skip to give the action to skip (e.g. the action that is about to be changed). skip can also be a tuple (collection, name) to define the action to skip. Just uses the dialog in widgets.shortcuteditdialog but implements conflict checking (without altering other shortcuts. The implementation of conflict checking in preferences/shortcuts.py also can change other shortcuts in the prefs dialog.) """ skip_ = lambda: a is skip if skip is None: skip = action elif isinstance(skip, tuple): skip_ = lambda: (collection, name) == skip from widgets import shortcuteditdialog dlg = shortcuteditdialog.ShortcutEditDialog(parent) with util.deleteLater(dlg): while dlg.editAction(action, default): # conflict checking shortcuts = action.shortcuts() if shortcuts: conflicts = {} for collection in self.actionCollections(): for name, a in collection.actions().items(): # we use collection.shortcuts(name) instead of a.shortcuts() # because the (real) actions returned by ShortcutCollection.action() # don't have the shortcuts set. if not skip_() and collection.shortcuts(name): for s1 in collection.shortcuts(name): for s2 in action.shortcuts(): if s2.matches(s1) or s1.matches(s2): # s2 conflicts with a conflicts.setdefault(a, []).append(s2) # do shortcuts remain? if s2 in shortcuts: shortcuts.remove(s2) if conflicts: msg = [_("This shortcut conflicts with the following command:", "This shortcut conflicts with the following commands:", len(conflicts))] msg.append("<br/>".join("{name} ({key})".format( name = actioncollection.removeAccels(a.text()), key=' \u2014 '.join(s.toString() for s in conflicts[a])) for a in conflicts)) msg = '<p>{0}</p>'.format('</p><p>'.join(msg)) box = QMessageBox(QMessageBox.Warning, _("Shortcut Conflict"), msg, QMessageBox.Ok | QMessageBox.Cancel, parent) box.button(QMessageBox.Ok).setText(_("Edit again")) if box.exec_() == QMessageBox.Ok: action.setShortcuts(shortcuts) continue else: break return True return False