def drawViewItemText(self, style: QStyle, painter: QPainter,
                         option: QStyleOptionViewItem, rect: QRect) -> None:
        """
        Draw view item text in `rect` using `style` and `painter`.
        """
        margin = style.pixelMetric(QStyle.PM_FocusFrameHMargin, None,
                                   option.widget) + 1
        rect = rect.adjusted(margin, 0, -margin, 0)
        font = option.font
        st = self.__static_text_elided_cache(option.text, font,
                                             option.fontMetrics,
                                             option.textElideMode,
                                             rect.width())
        tsize = st.size()
        textalign = int(option.displayAlignment)
        text_pos_x = text_pos_y = 0.0

        if textalign & _Qt_AlignLeft:
            text_pos_x = rect.left()
        elif textalign & _Qt_AlignRight:
            text_pos_x = rect.x() + rect.width() - tsize.width()
        elif textalign & _Qt_AlignHCenter:
            text_pos_x = rect.x() + rect.width() / 2 - tsize.width() / 2

        if textalign & _Qt_AlignVCenter:
            text_pos_y = rect.y() + rect.height() / 2 - tsize.height() / 2
        elif textalign & _Qt_AlignTop:
            text_pos_y = rect.top()
        elif textalign & _Qt_AlignBottom:
            text_pos_y = rect.top() + rect.height() - tsize.height()

        painter.setPen(self.__pen_cache(option.palette, option.state))
        painter.setFont(font)
        painter.drawStaticText(QPointF(text_pos_x, text_pos_y), st)
Exemplo n.º 2
0
    def paint(self, painter, option, index):
        rect = QRect(option.rect)

        is_selected = index.data(VariableSelectionModel.IsSelected)
        full_selection = index.model().sourceModel().is_full()
        if option.state & QStyle.State_MouseOver:
            if not full_selection or (full_selection and is_selected):
                txt = [" 添加 ", " 删除 "][is_selected]
                txtw = painter.fontMetrics().horizontalAdvance(txt)
                painter.save()
                painter.setPen(Qt.NoPen)
                painter.setBrush(option.palette.brush(QPalette.Button))
                brect = QRect(
                    rect.x() + rect.width() - 8 - txtw, rect.y(), txtw, rect.height()
                )
                painter.drawRoundedRect(brect, 4, 4)
                painter.setPen(option.palette.color(QPalette.ButtonText))
                painter.drawText(brect, Qt.AlignCenter, txt)
                painter.restore()

        painter.save()
        double_pen = painter.pen()
        double_pen.setWidth(2 * double_pen.width())
        if is_selected:
            next = index.sibling(index.row() + 1, index.column())
            if not next.isValid():
                painter.setPen(double_pen)
                painter.drawLine(rect.bottomLeft(), rect.bottomRight())
            elif not next.data(VariableSelectionModel.IsSelected):
                painter.drawLine(rect.bottomLeft(), rect.bottomRight())
        elif not index.row():
            down = QPoint(0, painter.pen().width())
            painter.setPen(double_pen)
            painter.drawLine(rect.topLeft() + down, rect.topRight() + down)
        else:
            prev = index.sibling(index.row() - 1, index.column())
            if prev.data(VariableSelectionModel.IsSelected):
                painter.drawLine(rect.topLeft(), rect.topRight())
        painter.restore()

        super().paint(painter, option, index)
Exemplo n.º 3
0
    def paint(self, painter, option, index):
        rect = QRect(option.rect)

        is_selected = index.data(VariableSelectionModel.IsSelected)
        if option.state & QStyle.State_MouseOver:
            txt = [" Add ", " Remove "][is_selected]
            txtw = painter.fontMetrics().width(txt)
            painter.save()
            painter.setPen(Qt.NoPen)
            painter.setBrush(QColor("#ccc"))
            brect = QRect(rect.x() + rect.width() - 8 - txtw, rect.y(), txtw,
                          rect.height())
            painter.drawRoundedRect(brect, 4, 4)
            painter.restore()
            painter.drawText(brect, Qt.AlignCenter, txt)

        painter.save()
        double_pen = painter.pen()
        double_pen.setWidth(2 * double_pen.width())
        if is_selected:
            next = index.sibling(index.row() + 1, index.column())
            if not next.isValid():
                painter.setPen(double_pen)
                painter.drawLine(rect.bottomLeft(), rect.bottomRight())
            elif not next.data(VariableSelectionModel.IsSelected):
                painter.drawLine(rect.bottomLeft(), rect.bottomRight())
        elif not index.row():
            down = QPoint(0, painter.pen().width())
            painter.setPen(double_pen)
            painter.drawLine(rect.topLeft() + down, rect.topRight() + down)
        else:
            prev = index.sibling(index.row() - 1, index.column())
            if prev.data(VariableSelectionModel.IsSelected):
                painter.drawLine(rect.topLeft(), rect.topRight())
        painter.restore()

        super().paint(painter, option, index)
