class MainWindow(QWidget): def draw(self, mainlineMove): self.i += 1 self.chessboard.push(mainlineMove) self.chessboardSvg = chess.svg.board(self.chessboard).encode('UTF-8') self.widgetSvg.load(self.chessboardSvg) def __init__(self, game): super().__init__() self.i = 0 if game == None: self.chessboard = chess.Board() else: self.chessboard = game.board() self.MOVES = [x for x in game.mainline_moves()] print(self.MOVES) self.setGeometry(100, 100, 1000, 1000) self.widgetSvg = QSvgWidget(parent=self) self.widgetSvg.setGeometry(10, 10, 960, 960) self.button = QPushButton("Next Move") self.button.clicked.connect(self.draw(self.MOVES[self.i])) self.chessboardSvg = chess.svg.board(self.chessboard).encode("UTF-8") self.widgetSvg.load(self.chessboardSvg)
def visualizeAI(self): svgBytes = self.ai.visualize() dialog = QDialog(self) dialog.setWindowTitle("AI Algorithm Visualization") svgWidget = QSvgWidget(dialog) svgWidget.load(QByteArray(svgBytes)) dialog.show()
class MainWindow(QWidget): def __init__(self): super().__init__() self.setGeometry(100, 100, 1000, 1000) self.widgetSvg = QSvgWidget(parent=self) self.widgetSvg.setGeometry(10, 10, 900, 900) self.chessboard = chess.Board() def play(self, ai_params): while not self.chessboard.is_game_over(): self.widgetSvg.load( chess.svg.board(self.chessboard, flipped=True).encode("UTF-8")) if self.chessboard.turn: print("Whites turn") self.chessboard.push(findBestMove(ai_params, self.chessboard)) else: print("Blacks turn") while True: legal_moves = [ str(move) for move in list(self.chessboard.legal_moves) ] move = input("Your move: ") if move in legal_moves: self.chessboard.push(chess.Move.from_uci(move)) break else: print(move, "This is not a legal move!") print("Legal moves are: ", legal_moves) self.widgetSvg.load( chess.svg.board(self.chessboard, flipped=True).encode("UTF-8")) print("Game Over")
def __init__(self, cover_path, figure_info, figure_title, figure_score, figure_desc, figure_count, video_url, cover_url, img_path, *args, **kwargs): super(ItemWidget, self).__init__(*args, **kwargs) self.setMaximumSize(220, 380) self.setMaximumSize(220, 380) self.img_path = img_path self.cover_url = cover_url layout = QVBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) # 图片label self.clabel = CoverLabel(cover_path, figure_info, video_url, self) layout.addWidget(self.clabel) # 片名和分数 flayout = QHBoxLayout() flayout.addWidget(QLabel(figure_title, self)) flayout.addItem(QSpacerItem( 20, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)) flayout.addWidget(QLabel(figure_score, self, styleSheet="color: red;")) layout.addLayout(flayout) # 主演 layout.addWidget( QLabel(figure_desc, self, styleSheet="color: #999999;", openExternalLinks=True)) # 播放量 blayout = QHBoxLayout() count_icon = QSvgWidget(self) count_icon.setMaximumSize(16, 16) count_icon.load(Svg_icon_play_sm) blayout.addWidget(count_icon) blayout.addWidget( QLabel(figure_count, self, styleSheet="color: #999999;")) layout.addLayout(blayout)
class MyWidget(QtWidgets.QWidget): def __init__(self): """ This is the main window from my GUI """ super().__init__() # Generate chess board self.setGeometry(0, 0, 620, 620) self.widgetSvg = QSvgWidget(parent=self) self.widgetSvg.setGeometry(10, 10, 600, 600) self.chessboard = chess.Board() self.chessboardSvg = chess.svg.board(self.chessboard).encode("UTF-8") self.widgetSvg.load(self.chessboardSvg) # configure and start thrread self.displayer_chess_game = ChessEngineBackend() self.thread = QtCore.QThread(self) self.displayer_chess_game.move.connect(self.move_callback) # NOQA self.displayer_chess_game.moveToThread(self.thread) self.thread.started.connect( self.displayer_chess_game.chess_engine_backend) # NOQA self.thread.start() @QtCore.pyqtSlot(str) def move_callback(self, move): """ """ self.chessboard.push(chess.Move.from_uci(move)) self.chessboardSvg = chess.svg.board(self.chessboard).encode("UTF-8") self.widgetSvg.load(self.chessboardSvg)
def __example_usage_latex_to_svg(): plt.rc('mathtext', fontset='cm') latex = r'$1 + \dfrac{1}{1 + \dfrac{1}{3}}$' app = QApplication(sys.argv) svg = QSvgWidget() svg.load(latex_to_svg(latex)) svg.show() sys.exit(app.exec_())
class BoardView(QWidget): def __init__(self, svg_board, **kwargs): super().__init__(**kwargs) self.svgWidget = QSvgWidget() layout = EvenLayout(self) layout.addWidget(self.svgWidget, 0, 0) self.press_pos = None self.refresh(svg_board) def refresh(self, svg_board): data = createQByteArray(svg_board) self.svgWidget.load(data) def mousePressEvent(self, event): self.press_pos = self.calculate_board_position(event) def mouseReleaseEvent(self, event): if self.press_pos: release_pos = self.calculate_board_position(event) if release_pos: piece = self.parent().game.get_piece_at_pos(self.press_pos) move = self.press_pos + release_pos if ((piece == 'P' and release_pos[1] == '8' and self.press_pos[1] == '7' and self.parent().game.board.turn) or (piece == 'p' and release_pos[1] == '1' and self.press_pos[1] == '2' and not self.parent().game.board.turn)): # Promotion # TODO: player input - await, asyncio move += 'q' elif self.press_pos == release_pos: move = '0000' # uci null move self.parent().execute_game_command(move) def mouseMoveEvent(self, event): # TODO: Implement drag piece-icon pass # Returns a tuple with the board position as uci and the piece at that position, # or None. def calculate_board_position(self, event): margin = self.svgWidget.size() * 0.05 + QSize(2, 2) square = (self.svgWidget.size() - 2 * margin) / 8 offset = (self.size() - self.svgWidget.size()) / 2. + margin x_coordinate = event.pos().x() - offset.width() y_coordinate = offset.height() + 8 * square.height() - event.pos().y() x_pos = int(1 + x_coordinate / square.width()) y_pos = int(1 + y_coordinate / square.height()) if (x_pos > 8 or x_pos < 1 or y_pos > 8 or y_pos < 1): return None else: return 'abcdefgh'[x_pos - 1] + str(y_pos)
def main(): FORMULA = r'\int_{-\infty}^\infty e^{-x^2}\,dx = \sqrt{\pi}' app = QApplication(sys.argv) svg = QSvgWidget() svg.load(tex2svg(FORMULA)) svg.show() sys.exit(app.exec_())
class GraphWindow(QMainWindow): """ Qt application window showing a single vector graph. Save graph using right click menu. Attributes: _graph_display Widget that shows the graph image _svg_bytes SVG bytes representing currently displayed image """ window_width = 600 # type: float # Height is computed from aspect ratio def __init__(self, graph_svg: bytes = None, aspect_ratio: float = 1.0) -> None: super().__init__() self._svg_bytes = bytes() # Add image widget: self._graph_display = QSvgWidget() self.setCentralWidget(self._graph_display) if graph_svg is not None: self.set_graph(graph_svg) self._graph_display.setContextMenuPolicy(Qt.CustomContextMenu) self._graph_display.customContextMenuRequested.connect( self._show_context_menu) self._graph_display.setToolTip("Right-click to save image") self._graph_display.setToolTipDuration(2000) # Set correct aspect ratio: self.setFixedSize(self.window_width, self.window_width * aspect_ratio) def _show_context_menu(self, point: QPoint) -> None: """ Show a context menu for the graph display. """ menu = QMenu(self) save_action = QAction("Save current image...", menu) save_action.triggered.connect( lambda checked: self._save_current_image()) menu.addAction(save_action) menu.exec(self._graph_display.mapToGlobal(point)) def _save_current_image(self): """ Copy current image and save it to a file. """ current_svg = deepcopy(self._svg_bytes) # type: bytes file_path = QFileDialog.getSaveFileUrl()[0].path() # type: str with open(file_path, "w") as outfile: outfile.write(current_svg.decode()) @pyqtSlot(bytes) def set_graph(self, graph_svg: bytes) -> None: """Renders SVG bytes in the graph display.""" self._graph_display.load(graph_svg) self._svg_bytes = graph_svg
class SvgButton(QPushButton): def __init__(self, parent, svg_path): QWidget.__init__(self, parent) self.svg = QSvgWidget(self) self.svg.load(svg_path) self.setFocusPolicy(Qt.NoFocus) def setSize(self, w, h) -> None: self.resize(w, h) self.svg.resize(w, h) def load_svg(self, svg_path): self.svg.load(svg_path)
class MainWindow(QWidget): def __init__(self, board): super().__init__() self.setGeometry(100, 100, 520, 520) self.widgetSvg = QSvgWidget(parent=self) self.widgetSvg.setGeometry(10, 10, 500, 500) self.set_board(board) def set_board(self, board): chessboardSvg = chess.svg.board(board).encode("UTF-8") self.widgetSvg.load(chessboardSvg)
class DotWidget(QWidget): def __init__(self, path:str, gui): super().__init__() self.gui = gui self.label = QLabel(f"Dot Dump:") self.display = QSvgWidget() self.display.load(path) self.layout = QVBoxLayout() self.layout.addWidget(self.label) self.layout.addWidget(self.display) self.setWindowTitle(self.gui.windowTitle()) self.setLayout(self.layout)
class MainWindow(QWidget): def __init__(self): super().__init__() self.setGeometry(100, 100, 1100, 1100) self.widgetSvg = QSvgWidget(parent=self) self.widgetSvg.setGeometry(10, 10, 1080, 1080) self.chessboard = chess.Board() self.chessboardSvg = chess.svg.board(self.chessboard).encode("UTF-8") self.widgetSvg.load(self.chessboardSvg) def paintEvent(self, event): self.chessboardSvg = chess.svg.board(self.chessboard).encode("UTF-8") self.widgetSvg.load(self.chessboardSvg)
class ScrollWindow(QScrollArea): def __init__(self, *args, **kwargs): super(ScrollWindow, self).__init__(*args, **kwargs) # self.setAcceptDrops(True) # 2 self.resize(800, 600) self.setFrameShape(self.NoFrame) self.setWidgetResizable(True) self.setAlignment(Qt.AlignCenter) self._loadStart = False # 网格窗口 self._widget = GridWidget(self) self._widget.loadStarted.connect(self.setLoadStarted) self.setWidget(self._widget) #################################################### # 连接竖着的滚动条滚动事件 # self.verticalScrollBar().actionTriggered.connect(self.onActionTriggered) #################################################### # 进度条 self.loadWidget = QSvgWidget(self, minimumHeight=180, minimumWidth=180, visible=False) self.loadWidget.load(Svg_icon_loading) def setLoadStarted(self, started): self._loadStart = started self.loadWidget.setVisible(started) def onActionTriggered(self, action): # 这里要判断action=QAbstractSlider.SliderMove,可以避免窗口大小改变的问题 # 同时防止多次加载同一个url if action != QAbstractSlider.SliderMove or self._loadStart: return # 使用sliderPosition获取值可以同时满足鼠标滑动和拖动判断 if self.verticalScrollBar().sliderPosition() == self.verticalScrollBar( ).maximum(): # 可以下一页了 self._widget.load() def resizeEvent(self, event): super(ScrollWindow, self).resizeEvent(event) self.loadWidget.setGeometry( int((self.width() - self.loadWidget.minimumWidth()) / 2), int((self.height() - self.loadWidget.minimumHeight()) / 2), self.loadWidget.minimumWidth(), self.loadWidget.minimumHeight())
class MainWindow(QWidget): def __init__(self): super().__init__() self.movehistory = [] self.game = chess.pgn.Game() self.setGeometry(1050, 200, 500, 500) self.widgetSvg = QSvgWidget(parent=self) self.widgetSvg.setGeometry(0, 0, 500, 500) self.boardSize = min(self.widgetSvg.width(), self.widgetSvg.height()) self.squareSize = self.boardSize / 8.0 self.pieceToMove = [None, None] self.board = chess.Board() self.drawBoard() @pyqtSlot(QWidget) def mousePressEvent(self, event): if self.board.is_game_over(claim_draw=True) is False: if not self.board.turn: move = select_move(self.board, DEPTH) self.board.push(move) self.movehistory.append(move) elif event.x() <= self.boardSize and event.y() <= self.boardSize: if event.buttons() == Qt.LeftButton: file = int((event.x()) / self.squareSize) rank = 7 - int((event.y()) / self.squareSize) square = chess.square(file, rank) piece = self.board.piece_at(square) coordinates = "{}{}".format(chr(file + 97), str(rank + 1)) if self.pieceToMove[0] is not None: move = chess.Move.from_uci("{}{}".format( self.pieceToMove[1], coordinates)) if move in self.board.legal_moves: self.board.push(move) self.movehistory.append(move) piece = None coordinates = None self.pieceToMove = [piece, coordinates] self.drawBoard() else: self.game.add_line(self.movehistory) print(self.game, file=open("gameresult.pgn", "w"), end="\n\n") print("Game over") exit() def drawBoard(self): lastmove = self.board.peek() if self.board.move_stack else None self.boardSvg = chess.svg.board(board=self.board, lastmove=lastmove).encode("UTF-8") self.drawBoardSvg = self.widgetSvg.load(self.boardSvg) return self.boardSvg
class MainWindow(QWidget): def __init__(self): super().__init__() self.setGeometry(100, 100, 850, 850) self.widgetSvg = QSvgWidget(parent=self) self.widgetSvg.setGeometry(10, 10, 800, 800) self.board = chess.Board() #'8/8/8/rnbqk3/ppppp3/8/PPPPP3/RNBQK3') self.boardSvg = chess.svg.board(self.board).encode("UTF-8") self.widgetSvg.load(self.boardSvg) def reload(self): self.boardSvg = chess.svg.board(self.board).encode("UTF-8") self.widgetSvg.load(self.boardSvg) def replaceBoard(self, board): self.board = board
def __init_ui(self) -> NoReturn: self.setFixedSize(120, 60) self.setAttribute(Qt.WA_DeleteOnClose) self.setObjectName("item") self.setStyleSheet(""" QFrame#item { border: 2px solid black; border-radius: 5px; background-color: """ + self._enum.background_color + """; } """) icon = QSvgWidget(self) icon.load(self._enum.path) icon.setFixedSize(40, 40) layout = QHBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.addWidget(icon) self.setLayout(layout)
class DownloadSpeedWidget(QWidget): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.downloaded_bytes = 0 self.total_bytes = 0 self.timer = QTimer() self.timer.start(1000) self.timer.timeout.connect(self.monitor_download_show) self.layout = QHBoxLayout() self.layout.setContentsMargins(0, 0, 0, 0) self.setLayout(self.layout) self.text = QLabel() self.set_text() size = self.text.fontMetrics().height() self.icon = QSvgWidget() self.icon.load(os.path.join(ASSETS_PATH, "speed_download.svg")) self.icon.setFixedSize(size * 0.7, size * 0.7) self.layout.addWidget(self.icon) self.layout.addWidget(self.text) def set_text(self): self.text.setText( f"{format_bytes(self.downloaded_bytes)}/s ({format_bytes(self.total_bytes)})" ) def reset(self): self.total_bytes = 0 def monitor_download(self, size): self.downloaded_bytes += size self.total_bytes += size def monitor_download_show(self): self.set_text() self.downloaded_bytes = 0
def get_svg_widget(self, element: Gui_Element, height: int, width: int, fill: str = "#FF9933", text: str = "00") -> QSvgWidget: svg = self.read_svg_from_file(element) svg = self.interpolate_svg(svg, fill, text) svg_bytes = bytearray(svg, encoding='utf-8') svg_widget = QSvgWidget() svg_widget.load(svg_bytes) #svg_widget.renderer().load(svg_bytes) if height > 0 and width > 0: size = QSize(width, height) svg_widget.setFixedSize(size) #else: # svg_widget.setFixedSize(svg_widget.renderer().defaultSize()) # svg_widget.setFixedSize(svg_w) return svg_widget
class MainWindow(QWidget): def __init__(self): super().__init__() self.setWindowTitle("PinkFish") self.setGeometry(400, 400, 1300, 1300) self.widgetSvg = QSvgWidget(self) self.widgetSvg.setGeometry(20, 20, 1200, 1200) self.chessboard = chess.Board() self.chessboardSvg = chess.svg.board(self.chessboard).encode("UTF-8") self.checkThreadTimer = QTimer(self) self.checkThreadTimer.setInterval(1000) self.checkThreadTimer.start() self.checkThreadTimer.timeout.connect(self.play) @pyqtSlot(QWidget) def mousePressEvent(self, event): if event.buttons() == Qt.LeftButton: self.update() @pyqtSlot(QWidget) def paintEvent(self, event): self.chessboardSvg = chess.svg.board(self.chessboard).encode("UTF-8") self.widgetSvg.load(self.chessboardSvg) def play(self): dirname = os.path.dirname(__file__) filename = os.path.join(dirname, 'stockfish-9-win\Windows\stockfish_9_x64') stockfish = Stockfish(filename) stockfish.set_skill_level(15) if not self.chessboard.is_game_over() and not self.chessboard.is_stalemate(): board = self.chessboard.fen() stockfish.set_fen_position(board) ai_move = stockfish.get_best_move() move = chess.Move.from_uci(ai_move) print(move) self.chessboard.push(move) self.update()
class MainWindow(QWidget): def __init__(self): super().__init__() self.setGeometry(100, 100, 500, 500) self.widgetSvg = QSvgWidget(parent=self) self.widgetSvg.setGeometry(10, 10, 480, 480) self.chessboard = chess.Board() self.chessboardSvg = chess.svg.board(self.chessboard).encode("UTF-8") self.widgetSvg.load(self.chessboardSvg) def paintEvent(self, event): self.game_over() self.rand() self.tela_jog() def tela_jog(self): self.chessboardSvg = chess.svg.board(self.chessboard).encode("UTF-8") self.widgetSvg.load(self.chessboardSvg) def game_over(self): if self.chessboard.is_game_over() == True: print(self.chessboard.outcome()) input() exit() def rand(self): legal_count = self.chessboard.legal_moves.count() move_list = list(self.chessboard.legal_moves) # Find legal moves which_move = random.randint(0, legal_count - 1) # Random move index first_move = move_list[which_move] # Select move move_holder = chess.Move.from_uci(str(first_move)) self.chessboard.push(move_holder) time.sleep(0.5)
class Window(QWidget): def __init__(self, left=100, top=100, width=500, height=500): super().__init__() self.left = max(0, left) self.top = max(0, top) self.width = max(20, width) self.height = max(20, height) self.update_geometry() self.widgetSvg = QSvgWidget(parent=self) self.widgetSvg.setGeometry(10, 10, self.width - 20, self.height - 20) def update_geometry(self): self.setGeometry(self.left, self.top, self.width, self.height) def display_svg(self, svg): self.widgetSvg.load(svg) self.repaint() def add_button(self, text, on_click): btn = QPushButton(text, self) self.height = self.height + 60 self.update_geometry() btn.move(int(self.width / 2) - 50, self.height - 50) btn.clicked.connect(on_click)
class Icon(QWidget): def __init__(self, parent, icon, size=None, padding=None, color=None): QWidget.__init__(self, parent) padding = padding or 0 self._theme_manager = ThemeManager.get(self) self._color = format_color( color or self._theme_manager.get_color('button_foreground'), ColorFormat.rgb_string_256) self._svgdoc = QDomDocument() self._icon_widget = QSvgWidget(self) self.loadIcon(icon) if size: self._icon_widget.setFixedSize( QSize(size.width() - 2 * padding, size.height() - 2 * padding)) self._layout = QHBoxLayout(self) self._layout.setContentsMargins(padding, padding, padding, padding) self._layout.addWidget(self._icon_widget, Qt.AlignCenter) def loadIcon(self, icon): file = QFile(f'icons:{icon}') file.open(QFile.ReadOnly) self._svgdoc.setContent(file.readAll()) self._svgdoc.documentElement().setAttribute('fill', self._color) file.close() self._icon_widget.load(self._svgdoc.toByteArray()) def setEnabled(self, enabled): QWidget.setEnabled(self, enabled) self._svgdoc.documentElement().setAttribute('fill', self._color) self._svgdoc.documentElement().setAttribute( 'fill-opacity', '1' if self.isEnabled() else '0.4') self._icon_widget.load(self._svgdoc.toByteArray()) def setColor(self, color=None): color = format_color( color or self._theme_manager.get_color('button_foreground'), ColorFormat.rgb_string_256) or self._color self._svgdoc.documentElement().setAttribute('fill', color) self._svgdoc.documentElement().setAttribute( 'fill-opacity', '1' if self.isEnabled() else '0.4') self._icon_widget.load(self._svgdoc.toByteArray())
class Result(QWidget): def __init__(self, parent=None): super(Result, self).__init__(parent) self.capture = Capture(None) self.draw = Canvas(None) self.setAcceptDrops(True) self.setWindowIcon(QIcon("favicon.ico")) # region WindowSettings self.setFixedWidth(600) self.setWindowTitle("MathPix+") self.setWindowFlags(Qt.MSWindowsFixedSizeDialogHint | Qt.WindowCloseButtonHint | Qt.WindowStaysOnTopHint) # endregion # region Svg self.svg_container = QWidget() self.svg_container.setFixedSize(QSize(578, 200)) self.svg = QSvgWidget(self.svg_container) # endregion # region Info Label self.label = QLabel() self.label.setFont(QFont("等线", 20)) self.label.setMaximumHeight(30) self.label.setAlignment(Qt.AlignCenter) # endregion # region Image self.img = QLabel() self.img.setFixedSize(578, 200) self.img.setAlignment(Qt.AlignCenter) # endregion # region TeX LineEdit self.tex = QLineEdit() self.tex.setAlignment(Qt.AlignCenter) self.tex.setFont(QFont("Cambria Math", 20)) self.tex.setMaximumHeight(60) self.tex.setPlaceholderText("Enter Your Tex Here") self.tex.setEchoMode(QLineEdit.Normal) self.tex.textChanged.connect(self.on_tex_changed) # endregion # region PushButtons self.save_as_raw_tex = QPushButton("&Raw") self.save_as_raw_tex.setFixedHeight(40) self.save_as_raw_tex.setFont(QFont("等线", 20)) self.save_as_raw_tex.clicked.connect( lambda: self.copy_tex_to_clipboard("")) self.save_as_inline_tex = QPushButton("&Inline") self.save_as_inline_tex.setFixedHeight(40) self.save_as_inline_tex.setFont(QFont("等线", 20)) self.save_as_inline_tex.clicked.connect( lambda: self.copy_tex_to_clipboard("$")) self.save_as_block_tex = QPushButton("&Block") self.save_as_block_tex.setFixedHeight(40) self.save_as_block_tex.setFont(QFont("等线", 20)) self.save_as_block_tex.clicked.connect( lambda: self.copy_tex_to_clipboard("$$")) self.open_img = QPushButton("&Open") self.open_img.setFixedHeight(40) self.open_img.setFont(QFont("等线", 20)) self.open_img.clicked.connect(lambda: self.get_tex(self.get_img())) self.snap_img = QPushButton("&Snap") self.snap_img.setFixedHeight(40) self.snap_img.setFont(QFont("等线", 20)) self.snap_img.clicked.connect(self.capture_img) self.draw_img = QPushButton("&Draw") self.draw_img.setFixedHeight(40) self.draw_img.setFont(QFont("等线", 20)) self.draw_img.clicked.connect(self.canvas_img) # endregion # region Layout self.copy_hlo = QHBoxLayout() self.open_hlo = QHBoxLayout() self.copy_hlo.addWidget(self.save_as_raw_tex) self.copy_hlo.addWidget(self.save_as_inline_tex) self.copy_hlo.addWidget(self.save_as_block_tex) self.open_hlo.addWidget(self.open_img) self.open_hlo.addWidget(self.snap_img) self.open_hlo.addWidget(self.draw_img) self.vlo = QVBoxLayout() self.vlo.addWidget(self.svg_container) self.vlo.addWidget(self.img) self.vlo.addWidget(self.label) self.vlo.addWidget(self.tex) self.vlo.addLayout(self.copy_hlo) self.vlo.addLayout(self.open_hlo) # endregion self.get_tex("") self.setLayout(self.vlo) def on_tex_changed(self): try: parser = math_text.MathTextParser('svg') parser.parse(r"$" + self.tex.text() + r"$") except ValueError: self.label.setText("TeX语法不正确") else: self.label.setText('') self.generate_svg(self.tex.text()) def copy_tex_to_clipboard(self, string): clipboard = QApplication.clipboard() clipboard.setText(string + self.tex.text() + string) self.label.setText("TeX已复制至剪贴板") def generate_svg(self, raw_tex): fig = Figure(figsize=(5, 4), dpi=300) canvas = FigureCanvasAgg(fig) fig.text(.5, .5, r"$" + raw_tex + r"$", fontsize=40) fig.savefig("output.svg", bbox_inches="tight", facecolor=(1, 1, 1, 0)) self.svg.load("output.svg") renderer = QSvgRenderer('output.svg').defaultSize() w = renderer.width() h = renderer.height() if w / h > 578 / 200: display_w = 578 display_h = int(578 * h / w) else: display_h = 200 display_w = int(200 * w / h) self.svg.setFixedWidth(display_w) self.svg.setFixedHeight(display_h) self.svg.setGeometry( QRect(289 - int(display_w / 2), 100 - int(display_h / 2), display_w, display_h)) def get_img(self): file_name, file_type = QFileDialog.getOpenFileName( self, "选取图片", "./", "所有文件 (*);;图片文件 (*.jpg *.png)") print(file_name, file_type) return file_name def get_tex(self, url=r"limit.jpg"): if url == "": self.set_data("limit.jpg", r"\lim_{x\rightarrow3}(\frac{x^{2}+9}{x-3})", 1) return with open(url, 'rb') as pic: base64_data = base64.b64encode(pic.read()) print(base64_data) img_url = "data:image/jpg;base64," + base64_data.decode() r = requests.post("https://api.mathpix.com/v3/latex", data=json.dumps({'url': img_url}), headers={ "app_id": "******", "app_key": "********************************", "Content-type": "application/json" }) print(json.dumps(json.loads(r.text), indent=4, sort_keys=True)) try: raw_data = json.loads(r.text) except AttributeError: return else: if "latex" in raw_data: tex = raw_data["latex"] else: return if "latex_confidence" in raw_data: confidence = raw_data["latex_confidence"] else: confidence = 1 self.set_data(url, tex, confidence) def set_data(self, img, tex, con): raw_img = QPixmap(img) w = raw_img.width() h = raw_img.height() if w / h > 578 / 200: self.img.setPixmap( raw_img.scaledToWidth(578, Qt.SmoothTransformation)) else: self.img.setPixmap( raw_img.scaledToHeight(200, Qt.SmoothTransformation)) tex_data = tex tex_data = tex_data.replace(r"\\", "\\") tex_data = tex_data.replace(' ', '') self.tex.setText(tex_data) self.generate_svg(tex_data) if con < 0.8: self.label.setText("置信值低于0.8, 建议进行人工校对 : ") def dragEnterEvent(self, event): if event.mimeData().hasUrls: event.accept() else: event.ignore() def dragMoveEvent(self, event): if event.mimeData().hasUrls: event.setDropAction(Qt.CopyAction) event.accept() else: event.ignore() def dropEvent(self, event): print("dropped") url = str(event.mimeData().urls()[0].toLocalFile()) print(url) self.get_tex(url) def capture_img(self): self.capture.grab_new_img() self.capture.show() self.capture.captured.connect(lambda: self.get_tex("capture.jpg")) def canvas_img(self): self.draw.show() self.draw.drawn.connect(lambda: self.get_tex("canvas.jpg"))
class Window(QListWidget): Page = 0 def __init__(self, *args, **kwargs): super(Window, self).__init__(*args, **kwargs) self.resize(800, 600) self.setFrameShape(self.NoFrame) # 无边框 self.setFlow(self.LeftToRight) # 从左到右 self.setWrapping(True) # 这三个组合可以达到和FlowLayout一样的效果 self.setResizeMode(self.Adjust) self._loadStart = False # 连接竖着的滚动条滚动事件 self.verticalScrollBar().actionTriggered.connect( self.onActionTriggered) # 进度条 self.loadWidget = QSvgWidget(self, minimumHeight=120, minimumWidth=120, visible=False) self.loadWidget.load(Svg_icon_loading) # 异步网络下载管理器 self._manager = QNetworkAccessManager(self) self._manager.finished.connect(self.onFinished) def load(self): if self.Page == -1: return self._loadStart = True self.loadWidget.setVisible(True) # 延迟一秒后调用目的在于显示进度条 QTimer.singleShot(1000, self._load) def _load(self): print("load url:", Url.format(self.Page * 30)) url = QUrl(Url.format(self.Page * 30)) self._manager.get(QNetworkRequest(url)) def onFinished(self, reply): # 请求完成后会调用该函数 req = reply.request() # 获取请求 iwidget = req.attribute(QNetworkRequest.User + 1, None) path = req.attribute(QNetworkRequest.User + 2, None) html = reply.readAll().data() reply.deleteLater() del reply if iwidget and path and html: # 这里是图片下载完毕 open(path, "wb").write(html) iwidget.setCover(path) return # 解析网页 self._parseHtml(html) self._loadStart = False self.loadWidget.setVisible(False) def _parseHtml(self, html): # encoding = chardet.detect(html) or {} # html = html.decode(encoding.get("encoding","utf-8")) html = HTML(html) # 查找所有的li list_item lis = html.xpath("//li[@class='list_item']") if not lis: self.Page = -1 # 后面没有页面了 return self.Page += 1 self._makeItem(lis) def _makeItem(self, lis): for li in lis: a = li.find("a") video_url = a.get("href") # 视频播放地址 img = a.find("img") cover_url = "http:" + img.get("r-lazyload") # 封面图片 figure_title = img.get("alt") # 电影名 figure_info = a.find("div/span") figure_info = "" if figure_info is None else figure_info.text # 影片信息 figure_score = "".join(li.xpath(".//em/text()")) # 评分 # 主演 figure_desc = "<span style=\"font-size: 12px;\">主演:</span>" + \ "".join([Actor.format(**dict(fd.items())) for fd in li.xpath(".//div[@class='figure_desc']/a")]) # 播放数 figure_count = ( li.xpath(".//div[@class='figure_count']/span/text()") or [""])[0] path = "cache/{0}.jpg".format( os.path.splitext(os.path.basename(video_url))[0]) cover_path = "Data/pic_v.png" if os.path.isfile(path): cover_path = path iwidget = ItemWidget(cover_path, figure_info, figure_title, figure_score, figure_desc, figure_count, video_url, cover_url, path, self._manager, self) item = QListWidgetItem(self) item.setSizeHint(iwidget.sizeHint()) self.setItemWidget(item, iwidget) def onActionTriggered(self, action): # 这里要判断action=QAbstractSlider.SliderMove,可以避免窗口大小改变的问题 # 同时防止多次加载同一个url if action != QAbstractSlider.SliderMove or self._loadStart: return # 使用sliderPosition获取值可以同时满足鼠标滑动和拖动判断 if self.verticalScrollBar().sliderPosition() == self.verticalScrollBar( ).maximum(): # 可以下一页了 self.load() def resizeEvent(self, event): super(Window, self).resizeEvent(event) self.loadWidget.setGeometry( int((self.width() - self.loadWidget.minimumWidth()) / 2), int((self.height() - self.loadWidget.minimumHeight()) / 2), self.loadWidget.minimumWidth(), self.loadWidget.minimumHeight())
class ScalableDial(QDial): # enum CustomPaintMode CUSTOM_PAINT_MODE_NULL = 0 # default (NOTE: only this mode has label gradient) CUSTOM_PAINT_MODE_CARLA_WET = 1 # color blue-green gradient (reserved #3) CUSTOM_PAINT_MODE_CARLA_VOL = 2 # color blue (reserved #3) CUSTOM_PAINT_MODE_CARLA_L = 3 # color yellow (reserved #4) CUSTOM_PAINT_MODE_CARLA_R = 4 # color yellow (reserved #4) CUSTOM_PAINT_MODE_CARLA_PAN = 5 # color yellow (reserved #3) CUSTOM_PAINT_MODE_COLOR = 6 # color, selectable (reserved #3) CUSTOM_PAINT_MODE_ZITA = 7 # custom zita knob (reserved #6) CUSTOM_PAINT_MODE_NO_GRADIENT = 8 # skip label gradient # enum Orientation HORIZONTAL = 0 VERTICAL = 1 HOVER_MIN = 0 HOVER_MAX = 9 MODE_DEFAULT = 0 MODE_LINEAR = 1 # signals dragStateChanged = pyqtSignal(bool) realValueChanged = pyqtSignal(float) def __init__(self, parent, index=0): QDial.__init__(self, parent) self.fDialMode = self.MODE_LINEAR self.fMinimum = 0.0 self.fMaximum = 1.0 self.fRealValue = 0.0 self.fPrecision = 10000 self.fIsInteger = False self.fIsHovered = False self.fIsPressed = False self.fHoverStep = self.HOVER_MIN self.fLastDragPos = None self.fLastDragValue = 0.0 self.fIndex = index self.fImage = QSvgWidget(":/scalable/dial_03.svg") self.fImageNum = "01" if self.fImage.sizeHint().width() > self.fImage.sizeHint().height(): self.fImageOrientation = self.HORIZONTAL else: self.fImageOrientation = self.VERTICAL self.fLabel = "" self.fLabelPos = QPointF(0.0, 0.0) self.fLabelFont = QFont(self.font()) self.fLabelFont.setPixelSize(8) self.fLabelWidth = 0 self.fLabelHeight = 0 if self.palette().window().color().lightness() > 100: # Light background c = self.palette().dark().color() self.fLabelGradientColor1 = c self.fLabelGradientColor2 = QColor(c.red(), c.green(), c.blue(), 0) self.fLabelGradientColorT = [ self.palette().buttonText().color(), self.palette().mid().color() ] else: # Dark background self.fLabelGradientColor1 = QColor(0, 0, 0, 255) self.fLabelGradientColor2 = QColor(0, 0, 0, 0) self.fLabelGradientColorT = [Qt.white, Qt.darkGray] self.fLabelGradient = QLinearGradient(0, 0, 0, 1) self.fLabelGradient.setColorAt(0.0, self.fLabelGradientColor1) self.fLabelGradient.setColorAt(0.6, self.fLabelGradientColor1) self.fLabelGradient.setColorAt(1.0, self.fLabelGradientColor2) self.fLabelGradientRect = QRectF(0.0, 0.0, 0.0, 0.0) self.fCustomPaintMode = self.CUSTOM_PAINT_MODE_NULL self.fCustomPaintColor = QColor(0xff, 0xff, 0xff) self.updateSizes() # Fake internal value, custom precision QDial.setMinimum(self, 0) QDial.setMaximum(self, self.fPrecision) QDial.setValue(self, 0) self.valueChanged.connect(self.slot_valueChanged) def getIndex(self): return self.fIndex def getBaseSize(self): return self.fImageBaseSize def forceWhiteLabelGradientText(self): self.fLabelGradientColor1 = QColor(0, 0, 0, 255) self.fLabelGradientColor2 = QColor(0, 0, 0, 0) self.fLabelGradientColorT = [Qt.white, Qt.darkGray] def setLabelColor(self, enabled, disabled): self.fLabelGradientColor1 = QColor(0, 0, 0, 255) self.fLabelGradientColor2 = QColor(0, 0, 0, 0) self.fLabelGradientColorT = [enabled, disabled] def updateSizes(self): if isinstance(self.fImage, QPixmap): self.fImageWidth = self.fImage.width() self.fImageHeight = self.fImage.height() else: self.fImageWidth = self.fImage.sizeHint().width() self.fImageHeight = self.fImage.sizeHint().height() if self.fImageWidth < 1: self.fImageWidth = 1 if self.fImageHeight < 1: self.fImageHeight = 1 if self.fImageOrientation == self.HORIZONTAL: self.fImageBaseSize = self.fImageHeight self.fImageLayersCount = self.fImageWidth / self.fImageHeight else: self.fImageBaseSize = self.fImageWidth self.fImageLayersCount = self.fImageHeight / self.fImageWidth self.setMinimumSize(self.fImageBaseSize, self.fImageBaseSize + self.fLabelHeight + 5) self.setMaximumSize(self.fImageBaseSize, self.fImageBaseSize + self.fLabelHeight + 5) if not self.fLabel: self.fLabelHeight = 0 self.fLabelWidth = 0 return self.fLabelWidth = QFontMetrics(self.fLabelFont).width(self.fLabel) self.fLabelHeight = QFontMetrics(self.fLabelFont).height() self.fLabelPos.setX( float(self.fImageBaseSize) / 2.0 - float(self.fLabelWidth) / 2.0) if self.fImageNum in ("01", "02", "07", "08", "09", "10"): self.fLabelPos.setY(self.fImageBaseSize + self.fLabelHeight) elif self.fImageNum in ("11", ): self.fLabelPos.setY(self.fImageBaseSize + self.fLabelHeight * 2 / 3) else: self.fLabelPos.setY(self.fImageBaseSize + self.fLabelHeight / 2) self.fLabelGradient.setStart(0, float(self.fImageBaseSize) / 2.0) self.fLabelGradient.setFinalStop( 0, self.fImageBaseSize + self.fLabelHeight + 5) self.fLabelGradientRect = QRectF( float(self.fImageBaseSize) / 8.0, float(self.fImageBaseSize) / 2.0, float(self.fImageBaseSize * 3) / 4.0, self.fImageBaseSize + self.fLabelHeight + 5) def setCustomPaintMode(self, paintMode): if self.fCustomPaintMode == paintMode: return self.fCustomPaintMode = paintMode self.update() def setCustomPaintColor(self, color): if self.fCustomPaintColor == color: return self.fCustomPaintColor = color self.update() def setLabel(self, label): if self.fLabel == label: return self.fLabel = label self.updateSizes() self.update() def setIndex(self, index): self.fIndex = index def setImage(self, imageId): self.fImageNum = "%02i" % imageId if imageId in (2, 6, 7, 8, 9, 10, 11, 12, 13): img = ":/bitmaps/dial_%s%s.png" % (self.fImageNum, "" if self.isEnabled() else "d") else: img = ":/scalable/dial_%s%s.svg" % (self.fImageNum, "" if self.isEnabled() else "d") if img.endswith(".png"): if not isinstance(self.fImage, QPixmap): self.fImage = QPixmap() else: if not isinstance(self.fImage, QSvgWidget): self.fImage = QSvgWidget() self.fImage.load(img) if self.fImage.width() > self.fImage.height(): self.fImageOrientation = self.HORIZONTAL else: self.fImageOrientation = self.VERTICAL # special svgs if self.fCustomPaintMode == self.CUSTOM_PAINT_MODE_NULL: # reserved for carla-wet, carla-vol, carla-pan and color if self.fImageNum == "03": self.fCustomPaintMode = self.CUSTOM_PAINT_MODE_COLOR # reserved for carla-L and carla-R elif self.fImageNum == "04": self.fCustomPaintMode = self.CUSTOM_PAINT_MODE_CARLA_L # reserved for zita elif self.fImageNum == "06": self.fCustomPaintMode = self.CUSTOM_PAINT_MODE_ZITA self.updateSizes() self.update() def setPrecision(self, value, isInteger): self.fPrecision = value self.fIsInteger = isInteger QDial.setMaximum(self, value) def setMinimum(self, value): self.fMinimum = value def setMaximum(self, value): self.fMaximum = value def rvalue(self): return self.fRealValue def setValue(self, value, emitSignal=False): if self.fRealValue == value or isnan(value): return if value <= self.fMinimum: qtValue = 0 self.fRealValue = self.fMinimum elif value >= self.fMaximum: qtValue = self.fPrecision self.fRealValue = self.fMaximum else: qtValue = round( float(value - self.fMinimum) / float(self.fMaximum - self.fMinimum) * self.fPrecision) self.fRealValue = value # Block change signal, we'll handle it ourselves self.blockSignals(True) QDial.setValue(self, qtValue) self.blockSignals(False) if emitSignal: self.realValueChanged.emit(self.fRealValue) @pyqtSlot(int) def slot_valueChanged(self, value): self.fRealValue = float(value) / self.fPrecision * ( self.fMaximum - self.fMinimum) + self.fMinimum self.realValueChanged.emit(self.fRealValue) @pyqtSlot() def slot_updateImage(self): self.setImage(int(self.fImageNum)) def minimumSizeHint(self): return QSize(self.fImageBaseSize, self.fImageBaseSize) def sizeHint(self): return QSize(self.fImageBaseSize, self.fImageBaseSize) def changeEvent(self, event): QDial.changeEvent(self, event) # Force svg update if enabled state changes if event.type() == QEvent.EnabledChange: self.slot_updateImage() def enterEvent(self, event): self.fIsHovered = True if self.fHoverStep == self.HOVER_MIN: self.fHoverStep = self.HOVER_MIN + 1 QDial.enterEvent(self, event) def leaveEvent(self, event): self.fIsHovered = False if self.fHoverStep == self.HOVER_MAX: self.fHoverStep = self.HOVER_MAX - 1 QDial.leaveEvent(self, event) def mousePressEvent(self, event): if self.fDialMode == self.MODE_DEFAULT: return QDial.mousePressEvent(self, event) if event.button() == Qt.LeftButton: self.fIsPressed = True self.fLastDragPos = event.pos() self.fLastDragValue = self.fRealValue self.dragStateChanged.emit(True) def mouseMoveEvent(self, event): if self.fDialMode == self.MODE_DEFAULT: return QDial.mouseMoveEvent(self, event) if not self.fIsPressed: return range = (self.fMaximum - self.fMinimum) / 4.0 pos = event.pos() dx = range * float(pos.x() - self.fLastDragPos.x()) / self.width() dy = range * float(pos.y() - self.fLastDragPos.y()) / self.height() value = self.fLastDragValue + dx - dy if value < self.fMinimum: value = self.fMinimum elif value > self.fMaximum: value = self.fMaximum elif self.fIsInteger: value = float(round(value)) self.setValue(value, True) def mouseReleaseEvent(self, event): if self.fDialMode == self.MODE_DEFAULT: return QDial.mouseReleaseEvent(self, event) if self.fIsPressed: self.fIsPressed = False self.dragStateChanged.emit(False) def paintEvent(self, event): painter = QPainter(self) event.accept() painter.save() painter.setRenderHint(QPainter.Antialiasing, True) if self.fLabel: if self.fCustomPaintMode == self.CUSTOM_PAINT_MODE_NULL: painter.setPen(self.fLabelGradientColor2) painter.setBrush(self.fLabelGradient) painter.drawRect(self.fLabelGradientRect) painter.setFont(self.fLabelFont) painter.setPen( self.fLabelGradientColorT[0 if self.isEnabled() else 1]) painter.drawText(self.fLabelPos, self.fLabel) if self.isEnabled(): normValue = float(self.fRealValue - self.fMinimum) / float(self.fMaximum - self.fMinimum) curLayer = int((self.fImageLayersCount - 1) * normValue) if self.fImageOrientation == self.HORIZONTAL: xpos = self.fImageBaseSize * curLayer ypos = 0.0 else: xpos = 0.0 ypos = self.fImageBaseSize * curLayer source = QRectF(xpos, ypos, self.fImageBaseSize, self.fImageBaseSize) if isinstance(self.fImage, QPixmap): target = QRectF(0.0, 0.0, self.fImageBaseSize, self.fImageBaseSize) painter.drawPixmap(target, self.fImage, source) else: self.fImage.renderer().render(painter, source) # Custom knobs (Dry/Wet and Volume) if self.fCustomPaintMode in (self.CUSTOM_PAINT_MODE_CARLA_WET, self.CUSTOM_PAINT_MODE_CARLA_VOL): # knob color colorGreen = QColor(0x5D, 0xE7, 0x3D).lighter(100 + self.fHoverStep * 6) colorBlue = QColor(0x3E, 0xB8, 0xBE).lighter(100 + self.fHoverStep * 6) # draw small circle ballRect = QRectF(8.0, 8.0, 15.0, 15.0) ballPath = QPainterPath() ballPath.addEllipse(ballRect) #painter.drawRect(ballRect) tmpValue = (0.375 + 0.75 * normValue) ballValue = tmpValue - floor(tmpValue) ballPoint = ballPath.pointAtPercent(ballValue) # draw arc startAngle = 218 * 16 spanAngle = -255 * 16 * normValue if self.fCustomPaintMode == self.CUSTOM_PAINT_MODE_CARLA_WET: painter.setBrush(colorBlue) painter.setPen(QPen(colorBlue, 0)) painter.drawEllipse( QRectF(ballPoint.x(), ballPoint.y(), 2.2, 2.2)) gradient = QConicalGradient(15.5, 15.5, -45) gradient.setColorAt(0.0, colorBlue) gradient.setColorAt(0.125, colorBlue) gradient.setColorAt(0.625, colorGreen) gradient.setColorAt(0.75, colorGreen) gradient.setColorAt(0.76, colorGreen) gradient.setColorAt(1.0, colorGreen) painter.setBrush(gradient) painter.setPen(QPen(gradient, 3)) else: painter.setBrush(colorBlue) painter.setPen(QPen(colorBlue, 0)) painter.drawEllipse( QRectF(ballPoint.x(), ballPoint.y(), 2.2, 2.2)) painter.setBrush(colorBlue) painter.setPen(QPen(colorBlue, 3)) painter.drawArc(4.0, 4.0, 26.0, 26.0, startAngle, spanAngle) # Custom knobs (L and R) elif self.fCustomPaintMode in (self.CUSTOM_PAINT_MODE_CARLA_L, self.CUSTOM_PAINT_MODE_CARLA_R): # knob color color = QColor(0xAD, 0xD5, 0x48).lighter(100 + self.fHoverStep * 6) # draw small circle ballRect = QRectF(7.0, 8.0, 11.0, 12.0) ballPath = QPainterPath() ballPath.addEllipse(ballRect) #painter.drawRect(ballRect) tmpValue = (0.375 + 0.75 * normValue) ballValue = tmpValue - floor(tmpValue) ballPoint = ballPath.pointAtPercent(ballValue) painter.setBrush(color) painter.setPen(QPen(color, 0)) painter.drawEllipse( QRectF(ballPoint.x(), ballPoint.y(), 2.0, 2.0)) # draw arc if self.fCustomPaintMode == self.CUSTOM_PAINT_MODE_CARLA_L: startAngle = 218 * 16 spanAngle = -255 * 16 * normValue else: startAngle = 322.0 * 16 spanAngle = 255.0 * 16 * (1.0 - normValue) painter.setPen(QPen(color, 2.5)) painter.drawArc(3.5, 3.5, 22.0, 22.0, startAngle, spanAngle) # Custom knobs (Color) elif self.fCustomPaintMode == self.CUSTOM_PAINT_MODE_COLOR: # knob color color = self.fCustomPaintColor.lighter(100 + self.fHoverStep * 6) # draw small circle ballRect = QRectF(8.0, 8.0, 15.0, 15.0) ballPath = QPainterPath() ballPath.addEllipse(ballRect) tmpValue = (0.375 + 0.75 * normValue) ballValue = tmpValue - floor(tmpValue) ballPoint = ballPath.pointAtPercent(ballValue) # draw arc startAngle = 218 * 16 spanAngle = -255 * 16 * normValue painter.setBrush(color) painter.setPen(QPen(color, 0)) painter.drawEllipse( QRectF(ballPoint.x(), ballPoint.y(), 2.2, 2.2)) painter.setBrush(color) painter.setPen(QPen(color, 3)) painter.drawArc(4.0, 4.8, 26.0, 26.0, startAngle, spanAngle) # Custom knobs (Zita) elif self.fCustomPaintMode == self.CUSTOM_PAINT_MODE_ZITA: a = normValue * pi * 1.5 - 2.35 r = 10.0 x = 10.5 y = 10.5 x += r * sin(a) y -= r * cos(a) painter.setBrush(Qt.black) painter.setPen(QPen(Qt.black, 2)) painter.drawLine(QPointF(11.0, 11.0), QPointF(x, y)) # Custom knobs else: painter.restore() return if self.HOVER_MIN < self.fHoverStep < self.HOVER_MAX: self.fHoverStep += 1 if self.fIsHovered else -1 QTimer.singleShot(20, self.update) else: # isEnabled() target = QRectF(0.0, 0.0, self.fImageBaseSize, self.fImageBaseSize) if isinstance(self.fImage, QPixmap): painter.drawPixmap(target, self.fImage, target) else: self.fImage.renderer().render(painter, target) painter.restore() def resizeEvent(self, event): QDial.resizeEvent(self, event) self.updateSizes()
class ChessBoard(QWidget, chess.Board): """ BRIEF An interactive chessboard that only allows legal moves """ ReadyForNextMove = pyqtSignal(str) GameOver = pyqtSignal() def __init__(self, parent=None): """ BRIEF Initialize the chessboard """ super().__init__(parent) self.setWindowTitle("Chess") self.svg_xy = 50 # top left x,y-pos of chessboard self.board_size = 600 # size of chessboard self.margin = 0.05 * self.board_size self.square_size = (self.board_size - 2 * self.margin) / 8.0 wnd_wh = self.board_size + 2 * self.svg_xy self.setMinimumSize(wnd_wh, wnd_wh) self.svg_widget = QSvgWidget(parent=self) self.svg_widget.setGeometry(self.svg_xy, self.svg_xy, self.board_size, self.board_size) self.last_click = None self.DrawBoard() @pyqtSlot(QWidget) def mousePressEvent(self, event): """ BRIEF Update the board state based on user clicks If the state changes, update the svg widget """ if self.LeftClickedBoard(event): this_click = self.GetClicked(event) if self.last_click: if self.last_click != this_click: uci = self.last_click + this_click self.ApplyMove(uci + self.GetPromotion(uci)) self.last_click = this_click def GetPromotion(self, uci): """ BRIEF Get the uci piece type the pawn will be promoted to """ if chess.Move.from_uci(uci + 'q') in self.legal_moves: dialog = PromotionDialog(self) if dialog.exec() == QDialog.Accepted: return dialog.SelectedPiece() return '' @pyqtSlot(str) def ApplyMove(self, uci): """ BRIEF Apply a move to the board """ move = chess.Move.from_uci(uci) if move in self.legal_moves: self.push(move) self.DrawBoard() print(self.fen()) if not self.is_game_over(): self.ReadyForNextMove.emit(self.fen()) else: print("Game over!") self.GameOver.emit() sys.stdout.flush() @pyqtSlot() def UndoMove(self): """ """ try: self.pop() self.DrawBoard() self.ReadyForNextMove.emit(self.fen()) except IndexError: pass def DrawBoard(self): """ BRIEF Redraw the chessboard based on board state Highlight src and dest squares for last move Highlight king if in check """ self.svg_widget.load(self._repr_svg_().encode("utf-8")) def GetClicked(self, event): """ BRIEF Get the algebraic notation for the clicked square """ top_left = self.svg_xy + self.margin file_i = int((event.x() - top_left) / self.square_size) rank_i = 7 - int((event.y() - top_left) / self.square_size) return chr(file_i + 97) + str(rank_i + 1) def LeftClickedBoard(self, event): """ BRIEF Check to see if they left-clicked on the chess board """ topleft = self.svg_xy + self.margin bottomright = self.board_size + self.svg_xy - self.margin return all([ event.buttons() == Qt.LeftButton, topleft < event.x() < bottomright, topleft < event.y() < bottomright, ])
class Notification(object): def __init__(self, window, styleFunction, stylesheet=["", "", ""], img_margin=5, top_margin=5, pos=[0, 0], size=[100, 20]): self._styleFunction = styleFunction self._currApp = '' self._pos = pos # Create components ### Notification Background self._background_main_stylesheet = stylesheet[0] self._background = QLabel(window) self._background.setGeometry(pos[0], pos[1], size[0], size[1]) self._background.hide() ### Notification Logo self._logo = None self._logo_geometry = [ img_margin, img_margin, size[1] - img_margin * 2, size[1] - img_margin * 2 ] ### Notification Title self._title = QLabel(self._background) self._title.setAttribute(Qt.WA_TranslucentBackground) self._title.setAlignment(Qt.AlignTop | Qt.AlignLeft) self._title.setStyleSheet('QLabel{' + stylesheet[1] + '}') self._title.setText('Title') self._title.adjustSize() self._title.setGeometry( img_margin + self._logo_geometry[2] + 4, top_margin, size[0] - img_margin - self._logo_geometry[2] - 4, self._title.height()) self._title.show() ### Notification Message self._message = QLabel(self._background) self._message.setAttribute(Qt.WA_TranslucentBackground) self._message.setAlignment(Qt.AlignTop | Qt.AlignLeft) self._message.setStyleSheet('QLabel{' + stylesheet[2] + '}') self._message.setText('Message') self._message.adjustSize() self._message.setGeometry( img_margin + self._logo_geometry[2] + 8, top_margin + self._title.height() + 2, size[0] - img_margin - self._logo_geometry[2] - 8, self._message.height() * 2) self._message.show() def setParent(self, p): self._background.setParent(p) def deleteLater(self): self._background.deleteLater() def setText(self, app, title, message): if self._currApp != app: logoPath, backgroundColor = self._styleFunction(app) if self._logo == None: self._logo = QSvgWidget(logoPath, self._background) self._logo.setGeometry(self._logo_geometry[0], self._logo_geometry[1], self._logo_geometry[2], self._logo_geometry[3]) self._logo.show() else: self._logo.load(logoPath) self._background.setStyleSheet('QLabel {background-color:' + backgroundColor + ';' + self._background_main_stylesheet + '}') self._logo.setStyleSheet('background-color:' + backgroundColor + ';') self._currApp = app # Update Textual Contents self._title.setText(title) self._message.setText(message) self._message.setWordWrap(True) def update(self): self._background.update() self._logo.update() self._title.update() self._message.update() def show(self): self._background.show() def hide(self): self._background.hide() def move(self, x, y): self._pos = [x, y] self._background.move(x, y) def moveX(self, x): self._pos[0] = x self._background.move(x, self._pos[1]) def moveY(self, y): self._pos[1] = y self._background.move(self._pos[0], y) def bringToFront(self): self._background.raise_() def bringToBack(self): self._background.lower()
def add_svg(self, title, data): svg = QSvgWidget() svg.load(data) scrollpane = QScrollArea() scrollpane.setWidget(svg) self.tabs.addTab(scrollpane, title)
def add_svg(self, title, data): editor = QSvgWidget() editor.load(data) self.tabs.addTab(editor, title)
class Emulator(QWidget, ui_emulator.Ui_Emulator): game_found = QtCore.pyqtSignal(int, dict, str, int) games_loaded = QtCore.pyqtSignal() def __init__(self, data): super(Emulator, self).__init__() self.setupUi(self) self.data = data self.processes = { } self.roms = [ ] self.settings = QSettings('SanderTheDragon', 'Qtendo') self.settings_prefix = 'emulation/emulator/' + self.data['name'].lower().replace(' ', '_') self.config_files = self.get_config_files() self.settings_dialog = emulator_settings.EmulatorSettingsDialog(parent=self, data=self.data) self.settings_dialog.accepted.connect(lambda: self.reload_settings()) self.settings_dialog.setWindowTitle(self.data['name'] + ' Settings') self.ui_create() self.ui_connect() Thread(target=self.find_games, daemon=True).start() def ui_create(self): self.pathLabel.setText(self.settings.value(self.settings_prefix + '/path', self.data['path'], type=str)) name = self.nameLabel.text() name = name.replace('{NAME}', self.data['name']) name = name.replace('{URL}', self.data['site']) version = self.data['version'] if version == 'Not Found' and len(self.pathLabel.text()) > 0: version_ = self.data['get_version'](self.pathLabel.text()) if len(version) > 0: version = version_ self.version_pos = name.find('{VERSION}') name = name[:self.version_pos] + version self.nameLabel.setText(name) if self.data['icon'].endswith('.svg'): self.svgWidget = QSvgWidget() self.svgWidget.load(':' + self.data['icon']) self.svgWidget.setMaximumSize(QSize(24, 24)) self.gridLayout.addWidget(self.svgWidget, 0, 0) else: self.iconLabel = QLabel() self.iconLabel.setPixmap(QPixmap(':' + self.data['icon']).scaled(24, 24)) self.gridLayout.addWidget(self.iconLabel, 0, 0) platformText = '' for platform in self.data['platforms'].keys(): platformText += ' • ' + platform + ' (' + ', '.join(self.data['platforms'][platform]) + ')<br/>' self.platformLabel.setText(self.platformLabel.text().replace('{PLATFORMS}', platformText)) self.gameList.insertColumn(0) self.gameList.insertColumn(1) self.gameList.insertColumn(2) self.gameList.insertColumn(3) self.gameList.insertColumn(4) self.gameList.setHorizontalHeaderLabels([ 'Platform', 'ID', 'Title', 'Region', 'Path' ]) self.gameList.horizontalHeader().setStretchLastSection(True) self.gameList.setContextMenuPolicy(Qt.CustomContextMenu) def ui_connect(self): self.game_found.connect(self.add_game) self.games_loaded.connect(self.done_loading) self.gameList.customContextMenuRequested.connect(lambda position: self.game_list_context_menu(position)) self.gameList.cellDoubleClicked.connect(lambda row, column: self.launch_game(self.gameList.item(row, 4).text())) self.refreshButton.pressed.connect(lambda: ( self.reset_list(), Thread(target=self.find_games, daemon=True).start() )) self.settingsButton.pressed.connect(lambda: self.settings_dialog.exec_()) self.filterEdit.textChanged.connect(lambda text: self.search()) def find_games(self): file_types = [ ] for platform in self.data['platforms'].keys(): file_types += self.data['platforms'][platform] logging.debug('[' + self.data['name'] + '] Searching for ( ' + ', '.join(file_types) + ' ) files') paths = self.settings.value('emulation/roms/paths', [ ], type=str) games_length = 0 for path in paths: if not os.path.isdir(path): continue possible_games = utils.find_files(path, file_types) games_length = len(possible_games) logging.debug('[' + self.data['name'] + '] Found ' + str(games_length) + ' possible ROM' + ('s' if games_length != 1 else '')) for game in possible_games: index = self.gameList.rowCount() try: rom_ = rom.Rom(game, self.data['platforms']) if rom_.is_rom: logging.debug('[' + self.data['name'] + '] \'' + game + '\' is a valid ROM') self.roms.append(rom_) info = rom_.module.get_info(game) self.game_found.emit(index, info, game, games_length) else: logging.debug('[' + self.data['name'] + '] \'' + game + '\' is not a valid ROM') games_length -= 1 except: traceback.print_exc() logging.debug('[' + self.data['name'] + '] Found ' + str(games_length) + ' ROM' + ('s' if games_length != 1 else '')) self.games_loaded.emit() def add_game(self, index, info, path, count): self.gameList.insertRow(index) if len(info.keys()) > 0: self.gameList.setItem(index, 0, QTableWidgetItem(info['platform'])) self.gameList.setItem(index, 1, QTableWidgetItem(info['id'])) self.gameList.setItem(index, 2, QTableWidgetItem(info['title'])) self.gameList.setItem(index, 3, QTableWidgetItem(info['region'] + '(' + info['region_code'] + ')')) self.gameList.setItem(index, 4, QTableWidgetItem(path)) self.gameList.resizeColumnsToContents() self.progressBar.setValue(int(100.0 / float(count) * float(index + 1))) def reset_list(self): self.refreshButton.setEnabled(False) self.progressBar.setValue(0) self.progressBar.setVisible(True) self.gameList.setSortingEnabled(False) self.gameList.setRowCount(0) self.roms.clear() def done_loading(self): self.gameList.setSortingEnabled(True) self.gameList.sortItems(1) self.progressBar.setVisible(False) self.refreshButton.setEnabled(True) self.search() def launch_game(self, path): if path in self.processes: return command = self.settings.value(self.settings_prefix + '/command', '{EXEC} {ARGS} {ROM}', type=str) command = command.replace('{EXEC}', self.settings.value(self.settings_prefix + '/path', self.data['path'], type=str)) command = command.replace('{ARGS}', ' '.join(self.data['arguments'])) command = command.replace('{ROM}', shlex.quote(path)) logging.info('[' + self.data['name'] + '] Launching: `' + command + '`') process = QProcess(self) process.finished.connect(functools.partial(self.end_game, path)) if self.settings.value('emulation/log/stdout', True, type=bool): process.readyReadStandardOutput.connect(functools.partial(lambda path: self.log_game(path, bytes(self.processes[path].readAllStandardOutput()).decode('utf-8')), path)) if self.settings.value('emulation/log/stderr', True, type=bool): process.readyReadStandardError.connect(functools.partial(lambda path: self.log_game(path, bytes(self.processes[path].readAllStandardError()).decode('utf-8'), logging.error), path)) process.errorOccurred.connect(functools.partial(lambda path, error: self.log_game(path, str(error), logging.error), path)) args = shlex.split(command) self.processes[path] = process self.processes[path].start(args[0], args[1:]) def log_game(self, path, message, method=logging.info): if '\n' in message: for line in message.split('\n'): self.log_game(path, line, method) else: message = utils.ansi_trim(message.strip()) if len(message) == 0: return method('[' + self.data['name'] + ':' + path[path.rfind('/') + 1:] + '] ' + message) def end_game(self, path, code, status): if code == 0: logging.info('[' + self.data['name'] + '] Process for \'' + path + '\' exited') else: logging.error('[' + self.data['name'] + '] Process for \'' + path + '\' exited with non-zero code: ' + str(code)) del self.processes[path] def reload_settings(self): self.pathLabel.setText(self.settings.value(self.settings_prefix + '/path', self.data['path'], type=str)) name = self.nameLabel.text() version = self.data['get_version'](self.pathLabel.text()) if len(version) > 0: name = name[:self.version_pos] + version self.nameLabel.setText(name) self.data['reload_settings']() def game_list_context_menu(self, position): menu = QMenu() launchAction = menu.addAction("Launch") menu.addSeparator() action = menu.exec_(self.gameList.mapToGlobal(position)) if action == launchAction: self.launch_game(self.gameList.item(self.gameList.selectionModel().selectedRows()[0].row(), 4).text()) def search(self): text = self.filterEdit.text() for i in range(self.gameList.rowCount()): if text.lower() in self.gameList.item(i, 2).text().lower(): self.gameList.showRow(i) else: self.gameList.hideRow(i)
class SuggestRow(QPushButton): def __init__(self, parent, suggestion: Suggestion): QWidget.__init__(self, parent) # defines whether the command has associated options self.has_options = True if hasattr(suggestion, "option_suggestions") else False # gets the current theme self.active_theme = parent.active_theme # gets the font self.custom_font = parent.custom_font # setting height the row width, height = [parent.width(), 57] self.resize(width, height) # makes command dictionary a class variable self.suggestion = suggestion # Stores information about the command the row will hold # widget creation self.icon = None # This can either be an svg or jpg file icon_path = self.suggestion.icon_name # gets the icon path if "svg" in icon_path: self.icon = QSvgWidget(self) self.icon.load(icon_path) else: pixmap = QPixmap(icon_path) icon = QLabel(self) icon.setPixmap(pixmap) self.icon = icon self.title_lbl = QLabel(self.suggestion.title, self) self.description_lbl = QLabel(self.suggestion.description, self) self.option_icon = QSvgWidget(self) self.option_icon.load(f"{ASSETS_DIR}svg{sep}ellipsis.svg") self.set_style() def set_style(self): # TODO: Add support for theming for icon and layout scalability components # set style and location of icon if "svg" in self.suggestion.icon_name: # different location and sizes depending on icon type self.icon.move(18, 18) self.icon.resize(20, 20) self.icon.setStyleSheet("background-color: rgba(0,0,0,0%);") else: self.icon.move(8, 8) self.icon.resize(40, 40) self.icon.setAlignment(Qt.AlignCenter) self.icon.setScaledContents(True) # set style for options icon self.option_icon.move(490, 16) self.option_icon.resize(25, 25) self.option_icon.setStyleSheet("background-color: rgba(0,0,0,0%);") self.option_icon.hide() # set style and location of title self.title_lbl.move(56, 9) self.title_lbl.setStyleSheet( f"font-size: 20px; color: {self.active_theme.foreground}; background-color: rgba(0,0,0,0%);" ) self.title_lbl.setFont(self.custom_font) # set style and location of description self.description_lbl.resize(479, 15) self.description_lbl.move(56, 33) self.description_lbl.setStyleSheet( f"font-size: 13px; color: {self.active_theme.foreground}; background-color: rgba(0,0,0,0%);" ) self.description_lbl.setFont(self.custom_font) # style for widget self.setStyleSheet(''' QPushButton { border: none; } QPushButton:hover { background-color: #251e1e; } QPushButton:hover:focus { background-color: #322828; } QPushButton:focus { background-color: #3f3232; outline: 0px } ''') def show_option_icon(self): if self.has_options: self.option_icon.show() def hide_option_icon(self): if self.has_options: self.option_icon.hide()
class SvgDiagram(E5MainWindow): """ Class implementing a dialog showing a SVG graphic. """ ZoomLevels = [ 1, 3, 5, 7, 9, 10, 20, 30, 50, 67, 80, 90, 100, 110, 120, 133, 150, 170, 200, 240, 300, 400, 500, 600, 700, 800, 900, 1000, ] ZoomLevelDefault = 100 def __init__(self, svgFile, parent=None, name=None): """ Constructor @param svgFile filename of a SVG graphics file to show (string) @param parent parent widget of the view (QWidget) @param name name of the view widget (string) """ super(SvgDiagram, self).__init__(parent) if name: self.setObjectName(name) else: self.setObjectName("SvgDiagram") self.setWindowTitle(self.tr("SVG-Viewer")) self.svgWidget = QSvgWidget() self.svgWidget.setObjectName("svgWidget") self.svgWidget.setBackgroundRole(QPalette.Base) self.svgWidget.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored) self.svgView = QScrollArea() self.svgView.setObjectName("svgView") self.svgView.setBackgroundRole(QPalette.Dark) self.svgView.setWidget(self.svgWidget) self.setCentralWidget(self.svgView) self.__zoomWidget = E5ZoomWidget( UI.PixmapCache.getPixmap("zoomOut.png"), UI.PixmapCache.getPixmap("zoomIn.png"), UI.PixmapCache.getPixmap("zoomReset.png"), self) self.statusBar().addPermanentWidget(self.__zoomWidget) self.__zoomWidget.setMapping( SvgDiagram.ZoomLevels, SvgDiagram.ZoomLevelDefault) self.__zoomWidget.valueChanged.connect(self.__doZoom) # polish up the dialog self.resize(QSize(800, 600).expandedTo(self.minimumSizeHint())) self.zoom = 1.0 self.svgFile = svgFile self.svgWidget.load(self.svgFile) self.svgWidget.resize(self.svgWidget.renderer().defaultSize()) self.__initActions() self.__initContextMenu() self.__initToolBars() self.grabGesture(Qt.PinchGesture) def __initActions(self): """ Private method to initialize the view actions. """ self.closeAct = \ QAction(UI.PixmapCache.getIcon("close.png"), self.tr("Close"), self) self.closeAct.triggered.connect(self.close) self.printAct = \ QAction(UI.PixmapCache.getIcon("print.png"), self.tr("Print"), self) self.printAct.triggered.connect(self.__printDiagram) self.printPreviewAct = \ QAction(UI.PixmapCache.getIcon("printPreview.png"), self.tr("Print Preview"), self) self.printPreviewAct.triggered.connect(self.__printPreviewDiagram) def __initContextMenu(self): """ Private method to initialize the context menu. """ self.__menu = QMenu(self) self.__menu.addAction(self.closeAct) self.__menu.addSeparator() self.__menu.addAction(self.printPreviewAct) self.__menu.addAction(self.printAct) self.setContextMenuPolicy(Qt.CustomContextMenu) self.customContextMenuRequested.connect(self.__showContextMenu) def __showContextMenu(self, coord): """ Private slot to show the context menu of the listview. @param coord the position of the mouse pointer (QPoint) """ self.__menu.popup(self.mapToGlobal(coord)) def __initToolBars(self): """ Private method to populate the toolbars with our actions. """ self.windowToolBar = QToolBar(self.tr("Window"), self) self.windowToolBar.setIconSize(UI.Config.ToolBarIconSize) self.windowToolBar.addAction(self.closeAct) self.graphicsToolBar = QToolBar(self.tr("Graphics"), self) self.graphicsToolBar.setIconSize(UI.Config.ToolBarIconSize) self.graphicsToolBar.addAction(self.printPreviewAct) self.graphicsToolBar.addAction(self.printAct) self.addToolBar(Qt.TopToolBarArea, self.windowToolBar) self.addToolBar(Qt.TopToolBarArea, self.graphicsToolBar) def getDiagramName(self): """ Public method to retrieve a name for the diagram. @return name for the diagram """ return self.svgFile def wheelEvent(self, evt): """ Protected method to handle wheel events. @param evt reference to the wheel event (QWheelEvent) """ if evt.modifiers() & Qt.ControlModifier: if qVersion() >= "5.0.0": delta = evt.angleDelta().y() else: delta = evt.delta() if delta < 0: self.__zoomOut() else: self.__zoomIn() evt.accept() return super(SvgDiagram, self).wheelEvent(evt) def event(self, evt): """ Public method handling events. @param evt reference to the event (QEvent) @return flag indicating, if the event was handled (boolean) """ if evt.type() == QEvent.Gesture: self.gestureEvent(evt) return True return super(SvgDiagram, self).event(evt) def gestureEvent(self, evt): """ Protected method handling gesture events. @param evt reference to the gesture event (QGestureEvent """ pinch = evt.gesture(Qt.PinchGesture) if pinch: if pinch.state() == Qt.GestureStarted: pinch.setScaleFactor(self.__zoom() / 100) else: self.__doZoom(int(pinch.scaleFactor() * 100)) evt.accept() ########################################################################### ## Private menu handling methods below. ########################################################################### def __adjustScrollBar(self, scrollBar, factor): """ Private method to adjust a scrollbar by a certain factor. @param scrollBar reference to the scrollbar object (QScrollBar) @param factor factor to adjust by (float) """ scrollBar.setValue( int(factor * scrollBar.value() + ((factor - 1) * scrollBar.pageStep() / 2))) def __levelForZoom(self, zoom): """ Private method determining the zoom level index given a zoom factor. @param zoom zoom factor (integer) @return index of zoom factor (integer) """ try: index = SvgDiagram.ZoomLevels.index(zoom) except ValueError: for index in range(len(SvgDiagram.ZoomLevels)): if zoom <= SvgDiagram.ZoomLevels[index]: break return index def __doZoom(self, value): """ Private method to set the zoom value in percent. @param value zoom value in percent (integer) """ oldValue = self.__zoom() if value != oldValue: self.svgWidget.resize(value / 100 * self.svgWidget.sizeHint()) factor = value / oldValue self.__adjustScrollBar(self.svgView.horizontalScrollBar(), factor) self.__adjustScrollBar(self.svgView.verticalScrollBar(), factor) self.__zoomWidget.setValue(value) def __zoomIn(self): """ Private method to zoom into the SVG. """ index = self.__levelForZoom(self.__zoom()) if index < len(SvgDiagram.ZoomLevels) - 1: self.__doZoom(SvgDiagram.ZoomLevels[index + 1]) def __zoomOut(self): """ Private method to zoom out of the SVG. """ index = self.__levelForZoom(self.__zoom()) if index > 0: self.__doZoom(SvgDiagram.ZoomLevels[index - 1]) def __zoomReset(self): """ Private method to reset the zoom value. """ self.__doZoom(SvgDiagram.ZoomLevels[SvgDiagram.ZoomLevelDefault]) def __zoom(self): """ Private method to get the current zoom factor in percent. @return current zoom factor in percent (integer) """ return int(self.svgWidget.width() / self.svgWidget.sizeHint().width() * 100.0) def __printDiagram(self): """ Private slot called to print the diagram. """ printer = QPrinter(mode=QPrinter.ScreenResolution) printer.setFullPage(True) if Preferences.getPrinter("ColorMode"): printer.setColorMode(QPrinter.Color) else: printer.setColorMode(QPrinter.GrayScale) if Preferences.getPrinter("FirstPageFirst"): printer.setPageOrder(QPrinter.FirstPageFirst) else: printer.setPageOrder(QPrinter.LastPageFirst) printerName = Preferences.getPrinter("PrinterName") if printerName: printer.setPrinterName(printerName) printDialog = QPrintDialog(printer, self) if printDialog.exec_(): self.__print(printer) def __printPreviewDiagram(self): """ Private slot called to show a print preview of the diagram. """ from PyQt5.QtPrintSupport import QPrintPreviewDialog printer = QPrinter(mode=QPrinter.ScreenResolution) printer.setFullPage(True) if Preferences.getPrinter("ColorMode"): printer.setColorMode(QPrinter.Color) else: printer.setColorMode(QPrinter.GrayScale) if Preferences.getPrinter("FirstPageFirst"): printer.setPageOrder(QPrinter.FirstPageFirst) else: printer.setPageOrder(QPrinter.LastPageFirst) printer.setPageMargins( Preferences.getPrinter("LeftMargin") * 10, Preferences.getPrinter("TopMargin") * 10, Preferences.getPrinter("RightMargin") * 10, Preferences.getPrinter("BottomMargin") * 10, QPrinter.Millimeter ) printerName = Preferences.getPrinter("PrinterName") if printerName: printer.setPrinterName(printerName) preview = QPrintPreviewDialog(printer, self) preview.paintRequested[QPrinter].connect(self.__print) preview.exec_() def __print(self, printer): """ Private slot to the actual printing. @param printer reference to the printer object (QPrinter) """ painter = QPainter() painter.begin(printer) # calculate margin and width of printout font = QFont("times", 10) painter.setFont(font) fm = painter.fontMetrics() fontHeight = fm.lineSpacing() marginX = printer.pageRect().x() - printer.paperRect().x() marginX = Preferences.getPrinter("LeftMargin") * \ int(printer.resolution() / 2.54) - marginX marginY = printer.pageRect().y() - printer.paperRect().y() marginY = Preferences.getPrinter("TopMargin") * \ int(printer.resolution() / 2.54) - marginY width = printer.width() - marginX - \ Preferences.getPrinter("RightMargin") * \ int(printer.resolution() / 2.54) height = printer.height() - fontHeight - 4 - marginY - \ Preferences.getPrinter("BottomMargin") * \ int(printer.resolution() / 2.54) # write a foot note s = self.tr("Diagram: {0}").format(self.getDiagramName()) tc = QColor(50, 50, 50) painter.setPen(tc) painter.drawRect(marginX, marginY, width, height) painter.drawLine(marginX, marginY + height + 2, marginX + width, marginY + height + 2) painter.setFont(font) painter.drawText(marginX, marginY + height + 4, width, fontHeight, Qt.AlignRight, s) # render the diagram painter.setViewport(marginX, marginY, width, height) self.svgWidget.renderer().render(painter) painter.end()