def mouseMoveEvent(self, event: QGraphicsSceneMouseEvent): box = QRectF(self.buttonDownRect) pos = event.pos() offset = pos - event.buttonDownPos(Qt.LeftButton) if self.handleSelected is None: box.translate(offset) new_box = box elif self.handleSelected == 0: pos = box.topLeft() + offset new_size = box.bottomRight() - pos width = max(new_size.x(), 0) height = max(new_size.y(), 0) left = min(pos.x(), box.right()) top = min(pos.y(), box.bottom()) new_box = QRectF(left, top, width, height) elif self.handleSelected == 1: pos = box.topLeft() + offset height = max(box.bottom() - pos.y(), 0) top = min(pos.y(), box.bottom()) new_box = QRectF(box.left(), top, box.width(), height) elif self.handleSelected == 2: pos = box.topRight() + offset top = min(pos.y(), box.bottom()) width = max(pos.x() - box.left(), 0) height = max(box.bottom() - pos.y(), 0) new_box = QRectF(box.left(), top, width, height) elif self.handleSelected == 3: pos = box.topRight() + offset width = max(pos.x() - box.left(), 0) new_box = QRectF(box.left(), box.top(), width, box.height()) elif self.handleSelected == 4: pos = box.bottomRight() + offset new_size = pos - box.topLeft() width = max(new_size.x(), 0) height = max(new_size.y(), 0) new_box = QRectF(box.left(), box.top(), width, height) elif self.handleSelected == 5: pos = box.bottomRight() + offset height = max(pos.y() - box.top(), 0) new_box = QRectF(box.left(), box.top(), box.width(), height) elif self.handleSelected == 6: pos = box.bottomLeft() + offset left = min(pos.x(), box.right()) width = max(box.right() - pos.x(), 0) height = max(pos.y() - box.top(), 0) new_box = QRectF(left, box.top(), width, height) elif self.handleSelected == 7: pos = box.bottomLeft() + offset left = min(pos.x(), box.right()) width = max(box.right() - pos.x(), 0) new_box = QRectF(left, box.top(), width, box.height()) new_box = QRectF(round(new_box.left()), round(new_box.top()), round(new_box.width()), round(new_box.height())) self.setRect(new_box) self.setHandlesPos() self.signalHandler.boxChanged.emit(self.tabIndex, self.rowIndex, new_box)
def get_points_rect(self): rect = QRectF() left = 1 right = -1 up = 1 down = -1 for p in self.points: if p.x() < left: left = p.x() if p.x() > right: right = p.x() if p.y() < up: up = p.y() if p.y() > down: down = p.y() rect.setLeft(left) rect.setRight(right) rect.setTop(up) rect.setBottom(down) self.width = rect.width() self.height = rect.height() # rect.setLeft(-self.width/2) # rect.setRight(self.width/2) # rect.setTop(-self.height/2) # rect.setBottom(self.height/2) return rect
def __init__( self, rect: QtCore.QRectF, px: float, py: float, trim_enabled: bool = False, parent: QtWidgets.QGraphicsItem = None, ): super().__init__(rect, parent=parent) pen = QtGui.QPen(QtCore.Qt.white, 2.0) pen.setCosmetic(True) self.setPen(pen) self.px = px self.py = py self.trim_enabled = trim_enabled self.top = rect.y() + rect.height() * 0.33 self.top = self.top - self.top % py self.bottom = rect.y() + rect.height() * 0.66 self.bottom = self.bottom - self.bottom % py self.changed = False
def get_points_rect(self): if len(self.points) == 0: return QRectF(0, 0, 0, 0) x_coords = [p.x() for p in self.points] y_coords = [p.y() for p in self.points] left = min(x_coords) right = max(x_coords) up = min(y_coords) down = max(y_coords) rect = QRectF(left, up, right - left, down - up) self.width = rect.width() self.height = rect.height() return rect
def show_all(self): # trick: # if user zoom in or zoom out too much view exceeds matrix limit # so need to set view's matrix to initial matrix self.setMatrix(self.initial_matrix) items = self.get_visible_items() rect = QRectF() for i in items: rect = rect.united(i.boundingRect()) # it is not working properly without updating the rect # need to set new position and edit rect's size rect.setX(rect.x() - 1) rect.setY(rect.y() - 1) rect.setWidth(rect.width() + 1) rect.setHeight(rect.height() + 1) self.fitInView(rect, Qt.KeepAspectRatio)
def draw_monitor_label(self, painter: QPainter, rect: QRectF, txt: str): painter.save() font = self.monitor_label_font font_metrics = QFontMetrics(font, painter.device()) bounding_rect = font_metrics.boundingRect(rect.toRect(), 0, txt) x_factor = rect.width() / bounding_rect.width() y_factor = rect.height() / bounding_rect.height() factor = min(x_factor, y_factor) font.setPointSizeF(font.pointSizeF() * factor) painter.setFont(font) painter.setPen(self.monitor_label_font_color) painter.drawText(rect, Qt.AlignCenter, txt) painter.restore()
def on_target_viewport_visible_scene_rect_changed(self, visible: QRectF): scene = self._target_view.scene() if scene is None: return x = (visible.x() - scene.sceneRect().x()) * self._scale y = (visible.y() - scene.sceneRect().y()) * self._scale width = visible.width() * self._scale height = visible.height() * self._scale mm_scene_w = self.sceneRect().width() mm_scene_h = self.sceneRect().height() minimap_vp_rect = QRectF() minimap_vp_rect.setTopLeft( QPoint(int(clamp(x, 0, mm_scene_w)), int(clamp(y, 0, mm_scene_h)))) minimap_vp_rect.setBottomRight( QPoint(int(clamp(x + width, 0, mm_scene_w)), int(clamp(y + height, 0, mm_scene_h)))) self._minimap_target_viewport_box.set_viewport_rect(minimap_vp_rect) self._minimap_target_viewport_box.update()
def reload_target_scene(self): """ Reload scene from target view, scaling the minimap view to properly fit the scene into view while preserving scene aspect ratio. """ scene = self._target_view.scene() if scene is None: return # Scale target scene dimensions to fit within widget bounds, preserving scene aspect ratio mm_max_w = self.maximumWidth() mm_max_h = self.maximumHeight() scene_rect = scene.sceneRect() scene_w = scene_rect.width() scene_h = scene_rect.height() if mm_max_w == 0 or mm_max_h == 0 or scene_w == 0 or scene_h == 0: return minimap_aspect_ratio = mm_max_w / mm_max_h scene_aspect_ratio = scene_w / scene_h if minimap_aspect_ratio < scene_aspect_ratio: self._scale = mm_max_w / scene_w else: self._scale = mm_max_h / scene_h scaled_scene_rect = QRectF(0, 0, int(scene_w * self._scale), int(scene_h * self._scale)) self.resize(scaled_scene_rect.width(), scaled_scene_rect.height()) self._minimap_scene.setSceneRect(scaled_scene_rect) self.setSceneRect(scaled_scene_rect) self._minimap_target_scene_viewer.set_scene_rect(scaled_scene_rect) self._minimap_target_viewport_box.set_scene_rect(scaled_scene_rect) self._minimap_target_scene_viewer.update_scene_drawing() self.on_target_viewport_visible_scene_rect_changed( self._target_view.visible_scene_rect)
def drawCropTool(self, img): """ Draws the 8 crop buttons around the displayed image, with their current margins. @param img: @type img: QImage """ r = self.parent().img.resize_coeff(self.parent()) self.formFactor = img.height() / img.width() left = self.btnDict['left'] top = self.btnDict['top'] bottom = self.btnDict['bottom'] right = self.btnDict['right'] # cRect = QRect(round(left.margin), round(top.margin), img.width() - round(right.margin + left.margin), # img.height() - round(bottom.margin + top.margin)) cRect = QRectF(left.margin, top.margin, img.width() - right.margin - left.margin, img.height() - bottom.margin - top.margin) p = cRect.topLeft() * r + QPoint(img.xOffset, img.yOffset) x, y = p.x(), p.y() w, h = cRect.width() * r, cRect.height() * r left.move(x - left.width(), y + h // 2) right.move(x + w, y + h // 2) top.move(x + w // 2, y - top.height()) bottom.move(x + w // 2, y + h) topLeft = self.btnDict['topLeft'] topLeft.move(x - topLeft.width(), y - topLeft.height()) topRight = self.btnDict['topRight'] topRight.move(x + w, y - topRight.height()) bottomLeft = self.btnDict['bottomLeft'] bottomLeft.move(x - bottomLeft.width(), y + h) bottomRight = self.btnDict['bottomRight'] bottomRight.move(x + w, y + h) self.crWidth, self.crHeight = img.width() - int(img.cropLeft) - int( img.cropRight), img.height() - int(img.cropTop) - int( img.cropBottom)
class ColorTransferFunctionViewer(DataViewer): def __init__(self, data: ColorTransferFunction = None): super().__init__(data) self.chart = ColorTransferFunctionChart(data) # self.chart.legend().hide() self.chart_rect_f = QRectF() self.axis_x = QtCharts.QValueAxis() # self.axis_x.setLabelFormat('%d') self.axis_x.setLabelFormat('%.1f') self.axis_x.setTitleText('Intensity') self.chart.addAxis(self.axis_x, Qt.AlignBottom) self.axis_y = QtCharts.QValueAxis() # self.axis_y.setTickCount(10) self.axis_y.setLabelFormat('%.2f') # self.axis_y.setTitleText('Magnitude') self.chart.addAxis(self.axis_y, Qt.AlignLeft) self.axis_x.setRange(0, self.data.points[-1].x) self.axis_y.setRange(0, 1) # Add an empty series, else |chart.mapToPosition| will no work self.series = self.add_series() self.chart_view = QtCharts.QChartView(self.chart) # self.chart_view.setRubberBand(QtCharts.QChartView.RectangleRubberBand) self.chart_view.setRenderHint(QPainter.Antialiasing) self.scene = self.chart_view.scene() self._interval_views = SortedList() self._point_views = [] if self.data is not None: self._add_interval_views() self._add_point_views() self.data.point_added.connect(self._on_point_added) grid_layout = QGridLayout() grid_layout.setContentsMargins(0, 0, 0, 0) grid_layout.addWidget(self.chart_view) self.setLayout(grid_layout) def _add_interval_views(self): for point, next_point in pairwise(self.data.points): if next_point is not None: self._add_interval_view(point, next_point) def _add_interval_view(self, begin_point: ColorTransferFunctionPoint, end_point: ColorTransferFunctionPoint) \ -> ColorTransferFunctionIntervalView: interval_view = ColorTransferFunctionIntervalView( self, begin_point, end_point, self.chart) interval_view.setZValue(10) # To display item on top of chart grid self.scene.addItem(interval_view) self._interval_views.add(interval_view) return interval_view def _add_point_views(self): for point in self.data.points: self._add_point_view(point) def _add_point_view( self, point: ColorTransferFunctionPoint ) -> ColorTransferFunctionPointView: point_view = ColorTransferFunctionPointView(self, point, self.chart) point_view.setZValue( 11) # To display item on top of chart grid and interval views self.scene.addItem(point_view) self._point_views.append(point_view) return point_view def _update_chart_size(self): top_left_pos = self.chart.mapToPosition( QPointF(self.axis_x.min(), self.axis_y.max())) bottom_right_pos = self.chart.mapToPosition( QPointF(self.axis_x.max(), self.axis_y.min())) self.chart_rect_f = QRectF(top_left_pos, bottom_right_pos) def _on_point_added(self, point: ColorTransferFunctionPoint): self._add_point_view(point) # Update existed interval views point_index = self.data.points.index(point) if 0 < point_index < len( self.data.points ) - 1: # Only if point was added between other existed points # Exclude cases, where point was added before first and after last points before_point_index = point_index - 1 before_point_interval = self._interval_views[before_point_index] before_point_interval.end_point = point before_point_interval.update() # Add new interval view if point_index == len( self.data.points ) - 1: # If was added last point (point after last interval) new_interval_view = self._add_interval_view( self.data.point_before(point), point) new_interval_view.update() else: new_interval_view = self._add_interval_view( point, self.data.point_after(point)) new_interval_view.update() def resizeEvent(self, resize_event: QResizeEvent): self._update_chart_size() # min_tick_count = self.axis_x.max() - self.axis_x.min() + 1 # tick_count = min(min_tick_count, self.width() / 50) tick_count = self.chart_rect_f.width() / 50 self.axis_x.setTickCount(round(tick_count)) self.axis_y.setTickCount(round(self.chart_rect_f.height() / 20)) for interval_view in self._interval_views: interval_view.update() for point_view in self._point_views: point_view.update_size() point_view.update_pos() def add_series(self): series = QtCharts.QLineSeries() series.setName('Color Transfer Function') self.chart.addSeries(series) series.attachAxis(self.axis_x) series.attachAxis(self.axis_y) return series
class Callout(QGraphicsItem): """ This class code was taken from \ https://code.qt.io/cgit/qt/qtcharts.git/tree/examples/charts/callout/callout.cpp?h=5.13 """ def __init__(self, chart): QGraphicsItem.__init__(self, chart) self.chart = chart self.rect = QRectF() self.anchor = QPointF() self.text_rect = QRectF() self.text = "" self.font = QFont() def boundingRect(self): anchor = self.mapFromParent(self.chart.mapToPosition(self.anchor)) rect = QRectF() rect.setLeft(min(self.rect.left(), anchor.x())) rect.setRight(max(self.rect.right(), anchor.x())) rect.setTop(min(self.rect.top(), anchor.y())) rect.setBottom(max(self.rect.bottom(), anchor.y())) return rect def paint(self, painter, option, widget): path = QPainterPath() path.addRoundedRect(self.rect, 5, 5) anchor = self.mapFromParent(self.chart.mapToPosition(self.anchor)) if not self.rect.contains(anchor): point1 = QPointF() point2 = QPointF() # establish the position of the anchor point in relation to m_rect above = anchor.y() <= self.rect.top() aboveCenter = anchor.y() > self.rect.top() and anchor.y( ) <= self.rect.center().y() belowCenter = anchor.y() > self.rect.center().y() and anchor.y( ) <= self.rect.bottom() below = anchor.y() > self.rect.bottom() onLeft = anchor.x() <= self.rect.left() leftOfCenter = anchor.x() > self.rect.left() and anchor.x( ) <= self.rect.center().x() rightOfCenter = anchor.x() > self.rect.center().x() and anchor.x( ) <= self.rect.right() onRight = anchor.x() > self.rect.right() # get the nearest m_rect corner x = (onRight + rightOfCenter) * self.rect.width() y = (below + belowCenter) * self.rect.height() cornerCase = (above and onLeft) or (above and onRight) or ( below and onLeft) or (below and onRight) vertical = qAbs(anchor.x() - x) > qAbs(anchor.y() - y) x1 = x + leftOfCenter * 10 - rightOfCenter * 20 + cornerCase * ( not vertical) * (onLeft * 10 - onRight * 20) y1 = y + aboveCenter * 10 - belowCenter * 20 + cornerCase * vertical * ( above * 10 - below * 20) x2 = x + leftOfCenter * 20 - rightOfCenter * 10 + cornerCase * ( not vertical) * (onLeft * 20 - onRight * 10) y2 = y + aboveCenter * 20 - belowCenter * 10 + cornerCase * vertical * ( above * 20 - below * 10) point1.setX(x1) point1.setY(y1) point2.setX(x2) point2.setY(y2) path.moveTo(point1) path.lineTo(anchor) path.lineTo(point2) path = path.simplified() painter.setBrush(QColor(255, 255, 255)) painter.drawPath(path) painter.drawText(self.text_rect, self.text) def set_anchor(self, point): self.anchor = point def updateGeometry(self): self.prepareGeometryChange() self.setPos(self.chart.mapToPosition(self.anchor) + QPoint(10, -50)) def set_text(self, text): self.text = text metrics = QFontMetrics(self.font) self.text_rect = metrics.boundingRect(QRect(0, 0, 150, 150), Qt.AlignLeft, self.text) self.text_rect.translate(5, 5) self.prepareGeometryChange() self.rect = QRectF(self.text_rect.adjusted(-5.0, -5.0, 5.0, 5.0))
class Callout(QGraphicsItem): def __init__(self, chart): QGraphicsItem.__init__(self, chart) self._chart = chart self._text = "" self._textRect = QRectF() self._anchor = QPointF() self._font = QFont() self._rect = QRectF() def boundingRect(self): anchor = self.mapFromParent(self._chart.mapToPosition(self._anchor)) rect = QRectF() rect.setLeft(min(self._rect.left(), anchor.x())) rect.setRight(max(self._rect.right(), anchor.x())) rect.setTop(min(self._rect.top(), anchor.y())) rect.setBottom(max(self._rect.bottom(), anchor.y())) return rect def paint(self, painter, option, widget): path = QPainterPath() path.addRoundedRect(self._rect, 5, 5) anchor = self.mapFromParent(self._chart.mapToPosition(self._anchor)) if not self._rect.contains(anchor) and not self._anchor.isNull(): point1 = QPointF() point2 = QPointF() # establish the position of the anchor point in relation to _rect above = anchor.y() <= self._rect.top() aboveCenter = (anchor.y() > self._rect.top() and anchor.y() <= self._rect.center().y()) belowCenter = (anchor.y() > self._rect.center().y() and anchor.y() <= self._rect.bottom()) below = anchor.y() > self._rect.bottom() onLeft = anchor.x() <= self._rect.left() leftOfCenter = (anchor.x() > self._rect.left() and anchor.x() <= self._rect.center().x()) rightOfCenter = (anchor.x() > self._rect.center().x() and anchor.x() <= self._rect.right()) onRight = anchor.x() > self._rect.right() # get the nearest _rect corner. x = (onRight + rightOfCenter) * self._rect.width() y = (below + belowCenter) * self._rect.height() cornerCase = ((above and onLeft) or (above and onRight) or (below and onLeft) or (below and onRight)) vertical = abs(anchor.x() - x) > abs(anchor.y() - y) x1 = (x + leftOfCenter * 10 - rightOfCenter * 20 + cornerCase * int(not vertical) * (onLeft * 10 - onRight * 20)) y1 = (y + aboveCenter * 10 - belowCenter * 20 + cornerCase * vertical * (above * 10 - below * 20)) point1.setX(x1) point1.setY(y1) x2 = (x + leftOfCenter * 20 - rightOfCenter * 10 + cornerCase * int(not vertical) * (onLeft * 20 - onRight * 10)) y2 = (y + aboveCenter * 20 - belowCenter * 10 + cornerCase * vertical * (above * 20 - below * 10)) point2.setX(x2) point2.setY(y2) path.moveTo(point1) path.lineTo(anchor) path.lineTo(point2) path = path.simplified() painter.setBrush(QColor(255, 255, 255)) painter.drawPath(path) painter.drawText(self._textRect, self._text) def mousePressEvent(self, event): event.setAccepted(True) def mouseMoveEvent(self, event): if event.buttons() & Qt.LeftButton: self.setPos( mapToParent(event.pos() - event.buttonDownPos(Qt.LeftButton))) event.setAccepted(True) else: event.setAccepted(False) def setText(self, text): self._text = text metrics = QFontMetrics(self._font) self._textRect = QRectF( metrics.boundingRect(QRect(0.0, 0.0, 150.0, 150.0), Qt.AlignLeft, self._text)) self._textRect.translate(5, 5) self.prepareGeometryChange() self._rect = self._textRect.adjusted(-5, -5, 5, 5) def setAnchor(self, point): self._anchor = QPointF(point) def updateGeometry(self): self.prepareGeometryChange() self.setPos(self._chart.mapToPosition(self._anchor) + QPointF(10, -50))
class Room: def __init__(self, type='genericRoom', p=QPointF(), w=-1, h=-1): self.type = type # corridor, bedroom, kitchen, bathroom, etc self.width = w self.height = h self.initial_point = p self.room_qrect = QRectF() self.room_qpolygon = QPolygonF() self.area = -1 self.side = None self.door_loc = -1 # 0 entre topL y topR, 1 entre topR y bottomR, 2 entre bottomR y bottomL y 3 entr bL y tL self.create_room() def create_room(self): print( f'Creating room of type {self.type} with width = {self.width} and height = {self.height}' ) self.room_qrect = QRectF(self.initial_point.x(), self.initial_point.y(), self.width, self.height) self.room_qpolygon = QPolygonF(self.room_qrect) self.area = abs(self.width * self.height) def update_room_dimensions(self): self.width = self.room_qrect.width() self.height = self.room_qrect.height() self.area = abs(self.width * self.height) self.room_qpolygon = QPolygonF(self.room_qrect) def add_door(self, door_location, room_side): self.side = room_side dict_location_line = { 'center': QLineF(self.room_qrect.topLeft(), self.room_qrect.topRight()), 'left': QLineF(self.room_qrect.topLeft(), self.room_qrect.bottomLeft()), 'right': QLineF(self.room_qrect.topRight(), self.room_qrect.bottomRight()) } line = dict_location_line[door_location] line_lenght = int(line.length()) step = line_lenght / 100. line_points = [] for t in np.arange(0.25, 0.75, step): line_point = line.pointAt(t) line_points.append(QPointF(line_point.x(), line_point.y())) random_center_door = random.choice(line_points) door_sides = { 'center': { 'left_door': QPointF(random_center_door.x() - 0.5, random_center_door.y()), 'right_door': QPointF(random_center_door.x() + 0.5, random_center_door.y()) }, 'left': { 'left_door': QPointF(random_center_door.x(), random_center_door.y() - 0.5), 'right_door': QPointF(random_center_door.x(), random_center_door.y() + 0.5) }, 'right': { 'left_door': QPointF(random_center_door.x(), random_center_door.y() - 0.5), 'right_door': QPointF(random_center_door.x(), random_center_door.y() + 0.5) } } if door_location == 'center': self.room_qpolygon = QPolygonF([ door_sides[door_location]['right_door'], self.room_qrect.topRight(), self.room_qrect.bottomRight(), self.room_qrect.bottomLeft(), self.room_qrect.topLeft(), door_sides[door_location]['left_door'] ]) self.door_loc = 0 elif door_location == 'right': if room_side == 'bottom': self.room_qpolygon = QPolygonF([ door_sides[door_location]['right_door'], self.room_qrect.topRight(), self.room_qrect.topLeft(), self.room_qrect.bottomLeft(), self.room_qrect.bottomRight(), door_sides[door_location]['left_door'] ]) elif room_side == 'top': self.room_qpolygon = QPolygonF([ door_sides[door_location]['right_door'], self.room_qrect.bottomRight(), self.room_qrect.bottomLeft(), self.room_qrect.topLeft(), self.room_qrect.topRight(), door_sides[door_location]['left_door'] ]) self.door_loc = 1 elif door_location == 'left': if room_side == 'bottom': self.room_qpolygon = QPolygonF([ door_sides[door_location]['right_door'], self.room_qrect.topLeft(), self.room_qrect.topRight(), self.room_qrect.bottomRight(), self.room_qrect.bottomLeft(), door_sides[door_location]['left_door'] ]) elif room_side == 'top': self.room_qpolygon = QPolygonF([ door_sides[door_location]['right_door'], self.room_qrect.bottomLeft(), self.room_qrect.bottomRight(), self.room_qrect.topRight(), self.room_qrect.topLeft(), door_sides[door_location]['left_door'] ]) self.door_loc = 3
class wheel(QWidget): currentColorChanged = Signal(QColor) def __init__(self, parent=None): super(wheel, self).__init__(parent) self.setFixedSize(256, 256) # start, end angles for value arc self.s_ang, self.e_ang = 135, 225 # offset angle and direction for color wheel self.o_ang, self.rot_d = 45, -1 # 1 for clock-wise, -1 for widdershins # other initializations self.pos = QPointF(-100, -100) self.vIdCen = QPointF(-100, -100) self.vIdAng = radians(self.s_ang) self.chPt = self.pos self.hue = self.sat = self.value = 255 self.setup() self.pos = self.cWhBox.center() self._namedColorList = [] self._namedColorPts = [] self._showNames = False self.setMouseTracking(True) self.installEventFilter(self) self._startedTimer = False ## def timerSpinner(self): ## "won't this be fun" ## self.o_ang -= 1; self.o_ang %= 360 ## stable = False ## ## colWhl = QConicalGradient(self.cen, self.o_ang) ## whl_cols = [Qt.red, Qt.magenta, ## Qt.blue, Qt.cyan, Qt.green, ## Qt.yellow, Qt.red] ## for i, c in enumerate(whl_cols[::self.rot_d]): ## colWhl.setColorAt(i / 6.0, c) ## ## if stable: # crosshairs stay on color ## t = radians(self.hue + self.o_ang * -self.rot_d) * -self.rot_d ## r = self.sat / 255.0 * self.cW_rad ## x, y = r * cos(t) + self.cen.x(), r * -sin(t) + self.cen.y() ## self.chPt = QPointF(x, y) ## else: # crosshairs stay on point ## t = atan2(self.cen.y() - self.pos.y(), self.pos.x() - self.cen.x()) ## h = (int(degrees(t)) - self.o_ang) * -self.rot_d ## self.hue = (h if h > 0 else h + 360) % 360 ## col = QColor(); col.setHsv(self.hue, self.sat, self.value) ## self.currentColorChanged.emit(col) ## ## self.cWhlBrush1 = QBrush(colWhl) ## self.update() def resizeEvent(self, event): self.setup() # re-construct the sizes self.setNamedColors(self._namedColorList) def getColor(self): col = QColor() col.setHsv(self.hue, self.sat, self.value) return col def setNamedColors(self, colorList): "sets list [(name, #html)] of named colors" self._namedColorList = colorList lst = [] r2 = (self.vAoBox.width() + self.vAiBox.width()) / 4.0 for i in self._namedColorList: h, s, v, a = QColor(i[1]).getHsv() t = radians(h + self.o_ang * -self.rot_d) * -self.rot_d r = s / 255.0 * self.cW_rad x, y = r * cos(t) + self.cen.x(), r * -sin(t) + self.cen.y() lst.append(QPointF(x, y)) #t2 = ((v / 255.0) * self.ang_w + radians(self.e_ang) + 2 * pi) % (2 * pi) #x, y = r2 * cos(t2) + self.cen.x(), r2 * -sin(t2) + self.cen.y() #lst.append(QPointF(x, y)) self._namedColorPts = lst def showNamedColors(self, flag=False): "show/hide location of named colors on color wheel" self._showNames = flag self.update() def setColor(self, color): # saturation -> radius h, s, v, a = color.getHsv() # hue -> angle self.hue, self.sat, self.value = h, s, v # value -> side bar thingy t = radians(h + self.o_ang * -self.rot_d) * -self.rot_d r = s / 255.0 * self.cW_rad x, y = r * cos(t) + self.cen.x(), r * -sin(t) + self.cen.y() self.chPt = QPointF(x, y) # hue, saturation self.vIdAng = t2 = (v / 255.0) * self.ang_w + radians(self.e_ang) self.vIdAng = t2 = t2 if t2 > 0 else t2 + 2 * pi r2 = self.vAoBox.width() / 2.0 x, y = r2 * cos(t2) + self.cen.x(), r2 * -sin(t2) + self.cen.y() self.vIdCen, self.vIdAng = QPointF(x, y), t2 # value self.vIdBox.moveCenter(self.vIdCen) self.update() def eventFilter(self, source, event): if (event.type() == QEvent.MouseButtonPress or (event.type() == QEvent.MouseMove and event.buttons() == Qt.LeftButton)): self.pos = pos = event.pos() t = atan2(self.cen.y() - pos.y(), pos.x() - self.cen.x()) if self.colWhlPath.contains(pos): # in the color wheel self.chPt = pos #if not self._startedTimer: # self.timer = QTimer() # self.timer.timeout.connect(self.timerSpinner) # self.timer.start(30.303) # self._startedTimer = True # hue -> mouse angle (same as t here) h = (int(degrees(t)) - self.o_ang) * -self.rot_d self.hue = (h if h > 0 else h + 360) % 360 # saturation -> mouse radius (clipped to wheel radius) m_rad = sqrt((self.pos.x() - self.cen.x())**2 + (self.pos.y() - self.cen.y())**2) self.sat = int(255 * min(m_rad / self.cW_rad, 1)) if self.vInArcPath.contains(pos): # in the value selection arc self.vIdAng = t if t > 0 else t + 2 * pi r2 = self.vAoBox.width() / 2.0 x, y = r2 * cos(t) + self.cen.x(), r2 * -sin(t) + self.cen.y() self.vIdCen = QPointF(x, y) self.vIdBox.moveCenter(self.vIdCen) self.value = int(255 * (t - radians(self.e_ang)) / self.ang_w) % 256 self.update() col = QColor() col.setHsv(self.hue, self.sat, self.value) self.currentColorChanged.emit(col) return QWidget.eventFilter(self, source, event) def paintEvent(self, event): painter = QPainter(self) painter.setRenderHint(painter.Antialiasing) #painter.setBrush(QBrush(Qt.black, Qt.NoBrush)) #painter.drawRect(self.winBox) # border # value selector indicator painter.setBrush(QBrush(Qt.black, Qt.SolidPattern)) painter.drawPie(self.vIdBox, 16 * (degrees(self.vIdAng) - 22.5), 720) # value selector arc painter.setClipPath(self.vArcPath) painter.setPen(Qt.NoPen) arc = QConicalGradient(self.cen, self.e_ang) color = QColor() color.setHsv(self.hue, self.sat, 255) arc.setColorAt(1 - (self.e_ang - self.s_ang) / 360.0, color) arc.setColorAt(1, Qt.black) arc.setColorAt(0, Qt.black) painter.setBrush(arc) painter.drawPath(self.vArcPath) painter.setClipPath(self.vArcPath, Qt.NoClip) # color wheel painter.setPen(Qt.NoPen) painter.setBrush(self.cWhlBrush1) painter.drawEllipse(self.cWhBox) painter.setBrush(self.cWhlBrush2) painter.drawEllipse(self.cWhBox) # crosshairs painter.setClipPath(self.colWhlPath) painter.setBrush(QBrush(Qt.black, Qt.SolidPattern)) chVert = QRectF(0, 0, 2, 20) chHort = QRectF(0, 0, 20, 2) chVert.moveCenter(self.chPt) chHort.moveCenter(self.chPt) painter.drawRect(chVert) painter.drawRect(chHort) # named color locations if self._showNames: painter.setClipPath(self.vArcPath, Qt.NoClip) painter.setPen(Qt.SolidLine) try: painter.drawPoints(*self._namedColorPts) # PyQt except: painter.drawPoints(self._namedColorPts) # PySide def setup(self): "sets bounds on value arc and color wheel" # bounding boxes self.winBox = QRectF(self.rect()) self.vIoBox = QRectF() # value indicator arc outer self.vIdBox = QRectF() # value indicator box self.vAoBox = QRectF() # value arc outer self.vAiBox = QRectF() # value arc inner self.cWhBox = QRectF() # color wheel self.vIdBox.setSize(QSizeF(15, 15)) self.vIoBox.setSize(self.winBox.size()) self.vAoBox.setSize(self.winBox.size() - self.vIdBox.size() / 2.0) self.vAiBox.setSize(self.vAoBox.size() - QSizeF(20, 20)) self.cWhBox.setSize(self.vAiBox.size() - QSizeF(20, 20)) # center - shifted to the right slightly x = self.winBox.width() - (self.vIdBox.width() + self.vAiBox.width()) / 2.0 self.cen = QPointF(x, self.winBox.height() / 2.0) # positions and initial settings self.vAoBox.moveCenter(self.cen) self.vAiBox.moveCenter(self.cen) self.cWhBox.moveCenter(self.cen) self.vIdBox.moveCenter(self.vIdCen) self.cW_rad = self.cWhBox.width() / 2.0 self.ang_w = radians(self.s_ang) - radians(self.e_ang) # gradients colWhl = QConicalGradient(self.cen, self.o_ang) whl_cols = [ Qt.red, Qt.magenta, Qt.blue, Qt.cyan, Qt.green, Qt.yellow, Qt.red ] for i, c in enumerate(whl_cols[::self.rot_d]): colWhl.setColorAt(i / 6.0, c) rad = min(self.cWhBox.width() / 2.0, self.cWhBox.height() / 2.0) cWhlFade = QRadialGradient(self.cen, rad, self.cen) cWhlFade.setColorAt(0, Qt.white) cWhlFade.setColorAt(1, QColor(255, 255, 255, 0)) self.cWhlBrush1 = QBrush(colWhl) self.cWhlBrush2 = QBrush(cWhlFade) # painter paths (arcs, wheel) rad = self.vAoBox.width() / 2.0 x, y = rad * cos(radians(self.s_ang)), -rad * sin(radians(self.s_ang)) x += self.cen.x() y += self.cen.y() self.vArcPath = QPainterPath(QPointF(x, y)) # value arc (for color) self.vArcPath.arcTo(self.vAoBox, self.s_ang, self.e_ang - self.s_ang) self.vArcPath.arcTo(self.vAiBox, self.e_ang, self.s_ang - self.e_ang) self.vArcPath.closeSubpath() self.vInArcPath = QPainterPath(QPointF(x, y)) # value arc (for mouse) self.vInArcPath.arcTo(self.vIoBox, self.s_ang, self.e_ang - self.s_ang) self.vInArcPath.arcTo(self.vAiBox, self.e_ang, self.s_ang - self.e_ang) self.vInArcPath.closeSubpath() self.colWhlPath = QPainterPath() self.colWhlPath.addEllipse(self.cWhBox)
def redraw(self): self.graphics_scene.clear() # draw screenshot self.graphics_scene.addPixmap(self.screenPixel) # prepare for drawing selected area rect = QRectF(self.selected_area) rect = rect.normalized() top_left_point = rect.topLeft() top_right_point = rect.topRight() bottom_left_point = rect.bottomLeft() bottom_right_point = rect.bottomRight() top_middle_point = (top_left_point + top_right_point) / 2 left_middle_point = (top_left_point + bottom_left_point) / 2 bottom_middle_point = (bottom_left_point + bottom_right_point) / 2 right_middle_point = (top_right_point + bottom_right_point) / 2 # draw the picture mask mask = QColor(0, 0, 0, 155) if self.selected_area == QRect(): self.graphics_scene.addRect(0, 0, self.screenPixel.width(), self.screenPixel.height(), QPen(Qt.NoPen), mask) else: self.graphics_scene.addRect(0, 0, self.screenPixel.width(), top_right_point.y(), QPen(Qt.NoPen), mask) self.graphics_scene.addRect(0, top_left_point.y(), top_left_point.x(), rect.height(), QPen(Qt.NoPen), mask) self.graphics_scene.addRect( top_right_point.x(), top_right_point.y(), self.screenPixel.width() - top_right_point.x(), rect.height(), QPen(Qt.NoPen), mask) self.graphics_scene.addRect( 0, bottom_left_point.y(), self.screenPixel.width(), self.screenPixel.height() - bottom_left_point.y(), QPen(Qt.NoPen), mask) # draw the toolBar if self.action != ACTION_SELECT: spacing = 5 # show the toolbar first, then move it to the correct position # because the width of it may be wrong if this is the first time it shows self.tooBar.show() dest = QPointF(rect.bottomRight() - QPointF(self.tooBar.width(), 0) - QPointF(spacing, -spacing)) if dest.x() < spacing: dest.setX(spacing) pen_set_bar_height = self.penSetBar.height( ) if self.penSetBar is not None else 0 if dest.y() + self.tooBar.height( ) + pen_set_bar_height >= self.height(): if rect.top() - self.tooBar.height( ) - pen_set_bar_height < spacing: dest.setY(rect.top() + spacing) else: dest.setY(rect.top() - self.tooBar.height() - pen_set_bar_height - spacing) self.tooBar.move(dest.toPoint()) if self.penSetBar is not None: self.penSetBar.show() self.penSetBar.move(dest.toPoint() + QPoint(0, self.tooBar.height() + spacing)) if self.action == ACTION_TEXT: self.penSetBar.showFontWidget() else: self.penSetBar.showPenWidget() else: self.tooBar.hide() if self.penSetBar is not None: self.penSetBar.hide() # draw the list for step in self.drawListResult: self.drawOneStep(step) if self.drawListProcess is not None: self.drawOneStep(self.drawListProcess) if self.action != ACTION_TEXT: self.drawListProcess = None if self.selected_area != QRect(): self.items_to_remove = [] # draw the selected rectangle pen = QPen(QColor(0, 255, 255), 2) self.items_to_remove.append(self.graphics_scene.addRect(rect, pen)) # draw the drag point radius = QPoint(3, 3) brush = QBrush(QColor(0, 255, 255)) self.items_to_remove.append( self.graphics_scene.addEllipse( QRectF(top_left_point - radius, top_left_point + radius), pen, brush)) self.items_to_remove.append( self.graphics_scene.addEllipse( QRectF(top_middle_point - radius, top_middle_point + radius), pen, brush)) self.items_to_remove.append( self.graphics_scene.addEllipse( QRectF(top_right_point - radius, top_right_point + radius), pen, brush)) self.items_to_remove.append( self.graphics_scene.addEllipse( QRectF(left_middle_point - radius, left_middle_point + radius), pen, brush)) self.items_to_remove.append( self.graphics_scene.addEllipse( QRectF(right_middle_point - radius, right_middle_point + radius), pen, brush)) self.items_to_remove.append( self.graphics_scene.addEllipse( QRectF(bottom_left_point - radius, bottom_left_point + radius), pen, brush)) self.items_to_remove.append( self.graphics_scene.addEllipse( QRectF(bottom_middle_point - radius, bottom_middle_point + radius), pen, brush)) self.items_to_remove.append( self.graphics_scene.addEllipse( QRectF(bottom_right_point - radius, bottom_right_point + radius), pen, brush)) # draw the textedit if self.textPosition is not None: textSpacing = 50 position = QPoint() if self.textPosition.x() + self.textInput.width( ) >= self.screenPixel.width(): position.setX(self.textPosition.x() - self.textInput.width()) else: position.setX(self.textPosition.x()) if self.textRect is not None: if self.textPosition.y() + self.textInput.height( ) + self.textRect.height() >= self.screenPixel.height(): position.setY(self.textPosition.y() - self.textInput.height() - self.textRect.height()) else: position.setY(self.textPosition.y() + self.textRect.height()) else: if self.textPosition.y() + self.textInput.height( ) >= self.screenPixel.height(): position.setY(self.textPosition.y() - self.textInput.height()) else: position.setY(self.textPosition.y()) self.textInput.move(position) self.textInput.show() # self.textInput.getFocus() # draw the magnifier if self.action == ACTION_SELECT: self.drawMagnifier() if self.mousePressed: self.drawSizeInfo() if self.action == ACTION_MOVE_SELECTED: self.drawSizeInfo()
class Apartment: def __init__(self, coppelia_, n_rooms): self.coppelia = coppelia_ self.num_rooms = n_rooms self.max_rooms_per_side = math.ceil(self.num_rooms / 2) self.initial_corridor_width = -1 self.initial_corridor_height = -1 self.initial_corridor = QRectF() self.fixed_height = random.uniform(4, 6) # Almacena los indices de las habitaciones que tendrán a su izquierda un pasillo self.dict_corridors_index_per_side = {'bottom': [], 'top': []} self.dict_rooms_per_side = {'bottom': [], 'top': []} self.dict_rooms_and_corridors_per_side = {'bottom': [], 'top': []} # Lista final una vez hechas todas las transformaciones self.total_rooms_and_corridors = [] self.create_initial_corridor() self.select_side_corridors() self.get_random_rooms() self.adjust_rooms() # to avoid narrow corridors self.add_doors() self.center_apartment() self.add_floor_per_room() self.add_walls() def create_initial_corridor(self): self.initial_corridor_height = random.uniform(1.5, 3) self.initial_corridor_width = random.uniform(self.num_rooms * 4 / 2, self.num_rooms * 8 / 2) self.initial_corridor = QRectF( 0, 0, self.initial_corridor_width, -self.initial_corridor_height ) # - height para que la parte de arriba sea el top self.initial_corridor.translate(-self.initial_corridor.center()) def select_side_corridors(self): # -1 sin pasillo, 0 antes de la primera habitacion, 1 antes de la segunda corridor_position = np.arange(-1, self.max_rooms_per_side) possibles_corridors_per_side = round(self.max_rooms_per_side / 2) if possibles_corridors_per_side == 0: possibles_corridors_per_side = 1 self.dict_corridors_index_per_side['top'] = random.sample( list(corridor_position), k=possibles_corridors_per_side) self.dict_corridors_index_per_side['bottom'] = random.sample( list(corridor_position), k=possibles_corridors_per_side) while -1 in self.dict_corridors_index_per_side['top']: self.dict_corridors_index_per_side['top'].remove(-1) while -1 in self.dict_corridors_index_per_side['bottom']: self.dict_corridors_index_per_side['bottom'].remove(-1) print('posicion pasillo parte superior', self.dict_corridors_index_per_side['top']) print('posicion pasillo parte inferior', self.dict_corridors_index_per_side['bottom']) def get_random_rooms(self): dict_opposite_side = {'bottom': 'top', 'top': 'bottom'} for i in range(0, self.num_rooms): random_side = random.choice(['top', 'bottom']) if len(self.dict_rooms_per_side[random_side] ) >= self.max_rooms_per_side: random_side = dict_opposite_side[random_side] # El indice de mi habitacion está en la lista de pasillos por indice luego tengo que añadir un pasillo a # su izquierda if len(self.dict_rooms_per_side[random_side] ) in self.dict_corridors_index_per_side[random_side]: self.add_corridor(random_side, self.initial_corridor_height, self.fixed_height) if len(self.dict_rooms_and_corridors_per_side[random_side]) == 0: if random_side == 'bottom': initial_point = self.initial_corridor.bottomLeft() else: initial_point = self.initial_corridor.topLeft() + QPointF( 0, self.fixed_height) else: initial_point = self.dict_rooms_and_corridors_per_side[ random_side][-1].room_qrect.topRight() room = Room(type='genericRoom', p=initial_point, w=random.uniform(4, 8), h=-self.fixed_height) self.dict_rooms_and_corridors_per_side[random_side].append(room) self.dict_rooms_per_side[random_side].append(room) for room_location in ['top', 'bottom']: if len(self.dict_rooms_per_side[room_location] ) in self.dict_corridors_index_per_side[room_location]: try: if self.dict_rooms_and_corridors_per_side[room_location][ -1].type != 'corridor': self.add_corridor(random_side, self.initial_corridor_height, self.fixed_height) except: print('there isnt rooms in this side of the corridor') def add_corridor(self, side, corridor_width, corridor_height): if len(self.dict_rooms_and_corridors_per_side[side]) == 0: if side == 'bottom': initial_point = self.initial_corridor.bottomLeft() else: initial_point = self.initial_corridor.topLeft() + QPointF( 0, self.fixed_height) else: initial_point = self.dict_rooms_and_corridors_per_side[side][ -1].room_qrect.topRight() corridor = Room(type='corridor', p=initial_point, w=corridor_width, h=-corridor_height) self.dict_rooms_and_corridors_per_side[side].append(corridor) def adjust_rooms(self): if self.num_rooms == 1: return dict_side_width = {'bottom': 0., 'right': 0., 'top': 0., 'left': 0.} for side, rooms in self.dict_rooms_per_side.items(): print(f' side {side} has {len(rooms)} rooms ') for room in rooms: r = room.room_qrect dict_side_width[side] += r.width() diff = abs(dict_side_width['top'] - dict_side_width['bottom']) dict_opposite_side = { 'bottom': 'top', 'right': 'left', 'top': 'bottom', 'left': 'right' } if dict_side_width['top'] > dict_side_width['bottom']: print('top side is longer') side_to_modify = 'bottom' else: print('bottom side is longer') side_to_modify = 'top' print(f'--- Modifying {side_to_modify} room ---') room_to_modify = self.dict_rooms_and_corridors_per_side[ side_to_modify][-1] opposite_room = self.dict_rooms_and_corridors_per_side[ dict_opposite_side[side_to_modify]][-1] my_side_right = room_to_modify.room_qrect.topRight() opposite_side_right = opposite_room.room_qrect.topRight() if room_to_modify.type == 'corridor': print(f' Room of type {room_to_modify.type} ') room_to_modify.room_qrect.setTopRight( QPointF(opposite_side_right.x(), my_side_right.y())) self.dict_rooms_and_corridors_per_side[side_to_modify][ -1] = room_to_modify self.dict_rooms_and_corridors_per_side[side_to_modify][ -1].update_room_dimensions() else: if diff < self.initial_corridor_height: print('widening room') num_corridors_to_add = 0 else: print('widening room and creating corridor') num_corridors_to_add = 1 print( f' Room of type {room_to_modify.type} -- adding {num_corridors_to_add} corridors' ) room_to_modify.room_qrect.setTopRight( QPointF( opposite_side_right.x() - num_corridors_to_add * self.initial_corridor_height, my_side_right.y())) self.dict_rooms_and_corridors_per_side[side_to_modify][ -1] = room_to_modify self.dict_rooms_and_corridors_per_side[side_to_modify][ -1].update_room_dimensions() if num_corridors_to_add > 0: self.add_corridor(side=side_to_modify, corridor_width=num_corridors_to_add * self.initial_corridor_height, corridor_height=self.fixed_height) def add_doors(self): opposite = {'bottom': 'top', 'top': 'bottom'} for current_side, rooms in self.dict_rooms_per_side.items(): for i, room in enumerate(rooms): possibles_door_locations = [opposite[current_side]] if i in self.dict_corridors_index_per_side[ current_side]: # Pasillo a la izquierda possibles_door_locations.append('left') if i + 1 in self.dict_corridors_index_per_side[current_side]: possibles_door_locations.append('right') door_location = random.choice(possibles_door_locations) room.add_door(door_location) def center_apartment(self): union_polygon = QPolygonF() for list in self.dict_rooms_and_corridors_per_side.values(): for room in list: union_polygon = union_polygon.united( room.room_qpolygon) # Para obtener el bounding box self.total_rooms_and_corridors.append(room) self.initial_corridor.setLeft(union_polygon.boundingRect().left()) self.initial_corridor.setRight(union_polygon.boundingRect().right()) self.total_rooms_and_corridors.append( Room(type='corridor', p=self.initial_corridor.topLeft(), w=self.initial_corridor.width(), h=self.initial_corridor.height())) union_polygon = union_polygon.united(self.initial_corridor) initial_center = union_polygon.boundingRect().center() union_polygon.translate(-initial_center) self.apartment_boundingRect = union_polygon.boundingRect() # Desplazo habitaciones y pasillos al centro for i, room in enumerate(self.total_rooms_and_corridors): room.room_qpolygon.translate( -initial_center ) # Desplazo los poligonos para que la habitación esté centrada room.room_qrect.translate(-initial_center) def add_walls(self): for i, room in enumerate(self.total_rooms_and_corridors): walls = [] if room.type == 'corridor': continue polygon = room.room_qpolygon prev_point = polygon[0] for i, curr_point in enumerate(polygon): if i == 0: continue walls.append(([prev_point.x(), prev_point.y(), .425], [curr_point.x(), curr_point.y(), .425])) prev_point = curr_point room.walls = walls wall_thread = WallCreator(data, walls) wall_thread.start() walls = [] polygon_br = QPolygonF(self.apartment_boundingRect, closed=True) prev_point_br = polygon_br[0] for i, curr_point_br in enumerate(polygon_br): if i == 0: continue walls.append(([prev_point_br.x(), prev_point_br.y(), .4], [curr_point_br.x(), curr_point_br.y(), .4])) prev_point_br = curr_point_br wall_thread = WallCreator(data, walls) wall_thread.start() def add_floor(self): # un suelo conjunto para el apartamento fscale_x = self.apartment_boundingRect.width() / 5 + 0.5 fscale_y = self.apartment_boundingRect.height() / 5 + 0.5 # Create and scale a floor r = self.coppelia.create_model( 'models/infrastructure/floors/5mX5m wooden floor.ttm', 0, 0, 0, 0) self.coppelia.scale_object(r, fscale_x, fscale_y, 1) for handle in self.coppelia.get_objects_children(r): self.coppelia.scale_object(handle, fscale_x, fscale_y, 1) def add_floor_per_room(self): for room in self.total_rooms_and_corridors: room_boundingRect = room.room_qpolygon.boundingRect() room_center = room_boundingRect.center() fscale_x = room_boundingRect.width() / 5 fscale_y = room_boundingRect.height() / 5 if room.type == 'corridor': floor = self.coppelia.create_model( 'models/infrastructure/floors/5mX5m wooden floor.ttm', room_center.x(), room_center.y(), 0, 0) else: floor = self.coppelia.create_model( 'models/infrastructure/floors/5mX5m concrete floor.ttm', room_center.x(), room_center.y(), 0, 0) self.coppelia.scale_object(floor, fscale_x, fscale_y, 1)
class Callout(QGraphicsItem): def __init__(self, chart): QGraphicsItem.__init__(self, chart) self._chart = chart self._text = "" self._textRect = QRectF() self._anchor = QPointF() self._font = QFont() self._rect = QRectF() def boundingRect(self): anchor = self.mapFromParent(self._chart.mapToPosition(self._anchor)) rect = QRectF() rect.setLeft(min(self._rect.left(), anchor.x())) rect.setRight(max(self._rect.right(), anchor.x())) rect.setTop(min(self._rect.top(), anchor.y())) rect.setBottom(max(self._rect.bottom(), anchor.y())) return rect def paint(self, painter, option, widget): path = QPainterPath() path.addRoundedRect(self._rect, 5, 5) anchor = self.mapFromParent(self._chart.mapToPosition(self._anchor)) if not self._rect.contains(anchor) and not self._anchor.isNull(): point1 = QPointF() point2 = QPointF() # establish the position of the anchor point in relation to _rect above = anchor.y() <= self._rect.top() aboveCenter = (anchor.y() > self._rect.top() and anchor.y() <= self._rect.center().y()) belowCenter = (anchor.y() > self._rect.center().y() and anchor.y() <= self._rect.bottom()) below = anchor.y() > self._rect.bottom() onLeft = anchor.x() <= self._rect.left() leftOfCenter = (anchor.x() > self._rect.left() and anchor.x() <= self._rect.center().x()) rightOfCenter = (anchor.x() > self._rect.center().x() and anchor.x() <= self._rect.right()) onRight = anchor.x() > self._rect.right() # get the nearest _rect corner. x = (onRight + rightOfCenter) * self._rect.width() y = (below + belowCenter) * self._rect.height() cornerCase = ((above and onLeft) or (above and onRight) or (below and onLeft) or (below and onRight)) vertical = abs(anchor.x() - x) > abs(anchor.y() - y) x1 = (x + leftOfCenter * 10 - rightOfCenter * 20 + cornerCase * int(not vertical) * (onLeft * 10 - onRight * 20)) y1 = (y + aboveCenter * 10 - belowCenter * 20 + cornerCase * vertical * (above * 10 - below * 20)) point1.setX(x1) point1.setY(y1) x2 = (x + leftOfCenter * 20 - rightOfCenter * 10 + cornerCase * int(not vertical) * (onLeft * 20 - onRight * 10)) y2 = (y + aboveCenter * 20 - belowCenter * 10 + cornerCase * vertical * (above * 20 - below * 10)) point2.setX(x2) point2.setY(y2) path.moveTo(point1) path.lineTo(anchor) path.lineTo(point2) path = path.simplified() painter.setBrush(QColor(255, 255, 255)) painter.drawPath(path) painter.drawText(self._textRect, self._text) def mousePressEvent(self, event): event.setAccepted(True) def mouseMoveEvent(self, event): if event.buttons() & Qt.LeftButton: self.setPos(mapToParent( event.pos() - event.buttonDownPos(Qt.LeftButton))) event.setAccepted(True) else: event.setAccepted(False) def setText(self, text): self._text = text metrics = QFontMetrics(self._font) self._textRect = QRectF(metrics.boundingRect( QRect(0.0, 0.0, 150.0, 150.0),Qt.AlignLeft, self._text)) self._textRect.translate(5, 5) self.prepareGeometryChange() self._rect = self._textRect.adjusted(-5, -5, 5, 5) def setAnchor(self, point): self._anchor = QPointF(point) def updateGeometry(self): self.prepareGeometryChange() self.setPos(self._chart.mapToPosition( self._anchor) + QPointF(10, -50))
class Room: def __init__(self, type='genericRoom', p=QPointF(), w=-1, h=-1): self.type = type # corridor, bedroom, kitchen, bathroom, etc self.width = w self.height = h self.initial_point = p self.room_qrect = QRectF() self.room_qpolygon = QPolygonF() self.area = -1 self.door_position = None self.create_room() def create_room(self): print( f'Creating room of type {self.type} with width = {self.width} and height = {self.height}' ) self.room_qrect = QRectF(self.initial_point.x(), self.initial_point.y(), self.width, self.height) self.room_qpolygon = QPolygonF(self.room_qrect) self.area = abs(self.width * self.height) def update_room_dimensions(self): self.width = self.room_qrect.width() self.height = self.room_qrect.height() self.area = abs(self.width * self.height) self.room_qpolygon = QPolygonF(self.room_qrect) def add_door(self, door_location): self.door_position = door_location # Diferenciar entre parte de arriba y parte de abajo dict_location_line = { 'top': QLineF(self.room_qrect.topLeft(), self.room_qrect.topRight()), 'bottom': QLineF(self.room_qrect.bottomLeft(), self.room_qrect.bottomRight()), 'left': QLineF(self.room_qrect.topLeft(), self.room_qrect.bottomLeft()), 'right': QLineF(self.room_qrect.topRight(), self.room_qrect.bottomRight()) } line = dict_location_line[door_location] line_lenght = int(line.length()) step = line_lenght / 100. line_points = [] for t in np.arange(0.25, 0.75, step): line_point = line.pointAt(t) line_points.append(QPointF(line_point.x(), line_point.y())) door = random.choice(line_points) room_polygon = { 'top': QPolygonF([ QPointF(door.x() + 0.5, door.y()), self.room_qrect.topRight(), self.room_qrect.bottomRight(), self.room_qrect.bottomLeft(), self.room_qrect.topLeft(), QPointF(door.x() - 0.5, door.y()) ]), 'bottom': QPolygonF([ QPointF(door.x() + 0.5, door.y()), self.room_qrect.bottomRight(), self.room_qrect.topRight(), self.room_qrect.topLeft(), self.room_qrect.bottomLeft(), QPointF(door.x() - 0.5, door.y()) ]), 'right': QPolygonF([ QPointF(door.x(), door.y() + 0.5), self.room_qrect.topRight(), self.room_qrect.topLeft(), self.room_qrect.bottomLeft(), self.room_qrect.bottomRight(), QPointF(door.x(), door.y() - 0.5) ]), 'left': QPolygonF([ QPointF(door.x(), door.y() + 0.5), self.room_qrect.topLeft(), self.room_qrect.topRight(), self.room_qrect.bottomRight(), self.room_qrect.bottomLeft(), QPointF(door.x(), door.y() - 0.5) ]) } self.room_qpolygon = room_polygon[door_location]
class NavBar(QWidget): """ 滑动导航条控件 作者:feiyangqingyun(QQ:517216493) 2016-10-8 译者:sunchuquin(QQ:1715216365) 2020-12-15 1. 可键盘按键上下移动元素功能 2. 支持窗体大小改变控件自动拉伸 3. 支持移动到第一个/末一个/上移/下移/移动到指定索引/移动到指定元素 4. 支持扁平处理 5. 支持纵向风格 6. 可设置圆角角度,包括主背景和选中元素背景 7. 可设置间距 8. 可设置导航条主背景渐变色 9. 可设置当前条目选中背景渐变色 10. 可设置条目未选中和选中的文字颜色 11. 可设置五种选中风格样式 12. 可设置线条颜色和宽度 13. 选中条目的宽度为条目文字集合中最长的一个 """ # 当前条目改变信号 currentItemChanged: Signal = Signal(int, str) # int: 当前条目的索引, str: 当前条目的文字 @QEnum class BarStyle(Enum): BARSTYLE_RECT = 0 # 圆角矩形 BARSTYLE_LINE_TOP = 1 # 顶部线条 BARSTYLE_LINE_RIGHT = 2 # 右侧线条 BARSTYLE_LINE_BOTTOM = 3 # 底部线条 BARSTYLE_LINE_LEFT = 4 # 左侧线条 def __init__(self, parent=None): super(NavBar, self).__init__(parent) self.__bgColorStart: QColor = QColor(121, 121, 121) # 导航条主背景渐变开始颜色 self.__bgColorEnd: QColor = QColor(78, 78, 78) # 导航条主背景渐变结束颜色 self.__old_bgColorEnd: QColor = self.__bgColorEnd # 用于扁平化切换 self.__barColorStart: QColor = QColor(46, 132, 243) # 导航条当前条目渐变开始颜色 self.__barColorEnd: QColor = QColor(39, 110, 203) # 导航条当前条目渐变结束颜色 self.__old_barColorEnd: QColor = self.__barColorEnd # 用于扁平化切换 self.__textNormalColor: QColor = QColor(230, 230, 230) # 文字正常颜色 self.__textSelectColor: QColor = QColor(255, 255, 255) # 文字选中颜色 self.__items: str = "" # 所有条目文字信息 self.__currentIndex: int = -1 # 当前选中条目索引 self.__currentItem: str = "" # 当前选中条目文字 self.__bgRadius: int = 0 # 背景圆角半径 self.__barRadius: int = 0 # 选中条目背景圆角半径 self.__space: int = 25 # 条目元素之间的间距 self.__lineWidth: int = 3 # 线条宽度 self.__lineColor: QColor = QColor(255, 107, 107) # 线条颜色 self.__barStyle: NavBar.BarStyle = NavBar.BarStyle.BARSTYLE_RECT # 选中元素样式 self.__keyMove: bool = True # 是否支持按键移动 self.__horizontal: bool = False # 是否横向显示 self.__flat: bool = False # 是否扁平化 self.__listItem: list = [] # 元素集合,成对出现,元素的名字,矩形区域范围 self.__barRect: QRectF = QRectF() # 选中区域的矩形 self.__targetRect: QRectF = QRectF() # 目标区域的矩形 self.__barLen: Decimal = Decimal(0) # 选中区域的长度 self.__targetLen: Decimal = Decimal(0) # 目标区域的长度 self.__initLen: Decimal = Decimal(10) # 导航条的长度 self.__step: int = 0 # 每次移动的步长 self.__isForward: bool = True # 是否往前移动 self.__isVirgin: bool = True # 是否首次处理 self.__timer: QTimer = QTimer(self) # 滑动绘制定时器 self.__timer.setInterval(10) self.__timer.timeout.connect(self.__slide) self.setItems("主界面|系统设置|防区管理|警情查询|视频预览") @staticmethod def __initStep(distance: int) -> int: """ 计算步长 """ n: int = 1 while True: if (n * n) > distance: break else: n += 1 return int(n * 1.4) def resizeEvent(self, event: QResizeEvent) -> None: """ 控件大小调整事件 """ index: int = 0 count: int = len(self.__listItem) if count == 0: return if (count > 0) and (not self.__currentItem): # 相当于初始化,只会执行一次 self.__currentIndex = 0 self.__currentItem = self.__listItem[0][0] for i in range(count): if self.__listItem[i][0] == self.__currentItem: index = i break self.moveTo_int(index) def mousePressEvent(self, event: QMouseEvent) -> None: """ 鼠标按压信号 """ self.moveTo_point(event.pos()) def keyPressEvent(self, event: QKeyEvent) -> None: """ """ if not self.__keyMove: return if (event.key() == Qt.Key_Left) or (event.key() == Qt.Key_Up): self.movePrevious() elif (event.key() == Qt.Key_Right) or (event.key() == Qt.Key_Down): self.moveNext() def paintEvent(self, event: QPaintEvent) -> None: """ """ # 绘制准备工作,启用反锯齿 painter: QPainter = QPainter(self) painter.setRenderHints(QPainter.Antialiasing | QPainter.TextAntialiasing) # 绘制背景色 self.drawBg(painter) # 绘制当前条目选中背景 self.drawBar(painter) # 绘制条目文字 self.drawText(painter) def drawBg(self, painter: QPainter) -> None: """ 绘制背景色 """ painter.save() painter.setPen(Qt.NoPen) bgGradient: QLinearGradient = QLinearGradient(QPoint(0, 0), QPoint(0, self.height())) bgGradient.setColorAt(0.0, self.__bgColorStart) bgGradient.setColorAt(1.0, self.__bgColorEnd) painter.setBrush(bgGradient) painter.drawRoundedRect(self.rect(), self.__bgRadius, self.__bgRadius) painter.restore() def drawBar(self, painter: QPainter) -> None: """ 绘制当前条目选中背景 """ painter.save() pen: QPen = QPen() # PySide2.QtGui.QPen barGradient: QLinearGradient = QLinearGradient( self.__barRect.topLeft(), self.__barRect.bottomLeft()) barGradient.setColorAt(0.0, self.__barColorStart) barGradient.setColorAt(1.0, self.__barColorEnd) painter.setBrush(barGradient) if self.barStyle == NavBar.BarStyle.BARSTYLE_RECT: painter.setPen(Qt.NoPen) painter.drawRoundedRect(self.__barRect, self.__barRadius, self.__barRadius) painter.restore() return else: pen.setWidthF(self.__lineWidth) pen.setBrush(barGradient) painter.setPen(pen) painter.drawRoundedRect(self.__barRect, self.__barRadius, self.__barRadius) pen.setColor(self.__lineColor) painter.setPen(pen) offset: Decimal = Decimal(self.__lineWidth) / 2 if self.__barStyle == NavBar.BarStyle.BARSTYLE_LINE_TOP: painter.drawLine(int(self.__barRect.left()), self.__barRect.top() + offset, int(self.__barRect.right()), self.__barRect.top() + offset) elif self.__barStyle == NavBar.BarStyle.BARSTYLE_LINE_TOP: painter.drawLine(self.__barRect.right() - offset, int(self.__barRect.top()), self.__barRect.right() - offset, int(self.__barRect.bottom())) elif self.__barStyle == NavBar.BarStyle.BARSTYLE_LINE_TOP: painter.drawLine(int(self.__barRect.left()), self.__barRect.bottom() - offset, int(self.__barRect.right()), self.__barRect.bottom() - offset) elif self.__barStyle == NavBar.BarStyle.BARSTYLE_LINE_TOP: painter.drawLine(self.__barRect.left() + offset, int(self.__barRect.top()), self.__barRect.left() + offset, int(self.__barRect.bottom())) # 这里还可以增加右侧倒三角型 painter.restore() def drawText(self, painter: QPainter) -> None: """ """ painter.save() textFont: QFont = QFont() textFont.setBold(True) painter.setFont(textFont) count: int = len(self.__listItem) self.__initLen = 0 # 横向导航时,字符区域取条目元素中最长的字符宽度 longText: str = "" for item in self.__items.split("|"): if len(item) > len(longText): longText = item if self.horizontal: textLen: Decimal = Decimal(painter.fontMetrics().width(longText)) else: textLen: Decimal = Decimal(painter.fontMetrics().height()) # 逐个绘制元素列表中的文字及文字背景 for i in range(count): strText: str = self.__listItem[i][0] left: QPointF = QPointF(self.__initLen, 0) right: QPointF = QPointF(self.__initLen + textLen + self.__space, self.height()) if not self.horizontal: left = QPointF(0, self.__initLen) right = QPointF(self.width(), self.__initLen + textLen + self.__space) textRect: QRectF = QRectF(left, right) self.__listItem[i][1] = textRect if self.__isVirgin: self.__barRect = textRect self.__isVirgin = False # 当前选中区域的文字显示选中文字颜色 if textRect == self.__listItem[self.__currentIndex][1]: painter.setPen(self.__textSelectColor) else: painter.setPen(self.__textNormalColor) painter.drawText(textRect, Qt.AlignCenter, strText) self.__initLen += textLen + self.__space painter.restore() def __slide(self) -> None: """ 滑动绘制 """ if self.__step > 1: self.__step -= 1 if self.horizontal: self.__barLen = self.__barRect.topLeft().x() else: self.__barLen = self.__barRect.topLeft().y() if self.__isForward: self.__barLen += self.__step if self.__barLen >= self.__targetLen: self.__barLen = self.__targetLen self.__timer.stop() else: self.__barLen -= self.__step if self.__barLen <= self.__targetLen: self.__barLen = self.__targetLen self.__timer.stop() if self.horizontal: self.__barRect = QRectF( QPointF(self.__barLen, 0), QPointF(self.__barLen + self.__barRect.width(), self.height())) else: self.__barRect = QRectF( QPointF(0, self.__barLen), QPointF(self.width(), self.__barLen + self.__barRect.height())) self.update() def getBgColorStart(self) -> QColor: """ 读取导航条主背景渐变开始颜色 """ return self.__bgColorStart def setBgColorStart(self, color_start: QColor) -> None: """ 设置导航条主背景渐变开始颜色 """ if self.__bgColorStart != color_start: self.__bgColorStart = color_start self.update() def getBgColorEnd(self) -> QColor: """ 读取导航条主背景渐变结束颜色 """ return self.__bgColorEnd def setBgColorEnd(self, color_end: QColor) -> None: """ 设置导航条主背景渐变结束颜色 """ if self.__bgColorEnd != color_end: self.__bgColorEnd = color_end self.__old_bgColorEnd = color_end self.update() def getBarColorStart(self) -> QColor: """ 读取导航条当前条目渐变开始颜色 """ return self.__barColorStart def setBarColorStart(self, color_start: QColor) -> None: """ 设置导航条当前条目渐变开始颜色 """ if self.__barColorStart != color_start: self.__barColorStart = color_start self.update() def getBarColorEnd(self) -> QColor: """ 读取导航条当前条目渐变结束颜色 """ return self.__barColorEnd def setBarColorEnd(self, color_end: QColor) -> None: """ 设置导航条当前条目渐变结束颜色 """ if self.__barColorEnd != color_end: self.__barColorEnd = color_end self.__old_barColorEnd = color_end self.update() def getTextNormalColor(self) -> QColor: """ 读取文字正常颜色 """ return self.__textNormalColor def setTextNormalColor(self, normal_color: QColor) -> None: """ 设置文字正常颜色 """ if self.__textNormalColor != normal_color: self.__textNormalColor = normal_color self.update() def getTextSelectColor(self) -> QColor: """ 读取文字选中颜色 """ return self.__textSelectColor def setTextSelectColor(self, select_color: QColor) -> None: """ 设置文字选中颜色 """ if self.__textSelectColor != select_color: self.__textSelectColor = select_color self.update() def getItems(self) -> str: """ 读取所有条目文字信息 """ return self.__items def setItems(self, items: str) -> None: """ 设置所有条目文字信息 """ self.__items = items self.__listItem.clear() for item in items.split("|"): self.__listItem.append([item, QRectF()]) self.update() def getCurrentIndex(self) -> int: """ 读取当前选中条目索引 """ return self.__currentIndex def setCurrentIndex(self, index: int) -> None: """ 设置当前选中条目索引 """ self.moveTo_int(index) def getCurrentItem(self) -> str: """ 读取当前选中条目文字 """ return self.__currentItem def setCurrentItem(self, item: str) -> None: """ 设置当前选中条目文字 """ self.moveTo_str(item) def getBgRadius(self) -> int: """ 读取背景圆角半径 """ return self.__bgRadius def setBgRadius(self, radius: int) -> None: """ 设置背景圆角半径 """ if self.__bgRadius != radius: self.__bgRadius = radius self.update() def getBarRadius(self) -> int: """ 读取选中条目背景圆角半径 """ return self.__barRadius def setBarRadius(self, radius: int) -> None: """ 设置选中条目背景圆角半径 """ if self.__barRadius != radius: self.__barRadius = radius self.update() def getSpace(self) -> int: """ 读取条目元素之间的间距 """ return self.__space def setSpace(self, space: int) -> None: """ 设置条目元素之间的间距 """ if self.space != space: self.space = space self.update() def getLineWidth(self) -> int: """ 读取线条宽度 """ return self.__lineWidth def setLineWidth(self, line_width: int) -> None: """ 设置线条宽度 """ if self.__lineWidth != line_width: self.__lineWidth = line_width self.update() def getLineColor(self) -> QColor: """ 读取线条颜色 """ return self.__lineColor def setLineColor(self, line_color: QColor) -> None: """ 设置线条颜色 """ if self.__lineColor != line_color: self.__lineColor = line_color self.update() def getBarStyle(self) -> BarStyle: """ 读取选中元素样式 """ return self.__barStyle def setBarStyle(self, bar_style: BarStyle) -> None: """ 设置选中元素样式 """ if self.__barStyle != bar_style: self.__barStyle = bar_style self.update() def getKeyMove(self) -> bool: """ 读取是否支持按键移动 """ return self.__keyMove def setKeyMove(self, key_move: bool) -> None: """ 设置是否支持按键移动 """ if self.__keyMove != key_move: self.__keyMove = key_move if key_move: self.setFocusPolicy(Qt.StrongFocus) else: self.setFocusPolicy(Qt.NoFocus) def getHorizontal(self) -> bool: """ 读取是否横向显示 """ return self.__horizontal def setHorizontal(self, horizontal: bool) -> None: """ 设置是否横向显示 """ if self.__horizontal != horizontal: self.__horizontal = horizontal self.update() def getFlat(self) -> bool: """ 读取是否扁平化 """ return self.__flat def setFlat(self, flat: bool) -> None: """ 设置是否扁平化 """ if self.__flat != flat: # 扁平后将初始颜色赋值给结束颜色达到扁平的效果,如果取消扁平则再次恢复原有的颜色 if flat: self.__bgColorEnd = self.__bgColorStart self.__barColorEnd = self.__barColorStart else: self.__bgColorEnd = self.__old_bgColorEnd self.__barColorEnd = self.__old_barColorEnd self.__flat = flat self.update() def sizeHint(self) -> QSize: """ 返回控件默认大小 """ return QSize(400, 30) def minimumSizeHint(self) -> QSize: """ 返回控件最小大小 """ return QSize(30, 30) def clearItem(self) -> None: """ 删除所有条目 """ self.__listItem.clear() self.update() def moveFirst(self) -> None: """ 移动到第一个条目 """ index: int = 0 if self.__currentIndex != index: self.moveTo_int(index) def moveLast(self) -> None: """ 移动到最后一个条目 """ index = len(self.__listItem) - 1 if self.__currentIndex != index: self.moveTo_int(index) def movePrevious(self) -> None: """ 往前移动条目 """ if self.__currentIndex > 0: self.__currentIndex -= 1 self.moveTo_int(self.__currentIndex) def moveNext(self) -> None: """ 往后移动条目 """ if self.__currentIndex < (len(self.__listItem) - 1): self.__currentIndex += 1 self.moveTo_int(self.__currentIndex) def moveTo_int(self, index: int) -> None: """ 移动到指定索引条目 """ if (index >= 0) and (len(self.__listItem) > index): rec: QRectF = QRectF(self.__listItem[index][1]) pos: QPoint = QPoint(int(rec.x()), int(rec.y())) self.moveTo_point(pos) def moveTo_str(self, item: str) -> None: """ 移动到指定文字条目 """ count: int = len(self.__listItem) for i in range(count): if self.__listItem[i][0] == item: self.moveTo_int(i) break def moveTo_point(self, point: QPointF) -> None: """ 移动到指定坐标位置条目 """ count: int = len(self.__listItem) for i in range(count): # 如果不是最后一个,则辨别指定项 if i != (count - 1): # 辨别方法,如果不在两项之间,则不是指定项 if self.__horizontal: if not ( (point.x() >= self.__listItem[i][1].topLeft().x()) and (point.x() < self.__listItem[i + 1][1].topLeft().x())): continue else: if not ( (point.y() >= self.__listItem[i][1].topLeft().y()) and (point.y() < self.__listItem[i + 1][1].topLeft().y())): continue self.__currentIndex = i self.__currentItem = self.__listItem[i][0] self.__targetRect = self.__listItem[i][1] if self.__horizontal: self.__targetLen = self.__targetRect.topLeft().x() self.__barLen = self.__barRect.topLeft().x() else: self.__targetLen = self.__targetRect.topLeft().y() self.__barLen = self.__barRect.topLeft().y() self.__isForward = (self.__targetLen > self.__barLen) distance: int = int(abs(self.__targetLen - self.__barLen)) # 重新获取每次移动的步长 self.__step = self.__initStep(int(distance)) self.__timer.start() self.currentItemChanged.emit(self.__currentIndex, self.__currentItem) break bgColorStart: QColor = property(fget=getBgColorStart, fset=setBgColorStart, fdel=None, doc="导航条主背景渐变开始颜色") bgColorEnd: QColor = property(fget=getBgColorEnd, fset=setBgColorEnd, fdel=None, doc="导航条主背景渐变结束颜色") barColorStart: QColor = property(fget=getBarColorStart, fset=setBarColorStart, fdel=None, doc="导航条当前条目渐变开始颜色") barColorEnd: QColor = property(fget=getBarColorEnd, fset=setBarColorEnd, fdel=None, doc="导航条当前条目渐变结束颜色") textNormalColor: QColor = property(fget=getTextNormalColor, fset=setTextNormalColor, fdel=None, doc="文字正常颜色") textSelectColor: QColor = property(fget=getTextSelectColor, fset=setTextSelectColor, fdel=None, doc="文字选中颜色") items: str = property(fget=getItems, fset=setItems, fdel=None, doc="所有条目文字信息") currentIndex: int = property(fget=getCurrentIndex, fset=setCurrentIndex, fdel=None, doc="当前选中条目索引") currentItem: str = property(fget=getCurrentItem, fset=setCurrentItem, fdel=None, doc="当前选中条目文字") bgRadius: int = property(fget=getBgRadius, fset=setBgRadius, fdel=None, doc="背景圆角半径") barRadius: int = property(fget=getBarRadius, fset=setBarRadius, fdel=None, doc="选中条目背景圆角半径") space: int = property(fget=getSpace, fset=setSpace, fdel=None, doc="条目元素之间的间距") lineWidth: int = property(fget=getLineWidth, fset=setLineWidth, fdel=None, doc="线条宽度") lineColor: QColor = property(fget=getLineColor, fset=setLineColor, fdel=None, doc="线条颜色") barStyle: BarStyle = property(fget=getBarStyle, fset=setBarStyle, fdel=None, doc="选中元素样式") keyMove: bool = property(fget=getKeyMove, fset=setKeyMove, fdel=None, doc="是否支持按键移动") horizontal: bool = property(fget=getHorizontal, fset=setHorizontal, fdel=None, doc="是否横向显示") flat: bool = property(fget=getFlat, fset=setFlat, fdel=None, doc="是否扁平化")