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