Exemplo n.º 4
0
    def paint(self, painter, option, index):
        rect = QRect(option.rect)

        is_selected = index.data(VariableSelectionModel.IsSelected)
        if option.state & QStyle.State_MouseOver:
            txt = [" Add ", " Remove "][is_selected]
            txtw = painter.fontMetrics().width(txt)
            painter.save()
            painter.setPen(Qt.NoPen)
            painter.setBrush(QColor("#ccc"))
            brect = QRect(rect.x() + rect.width() - 8 - txtw, rect.y(),
                          txtw, rect.height())
            painter.drawRoundedRect(brect, 4, 4)
            painter.restore()
            painter.drawText(brect, Qt.AlignCenter, txt)

        painter.save()
        double_pen = painter.pen()
        double_pen.setWidth(2 * double_pen.width())
        if is_selected:
            next = index.sibling(index.row() + 1, index.column())
            if not next.isValid():
                painter.setPen(double_pen)
                painter.drawLine(rect.bottomLeft(), rect.bottomRight())
            elif not next.data(VariableSelectionModel.IsSelected):
                painter.drawLine(rect.bottomLeft(), rect.bottomRight())
        elif not index.row():
            down = QPoint(0, painter.pen().width())
            painter.setPen(double_pen)
            painter.drawLine(rect.topLeft() + down, rect.topRight() + down)
        else:
            prev = index.sibling(index.row() - 1, index.column())
            if prev.data(VariableSelectionModel.IsSelected):
                painter.drawLine(rect.topLeft(), rect.topRight())
        painter.restore()

        super().paint(painter, option, index)
Exemplo n.º 5
0
    def __paintEventNoStyle(self):
        p = QPainter(self)
        opt = QStyleOptionToolButton()
        self.initStyleOption(opt)

        fm = QFontMetrics(opt.font)
        palette = opt.palette

        # highlight brush is used as the background for the icon and background
        # when the tab is expanded and as mouse hover color (lighter).
        brush_highlight = palette.highlight()
        if opt.state & QStyle.State_Sunken:
            # State 'down' pressed during a mouse press (slightly darker).
            background_brush = brush_darker(brush_highlight, 110)
        elif opt.state & QStyle.State_MouseOver:
            background_brush = brush_darker(brush_highlight, 95)
        elif opt.state & QStyle.State_On:
            background_brush = brush_highlight
        else:
            # The default button brush.
            background_brush = palette.button()

        rect = opt.rect

        icon_area_rect = QRect(rect)
        icon_area_rect.setRight(int(icon_area_rect.height() * 1.26))

        text_rect = QRect(rect)
        text_rect.setLeft(icon_area_rect.right() + 10)

        # Background  (TODO: Should the tab button have native
        # toolbutton shape, drawn using PE_PanelButtonTool or even
        # QToolBox tab shape)

        # Default outline pen
        pen = QPen(palette.color(QPalette.Mid))

        p.save()
        p.setPen(Qt.NoPen)
        p.setBrush(QBrush(background_brush))
        p.drawRect(rect)

        # Draw the background behind the icon if the background_brush
        # is different.
        if not opt.state & QStyle.State_On:
            p.setBrush(brush_highlight)
            p.drawRect(icon_area_rect)
            # Line between the icon and text
            p.setPen(pen)
            p.drawLine(icon_area_rect.topRight(),
                       icon_area_rect.bottomRight())

        if opt.state & QStyle.State_HasFocus:
            # Set the focus frame pen and draw the border
            pen = QPen(QColor(FOCUS_OUTLINE_COLOR))
            p.setPen(pen)
            p.setBrush(Qt.NoBrush)
            # Adjust for pen
            rect = rect.adjusted(0, 0, -1, -1)
            p.drawRect(rect)

        else:
            p.setPen(pen)
            # Draw the top/bottom border
            if self.position == QStyleOptionToolBox.OnlyOneTab or \
                    self.position == QStyleOptionToolBox.Beginning or \
                    self.selected & \
                        QStyleOptionToolBox.PreviousIsSelected:

                p.drawLine(rect.topLeft(), rect.topRight())

            p.drawLine(rect.bottomLeft(), rect.bottomRight())

        p.restore()

        p.save()
        text = fm.elidedText(opt.text, Qt.ElideRight, text_rect.width())
        p.setPen(QPen(palette.color(QPalette.ButtonText)))
        p.setFont(opt.font)

        p.drawText(text_rect,
                   int(Qt.AlignVCenter | Qt.AlignLeft) | \
                   int(Qt.TextSingleLine),
                   text)

        if not opt.icon.isNull():
            if opt.state & QStyle.State_Enabled:
                mode = QIcon.Normal
            else:
                mode = QIcon.Disabled
            if opt.state & QStyle.State_On:
                state = QIcon.On
            else:
                state = QIcon.Off
            icon_area_rect = icon_area_rect
            icon_rect = QRect(QPoint(0, 0), opt.iconSize)
            icon_rect.moveCenter(icon_area_rect.center())
            opt.icon.paint(p, icon_rect, Qt.AlignCenter, mode, state)
        p.restore()
