diff --git a/SearchTools/GatherTools.py b/SearchTools/GatherTools.py index 441d003..e69de29 100644 --- a/SearchTools/GatherTools.py +++ b/SearchTools/GatherTools.py @@ -1,2 +0,0 @@ -# action = json.loads(str(index.model().data(index.siblingAtColumn(2)))) -#² actionHandlers[action['handler']](action) diff --git a/SearchTools/SearchTools.py b/SearchTools/SearchTools.py index fef7d7a..a91d0d0 100644 --- a/SearchTools/SearchTools.py +++ b/SearchTools/SearchTools.py @@ -3,17 +3,53 @@ if True: def toolbarAction(act): print('show toolbar ' + act['toolbar'] + ' from workbenches ' + repr(act['workbenches'])) - def toolAction(act): - print('start action for tool ' + act['toolbar'] + '.' + act['tool'] + ' from workbenches ' + repr(act['workbenches'])) def subToolAction(act): - print('start action for tool ' + act['toolbar'] + '.' + act['tool'] + '.' + act['subTool'] + ' from workbenches ' + repr(act['workbenches'])) - def documentObjectAction(): - print('select object ' + o.Document.Name + '.' + o.Name) - def documentAction(): - print('switch to document ' + o.Document.Name) + toolPath = act['toolbar'] + '.' + act['tool'] + if 'subTool' in act: + toolPath = toolPath + '.' + act['subTool'] + def runTool(): + for the_toolbar in mw.findChildren(QtGui.QToolBar, act['toolbar']): + for tbt in the_toolbar.findChildren(QtGui.QToolButton): + if tbt.text() == act['tool']: + action = None + if 'subTool' in act: + men = tbt.menu() + if men: + for mac in men.actions(): + if mac.text() == act['subTool']: + action = mac + break + else: + action = tbt.defaultAction() + if 'showMenu' in act and act['showMenu']: + print('Popup submenu of tool ' + toolPath + ' available in workbenches ' + repr(act['workbenches'])) + the_toolbar.show() + tbt.showMenu() + return True + elif action is not None: + print('Run action of tool ' + toolPath + ' available in workbenches ' + repr(act['workbenches'])) + action.trigger() + return True + return False + if runTool(): + return + else: + for workbench in act['workbenches']: + print('Activating workbench ' + workbench + ' to access tool ' + toolPath) + Gui.activateWorkbench(workbench) + if runTool(): + return + print('Tool ' + toolPath + ' not found, was it offered by an extension that is no longer present?') + def documentObjectAction(act): + print('select object ' + act['document'] + '.' + act['object']) + Gui.Selection.addSelection(act['document'], act['object']) + def documentAction(act): + # Todo: this should also select the document in the tree view + print('switch to document ' + act['document']) + App.setActiveDocument(act['document']) actionHandlers = { 'toolbarAction': toolbarAction, - 'toolAction': toolAction, + 'toolAction': subToolAction, 'subToolAction': subToolAction, 'documentObjectAction': documentObjectAction, 'documentAction': documentAction @@ -48,14 +84,22 @@ if True: self.listView = QtGui.QListView(self) self.listView.setWindowFlags(QtGui.Qt.ToolTip) self.listView.setWindowFlag(QtGui.Qt.FramelessWindowHint) + self.listView.setSelectionMode(QtGui.QAbstractItemView.SingleSelection) self.listView.setModel(self.proxyModel) self.listView.setItemDelegate(itemDelegate) # https://stackoverflow.com/a/65930408/324969 # make the QListView non-editable self.listView.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers) + # Create pane for showing extra info about the currently-selected tool + #self.extraInfo = QtGui.QLabel() + self.extraInfo = QtGui.QTextEdit() + self.extraInfo.setReadOnly(True) + self.extraInfo.setWindowFlags(QtGui.Qt.ToolTip) + self.extraInfo.setWindowFlag(QtGui.Qt.FramelessWindowHint) + self.extraInfo.setAlignment(QtCore.Qt.AlignTop) # Connect signals and slots self.textChanged.connect(self.filterModel) - self.listView.clicked.connect(self.selectResult) - self.listView.selectionModel().selectionChanged.connect(self.showExtraInfo) + self.listView.clicked.connect(lambda x: self.selectResult('select', x)) + self.listView.selectionModel().selectionChanged.connect(self.onSelectionChanged) # Thanks to https://saurabhg.com/programming/search-box-using-qlineedit/ for indicating a few useful options ico = QtGui.QIcon(':/icons/help-browser.svg') #ico = QtGui.QIcon(':/icons/WhatsThis.svg') @@ -69,15 +113,15 @@ if True: self.showList() super(SearchBox, self).focusInEvent(qFocusEvent) def focusOutEvent(self, qFocusEvent): - self.listView.hide() + self.hideList() super(SearchBox, self).focusOutEvent(qFocusEvent) def keyPressEvent(self, qKeyEvent): key = qKeyEvent.key() listMovementKeys = { QtCore.Qt.Key_Down: lambda current, nbRows: (current + 1) % nbRows, QtCore.Qt.Key_Up: lambda current, nbRows: (current - 1) % nbRows, - QtCore.Qt.Key_PageDown: lambda current, nbRows: max(current + min(1, self.maxVisibleRows / 2), nbRows), - QtCore.Qt.Key_PageUp: lambda current, nbRows: min(current - min(1, self.maxVisibleRows / 2), 0), + QtCore.Qt.Key_PageDown: lambda current, nbRows: min(current + max(1, self.maxVisibleRows / 2), nbRows - 1), + QtCore.Qt.Key_PageUp: lambda current, nbRows: max(current - max(1, self.maxVisibleRows / 2), 0), } acceptKeys = { QtCore.Qt.Key_Enter: 'select', @@ -101,14 +145,67 @@ if True: elif key in acceptKeys: self.showList() if currentIndex.isValid(): - self.selectResult(acceptKeys[key], currentIndex, currentIndex.data()) + self.selectResult(acceptKeys[key], currentIndex) elif key in cancelKeys: - self.listView.hide() + self.hideList() self.clearFocus() else: self.showList() super(SearchBox, self).keyPressEvent(qKeyEvent) def showList(self): + self.setFloatingWidgetsGeometry() + self.listView.show() + self.showExtraInfo() + def hideList(self): + self.listView.hide() + self.hideExtraInfo() + def hideExtraInfo(self): + self.extraInfo.hide() + def selectResult(self, mode, index): + action = str(index.model().itemData(index.siblingAtColumn(2))[0]) + self.hideList() + # TODO: allow other options, e.g. some items could act as combinators / cumulative filters + self.setText('') + self.filterModel(self.text()) + # TODO: emit index relative to the base model + self.resultSelected.emit(index, action) + def filterModel(self, userInput): + def matches(s): + return userInput.lower() in s.lower() + def filterGroup(group): + if matches(group['text']): + # If a group matches, include the entire subtree (might need to disable this if it causes too much noise) + return group + else: + subitems = filterGroups(group['subitems']) + if len(subitems) > 0 or matches(group['text']): + return { 'text': group['text'], 'icon': group['icon'], 'action': group['action'], 'toolTipHTML':group['toolTipHTML'], 'subitems': subitems } + else: + return None + def filterGroups(groups): + groups = (filterGroup(group) for group in groups) + return [group for group in groups if group is not None] + def addGroups(filteredGroups, depth=0): + for group in filteredGroups: + mdl.appendRow([QtGui.QStandardItem(group['icon'] or QtGui.QIcon(), group['text']), + QtGui.QStandardItem(str(depth)), + QtGui.QStandardItem(group['action']), + QtGui.QStandardItem(group['toolTipHTML'])]) + addGroups(group['subitems'], depth+1) + mdl = QtGui.QStandardItemModel() + mdl.appendColumn([]) + addGroups(filterGroups(self.itemGroups)) + self.proxyModel.setSourceModel(mdl) + # TODO: try to find the already-highlighted item + nbRows = self.listView.model().rowCount() + if nbRows > 0: + index = self.listView.model().index(0, 0) + self.listView.setCurrentIndex(index) + self.setExtraInfo(index) + else: + self.clearExtraInfo() + #self.showList() + def setFloatingWidgetsGeometry(self): def getScreenPosition(widget): geo = widget.geometry() parent = widget.parent() @@ -123,174 +220,46 @@ if True: # TODO: this can still bump into the bottom of the screen, in that case we should flip w = max(siz.width(), hint_w) h = 100 + extraw = w # choose a preferred width that doesn't change all the time, + # self.extraInfo.sizeHint().width() would change for every item. + extrax = x - extraw if screen is not None: scr = screen.geometry() x = min(scr.x() + scr.width() - hint_w, x) - self.listView.setGeometry(x, y, w, h) - self.listView.show() - def selectResult(self): - self.listView.hide() - # TODO: allow other options, e.g. some items could act as combinators / cumulative filters - self.setText('') - self.filterModel(self.text()) - self.resultSelected.emit("TODO: index", "TODO: str") - def filterModel(self, userInput): - def matches(s): - return userInput.lower() in s.lower() - def filterGroup(group): - if matches(group['text']): - # If a group matches, include the entire subtree (might need to disable this if it causes too much noise) - return group + extraleftw = x - scr.x() + extrarightw = scr.x() + scr.width() - x + # flip the extraInfo if it doesn't fit on the screen + if extraleftw < extraw and extrarightw > extraleftw: + extrax = x + w + extraw = min(extrarightw, extraw) else: - subitems = filterGroups(group['subitems']) - if len(subitems) > 0 or matches(group['text']): - return { 'text': group['text'], 'icon': group['icon'], 'action': group['action'], 'subitems': subitems } - else: - return None - def filterGroups(groups): - groups = (filterGroup(group) for group in groups) - return [group for group in groups if group is not None] - def addGroups(filteredGroups, depth=0): - for group in filteredGroups: - mdl.appendRow([QtGui.QStandardItem(group['icon'] or QtGui.QIcon(), group['text']), - QtGui.QStandardItem(str(depth)), - QtGui.QStandardItem(group['action'])]) - addGroups(group['subitems'], depth+1) - mdl = QtGui.QStandardItemModel() - mdl.appendColumn([]) - addGroups(filterGroups(self.itemGroups)) + extrax = x - extraw + extraw = min(extraleftw, extraw) + self.listView.setGeometry(x, y, w, h) + self.extraInfo.setGeometry(extrax, y, extraw, h) + def onSelectionChanged(self, selected, deselected): + # The list has .setSelectionMode(QtGui.QAbstractItemView.SingleSelection), + # so there is always at most one index in selected.indexes() and at most one + # index in deselected.indexes() + selected = selected.indexes() + deselected = deselected.indexes() + if len(selected) > 0: + index = selected[0] + self.setExtraInfo(index) + # Poor attempt to circumvent a glitch where the extra info pane stays visible after pressing Return + if not self.listView.isHidden(): + self.showExtraInfo() + elif len(deselected) > 0: + self.hideExtraInfo() + def setExtraInfo(self, index): + toolTipHTML = str(index.model().itemData(index.siblingAtColumn(3))[0]) + self.extraInfo.setText(toolTipHTML) + self.setFloatingWidgetsGeometry() + def clearExtraInfo(self, index): + self.extraInfo.setText('') + def showExtraInfo(self): + self.extraInfo.show() - print('setSourceModel for userInput ' + repr(userInput) + ' with ' + str(mdl.rowCount()) + ' rows.') - self.proxyModel.setSourceModel(mdl) - #print('TODO: do the actual filtering') - #flt = QtCore.QSortFilterProxyModel() - #flt.setSourceModel(self.model) - #flt.setFilterCaseSensitivity(QtCore.Qt.CaseSensitivity.CaseInsensitive) - #flt.setFilterWildcard(query) - #self.listView.setModel(flt) - # TODO: try to find the already-highlighted item - nbRows = self.listView.model().rowCount() - if nbRows > 0: - index = self.listView.model().index(0, 0) - self.listView.setCurrentIndex(index) - #self.showList() - def showExtraInfo(selected, deselected): - print('show extra info...', repr(selected)) - pass - #mdl = QtCore.QStringListModel(['aaa', 'aab', 'aac', 'bxy', 'bac']) - #sbx = SearchBox(mdl, 10, None) - #sbx.show() - - # Inspired by https://stackoverflow.com/a/7767999/324969 - #class SearchQCompleter(QtGui.QCompleter): - # def __init__(self, model, itemDelegate): - # super(SearchQCompleter, self).__init__() - # super(SearchQCompleter, self).setModel(QtCore.QIdentityProxyModel()) - # #https://stackoverflow.com/a/65930408/324969 - # super(SearchQCompleter, self).popup().setItemDelegate(itemDelegate) - # self.setModel(model) - # - # def setModel(self, itemGroups): - # self.itemGroups = itemGroups - # self.filterModel('') - # - # def filterModel(self, userInput): - # def matches(s): - # return userInput.lower() in s.lower() - # def filterGroup(group): - # if matches(group['text']): - # # If a group matches, include the entire subtree (might need to disable this if it causes too much noise) - # return group - # else: - # subitems = filterGroups(group['subitems']) - # if len(subitems) > 0 or matches(group['text']): - # return { 'text': group['text'], 'icon': group['icon'], 'action': group['action'], 'subitems': subitems } - # else: - # return None - # def filterGroups(groups): - # groups = (filterGroup(group) for group in groups) - # return [group for group in groups if group is not None] - # def addGroups(filteredGroups, depth=0): - # for group in filteredGroups: - # mdl.appendRow([QtGui.QStandardItem(group['icon'] or QtGui.QIcon(), group['text']), - # QtGui.QStandardItem(str(depth)), - # QtGui.QStandardItem(group['action'])]) - # addGroups(group['subitems'], depth+1) - # mdl = QtGui.QStandardItemModel() - # mdl.appendColumn([]) - # addGroups(filterGroups(itemGroups)) - # - # print('setSourceModel for userInput ' + repr(userInput) + ' with ' + str(mdl.rowCount()) + ' rows.') - # self.model().setSourceModel(mdl) - # # https://stackoverflow.com/a/65930408/324969 - # - # # the splitPath(self, path) method is called every time the input string changes, before - # # drawing the completion list, we latch onto this method to also update the model to contain - # # the appropriate results, as given by the custom filterAcceptsRow method above. - # def splitPath(self, path): - # self.filterModel(path) - # # Pretend that the user endered the empty string, so that all items from the filteredProxyModel match. - # return '' - # - #class SearchBox(QtGui.QLineEdit): - # resultSelected = QtCore.Signal(int, str) - # def __init__(self, itemGroups): - # super(SearchBox, self).__init__() - # qom = SearchQCompleter(itemGroups, IndentedItemDelegate()) - # qom.setMaxVisibleItems(20) - # #qom.setCompletionMode(QtGui.QCompleter.CompletionMode.PopupCompletion) - # # Thanks to https://saurabhg.com/programming/search-box-using-qlineedit/ for indicating a few useful options - # ico = QtGui.QIcon(':/icons/help-browser.svg') - # #ico = QtGui.QIcon(':/icons/WhatsThis.svg') - # self.addAction(ico, QtGui.QLineEdit.LeadingPosition) - # self.setClearButtonEnabled(True) - # self.setPlaceholderText('Search tools, prefs & tree') - # self.setFixedWidth(200) - # # Signals & slots for enter / click - # def completerActivated(index): - # print('fooooooooooooo') - # print(qom.model().rowCount(), index.row()) - # # TODO: run the action! - # result = str(qom.model().data(index.siblingAtColumn(1))) - # print('res='+result) - # self.clear() - # self.completer().setCompletionPrefix('') - # self.resultSelected.emit(str(index), result) - # def returnPressed(): - # #self.clear() - # #self.completer().setCompletionPrefix('') - # pass - # #text = sea.text() - # #self.clear() - # #self.resultSelected.emit('text returnPressed' + text) - # self.returnPressed.connect(returnPressed) - # #QtCore.QObject.connect(self.completer(), QtCore.SIGNAL('activated(QModelIndex)'), completerActivated) #, QtCore.Qt.ConnectionType.QueuedConnection) - # qom.activated.connect(completerActivated, QtCore.Qt.ConnectionType.DirectConnection) #, QtCore.Qt.ConnectionType.QueuedConnection) - # #self.completer().activated.connect(returnPressedOrCompleterActivated) - # def textChanged(): - # print('textChanged') - # # Workaround: Clear completion prefix and still show the completion box when doing backspace after typing a single character - # if self.text() == '': - # self.completer().setCompletionPrefix(self.text()) - # self.completer().complete() - # self.textChanged.connect(textChanged) - # QtCore.QObject.connect(qom.popup(), QtCore.SIGNAL('clicked(QModelIndex)'), lambda x: print(x)) - # self.setCompleter(qom) - # def focusInEvent(self, e): - # super(SearchBox, self).focusInEvent(e) - # self.completer().setCompletionPrefix(self.text()) - # self.completer().complete() - # self.completer().setCurrentRow(1) # Does not work - # #d=QtGui.QKeyEvent(QtCore.QEvent.KeyPress, QtCore.Qt.Key_Down, QtCore.Qt.NoModifier) - # #QtCore.QCoreApplication.postEvent(self, d) - # def mousePressEvent(self, e): - # super(SearchBox, self).mousePressEvent(e) - # self.completer().setCompletionPrefix(self.text()) - # self.completer().complete() - # self.completer().setCurrentRow(1) # Does not work - # #d=QtGui.QKeyEvent(QtCore.QEvent.KeyPress, QtCore.Qt.Key_Down, QtCore.Qt.NoModifier) - # #QtCore.QCoreApplication.postEvent(self, d) - # mw = Gui.getMainWindow() mdi = mw.findChild(QtGui.QMdiArea) @@ -299,16 +268,20 @@ if True: mwx = QtGui.QMainWindow() mbr = mw.findChildren(QtGui.QToolBar, 'File')[0] - all_tbs = set() - for wbname, workbench in Gui.listWorkbenches().items(): - try: - tbs = workbench.listToolbars() - except: - continue - # careful, tbs contains all the toolbars of the workbench, including shared toolbars - for tb in tbs: - all_tbs.add(tb) - + def getAllToolbars(): + all_tbs = dict() + for wbname, workbench in Gui.listWorkbenches().items(): + try: + tbs = workbench.listToolbars() + except: + continue + # careful, tbs contains all the toolbars of the workbench, including shared toolbars + for tb in tbs: + if tb not in all_tbs: + all_tbs[tb] = set() + all_tbs[tb].add(wbname) + return all_tbs + import json def serializeIcon(icon): iconPixmaps = {} @@ -331,7 +304,8 @@ if True: 'workbenches': tool['workbenches'], 'toolbar': tool['toolbar'], 'text': tool['text'], - 'icon': serializeIcon(tool['icon']) + 'icon': serializeIcon(tool['icon']), + 'toolTipHTML': tool['toolTipHTML'] } def deserializeIcon(iconPixmaps): @@ -352,76 +326,68 @@ if True: 'workbenches': tool['workbenches'], 'toolbar': tool['toolbar'], 'text': tool['text'], - 'icon': deserializeIcon(tool['icon']) + 'icon': deserializeIcon(tool['icon']), + 'toolTipHTML': tool['toolTipHTML'] } - #serialized = serializeTools([{'workbenches': ['wb1', 'wb2'], 'toolbar': 'tbr', 'text': 'aa', 'icon': ic}]) - #serialized = serializeTools([]) - - #TODO:save in App.getUserAppDataDir() ################################################################################################################ + # TODO: save serialized tools in App.getUserAppDataDir() ################################################################################################################ + # + never cache the document objects def GatherTools(): itemGroups = [] - for toolbarName in all_tbs: + all_tbs = getAllToolbars() + for toolbarName, toolbarIsInWorkbenches in all_tbs.items(): + toolbarIsInWorkbenches = sorted(list(toolbarIsInWorkbenches)) for the_toolbar in mw.findChildren(QtGui.QToolBar, toolbarName): - group = [] - - for tbt in the_toolbar.findChildren(QtGui.QToolButton): - text = tbt.text() - act = tbt.defaultAction() - if text != '': - # TODO: there also is the tooltip - icon = tbt.icon() - #sim.appendRow([QtGui.QStandardItem(icon, 't:' + text), QtGui.QStandardItem('tool')]) - men = tbt.menu() + group = [] + for tbt in the_toolbar.findChildren(QtGui.QToolButton): + text = tbt.text() + act = tbt.defaultAction() + if text != '': + # TODO: there also is the tooltip + icon = tbt.icon() + men = tbt.menu() + subgroup = [] + if men: subgroup = [] - if men: - subgroup = [] - for mac in men.actions(): - if mac.text(): - #all_actions.append(mac.trigger) - action = { 'handler': 'subToolAction', 'workbenches': [], 'toolbar': toolbarName, 'tool': text, 'subtool': mac.text() } - #print('whaaaat', str(len(all_actions))) - subgroup.append({'icon':mac.icon(), 'text':mac.text(), 'action':json.dumps(action), 'subitems':[]}) - #all_actions.append(tbt.actions().trigger) - #global lalala - #lalala=tbt - #print('whuuuut', str(len(all_actions))) - action = { 'handler': 'toolAction', 'workbenches': [], 'toolbar': toolbarName, 'tool': text } - group.append({'icon':icon, 'text':text, 'action': json.dumps(action), 'subitems': subgroup}) - - #viu = mw.findChildren(QtGui.QToolBar, 'View')[0] - #tbt = viu.findChildren(QtGui.QToolButton) - #men = tbt[3].menu() - #acs = men.actions() # QtGui.QAction list - #act = tbt[2].defaultAction() # QtGui.QAction - #act.trigger() - action = { 'handler': 'toolbarAction', 'workbenches': [], 'toolbar': toolbarName } - itemGroups.append({ - 'icon': QtGui.QIcon(':/icons/Group.svg'), - 'text': toolbarName, - 'action': json.dumps(action), - 'subitems': group - }) + for mac in men.actions(): + if mac.text(): + action = { 'handler': 'subToolAction', 'workbenches': toolbarIsInWorkbenches, 'toolbar': toolbarName, 'tool': text, 'subTool': mac.text() } + subgroup.append({'icon':mac.icon(), 'text':mac.text(), 'toolTipHTML': mac.toolTip(), 'action':json.dumps(action), 'subitems':[]}) + # The default action of a menu changes dynamically, instead of triggering the last action, just show the menu. + action = { 'handler': 'toolAction', 'workbenches': toolbarIsInWorkbenches, 'toolbar': toolbarName, 'tool': text, 'showMenu': bool(men) } + group.append({'icon':icon, 'text':text, 'toolTipHTML': tbt.toolTip(), 'action': json.dumps(action), 'subitems': subgroup}) + # TODO: move the 'workbenches' field to the itemgroup + action = { 'handler': 'toolbarAction', 'workbenches': toolbarIsInWorkbenches, 'toolbar': toolbarName } + itemGroups.append({ + 'icon': QtGui.QIcon(':/icons/Group.svg'), + 'text': toolbarName, + 'toolTipHTML': '<p>Display toolbar ' + toolbarName + '</p><p>This toolbar appears in the following workbenches: <ul>' + ''.join(['<li>' + wb + '</li>' for wb in toolbarIsInWorkbenches]) + '</ul></p>', + 'action': json.dumps(action), + 'subitems': group + }) # def document(doc): group = [] for o in doc.Objects: - #all_actions.append(lambda: Gui.Selection.addSelection(o.Document.Name, o.Name)) + #all_actions.append(lambda: ) action = { 'handler': 'documentObjectAction', 'document': o.Document.Name, 'object': o.Name } item = { 'icon': o.ViewObject.Icon if o.ViewObject and o.ViewObject.Icon else None, 'text': o.Label + ' (' + o.Name + ')', + # TODO: preview of the object + 'toolTipHTML': '<p>' + o.Label + '</p><p><code>App.getDocument(' + repr(o.Document.Name) + ').getObject(' + repr(o.Name) + ')</code></p><p>' + '</p><p><img src="data:image/png;base64,.............."></p>', 'action': json.dumps(action), 'subitems': [] } group.append(item) - # Todo: this should also select the document in the tree view - action = { 'handler': 'documentAction', 'document': o.Document.Name } + action = { 'handler': 'documentAction', 'document': doc.Name } itemGroups.append({ 'icon': QtGui.QIcon(':/icons/Document.svg'), 'text': doc.Label + ' (' + doc.Name + ')', + # TODO: preview of the document + 'toolTipHTML': '<p>' + doc.Label + '</p><p><code>App.getDocument(' + repr(doc.Name) + ')</code></p><p><img src="data:image/png;base64,.............."></p>', 'action':json.dumps(action), 'subitems': group }) if App.ActiveDocument: @@ -435,6 +401,7 @@ if True: return { 'icon': serializeIcon(itemGroup['icon']), 'text': itemGroup['text'], + 'toolTipHTML': itemGroup['toolTipHTML'], 'action': itemGroup['action'], 'subitems': serializeItemGroups(itemGroup['subitems']) } @@ -443,10 +410,10 @@ if True: def serialize(itemGroups): return json.dumps(serializeItemGroups(itemGroups)) def deserializeItemGroup(itemGroup): - #print('dIG' + text + " : " + repr(itemGroup['icon'])[:100] + "......................." + repr(itemGroup['icon'])[-100:]) return { 'icon': deserializeIcon(itemGroup['icon']), 'text': itemGroup['text'], + 'toolTipHTML': itemGroup['toolTipHTML'], 'action': itemGroup['action'], 'subitems': deserializeItemGroups(itemGroup['subitems']) } @@ -460,7 +427,10 @@ if True: # sea = SearchBox(itemGroups) - sea.resultSelected.connect(lambda x, y: print('aaa' + repr(y) + 'end')) + def onResultSelected(index, metadata): + action = json.loads(metadata) + actionHandlers[action['handler']](action) + sea.resultSelected.connect(onResultSelected) wax = QtGui.QWidgetAction(None) wax.setDefaultWidget(sea) #mbr.addWidget(sea) diff --git a/SearchTools/SearchTools_notes.py b/SearchTools/SearchTools_notes.py index 6a242ad..bd25882 100644 --- a/SearchTools/SearchTools_notes.py +++ b/SearchTools/SearchTools_notes.py @@ -191,3 +191,153 @@ # return True #filteredProxyModel = FilterProxyModel() #filteredProxyModel.setSourceModel(mdl) + + + + + + + + + + + + + + + + + + + #mdl = QtCore.QStringListModel(['aaa', 'aab', 'aac', 'bxy', 'bac']) + #sbx = SearchBox(mdl, 10, None) + #sbx.show() + + # Inspired by https://stackoverflow.com/a/7767999/324969 + #class SearchQCompleter(QtGui.QCompleter): + # def __init__(self, model, itemDelegate): + # super(SearchQCompleter, self).__init__() + # super(SearchQCompleter, self).setModel(QtCore.QIdentityProxyModel()) + # #https://stackoverflow.com/a/65930408/324969 + # super(SearchQCompleter, self).popup().setItemDelegate(itemDelegate) + # self.setModel(model) + # + # def setModel(self, itemGroups): + # self.itemGroups = itemGroups + # self.filterModel('') + # + # def filterModel(self, userInput): + # def matches(s): + # return userInput.lower() in s.lower() + # def filterGroup(group): + # if matches(group['text']): + # # If a group matches, include the entire subtree (might need to disable this if it causes too much noise) + # return group + # else: + # subitems = filterGroups(group['subitems']) + # if len(subitems) > 0 or matches(group['text']): + # return { 'text': group['text'], 'icon': group['icon'], 'action': group['action'], 'subitems': subitems } + # else: + # return None + # def filterGroups(groups): + # groups = (filterGroup(group) for group in groups) + # return [group for group in groups if group is not None] + # def addGroups(filteredGroups, depth=0): + # for group in filteredGroups: + # mdl.appendRow([QtGui.QStandardItem(group['icon'] or QtGui.QIcon(), group['text']), + # QtGui.QStandardItem(str(depth)), + # QtGui.QStandardItem(group['action'])]) + # addGroups(group['subitems'], depth+1) + # mdl = QtGui.QStandardItemModel() + # mdl.appendColumn([]) + # addGroups(filterGroups(itemGroups)) + # + # print('setSourceModel for userInput ' + repr(userInput) + ' with ' + str(mdl.rowCount()) + ' rows.') + # self.model().setSourceModel(mdl) + # # https://stackoverflow.com/a/65930408/324969 + # + # # the splitPath(self, path) method is called every time the input string changes, before + # # drawing the completion list, we latch onto this method to also update the model to contain + # # the appropriate results, as given by the custom filterAcceptsRow method above. + # def splitPath(self, path): + # self.filterModel(path) + # # Pretend that the user endered the empty string, so that all items from the filteredProxyModel match. + # return '' + # + #class SearchBox(QtGui.QLineEdit): + # resultSelected = QtCore.Signal(int, str) + # def __init__(self, itemGroups): + # super(SearchBox, self).__init__() + # qom = SearchQCompleter(itemGroups, IndentedItemDelegate()) + # qom.setMaxVisibleItems(20) + # #qom.setCompletionMode(QtGui.QCompleter.CompletionMode.PopupCompletion) + # # Thanks to https://saurabhg.com/programming/search-box-using-qlineedit/ for indicating a few useful options + # ico = QtGui.QIcon(':/icons/help-browser.svg') + # #ico = QtGui.QIcon(':/icons/WhatsThis.svg') + # self.addAction(ico, QtGui.QLineEdit.LeadingPosition) + # self.setClearButtonEnabled(True) + # self.setPlaceholderText('Search tools, prefs & tree') + # self.setFixedWidth(200) + # # Signals & slots for enter / click + # def completerActivated(index): + # print('fooooooooooooo') + # print(qom.model().rowCount(), index.row()) + # # TODO: run the action! + # result = str(qom.model().data(index.siblingAtColumn(1))) + # print('res='+result) + # self.clear() + # self.completer().setCompletionPrefix('') + # self.resultSelected.emit(str(index), result) + # def returnPressed(): + # #self.clear() + # #self.completer().setCompletionPrefix('') + # pass + # #text = sea.text() + # #self.clear() + # #self.resultSelected.emit('text returnPressed' + text) + # self.returnPressed.connect(returnPressed) + # #QtCore.QObject.connect(self.completer(), QtCore.SIGNAL('activated(QModelIndex)'), completerActivated) #, QtCore.Qt.ConnectionType.QueuedConnection) + # qom.activated.connect(completerActivated, QtCore.Qt.ConnectionType.DirectConnection) #, QtCore.Qt.ConnectionType.QueuedConnection) + # #self.completer().activated.connect(returnPressedOrCompleterActivated) + # def textChanged(): + # print('textChanged') + # # Workaround: Clear completion prefix and still show the completion box when doing backspace after typing a single character + # if self.text() == '': + # self.completer().setCompletionPrefix(self.text()) + # self.completer().complete() + # self.textChanged.connect(textChanged) + # QtCore.QObject.connect(qom.popup(), QtCore.SIGNAL('clicked(QModelIndex)'), lambda x: print(x)) + # self.setCompleter(qom) + # def focusInEvent(self, e): + # super(SearchBox, self).focusInEvent(e) + # self.completer().setCompletionPrefix(self.text()) + # self.completer().complete() + # self.completer().setCurrentRow(1) # Does not work + # #d=QtGui.QKeyEvent(QtCore.QEvent.KeyPress, QtCore.Qt.Key_Down, QtCore.Qt.NoModifier) + # #QtCore.QCoreApplication.postEvent(self, d) + # def mousePressEvent(self, e): + # super(SearchBox, self).mousePressEvent(e) + # self.completer().setCompletionPrefix(self.text()) + # self.completer().complete() + # self.completer().setCurrentRow(1) # Does not work + # #d=QtGui.QKeyEvent(QtCore.QEvent.KeyPress, QtCore.Qt.Key_Down, QtCore.Qt.NoModifier) + # #QtCore.QCoreApplication.postEvent(self, d) + # + + #sim.appendRow([QtGui.QStandardItem(icon, 't:' + text), QtGui.QStandardItem('tool')]) + + #all_actions.append(mac.trigger) + + #print('whaaaat', str(len(all_actions))) + + #all_actions.append(tbt.actions().trigger) + #global lalala + #lalala=tbt + #print('whuuuut', str(len(all_actions))) + + #viu = mw.findChildren(QtGui.QToolBar, 'View')[0] + #tbt = viu.findChildren(QtGui.QToolButton) + #men = tbt[3].menu() + #acs = men.actions() # QtGui.QAction list + #act = tbt[2].defaultAction() # QtGui.QAction + #act.trigger()