def store_file(self, id, file): id = id.replace('/', '_') directory = ApplicationData.get('images') filename = os.path.join(directory, id + '.png') if filename == os.path.normpath(file): return self.iconmap.get(id, None) makedirs(directory) pixmap = QPixmap() if file is not None and pixmap.load(file): if pixmap.size().width() > self.max_size or pixmap.size().height( ) > self.max_size: pixmap = pixmap.scaled(self.max_size, self.max_size, Qt.KeepAspectRatio, Qt.SmoothTransformation) buffer = QBuffer() pixmap.save(buffer, 'png') data = str(buffer.data()) with open(filename, 'wb') as f: f.write(data.encode()) icon = QIcon(pixmap) icon.filename = filename icon.content = data icon.content_type = 'image/png' else: unlink(filename) icon = None self.iconmap[id] = icon return icon
def show_splash(version=''): splash_fname = utils.get_resource_path('icons/splash.jpg') splash_pix = QPixmap(splash_fname) size = splash_pix.size()*.35 splash_pix = splash_pix.scaled(size, Qt.KeepAspectRatio, transformMode=Qt.SmoothTransformation) numbers = {} for number in list(range(10)) + ['point']: fname = utils.get_resource_path('icons/{}.png'.format(number)) pix = QPixmap(fname) size = pix.size() * .65 numbers[str(number)] = pix.scaled(size, Qt.KeepAspectRatio, transformMode=Qt.SmoothTransformation) numbers['.'] = numbers['point'] painter = QPainter(splash_pix) painter.begin(splash_pix) x, y = 470, 70 for digit in version: painter.drawPixmap(x, y, numbers[digit]) x += numbers[digit].rect().width()/3 painter.end() splash = QSplashScreen(splash_pix, Qt.WindowStaysOnBottomHint) splash.show() return splash
def init(self, ddir): """加载lolita.dat配置""" if not ddir or not isinstance(ddir, str): return try: if PY3: conf = json.loads(open(ddir + "/lolita.dat", "rb").read().decode()) else: conf = json.loads(open(ddir + "/lolita.dat", "rb").read(), "utf-8") normal = conf.get("normal", "").format(DATA_DIR = ddir) move = conf.get("move", "").format(DATA_DIR = ddir) hover = conf.get("hover", "").format(DATA_DIR = ddir) press = conf.get("press", "").format(DATA_DIR = ddir) image = QPixmap(normal) self.resize(image.size()) # 设置窗口的大小 self.setMinimumSize(image.size()) self.setMaximumSize((image.size())) self.movies = { "normal": image, # 普通 "move": QPixmap(move), # 移动 "hover": QMovie(hover), # 悬停(会动) "press": QPixmap(press), # 按下 } self._currentImage = image # 当前的图形 self.update() # 声音播放列表 playList = conf.get("playList", []) self.player = LolitaPlayer(playList, ddir) self.player.setCurrentIndex(0) except Exception as e: self.close() traceback.print_exc(e)
def store_data(self, id, data): id = id.replace('/', '_') directory = ApplicationData.get('images') filename = os.path.join(directory, id + '.png') makedirs(directory) pixmap = QPixmap() if data is not None and pixmap.loadFromData(data): image_size = pixmap.size() if image_size.width() > self.max_size or image_size.height() > self.max_size: pixmap = pixmap.scaled(self.max_size, self.max_size, Qt.KeepAspectRatio, Qt.SmoothTransformation) if imghdr.what(None, data) != 'png' or pixmap.size() != image_size: buffer = QBuffer() pixmap.save(buffer, 'png') data = str(buffer.data()) with open(filename, 'wb') as f: f.write(data) icon = QIcon(pixmap) icon.filename = filename icon.content = data icon.content_type = 'image/png' else: unlink(filename) icon = None self.iconmap[id] = icon return icon
def store_file(self, id, file): id = id.replace('/', '_') directory = ApplicationData.get('images') filename = os.path.join(directory, id + '.png') if filename == os.path.normpath(file): return self.iconmap.get(id, None) makedirs(directory) pixmap = QPixmap() if file is not None and pixmap.load(file): if pixmap.size().width() > self.max_size or pixmap.size().height() > self.max_size: pixmap = pixmap.scaled(self.max_size, self.max_size, Qt.KeepAspectRatio, Qt.SmoothTransformation) buffer = QBuffer() pixmap.save(buffer, 'png') data = str(buffer.data()) with open(filename, 'wb') as f: f.write(data) icon = QIcon(pixmap) icon.filename = filename icon.content = data icon.content_type = 'image/png' else: unlink(filename) icon = None self.iconmap[id] = icon return icon
def store_data(self, id, data): id = id.replace('/', '_') directory = ApplicationData.get('images') filename = os.path.join(directory, id + '.png') makedirs(directory) pixmap = QPixmap() if data is not None and pixmap.loadFromData(data): image_size = pixmap.size() if image_size.width() > self.max_size or image_size.height( ) > self.max_size: pixmap = pixmap.scaled(self.max_size, self.max_size, Qt.KeepAspectRatio, Qt.SmoothTransformation) if imghdr.what(None, data) != 'png' or pixmap.size() != image_size: buffer = QBuffer() pixmap.save(buffer, 'png') data = str(buffer.data()) with open(filename, 'wb') as f: f.write(data.encode()) icon = QIcon(pixmap) icon.filename = filename icon.content = data icon.content_type = 'image/png' else: unlink(filename) icon = None self.iconmap[id] = icon return icon
def initUI(self, gameboard): self.setFixedSize((gameboard.width - 1) * blockSize, 40) moneypix = QPixmap(os.path.join('./Pictures/', "money.png")) moneyLabel = QLabel(self) moneyLabel.setPixmap(moneypix) moneyLabel.setFixedSize(moneypix.size()) moneyLabel.move(5, 10) moneyLabel.show() wavepix = QPixmap(os.path.join('./Pictures/', "waves.png")) waveLabel = QLabel(self) waveLabel.setPixmap(wavepix) waveLabel.setFixedSize(wavepix.size()) waveLabel.move((gameboard.width - 1) * blockSize / 2 - 82, 8) waveLabel.show() self.hearts = [] heart = QPixmap(os.path.join('./Pictures/', 'heart.png')) self.heart_lost = QPixmap(os.path.join('./Pictures/', 'heart_lost.png')) i = 1 while i <= self.parent.gameboard.startingLives: heartLabel = QLabel(self) heartLabel.setPixmap(heart) self.hearts.append([True, heartLabel]) heartLabel.move((gameboard.width - 1) * blockSize - (2 + i * 12), 18) heartLabel.show() i += 1 self.show()
class ButtonPixmap(QGraphicsObject): clicked = pyqtSignal(str,str) def __init__(self, pix,name=None): super(ButtonPixmap, self).__init__() self.p = QPixmap(pix) self.name = name def paint(self, painter, option, widget): painter.drawPixmap(QPointF(), self.p) def boundingRect(self): return QRectF(QPointF(0, 0), QSizeF(self.p.size())) def mousePressEvent(self, event): #print("鼠标按下:::",self.name) self.clicked.emit(self.name,"press") event.accept() def mouseReleaseEvent(self, event): #print("鼠标释放:::",self.name) self.clicked.emit(self.name,"release") event.accept() def setGeometry(self, rect): super(ButtonPixmap, self).setGeometry(rect) if rect.size().width() > self.p.size().width(): self.p = self.p.scaled(rect.size().toSize()) else: self.p = QPixmap(self.p)
def initUI(self, gameboard): self.setFixedSize((gameboard.width - 1)*blockSize, 40) moneypix = QPixmap(os.path.join('./Pictures/', "money.png")) moneyLabel = QLabel(self) moneyLabel.setPixmap(moneypix) moneyLabel.setFixedSize(moneypix.size()) moneyLabel.move(5, 10) moneyLabel.show() wavepix = QPixmap(os.path.join('./Pictures/', "waves.png")) waveLabel = QLabel(self) waveLabel.setPixmap(wavepix) waveLabel.setFixedSize(wavepix.size()) waveLabel.move((gameboard.width - 1)*blockSize / 2 - 82, 8) waveLabel.show() self.hearts = [] heart = QPixmap(os.path.join('./Pictures/', 'heart.png')) self.heart_lost = QPixmap(os.path.join('./Pictures/', 'heart_lost.png')) i = 1 while i <= self.parent.gameboard.startingLives: heartLabel = QLabel(self) heartLabel.setPixmap(heart) self.hearts.append([True, heartLabel]) heartLabel.move((gameboard.width - 1)*blockSize - (2 + i * 12), 18) heartLabel.show() i += 1 self.show()
def _download_img(self, idx: int, url: str): self.logger.info('Downloading image [{}] from {}'.format(idx, url)) res = self.http_client.get(url) if res: if not res.content: self.logger.warning('Image [{}] from {} has no content'.format( idx, url)) self.loaded_imgs.append( self.i18n['screenshots.download.no_content']) self._load_img() else: self.logger.info( 'Image [{}] successfully downloaded'.format(idx)) pixmap = QPixmap() pixmap.loadFromData(res.content) if pixmap.size().height() > self.MAX_HEIGHT or pixmap.size( ).width() > self.MAX_WIDTH: pixmap = pixmap.scaled(self.MAX_WIDTH, self.MAX_HEIGHT, Qt.KeepAspectRatio, Qt.SmoothTransformation) self.loaded_imgs.append(pixmap) if self.img_idx == idx: self._load_img() else: self.logger.info("Could not retrieve image [{}] from {}".format( idx, url)) self.loaded_imgs.append( self.i18n['screenshots.download.no_response']) self._load_img()
def __init__(self, theme): QWidget.__init__(self) self.theme = theme self.themename = theme.name self.setMaximumWidth(400) picurl = ":/images/theme.png" self.url = theme.sample_url if theme.sample_pic: picurl = theme.sample_pic pic = FlatButton(picurl, picurl) map = QPixmap(400, 200) p = QPainter(map) p.setRenderHint(QPainter.Antialiasing) p.setRenderHint(QPainter.TextAntialiasing) p.drawImage(QRect(0, 0, 400, 200), QImage(picurl)) p.fillRect(0, 0, map.size().width(), map.size().height(), QColor(69, 187, 230, 125)) if self.url: w = 100 h = 30 p.fillRect(QRect((400 - w) / 2, (200 - h) / 2, w, h), QColor(69, 187, 230, 255)) p.drawRoundedRect(QRect((400 - w) / 2, (200 - h) / 2, w, h), 5, 5) font = QFont() font.setFamily("Arial") font.setBold(True) font.setPixelSize(20) p.setFont(font) p.setPen(QPen(Qt.black)) p.drawText(QRectF(0, 0, 400, 200), "PREVIEW", QTextOption(Qt.AlignHCenter | Qt.AlignVCenter)) pic.clicked.connect(self.clicked) else: pic.setCursor(Qt.ArrowCursor) del p pic.setHoverPixmap(map) pic.setMaximumSize(400, 200) pic.setScaledContents(True) if theme.aktiv: name = QLabel(theme.name.upper() + " (Aktiv)") else: name = QLabel(theme.name.upper()) fnt = name.font() fnt.setPointSize(13) fnt.setBold(True) name.setFont(fnt) layout = QGridLayout() layout.addWidget(pic, 0, 0, 1, 2) layout.addWidget(name, 1, 0) if not theme.aktiv: activate = QPushButton("Activate") layout.addWidget(activate, 1, 1, 1, 1, Qt.AlignRight) activate.clicked.connect(self.activate) self.setLayout(layout)
def _pixmap(self, dataBytes, h=200, w=150): pixmap = QPixmap() pixmap.loadFromData(dataBytes) if h is None or w is None: return pixmap height = pixmap.size().height() width = pixmap.size().width() if width == 0 or height == 0: return pixmap return pixmap.scaled(w, h, Qt.IgnoreAspectRatio, Qt.SmoothTransformation)
def set_image(self, img: QPixmap = None): if img is None: self.view_img.clear() return w = img.size().width() h = img.size().height() if w > h: pix = img.scaledToWidth(60) else: pix = img.scaledToHeight(60) self.view_img.setPixmap(pix)
def set_Icon(self, img): if img != "": pixmap = QPixmap() ret = pixmap.load(img) if not ret: qDebug(f"{img}加载图片失败") self.setFixedSize(pixmap.size()) self.setStyleSheet("QPushButton{border:0px;}") self.setIcon(QIcon(pixmap)) self.setIconSize(pixmap.size())
def update_background(self, image_path): #Update Background Picture self.scene.clear() pixMap = QPixmap(image_path) self.scene.addPixmap(pixMap) h = pixMap.size().height() w = pixMap.size().width() self.ui.graphic_background.setScene(self.scene) self.ui.graphic_background.fitInView(QRectF(0, 0, w, h), #Qt.KeepAspectRatio ) self.scene.update()
def init_gui(self): game_over_png = QPixmap('../pommerman/resources/game_over.png') self.mainframe.setPixmap(game_over_png) self.setGeometry(200, 200, game_over_png.size().width(), game_over_png.size().height()) self.setWindowTitle('Game Over') continue_png = QPixmap('../pommerman/resources/continue.png') save_png = QPixmap('../pommerman/resources/save.png') create_button(self.continue_button, continue_png, 172, 355) create_button(self.save_button, save_png, 173, 393) self.continue_button.clicked.connect(self.continue_action) self.save_button.clicked.connect(self.save)
def update_pixmap(self, file_name): pixmap = QPixmap(file_name) # Scale the pixmap if it's too large screen_avail = QDesktopWidget().availableGeometry() max_width = int(screen_avail.width() * 0.8) max_height = int(screen_avail.height() * 0.8) if pixmap.size().width() > max_width: pixmap = pixmap.scaledToWidth(max_width) if pixmap.size().height() > max_height: pixmap = pixmap.scaledToHeight(max_height) # Update the center widget with the pixmap self.lbl_img.setPixmap(pixmap) self.center()
class AnimeWaiting(QLabel): def __init__(self, parent) -> None: super().__init__(parent) self.setScaledContents(True) self.bgpix = QPixmap('等待.svg') self.setPixmap(self.bgpix) timer = QTimer(self) timer.timeout.connect(self.update_func) timer.start(10) self.rotate_angle = 0 def update_func(self): self.rotate_angle += 1 if self.rotate_angle == 360: self.rotate_angle = 0 self.update() def paintEvent(self, event: QPaintEvent) -> None: painter = QPainter(self) sz = self.bgpix.size() w, h = sz.width(), sz.height() painter.translate(w / 2, h / 2) painter.rotate(self.rotate_angle) painter.translate(-w / 2, -h / 2) painter.drawPixmap(0, 0, w, h, self.bgpix)
def initUI(self): self.setFixedSize(400, 260) self.setWindowTitle('Fomoire!') fomoire = QPixmap(os.path.join('./Pictures/', "fomoire.png")) logo = QLabel(self) logo.setPixmap(fomoire) logo.setFixedSize(fomoire.size()) logo.move(400 / 2 - fomoire.width() / 2, 15) towerDefence = QLabel('A Tower Defence Game', self) towerDefence.move(130, 66) selectMap = QLabel('Select map:', self) selectMap.move(50, 105) mapList = QComboBox(self) mapList.addItem('No map selected') for mapFile in os.listdir('./Maps/'): mapList.addItem(mapFile) mapList.move(135, 102) mapList.activated[str].connect(self.selectMap) setSpeed = QLabel('Set game speed:', self) setSpeed.move(50, 140) slow = QLabel('Slow', self) slow.move(170, 140) slider = QSlider(Qt.Horizontal, self) slider.setFocusPolicy(Qt.NoFocus) slider.setSliderPosition(100 - self.gameSpeed) slider.setGeometry(210, 140, 100, 20) slider.valueChanged[int].connect(self.changeGameSpeed) fast = QLabel('Fast', self) fast.move(325, 140) start = QPushButton('Start game!', self) start.move(145, 175) start.clicked[bool].connect(self.startGame) quitButton = QPushButton('Quit', self) quitButton.move(168, 210) quitButton.clicked[bool].connect(qApp.quit) barbarian = QLabel(self) brbr = QPixmap(os.path.join('./Pictures/', "barbarian.png")) barbarian.setPixmap(brbr) barbarian.move(70, 185) berserker = QLabel(self) berber = QPixmap(os.path.join('./Pictures/', "berserker_left.png")) berserker.setPixmap(berber) berserker.move(290, 185) self.show()
class Gui(QWidget): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.layout = QVBoxLayout() self.img_label = QLabel() self.image = QPixmap(":/ww_banner.png") size = self.image.size() width = math.floor(size.width() * 0.33) height = math.floor(size.height() * 0.33) self.image = self.image.scaled(width, height, QtCore.Qt.KeepAspectRatio) self.img_label.setPixmap(self.image) self.text_label = QLabel( "Talvisota - Winter War launcher. This launcher is ran minimized " "to track Steam play time. You may close this window or leave it open.\r\n\r\n" "Please note that the standard Rising Storm 2: Vietnam main menu and " "server browser are used for Talvisota - Winter War." ) self.text_label.setWordWrap(True) self.text_label.setTextInteractionFlags(QtCore.Qt.TextBrowserInteraction) self.layout.addWidget(self.img_label) self.layout.addWidget(self.text_label) self.setLayout(self.layout) self.setWindowFlags(QtCore.Qt.MSWindowsFixedSizeDialogHint) self.setWindowTitle("Talvisota - Winter War") self.setWindowIcon(QIcon(":/ww_icon.ico")) self.setWindowState(QtCore.Qt.WindowMinimized) def warn(self, title: str, msg: str): QMessageBox.warning(self, title, msg)
def loadDataFromFile(self, filePath, which, point, tmpPoint = None, newPoint = None): pixmap = QPixmap(filePath) self.size = pixmap.size() painter = QPainter(pixmap) painter.drawPixmap(pixmap.rect(), pixmap) if point is not None: pen = QPen(QtCore.Qt.red, 5) painter.setPen(pen) painter.setBrush(QtCore.Qt.red) for n in point: painter.drawEllipse(QPointF(n[0], n[1]), 10, 10) if tmpPoint != None: painter.setPen(QPen(QtCore.Qt.green, 5)) painter.setBrush(QtCore.Qt.green) painter.drawEllipse(QPointF(tmpPoint[0], tmpPoint[1]), 10, 10) if point == self.leftPoint: self.tmpLDotPic = pixmap else: self.tmpRDotPic = pixmap if newPoint != None: painter.setPen(QPen(QtCore.Qt.blue, 5)) painter.setBrush(QtCore.Qt.blue) for n in newPoint: painter.drawEllipse(QPointF(n[0], n[1]), 10, 10) if point == self.leftPoint: self.leftDot = pixmap if point == self.rightPoint: self.rightDot = pixmap del painter which.setPixmap(pixmap) which.setScaledContents(True) if self.Left & self.Right: self.loadedState()
def mousePressEvent(self, e: QtGui.QMouseEvent) -> None: item = self.itemAt(e.pos()) if item is None: # if not clicking on item, do nothing return if e.button() == Qt.RightButton: self.takeItem(self.row(item)) self.parentWidget().parent().listWidget.addItem(item) pass else: rect = self.visualItemRect(item) itemPos = self.mapToParent(QPoint(rect.x(), rect.y())) filename = item.filename label = MovableLabel(self.window, filename) pixmap = QPixmap('Players/' + filename).scaledToHeight( 100, QtCore.Qt.SmoothTransformation) label.setPixmap(pixmap) label.setFixedSize(pixmap.size()) label.move(itemPos) label.grabMouse() label.oldPos = itemPos label.clicked = False label.show() label.ogIndex = self.row(item) self.takeItem(self.row(item))
def __init__(self, painting_model: PaintingModel, painting_controller: PaintingController): super().__init__() self.model = painting_model self.controller = painting_controller trump_painting = QPixmap( resource_filename('design.ui', 'resources/donald-trump.png') ) self.scene_img = None pixmap_size = trump_painting.size() self.setMinimumSize(pixmap_size) self._height_for_width_factor = 1.0 * pixmap_size.height() / pixmap_size.width() self.grid_layout = QtWidgets.QGridLayout(self) self.grid_layout.setContentsMargins(0, 0, 0, 0) self.painting_view = QtWidgets.QGraphicsView(self) self.painting_view.setResizeAnchor(0) self.painting_view.setAlignment(Qt.AlignLeft | Qt.AlignTop) self.painting_scene = QtWidgets.QGraphicsScene() self.painting_view.setScene(self.painting_scene) self.grid_layout.addWidget(self.painting_view, 0, 0) self.painting_view.setRenderHint(QtGui.QPainter.Antialiasing) self.painting_view.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.painting_view.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.radius = 5 self.painting_scene.addPixmap(trump_painting) self._painting_graphics = QGraphicsView() self.model.subscribe_update_function(self.update_world_image)
def launch_main(xml_fname=None, introspect_fname=None): app = QApplication(sys.argv) import time start = time.time() splash_fname = utils.get_resource_path('icons/splash.jpg') splash_pix = QPixmap(splash_fname) size = splash_pix.size()*.35 splash_pix = splash_pix.scaled(size, Qt.KeepAspectRatio, transformMode=Qt.SmoothTransformation) # # below makes the pixmap half transparent painter = QPainter(splash_pix) painter.setCompositionMode(painter.CompositionMode_DestinationAtop) painter.end() splash = QSplashScreen(splash_pix, Qt.WindowStaysOnTopHint) splash.show() app.processEvents() time.sleep(2) app.processEvents() mdwiz = PyMdWizardMainForm() mdwiz.show() splash.finish(mdwiz) if xml_fname is not None and os.path.exists(xml_fname): mdwiz.open_file(xml_fname) if introspect_fname is not None and os.path.exists(introspect_fname): mdwiz.metadata_root.eainfo.detaileds[0].populate_from_fname(introspect_fname) mdwiz.metadata_root.eainfo.ui.fgdc_eainfo.setCurrentIndex(1) app.exec_()
class ShapeWidget(QWidget): def __init__(self, parent=None): super(ShapeWidget, self).__init__(parent) self.i = 1 self.mypix() self.timer = QTimer() self.timer.setInterval(500) # 500毫秒 self.timer.timeout.connect(self.timeChange) self.timer.start() # 顯示不規則 pic def mypix(self): self.update() if self.i == 5: self.i = 1 self.mypic = { 1: './images/left.png', 2: "./images/up.png", 3: './images/right.png', 4: './images/down.png' } self.pix = QPixmap( self.mypic[self.i], "0", Qt.AvoidDither | Qt.ThresholdDither | Qt.ThresholdAlphaDither) self.resize(self.pix.size()) self.setMask(self.pix.mask()) self.dragPosition = None def mousePressEvent(self, event): if event.button() == Qt.LeftButton: self.m_drag = True self.m_DragPosition = event.globalPos() - self.pos() event.accept() self.setCursor(QCursor(Qt.OpenHandCursor)) def mouseMoveEvent(self, QMouseEvent): if Qt.LeftButton and self.m_drag: self.move(QMouseEvent.globalPos() - self.m_DragPosition) QMouseEvent.accept() def mouseReleaseEvent(self, QMouseEvent): self.m_drag = False self.setCursor(QCursor(Qt.ArrowCursor)) def paintEvent(self, event): painter = QPainter(self) painter.drawPixmap(0, 0, self.pix.width(), self.pix.height(), self.pix) # 按兩下滑鼠事件 def mouseDoubleClickEvent(self, event): if event.button() == 1: self.i += 1 self.mypix() # 每500毫秒修改paint def timeChange(self): self.i += 1 self.mypix()
def __init__(self, vertices_model: VerticesModel, vertices_controller: VerticesController): super().__init__() self.model = vertices_model self.controller = vertices_controller trump_painting = QPixmap( resource_filename('design.ui', 'resources/donald-trump.png')) self.scene_img = None pixmap_size = trump_painting.size() self.setMinimumSize(pixmap_size) self._height_for_width_factor = 1.0 * pixmap_size.height( ) / pixmap_size.width() self.path_lines_pen = QtGui.QPen(QColor('#f44280'), 5, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin) self.path_points_pen = QtGui.QPen(QColor('#95ff95'), 10) self.grid_layout = QtWidgets.QGridLayout(self) self.grid_layout.setContentsMargins(0, 0, 0, 0) self.vertices_view = QtWidgets.QGraphicsView(self) self.vertices_view.setResizeAnchor(0) self.vertices_view.setAlignment(Qt.AlignLeft | Qt.AlignTop) self.vertices_scene = QtWidgets.QGraphicsScene() self.vertices_view.setScene(self.vertices_scene) self.grid_layout.addWidget(self.vertices_view, 0, 0) self.vertices_view.setRenderHint(QtGui.QPainter.Antialiasing) self.vertices_view.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.vertices_view.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.radius = 5 self.vertices_scene.addPixmap(trump_painting) self._painting_graphics = QGraphicsView() self.model.subscribe_update_function(self.draw_path)
def __init__(self, pixmap, overlay=None, grayout=False): super(CompositePixmap, self).__init__() base_pixmap = QPixmap(pixmap) if grayout: painter = QPainter(base_pixmap) painter.setCompositionMode(painter.CompositionMode_SourceIn) painter.fillRect(base_pixmap.rect(), QColor(128, 128, 128, 128)) painter.end() if overlay: width = int(base_pixmap.size().width() / 2) height = int(base_pixmap.size().height() / 2) overlay_pixmap = QPixmap(overlay).scaled(width, height) painter = QPainter(base_pixmap) painter.drawPixmap(width, height, overlay_pixmap) painter.end() self.swap(base_pixmap)
class Chessman(QLabel): """ 棋子类 """ def __init__(self, color: str, parent=None): assert color == 'w' or color == 'b' super().__init__(parent=parent) self.color = color self.pic = None if color == 'w': self.pic = QPixmap("./sources/white.png") else: self.pic = QPixmap("./sources/black.png") self.setFixedSize(self.pic.size()) self.setPixmap(self.pic) def move_chessman(self, board, coord: QtCore.QPoint): x, y = coord.x(), coord.y() x0 = (x - PIVOT[0]) // (BORDER_SIZE + GRID_SIZE) y0 = (y - PIVOT[1]) // (BORDER_SIZE + GRID_SIZE) if x0 < 8 and y0 < 8: pic_x = PIVOT[0] + x0 * (BORDER_SIZE + GRID_SIZE) + BORDER_SIZE pic_y = PIVOT[1] + y0 * (BORDER_SIZE + GRID_SIZE) + BORDER_SIZE super().move(pic_x, pic_y) board.board[y0][x0] = self def reverse(self): self.color = 'w' if self.color == 'b' else 'b' if self.color == 'w': self.pic = QPixmap("./sources/white.png") else: self.pic = QPixmap("./sources/black.png") self.setPixmap(self.pic)
def _printLabel(self): # self.label.setText(self.__args[5]) if self.label.setObjectName=='Item_Name'else self.label.setText('') dialog = QPrintDialog(self.__printer, self) #choose printer if not dialog.exec_(): return for o in self.__oL: #set value to label # print(o.objectName()) if o.objectName() == 'ItemCode': o.setText(self.__args[1]) if o.objectName() == 'Barcode': o.setBarcode(self.__args[2]) if o.objectName() == 'ItemBarcode': o.setText(self.__args[2]) if o.objectName() == 'PLU': o.setText(self.__args[3]) if o.objectName() == 'ItemName': o.setText(self.__args[4]) # for s in self.__args: # print(s) painter = QPainter(self.__printer) image = QPixmap() image = self.grab( QRect(QPoint(0, 0), QSize(self.size().width(), self.size().height()))) # /* 绘制窗口至画布 */ rect = painter.viewport() size = image.size() size.scale(rect.size(), Qt.KeepAspectRatio) painter.setViewport(rect.x(), rect.y(), size.width(), size.height()) painter.setWindow(image.rect()) painter.drawPixmap(0, 0, image)
def initColour(self, colourKey, button, prefMethod, byName=False, hasAlpha=False): """ Public method to initialize a colour selection button. @param colourKey key of the colour resource (string) @param button reference to a button to show the colour on (QPushButton) @param prefMethod preferences method to get the colour @keyparam byName flag indicating to retrieve/save by colour name (boolean) @keyparam hasAlpha flag indicating to allow alpha channel (boolean) """ colour = QColor(prefMethod(colourKey)) size = button.size() pm = QPixmap(size.width() / 2, size.height() / 2) pm.fill(colour) button.setIconSize(pm.size()) button.setIcon(QIcon(pm)) button.setProperty("colorKey", colourKey) button.setProperty("hasAlpha", hasAlpha) button.clicked.connect(self.__selectColourSlot) self.__coloursDict[colourKey] = [colour, byName]
class PuyoView(QAbstractButton): rightclick = pyqtSignal() leftclick = pyqtSignal() def __init__(self, image, opacity, parent=None): super().__init__(parent) self.setFocusPolicy(Qt.NoFocus) self.setGraphic(image, opacity) def mousePressEvent(self, event): if event.button() == Qt.LeftButton: self.leftclick.emit() if event.button() == Qt.RightButton: self.rightclick.emit() def setGraphic(self, image, opacity): height, width, channel = image.shape qimg = QImage( image.tobytes(), width, height, channel * width, QImage.Format_RGBA8888, ) self.image = QPixmap(qimg) self.opacity = opacity self.update() def paintEvent(self, _): painter = QPainter(self) painter.setOpacity(self.opacity) painter.drawPixmap(self.rect(), self.image) def sizeHint(self): return self.image.size()
def __init__(self, pixmap, text, size=0.5, corner=BottomRight): super().__init__() base_pixmap = QPixmap(pixmap) base_size = base_pixmap.size() base_max = min(base_size.height(), base_size.width()) if not base_max: # Because gridsync.gui.systray.animation.currentPixmap() returns # a blank pixmap when unpausing the animation for the first time. # Returning early to prevents QPainter from spewing warnings. self.swap(base_pixmap) return badge_max = base_max * size pen_width = badge_max * 0.05 rect = QRect(base_max * max(corner[0] - size, 0) + pen_width, base_max * max(corner[1] - size, 0) + pen_width, badge_max - pen_width, badge_max - pen_width) painter = QPainter(base_pixmap) painter.setRenderHint(QPainter.Antialiasing) painter.setPen(QPen(Qt.red, pen_width)) painter.setBrush(QBrush(Qt.red)) painter.drawEllipse(rect) if text: font = painter.font() font.setPixelSize(badge_max - pen_width) painter.setFont(font) painter.setPen(Qt.white) painter.drawText(rect, Qt.AlignCenter, str(text)) painter.end() self.swap(base_pixmap)
class Chessman(QLabel): ''' 棋子类 ''' def __init__(self, color, parent=None): super().__init__(parent=parent) self.pic = None self.color = color if color == 'w': # 白棋 self.pic = QPixmap("source/白子.png") elif color == 'b': # 黑棋 self.pic = QPixmap("source/黑子.png") else: raise InvailidArguementsException("构造棋子时的参数错误,请传入'b'(黑棋)或者'w'(白棋)") self.setFixedSize(self.pic.size()) self.setPixmap(self.pic) def move(self, a0: QtCore.QPoint): # 通过点击点的位置,定位到棋盘的相交点上 x = a0.x() y = a0.y() if (x - 50) % 30 <= 15: # 对三十求余小于等于15,落子在左半边的交线上 x = (x - 50) // 30 * 30 # 整除三十再乘以三十,目的是过滤掉除以三十的余数,使其正好落在标线上 else: x = (x - 50) // 30 * 30 + 30 # 对三十求余大于15,落子在右半边的交线上 if (y - 50) % 30 <= 15: # 对三十求余小于等于15,落子在上半边的交线上 y = (y - 50) // 30 * 30 # 整除三十再乘以三十,目的是过滤掉除以三十的余数,使其正好落在标线上 else: y = (y - 50) // 30 * 30 + 30 # 对三十求余大于15,落子在下半边的交线上 #最后横纵坐标各减去图片的一般,并且各加上50恢复原来的坐标 x = x - self.pic.width() / 2 + 50 y = y - self.pic.height() / 2 + 50 super().move(x, y)
class MonoidAboutWindow(QDialog): """ Applications About window. At the moment this window just displays the Monoid logo, without any additional informtion. """ def __init__(self, app, *args, **kwargs): super(MonoidAboutWindow, self).__init__(*args, **kwargs) # Load required data for about window. self.logo = QPixmap("images/logo.png") self.app = app # Configure window. self.setWindowTitle("About {0}".format(app.applicationName())) # Add logo and app name labels. self.logo_view = QLabel(self) self.logo_view.setPixmap(self.logo) self.logo_view.setToolTip("I love you!") def show(self, *args): """ Set the size to a fixed one after showing the window to disable the minimize and maximize button. """ super(MonoidAboutWindow, self).show(*args) self.setFixedSize(self.logo.size())
def init_big_button(button, image_name): pix = QPixmap('/usr/share/ubiquity/qt/images/' + image_name) icon = QIcon(pix) button.setIcon(icon) button.setIconSize(pix.size()) # Set a fixed height to ensure the button text is not cropped # when the window is resized button.setFixedHeight(button.sizeHint().height())
def __init__(self, parent, **kwargs): super().__init__(parent, **kwargs) pixmap = QPixmap(':/search_clear_13') self.setIcon(QIcon(pixmap)) self.setIconSize(pixmap.size()) self.setCursor(Qt.ArrowCursor) self.setPopupMode(QToolButton.InstantPopup) stylesheet = "QToolButton { border: none; padding: 0px; }" self.setStyleSheet(stylesheet)
def create_scene(self): pixmap = QPixmap(self.image_filename) size = pixmap.size() self.imageSizeChanged.emit(size) self._item = QGraphicsPixmapItem(pixmap) self._item.setCacheMode(QGraphicsPixmapItem.NoCache) self._item.setZValue(0) self.scene().addItem(self._item) self.__emit_scale_factor()
class ImageWidget(QWidget): def __init__(self, imagePath, parent): super(ImageWidget, self).__init__(parent) self.picture = QPixmap(imagePath) size = self.picture.size() self.picture = self.picture.scaled(size.width() / 2, size.height() / 2, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation) def paintEvent(self, event): painter = QPainter(self) painter.drawPixmap(0, 0, self.picture)
class Pixmap(QGraphicsObject): def __init__(self, pix): super(Pixmap, self).__init__() self.p = QPixmap(pix) def paint(self, painter, option, widget): painter.drawPixmap(QPointF(), self.p) def boundingRect(self): return QRectF(QPointF(0, 0), QSizeF(self.p.size()))
def loadImage(self): """Open a file dialog. """ path, filters = QFileDialog.getOpenFileName(self, self.tr('Open file'), '.', self.tr('Image (*.jpg *.png *.jpeg *.bmp)')) if path: self.sourcePathField.setText(path) pixmap = QPixmap(path) pixmap = self.fitImageToScreen(pixmap) self.imageLabel.setPixmap(pixmap) self.imageLabel.setFixedSize(pixmap.size())
def _load_icon(filename, backgroundColor, width, height): foreground = QPixmap() foreground.load(filename) pixmap = QPixmap(foreground.size()) pixmap.fill(backgroundColor) painter = QPainter() painter.begin(pixmap) painter.drawPixmap(QPointF(0, 0), foreground) painter.end() pixmap = pixmap.scaled(QSize(width, height), Qt.KeepAspectRatio, Qt.SmoothTransformation) return pixmap
class ShapeWidget(QWidget): def __init__(self,parent=None): super(ShapeWidget,self).__init__(parent) self.i = 1 self.mypix() self.timer = QTimer() self.timer.setInterval(500) # 500毫秒 self.timer.timeout.connect(self.timeChange) self.timer.start() # 显示不规则 pic def mypix(self): self.update() if self.i == 5: self.i = 1 self.mypic = {1: './images/left.png', 2: "./images/up.png", 3: './images/right.png', 4: './images/down.png'} self.pix = QPixmap(self.mypic[self.i], "0", Qt.AvoidDither | Qt.ThresholdDither | Qt.ThresholdAlphaDither) self.resize(self.pix.size()) self.setMask(self.pix.mask()) self.dragPosition = None def mousePressEvent(self, event): if event.button() == Qt.LeftButton: self.m_drag=True self.m_DragPosition=event.globalPos()-self.pos() event.accept() self.setCursor(QCursor(Qt.OpenHandCursor)) def mouseMoveEvent(self, QMouseEvent): if Qt.LeftButton and self.m_drag: self.move(QMouseEvent.globalPos()- self.m_DragPosition ) QMouseEvent.accept() def mouseReleaseEvent(self, QMouseEvent): self.m_drag=False self.setCursor(QCursor(Qt.ArrowCursor)) def paintEvent(self, event): painter = QPainter(self) painter.drawPixmap(0, 0, self.pix.width(),self.pix.height(),self.pix) # 鼠标双击事件 def mouseDoubleClickEvent(self, event): if event.button() == 1: self.i += 1 self.mypix() # 每500毫秒修改paint def timeChange(self): self.i += 1 self.mypix()
def requestPixmap(self, id, size): row = int(id) if row < 0 or row >= self.model.rowCount(): return QPixmap(), QSize() data = self.model.imageData(row) if data == None: return QPixmap(), QSize() pixmap = QPixmap() if not pixmap.loadFromData(data): return QPixmap(), QSize() return pixmap, pixmap.size()
class Pixmap(QGraphicsWidget): clicked = pyqtSignal() def __init__(self, pix, parent=None): super(Pixmap, self).__init__(parent) self.orig = QPixmap(pix) self.p = QPixmap(pix) def paint(self, painter, option, widget): painter.drawPixmap(QPointF(), self.p) def mousePressEvent(self, ev): self.clicked.emit() def setGeometry(self, rect): super(Pixmap, self).setGeometry(rect) if rect.size().width() > self.orig.size().width(): self.p = self.orig.scaled(rect.size().toSize()) else: self.p = QPixmap(self.orig)
class ImageViewerCenter(QScrollArea): topZoneEntered = Signal() topZoneLeft = Signal() leftZoneEntered = Signal() leftZoneLeft = Signal() def __init__(self): QScrollArea.__init__(self) self.zoomMode = ZOOM_FACTOR self.zoomFactor = 1 self.moving = None imgWidget = QLabel() imgWidget.setMouseTracking(True) imgWidget.setAlignment(Qt.AlignCenter) self.setWidget(imgWidget) self.setAlignment(Qt.AlignCenter) self.setMouseTracking(True) self.setFrameShape(self.NoFrame) self.setWidgetResizable(True) pal = QPalette() pal.setColor(QPalette.Window, Qt.black) self.setPalette(pal) self.leftZone = False self.topZone = False ### events def mousePressEvent(self, ev): self.moving = (ev.pos().x(), ev.pos().y()) self.movingScrolls = (self.horizontalScrollBar().value(), self.verticalScrollBar().value()) def mouseReleaseEvent(self, ev): self.moving = False def mouseMoveEvent(self, ev): if self.moving: p = ev.pos() self.horizontalScrollBar().setValue(self.movingScrolls[0] - (p.x() - self.moving[0])) self.verticalScrollBar().setValue(self.movingScrolls[1] - (p.y() - self.moving[1])) else: newLeft = (ev.x() < 30) if newLeft and not self.leftZone: self.leftZoneEntered.emit() elif self.leftZone and not newLeft: self.leftZoneLeft.emit() self.leftZone = newLeft newTop = (ev.y() < 30) if newTop and not self.topZone: self.topZoneEntered.emit() elif self.topZone and not newTop: self.topZoneLeft.emit() self.topZone = newTop def resizeEvent(self, ev): super(ImageViewerCenter, self).resizeEvent(ev) if self.zoomMode != ZOOM_FACTOR: self._rebuildZoom() def keyPressEvent_(self, ev): if ev.key() not in (Qt.Key_PageUp, Qt.Key_PageDown): QScrollArea.keyPressEvent(self, ev) def keyReleaseEvent_(self, ev): if ev.key() == Qt.Key_PageUp: self.imageviewer.prevImage_s() elif ev.key() == Qt.Key_PageDown: self.imageviewer.nextImage_s() else: QScrollArea.keyReleaseEvent(self, ev) ### public def setZoomMode(self, mode): self.zoomMode = mode self._rebuildZoom() def setZoomFactor(self, factor): self.zoomFactor = factor self.setZoomMode(ZOOM_FACTOR) def multiplyZoomFactor(self, factor): self.setZoomFactor(self.zoomFactor * factor) def setFile(self, file): self.file = file self.originalPixmap = QPixmap(file) #~ self.widget().setPixmap() self._rebuildZoom() ### def _rebuildZoom(self): if self.zoomMode == ZOOM_FACTOR: if self.zoomFactor == 1: self._setPixmap(self.originalPixmap) else: self._setPixmap(self._getScaledPixmap(self.originalPixmap.size() * self.zoomFactor)) elif self.zoomMode == ZOOM_FITALL: newpix = self._getScaledPixmap(self.viewport().size()) self._setPixmap(newpix) self.zoomFactor = newpix.size().width() / float(self.originalPixmap.size().width()) elif self.zoomMode == ZOOM_FITCUT: newpix = self._getScaledPixmap(self.viewport().size(), Qt.KeepAspectRatioByExpanding) self._setPixmap(newpix) self.zoomFactor = newpix.size().width() / float(self.originalPixmap.size().width()) def _getScaledPixmap(self, size, mode=Qt.KeepAspectRatio): return self.originalPixmap.scaled(size, mode, Qt.SmoothTransformation) def _setPixmap(self, pixmap): self.widget().setPixmap(pixmap)
class Ground(QLabel): def __init__(self, parent): QLabel.__init__(self, parent) self.symbol = QPixmap() self.symbol.load('gnd.png') self.symbol = self.symbol.scaledToWidth(30) self.resize(self.symbol.size()) self.setPixmap(self.symbol) self.move(300, 300) self.show() self.Node = node(parent) self.Node.move(self.x() + 10, self.y()) self.Node.id = 'N0' self.Node.show() self.parent().node_dict['N0'].add(self) # When right click on element a menu pops on self.PropMenu = QMenu(parent) # self.PropMenu.addAction('Rotate') self.PropMenu.addAction('Delete') # Setting Geometry of menu self.PropMenu.move(parent.frameGeometry().left() + self.x() + 30, parent.frameGeometry().top() + self.y() + 30) def mousePressEvent(self, event): self.__mousePressPos = None self.__mouseMovePos = None if event.button() == QtCore.Qt.LeftButton: self.__mousePressPos = event.globalPos() self.__mouseMovePos = event.globalPos() if event.button() == QtCore.Qt.RightButton: action = self.PropMenu.exec() if action is not None: if action.text() == 'Delete': print('Delete clicked') self.deleteMe() # elif action.text() == 'Rotate': # print('Rotate clicked') # # self.Rotate() super(Ground, self).mousePressEvent(event) def mouseMoveEvent(self, event): if event.buttons() == QtCore.Qt.LeftButton: # adjust offset from clicked point to origin of widget currPos = self.mapToGlobal(self.pos()) globalPos = event.globalPos() diff = globalPos - self.__mouseMovePos newPos = self.mapFromGlobal(currPos + diff) # Moving element to new position self.move(newPos) self.Node.move(newPos.x() + 10, newPos.y() ) self.PropMenu.move(self.parent().frameGeometry().left() + newPos.x() + 30, self.parent().frameGeometry().top() + newPos.y() + 30) self.__mouseMovePos = globalPos super(Ground, self).mouseMoveEvent(event) def mouseReleaseEvent(self, event): if self.__mousePressPos is not None: moved = event.globalPos() - self.__mousePressPos if moved.manhattanLength() > 3: event.ignore() return super(Ground, self).mouseReleaseEvent(event) def deleteMe(self): self.hide() self.Node.hide()
class Inductor(element): def __init__(self, name, parent, value = 0.01): element.__init__(self, name, parent, value) self.Impedance = 1j *2 * np.pi * Current_src.freq * self.value # adding image to resistor(that is a QLabel object itself) as its symbol self.symbol = QPixmap() self.symbol.load('ind.png') self.symbol = self.symbol.scaledToWidth(100) self.resize(self.symbol.size()) self.setPixmap(self.symbol) self.move(200, 100) self.valueText = QLineEdit(parent) self.valueText.move(self.x() + 30, self.y() + 40) self.valueText.resize(50, 15) self.valueText.setText(str(self.value) + ' H') self.idLabel = QLabel(parent) self.idLabel.move(self.x() + 45, self.y()) self.idLabel.resize(20, 15) self.idLabel.setText(str(self.name)) self.idLabel.show() self.nodeP = node(parent) self.nodeS = node(parent) self.nodeP.move(self.x(), self.y() + 22) self.nodeS.move(self.x() + 90, self.y() + 22) self.nodeP.show() self.nodeS.show() # first element tahr is connected to a ned is It's element. self.nodeP.connected_elements.add(self.name) self.nodeS.connected_elements.add(self.name) # When right click on element a menu pops on self.PropMenu = QMenu(parent) self.PropMenu.addAction('Change Value') self.PropMenu.addAction('Rotate') self.PropMenu.addAction('Delete') # Setting Geometry of menu self.PropMenu.move(parent.frameGeometry().left() + self.x() + 20, parent.frameGeometry().top() + self.y() + 100) self.isHorizontal = True self.direction_label = QLabel(parent) self.direction = QPixmap() self.valueText.show() self.show() def mousePressEvent(self, event): self.__mousePressPos = None self.__mouseMovePos = None if event.button() == QtCore.Qt.LeftButton: self.__mousePressPos = event.globalPos() self.__mouseMovePos = event.globalPos() if event.button() == QtCore.Qt.RightButton: action = self.PropMenu.exec() if action is not None: if action.text() == 'Change Value': print('Change Value clicked') self.value = float(re.match('\d*(?:.\d*e?-?\d*)?', self.valueText.text()).group()) self.Impedance = 1j *2 * np.pi * Current_src.freq * self.value elif action.text() == 'Rotate': print('Rotate clicked') self.Rotate() elif action.text() == 'Delete': print('Delete clicked') self.deleteMe() self.parent().elements.remove(self) super(Inductor, self).mousePressEvent(event) def mouseMoveEvent(self, event): if event.buttons() == QtCore.Qt.LeftButton: # adjust offset from clicked point to origin of widget currPos = self.mapToGlobal(self.pos()) globalPos = event.globalPos() diff = globalPos - self.__mouseMovePos newPos = self.mapFromGlobal(currPos + diff) # Moving element to new position self.move(newPos) if self.isHorizontal: self.nodeP.move(newPos.x(), newPos.y() + 22) self.nodeS.move(newPos.x() + 90, newPos.y() + 22) self.valueText.move(newPos.x() + 30, newPos.y() + 40) self.idLabel.move(newPos.x() + 45, newPos.y()) self.PropMenu.move(self.parent().frameGeometry().left() + newPos.x() + 20, self.parent().frameGeometry().top() + newPos.y() + 100) else: self.nodeP.move(newPos.x() + 18, newPos.y()) self.nodeS.move(newPos.x() + 18, newPos.y() + 90) self.valueText.move(newPos.x() + 40, newPos.y() + 45) self.idLabel.move(newPos.x() + 40, newPos.y() + 25) self.PropMenu.move(self.parent().frameGeometry().left() + newPos.x() + 100, self.parent().frameGeometry().top() + newPos.y() + 20) self.__mouseMovePos = globalPos super(Inductor, self).mouseMoveEvent(event) def mouseReleaseEvent(self, event): if self.__mousePressPos is not None: moved = event.globalPos() - self.__mousePressPos if moved.manhattanLength() > 3: event.ignore() return super(Inductor, self).mouseReleaseEvent(event) def Rotate(self): if(self.isHorizontal): self.symbol = QPixmap() self.symbol.load('ind2.png') self.symbol = self.symbol.scaledToHeight(100) self.resize(self.symbol.size()) self.setPixmap(self.symbol) self.valueText.move(self.x() + 40, self.y() + 45) self.idLabel.move(self.x() + 40, self.y() + 25) self.valueText.setText(str(self.value) + ' H') self.nodeP.move(self.x() + 18, self.y()) self.nodeS.move(self.x() + 18, self.y() + 90) self.isHorizontal = False else: self.symbol = QPixmap() self.symbol.load('ind.png') self.symbol = self.symbol.scaledToWidth(100) self.resize(self.symbol.size()) self.setPixmap(self.symbol) self.valueText.move(self.x() + 30, self.y() + 40) self.idLabel.move(self.x() + 45, self.y()) self.valueText.setText(str(self.value) + ' H') self.nodeP.move(self.x(), self.y() + 22) self.nodeS.move(self.x() + 90, self.y() + 22) self.isHorizontal = True def deleteMe(self): self.hide() self.nodeP.hide() self.nodeS.hide() self.idLabel.hide() self.valueText.hide() def show_detail(self): self.setStyleSheet("background-color: yellow") if self.isHorizontal: self.direction.load('arrow_right.png') self.direction = self.direction.scaledToWidth(100) self.direction_label.resize(self.direction.size()) self.direction_label.setPixmap(self.direction) self.direction_label.move(self.x(), self.y()) self.direction_label.show() else: self.direction.load('arrow_down.png') self.direction = self.direction.scaledToHeight(100) self.direction_label.resize(self.direction.size()) self.direction_label.setPixmap(self.direction) self.direction_label.move(self.x(), self.y()) self.direction_label.show() def hide_detail(self): self.setStyleSheet("background-color: none") self.direction_label.hide()
class MainWindow(QMainWindow): def __init__(self): super().__init__() self.current_dir = os.getcwd() self.descriptors = {} self.image_names = [] self.current_index = 0 self.pixmap = None self.scaledPixmap = None self.initUI() self.reloadDirectory() def initUI(self): centralWidget = QWidget() self.setCentralWidget(centralWidget) self.statusBar() # menu openIcon = qApp.style().standardIcon(QStyle.SP_DirOpenIcon) openFolder = QAction(openIcon, 'Open folder..', self) openFolder.setShortcut('Ctrl+O') openFolder.setStatusTip('Open a new folder with images') openFolder.triggered.connect(self.showDialog) exitIcon = qApp.style().standardIcon(QStyle.SP_DialogCloseButton) exitAction = QAction(exitIcon, '&Exit', self) exitAction.setShortcut('Ctrl+Q') exitAction.setStatusTip('Exit application') exitAction.triggered.connect(qApp.quit) menubar = self.menuBar() menubar.setNativeMenuBar(False) fileMenu = menubar.addMenu('&File') fileMenu.addAction(openFolder) fileMenu.addAction(exitAction) aboutMenu = menubar.addMenu('&About') aboutAction = QAction('&About', self) aboutAction.setShortcut('F1') aboutAction.setStatusTip('About the author') aboutAction.triggered.connect(self.about) aboutMenu.addAction(aboutAction) # window contents self.label = CustomQLabel(self) self.label.setMinimumSize(1, 1) self.label.setAlignment(Qt.AlignCenter) self.label.mousePressEvent = self.handleImageClick self.label.installEventFilter(self) self.commentEdit = QLineEdit() self.commentEdit.setPlaceholderText("Enter comment here...") self.commentEdit.setDragEnabled(True) # Allows drag-n-drop in the text field self.commentEdit.textChanged.connect(self.saveComment) prevIcon = qApp.style().standardIcon(QStyle.SP_ArrowBack) prevButton = QPushButton("Previous image") prevButton.setIcon(prevIcon) prevButton.setToolTip("PageUp") nextIcon = qApp.style().standardIcon(QStyle.SP_ArrowForward) nextButton = QPushButton("Next image") nextButton.setIcon(nextIcon) nextButton.setToolTip("PageDown") prevButton.clicked.connect(self.moveBackward) nextButton.clicked.connect(self.moveForward) hbox = QHBoxLayout() hbox.addWidget(self.commentEdit) hbox.addWidget(prevButton) hbox.addWidget(nextButton) vbox = QVBoxLayout() vbox.addWidget(self.label) vbox.addLayout(hbox) centralWidget.setLayout(vbox) # window itself self.setGeometry(300, 300, 800, 480) self.center() self.setWindowTitle('Tickster') self.show() def about(self): QMessageBox.about(self, "About", """<b> Tickster version %s </b> <p>Copyright © by Dmitry Nikulin. October 2015. <ul> <li>Python %s</li> <li>PyQt %s</li> <li>Qt %s</li> </ul> <p>Esc to exit, Ctrl+O to open a folder, PageUp/PageDown to navigate.</p> <p>Data is saved automatically in <b>%s</b> when switching between images, directories, and on exit.</p>""" % (__version__, platform.python_version(), PYQT_VERSION_STR, QT_VERSION_STR, csv_filename)) def eventFilter(self, source, event): if source is self.label and event.type() == QEvent.Resize: # re-scaling the pixmap when the label resizes self.setPixmap() return super(MainWindow, self).eventFilter(source, event) def center(self): qr = self.frameGeometry() cp = QDesktopWidget().availableGeometry().center() qr.moveCenter(cp) self.move(qr.topLeft()) def keyPressEvent(self, event): if event.key() == Qt.Key_Escape: self.close() elif event.key() == Qt.Key_PageDown: self.moveForward() elif event.key() == Qt.Key_PageUp: self.moveBackward() def closeEvent(self, event): if self.image_names: self.saveDescriptors() event.accept() def moveBackward(self): if self.image_names: self.saveDescriptors() if self.current_index == 0: self.current_index = len(self.image_names) - 1 else: self.current_index -= 1 self.reloadImage() else: self.statusBar().showMessage('No images loaded') def moveForward(self): if self.image_names: self.saveDescriptors() if self.current_index == len(self.image_names) - 1: self.current_index = 0 else: self.current_index += 1 self.reloadImage() else: self.statusBar().showMessage('No images loaded') def saveComment(self, text): if self.image_names: self.descriptors[self.image_names[self.current_index]].comment = text def showDialog(self): new_dir = QFileDialog.getExistingDirectory(self, 'Open image folder', self.current_dir) if new_dir: if self.image_names: self.saveDescriptors() self.current_dir = new_dir self.reloadDirectory() else: self.statusBar().showMessage('Nothing was selected') def reloadDirectory(self): self.image_names = [] self.descriptors = {} self.current_index = 0 self.label.clear() contents = os.listdir(self.current_dir) if csv_filename in contents: with open(os.path.join(self.current_dir, csv_filename)) as f: reader = csv.reader(f) for row in reader: desc = row_to_desc(row) self.descriptors[desc.filename] = desc self.image_names = [fname for fname in contents if is_image_filename(fname)] if self.image_names: self.image_names.sort() self.reloadImage() else: self.statusBar().showMessage('No images found in %s' % self.current_dir) self.label.setText("No images found") def reloadImage(self): image_name = self.image_names[self.current_index] if image_name not in self.descriptors: self.descriptors[image_name] = ImageDescriptor(image_name, [], '') self.commentEdit.setText(self.descriptors[image_name].comment) # ticks are drawn elsewhere self.pixmap = QPixmap(os.path.join(self.current_dir, image_name)) self.setPixmap() self.updateStatusBar() def setPixmap(self): if self.image_names: self.scaledPixmap = self.pixmap.scaled( self.label.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation ) self.label.setPixmap(self.scaledPixmap) def handleImageClick(self, event): if self.image_names: ticks = self.descriptors[self.image_names[self.current_index]].ticks pt = event.pos() x, y = pt.x(), pt.y() pixmap_x = (x * 2 + self.scaledPixmap.size().width() - self.label.size().width()) / 2 pixmap_y = (y * 2 + self.scaledPixmap.size().height() - self.label.size().height()) / 2 # adding +1 so that the indexes are 1-based instead of 0-based image_x = round(pixmap_x * self.pixmap.size().width() / self.scaledPixmap.size().width()) + 1 image_y = round(pixmap_y * self.pixmap.size().height() / self.scaledPixmap.size().height()) + 1 if 1 <= image_x <= self.pixmap.size().width() and 1 <= image_y <= self.pixmap.size().height(): if len(ticks) >= 4: del ticks[0] ticks.append((image_x, image_y)) self.updateStatusBar() event.accept() self.label.repaint() def updateStatusBar(self): if self.image_names: desc = self.descriptors[self.image_names[self.current_index]] self.statusBar().showMessage('[%d / %d (%d)] %s | %r' % ( self.current_index + 1, len(self.image_names), len(self.descriptors), desc.filename, desc.ticks )) def drawTicks(self, qp): if self.image_names: ticks = self.descriptors[self.image_names[self.current_index]].ticks for i, (image_x, image_y) in enumerate(ticks): pen = QPen(cross_colors[i], 2, Qt.SolidLine) qp.setPen(pen) # subtracting 1 because the coordinates are stored as 1-based # no need to care whether they fit in the image pixmap_x = (image_x - 1) * self.scaledPixmap.size().width() / self.pixmap.size().width() pixmap_y = (image_y - 1) * self.scaledPixmap.size().height() / self.pixmap.size().height() x = round((pixmap_x * 2 + self.label.size().width() - self.scaledPixmap.size().width()) / 2) y = round((pixmap_y * 2 + self.label.size().height() - self.scaledPixmap.size().height()) / 2) cross_t = y - (cross_size // 2) cross_b = y + (cross_size // 2) cross_l = x - (cross_size // 2) cross_r = x + (cross_size // 2) qp.drawLine(x, cross_t, x, cross_b) qp.drawLine(cross_l, y, cross_r, y) def saveDescriptors(self): # skipping empty descriptors rows = [desc_to_row(desc) for desc in self.descriptors.values() if (desc.ticks or desc.comment)] if rows: rows.sort(key=lambda row: row[0]) with open(os.path.join(self.current_dir, csv_filename), 'w') as f: writer = csv.writer(f) writer.writerows(rows)
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 __init__(self): super().__init__() self._scale = round(QFontMetrics(QCoreApplication.instance().font()).ascent() / 12) splash_image = QPixmap(Resources.getPath(Resources.Images, "cura.png")) self.setPixmap(splash_image.scaled(splash_image.size() * self._scale))
class ImageDisplay(QWidget): def __init__(self, my_book, parent=None): super().__init__(parent) self.my_book = my_book # register metadata readers and writers md = my_book.get_meta_manager() md.register(C.MD_IZ,self._zoom_read,self._zoom_write) md.register(C.MD_IX,self._link_read,self._link_write) # Create our widgets including cursor_to_image and # image_to_cursor pushbuttons. self._uic() # set defaults in case no metadata self.cursor_to_image.setChecked(True) self.image_to_cursor.setChecked(False) self.zoom_factor = 0.25 self.png_path = None # disable all widgetry until we get some metadata self._disable() # end of __init__() # Disable our widgets because we have no image to show. def _disable(self): self.no_image = True self.last_index = None # compares unequal to any self.pix_map = QPixmap() self.image = QImage() self.cursor_to_image.setEnabled(False) self.image_to_cursor.setEnabled(False) self.zoom_pct.setEnabled(False) self.zoom_to_width.setEnabled(False) self.zoom_to_height.setEnabled(False) self.image_display.setPixmap(self.gray_image) self.image_display.setToolTip( _TR('Image view tooltip', 'Display of one scanned page (no images available)') ) # Enable our widgets, we have images to show. At this time the Book # has definitely created an edit view and a page model. def _enable(self): self.edit_view = self.my_book.get_edit_view() self.editor = self.edit_view.Editor # access to actual QTextEdit self.page_data = self.my_book.get_page_model() self.cursor_to_image.setEnabled(True) self.image_to_cursor.setEnabled(True) self.zoom_to_width.setEnabled(True) self.zoom_to_height.setEnabled(True) self.image_display.setToolTip( _TR('Image view tooltip', 'Display of one scanned page from the book') ) self.no_image = False self.zoom_pct.setEnabled(True) # the following triggers entry to _new_zoom_pct() below self.zoom_pct.setValue(int(100*self.zoom_factor)) # Metadata: read or write the {{IMAGEZOOM f}} section. # Parameter f should be a decimal number between 0.15 and 2.0 # but we do not depend on text the user could edit. def _zoom_read(self, qts, section, vers, parm): try: z = float(parm) # throws exception on a bad literal if math.isnan(z) or (z < 0.15) or (z > 2.0) : raise ValueError self.zoom_factor = z except: imageview_logger.error('Invalid IMAGEZOOM "{0}" ignored'.format(parm)) def _zoom_write(self, qts, section): qts << metadata.open_line(section, str(self.zoom_factor)) # Metadata: read or write the {{IMAGELINK b}} section. The parameter should # be an int 0/1/2/3. Bit 0 represents the state of cursor_to_image # (usually 1); bit 1 represents the state of image_to_cursor (usually 0). def _link_read(self, qts, section, vers, parm): try: b = int(parm) # exception on a bad literal if (b < 0) or (b > 3) : raise ValueError self.cursor_to_image.setChecked( True if b & 1 else False ) self.image_to_cursor.setChecked( True if b & 2 else False ) except : imageview_logger.error('Invalid IMAGELINKING "{0}" ignored'.format(parm)) def _link_write(self, qts, section): b = 0 if self.cursor_to_image.isChecked() : b |= 1 if self.image_to_cursor.isChecked() : b |= 2 qts << metadata.open_line(section, str(b)) # The Book calls here after it has loaded a book with defined page data, # passing the path to the folder containing the book. If we can find a # folder named 'pngs' in it we record that path and enable our widgets, # and fake a cursorMoved signal to display the current edit page. def set_path(self,book_folder_path): book_dir = QDir(book_folder_path) if book_dir.exists('pngs') : self.png_dir = QDir(book_dir.absoluteFilePath('pngs')) self._enable() self.cursor_move() # Come here to display or re-display an image. The last-displayed # page image index (if any) is in self.last_index. The desired page # index is passed as the argument, which may be: # * the same as last_index, for example on a change of zoom%. Just # redisplay the current page. # * negative or None if the cursor is "above" the first available page or on # a Page-Up keystroke. Display the gray image. # * greater than page_data.page_count() on a Page-Down keystroke, # display the last available page. # If different from last_index, try to load the .png file for that # page. If that fails, use the gray image. Otherwise display that # page and save it as last_index. def _show_page(self, page_index): if page_index != self.last_index : self.last_index = page_index # change of page, see if we have a filename for it self.pix_map = self.gray_image # assume failure... im_name = self.page_data.filename(page_index) if im_name : # pagedata has a filename; of course there is no guarantee # such a file exists now or ever did. im_name += '.png' if self.png_dir.exists(im_name) : self.image = QImage(self.png_dir.absoluteFilePath(im_name)) if not self.image.isNull(): # we loaded it ok, make a full-scale pixmap for display self.pix_map = QPixmap.fromImage(self.image,Qt.ColorOnly) # Whether new page or not, rescale to current zoom. The .resize method # takes a QSize; pix_map.size() returns one, and it supports * by a real. self.image_display.setPixmap(self.pix_map) self.image_display.resize( self.zoom_factor * self.pix_map.size() ) # Slot to receive the cursorMoved signal from the editview widget. If we # are in no_image state, do nothing. If the cursor_to_image switch is # not checked, do nothing. Else get the character position of # the high-end of the current edit selection, and use that to get the # current page index from pagedata, and pass that to _show_page. def cursor_move(self): if self.no_image : return if self.cursor_to_image.isChecked() : pos = self.editor.textCursor().selectionEnd() self._show_page( self.page_data.page_index(pos) ) # Slots to receive the signals from our zoom percent and zoom-to buttons. # The controls are disabled while we are in no_image state, so if a signal # arrives, we are not in that state. # # These are strictly internal hence _names. # Any change in the value of the zoom % spin-box including setValue(). def _new_zoom_pct(self,new_value): self.zoom_factor = self.zoom_pct.value() / 100 self._show_page(self.last_index) # Set a new zoom factor (a real) and update the zoom pct spinbox. # Setting zoom_pct triggers a signal to _new_zoom_pct above, and # thence to _show_page which repaints the page at the new scale value. def _set_zoom_real(self,new_value): zoom = max(new_value, ZOOM_FACTOR_MIN) zoom = min(zoom, ZOOM_FACTOR_MAX) self.zoom_factor = zoom self.zoom_pct.setValue(int(100*zoom)) # Re-implement keyPressEvent in order to provide zoom and page up/down. # ctrl-plus increases the image size by 1.25 # ctrl-minus decreases the image size by 0.8 # page-up displays the next-higher page # page-down displays the next-lower page def keyPressEvent(self, event): # assume we will not handle this key and clear its accepted flag event.ignore() if self.no_image or (self.last_index is None) : return # ignore keys until we are showing some image # We have images to show, check the key value. modkey = int( int(event.key() | (int(event.modifiers()) & C.KEYPAD_MOD_CLEAR)) ) if modkey in C.KEYS_ZOOM : event.accept() fac = (0.8) if (modkey == C.CTL_MINUS) else (1.25) self._set_zoom_real( fac * self.zoom_factor) elif (event.key() == Qt.Key_PageUp) or (event.key() == Qt.Key_PageDown) : event.accept() pgix = self.last_index + (1 if (event.key() == Qt.Key_PageDown) else -1) # If not paging off either end, show that page if pgix >= 0 and pgix < self.page_data.page_count() : self._show_page(pgix) if self.image_to_cursor.isChecked(): self.edit_view.show_position(self.page_data.position(pgix)) # Zoom to width and zoom to height are basically the same thing: # 1. Using the QImage of the current page in self.image, # scan its pixels to find the width (height) of the nonwhite area. # 2. Get the ratio of that to our image label's viewport width (height). # 3. Set that ratio as the zoom factor and redraw the image. # 5. Set the scroll position(s) of our scroll area to left-justify the text. # # We get access to the pixel data using QImage.bits() which gives us a # "sip.voidptr" object that we can index to get byte values. def _zoom_to_width(self): # Generic loop to scan inward from the left or right edge of one # column inward until a dark pixel is seen, returning that margin. def inner_loop(row_range, col_start, margin, col_step): pa, pb = 255, 255 # virtual white outside column for row in row_range: for col in range(col_start, margin, col_step): pc = color_table[ bytes_ptr[row+col] ] if (pa + pb + pc) < 24 : # black or dark gray trio margin = col # new, narrower, margin break # no need to look further on this row pa, pb = pb, pc # else shift 3-pixel window return margin - (2*col_step) # allow for 3-px window if self.no_image or self.image.isNull() : return # nothing to do scale_factor = 4 orig_rows = self.image.height() # number of pixels high orig_cols = self.image.width() # number of logical pixels across # Scale the image to 1/4 size (1/16 the pixel count) and then force # it to indexed-8 format, one byte per pixel. work_image = self.image.scaled( QSize(int(orig_cols/scale_factor),int(orig_rows/scale_factor)), Qt.KeepAspectRatio, Qt.FastTransformation) work_image = work_image.convertToFormat(QImage.Format_Indexed8,Qt.ColorOnly) # Get a reduced version of the color table by extracting just the GG # values of each entry, as a dict keyed by the pixel byte value. For # PNG-2, this gives [0,255] but it could have 8, 16, even 256 elements. color_table = { bytes([c]): int((work_image.color(c) >> 8) & 255) for c in range(work_image.colorCount()) } # Establish limits for the inner loop rows = work_image.height() # number of pixels high cols = work_image.width() # number of logical pixels across stride = (cols + 3) & (-4) # scan-line width in bytes bytes_ptr = work_image.bits() # uchar * a_bunch_o_pixels bytes_ptr.setsize(stride * rows) # make the pointer indexable # Scan in from left and from right to find the outermost dark spots. # Pages tend to start with many lines of white pixels so in hopes of # establishing a narrow margin quickly, scan from the middle to the # end, then do the top half. left_margin = inner_loop( range(int(rows/2)*stride, (rows-1)*stride, stride*2), 0, int(cols/2), 1 ) left_margin = inner_loop( range(0, int(rows/2)*stride, stride*2), 0, left_margin, 1 ) # Now do exactly the same but for the right margin. right_margin = inner_loop( range(int(rows/2)*stride, (rows-1)*stride, stride*2), cols-1, int(cols/2), -1 ) right_margin = inner_loop( range(0, int(rows/2)*stride, stride*2), cols-1, right_margin, -1 ) # Adjust the margins by the scale factor to fit the full size image. #left_margin = max(0,left_margin*scale_factor-scale_factor) #right_margin = min(orig_cols,right_margin*scale_factor+scale_factor) left_margin = left_margin*scale_factor right_margin = right_margin*scale_factor text_size = right_margin - left_margin + 2 port_width = self.scroll_area.viewport().width() # Set the new zoom factor, after limiting by min/max values self._set_zoom_real(port_width/text_size) # Set the scrollbar to show the page from its left margin. self.scroll_area.horizontalScrollBar().setValue( int( left_margin * self.zoom_factor) ) # and that completes zoom-to-width def _zoom_to_height(self): def dark_row(row_start, cols): ''' Scan one row of pixels and return True if it contains at least one 3-pixel blob of darkness, or False if not. ''' pa, pb = 255, 255 for c in range(row_start,row_start+cols): pc = color_table[ bytes_ptr[c] ] if (pa + pb + pc) < 24 : # black or dark gray trio return True pa, pb = pb, pc return False # row was all-white-ish if self.no_image or self.image.isNull() : return # nothing to do scale_factor = 4 orig_rows = self.image.height() # number of pixels high orig_cols = self.image.width() # number of logical pixels across # Scale the image to 1/4 size (1/16 the pixel count) and then force # it to indexed-8 format, one byte per pixel. work_image = self.image.scaled( QSize(int(orig_cols/scale_factor),int(orig_rows/scale_factor)), Qt.KeepAspectRatio, Qt.FastTransformation) work_image = work_image.convertToFormat(QImage.Format_Indexed8,Qt.ColorOnly) # Get a reduced version of the color table by extracting just the GG # values of each entry, as a dict keyed by the pixel byte value. For # PNG-2, this gives [0,255] but it could have 8, 16, even 256 elements. color_table = { bytes([c]): int((work_image.color(c) >> 8) & 255) for c in range(work_image.colorCount()) } rows = work_image.height() # number of pixels high cols = work_image.width() # number of logical pixels across stride = (cols + 3) & (-4) # scan-line width in bytes bytes_ptr = work_image.bits() # uchar * a_bunch_o_pixels bytes_ptr.setsize(stride * rows) # make the pointer indexable # Scan the image rows from the top down looking for one with darkness for top_row in range(rows): if dark_row(top_row*stride, cols): break if top_row > (rows/2) : # too much white, skip it return for bottom_row in range(rows-1, top_row, -1): if dark_row(bottom_row*stride, cols) : break # bottom_row has to be >= top_row. if they are too close together # set_zoom_real will limit the zoom to 200%. top_row = top_row*scale_factor bottom_row = bottom_row*scale_factor text_height = bottom_row - top_row + 1 port_height = self.scroll_area.viewport().height() self._set_zoom_real(port_height/text_height) self.scroll_area.verticalScrollBar().setValue( int( top_row * self.zoom_factor ) ) # and that completes zoom-to-height # Build the widgetary contents. The widget consists mostly of a vertical # layout with two items: A scrollArea containing a QLabel used to display # an image, and a horizontal layout containing the zoom controls. # TODO: figure out design and location of two cursor-link tool buttons. def _uic(self): # Function to return the actual width of the label text # of a widget. Get the fontMetrics and ask it for the width. def _label_width(widget): fm = widget.fontMetrics() return fm.width(widget.text()) # Create a gray field to use when no image is available self.gray_image = QPixmap(700,900) self.gray_image.fill(QColor("gray")) # Build the QLabel that displays the image pixmap. It gets all # available space and scales its contents to fit that space. self.image_display = QLabel() self.image_display.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored) self.image_display.setScaledContents(True) # Create a scroll area within which to display the image. It will # create a horizontal and/or vertical scroll bar when # the image_display size exceeds the size of the scroll area. self.scroll_area = QScrollArea() self.scroll_area.setBackgroundRole(QPalette.Dark) self.scroll_area.setSizeAdjustPolicy(QAbstractScrollArea.AdjustToContents) self.scroll_area.setWidget(self.image_display) # Make sure the scroll area does not swallow user keystrokes self.setFocusPolicy(Qt.ClickFocus) # focus into whole widget self.scroll_area.setFocusProxy(self) # you, pass it on. # Create the image-linking toolbuttons. # Cursor-to-image uses left-hands. c2i_on = QPixmap(':/hand-left-closed.png') c2i_off = QPixmap(':/hand-left-open.png') c2i_con = QIcon() c2i_con.addPixmap(c2i_on,QIcon.Normal,QIcon.On) c2i_con.addPixmap(c2i_off,QIcon.Normal,QIcon.Off) self.cursor_to_image = QToolButton() self.cursor_to_image.setCheckable(True) self.cursor_to_image.setContentsMargins(0,0,0,0) self.cursor_to_image.setIconSize(QSize(30,24)) self.cursor_to_image.setMaximumSize(QSize(32,26)) self.cursor_to_image.setIcon(c2i_con) # Image-to-cursor uses right-hands. i2c_on = QPixmap(':/hand-right-closed.png') i2c_off = QPixmap(':/hand-right-open.png') i2c_con = QIcon() i2c_con.addPixmap(i2c_on,QIcon.Normal,QIcon.On) i2c_con.addPixmap(i2c_off,QIcon.Normal,QIcon.Off) self.image_to_cursor = QToolButton() self.image_to_cursor.setCheckable(True) self.image_to_cursor.setContentsMargins(0,0,0,0) self.image_to_cursor.setIconSize(QSize(30,24)) self.image_to_cursor.setMaximumSize(QSize(32,26)) self.image_to_cursor.setIcon(i2c_con) # Create a spinbox to set the zoom from 15 to 200 and connect its # signal to our slot. self.zoom_pct = QSpinBox() self.zoom_pct.setRange( int(100*ZOOM_FACTOR_MIN),int(100*ZOOM_FACTOR_MAX)) self.zoom_pct.setToolTip( _TR('Imageview zoom control tooltip', 'Set the magnification of the page image') ) # Connect the valueChanged(int) signal as opposed to the # valueChanged(str) signal. self.zoom_pct.valueChanged['int'].connect(self._new_zoom_pct) # Create a label for the zoom spinbox. (the label is not saved as a # class member, its layout will keep it in focus) Not translating # the word "Zoom". pct_label = QLabel( '&Zoom {0}-{1}%'.format( str(self.zoom_pct.minimum() ), str(self.zoom_pct.maximum() ) ) ) pct_label.setAlignment(Qt.AlignRight | Qt.AlignVCenter) pct_label.setBuddy(self.zoom_pct) # Create the to-width and to-height zoom buttons. Make # sure their widths are equal after translation. self.zoom_to_width = QPushButton( _TR('Imageview zoom control button name','to Width') ) self.zoom_to_width.setToolTip( _TR('Imageview zoom control tooltip', 'Adjust the image to fill the window side to side.') ) self.zoom_to_width.clicked.connect(self._zoom_to_width) self.zoom_to_height = QPushButton( _TR('Imageview zoom control button name','to Height') ) self.zoom_to_height.setToolTip( _TR('Imageview zoom control tooltip', 'Adjust the image to fill the window top to bottom.') ) self.zoom_to_height.clicked.connect(self._zoom_to_height) w = 20 + max(_label_width(self.zoom_to_height),_label_width(self.zoom_to_width)) self.zoom_to_height.setMinimumWidth(w) self.zoom_to_width.setMinimumWidth(w) # Create an HBox for the top of the panel which contains only # the cursor-to-image link button. tophbox = QHBoxLayout() tophbox.setContentsMargins(0,0,0,0) tophbox.addWidget(self.cursor_to_image,0) tophbox.addStretch() # left-align the button # Create an HBox layout to contain the above controls, using # spacers left and right to center them and a spacers between # to control the spacing. zhbox = QHBoxLayout() zhbox.setContentsMargins(0,0,0,0) zhbox.addWidget(self.image_to_cursor,0) zhbox.addStretch(2) # left and right spacers have stretch 2 zhbox.addWidget(pct_label,0) zhbox.addWidget(self.zoom_pct,0) zhbox.addStretch(1) # spacers between widgets are stretch 1 zhbox.addWidget(self.zoom_to_height,0) zhbox.addSpacing(10) # juuuust a little space between buttons zhbox.addWidget(self.zoom_to_width,0) zhbox.addStretch(2) # right side spacer # With all the pieces in hand, create our layout with a stack of # image over row of controls. vbox = QVBoxLayout() vbox.setContentsMargins(0,0,0,0) vbox.addLayout(tophbox,0) # The image gets a high stretch and default alignment. vbox.addWidget(self.scroll_area,2) vbox.addLayout(zhbox,0) self.setLayout(vbox) # And that completes the UI setup.
class LightMaps(QWidget): def __init__(self, parent = None): super(LightMaps, self).__init__(parent) self.pressed = False self.snapped = False self.zoomed = False self.invert = False self._normalMap = SlippyMap(self) self._largeMap = SlippyMap(self) self.pressPos = QPoint() self.dragPos = QPoint() self.tapTimer = QBasicTimer() self.zoomPixmap = QPixmap() self.maskPixmap = QPixmap() self._normalMap.updated.connect(self.updateMap) self._largeMap.updated.connect(self.update) def setCenter(self, lat, lng): self._normalMap.latitude = lat self._normalMap.longitude = lng self._normalMap.invalidate() self._largeMap.invalidate() # slots def toggleNightMode(self): self.invert = not self.invert self.update() def updateMap(self, r): self.update(r) def activateZoom(self): self.zoomed = True self.tapTimer.stop() self._largeMap.zoom = self._normalMap.zoom + 1 self._largeMap.width = self._normalMap.width * 2 self._largeMap.height = self._normalMap.height * 2 self._largeMap.latitude = self._normalMap.latitude self._largeMap.longitude = self._normalMap.longitude self._largeMap.invalidate() self.update() def resizeEvent(self, event): self._normalMap.width = self.width() self._normalMap.height = self.height() self._normalMap.invalidate() self._largeMap.width = self._normalMap.width * 2 self._largeMap.height = self._normalMap.height * 2 self._largeMap.invalidate() def paintEvent(self, event): p = QPainter() p.begin(self) self._normalMap.render(p, event.rect()) p.setPen(Qt.black) p.drawText(self.rect(), Qt.AlignBottom | Qt.TextWordWrap, "Map data CCBYSA 2009 OpenStreetMap.org contributors") p.end() if self.zoomed: dim = min(self.width(), self.height()) magnifierSize = min(MAX_MAGNIFIER, dim * 2 / 3) radius = magnifierSize / 2 ring = radius - 15 box = QSize(magnifierSize, magnifierSize) # reupdate our mask if self.maskPixmap.size() != box: self.maskPixmap = QPixmap(box) self.maskPixmap.fill(Qt.transparent) g = QRadialGradient() g.setCenter(radius, radius) g.setFocalPoint(radius, radius) g.setRadius(radius) g.setColorAt(1.0, QColor(255, 255, 255, 0)) g.setColorAt(0.5, QColor(128, 128, 128, 255)) mask = QPainter(self.maskPixmap) mask.setRenderHint(QPainter.Antialiasing) mask.setCompositionMode(QPainter.CompositionMode_Source) mask.setBrush(g) mask.setPen(Qt.NoPen) mask.drawRect(self.maskPixmap.rect()) mask.setBrush(QColor(Qt.transparent)) mask.drawEllipse(g.center(), ring, ring) mask.end() center = self.dragPos - QPoint(0, radius) center += QPoint(0, radius / 2) corner = center - QPoint(radius, radius) xy = center * 2 - QPoint(radius, radius) # only set the dimension to the magnified portion if self.zoomPixmap.size() != box: self.zoomPixmap = QPixmap(box) self.zoomPixmap.fill(Qt.lightGray) if True: p = QPainter(self.zoomPixmap) p.translate(-xy) self._largeMap.render(p, QRect(xy, box)) p.end() clipPath = QPainterPath() clipPath.addEllipse(QPointF(center), ring, ring) p = QPainter(self) p.setRenderHint(QPainter.Antialiasing) p.setClipPath(clipPath) p.drawPixmap(corner, self.zoomPixmap) p.setClipping(False) p.drawPixmap(corner, self.maskPixmap) p.setPen(Qt.gray) p.drawPath(clipPath) if self.invert: p = QPainter(self) p.setCompositionMode(QPainter.CompositionMode_Difference) p.fillRect(event.rect(), Qt.white) p.end() def timerEvent(self, event): if not self.zoomed: self.activateZoom() self.update() def mousePressEvent(self, event): if event.buttons() != Qt.LeftButton: return self.pressed = self.snapped = True self.pressPos = self.dragPos = event.pos() self.tapTimer.stop() self.tapTimer.start(HOLD_TIME, self) def mouseMoveEvent(self, event): if not event.buttons(): return if not self.zoomed: if not self.pressed or not self.snapped: delta = event.pos() - self.pressPos self.pressPos = event.pos() self._normalMap.pan(delta) return else: threshold = 10 delta = event.pos() - self.pressPos if self.snapped: self.snapped &= delta.x() < threshold self.snapped &= delta.y() < threshold self.snapped &= delta.x() > -threshold self.snapped &= delta.y() > -threshold if not self.snapped: self.tapTimer.stop() else: self.dragPos = event.pos() self.update() def mouseReleaseEvent(self, event): self.zoomed = False self.update() def keyPressEvent(self, event): if not self.zoomed: if event.key() == Qt.Key_Left: self._normalMap.pan(QPoint(20, 0)) if event.key() == Qt.Key_Right: self._normalMap.pan(QPoint(-20, 0)) if event.key() == Qt.Key_Up: self._normalMap.pan(QPoint(0, 20)) if event.key() == Qt.Key_Down: self._normalMap.pan(QPoint(0, -20)) if event.key() == Qt.Key_Z or event.key() == Qt.Key_Select: self.dragPos = QPoint(self.width() / 2, self.height() / 2) self.activateZoom() else: if event.key() == Qt.Key_Z or event.key() == Qt.Key_Select: self.zoomed = False self.update() delta = QPoint(0, 0) if event.key() == Qt.Key_Left: delta = QPoint(-15, 0) if event.key() == Qt.Key_Right: delta = QPoint(15, 0) if event.key() == Qt.Key_Up: delta = QPoint(0, -15) if event.key() == Qt.Key_Down: delta = QPoint(0, 15) if delta != QPoint(0, 0): self.dragPos += delta self.update()
class Shufti(ShuftiWindow): def __init__(self): super(Shufti,self).__init__() try: self.key = sys.argv[1] except IndexError: print('\nshufti 2.2\n\nTo use shufti from the terminal, you must specify the full path to an image as a parameter.\n') sys.exit(1) self.dbSanitise() self.formats = ('.png', '.jpg', '.jpeg', '.gif', '.bmp', '.pbm', '.pgm', '.ppm', '.xbm', '.xpm', '.dds', '.icns', '.jp2', '.mng', '.tga', '.tiff', '.wbmp', '.webp') try: open(self.key, 'r') except IOError: print('There was an error opening the file') sys.exit(1) if self.key.lower().endswith(self.formats): # If inshuft = 0, the image is not in shufti's image database self.inshuft = 0 self.rotval = 0 self.rotvals = (0,-90,-180,-270) self.dbfile = expanduser("~/.config/shufti/shufti.db") self.dbdir = os.path.dirname(self.dbfile) if not os.path.isfile(self.dbfile): self.createDB() self.db = QtSql.QSqlDatabase.addDatabase('QSQLITE') self.db.setDatabaseName(self.dbfile) self.db.open() self.query = QtSql.QSqlQuery() self.dbSearch(self.dbkey) # Set common window attributes self.path, self.title = os.path.split(self.key) self.setWindowTitle(str(self.title)) self.img = QPixmap(self.key) self.scene = QGraphicsScene() self.scene.addPixmap(self.img) self.view = ShuftiView(self.scene, self) self.view.setDragMode(QGraphicsView.ScrollHandDrag) self.view.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) self.view.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) # Create array of images in current image dir self.imgfiles = [] for filename in glob.glob(str(self.path) + '/*'): base, ext = os.path.splitext(filename) if ext.lower() in self.formats: self.imgfiles.append(filename) # Find location of current image in imgfiles array self.dirpos = 0 while self.dirpos < len(self.imgfiles) and self.imgfiles[self.dirpos] != self.key: self.dirpos += 1 # If we have no inshuftery, we use the defaults if self.inshuft == 0: self.newImage() else: self.oldImage() else: print("Unsupported file format") sys.exit(1) def newImage(self): self.zoom = 1 self.rotate = 0 self.resize(self.img.size()) self.view.resize(self.img.width() + 2, self.img.height() + 2) self.show() self.view.verticalScrollBar().setValue(0) self.view.horizontalScrollBar().setValue(0) def oldImage(self): if self.rotate == -90: self.rotval = 1 elif self.rotate == -180: self.rotval = 2 elif self.rotate == -270: self.rotval = 3 self.resize(self.img.size()) self.updateView() self.show() self.setGeometry(self.winposx, self.winposy, self.winsizex, self.winsizey) self.view.verticalScrollBar().setValue(self.vscroll) self.view.horizontalScrollBar().setValue(self.hscroll) def toggleFullscreen(self): if self.isFullScreen(): self.showNormal() else: self.showFullScreen() def keyPressEvent(self, event): if event.key() == QtCore.Qt.Key_F11: self.toggleFullscreen() elif event.key() == QtCore.Qt.Key_Equal or event.key() == QtCore.Qt.Key_E: self.zoomIn() elif event.key() == QtCore.Qt.Key_Minus or event.key() == QtCore.Qt.Key_D: self.zoomOut() elif event.key() == QtCore.Qt.Key_1: self.zoomReset() elif event.key() == QtCore.Qt.Key_S: self.rotateImg(-1) elif event.key() == QtCore.Qt.Key_R: self.rotateImg(1) elif event.key() == QtCore.Qt.Key_F: self.fitView() elif event.key() == QtCore.Qt.Key_Space: self.dirBrowse(1) elif event.key() == QtCore.Qt.Key_Backspace: self.dirBrowse(-1) elif event.key() == QtCore.Qt.Key_V: self.vertMax() elif event.key() == QtCore.Qt.Key_Z: self.horizMax() elif event.key() == QtCore.Qt.Key_Q: self.close() def mouseDoubleClickEvent(self, event): self.toggleFullscreen() def createDB(self): if not os.path.exists(self.dbdir): os.makedirs(self.dbdir) self.db = QtSql.QSqlDatabase.addDatabase('QSQLITE') self.db.setDatabaseName(self.dbfile) self.query = QtSql.QSqlQuery() self.db.open() self.query.exec_("create table shuftery(filename text primary key, " "zoom real, winposx int, winposy int, winsizex int, winsizey int, " "hscroll int, vscroll int, rotate int)") return True def zoomIn(self): self.zoom *= 1.05 self.updateView() def zoomOut(self): self.zoom /= 1.05 self.updateView() def zoomReset(self): self.zoom = 1 self.updateView() def rotateImg(self, clock): self.rotval += clock if self.rotval == 4: self.rotval = 0 elif self.rotval < 0: self.rotval = 3 self.rotate = self.rotvals[self.rotval] self.updateView() def fitView(self): self.view.fitInView(self.scene.sceneRect(), QtCore.Qt.KeepAspectRatio) if self.rotate == 0: self.zoom = self.view.transform().m11() elif self.rotate == -90: self.zoom = (self.view.transform().m12()) * -1 elif self.rotate == -180: self.zoom = (self.view.transform().m11()) * -1 else: self.zoom = self.view.transform().m12() def updateView(self): self.view.setTransform(QTransform().scale(self.zoom, self.zoom).rotate(self.rotate)) def winState(self): self.winsizex = self.geometry().width() self.winsizey = self.geometry().height() self.vscroll = self.view.verticalScrollBar().value() self.hscroll = self.view.horizontalScrollBar().value() self.winposx = self.pos().x() self.winposy = self.pos().y() def dbInsert(self): self.query.exec_("insert into shuftery values('%s" % self.dbkey + "', " + str(self.zoom) + ", " + str(self.winposx) + ", " + str(self.winposy) + ", " + str(self.winsizex) + ", " + str(self.winsizey) + ", " + str(self.hscroll) + ", " + str(self.vscroll) + ", " + str(self.rotate) + ")") def dbUpdate(self): self.query.exec_("update shuftery set zoom=" + str(self.zoom) + ", winposx=" + str(self.winposx) + ", winposy=" + str(self.winposy) + ", winsizex=" + str(self.winsizex) + ", winsizey=" + str(self.winsizey) + ", hscroll=" + str(self.hscroll) + ", vscroll=" + str(self.vscroll) + ", rotate=" + str(self.rotate) + " where filename='%s'" % self.dbkey) def dbSearch(self, field): self.query.exec_("SELECT * FROM shuftery WHERE filename='%s'" % field) # If the image is found in shufti.db, load the previous view settings while self.query.next() and self.inshuft == 0: self.zoom = self.query.value(1) self.winposx = self.query.value(2) self.winposy = self.query.value(3) self.winsizex = self.query.value(4) self.winsizey = self.query.value(5) self.hscroll = self.query.value(6) self.vscroll = self.query.value(7) self.rotate = self.query.value(8) self.inshuft = 1 def dbSanitise(self): self.dbkey = self.key.replace("\"", "\"\"") self.dbkey = self.dbkey.replace("\'", "\'\'") self.dbkey = self.dbkey.replace("\\", "\\\\") def dirBrowse(self, direc): if len(self.imgfiles) > 1: self.dirpos += direc if self.dirpos > (len(self.imgfiles) - 1): self.dirpos = 0 elif self.dirpos < 0: self.dirpos = (len(self.imgfiles) - 1) shufti.winState() if self.inshuft == 0: shufti.dbInsert() else: shufti.dbUpdate() self.key = self.imgfiles[self.dirpos] self.dbSanitise() self.path, self.title = os.path.split(self.key) self.setWindowTitle(str(self.title) + " - shufti") self.inshuft = 0 self.dbSearch(self.dbkey) self.scene.clear() self.view.resetTransform() self.img = QPixmap(self.key) self.scene.addPixmap(self.img) if self.inshuft == 0: self.newImage() else: self.oldImage() def vertMax(self): self.screen_res = app.desktop().availableGeometry(shufti) self.screenh = self.screen_res.height() self.winsizex = self.geometry().width() self.winposx = self.pos().x() self.setGeometry(self.winposx, 0, self.winsizex, self.screenh) def horizMax(self): self.screen_res = app.desktop().availableGeometry(shufti) self.screenw = self.screen_res.width() self.winsizey = self.geometry().height() self.winposy = self.pos().y() self.setGeometry(0, self.winposy, self.screenw, self.winsizey) def about(self): self.pop = AboutShufti() self.pop.resize(450, 200) self.pop.setWindowTitle("About shufti") self.pop.show()
class SnapshotFreehandGrabber(QWidget): """ Class implementing a grabber widget for a freehand snapshot region. @signal grabbed(QPixmap) emitted after the region was grabbed """ grabbed = pyqtSignal(QPixmap) def __init__(self): """ Constructor """ super(SnapshotFreehandGrabber, self).__init__( None, Qt.X11BypassWindowManagerHint | Qt.WindowStaysOnTopHint | Qt.FramelessWindowHint | Qt.Tool) self.__selection = QPolygon() self.__mouseDown = False self.__newSelection = False self.__handleSize = 10 self.__showHelp = True self.__grabbing = False self.__dragStartPoint = QPoint() self.__selectionBeforeDrag = QPolygon() self.__locale = QLocale() 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.__pBefore = QPoint() 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() self.activateWindow() 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) pol = QPolygon(self.__selection) if not self.__selection.boundingRect().isNull(): # Draw outline around selection. # Important: the 1px-wide outline is *also* part of the # captured free-region pen = QPen(handleColor, 1, Qt.SolidLine, Qt.SquareCap, Qt.BevelJoin) painter.setPen(pen) painter.drawPolygon(pol) # Draw the grey area around the selection. grey = QRegion(self.rect()) grey = grey - QRegion(pol) painter.setClipRegion(grey) painter.setPen(Qt.NoPen) painter.setBrush(overlayColor) painter.drawRect(self.rect()) painter.setClipRect(self.rect()) drawPolygon(painter, pol, 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) drawPolygon(painter, self.__helpTextRect, textColor, textBackgroundColor) painter.drawText( self.__helpTextRect.adjusted(3, 3, -3, -3), Qt.TextWordWrap, self.__helpText) if self.__selection.isEmpty(): 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. boundingRect = self.__selection.boundingRect() txt = "{0}, {1} ({2} x {3})".format( self.__locale.toString(boundingRect.x()), self.__locale.toString(boundingRect.y()), self.__locale.toString(boundingRect.width()), self.__locale.toString(boundingRect.height()) ) textRect = painter.boundingRect(self.rect(), Qt.AlignLeft, txt) boundingRect = textRect.adjusted(-4, 0, 0, 0) polBoundingRect = pol.boundingRect() if (textRect.width() < polBoundingRect.width() - 2 * self.__handleSize) and \ (textRect.height() < polBoundingRect.height() - 2 * self.__handleSize) and \ polBoundingRect.width() > 100 and \ polBoundingRect.height() > 100: # center, unsuitable for small selections boundingRect.moveCenter(polBoundingRect.center()) textRect.moveCenter(polBoundingRect.center()) elif polBoundingRect.y() - 3 > textRect.height() and \ polBoundingRect.x() + textRect.width() < self.rect().width(): # on top, left aligned boundingRect.moveBottomLeft( QPoint(polBoundingRect.x(), polBoundingRect.y() - 3)) textRect.moveBottomLeft( QPoint(polBoundingRect.x() + 2, polBoundingRect.y() - 3)) elif polBoundingRect.x() - 3 > textRect.width(): # left, top aligned boundingRect.moveTopRight( QPoint(polBoundingRect.x() - 3, polBoundingRect.y())) textRect.moveTopRight( QPoint(polBoundingRect.x() - 5, polBoundingRect.y())) elif (polBoundingRect.bottom() + 3 + textRect.height() < self.rect().bottom()) and \ polBoundingRect.right() > textRect.width(): # at bottom, right aligned boundingRect.moveTopRight( QPoint(polBoundingRect.right(), polBoundingRect.bottom() + 3)) textRect.moveTopRight( QPoint(polBoundingRect.right() - 2, polBoundingRect.bottom() + 3)) elif polBoundingRect.right() + textRect.width() + 3 < \ self.rect().width(): # right, bottom aligned boundingRect.moveBottomLeft( QPoint(polBoundingRect.right() + 3, polBoundingRect.bottom())) textRect.moveBottomLeft( QPoint(polBoundingRect.right() + 5, polBoundingRect.bottom())) # If the above didn't catch it, you are running on a very # tiny screen... drawPolygon(painter, boundingRect, textColor, textBackgroundColor) painter.drawText(textRect, Qt.AlignHCenter, txt) if (polBoundingRect.height() > self.__handleSize * 2 and polBoundingRect.width() > self.__handleSize * 2) or \ not self.__mouseDown: painter.setBrush(Qt.transparent) painter.setClipRegion(QRegion(pol)) painter.drawPolygon(QPolygon(self.rect())) def mousePressEvent(self, evt): """ Protected method to handle mouse button presses. @param evt mouse press event (QMouseEvent) """ self.__pBefore = evt.pos() self.__showHelp = not self.__helpTextRect.contains(evt.pos()) if evt.button() == Qt.LeftButton: self.__mouseDown = True self.__dragStartPoint = evt.pos() self.__selectionBeforeDrag = QPolygon(self.__selection) if not self.__selection.containsPoint(evt.pos(), Qt.WindingFill): self.__newSelection = True self.__selection = QPolygon() else: self.setCursor(Qt.ClosedHandCursor) elif evt.button() == Qt.RightButton: self.__newSelection = False self.__selection = QPolygon() 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() self.__selection.append(p) else: # moving the whole selection p = evt.pos() - self.__pBefore # Offset self.__pBefore = evt.pos() # save position for next iteration self.__selection.translate(p) self.update() else: if self.__selection.boundingRect().isEmpty(): return if self.__selection.containsPoint(evt.pos(), Qt.WindingFill): self.setCursor(Qt.OpenHandCursor) else: self.setCursor(Qt.CrossCursor) 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.__selection.containsPoint(evt.pos(), Qt.WindingFill): 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.__grabRegion() 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.__grabRegion() else: evt.ignore() def __grabRegion(self): """ Private method to grab the selected region (i.e. do the snapshot). """ pol = QPolygon(self.__selection) if not pol.isEmpty(): self.__grabbing = True xOffset = self.__pixmap.rect().x() - pol.boundingRect().x() yOffset = self.__pixmap.rect().y() - pol.boundingRect().y() translatedPol = pol.translated(xOffset, yOffset) pixmap2 = QPixmap(pol.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.drawPolygon(translatedPol) pt.setCompositionMode(QPainter.CompositionMode_SourceIn) else: pt.setClipRegion(QRegion(translatedPol)) pt.setCompositionMode(QPainter.CompositionMode_Source) pt.drawPixmap(pixmap2.rect(), self.__pixmap, pol.boundingRect()) pt.end() self.grabbed.emit(pixmap2)
class KEyesWidget(QWidget): update_interval = 50 # ms faces = { "Aaron": ("keyes-aaron.png", (49, 63, 12, 8), (79, 63, 12, 8)), "Adrian": ("keyes-adrian.png", (46, 67, 11, 6), (74, 68, 11, 6)), "Cornelius": ("keyes-cornelius.png", (49, 68, 11, 6), (79, 68, 11, 6)), "Eva": ("keyes-eva.png", (51, 63, 12, 6), (83, 63, 12, 6)), "Sebastian": ("keyes-sebastian.png", (50, 58, 14, 7), (83, 58, 14, 7)), } def __init__(self): QLabel.__init__(self) self.setWindowFlags(self.windowFlags() | Qt.FramelessWindowHint) self.setAttribute(Qt.WA_NoSystemBackground) self.setMouseTracking(True) self.dragPosition = QPoint(0, 0) self.mousePosition = QCursor.pos() self.actionFaces = QActionGroup(self) allFaceActions = [] for name in sorted(self.faces): action = QAction(name, self.actionFaces) action.setCheckable(True) allFaceActions.append(action) self.actionFaces.triggered.connect(self.actionUpdateFace) startAction = random.choice(allFaceActions) startAction.setChecked(True) self.actionUpdateFace(startAction) self.actionQuit = QAction("Quit", self) self.actionQuit.triggered.connect(QApplication.instance().quit) self.timer = QTimer() self.timer.timeout.connect(self.updateFromMousePosition) self.timer.start(self.update_interval) self.painter = None def actionUpdateFace(self, action): self.setFace(action.text()) def setFace(self, name): self.setWindowTitle(name) self.pixmap = QPixmap(normalize_path(self.faces[name][0])) self.setWindowIcon(QIcon(self.pixmap)) self.eyes = [Eye(*self.faces[name][1]), Eye(*self.faces[name][2])] self.setMask(self.pixmap.createHeuristicMask()) if self.isVisible(): self.update() def updateFromMousePosition(self): newPosition = QCursor.pos() if newPosition == self.mousePosition: return self.mousePosition = newPosition if self.isVisible(): self.update() def mousePressEvent(self, event): if event.button() == Qt.LeftButton: self.dragPosition = event.globalPos() - self.frameGeometry().topLeft() event.accept() def mouseMoveEvent(self, event): if event.buttons() == Qt.LeftButton: self.move(event.globalPos() - self.dragPosition) event.accept() def contextMenuEvent(self, event): menu = QMenu(self) menu.addActions(self.actionFaces.actions()) menu.addSeparator() menu.addAction(self.actionQuit) menu.exec_(event.globalPos()) def paintEvent(self, event): painter = QPainter(self) self.painter = painter painter.drawPixmap(QPoint(0, 0), self.pixmap) for eye in self.eyes: eye.render(self) self.painter = None def sizeHint(self): return self.pixmap.size()