Exemplo n.º 6
0
    def showPopup(self):
        # type: () -> None
        """
        Reimplemented from QComboBox.showPopup

        Popup up a customized view and filter edit line.

        Note
        ----
        The .popup(), .lineEdit(), .completer() of the base class are not used.
        """
        if self.__popup is not None:
            # We have user entered state that cannot be disturbed
            # (entered filter text, scroll offset, ...)
            return  # pragma: no cover

        if self.count() == 0:
            return

        opt = QStyleOptionComboBox()
        self.initStyleOption(opt)
        popup = QListView(
            uniformItemSizes=True,
            horizontalScrollBarPolicy=Qt.ScrollBarAlwaysOff,
            verticalScrollBarPolicy=Qt.ScrollBarAsNeeded,
            iconSize=self.iconSize(),
        )
        popup.setFocusProxy(self.__searchline)
        popup.setParent(self, Qt.Popup | Qt.FramelessWindowHint)
        popup.setItemDelegate(_ComboBoxListDelegate(popup))
        proxy = QSortFilterProxyModel(
            popup, filterCaseSensitivity=Qt.CaseInsensitive
        )
        proxy.setFilterKeyColumn(self.modelColumn())
        proxy.setSourceModel(self.model())
        popup.setModel(proxy)
        root = proxy.mapFromSource(self.rootModelIndex())
        popup.setRootIndex(root)

        self.__popup = popup
        self.__proxy = proxy
        self.__searchline.setText("")
        self.__searchline.setPlaceholderText("Filter...")
        self.__searchline.setVisible(True)
        self.__searchline.textEdited.connect(proxy.setFilterFixedString)

        style = self.style()  # type: QStyle

        popuprect_origin = style.subControlRect(
            QStyle.CC_ComboBox, opt, QStyle.SC_ComboBoxListBoxPopup, self
        )  # type: QRect
        popuprect_origin = QRect(
            self.mapToGlobal(popuprect_origin.topLeft()),
            popuprect_origin.size()
        )
        editrect = style.subControlRect(
            QStyle.CC_ComboBox, opt, QStyle.SC_ComboBoxEditField, self
        )  # type: QRect
        self.__searchline.setGeometry(editrect)
        desktop = QApplication.desktop()
        screenrect = desktop.availableGeometry(self)  # type: QRect

        # get the height for the view
        listrect = QRect()
        for i in range(min(proxy.rowCount(root), self.maxVisibleItems())):
            index = proxy.index(i, self.modelColumn(), root)
            if index.isValid():
                listrect = listrect.united(popup.visualRect(index))
            if listrect.height() >= screenrect.height():
                break
        window = popup.window()  # type: QWidget
        window.ensurePolished()
        if window.layout() is not None:
            window.layout().activate()
        else:
            QApplication.sendEvent(window, QEvent(QEvent.LayoutRequest))

        margins = qwidget_margin_within(popup.viewport(), window)
        height = (listrect.height() + 2 * popup.spacing() +
                  margins.top() + margins.bottom())

        popup_size = (QSize(popuprect_origin.width(), height)
                      .expandedTo(window.minimumSize())
                      .boundedTo(window.maximumSize())
                      .boundedTo(screenrect.size()))
        popuprect = QRect(popuprect_origin.bottomLeft(), popup_size)

        popuprect = dropdown_popup_geometry(
            popuprect, popuprect_origin, screenrect)
        popup.setGeometry(popuprect)

        current = proxy.mapFromSource(
            self.model().index(self.currentIndex(), self.modelColumn(),
                               self.rootModelIndex()))
        popup.setCurrentIndex(current)
        popup.scrollTo(current, QAbstractItemView.EnsureVisible)
        popup.show()
        popup.setFocus(Qt.PopupFocusReason)
        popup.installEventFilter(self)
        popup.viewport().installEventFilter(self)
        popup.viewport().setMouseTracking(True)
        self.update()
        self.__popupTimer.restart()
