vibefpmgui.command_palette
View → Command Palette… (⌘⇧P / Ctrl+Shift+P): every command in the menus, found by typing part of its name, as in VS Code.
The commands aren't listed here: they're the menu bar's actions, read when
the palette opens (collect), so a command added to a menu is in the
palette too. A menu that fills itself when it opens (Syntax Colors, Window
Style, Recent Projects…) is asked to, as it is when it's opened. Running a
command triggers its action, as choosing it in its menu does.
1"""View → Command Palette… (⌘⇧P / Ctrl+Shift+P): every command in the menus, 2found by typing part of its name, as in VS Code. 3 4The commands aren't listed here: they're the menu bar's actions, read when 5the palette opens (`collect`), so a command added to a menu is in the 6palette too. A menu that fills itself when it opens (Syntax Colors, Window 7Style, Recent Projects…) is asked to, as it is when it's opened. Running a 8command triggers its action, as choosing it in its menu does. 9""" 10 11from __future__ import annotations 12 13import re 14from typing import NamedTuple 15 16from qtpy.QtCore import QEvent, QRect, QSize, Qt, QTimer 17from qtpy.QtGui import QAction, QColor, QFontMetrics, QIcon, QKeySequence, QPalette 18from qtpy.QtWidgets import ( 19 QApplication, 20 QFrame, 21 QLineEdit, 22 QListWidget, 23 QListWidgetItem, 24 QMenu, 25 QMenuBar, 26 QStyle, 27 QStyledItemDelegate, 28 QVBoxLayout, 29 QWidget, 30) 31 32SEPARATOR = " › " 33MAX_RECENT = 12 34COMMAND_ROLE = Qt.UserRole + 1 # an item's Command 35 36 37class Command(NamedTuple): 38 path: tuple[str, ...] # its menus' titles, then its own ("View", "Theme", "Nord") 39 action: QAction 40 41 @property 42 def name(self) -> str: 43 return self.path[-1] 44 45 @property 46 def title(self) -> str: 47 return SEPARATOR.join(self.path) 48 49 50def clean(text: str) -> str: 51 """A menu text as shown: no "&" for its mnemonic ("&&" is an "&"), no 52 tab and what's after it (a shortcut some menus put in their text).""" 53 text = text.split("\t", 1)[0] 54 return re.sub(r"&(.)", r"\1", text).strip() 55 56 57def collect(menu_bar: QMenuBar) -> list[Command]: 58 """Every command in the menu bar's menus, in menu order: not separators, 59 submenus themselves or hidden actions. 60 61 A submenu is found from its menu (``QMenu.menuAction()``), never with 62 ``QAction.menu()``: in PySide that leaves a menu the menu bar made (and 63 its actions) deleted as far as Python knows, which broke the app's own 64 menus.""" 65 commands: list[Command] = [] 66 seen: set[int] = set() 67 submenus = {m.menuAction(): m for m in menu_bar.window().findChildren(QMenu)} 68 69 def walk(menu: QMenu, path: tuple[str, ...]): 70 menu.aboutToShow.emit() # (a menu filled when it opens: filled now, as then) 71 submenus.update({m.menuAction(): m for m in menu.findChildren(QMenu)}) # (made by filling it) 72 for action in menu.actions(): 73 if action.isSeparator() or not action.isVisible() or not clean(action.text()): 74 continue 75 here = (*path, clean(action.text())) 76 submenu = submenus.get(action) 77 if submenu is not None: 78 walk(submenu, here) 79 elif id(action) not in seen: 80 seen.add(id(action)) 81 commands.append(Command(here, action)) 82 83 for action in menu_bar.actions(): 84 submenu = submenus.get(action) 85 if submenu is not None and action.isVisible(): 86 walk(submenu, (clean(action.text()),)) 87 return commands 88 89 90def _word_starts(text: str) -> set[int]: 91 return {0} | {i for i in range(1, len(text)) if not text[i - 1].isalnum() and text[i].isalnum()} 92 93 94def _subsequence(word: str, text: str, starts: set[int], prefer: bool = True) -> int | None: 95 """How well ``word``'s letters appear, in order, in ``text`` (None: they 96 don't): each letter counts, more at a word's start or right after the 97 one before. Each letter goes at the next word's start that has it, if 98 any (``prefer``); if that leaves a later letter unmatched, at the next 99 place it is.""" 100 score, at, last, first = 0, 0, -2, None 101 for ch in word: 102 found = text.find(ch, at) 103 if found < 0: 104 return None if prefer is False else _subsequence(word, text, starts, prefer=False) 105 if prefer and found != last + 1 and found not in starts: 106 later = found 107 while later >= 0 and later not in starts: 108 later = text.find(ch, later + 1) 109 if later >= 0: 110 found = later 111 gap = found - last - 1 if last >= 0 else 0 112 score += 1 + (3 if found in starts else 0) + (2 if found == last + 1 else 0) - min(gap, 4) 113 first = found if first is None else first 114 last, at = found, found + 1 115 if first is not None and last - first + 1 > max(3 * len(word), len(word) + 8): 116 return None # (its letters scattered far apart: not what was meant) 117 return score 118 119 120def score(query: str, command: Command) -> int | None: 121 """How well a command matches what's typed (None: it doesn't). Each word 122 typed must match, letters in order (so "mvup" finds Move Line Up); in 123 the command's own name counts more than in its menus' names.""" 124 words = query.lower().split() 125 if not words: 126 return 0 127 name, title = command.name.lower(), command.title.lower() 128 name_starts, title_starts = _word_starts(name), _word_starts(title) 129 total = 0 130 for word in words: 131 in_name = _subsequence(word, name, name_starts) 132 in_title = _subsequence(word, title, title_starts) 133 if in_name is None and in_title is None: 134 return None 135 total += max(in_name * 2 if in_name is not None else 0, in_title or 0) 136 if name.startswith(word): 137 total += 5 138 if name.rstrip("…") == " ".join(words): 139 total += 10 # (just its name: it before others that start the same) 140 return total 141 142 143REVEAL_LETTERS = 5 # a hidden command's name: at least this much of it typed, to show it 144 145 146def revealed(query: str, command: Command) -> bool: 147 """Whether a hidden command shows for what's typed: the start of its name, 148 at least REVEAL_LETTERS letters of it (spaces and case aside).""" 149 typed = "".join(query.lower().split()) 150 return len(typed) >= REVEAL_LETTERS and "".join(command.name.lower().split()).startswith(typed) 151 152 153def ranked(query: str, commands: list[Command], recent: list[str]) -> list[Command]: 154 """The commands matching ``query``, best first: the best match, then the 155 most recently used, then those available now (the others are listed, 156 greyed), then the fewest menus deep, then the shortest name, then menu 157 order. Nothing typed: the 158 recently used first, then the menus' order.""" 159 order = {title: n for n, title in enumerate(recent)} 160 matched = [] 161 for index, command in enumerate(commands): 162 points = score(query, command) 163 if points is not None: 164 matched.append((-points, order.get(command.title, len(recent)), not command.action.isEnabled(), 165 len(command.path), len(command.name), index, command)) 166 matched.sort(key=lambda m: m[:6] if query.strip() else (m[1], m[2], m[5])) 167 return [m[-1] for m in matched] 168 169 170class _ItemDelegate(QStyledItemDelegate): 171 """A command: its menus' names muted, then its own; a tick if it's 172 checked; its shortcut on the right.""" 173 174 def sizeHint(self, option, index): 175 return QSize(option.rect.width(), QFontMetrics(option.font).height() + 10) 176 177 def paint(self, painter, option, index): 178 command: Command = index.data(COMMAND_ROLE) 179 style = option.widget.style() if option.widget else QApplication.style() 180 style.drawPrimitive(QStyle.PE_PanelItemViewItem, option, painter, option.widget) 181 selected = bool(option.state & QStyle.State_Selected) 182 pal = option.palette 183 text = pal.color(QPalette.HighlightedText if selected else QPalette.Text) 184 muted = QColor(text) 185 muted.setAlpha(150) 186 if not command.action.isEnabled(): 187 text.setAlpha(110) 188 muted.setAlpha(80) 189 fm = QFontMetrics(option.font) 190 rect = option.rect.adjusted(10, 0, -10, 0) 191 painter.save() 192 shortcut = command.action.shortcut().toString(QKeySequence.NativeText) 193 if shortcut: 194 painter.setPen(muted) 195 painter.drawText(rect, Qt.AlignRight | Qt.AlignVCenter, shortcut) 196 rect.setRight(rect.right() - fm.horizontalAdvance(shortcut) - 16) 197 x = rect.left() 198 tick = "✓ " if command.action.isCheckable() and command.action.isChecked() else "" 199 menus = SEPARATOR.join(command.path[:-1]) + SEPARATOR if len(command.path) > 1 else "" 200 for part, color in ((tick, text), (menus, muted), (command.name, text)): 201 if not part: 202 continue 203 painter.setPen(color) 204 width = rect.right() - x 205 shown = fm.elidedText(part, Qt.ElideMiddle, max(0, width)) 206 painter.drawText(QRect(x, rect.top(), max(0, width), rect.height()), Qt.AlignLeft | Qt.AlignVCenter, 207 shown) 208 x += fm.horizontalAdvance(shown) 209 painter.restore() 210 211 212class CommandPalette(QFrame): 213 """The palette: a field to type in, the matching commands under it. 214 ``recent`` (the titles of the commands last run, latest first) is kept 215 up to date in place; ``recent_changed`` (if set) is called after.""" 216 217 WIDTH = 620 218 ROWS = 14 219 220 def __init__(self, window: QWidget, recent: list[str] | None = None): 221 super().__init__(window, Qt.Popup) 222 self.window_ = window 223 self.recent = recent if recent is not None else [] 224 self.recent_changed = None 225 self.commands: list[Command] = [] 226 self.hidden: list[Command] = [] # (in no menu: shown only when their name is typed, see revealed) 227 self.setFrameShape(QFrame.StyledPanel) 228 self.field = QLineEdit() 229 self.field.setPlaceholderText("Type a command (letters in order: \"mvup\" finds Move Line Up)") 230 self.field.setClearButtonEnabled(True) 231 self.field.textChanged.connect(self._filter) 232 self.field.installEventFilter(self) 233 # Its icon, in front of what's typed (the menu item's: see set_icon). 234 self.icon_action = self.field.addAction(QIcon(), QLineEdit.LeadingPosition) 235 self.list = QListWidget() 236 self.list.setItemDelegate(_ItemDelegate(self.list)) 237 self.list.setUniformItemSizes(True) 238 self.list.setFocusPolicy(Qt.NoFocus) 239 self.list.itemClicked.connect(lambda item: self.run(item.data(COMMAND_ROLE))) 240 layout = QVBoxLayout(self) 241 layout.setContentsMargins(6, 6, 6, 6) 242 layout.setSpacing(6) 243 layout.addWidget(self.field) 244 layout.addWidget(self.list) 245 246 def set_icon(self, icon: QIcon): 247 """The icon in front of the field (the menu item's, in the theme's colors).""" 248 self.icon_action.setIcon(icon) 249 250 def open(self): 251 """Read the menus, and show the palette at the top of the window.""" 252 menu_bar = self.window_.menuBar() if hasattr(self.window_, "menuBar") else None 253 self.commands = collect(menu_bar) if menu_bar is not None else [] 254 self.field.clear() 255 self._filter("") 256 width = min(self.WIDTH, max(300, self.window_.width() - 40)) 257 row = self.list.sizeHintForRow(0) if self.list.count() else 24 258 self.resize(width, self.field.sizeHint().height() + row * self.ROWS + 24) 259 top = self.window_.mapToGlobal(self.window_.rect().topLeft()) 260 self.move(top.x() + (self.window_.width() - width) // 2, top.y() + 40) 261 self.show() 262 self.raise_() 263 self.field.setFocus() 264 265 def _filter(self, text: str): 266 self.list.clear() 267 shown = self.commands + [c for c in self.hidden if revealed(text, c)] 268 for command in ranked(text, shown, self.recent): 269 item = QListWidgetItem(command.title) 270 item.setData(COMMAND_ROLE, command) 271 if not command.action.isEnabled(): 272 item.setToolTip("Not available now") 273 elif command.action.statusTip(): 274 item.setToolTip(command.action.statusTip()) 275 self.list.addItem(item) 276 if self.list.count(): 277 self.list.setCurrentRow(0) 278 279 def eventFilter(self, obj, event): 280 if obj is self.field and event.type() == QEvent.KeyPress: 281 key = event.key() 282 steps = {Qt.Key_Up: -1, Qt.Key_Down: 1, Qt.Key_PageUp: -self.ROWS, Qt.Key_PageDown: self.ROWS} 283 if key in steps and self.list.count(): 284 row = max(0, min(self.list.count() - 1, self.list.currentRow() + steps[key])) 285 self.list.setCurrentRow(row) 286 return True 287 if key in (Qt.Key_Return, Qt.Key_Enter): 288 item = self.list.currentItem() 289 if item is not None: 290 self.run(item.data(COMMAND_ROLE)) 291 return True 292 if key == Qt.Key_Escape: 293 self.close() 294 return True 295 return super().eventFilter(obj, event) 296 297 def run(self, command: Command): 298 """Run a command (if it's available now): the palette closes first, so 299 the command acts where the focus was (e.g. the editor).""" 300 if not command.action.isEnabled(): 301 return 302 title = command.title 303 if title in self.recent: 304 self.recent.remove(title) 305 self.recent.insert(0, title) 306 del self.recent[MAX_RECENT:] 307 if self.recent_changed is not None: 308 self.recent_changed(self.recent) 309 self.close() 310 QTimer.singleShot(0, self.window_, command.action.trigger) 311 312 def current_titles(self) -> list[str]: 313 """(The titles listed, in order: for the tests.)""" 314 return [self.list.item(i).data(COMMAND_ROLE).title for i in range(self.list.count())]
38class Command(NamedTuple): 39 path: tuple[str, ...] # its menus' titles, then its own ("View", "Theme", "Nord") 40 action: QAction 41 42 @property 43 def name(self) -> str: 44 return self.path[-1] 45 46 @property 47 def title(self) -> str: 48 return SEPARATOR.join(self.path)
Command(path, action)
51def clean(text: str) -> str: 52 """A menu text as shown: no "&" for its mnemonic ("&&" is an "&"), no 53 tab and what's after it (a shortcut some menus put in their text).""" 54 text = text.split("\t", 1)[0] 55 return re.sub(r"&(.)", r"\1", text).strip()
A menu text as shown: no "&" for its mnemonic ("&&" is an "&"), no tab and what's after it (a shortcut some menus put in their text).
58def collect(menu_bar: QMenuBar) -> list[Command]: 59 """Every command in the menu bar's menus, in menu order: not separators, 60 submenus themselves or hidden actions. 61 62 A submenu is found from its menu (``QMenu.menuAction()``), never with 63 ``QAction.menu()``: in PySide that leaves a menu the menu bar made (and 64 its actions) deleted as far as Python knows, which broke the app's own 65 menus.""" 66 commands: list[Command] = [] 67 seen: set[int] = set() 68 submenus = {m.menuAction(): m for m in menu_bar.window().findChildren(QMenu)} 69 70 def walk(menu: QMenu, path: tuple[str, ...]): 71 menu.aboutToShow.emit() # (a menu filled when it opens: filled now, as then) 72 submenus.update({m.menuAction(): m for m in menu.findChildren(QMenu)}) # (made by filling it) 73 for action in menu.actions(): 74 if action.isSeparator() or not action.isVisible() or not clean(action.text()): 75 continue 76 here = (*path, clean(action.text())) 77 submenu = submenus.get(action) 78 if submenu is not None: 79 walk(submenu, here) 80 elif id(action) not in seen: 81 seen.add(id(action)) 82 commands.append(Command(here, action)) 83 84 for action in menu_bar.actions(): 85 submenu = submenus.get(action) 86 if submenu is not None and action.isVisible(): 87 walk(submenu, (clean(action.text()),)) 88 return commands
Every command in the menu bar's menus, in menu order: not separators, submenus themselves or hidden actions.
A submenu is found from its menu (QMenu.menuAction()), never with
QAction.menu(): in PySide that leaves a menu the menu bar made (and
its actions) deleted as far as Python knows, which broke the app's own
menus.
121def score(query: str, command: Command) -> int | None: 122 """How well a command matches what's typed (None: it doesn't). Each word 123 typed must match, letters in order (so "mvup" finds Move Line Up); in 124 the command's own name counts more than in its menus' names.""" 125 words = query.lower().split() 126 if not words: 127 return 0 128 name, title = command.name.lower(), command.title.lower() 129 name_starts, title_starts = _word_starts(name), _word_starts(title) 130 total = 0 131 for word in words: 132 in_name = _subsequence(word, name, name_starts) 133 in_title = _subsequence(word, title, title_starts) 134 if in_name is None and in_title is None: 135 return None 136 total += max(in_name * 2 if in_name is not None else 0, in_title or 0) 137 if name.startswith(word): 138 total += 5 139 if name.rstrip("…") == " ".join(words): 140 total += 10 # (just its name: it before others that start the same) 141 return total
How well a command matches what's typed (None: it doesn't). Each word typed must match, letters in order (so "mvup" finds Move Line Up); in the command's own name counts more than in its menus' names.
147def revealed(query: str, command: Command) -> bool: 148 """Whether a hidden command shows for what's typed: the start of its name, 149 at least REVEAL_LETTERS letters of it (spaces and case aside).""" 150 typed = "".join(query.lower().split()) 151 return len(typed) >= REVEAL_LETTERS and "".join(command.name.lower().split()).startswith(typed)
Whether a hidden command shows for what's typed: the start of its name, at least REVEAL_LETTERS letters of it (spaces and case aside).
154def ranked(query: str, commands: list[Command], recent: list[str]) -> list[Command]: 155 """The commands matching ``query``, best first: the best match, then the 156 most recently used, then those available now (the others are listed, 157 greyed), then the fewest menus deep, then the shortest name, then menu 158 order. Nothing typed: the 159 recently used first, then the menus' order.""" 160 order = {title: n for n, title in enumerate(recent)} 161 matched = [] 162 for index, command in enumerate(commands): 163 points = score(query, command) 164 if points is not None: 165 matched.append((-points, order.get(command.title, len(recent)), not command.action.isEnabled(), 166 len(command.path), len(command.name), index, command)) 167 matched.sort(key=lambda m: m[:6] if query.strip() else (m[1], m[2], m[5])) 168 return [m[-1] for m in matched]
The commands matching query, best first: the best match, then the
most recently used, then those available now (the others are listed,
greyed), then the fewest menus deep, then the shortest name, then menu
order. Nothing typed: the
recently used first, then the menus' order.
213class CommandPalette(QFrame): 214 """The palette: a field to type in, the matching commands under it. 215 ``recent`` (the titles of the commands last run, latest first) is kept 216 up to date in place; ``recent_changed`` (if set) is called after.""" 217 218 WIDTH = 620 219 ROWS = 14 220 221 def __init__(self, window: QWidget, recent: list[str] | None = None): 222 super().__init__(window, Qt.Popup) 223 self.window_ = window 224 self.recent = recent if recent is not None else [] 225 self.recent_changed = None 226 self.commands: list[Command] = [] 227 self.hidden: list[Command] = [] # (in no menu: shown only when their name is typed, see revealed) 228 self.setFrameShape(QFrame.StyledPanel) 229 self.field = QLineEdit() 230 self.field.setPlaceholderText("Type a command (letters in order: \"mvup\" finds Move Line Up)") 231 self.field.setClearButtonEnabled(True) 232 self.field.textChanged.connect(self._filter) 233 self.field.installEventFilter(self) 234 # Its icon, in front of what's typed (the menu item's: see set_icon). 235 self.icon_action = self.field.addAction(QIcon(), QLineEdit.LeadingPosition) 236 self.list = QListWidget() 237 self.list.setItemDelegate(_ItemDelegate(self.list)) 238 self.list.setUniformItemSizes(True) 239 self.list.setFocusPolicy(Qt.NoFocus) 240 self.list.itemClicked.connect(lambda item: self.run(item.data(COMMAND_ROLE))) 241 layout = QVBoxLayout(self) 242 layout.setContentsMargins(6, 6, 6, 6) 243 layout.setSpacing(6) 244 layout.addWidget(self.field) 245 layout.addWidget(self.list) 246 247 def set_icon(self, icon: QIcon): 248 """The icon in front of the field (the menu item's, in the theme's colors).""" 249 self.icon_action.setIcon(icon) 250 251 def open(self): 252 """Read the menus, and show the palette at the top of the window.""" 253 menu_bar = self.window_.menuBar() if hasattr(self.window_, "menuBar") else None 254 self.commands = collect(menu_bar) if menu_bar is not None else [] 255 self.field.clear() 256 self._filter("") 257 width = min(self.WIDTH, max(300, self.window_.width() - 40)) 258 row = self.list.sizeHintForRow(0) if self.list.count() else 24 259 self.resize(width, self.field.sizeHint().height() + row * self.ROWS + 24) 260 top = self.window_.mapToGlobal(self.window_.rect().topLeft()) 261 self.move(top.x() + (self.window_.width() - width) // 2, top.y() + 40) 262 self.show() 263 self.raise_() 264 self.field.setFocus() 265 266 def _filter(self, text: str): 267 self.list.clear() 268 shown = self.commands + [c for c in self.hidden if revealed(text, c)] 269 for command in ranked(text, shown, self.recent): 270 item = QListWidgetItem(command.title) 271 item.setData(COMMAND_ROLE, command) 272 if not command.action.isEnabled(): 273 item.setToolTip("Not available now") 274 elif command.action.statusTip(): 275 item.setToolTip(command.action.statusTip()) 276 self.list.addItem(item) 277 if self.list.count(): 278 self.list.setCurrentRow(0) 279 280 def eventFilter(self, obj, event): 281 if obj is self.field and event.type() == QEvent.KeyPress: 282 key = event.key() 283 steps = {Qt.Key_Up: -1, Qt.Key_Down: 1, Qt.Key_PageUp: -self.ROWS, Qt.Key_PageDown: self.ROWS} 284 if key in steps and self.list.count(): 285 row = max(0, min(self.list.count() - 1, self.list.currentRow() + steps[key])) 286 self.list.setCurrentRow(row) 287 return True 288 if key in (Qt.Key_Return, Qt.Key_Enter): 289 item = self.list.currentItem() 290 if item is not None: 291 self.run(item.data(COMMAND_ROLE)) 292 return True 293 if key == Qt.Key_Escape: 294 self.close() 295 return True 296 return super().eventFilter(obj, event) 297 298 def run(self, command: Command): 299 """Run a command (if it's available now): the palette closes first, so 300 the command acts where the focus was (e.g. the editor).""" 301 if not command.action.isEnabled(): 302 return 303 title = command.title 304 if title in self.recent: 305 self.recent.remove(title) 306 self.recent.insert(0, title) 307 del self.recent[MAX_RECENT:] 308 if self.recent_changed is not None: 309 self.recent_changed(self.recent) 310 self.close() 311 QTimer.singleShot(0, self.window_, command.action.trigger) 312 313 def current_titles(self) -> list[str]: 314 """(The titles listed, in order: for the tests.)""" 315 return [self.list.item(i).data(COMMAND_ROLE).title for i in range(self.list.count())]
The palette: a field to type in, the matching commands under it.
recent (the titles of the commands last run, latest first) is kept
up to date in place; recent_changed (if set) is called after.
221 def __init__(self, window: QWidget, recent: list[str] | None = None): 222 super().__init__(window, Qt.Popup) 223 self.window_ = window 224 self.recent = recent if recent is not None else [] 225 self.recent_changed = None 226 self.commands: list[Command] = [] 227 self.hidden: list[Command] = [] # (in no menu: shown only when their name is typed, see revealed) 228 self.setFrameShape(QFrame.StyledPanel) 229 self.field = QLineEdit() 230 self.field.setPlaceholderText("Type a command (letters in order: \"mvup\" finds Move Line Up)") 231 self.field.setClearButtonEnabled(True) 232 self.field.textChanged.connect(self._filter) 233 self.field.installEventFilter(self) 234 # Its icon, in front of what's typed (the menu item's: see set_icon). 235 self.icon_action = self.field.addAction(QIcon(), QLineEdit.LeadingPosition) 236 self.list = QListWidget() 237 self.list.setItemDelegate(_ItemDelegate(self.list)) 238 self.list.setUniformItemSizes(True) 239 self.list.setFocusPolicy(Qt.NoFocus) 240 self.list.itemClicked.connect(lambda item: self.run(item.data(COMMAND_ROLE))) 241 layout = QVBoxLayout(self) 242 layout.setContentsMargins(6, 6, 6, 6) 243 layout.setSpacing(6) 244 layout.addWidget(self.field) 245 layout.addWidget(self.list)
__init__(self, /, parent: PySide6.QtWidgets.QWidget | None = None, f: PySide6.QtCore.Qt.WindowType = Default(Qt.WindowFlags), *, frameShape: PySide6.QtWidgets.QFrame.Shape | None = None, frameShadow: PySide6.QtWidgets.QFrame.Shadow | None = None, lineWidth: int | None = None, midLineWidth: int | None = None, frameWidth: int | None = None, frameRect: PySide6.QtCore.QRect | None = None) -> None
Initialize self. See help(type(self)) for accurate signature.
247 def set_icon(self, icon: QIcon): 248 """The icon in front of the field (the menu item's, in the theme's colors).""" 249 self.icon_action.setIcon(icon)
The icon in front of the field (the menu item's, in the theme's colors).
251 def open(self): 252 """Read the menus, and show the palette at the top of the window.""" 253 menu_bar = self.window_.menuBar() if hasattr(self.window_, "menuBar") else None 254 self.commands = collect(menu_bar) if menu_bar is not None else [] 255 self.field.clear() 256 self._filter("") 257 width = min(self.WIDTH, max(300, self.window_.width() - 40)) 258 row = self.list.sizeHintForRow(0) if self.list.count() else 24 259 self.resize(width, self.field.sizeHint().height() + row * self.ROWS + 24) 260 top = self.window_.mapToGlobal(self.window_.rect().topLeft()) 261 self.move(top.x() + (self.window_.width() - width) // 2, top.y() + 40) 262 self.show() 263 self.raise_() 264 self.field.setFocus()
Read the menus, and show the palette at the top of the window.
280 def eventFilter(self, obj, event): 281 if obj is self.field and event.type() == QEvent.KeyPress: 282 key = event.key() 283 steps = {Qt.Key_Up: -1, Qt.Key_Down: 1, Qt.Key_PageUp: -self.ROWS, Qt.Key_PageDown: self.ROWS} 284 if key in steps and self.list.count(): 285 row = max(0, min(self.list.count() - 1, self.list.currentRow() + steps[key])) 286 self.list.setCurrentRow(row) 287 return True 288 if key in (Qt.Key_Return, Qt.Key_Enter): 289 item = self.list.currentItem() 290 if item is not None: 291 self.run(item.data(COMMAND_ROLE)) 292 return True 293 if key == Qt.Key_Escape: 294 self.close() 295 return True 296 return super().eventFilter(obj, event)
eventFilter(self, watched: PySide6.QtCore.QObject, event: PySide6.QtCore.QEvent, /) -> bool
298 def run(self, command: Command): 299 """Run a command (if it's available now): the palette closes first, so 300 the command acts where the focus was (e.g. the editor).""" 301 if not command.action.isEnabled(): 302 return 303 title = command.title 304 if title in self.recent: 305 self.recent.remove(title) 306 self.recent.insert(0, title) 307 del self.recent[MAX_RECENT:] 308 if self.recent_changed is not None: 309 self.recent_changed(self.recent) 310 self.close() 311 QTimer.singleShot(0, self.window_, command.action.trigger)
Run a command (if it's available now): the palette closes first, so the command acts where the focus was (e.g. the editor).