def Topologic_result_context_menu(self, point): """Context menu for the type synthesis results.""" index = self.Topologic_result.currentIndex().row() self.add_collection.setEnabled(index>-1) self.copy_edges.setEnabled(index>-1) self.copy_image.setEnabled(index>-1) action = self.popMenu_topo.exec_(self.Topologic_result.mapToGlobal(point)) if not action: return clipboard = QApplication.clipboard() if action==self.add_collection: self.addCollection(self.answer[index].edges) elif action==self.copy_edges: clipboard.setText(str(self.answer[index].edges)) elif action==self.copy_image: #Turn the transparent background to white. image1 = self.atlas_image() image2 = QImage(image1.size(), image1.format()) image2.fill(QColor(Qt.white).rgb()) painter = QPainter(image2) painter.drawImage(QPointF(0, 0), image1) painter.end() pixmap = QPixmap() pixmap.convertFromImage(image2) clipboard.setPixmap(pixmap)
def __save_atlas(self): """Save function as same as type synthesis widget.""" count = self.collection_list.count() if not count: return lateral, ok = QInputDialog.getInt(self, "Atlas", "The number of lateral:", 5, 1) if not ok: return file_name = self.output_to("Atlas image", qt_image_format) if not file_name: return icon_size = self.collection_list.iconSize() width = icon_size.width() image_main = QImage( QSize(lateral * width if count > lateral else count * width, ((count // lateral) + bool(count % lateral)) * width), self.collection_list.item(0).icon().pixmap( icon_size).toImage().format()) image_main.fill(Qt.transparent) painter = QPainter(image_main) for row in range(count): image = self.collection_list.item(row).icon().pixmap( icon_size).toImage() painter.drawImage( QPointF(row % lateral * width, row // lateral * width), image) painter.end() pixmap = QPixmap() pixmap.convertFromImage(image_main) pixmap.save(file_name) self.save_reply_box("Atlas", file_name)
def on_save_atlas_clicked(self): """Save function as same as type synthesis widget.""" count = self.collection_list.count() if not count: return lateral, ok = QInputDialog.getInt(self, "Atlas", "The number of lateral:", 5, 1, 10) if not ok: return fileName = self.outputTo("Atlas image", Qt_images) if not fileName: return icon_size = self.collection_list.iconSize() width = icon_size.width() image_main = QImage( QSize(lateral * width if count > lateral else count * width, ((count // lateral) + bool(count % lateral)) * width), self.collection_list.item(0).icon().pixmap( icon_size).toImage().format()) image_main.fill(QColor(Qt.white).rgb()) painter = QPainter(image_main) for row in range(count): image = self.collection_list.item(row).icon().pixmap( icon_size).toImage() painter.drawImage( QPointF(row % lateral * width, row // lateral * width), image) painter.end() pixmap = QPixmap() pixmap.convertFromImage(image_main) pixmap.save(fileName, format=QFileInfo(fileName).suffix()) self.saveReplyBox("Atlas", fileName)
def __save_atlas(self): """Saving all the atlas to image file. We should turn transparent background to white first. Then using QImage class to merge into one image. """ file_name = "" lateral = 0 if self.save_edges_auto.isChecked(): lateral, ok = QInputDialog.getInt(self, "Atlas", "The number of lateral:", 5, 1, 10) if not ok: return file_name = self.outputTo("Atlas image", qt_image_format) if file_name: reply = QMessageBox.question( self, "Type synthesis", "Do you want to Re-synthesis?", (QMessageBox.Yes | QMessageBox.YesToAll | QMessageBox.Cancel), QMessageBox.Yes) if reply == QMessageBox.Yes: self.__structure_synthesis() elif reply == QMessageBox.YesToAll: self.__structure_synthesis_all() count = self.structure_list.count() if not count: return if not lateral: lateral, ok = QInputDialog.getInt(self, "Atlas", "The number of lateral:", 5, 1, 10) if not ok: return if not file_name: file_name = self.outputTo("Atlas image", qt_image_format) if not file_name: return width = self.structure_list.iconSize().width() image_main = QImage( QSize(lateral * width if count > lateral else count * width, ((count // lateral) + bool(count % lateral)) * width), self.__atlas_image(0).format()) image_main.fill(QColor(Qt.white).rgb()) painter = QPainter(image_main) for row in range(count): image = self.__atlas_image(row) painter.drawImage( QPointF(row % lateral * width, row // lateral * width), image) painter.end() pixmap = QPixmap() pixmap.convertFromImage(image_main) pixmap.save(file_name, format=QFileInfo(file_name).suffix()) self.saveReplyBox("Atlas", file_name)
class BaseCanvas(QWidget): """The subclass can draw a blank canvas more easier.""" def __init__(self, parent: QWidget): """Set the parameters for drawing.""" super(BaseCanvas, self).__init__(parent) self.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)) self.setFocusPolicy(Qt.StrongFocus) self.painter = QPainter() # Origin coordinate. self.ox = self.width() / 2 self.oy = self.height() / 2 # Canvas zoom rate. self.rate = 2. self.zoom = 2. * self.rate # Joint size. self.joint_size = 3 # Canvas line width. self.link_width = 3 self.path_width = 3 # Font size. self.font_size = 15 # Show point mark or dimension. self.show_point_mark = True self.show_dimension = True # Path track. self.Path = _Path() # Path solving. self.target_path = {} self.show_target_path = False # Background self.background = QImage() self.background_opacity = 1. self.background_scale = 1 self.background_offset = QPointF(0, 0) def paintEvent(self, event): """Using a QPainter under 'self', so just change QPen or QBrush before painting. """ self.painter.begin(self) self.painter.fillRect(event.rect(), QBrush(Qt.white)) # Translation self.painter.translate(self.ox, self.oy) # Background if not self.background.isNull(): rect = self.background.rect() self.painter.setOpacity(self.background_opacity) img_origin: QPointF = self.background_offset * self.zoom self.painter.drawImage( QRectF(img_origin, QSizeF( rect.width() * self.background_scale * self.zoom, rect.height() * self.background_scale * self.zoom )), self.background, QRectF(rect) ) self.painter.setOpacity(1) # Show frame. pen = QPen(Qt.blue) pen.setWidth(1) self.painter.setPen(pen) self.painter.setFont(QFont("Arial", self.font_size)) # Draw origin lines. pen.setColor(Qt.gray) self.painter.setPen(pen) x_l = -self.ox x_r = self.width() - self.ox self.painter.drawLine(QPointF(x_l, 0), QPointF(x_r, 0)) y_t = self.height() - self.oy y_b = -self.oy self.painter.drawLine(QPointF(0, y_b), QPointF(0, y_t)) def indexing(v): """Draw tick.""" return int(v / self.zoom - v / self.zoom % 5) for x in range(indexing(x_l), indexing(x_r) + 1, 5): self.painter.drawLine( QPointF(x, 0) * self.zoom, QPointF(x * self.zoom, -10 if x % 10 == 0 else -5) ) for y in range(indexing(y_b), indexing(y_t) + 1, 5): self.painter.drawLine( QPointF(0, y) * self.zoom, QPointF(10 if y % 10 == 0 else 5, y * self.zoom) ) # Please to call the "end" method when ending paint event. # self.painter.end() def drawPoint( self, i: int, cx, cy, fix: bool, color: QColor ): """Draw a joint.""" pen = QPen(color) pen.setWidth(2) self.painter.setPen(pen) x = cx * self.zoom y = cy * -self.zoom if fix: bottom = y + 20 width = 10 # Draw a triangle below. self.painter.drawPolygon( QPointF(x, y), QPointF(x - width, bottom), QPointF(x + width, bottom) ) r = self.joint_size * 2 else: r = self.joint_size self.painter.drawEllipse(QPointF(x, y), r, r) if not self.show_point_mark: return pen.setColor(Qt.darkGray) pen.setWidth(2) self.painter.setPen(pen) text = f"[{i}]" if type(i) == str else f"[Point{i}]" if self.show_dimension: text += f":({cx:.02f}, {cy:.02f})" self.painter.drawText(QPointF(x, y) + QPointF(6, -6), text) def drawTargetPath(self): """Draw solving path.""" pen = QPen() pen.setWidth(self.path_width) for i, name in enumerate(sorted(self.target_path)): path = self.target_path[name] road, dot, brush = target_path_style(i) pen.setColor(road) self.painter.setPen(pen) self.painter.setBrush(brush) if len(path) == 1: x, y = path[0] p = QPointF(x, -y) * self.zoom self.painter.drawText(p + QPointF(6, -6), name) pen.setColor(dot) self.painter.setPen(pen) self.painter.drawEllipse(p, self.joint_size, self.joint_size) else: painter_path = QPainterPath() for j, (x, y) in enumerate(path): p = QPointF(x, -y) * self.zoom self.painter.drawEllipse(p, self.joint_size, self.joint_size) if j == 0: self.painter.drawText(p + QPointF(6, -6), name) painter_path.moveTo(p) else: x2, y2 = path[j - 1] self.__draw_arrow(x, -y, x2, -y2, zoom=True) painter_path.lineTo(p) pen.setColor(road) self.painter.setPen(pen) self.painter.drawPath(painter_path) for x, y in path: pen.setColor(dot) self.painter.setPen(pen) p = QPointF(x, -y) * self.zoom self.painter.drawEllipse(p, self.joint_size, self.joint_size) self.painter.setBrush(Qt.NoBrush) def __draw_arrow( self, x1: float, y1: float, x2: float, y2: float, *, zoom: bool = False, text: str = '' ): """Front point -> Back point""" if zoom: x1 *= self.zoom y1 *= self.zoom x2 *= self.zoom y2 *= self.zoom a = atan2(y2 - y1, x2 - x1) x1 = (x1 + x2) / 2 - 7.5 * cos(a) y1 = (y1 + y2) / 2 - 7.5 * sin(a) first_point = QPointF(x1, y1) self.painter.drawLine(first_point, QPointF( x1 + 15 * cos(a + radians(20)), y1 + 15 * sin(a + radians(20)) )) self.painter.drawLine(first_point, QPointF( x1 + 15 * cos(a - radians(20)), y1 + 15 * sin(a - radians(20)) )) if not text: return # Font font = self.painter.font() font_copy = QFont(font) font.setBold(True) font.setPointSize(font.pointSize() + 8) self.painter.setFont(font) # Color pen = self.painter.pen() color = pen.color() pen.setColor(color.darker()) self.painter.setPen(pen) self.painter.drawText(first_point, text) pen.setColor(color) self.painter.setPen(pen) self.painter.setFont(font_copy) def drawCurve(self, path: Sequence[Tuple[float, float]]): """Draw path as curve.""" if len(set(path)) <= 2: return painter_path = QPainterPath() error = False for i, (x, y) in enumerate(path): if isnan(x): error = True self.painter.drawPath(painter_path) painter_path = QPainterPath() else: x *= self.zoom y *= -self.zoom if i == 0: painter_path.moveTo(x, y) self.painter.drawEllipse(QPointF(x, y), self.joint_size, self.joint_size) continue if error: painter_path.moveTo(x, y) error = False else: painter_path.lineTo(x, y) self.painter.drawPath(painter_path) def drawDot(self, path: Sequence[Tuple[float, float]]): """Draw path as dots.""" if len(set(path)) <= 2: return for x, y in path: if isnan(x): continue self.painter.drawPoint(QPointF(x, -y) * self.zoom) def solutionPolygon( self, func: str, args: Sequence[str], target: str, pos: Union[Tuple[VPoint, ...], Dict[int, Tuple[float, float]]] ) -> Tuple[List[QPointF], QColor]: """Get solution polygon.""" if func == 'PLLP': color = QColor(121, 171, 252) params = [args[0], args[-1]] elif func == 'PLAP': color = QColor(249, 84, 216) params = [args[0]] else: if func == 'PLPP': color = QColor(94, 255, 185) else: # PXY color = QColor(249, 175, 27) params = [args[0]] params.append(target) tmp_list = [] for name in params: try: index = int(name.replace('P', '')) except ValueError: continue else: x, y = pos[index] tmp_list.append(QPointF(x, -y) * self.zoom) return tmp_list, color def drawSolution( self, func: str, args: Sequence[str], target: str, pos: Union[Tuple[VPoint, ...], Dict[int, Tuple[float, float]]] ): """Draw the solution triangle.""" points, color = self.solutionPolygon(func, args, target, pos) color.setAlpha(150) pen = QPen(color) pen.setWidth(self.joint_size) self.painter.setPen(pen) def draw_arrow(index: int, text: str): """Draw arrow.""" self.__draw_arrow( points[-1].x(), points[-1].y(), points[index].x(), points[index].y(), text=text ) draw_arrow(0, args[1]) if func == 'PLLP': draw_arrow(1, args[2]) color.setAlpha(30) self.painter.setBrush(QBrush(color)) self.painter.drawPolygon(QPolygonF(points)) self.painter.setBrush(Qt.NoBrush)