Exemplo n.º 7
0
class MapMixin:
    """
    This mixin provides maps for `OWScatterPlotBase` based visualizations.

    It expects an `OWScatterPlotBase` like object that must have:
     * `plot_widget` attribute of type pg.PlotWidget
     * `view_box` attribute of type MapViewBox
     * `master` attribute of type OWWidget
    """
    show_internet_error = Signal(bool)

    def __init__(self):
        self.tile_provider = TILE_PROVIDERS[DEFAULT_TILE_PROVIDER]

        self.tile_attribution = AttributionItem(
            parent=self.plot_widget.getViewBox())
        self.tile_attribution.setHtml(self.tile_provider.attribution)

        self.mem_cache = {}
        self.map = None  # type: Optional[Image.Image]

        # we use a background map so transitions between zoom levels looks nicer
        self.b_map_item = None
        self.map_item = None
        self.__new_map_items()

        self.ts = QRect(0, 0, 1, 1)
        self.ts_norm = QRect(0, 0, 1, 1)
        self.tz = 1
        self.zoom_changed = False

        self.loader = ImageLoader(self)
        self.futures = []

        self.view_box.setAspectLocked(lock=True, ratio=1)
        self.view_box.sigRangeChangedManually.connect(self.update_map)
        self.view_box.set_tile_provider(self.tile_provider)

    def _create_legend(self, anchor, brush=QBrush(QColor(232, 232, 232, 200))):
        # by default the legend transparency was to high for colorful maps
        legend = LegendItem(brush=brush)
        legend.setParentItem(self.plot_widget.getViewBox())
        legend.restoreAnchor(anchor)
        return legend

    @staticmethod
    def __new_map_item(z):
        map_item = ImageItem(autoLevels=False)
        map_item.setOpts(axisOrder='row-major')
        map_item.setZValue(z)
        return map_item

    def __new_map_items(self):
        if self.b_map_item is not None:
            self.plot_widget.removeItem(self.b_map_item)
        self.b_map_item = self.__new_map_item(-3)
        self.plot_widget.addItem(self.b_map_item)

        if self.map_item is not None:
            self.plot_widget.removeItem(self.map_item)
        self.map_item = self.__new_map_item(-2)
        self.plot_widget.addItem(self.map_item)

    def _update_view_range(self, min_x, max_x, min_y, max_y, keep_zoom=False):
        if not keep_zoom:
            self.view_box.recalculate_zoom(max_x - min_x, max_y - min_y)

        center = Point(min_x + (max_x - min_x) / 2,
                       min_y + (max_y - min_y) / 2)
        self.view_box.match_zoom(center)

    def update_map(self):
        """Get current view box to calculate which tiles to draw."""
        [min_x, max_x], [min_y, max_y] = self.view_box.viewRange()

        new_zoom = self.view_box.get_zoom()
        self.zoom_changed = self.tz != new_zoom
        self.tz = new_zoom

        # flip y to calculate edge tiles
        tile_min_x, tile_max_y = norm2tile(min_x, 1 - min_y, self.tz)
        tile_max_x, tile_min_y = norm2tile(max_x, 1 - max_y, self.tz)

        # round them to get edge tiles x, y
        tile_min_x = max(int(np.floor(tile_min_x)), 0)
        tile_min_y = max(int(np.floor(tile_min_y)), 0)
        tile_max_x = min(int(np.ceil(tile_max_x)), 2 ** self.tz)
        tile_max_y = min(int(np.ceil(tile_max_y)), 2 ** self.tz)

        self.ts = QRect(tile_min_x, tile_min_y,
                        tile_max_x - tile_min_x,
                        tile_max_y - tile_min_y)

        # transform rounded tile coordinates back
        min_edge_x, min_edge_y = tile2norm(tile_min_x, tile_min_y, self.tz)
        max_edge_x, max_edge_y = tile2norm(tile_max_x, tile_max_y, self.tz)

        # flip y back to transform map
        min_edge_y, max_edge_y = 1 - min_edge_y, 1 - max_edge_y

        # rectangle where to put the map into
        self.ts_norm = QRectF(min_edge_x, min_edge_y,
                              max_edge_x - min_edge_x,
                              max_edge_y - min_edge_y)

        self._map_z_shift()
        self._load_new_map()

    def _map_z_shift(self):
        """If zoom changes move current map to background and draw new over it"""
        if self.zoom_changed:
            self.plot_widget.removeItem(self.b_map_item)
            self.b_map_item = self.map_item
            self.b_map_item.setZValue(-3)
            self.map_item = self.__new_map_item(-2)
            self.plot_widget.addItem(self.map_item)

    def _load_new_map(self):
        """Prepare tiles that are needed in new view."""
        in_mem = []
        to_download = []

        for x in range(self.ts.width()):
            for y in range(self.ts.height()):
                tile = _TileItem(x=self.ts.x() + x, y=self.ts.y() + y,
                                 z=self.tz, tile_provider=self.tile_provider)
                if tile in self.mem_cache:
                    in_mem.append(tile)
                else:
                    to_download.append(tile)

        self._load_from_mem(in_mem)
        self._load_from_net(to_download)

    def _load_from_mem(self, tiles: List[_TileItem]):
        """Create new image object to draw tiles onto it.
        Tiles that are stored in memory are drawn immediately."""
        self.map = Image.new('RGBA',
                             (self.ts.width() * self.tile_provider.size,
                              self.ts.height() * self.tile_provider.size),
                             color="#ffffff00")
        for t in tiles:
            self._add_patch(t)

        self._update_map_item()

    def _add_patch(self, t):
        """Add tile to full image."""
        px = (t.x - self.ts.x()) * self.tile_provider.size
        py = (t.y - self.ts.y()) * self.tile_provider.size
        self.map.paste(self.mem_cache[t], (px, py))

    def _update_map_item(self):
        """Update ImageItem with current image."""
        self.map_item.setImage(np.array(self.map))
        self.map_item.setRect(self.ts_norm)

    def _load_from_net(self, tiles: List[_TileItem]):
        """Tiles that are not in memory are downloaded concurrently and are
        added to the main image dynamically."""

        if self.zoom_changed:
            self._cancel_futures()
            self.futures = []

        for t in tiles:
            self._load_one_from_net(t)

    def _load_one_from_net(self, t: _TileItem):
        """
        Download a tile from the internet. For a tile if we already tried
        to download it three times then show no internet error. If we managed
        to get a tile from the internet clear no internet warning
        """
        if t.n_loadings == 3:
            print(t)
            self.show_internet_error.emit(True)
            return

        future = self.loader.get(t)
        @future.add_done_callback
        def set_tile(_future):
            if _future.cancelled():
                return

            assert _future.done()

            _tile = _future._tile
            if _future.exception():
                _tile.n_loadings += 1
                # retry to download image
                self._load_one_from_net(_tile)
            else:
                img = _future.result()                
                # Added by Jean 2020/05/30 for support of tianditu.gov.cn
                # Download tianditu.gov.cn text marker for the same tile and merge it to the map tile
