def drawBg(self, event, painter): painter.save() painter.setPen(Qt.NoPen) if self.checked: painter.setBrush(self.bgColorOn) else: painter.setBrush(self.bgColorOff) rect = QRect(0, 0, self.width(), self.height()) # 半径为高度的一半 radius = rect.height() / 2 # 圆的宽度为高度 circleWidth = rect.height() path = QPainterPath() path.moveTo(radius, rect.left()) path.arcTo(QRectF(rect.left(), rect.top(), circleWidth, circleWidth), 90, 180) path.lineTo(rect.width() - radius, rect.height()) path.arcTo( QRectF(rect.width() - rect.height(), rect.top(), circleWidth, circleWidth), 270, 180) path.lineTo(radius, rect.top()) painter.drawPath(path) painter.restore()
def paintHorizontalCell(self, painter: QPainter, hv: QHeaderView, cellIndex: QModelIndex, leafIndex: QModelIndex, logicalLeafIndex: int, styleOptions: QStyleOptionHeader, sectionRect: QRect, top: int): uniopt = QStyleOptionHeader(styleOptions) self.setForegroundBrush(uniopt, cellIndex) self.setBackgroundBrush(uniopt, cellIndex) height = self.cellSize(cellIndex, hv, uniopt).height() if cellIndex == leafIndex: height = sectionRect.height() - top left = self.currentCellLeft(cellIndex, leafIndex, logicalLeafIndex, sectionRect.left(), hv) width = self.currentCellWidth(cellIndex, leafIndex, logicalLeafIndex, hv) r = QRect(left, top, width, height) uniopt.text = cellIndex.data(Qt.DisplayRole) painter.save() uniopt.rect = r if cellIndex.data(Qt.UserRole): hv.style().drawControl(QStyle.CE_HeaderSection, uniopt, painter, hv) m = QTransform() m.rotate(-90) painter.setWorldMatrix(m, True) new_r = QRect(0, 0, r.height(), r.width()) new_r.moveCenter(QPoint(-r.center().y(), r.center().x())) uniopt.rect = new_r hv.style().drawControl(QStyle.CE_HeaderLabel, uniopt, painter, hv) else: hv.style().drawControl(QStyle.CE_Header, uniopt, painter, hv) painter.restore() return top + height
def drawBox(self, painter): """ Paint box """ color = self._color rect = QRect(0, 0, self._box_sqsize, self._box_sqsize) for sz in range(3): painter.setPen(color.lighter(200 // (sz + 1))) painter.drawRect(rect.x() + sz, rect.y() + sz, rect.width() - (sz * 2), rect.height() - (sz * 2)) painter.fillRect(rect.x() + 3, rect.y() + 3, rect.width() - 6, rect.height() - 6, color.lighter(200)) if self._checked: painter.setPen(QColor(0, 0, 0)) painter.drawLine(rect.x() + 2, rect.y() + (rect.height() // 2), rect.width() // 2, rect.y() + (rect.height() - 2)) painter.drawLine(rect.width() // 2, rect.y() + (rect.height() - 2), rect.width() - 1, rect.y() + 2)
def centerMultiScreen(self): mouse_pointer_position = QApplication.desktop().cursor().pos() screen = QApplication.desktop().screenNumber(mouse_pointer_position) print("current screen " + str(screen)) self.window_one = QRect(QApplication.desktop().screenGeometry(0)) self.window_two = QRect(QApplication.desktop().screenGeometry(1)) if screen == 0: window_geometry = QRect(QApplication.desktop().screenGeometry(1)) self.resize(window_geometry.width(), window_geometry.height()) center_point = QApplication.desktop().screenGeometry(1).center() self.arrowsSet.height_k = (self.window_two.height() / self.window_one.height()) self.arrowsSet.width_k = (self.window_two.width() / self.window_one.width()) elif screen == 1: window_geometry = QRect(QApplication.desktop().screenGeometry(0)) self.resize(window_geometry.width(), window_geometry.height()) center_point = QApplication.desktop().screenGeometry(0).center() self.arrowsSet.height_k = (self.window_one.height() / self.window_two.height()) self.arrowsSet.width_k = (self.window_one.width() / self.window_two.width()) else: window_geometry = self.frameGeometry() center_point = QDesktopWidget.availableGeometry().center() window_geometry.moveCenter(center_point) self.move(window_geometry.topLeft())
def _calculate_output_image_parameters(self, web_viewport: QRect, img_width: Optional[int], img_height: Optional[int] ) -> Tuple[QRect, QSize]: """ Calculate parameters of the resulting image to render - coordinates and size of rescaled and truncated image. Return ``(image_viewport, image_size)`` tuple. ``web_viewport`` is a QRect to render, in webpage coordinates. FIXME: add tests for it, to make the behavior clear. """ if img_width is None: img_width = web_viewport.width() ratio = 1.0 else: if img_width == 0 or web_viewport.width() == 0: ratio = 1.0 else: ratio = img_width / float(web_viewport.width()) image_viewport = QRect( QPoint(0, 0), QSize(img_width, round(web_viewport.height() * ratio))) if img_height is None: img_height = image_viewport.height() image_size = QSize(img_width, img_height) return image_viewport, image_size
class ElevationSquare(QGraphicsItem): """A red-green filled square with altitudes""" def __init__(self, start, end, levels, parent=None): super().__init__(parent) self.start, self.end = start, end self.levels = levels self.rect = QRect(0, 0, 100, 100) def paint(self, painter: QPainter, option, widget=None): painter.setPen(QPen(Qt.black, 0)) self.rect = getRect(widget) gradient = QLinearGradient(self.rect.topLeft(), self.rect.bottomLeft()) gradient.setColorAt(0, Qt.red) gradient.setColorAt(1, Qt.green) painter.setBrush(gradient) painter.drawRect(self.rect) metrics = painter.fontMetrics() for level in self.levels: text = str(int(level)) w, h = metrics.width(text), metrics.height() y = self.rect.height() - (level - self.start) / ( self.end - self.start) * self.rect.height() + self.rect.y() - h / 2 x = self.rect.x() - w - 10 text_rect = QRectF(x, y, w, h) painter.drawText(text_rect, Qt.AlignRight, text) def boundingRect(self): adjust = 2 return QRectF(self.rect.x() - adjust, self.rect.y() - adjust, self.rect.width() + adjust, self.rect.height() + adjust)
def setTrackGeometry(self, rect: QtCore.QRect, sidebar_width: int, separator_height: int, show_top_sep: bool) -> None: handle_width = sidebar_width if show_top_sep: self.top_separator.setVisible(True) self.top_separator.setGeometry(rect.x(), rect.y() - separator_height, rect.width(), separator_height) else: self.top_separator.setVisible(False) self.handle.setVisible(True) self.handle.setGeometry(rect.x(), rect.y(), handle_width, rect.height()) self.track_editor.setVisible(True) self.track_editor.setGeometry(rect.x() + handle_width, rect.y(), rect.width() - handle_width, rect.height()) self.__hide_label_small_track = (rect.height() < self.label.height() + 4) self.label.move(rect.x() + handle_width + 4, rect.y() + 2) self.label.setVisible(not self.__hide_label_under_mouse and not self.__hide_label_small_track) self.separator.setVisible(True) self.separator.setGeometry(rect.x(), rect.y() + rect.height(), rect.width(), separator_height)
def paintEvent(self, e: QPaintEvent) -> None: painter = QPainter(self) painter.setPen(QPen(Qt.black, 2)) # paint background brush = QBrush() brush.setColor(QColor('black')) brush.setStyle(Qt.SolidPattern) rect = QRect(0, 0, painter.device().width(), painter.device().height()) painter.fillRect(rect, brush) pix_ratio = self.pix.rect().width() / self.pix.rect().height() w_ratio = rect.width() / rect.height() if pix_ratio > w_ratio: w = rect.width() h = rect.width() / pix_ratio x = 0 y = (rect.height() - h) / 2 else: h = rect.height() w = rect.height() * pix_ratio x = (rect.width() - w) / 2 y = 0 self.pix_rect = QRect(int(x), int(y), int(w), int(h)) painter.drawPixmap(self.pix_rect, self.pix, self.pix.rect()) for rid, r in self.robot_manager.robots.items(): pos = r.pos.pos xr = pos.x * w / 3000 + x yr = (2000 - pos.y) * h / 2000 + y painter.setBrush(Qt.red) center = QPoint(int(xr), int(yr)) painter.drawEllipse(center, ROBOT_SIZE, ROBOT_SIZE) head = center + QPoint(ROBOT_SIZE * cos(-pos.theta), ROBOT_SIZE * sin(-pos.theta)) painter.setPen(QPen(Qt.black, 2)) painter.drawLine(center, head) a1 = head + QPoint(ARROW_SIZE * cos(-pos.theta + ARROW_ANGLE), ARROW_SIZE * sin(-pos.theta + ARROW_ANGLE)) a2 = head + QPoint(ARROW_SIZE * cos(-pos.theta - ARROW_ANGLE), ARROW_SIZE * sin(-pos.theta - ARROW_ANGLE)) painter.setBrush(Qt.black) painter.drawPolygon(a1, head, a2) pos_cmd = self.getPosCmd() if pos_cmd is not None: x, y, theta = pos_cmd if theta is not None: painter.drawLine(self.press_pos.x(), self.press_pos.y(), self.current_pos.x(), self.current_pos.y()) a1 = self.current_pos + QPoint( ARROW_SIZE * cos(-theta + ARROW_ANGLE), ARROW_SIZE * sin(-theta + ARROW_ANGLE)) a2 = self.current_pos + QPoint( ARROW_SIZE * cos(-theta - ARROW_ANGLE), ARROW_SIZE * sin(-theta - ARROW_ANGLE)) painter.setBrush(Qt.black) painter.drawPolygon(a1, self.current_pos, a2)
class VisualWindow(QWidget): def __init__(self, parent=None): super(VisualWindow, self).__init__(parent) self.setWindowFlags(QtCore.Qt.Dialog) self.arrowsSet = Arrows() self.initUI() def initUI(self): self.setLayout(self.arrowsSet) self.setColorPalette(confParser.config_file.read("color", "bg_color")) self.setHidden(True) def find_available_desktops(self): number_available_desktops = QDesktopWidget().screenCount() if number_available_desktops > 1: self.centerMultiScreen() else: self.centerSingleScreen() def centerSingleScreen(self): qr = self.frameGeometry() cp = QDesktopWidget().availableGeometry().center() qr.moveCenter(cp) self.move(qr.topLeft()) def centerMultiScreen(self): mouse_pointer_position = QApplication.desktop().cursor().pos() screen = QApplication.desktop().screenNumber(mouse_pointer_position) print("current screen " + str(screen)) self.window_one = QRect(QApplication.desktop().screenGeometry(0)) self.window_two = QRect(QApplication.desktop().screenGeometry(1)) if screen == 0: window_geometry = QRect(QApplication.desktop().screenGeometry(1)) self.resize(window_geometry.width(), window_geometry.height()) center_point = QApplication.desktop().screenGeometry(1).center() self.arrowsSet.height_k = (self.window_two.height() / self.window_one.height()) self.arrowsSet.width_k = (self.window_two.width() / self.window_one.width()) elif screen == 1: window_geometry = QRect(QApplication.desktop().screenGeometry(0)) self.resize(window_geometry.width(), window_geometry.height()) center_point = QApplication.desktop().screenGeometry(0).center() self.arrowsSet.height_k = (self.window_one.height() / self.window_two.height()) self.arrowsSet.width_k = (self.window_one.width() / self.window_two.width()) else: window_geometry = self.frameGeometry() center_point = QDesktopWidget.availableGeometry().center() window_geometry.moveCenter(center_point) self.move(window_geometry.topLeft()) def setColorPalette(self, current_bg_color): confParser.config_file.write("color", "bg_color", current_bg_color) background_color = QColor() background_color.setNamedColor(current_bg_color) p = self.palette() p.setColor(self.backgroundRole(), background_color) self.setPalette(p)
def paintEvent(self, e): painter = QPainter(self) draw_rect = e.rect() # SLIDER--------------------------------------------------------------------- # BAR -------- painter.setPen(self._bgColor.lighter(200)) painter.drawRect(draw_rect.adjusted(0, 0, -1, -1)) painter.fillRect(draw_rect.adjusted(1, 1, -1, -1), self._bgColor) # THUMB -------- painter.setPen(self._thumbColor.lighter()) painter.drawRect(self._thumbRect.adjusted(0, 0, -1, -1)) painter.fillRect(self._thumbRect.adjusted(1, 1, -1, -1), self._thumbColor) # VALUE LABEL -------- value_text = str(self._value) value_label_size = QSize(self._fontMetrics.width(value_text), self._fontMetrics.height()) value_indicator_rect = QRect(self._thumbRect.right() + 2, self._thumbRect.top(), value_label_size.width() + 10, self._thumbRect.height()) if value_indicator_rect.right() > draw_rect.right(): value_indicator_rect.setRect( self._thumbRect.left() - value_indicator_rect.width() - 1, value_indicator_rect.top(), value_indicator_rect.width(), value_indicator_rect.height()) painter.setPen(self._indicatorColor.lighter()) painter.drawRect(value_indicator_rect.adjusted(0, 0, -1, -1)) painter.fillRect(value_indicator_rect.adjusted(1, 1, -1, -1), self._indicatorColor) painter.setPen(Qt.white) painter.drawText( value_indicator_rect.left() + value_indicator_rect.width() / 2 - value_label_size.width() / 2, value_indicator_rect.top() + value_indicator_rect.height() / 2 + value_label_size.height() / 3, value_text)
def paintEvent(self, event): QWidget.paintEvent(self, event) width, height = self.width(), self.height() polygon = QPolygon() for i, rate in enumerate(self.loads): x = width - i * self.pointDistance y = height - rate * height if x < self.boxWidth: break polygon.append(QPoint(x, y)) painter = QPainter(self) pen = QPen() pen.setColor(Qt.darkGreen) painter.setPen(pen) painter.setRenderHint(QPainter.Antialiasing, True) #画网格 painter.setOpacity(0.5) gridSize = self.pointDistance * 4 deltaX = (width - self.boxWidth) % gridSize + self.boxWidth deltaY = height % gridSize for i in range(int(width / gridSize)): x = deltaX + gridSize * i painter.drawLine(x, 0, x, height) for j in range(int(height / gridSize)): y = j * gridSize + deltaY painter.drawLine(self.boxWidth, y, width, y) #画折线 pen.setColor(Qt.darkCyan) pen.setWidth(2) painter.setPen(pen) painter.setOpacity(1) painter.drawPolyline(polygon) #画展示框 if len(self.loads) > 0: rate = self.loads[0] else: rate = 1.0 rect1 = QRect(4, height * 0.05, self.boxWidth - 9, height * 0.7) rect2 = QRect(4, height * 0.8, self.boxWidth - 9, height * 0.2) centerX = int(rect1.width() / 2) + 1 pen.setWidth(1) for i in range(rect1.height()): if i % 4 == 0: continue if (rect1.height() - i) / rect1.height() > rate: pen.setColor(Qt.darkGreen) else: pen.setColor(Qt.green) painter.setPen(pen) for j in range(rect1.width()): if centerX - 1 <= j <= centerX + 1: continue painter.drawPoint(rect1.x() + j, rect1.y() + i) pen.setColor(Qt.black) painter.setPen(pen) painter.drawText(rect2, Qt.AlignHCenter | Qt.AlignVCenter, str(int(rate * 100)) + "%")
def _calculate_tiling(self, to_paint: QRect) -> _TilingOptions: tile_maxsize = defaults.TILE_MAXSIZE tile_hsize = min(tile_maxsize, to_paint.width()) tile_vsize = min(tile_maxsize, to_paint.height()) htiles = 1 + (to_paint.width() - 1) // tile_hsize vtiles = 1 + (to_paint.height() - 1) // tile_vsize tile_size = QSize(tile_hsize, tile_vsize) return _TilingOptions(horizontal_count=htiles, vertical_count=vtiles, tile_size=tile_size)
def font_charBmp(font, char): metric = QFontMetrics(font).boundingRect(char) char_rect = QRect(0, 0, metric.width() + 1, metric.height()) chr_img = QImage(char_rect.width() + 1, char_rect.height(), QImage.Format_Mono) chr_img.fill(0) # set img painter and draw char to bmp painter = QPainter(chr_img) painter.setPen(QPen(Qt.white)) painter.setFont(font) painter.drawText(char_rect, Qt.AlignJustify, char) painter.end() del (painter) # crop left / right x0 = 0 x1 = char_rect.width() while x0 < x1 - 1: data_col = 0 for col in range(char_rect.height()): data_col += chr_img.pixel(x0, col) & 0x00FFFFFF if not data_col: x0 += 1 else: break char_rect.setX(x0) while x1 > x0 + 1: x1 -= 1 data_col = 0 for col in range(char_rect.height()): data_col += chr_img.pixel(x1, col) & 0x00FFFFFF if not data_col: char_rect.setWidth(x1 - x0) else: break # crop bottom y1 = char_rect.height() while y1 > 1: y1 -= 1 data_row = 0 for row in range(char_rect.width()): data_row += chr_img.pixel(row, y1) & 0x00FFFFFF if not data_row: char_rect.setHeight(y1) else: break chr_img = chr_img.copy(char_rect) # chr_img.save( '.\\img\\0%s.bmp' % char, 'bmp' ) return chr_img
class GraphicsTile(QGraphicsItem): def __init__(self, x_y_w_h, slide_path, level, downsample): super().__init__() self.x_y_w_h = x_y_w_h self.slide_rect_0 = QRect(int(x_y_w_h[0] * downsample), int(self.x_y_w_h[1] * downsample), x_y_w_h[2], x_y_w_h[3]) self.slide_path = slide_path self.level = level self.downsample = downsample self.setAcceptedMouseButtons(Qt.NoButton) self.setAcceptHoverEvents(True) self.cache_key = slide_path + str(level) + str(self.slide_rect_0) # self.setCacheMode(QGraphicsItem.ItemCoordinateCache, self.rect.size()) # self.setFlag(QGraphicsItem.ItemClipsToShape, True) def pilimage_to_pixmap(self, pilimage): qim = ImageQt(pilimage) pix = QtGui.QPixmap.fromImage(qim) return pix def boundingRect(self): return QRectF(0, 0, self.slide_rect_0.width(), self.slide_rect_0.height()) def paint(self, painter: QtGui.QPainter, option: QStyleOptionGraphicsItem, widget: typing.Optional[QWidget] = ...): # print("pos ", self.pos()) # print("paint ", self.slide_rect_0, option.rect, option.exposedRect) self.pixmap = QPixmapCache.find(self.cache_key) if not self.pixmap: # print("read", self.slide_rect_0) with openslide.open_slide(self.slide_path) as slide: tile_pilimage = slide.read_region( (self.slide_rect_0.x(), self.slide_rect_0.y()), self.level, (self.slide_rect_0.width(), self.slide_rect_0.height())) self.pixmap = self.pilimage_to_pixmap(tile_pilimage) QPixmapCache.insert(self.cache_key, self.pixmap) # painter.drawPixmap(self.slide_rect_0, self.pixmap) painter.drawPixmap(self.boundingRect().toRect(), self.pixmap) def __str__(self) -> str: return "{}: slide_path: {}, slide_rect_0: {}".format( self.__class__.__name__, self.slide_path, self.slide_rect_0) def __repr__(self) -> str: return self.__str__()
def setGeometry(self, rect: QtCore.QRect, maxPriority: int) -> None: min_sizes = [] children = [] stretch_weights = [] if self.__orientation == Qt.Horizontal: total_size = rect.width() else: total_size = rect.height() first = True for child in self.children(): if child.hasContent(maxPriority): if not first: total_size -= self.spacing() first = False child.setVisible(True) if self.__orientation == Qt.Horizontal: child_min_size = child.minimumSize(maxPriority).width() else: child_min_size = child.minimumSize(maxPriority).height() min_sizes.append(child_min_size) children.append(child) stretch_weights.append(child.stretch()) else: child.setVisible(False) sizes = self._computeSizes(total_size, children, min_sizes, stretch_weights) if self.__orientation == Qt.Horizontal: pos = rect.left() else: pos = rect.top() for idx, child in enumerate(children): if idx > 0: pos += self.spacing() size = sizes[idx] if self.__orientation == Qt.Horizontal: child.setGeometry( QtCore.QRect(pos, rect.top(), size, rect.height()), maxPriority) else: child.setGeometry( QtCore.QRect(rect.left(), pos, rect.width(), size), maxPriority) pos += size
def __init__(self, parent=None): """ Constructor @param parent reference to the parent widget @type QWidget """ super(MainWindow, self).__init__(parent) self.setupUi(self) screenSize = QRect() desktop = QDesktopWidget() screenSize = desktop.availableGeometry(self) aps_ratio = 83 / 100 # set prefer ratio if you want height = screenSize.height() * aps_ratio print('height', height, screenSize.width(), screenSize.height()) self.setFixedSize(screenSize.width(), height) self.prevGraphicsView.setFrameShape(QFrame.NoFrame) self.currGraphicsView.setFrameShape(QFrame.NoFrame) self.prevImgPath = '' self.currImgPath = '' self.prevXMLPath = '' self.currXMLPath = '' self.prev_image_list = None self.prevScene = QGraphicsScene() self.prevGraphicsView.setScene(self.prevScene) self.currScene = QGraphicsScene() self.currGraphicsView.setScene(self.currScene) self.num_of_index = ' ' self.curr_image_list = [] self.prev_xml_list = [] self.rubberBand = QRubberBand(QRubberBand.Rectangle, self) self.origin = QPoint() self.prev_count_mouse_click = None self.curr_count_mouse_click = None self.prevGraphicsView.mousePressEvent = self.prev_mouse_press self.prevGraphicsView.mouseMoveEvent = self.prev_mouse_move self.prevGraphicsView.mouseReleaseEvent = self.prev_mouse_release self.currGraphicsView.mousePressEvent = self.curr_mouse_press self.currGraphicsView.mouseMoveEvent = self.curr_mouse_move self.currGraphicsView.mouseReleaseEvent = self.curr_mouse_release self.overlay = Overlay()
def font_charBmp(font, char): metric = QFontMetrics( font ).boundingRect( char ) char_rect = QRect( 0, 0, metric.width(), metric.height() ) chr_img = QImage( char_rect.width()+1, char_rect.height(), QImage.Format_Mono ) chr_img.fill(0) # set img painter and draw char to bmp painter = QPainter( chr_img ) painter.setPen( QPen(Qt.white) ) painter.setFont( font ) painter.drawText( char_rect, Qt.AlignJustify, char ) painter.end() del(painter) # crop left / right x0 = 0 x1 = char_rect.width() while x0 < x1 - 1: data_col = 0 for col in range( char_rect.height() ): data_col += chr_img.pixel(x0, col) & 0x00FFFFFF if not data_col: x0 += 1 else: break char_rect.setX(x0) while x1 > x0 + 1: x1 -= 1 data_col = 0 for col in range( char_rect.height() ): data_col += chr_img.pixel(x1, col) & 0x00FFFFFF if not data_col: char_rect.setWidth(x1 - x0) else: break # crop bottom y1 = char_rect.height() while y1 > 1: y1 -= 1 data_row = 0 for row in range( char_rect.width() ): data_row += chr_img.pixel(row, y1) & 0x00FFFFFF if not data_row: char_rect.setHeight(y1) else: break chr_img = chr_img.copy( char_rect ) # chr_img.save( '.\\img\\0%s.bmp' % char, 'bmp' ) return chr_img
def drawBackground(self, painter, rect): roomRect = QRect(rect.x() + (rect.width() - self.roomWidth) * .5, rect.y() + (rect.height() - self.roomHeight) * .5, self.roomWidth, self.roomHeight) gridWidth = roomRect.width() - (roomRect.width() % self.gridSize) gridHeight = roomRect.height() - (roomRect.height() % self.gridSize) left = int((self.roomWidth - gridWidth + 2 * roomRect.left()) * .5) top = int((self.roomHeight - gridHeight + 2 * roomRect.top()) * .5) painter.setPen(QPen(QColor("black"), 2, Qt.SolidLine)) if self.grid: for x in range(left, int(roomRect.right()), self.gridSize): for y in range(top, int(roomRect.bottom()), self.gridSize): painter.drawPoint(x, y) painter.drawRect(roomRect)
def verticalGradient(painter: QPainter, spanRect: QRect, clipRect: QRect): keyColor = BASE_COLOR key = "verticalGradient %d %d %d %d %d" % (spanRect.width( ), spanRect.height(), clipRect.width(), clipRect.height(), keyColor.rgb()) pixmap = QPixmapCache.find(key) if pixmap is None: pixmap = QPixmap(clipRect.size()) p = QPainter(pixmap) rect = QRect(0, 0, clipRect.width(), clipRect.height()) verticalGradientHelper(p, spanRect, rect) p.end() QPixmapCache.insert(key, pixmap) painter.drawPixmap(clipRect.topLeft(), pixmap)
def mouseReleaseEvent(self, event): self.rubberBand.hide() zoreRect = QRect(self.origin, event.pos()).normalized() pixmapSize = self.pixmap().size() pixX1 = zoreRect.x() pixX2 = zoreRect.x() + zoreRect.width() pixY1 = zoreRect.y() pixY2 = zoreRect.y() + zoreRect.height() width = pixmapSize.width() height = pixmapSize.height() x1 = pixX1/width x1 = x1 if x1 >= 0.0 else 0.0 x1 = x1 if x1 <= 1.0 else 1.0 y1 = pixY1/height y1 = y1 if y1 >= 0.0 else 0.0 y1 = y1 if y1 <= 1.0 else 1.0 x2 = pixX2/width x2 = x2 if x2 >= 0.0 else 0.0 x2 = x2 if x2 <= 1.0 else 1.0 y2 = pixY2/height y2 = y2 if y2 >= 0.0 else 0.0 y2 = y2 if y2 <= 1.0 else 1.0 rect = QRect(min(pixX1, pixX2), min(pixY1, pixY2), abs(pixX1- pixX2), abs(pixY1- pixY2)) selectedImg = self.pixmap().copy(rect) self.areaSelected.emit(min(x1, x2), min(y1, y2), np.abs(x1-x2), np.abs(y1-y2), selectedImg)
def calculate_relative_position(parent_rect: QtCore.QRect, own_size: QtCore.QSize, constraint: RelativeLayoutConstraint): """ Calculates the position of the element, given its size, the position and size of the parent and a relative layout constraint. The position is the position of the parent plus the weighted size of the parent, the weighted size of the element and an offset. The weights and the offset are given by the constraint for each direction. :param parent_rect: parent coordinates and size as rectangle :param own_size: size of the element (width and height) :param constraint: relative layout constraint to apply :return: tuple of recommended x and y positions of the element """ """ Returns the left, upper corner of an object if the parent rectangle (QRect) is given and our own size (QSize) and a relative layout constraint (see RelativeLayoutConstraint). """ x = (parent_rect.x() + constraint.x[0] * parent_rect.width() + constraint.x[1] * own_size.width() + constraint.x[2]) y = (parent_rect.y() + constraint.y[0] * parent_rect.height() + constraint.y[1] * own_size.height() + constraint.y[2]) return x, y
def capturedArea(self): captured = QRect(self.mCaptureStart, self.tilePosition()).normalized() if (captured.width() == 0): captured.adjust(-1, 0, 1, 0) if (captured.height() == 0): captured.adjust(0, -1, 0, 1) return captured
def paint(self, painter, option, widget=None): rect = QRect(self.border, self.attrH - self.radius, self.attrW - self.border, self.attrH) self.AttrsData[self] = self.attrData name = self.key self._attrBrush.setColor(QColor(247, 151, 47, 255)) if self.alternate: self._attrBrushAlt.setColor( convert_to_QColor([19, 17, 15, 255], True, 20)) self._attrPen.setColor(QColor(16, 102, 162, 255)) painter.setPen(self._attrPen) painter.setBrush(self._attrBrush) painter.drawRect(rect) painter.setPen(QColor(69, 149, 62, 255)) painter.setFont(self._attrTextFont) if self.node.drawingConnection: if self.knobL.slotType == 'slot' and self.knobR.slotType == 'slot': painter.setPen(QColor(100, 100, 100, 255)) textRect = QRect(rect.left() + self.radius, rect.top(), rect.width() - 2 * self.radius, rect.height()) painter.drawText(textRect, center, name)
def playVideo(self): """ 播放视频 :return: """ if self.playCapture.isOpened(): ret, frame = self.playCapture.read() if ret: # self.treeview_videofiledir.setDisabled(True) # 获取视频播放label的大小 s = self.picturelabel.rect() # frame重置大小 R_frame = cv2.resize(frame, (QRect.width(s), QRect.height(s))) if R_frame.ndim == 3: R_frame_RGB = cv2.cvtColor(R_frame, cv2.COLOR_BGR2RGB) elif R_frame.ndim == 2: R_frame_RGB = cv2.cvtColor(R_frame, cv2.COLOR_GRAY2BGR) qImage = QtGui.QImage(R_frame_RGB[:], R_frame_RGB.shape[1], R_frame_RGB.shape[0], QtGui.QImage.Format_RGB888) pixmap = QPixmap.fromImage(qImage) self.picturelabel.setPixmap(pixmap) else: # 释放VideoCapture self.playCapture.release() # 关闭线程 self.timer.stop()
def onRectSet(self, r: QRect): """Rectangle has been selected""" self.selected_rectangle = r if self.graphicsView.hasImage() and r.width() > 1 and r.height() > 1: self.confirm_area_action.setDisabled(False) else: self.confirm_area_action.setDisabled(True)
def request(self, qrect, along_through=None): assert isinstance(qrect, QRect) # Widen request with a 1-pixel halo, to make sure edges on the tile borders are shown. qrect = QRect(qrect.x(), qrect.y(), qrect.width() + 1, qrect.height() + 1) s = rect2slicing(qrect) arrayreq = self._arraySource2D.request(s, along_through) return SegmentationEdgesItemRequest(arrayreq, self._layer, qrect, self._is_clickable)
def is_inside_limit(limit: QRect, pos: QPoint): if pos.x() < limit.x() or pos.x() > limit.width(): return False elif pos.y() < limit.y() or pos.y() > limit.height(): return False return True
def paintEvent(self, event): content_rect = self.contentsRect() if self.spinner_size: size = min(self.spinner_size, content_rect.width(), content_rect.height()) else: size = min(content_rect.width(), content_rect.height()) dot_count = 5 dot_size = int(size / dot_count) * 1.5 spinner_rect = QRect(content_rect.left(), content_rect.top(), size, size) painter = QPainter(self) painter.setClipRect(content_rect) if self.timer_id: diff_height = content_rect.height() - size offs_y = 0 if diff_height > 0: if self.vertical_align == Qt.AlignVCenter: offs_y = diff_height / 2 elif self.vertical_align == Qt.AlignBottom: offs_y = diff_height x_center = spinner_rect.left( ) + spinner_rect.width() / 2 - dot_size / 2 y_center = spinner_rect.top( ) + offs_y + spinner_rect.height() / 2 - dot_size / 2 painter.save() for i in range(dot_count): if self.counter % dot_count == i: painter.setBrush(QBrush(QColor(0, 0, 0))) d_size = dot_size * 1.1 else: painter.setBrush(QBrush(QColor(200, 200, 200))) d_size = dot_size r = size / 2 - dot_size / 2 x = r * math.cos(2 * math.pi * i / dot_count) y = r * math.sin(2 * math.pi * i / dot_count) painter.drawEllipse(x_center + x, y_center + y, d_size, d_size) painter.restore() if self.message: # painter.setPen(QPen(Qt.black)) if self.font_size: f = painter.font() f.setPointSize(self.font_size) painter.setFont(f) text_rect = QRect(content_rect) text_rect.translate( spinner_rect.width() + SpinnerWidget.SPINNER_TO_TEXT_DISTANCE if self.timer_id else 0, 0) painter.drawText(text_rect, Qt.AlignLeft | Qt.AlignVCenter, self.message) painter.end()
def paint(self, painter, rect): update_rect = rect & self.rect() if not update_rect: return image_rect = QRect(update_rect.topLeft() - self.rect().topLeft(), update_rect.size()) image = cache.image(self) self._waiting = not image if image: painter.drawImage(update_rect, image, image_rect) else: # schedule an image to be generated, if done our update() method is called cache.generate(self) # find suitable image to be scaled from other size image = cache.image(self, False) if image: hscale = float(image.width()) / self.width() vscale = float(image.height()) / self.height() image_rect = QRectF(image_rect.x() * hscale, image_rect.y() * vscale, image_rect.width() * hscale, image_rect.height() * vscale) painter.drawImage(QRectF(update_rect), image, image_rect) else: # draw blank paper, using the background color of the cache rendering (if set) # or from the document itself. color = (cache.options(self.document()).paperColor() or cache.options().paperColor() or self.document().paperColor()) painter.fillRect(update_rect, color)
def paint(self, painter, option, index): """Performs custom painting of value of data in the model and decorations. Performs custom painting of value of data in the model at the specified index plus any decorations used in that column. Args: painter - QPainter option - QStyleOptionViewItem index - QModelIndex """ xOffset = 0 # First added for #15, the painting of custom amount information. This can # be used as a pattern for painting any column of information. value_painter = self._get_value_painter(index) self._display_text = value_painter is None QStyledItemDelegate.paint(self, painter, option, index) if value_painter is not None: value_option = QStyleOptionViewItem(option) rect = value_option.rect rect = QRect(rect.left(), rect.top(), rect.width() - xOffset, rect.height()) value_option.rect = rect value_painter.paint(painter, value_option, index) decorations = self._get_decorations(index, bool(option.state & QStyle.State_Selected)) for dec in decorations: pixmap = dec.pixmap x = option.rect.right() - pixmap.width() - xOffset y = option.rect.center().y() - (pixmap.height() // 2) rect = QRect(x, y, pixmap.width(), pixmap.height()) painter.drawPixmap(rect, pixmap) xOffset += pixmap.width()
def _draw_pixmap(self, painter, option, item): """Draw the actual pixmap of the thumbnail. This calculates the size of the pixmap, applies padding and appropriately centers the image. Args: painter: The QPainter. option: The QStyleOptionViewItem. item: The ThumbnailItem. """ painter.save() # Original thumbnail pixmap pixmap = item.icon().pixmap(256) # Rectangle that can be filled by the pixmap rect = QRect( option.rect.x() + self.padding, option.rect.y() + self.padding, option.rect.width() - 2 * self.padding, option.rect.height() - 2 * self.padding, ) # Size the pixmap should take size = pixmap.size().scaled(rect.size(), Qt.KeepAspectRatio) # Coordinates to center the pixmap diff_x = (rect.width() - size.width()) / 2.0 diff_y = (rect.height() - size.height()) / 2.0 x = int(option.rect.x() + self.padding + diff_x) y = int(option.rect.y() + self.padding + diff_y) # Draw painter.drawPixmap(x, y, size.width(), size.height(), pixmap) painter.restore() if item.marked: self._draw_mark(painter, option, x + size.width(), y + size.height())
def __init__(self, parent: QWidget, rect: QRect = None): super().__init__(parent) if rect is None: x = parent.x() + parent.width() / 2 - 150 y = parent.y() + parent.height() / 2 - 50 window_rect = QRect(x, y, 300, 100) else: x = rect.x() + rect.width() / 2 - 150 y = rect.y() + rect.height() / 2 - 100 window_rect = QRect(x, y, 300, 100) self.setGeometry(window_rect) self.action = QHBoxLayout() self.action.addStretch() self.action.addStretch() self.text = QLabel() self.text.setWordWrap(True) self.text.setStyleSheet("color:#666666;") layout = QVBoxLayout() layout.setSpacing(18) layout.addWidget(self.text, alignment=Qt.AlignTop) layout.addLayout(self.action) layout.setContentsMargins(12, 12, 12, 0) widget = QWidget() widget.setLayout(layout) widget.setContentsMargins(0, 8, 0, 0) widget.setObjectName(Parapluie.Object_Raised_Off) self.setCentralWidget(widget)
def __init__(self, pos: QRect, inputSlot, editor=None, scene=None, parent=None, qcolor=QColor(0, 0, 255)): """ Couples the functionality of the lazyflow operator OpSubRegion which gets a subregion of interest and the functionality of the resizable rectangle Item. Keeps the two functionality separated :param pos: initial position :param inputSlot: Should be the output slot of another operator from which you would like monitor a subregion :param scene: the scene where to put the graphics item :param parent: the parent object if any :param qcolor: initial color of the rectangle """ assert inputSlot.meta.getTaggedShape()["c"] == 1 assert parent is None, "FIXME: QT structure does not seem to be implemented thoroughly. parent is always None!" self._rectItem = QGraphicsResizableRect(pos.x(), pos.y(), pos.height(), pos.width(), scene, parent, editor) # self._rectItem.color=qcolor # FIXME: color can't be set # sub region corresponding to the rectangle region self._opsub = OpSubRegion(graph=inputSlot.operator.graph, parent=inputSlot.operator.parent) self._inputSlot = inputSlot # input slot which connect to the sub array self.boxLabel = None # a reference to the label in the labellist model self._initConnect()
def _draw_volume(self, painter, within_bounds=True, clip_screen=True): if within_bounds: (bounds, render_uv) = self._bounds2uv(self._screen_bounds_display, self._scale_display, clip_screen=clip_screen) draw_rect = QRect(QPoint(*bounds[0]), QPoint(*bounds[1])) else: render_uv = [(-1, 1), (1, -1)] draw_rect = self.rect() self._resolution_display = [ draw_rect.width() // self._scale_display, draw_rect.height() // self._scale_display ] job = RenderJob(modelview=self._view * self._model * self._state.aspect_xform, projection=self._projection, resolution=self._resolution_display, uv=render_uv, rect=draw_rect) self._state.display_volume.render(job) render = self._state.display_volume.last_render if render is not None and render.image is not None: self._render_times.append(render.duration) painter.drawImage( render.rect, QImage(render.image, render.image.shape[1], render.image.shape[0], QImage.Format_RGBA8888))
def paintEvent(self, e): ''' 重写绘图事件 ''' painter = QPainter(self) if self.display_image is None: painter.setPen(Qt.black) painter.setFont(QFont("Arial", 30)) painter.drawText(e.rect(), Qt.AlignCenter, "Identity Finder") else: window_rect = self.rect() image_rect = QRect(0, 0, self.display_width, self.display_height) ratio_of_image = image_rect.width() / image_rect.height() ratio_of_window = window_rect.width() / window_rect.height() if ratio_of_image > ratio_of_window: window_rect.setHeight(window_rect.width()/ratio_of_image) delta = (self.rect().height()-window_rect.height())*0.5 window_rect.setY(delta) window_rect.setHeight(window_rect.height()+delta) else: window_rect.setWidth(window_rect.height()*ratio_of_image) delta = (self.rect().width()-window_rect.width())*0.5 window_rect.setX(delta) window_rect.setWidth(window_rect.width()+delta) painter.drawImage(window_rect, self.display_image, image_rect) return
def subControlRect(self, control, option, subControl, widget=None): rect = super(SliderStyle, self).subControlRect( control, option, subControl, widget) if subControl == QStyle.SC_SliderHandle: if option.orientation == Qt.Horizontal: # 高度1/3 radius = int(widget.height() / 3) offset = int(radius / 3) if option.state & QStyle.State_MouseOver: x = min(rect.x() - offset, widget.width() - radius) x = x if x >= 0 else 0 else: radius = offset x = min(rect.x(), widget.width() - radius) rect = QRect(x, int((rect.height() - radius) / 2), radius, radius) else: # 宽度1/3 radius = int(widget.width() / 3) offset = int(radius / 3) if option.state & QStyle.State_MouseOver: y = min(rect.y() - offset, widget.height() - radius) y = y if y >= 0 else 0 else: radius = offset y = min(rect.y(), widget.height() - radius) rect = QRect(int((rect.width() - radius) / 2), y, radius, radius) return rect return rect
def __updateHandles(self): """ Private method to update the handles. """ r = QRect(self.__selection) s2 = self.__handleSize // 2 self.__TLHandle.moveTopLeft(r.topLeft()) self.__TRHandle.moveTopRight(r.topRight()) self.__BLHandle.moveBottomLeft(r.bottomLeft()) self.__BRHandle.moveBottomRight(r.bottomRight()) self.__LHandle.moveTopLeft(QPoint(r.x(), r.y() + r.height() // 2 - s2)) self.__THandle.moveTopLeft(QPoint(r.x() + r.width() // 2 - s2, r.y())) self.__RHandle.moveTopRight( QPoint(r.right(), r.y() + r.height() // 2 - s2)) self.__BHandle.moveBottomLeft( QPoint(r.x() + r.width() // 2 - s2, r.bottom()))
def apply_selection(self, rect: QRect): self.selection_active = False self.group.set_position(rect.x(), rect.y()) self.group.set_size(rect.width(), rect.height()) if self.rect_was_displayed and self.visibility_checkbox.isChecked(): self.__add_roi_rect() self.rect_was_displayed = False
def __normalizeSelection(self, sel): """ Private method to normalize the given selection. @param sel selection to be normalized (QRect) @return normalized selection (QRect) """ rect = QRect(sel) if rect.width() <= 0: left = rect.left() width = rect.width() rect.setLeft(left + width - 1) rect.setRight(left) if rect.height() <= 0: top = rect.top() height = rect.height() rect.setTop(top + height - 1) rect.setBottom(top) return rect
def _paint_icon(self, iconic, painter, rect, mode, state, options): """Paint a single icon.""" painter.save() color = options['color'] char = options['char'] color_options = { QIcon.On: { QIcon.Normal: (options['color_on'], options['on']), QIcon.Disabled: (options['color_on_disabled'], options['on_disabled']), QIcon.Active: (options['color_on_active'], options['on_active']), QIcon.Selected: (options['color_on_selected'], options['on_selected']) }, QIcon.Off: { QIcon.Normal: (options['color_off'], options['off']), QIcon.Disabled: (options['color_off_disabled'], options['off_disabled']), QIcon.Active: (options['color_off_active'], options['off_active']), QIcon.Selected: (options['color_off_selected'], options['off_selected']) } } color, char = color_options[state][mode] painter.setPen(QColor(color)) # A 16 pixel-high icon yields a font size of 14, which is pixel perfect # for font-awesome. 16 * 0.875 = 14 # The reason why the glyph size is smaller than the icon size is to # account for font bearing. draw_size = 0.875 * qRound(rect.height() * options['scale_factor']) prefix = options['prefix'] # Animation setup hook animation = options.get('animation') if animation is not None: animation.setup(self, painter, rect) painter.setFont(iconic.font(prefix, draw_size)) if 'offset' in options: rect = QRect(rect) rect.translate(options['offset'][0] * rect.width(), options['offset'][1] * rect.height()) painter.setOpacity(options.get('opacity', 1.0)) painter.drawText(rect, Qt.AlignCenter | Qt.AlignVCenter, char) painter.restore()
def scene_item_rects_updated(self, items): """The user moved or resized items in the scene """ debug_print('GraphicsItemView.item_rects_updated') for index, item in zip(self.indexes_of_items(items), items): # item.sceneBoundingRect() is the items rects in the correct # coordinates system debug_print('Row [{0}] updated'.format(index.row())) rect = item.sceneBoundingRect() # Cumbersome conversion to ints rect = QRect(rect.left(), rect.top(), rect.width(), rect.height()) self.model().setData(index, rect, RectRole)
def set_rect(self, new_rect): """Sets a new QRect in integer coordinates """ # Cumbersome conversion to ints current = self.sceneBoundingRect() current = QRect(current.left(), current.top(), current.width(), current.height()) if current != new_rect: msg = 'Update rect for [{0}] from [{1}] to [{2}]' debug_print(msg.format(self, current, new_rect)) self.prepareGeometryChange() # setrect() expects floating point rect self.setRect(QRectF(new_rect))
def drawBox(self, painter): """ Paint box """ color = self._color rect = QRect(0, 0, self._box_sqsize, self._box_sqsize) for sz in range(3): painter.setPen(color.lighter(200 // (sz+1))) painter.drawRect( rect.x()+sz, rect.y()+sz, rect.width()-(sz*2), rect.height()-(sz*2) ) painter.fillRect(rect.x()+3, rect.y()+3, rect.width()-6, rect.height()-6, color.lighter(200)) if self._checked: painter.setPen(QColor(0, 0, 0)) painter.drawLine( rect.x()+2, rect.y() + (rect.height()//2), rect.width()//2, rect.y() + (rect.height()-2)) painter.drawLine( rect.width()//2, rect.y() + (rect.height()-2), rect.width()-1, rect.y() + 2)
def _handleMousePressEvent(self, event, superMethod): callsuper = True pos = event.pos() index = self.indexAt(pos) if index.isValid(): selected = index in self.selectedIndexes() rect = self.visualRect(index) relativePos = QPoint(pos.x()-rect.x(), pos.y()-rect.y()) delegate = self.itemDelegate(index) if hasattr(delegate, 'handleClick'): rect = QRect(0, 0, rect.width(), rect.height()) if delegate.handleClick(index, relativePos, rect, selected): callsuper = False if callsuper: superMethod(self, event)
class SliceWidget(QGraphicsItem): @inject.params(gs=GState) def __init__(self, slicee, r=None, R=None, gs=None, parent=None): logger.info('%s init', self.__class__.__qualname__) self._engine = SliceLayoutEngine(r=r, R=R, spacing=4) super().__init__(parent) self.sty = gs.sty['Bud'] self.cfg = gs.cfg self.text = gs.bud['slices'][0].get('name') self._text_rf = QRect(0, 0, 16, 16) self.font = QFont(str(self.sty['Text']['font']), self._text_rf.height()) self.build(slicee) def build(self, slicee): # BUG: engine don't set boundings rings = map(lambda rg: RingWidget(rg, parent=self), slicee['rings']) self._engine.insert(rings) def boundingRect(self): R = self._engine.R return QRectF(-R, -R, 2*R, 2*R) def paint(self, p, option, wdg): if __debug__ and piony.G_DEBUG_VISUALS: self._drawDbg(p) if self.text: self.drawText(p) # if self._ring: # self._ring.paint(p, option, wdg) for item in self._engine.items: item.paint(p, option, wdg) def drawText(self, p): sz = self._text_rf.size() * float(self.sty['Text']['scale']) base.adjustFontSize(p, self.text, sz) p.setPen(QColor(*list(self.sty['Text']['color']))) if __debug__ and piony.G_DEBUG_VISUALS: p.drawRect(self._text_rf) if self.text and self._text_rf: p.drawText(self._text_rf, Qt.AlignCenter, self.text) # <Dbg> -------------------- if __debug__: def _drawDbg(self, p): p.setPen(Qt.NoPen) p.setBrush(QColor(0, 255, 255, 50)) p.drawRect(self.boundingRect())
def resizeEvent(self, evt): """ Protected method to handle resize events. @param evt resize event (QResizeEvent) """ if self.__selection.isNull(): return r = QRect(self.__selection) r.setTopLeft(self.__limitPointToRect(r.topLeft(), self.rect())) r.setBottomRight(self.__limitPointToRect(r.bottomRight(), self.rect())) if r.width() <= 1 or r.height() <= 1: # This just results in ugly drawing... self.__selection = QRect() else: self.__selection = self.__normalizeSelection(r)
def __mouseMove(self, event): if self._leftButtonPressed: if self._mousePress != Edge.NoEdge: left = self._rubberBand.frameGeometry().left() top = self._rubberBand.frameGeometry().top() right = self._rubberBand.frameGeometry().right() bottom = self._rubberBand.frameGeometry().bottom() if self._mousePress == Edge.Top: top = event.globalPos().y() else: if self._mousePress == Edge.Bottom: bottom = event.globalPos().y() else: if self._mousePress == Edge.Right: right = event.globalPos().x() else: if self._mousePress == Edge.Left: left = event.globalPos().x() else: if self._mousePress == Edge.TopRight: top = event.globalPos().y() right = event.globalPos().x() else: if self._mousePress == Edge.TopLeft: top = event.globalPos().y() left = event.globalPos().x() else: if self._mousePress == Edge.BottomLeft: bottom = event.globalPos().y() left = event.globalPos().x() else: if self._mousePress == Edge.BottomRight: bottom = event.globalPos().y() right = event.globalPos().x() newRect = QRect(QPoint(left, top), QPoint(right, bottom)) if newRect.width() < self._parent.minimumWidth(): left = self.frameGeometry().x() else: if newRect.height() < self._parent.minimumHeight(): top = self.frameGeometry().y() self._parent.setGeometry(QRect(QPoint(left, top), QPoint(right, bottom))) self._rubberBand.setGeometry(QRect(QPoint(left, top), QPoint(right, bottom))) else: self.__updateCursorShape(event.globalPos())
def scene_box_added(self, rect): """The user added a box """ m = self.model() row = len(self._rows) if not m.insertRow(row): raise InselectError('Could not insert row') else: # Cumbersome conversion to ints rect = QRect(rect.left(), rect.top(), rect.width(), rect.height()) if not m.setData(m.index(row, 0), rect, RectRole): raise InselectError('Could not set rect') else: # Select the new box self.scene.clearSelection() item = next(self.items_of_rows([row])) item.setSelected(True) item.update()
def _calculate_image_parameters(self, web_viewport, img_width, img_height): """ :return: (image_viewport, image_size) """ if img_width is None: img_width = web_viewport.width() ratio = 1.0 else: if img_width == 0 or web_viewport.width() == 0: ratio = 1.0 else: ratio = img_width / float(web_viewport.width()) image_viewport = QRect( QPoint(0, 0), QSize(img_width, round(web_viewport.height() * ratio))) if img_height is None: img_height = image_viewport.height() image_size = QSize(img_width, img_height) return image_viewport, image_size
def _rect_on_view_python(self, elem_geometry): """Python implementation for rect_on_view.""" if elem_geometry is None: geometry = self._elem.geometry() else: geometry = elem_geometry frame = self._elem.webFrame() rect = QRect(geometry) while frame is not None: rect.translate(frame.geometry().topLeft()) rect.translate(frame.scrollPosition() * -1) frame = frame.parentFrame() # We deliberately always adjust the zoom here, even with # adjust_zoom=False if elem_geometry is None: zoom = self._elem.webFrame().zoomFactor() if not config.get('ui', 'zoom-text-only'): rect.moveTo(rect.left() / zoom, rect.top() / zoom) rect.setWidth(rect.width() / zoom) rect.setHeight(rect.height() / zoom) return rect
def _paint_icon(self, iconic, painter, rect, mode, state, options): """Paint a single icon""" painter.save() color, char = options['color'], options['char'] if mode == QIcon.Disabled: color = options.get('color_disabled', color) char = options.get('disabled', char) elif mode == QIcon.Active: color = options.get('color_active', color) char = options.get('active', char) elif mode == QIcon.Selected: color = options.get('color_selected', color) char = options.get('selected', char) painter.setPen(QColor(color)) # A 16 pixel-high icon yields a font size of 14, which is pixel perfect # for font-awesome. 16 * 0.875 = 14 # The reason for not using full-sized glyphs is the negative bearing of # fonts. draw_size = 0.875 * qRound(rect.height() * options['scale_factor']) prefix = options['prefix'] # Animation setup hook animation = options.get('animation') if animation is not None: animation.setup(self, painter, rect) painter.setFont(iconic.font(prefix, draw_size)) if 'offset' in options: rect = QRect(rect) rect.translate(options['offset'][0] * rect.width(), options['offset'][1] * rect.height()) painter.setOpacity(options.get('opacity', 1.0)) painter.drawText(rect, Qt.AlignCenter | Qt.AlignVCenter, char) painter.restore()
def paintEvent(self, ev): """Called when paint is needed, finds out which page to magnify.""" layout = self.parent().surface().pageLayout() pos = self.geometry().center() - self.parent().surface().pos() page = layout.pageAt(pos) if not page: return pagePos = pos - page.pos() max_zoom = self.parent().surface().view().MAX_ZOOM * self.MAX_EXTRA_ZOOM newPage = Page(page, min(max_zoom, self._scale * page.scale())) if not newPage.same_page(self._page): if self._page: self._page.magnifier = None self._page = newPage self._page.magnifier = self relx = pagePos.x() / float(page.width()) rely = pagePos.y() / float(page.height()) image = cache.image(self._page) img_rect = QRect(self.rect()) img_rect.setSize( img_rect.size()*self._page._retinaFactor ); if not image: cache.generate(self._page) image = cache.image(self._page, False) if image: img_rect.setWidth(img_rect.width() * image.width() / self._page.physWidth()) img_rect.setHeight(img_rect.height() * image.height() / self._page.physHeight()) if image: img_rect.moveCenter(QPoint(relx * image.width(), rely * image.height())) p = QPainter(self) p.drawImage(self.rect(), image, img_rect) p.setRenderHint(QPainter.Antialiasing, True) p.setPen(QPen(QColor(192, 192, 192, 128), 6)) p.drawEllipse(self.rect().adjusted(2, 2, -2, -2))
class Page(object): """Represents a page from a Poppler.Document. It maintains its own size and can draw itself using the cache. It also can maintain a list of links and return links at certain points or rectangles. The visible attribute (setVisible and visible) defaults to True but can be set to False to hide the page from a Surface (this is done by the Layout). """ def __init__(self, document, pageNumber): self._document = document self._pageNumber = pageNumber self._pageSize = document.page(pageNumber).pageSize() self._rotation = popplerqt5.Poppler.Page.Rotate0 self._rect = QRect() self._scale = 1.0 self._visible = True self._layout = lambda: None self._waiting = True # whether image still needs to be generated def document(self): """Returns the document.""" return self._document def pageNumber(self): """Returns the page number.""" return self._pageNumber def pageSize(self): """The page size in points (1/72 inch), taking rotation into account.""" return self._pageSize def layout(self): """Returns the Layout if we are part of one.""" return self._layout() def visible(self): """Returns True if this page is visible (will be displayed).""" return self._visible def setVisible(self, visible): """Sets whether this page is visible (will be displayed).""" self._visible = visible def rect(self): """Returns our QRect(), with position and size.""" return self._rect def size(self): """Returns our size.""" return self._rect.size() def height(self): """Returns our height.""" return self._rect.height() def width(self): """Returns our width.""" return self._rect.width() def pos(self): """Returns our position.""" return self._rect.topLeft() def setPos(self, point): """Sets our position (affects the Layout).""" self._rect.moveTopLeft(point) def setRotation(self, rotation): """Sets our Poppler.Page.Rotation.""" old, self._rotation = self._rotation, rotation if (old ^ rotation) & 1: self._pageSize.transpose() self.computeSize() def rotation(self): """Returns our rotation.""" return self._rotation def computeSize(self): """Recomputes our size.""" xdpi, ydpi = self.layout().dpi() if self.layout() else (72.0, 72.0) x = round(self._pageSize.width() * xdpi / 72.0 * self._scale) y = round(self._pageSize.height() * ydpi / 72.0 * self._scale) self._rect.setSize(QSize(x, y)) def setScale(self, scale): """Changes the display scale.""" self._scale = scale self.computeSize() def scale(self): """Returns our display scale.""" return self._scale def scaleForWidth(self, width): """Returns the scale we need to display ourselves at the given width.""" if self.layout(): return width * 72.0 / self.layout().dpi()[0] / self._pageSize.width() else: return float(width) / self._pageSize.width() def scaleForHeight(self, height): """Returns the scale we need to display ourselves at the given height.""" if self.layout(): return height * 72.0 / self.layout().dpi()[1] / self._pageSize.height() else: return float(height) / self._pageSize.height() def setWidth(self, width): """Change our scale to force our width to the given value.""" self.setScale(self.scaleForWidth(width)) def setHeight(self, height): """Change our scale to force our height to the given value.""" self.setScale(self.scaleForHeight(height)) def paint(self, painter, rect): update_rect = rect & self.rect() if not update_rect: return image_rect = QRect(update_rect.topLeft() - self.rect().topLeft(), update_rect.size()) image = cache.image(self) self._waiting = not image if image: painter.drawImage(update_rect, image, image_rect) else: # schedule an image to be generated, if done our update() method is called cache.generate(self) # find suitable image to be scaled from other size image = cache.image(self, False) if image: hscale = float(image.width()) / self.width() vscale = float(image.height()) / self.height() image_rect = QRectF(image_rect.x() * hscale, image_rect.y() * vscale, image_rect.width() * hscale, image_rect.height() * vscale) painter.drawImage(QRectF(update_rect), image, image_rect) else: # draw blank paper, using the background color of the cache rendering (if set) # or from the document itself. color = (cache.options(self.document()).paperColor() or cache.options().paperColor() or self.document().paperColor()) painter.fillRect(update_rect, color) def update(self): """Called when an image is drawn.""" # only redraw when we were waiting for a correctly sized image. if self._waiting and self.layout(): self.layout().updatePage(self) def repaint(self): """Call this to force a repaint (e.g. when the rendering options are changed).""" self._waiting = True cache.generate(self) def image(self, rect, xdpi=72.0, ydpi=None, options=None): """Returns a QImage of the specified rectangle (relative to our top-left position). xdpi defaults to 72.0 and ydpi defaults to xdpi. options may be a render.RenderOptions instance that will set some document rendering options just before rendering the image. """ if ydpi is None: ydpi = xdpi hscale = (xdpi * self.pageSize().width()) / (72.0 * self.width()) vscale = (ydpi * self.pageSize().height()) / (72.0 * self.height()) x = rect.x() * hscale y = rect.y() * vscale w = rect.width() * hscale h = rect.height() * vscale with lock(self.document()): options and options.write(self.document()) page = self.document().page(self._pageNumber) image = page.renderToImage(xdpi, ydpi, x, y, w, h, self._rotation) image.setDotsPerMeterX(int(xdpi * 39.37)) image.setDotsPerMeterY(int(ydpi * 39.37)) return image def linksAt(self, point): """Returns a list() of zero or more links touched by point (relative to surface). The list is sorted with the smallest rectangle first. """ # Poppler.Link objects have their linkArea() ranging in width and height # from 0.0 to 1.0, so divide by resp. height and width of the Page. point = point - self.pos() x = float(point.x()) / self.width() y = float(point.y()) / self.height() # rotate if self._rotation: if self._rotation == popplerqt5.Poppler.Page.Rotate90: x, y = y, 1-x elif self._rotation == popplerqt5.Poppler.Page.Rotate180: x, y = 1-x, 1-y else: # 270 x, y = 1-y, x return list(sorted(cache.links(self).at(x, y), key=lambda link: link.linkArea().width())) def linksIn(self, rect): """Returns an unordered set() of links enclosed in rectangle (relative to surface).""" rect = rect.normalized() rect.translate(-self.pos()) left = float(rect.left()) / self.width() top = float(rect.top()) / self.height() right = float(rect.right()) / self.width() bottom = float(rect.bottom()) / self.height() # rotate if self._rotation: if self._rotation == popplerqt5.Poppler.Page.Rotate90: left, top, right, bottom = top, 1-right, bottom, 1-left elif self._rotation == popplerqt5.Poppler.Page.Rotate180: left, top, right, bottom = 1-right, 1-bottom, 1-left, 1-top else: # 270 left, top, right, bottom = 1-bottom, left, 1-top, right return cache.links(self).inside(left, top, right, bottom) def linkRect(self, linkarea): """Returns a QRect encompassing the linkArea (of a link) in coordinates of our rect().""" left, top, right, bottom = linkarea.normalized().getCoords() # rotate if self._rotation: if self._rotation == popplerqt5.Poppler.Page.Rotate90: left, top, right, bottom = 1-bottom, left, 1-top, right elif self._rotation == popplerqt5.Poppler.Page.Rotate180: left, top, right, bottom = 1-right, 1-bottom, 1-left, 1-top else: # 270 left, top, right, bottom = top, 1-right, bottom, 1-left rect = QRect() rect.setCoords(left * self.width(), top * self.height(), right * self.width(), bottom * self.height()) rect.translate(self.pos()) return rect def text(self, rect): """Returns text inside rectangle (relative to surface).""" rect = rect.normalized() rect.translate(-self.pos()) w, h = self.pageSize().width(), self.pageSize().height() left = float(rect.left()) / self.width() * w top = float(rect.top()) / self.height() * h right = float(rect.right()) / self.width() * w bottom = float(rect.bottom()) / self.height() * h if self._rotation: if self._rotation == popplerqt5.Poppler.Page.Rotate90: left, top, right, bottom = top, w-right, bottom, w-left elif self._rotation == popplerqt5.Poppler.Page.Rotate180: left, top, right, bottom = w-right, h-bottom, w-left, h-top else: # 270 left, top, right, bottom = h-bottom, left, h-top, right rect = QRectF() rect.setCoords(left, top, right, bottom) with lock(self.document()): page = self.document().page(self._pageNumber) return page.text(rect) def searchRect(self, rectF): """Returns a QRect encompassing the given rect (in points) to our position, size and rotation.""" rect = rectF.normalized() left, top, right, bottom = rect.getCoords() w, h = self.pageSize().width(), self.pageSize().height() hscale = self.width() / float(w) vscale = self.height() / float(h) if self._rotation: if self._rotation == popplerqt5.Poppler.Page.Rotate90: left, top, right, bottom = w-bottom, left, w-top, right elif self._rotation == popplerqt5.Poppler.Page.Rotate180: left, top, right, bottom = w-right, h-bottom, w-left, h-top else: # 270 left, top, right, bottom = top, h-right, bottom, h-left rect = QRect() rect.setCoords(left * hscale, top * vscale, right * hscale, bottom * vscale) return rect
class SnapshotRegionGrabber(QWidget): """ Class implementing a grabber widget for a rectangular snapshot region. @signal grabbed(QPixmap) emitted after the region was grabbed """ grabbed = pyqtSignal(QPixmap) StrokeMask = 0 FillMask = 1 Rectangle = 0 Ellipse = 1 def __init__(self, mode=Rectangle): """ Constructor @param mode region grabber mode (SnapshotRegionGrabber.Rectangle or SnapshotRegionGrabber.Ellipse) """ super(SnapshotRegionGrabber, self).__init__( None, Qt.X11BypassWindowManagerHint | Qt.WindowStaysOnTopHint | Qt.FramelessWindowHint | Qt.Tool) assert mode in [SnapshotRegionGrabber.Rectangle, SnapshotRegionGrabber.Ellipse] self.__mode = mode self.__selection = QRect() self.__mouseDown = False self.__newSelection = False self.__handleSize = 10 self.__mouseOverHandle = None self.__showHelp = True self.__grabbing = False self.__dragStartPoint = QPoint() self.__selectionBeforeDrag = QRect() # naming conventions for handles # T top, B bottom, R Right, L left # 2 letters: a corner # 1 letter: the handle on the middle of the corresponding side self.__TLHandle = QRect(0, 0, self.__handleSize, self.__handleSize) self.__TRHandle = QRect(0, 0, self.__handleSize, self.__handleSize) self.__BLHandle = QRect(0, 0, self.__handleSize, self.__handleSize) self.__BRHandle = QRect(0, 0, self.__handleSize, self.__handleSize) self.__LHandle = QRect(0, 0, self.__handleSize, self.__handleSize) self.__THandle = QRect(0, 0, self.__handleSize, self.__handleSize) self.__RHandle = QRect(0, 0, self.__handleSize, self.__handleSize) self.__BHandle = QRect(0, 0, self.__handleSize, self.__handleSize) self.__handles = [self.__TLHandle, self.__TRHandle, self.__BLHandle, self.__BRHandle, self.__LHandle, self.__THandle, self.__RHandle, self.__BHandle] self.__helpTextRect = QRect() self.__helpText = self.tr( "Select a region using the mouse. To take the snapshot, press" " the Enter key or double click. Press Esc to quit.") self.__pixmap = QPixmap() self.setMouseTracking(True) QTimer.singleShot(200, self.__initialize) def __initialize(self): """ Private slot to initialize the rest of the widget. """ self.__desktop = QApplication.desktop() x = self.__desktop.x() y = self.__desktop.y() if qVersion() >= "5.0.0": self.__pixmap = QApplication.screens()[0].grabWindow( self.__desktop.winId(), x, y, self.__desktop.width(), self.__desktop.height()) else: self.__pixmap = QPixmap.grabWindow( self.__desktop.winId(), x, y, self.__desktop.width(), self.__desktop.height()) self.resize(self.__pixmap.size()) self.move(x, y) self.setCursor(Qt.CrossCursor) self.show() self.grabMouse() self.grabKeyboard() def paintEvent(self, evt): """ Protected method handling paint events. @param evt paint event (QPaintEvent) """ if self.__grabbing: # grabWindow() should just get the background return painter = QPainter(self) pal = QPalette(QToolTip.palette()) font = QToolTip.font() handleColor = pal.color(QPalette.Active, QPalette.Highlight) handleColor.setAlpha(160) overlayColor = QColor(0, 0, 0, 160) textColor = pal.color(QPalette.Active, QPalette.Text) textBackgroundColor = pal.color(QPalette.Active, QPalette.Base) painter.drawPixmap(0, 0, self.__pixmap) painter.setFont(font) r = QRect(self.__selection) if not self.__selection.isNull(): grey = QRegion(self.rect()) if self.__mode == SnapshotRegionGrabber.Ellipse: reg = QRegion(r, QRegion.Ellipse) else: reg = QRegion(r) grey = grey.subtracted(reg) painter.setClipRegion(grey) painter.setPen(Qt.NoPen) painter.setBrush(overlayColor) painter.drawRect(self.rect()) painter.setClipRect(self.rect()) drawRect(painter, r, handleColor) if self.__showHelp: painter.setPen(textColor) painter.setBrush(textBackgroundColor) self.__helpTextRect = painter.boundingRect( self.rect().adjusted(2, 2, -2, -2), Qt.TextWordWrap, self.__helpText).translated( -self.__desktop.x(), -self.__desktop.y()) self.__helpTextRect.adjust(-2, -2, 4, 2) drawRect(painter, self.__helpTextRect, textColor, textBackgroundColor) painter.drawText( self.__helpTextRect.adjusted(3, 3, -3, -3), Qt.TextWordWrap, self.__helpText) if self.__selection.isNull(): return # The grabbed region is everything which is covered by the drawn # rectangles (border included). This means that there is no 0px # selection, since a 0px wide rectangle will always be drawn as a line. txt = "{0:n}, {1:n} ({2:n} x {3:n})".format( self.__selection.x(), self.__selection.y(), self.__selection.width(), self.__selection.height()) textRect = painter.boundingRect(self.rect(), Qt.AlignLeft, txt) boundingRect = textRect.adjusted(-4, 0, 0, 0) if textRect.width() < r.width() - 2 * self.__handleSize and \ textRect.height() < r.height() - 2 * self.__handleSize and \ r.width() > 100 and \ r.height() > 100: # center, unsuitable for small selections boundingRect.moveCenter(r.center()) textRect.moveCenter(r.center()) elif r.y() - 3 > textRect.height() and \ r.x() + textRect.width() < self.rect().width(): # on top, left aligned boundingRect.moveBottomLeft(QPoint(r.x(), r.y() - 3)) textRect.moveBottomLeft(QPoint(r.x() + 2, r.y() - 3)) elif r.x() - 3 > textRect.width(): # left, top aligned boundingRect.moveTopRight(QPoint(r.x() - 3, r.y())) textRect.moveTopRight(QPoint(r.x() - 5, r.y())) elif r.bottom() + 3 + textRect.height() < self.rect().bottom() and \ r.right() > textRect.width(): # at bottom, right aligned boundingRect.moveTopRight(QPoint(r.right(), r.bottom() + 3)) textRect.moveTopRight(QPoint(r.right() - 2, r.bottom() + 3)) elif r.right() + textRect.width() + 3 < self.rect().width(): # right, bottom aligned boundingRect.moveBottomLeft(QPoint(r.right() + 3, r.bottom())) textRect.moveBottomLeft(QPoint(r.right() + 5, r.bottom())) # If the above didn't catch it, you are running on a very # tiny screen... drawRect(painter, boundingRect, textColor, textBackgroundColor) painter.drawText(textRect, Qt.AlignHCenter, txt) if (r.height() > self.__handleSize * 2 and r.width() > self.__handleSize * 2) or \ not self.__mouseDown: self.__updateHandles() painter.setPen(Qt.NoPen) painter.setBrush(handleColor) painter.setClipRegion( self.__handleMask(SnapshotRegionGrabber.StrokeMask)) painter.drawRect(self.rect()) handleColor.setAlpha(60) painter.setBrush(handleColor) painter.setClipRegion( self.__handleMask(SnapshotRegionGrabber.FillMask)) painter.drawRect(self.rect()) def resizeEvent(self, evt): """ Protected method to handle resize events. @param evt resize event (QResizeEvent) """ if self.__selection.isNull(): return r = QRect(self.__selection) r.setTopLeft(self.__limitPointToRect(r.topLeft(), self.rect())) r.setBottomRight(self.__limitPointToRect(r.bottomRight(), self.rect())) if r.width() <= 1 or r.height() <= 1: # This just results in ugly drawing... self.__selection = QRect() else: self.__selection = self.__normalizeSelection(r) def mousePressEvent(self, evt): """ Protected method to handle mouse button presses. @param evt mouse press event (QMouseEvent) """ self.__showHelp = not self.__helpTextRect.contains(evt.pos()) if evt.button() == Qt.LeftButton: self.__mouseDown = True self.__dragStartPoint = evt.pos() self.__selectionBeforeDrag = QRect(self.__selection) if not self.__selection.contains(evt.pos()): self.__newSelection = True self.__selection = QRect() else: self.setCursor(Qt.ClosedHandCursor) elif evt.button() == Qt.RightButton: self.__newSelection = False self.__selection = QRect() self.setCursor(Qt.CrossCursor) self.update() def mouseMoveEvent(self, evt): """ Protected method to handle mouse movements. @param evt mouse move event (QMouseEvent) """ shouldShowHelp = not self.__helpTextRect.contains(evt.pos()) if shouldShowHelp != self.__showHelp: self.__showHelp = shouldShowHelp self.update() if self.__mouseDown: if self.__newSelection: p = evt.pos() r = self.rect() self.__selection = self.__normalizeSelection( QRect(self.__dragStartPoint, self.__limitPointToRect(p, r))) elif self.__mouseOverHandle is None: # moving the whole selection r = self.rect().normalized() s = self.__selectionBeforeDrag.normalized() p = s.topLeft() + evt.pos() - self.__dragStartPoint r.setBottomRight( r.bottomRight() - QPoint(s.width(), s.height()) + QPoint(1, 1)) if not r.isNull() and r.isValid(): self.__selection.moveTo(self.__limitPointToRect(p, r)) else: # dragging a handle r = QRect(self.__selectionBeforeDrag) offset = evt.pos() - self.__dragStartPoint if self.__mouseOverHandle in \ [self.__TLHandle, self.__THandle, self.__TRHandle]: r.setTop(r.top() + offset.y()) if self.__mouseOverHandle in \ [self.__TLHandle, self.__LHandle, self.__BLHandle]: r.setLeft(r.left() + offset.x()) if self.__mouseOverHandle in \ [self.__BLHandle, self.__BHandle, self.__BRHandle]: r.setBottom(r.bottom() + offset.y()) if self.__mouseOverHandle in \ [self.__TRHandle, self.__RHandle, self.__BRHandle]: r.setRight(r.right() + offset.x()) r.setTopLeft(self.__limitPointToRect(r.topLeft(), self.rect())) r.setBottomRight( self.__limitPointToRect(r.bottomRight(), self.rect())) self.__selection = self.__normalizeSelection(r) self.update() else: if self.__selection.isNull(): return found = False for r in self.__handles: if r.contains(evt.pos()): self.__mouseOverHandle = r found = True break if not found: self.__mouseOverHandle = None if self.__selection.contains(evt.pos()): self.setCursor(Qt.OpenHandCursor) else: self.setCursor(Qt.CrossCursor) else: if self.__mouseOverHandle in [self.__TLHandle, self.__BRHandle]: self.setCursor(Qt.SizeFDiagCursor) elif self.__mouseOverHandle in [self.__TRHandle, self.__BLHandle]: self.setCursor(Qt.SizeBDiagCursor) elif self.__mouseOverHandle in [self.__LHandle, self.__RHandle]: self.setCursor(Qt.SizeHorCursor) elif self.__mouseOverHandle in [self.__THandle, self.__BHandle]: self.setCursor(Qt.SizeVerCursor) def mouseReleaseEvent(self, evt): """ Protected method to handle mouse button releases. @param evt mouse release event (QMouseEvent) """ self.__mouseDown = False self.__newSelection = False if self.__mouseOverHandle is None and \ self.__selection.contains(evt.pos()): self.setCursor(Qt.OpenHandCursor) self.update() def mouseDoubleClickEvent(self, evt): """ Protected method to handle mouse double clicks. @param evt mouse double click event (QMouseEvent) """ self.__grabRect() def keyPressEvent(self, evt): """ Protected method to handle key presses. @param evt key press event (QKeyEvent) """ if evt.key() == Qt.Key_Escape: self.grabbed.emit(QPixmap()) elif evt.key() in [Qt.Key_Enter, Qt.Key_Return]: self.__grabRect() else: evt.ignore() def __updateHandles(self): """ Private method to update the handles. """ r = QRect(self.__selection) s2 = self.__handleSize // 2 self.__TLHandle.moveTopLeft(r.topLeft()) self.__TRHandle.moveTopRight(r.topRight()) self.__BLHandle.moveBottomLeft(r.bottomLeft()) self.__BRHandle.moveBottomRight(r.bottomRight()) self.__LHandle.moveTopLeft(QPoint(r.x(), r.y() + r.height() // 2 - s2)) self.__THandle.moveTopLeft(QPoint(r.x() + r.width() // 2 - s2, r.y())) self.__RHandle.moveTopRight( QPoint(r.right(), r.y() + r.height() // 2 - s2)) self.__BHandle.moveBottomLeft( QPoint(r.x() + r.width() // 2 - s2, r.bottom())) def __handleMask(self, maskType): """ Private method to calculate the handle mask. @param maskType type of the mask to be used (SnapshotRegionGrabber.FillMask or SnapshotRegionGrabber.StrokeMask) @return calculated mask (QRegion) """ mask = QRegion() for rect in self.__handles: if maskType == SnapshotRegionGrabber.StrokeMask: r = QRegion(rect) mask += r.subtracted(QRegion(rect.adjusted(1, 1, -1, -1))) else: mask += QRegion(rect.adjusted(1, 1, -1, -1)) return mask def __limitPointToRect(self, point, rect): """ Private method to limit the given point to the given rectangle. @param point point to be limited (QPoint) @param rect rectangle the point shall be limited to (QRect) @return limited point (QPoint) """ q = QPoint() if point.x() < rect.x(): q.setX(rect.x()) elif point.x() < rect.right(): q.setX(point.x()) else: q.setX(rect.right()) if point.y() < rect.y(): q.setY(rect.y()) elif point.y() < rect.bottom(): q.setY(point.y()) else: q.setY(rect.bottom()) return q def __normalizeSelection(self, sel): """ Private method to normalize the given selection. @param sel selection to be normalized (QRect) @return normalized selection (QRect) """ rect = QRect(sel) if rect.width() <= 0: left = rect.left() width = rect.width() rect.setLeft(left + width - 1) rect.setRight(left) if rect.height() <= 0: top = rect.top() height = rect.height() rect.setTop(top + height - 1) rect.setBottom(top) return rect def __grabRect(self): """ Private method to grab the selected rectangle (i.e. do the snapshot). """ if self.__mode == SnapshotRegionGrabber.Ellipse: ell = QRegion(self.__selection, QRegion.Ellipse) if not ell.isEmpty(): self.__grabbing = True xOffset = self.__pixmap.rect().x() - ell.boundingRect().x() yOffset = self.__pixmap.rect().y() - ell.boundingRect().y() translatedEll = ell.translated(xOffset, yOffset) pixmap2 = QPixmap(ell.boundingRect().size()) pixmap2.fill(Qt.transparent) pt = QPainter() pt.begin(pixmap2) if pt.paintEngine().hasFeature(QPaintEngine.PorterDuff): pt.setRenderHints( QPainter.Antialiasing | QPainter.HighQualityAntialiasing | QPainter.SmoothPixmapTransform, True) pt.setBrush(Qt.black) pt.setPen(QPen(QBrush(Qt.black), 0.5)) pt.drawEllipse(translatedEll.boundingRect()) pt.setCompositionMode(QPainter.CompositionMode_SourceIn) else: pt.setClipRegion(translatedEll) pt.setCompositionMode(QPainter.CompositionMode_Source) pt.drawPixmap(pixmap2.rect(), self.__pixmap, ell.boundingRect()) pt.end() self.grabbed.emit(pixmap2) else: r = QRect(self.__selection) if not r.isNull() and r.isValid(): self.__grabbing = True self.grabbed.emit(self.__pixmap.copy(r))
def paintEvent(self, evt): """ Protected method handling paint events. @param evt paint event (QPaintEvent) """ if self.__grabbing: # grabWindow() should just get the background return painter = QPainter(self) pal = QPalette(QToolTip.palette()) font = QToolTip.font() handleColor = pal.color(QPalette.Active, QPalette.Highlight) handleColor.setAlpha(160) overlayColor = QColor(0, 0, 0, 160) textColor = pal.color(QPalette.Active, QPalette.Text) textBackgroundColor = pal.color(QPalette.Active, QPalette.Base) painter.drawPixmap(0, 0, self.__pixmap) painter.setFont(font) r = QRect(self.__selection) if not self.__selection.isNull(): grey = QRegion(self.rect()) if self.__mode == SnapshotRegionGrabber.Ellipse: reg = QRegion(r, QRegion.Ellipse) else: reg = QRegion(r) grey = grey.subtracted(reg) painter.setClipRegion(grey) painter.setPen(Qt.NoPen) painter.setBrush(overlayColor) painter.drawRect(self.rect()) painter.setClipRect(self.rect()) drawRect(painter, r, handleColor) if self.__showHelp: painter.setPen(textColor) painter.setBrush(textBackgroundColor) self.__helpTextRect = painter.boundingRect( self.rect().adjusted(2, 2, -2, -2), Qt.TextWordWrap, self.__helpText).translated( -self.__desktop.x(), -self.__desktop.y()) self.__helpTextRect.adjust(-2, -2, 4, 2) drawRect(painter, self.__helpTextRect, textColor, textBackgroundColor) painter.drawText( self.__helpTextRect.adjusted(3, 3, -3, -3), Qt.TextWordWrap, self.__helpText) if self.__selection.isNull(): return # The grabbed region is everything which is covered by the drawn # rectangles (border included). This means that there is no 0px # selection, since a 0px wide rectangle will always be drawn as a line. txt = "{0:n}, {1:n} ({2:n} x {3:n})".format( self.__selection.x(), self.__selection.y(), self.__selection.width(), self.__selection.height()) textRect = painter.boundingRect(self.rect(), Qt.AlignLeft, txt) boundingRect = textRect.adjusted(-4, 0, 0, 0) if textRect.width() < r.width() - 2 * self.__handleSize and \ textRect.height() < r.height() - 2 * self.__handleSize and \ r.width() > 100 and \ r.height() > 100: # center, unsuitable for small selections boundingRect.moveCenter(r.center()) textRect.moveCenter(r.center()) elif r.y() - 3 > textRect.height() and \ r.x() + textRect.width() < self.rect().width(): # on top, left aligned boundingRect.moveBottomLeft(QPoint(r.x(), r.y() - 3)) textRect.moveBottomLeft(QPoint(r.x() + 2, r.y() - 3)) elif r.x() - 3 > textRect.width(): # left, top aligned boundingRect.moveTopRight(QPoint(r.x() - 3, r.y())) textRect.moveTopRight(QPoint(r.x() - 5, r.y())) elif r.bottom() + 3 + textRect.height() < self.rect().bottom() and \ r.right() > textRect.width(): # at bottom, right aligned boundingRect.moveTopRight(QPoint(r.right(), r.bottom() + 3)) textRect.moveTopRight(QPoint(r.right() - 2, r.bottom() + 3)) elif r.right() + textRect.width() + 3 < self.rect().width(): # right, bottom aligned boundingRect.moveBottomLeft(QPoint(r.right() + 3, r.bottom())) textRect.moveBottomLeft(QPoint(r.right() + 5, r.bottom())) # If the above didn't catch it, you are running on a very # tiny screen... drawRect(painter, boundingRect, textColor, textBackgroundColor) painter.drawText(textRect, Qt.AlignHCenter, txt) if (r.height() > self.__handleSize * 2 and r.width() > self.__handleSize * 2) or \ not self.__mouseDown: self.__updateHandles() painter.setPen(Qt.NoPen) painter.setBrush(handleColor) painter.setClipRegion( self.__handleMask(SnapshotRegionGrabber.StrokeMask)) painter.drawRect(self.rect()) handleColor.setAlpha(60) painter.setBrush(handleColor) painter.setClipRegion( self.__handleMask(SnapshotRegionGrabber.FillMask)) painter.drawRect(self.rect())
class BrushingModel(QObject): brushSizeChanged = pyqtSignal(int) brushColorChanged = pyqtSignal(QColor) brushStrokeAvailable = pyqtSignal(QPointF, object) drawnNumberChanged = pyqtSignal(int) minBrushSize = 1 maxBrushSize = 61 defaultBrushSize = 3 defaultDrawnNumber = 1 defaultColor = Qt.white erasingColor = Qt.black erasingNumber = 100 def __init__(self, parent=None): QObject.__init__(self, parent=parent) self.sliceRect = None self.bb = QRect() # bounding box enclosing the drawing self.brushSize = self.defaultBrushSize self.drawColor = self.defaultColor self._temp_color = None self._temp_number = None self.drawnNumber = self.defaultDrawnNumber self.pos = None self.erasing = False self._hasMoved = False self.drawOnto = None # an empty scene, where we add all drawn line segments # a QGraphicsLineItem, and which we can use to then # render to an image self.scene = QGraphicsScene() def toggleErase(self): self.erasing = not (self.erasing) if self.erasing: self.setErasing() else: self.disableErasing() def setErasing(self): self.erasing = True self._temp_color = self.drawColor self._temp_number = self.drawnNumber self.setBrushColor(self.erasingColor) self.brushColorChanged.emit(self.erasingColor) self.setDrawnNumber(self.erasingNumber) def disableErasing(self): self.erasing = False self.setBrushColor(self._temp_color) self.brushColorChanged.emit(self.drawColor) self.setDrawnNumber(self._temp_number) def setBrushSize(self, size): self.brushSize = size self.brushSizeChanged.emit(self.brushSize) def setDrawnNumber(self, num): self.drawnNumber = num self.drawnNumberChanged.emit(num) def getBrushSize(self): return self.brushSize def brushSmaller(self): b = self.brushSize if b > self.minBrushSize: self.setBrushSize(b - 1) def brushBigger(self): b = self.brushSize if self.brushSize < self.maxBrushSize: self.setBrushSize(b + 1) def setBrushColor(self, color): self.drawColor = QColor(color) self.brushColorChanged.emit(self.drawColor) def beginDrawing(self, pos, sliceRect): """ pos -- QPointF-like """ self.sliceRect = sliceRect self.scene.clear() self.bb = QRect() self.pos = QPointF(pos.x(), pos.y()) self._hasMoved = False def endDrawing(self, pos): has_moved = self._hasMoved # _hasMoved will change after calling moveTo if has_moved: self.moveTo(pos) else: assert self.pos == pos self.moveTo(QPointF(pos.x() + 0.0001, pos.y() + 0.0001)) # move a little # Qt seems to use strange rules for determining which pixels to set when rendering a brush stroke to a QImage. # We seem to get better results if we do the following: # 1) Slightly offset the source window because apparently there is a small shift in the data # 2) Render the scene to an image that is MUCH larger than the scene resolution (4x by 4x) # 3) Downsample each 4x4 patch from the large image back to a single pixel in the final image, # applying some threshold to determine if the final pixel is on or off. tempi = QImage( QSize(4 * self.bb.width(), 4 * self.bb.height()), QImage.Format_ARGB32_Premultiplied ) # TODO: format tempi.fill(0) painter = QPainter(tempi) # Offset the source window. At first I thought the right offset was 0.5, because # that would seem to make sure points are rounded to pixel CENTERS, but # experimentation indicates that 0.25 is slightly better for some reason... source_rect = QRectF(QPointF(self.bb.x() + 0.25, self.bb.y() + 0.25), QSizeF(self.bb.width(), self.bb.height())) target_rect = QRectF(QPointF(0, 0), QSizeF(4 * self.bb.width(), 4 * self.bb.height())) self.scene.render(painter, target=target_rect, source=source_rect) painter.end() # Now downsample: convert each 4x4 patch into a single pixel by summing and dividing ndarr = qimage2ndarray.rgb_view(tempi)[:, :, 0].astype(int) ndarr = ndarr.reshape((ndarr.shape[0],) + (ndarr.shape[1] // 4,) + (4,)) ndarr = ndarr.sum(axis=-1) ndarr = ndarr.transpose() ndarr = ndarr.reshape((ndarr.shape[0],) + (ndarr.shape[1] // 4,) + (4,)) ndarr = ndarr.sum(axis=-1) ndarr = ndarr.transpose() ndarr //= 4 * 4 downsample_threshold = (7.0 / 16) * 255 labels = numpy.where(ndarr >= downsample_threshold, numpy.uint8(self.drawnNumber), numpy.uint8(0)) labels = labels.swapaxes(0, 1) assert labels.shape[0] == self.bb.width() assert labels.shape[1] == self.bb.height() ## ## ensure that at least one pixel is label when the brush size is 1 ## ## this happens when the user just clicked without moving ## in that case the lineitem will be so tiny, that it won't be rendered ## into a single pixel by the code above if not has_moved and self.brushSize <= 1 and numpy.count_nonzero(labels) == 0: labels[labels.shape[0] // 2, labels.shape[1] // 2] = self.drawnNumber self.brushStrokeAvailable.emit(QPointF(self.bb.x(), self.bb.y()), labels) def dumpDraw(self, pos): res = self.endDrawing(pos) self.beginDrawing(pos, self.sliceRect) return res def moveTo(self, pos): # data coordinates oldX, oldY = self.pos.x(), self.pos.y() x, y = pos.x(), pos.y() line = QGraphicsLineItem(oldX, oldY, x, y) line.setPen(QPen(QBrush(Qt.white), self.brushSize, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin)) self.scene.addItem(line) self._hasMoved = True # update bounding Box if not self.bb.isValid(): self.bb = QRect(QPoint(oldX, oldY), QSize(1, 1)) # grow bounding box self.bb.setLeft(min(self.bb.left(), max(0, x - self.brushSize // 2 - 1))) self.bb.setRight(max(self.bb.right(), min(self.sliceRect[0] - 1, x + self.brushSize // 2 + 1))) self.bb.setTop(min(self.bb.top(), max(0, y - self.brushSize // 2 - 1))) self.bb.setBottom(max(self.bb.bottom(), min(self.sliceRect[1] - 1, y + self.brushSize // 2 + 1))) # update/move position self.pos = pos
def updateBrush(self, cursorPos, _list = None): # get the current tile layer currentLayer = self.currentTileLayer() layerWidth = currentLayer.width() layerHeight = currentLayer.height() numTiles = layerWidth * layerHeight paintCorner = 0 # if we are in vertex paint mode, the bottom right corner on the map will appear as an invalid tile offset... if (self.mBrushMode == BrushMode.PaintVertex): if (cursorPos.x() == layerWidth): cursorPos.setX(cursorPos.x() - 1) paintCorner |= 1 if (cursorPos.y() == layerHeight): cursorPos.setY(cursorPos.y() - 1) paintCorner |= 2 # if the cursor is outside of the map, bail out if (not currentLayer.bounds().contains(cursorPos)): return terrainTileset = None terrainId = -1 if (self.mTerrain): terrainTileset = self.mTerrain.tileset() terrainId = self.mTerrain.id() # allocate a buffer to build the terrain tilemap (TODO: this could be retained per layer to save regular allocation) newTerrain = [] for i in range(numTiles): newTerrain.append(0) # allocate a buffer of flags for each tile that may be considered (TODO: this could be retained per layer to save regular allocation) checked = array('B') for i in range(numTiles): checked.append(0) # create a consideration list, and push the start points transitionList = QList() initialTiles = 0 if (_list): # if we were supplied a list of start points for p in _list: transitionList.append(p) initialTiles += 1 else: transitionList.append(cursorPos) initialTiles = 1 brushRect = QRect(cursorPos, cursorPos) # produce terrain with transitions using a simple, relative naive approach (considers each tile once, and doesn't allow re-consideration if selection was bad) while (not transitionList.isEmpty()): # get the next point in the consideration list p = transitionList.takeFirst() x = p.x() y = p.y() i = y*layerWidth + x # if we have already considered this point, skip to the next # TODO: we might want to allow re-consideration if prior tiles... but not for now, this would risk infinite loops if (checked[i]): continue tile = currentLayer.cellAt(p).tile currentTerrain = terrain(tile) # get the tileset for this tile tileset = None if (terrainTileset): # if we are painting a terrain, then we'll use the terrains tileset tileset = terrainTileset elif (tile): # if we're erasing terrain, use the individual tiles tileset (to search for transitions) tileset = tile.tileset() else: # no tile here and we're erasing terrain, not much we can do continue # calculate the ideal tile for this position preferredTerrain = 0xFFFFFFFF mask = 0 if (initialTiles): # for the initial tiles, we will insert the selected terrain and add the surroundings for consideration if (self.mBrushMode == BrushMode.PaintTile): # set the whole tile to the selected terrain preferredTerrain = makeTerrain(terrainId) mask = 0xFFFFFFFF else: # Bail out if encountering a tile from a different tileset if (tile and tile.tileset() != tileset): continue # calculate the corner mask mask = 0xFF << (3 - paintCorner)*8 # mask in the selected terrain preferredTerrain = (currentTerrain & ~mask) | (terrainId << (3 - paintCorner)*8) initialTiles -= 1 # if there's nothing to paint... skip this tile if (preferredTerrain == currentTerrain and (not tile or tile.tileset() == tileset)): continue else: # Bail out if encountering a tile from a different tileset if (tile and tile.tileset() != tileset): continue # following tiles each need consideration against their surroundings preferredTerrain = currentTerrain mask = 0 # depending which connections have been set, we update the preferred terrain of the tile accordingly if (y > 0 and checked[i - layerWidth]): preferredTerrain = ((terrain(newTerrain[i - layerWidth]) << 16) | (preferredTerrain & 0x0000FFFF))&0xFFFFFFFF mask |= 0xFFFF0000 if (y < layerHeight - 1 and checked[i + layerWidth]): preferredTerrain = ((terrain(newTerrain[i + layerWidth]) >> 16) | (preferredTerrain & 0xFFFF0000))&0xFFFFFFFF mask |= 0x0000FFFF if (x > 0 and checked[i - 1]): preferredTerrain = (((terrain(newTerrain[i - 1]) << 8) & 0xFF00FF00) | (preferredTerrain & 0x00FF00FF))&0xFFFFFFFF mask |= 0xFF00FF00 if (x < layerWidth - 1 and checked[i + 1]): preferredTerrain = (((terrain(newTerrain[i + 1]) >> 8) & 0x00FF00FF) | (preferredTerrain & 0xFF00FF00))&0xFFFFFFFF mask |= 0x00FF00FF # find the most appropriate tile in the tileset paste = None if (preferredTerrain != 0xFFFFFFFF): paste = findBestTile(tileset, preferredTerrain, mask) if (not paste): continue # add tile to the brush newTerrain[i] = paste checked[i] = True # expand the brush rect to fit the edit set brushRect |= QRect(p, p) # consider surrounding tiles if terrain constraints were not satisfied if (y > 0 and not checked[i - layerWidth]): above = currentLayer.cellAt(x, y - 1).tile if (topEdge(paste) != bottomEdge(above)): transitionList.append(QPoint(x, y - 1)) if (y < layerHeight - 1 and not checked[i + layerWidth]): below = currentLayer.cellAt(x, y + 1).tile if (bottomEdge(paste) != topEdge(below)): transitionList.append(QPoint(x, y + 1)) if (x > 0 and not checked[i - 1]): left = currentLayer.cellAt(x - 1, y).tile if (leftEdge(paste) != rightEdge(left)): transitionList.append(QPoint(x - 1, y)) if (x < layerWidth - 1 and not checked[i + 1]): right = currentLayer.cellAt(x + 1, y).tile if (rightEdge(paste) != leftEdge(right)): transitionList.append(QPoint(x + 1, y)) # create a stamp for the terrain block stamp = TileLayer(QString(), brushRect.left(), brushRect.top(), brushRect.width(), brushRect.height()) for y in range(brushRect.top(), brushRect.bottom()+1): for x in range(brushRect.left(), brushRect.right()+1): i = y*layerWidth + x if (not checked[i]): continue tile = newTerrain[i] if (tile): stamp.setCell(x - brushRect.left(), y - brushRect.top(), Cell(tile)) else: # TODO: we need to do something to erase tiles where checked[i] is True, and newTerrain[i] is NULL # is there an eraser stamp? investigate how the eraser works... pass # set the new tile layer as the brush self.brushItem().setTileLayer(stamp) del checked del newTerrain self.mPaintX = cursorPos.x() self.mPaintY = cursorPos.y() self.mOffsetX = cursorPos.x() - brushRect.left() self.mOffsetY = cursorPos.y() - brushRect.top()
class Surface(QWidget): rightClicked = pyqtSignal(QPoint) linkClicked = pyqtSignal(QEvent, page.Page, popplerqt5.Poppler.Link) linkHovered = pyqtSignal(page.Page, popplerqt5.Poppler.Link) linkLeft = pyqtSignal() linkHelpRequested = pyqtSignal(QPoint, page.Page, popplerqt5.Poppler.Link) selectionChanged = pyqtSignal(QRect) def __init__(self, view): super(Surface, self).__init__(view) self.setBackgroundRole(QPalette.Dark) self._view = weakref.ref(view) self._currentLinkId = None self._selecting = False self._magnifying = False self._magnifier = None self.setMagnifier(magnifier.Magnifier()) self.setMagnifierModifiers(Qt.CTRL) self._selection = QRect() self._rubberBand = CustomRubberBand(self) self._scrolling = False self._scrollTimer = QTimer(interval=100, timeout=self._scrollTimeout) self._pageLayout = None self._highlights = weakref.WeakKeyDictionary() self.setPageLayout(layout.Layout()) self.setContextMenuPolicy(Qt.PreventContextMenu) self.setLinksEnabled(True) self.setSelectionEnabled(True) self.setShowUrlTips(True) self.view().cursorNeedUpdate.connect(self.updateCursor) def pageLayout(self): return self._pageLayout def setPageLayout(self, layout): old, self._pageLayout = self._pageLayout, layout if old: old.redraw.disconnect(self.redraw) old.changed.disconnect(self.updateLayout) layout.redraw.connect(self.redraw) layout.changed.connect(self.updateLayout) def view(self): """Returns our associated View.""" return self._view() def viewportRect(self): """Returns the rectangle of us that is visible in the View.""" return self.view().viewport().rect().translated(-self.pos()) def setSelectionEnabled(self, enabled): """Enables or disables selecting rectangular regions.""" self._selectionEnabled = enabled if not enabled: self.clearSelection() self._rubberBand.hide() self._selecting = False def selectionEnabled(self): """Returns True if selecting rectangular regions is enabled.""" return self._selectionEnabled def setLinksEnabled(self, enabled): """Enables or disables the handling of Poppler.Links in the pages.""" self._linksEnabled = enabled def linksEnabled(self): """Returns True if the handling of Poppler.Links in the pages is enabled.""" return self._linksEnabled def setShowUrlTips(self, enabled): """Enables or disables showing the URL in a tooltip when hovering a link. (Of course also setLinksEnabled(True) if you want this.) """ self._showUrlTips = enabled def showUrlTips(self): """Returns True if URLs are shown in a tooltip when hovering a link.""" return self._showUrlTips def setMagnifier(self, magnifier): """Sets the Magnifier to use (or None to disable the magnifier). The Surface takes ownership of the Magnifier. """ if self._magnifier: self._magnifier.setParent(None) magnifier.setParent(self.view()) self._magnifier = magnifier def magnifier(self): """Returns the currently set magnifier.""" return self._magnifier def setMagnifierModifiers(self, modifiers): """Sets the modifiers (e.g. Qt.CTRL) to show the magnifier. Use None to show the magnifier always (instead of dragging). """ self._magnifierModifiers = modifiers def magnifierModifiers(self): """Returns the currently set keyboard modifiers (e.g. Qt.CTRL) to show the magnifier.""" return self._magnifierModifiers def hasSelection(self): """Returns True if there is a selection.""" return bool(self._selection) def setSelection(self, rect): """Sets the selection rectangle.""" rect = rect.normalized() old, self._selection = self._selection, rect self._rubberBand.setVisible(rect.isValid()) self._rubberBand.setGeometry(rect) if rect != old: self.selectionChanged.emit(rect) def selection(self): """Returns the selection rectangle (normalized) or an invalid QRect().""" return QRect(self._selection) def clearSelection(self): """Hides the selection rectangle.""" self.setSelection(QRect()) def selectedPages(self): """Return a list of the Page objects the selection encompasses.""" return list(self.pageLayout().pagesAt(self.selection())) def selectedPage(self): """Return the Page thas is selected for the largest part, or None.""" pages = self.selectedPages() if not pages: return def key(page): size = page.rect().intersected(self.selection()).size() return size.width() + size.height() return max(pages, key = key) def selectedPageRect(self, page): """Return the QRect on the page that falls in the selection.""" return self.selection().normalized().intersected(page.rect()).translated(-page.pos()) def selectedText(self): """Return all text falling in the selection.""" return '\n'.join(page.text(self.selection()) for page in self.selectedPages()) def redraw(self, rect): """Called when the Layout wants to redraw a rectangle.""" self.update(rect) def updateLayout(self): """Conforms ourselves to our layout (that must already be updated.)""" self.clearSelection() self.resize(self._pageLayout.size()) self.update() 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 = collections.defaultdict(list) for page, area in areas: d[page].append(area) d = weakref.WeakKeyDictionary(d) 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 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 paintEvent(self, ev): """Handle PaintEvent on the surface to highlight the selection.""" painter = QPainter(self) pages = list(self.pageLayout().pagesAt(ev.rect())) for page in pages: page.paint(painter, ev.rect()) for highlighter, (d, t) in self._highlights.items(): rects = [] for page in pages: try: rects.extend(map(page.linkRect, d[page])) except KeyError: continue if rects: highlighter.paintRects(painter, rects) def handleMousePressEvent(self, ev): """Handle mouse press for various operations - links to source, - magnifier, - selection highlight, If event was used, return true to indicate processing should stop. """ # As the event comes from the view, we need to map it locally. pos = self.mapFromParent(ev.pos()) # selecting? if self._selectionEnabled: if self.hasSelection(): edge = selectionEdge(pos, self.selection()) if edge == _OUTSIDE: self.clearSelection() else: if ev.button() != Qt.RightButton or edge != _INSIDE: self._selecting = True self._selectionEdge = edge self._selectionRect = self.selection() self._selectionPos = pos if edge == _INSIDE: self.setCursor(Qt.SizeAllCursor) return True if not self._selecting: if ev.modifiers() & Qt.ShiftModifier and ev.button() == Qt.LeftButton and self._linksEnabled: page, link = self.pageLayout().linkAt(pos) if link: self.linkClickEvent(ev, page, link) return True if ev.button() == Qt.RightButton or int(ev.modifiers()) & _SCAM: if not (int(ev.modifiers()) & _SCAM == self._magnifierModifiers and ev.button() == Qt.LeftButton): self._selecting = True self._selectionEdge = _RIGHT | _BOTTOM self._selectionRect = QRect(pos, QSize(0, 0)) self._selectionPos = pos return True # link? if self._linksEnabled: page, link = self.pageLayout().linkAt(pos) if link: self.linkClickEvent(ev, page, link) return True # magnifier? if (self._magnifier and int(ev.modifiers()) & _SCAM == self._magnifierModifiers and ev.button() == Qt.LeftButton): self._magnifier.moveCenter(pos) self._magnifier.show() self._magnifier.raise_() self._magnifying = True self.setCursor(QCursor(Qt.BlankCursor)) return True return False def handleMouseReleaseEvent(self, ev): """Handle mouse release events for various operations: - hide magnifier, - selection. If event was used, return true to indicate processing should stop. """ consumed = False if self._magnifying: self._magnifier.hide() self._magnifying = False self.unsetCursor() consumed = True elif self._selecting: self._selecting = False selection = self._selectionRect.normalized() if selection.width() < 8 and selection.height() < 8: self.clearSelection() else: self.setSelection(selection) if self._scrolling: self.stopScrolling() self.unsetCursor() consumed = True if ev.button() == Qt.RightButton: # As the event comes from the view, we need to map it locally. self.rightClick(self.mapFromParent(ev.pos())) consumed = True return consumed def handleMouseMoveEvent(self, ev): """Handle mouse move events for various operations: - move magnifier, - selection extension. If event was used, return true to indicate processing should stop. """ consumed = False if self._magnifying: # As the event comes from the view, we need to map it locally. self._magnifier.moveCenter(self.mapFromParent(ev.pos())) consumed = True elif self._selecting: # As the event comes from the view, we need to map it locally. pos = self.mapFromParent(ev.pos()) self._moveSelection(pos) self._rubberBand.show() # check if we are dragging close to the edge of the view, scroll if needed view = self.viewportRect() dx = pos.x() - view.left() - 12 if dx >= 0: dx = pos.x() - view.right() + 12 if dx < 0: dx = 0 dy = pos.y() - view.top() - 12 if dy >= 0: dy = pos.y() - view.bottom() + 12 if dy < 0: dy = 0 if dx or dy: self.startScrolling(dx, dy) elif self._scrolling: self.stopScrolling() consumed = True return consumed def handleMoveEvent(self, ev): """Handle move events for various operations: - move magnifier, - selection extension. If event was used, return true to indicate processing should stop. """ consumed = False pos = self.mapFromGlobal(QCursor.pos()) if self._selecting: self._moveSelection(pos) consumed = True elif self._magnifying: self._magnifier.moveCenter(pos) consumed = True return consumed def handleHelpEvent(self, ev): """Handle help event: show link if any.""" if self._linksEnabled: page, link = self.pageLayout().linkAt(self.mapFromParent(ev.pos())) if link: self.linkHelpEvent(ev.globalPos(), page, link) return True def updateKineticCursor(self, active): """Cursor handling when kinetic move starts/stops. - reset the cursor and hide tooltips if visible at start, - update the cursor and show the appropriate tooltips at stop. Used as a slot linked to the kineticStarted() signal. """ if active: self.unsetCursor() if QToolTip.isVisible(): QToolTip.hideText() else: self.updateCursor(QCursor.pos()) if self._linksEnabled: page, link = self.pageLayout().linkAt(self.mapFromGlobal(QCursor.pos())) if link: self.linkHelpEvent(QCursor.pos(), page, link) def updateCursor(self, evpos): """Set the cursor to the right glyph, depending on action""" pos = self.mapFromGlobal(evpos) cursor = None edge = _OUTSIDE if self._selectionEnabled and self.hasSelection(): edge = selectionEdge(pos, self.selection()) if edge is not _OUTSIDE: if edge in (_TOP, _BOTTOM): cursor = Qt.SizeVerCursor elif edge in (_LEFT, _RIGHT): cursor = Qt.SizeHorCursor elif edge in (_LEFT | _TOP, _RIGHT | _BOTTOM): cursor = Qt.SizeFDiagCursor elif edge in (_TOP | _RIGHT, _BOTTOM | _LEFT): cursor = Qt.SizeBDiagCursor elif edge is _INSIDE: cursor = Qt.SizeAllCursor elif self._linksEnabled: page, link = self.pageLayout().linkAt(pos) if link: cursor = Qt.PointingHandCursor lid = id(link) else: lid = None if lid != self._currentLinkId: if self._currentLinkId is not None: self.linkHoverLeave() self._currentLinkId = lid if link: self.linkHoverEnter(page, link) self.setCursor(cursor) if cursor else self.unsetCursor() def linkHelpEvent(self, globalPos, page, link): """Called when a QHelpEvent occurs on a link. The default implementation shows a tooltip if showUrls() is True, and emits the linkHelpRequested() signal. """ if self._showUrlTips and isinstance(link, popplerqt5.Poppler.LinkBrowse): QToolTip.showText(globalPos, link.url(), self, page.linkRect(link.linkArea())) self.linkHelpRequested.emit(globalPos, page, link) def rightClick(self, pos): """Called when the right mouse button is released. (Use this instead of the contextMenuEvent as that one also fires when starting a right-button selection.) The default implementation emits the rightClicked(pos) signal and also sends a ContextMenu event to the View widget. """ self.rightClicked.emit(pos) QApplication.postEvent(self.view().viewport(), QContextMenuEvent(QContextMenuEvent.Mouse, pos + self.pos())) def linkClickEvent(self, ev, page, link): """Called when a link is clicked. The default implementation emits the linkClicked(event, page, link) signal. """ self.linkClicked.emit(ev, page, link) def linkHoverEnter(self, page, link): """Called when the mouse hovers over a link. The default implementation emits the linkHovered(page, link) signal. """ self.linkHovered.emit(page, link) def linkHoverLeave(self): """Called when the mouse does not hover a link anymore. The default implementation emits the linkLeft() signal. """ self.linkLeft.emit() def startScrolling(self, dx, dy): """Starts scrolling dx, dy about 10 times a second. Stops automatically when the end is reached. """ self._scrolling = QPoint(dx, dy) self._scrollTimer.isActive() or self._scrollTimer.start() def stopScrolling(self): """Stops scrolling.""" self._scrolling = False self._scrollTimer.stop() def _scrollTimeout(self): """(Internal) Called by the _scrollTimer.""" # change the scrollbars, but check how far they really moved. pos = self.pos() self.view().fastScrollBy(self._scrolling) diff = pos - self.pos() if not diff: self.stopScrolling() def _moveSelection(self, pos): """(Internal) Moves the dragged selection edge or corner to the given pos (QPoint).""" diff = pos - self._selectionPos self._selectionPos = pos edge = self._selectionEdge self._selectionRect.adjust( diff.x() if edge & _LEFT else 0, diff.y() if edge & _TOP else 0, diff.x() if edge & _RIGHT else 0, diff.y() if edge & _BOTTOM else 0) rect = self._selectionRect.normalized() self._rubberBand.setVisible(rect.isValid()) self._rubberBand.setGeometry(rect) if self.cursor().shape() in (Qt.SizeBDiagCursor, Qt.SizeFDiagCursor): # we're dragging a corner, use correct diagonal cursor bdiag = (edge in (3, 12)) ^ (self._selectionRect.width() * self._selectionRect.height() >= 0) self.setCursor(Qt.SizeBDiagCursor if bdiag else Qt.SizeFDiagCursor)