def __init__( self, image: ScaledImageItem, data: np.ndarray, pen: QtGui.QPen = None, font: QtGui.QFont = None, parent: QtWidgets.QGraphicsItem = None, ): super().__init__(image, parent) if pen is None: pen = QtGui.QPen(QtCore.Qt.white, 2.0, QtCore.Qt.DotLine) pen.setCosmetic(True) pen.setCapStyle(QtCore.Qt.RoundCap) if font is None: font = QtGui.QFont() self.image_data = data self.sliced: Optional[np.ndarray] = None self.pen = pen self.font = font self.line = QtCore.QLineF() self.poly = QtGui.QPolygonF() self.action_copy_to_clipboard = qAction( "insert-text", "Copy To Clipboard", "Copy slice values to the clipboard.", self.actionCopyToClipboard, )
def update_chart(self, task_list, current_date=None): self.chart.removeSeries(self.series) self.task_list = task_list result = TaskAnalyser.getPointFromTasks(self.task_list, self.max_bound, self.interval) self.axisX.setMin(self.max_bound - datetime.timedelta(days=self.interval - 1)) self.axisY.setRange(min(result), max(result)) self.qdate = DatetimeParser.fromDateToQDate(str(self.max_bound.date())) self.axisX.setMax(self.max_bound) self.series = QtCharts.QLineSeries() self.series.setName("Karma points") self.axisX.setTickCount(self.interval) result.reverse() for i in range(len(result)): self.series.append( self.qdate.addDays(-self.interval - 1 - i).toMSecsSinceEpoch(), result[i]) pen = QPen() pen.setWidth(3) pen.setColor(QColor(242, 183, 54)) pen.setCapStyle(Qt.RoundCap) self.series.setPen(pen) self.chart.addSeries(self.series) self.update()
def paintEvent(self, paintEvent: QPaintEvent): painter = QPainter(self) painter.setBackgroundMode(Qt.TransparentMode) painter.setRenderHint(QPainter.Antialiasing) brush = QBrush() brush.setStyle(Qt.SolidPattern) pen = QPen() pen.setJoinStyle(Qt.RoundJoin) pen.setCapStyle(Qt.RoundCap) center = QPoint(self.width() // 2, self.height() // 2) radius = 0.45 * min(self.width(), self.height()) pen.setColor(self.palette().color(self.color[0])) brush.setColor(self.palette().color(self.color[1])) if self.highlight is True: pen.setColor(self.palette().color(QPalette.Highlight)) pen.setWidth(round(0.15 * radius)) painter.setBrush(brush) painter.setPen(pen) painter.drawEllipse(center, radius, radius) if self.checked is True: brush.setColor(self.palette().color(QPalette.Background)) pen.setColor(self.palette().color(QPalette.Background)) painter.setPen(pen) painter.setBrush(brush) painter.drawEllipse(center, 0.40 * radius, 0.40 * radius) del painter, brush, pen
def paintEvent(self, event): s = self.size() qp = QPainter() qp.begin(self) qp.setRenderHint(QPainter.Antialiasing, True) qp.setPen(self.penColor) qp.setBrush(self.fillColor) qp.drawRect(0, 0, s.width(), s.height()) if self.drawDrag: pen = QPen(Qt.white, 5) pen.setCapStyle(Qt.RoundCap) qp.setPen(pen) qp.setBrush(self.fillColor) outerWidth = s.width() - 60 outerHeight = s.height() - 60 ow = int(s.width() / 2 - outerWidth / 2) oh = int(s.height() / 2 - outerHeight / 2) qp.drawRoundedRect(ow, oh, outerWidth, outerHeight, 5, 5) qp.setBrush(Qt.white) thickness = 12 length = 50 roundness = thickness / 2 vS = int(s.width() / 2 - thickness / 2) vE = int(s.height() / 2 - length / 2) qp.drawRoundedRect(vS, vE, thickness, length, roundness, roundness) hS = int(s.width() / 2 - length / 2) hE = int(s.height() / 2 - thickness / 2) qp.drawRoundedRect(hS, hE, length, thickness, roundness, roundness) qp.end()
def __init__( self, image: ScaledImageItem, pen: QtGui.QPen = None, font: QtGui.QFont = None, unit: str = "μm", parent: QtWidgets.QGraphicsItem = None, ): super().__init__(image, None, parent) if pen is None: pen = QtGui.QPen(QtCore.Qt.white, 2.0) pen.setCosmetic(True) pen.setStyle(QtCore.Qt.DashLine) pen.setCapStyle(QtCore.Qt.RoundCap) if font is None: font = QtGui.QFont() self.pen = pen self.font = font self.text = "" self.unit = unit self.line = QtCore.QLineF()
def paint(self, painter, options, widget): pen = QPen() pen.setColor(self.color) pen.setStyle(Qt.DashLine) pen.setCapStyle(Qt.RoundCap) pen.setWidth(ConnectionIndicator.PEN_WIDTH) painter.setPen(pen) painter.drawLine(self._srcPoint, self._destPoint)
def _make_pen(self): pen = QPen() pen.setWidth(self._width) color = QGuiApplication.palette().color(QPalette.Normal, QPalette.WindowText) color.setAlphaF(0.8) pen.setColor(color) pen.setStyle(Qt.SolidLine) pen.setCapStyle(Qt.RoundCap) return pen
def paintEvent(self, paintEvent: QPaintEvent): pen = QPen() pen.setJoinStyle(Qt.RoundJoin) pen.setCapStyle(Qt.RoundCap) painter = QPainter(self) painter.translate(self.paintOffset) painter.setBackgroundMode(Qt.TransparentMode) painter.setRenderHint(QPainter.Antialiasing) if self.nodes is not None: painted = set() def paintNode(node): nonlocal painter, painted if node in painted: return painted.add(node) for link in node.links: if link not in painted: painter.drawLine(node.point, link.point) paintNode(link) color = self.palette().color(QPalette.Dark) pen.setColor(color) pen.setWidth(0.50 * self.paintStep) painter.setPen(pen) for node in self.nodes.values(): if paintEvent.region().contains(node.point): paintNode(node) if self.startNode is not None: color = self.palette().color(QPalette.Dark) pen.setColor(color) pen.setWidth(0.75 * self.paintStep) painter.setPen(pen) if paintEvent.region().contains(self.startNode.point): painter.drawPoint(self.startNode.point) if self.finishNode is not None and paintEvent.region().contains(self.finishNode.point): color = self.palette().color(QPalette.Dark).darker(120) pen.setColor(color) pen.setWidth(0.75 * self.paintStep) painter.setPen(pen) painter.drawPoint(self.finishNode.point) if self.player is not None: color = self.palette().color(QPalette.Highlight) color.setAlpha(196) pen.setColor(color) pen.setWidth(0.90 * self.paintStep) painter.setPen(pen) painter.drawPoint(self.player) del painter, pen
def paint(self, painter, option, widget=None): for i in range(1, len(self.points)): pen = QPen() pen.setColor(self.color) pen_width = (self.stroke_weights[i] + 0.2) * self.base_stroke_weight pen.setWidthF(pen_width) if i == 1 or i == len(self.points) - 1: pen.setCapStyle(Qt.RoundCap) painter.setPen(pen) painter.setRenderHint(QPainter.Antialiasing) painter.setRenderHint(QPainter.HighQualityAntialiasing) painter.drawLine(self.points[i - 1], self.points[i])
def initialize_qpen(qt_color, width=2): """ Initializes a Qt QPen with color qt_clor :param width: :param qt_color: A color from Qt: Qt.Color :return: returns the created QPen """ mpen = QPen() mpen.setCapStyle(Qt.FlatCap) mpen.setWidth(width) mpen.setColor(qt_color) return mpen
def reset(self, parameters: Parameters): self._parameters = parameters self._total_label.setText("Éponges détectées : 0") for i in reversed(range(self._legend_layout.count())): self._legend_layout.itemAt(i).widget().setParent(None) self._chart.removeAllSeries() self._legend_items = {} self._series = {} for i, m in parameters.selectedMorphotypes().items(): self._series[i] = QtCharts.QLineSeries(self) self._series[i].setName(m.name()) self._series[i].setColor(m.color()) pen = QPen(m.color(), 3) pen.setCapStyle(Qt.RoundCap) self._series[i].setPen(pen) self._series[i].append(0, 0) self._chart.addSeries(self._series[i]) self._legend_items[i] = ChartLegendItem(m.name(), m.color(), self) self._legend_items[i].toggled.connect(self._legendItemToggled) for i, k in enumerate(self._legend_items.keys()): row = i % 2 + 1 col = i // 2 + 1 self._legend_layout.addWidget(self._legend_items[k], row, col) axis_pen = QPen(Loader.QSSColor("@dark")) axis_pen.setWidth(2) grid_line_pen = QPen(Loader.QSSColor("@light-gray")) labels_font = QFont(Loader.QSSVariable("@font")) labels_font.setPointSize(10) self._chart.createDefaultAxes() for axis in (self._chart.axisX(), self._chart.axisY()): axis.setRange(0, 4) axis.setLinePen(axis_pen) axis.setGridLinePen(grid_line_pen) axis.setLabelFormat("%d") axis.setLabelsFont(labels_font)
def pathComponent(self): pathPainter = QPainter(self) pathPainter.setRenderHint(QPainter.Antialiasing) penPath = QPen() penPath.setStyle(Qt.SolidLine) penPath.setWidth(self.pathWidth) penPath.setBrush( QColor(self.pathColor[0], self.pathColor[1], self.pathColor[2])) penPath.setCapStyle(Qt.RoundCap) penPath.setJoinStyle(Qt.RoundJoin) pathPainter.setPen(penPath) pathPainter.drawArc(self.positionX + self.posFactor, self.positionY + self.posFactor, self.rpb_Size - self.sizeFactor, self.rpb_Size - self.sizeFactor, 0, 360 * 16) pathPainter.end()
def lineComponent(self): linePainter = QPainter(self) linePainter.setRenderHint(QPainter.Antialiasing) penLine = QPen() penLine.setStyle(self.rpb_lineStyle) penLine.setWidth(self.lineWidth) penLine.setBrush( QColor(self.lineColor[0], self.lineColor[1], self.lineColor[2])) penLine.setCapStyle(self.rpb_lineCap) penLine.setJoinStyle(Qt.RoundJoin) linePainter.setPen(penLine) linePainter.drawArc(self.positionX + self.posFactor, self.positionY + self.posFactor, self.rpb_Size - self.sizeFactor, self.rpb_Size - self.sizeFactor, self.startPosition, self.rpb_value) linePainter.end()
class FlowTheme: """A FlowTheme holds all design information for the flow. And it defines what themes exist. Notice, that all drawing of NodeInstances and PortInstances is done by NIPainter classes, while for each FlowTheme there exists exactly one NIPainter class. HOW TO CREATE NEW THEMES - Create a new subclass of NIPainter in NodeInstancePainter.py, and implement all methods that are passed in NIPainter (take a look at the other already existing NIPainter subclasses there for reference) - Add a new theme entry to the flow_themes array of DesignContainer and reference the new NIPainter subclass for your new theme you just made""" def __init__(self, name, flow_conn_exec_color, flow_conn_exec_width, flow_conn_exec_pen_style, flow_conn_data_color, flow_conn_data_width, flow_conn_data_pen_style, node_instance_painter, flow_background_color=QColor('#333333')): self.name = name self.node_inst_painter = node_instance_painter self.flow_conn_exec_pen = QPen(flow_conn_exec_color, flow_conn_exec_width) self.flow_conn_exec_pen.setStyle(flow_conn_exec_pen_style) self.flow_conn_exec_pen.setCapStyle(Qt.RoundCap) self.flow_conn_data_pen = QPen(flow_conn_data_color, flow_conn_data_width) self.flow_conn_data_pen.setStyle(flow_conn_data_pen_style) self.flow_conn_data_pen.setCapStyle(Qt.RoundCap) self.flow_background_color = flow_background_color def get_flow_conn_pen_inst(self, connection_type): if connection_type == 'data': return self.flow_conn_data_pen.__copy__() else: return self.flow_conn_exec_pen.__copy__()
class BoltPainter(BasePainter): def __init__(self, parent): BasePainter.__init__(self, parent) self._pen_z_1 = None self._pen_z_2 = None self._pen_z_0 = None self._length = None def setup(self): if self.is_set: return self._pen_z_2 = QPen(QColor(255, 0, 0), 5, Qt.SolidLine) self._pen_z_2.setCapStyle(Qt.RoundCap) self._pen_z_1 = QPen(QColor(255, 100, 100), 5, Qt.SolidLine) self._pen_z_1.setCapStyle(Qt.RoundCap) self._pen_z_0 = QPen(QColor(255, 255, 255), 5, Qt.SolidLine) self._pen_z_0.setCapStyle(Qt.RoundCap) self._length = 25 self.is_set = True def paint(self, painter: QPainter, model: BaseModel): if model.is_alive: assert self.is_set, 'Painter is not set.' _origin = self.transform(model.x, model.y, is_point=True) for _i, _pen in enumerate( [self._pen_z_2, self._pen_z_1, self._pen_z_0]): _factor = (3 - _i) / 3 _tail_x = _origin.x() - model.dir_y * self._length * _factor _tail_y = _origin.y() + model.dir_x * self._length * _factor _tail = QPoint(_tail_x, _tail_y) painter.setPen(_pen) painter.drawLine(_origin, _tail)
class ArcItem(QGraphicsLineItem): """Arc item to use with GraphViewForm. Connects a RelationshipItem to an ObjectItem.""" def __init__(self, rel_item, obj_item, width, is_wip=False): """Initializes item. Args: rel_item (spinetoolbox.widgets.graph_view_graphics_items.RelationshipItem): relationship item obj_item (spinetoolbox.widgets.graph_view_graphics_items.ObjectItem): object item width (float): Preferred line width """ super().__init__() self.rel_item = rel_item self.obj_item = obj_item self._width = float(width) self.is_wip = is_wip src_x = rel_item.x() src_y = rel_item.y() dst_x = obj_item.x() dst_y = obj_item.y() self.setLine(src_x, src_y, dst_x, dst_y) self._pen = QPen() self._pen.setWidth(self._width) color = QGuiApplication.palette().color(QPalette.Normal, QPalette.WindowText) color.setAlphaF(0.8) self._pen.setColor(color) self._pen.setStyle(Qt.SolidLine) self._pen.setCapStyle(Qt.RoundCap) self.setPen(self._pen) self.setZValue(-2) rel_item.add_arc_item(self) obj_item.add_arc_item(self) if self.is_wip: self.become_wip() self.setCursor(Qt.ArrowCursor) def mousePressEvent(self, event): """Accepts the event so it's not propagated.""" event.accept() def other_item(self, item): return { self.rel_item: self.obj_item, self.obj_item: self.rel_item }.get(item) def become_wip(self): """Turns this arc into a work-in-progress.""" self.is_wip = True self._pen.setStyle(Qt.DotLine) self.setPen(self._pen) def become_whole(self): """Removes the wip status from this arc.""" self.is_wip = False self._pen.setStyle(Qt.SolidLine) self.setPen(self._pen) def move_rel_item_by(self, pos_diff): """Moves source point. Args: pos_diff (QPoint) """ line = self.line() line.setP1(line.p1() + pos_diff) self.setLine(line) def move_obj_item_by(self, pos_diff): """Moves destination point. Args: pos_diff (QPoint) """ line = self.line() line.setP2(line.p2() + pos_diff) self.setLine(line) def adjust_to_zoom(self, transform): """Adjusts the item's geometry so it stays the same size after performing a zoom. Args: transform (QTransform): The view's transformation matrix after the zoom. """ factor = transform.m11() if factor < 1: return scaled_width = self._width / factor self._pen.setWidthF(scaled_width) self.setPen(self._pen) def wipe_out(self): self.obj_item.arc_items.remove(self) self.rel_item.arc_items.remove(self)
class spiralProgressBar(QtWidgets.QWidget): def __init__(self, parent=None): super(spiralProgressBar, self).__init__(parent) self.positionX = 0 self.positionY = 0 self.spb_Size = 0 self.posFactor = 0 self.sizeFactor = 0 self.spb_maximSize = (0, 0) self.spb_minimSize = (0, 0) self.spb_dynamicMin = True self.spb_dynamicMax = True self.noProgBar = 3 self.spb_value = [-48 * 16, -24 * 16, -12 * 16] self.spb_minimValue = [0, 0, 0] self.spb_maximValue = [100, 100, 100] self.spb_startPos = [ self.startPosFlags.North, self.startPosFlags.North, self.startPosFlags.North ] self.spb_direction = [ self.rotationFlags.Clockwise, self.rotationFlags.Clockwise, self.rotationFlags.Clockwise ] self.lineWidth = 5 self.lineColor = [[0, 159, 227], [0, 159, 227], [0, 159, 227]] self.lineStyle = [ self.lineStyleFlags.SolidLine, self.lineStyleFlags.SolidLine, self.lineStyleFlags.SolidLine ] self.lineCap = [ self.lineCapFlags.RoundCap, self.lineCapFlags.RoundCap, self.lineCapFlags.RoundCap ] self.varWidth = False self.widthIncr = 1 self.pathWidth = 5 self.pathColor = [[179, 241, 215], [179, 241, 215], [179, 241, 215]] self.pathPresent = True self.pathStyle = [ self.lineStyleFlags.SolidLine, self.lineStyleFlags.SolidLine, self.lineStyleFlags.SolidLine ] self.pathIndepend = False self.spb_gap = self.lineWidth * 2 #GAP BETWEEN THE ROUNDPROGRESS BAR MAKING A SPIRAL PROGRESS BAR. self.gapCngd = False self.spb_cngSize = 1 #------------------------------------------------------CLASS ENUMERATORS class lineStyleFlags: SolidLine = Qt.SolidLine DotLine = Qt.DotLine DashLine = Qt.DashLine class lineCapFlags: SquareCap = Qt.SquareCap RoundCap = Qt.RoundCap class rotationFlags: Clockwise = -1 AntiClockwise = 1 class startPosFlags: North = 90 * 16 South = -90 * 16 East = 0 * 16 West = 180 * 16 #------------------------------------------------------METHODS FOR CHANGING THE PROPERTY OF THE SPIRALPROGRESSBAR :SOLTS def spb_setMinimumSize(self, width, height): """ Minimum Size of the Widget ... Parameters -------------- width : int width of the Widget height : int height of the Widget Raises -------------- none """ self.spb_dynamicMin = False self.setMinimumSize(width, height) self.spb_minimSize = (width, height) self.update() def spb_setMaximumSize(self, width, height): """ Maximum Size of the Widget ... Parameters -------------- width : int width of the Widget height : int height of the Widget Raises -------------- none """ self.spb_dynamicMax = False self.setMaximumSize(width, height) self.spb_maximSize = (width, height) self.update() def spb_setNoProgressBar(self, num): """ By default the Number of progress bar in spiralProgressBar is: 3, Users can increase the number of progress bar upto 6.(min: 2), this function is used to do exactly that. ... Parameters -------------- num : int Number of progress bar. Raises -------------- Exception : "Supported Format: int and not: " + type(num) raised when the user passes a non-int 'num' to the method. """ if type(num) != type( 5 ): #MAKING SURE THAT THE ENTERED IS A NUMBER AND NOT A STRING OR OTHERS raise Exception("Supported Format: int and not: " + str(type(num))) if num <= 6 and num >= 2: self.noProgBar = num self.spb_value = [] self.spb_maximValue = [] self.spb_minimValue = [] self.spb_startPos = [] self.spb_direction = [] self.lineColor = [] self.lineStyle = [] self.lineCap = [] for each in range(0, self.noProgBar, 1): self.spb_value.append(-12 * self.noProgBar * 16 / (each + 1)) self.spb_maximValue.append(100) self.spb_minimValue.append(0) self.spb_startPos.append(self.startPosFlags.North) self.spb_direction.append(self.rotationFlags.Clockwise) self.lineColor.append([0, 159, 227]) self.lineStyle.append(self.lineStyleFlags.SolidLine) self.lineCap.append(self.lineCapFlags.RoundCap) self.pathColor.append([179, 241, 215]) self.pathStyle.append(self.lineStyleFlags.SolidLine) self.update() def spb_setValue(self, value): #value: TUPLE OF (value1, value2, value3) """ Set the current value of the Progress Bar. maximum value >= Value >= minimum Value The user can set the value of each progress bar within the spiralprogressbar independely. The 'value' tuple element order corresponds to the outer to inner most progressbar. ... Parameters -------------- value : tuple Ex: value = (0, 50, 22), this means value of outermost progress bar has the value of 0, midden one to 50, and innermost to 22. Raises -------------- Exception : "Value should be a tuple and not " + type(value) Rasied when the user passes a non-tuple data type to the module. ValueError : "Tuple length more than number of Progress Bars" Raised when the tuple contains more element than the number of concentric progress bar in the spiralProgressBar widget. ValueError : "Tuple length less than the number of Progress Bars" Raised when the tuple contains less element than the number of concentric progress bar in the spiralProgressBar widget. """ if type(value) != type(()): #IF INPUT IS NOT A TUPLE raise Exception("Value should be a Tuple and not " + str(type(value))) elif len( value ) > self.noProgBar: #IF TUPLE LENGTH IS MORE THAN THE NUMBER OF PROGRESS BAR raise ValueError("Tuple length more than number of Progress Bars") elif len( value ) < self.noProgBar: #IF INPUT TUPLE LENGTH IS LESS THAN THE NUMBER OF PROGRESS BAR raise ValueError( "Tuple length less than the number of Progress Bars") elif self.spb_value != value: #IF EVERY THING GOES RIGHT for each in range(0, self.noProgBar, 1): if value[each] != 'nc': #nc: NOC CHANGE STRING FOR ELEIMINATING THE NO CHANGE PROGRESS VALUES if value[each] < self.spb_minimValue[each]: spiralProgressBar.convValue(self, self.spb_minimValue[each], each) elif value[each] > self.spb_maximValue[each]: spiralProgressBar.convValue(self, self.spb_maximValue[each], each) else: spiralProgressBar.convValue(self, value[each], each) self.update() def spb_setMaximum(self, maxVal): """ Maximum Value of the progressbar, default is 100. ... Parameters -------------- maxVal : tuple Maximum value of each progressbar, in tuple, with elements in order Ex: maxVal = (100, 200, 300) : corresponding to 100 for the outermost, 200 for middle progress bar, 300 for innermost progressbar. Raises -------------- Exception : "The Max. for should be in form of a Tuple and not: " + type(maxVal) Rasied when the user passes a non-tuple data type to the module. ValueError : "Tuple length more than number of Progress Bars" Raised when the tuple contains more element than the number of concentric progress bar in the spiralProgressBar widget. ValueError : "Tuple length less than the number of Progress Bars" Raised when the tuple contains less element than the number of concentric progress bar in the spiralProgressBar widget. """ if type(maxVal) != type(()): #IF INPUT IS NOT A TUPLE raise Exception( "The Max. for should be in form of a Tuple and not: " + str(type(maxVal))) elif len( maxVal ) > self.noProgBar: #IF TUPLE LENGTH IS MORE THAN THE NUMBER OF PROGRESS BAR raise ValueError("Tuple length more than number of Progress Bars") elif len( maxVal ) < self.noProgBar: #IF INPUT TUPLE LENGTH IS LESS THAN THE NUMBER OF PROGRESS BAR raise ValueError( "Tuple length less than the number of Progress Bars") elif self.spb_maximValue != maxVal: for each in range(0, self.noProgBar, 1): #TO AVOID FUTURE DIVISION BY ZERO ERROR if maxVal[each] == self.spb_minimValue[each]: raise ValueError( "Maximum and Minimum Value Cannot be the Same") self.spb_maximValue = list(maxVal) self.update() def spb_setMinimum(self, minVal): """ Minimum Value of the progressbar, default is 0. ... Parameters -------------- minVal : tuple Minimum value of each progressbar, in tuple, with elements in order Ex: minVal = (0, 10, 20) : corresponding to 0 for the outermost, 10 for middle progress bar, 20 for innermost progressbar. Raises -------------- Exception : "The Min. for should be in form of a Tuple and not: " + type(minVal) Rasied when the user passes a non-tuple data type to the module. ValueError : "Tuple length more than number of Progress Bars" Raised when the tuple contains more element than the number of concentric progress bar in the spiralProgressBar widget. ValueError : "Tuple length less than the number of Progress Bars" Raised when the tuple contains less element than the number of concentric progress bar in the spiralProgressBar widget. """ if type(minVal) != type(()): #IF INPUT IS NOT A TUPLE raise Exception( "The Min. for should be in form of a Tuple and not: " + str(type(minVal))) elif len( minVal ) > self.noProgBar: #IF TUPLE LENGTH IS MORE THAN THE NUMBER OF PROGRESS BAR raise ValueError("Tuple length more than number of Progress Bars") elif len( minVal ) < self.noProgBar: #IF INPUT TUPLE LENGTH IS LESS THAN THE NUMBER OF PROGRESS BAR raise ValueError( "Tuple length less than the number of Progress Bars") elif self.spb_minimValue != minVal: for each in range(0, self.noProgBar, 1): #TO AVOID FUTURE DIVISION BY ZERO ERROR if minVal[each] == self.spb_maximValue[each]: raise ValueError( "Maximum and Minimum Value Cannot be the Same") self.spb_minimValue = list(minVal) self.update() def spb_setRange(self, minTuple, maxTuple): """ This function does the job of setting the Maximum value and Minimum value in one go. ... Parameters -------------- maxTuple : tuple Maximum value of each progressbar, in tuple, with elements in order Ex: maxVal = (100, 200, 300) : corresponding to 100 for the outermost, 200 for middle progress bar, 300 for innermost progressbar. minVal : tuple Minimum value of each progressbar, in tuple, with elements in order Ex: minVal = (0, 10, 20) : corresponding to 0 for the outermost, 10 for middle progress bar, 20 for innermost progressbar. Raises -------------- Exception : "The Minimum and Maximum should be a Tuple" Rasied when the user passes a non-tuple data type to the module. ValueError : "Tuple length more than number of Progress Bars" Raised when the tuple contains more element than the number of concentric progress bar in the spiralProgressBar widget. ValueError : "Tuple length less than the number of Progress Bars" Raised when the tuple contains less element than the number of concentric progress bar in the spiralProgressBar widget. """ if type(minTuple) != type(()) or type(maxTuple) != type(()): raise Exception("The Minimum and Maximum should be a Tuple") elif len(minTuple) > self.noProgBar or len(maxTuple) > self.noProgBar: raise ValueError( "Minimum/Maximum Tuple length exceeds the number of Progress Bar" ) elif len(minTuple) < self.noProgBar or len(maxTuple) < self.noProgBar: raise ValueError( "Minimum/Maximum Tuple length is less than the number of Progress Bar" ) for each in range(0, self.noProgBar, 1): if minTuple[each] == maxTuple[each]: raise ValueError("Minimum and Maximum cannot be the Same") self.spb_minimValue = minTuple self.spb_maximValue = maxTuple self.update() def spb_setGap(self, gap): """ Set the Gap between each concentric circle in the spiralProgressBar. Default is : gap = 2*line width ... Parameters -------------- gap : int Try different settings by passing an int to the function: 'int' corresponds to the "px" seperation between the concentric circles. Raises -------------- Exception : "Gap should be an integer and not: " + type(gap) Rasied when the user passes a non-tuple data type to the module. """ if type(gap) != type(5): raise ValueError("Gap should be an integer and not: " + str(type(gap))) else: self.spb_gap = gap self.gapCngd = True self.update() def spb_setInitialPos(self, position): """ Sets the statring point of the progress bar or the 0% position. Default is 'North' ... Parameters -------------- position : tuple The tuple elements accepts only string of : 'North', 'South', 'East' and 'West'. The order of arrangment matters i.e. the first element corresponds to the outer most concentric progress bar and the last element correspinds to the innermost circle. Ex : position = ('North', 'South', 'East') Raises -------------- Exception : "Position should be a Tuple and not " + type(position) Rasied when the user passes a non-tuple data type to the module. ValueError : "Tuple length more than number of Progress Bars" Raised when the tuple contains more element than the number of concentric progress bar in the spiralProgressBar widget. ValueError : "Tuple length less than the number of Progress Bars" Raised when the tuple contains less element than the number of concentric progress bar in the spiralProgressBar widget. """ if type(position) != type(()): #IF INPUT IS NOT A TUPLE raise Exception("Position should be a Tuple and not " + str(type(position))) elif len( position ) > self.noProgBar: #IF TUPLE LENGTH IS MORE THAN THE NUMBER OF PROGRESS BAR raise ValueError("Tuple length more than number of Progress Bars") elif len( position ) < self.noProgBar: #IF INPUT TUPLE LENGTH IS LESS THAN THE NUMBER OF PROGRESS BAR raise ValueError( "Tuple length less than the number of Progress Bars") else: for each in range(0, self.noProgBar, 1): if type(position[each]) != type("string"): raise Exception( "Position Tuple elements should be String and not: " + str(type(position[each]))) elif position[each] == 'North': self.spb_startPos[each] = self.startPosFlags.North elif position[each] == 'South': self.spb_startPos[each] = self.startPosFlags.South elif position[each] == 'East': self.spb_startPos[each] = self.startPosFlags.East elif position[each] == 'West': self.spb_startPos[each] = self.startPosFlags.West else: raise Exception( "Position can hold Property: 'North', 'South', 'East' and 'West' and not: " + position[each]) self.update() def spb_reset(self): """ Resets the progress bar to the 0%. ... Parameters -------------- none Raises -------------- none """ for each in range(0, self.noProgBar, 1): spiralProgressBar.convValue(self, self.spb_minimValue[each], each) self.update() def spb_setGeometry(self, posX, posY): """ This module changes the position of the widget. Default it is : (0, 0). ... Parameters -------------- posX : int The vertical position of the widget from the top of the window inside which the widget lies. By default it is 0. The user can change the position to better suite his style and positioning of the widget. posY : int Raises -------------- Exception : Position should be an int If the user passes a non-int data type. """ if type(posX) != type(5) or type(posY) != type(5): raise Exception("Position should be a int and not: X" + str(type(posX))) + ", Y: " + str(type(posY)) return if self.positionX != posX: self.positionX = posX if self.positionY != posY: self.positionY = posY self.update() def spb_setDirection(self, direction): """ Direction of rotation of the spiral progress bar. ... Parameters -------------- direction : tuple Direction that the round progress bar can hold are : 'Clockwise' and 'AntiClockwise' Default is 'Clockwise'. The tuple take string as elements corresponding to the direction of each of the concentric circles. Raises -------------- Exception : "Direction should be a Tuple" Rasied when the user passes a non-tuple data type to the module. ValueError : "Tuple length more than number of Progress Bars" Raised when the tuple contains more element than the number of concentric progress bar in the spiralProgressBar widget. ValueError : "Tuple length less than the number of Progress Bars" Raised when the tuple contains less element than the number of concentric progress bar in the spiralProgressBar widget. Exception : "Direction Tuple elements should be String" Rasies when the elements of the tuple is not a string. """ if type(direction) != type(()): #IF INPUT IS NOT A TUPLE raise Exception("Direction should be a Tuple and not " + str(type(direction))) elif len( direction ) > self.noProgBar: #IF TUPLE LENGTH IS MORE THAN THE NUMBER OF PROGRESS BAR raise ValueError("Tuple length more than number of Progress Bars") elif len( direction ) < self.noProgBar: #IF INPUT TUPLE LENGTH IS LESS THAN THE NUMBER OF PROGRESS BAR raise ValueError( "Tuple length less than the number of Progress Bars") else: for each in range(0, self.noProgBar, 1): if type(direction[each]) != type("String"): raise Exception( "Direction Tuple elements should be String and not: " + str(type(direction[each]))) elif direction[each] == 'Clockwise': self.spb_direction[each] = self.rotationFlags.Clockwise elif direction[each] == 'AntiClockwise': self.spb_direction[each] = self.rotationFlags.AntiClockwise else: raise Exception( "Direction can hold Property: 'Clockwise'/'AntiClockwise' and not: " + str(type(direction[each]))) self.update() def variableWidth(self, inp): """ A flag for varing the progress bar size. ... Parameters -------------- inp : bool True : Changes the size of the width of line progressely. Raises -------------- Exception : Variable width should be a bool : True/False Rasied when the user passes a non-bool data type to the module. """ if type(inp) != type(True): raise Exception("Variable Width should be a Bool and not " + str(type(inp))) else: self.varWidth = inp self.update() def spb_widthIncrement(self, increm): """ Width increment for incrment in the line width. Default is 1px. User can sepcify the amount of px to increment form the outer to inner circle progressbar. ... Parameters -------------- incrment : int Increment passed to the module as int px. Raises -------------- Exception : Increment should be an integer Rasied when the user passes a non-int data type to the module. """ if type(increm) != type(5): raise Exception("Increment should be an integer and not " + str(type(increm))) else: self.widthIncr = increm self.update() def spb_lineWidth(self, width): """ Line width of the circles in the spiral progress bar. ... Parameters -------------- width : int Raises -------------- Exception : Width should be an Integer Rasied when the user passes a non-int data type to the module. """ if type(width) != type(5): raise Exception("Width should be an Integer and not " + str(type(width))) else: self.lineWidth = width if self.gapCngd != True: self.spb_gap = self.lineWidth * 2 self.update() def spb_lineColor(self, color): """ Color of line in the spiral progress bar. Each concentric progress bar has its own color settings. ... Parameters -------------- color : tuple Color tuple corresponds to the color of each line which is a tuple of (R, G, B). Ex : color = ((R, G, B), (R, G, B), (R, G, B)) Elements of the color tuple is in correspondance with the order : outer to innermost circles in progress bar. Raises -------------- Exception : Color should be a Tuple Rasied when the user passes a non-tuple data type to the module. ValueError : "Tuple length more than number of Progress Bars" Raised when the tuple contains more element than the number of concentric progress bar in the spiralProgressBar widget. ValueError : "Tuple length less than the number of Progress Bars" Raised when the tuple contains less element than the number of concentric progress bar in the spiralProgressBar widget. """ if type(color) != type(()): raise Exception("Color should be a Tuple and not " + str(type(Color))) elif type(color[0]) != type(()): raise Exception( "Color should be in Format: ((R, G, B), (R, G, B), (R, G, B)) and not any other" ) elif len(color) > self.noProgBar: raise ValueError("Tuple length more than number of Progress Bars") elif len( color ) < self.noProgBar: #IF INPUT TUPLE LENGTH IS LESS THAN THE NUMBER OF PROGRESS BAR raise ValueError( "Tuple length less than the number of Progress Bars") else: for each in range(0, self.noProgBar, 1): if len(color[each]) != 3: raise Exception('Color should be in format (R, G, B)') elif self.lineColor[each] != color[each]: self.lineColor[each] = color[each] self.update() def spb_lineStyle(self, style): """ line style of the spiral progress bar. ... Parameters -------------- style : tuple Style types : 'SolidLine', 'DotLine' and 'DashLine'. Users can pass the style for each progress bar in the order : first element corresponds to the styleof outermost progressbar and viceversa. Raises -------------- Exception : Style should be a tuple Rasied when the user passes a non-tuple data type to the module. ValueError : "Tuple length more than number of Progress Bars" Raised when the tuple contains more element than the number of concentric progress bar in the spiralProgressBar widget. ValueError : "Tuple length less than the number of Progress Bars" Raised when the tuple contains less element than the number of concentric progress bar in the spiralProgressBar widget. """ if type(style) != type(()): raise Exception("Style should be a tuple and not: " + str(type(style))) elif len( style ) > self.noProgBar: #IF TUPLE LENGTH IS MORE THAN THE NUMBER OF PROGRESS BAR raise ValueError("Tuple length more than number of Progress Bars") elif len( style ) < self.noProgBar: #IF INPUT TUPLE LENGTH IS LESS THAN THE NUMBER OF PROGRESS BAR raise ValueError( "Tuple length less than the number of Progress Bars") else: for each in range(0, self.noProgBar, 1): if type(style[each]) != type("String"): raise Exception( "Style Tuple element should be a String and not: " + str(type(style[each]))) elif style[each] == 'SolidLine': self.lineStyle[each] = self.lineStyleFlags.SolidLine elif style[each] == 'DotLine': self.lineStyle[each] = self.lineStyleFlags.DotLine elif style[each] == 'DashLine': self.lineStyle[each] = self.lineStyleFlags.DashLine else: raise Exception( "Style can hold 'SolidLine', DotLine' and 'DashLine' only." ) self.update() def spb_lineCap(self, cap): """ Cap i.e. the end of the line : to be Round or Square. ... Parameters -------------- cap : tuple Cap : 'RoundCap' and 'SquareCap'. Users can pass the desired cap of the line as a string passed in the following order of : Outer progress bar : first element in the tuple and viceversa. Raises -------------- Exception : Cap should be a tuple Rasied when the user passes a non-tuple data type to the module. ValueError : "Tuple length more than number of Progress Bars" Raised when the tuple contains more element than the number of concentric progress bar in the spiralProgressBar widget. ValueError : "Tuple length less than the number of Progress Bars" Raised when the tuple contains less element than the number of concentric progress bar in the spiralProgressBar widget. """ if type(cap) != type(()): raise Exception("Cap should be a tuple and not: " + str(type(cap))) elif len( cap ) > self.noProgBar: #IF TUPLE LENGTH IS MORE THAN THE NUMBER OF PROGRESS BAR raise ValueError("Tuple length more than number of Progress Bars") elif len( cap ) < self.noProgBar: #IF INPUT TUPLE LENGTH IS LESS THAN THE NUMBER OF PROGRESS BAR raise ValueError( "Tuple length less than the number of Progress Bars") else: for each in range(0, self.noProgBar, 1): if type(cap[each]) != type("String"): raise Exception( 'Cap Tuple element should be a String and not a: ' + str(type(cap[each]))) elif cap[each] == 'SquareCap': self.lineCap[each] = self.lineCapFlags.SquareCap elif cap[each] == 'RoundCap': self.lineCap[each] = self.lineCapFlags.RoundCap else: raise Exception( "Cap can hold 'SquareCap' and 'RoundCap' only") self.update() def spb_setPathHidden(self, hide): """ Hides the path in the spiral progress bar. ... Parameters -------------- hide : bool Raises -------------- Exception : Hidden accept a bool Rasied when the user passes a non-bool data type to the module. """ if type(hide) != type(True): raise Exception("Hidden accept a bool and not: " + str(type(hide))) elif hide == True: self.pathPresent = False else: self.pathPresent = True def spb_pathColor(self, color): """ Color of path in the spiral progress bar. Each concentric progress bar has its own color settings. ... Parameters -------------- color : tuple Color tuple corresponds to the color of each path which is a tuple of (R, G, B). Ex : color = ((R, G, B), (R, G, B), (R, G, B)) Elements of the color tuple is in correspondance with the order : outer to innermost circles in progress bar. Raises -------------- Exception : Color should be a Tuple Rasied when the user passes a non-tuple data type to the module. ValueError : "Tuple length more than number of Progress Bars" Raised when the tuple contains more element than the number of concentric progress bar in the spiralProgressBar widget. ValueError : "Tuple length less than the number of Progress Bars" Raised when the tuple contains less element than the number of concentric progress bar in the spiralProgressBar widget. """ if type(color) != type(()): raise Exception("Color should be a Tuple and not " + str(type(Color))) elif type(color[0]) != type(()): raise Exception( "Color should be in Format: ((R, G, B), (R, G, B), (R, G, B)) and not any other" ) elif len(color) > self.noProgBar: raise ValueError("Tuple length more than number of Progress Bars") elif len( color ) < self.noProgBar: #IF INPUT TUPLE LENGTH IS LESS THAN THE NUMBER OF PROGRESS BAR raise ValueError( "Tuple length less than the number of Progress Bars") else: for each in range(0, self.noProgBar, 1): if len(color[each]) != 3: raise Exception('Color should be in format (R, G, B)') elif self.pathColor[each] != color[each]: self.pathColor[each] = color[each] self.update() #------------------------------------------------------METHODS FOR GETTING THE PROPERTY OF SPIRALPROGRESSBAR SLOTS #------------------------------------------------------ENGINE: WHERE ALL THE REAL STUFF TAKE PLACE: WORKING OF THE SPIRALPROGRESSBAR def spb_MinimumSize(self, dynMax, minim, maxim): """ Realtime automatic minimum size determiner for the spiral progress bar. For this to achieve the function first checks the size of the layout, where the spiralprogressbar lies. From that info the, it calculate the minimum size for the spiral progressbar so that all the circles in the spiral progress bar is clearly visible. ... Parameters -------------- none. Return -------------- none. """ spb_Height = self.height() spb_Width = self.width() if dynMax: if spb_Width >= spb_Height and spb_Height >= minim[1]: self.spb_Size = spb_Height elif spb_Width < spb_Height and spb_Width >= minim[0]: self.spb_Size = spb_Width else: if spb_Width >= spb_Height and spb_Height <= maxim[1]: self.spb_Size = spb_Height elif spb_Width < spb_Height and spb_Width <= maxim[0]: self.spb_Size = spb_Width def geometricFactor(self): """ Width of the line should be subrtracted from the size of the progrress bar, inorder to properly fit inot the layout properly without any cut in the widget margins. ... Parameters -------------- none. Return -------------- none. """ self.posFactor = self.lineWidth / 2 + 1 self.sizeFactor = self.lineWidth + 1 def convValue(self, value, pos): """ Convert the value from the user entered to the percentage depending on the maximum and minimum value. Calculagted by the relation : (value - minimum)/(maximum - minimum) ... Parameters -------------- none. Return -------------- none. """ self.spb_value[pos] = ( (value - self.spb_minimValue[pos]) / (self.spb_maximValue[pos] - self.spb_minimValue[pos])) * 360 * 16 self.spb_value[pos] = self.spb_direction[pos] * self.spb_value[pos] def paintEvent(self, event: QPaintEvent): """ The place where the drawing takes palce. ... Parameters -------------- none. Return -------------- none. """ if self.spb_dynamicMin: self.setMinimumSize( QSize(self.lineWidth * 6 + self.pathWidth * 6, self.lineWidth * 6 + self.pathWidth * 6)) spiralProgressBar.spb_MinimumSize(self, self.spb_dynamicMax, self.spb_minimSize, self.spb_maximSize) spiralProgressBar.geometricFactor(self) spiralIncrem = 0 spiralIncrem2 = 0 if self.pathIndepend != True: self.pathWidth = self.lineWidth self.tempWidth = self.pathWidth if self.pathPresent: for path in range(0, self.noProgBar, 1): if self.varWidth == True: #CREAETS A INCREASING OR DECREASING TYPE OF WITH self.tempWidth = self.tempWidth + self.widthIncr if self.gapCngd != True: self.spb_gap = self.tempWidth * 2 self.pathPainter = QPainter(self) self.pathPainter.setRenderHint(QPainter.Antialiasing) self.penPath = QPen() self.penPath.setStyle(self.pathStyle[path]) self.penPath.setWidth(self.tempWidth) self.penPath.setBrush( QColor(self.pathColor[path][0], self.pathColor[path][1], self.pathColor[path][2])) self.pathPainter.setPen(self.penPath) self.pathPainter.drawArc( self.positionX + self.posFactor + self.spb_cngSize * spiralIncrem2, self.positionY + self.posFactor + self.spb_cngSize * spiralIncrem2, self.spb_Size - self.sizeFactor - 2 * self.spb_cngSize * spiralIncrem2, self.spb_Size - self.sizeFactor - 2 * self.spb_cngSize * spiralIncrem2, self.spb_startPos[path], 360 * 16) self.pathPainter.end() spiralIncrem2 = spiralIncrem2 + self.spb_gap self.tempWidth = self.lineWidth #TEMPWIDTH TEMPORARLY STORES THE LINEWIDTH, USEFUL IN VARIABLE WIDTH OPTION. for bar in range(0, self.noProgBar, 1): if self.varWidth == True: #CREAETS A INCREASING OR DECREASING TYPE OF WITH self.tempWidth = self.tempWidth + self.widthIncr if self.gapCngd != True: self.spb_gap = self.tempWidth * 2 self.linePainter = QPainter(self) self.linePainter.setRenderHint(QPainter.Antialiasing) self.penLine = QPen() self.penLine.setStyle(self.lineStyle[bar]) self.penLine.setWidth(self.tempWidth) self.penLine.setCapStyle(self.lineCap[bar]) self.penLine.setBrush( QColor(self.lineColor[bar][0], self.lineColor[bar][1], self.lineColor[bar][2])) self.linePainter.setPen(self.penLine) self.linePainter.drawArc( self.positionX + self.posFactor + self.spb_cngSize * spiralIncrem, self.positionY + self.posFactor + self.spb_cngSize * spiralIncrem, self.spb_Size - self.sizeFactor - 2 * self.spb_cngSize * spiralIncrem, self.spb_Size - self.sizeFactor - 2 * self.spb_cngSize * spiralIncrem, self.spb_startPos[bar], self.spb_value[bar]) self.linePainter.end() spiralIncrem = spiralIncrem + self.spb_gap
def drawForeground(self, painter, rect): """Draws all connections and borders around selected items.""" pen = QPen() if Design.flow_theme == 'dark std': # pen.setColor('#BCBBF2') pen.setWidth(5) pen.setCapStyle(Qt.RoundCap) elif Design.flow_theme == 'dark tron': # pen.setColor('#452666') pen.setWidth(4) pen.setCapStyle(Qt.RoundCap) elif Design.flow_theme == 'ghostly' or Design.flow_theme == 'blender': pen.setWidth(2) pen.setCapStyle(Qt.RoundCap) # DRAW CONNECTIONS for ni in self.all_node_instances: for o in ni.outputs: for cpi in o.connected_port_instances: if o.type_ == 'data': pen.setStyle(Qt.DashLine) elif o.type_ == 'exec': pen.setStyle(Qt.SolidLine) path = self.connection_path( o.gate.get_scene_center_pos(), cpi.gate.get_scene_center_pos()) w = path.boundingRect().width() h = path.boundingRect().height() gradient = QRadialGradient(path.boundingRect().center(), pythagoras(w, h) / 2) r = 0 g = 0 b = 0 if Design.flow_theme == 'dark std': r = 188 g = 187 b = 242 elif Design.flow_theme == 'dark tron': r = 0 g = 120 b = 180 elif Design.flow_theme == 'ghostly' or Design.flow_theme == 'blender': r = 0 g = 17 b = 25 gradient.setColorAt(0.0, QColor(r, g, b, 255)) gradient.setColorAt(0.75, QColor(r, g, b, 200)) gradient.setColorAt(0.95, QColor(r, g, b, 0)) gradient.setColorAt(1.0, QColor(r, g, b, 0)) pen.setBrush(gradient) painter.setPen(pen) painter.drawPath(path) # DRAW CURRENTLY DRAGGED CONNECTION if self.dragging_connection: pen = QPen('#101520') pen.setWidth(3) pen.setStyle(Qt.DotLine) painter.setPen(pen) gate_pos = self.gate_selected.get_scene_center_pos() if self.gate_selected.parent_port_instance.direction == 'output': painter.drawPath( self.connection_path(gate_pos, self.last_mouse_move_pos)) else: painter.drawPath( self.connection_path(self.last_mouse_move_pos, gate_pos)) # DRAW SELECTED NIs BORDER for ni in self.selected_node_instances(): pen = QPen(QColor('#245d75')) pen.setWidth(3) painter.setPen(pen) painter.setBrush(Qt.NoBrush) size_factor = 1.2 x = ni.pos().x() - ni.boundingRect().width() / 2 * size_factor y = ni.pos().y() - ni.boundingRect().height() / 2 * size_factor w = ni.boundingRect().width() * size_factor h = ni.boundingRect().height() * size_factor painter.drawRoundedRect(x, y, w, h, 10, 10) # DRAW SELECTED DRAWINGS BORDER for p_o in self.selected_drawings(): pen = QPen(QColor('#a3cc3b')) pen.setWidth(2) painter.setPen(pen) painter.setBrush(Qt.NoBrush) size_factor = 1.05 x = p_o.pos().x() - p_o.width / 2 * size_factor y = p_o.pos().y() - p_o.height / 2 * size_factor w = p_o.width * size_factor h = p_o.height * size_factor painter.drawRoundedRect(x, y, w, h, 6, 6) painter.drawEllipse(p_o.pos().x(), p_o.pos().y(), 2, 2)
def getPenFromCmnd(self, peninfo): ''' Returns a QPen based on the information in the dictionary peninfo. A ValueError is raised if the value for the "style", "capstyle", or "joinstyle" key, if given, is not recognized. Recognized keys in the outline dictionary are: "color": color name or 24-bit RGB integer value (eg, 0xFF0088) "alpha": alpha value from 0 (transparent) to 255 (opaque) "width": pen width in points (1/72 inches); possibly further scaled by the width scaling factor "style": pen style name ("solid", "dash", "dot", "dashdot", "dashdotdot") "capstyle": pen cap style name ("square", "flat", "round") "joinstyle": pen join style name ("bevel", "miter", "round") ''' try: mycolor = self.getColorFromCmnd(peninfo) mypen = QPen(mycolor) except KeyError: mypen = QPen() try: penwidth = float(peninfo["width"]) penwidth *= self.__viewer.widthScalingFactor() mypen.setWidthF(penwidth) except KeyError: pass try: mystyle = peninfo["style"] if mystyle == "solid": mystyle = Qt.SolidLine elif mystyle == "dash": mystyle = Qt.DashLine elif mystyle == "dot": mystyle = Qt.DotLine elif mystyle == "dashdot": mystyle = Qt.DashDotLine elif mystyle == "dashdotdot": mystyle = Qt.DashDotDotLine else: raise ValueError("Unknown pen style '%s'" % str(mystyle)) mypen.setStyle(mystyle) except KeyError: pass try: mystyle = peninfo["capstyle"] if mystyle == "square": mystyle = Qt.SquareCap elif mystyle == "flat": mystyle = Qt.FlatCap elif mystyle == "round": mystyle = Qt.RoundCap else: raise ValueError("Unknown pen cap style '%s'" % str(mystyle)) mypen.setCapStyle(mystyle) except KeyError: pass try: mystyle = peninfo["joinstyle"] if mystyle == "bevel": mystyle = Qt.BevelJoin elif mystyle == "miter": mystyle = Qt.MiterJoin elif mystyle == "round": mystyle = Qt.RoundJoin else: raise ValueError("Unknown pen join style '%s'" % str(mystyle)) mypen.setJoinStyle(mystyle) except KeyError: pass return mypen
def draw_mini_map(self, painter, is_full=False): _pixmap_minimap = QPixmap( QSize(min([self.width(), self.height()]), min([self.width(), self.height()]))) if is_full else QPixmap( QSize(350, 350)) if is_full: _p_origin = QPoint((self.width() - _pixmap_minimap.width()) // 2, (self.height() - _pixmap_minimap.height()) // 2) else: _p_origin = QPoint(self.width() - _pixmap_minimap.width(), self.height() - _pixmap_minimap.height()) _pixmap_minimap.fill(QColor('#00284d')) # DRAW DEAD ZONE _dz_radius = self.controller.get_dead_zone_radius() _xy = QPoint(*self.controller.get_dead_zone_center().tolist()) # GET MOBILE OBJECTS _model_ships = self.controller.get_data_from_players(enemy_only=False) _model_bolts = self.controller.get_data_from_bolts() _model_asteroids = self.controller.get_data_from_asteroids() # SET MINI MAP _w_ratio = _pixmap_minimap.width() / (PartyConst.WIDTH * 1.25) _h_ratio = _pixmap_minimap.height() / (PartyConst.HEIGHT * 1.25) _minimap_painter = QPainter(_pixmap_minimap) _minimap_painter.setOpacity(1) for _ship in _model_ships: _w = _ship.radius * _w_ratio if _w < 3: _w = 3 _x, _y = int( _ship.x * _w_ratio) + _pixmap_minimap.width() // 2, int( _ship.y * _h_ratio) + _pixmap_minimap.height() // 2 if _ship.is_alive: _factor_1 = (time.time() * 2) % 2 _factor_2 = (time.time() * 2 + .5) % 2 _minimap_painter.setPen( QPen(QColor('#ffffff'), 1, Qt.SolidLine)) _minimap_painter.setOpacity(1 - _factor_1) _minimap_painter.drawEllipse(QPoint(_x, _y), int(20 * _factor_1), int(20 * _factor_1)) _minimap_painter.setOpacity(1 - _factor_2) _minimap_painter.drawEllipse(QPoint(_x, _y), int(20 * _factor_2), int(20 * _factor_2)) _minimap_painter.setPen( QPen(QColor(*_ship.color), _w, Qt.SolidLine)) _minimap_painter.setOpacity(1) _minimap_painter.drawPoint(_x, _y) for _astroid in _model_asteroids: _w = _astroid.radius * _w_ratio if _w < 5: _w = 5 _pen = QPen(QColor('#ffb86c'), _w, Qt.SolidLine) _pen.setCapStyle(Qt.RoundCap) _minimap_painter.setPen(_pen) _x, _y = int( _astroid.x * _w_ratio) + _pixmap_minimap.width() // 2, int( _astroid.y * _h_ratio) + _pixmap_minimap.height() // 2 _minimap_painter.drawPoint(_x, _y) for _bolt in _model_bolts: _w = _bolt.radius * _w_ratio if _w < 1: _w = 1 _minimap_painter.setPen(QPen(QColor('#ff5555'), _w, Qt.SolidLine)) _x, _y = int( _bolt.x * _w_ratio) + _pixmap_minimap.width() // 2, int( _bolt.y * _h_ratio) + _pixmap_minimap.height() // 2 _minimap_painter.drawPoint(_x, _y) _xy.setX(_xy.x() * _w_ratio + _pixmap_minimap.width() // 2) _xy.setY(_xy.y() * _h_ratio + _pixmap_minimap.height() // 2) _minimap_painter.setPen(QPen(QColor('#8be9fd'), 3, Qt.SolidLine)) _minimap_painter.drawEllipse(_xy, _dz_radius * _w_ratio, _dz_radius * _h_ratio) if not is_full: _minimap_painter.setPen(QPen(QColor('#ffffff'), 1, Qt.SolidLine)) _x = -self.camera_pst.x() * _w_ratio + _pixmap_minimap.width() // 2 _y = self.camera_pst.y() * _h_ratio + _pixmap_minimap.height() // 2 _w = self.width() * _w_ratio _h = self.height() * _h_ratio _minimap_painter.drawRect(_x, _y - _h, _w, _h) _minimap_painter.end() _pixmap_minimap = _pixmap_minimap.transformed(QTransform().scale( 1, -1)) painter.setOpacity(1 if is_full else .75) painter.drawPixmap(_p_origin, _pixmap_minimap)
def draw(self, painter: QPainter): render_display(self.display, painter) pen = QPen() pen.setWidth(self.width // 50) pen.setCapStyle(Qt.PenCapStyle.RoundCap) painter.setPen(pen)
class Canvas(QWidget): content_changed = Signal() _background_color = QColor.fromRgb(0, 0, 0) _foreground_color = QColor.fromRgb(255, 255, 255) def __init__(self, parent, w, h, pen_width, scale): super().__init__(parent) self.w = w self.h = h self.scaled_w = scale * w self.scaled_h = scale * h self.scale = scale # Set size self.setFixedSize(self.scaled_w, self.scaled_h) # Create image self.small_image = QImage(self.w, self.h, QImage.Format_RGB32) self.small_image.fill(self._background_color) self.large_image = QImage(self.scaled_w, self.scaled_h, QImage.Format_RGB32) self.large_image.fill(self._background_color) # Create pen self.pen = QPen() self.pen.setColor(self._foreground_color) self.pen.setJoinStyle(Qt.RoundJoin) self.pen.setCapStyle(Qt.RoundCap) self.pen.setWidthF(scale * pen_width) # There is currently no path self.currentPath = None self.content_changed.connect(self.repaint) def _get_painter(self, paintee): painter = QPainter(paintee) painter.setPen(self.pen) painter.setRenderHint(QPainter.Antialiasing, True) return painter def _derive_small_image(self, large_image=None): if large_image is None: large_image = self.large_image # Downsample image self.small_image = large_image.scaled(self.w, self.h, mode=Qt.SmoothTransformation) self.content_changed.emit() def _current_path_updated(self, terminate_path=False): # Determine whether to draw on the large image directly or whether to make a temporary copy paintee = self.large_image if terminate_path else self.large_image.copy( ) # Draw path on the large image of choice painter = self._get_painter(paintee) if self.currentPath.elementCount() != 1: painter.drawPath(self.currentPath) else: painter.drawPoint(self.currentPath.elementAt(0)) painter.end() # Optionally terminate the path if terminate_path: self.currentPath = None # Downsample image self._derive_small_image(paintee) def _clear_image(self): self.large_image.fill(self._background_color) self._derive_small_image() def get_content(self): return np.asarray(self.small_image.constBits()).reshape( (self.h, self.w, -1)) def set_content(self, image_rgb): for row in range(image_rgb.shape[0]): for col in range(image_rgb.shape[1]): self.small_image.setPixel(col, row, image_rgb[row, col]) self.large_image = self.small_image.scaled( self.scaled_w, self.scaled_h, mode=Qt.SmoothTransformation) self._derive_small_image() self.content_changed.emit() def mousePressEvent(self, event): if event.button() == Qt.LeftButton: # Create new path self.currentPath = QPainterPath() self.currentPath.moveTo(event.pos()) self._current_path_updated() def mouseMoveEvent(self, event): if (event.buttons() & Qt.LeftButton) and self.currentPath is not None: # Add point to current path self.currentPath.lineTo(event.pos()) self._current_path_updated() def mouseReleaseEvent(self, event): if (event.button() == Qt.LeftButton) and self.currentPath is not None: # Add terminal point to current path self.currentPath.lineTo(event.pos()) self._current_path_updated(terminate_path=True) elif event.button() == Qt.RightButton: self._clear_image() def paintEvent(self, event): paint_rect = event.rect() # Only paint the surface that needs painting painter = self._get_painter(self) # Draw image painter.scale(self.scale, self.scale) painter.drawImage(paint_rect, self.small_image, paint_rect) painter.end() painter = self._get_painter(self) #if self.currentPath is not None: # painter.drawPath(self.currentPath) @Slot() def repaint(self): super().repaint()
class ImageSegmenterView(QGraphicsView): photoClicked = Signal(QPoint) def __init__(self, parent): super(ImageSegmenterView, self).__init__(parent) self._zoom = 0 self.empty = True self._scene = QGraphicsScene(self) self._photo = QGraphicsPixmapItem() self.image_hidden = False self._seglayer = QGraphicsPixmapItem() self._seglayer.setOpacity(0.5) self._scene.addItem(self._photo) self._scene.addItem(self._seglayer) self.setScene(self._scene) self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse) self.setResizeAnchor(QGraphicsView.AnchorUnderMouse) # self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) # self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) self.setBackgroundBrush(QBrush(QtCore.Qt.darkGray)) self.setFrameShape(QFrame.NoFrame) self.seg_image = None self.start = False self.prev_point = None self.painter = None self.segmenter_pen = QPen(QtCore.Qt.green, 30, QtCore.Qt.SolidLine) self.segmenter_pen.setCapStyle(QtCore.Qt.RoundCap) self.segmenter_pen.setJoinStyle(QtCore.Qt.RoundJoin) self.erase = False self.changed = False self.history = collections.deque(maxlen=10) self.future = collections.deque(maxlen=10) def hasPhoto(self): return not self.empty def fitInView(self, scale=True): rect = QtCore.QRectF(self._photo.pixmap().rect()) if not rect.isNull(): self.setSceneRect(rect) if self.hasPhoto(): unity = self.transform().mapRect(QtCore.QRectF(0, 0, 1, 1)) self.scale(1 / unity.width(), 1 / unity.height()) viewrect = self.viewport().rect() scenerect = self.transform().mapRect(rect) factor = min(viewrect.width() / scenerect.width(), viewrect.height() / scenerect.height()) self.scale(factor, factor) self._zoom = 0 def setPhoto(self, pixmap=None): self._zoom = 0 if pixmap and not pixmap.isNull(): self.empty = False self.changed = False self._photo.setPixmap(pixmap) self.seg_image = QImage(pixmap.width(), pixmap.height(), QImage.Format_ARGB32_Premultiplied) self.seg_image.fill(QtCore.Qt.transparent) self._seglayer.setPixmap(QPixmap.fromImage(self.seg_image)) else: self._empty = True self._photo.setPixmap(QPixmap()) self.fitInView() def save_state(self): if self.future is not None: while len(self.future) > 0: present = self.future.pop() del present if self.seg_image is not None: self.history.append(self.seg_image.copy()) def clear_history(self): while len(self.history) > 0: present = self.history.pop() del present def undo(self): if len(self.history) > 0: self.future.append(self.seg_image) present = self.history.pop() self.seg_image = present self._seglayer.setPixmap(QPixmap.fromImage(self.seg_image)) def redo(self): if len(self.future) > 0: self.history.append(self.seg_image) present = self.future.pop() self.seg_image = present self._seglayer.setPixmap(QPixmap.fromImage(self.seg_image)) def setSegLayer(self, pixmap=None): if not self._photo.pixmap().isNull(): self.save_state() self.seg_image = QImage(pixmap.toImage()) self._seglayer.setPixmap(QPixmap.fromImage(self.seg_image)) def resetSegLayer(self): if not self._photo.pixmap().isNull(): self.changed = True self.save_state() del self.seg_image self.seg_image = QImage(self._photo.pixmap().width(), self._photo.pixmap().height(), QImage.Format_ARGB32_Premultiplied) self.seg_image.fill(QtCore.Qt.transparent) self._seglayer.setPixmap(QPixmap.fromImage(self.seg_image)) def wheelEvent(self, event): if self.hasPhoto() and not self.start: if event.angleDelta().y() > 0: factor = 1.25 self._zoom += 1 else: factor = 0.8 self._zoom -= 1 if self._zoom > 0: self.scale(factor, factor) elif self._zoom == 0: self.fitInView() else: self._zoom = 0 def mousePressEvent(self, event): if not self._photo.pixmap().isNull(): if event.button() == QtCore.Qt.LeftButton: self.save_state() self.start = True self.painter = QPainter(self.seg_image) if self.erase: self.painter.setCompositionMode( QPainter.CompositionMode_Clear) self.painter.setPen(self.segmenter_pen) self.paint_point(event.pos()) elif event.button() == QtCore.Qt.RightButton: if not self._photo.pixmap().isNull(): self.setDragMode(QGraphicsView.ScrollHandDrag) self.scroll_origin = self.mapToScene(event.pos()) # if self._photo.isUnderMouse(): # self.photoClicked.emit(self.mapToScene(event.pos()).toPoint()) super(ImageSegmenterView, self).mousePressEvent(event) def mouseMoveEvent(self, event): if not self._photo.pixmap().isNull(): if self.start: self.paint_point(event.pos()) if event.buttons() & QtCore.Qt.RightButton: newpoint = self.mapToScene(event.pos()) translation = newpoint - self.scroll_origin self.translate(translation.x(), translation.y()) self.scroll_origin = self.mapToScene(event.pos()) super(ImageSegmenterView, self).mouseMoveEvent(event) def mouseReleaseEvent(self, event): if not self._photo.pixmap().isNull(): if self.start: self.start = False self.prev_point = None self.painter.end() if self.dragMode() == QGraphicsView.ScrollHandDrag: self.setDragMode(QGraphicsView.NoDrag) def paint_point(self, pos): self.changed = True pos = self.mapToScene(pos).toPoint() if self.prev_point is not None: self.painter.drawLine(self.prev_point, pos) else: self.painter.drawPoint(pos) self.prev_point = pos self._seglayer.setPixmap(QPixmap.fromImage(self.seg_image)) def set_foreground(self): self.erase = False self.segmenter_pen.setColor(QtCore.Qt.green) def set_possible_foreground(self): self.erase = False self.segmenter_pen.setColor(QtCore.Qt.blue) def set_possible_background(self): self.erase = True self.segmenter_pen.setColor(QtCore.Qt.transparent) def set_background(self): self.erase = False self.segmenter_pen.setColor(QtCore.Qt.red) def set_pen_size(self, size): self.segmenter_pen.setWidth(size) def set_opacity(self, value): self._seglayer.setOpacity(value / 100) def hide_image(self): if self.image_hidden: self._photo.setOpacity(1) self.image_hidden = False else: self._photo.setOpacity(0) self.image_hidden = True
class Style: """A collection of sizes and fonts, as well as a default palette that is used to draw the items in the scheme.""" class SizeMetric(Enum): """Various available size metrics. A size metric is a style dependent size represented by a single value. This enum contains a set of "keys", that can be used as arguments in `Style.inchMetric()` or `Style.pixelMetric()` to get the corresponding values. """ NodeFrameCornerRadius = 0 """Rounded rectangle corner radius.""" NodeFrameTextPadding = auto() """Padding for text inside the node body.""" NodeDividerTextMargin = auto() """Margin between text and the title/description divider.""" NodeMessagePadding = auto() """Padding between messages below the node.""" NodeConnectionPadding = auto() """Padding between connections.""" NodeFrameWidth = auto() """Width of the rounded rectangle frame.""" NodeWidth = auto() """Body width.""" EdgeWidth = 20 """Width of the curved line. Also width of the connection stem.""" EdgeBezierPointOffset = auto() """Max horizontal offset of control points on a bezier curve compared to its endpoints.""" EdgeBezierCloseDistance = auto() """Distance that's considered "too close" by the bezier curve calculation algorithm. If distance between source and target is less that this variable, control points are moved closer together. """ EdgeDragPrecisionRadius = auto() """When starting drawing an edge, mouse pointer should be inside a circle, centered on the tip of a connection, and this radius.""" ConnectionStemLength = 40 """Length of the line going out of the node body.""" ConnectionTextLength = auto() """Horizontal length of the box in which the text is drawn.""" ConnectionStemTextMargin = auto() """Margin between the end of the stem and the node's description.""" MessageIconSize = 60 """Size of the icon (width and height). The icon is expected to be square.""" MessageTextLength = auto() """Horizontal length of the box in which the text is drawn.""" MessageIconTextMargin = auto() """Margin between the icon and the message's text.""" PasteOffset = 80 """Amount by which the selection should be shifted when pasted from clipboard.""" class Font(Enum): """Various fonts used in the scheme items. This enum contains a set of "keys", that can be used as arguments in `Style.font()` to get the corresponding values. """ Default = 0 """Default font for non-specific purposes.""" NodeTitle = auto() """Font used in the node's title.""" NodeDescription = auto() """Font used in the node's description.""" ConnectionText = auto() """Font used in the connection's description.""" MessageText = auto() """Font used in the message's text.""" def __init__(self): """Constructs a Style.""" SM = self.SizeMetric FT = self.Font self._inch_metric = { SM.NodeFrameCornerRadius: 0.1, SM.NodeFrameTextPadding: 0.1, SM.NodeDividerTextMargin: 0.05, SM.NodeMessagePadding: 0.05, SM.NodeConnectionPadding: 0.15, SM.NodeFrameWidth: 0.02, SM.NodeWidth: 2, SM.EdgeWidth: 0.02, SM.EdgeBezierPointOffset: 0.8, SM.EdgeBezierCloseDistance: 1.5, SM.EdgeDragPrecisionRadius: 0.1, SM.ConnectionStemLength: 0.2, SM.ConnectionTextLength: 1.5, SM.ConnectionStemTextMargin: 0.05, SM.MessageIconSize: 0.16, SM.MessageTextLength: 2.5, SM.MessageIconTextMargin: 0.05, SM.PasteOffset: 0.5 } default_font = QFont("Segoe UI") default_font.setPointSizeF(10.5) title_font = QFont("Segoe UI Semibold") title_font.setPointSizeF(10.5) self._font = { FT.Default: default_font, FT.NodeTitle: title_font, FT.NodeDescription: QFont(default_font), FT.ConnectionText: QFont(default_font), FT.MessageText: QFont(default_font) } self._frame_pen = QPen() self._frame_pen.setCapStyle(Qt.PenCapStyle.FlatCap) self._frame_pen.setWidthF(self.pixelMetric(SM.NodeFrameWidth)) self._edge_pen = QPen() self._edge_pen.setCapStyle(Qt.PenCapStyle.FlatCap) self._edge_pen.setWidthF(self.pixelMetric(SM.EdgeWidth)) def framePen(self, palette: Palette) -> QPen: result = QPen(self._frame_pen) result.setColor(palette.color(Palette.Frame)) return result def edgePen(self, palette: Palette) -> QPen: result = QPen(self._edge_pen) result.setColor(palette.color(Palette.Edge)) return result def inchMetric(self, metric: SizeMetric, /): return self._inch_metric[metric] def pixelMetric(self, metric: SizeMetric, /): return px(self._inch_metric[metric]) def font(self, which: Font, /): return self._font[which]