# from contextily.tile import _fetch_tile                
#   def _load_one_from_net(self, t: _TileItem):
#       def set_tile(_future):
#       ......
                if "tianditu" in _tile.url:
                    url = _tile.url
                    print(url)
                    getMarker = False
                    if "vec_w" in url:
                        url = url.replace("vec_w","cva_w") 
                        getMarker = True
                    elif "img_w" in url:
                        url = url.replace("img_w","cia_w")
                        getMarker = True
                    elif "ter_w" in url:
                        url = url.replace("ter_w","cta_w")
                        getMarker = True
                    if getMarker:
                        # Dowload the corresponding marker tile 
                        image = Image.fromarray( _fetch_tile(url, 0, 3), 'RGBA')
                        # Merge the marker tile to the corresponding map tile
                        r, g, b, alpha = image.split()
                        img = img.convert("RGBA")
                        img = Image.composite(image, img, alpha)
                        print(url)
                                        
                if not _tile.disc_cache:
                    self.show_internet_error.emit(False)
                self.mem_cache[_tile] = img
                self._add_patch(_tile)
                self._update_map_item()
                self.futures.remove(_future)

        self.futures.append(future)

    def _cancel_futures(self):
        for future in self.futures:
            future.cancel()
            if future._reply is not None:
                future._reply.close()
                future._reply.deleteLater()
                future._reply = None

    def _update_tile_provider(self, tp):
        self.clear_map()
        self.tile_provider = tp
        self.view_box.set_tile_provider(self.tile_provider)
        self.tile_attribution.setHtml(self.tile_provider.attribution)
        self.update_map()

    def clear_map(self):
        self._cancel_futures()
        self.futures = []
        self.map = None
        self.tz = 1
        self.__new_map_items()
