source: orange/Orange/OrangeCanvas/orngTabs.py @ 10823:572438ee5431

Revision 10823:572438ee5431, 32.0 KB checked in by Ales Erjavec <ales.erjavec@…>, 2 years ago (diff)

The canvas context menu now also enables widgets which can connect using dynamic channels.

Line 
1# Author: Gregor Leban (gregor.leban@fri.uni-lj.si)
2# Description:
3#    tab for showing widgets and widget button class
4#
5from PyQt4.QtCore import *
6from PyQt4.QtGui import *
7import os.path, sys
8from string import strip, count, replace
9import orngDoc, orngOutput, orngRegistry, orngSignalManager
10from orngSignalManager import InputSignal, OutputSignal, resolveSignal
11import OWGUIEx
12import orngHelp
13
14WB_TOOLBOX = 0
15WB_TREEVIEW = 1
16WB_TREEVIEW_NO_ICONS = 2
17WB_TABBAR_NO_TEXT = 3
18WB_TABBAR_TEXT = 4
19
20# we have to use a custom class since QLabel by default ignores the mouse
21# events if it is showing text (it does not ignore events if it's showing an icon)
22class OrangeLabel(QLabel):
23    def mousePressEvent(self, e):
24        pos = self.mapToParent(e.pos())
25        ev = QMouseEvent(e.type(), pos, e.button(), e.buttons(), e.modifiers())
26        self.parent().mousePressEvent(ev)
27
28    def mouseMoveEvent(self, e):
29        pos = self.mapToParent(e.pos())
30        ev = QMouseEvent(e.type(), pos, e.button(), e.buttons(), e.modifiers())
31        self.parent().mouseMoveEvent(ev)
32
33    def mouseReleaseEvent(self, e):
34        pos = self.mapToParent(e.pos())
35        ev = QMouseEvent(e.type(), pos, e.button(), e.buttons(), e.modifiers())
36        self.parent().mouseReleaseEvent(ev)
37
38
39
40class WidgetButtonBase():
41    def __init__(self, name, widgetInfo, widgetTabs, canvasDlg):
42        self.shiftPressed = 0
43        self.ctrlPressed = 0
44        self.name = name
45        self.widgetInfo = widgetInfo
46        self.widgetTabs = widgetTabs
47        self.canvasDlg = canvasDlg
48
49    def clicked(self, rightClick = False, pos = None):
50        if self.ctrlPressed:
51            qApp.canvasDlg.helpWindow.showHelpFor(self.widgetInfo, False)
52            return
53        win = self.canvasDlg.schema
54        if pos:
55            pos = win.mapFromGlobal(pos)
56            win.addWidget(self.widgetInfo, pos.x(), pos.y())
57        else:
58            win.addWidget(self.widgetInfo)
59        if (rightClick or self.shiftPressed):
60            import orngCanvasItems
61            if isinstance(rightClick, orngCanvasItems.CanvasWidget):
62                win.addLine(rightClick, win.widgets[-1])
63            elif len(win.widgets) > 1:
64                win.addLine(win.widgets[-2], win.widgets[-1])
65       
66        #return win.widgets[-1]
67
68       
69class WidgetButton(QFrame, WidgetButtonBase):
70    def __init__(self, tab, name, widgetInfo, widgetTabs, canvasDlg, buttonType = 2, size=30):
71        QFrame.__init__(self)
72        WidgetButtonBase.__init__(self, name, widgetInfo, widgetTabs, canvasDlg)
73
74        self.buttonType = buttonType
75        self.iconSize = size
76        self.setLayout(buttonType == WB_TOOLBOX and QHBoxLayout() or QVBoxLayout())
77        self.pixmapWidget = QLabel(self)
78
79        self.textWidget = OrangeLabel(self)
80        if buttonType == WB_TABBAR_NO_TEXT:
81            self.textWidget.hide()
82
83        self.layout().setMargin(3)
84        if buttonType != WB_TOOLBOX:
85            self.layout().setSpacing(0)
86           
87        self.icon = canvasDlg.getWidgetIcon(widgetInfo)
88        self.pixmapWidget.setPixmap(self.icon.pixmap(self.iconSize, self.iconSize))
89        self.pixmapWidget.setScaledContents(1)
90        self.pixmapWidget.setFixedSize(QSize(self.iconSize, self.iconSize))
91
92        #split long names into two lines
93        buttonName = name
94        if self.buttonType == WB_TABBAR_TEXT:
95            numSpaces = count(buttonName, " ")
96            if numSpaces == 1: buttonName = replace(buttonName, " ", "<br>")
97            elif numSpaces > 1:
98                mid = len(buttonName)/2; i = 0
99                found = 0
100                while "<br>" not in buttonName:
101                    if buttonName[mid + i] == " ": buttonName = buttonName[:mid + i] + "<br>" + buttonName[(mid + i + 1):]
102                    elif buttonName[mid - i] == " ": buttonName = buttonName[:mid - i] + "<br>" + buttonName[(mid - i + 1):]
103                    i+=1
104            else:
105                buttonName += "<br>"
106
107        self.layout().addWidget(self.pixmapWidget)
108        self.layout().addWidget(self.textWidget)
109
110        if self.buttonType != WB_TOOLBOX:
111            self.textWidget.setText("<div align=\"center\">" + buttonName + "</div>")
112            self.layout().setAlignment(self.pixmapWidget, Qt.AlignHCenter)
113            self.layout().setAlignment(self.textWidget, Qt.AlignHCenter)
114        else:
115            self.textWidget.setText(name)
116        self.setToolTip(widgetInfo.tooltipText)
117
118
119    # we need to handle context menu event, otherwise we get a popup when pressing the right button on one of the icons
120    def contextMenuEvent(self, ev):
121        ev.accept()
122
123    def mouseMoveEvent(self, e):
124        ### Semaphore "busy" is needed for some widgets whose loading takes more time, e.g. Select Data
125        ### Since the active window cannot change during dragging, we wouldn't have to remember the window; but let's leave the code in, it can't hurt
126        schema = self.canvasDlg.schema
127        if hasattr(self, "busy"):
128            return
129        self.busy = 1
130
131        inside = schema.canvasView.rect().contains(schema.canvasView.mapFromGlobal(self.mapToGlobal(e.pos())) - QPoint(50,50))
132        p = QPointF(schema.canvasView.mapFromGlobal(self.mapToGlobal(e.pos()))) + QPointF(schema.canvasView.mapToScene(QPoint(0, 0)))
133
134        dinwin, widget = getattr(self, "widgetDragging", (None, None))
135        if dinwin and (dinwin != schema or not inside):
136             dinwin.removeWidget(widget)
137             delattr(self, "widgetDragging")
138             #dinwin.canvasView.scene().update()
139
140        if inside:
141            if not widget:
142                widget = schema.addWidget(self.widgetInfo, p.x() - 24, p.y() - 24)
143                self.widgetDragging = schema, widget
144
145            # in case we got an exception when creating a widget instance
146            if widget == None:
147                delattr(self, "busy")
148                return
149
150            widget.setCoords(p.x() - widget.rect().width()/2, p.y() - widget.rect().height()/2)
151
152            import orngCanvasItems
153            items = schema.canvas.collidingItems(widget)
154            widget.invalidPosition = widget.selected = (schema.canvasView.findItemTypeCount(items, orngCanvasItems.CanvasWidget) > 0)
155
156        delattr(self, "busy")
157
158    def mousePressEvent(self, e):
159        self.setFrameShape(QFrame.StyledPanel)
160        self.layout().setMargin(self.layout().margin()-2)
161
162    def mouseReleaseEvent(self, e):
163        self.layout().setMargin(self.layout().margin()+2)
164        self.setFrameShape(QFrame.NoFrame)
165        dinwin, widget = getattr(self, "widgetDragging", (None, None))
166        self.shiftPressed = e.modifiers() & Qt.ShiftModifier
167        self.ctrlPressed = e.modifiers() & Qt.ControlModifier
168        if widget:
169            if widget.invalidPosition:
170                dinwin.removeWidget(widget)
171#                dinwin.canvasView.scene().update()
172            elif self.shiftPressed and len(dinwin.widgets) > 1:
173                dinwin.addLine(dinwin.widgets[-2], dinwin.widgets[-1])
174            delattr(self, "widgetDragging")
175       
176        # we say that we clicked the button only if we released the mouse inside the button
177        if e.pos().x() >= 0 and e.pos().x() < self.width() and e.pos().y() > 0 and e.pos().y() < self.height():
178            self.clicked(e.button() == Qt.RightButton)
179
180    def wheelEvent(self, ev):
181        if self.parent() and self.buttonType != WB_TOOLBOX:
182            hs = self.parent().tab.horizontalScrollBar()
183            hs.setValue(min(max(hs.minimum(), hs.value()-ev.delta()), hs.maximum()))
184        else:
185            QFrame.wheelEvent(self, ev)
186
187
188class WidgetTreeItem(QTreeWidgetItem, WidgetButtonBase):
189    def __init__(self, parent, name, widgetInfo, tabs, canvasDlg, wbType=1):
190        QTreeWidgetItem.__init__(self, parent)
191        WidgetButtonBase.__init__(self, name, widgetInfo, tabs, canvasDlg)
192       
193        if wbType == WB_TREEVIEW:
194            self.setIcon(0, canvasDlg.getWidgetIcon(widgetInfo))
195        self.setText(0, name)
196        self.setToolTip(0, widgetInfo.tooltipText)
197   
198    def adjustSize(self):
199        pass
200
201
202class MyTreeWidget(QTreeWidget):
203    def __init__(self, canvasDlg, parent = None):
204        QTreeWidget.__init__(self, parent)
205        self.canvasDlg = canvasDlg
206        self.setMouseTracking(1)
207        self.setHeaderHidden(1)
208        self.mousePressed = 0
209        self.mouseRightClick = 0
210        self.connect(self, SIGNAL("itemClicked (QTreeWidgetItem *,int)"), self.itemClicked)
211        self.setStyleSheet(""" QTreeView::item {padding: 2px 0px 2px 0px} """)          # show items a little bit apart from each other
212
213       
214    def mouseMoveEvent(self, e):
215        if not self.mousePressed:   # this is needed, otherwise another widget in the tree might get selected while we drag the icon to the canvas
216            QTreeWidget.mouseMoveEvent(self, e)
217        ### Semaphore "busy" is needed for some widgets whose loading takes more time, e.g. Select Data
218        ### Since the active window cannot change during dragging, we wouldn't have to remember the window; but let's leave the code in, it can't hurt
219        schema = self.canvasDlg.schema
220        if hasattr(self, "busy"):
221            return
222        self.busy = 1
223
224        inside = schema.canvasView.rect().contains(schema.canvasView.mapFromGlobal(self.mapToGlobal(e.pos())) - QPoint(50,50))
225        p = QPointF(schema.canvasView.mapFromGlobal(self.mapToGlobal(e.pos()))) + QPointF(schema.canvasView.mapToScene(QPoint(0, 0)))
226
227        dinwin, widget = getattr(self, "widgetDragging", (None, None))
228        if dinwin and (dinwin != schema or not inside):
229             dinwin.removeWidget(widget)
230             delattr(self, "widgetDragging")
231#             dinwin.canvasView.scene().update()
232
233        if inside:
234            if not widget and self.selectedItems() != [] and isinstance(self.selectedItems()[0], WidgetTreeItem):
235                widget = schema.addWidget(self.selectedItems()[0].widgetInfo, p.x() - 24, p.y() - 24)
236                self.widgetDragging = schema, widget
237
238            # in case we got an exception when creating a widget instance
239            if widget == None:
240                delattr(self, "busy")
241                return
242
243            widget.setCoords(p.x() - widget.rect().width()/2, p.y() - widget.rect().height()/2)
244#            schema.canvasView.scene().update()
245
246            import orngCanvasItems
247            items = schema.canvas.collidingItems(widget)
248            widget.invalidPosition = widget.selected = (schema.canvasView.findItemTypeCount(items, orngCanvasItems.CanvasWidget) > 0)
249
250        delattr(self, "busy")
251       
252    def mousePressEvent(self, e):
253        QTreeWidget.mousePressEvent(self, e)
254        self.mousePressed = 1
255        self.shiftPressed = bool(e.modifiers() & Qt.ShiftModifier)
256        self.ctrlPressed = bool(e.modifiers() & Qt.ControlModifier)
257        self.mouseRightClick = e.button() == Qt.RightButton
258       
259    def mouseReleaseEvent(self, e):
260        QTreeWidget.mouseReleaseEvent(self, e)
261        dinwin, widget = getattr(self, "widgetDragging", (None, None))
262        self.shiftPressed = bool(e.modifiers() & Qt.ShiftModifier)
263        self.ctrlPressed = bool(e.modifiers() & Qt.ControlModifier)
264        if widget:
265            if widget.invalidPosition:
266                dinwin.removeWidget(widget)
267#                dinwin.canvasView.scene().update()
268            elif self.shiftPressed and len(dinwin.widgets) > 1:
269                dinwin.addLine(dinwin.widgets[-2], dinwin.widgets[-1])
270            delattr(self, "widgetDragging")
271           
272        self.mousePressed = 0
273       
274    def itemClicked(self, item, column):
275        if isinstance(item, WidgetTreeFolder):
276            return
277        if self.ctrlPressed:
278            qApp.canvasDlg.helpWindow.showHelpFor(item.widgetInfo, False)
279            return
280        win = self.canvasDlg.schema
281        win.addWidget(item.widgetInfo)
282        if (self.mouseRightClick or self.shiftPressed) and len(win.widgets) > 1:
283            win.addLine(win.widgets[-2], win.widgets[-1])
284   
285   
286   
287
288           
289class WidgetScrollArea(QScrollArea):
290    def wheelEvent(self, ev):
291        hs = self.horizontalScrollBar()
292        hs.setValue(min(max(hs.minimum(), hs.value()-ev.delta()), hs.maximum()))
293
294
295
296class WidgetListBase:
297    def __init__(self, canvasDlg, widgetInfo):
298        self.canvasDlg = canvasDlg
299        self.widgetInfo = widgetInfo
300        self.allWidgets = []
301        self.tabDict = {}
302        self.tabs = []
303
304    def createWidgetTabs(self, widgetTabList, widgetRegistry, widgetDir, picsDir, defaultPic):
305        self.widgetDir = widgetDir
306        self.picsDir = picsDir
307        self.defaultPic = defaultPic
308        widgetTypeList = self.canvasDlg.settings["widgetListType"]
309        size = min(len(self.canvasDlg.toolbarIconSizeList)-1, self.canvasDlg.settings["toolbarIconSize"])
310        iconSize = self.canvasDlg.toolbarIconSizeList[size]
311       
312        # find tab names that are not in widgetTabList
313        extraTabs = [(name, 1) for name in widgetRegistry.keys() if name not in [tab for (tab, s) in widgetTabList]]
314        extraTabs.sort()
315
316        # first insert the default tab names
317        for (tabName, show) in widgetTabList + extraTabs:
318            if not show or not widgetRegistry.has_key(tabName): continue
319            tab = self.insertWidgetTab(tabName, show)
320           
321            widgets = [(int(widgetInfo.priority), name, widgetInfo) for (name, widgetInfo) in widgetRegistry[tabName].items()]
322            widgets.sort()
323            exIndex = 0
324            for (priority, name, widgetInfo) in widgets:
325                if isinstance(self, WidgetTree):
326                    button = WidgetTreeItem(tab, name, widgetInfo, self, self.canvasDlg, widgetTypeList)
327                else:
328                    button = WidgetButton(tab, name, widgetInfo, self, self.canvasDlg, widgetTypeList, iconSize)
329                    for k in range(priority/1000 - exIndex):
330                        tab.layout().addSpacing(10)
331                    exIndex = priority / 1000
332                    tab.layout().addWidget(button)
333                tab.widgets.append(button)
334                self.allWidgets.append(button)
335
336            if hasattr(tab, "adjustSize"):
337                tab.adjustSize()
338       
339        # return the list of tabs and their status (shown/hidden)
340        return widgetTabList + extraTabs
341                   
342
343
344
345class WidgetTabs(WidgetListBase, QTabWidget):
346    def __init__(self, canvasDlg, widgetInfo, *args):
347        WidgetListBase.__init__(self, canvasDlg, widgetInfo)
348        QTabWidget.__init__(self, *args)
349
350    def insertWidgetTab(self, name, show = 1):
351        if self.tabDict.has_key(name):
352            if show: self.tabDict[name].tab.show()
353            else:    self.tabDict[name].tab.hide()
354            return self.tabDict[name]
355       
356        tab = WidgetScrollArea(self)
357        tab.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
358        tab.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
359
360        widgetSpace = QWidget(self)
361        widgetSpace.setLayout(QHBoxLayout())
362        widgetSpace.layout().setSpacing(0)
363        widgetSpace.layout().setMargin(0)
364        widgetSpace.tab = tab
365        widgetSpace.widgets = []
366        tab.setWidget(widgetSpace)
367
368        self.tabDict[name] = widgetSpace
369
370        if show:
371            self.addTab(tab, name)
372            self.tabs.append((name, 2, widgetSpace))
373        else:
374            tab.hide()
375            self.tabs.append((name, 0, widgetSpace))
376
377        return widgetSpace
378
379
380class WidgetTree(WidgetListBase, QDockWidget):
381    def __init__(self, canvasDlg, widgetInfo, *args):
382        WidgetListBase.__init__(self, canvasDlg, widgetInfo)
383        QDockWidget.__init__(self, "Widgets")
384        self.treeWidget = MyTreeWidget(canvasDlg, self)
385        self.treeWidget.tabDict = self.tabDict
386        self.treeWidget.setFocusPolicy(Qt.ClickFocus)    # this is needed otherwise the document window will sometimes strangely lose focus and the output window will be focused
387        self.actions = categoriesPopup.allActions
388
389        # a widget container to hold the search area and the widget tree
390        self.containerWidget = QWidget()
391        containerBoxLayout = QBoxLayout(QBoxLayout.TopToBottom, self.containerWidget)
392        if sys.platform == "darwin":
393            containerBoxLayout.setContentsMargins(0,0,0,0)
394        self.widgetSuggestEdit = OWGUIEx.lineEditHint(self, None, None, useRE = 0, caseSensitive = 0, matchAnywhere = 1, autoSizeListWidget = 1, callback = self.widgetSuggestCallback)
395        self.widgetSuggestEdit.setItems([QListWidgetItem(action.icon(), action.widgetInfo.name) for action in self.actions])
396        containerBoxLayout.insertWidget(0, self.widgetSuggestEdit)
397        containerBoxLayout.insertWidget(1, self.treeWidget)
398       
399        self.setWidget(self.containerWidget)
400        iconSize = self.canvasDlg.toolbarIconSizeList[self.canvasDlg.settings["toolbarIconSize"]]
401        self.treeWidget.setIconSize(QSize(iconSize, iconSize))
402#        self.treeWidget.setRootIsDecorated(0)
403               
404
405    def insertWidgetTab(self, name, show = 1):
406        path = name.split("/")
407        parent = self
408        for i in xrange(len(path)):
409            fullName = "/".join(path[:i+1])
410            name = path[i]           
411            if parent.tabDict.has_key(name):
412                parent.tabDict[name].setHidden(not show)
413                parent = parent.tabDict[name]
414                continue
415       
416            item = WidgetTreeFolder(self.treeWidget if parent==self else parent, name)
417            item.widgets = []
418            parent.tabDict[name] = item
419
420            if not show:
421                item.setHidden(1)
422            if self.canvasDlg.settings.has_key("treeItemsOpenness") and self.canvasDlg.settings["treeItemsOpenness"].has_key(fullName):
423                item.setExpanded(self.canvasDlg.settings["treeItemsOpenness"][fullName])
424            elif not self.canvasDlg.settings.has_key("treeItemsOpenness") and self.treeWidget.topLevelItemCount() == 1:
425                item.setExpanded(1)
426            self.tabs.append((fullName, 2*int(show), item))
427
428            parent = item
429        return parent
430   
431   
432    def widgetSuggestCallback(self):
433        text = str(self.widgetSuggestEdit.text())
434        for action in self.actions:
435            if action.widgetInfo.name == text:
436                self.widgetInfo = action.widgetInfo
437                self.canvasDlg.schema.addWidget(action.widgetInfo)
438                self.widgetSuggestEdit.clear()
439                return
440
441
442class WidgetTreeFolder(QTreeWidgetItem):
443    def __init__(self, parent, name):
444        QTreeWidgetItem.__init__(self, [name])
445        ix = len(parent.tabDict)
446        if hasattr(parent, "insertTopLevelItem"):
447            parent.insertTopLevelItem(ix, self)
448        else:
449            parent.insertChild(ix, self)
450        self.tabDict = {}
451#        item.setChildIndicatorPolicy(item.ShowIndicator)
452   
453    def mousePressEvent(self, e):
454        self.treeItem.setExpanded(not self.treeItem.isExpanded())
455         
456               
457
458# button that contains the name of the widget category.
459# when clicked it shows or hides the widgets in the category
460class WidgetTreeButton(QPushButton):
461    def __init__(self, treeItem, name, parent):
462        QPushButton.__init__(self, name, parent)
463        self.treeItem = treeItem
464       
465    def mousePressEvent(self, e):
466        self.treeItem.setExpanded(not self.treeItem.isExpanded())
467
468class WidgetToolBox(WidgetListBase, QDockWidget):
469    def __init__(self, canvasDlg, widgetInfo, *args):
470        WidgetListBase.__init__(self, canvasDlg, widgetInfo)
471        QDockWidget.__init__(self, "Widgets")
472        self.actions = categoriesPopup.allActions
473        self.toolbox = MyQToolBox(canvasDlg.settings["toolboxWidth"], self)
474        self.toolbox.setFocusPolicy(Qt.ClickFocus)    # this is needed otherwise the document window will sometimes strangely lose focus and the output window will be focused
475        self.toolbox.layout().setSpacing(0)
476
477        # a widget container to hold the search area and the widget tree
478        self.containerWidget = QWidget()
479        containerBoxLayout = QBoxLayout(QBoxLayout.TopToBottom, self.containerWidget)
480        if sys.platform == "darwin":
481            containerBoxLayout.setContentsMargins(0, 0, 0, 0)
482        self.widgetSuggestEdit = OWGUIEx.lineEditHint(self, None, None, useRE = 0, caseSensitive = 0, matchAnywhere = 1, autoSizeListWidget = 1, callback = self.widgetSuggestCallback)
483        self.widgetSuggestEdit.setItems([QListWidgetItem(action.icon(), action.widgetInfo.name) for action in self.actions])
484        containerBoxLayout.insertWidget(0, self.widgetSuggestEdit)
485        containerBoxLayout.insertWidget(1, self.toolbox)
486
487        self.setWidget(self.containerWidget)
488
489
490    def insertWidgetTab(self, name, show = 1):
491        if self.tabDict.has_key(name):
492            if show: self.tabDict[name].scrollArea.show()
493            else:    self.tabDict[name].scrollArea.hide()
494            return self.tabDict[name]
495       
496        sa = QScrollArea(self.toolbox)
497        sa.setBackgroundRole(QPalette.Base)
498        tab = QFrame(self)
499        tab.scrollArea = sa
500        tab.widgets = []
501        sa.setWidget(tab)
502        sa.setWidgetResizable(0)
503        sa.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
504        tab.setBackgroundRole(QPalette.Base)
505        tab.setLayout(QVBoxLayout())
506        tab.layout().setMargin(0)
507        tab.layout().setSpacing(0)
508        tab.layout().setContentsMargins(6, 6, 6, 6)
509        self.tabDict[name] = tab
510
511        if show:
512            self.toolbox.addItem(sa, name)
513            self.tabs.append((name, 2, tab))
514        else:
515            sa.hide()
516            self.tabs.append((name, 0, tab))
517
518        return tab
519
520    def widgetSuggestCallback(self):
521        text = str(self.widgetSuggestEdit.text())
522        for action in self.actions:
523            if action.widgetInfo.name == text:
524                self.widgetInfo = action.widgetInfo
525                self.canvasDlg.schema.addWidget(action.widgetInfo)
526                self.widgetSuggestEdit.clear()
527                return
528
529
530class MyQToolBox(QToolBox):
531    def __init__(self, size, parent):
532        QToolBox.__init__(self, parent)
533        self.desiredSize = size
534
535    def sizeHint(self):
536        return QSize(self.desiredSize, 100)
537
538
539class CanvasWidgetAction(QWidgetAction):
540    def __init__(self, parent, actions):
541        QWidgetAction.__init__(self, parent)
542        self.parent = parent
543        self.actions = actions
544        self.widgetSuggestEdit = OWGUIEx.lineEditHint(self.parent, None, None, useRE = 0, caseSensitive = 0, matchAnywhere = 1, callback = self.callback, autoSizeListWidget = 1)
545        self.widgetSuggestEdit.setItems([QListWidgetItem(action.icon(), action.widgetInfo.name) for action in actions])
546        self.widgetSuggestEdit.setStyleSheet(""" QLineEdit { background: #fffff0; border: 1px solid orange} """)
547        self.widgetSuggestEdit.listWidget.setStyleSheet(""" QListView { background: #fffff0; } QListView::item {padding: 3px 0px 3px 0px} QListView::item:selected { color: white; background: blue;} """)
548        self.widgetSuggestEdit.listWidget.setIconSize(QSize(16,16)) 
549        self.setDefaultWidget(self.widgetSuggestEdit)
550        self._in_callback = False
551       
552    def callback(self):
553        if not self._in_callback:
554            try:
555                self._in_callback = True
556                text = str(self.widgetSuggestEdit.text())
557                for action in self.actions:
558                    if action.widgetInfo.name == text:
559                        self.widgetInfo = action.widgetInfo
560                        self.parent.setActiveAction(self)
561                        self.activate(QAction.Trigger)
562                        QApplication.sendEvent(self.widgetSuggestEdit, QKeyEvent(QEvent.KeyPress, Qt.Key_Enter, Qt.NoModifier))
563                        return
564            finally:
565                self._in_callback = False
566       
567
568class CanvasPopup(QMenu):
569    def __init__(self, canvasDlg):
570        QMenu.__init__(self, canvasDlg)
571        self.allActions = []
572        self.catActions = []
573        self.allCatActions = []
574        self.quickActions = []
575        self.candidates = []
576        self.canvasDlg = canvasDlg
577        cats = orngRegistry.readCategories(silent=True)
578        self.suggestDict = dict([(widget.name, widget) for widget in reduce(list.__add__, [cat.values() for cat in cats.values()], [])])
579        self.suggestItems = [QListWidgetItem(self.canvasDlg.getWidgetIcon(widget), widget.name) for widget in self.suggestDict.values()]
580        self.categoriesYOffset = 0
581        self._canConnectCache = {}
582               
583    def showEvent(self, ev):
584        QMenu.showEvent(self, ev)
585#        if self.actions() != []:
586#            self.actions()[0].defaultWidget().setFocus()
587        if self.actions() != []:
588            self.actions()[0].defaultWidget().setFocus()
589       
590   
591    def addWidgetSuggest(self):
592        actions = [action for action in self.allActions if action.isEnabled()]
593        self.addAction(CanvasWidgetAction(self, actions))
594        self.addSeparator()
595       
596    def showAllWidgets(self):
597        for cat in self.catActions:
598            cat.setEnabled(True)
599        for act in self.allActions:
600            act.setEnabled(True)
601           
602    def selectActions(self, actClassesAttr, widgetClasses):
603        for cat in self.allCatActions:
604            cat.setEnabled(False)
605           
606        for act in self.allActions:
607            if getattr(act.widgetInfo, actClassesAttr) & widgetClasses:
608                act.setEnabled(True)
609                obj = act
610                while hasattr(obj, "category"):
611                    obj = obj.category
612                    obj.setEnabled(True)
613            else:
614                act.setEnabled(False)
615
616    def selectInputActions(self, widgetInfo):
617        """Enable widgets which can consume the output from `widgetInfo`'s
618        output channels.
619       
620        """
621        for cat in self.allCatActions:
622            cat.setEnabled(False)
623
624        for act in self.allActions:
625            if self.canConnect(widgetInfo, act.widgetInfo):
626                act.setEnabled(True)
627                obj = act
628                while hasattr(obj, "category"):
629                    obj = obj.category
630                    obj.setEnabled(True)
631            else:
632                act.setEnabled(False)
633
634    def selectOutputActions(self, widgetInfo):
635        """Enable widgets which can produce the input for `widgetInfo`'s
636        input channels.
637         
638        """
639        for cat in self.allCatActions:
640            cat.setEnabled(False)
641
642        for act in self.allActions:
643            if self.canConnect(act.widgetInfo, widgetInfo):
644                act.setEnabled(True)
645                obj = act
646                while hasattr(obj, "category"):
647                    obj = obj.category
648                    obj.setEnabled(True)
649            else:
650                act.setEnabled(False)
651
652    def canConnect(self, outWidgetDesc, inWidgetDesc):
653        """Can connect any output from outWidgetDesc to input
654        from inWidgetDesc.
655       
656        """
657        if (outWidgetDesc, inWidgetDesc) not in self._canConnectCache:
658            ret = any(orngSignalManager.canConnect(
659                        resolveSignal(out),
660                        resolveSignal(in_), dynamic=True) \
661                      for out in outWidgetDesc.outputs \
662                      for in_ in inWidgetDesc.inputs
663                      )
664            self._canConnectCache[(outWidgetDesc, inWidgetDesc)] = ret
665        return self._canConnectCache[(outWidgetDesc, inWidgetDesc)]
666
667    def updateWidgesByOutputs(self, widgetInfo):
668        self.selectOutputActions(widgetInfo)
669
670    def updateWidgetsByInputs(self, widgetInfo):
671        self.selectInputActions(widgetInfo)
672
673    def updatePredictedWidgets(self, widgets, actClassesAttr, ioClasses=None):
674        self.candidates = []
675        for widget in widgets:
676            if ioClasses == None:
677                self.candidates.append(widget)
678            else:
679                # filter widgets by allowed signal
680                added = False
681                for category, show in self.canvasDlg.settings["WidgetTabs"]:
682                    if not show or not self.canvasDlg.widgetRegistry.has_key(category):
683                        continue
684   
685                    for candidate in self.canvasDlg.widgetRegistry[category]:
686                        if widget.strip().lower() == candidate.strip().lower():
687                            if getattr(self.canvasDlg.widgetRegistry[category][candidate], actClassesAttr) & ioClasses:
688                                self.candidates.append(candidate)
689                                added = True
690                    if added:
691                        break
692        self.candidates = self.candidates[:3]
693       
694    def updateMenu(self):
695        self.clear()
696        self.addWidgetSuggest()
697        for c in self.candidates:
698            for category, show in self.canvasDlg.settings["WidgetTabs"]:
699                if not show or not self.canvasDlg.widgetRegistry.has_key(category):
700                    continue
701               
702                if c in self.canvasDlg.widgetRegistry[category]:
703                    widgetInfo = self.canvasDlg.widgetRegistry[category][c]
704                   
705                    icon = self.canvasDlg.getWidgetIcon(widgetInfo)
706                    act = self.addAction(icon, widgetInfo.name)
707                    act.widgetInfo = widgetInfo
708                    act.setIconVisibleInMenu(True)
709                    self.quickActions.append(act)
710                    break
711        self.categoriesYOffset = self.sizeHint().height()
712        self.addSeparator()
713        for m in self.catActions:
714            self.addMenu(m)
715           
716   
717       
718
719def constructCategoriesPopup(canvasDlg):
720    global categoriesPopup
721    categoriesPopup = CanvasPopup(canvasDlg)
722    categoriesPopup.setStyleSheet(""" QMenu { background-color: #fffff0; selection-background-color: blue; } QMenu::item { color: black; selection-color: white } QMenu::item:disabled { color: #dddddd } QMenu::separator {height: 3px; background: #dddddd; margin-left: 3px; margin-right: 4px;}""")
723
724    catmenuDict = {}
725    for category, show in canvasDlg.settings["WidgetTabs"]:
726        if not show or not canvasDlg.widgetRegistry.has_key(category):
727            continue
728        path = category.split("/")
729        catmenu = categoriesPopup
730        catmenu.categoryCount = 0
731        for i in xrange(len(path)):
732            fullName = "/".join(path[:i+1])
733            if fullName in catmenuDict:
734                catmenu = catmenuDict[fullName]
735            else:
736                oldcatmenu = catmenu
737                catmenu = catmenu.addMenu(path[i])  # Would be better to insert categories before widgets, but API is rather hard to use ...
738                oldcatmenu.categoryCount += 1
739                catmenu.categoryCount = 0
740                catmenuDict[fullName] = catmenu
741                categoriesPopup.allCatActions.append(catmenu)
742                if i==0:
743                    categoriesPopup.catActions.append(catmenu)
744                else:
745                    catmenu.category = oldcatmenu
746        for widgetInfo in sorted(canvasDlg.widgetRegistry[category].values(), key=lambda x:x.priority):
747            icon = QIcon(canvasDlg.getWidgetIcon(widgetInfo))
748            act = catmenu.addAction(icon, widgetInfo.name)
749            act.widgetInfo = widgetInfo
750            act.category = catmenu
751            act.setIconVisibleInMenu(True)
752            categoriesPopup.allActions.append(act)
753         
754#def constructWidgetSuggest(canvasDlg):
755#    global widgetSuggestEdit
756#    widgetSuggestEdit = OWGUIEx.suggestLineEdit(None, None, None, useRE = 0, caseSensitive = 0, matchAnywhere = 1)
757#    widgetSuggestEdit.setWindowFlags(Qt.Popup)
758#    widgetSuggestEdit.listWidget.setSpacing(2)
759#    widgetSuggestEdit.setStyleSheet(""" QLineEdit { background: #fffff0;} """)
760#    widgetSuggestEdit.listWidget.setStyleSheet(""" QListView { background: #fffff0; } QListView::item {padding: 3px 0px 3px 0px} QListView::item:selected, QListView::item:hover { color: white; background: blue;} """)
761#   
762#    cats = orngRegistry.readCategories()
763#    items = []
764#    for cat in cats.values():
765#        for widget in cat.values():
766#            iconNames = canvasDlg.getFullWidgetIconName(widget)
767#            icon = QIcon()
768#            for name in iconNames:
769#                icon.addPixmap(QPixmap(name))
770#            items.append(QListWidgetItem(icon, widget.name))
771#    widgetSuggestEdit.setItems(items)
772#       
773#   
774#   
Note: See TracBrowser for help on using the repository browser.