def __updateGeometry(self): """ Update the shadow geometry to fit the widget's changed geometry. """ widget = self.__widget parent = self.__widgetParent radius = self.radius_ pos = widget.pos() if parent != widget.parentWidget(): pos = widget.parentWidget().mapTo(parent, pos) geom = QRect(pos, widget.size()) geom.adjust(-radius, -radius, radius, radius) if geom != self.geometry(): self.setGeometry(geom) # Set the widget mask (punch a hole through to the `widget` instance. rect = self.rect() mask = QRegion(rect) transparent = QRegion(rect.adjusted(radius, radius, -radius, -radius)) mask = mask.subtracted(transparent) self.setMask(mask)
def removeTab(self, index): """ Remove a tab at `index`. """ if index >= 0 and index < self.count(): tab = self.__tabs.pop(index) layout_index = self.layout().indexOf(tab.button) if layout_index != -1: self.layout().takeAt(layout_index) self.__group.removeButton(tab.button) tab.button.removeEventFilter(self) if tab.button is self.__sloppyButton: self.__sloppyButton = None self.__sloppyRegion = QRegion() tab.button.deleteLater() tab.button.setParent(None) if self.currentIndex() == index: if self.count(): self.setCurrentIndex(max(index - 1, 0)) else: self.setCurrentIndex(-1)
def paintEvent(self, event): super(SelectArea, self).paintEvent(event) painter = QPainter(self) drawRegion = QRegion(self.pixmap.rect()) drawRegion = drawRegion.subtract(QRegion(self.selected)) painter.setClipRegion(drawRegion) painter.setBrush(QBrush(QColor(0, 0, 0, 120))) painter.drawRect(self.pixmap.rect()) painter.setClipping(False) if self.selected: # направляющие линии painter.setPen(QPen(QColor(255, 255, 255))) painter.setBrush(QBrush(QColor(0, 0, 0, 0))) vertical = self.selected.normalized() vertical.setLeft(-1) vertical.setRight(self.pixmap.rect().right() + 1) horizontal = self.selected.normalized() horizontal.setTop(-1) horizontal.setBottom(self.pixmap.rect().bottom() + 1) painter.drawRects( vertical, horizontal ) # координаты начала bound = self.pixmap.rect() bound.setBottomRight(self.selected.topLeft() - QPoint(5, 5)) painter.drawText( bound, Qt.AlignBottom | Qt.AlignRight, '(%d, %d)' % (self.selected.topLeft().x(), self.selected.topLeft().y()) ) # ширина/высота bound = self.pixmap.rect() bound.setTopLeft(self.selected.bottomRight() + QPoint(5, 5)) painter.drawText( bound, Qt.AlignTop | Qt.AlignLeft, '(%d, %d)' % (self.selected.width(), self.selected.height()) )
def paintEvent(self, e): """ Overload the standard paintEvent function """ #p = QPainter(self.graphicsView) ## our painter p = QPainter(self) ## our painter #r1 = QRegion ( QRect(100,100,200,80), QRegion.Ellipse() ) r1 = QRegion(QRect(self.x, 10, 5, 100)) r2 = QRegion(QRect(100, 120, 10, 30)) ## r2 = rectangular region r3 = QRegion(r1.intersect(r2)) ## r3 = intersection #p.setClipRegion( r1 ) ## set clip region self.drawPoints(p) ## paint clipped graphics
def paintEvent(self, e): """ Overload the standard paintEvent function """ #p = QPainter(self.graphicsView) ## our painter p = QPainter(self) ## our painter #r1 = QRegion ( QRect(100,100,200,80), QRegion.Ellipse() ) r1 = QRegion ( QRect(self.x,10,5,100)) r2 = QRegion ( QRect(100,120,10,30) ) ## r2 = rectangular region r3 = QRegion (r1.intersect( r2 )) ## r3 = intersection #p.setClipRegion( r1 ) ## set clip region self.drawPoints(p) ## paint clipped graphics
def setCurrentIndex(self, index): """ Set the current tab index. """ if self.__currentIndex != index: self.__currentIndex = index self.__sloppyRegion = QRegion() self.__sloppyButton = None if index != -1: self.__tabs[index].button.setChecked(True) self.currentChanged.emit(index)
def _paint_widget(self, p): if self._widget is not None and (self.mode() == Mode.DEBUG or self.mode() == Mode.EDIT_LOGIC): p.setClipRect(Block.padding + 5, 5 + Block.padding, self.width() - 2 * Block.padding - 10, 25) p.translate(self.padding + 5, 5 + self.padding) self._widget.render(p, QPoint(), QRegion(0, 0, self.width(), 25), QWidget.IgnoreMask)
def __init__(self, parent=None, **kwargs): QWidget.__init__(self, parent, **kwargs) layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) self.setLayout(layout) self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Expanding) self.__tabs = [] self.__currentIndex = -1 self.__changeOnHover = False self.__iconSize = QSize(26, 26) self.__group = QButtonGroup(self, exclusive=True) self.__group.buttonPressed[QAbstractButton].connect( self.__onButtonPressed ) self.setMouseTracking(True) self.__sloppyButton = None self.__sloppyRegion = QRegion() self.__sloppyTimer = QTimer(self, singleShot=True) self.__sloppyTimer.timeout.connect(self.__onSloppyTimeout)
def draw(self, painter): src_pixmap, offset = self.sourcePixmap() painter.save() painter.setOpacity(self.transparency) painter.drawPixmap(offset, src_pixmap) painter.restore() #now find the clipping region: src_r = QRegion(self.sourceBoundingRect().toRect()) effect_r = QRegion(self.boundingRect().toRect()) clip_region = effect_r.subtracted(src_r) painter.save() painter.setClipRegion(clip_region) QGraphicsDropShadowEffect.draw(self, painter) painter.restore()
def clearHighlight(self, highlighter): """Removes the highlighted areas of the given highlighter.""" try: (d, t) = self._highlights[highlighter] except KeyError: return del self._highlights[highlighter] self.update(sum((page.rect() for page in d), QRegion()))
def __calcSloppyRegion(self, current): """ Given a current mouse cursor position return a region of the widget where hover/move events should change the current tab only on a timeout. """ p1 = current + QPoint(0, 2) p2 = current + QPoint(0, -2) p3 = self.pos() + QPoint(self.width() + 10, 0) p4 = self.pos() + QPoint(self.width() + 10, self.height()) return QRegion(QPolygon([p1, p2, p3, p4]))
def __init__(self): self.snapshots = snapshots.Snapshots() self.config = self.snapshots.config if len(sys.argv) > 1: if not self.config.set_current_profile(sys.argv[1]): logger.warning("Failed to change Profile_ID %s" % sys.argv[1], self) self.qapp = qt4tools.create_qapplication(self.config.APP_NAME) import icon self.icon = icon self.qapp.setWindowIcon(icon.BIT_LOGO) self.status_icon = QSystemTrayIcon(icon.BIT_LOGO) #self.status_icon.actionCollection().clear() self.contextMenu = QMenu() self.menuProfileName = self.contextMenu.addAction( _('Profile: "%s"') % self.config.get_profile_name()) qt4tools.set_font_bold(self.menuProfileName) self.contextMenu.addSeparator() self.menuStatusMessage = self.contextMenu.addAction(_('Done')) self.menuProgress = self.contextMenu.addAction('') self.menuProgress.setVisible(False) self.contextMenu.addSeparator() self.startBIT = self.contextMenu.addAction(icon.BIT_LOGO, _('Start BackInTime')) QObject.connect(self.startBIT, SIGNAL('triggered()'), self.onStartBIT) self.status_icon.setContextMenu(self.contextMenu) self.pixmap = icon.BIT_LOGO.pixmap(24) self.progressBar = QProgressBar() self.progressBar.setMinimum(0) self.progressBar.setMaximum(100) self.progressBar.setValue(0) self.progressBar.setTextVisible(False) self.progressBar.resize(24, 6) self.progressBar.render(self.pixmap, sourceRegion=QRegion(0, -14, 24, 6), flags=QWidget.RenderFlags( QWidget.DrawChildren)) self.first_error = self.config.is_notify_enabled() self.popup = None self.last_message = None self.timer = QTimer() QObject.connect(self.timer, SIGNAL('timeout()'), self.update_info) self.ppid = os.getppid()
def rubberBandRegion(self, rect): viewport = self.viewport() mask = QStyleHintReturnMask() option = QStyleOptionRubberBand() option.initFrom(viewport) option.rect = rect option.opaque = False option.shape = QRubberBand.Rectangle tmp = QRegion() tmp += rect if viewport.style().styleHint(QStyle.SH_RubberBand_Mask, option, viewport, mask): tmp &= mask.region return tmp
def __init__(self, parent=None, **kwargs): QWidget.__init__(self, parent, **kwargs) layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) self.setLayout(layout) self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Expanding) self.__tabs = [] self.__currentIndex = -1 self.__changeOnHover = False self.__iconSize = QSize(26, 26) self.__group = QButtonGroup(self, exclusive=True) self.__group.buttonPressed[QAbstractButton].connect( self.__onButtonPressed) self.setMouseTracking(True) self.__sloppyButton = None self.__sloppyRegion = QRegion() self.__sloppyTimer = QTimer(self, singleShot=True) self.__sloppyTimer.timeout.connect(self.__onSloppyTimeout)
def autoCropRect(image): """Returns a QRect specifying the contents of the QImage. Edges of the image are trimmed if they have the same color. """ # pick the color at most of the corners colors = collections.defaultdict(int) w, h = image.width(), image.height() for x, y in (0, 0), (w - 1, 0), (w - 1, h - 1), (0, h - 1): colors[image.pixel(x, y)] += 1 most = max(colors, key=colors.get) # let Qt do the masking work mask = image.createMaskFromColor(most) return QRegion(QBitmap.fromImage(mask)).boundingRect()
def visualRegionForSelection(self, selection): print "visualRegionForSelection" ranges = selection.count() if (ranges == 0): return QRect() region = QRegion() # for (int i = 0; i < ranges; ++i) # QItemSelectionRange range = selection.at(i); # for (int row = range.top(); row <= range.bottom(); ++row) # for (int col = range.left(); col <= range.right(); ++col) # QModelIndex index = model()->index(row, col, rootIndex()); # region += visualRect(index); return region
def renderImage(self): contentsSize = self.m_mainFrame.contentsSize() contentsSize -= QSize(self.m_scrollPosition.x(), self.m_scrollPosition.y()) frameRect = QRect(QPoint(0, 0), contentsSize) if not self.m_clipRect.isEmpty(): frameRect = self.m_clipRect viewportSize = self.m_webPage.viewportSize() self.m_webPage.setViewportSize(contentsSize) image = QImage(frameRect.size(), QImage.Format_ARGB32) image.fill(qRgba(255, 255, 255, 0)) painter = QPainter() # We use tiling approach to work-around Qt software rasterizer bug # when dealing with very large paint device. # See http://code.google.com/p/phantomjs/issues/detail?id=54. tileSize = 4096 htiles = (image.width() + tileSize - 1) / tileSize vtiles = (image.height() + tileSize - 1) / tileSize for x in range(htiles): for y in range(vtiles): tileBuffer = QImage(tileSize, tileSize, QImage.Format_ARGB32) tileBuffer.fill(qRgba(255, 255, 255, 0)) # Render the web page onto the small tile first painter.begin(tileBuffer) painter.setRenderHint(QPainter.Antialiasing, True) painter.setRenderHint(QPainter.TextAntialiasing, True) painter.setRenderHint(QPainter.SmoothPixmapTransform, True) painter.translate(-frameRect.left(), -frameRect.top()) painter.translate(-x * tileSize, -y * tileSize) self.m_mainFrame.render(painter, QRegion(frameRect)) painter.end() # Copy the tile to the main buffer painter.begin(image) painter.setCompositionMode(QPainter.CompositionMode_Source) painter.drawImage(x * tileSize, y * tileSize, tileBuffer) painter.end() self.m_webPage.setViewportSize(viewportSize) return image
def render(self, fileName): fileInfo = QFileInfo(fileName) path = QDir() path.mkpath(fileInfo.absolutePath()) if fileName.lower().endswith('.pdf'): return self.renderPdf(fileName) viewportSize = QSize(self.m_page.viewportSize()) pageSize = QSize(self.m_page.mainFrame().contentsSize()) bufferSize = QSize() if not self.m_clipRect.isEmpty(): bufferSize = self.m_clipRect.size() else: bufferSize = self.m_page.mainFrame().contentsSize() if pageSize == '': return False image = QImage(bufferSize, QImage.Format_ARGB32) image.fill(qRgba(255, 255, 255, 0)) p = QPainter(image) p.setRenderHint(QPainter.Antialiasing, True) p.setRenderHint(QPainter.TextAntialiasing, True) p.setRenderHint(QPainter.SmoothPixmapTransform, True) self.m_page.setViewportSize(pageSize) if not self.m_clipRect.isEmpty(): p.translate(-self.m_clipRect.left(), -self.m_clipRect.top()) self.m_page.mainFrame().render(p, QRegion(self.m_clipRect)) else: self.m_page.mainFrame().render(p) p.end() self.m_page.setViewportSize(viewportSize) return image.save(fileName)
def update_info(self): if not tools.is_process_alive(self.ppid): self.prepare_exit() self.qapp.exit(0) return message = self.snapshots.get_take_snapshot_message() if message is None and self.last_message is None: message = (0, _('Working...')) if not message is None: if message != self.last_message: self.last_message = message self.menuStatusMessage.setText('\n'.join(tools.wrap_line(self.last_message[1],\ size = 80,\ delimiters = '',\ new_line_indicator = '') \ )) self.status_icon.setToolTip(self.last_message[1]) pg = progress.ProgressFile(self.config) if pg.isFileReadable(): pg.load() percent = pg.get_int_value('percent') if percent != self.progressBar.value(): self.progressBar.setValue(percent) self.progressBar.render(self.pixmap, sourceRegion=QRegion(0, -14, 24, 6), flags=QWidget.RenderFlags( QWidget.DrawChildren)) self.status_icon.setIcon(QIcon(self.pixmap)) self.menuProgress.setText(' | '.join(self.getMenuProgress(pg))) self.menuProgress.setVisible(True) else: self.status_icon.setIcon(self.icon.BIT_LOGO) self.menuProgress.setVisible(False)
def paintEvent(self, ev): color = self.palette().color(QPalette.Highlight) painter = QPainter(self) # Filled rectangle. painter.setClipRect(self.rect()) color.setAlpha(50) painter.fillRect(self.rect().adjusted(2, 2, -2, -2), color) # Thin rectangle outside. color.setAlpha(150) painter.setPen(color) painter.drawRect(self.rect().adjusted(0, 0, -1, -1)) # Pseudo-handles at the corners and sides color.setAlpha(100) pen = QPen(color) pen.setWidth(8) painter.setPen(pen) painter.setBackgroundMode(Qt.OpaqueMode) # Clip at 4 corners region = QRegion(QRect(0, 0, 20, 20)) region += QRect(self.rect().width() - 20, 0, 20, 20) region += QRect(self.rect().width() - 20, self.rect().height() - 20, 20, 20) region += QRect(0, self.rect().height() - 20, 20, 20) # Clip middles region += QRect(0, self.rect().height() / 2 - 10, self.rect().width(), 20) region += QRect(self.rect().width() / 2 - 10, 0, 20, self.rect().height()) # Draw thicker rectangles, clipped at corners and sides. painter.setClipRegion(region) painter.drawRect(self.rect())
def highlight(self, highlighter, areas, msec=0): """Highlights the list of areas using the given highlighter. Every area is a two-tuple (page, rect), where rect is a rectangle inside (0, 0, 1, 1) like the linkArea attribute of a Poppler.Link. """ d = weakref.WeakKeyDictionary() for page, areas in itertools.groupby(sorted(areas), lambda a: a[0]): d[page] = list(area[1] for area in areas) if msec: def clear(selfref=weakref.ref(self)): self = selfref() if self: self.clearHighlight(highlighter) t = QTimer(singleShot=True, timeout=clear) t.start(msec) else: t = None self.clearHighlight(highlighter) self._highlights[highlighter] = (d, t) self.update(sum((page.rect() for page in d), QRegion()))
def _render_qwebpage_tiled(web_page, logger, web_rect, render_rect, canvas_size): """ Render web page tile-by-tile. This function works around bugs in QPaintEngine that occur when render_rect is larger than 32k pixels in either dimension. """ # One bug is worked around by rendering the page one tile at a time onto a # small-ish temporary image. The magic happens in viewport-window # transformation: painter viewport is moved appropriately so that rendering # region is overlayed onto a temporary "render" image which is then pasted # into the resulting one. # # The other bug manifests itself when you do painter.drawImage when pasting # the rendered tiles. Once you reach 32'768 along either dimension all of # a sudden drawImage simply stops drawing anything. This is a known # limitation of Qt painting system where coordinates are signed short ints. # The simplest workaround that comes to mind is to use pillow for pasting. tile_conf = _calculate_tiling( to_paint=render_rect.intersected(QRect(QPoint(0, 0), canvas_size))) canvas = Image.new('RGBA', _qsize_to_tuple(canvas_size)) ratio = render_rect.width() / float(web_rect.width()) tile_qimage = QImage(tile_conf['tile_size'], QImage.Format_ARGB32) painter = QPainter(tile_qimage) try: painter.setRenderHint(QPainter.Antialiasing, True) painter.setRenderHint(QPainter.TextAntialiasing, True) painter.setRenderHint(QPainter.SmoothPixmapTransform, True) painter.setWindow(web_rect) # painter.setViewport here seems superfluous (actual viewport is being # set inside the loop below), but it is not. For some reason, if # viewport is reset after setClipRect, clipping rectangle is adjusted, # which is not what we want. painter.setViewport(render_rect) # painter.setClipRect(web_rect) for i in xrange(tile_conf['horizontal_count']): left = i * tile_qimage.width() for j in xrange(tile_conf['vertical_count']): top = j * tile_qimage.height() painter.setViewport(render_rect.translated(-left, -top)) logger.log("Rendering with viewport=%s" % painter.viewport(), min_level=2) clip_rect = QRect( QPoint(floor(left / ratio), floor(top / ratio)), QPoint(ceil((left + tile_qimage.width()) / ratio), ceil((top + tile_qimage.height()) / ratio))) web_page.mainFrame().render(painter, QRegion(clip_rect)) tile_image = qimage_to_pil_image(tile_qimage) # If this is the bottommost tile, its bottom may have stuff # left over from rendering the previous tile. Make sure these # leftovers don't garble the bottom of the canvas which can be # larger than render_rect because of "height=" option. rendered_vsize = min(render_rect.height() - top, tile_qimage.height()) if rendered_vsize < tile_qimage.height(): box = (0, 0, tile_qimage.width(), rendered_vsize) tile_image = tile_image.crop(box) logger.log("Pasting rendered tile to coords: %s" % ((left, top),), min_level=2) canvas.paste(tile_image, (left, top)) finally: # It is important to end painter explicitly in python code, because # Python finalizer invocation order, unlike C++ destructors, is not # deterministic and there is a possibility of image's finalizer running # before painter's which may break tests and kill your cat. painter.end() return WrappedPillowImage(canvas)
class TabBarWidget(QWidget): """ A tab bar widget using tool buttons as tabs. """ # TODO: A uniform size box layout. currentChanged = Signal(int) def __init__(self, parent=None, **kwargs): QWidget.__init__(self, parent, **kwargs) layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) self.setLayout(layout) self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Expanding) self.__tabs = [] self.__currentIndex = -1 self.__changeOnHover = False self.__iconSize = QSize(26, 26) self.__group = QButtonGroup(self, exclusive=True) self.__group.buttonPressed[QAbstractButton].connect( self.__onButtonPressed ) self.setMouseTracking(True) self.__sloppyButton = None self.__sloppyRegion = QRegion() self.__sloppyTimer = QTimer(self, singleShot=True) self.__sloppyTimer.timeout.connect(self.__onSloppyTimeout) def setChangeOnHover(self, changeOnHover): """ If set to ``True`` the tab widget will change the current index when the mouse hovers over a tab button. """ if self.__changeOnHover != changeOnHover: self.__changeOnHover = changeOnHover def changeOnHover(self): """ Does the current tab index follow the mouse cursor. """ return self.__changeOnHover def count(self): """ Return the number of tabs in the widget. """ return len(self.__tabs) def addTab(self, text, icon=None, toolTip=None): """ Add a new tab and return it's index. """ return self.insertTab(self.count(), text, icon, toolTip) def insertTab(self, index, text, icon=None, toolTip=None): """ Insert a tab at `index` """ button = TabButton(self, objectName="tab-button") button.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) button.setIconSize(self.__iconSize) button.setMouseTracking(True) self.__group.addButton(button) button.installEventFilter(self) tab = _Tab(text, icon, toolTip, button, None, None) self.layout().insertWidget(index, button) self.__tabs.insert(index, tab) self.__updateTab(index) if self.currentIndex() == -1: self.setCurrentIndex(0) return index def removeTab(self, index): """ Remove a tab at `index`. """ if index >= 0 and index < self.count(): self.layout().takeItem(index) tab = self.__tabs.pop(index) self.__group.removeButton(tab.button) tab.button.removeEventFilter(self) if tab.button is self.__sloppyButton: self.__sloppyButton = None self.__sloppyRegion = QRegion() tab.button.deleteLater() if self.currentIndex() == index: if self.count(): self.setCurrentIndex(max(index - 1, 0)) else: self.setCurrentIndex(-1) def setTabIcon(self, index, icon): """ Set the `icon` for tab at `index`. """ self.__tabs[index] = self.__tabs[index]._replace(icon=icon) self.__updateTab(index) def setTabToolTip(self, index, toolTip): """ Set `toolTip` for tab at `index`. """ self.__tabs[index] = self.__tabs[index]._replace(toolTip=toolTip) self.__updateTab(index) def setTabText(self, index, text): """ Set tab `text` for tab at `index` """ self.__tabs[index] = self.__tabs[index]._replace(text=text) self.__updateTab(index) def setTabPalette(self, index, palette): """ Set the tab button palette. """ self.__tabs[index] = self.__tabs[index]._replace(palette=palette) self.__updateTab(index) def setCurrentIndex(self, index): """ Set the current tab index. """ if self.__currentIndex != index: self.__currentIndex = index self.__sloppyRegion = QRegion() self.__sloppyButton = None if index != -1: self.__tabs[index].button.setChecked(True) self.currentChanged.emit(index) def currentIndex(self): """ Return the current index. """ return self.__currentIndex def button(self, index): """ Return the `TabButton` instance for index. """ return self.__tabs[index].button def setIconSize(self, size): if self.__iconSize != size: self.__iconSize = size for tab in self.__tabs: tab.button.setIconSize(self.__iconSize) def __updateTab(self, index): """ Update the tab button. """ tab = self.__tabs[index] b = tab.button if tab.text: b.setText(tab.text) if tab.icon is not None and not tab.icon.isNull(): b.setIcon(tab.icon) if tab.palette: b.setPalette(tab.palette) def __onButtonPressed(self, button): for i, tab in enumerate(self.__tabs): if tab.button is button: self.setCurrentIndex(i) break def __calcSloppyRegion(self, current): """ Given a current mouse cursor position return a region of the widget where hover/move events should change the current tab only on a timeout. """ p1 = current + QPoint(0, 2) p2 = current + QPoint(0, -2) p3 = self.pos() + QPoint(self.width()+10, 0) p4 = self.pos() + QPoint(self.width()+10, self.height()) return QRegion(QPolygon([p1, p2, p3, p4])) def __setSloppyButton(self, button): """ Set the current sloppy button (a tab button inside sloppy region) and reset the sloppy timeout. """ if not button.isChecked(): self.__sloppyButton = button delay = self.style().styleHint(QStyle.SH_Menu_SubMenuPopupDelay, None) # The delay timeout is the same as used by Qt in the QMenu. self.__sloppyTimer.start(delay) else: self.__sloppyTimer.stop() def __onSloppyTimeout(self): if self.__sloppyButton is not None: button = self.__sloppyButton self.__sloppyButton = None if not button.isChecked(): index = [tab.button for tab in self.__tabs].index(button) self.setCurrentIndex(index) def eventFilter(self, receiver, event): if event.type() == QEvent.MouseMove and \ isinstance(receiver, TabButton): pos = receiver.mapTo(self, event.pos()) if self.__sloppyRegion.contains(pos): self.__setSloppyButton(receiver) else: if not receiver.isChecked(): index = [tab.button for tab in self.__tabs].index(receiver) self.setCurrentIndex(index) #also update sloppy region if mouse is moved on the same icon self.__sloppyRegion = self.__calcSloppyRegion(pos) return QWidget.eventFilter(self, receiver, event) def leaveEvent(self, event): self.__sloppyButton = None self.__sloppyRegion = QRegion() return QWidget.leaveEvent(self, event)
def resizeEvent(self, ev): """Called on resize, sets our circular mask.""" self.setMask(QRegion(self.rect(), QRegion.Ellipse))
def _render_qwebpage_tiled(web_page, logger, viewport_size, image_size): tile_maxsize = defaults.TILE_MAXSIZE draw_width = viewport_size.width() draw_height = min(viewport_size.height(), image_size.height()) # One bug is worked around by rendering the page one tile at a time onto a # small-ish temporary image. The magic happens in viewport-window # transformation: # # - Sizes of tile painter viewport and tile painter window match # webpage viewport size to avoid rescaling. # - Tile painter window is moved appropriately so that tile region is # overlayed onto the temporary image. tile_hsize = min(tile_maxsize, draw_width) tile_vsize = min(tile_maxsize, draw_height) htiles = 1 + (draw_width - 1) // tile_hsize vtiles = 1 + (draw_height - 1) // tile_vsize tile_image = QImage(QSize(tile_hsize, tile_vsize), QImage.Format_ARGB32) ratio = viewport_size.width() / float(web_page.viewportSize().width()) # The other bug manifests itself when you do painter.drawImage trying to # concatenate tiles onto a single image and once you reach 32'768 along # either dimension all of a sudden drawImage simply stops drawing anything. # The simplest workaround that comes to mind is to use pillow for pasting # images. pil_image = Image.new(mode='RGBA', size=(image_size.width(), image_size.height())) painter = QPainter(tile_image) try: painter.setRenderHint(QPainter.Antialiasing, True) painter.setRenderHint(QPainter.TextAntialiasing, True) painter.setRenderHint(QPainter.SmoothPixmapTransform, True) painter.setWindow(QRect(QPoint(0, 0), web_page.viewportSize())) painter_viewport = QRect(QPoint(0, 0), viewport_size) for i in xrange(htiles): for j in xrange(vtiles): left, top = i * tile_hsize, j * tile_vsize painter.setViewport(painter_viewport.translated(-left, -top)) logger.log("Rendering with viewport=%s" % painter.viewport(), min_level=2) clip_rect = QRect( QPoint(floor(left / ratio), floor(top / ratio)), QPoint(ceil((left + tile_hsize) / ratio), ceil((top + tile_vsize) / ratio))) web_page.mainFrame().render(painter, QRegion(clip_rect)) pil_tile_image = qimage_to_pil_image(tile_image) if viewport_size.height() - top < tile_vsize: # If this is the last tile, make sure that the bottom of # the image is not garbled: the last tile's bottom may be # clipped and will then have stuff left over from rendering # the previous tile. Crop it, because the image can be # taller than the viewport because of "height=" option. box = (0, 0, tile_hsize, viewport_size.height() - top) pil_tile_image = pil_tile_image.crop(box) if logger: logger.log("Pasting rendered tile to coords: %s" % ((left, top), ), min_level=2) pil_image.paste(pil_tile_image, (left, top)) # Make sure that painter.end() is invoked before destroying the # underlying image. finally: painter.end() return pil_image
def leaveEvent(self, event): self.__sloppyButton = None self.__sloppyRegion = QRegion() return QWidget.leaveEvent(self, event)
class TabBarWidget(QWidget): """ A tab bar widget using tool buttons as tabs. """ # TODO: A uniform size box layout. currentChanged = Signal(int) def __init__(self, parent=None, **kwargs): QWidget.__init__(self, parent, **kwargs) layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) self.setLayout(layout) self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Expanding) self.__tabs = [] self.__currentIndex = -1 self.__changeOnHover = False self.__iconSize = QSize(26, 26) self.__group = QButtonGroup(self, exclusive=True) self.__group.buttonPressed[QAbstractButton].connect( self.__onButtonPressed) self.setMouseTracking(True) self.__sloppyButton = None self.__sloppyRegion = QRegion() self.__sloppyTimer = QTimer(self, singleShot=True) self.__sloppyTimer.timeout.connect(self.__onSloppyTimeout) def setChangeOnHover(self, changeOnHover): """ If set to ``True`` the tab widget will change the current index when the mouse hovers over a tab button. """ if self.__changeOnHover != changeOnHover: self.__changeOnHover = changeOnHover def changeOnHover(self): """ Does the current tab index follow the mouse cursor. """ return self.__changeOnHover def count(self): """ Return the number of tabs in the widget. """ return len(self.__tabs) def addTab(self, text, icon=None, toolTip=None): """ Add a new tab and return it's index. """ return self.insertTab(self.count(), text, icon, toolTip) def insertTab(self, index, text, icon=None, toolTip=None): """ Insert a tab at `index` """ button = TabButton(self, objectName="tab-button") button.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) button.setIconSize(self.__iconSize) button.setMouseTracking(True) self.__group.addButton(button) button.installEventFilter(self) tab = _Tab(text, icon, toolTip, button, None, None) self.layout().insertWidget(index, button) self.__tabs.insert(index, tab) self.__updateTab(index) if self.currentIndex() == -1: self.setCurrentIndex(0) return index def removeTab(self, index): """ Remove a tab at `index`. """ if index >= 0 and index < self.count(): self.layout().takeItem(index) tab = self.__tabs.pop(index) self.__group.removeButton(tab.button) tab.button.removeEventFilter(self) if tab.button is self.__sloppyButton: self.__sloppyButton = None self.__sloppyRegion = QRegion() tab.button.deleteLater() if self.currentIndex() == index: if self.count(): self.setCurrentIndex(max(index - 1, 0)) else: self.setCurrentIndex(-1) def setTabIcon(self, index, icon): """ Set the `icon` for tab at `index`. """ self.__tabs[index] = self.__tabs[index]._replace(icon=icon) self.__updateTab(index) def setTabToolTip(self, index, toolTip): """ Set `toolTip` for tab at `index`. """ self.__tabs[index] = self.__tabs[index]._replace(toolTip=toolTip) self.__updateTab(index) def setTabText(self, index, text): """ Set tab `text` for tab at `index` """ self.__tabs[index] = self.__tabs[index]._replace(text=text) self.__updateTab(index) def setTabPalette(self, index, palette): """ Set the tab button palette. """ self.__tabs[index] = self.__tabs[index]._replace(palette=palette) self.__updateTab(index) def setCurrentIndex(self, index): """ Set the current tab index. """ if self.__currentIndex != index: self.__currentIndex = index self.__sloppyRegion = QRegion() self.__sloppyButton = None if index != -1: self.__tabs[index].button.setChecked(True) self.currentChanged.emit(index) def currentIndex(self): """ Return the current index. """ return self.__currentIndex def button(self, index): """ Return the `TabButton` instance for index. """ return self.__tabs[index].button def setIconSize(self, size): if self.__iconSize != size: self.__iconSize = size for tab in self.__tabs: tab.button.setIconSize(self.__iconSize) def __updateTab(self, index): """ Update the tab button. """ tab = self.__tabs[index] b = tab.button if tab.text: b.setText(tab.text) if tab.icon is not None and not tab.icon.isNull(): b.setIcon(tab.icon) if tab.palette: b.setPalette(tab.palette) def __onButtonPressed(self, button): for i, tab in enumerate(self.__tabs): if tab.button is button: self.setCurrentIndex(i) break def __calcSloppyRegion(self, current): """ Given a current mouse cursor position return a region of the widget where hover/move events should change the current tab only on a timeout. """ p1 = current + QPoint(0, 2) p2 = current + QPoint(0, -2) p3 = self.pos() + QPoint(self.width() + 10, 0) p4 = self.pos() + QPoint(self.width() + 10, self.height()) return QRegion(QPolygon([p1, p2, p3, p4])) def __setSloppyButton(self, button): """ Set the current sloppy button (a tab button inside sloppy region) and reset the sloppy timeout. """ if not button.isChecked(): self.__sloppyButton = button delay = self.style().styleHint(QStyle.SH_Menu_SubMenuPopupDelay, None) # The delay timeout is the same as used by Qt in the QMenu. self.__sloppyTimer.start(delay) else: self.__sloppyTimer.stop() def __onSloppyTimeout(self): if self.__sloppyButton is not None: button = self.__sloppyButton self.__sloppyButton = None if not button.isChecked(): index = [tab.button for tab in self.__tabs].index(button) self.setCurrentIndex(index) def eventFilter(self, receiver, event): if event.type() == QEvent.MouseMove and \ isinstance(receiver, TabButton): pos = receiver.mapTo(self, event.pos()) if self.__sloppyRegion.contains(pos): self.__setSloppyButton(receiver) else: if not receiver.isChecked(): index = [tab.button for tab in self.__tabs].index(receiver) self.setCurrentIndex(index) #also update sloppy region if mouse is moved on the same icon self.__sloppyRegion = self.__calcSloppyRegion(pos) return QWidget.eventFilter(self, receiver, event) def leaveEvent(self, event): self.__sloppyButton = None self.__sloppyRegion = QRegion() return QWidget.leaveEvent(self, event)