Exemplo n.º 8
0
    def __layout(self):
        # position itself over `widget`
        # pylint: disable=too-many-branches
        widget = self.__widget
        if widget is None:
            return

        alignment = self.__alignment
        policy = self.sizePolicy()

        if widget.window() is self.window() and not self.isWindow():
            if widget.isWindow():
                bounds = widget.rect()
            else:
                bounds = QRect(widget.mapTo(widget.window(), QPoint(0, 0)),
                               widget.size())
            tl = self.parent().mapFrom(widget.window(), bounds.topLeft())
            bounds = QRect(tl, widget.size())
        else:
            if widget.isWindow():
                bounds = widget.geometry()
            else:
                bounds = QRect(widget.mapToGlobal(QPoint(0, 0)), widget.size())

            if self.isWindow():
                bounds = bounds
            else:
                bounds = QRect(self.parent().mapFromGlobal(bounds.topLeft()),
                               bounds.size())

        sh = self.sizeHint()
        minsh = self.minimumSizeHint()
        minsize = self.minimumSize()
        if minsize.isNull():
            minsize = minsh
        maxsize = bounds.size().boundedTo(self.maximumSize())
        minsize = minsize.boundedTo(maxsize)
        effectivesh = sh.expandedTo(minsize).boundedTo(maxsize)

        hpolicy = policy.horizontalPolicy()
        vpolicy = policy.verticalPolicy()

        if not effectivesh.isValid():
            effectivesh = QSize(0, 0)
            vpolicy = hpolicy = QSizePolicy.Ignored

        def getsize(hint, minimum, maximum, policy):
            if policy == QSizePolicy.Ignored:
                return maximum
            elif policy & QSizePolicy.ExpandFlag:
                return maximum
            else:
                return max(hint, minimum)

        width = getsize(effectivesh.width(), minsize.width(), maxsize.width(),
                        hpolicy)

        heightforw = self.heightForWidth(width)
        if heightforw > 0:
            height = getsize(heightforw, minsize.height(), maxsize.height(),
                             vpolicy)
        else:
            height = getsize(effectivesh.height(), minsize.height(),
                             maxsize.height(), vpolicy)

        size = QSize(width, height)
        if alignment & Qt.AlignLeft:
            x = bounds.x()
        elif alignment & Qt.AlignRight:
            x = bounds.x() + bounds.width() - size.width()
        else:
            x = bounds.x() + max(0, bounds.width() - size.width()) // 2

        if alignment & Qt.AlignTop:
            y = bounds.y()
        elif alignment & Qt.AlignBottom:
            y = bounds.y() + bounds.height() - size.height()
        else:
            y = bounds.y() + max(0, bounds.height() - size.height()) // 2

        geom = QRect(QPoint(x, y), size)
        self.setGeometry(geom)
