Changeset 11495:12e60f527869 in orange


Ignore:
Timestamp:
05/08/13 20:06:17 (11 months ago)
Author:
Ales Erjavec <ales.erjavec@…>
Branch:
default
Message:

Added a popup menu class with widgets displayed in a list.

Location:
Orange/OrangeCanvas
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • Orange/OrangeCanvas/application/canvasmain.py

    r11488 r11495  
    1414    QMainWindow, QWidget, QAction, QActionGroup, QMenu, QMenuBar, QDialog, 
    1515    QFileDialog, QMessageBox, QVBoxLayout, QSizePolicy, QColor, QKeySequence, 
    16     QIcon, QToolBar, QToolButton, QDockWidget, QDesktopServices, QApplication 
     16    QIcon, QToolBar, QToolButton, QDockWidget, QDesktopServices, QApplication, 
     17    QCursor 
    1718) 
    1819 
     
    3839from ..help import HelpManager 
    3940 
    40 from .canvastooldock import CanvasToolDock, QuickCategoryToolbar 
     41from .canvastooldock import CanvasToolDock, QuickCategoryToolbar, \ 
     42                            CategoryPopupMenu 
    4143from .aboutdialog import AboutDialog 
    4244from .schemeinfo import SchemeInfoDialog 
     
    749751        """ 
    750752        category = action.text() 
    751         for i in range(self.widgets_tool_box.count()): 
    752             cat_act = self.widgets_tool_box.tabAction(i) 
    753             if cat_act.text() == category: 
    754                 if not cat_act.isChecked(): 
    755                     # Trigger the action to expand the tool grid contained 
    756                     # within. 
    757                     cat_act.trigger() 
    758  
    759             else: 
    760                 if cat_act.isChecked(): 
    761                     # Trigger the action to hide the tool grid contained 
    762                     # within. 
    763                     cat_act.trigger() 
    764  
    765         self.dock_widget.expand() 
     753        if self.use_popover: 
     754            # Show a popup menu with the widgets in the category 
     755            m = CategoryPopupMenu(self.quick_category) 
     756            reg = self.widget_registry.model() 
     757            i = index(self.widget_registry.categories(), category, 
     758                      predicate=lambda name, cat: cat.name == name) 
     759            if i != -1: 
     760                m.setCategoryItem(reg.item(i)) 
     761                action = m.exec_(QCursor.pos()) 
     762                if action is not None: 
     763                    self.on_tool_box_widget_activated(action) 
     764 
     765        else: 
     766            for i in range(self.widgets_tool_box.count()): 
     767                cat_act = self.widgets_tool_box.tabAction(i) 
     768                cat_act.setChecked(cat_act.text() == category) 
     769 
     770            self.dock_widget.expand() 
    766771 
    767772    def set_scheme_margins_enabled(self, enabled): 
     
    17371742                           type=bool) 
    17381743 
     1744        self.use_popover = \ 
     1745            settings.value("toolbox-dock-use-popover-menu", defaultValue=True, 
     1746                           type=bool) 
     1747 
    17391748 
    17401749def updated_flags(flags, mask, state): 
  • Orange/OrangeCanvas/application/canvastooldock.py

    r11243 r11495  
    33 
    44""" 
     5import sys 
     6 
    57from PyQt4.QtGui import ( 
    68    QWidget, QSplitter, QVBoxLayout, QTextEdit, QAction, QPalette, 
    7     QSizePolicy 
     9    QSizePolicy, QApplication, QDrag 
    810) 
    911 
    10 from PyQt4.QtCore import Qt, QSize, QObject, QPropertyAnimation, QEvent 
    11 from PyQt4.QtCore import pyqtProperty as Property 
     12from PyQt4.QtCore import ( 
     13    Qt, QSize, QObject, QPropertyAnimation, QEvent, QRect, 
     14    QModelIndex, QPersistentModelIndex, QEventLoop, QMimeData 
     15) 
     16 
     17from PyQt4.QtCore import pyqtProperty as Property, pyqtSignal as Signal 
    1218 
    1319from ..gui.toolgrid import ToolGrid 
    1420from ..gui.toolbar import DynamicResizeToolBar 
    1521from ..gui.quickhelp import QuickHelp 
     22from ..gui.framelesswindow import FramelessWindow 
     23from ..document.quickmenu import MenuPage 
    1624from .widgettoolbox import WidgetToolBox, iter_item 
    1725 
    1826from ..registry.qt import QtWidgetRegistry 
     27from ..utils.qtcompat import toPyObject 
    1928 
    2029 
     
    337346                action = self._gridSlots[index].action 
    338347                self.removeAction(action) 
     348 
     349 
     350class CategoryPopupMenu(FramelessWindow): 
     351    triggered = Signal(QAction) 
     352    hovered = Signal(QAction) 
     353 
     354    def __init__(self, parent=None, **kwargs): 
     355        FramelessWindow.__init__(self, parent, **kwargs) 
     356        self.setWindowFlags(self.windowFlags() | Qt.Popup) 
     357 
     358        layout = QVBoxLayout() 
     359        layout.setContentsMargins(6, 6, 6, 6) 
     360 
     361        self.__menu = MenuPage() 
     362        self.__menu.setActionRole(QtWidgetRegistry.WIDGET_ACTION_ROLE) 
     363 
     364        if sys.platform == "darwin": 
     365            self.__menu.view().setAttribute(Qt.WA_MacShowFocusRect, False) 
     366 
     367        self.__menu.triggered.connect(self.__onTriggered) 
     368        self.__menu.hovered.connect(self.hovered) 
     369 
     370        self.__dragListener = ItemViewDragStartEventListener(self) 
     371        self.__dragListener.dragStarted.connect(self.__onDragStarted) 
     372 
     373        self.__menu.view().viewport().installEventFilter(self.__dragListener) 
     374 
     375        layout.addWidget(self.__menu) 
     376 
     377        self.setLayout(layout) 
     378 
     379        self.__action = None 
     380        self.__loop = None 
     381        self.__item = None 
     382 
     383    def setCategoryItem(self, item): 
     384        """ 
     385        Set the category root item (:class:`QStandardItem`). 
     386        """ 
     387        self.__item = item 
     388        model = item.model() 
     389        self.__menu.setModel(model) 
     390        self.__menu.setRootIndex(item.index()) 
     391 
     392    def popup(self, pos=None): 
     393        if pos is None: 
     394            pos = self.pos() 
     395        geom = widget_popup_geometry(pos, self) 
     396        self.setGeometry(geom) 
     397        self.show() 
     398 
     399    def exec_(self, pos=None): 
     400        self.popup(pos) 
     401        self.__loop = QEventLoop() 
     402 
     403        self.__action = None 
     404        self.__loop.exec_() 
     405        self.__loop = None 
     406 
     407        if self.__action is not None: 
     408            action = self.__action 
     409        else: 
     410            action = None 
     411        return action 
     412 
     413    def hideEvent(self, event): 
     414        if self.__loop is not None: 
     415            self.__loop.exit(0) 
     416 
     417        return FramelessWindow.hideEvent(self, event) 
     418 
     419    def __onTriggered(self, action): 
     420        self.__action = action 
     421        self.triggered.emit(action) 
     422        self.hide() 
     423 
     424        if self.__loop: 
     425            self.__loop.exit(0) 
     426 
     427    def __onDragStarted(self, index): 
     428        desc = toPyObject(index.data(QtWidgetRegistry.WIDGET_DESC_ROLE)) 
     429        icon = toPyObject(index.data(Qt.DecorationRole)) 
     430 
     431        drag_data = QMimeData() 
     432        drag_data.setData( 
     433            "application/vnv.orange-canvas.registry.qualified-name", 
     434            desc.qualified_name 
     435        ) 
     436        drag = QDrag(self) 
     437        drag.setPixmap(icon.pixmap(38)) 
     438        drag.setMimeData(drag_data) 
     439 
     440        # TODO: Should animate (accept) hide. 
     441        self.hide() 
     442 
     443        # When a drag is started and the menu hidden the item's tool tip 
     444        # can still show for a short time UNDER the cursor preventing a 
     445        # drop. 
     446        viewport = self.__menu.view().viewport() 
     447        filter = ToolTipEventFilter() 
     448        viewport.installEventFilter(filter) 
     449 
     450        drag.exec_(Qt.CopyAction) 
     451 
     452        viewport.removeEventFilter(filter) 
     453 
     454 
     455class ItemViewDragStartEventListener(QObject): 
     456    dragStarted = Signal(QModelIndex) 
     457 
     458    def __init__(self, parent=None): 
     459        QObject.__init__(self, parent) 
     460        self._pos = None 
     461        self._index = None 
     462 
     463    def eventFilter(self, viewport, event): 
     464        view = viewport.parent() 
     465 
     466        if event.type() == QEvent.MouseButtonPress and \ 
     467                event.button() == Qt.LeftButton: 
     468 
     469            index = view.indexAt(event.pos()) 
     470 
     471            if index is not None: 
     472                self._pos = event.pos() 
     473                self._index = QPersistentModelIndex(index) 
     474 
     475        elif event.type() == QEvent.MouseMove and self._pos is not None and \ 
     476                ((self._pos - event.pos()).manhattanLength() >= 
     477                 QApplication.startDragDistance()): 
     478 
     479            if self._index.isValid(): 
     480                # Map to a QModelIndex in the model. 
     481                index = self._index 
     482                index = index.model().index(index.row(), index.column(), 
     483                                            index.parent()) 
     484                self._pos = None 
     485                self._index = None 
     486 
     487                self.dragStarted.emit(index) 
     488 
     489        return QObject.eventFilter(self, view, event) 
     490 
     491 
     492class ToolTipEventFilter(QObject): 
     493    def eventFilter(self, receiver, event): 
     494        if event.type() == QEvent.ToolTip: 
     495            return True 
     496 
     497        return QObject.eventFilter(self, receiver, event) 
     498 
     499 
     500def widget_popup_geometry(pos, widget): 
     501    widget.ensurePolished() 
     502 
     503    if widget.testAttribute(Qt.WA_Resized): 
     504        size = widget.size() 
     505    else: 
     506        size = widget.sizeHint() 
     507 
     508    desktop = QApplication.desktop() 
     509    screen_geom = desktop.availableGeometry(pos) 
     510 
     511    # Adjust the size to fit inside the screen. 
     512    if size.height() > screen_geom.height(): 
     513        size.setHeight(screen_geom.height()) 
     514    if size.width() > screen_geom.width(): 
     515        size.setWidth(screen_geom.width()) 
     516 
     517    geom = QRect(pos, size) 
     518 
     519    if geom.top() < screen_geom.top(): 
     520        geom.setTop(screen_geom.top()) 
     521 
     522    if geom.left() < screen_geom.left(): 
     523        geom.setLeft(screen_geom.left()) 
     524 
     525    bottom_margin = screen_geom.bottom() - geom.bottom() 
     526    right_margin = screen_geom.right() - geom.right() 
     527    if bottom_margin < 0: 
     528        # Falls over the bottom of the screen, move it up. 
     529        geom.translate(0, bottom_margin) 
     530 
     531    # TODO: right to left locale 
     532    if right_margin < 0: 
     533        # Falls over the right screen edge, move the menu to the 
     534        # other side of pos. 
     535        geom.translate(-size.width(), 0) 
     536 
     537    return geom 
  • Orange/OrangeCanvas/config.py

    r11488 r11495  
    7070     ("mainwindow/toolbox-dock-movable", bool, True, 
    7171      "Is the canvas toolbox movable (between left and right edge)"), 
     72 
     73     ("mainwindow/toolbox-dock-use-popover-menu", bool, True, 
     74      "Use a popover menu to select a widget when clicking on a category " 
     75      "button"), 
    7276 
    7377     ("mainwindow/number-of-recent-schemes", int, 7, 
  • Orange/OrangeCanvas/styles/orange.qss

    r11491 r11495  
    103103 
    104104 
    105  
    106 /* 
    107  *QuickCategoryToolbar _QuickCategoryButton { 
    108  *    qproperty-nativeStyling_: "true"; 
    109  *    background-color: palette(button); 
    110  *    border: none; 
    111  *    border-bottom: 1px solid palette(dark); 
    112  *} 
    113  */ 
     105/* QuickCategoryToolbar popup menus */ 
     106 
     107CategoryPopupMenu { 
     108    background-color: #E9EFF2; 
     109} 
     110 
     111CategoryPopupMenu ToolTree QTreeView::item { 
     112    height: 25px; 
     113    border-bottom: 1px solid #e9eff2; 
     114} 
     115 
     116CategoryPopupMenu QTreeView::item:hover { 
     117    background: qlineargradient( 
     118        x1: 0, y1: 0, x2: 0, y2: 1, 
     119        stop: 0 #688EF6, 
     120        stop: 0.5 #4047f4, 
     121        stop: 1.0 #2D68F3 
     122    ); 
     123    color: white; 
     124} 
     125 
     126CategoryPopupMenu QTreeView::item:selected { 
     127    background-color: blue; 
     128    color: white; 
     129} 
    114130 
    115131 
Note: See TracChangeset for help on using the changeset viewer.