def create_painter(self) -> typing.Iterator[QPainter]: white = QColor('white') pixmap = QPixmap(self.width, self.height) pixmap.fill(white) painter = QPainter(pixmap) try: yield painter finally: painter.end()
def slotPrintPreview(self): pix = QPixmap(1000, 200) pix.fill(Qt.white) painter = QPainter(pix) view.print(painter, pix.rect()) painter.end() label = QLabel(this) label.setPixmap(pix) label.show()
def printPreview(self): preview = QLabel(self, Qt.Window) preview.setAttribute(Qt.WA_DeleteOnClose) preview.setScaledContents(True) preview.setWindowTitle("Print Preview") pix = QPixmap(1000, 300) pix.fill(Qt.white) p = QPainter(pix) p.setRenderHints(QPainter.Antialiasing) self.ui.ganttView.print_(p, pix.rect()) preview.setPixmap(pix) preview.show()
def create_icon(player_colour: QColor) -> QPixmap: size = 200 icon = QPixmap(size, size) icon.fill(Qt.transparent) painter = QPainter(icon) try: painter.setBrush(player_colour) pen = QPen() pen.setWidth(3) painter.setPen(pen) painter.drawEllipse(1, 1, size - 2, size - 2) finally: painter.end() return icon
def show_synced(self, is_sync: bool) -> None: p1 = QPixmap(self.icon().pixmap(self.icon().actualSize(QSize(1024, 1024)))) p2 = self.get_icon(is_sync) mode = QPainter.CompositionMode_SourceOver s = p1.size().expandedTo(p2.size()) result = QPixmap(s) result.fill(Qt.transparent) painter = QPainter(result) painter.setRenderHint(QPainter.Antialiasing) painter.drawPixmap(QPoint(), p1) painter.setCompositionMode(mode) painter.drawPixmap(result.rect(), p2, p2.rect()) painter.end() self.setIcon(QIcon(result))
def shape_to_pixelmap(item_type, pen, brush, shape) -> QPixmap: pixmap = QPixmap(50, 50) pixmap.fill(Qt.transparent) painter = QPainter(pixmap) painter.setRenderHint(QPainter.Antialiasing) painter.setPen(pen) painter.setBrush(brush) if item_type == QGraphicsRectItem.type(QGraphicsRectItem()): painter.drawRect(QRect(10, 15, 30, 20)) elif item_type == QGraphicsEllipseItem.type(QGraphicsEllipseItem()): painter.drawEllipse(QRect(10, 10, 30, 30)) elif item_type == QGraphicsPolygonItem.type(QGraphicsPolygonItem()): if shape.polygon().size() == 3: painter.drawPolygon(QPolygon([QPoint(10, 40), QPoint(40, 40), QPoint(25, 10)])) else: painter.drawPolygon(QPolygon([QPoint(12, 40), QPoint(23, 36), QPoint(37, 24), QPoint(23, 12), QPoint(7, 16)])) elif item_type == QGraphicsLineItem.type(QGraphicsLineItem()): painter.drawLine(QLine(10, 40, 40, 10)) return pixmap
def assemble_board(self) -> QPixmap: full_board = self.load_pixmap('board-1.png') board_size = self.start_state.size if board_size == 4: return full_board full_height = full_board.height() full_width = full_board.width() left_width = round(full_width * 0.28) mid_width = round(full_width * 0.22) right_width = round(full_width * 0.279) top_height = round(full_height * 0.28) mid_height = round(full_height * 0.21) bottom_height = round(full_height * 0.29) final_width = left_width + (board_size - 2) * mid_width + right_width final_height = top_height + (board_size - 2) * mid_height + bottom_height assembled_board = QPixmap(final_width, final_height) assembled_board.fill(Qt.transparent) assembled_painter = QPainter(assembled_board) # top left assembled_painter.drawPixmap(0, 0, left_width, top_height, full_board, 0, 0, left_width, top_height) for j in range(board_size - 2): # top middle assembled_painter.drawPixmap(left_width + j * mid_width, 0, mid_width, top_height, full_board, left_width, 0, mid_width, top_height) # top right assembled_painter.drawPixmap(final_width - right_width, 0, right_width, top_height, full_board, full_width - right_width, 0, right_width, top_height) for i in range(board_size - 2): # left middle assembled_painter.drawPixmap(0, top_height + i * mid_height, left_width, mid_height, full_board, 0, top_height, left_width, mid_height) for j in range(board_size - 2): # middle middle assembled_painter.drawPixmap(left_width + j * mid_width, top_height + i * mid_height, mid_width, mid_height, full_board, left_width, top_height, mid_width, mid_height) # right middle assembled_painter.drawPixmap(final_width - right_width, top_height + i * mid_height, right_width, mid_height, full_board, full_width - right_width, top_height, right_width, mid_height) # bottom left assembled_painter.drawPixmap(0, final_height - bottom_height, left_width, bottom_height, full_board, 0, full_height - bottom_height, left_width, bottom_height) for j in range(board_size - 2): # bottom middle assembled_painter.drawPixmap(left_width + j * mid_width, final_height - bottom_height, mid_width, bottom_height, full_board, left_width, full_height - bottom_height, mid_width, bottom_height) # bottom right assembled_painter.drawPixmap(final_width - right_width, final_height - bottom_height, right_width, bottom_height, full_board, full_width - right_width, full_height - bottom_height, right_width, bottom_height) return assembled_board
class PixmapDiffer: def __init__(self): self.name = None self.actual_pixmap = self.expected_pixmap = None self.actual = self.expected = None self.different_pixels = 0 self.diff_min_x = self.diff_min_y = None self.diff_max_x = self.diff_max_y = None self.max_diff = 0 self.names = set() self.work_dir: Path = (Path(__file__).parent.parent / 'tests' / 'pixmap_diffs') self.work_dir.mkdir(exist_ok=True) for work_file in self.work_dir.iterdir(): if work_file.name == 'README.md': continue assert work_file.suffix == '.png', work_file work_file.unlink() @contextmanager def create_painters(self, width: int, height: int, name: str, max_diff: int = 0 ) -> typing.Iterator[typing.Tuple[QPainter, QPainter]]: self.max_diff = max_diff try: yield self.start(width, height, name) finally: self.end() self.assert_equal() def start(self, width: int, height: int, name: str) -> typing.Tuple[QPainter, QPainter]: """ Create painters for the actual and expected images. Caller must either call end() or assert_equal() to properly clean up the painters and pixmaps. Caller may either paint through the returned painters, or call the end() method and create a new painter on the same device. Order matters, though! """ assert name not in self.names, f'Duplicate name: {name!r}.' self.names.add(name) self.name = name white = QColor('white') self.actual_pixmap = QPixmap(width, height) self.actual_pixmap.fill(white) self.actual = QPainter(self.actual_pixmap) self.expected_pixmap = QPixmap(width, height) self.expected_pixmap.fill(white) self.expected = QPainter(self.expected_pixmap) return self.actual, self.expected def end(self): if self.actual and self.actual.isActive(): self.actual.end() if self.expected and self.expected.isActive(): self.expected.end() def assert_equal(self): __tracebackhide__ = True self.end() self.different_pixels = 0 actual_image: QImage = self.actual.device().toImage() expected_image: QImage = self.expected.device().toImage() diff_pixmap = QPixmap(actual_image.width(), actual_image.height()) diff = QPainter(diff_pixmap) try: white = QColor('white') diff.fillRect(0, 0, actual_image.width(), actual_image.height(), white) for x in range(actual_image.width()): for y in range(actual_image.height()): actual_colour = actual_image.pixelColor(x, y) expected_colour = expected_image.pixelColor(x, y) diff.setPen( self.diff_colour(actual_colour, expected_colour, x, y)) diff.drawPoint(x, y) finally: diff.end() diff_image: QImage = diff.device().toImage() display_diff(actual_image, diff_image, expected_image, self.different_pixels) if self.different_pixels == 0: return actual_image.save(str(self.work_dir / (self.name + '_actual.png'))) expected_image.save(str(self.work_dir / (self.name + '_expected.png'))) diff_path = self.work_dir / (self.name + '_diff.png') is_saved = diff_image.save(str(diff_path)) diff_width = self.diff_max_x - self.diff_min_x + 1 diff_height = self.diff_max_y - self.diff_min_y + 1 diff_section = QImage(diff_width, diff_height, QImage.Format_RGB32) diff_section_painter = QPainter(diff_section) try: diff_section_painter.drawPixmap(0, 0, diff_width, diff_height, QPixmap.fromImage(diff_image), self.diff_min_x, self.diff_min_y, diff_width, diff_height) finally: diff_section_painter.end() # To see an image dumped in the Travis CI log, copy the text from the # log, and paste it in test_pixmap_differ.test_decode_image. print(f'Encoded image of differing section ' f'({self.diff_min_x}, {self.diff_min_y}) - ' f'({self.diff_max_x}, {self.diff_max_y}):') print(encode_image(diff_section)) message = f'Found {self.different_pixels} different pixels, ' message += f'see' if is_saved else 'could not write' message += f' {diff_path.relative_to(Path(__file__).parent.parent)}.' assert self.different_pixels == 0, message def diff_colour(self, actual_colour: QColor, expected_colour: QColor, x: int, y: int): diff_size = (abs(actual_colour.red() - expected_colour.red()) + abs(actual_colour.green() - expected_colour.green()) + abs(actual_colour.blue() - expected_colour.blue())) if diff_size <= self.max_diff: diff_colour = actual_colour.toRgb() diff_colour.setAlpha(diff_colour.alpha() // 3) return diff_colour if self.different_pixels == 0: self.diff_min_x = self.diff_max_x = x self.diff_min_y = self.diff_max_y = y else: self.diff_min_x = min(self.diff_min_x, x) self.diff_max_x = max(self.diff_max_x, x) self.diff_min_y = min(self.diff_min_y, y) self.diff_max_y = max(self.diff_max_y, y) self.different_pixels += 1 # Colour dr = 0xff dg = (actual_colour.green() + expected_colour.green()) // 5 db = (actual_colour.blue() + expected_colour.blue()) // 5 # Opacity da = 0xff return QColor(dr, dg, db, da)
def test_board_size_3(pixmap_differ: PixmapDiffer): actual: QPainter expected: QPainter with pixmap_differ.create_painters(240, 240, 'margo_board_size_3') as (actual, expected): expected_scene = QGraphicsScene(0, 0, 240, 240) full_board = MargoDisplay.load_pixmap('board-1.png') width = full_board.width() height = full_board.height() top_height = round(height * 0.28) mid_height = round(height * 0.21) bottom_height = round(height * 0.29) left_width = round(width * 0.28) mid_width = round(width * 0.22) right_width = round(width * 0.279) assembled_board = QPixmap(left_width + mid_width + right_width, top_height + mid_height + bottom_height) assembled_board.fill(Qt.transparent) assembled_painter = QPainter(assembled_board) # top left assembled_painter.drawPixmap(0, 0, left_width, top_height, full_board, 0, 0, left_width, top_height) # top middle assembled_painter.drawPixmap(left_width, 0, mid_width, top_height, full_board, left_width, 0, mid_width, top_height) # top right assembled_painter.drawPixmap(left_width + mid_width, 0, right_width, top_height, full_board, width - right_width, 0, right_width, top_height) # left middle assembled_painter.drawPixmap(0, top_height, left_width, mid_height, full_board, 0, top_height, left_width, mid_height) # middle middle assembled_painter.drawPixmap(left_width, top_height, mid_width, mid_height, full_board, left_width, top_height, mid_width, mid_height) # right middle assembled_painter.drawPixmap(left_width + mid_width, top_height, right_width, mid_height, full_board, width - right_width, top_height, right_width, mid_height) # bottom left assembled_painter.drawPixmap(0, top_height + mid_height, left_width, bottom_height, full_board, 0, height - bottom_height, left_width, bottom_height) # bottom middle assembled_painter.drawPixmap(left_width, top_height + mid_height, mid_width, bottom_height, full_board, left_width, height - bottom_height, mid_width, bottom_height) # bottom right assembled_painter.drawPixmap(left_width + mid_width, top_height + mid_height, right_width, bottom_height, full_board, width - right_width, height - bottom_height, right_width, bottom_height) assembled_painter.end() scaled_board = assembled_board.scaled(240, 240, Qt.KeepAspectRatio, Qt.SmoothTransformation) board_item = expected_scene.addPixmap(scaled_board) board_item.setPos(2, 0) white_ball = MargoDisplay.load_pixmap('ball-w-shadow-1.png', QSize(76, 76)) black_ball = MargoDisplay.load_pixmap('ball-b-shadow-1.png', QSize(76, 76)) expected_scene.addPixmap(white_ball).setPos(15, 145) expected_scene.addPixmap(black_ball).setPos(81, 79) expected_scene.render(expected) display = MargoDisplay(size=3) display.resize(348, 264) board_text = """\ A C E 5 . . . 5 3 . B . 3 1 W . . 1 A C E >B """ display.update_board(SpargoState(board_text, size=3)) render_display(display, actual)
def test_board_size_2(pixmap_differ: PixmapDiffer): actual: QPainter expected: QPainter with pixmap_differ.create_painters(240, 240, 'margo_board_size_2') as (actual, expected): expected_scene = QGraphicsScene(0, 0, 240, 240) full_board = MargoDisplay.load_pixmap('board-1.png') width = full_board.width() height = full_board.height() top_height = round(height * 0.28) left_width = round(width * 0.28) right_width = round(width * 0.279) bottom_height = round(height * 0.29) assembled_board = QPixmap(left_width + right_width, top_height + bottom_height) assembled_board.fill(Qt.transparent) assembled_painter = QPainter(assembled_board) # top left assembled_painter.drawPixmap(0, 0, left_width, top_height, full_board, 0, 0, left_width, top_height) # top right assembled_painter.drawPixmap(left_width, 0, right_width, top_height, full_board, width - right_width, 0, right_width, top_height) # bottom left assembled_painter.drawPixmap(0, top_height, left_width, bottom_height, full_board, 0, height - bottom_height, left_width, bottom_height) # bottom right assembled_painter.drawPixmap(left_width, top_height, right_width, bottom_height, full_board, width - right_width, height - bottom_height, right_width, bottom_height) assembled_painter.end() scaled_board = assembled_board.scaled(232, 240, Qt.KeepAspectRatio, Qt.SmoothTransformation) board_item = expected_scene.addPixmap(scaled_board) board_item.setPos(4, 0) white_ball = MargoDisplay.load_pixmap('ball-w-shadow-1.png', QSize(103, 103)) black_ball = MargoDisplay.load_pixmap('ball-b-shadow-1.png', QSize(103, 103)) expected_scene.addPixmap(white_ball).setPos(23, 108) expected_scene.addPixmap(black_ball).setPos(113, 18) expected_scene.render(expected) display = MargoDisplay(size=2) trigger_resize(display, 292, 240) display.resize(348, 264) board_text = """\ A C 3 . B 3 1 W . 1 A C >B """ display.update_board(SpargoState(board_text, size=2)) render_display(display, actual)
def setupMenu(self) -> None: self.setMenuBar(QMenuBar(self)) settings = QSettings() # mods menu menuMods: QMenu = self.menuBar().addMenu('&Mods') downIcon = QIcon(str(getRuntimePath('resources/icons/down.ico'))) gearIcon = QIcon(str(getRuntimePath('resources/icons/gear.ico'))) dirsIcon = QIcon(str( getRuntimePath('resources/icons/open-folder.ico'))) colrIcon = QIcon( str(getRuntimePath('resources/icons/color-circle.ico'))) smilIcon = QIcon(str(getRuntimePath('resources/icons/smile.ico'))) actionAddModFromFile = menuMods.addAction('&Add Mods') actionAddModFromFile.triggered.connect(self.showAddModFromFileDialog) actionAddModFromFolder = menuMods.addAction('Add u&npacked Mod') actionAddModFromFolder.triggered.connect( self.showAddModFromFolderDialog) actionDownloadMod = menuMods.addAction('&Download Mod') actionDownloadMod.setIcon(downIcon) actionDownloadMod.triggered.connect(self.showDownloadModDialog) menuMods.addSeparator() actionGetInfo = menuMods.addAction('Update Mod de&tails') actionGetInfo.setIcon(downIcon) actionGetInfo.triggered.connect(self.showGetInfoDialog) actionGetUpdates = menuMods.addAction('Check for Mod &updates') actionGetUpdates.setIcon(downIcon) actionGetUpdates.triggered.connect(self.showGetUpdatesDialog) menuMods.addSeparator() actionExport = menuMods.addAction('&Export Modlist') actionExport.triggered.connect(self.showExportDialog) menuMods.aboutToShow.connect(lambda: [ actionDownloadMod.setDisabled(not str(settings.value('nexusAPIKey'))), actionGetInfo.setDisabled( not str(settings.value('nexusAPIKey')) or \ not len(self.model) or \ not self.mainwidget.modlist.selectionModel().hasSelection()), actionGetUpdates.setDisabled( not str(settings.value('nexusAPIKey')) or \ not len(self.model) or \ not self.mainwidget.modlist.selectionModel().hasSelection()), actionExport.setDisabled(not len(self.model)) ]) # view menu menuView: QMenu = self.menuBar().addMenu('&View') showSummary = menuView.addAction('Show &Summary') showSummary.setCheckable(True) showSummary.setChecked(settings.value('showSummary', 'True') == 'True') showSummary.triggered.connect(lambda checked: [ settings.setValue('showSummary', str(checked)), self.mainwidget.summary.setVisible(checked) ]) menuView.addSeparator() toggleHighlightNewest = menuView.addAction('Highlight &Newest') toggleHighlightNewest.setCheckable(True) toggleHighlightNewest.setChecked( settings.value('highlightNewest', 'True') == 'True') toggleHighlightNewest.triggered.connect(lambda checked: [ settings.setValue('highlightNewest', str(checked)), self.model.updateCallbacks.fire(self.model) ]) iconHighlightNewest = QPixmap(256, 256) iconHighlightNewest.fill(Qt.transparent) painter = QPainter(iconHighlightNewest) painter.setBrush(QBrush(QColor(222, 255, 222))) painter.drawEllipse(10, 10, 236, 236) toggleHighlightNewest.setIcon(QIcon(iconHighlightNewest)) painter.end() toggleHighlightRecent = menuView.addAction('Highlight &Recent') toggleHighlightRecent.setCheckable(True) toggleHighlightRecent.setChecked( settings.value('highlightRecent', 'True') == 'True') toggleHighlightRecent.triggered.connect(lambda checked: [ settings.setValue('highlightRecent', str(checked)), self.model.updateCallbacks.fire(self.model) ]) iconHighlightRecent = QPixmap(256, 256) iconHighlightRecent.fill(Qt.transparent) painter = QPainter(iconHighlightRecent) painter.setBrush(QBrush(QColor(222, 226, 255))) painter.drawEllipse(10, 10, 236, 236) toggleHighlightRecent.setIcon(QIcon(iconHighlightRecent)) painter.end() toggleHighlightUnmanaged = menuView.addAction('Highlight &Unmanaged') toggleHighlightUnmanaged.setCheckable(True) toggleHighlightUnmanaged.setChecked( settings.value('highlightUnmanaged', 'True') == 'True') toggleHighlightUnmanaged.triggered.connect(lambda checked: [ settings.setValue('highlightUnmanaged', str(checked)), self.model.updateCallbacks.fire(self.model) ]) iconHighlightUnmanaged = QPixmap(256, 256) iconHighlightUnmanaged.fill(Qt.transparent) painter = QPainter(iconHighlightUnmanaged) painter.setBrush(QBrush(QColor(250, 220, 220))) painter.drawEllipse(10, 10, 236, 236) toggleHighlightUnmanaged.setIcon(QIcon(iconHighlightUnmanaged)) painter.end() toggleHighlightDisabled = menuView.addAction('Highlight &Disabled') toggleHighlightDisabled.setCheckable(True) toggleHighlightDisabled.setChecked( settings.value('highlightDisabled', 'True') == 'True') toggleHighlightDisabled.triggered.connect(lambda checked: [ settings.setValue('highlightDisabled', str(checked)), self.model.updateCallbacks.fire(self.model) ]) iconHighlightDisabled = QPixmap(256, 256) iconHighlightDisabled.fill(Qt.transparent) painter = QPainter(iconHighlightDisabled) painter.setBrush(QBrush(QColor(230, 230, 230))) painter.drawEllipse(10, 10, 236, 236) toggleHighlightDisabled.setIcon(QIcon(iconHighlightDisabled)) painter.end() menuView.addSeparator() toggleColors = menuView.addAction('&Colored Icons') toggleColors.setCheckable(True) toggleColors.setChecked(settings.value('iconColors', 'True') == 'True') toggleColors.triggered.connect(lambda checked: [ settings.setValue('iconColors', str(checked)), self.mainwidget.modlist.listmodel.setIcons(), self.model.updateCallbacks.fire(self.model) ]) toggleColors.setIcon(colrIcon) menuView.addSeparator() toggleCompact = menuView.addAction('Compact &Mode') toggleCompact.setCheckable(True) toggleCompact.setChecked( settings.value('compactMode', 'False') == 'True') toggleCompact.triggered.connect(lambda checked: [ settings.setValue('compactMode', str(checked)), self.mainwidget.modlist.setSectionSize(checked) ]) # settings menu menuSettings: QMenu = self.menuBar().addMenu('&Tools') actionSettings = menuSettings.addAction('&Settings') actionSettings.setIcon(gearIcon) actionSettings.triggered.connect(self.showSettingsDialog) menuSettings.addSeparator() actionOpenGameDirectory = menuSettings.addAction( 'Open &Game directory') actionOpenGameDirectory.setIcon(dirsIcon) actionOpenGameDirectory.triggered.connect( lambda: util.openDirectory(self.model.gamepath)) actionOpenConfigDirectory = menuSettings.addAction( 'Open &Config directory') actionOpenConfigDirectory.setIcon(dirsIcon) actionOpenConfigDirectory.triggered.connect( lambda: util.openDirectory(self.model.configpath)) # info menu menuInfo: QMenu = self.menuBar().addMenu('&Info') actionFeedback = menuInfo.addAction('Send &Feedback') actionFeedback.setIcon(smilIcon) actionFeedback.triggered.connect( lambda: QDesktopServices.openUrl(QUrl(w3modmanager.URL_ISSUES))) menuInfo.addSeparator() actionAbout = menuInfo.addAction('&About') actionAbout.setIcon(QIcon.fromTheme('document-open')) actionAbout.triggered.connect(self.showAboutDialog)