Exemplo n.º 9
0
    def showPopup(self):
        # type: () -> None
        """
        Reimplemented from QComboBox.showPopup

        Popup up a customized view and filter edit line.

        Note
        ----
        The .popup(), .lineEdit(), .completer() of the base class are not used.
        """
        if self.__popup is not None:
            # We have user entered state that cannot be disturbed
            # (entered filter text, scroll offset, ...)
            return  # pragma: no cover

        if self.count() == 0:
            return

        opt = QStyleOptionComboBox()
        self.initStyleOption(opt)
        popup = QListView(
            uniformItemSizes=True,
            horizontalScrollBarPolicy=Qt.ScrollBarAlwaysOff,
            verticalScrollBarPolicy=Qt.ScrollBarAsNeeded,
            iconSize=self.iconSize(),
        )
        popup.setFocusProxy(self.__searchline)
        popup.setParent(self, Qt.Popup | Qt.FramelessWindowHint)
        popup.setItemDelegate(_ComboBoxListDelegate(popup))
        proxy = QSortFilterProxyModel(
            popup, filterCaseSensitivity=Qt.CaseInsensitive
        )
        proxy.setFilterKeyColumn(self.modelColumn())
        proxy.setSourceModel(self.model())
        popup.setModel(proxy)
        root = proxy.mapFromSource(self.rootModelIndex())
        popup.setRootIndex(root)

        self.__popup = popup
        self.__proxy = proxy
        self.__searchline.setText("")
        self.__searchline.setPlaceholderText("Filter...")
        self.__searchline.setVisible(True)
        self.__searchline.textEdited.connect(proxy.setFilterFixedString)

        style = self.style()  # type: QStyle

        popuprect_origin = style.subControlRect(
            QStyle.CC_ComboBox, opt, QStyle.SC_ComboBoxListBoxPopup, self
        )  # type: QRect
        popuprect_origin = QRect(
            self.mapToGlobal(popuprect_origin.topLeft()),
            popuprect_origin.size()
        )
        editrect = style.subControlRect(
            QStyle.CC_ComboBox, opt, QStyle.SC_ComboBoxEditField, self
        )  # type: QRect
        self.__searchline.setGeometry(editrect)
        desktop = QApplication.desktop()
        screenrect = desktop.availableGeometry(self)  # type: QRect

        # get the height for the view
        listrect = QRect()
        for i in range(min(proxy.rowCount(root), self.maxVisibleItems())):
            index = proxy.index(i, self.modelColumn(), root)
            if index.isValid():
                listrect = listrect.united(popup.visualRect(index))
            if listrect.height() >= screenrect.height():
                break
        window = popup.window()  # type: QWidget
        window.ensurePolished()
        if window.layout() is not None:
            window.layout().activate()
        else:
            QApplication.sendEvent(window, QEvent(QEvent.LayoutRequest))

        margins = qwidget_margin_within(popup.viewport(), window)
        height = (listrect.height() + 2 * popup.spacing() +
                  margins.top() + margins.bottom())

        popup_size = (QSize(popuprect_origin.width(), height)
                      .expandedTo(window.minimumSize())
                      .boundedTo(window.maximumSize())
                      .boundedTo(screenrect.size()))
        popuprect = QRect(popuprect_origin.bottomLeft(), popup_size)

        popuprect = dropdown_popup_geometry(
            popuprect, popuprect_origin, screenrect)
        popup.setGeometry(popuprect)

        current = proxy.mapFromSource(
            self.model().index(self.currentIndex(), self.modelColumn(),
                               self.rootModelIndex()))
        popup.setCurrentIndex(current)
        popup.scrollTo(current, QAbstractItemView.EnsureVisible)
        popup.show()
        popup.setFocus(Qt.PopupFocusReason)
        popup.installEventFilter(self)
        popup.viewport().installEventFilter(self)
        popup.viewport().setMouseTracking(True)
        self.update()
        self.__popupTimer.restart()
Exemplo n.º 10
0
    def __layout(self):
        # position itself over `widget`
        widget = self.__widget
        if widget is None:
            return

        alignment = self.__alignment
        policy = self.sizePolicy()

        if widget.isWindow():
            bounds = widget.geometry()
        else:

            bounds = QRect(widget.mapToGlobal(QPoint(0, 0)),
                           widget.size())
        if self.isWindow():
            bounds = bounds
        else:
            bounds = QRect(self.parent().mapFromGlobal(bounds.topLeft()),
                           bounds.size())

        sh = self.sizeHint()
        minsh = self.minimumSizeHint()
        minsize = self.minimumSize()
        if minsize.isNull():
            minsize = minsh
        maxsize = bounds.size().boundedTo(self.maximumSize())
        minsize = minsize.boundedTo(maxsize)
        effectivesh = sh.expandedTo(minsize).boundedTo(maxsize)

        hpolicy = policy.horizontalPolicy()
        vpolicy = policy.verticalPolicy()

        def getsize(hint, minimum, maximum, policy):
            if policy == QSizePolicy.Ignored:
                return maximum
            elif policy & QSizePolicy.ExpandFlag:
                return maximum
            else:
                return max(hint, minimum)

        width = getsize(effectivesh.width(), minsize.width(),
                        maxsize.width(), hpolicy)

        heightforw = self.heightForWidth(width)
        if heightforw > 0:
            height = getsize(heightforw, minsize.height(),
                             maxsize.height(), vpolicy)
        else:
            height = getsize(effectivesh.height(), minsize.height(),
                             maxsize.height(), vpolicy)

        size = QSize(width, height)
        if alignment & Qt.AlignLeft:
            x = bounds.x()
        elif alignment & Qt.AlignRight:
            x = bounds.right() - size.width()
        else:
            x = bounds.x() + max(0, bounds.width() - size.width()) // 2

        if alignment & Qt.AlignTop:
            y = bounds.y()
        elif alignment & Qt.AlignBottom:
            y = bounds.bottom() - size.height()
        else:
            y = bounds.y() + max(0, bounds.height() - size.height()) // 2

        geom = QRect(QPoint(x, y), size)
        self.setGeometry(geom)
Exemplo n.º 11
0
    def __paintEventNoStyle(self):
        p = QPainter(self)
        opt = QStyleOptionToolButton()
        self.initStyleOption(opt)

        fm = QFontMetrics(opt.font)
        palette = opt.palette

        # highlight brush is used as the background for the icon and background
        # when the tab is expanded and as mouse hover color (lighter).
        brush_highlight = palette.highlight()
        foregroundrole = QPalette.ButtonText
        if opt.state & QStyle.State_Sunken:
            # State 'down' pressed during a mouse press (slightly darker).
            background_brush = brush_darker(brush_highlight, 110)
            foregroundrole = QPalette.HighlightedText
        elif opt.state & QStyle.State_MouseOver:
            background_brush = brush_darker(brush_highlight, 95)
            foregroundrole = QPalette.HighlightedText
        elif opt.state & QStyle.State_On:
            background_brush = brush_highlight
            foregroundrole = QPalette.HighlightedText
        else:
            # The default button brush.
            background_brush = palette.button()

        rect = opt.rect

        icon_area_rect = QRect(rect)
        icon_area_rect.setRight(int(icon_area_rect.height() * 1.26))

        text_rect = QRect(rect)
        text_rect.setLeft(icon_area_rect.right() + 10)

        # Background  (TODO: Should the tab button have native
        # toolbutton shape, drawn using PE_PanelButtonTool or even
        # QToolBox tab shape)

        # Default outline pen
        pen = QPen(palette.color(QPalette.Mid))

        p.save()
        p.setPen(Qt.NoPen)
        p.setBrush(QBrush(background_brush))
        p.drawRect(rect)

        # Draw the background behind the icon if the background_brush
        # is different.
        if not opt.state & QStyle.State_On:
            p.setBrush(brush_highlight)
            p.drawRect(icon_area_rect)
            # Line between the icon and text
            p.setPen(pen)
            p.drawLine(icon_area_rect.topRight(),
                       icon_area_rect.bottomRight())

        if opt.state & QStyle.State_HasFocus:
            # Set the focus frame pen and draw the border
            pen = QPen(QColor(FOCUS_OUTLINE_COLOR))
            p.setPen(pen)
            p.setBrush(Qt.NoBrush)
            # Adjust for pen
            rect = rect.adjusted(0, 0, -1, -1)
            p.drawRect(rect)

        else:
            p.setPen(pen)
            # Draw the top/bottom border
            if self.position == QStyleOptionToolBox.OnlyOneTab or \
                    self.position == QStyleOptionToolBox.Beginning or \
                    self.selected & \
                        QStyleOptionToolBox.PreviousIsSelected:

                p.drawLine(rect.topLeft(), rect.topRight())

            p.drawLine(rect.bottomLeft(), rect.bottomRight())

        p.restore()

        p.save()
        text = fm.elidedText(opt.text, Qt.ElideRight, text_rect.width())
        p.setPen(QPen(palette.color(foregroundrole)))
        p.setFont(opt.font)

        p.drawText(text_rect,
                   int(Qt.AlignVCenter | Qt.AlignLeft) | \
                   int(Qt.TextSingleLine),
                   text)

        if not opt.icon.isNull():
            if opt.state & QStyle.State_Enabled:
                mode = QIcon.Normal
            else:
                mode = QIcon.Disabled
            if opt.state & QStyle.State_On:
                state = QIcon.On
            else:
                state = QIcon.Off
            icon_area_rect = icon_area_rect
            icon_rect = QRect(QPoint(0, 0), opt.iconSize)
            icon_rect.moveCenter(icon_area_rect.center())
            opt.icon.paint(p, icon_rect, Qt.AlignCenter, mode, state)
        p.restore()