class GraphicsScene(QGraphicsScene): def __init__(self): super().__init__() self._pos = QPointF() self._current_item = None # Полупрозрачный цвет self._item_color = QColor(0, 0, 255, 128) def mousePressEvent(self, event): super().mousePressEvent(event) self._pos = event.scenePos() self._current_item = QGraphicsRectItem() self._current_item.setBrush(self._item_color) self.addItem(self._current_item) self._current_item.setRect(QRectF(self._pos, self._pos)) def mouseMoveEvent(self, event): super().mouseMoveEvent(event) if self._current_item: rect = QRectF(self._pos, event.scenePos()).normalized() self._current_item.setRect(rect) def mouseReleaseEvent(self, event): super().mouseReleaseEvent(event) # Убираем после отпускания кнопки мыши self.removeItem(self._current_item) self._current_item = None
def create_fromDB(self,pos_list=[],obj_type=""): if obj_type =="rois": num_rois = len(pos_list) for i in range(num_rois): points = pos_list[i] rect=QGraphicsRectItem() rect.setPen(QPen(Qt.green)) rect.setRect(points[0],points[1],points[2],points[3]) self.rectgroup.addToGroup(rect) self.scene.addItem(self.rectgroup) elif obj_type =="vector1": num_vec = len(pos_list) for i in range(num_vec): points = pos_list[i] vec=QGraphicsLineItem() vec.setPen(QPen(Qt.green)) vec.setLine(points[0],points[1],points[2],points[3]) self.linegroup.addToGroup(vec) self.scene.addItem(self.linegroup) elif obj_type =="vector2": num_vec = len(pos_list) for i in range(num_vec): points = pos_list[i] vec=QGraphicsLineItem() vec.setPen(QPen(Qt.green)) vec.setLine(points[0],points[1],points[2],points[3]) self.linegroup2.addToGroup(vec) self.scene.addItem(self.linegroup2)
def __init__(self, parent=None): QGraphicsScene.__init__(self, parent) # hold the set of keys we're pressing self.keys_pressed = set() # use a timer to get 60Hz refresh (hopefully) self.timer = QBasicTimer() self.timer.start(FRAME_TIME_MS, self) bg = QGraphicsRectItem() bg.setRect(-1, -1, SCREEN_WIDTH + 2, SCREEN_HEIGHT + 2) bg.setBrush(QBrush(Qt.black)) self.addItem(bg) self.player = Player() self.player.setPos((SCREEN_WIDTH - self.player.pixmap().width()) / 2, (SCREEN_HEIGHT - self.player.pixmap().height()) / 2) self.bullets = [ Bullet(PLAYER_BULLET_X_OFFSETS[0], PLAYER_BULLET_Y), Bullet(PLAYER_BULLET_X_OFFSETS[1], PLAYER_BULLET_Y) ] for b in self.bullets: b.setPos(SCREEN_WIDTH, SCREEN_HEIGHT) self.addItem(b) self.addItem(self.player) self.view = QGraphicsView(self) self.view.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.view.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.view.show() self.view.setFixedSize(SCREEN_WIDTH, SCREEN_HEIGHT) self.setSceneRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)
class StimulusGraphicsScene(QGraphicsScene): def __init__(self, parent=None): super().__init__(parent) self._invert = False self.homography_transform = None self.setBackgroundBrush(Qt.black) monitor_width = 3200 monitor_height = 1800 self.background_image = QGraphicsRectItem(0, 0, 1, 1) brush = QBrush() brush.setStyle(Qt.SolidPattern) brush.setColor(Qt.black) self.background_image.setBrush(brush) self.addItem(self.background_image) self.visible_group = QGraphicsItemGroup() self.visible_stimuli = [] # self.sceneRectChanged.connect(self.on_sceneRectChanged) # test_rect = QGraphicsRectItem(-50, -50, 100, 100) # brush2 = QBrush() # brush2.setStyle(Qt.CrossPattern) # brush2.setColor(Qt.blue) # test_rect.setBrush(brush2) # self.addItem(test_rect) # self.test_rect = test_rect #test_rect.setPos(monitor_width/2, monitor_height/2) def display_points(self, points): #log.debug("Adding {} to scene.".format(points)) for p in self.visible_stimuli: self.removeItem(p) self.visible_stimuli = [] for p in points: log.debug("Adding at {} of size {}".format(p.pos(), p.rect().width())) self.addItem(p) self.visible_stimuli.append(p) # self.update() @property def invert(self): return self._invert @invert.setter def invert(self, value): self._invert = value self.update() def update_background(self): self.background_image.setRect(self.sceneRect())
def set_boxes(self, boxes): for box in boxes: box_item = QGraphicsRectItem() box_item.setPen(QPen(Qt.red, 4)) box_item.setBrush(QBrush(Qt.NoBrush)) box_item.setZValue(1) self.scene.addItem(box_item) box_item.hide() box_item.setRect(QRectF(box[0], box[1], box[2]-box[0], box[3]-box[1])) self.box_item_list.append(box_item)
def init_background(self): window_bg = QGraphicsRectItem() window_bg.setRect(-OFSET[0], -OFSET[1], SCREEN_WIDTH, SCREEN_HEIGHT) window_bg.setBrush(QBrush(QColor(130, 128, 129))) self.addItem(window_bg) game_bg = QGraphicsRectItem() game_bg.setRect(0, 0, WIDTH - 1, HEIGHT - 1) game_bg.setBrush(QBrush(Qt.black)) self.addItem(game_bg)
class ImageCropper(QGraphicsView): cropped = pyqtSignal(QPointF, QPointF) def __init__( self, parent=None, ): super().__init__(QGraphicsScene(), parent) self.setAlignment(Qt.AlignCenter) self._click_pos = None self._release_pos = None def set_image(self, image): qtimage = QImage(image.data, image.shape[1], image.shape[0], QImage.Format_RGB888).rgbSwapped() self.scene().clear() self._rect = QGraphicsRectItem() self._rect.setPen(QPen(Qt.red, 2, Qt.SolidLine)) self._image = self.scene().addPixmap(QPixmap.fromImage(qtimage)) self.scene().addItem(self._rect) def mousePressEvent(self, event): scene_position = self.mapToScene(event.pos()) image_position = self._image.mapFromScene(scene_position) if self._image.contains(image_position): self._click_pos = image_position self._release_pos = None else: self._click_pos = None self._release_pos = None def mouseReleaseEvent(self, event): scene_position = self.mapToScene(event.pos()) image_position = self._image.mapFromScene(scene_position) if self._image.contains(image_position): self._release_pos = image_position if self._click_pos is not None: self.cropped.emit(self._click_pos, self._release_pos) else: self._click_pos = None else: self._click_pos = None self._release_pos = None def mouseMoveEvent(self, event): scene_position = self.mapToScene(event.pos()) image_position = self._image.mapFromScene(scene_position) if (self._click_pos is not None) and (self._image.contains(image_position)): self._rect.setRect(QRectF(self._click_pos, scene_position)) else: self._rect.setRect(QRectF())
def show_text(self, text, size): self.update() window_bg = QGraphicsRectItem() window_bg.setRect(-OFSET[0], -OFSET[1], SCREEN_WIDTH, SCREEN_HEIGHT) window_bg.setBrush(QBrush(Qt.black)) self.addItem(window_bg) self.text = QGraphicsTextItem(text) self.text.setDefaultTextColor(Qt.white) self.text.setPos(window_bg.rect().center() - QPoint(120, size)) self.text.setFont(QFont('Press Start', int(size))) self.addItem(self.text)
def printAttributes(self, background, border, text): """ Prints the attributes of the node The attributes are a key, value pair :param background: background color of the node :param border: border color for the node :param text: text color for the node """ y = self.y() + self.headerHeight x = self.x() self.attributesHeight = 0 for k, v in self.node.attributes.items(): key = QGraphicsTextItem() key.setFont(Configuration.font) key.setDefaultTextColor(QColor(text)) key.setTextWidth(100) key.setPlainText(k) keyHeight = int(key.boundingRect().height() / 20 + 0.5) * 20 value = QGraphicsTextItem() value.setFont(Configuration.font) value.setDefaultTextColor(QColor(text)) value.setTextWidth(100) value.setPlainText(v) valueHeight = int(value.boundingRect().height() / 20 + 0.5) * 20 height = valueHeight if valueHeight > keyHeight else keyHeight keyRect = QGraphicsRectItem() keyRect.setRect(x, y, 100, height) valueRect = QGraphicsRectItem() valueRect.setRect(x + 100, y, 100, height) keyRect.setBrush(QBrush(QColor(background))) valueRect.setBrush(QBrush(QColor(background))) keyRect.setPen(QPen(QColor(border), 2)) valueRect.setPen(QPen(QColor(border), 2)) key.setPos(x, y - 2) value.setPos(x + 100, y - 2) self.attributes.addToGroup(keyRect) self.attributes.addToGroup(valueRect) self.attributes.addToGroup(key) self.attributes.addToGroup(value) y = y + height self.attributesHeight += height self.addToGroup(self.attributes)
def __init__(self, parent=None): QGraphicsScene.__init__(self, parent) bg = QGraphicsRectItem() bg.setRect(0, 0, globals.SCREEN_WIDTH, globals.SCREEN_HEIGHT) bg.setBrush(QBrush(Qt.black)) self.addItem(bg) self.titleFont = QtGui.QFont() self.titleFont.setPointSize(40) self.buttonFont = QtGui.QFont() self.buttonFont.setPointSize(20) self.title = QtWidgets.QGraphicsTextItem('Kario Game') self.title.setDefaultTextColor(QtGui.QColor(255, 0, 0)) self.title.setFont(self.titleFont) self.title.setPos(190, 100) self.b1 = QtWidgets.QGraphicsTextItem('PLAY') self.b1.setDefaultTextColor(QtGui.QColor(255, 0, 0)) self.b1.setFont(self.buttonFont) self.b1.setPos(150, 300) self.b2 = QtWidgets.QGraphicsTextItem('INFO') self.b2.setDefaultTextColor(QtGui.QColor(255, 0, 0)) self.b2.setFont(self.buttonFont) self.b2.setPos(550, 300) button1 = QGraphicsRectItem(147, 300, 104, 60) button1.setBrush(QBrush(QtGui.QColor(38, 38, 38))) button2 = QGraphicsRectItem(547, 300, 106, 60) button2.setBrush(QBrush(QtGui.QColor(38, 38, 38))) self.addItem(button1) self.addItem(button2) self.addItem(self.title) self.addItem(self.b1) self.addItem(self.b2) with open("static/highscore.json") as f: data = json.load(f) self.highscore = data["highscore"] self.hs = QtWidgets.QGraphicsTextItem('Current Highscore: ' + str(int(self.highscore))) self.hs.setDefaultTextColor(QtGui.QColor(200, 0, 0)) self.hs.setFont(self.buttonFont) self.hs.setPos(200, 400) self.addItem(self.hs) self.view = Camera(self) self.view.ensureVisible(bg)
class PickingStation(VisualizerGraphicItem): def __init__(self, ID=0, x=0, y=0): super(self.__class__, self).__init__(ID, x, y) self._kind_name = 'pickingStation' self._items = [] self._graphics_item = QGraphicsRectItem(self) self._items.append(QGraphicsRectItem(self._graphics_item)) self._items.append(QGraphicsRectItem(self._graphics_item)) self._text = QGraphicsTextItem(self._graphics_item) def set_rect(self, rect): scale = config.get('display', 'id_font_scale') bold = config.get('display', 'id_font_bold') self._text.setFont(QFont('', rect.width() * 0.08 * scale)) self._text.setPos(rect.x(), rect.y() + 0.6 * rect.height()) self._text.setDefaultTextColor( QColor(config.get('display', 'id_font_color'))) if self._display_mode == 0: if bold: self._text.setHtml('<b>P(' + str(self._id) + ')</b>') else: self._text.setHtml('P(' + str(self._id) + ')') self._graphics_item.setRect(rect.x(), rect.y(), rect.width(), rect.height()) self._items[0].setRect(rect.x() + rect.width() / 5, rect.y(), rect.width() / 5, rect.height()) self._items[1].setRect(rect.x() + rect.width() / 5 * 3, rect.y(), rect.width() / 5, rect.height()) elif self._display_mode == 1: self._text.setPlainText('') self._graphics_item.setRect(rect.x(), rect.y(), rect.width(), rect.height()) self._items[0].setRect(rect.x() + rect.width() / 5, rect.y(), rect.width() / 5, rect.height()) self._items[1].setRect(rect.x() + rect.width() / 5 * 3, rect.y(), rect.width() / 5, rect.height()) def determine_color(self, number, count, pattern=None): color = self._colors[0] color2 = self._colors[1] color.setAlpha(150) color2.setAlpha(150) brush = QBrush(color) brush2 = QBrush(color2) self._graphics_item.setBrush(brush) self._items[0].setBrush(brush2) self._items[1].setBrush(brush2) def get_rect(self): return self._graphics_item.rect()
def _configureOutline(self, outline: QGraphicsRectItem) -> QRectF: """Adjusts `outline` size with default padding. Args: outline: Description Returns: o_rect: `outline` QRectF adjusted by _BOUNDING_RECT_PADDING """ _p = self._BOUNDING_RECT_PADDING o_rect = self.rect().adjusted(-_p, -_p, _p, _p) outline.setRect(o_rect) return o_rect
def __init__(self, cam, menu, parent=None): QGraphicsScene.__init__(self, parent) self.view = cam self.menu = menu bg = QGraphicsRectItem() bg.setRect(0, 0, globals.SCREEN_WIDTH, globals.SCREEN_HEIGHT) bg.setBrush(QBrush(Qt.black)) self.addItem(bg) self.button1 = QGraphicsRectItem(40, 500, 110, 60) self.button1.setBrush(QBrush(QtGui.QColor(38, 38, 38))) self.addItem(self.button1) self.buttonFont = QtGui.QFont() self.buttonFont.setPointSize(20) self.textFont = QtGui.QFont() self.textFont.setPointSize(18) self.titleFont = QtGui.QFont() self.titleFont.setPointSize(30) self.b1 = QtWidgets.QGraphicsTextItem('BACK') self.b1.setDefaultTextColor(QtGui.QColor(255, 0, 0)) self.b1.setPos(43, 500) self.b1.setFont(self.buttonFont) self.addItem(self.b1) self.title = QtWidgets.QGraphicsTextItem('How To Play') self.title.setDefaultTextColor(QtGui.QColor(255, 0, 0)) self.title.setPos(200, 25) self.title.setFont(self.titleFont) self.addItem(self.title) text = ( 'Kario can move with left and right arrow keys and jump with space. ' + 'Your job is to collect every price as quickly as possible but be aware of ' + 'turtle enemys: If they touch you, you will die! How ever, you can' + 'distroy them by jumping on them. If you have collected all of the ' + 'prices and succesfully make it to the goal, you win the level!') self.text = QtWidgets.QGraphicsTextItem(text) self.text.setDefaultTextColor(QtGui.QColor(255, 255, 255)) self.text.setPos(85, 130) self.text.setFont(self.textFont) self.text.adjustSize() self.addItem(self.text) self.view.update_scene(self)
def updateGraphicsRectItem(rect: QGraphicsRectItem, rectF: QRectF = None, pen: QPen = None, brush: QBrush = None): """ 更新QGraphicsRectItem属性 :param rect: QGraphicsRectItem :param rectF: 矩形rect :param pen: 边框画笔 :param brush: 填充画刷 """ if rectF: rect.setRect(rectF) if pen: rect.setPen(pen) if brush: rect.setBrush(brush)
def __init__(self, size, scene: QGraphicsScene): self.rectItems = [] self.pixmap = QPixmap(QSize(820, 820)) self.painter = QPainter(self.pixmap) self.scene = scene self.owners = None self.values = None pen = QPen() pen.setStyle(Qt.NoPen) for index in range(size**2): item = QGraphicsRectItem() item.setRect(int(index / size), int(index % size), 0.9, 0.9) item.setPen(pen) scene.addItem(item) self.rectItems.append(item)
def __init__(self, size, scene: QGraphicsScene): self.rectItems = [] self.pixmap = QPixmap(QSize(820,820)) self.painter = QPainter(self.pixmap) self.scene = scene self.owners = None self.values = None pen = QPen() pen.setStyle(Qt.NoPen) for index in range(size**2): item = QGraphicsRectItem() item.setRect(int(index/size), int(index%size), 0.9, 0.9) item.setPen(pen) scene.addItem(item) self.rectItems.append(item)
class MyView(QWidget): def __init__(self): super().__init__() self.game_init() def game_init(self): self.scene = QGraphicsScene(parent=self) self.player = QGraphicsRectItem() self.player.setRect(0, 0, 100, 100) # add the item to the scene self.scene.addItem(self.player) self.view = QGraphicsView(self.scene, parent=self) # add a view, widget(invisible) self.show()
class CustomDragWidget(QGraphicsWidget): def __init__(self,): super().__init__() self.squareItem = QGraphicsRectItem() self.squareItem.setBrush(QBrush(QColor(Qt.blue))) self.squareItem.setPen(QPen(QColor(Qt.black), 2)) self.squareItem.setRect(0, 0, 50, 50) self.squareItem.setParentItem(self) self.setAcceptDrops(True) def mousePressEvent(self, event): mime = QMimeData() itemData = QByteArray() mime.setData('application/x-dnditemdata', itemData) drag = QDrag(self) drag.setMimeData(mime) drag.exec_(Qt.MoveAction) def dropEvent(self, event): event.accept()
def approve_obj(self): self.scene.removeItem(self.rect) self.scene.removeItem(self.line) viewBBox = self.zoomStack[-1] if len( self.zoomStack) else self.sceneRect() selectionBBox = self.scene.selectionArea().boundingRect().intersected( viewBBox) rect = QGraphicsRectItem() rect.setRect(selectionBBox) rect.setPen(QPen(Qt.green)) self.rectgroup.addToGroup(rect) self.scene.addItem(self.rectgroup) line = QGraphicsLineItem() line.setLine(QLineF(self.start, self.current)) line.setPen(QPen(Qt.green)) self.linegroup.addToGroup(line) self.scene.addItem(self.linegroup) return (selectionBBox, self.start, self.current)
def makeRoad(self, parent): self.line_space = (parent.height() / (LINE_COUNT - 2)) - LINE_H bg = QGraphicsRectItem() bg.setRect(0, 0, parent.width(), parent.height()) bg.setBrush(QBrush(Qt.gray)) self.scene.addItem(bg) self.lines = [] self.topLineIndex = 0 ax = (parent.width() / 2) - (LINE_W / 2) for i in range(LINE_COUNT): line = QGraphicsRectItem() ay = (i - 1) * (LINE_H + self.line_space) line.setRect(0, 0, LINE_W, LINE_H) line.setPos(ax, ay) line.setBrush(QBrush(Qt.white)) self.scene.addItem(line) self.lines.append(line) """ Because of the lines, the scene isn't in the middle """ """ So we add an extra rectangle to center the scene """ spaceFill = QGraphicsRectItem() ay = (LINE_COUNT - 1) * (LINE_H + self.line_space) - self.line_space spaceFill.setRect(ax, ay, LINE_W, self.line_space) self.scene.addItem(spaceFill) self.setFixedSize(parent.width(), parent.height())
def init_ui(self): scene = QGraphicsScene() scene.setBackgroundBrush(QColor(100, 100, 100)) scene.setItemIndexMethod(QGraphicsScene.BspTreeIndex) scene.setSceneRect(scene.itemsBoundingRect()) self.setDragMode(QGraphicsView.RubberBandDrag) self.setViewportUpdateMode(QGraphicsView.FullViewportUpdate) self.setRenderHints(QPainter.Antialiasing | QPainter.TextAntialiasing) self.frame_item = QGraphicsPixmapItem() self.text_item_offset = 0 self.rect_item_array = [] self.text_item_array = [] for i in range(0, 5): rect_item = QGraphicsRectItem() rect_item.setVisible(False) rect_item.setZValue(20.0) rect_item.setPen(QPen(Qt.red, 5)) rect_item.setRect(20, 20, 20, 20) scene.addItem(rect_item) self.rect_item_array.append(rect_item) text_item = QGraphicsSimpleTextItem("") text_item.setBrush(QBrush(Qt.red)) text_item.setZValue(20.0) text_item.setPos(10, 50) text_item.setFont(QFont("黑体", 32)) text_item.setVisible(False) scene.addItem(text_item) self.text_item_array.append(text_item) scene.addItem(self.frame_item) self.curr_factor = 1.0 self.setScene(scene)
def init_ui(self): scene = QGraphicsScene() scene.setBackgroundBrush(QColor(100, 100, 100)) scene.setItemIndexMethod(QGraphicsScene.BspTreeIndex) scene.setSceneRect(scene.itemsBoundingRect()) self.setDragMode(QGraphicsView.RubberBandDrag) self.setViewportUpdateMode(QGraphicsView.FullViewportUpdate) self.setRenderHints(QPainter.Antialiasing | QPainter.TextAntialiasing) self.frame_item = QGraphicsPixmapItem() self.text_item_offset = 0 self.rect_item_array = [] self.text_item_array = [] for i in range(0, 5): rect_item = QGraphicsRectItem() rect_item.setVisible(False) rect_item.setZValue(20.0) rect_item.setPen(QPen(Qt.red, 5)) rect_item.setRect(20, 20, 20, 20) scene.addItem(rect_item) self.rect_item_array.append(rect_item) text_item = QGraphicsSimpleTextItem("") text_item.setBrush(QBrush(Qt.red)) text_item.setZValue(20.0) text_item.setPos(10, 50) text_item.setFont(QFont("黑体", 24)) text_item.setVisible(False) scene.addItem(text_item) self.text_item_array.append(text_item) scene.addItem(self.frame_item) self.curr_factor = 1.0 self.setScene(scene)
def setTiles(self): #background brush = QBrush( ) #QBrush(画刷)是一个基本的图形对象,用于填充如矩形,椭圆形,多边形等形状,QBrush有三种类型,预定义,过渡,和纹理图案 pix = QPixmap(os.getcwd() + "/robotImages/tile.png") brush.setTexture(pix) brush.setStyle(24) self.setBackgroundBrush(brush) #wall:left、right、top、bottom都是QGraphicsRectItem类型 #left left = QGraphicsRectItem() pix = QPixmap(os.getcwd() + "/robotImages/tileVert.png") #获取贴图 left.setRect(QRectF(0, 0, pix.width(), self.height)) #尺寸:宽是图片宽度 ,高度是战场高度 brush.setTexture(pix) #设置贴图函数 brush.setStyle( 24 ) #参数24指平铺格式(参数为枚举类型)详情见 https://doc.qt.io/qt-5/qt.html#BrushStyle-enum left.setBrush(brush) left.name = 'left' self.addItem(left) #right right = QGraphicsRectItem() right.setRect(self.width - pix.width(), 0, pix.width(), self.height) #尺寸:宽是图片的宽度、高是战场的高 right.setBrush(brush) right.name = 'right' self.addItem(right) #top top = QGraphicsRectItem() pix = QPixmap(os.getcwd() + "/robotImages/tileHori.png") top.setRect(QRectF(0, 0, self.width, pix.height())) brush.setTexture(pix) brush.setStyle(24) top.setBrush(brush) top.name = 'top' self.addItem(top) #bottom bottom = QGraphicsRectItem() bottom.setRect(0, self.height - pix.height(), self.width, pix.height()) bottom.setBrush(brush) bottom.name = 'bottom' self.addItem(bottom)
class Desklet(object): def __init__(self): self.rect = QRectF() self.style = Style() self.root = QGraphicsItemGroup() if False: self.debug_rect = QGraphicsRectItem(self.root) self.debug_rect.setPen(QPen(QColor(255, 0, 0))) else: self.debug_rect = None def set_style(self, style): self.style = style def set_rect(self, rect): self.rect = rect if self.debug_rect: self.debug_rect.setRect(rect) def layout(self): pass
class MyScene(QGraphicsScene): def __init__(self, data, parent=None): QGraphicsScene.__init__(self, parent) self.data = data self.rec = QGraphicsRectItem() self.plot: MyItem(data) = None self.bounding_rect = QGraphicsRectItem() self.setBackgroundBrush(QColor('#14161f')) self.addItem(self.bounding_rect) self.printed = False def mouseMoveEvent(self, event: 'QGraphicsSceneMouseEvent'): print() print("rec rect : ", self.rec.rect()) print("Scene rect : ", self.sceneRect()) print("ItemBounding rect : ", self.itemsBoundingRect()) print("transform : ", self.plot.transform().m11(), ", ", self.plot.transform().m22()) item = self.itemAt(event.scenePos(), self.plot.transform()) if item and isinstance(item, MyItem): print() print('collides path : ', self.rec.collidesWithPath(item.mapToScene(item.path))) print('collides item : ', self.rec.collidesWithItem(item)) super().mouseMoveEvent(event) def print_bound(self, rect): self.bounding_rect.setPen(QPen(Qt.green)) self.bounding_rect.setRect(rect.x() + 5, rect.y() + 5, rect.width() - 10, rect.height() - 10)
class CplxItem(KineticsDisplayItem): defaultWidth = 10 defaultHeight = 10 name = constants.ITEM def __init__(self, *args, **kwargs): KineticsDisplayItem.__init__(self, *args, **kwargs) self.gobj = QGraphicsRectItem(0, 0, CplxItem.defaultWidth, CplxItem.defaultHeight, self) self.gobj.mobj = self.mobj self._conc = self.mobj.conc self._n = self.mobj.n doc = "Conc\t: " + str(self._conc) + "\nn\t: " + str(self._n) self.gobj.setToolTip(doc) def updateValue(self, gobj): self._gobj = gobj if (isinstance(self._gobj, moose.PoolBase)): self._conc = self.mobj.conc self._n = self.mobj.n doc = "Conc\t: " + str(self._conc) + "\nn\t: " + str(self._n) self.gobj.setToolTip(doc) def setDisplayProperties(self, x, y, textcolor, bgcolor): """Set the display properties of this item.""" self.setGeometry(self.gobj.boundingRect().width() / 2, self.gobj.boundingRect().height(), self.gobj.boundingRect().width(), self.gobj.boundingRect().height()) self.setFlag(QGraphicsItem.ItemIsMovable, False) def refresh(self, scale): defaultWidth = CplxItem.defaultWidth * scale defaultHeight = CplxItem.defaultHeight * scale self.gobj.setRect(0, 0, defaultWidth, defaultHeight)
class ZoomableScene(QGraphicsScene): def __init__(self, parent=None): super().__init__(parent) self.noise_area = None self.ones_area = None self.zeros_area = None self.ones_arrow = None self.zeros_arrow = None self.selection_area = ROI(0, 0, 0, 0, fillcolor=constants.SELECTION_COLOR, opacity=constants.SELECTION_OPACITY) self.addItem(self.selection_area) def draw_noise_area(self, y, h): x = self.sceneRect().x() w = self.sceneRect().width() if self.ones_area is not None: self.ones_area.hide() if self.zeros_area is not None: self.zeros_area.hide() if self.noise_area is None or self.noise_area.scene() != self: roi = ROI(x, y, w, h, fillcolor=constants.NOISE_COLOR, opacity=constants.NOISE_OPACITY) # roi.setPen(QPen(constants.NOISE_COLOR, Qt.FlatCap)) self.noise_area = roi self.addItem(self.noise_area) else: self.noise_area.show() self.noise_area.setY(y) self.noise_area.height = h def draw_sep_area(self, ymid): x = self.sceneRect().x() y = self.sceneRect().y() h = self.sceneRect().height() w = self.sceneRect().width() # padding = constants.SEPARATION_PADDING padding = 0 if self.noise_area is not None: self.noise_area.hide() if self.ones_area is None: self.ones_area = QGraphicsRectItem(x, y, w, h / 2 - padding + ymid) self.ones_area.setBrush(constants.ONES_AREA_COLOR) self.ones_area.setOpacity(constants.SEPARATION_OPACITY) self.ones_area.setPen(QPen(constants.TRANSPARENT_COLOR, Qt.FlatCap)) self.addItem(self.ones_area) else: self.ones_area.show() self.ones_area.setRect(x, y, w, h / 2 - padding + ymid) start = y + h / 2 + padding + ymid if self.zeros_area is None: self.zeros_area = QGraphicsRectItem(x, start, w, (y + h) - start) self.zeros_area.setBrush(constants.ZEROS_AREA_COLOR) self.zeros_area.setOpacity(constants.SEPARATION_OPACITY) self.zeros_area.setPen( QPen(constants.TRANSPARENT_COLOR, Qt.FlatCap)) self.addItem(self.zeros_area) else: self.zeros_area.show() self.zeros_area.setRect(x, start, w, (y + h) - start) def clear(self): self.noise_area = None self.ones_area = None self.zeros_area = None self.zeros_arrow = None self.ones_arrow = None self.selection_area = None super().clear() def dragEnterEvent(self, event: QGraphicsSceneDragDropEvent): event.accept() def dragMoveEvent(self, event: QGraphicsSceneDragDropEvent): event.accept()
class BankNodeItem (NodeItem): maincolor = FlPalette.bank altcolor = FlPalette.bankvar label = "%s Bank" def __init__ (self, nodeobj, parent=None, view=None, state=1): super().__init__(nodeobj, parent, view, state) self.rect = QRectF() self.setZValue(-1) self.updatecomment() self.updatebanktype() self.updatebankmode() def nudgechildren(self): super().nudgechildren() for sub in self.sublist(): sub.setrank(self) def sublist (self): ID = self.nodeobj.ID itemtable = self.view.itemtable if self.state == 1 and ID in itemtable and not self.iscollapsed(): children = [] for child in self.nodeobj.subnodes: if child in itemtable[ID]: item = itemtable[ID][child] else: continue if item.state > -1: children.append(item) return children else: return [] def treeposition (self, ranks=None): self.updatelayout(external=True) return super().treeposition(ranks) def graphicsetup (self): super().graphicsetup() darkbrush = QBrush(FlPalette.bg) nopen = QPen(0) viewport = self.view.viewport() self.btypeicon = QGraphicsPixmapItemCond(self.icon, self, viewport) self.btypeicon.setPos(self.iconx-self.style.itemmargin-self.iwidth, self.style.itemmargin) self.iconx = self.btypeicon.x() self.fggroup.addToGroup(self.btypeicon) self.centerbox = QGraphicsRectItem(self) self.centerbox.setCacheMode(QGraphicsItem.DeviceCoordinateCache) self.centerbox.setRect(QRectF()) self.centerbox.setBrush(darkbrush) self.centerbox.setPen(nopen) self.centerbox.setPos(0, self.nodelabel.y()+self.nodelabel.boundingRect().height()+self.style.itemmargin*2) self.fggroup.addToGroup(self.centerbox) def updatebanktype (self): types = {"talk": "(T)", "response": "(R)", "": ""} self.nodelabel.setText("%s Bank %s" % (self.realid(), types[self.nodeobj.banktype])) def updatebankmode (self): icons = {"First": "bank-first", "All": "bank-all", "Append": "bank-append", "": "blank"} pixmap = self.pixmap("images/%s.png" % icons[self.nodeobj.bankmode]) self.btypeicon.setPixmap(pixmap) if self.nodeobj.bankmode: self.btypeicon.setToolTip("Bank mode: %s" % self.nodeobj.bankmode) else: self.btypeicon.setToolTip("") def updatecenterbox (self): verticalpos = self.centerbox.y() maxwidth = self.style.nodetextwidth subnodes = self.sublist() for subnode in subnodes: if subnode.nodeobj.typename == "bank": subnode.updatelayout(external=True) noderect = subnode.boundingRect() nodeheight = noderect.height() nodewidth = noderect.width() subnode.show() subnode.yoffset = self.mapToScene(0,verticalpos + nodeheight/2+self.style.activemargin).y()-self.y_bottom() verticalpos += nodeheight+self.style.activemargin*2 maxwidth = max(maxwidth, nodewidth) centerrect = self.centerbox.rect() centerrect.setWidth(maxwidth+self.style.selectmargin*2) centerrect.setHeight(verticalpos-self.centerbox.y()) self.centerbox.setRect(centerrect) centerrect = self.centerbox.mapRectToParent(centerrect) self.comment.setY(centerrect.bottom()+self.style.itemmargin) def updatelayout (self, external=False): subnodes = self.sublist() if self.iscollapsed(): rect = self.nodelabel.mapRectToParent(self.nodelabel.boundingRect()) else: self.updatecenterbox() rect = self.fggroup.childrenBoundingRect() mainrect = rect.marginsAdded(self.style.banknodemargins) self.mainbox.setRect(mainrect) self.shadowbox.setRect(mainrect) self.selectbox.setRect(mainrect.marginsAdded(self.style.selectmargins)) activerect = mainrect.marginsAdded(self.style.activemargins) self.activebox.setRect(activerect) oldypos = self.centerbox.mapToScene(self.centerbox.pos()).y() self.graphgroup.setPos(-activerect.width()//2-activerect.x(), -activerect.height()//2-activerect.y()) newypos = self.centerbox.mapToScene(self.centerbox.pos()).y() for subnode in subnodes: subnode.yoffset += newypos - oldypos subnode.setY(self.y()) self.prepareGeometryChange() self.rect = self.graphgroup.mapRectToParent(mainrect) if not external: self.view.updatelayout() def setY (self, y): super().setY(y) for subnode in self.sublist(): subnode.setY(y) def contextMenuEvent (self, event): menu = QMenu() if self.isselected(): window = FlGlob.mainwindow menu.addAction(window.actions["collapse"]) if self.isghost(): menu.addAction(window.actions["selectreal"]) menu.addAction(window.actions["copynode"]) menu.addMenu(window.subnodemenu) menu.addMenu(window.addmenu) menu.addAction(window.actions["moveup"]) menu.addAction(window.actions["movedown"]) menu.addAction(window.actions["parentswap"]) menu.addAction(window.actions["unlinknode"]) menu.addAction(window.actions["unlinkstree"]) menu.addAction(window.actions["settemplate"]) menu.addMenu(window.transformmenu) if not menu.isEmpty(): menu.exec_(event.screenPos())
class IDView(QGraphicsView): """Simple extension of QGraphicsView - containing an image and click-to-zoom/unzoom """ def __init__(self, parent, fnames): QGraphicsView.__init__(self) self.parent = parent self.initUI(fnames) def initUI(self, fnames): # Make QGraphicsScene self.scene = QGraphicsScene() # TODO = handle different image sizes. self.images = {} self.imageGItem = QGraphicsItemGroup() self.scene.addItem(self.imageGItem) self.updateImage(fnames) self.setBackgroundBrush(QBrush(Qt.darkCyan)) self.parent.tool = "zoom" self.boxFlag = False self.originPos = QPointF(0, 0) self.currentPos = self.originPos self.boxItem = QGraphicsRectItem() self.boxItem.setPen(QPen(Qt.darkCyan, 1)) self.boxItem.setBrush(QBrush(QColor(0, 255, 0, 64))) def updateImage(self, fnames): """Update the image with that from filename""" for n in self.images: self.imageGItem.removeFromGroup(self.images[n]) self.images[n].setVisible(False) if fnames is not None: x = 0 n = 0 for fn in fnames: self.images[n] = QGraphicsPixmapItem(QPixmap(fn)) self.images[n].setTransformationMode(Qt.SmoothTransformation) self.images[n].setPos(x, 0) self.images[n].setVisible(True) self.scene.addItem(self.images[n]) x += self.images[n].boundingRect().width() + 10 self.imageGItem.addToGroup(self.images[n]) n += 1 # Set sensible sizes and put into the view, and fit view to the image. br = self.imageGItem.boundingRect() self.scene.setSceneRect( 0, 0, max(1000, br.width()), max(1000, br.height()), ) self.setScene(self.scene) self.fitInView(self.imageGItem, Qt.KeepAspectRatio) def deleteRect(self): if self.boxItem.scene() is None: return self.scene.removeItem(self.boxItem) self.parent.rectangle = None def mousePressEvent(self, event): if self.parent.tool == "rect": self.originPos = self.mapToScene(event.pos()) self.currentPos = self.originPos self.boxItem.setRect(QRectF(self.originPos, self.currentPos)) if self.boxItem.scene() is None: self.scene.addItem(self.boxItem) self.boxFlag = True else: super(IDView, self).mousePressEvent(event) def mouseMoveEvent(self, event): if self.parent.tool == "rect" and self.boxFlag: self.currentPos = self.mapToScene(event.pos()) if self.boxItem is None: return else: self.boxItem.setRect(QRectF(self.originPos, self.currentPos)) else: super(IDView, self).mousePressEvent(event) def mouseReleaseEvent(self, event): if self.boxFlag: self.boxFlag = False self.parent.rectangle = self.boxItem.rect() self.parent.whichFile = self.parent.vTW.currentIndex() return """Left/right click to zoom in and out""" if (event.button() == Qt.RightButton) or ( QGuiApplication.queryKeyboardModifiers() == Qt.ShiftModifier): self.scale(0.8, 0.8) else: self.scale(1.25, 1.25) self.centerOn(event.pos()) def resetView(self): """Reset the view to its reasonable initial state.""" self.fitInView(self.imageGItem, Qt.KeepAspectRatio)
def _draw_background(self): bg = QGraphicsRectItem() bg.setRect(-1, -1, SCREEN_WIDTH + 1, SCREEN_HEIGHT + 1) bg.setBrush(QBrush(Qt.black)) self.addItem(bg)
class CalendarDesklet(Desklet): def __init__(self): super().__init__() self.model = CalendarModel() self.cursor_pos = None self.cursor = QGraphicsRectItem(self.root) self.header = QGraphicsSimpleTextItem(self.root) self.weekdays = [] days = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"] for day in days: self.weekdays.append(QGraphicsSimpleTextItem(day, self.root)) self.header_line = QGraphicsLineItem(self.root) self.days = [] for _ in range(0, 6 * 7): self.days.append(QGraphicsSimpleTextItem(self.root)) def next_month(self): self.model.next_month() self.layout() def previous_month(self): self.model.previous_month() self.layout() def set_rect(self, rect): super().set_rect(rect) self.layout() def set_style(self, style): super().set_style(style) font = QFont(style.font) font.setPixelSize(48) self.header.setBrush(style.midcolor) self.header.setFont(font) font = QFont(style.font) font.setPixelSize(32) self.header_line.setPen(style.foreground_color) self.cursor.setBrush(style.midcolor) self.cursor.setPen(QPen(Qt.NoPen)) for widget in self.weekdays: widget.setFont(font) widget.setBrush(style.foreground_color) for widget in self.days: widget.setFont(font) widget.setBrush(self.style.foreground_color) self.layout() def layout(self): cell_width = (self.rect.width()) / 7.0 cell_height = (self.rect.height() - 64) / 7.0 x = self.rect.left() y = self.rect.top() fm = QFontMetrics(self.header.font()) rect = fm.boundingRect(self.header.text()) self.header.setPos(x + self.rect.width() / 2 - rect.width() / 2, y) y += fm.height() for row, day in enumerate(self.weekdays): fm = QFontMetrics(day.font()) rect = fm.boundingRect(day.text()) day.setPos(x + row * cell_width + cell_width / 2 - rect.width() / 2, y) y += fm.height() self.header_line.setLine(x, y, x + self.rect.width() - 3, y) y += 8 for n, widget in enumerate(self.days): col = n % 7 row = n // 7 rect = fm.boundingRect(widget.text()) widget.setPos(x + col * cell_width + cell_width / 2 - rect.width() / 2, y + row * cell_height + cell_height / 2 - fm.height() / 2) # if day.month != self.now.month: # widget.setBrush(self.style.midcolor) # else: if self.cursor_pos is not None: self.cursor.setRect(x + self.cursor_pos[0] * cell_width, y + self.cursor_pos[1] * cell_height, cell_width, cell_height) self.cursor.show() else: self.cursor.hide() def update(self, now): self.model.update(now) # update header self.header.setText( date(self.model.year, self.model.month, 1).strftime("%B %Y")) # calculate the date of the top/left calendar entry current_date = date(self.model.year, self.model.month, 1) current_date = current_date - timedelta(current_date.weekday()) self.cursor_pos = None for n, widget in enumerate(self.days): col = n % 7 row = n // 7 if current_date == self.model.today: self.cursor_pos = (col, row) widget.setText("%d" % current_date.day) self.days[n] = widget current_date += timedelta(days=1) self.layout()
class PianoRoll(QGraphicsScene): '''the piano roll''' noteclicked = pyqtSignal(int,bool) midievent = pyqtSignal(list) measureupdate = pyqtSignal(int) modeupdate = pyqtSignal(str) default_ghost_vel = 100 def __init__(self, time_sig = '4/4', num_measures = 4, quantize_val = '1/8'): QGraphicsScene.__init__(self) self.setBackgroundBrush(QColor(50, 50, 50)) self.notes = [] self.removed_notes = [] self.selected_notes = [] self.piano_keys = [] self.marquee_select = False self.marquee_rect = None self.marquee = None self.ghost_note = None self.ghost_rect = None self.ghost_rect_orig_width = None self.ghost_vel = self.default_ghost_vel self.ignore_mouse_events = False self.insert_mode = False self.velocity_mode = False self.place_ghost = False self.last_mouse_pos = QPointF() ## dimensions self.padding = 2 ## piano dimensions self.note_height = 10 self.start_octave = -2 self.end_octave = 8 self.notes_in_octave = 12 self.total_notes = (self.end_octave - self.start_octave) * self.notes_in_octave + 1 self.piano_height = self.note_height * self.total_notes self.octave_height = self.notes_in_octave * self.note_height self.piano_width = 34 ## height self.header_height = 20 self.total_height = self.piano_height - self.note_height + self.header_height #not sure why note_height is subtracted ## width self.full_note_width = 250 # i.e. a 4/4 note self.snap_value = None self.quantize_val = quantize_val ### dummy vars that will be changed self.time_sig = (0,0) self.measure_width = 0 self.num_measures = 0 self.max_note_length = 0 self.grid_width = 0 self.value_width = 0 self.grid_div = 0 self.piano = None self.header = None self.play_head = None self.setGridDiv() self.default_length = 1. / self.grid_div # ------------------------------------------------------------------------- # Callbacks def movePlayHead(self, transportInfo): ticksPerBeat = transportInfo['ticksPerBeat'] max_ticks = ticksPerBeat * self.time_sig[0] * self.num_measures cur_tick = ticksPerBeat * self.time_sig[0] * transportInfo['bar'] + ticksPerBeat * transportInfo['beat'] + transportInfo['tick'] frac = (cur_tick % max_ticks) / max_ticks self.play_head.setPos(QPointF(frac * self.grid_width, 0)) def setTimeSig(self, time_sig): self.time_sig = time_sig self.measure_width = self.full_note_width * self.time_sig[0]/self.time_sig[1] self.max_note_length = self.num_measures * self.time_sig[0]/self.time_sig[1] self.grid_width = self.measure_width * self.num_measures self.setGridDiv() def setMeasures(self, measures): #try: self.num_measures = float(measures) self.max_note_length = self.num_measures * self.time_sig[0]/self.time_sig[1] self.grid_width = self.measure_width * self.num_measures self.refreshScene() #except: #pass def setDefaultLength(self, length): v = list(map(float, length.split('/'))) if len(v) < 3: self.default_length = v[0] if len(v) == 1 else v[0] / v[1] pos = self.enforce_bounds(self.last_mouse_pos) if self.insert_mode: self.makeGhostNote(pos.x(), pos.y()) def setGridDiv(self, div=None): if not div: div = self.quantize_val try: val = list(map(int, div.split('/'))) if len(val) < 3: self.quantize_val = div self.grid_div = val[0] if len(val)==1 else val[1] self.value_width = self.full_note_width / float(self.grid_div) if self.grid_div else None self.setQuantize(div) self.refreshScene() except ValueError: pass def setQuantize(self, value): val = list(map(float, value.split('/'))) if len(val) == 1: self.quantize(val[0]) self.quantize_val = value elif len(val) == 2: self.quantize(val[0] / val[1]) self.quantize_val = value # ------------------------------------------------------------------------- # Event Callbacks def keyPressEvent(self, event): QGraphicsScene.keyPressEvent(self, event) if event.key() == Qt.Key_F: if not self.insert_mode: # turn off velocity mode self.velocity_mode = False # enable insert mode self.insert_mode = True self.place_ghost = False self.makeGhostNote(self.last_mouse_pos.x(), self.last_mouse_pos.y()) self.modeupdate.emit('insert_mode') else: # turn off insert mode self.insert_mode = False self.place_ghost = False if self.ghost_note is not None: self.removeItem(self.ghost_note) self.ghost_note = None self.modeupdate.emit('') elif event.key() == Qt.Key_D: if not self.velocity_mode: # turn off insert mode self.insert_mode = False self.place_ghost = False if self.ghost_note is not None: self.removeItem(self.ghost_note) self.ghost_note = None # enable velocity mode self.velocity_mode = True self.modeupdate.emit('velocity_mode') else: # turn off velocity mode self.velocity_mode = False self.modeupdate.emit('') elif event.key() == Qt.Key_A: for note in self.notes: if not note.isSelected(): has_unselected = True break else: has_unselected = False # select all notes if has_unselected: for note in self.notes: note.setSelected(True) self.selected_notes = self.notes[:] # unselect all else: for note in self.notes: note.setSelected(False) self.selected_notes = [] elif event.key() in (Qt.Key_Delete, Qt.Key_Backspace): # remove selected notes from our notes list self.notes = [note for note in self.notes if note not in self.selected_notes] # delete the selected notes for note in self.selected_notes: self.removeItem(note) self.midievent.emit(["midievent-remove", note.note[0], note.note[1], note.note[2], note.note[3]]) del note self.selected_notes = [] def mousePressEvent(self, event): QGraphicsScene.mousePressEvent(self, event) # mouse click on left-side piano area if self.piano.contains(event.scenePos()): self.ignore_mouse_events = True return clicked_notes = [] for note in self.notes: if note.pressed or note.back.stretch or note.front.stretch: clicked_notes.append(note) print("clicked_notes", clicked_notes) # mouse click on existing notes if clicked_notes: keep_selection = all(note in self.selected_notes for note in clicked_notes) if keep_selection: for note in self.selected_notes: note.setSelected(True) return for note in self.selected_notes: if note not in clicked_notes: note.setSelected(False) for note in clicked_notes: if note not in self.selected_notes: note.setSelected(True) self.selected_notes = clicked_notes return # mouse click on empty area (no note selected) for note in self.selected_notes: note.setSelected(False) self.selected_notes = [] if event.button() != Qt.LeftButton: return if self.insert_mode: self.place_ghost = True else: self.marquee_select = True self.marquee_rect = QRectF(event.scenePos().x(), event.scenePos().y(), 1, 1) self.marquee = QGraphicsRectItem(self.marquee_rect) self.marquee.setBrush(QColor(255, 255, 255, 100)) self.addItem(self.marquee) def mouseMoveEvent(self, event): QGraphicsScene.mouseMoveEvent(self, event) self.last_mouse_pos = event.scenePos() if self.ignore_mouse_events: return pos = self.enforce_bounds(self.last_mouse_pos) if self.insert_mode: if self.ghost_note is None: self.makeGhostNote(pos.x(), pos.y()) max_x = self.grid_width + self.piano_width # placing note, only width needs updating if self.place_ghost: pos_x = pos.x() min_x = self.ghost_rect.x() + self.ghost_rect_orig_width if pos_x < min_x: pos_x = min_x new_x = self.snap(pos_x) self.ghost_rect.setRight(new_x) self.ghost_note.setRect(self.ghost_rect) #self.adjust_note_vel(event) # ghostnote following mouse around else: pos_x = pos.x() if pos_x + self.ghost_rect.width() >= max_x: pos_x = max_x - self.ghost_rect.width() elif pos_x > self.piano_width + self.ghost_rect.width()*3/4: pos_x -= self.ghost_rect.width()/2 new_x, new_y = self.snap(pos_x, pos.y()) self.ghost_rect.moveTo(new_x, new_y) self.ghost_note.setRect(self.ghost_rect) return if self.marquee_select: marquee_orig_pos = event.buttonDownScenePos(Qt.LeftButton) if marquee_orig_pos.x() < pos.x() and marquee_orig_pos.y() < pos.y(): self.marquee_rect.setBottomRight(pos) elif marquee_orig_pos.x() < pos.x() and marquee_orig_pos.y() > pos.y(): self.marquee_rect.setTopRight(pos) elif marquee_orig_pos.x() > pos.x() and marquee_orig_pos.y() < pos.y(): self.marquee_rect.setBottomLeft(pos) elif marquee_orig_pos.x() > pos.x() and marquee_orig_pos.y() > pos.y(): self.marquee_rect.setTopLeft(pos) self.marquee.setRect(self.marquee_rect) for note in self.selected_notes: note.setSelected(False) self.selected_notes = [] for item in self.collidingItems(self.marquee): if item in self.notes: item.setSelected(True) self.selected_notes.append(item) return if event.buttons() != Qt.LeftButton: return if self.velocity_mode: for note in self.selected_notes: note.updateVelocity(event) return x = y = False for note in self.selected_notes: if note.back.stretch: x = True break for note in self.selected_notes: if note.front.stretch: y = True break for note in self.selected_notes: note.back.stretch = x note.front.stretch = y note.moveEvent(event) def mouseReleaseEvent(self, event): QGraphicsScene.mouseReleaseEvent(self, event) if self.ignore_mouse_events: self.ignore_mouse_events = False return if self.marquee_select: self.marquee_select = False self.removeItem(self.marquee) self.marquee = None if self.insert_mode and self.place_ghost: self.place_ghost = False note_start = self.get_note_start_from_x(self.ghost_rect.x()) note_num = self.get_note_num_from_y(self.ghost_rect.y()) note_length = self.get_note_length_from_x(self.ghost_rect.width()) note = self.drawNote(note_num, note_start, note_length, self.ghost_vel) note.setSelected(True) self.selected_notes.append(note) self.midievent.emit(["midievent-add", note_num, note_start, note_length, self.ghost_vel]) pos = self.enforce_bounds(self.last_mouse_pos) pos_x = pos.x() if pos_x > self.piano_width + self.ghost_rect.width()*3/4: pos_x -= self.ghost_rect.width()/2 self.makeGhostNote(pos_x, pos.y()) for note in self.selected_notes: note.back.stretch = False note.front.stretch = False # ------------------------------------------------------------------------- # Internal Functions def drawHeader(self): self.header = QGraphicsRectItem(0, 0, self.grid_width, self.header_height) #self.header.setZValue(1.0) self.header.setPos(self.piano_width, 0) self.addItem(self.header) def drawPiano(self): piano_keys_width = self.piano_width - self.padding labels = ('B','Bb','A','Ab','G','Gb','F','E','Eb','D','Db','C') black_notes = (2,4,6,9,11) piano_label = QFont() piano_label.setPointSize(6) self.piano = QGraphicsRectItem(0, 0, piano_keys_width, self.piano_height) self.piano.setPos(0, self.header_height) self.addItem(self.piano) key = PianoKeyItem(piano_keys_width, self.note_height, 78, self.piano) label = QGraphicsSimpleTextItem('C9', key) label.setPos(18, 1) label.setFont(piano_label) key.setBrush(QColor(255, 255, 255)) for i in range(self.end_octave - self.start_octave, 0, -1): for j in range(self.notes_in_octave, 0, -1): note = (self.end_octave - i + 3) * 12 - j if j in black_notes: key = PianoKeyItem(piano_keys_width/1.4, self.note_height, note, self.piano) key.setBrush(QColor(0, 0, 0)) key.setZValue(1.0) key.setPos(0, self.note_height * j + self.octave_height * (i - 1)) elif (j - 1) and (j + 1) in black_notes: key = PianoKeyItem(piano_keys_width, self.note_height * 2, note, self.piano) key.setBrush(QColor(255, 255, 255)) key.setPos(0, self.note_height * j + self.octave_height * (i - 1) - self.note_height/2.) elif (j - 1) in black_notes: key = PianoKeyItem(piano_keys_width, self.note_height * 3./2, note, self.piano) key.setBrush(QColor(255, 255, 255)) key.setPos(0, self.note_height * j + self.octave_height * (i - 1) - self.note_height/2.) elif (j + 1) in black_notes: key = PianoKeyItem(piano_keys_width, self.note_height * 3./2, note, self.piano) key.setBrush(QColor(255, 255, 255)) key.setPos(0, self.note_height * j + self.octave_height * (i - 1)) if j == 12: label = QGraphicsSimpleTextItem('{}{}'.format(labels[j - 1], self.end_octave - i + 1), key) label.setPos(18, 6) label.setFont(piano_label) self.piano_keys.append(key) def drawGrid(self): black_notes = [2,4,6,9,11] scale_bar = QGraphicsRectItem(0, 0, self.grid_width, self.note_height, self.piano) scale_bar.setPos(self.piano_width, 0) scale_bar.setBrush(QColor(100,100,100)) clearpen = QPen(QColor(0,0,0,0)) for i in range(self.end_octave - self.start_octave, self.start_octave - self.start_octave, -1): for j in range(self.notes_in_octave, 0, -1): scale_bar = QGraphicsRectItem(0, 0, self.grid_width, self.note_height, self.piano) scale_bar.setPos(self.piano_width, self.note_height * j + self.octave_height * (i - 1)) scale_bar.setPen(clearpen) if j not in black_notes: scale_bar.setBrush(QColor(120,120,120)) else: scale_bar.setBrush(QColor(100,100,100)) measure_pen = QPen(QColor(0, 0, 0, 120), 3) half_measure_pen = QPen(QColor(0, 0, 0, 40), 2) line_pen = QPen(QColor(0, 0, 0, 40)) for i in range(0, int(self.num_measures) + 1): measure = QGraphicsLineItem(0, 0, 0, self.piano_height + self.header_height - measure_pen.width(), self.header) measure.setPos(self.measure_width * i, 0.5 * measure_pen.width()) measure.setPen(measure_pen) if i < self.num_measures: number = QGraphicsSimpleTextItem('%d' % (i + 1), self.header) number.setPos(self.measure_width * i + 5, 2) number.setBrush(Qt.white) for j in self.frange(0, self.time_sig[0]*self.grid_div/self.time_sig[1], 1.): line = QGraphicsLineItem(0, 0, 0, self.piano_height, self.header) line.setZValue(1.0) line.setPos(self.measure_width * i + self.value_width * j, self.header_height) if j == self.time_sig[0]*self.grid_div/self.time_sig[1] / 2.0: line.setPen(half_measure_pen) else: line.setPen(line_pen) def drawPlayHead(self): self.play_head = QGraphicsLineItem(self.piano_width, self.header_height, self.piano_width, self.total_height) self.play_head.setPen(QPen(QColor(255,255,255,50), 2)) self.play_head.setZValue(1.) self.addItem(self.play_head) def refreshScene(self): list(map(self.removeItem, self.notes)) self.selected_notes = [] self.piano_keys = [] self.place_ghost = False if self.ghost_note is not None: self.removeItem(self.ghost_note) self.ghost_note = None self.clear() self.drawPiano() self.drawHeader() self.drawGrid() self.drawPlayHead() for note in self.notes[:]: if note.note[1] >= (self.num_measures * self.time_sig[0]): self.notes.remove(note) self.removed_notes.append(note) #self.midievent.emit(["midievent-remove", note.note[0], note.note[1], note.note[2], note.note[3]]) elif note.note[2] > self.max_note_length: new_note = note.note[:] new_note[2] = self.max_note_length self.notes.remove(note) self.drawNote(new_note[0], new_note[1], self.max_note_length, new_note[3], False) self.midievent.emit(["midievent-remove", note.note[0], note.note[1], note.note[2], note.note[3]]) self.midievent.emit(["midievent-add", new_note[0], new_note[1], new_note[2], new_note[3]]) for note in self.removed_notes[:]: if note.note[1] < (self.num_measures * self.time_sig[0]): self.removed_notes.remove(note) self.notes.append(note) list(map(self.addItem, self.notes)) if self.views(): self.views()[0].setSceneRect(self.itemsBoundingRect()) def clearNotes(self): self.clear() self.notes = [] self.removed_notes = [] self.selected_notes = [] self.drawPiano() self.drawHeader() self.drawGrid() def makeGhostNote(self, pos_x, pos_y): """creates the ghostnote that is placed on the scene before the real one is.""" if self.ghost_note is not None: self.removeItem(self.ghost_note) length = self.full_note_width * self.default_length pos_x, pos_y = self.snap(pos_x, pos_y) self.ghost_vel = self.default_ghost_vel self.ghost_rect = QRectF(pos_x, pos_y, length, self.note_height) self.ghost_rect_orig_width = self.ghost_rect.width() self.ghost_note = QGraphicsRectItem(self.ghost_rect) self.ghost_note.setBrush(QColor(230, 221, 45, 100)) self.addItem(self.ghost_note) def drawNote(self, note_num, note_start, note_length, note_velocity, add=True): """ note_num: midi number, 0 - 127 note_start: 0 - (num_measures * time_sig[0]) so this is in beats note_length: 0 - (num_measures * time_sig[0]/time_sig[1]) this is in measures note_velocity: 0 - 127 """ info = [note_num, note_start, note_length, note_velocity] if not note_start % (self.num_measures * self.time_sig[0]) == note_start: #self.midievent.emit(["midievent-remove", note_num, note_start, note_length, note_velocity]) while not note_start % (self.num_measures * self.time_sig[0]) == note_start: self.setMeasures(self.num_measures+1) self.measureupdate.emit(self.num_measures) self.refreshScene() x_start = self.get_note_x_start(note_start) if note_length > self.max_note_length: note_length = self.max_note_length + 0.25 x_length = self.get_note_x_length(note_length) y_pos = self.get_note_y_pos(note_num) note = NoteItem(self.note_height, x_length, info) note.setPos(x_start, y_pos) self.notes.append(note) if add: self.addItem(note) return note # ------------------------------------------------------------------------- # Helper Functions def frange(self, x, y, t): while x < y: yield x x += t def quantize(self, value): self.snap_value = float(self.full_note_width) * value if value else None def snap(self, pos_x, pos_y = None): if self.snap_value: pos_x = int(round((pos_x - self.piano_width) / self.snap_value)) * self.snap_value + self.piano_width if pos_y is not None: pos_y = int((pos_y - self.header_height) / self.note_height) * self.note_height + self.header_height return (pos_x, pos_y) if pos_y is not None else pos_x def adjust_note_vel(self, event): m_pos = event.scenePos() #bind velocity to vertical mouse movement self.ghost_vel += (event.lastScenePos().y() - m_pos.y())/10 if self.ghost_vel < 0: self.ghost_vel = 0 elif self.ghost_vel > 127: self.ghost_vel = 127 m_width = self.ghost_rect.x() + self.ghost_rect_orig_width if m_pos.x() < m_width: m_pos.setX(m_width) m_new_x = self.snap(m_pos.x()) self.ghost_rect.setRight(m_new_x) self.ghost_note.setRect(self.ghost_rect) def enforce_bounds(self, pos): pos = QPointF(pos) if pos.x() < self.piano_width: pos.setX(self.piano_width) elif pos.x() >= self.grid_width + self.piano_width: pos.setX(self.grid_width + self.piano_width - 1) if pos.y() < self.header_height + self.padding: pos.setY(self.header_height + self.padding) return pos def get_note_start_from_x(self, note_x): return (note_x - self.piano_width) / (self.grid_width / self.num_measures / self.time_sig[0]) def get_note_x_start(self, note_start): return self.piano_width + (self.grid_width / self.num_measures / self.time_sig[0]) * note_start def get_note_x_length(self, note_length): return float(self.time_sig[1]) / self.time_sig[0] * note_length * self.grid_width / self.num_measures def get_note_length_from_x(self, note_x): return float(self.time_sig[0]) / self.time_sig[1] * self.num_measures / self.grid_width * note_x def get_note_y_pos(self, note_num): return self.header_height + self.note_height * (self.total_notes - note_num - 1) def get_note_num_from_y(self, note_y_pos): return -(int((note_y_pos - self.header_height) / self.note_height) - self.total_notes + 1) def move_note(self, old_note, new_note): self.midievent.emit(["midievent-remove", old_note[0], old_note[1], old_note[2], old_note[3]]) self.midievent.emit(["midievent-add", new_note[0], new_note[1], new_note[2], new_note[3]])
def updateLine(self): if self.points is not None: diameter = 2*self.radius rect = QRectF(-self.radius, -self.radius, diameter, diameter) if self.itemPos is not None: # TODO: NaNの時のEllipseItemの挙動を考える point = self.points[self.itemPos] if not isinstance(self.item, self.itemType): print("call") scene = self.scene() if scene is not None: scene.removeItem(self.item) self.item = self.itemType(self) self.item.setZValue(10) self.item.setBrush(self.color) self.item.setRect(rect) self.setItemIsMovable(self.isItemMovable) elif self.drawItemFlag: self.item.show() self.item.setPos(*point) self.item.mouseMoveEvent = self.generateItemMouseMoveEvent(self.item, point) self.item.mousePressEvent = self.generateItemMousePressEvent(self.item, point) self.textItem.setPos(*point) prev_range = range(self.itemPos, -1, -self.markDelta)[1:] next_range = range(self.itemPos, len(self.points), self.markDelta)[1:] num_mark = len(prev_range) + len(next_range) rect_half = QRectF(-self.radius/2, -self.radius/2, diameter/2, diameter/2) while num_mark < len(self.markItemList) and len(self.markItemList)!=0: markItem = self.markItemList.pop() markTextItem = self.markTextItemList.pop() scene = self.scene() if scene is not None: scene.removeItem(markItem) scene.removeItem(markTextItem) current_path = os.path.dirname(os.path.realpath(__file__)) while len(self.markItemList) < num_mark: # TODO: 目盛りを矢印に. # markItem = QGraphicsSvgItem(os.path.join(current_path, "svg", "small_arrow.svg"), self) markItem = QGraphicsRectItem(self) markItem.setBrush(Qt.black) markItem.setRect(rect_half) markItem.setZValue(9) # markItem.setFlags(QGraphicsItem.ItemIgnoresParentOpacity) # markItem.setOpacity(1) # print(markItem.boundingRect()) # xlate = markItem.boundingRect().center() # t = QTransform() # # t.translate(xlate.x(), xlate.y()) # t.rotate(90) # t.scale(0.03, 0.03) # # t.translate(-xlate.x(), -xlate.y()) # markItem.setTransform(t) self.markItemList.append(markItem) markTextItem = GraphicsTextItemWithBackground(self) markTextItem.setBackgroundColor(Qt.black) markTextItem.setDefaultTextColor(Qt.white) self.markTextItemList.append(markTextItem) for markItem, markTextItem, index in zip(self.markItemList, self.markTextItemList, chain(prev_range, next_range)): markItem.setPos(*self.points[index]) markTextItem.setPos(*self.points[index]) markTextItem.setPlainText(str(int((index-self.itemPos)/self.markDelta))) if self.drawMarkItemFlag: markItem.show() markTextItem.show() else: markItem.hide() markTextItem.hide() else: self.item.hide() self.textItem.hide() for item, textItem in zip(self.markItemList, self.markTextItemList): item.hide() textItem.hide() self.update()
class PianoRoll(QGraphicsScene): '''the piano roll''' midievent = pyqtSignal(list) measureupdate = pyqtSignal(int) modeupdate = pyqtSignal(str) def __init__(self, time_sig = '4/4', num_measures = 4, quantize_val = '1/8'): QGraphicsScene.__init__(self) self.setBackgroundBrush(QColor(50, 50, 50)) self.mousePos = QPointF() self.notes = [] self.selected_notes = [] self.piano_keys = [] self.marquee_select = False self.insert_mode = False self.velocity_mode = False self.place_ghost = False self.ghost_note = None self.default_ghost_vel = 100 self.ghost_vel = self.default_ghost_vel ## dimensions self.padding = 2 ## piano dimensions self.note_height = 10 self.start_octave = -2 self.end_octave = 8 self.notes_in_octave = 12 self.total_notes = (self.end_octave - self.start_octave) \ * self.notes_in_octave + 1 self.piano_height = self.note_height * self.total_notes self.octave_height = self.notes_in_octave * self.note_height self.piano_width = 34 ## height self.header_height = 20 self.total_height = self.piano_height - self.note_height + self.header_height #not sure why note_height is subtracted ## width self.full_note_width = 250 # i.e. a 4/4 note self.snap_value = None self.quantize_val = quantize_val ### dummy vars that will be changed self.time_sig = 0 self.measure_width = 0 self.num_measures = 0 self.max_note_length = 0 self.grid_width = 0 self.value_width = 0 self.grid_div = 0 self.piano = None self.header = None self.play_head = None self.setTimeSig(time_sig) self.setMeasures(num_measures) self.setGridDiv() self.default_length = 1. / self.grid_div # ------------------------------------------------------------------------- # Callbacks def movePlayHead(self, transport_info): # TODO: need conversion between frames and PPQ x = 105. # works for 120bpm total_duration = self.time_sig[0] * self.num_measures * x pos = transport_info['frame'] / x frac = (pos % total_duration) / total_duration self.play_head.setPos(QPointF(frac * self.grid_width, 0)) def setTimeSig(self, time_sig): try: new_time_sig = list(map(float, time_sig.split('/'))) if len(new_time_sig)==2: self.time_sig = new_time_sig self.measure_width = self.full_note_width * self.time_sig[0]/self.time_sig[1] self.max_note_length = self.num_measures * self.time_sig[0]/self.time_sig[1] self.grid_width = self.measure_width * self.num_measures self.setGridDiv() except ValueError: pass def setMeasures(self, measures): try: self.num_measures = float(measures) self.max_note_length = self.num_measures * self.time_sig[0]/self.time_sig[1] self.grid_width = self.measure_width * self.num_measures self.refreshScene() except: pass def setDefaultLength(self, length): try: v = list(map(float, length.split('/'))) if len(v) < 3: self.default_length = \ v[0] if len(v)==1 else \ v[0] / v[1] pos = self.enforce_bounds(self.mousePos) if self.insert_mode: self.makeGhostNote(pos.x(), pos.y()) except ValueError: pass def setGridDiv(self, div=None): if not div: div = self.quantize_val try: val = list(map(int, div.split('/'))) if len(val) < 3: self.quantize_val = div self.grid_div = val[0] if len(val)==1 else val[1] self.value_width = self.full_note_width / float(self.grid_div) if self.grid_div else None self.setQuantize(div) self.refreshScene() except ValueError: pass def setQuantize(self, value): try: val = list(map(float, value.split('/'))) if len(val) == 1: self.quantize(val[0]) self.quantize_val = value elif len(val) == 2: self.quantize(val[0] / val[1]) self.quantize_val = value except ValueError: pass # ------------------------------------------------------------------------- # Event Callbacks def keyPressEvent(self, event): QGraphicsScene.keyPressEvent(self, event) if event.key() == Qt.Key_F: if not self.insert_mode: self.velocity_mode = False self.insert_mode = True self.makeGhostNote(self.mousePos.x(), self.mousePos.y()) self.modeupdate.emit('insert_mode') elif self.insert_mode: self.insert_mode = False if self.place_ghost: self.place_ghost = False self.removeItem(self.ghost_note) self.ghost_note = None self.modeupdate.emit('') elif event.key() == Qt.Key_D: if self.velocity_mode: self.velocity_mode = False self.modeupdate.emit('') else: if self.insert_mode: self.removeItem(self.ghost_note) self.ghost_note = None self.insert_mode = False self.place_ghost = False self.velocity_mode = True self.modeupdate.emit('velocity_mode') elif event.key() == Qt.Key_A: if all((note.isSelected() for note in self.notes)): for note in self.notes: note.setSelected(False) self.selected_notes = [] else: for note in self.notes: note.setSelected(True) self.selected_notes = self.notes[:] elif event.key() in (Qt.Key_Delete, Qt.Key_Backspace): self.notes = [note for note in self.notes if note not in self.selected_notes] for note in self.selected_notes: self.removeItem(note) self.midievent.emit(["midievent-remove", note.note[0], note.note[1], note.note[2], note.note[3]]) del note self.selected_notes = [] def mousePressEvent(self, event): QGraphicsScene.mousePressEvent(self, event) if not (any(key.pressed for key in self.piano_keys) or any(note.pressed for note in self.notes)): for note in self.selected_notes: note.setSelected(False) self.selected_notes = [] if event.button() == Qt.LeftButton: if self.insert_mode: self.place_ghost = True else: self.marquee_select = True self.marquee_rect = QRectF(event.scenePos().x(), event.scenePos().y(), 1, 1) self.marquee = QGraphicsRectItem(self.marquee_rect) self.marquee.setBrush(QColor(255, 255, 255, 100)) self.addItem(self.marquee) else: for s_note in self.notes: if s_note.pressed and s_note in self.selected_notes: break elif s_note.pressed and s_note not in self.selected_notes: for note in self.selected_notes: note.setSelected(False) self.selected_notes = [s_note] break for note in self.selected_notes: if not self.velocity_mode: note.mousePressEvent(event) def mouseMoveEvent(self, event): QGraphicsScene.mouseMoveEvent(self, event) self.mousePos = event.scenePos() if not (any((key.pressed for key in self.piano_keys))): m_pos = event.scenePos() if self.insert_mode and self.place_ghost: #placing a note m_width = self.ghost_rect.x() + self.ghost_rect_orig_width if m_pos.x() > m_width: m_new_x = self.snap(m_pos.x()) self.ghost_rect.setRight(m_new_x) self.ghost_note.setRect(self.ghost_rect) #self.adjust_note_vel(event) else: m_pos = self.enforce_bounds(m_pos) if self.insert_mode: #ghostnote follows mouse around (m_new_x, m_new_y) = self.snap(m_pos.x(), m_pos.y()) self.ghost_rect.moveTo(m_new_x, m_new_y) try: self.ghost_note.setRect(self.ghost_rect) except RuntimeError: self.ghost_note = None self.makeGhostNote(m_new_x, m_new_y) elif self.marquee_select: marquee_orig_pos = event.buttonDownScenePos(Qt.LeftButton) if marquee_orig_pos.x() < m_pos.x() and marquee_orig_pos.y() < m_pos.y(): self.marquee_rect.setBottomRight(m_pos) elif marquee_orig_pos.x() < m_pos.x() and marquee_orig_pos.y() > m_pos.y(): self.marquee_rect.setTopRight(m_pos) elif marquee_orig_pos.x() > m_pos.x() and marquee_orig_pos.y() < m_pos.y(): self.marquee_rect.setBottomLeft(m_pos) elif marquee_orig_pos.x() > m_pos.x() and marquee_orig_pos.y() > m_pos.y(): self.marquee_rect.setTopLeft(m_pos) self.marquee.setRect(self.marquee_rect) self.selected_notes = [] for item in self.collidingItems(self.marquee): if item in self.notes: self.selected_notes.append(item) for note in self.notes: if note in self.selected_notes: note.setSelected(True) else: note.setSelected(False) elif self.velocity_mode: if Qt.LeftButton == event.buttons(): for note in self.selected_notes: note.updateVelocity(event) elif not self.marquee_select: #move selected if Qt.LeftButton == event.buttons(): x = y = False if any(note.back.stretch for note in self.selected_notes): x = True elif any(note.front.stretch for note in self.selected_notes): y = True for note in self.selected_notes: note.back.stretch = x note.front.stretch = y note.moveEvent(event) def mouseReleaseEvent(self, event): if not (any((key.pressed for key in self.piano_keys)) or any((note.pressed for note in self.notes))): if event.button() == Qt.LeftButton: if self.place_ghost and self.insert_mode: self.place_ghost = False note_start = self.get_note_start_from_x(self.ghost_rect.x()) note_num = self.get_note_num_from_y(self.ghost_rect.y()) note_length = self.get_note_length_from_x(self.ghost_rect.width()) self.drawNote(note_num, note_start, note_length, self.ghost_vel) self.midievent.emit(["midievent-add", note_num, note_start, note_length, self.ghost_vel]) self.makeGhostNote(self.mousePos.x(), self.mousePos.y()) elif self.marquee_select: self.marquee_select = False self.removeItem(self.marquee) elif not self.marquee_select: for note in self.selected_notes: old_info = note.note[:] note.mouseReleaseEvent(event) if self.velocity_mode: note.setSelected(True) if not old_info == note.note: self.midievent.emit(["midievent-remove", old_info[0], old_info[1], old_info[2], old_info[3]]) self.midievent.emit(["midievent-add", note.note[0], note.note[1], note.note[2], note.note[3]]) # ------------------------------------------------------------------------- # Internal Functions def drawHeader(self): self.header = QGraphicsRectItem(0, 0, self.grid_width, self.header_height) #self.header.setZValue(1.0) self.header.setPos(self.piano_width, 0) self.addItem(self.header) def drawPiano(self): piano_keys_width = self.piano_width - self.padding labels = ('B','Bb','A','Ab','G','Gb','F','E','Eb','D','Db','C') black_notes = (2,4,6,9,11) piano_label = QFont() piano_label.setPointSize(6) self.piano = QGraphicsRectItem(0, 0, piano_keys_width, self.piano_height) self.piano.setPos(0, self.header_height) self.addItem(self.piano) key = PianoKeyItem(piano_keys_width, self.note_height, self.piano) label = QGraphicsSimpleTextItem('C8', key) label.setPos(18, 1) label.setFont(piano_label) key.setBrush(QColor(255, 255, 255)) for i in range(self.end_octave - self.start_octave, self.start_octave - self.start_octave, -1): for j in range(self.notes_in_octave, 0, -1): if j in black_notes: key = PianoKeyItem(piano_keys_width/1.4, self.note_height, self.piano) key.setBrush(QColor(0, 0, 0)) key.setZValue(1.0) key.setPos(0, self.note_height * j + self.octave_height * (i - 1)) elif (j - 1) and (j + 1) in black_notes: key = PianoKeyItem(piano_keys_width, self.note_height * 2, self.piano) key.setBrush(QColor(255, 255, 255)) key.setPos(0, self.note_height * j + self.octave_height * (i - 1) - self.note_height/2.) elif (j - 1) in black_notes: key = PianoKeyItem(piano_keys_width, self.note_height * 3./2, self.piano) key.setBrush(QColor(255, 255, 255)) key.setPos(0, self.note_height * j + self.octave_height * (i - 1) - self.note_height/2.) elif (j + 1) in black_notes: key = PianoKeyItem(piano_keys_width, self.note_height * 3./2, self.piano) key.setBrush(QColor(255, 255, 255)) key.setPos(0, self.note_height * j + self.octave_height * (i - 1)) if j == 12: label = QGraphicsSimpleTextItem('{}{}'.format(labels[j - 1], self.end_octave - i), key ) label.setPos(18, 6) label.setFont(piano_label) self.piano_keys.append(key) def drawGrid(self): black_notes = [2,4,6,9,11] scale_bar = QGraphicsRectItem(0, 0, self.grid_width, self.note_height, self.piano) scale_bar.setPos(self.piano_width, 0) scale_bar.setBrush(QColor(100,100,100)) clearpen = QPen(QColor(0,0,0,0)) for i in range(self.end_octave - self.start_octave, self.start_octave - self.start_octave, -1): for j in range(self.notes_in_octave, 0, -1): scale_bar = QGraphicsRectItem(0, 0, self.grid_width, self.note_height, self.piano) scale_bar.setPos(self.piano_width, self.note_height * j + self.octave_height * (i - 1)) scale_bar.setPen(clearpen) if j not in black_notes: scale_bar.setBrush(QColor(120,120,120)) else: scale_bar.setBrush(QColor(100,100,100)) measure_pen = QPen(QColor(0, 0, 0, 120), 3) half_measure_pen = QPen(QColor(0, 0, 0, 40), 2) line_pen = QPen(QColor(0, 0, 0, 40)) for i in range(0, int(self.num_measures) + 1): measure = QGraphicsLineItem(0, 0, 0, self.piano_height + self.header_height - measure_pen.width(), self.header) measure.setPos(self.measure_width * i, 0.5 * measure_pen.width()) measure.setPen(measure_pen) if i < self.num_measures: number = QGraphicsSimpleTextItem('%d' % (i + 1), self.header) number.setPos(self.measure_width * i + 5, 2) number.setBrush(Qt.white) for j in self.frange(0, self.time_sig[0]*self.grid_div/self.time_sig[1], 1.): line = QGraphicsLineItem(0, 0, 0, self.piano_height, self.header) line.setZValue(1.0) line.setPos(self.measure_width * i + self.value_width * j, self.header_height) if j == self.time_sig[0]*self.grid_div/self.time_sig[1] / 2.0: line.setPen(half_measure_pen) else: line.setPen(line_pen) def drawPlayHead(self): self.play_head = QGraphicsLineItem(self.piano_width, self.header_height, self.piano_width, self.total_height) self.play_head.setPen(QPen(QColor(255,255,255,50), 2)) self.play_head.setZValue(1.) self.addItem(self.play_head) def refreshScene(self): list(map(self.removeItem, self.notes)) self.selected_notes = [] self.piano_keys = [] self.clear() self.drawPiano() self.drawHeader() self.drawGrid() self.drawPlayHead() for note in self.notes[:]: if note.note[1] >= (self.num_measures * self.time_sig[0]): self.notes.remove(note) self.midievent.emit(["midievent-remove", note.note[0], note.note[1], note.note[2], note.note[3]]) elif note.note[2] > self.max_note_length: new_note = note.note[:] new_note[2] = self.max_note_length self.notes.remove(note) self.drawNote(new_note[0], new_note[1], self.max_note_length, new_note[3], False) self.midievent.emit(["midievent-remove", note.note[0], note.note[1], note.note[2], note.note[3]]) self.midievent.emit(["midievent-add", new_note[0], new_note[1], new_note[2], new_note[3]]) list(map(self.addItem, self.notes)) if self.views(): self.views()[0].setSceneRect(self.itemsBoundingRect()) def clearNotes(self): self.clear() self.notes = [] self.selected_notes = [] self.drawPiano() self.drawHeader() self.drawGrid() def makeGhostNote(self, pos_x, pos_y): """creates the ghostnote that is placed on the scene before the real one is.""" if self.ghost_note: self.removeItem(self.ghost_note) length = self.full_note_width * self.default_length (start, note) = self.snap(pos_x, pos_y) self.ghost_vel = self.default_ghost_vel self.ghost_rect = QRectF(start, note, length, self.note_height) self.ghost_rect_orig_width = self.ghost_rect.width() self.ghost_note = QGraphicsRectItem(self.ghost_rect) self.ghost_note.setBrush(QColor(230, 221, 45, 100)) self.addItem(self.ghost_note) def drawNote(self, note_num, note_start=None, note_length=None, note_velocity=None, add=True): """ note_num: midi number, 0 - 127 note_start: 0 - (num_measures * time_sig[0]) so this is in beats note_length: 0 - (num_measures * time_sig[0]/time_sig[1]) this is in measures note_velocity: 0 - 127 """ info = [note_num, note_start, note_length, note_velocity] if not note_start % (self.num_measures * self.time_sig[0]) == note_start: #self.midievent.emit(["midievent-remove", note_num, note_start, note_length, note_velocity]) while not note_start % (self.num_measures * self.time_sig[0]) == note_start: self.setMeasures(self.num_measures+1) self.measureupdate.emit(self.num_measures) self.refreshScene() x_start = self.get_note_x_start(note_start) if note_length > self.max_note_length: note_length = self.max_note_length + 0.25 x_length = self.get_note_x_length(note_length) y_pos = self.get_note_y_pos(note_num) note = NoteItem(self.note_height, x_length, info) note.setPos(x_start, y_pos) self.notes.append(note) if add: self.addItem(note) # ------------------------------------------------------------------------- # Helper Functions def frange(self, x, y, t): while x < y: yield x x += t def quantize(self, value): self.snap_value = float(self.full_note_width) * value if value else None def snap(self, pos_x, pos_y = None): if self.snap_value: pos_x = int(round((pos_x - self.piano_width) / self.snap_value)) \ * self.snap_value + self.piano_width if pos_y: pos_y = int((pos_y - self.header_height) / self.note_height) \ * self.note_height + self.header_height return (pos_x, pos_y) if pos_y else pos_x def adjust_note_vel(self, event): m_pos = event.scenePos() #bind velocity to vertical mouse movement self.ghost_vel += (event.lastScenePos().y() - m_pos.y())/10 if self.ghost_vel < 0: self.ghost_vel = 0 elif self.ghost_vel > 127: self.ghost_vel = 127 m_width = self.ghost_rect.x() + self.ghost_rect_orig_width if m_pos.x() < m_width: m_pos.setX(m_width) m_new_x = self.snap(m_pos.x()) self.ghost_rect.setRight(m_new_x) self.ghost_note.setRect(self.ghost_rect) def enforce_bounds(self, pos): if pos.x() < self.piano_width: pos.setX(self.piano_width) elif pos.x() > self.grid_width + self.piano_width: pos.setX(self.grid_width + self.piano_width) if pos.y() < self.header_height + self.padding: pos.setY(self.header_height + self.padding) return pos def get_note_start_from_x(self, note_x): return (note_x - self.piano_width) / (self.grid_width / self.num_measures / self.time_sig[0]) def get_note_x_start(self, note_start): return self.piano_width + \ (self.grid_width / self.num_measures / self.time_sig[0]) * note_start def get_note_x_length(self, note_length): return float(self.time_sig[1]) / self.time_sig[0] * note_length * self.grid_width / self.num_measures def get_note_length_from_x(self, note_x): return float(self.time_sig[0]) / self.time_sig[1] * self.num_measures / self.grid_width \ * note_x def get_note_y_pos(self, note_num): return self.header_height + self.note_height * (self.total_notes - note_num - 1) def get_note_num_from_y(self, note_y_pos): return -(((note_y_pos - self.header_height) / self.note_height) - self.total_notes + 1)
class NodeItem(QGraphicsItem): def __init__ (self, nodeobj, parent=None, view=None, state=1): super().__init__() self.edge = None self.linkIDs = None self.children = None self.childpos = None self.nodeobj = nodeobj self.style = FlGlob.mainwindow.style self.view = weakref.proxy(view) self.refID = parent.realid() if parent is not None else None self.state = state self.setrank(parent) self.setCursor(Qt.ArrowCursor) self.yoffset = 0 self.graphicsetup() self.setstate(state) def id (self): return (self.refID, self.nodeobj.ID) def realid (self): return self.nodeobj.ID def childlist (self, generate=False): ID = self.nodeobj.ID itemtable = self.view.itemtable if self.state == 1 and ID in itemtable and not self.iscollapsed(): if self.children and self.nodeobj.linkIDs == self.linkIDs and None not in [c() for c in self.children]: ret = self.children else: children = [] for child in self.nodeobj.linkIDs: if child in itemtable[ID]: item = itemtable[ID][child] else: continue children.append(weakref.ref(item)) self.linkIDs = self.nodeobj.linkIDs.copy() self.children = children ret = children else: ret = [] if generate: x = self.x() y = self.y() self.childpos = [] for target in ret: t = target() self.childpos.append((t.x()+t.boundingRect().left()-self.style.activemargin-x, t.y()-y)) if self.edge: if self.childpos != self.edge.childpos: self.edge.prepareGeometryChange() self.edge.sourceright = self.boundingRect().right() self.edge.update(self.edge.boundingRect()) return ret def setedge (self, edge): self.edge = edge edge.setX(self.x()) def setactive (self, active): if active: self.activebox.show() self.mainbox.setBrush(QBrush(self.altcolor)) else: self.activebox.hide() self.mainbox.setBrush(QBrush(self.maincolor)) def setselected (self, selected): if selected: self.selectbox.show() else: self.selectbox.hide() def setstate (self, state): self.state = state if state == 1: # normal self.show() self.graphgroup.setOpacity(1) self.shadowbox.show() elif state == 0: # ghost self.show() self.graphgroup.setOpacity(0.7) self.shadowbox.hide() elif state == -1: # hidden self.hide() def setplaymode (self, playmode): if playmode: self.setOpacity(0.5) else: self.setOpacity(1) def setY (self, y): parent = self.view.itembyID(self.refID) y += self.getyoffset() if self.edge is not None: self.edge.setY(y) super().setY(y) def setrank (self, parent): if parent is None: return if self.issubnode(): x = parent.x() self.setX(x) else: x = parent.x()+self.style.rankwidth self.setX(x) self.nudgechildren() if self.edge is not None: self.edge.setX(x) def nudgechildren (self): for child in self.childlist(): child().setrank(self) def getyoffset (self): if self.nodeobj.nodebank == -1: return self.yoffset else: return self.view.itembyID(self.refID).getyoffset() + self.yoffset def hide (self): super().hide() if self.edge: self.edge.hide() def show (self): super().show() if self.edge: self.edge.show() def issubnode (self): return self.nodeobj.nodebank is not -1 def isghost (self): return not self.state def realnode (self): return self.view.itembyID(self.nodeobj.ID) def isactive (self): return self.view.activenode is self def isselected (self): return self.view.selectednode is self def iscollapsed (self): return self.id() in self.view.collapsednodes def y_top (self): return self.y() - self.boundingRect().height()//2 def y_bottom (self): return self.y() + self.boundingRect().height()//2 def bulkshift (self, children, diff): self.setY(self.y() + diff) if children is None: children = [c() for c in self.childlist()] for child in children: child.bulkshift(None, diff) def treeposition (self, ranks=None): if ranks is None: ranks = dict() localranks = dict() children = [c() for c in self.childlist()] for child in children: localranks = child.treeposition(localranks) rank = self.x() // self.style.rankwidth if children: top = children[0].y_top() bottom = children[-1].y_bottom() self.setY((top+bottom)//2) localranks[rank] = [self.y_top, self.y_bottom] streeshift = None for r in localranks: if r in ranks: rankshift = ranks[r][1]() + self.style.rowgap - localranks[r][0]() if streeshift is None or rankshift > streeshift: streeshift = rankshift ranks[r][1] = localranks[r][1] else: ranks[r] = localranks[r] if streeshift: self.bulkshift(children, streeshift) return ranks def siblings (self): if self.refID is None: return None parent = self.view.nodecontainer.nodes[self.refID] if self.issubnode(): return parent.subnodes else: return parent.linkIDs def siblingabove (self): sibs = self.siblings() if sibs is None or self.nodeobj.ID not in sibs: return None myindex = sibs.index(self.nodeobj.ID) if myindex: sibID = (self.refID, sibs[myindex-1]) return self.view.itembyfullID(sibID) else: return None def siblingbelow (self): sibs = self.siblings() if sibs is None or self.nodeobj.ID not in sibs: return None myindex = sibs.index(self.nodeobj.ID) if len(sibs) > myindex+1: sibID = (self.refID, sibs[myindex+1]) return self.view.itembyfullID(sibID) else: return None def subtreesize (self, depth=-1): """Find vertical extents of a subtree. Returns min/max y coordinates up to given depth (negative depth means whole subtree).""" # calculate child positions for EgdeItem only once when calculating scenerect if depth<0: generate = True else: generate = False children = [c() for c in self.childlist(generate=generate)] maxdepth = abs(depth) if children and depth: nextdepth = depth-1 ymin = self.y_top() ymax = self.y_bottom() for child in children: top, bottom, depth = child.subtreesize(nextdepth) ymin = min(ymin, top) ymax = max(ymax, bottom) maxdepth = max(maxdepth, depth) else: ymin = self.y_top() ymax = self.y_bottom() return ymin, ymax, maxdepth def boundingRect (self): return self.rect def paint (self, painter, style, widget): pass def pixmap (self, path): return QPixmap(path).scaledToWidth(self.style.boldheight, Qt.SmoothTransformation) def graphicsetup (self): lightbrush = QBrush(FlPalette.light) mainbrush = QBrush(self.maincolor) altbrush = QBrush(self.altcolor) nopen = QPen(0) viewport = self.view.viewport() self.graphgroup = QGraphicsItemGroup(self) self.fggroup = QGraphicsItemGroup(self) self.shadowbox = QGraphicsRectItem(self) self.shadowbox.setCacheMode(QGraphicsItem.DeviceCoordinateCache) self.shadowbox.setBrush(FlPalette.dark) self.shadowbox.setPen(nopen) self.shadowbox.setPos(*(self.style.shadowoffset,)*2) self.graphgroup.addToGroup(self.shadowbox) self.activebox = QGraphicsRectItem(self) self.activebox.setCacheMode(QGraphicsItem.DeviceCoordinateCache) activepen = QPen(self.maincolor, self.style.selectmargin, join=Qt.MiterJoin) self.activebox.setPen(activepen) self.activebox.hide() self.graphgroup.addToGroup(self.activebox) self.selectbox = QGraphicsRectItem(self) self.selectbox.setCacheMode(QGraphicsItem.DeviceCoordinateCache) selectpen = QPen(FlPalette.light, self.style.selectmargin, join=Qt.MiterJoin) self.selectbox.setPen(selectpen) self.selectbox.hide() self.graphgroup.addToGroup(self.selectbox) self.mainbox = QGraphicsRectItem(self) self.mainbox.setCacheMode(QGraphicsItem.DeviceCoordinateCache) self.mainbox.setBrush(mainbrush) self.mainbox.setPen(nopen) self.graphgroup.addToGroup(self.mainbox) self.nodelabel = QGraphicsSimpleTextItemCond(self, viewport) self.nodelabel.setBrush(lightbrush) self.nodelabel.setFont(self.style.boldfont) self.nodelabel.setText(self.label % self.realid()) self.nodelabel.setPos(self.style.itemmargin, self.style.itemmargin) self.fggroup.addToGroup(self.nodelabel) self.icon = self.pixmap("images/blank.png") self.iwidth = self.icon.width() self.iconx = self.style.nodetextwidth self.condicon = QGraphicsPixmapItemCond(self.icon, self, viewport) self.condicon.setPos(self.iconx-self.iwidth, self.style.itemmargin) self.iconx = self.condicon.x() self.fggroup.addToGroup(self.condicon) self.randicon = QGraphicsPixmapItemCond(self.icon, self, viewport) self.randicon.setPos(self.iconx-self.style.itemmargin-self.iwidth, self.style.itemmargin) self.iconx = self.randicon.x() self.fggroup.addToGroup(self.randicon) self.exiticon = QGraphicsPixmapItemCond(self.icon, self, viewport) self.exiticon.setPos(self.iconx-self.style.itemmargin-self.iwidth, self.style.itemmargin) self.iconx = self.exiticon.x() self.fggroup.addToGroup(self.exiticon) self.entericon = QGraphicsPixmapItemCond(self.icon, self, viewport) self.entericon.setPos(self.iconx-self.style.itemmargin-self.iwidth, self.style.itemmargin) self.iconx = self.entericon.x() self.fggroup.addToGroup(self.entericon) self.persisticon = QGraphicsPixmapItemCond(self.icon, self, viewport) self.persisticon.setPos(self.iconx-self.style.itemmargin-self.iwidth, self.style.itemmargin) self.iconx = self.persisticon.x() self.fggroup.addToGroup(self.persisticon) self.comment = QGraphicsTextItemCond(self, viewport) self.comment.setTextWidth(self.style.nodetextwidth) self.comment.setDefaultTextColor(FlPalette.light) self.comment.setPos(0, self.nodelabel.y()+self.nodelabel.boundingRect().height()+self.style.itemmargin) self.fggroup.addToGroup(self.comment) self.graphgroup.addToGroup(self.fggroup) self.view.nodedocs[self.realid()]["comment"].contentsChanged.connect(self.updatecomment) self.updatecondition() self.updateenterscripts() self.updateexitscripts() self.updaterandweight() self.updatepersistence() # Never call updatelayout() from here (or any inheritable reimplementation)! def collapse (self, collapse): for item in self.fggroup.childItems(): if item is not self.nodelabel: if collapse: item.hide() else: item.show() self.updatelayout() def updatecondition (self): icons = {True: "key", False: "blank"} pixmap = self.pixmap("images/%s.png" % icons[self.nodeobj.hascond()]) self.condicon.setPixmap(pixmap) if self.nodeobj.hascond(): self.condicon.setToolTip("Condition") else: self.condicon.setToolTip("") def updateenterscripts (self): icons = {True: "script-enter", False: "blank"} pixmap = self.pixmap("images/%s.png" % icons[self.nodeobj.hasenterscripts()]) self.entericon.setPixmap(pixmap) if self.nodeobj.hasenterscripts(): self.entericon.setToolTip("Enter Scripts") else: self.entericon.setToolTip("") def updateexitscripts (self): icons = {True: "script-exit", False: "blank"} pixmap = self.pixmap("images/%s.png" % icons[self.nodeobj.hasexitscripts()]) self.exiticon.setPixmap(pixmap) if self.nodeobj.hasexitscripts(): self.exiticon.setToolTip("Exit Scripts") else: self.exiticon.setToolTip("") def updaterandweight (self): icons = {True: "dice", False: "blank"} pixmap = self.pixmap("images/%s.png" % icons[bool(self.nodeobj.randweight)]) self.randicon.setPixmap(pixmap) if self.nodeobj.randweight: self.randicon.setToolTip("Random Weight: %s" % self.nodeobj.randweight) else: self.randicon.setToolTip("") def updatepersistence (self): icons = {"Mark": "mark", "OncePerConv": "once", "OnceEver": "onceever", "": "blank"} pixmap = self.pixmap("images/%s.png" % icons[self.nodeobj.persistence]) self.persisticon.setPixmap(pixmap) if self.nodeobj.persistence: self.persisticon.setToolTip("Persistence: %s" % self.nodeobj.persistence) else: self.persisticon.setToolTip("") def updatecomment (self): self.fggroup.removeFromGroup(self.comment) contents = self.view.nodedocs[self.realid()]["comment"].toPlainText() if not contents: self.comment.hide() else: self.comment.show() self.comment.setPlainText(contents) self.fggroup.addToGroup(self.comment) self.updatelayout() def updatelayout (self): if self.iscollapsed(): rect = self.nodelabel.mapRectToParent(self.nodelabel.boundingRect()) else: rect = self.fggroup.childrenBoundingRect() mainrect = rect.marginsAdded(self.style.nodemargins) self.mainbox.setRect(mainrect) self.shadowbox.setRect(mainrect) self.selectbox.setRect(mainrect.marginsAdded(self.style.selectmargins)) activerect = mainrect.marginsAdded(self.style.activemargins) self.activebox.setRect(activerect) self.graphgroup.setPos(-activerect.width()//2-activerect.x(), -activerect.height()//2-activerect.y()) self.prepareGeometryChange() self.rect = self.graphgroup.mapRectToParent(mainrect) self.view.updatelayout() def mouseDoubleClickEvent (self, event): super().mouseDoubleClickEvent(event) event.accept() if event.button() == Qt.LeftButton: self.view.setactivenode(self) def mousePressEvent (self, event): super().mousePressEvent(event) if event.button() & (Qt.LeftButton | Qt.RightButton) : self.view.setselectednode(self) event.accept() def __repr__ (self): return "<%s %s>" % (type(self).__name__, self.id())
def add_chart(self, text, data, eq=False): items = [] text_item = QGraphicsTextItem(text) text_item.setFont(ComparisionChart.arial_font) text_item.setPos(0,0) items.append(text_item) next_y = text_item.boundingRect().height()+5 if not data: text_item = QGraphicsTextItem("prazno") text_item.setFont(ComparisionChart.font) text_item.setPos(0,next_y) items.append(text_item) group = self.scene.createItemGroup(items) return group item_max = 0 before_max = len("before") after_max = len("after") def fmt_num(number): return "{:.2f}".format(number) def fmt_num_align(number, width): fmt = "{:^%d.2f}" % width return fmt.format(number) for item, before, after in data: item_max = max(item_max, len(item)) before_max = max(before_max, len(fmt_num(before))) after_max = max(after_max, len(fmt_num(after))) before_larger = False if before > after: before_larger = True item_max_s= item_max*9 before_max_s= before_max*9 after_max_s= after_max*9 full_size = before_max_s+5+after_max_s start_chart = item_max_s+5 for item, before, after in data: if before_larger: large = before small = after else: large = after small = before if not eq: rect_item = QGraphicsRectItem() old_height = 0.8*text_item.boundingRect().height() rect_item.setRect(start_chart,next_y, full_size, old_height) if before_larger: rect_item.setBrush(ComparisionChart.color1_brush) else: rect_item.setBrush(ComparisionChart.color2_brush) rect_item.setPen(ComparisionChart.no_pen) items.append(rect_item) rect_item = QGraphicsRectItem() new_height = 0.4*text_item.boundingRect().height() rect_item.setRect(start_chart,next_y+(old_height-new_height)/2, full_size*(small/large), new_height) if before_larger: rect_item.setBrush(ComparisionChart.color2_brush) else: rect_item.setBrush(ComparisionChart.color1_brush) rect_item.setPen(ComparisionChart.no_pen) items.append(rect_item) text_item = QGraphicsTextItem(item) text_item.setFont(ComparisionChart.font) text_item.setPos(0, next_y) items.append(text_item) text_item = QGraphicsTextItem(fmt_num_align(before, before_max)) text_item.setFont(ComparisionChart.font) text_item.setPos(item_max_s, next_y) items.append(text_item) text_item = QGraphicsTextItem(fmt_num_align(after, after_max)) text_item.setFont(ComparisionChart.font) text_item.setPos(item_max_s+5+before_max_s, next_y) items.append(text_item) next_y+=text_item.boundingRect().height() #barsets = [QBarSet("previous"), QBarSet("current")] #cats = [] #for i, (item, before, now) in enumerate(data): #print (item, before, now) #barsets[1].append(before) #barsets[0].append(now) #cats.append(item) #axis_y = QBarCategoryAxis() #axis_y.append(cats) #barseries = QHorizontalBarSeries() #barseries.append(barsets) #chart = QChart() #chart.addSeries(barseries) #chart.setTitle(text) #chart.setAnimationOptions(QChart.SeriesAnimations) #chart.setAxisY(axis_y, barseries) #chart.setPos(0, next_y) #chart.setPreferredWidth(width) #chart.setPreferredHeight(500) ##chart.setSize(200,200) ##print ("SIZE:", chart.size().toSize().width(), ##chart.size().toSize().height()) ##print ("CHART:", chart.boundingRect().width(), ##chart.boundingRect().height()) #items.append(chart) group = self.scene.createItemGroup(items) return group
class MyCanvas(QGraphicsView): """ 画布窗体类,继承自QGraphicsView,采用QGraphicsView、QGraphicsScene、QGraphicsItem的绘图框架 """ def __init__(self, *args): super().__init__(*args) self.main_window = None self.list_widget = None self.item_dict = {} self.selected_id = '' self.status = '' self.temp_algorithm = '' self.temp_id = '' self.temp_item = None self.temp_color = QColor(0, 0, 0) self.origin_pos = None self.trans_center = None self.origin_p_list = None self.border = None def start_draw_line(self, algorithm, item_id): self.status = 'line' self.temp_algorithm = algorithm self.temp_id = item_id self.temp_item = None def start_draw_polygon(self, algorithm, item_id): self.status = 'polygon' self.temp_algorithm = algorithm self.temp_id = item_id self.temp_item = None def start_draw_ellipse(self, algorithm, item_id): self.status = 'ellipse' self.temp_algorithm = algorithm self.temp_id = item_id self.temp_item = None def start_draw_curve(self, algorithm, item_id): self.status = 'curve' self.temp_algorithm = algorithm self.temp_id = item_id self.temp_item = None def start_translate(self): self.status = 'translate' self.temp_item = None def start_rotate(self): self.status = 'rotate' self.temp_item = None self.trans_center = None self.origin_p_list = None def start_scale(self): self.status = 'scale' self.temp_item = None self.trans_center = None self.origin_p_list = None def start_clip(self, algorithm): self.status = 'clip' self.temp_algorithm = algorithm self.temp_item = None self.origin_pos = None self.origin_p_list = None def start_delete(self): if self.selected_id != '': self.main_window.is_modified = True self.temp_item = self.item_dict[self.selected_id] number = self.list_widget.findItems(self.selected_id, Qt.MatchContains) row = self.list_widget.row(number[0]) # self.list_widget.removeItemWidget(self.number[0]) temp_id = self.selected_id self.clear_selection() self.list_widget.clearSelection() self.scene().removeItem(self.temp_item) self.temp_item = None del self.item_dict[temp_id] self.list_widget.takeItem(row) self.updateScene([self.sceneRect()]) def start_freedom(self, item_id): self.status = 'freedom' self.temp_id = item_id self.temp_item = None def finish_draw(self): self.temp_id = self.main_window.get_id(True) def clear_selection(self): if self.selected_id != '': self.item_dict[self.selected_id].selected = False self.selected_id = '' def selection_changed(self, selected): if self.status == 'polygon' or self.status == 'curve': self.finish_draw() self.trans_center = None if self.selected_id != '': self.item_dict[self.selected_id].selected = False self.item_dict[self.selected_id].update() if selected != '': self.main_window.statusBar().showMessage('图元选择: %s' % selected) self.selected_id = selected self.item_dict[selected].selected = True self.item_dict[selected].update() self.status = '' self.updateScene([self.sceneRect()]) def mousePressEvent(self, event: QMouseEvent) -> None: # print('press') pos = self.mapToScene(event.localPos().toPoint()) x = int(pos.x()) y = int(pos.y()) if self.status == 'line': self.temp_item = MyItem(self.temp_id, self.status, [[x, y], [x, y]], self.temp_algorithm, self.temp_color) self.scene().addItem(self.temp_item) self.main_window.is_modified = True elif self.status == 'ellipse': self.temp_item = MyItem(self.temp_id, self.status, [[x, y], [x, y]], self.temp_algorithm, self.temp_color) self.scene().addItem(self.temp_item) self.main_window.is_modified = True elif self.status == 'polygon' or self.status == 'curve': if self.temp_item is None: self.temp_item = MyItem(self.temp_id, self.status, [[x, y]], self.temp_algorithm, self.temp_color) self.scene().addItem(self.temp_item) else: self.temp_item.p_list.append([x, y]) self.main_window.is_modified = True elif self.status == 'translate': if self.selected_id != '': self.main_window.is_modified = True self.temp_item = self.item_dict[self.selected_id] self.origin_pos = pos self.origin_p_list = self.temp_item.p_list elif self.status == 'rotate': if self.selected_id != '': self.main_window.is_modified = True self.temp_item = self.item_dict[self.selected_id] self.origin_p_list = self.temp_item.p_list if self.trans_center is None: self.trans_center = pos else: self.origin_pos = pos elif self.status == 'scale': if self.selected_id != '': self.main_window.is_modified = True self.temp_item = self.item_dict[self.selected_id] self.origin_p_list = self.temp_item.p_list if self.trans_center is None: self.trans_center = pos else: self.origin_pos = pos elif self.status == 'clip': if self.selected_id != '': self.temp_item = self.item_dict[self.selected_id] if self.temp_item.item_type == 'line': self.main_window.is_modified = True self.origin_pos = pos self.origin_p_list = self.temp_item.p_list elif self.status == 'freedom': self.main_window.is_modified = True self.temp_item = MyItem(self.temp_id, self.status, [[x, y]], '', self.temp_color) self.scene().addItem(self.temp_item) self.updateScene([self.sceneRect()]) super().mousePressEvent(event) def mouseMoveEvent(self, event: QMouseEvent) -> None: pos = self.mapToScene(event.localPos().toPoint()) x = int(pos.x()) y = int(pos.y()) if self.status == 'line': self.temp_item.p_list[1] = [x, y] elif self.status == 'ellipse': self.temp_item.p_list[1] = [x, y] elif self.status == 'polygon': self.temp_item.p_list[-1] = [x, y] elif self.status == 'curve': self.temp_item.p_list[-1] = [x, y] elif self.status == 'translate': if self.selected_id != '': dx = x - int(self.origin_pos.x()) dy = y - int(self.origin_pos.y()) self.temp_item.p_list = alg.translate(self.origin_p_list, dx, dy) elif self.status == 'rotate': if self.selected_id != '' and self.trans_center is not None and self.origin_pos is not None: x_origin, y_origin = int(self.origin_pos.x() - self.trans_center.x()), int( self.origin_pos.y() - self.trans_center.y()) len_origin = math.sqrt(x_origin**2 + y_origin**2) x_now, y_now = x - int(self.trans_center.x()), y - int( self.trans_center.y()) len_now = math.sqrt(x_now**2 + y_now**2) if len_origin != 0 and len_now != 0: sin_origin = y_origin / len_origin cos_origin = x_origin / len_origin sin_now = y_now / len_now cos_now = x_now / len_now delta_sin = sin_now * cos_origin - cos_now * sin_origin delta_cos = cos_now * cos_origin + sin_now * sin_origin if delta_cos >= 0: r = math.asin(delta_sin) else: r = math.pi - math.asin(delta_sin) self.temp_item.p_list = alg.rotate( self.origin_p_list, int(self.trans_center.x()), int(self.trans_center.y()), r, False) elif self.status == 'scale': if self.selected_id != '' and self.trans_center is not None and self.origin_pos is not None: x_last, y_last = int(self.origin_pos.x() - self.trans_center.x()), int( self.origin_pos.y() - self.trans_center.y()) len_last = math.sqrt(x_last**2 + y_last**2) if len_last != 0: x_now, y_now = x - int(self.trans_center.x()), y - int( self.trans_center.y()) len_now = math.sqrt(x_now**2 + y_now**2) self.temp_item.p_list = alg.scale( self.origin_p_list, int(self.trans_center.x()), int(self.trans_center.y()), len_now / len_last) elif self.status == 'clip': if self.selected_id != '' and self.origin_pos is not None and self.temp_item.item_type == 'line': x_min = min(int(self.origin_pos.x()), x) x_max = max(int(self.origin_pos.x()), x) y_min = min(int(self.origin_pos.y()), y) y_max = max(int(self.origin_pos.y()), y) ''' self.temp_item.p_list = alg.clip(self.origin_p_list, x_min, y_min, x_max, y_max, self.temp_algorithm) print(self.temp_item.p_list) ''' if self.border is None: self.border = QGraphicsRectItem(x_min - 1, y_min - 1, x_max - x_min + 2, y_max - y_min + 2) self.scene().addItem(self.border) self.border.setPen(QColor(0, 255, 255)) else: self.border.setRect(x_min - 1, y_min - 1, x_max - x_min + 2, y_max - y_min + 2) elif self.status == 'freedom': self.temp_item.p_list.append([x, y]) self.updateScene([self.sceneRect()]) super().mouseMoveEvent(event) def mouseReleaseEvent(self, event: QMouseEvent) -> None: if self.status == 'line': self.item_dict[self.temp_id] = self.temp_item self.list_widget.addItem(self.temp_id) self.finish_draw() elif self.status == 'ellipse': self.item_dict[self.temp_id] = self.temp_item self.list_widget.addItem(self.temp_id) self.finish_draw() elif self.status == 'polygon': self.item_dict[self.temp_id] = self.temp_item if not self.list_widget.findItems(self.temp_id, Qt.MatchContains): self.list_widget.addItem(self.temp_id) elif self.status == 'curve': self.item_dict[self.temp_id] = self.temp_item if not self.list_widget.findItems(self.temp_id, Qt.MatchContains): self.list_widget.addItem(self.temp_id) elif self.status == 'clip': pos = self.mapToScene(event.localPos().toPoint()) x = int(pos.x()) y = int(pos.y()) if self.selected_id != '' and self.origin_pos is not None and self.temp_item.item_type == 'line': x_min = min(int(self.origin_pos.x()), x) x_max = max(int(self.origin_pos.x()), x) y_min = min(int(self.origin_pos.y()), y) y_max = max(int(self.origin_pos.y()), y) temp_p_list = alg.clip(self.origin_p_list, x_min, y_min, x_max, y_max, self.temp_algorithm) if len(temp_p_list) == 0: # self.selected_id = '' # print(self.number) number = self.list_widget.findItems( self.selected_id, Qt.MatchContains) row = self.list_widget.row(number[0]) # self.list_widget.removeItemWidget(self.number[0]) temp_id = self.selected_id self.clear_selection() self.list_widget.clearSelection() self.scene().removeItem(self.temp_item) self.temp_item = None del self.item_dict[temp_id] self.list_widget.takeItem(row) else: self.temp_item.p_list = temp_p_list if self.border is not None: self.scene().removeItem(self.border) self.border = None self.updateScene([self.sceneRect()]) elif self.status == 'freedom': self.item_dict[self.temp_id] = self.temp_item self.list_widget.addItem(self.temp_id) self.finish_draw() super().mouseReleaseEvent(event)
class MainController(QObject): appStart = pyqtSignal() newCommand = pyqtSignal(tuple) def __init__(self): super(MainController, self).__init__() self.ser = Serial(WIFLY_SERIAL_PORT, WIFLY_BAUD_RATE) self.wiflyReceiver = WiflyReceiver(self.ser) self.wiflySender = WiflySender(self.ser) self.rover = Rover() self.mainWidget = MainWidget() self.wiflyReceiverThread = QThread() self.wiflyReceiver.moveToThread(self.wiflyReceiverThread) self.wiflySenderThread = QThread() self.wiflySender.moveToThread(self.wiflySenderThread) self.simState = SIMULATION_STATE_PHASE_1 self.simTimer = QTimer() self.simTimer.setSingleShot(True) self.wiflyReceiver.msgReceived.connect(self.mainWidget.appendMsg) self.wiflyReceiver.msgReceived.connect(self.rover.processData) self.newCommand.connect(self.wiflySender.sendMsg) self.appStart.connect(self.wiflyReceiver.processMsg) self.mainWidget.ui.gearSlider.valueChanged.connect(self.manualGearChange) self.mainWidget.ui.upButton.clicked.connect(self.manualMoveForward) self.mainWidget.ui.downButton.clicked.connect(self.manualMoveBackward) self.mainWidget.ui.leftButton.clicked.connect(self.manualMoveLeft) self.mainWidget.ui.rightButton.clicked.connect(self.manualMoveRight) self.mainWidget.ui.brakeButton.clicked.connect(self.manualStop) self.mainWidget.ui.simulationButton.clicked.connect(self.simulationStart) self.rover.newRoverPosition.connect(self.drawRover) self.rover.newWallDetected.connect(self.drawNewWall) self.simTimer.timeout.connect(self.simulationUpdate) self.mapScene = QGraphicsScene(0, 0, WORLD_X / CANVAS_RATIO, WORLD_Y / CANVAS_RATIO) self.mainWidget.ui.mappingGraphicsView.setScene(self.mapScene) self.roverRect = QGraphicsRectItem() self.mapScene.addItem(self.roverRect) """ rect1 = QGraphicsRectItem() rect2 = QGraphicsRectItem() self.mapScene.addItem(rect1) self.mapScene.addItem(rect2) rect1.setRect(100, 100, 20, 40) rect2.setRect(100, 100, 20, 40) #rect.moveBy(10, 50) rect2.setTransformOriginPoint(100, 100) rect2.setRotation(-10) print rect1.rect().center() #print rect2.transformOriginPoint().x(), rect2.transformOriginPoint().y() """ @pyqtSlot(tuple, tuple) def drawNewWall(self, wallFront, wallRear): pFront = QGraphicsRectItem(wallFront[0] / CANVAS_RATIO, wallFront[1] / CANVAS_RATIO, DOT_SIZE, DOT_SIZE) pRear = QGraphicsRectItem(wallRear[0] / CANVAS_RATIO, wallRear[1] / CANVAS_RATIO, DOT_SIZE, DOT_SIZE) self.mapScene.addItem(pFront) self.mapScene.addItem(pRear) @pyqtSlot(tuple, float) def drawRover(self, center, orientation): self.roverRect.setRect((center[0] - ROVER_WIDTH / 2) / CANVAS_RATIO, (center[1] - ROVER_LENGTH / 2) / CANVAS_RATIO, ROVER_WIDTH / CANVAS_RATIO, ROVER_LENGTH / CANVAS_RATIO) self.roverRect.setTransformOriginPoint(center[0] / CANVAS_RATIO, center[1] / CANVAS_RATIO) self.roverRect.setRotation(math.degrees(-orientation)) @pyqtSlot() def manualGearChange(self): gear = self.mainWidget.ui.gearSlider.value() self.mainWidget.ui.gearLcdNumber.display(gear) self.rover.roverGear = gear self.rover.updateMotorCommand() self.newCommand.emit(tuple(self.rover.commandMsg)) @pyqtSlot() def manualMoveForward(self): self.rover.roverDirection = ROVER_DIRECTION_FORWARD self.rover.updateMotorCommand() self.newCommand.emit(tuple(self.rover.commandMsg)) @pyqtSlot() def manualMoveBackward(self): self.rover.roverDirection = ROVER_DIRECTION_BACKWARD self.rover.updateMotorCommand() self.newCommand.emit(tuple(self.rover.commandMsg)) @pyqtSlot() def manualMoveLeft(self): self.rover.roverDirection = ROVER_DIRECTION_LEFT self.rover.updateMotorCommand() self.newCommand.emit(tuple(self.rover.commandMsg)) @pyqtSlot() def manualMoveRight(self): self.rover.roverDirection = ROVER_DIRECTION_RIGHT self.rover.updateMotorCommand() self.newCommand.emit(tuple(self.rover.commandMsg)) @pyqtSlot() def manualStop(self): self.mainWidget.ui.gearSlider.setValue(0) @pyqtSlot() def simulationStart(self): self.simState == SIMULATION_STATE_PHASE_1 self.simTimer.start(5000) @pyqtSlot() def simulationUpdate(self): if self.simState == SIMULATION_STATE_PHASE_1: self.simState = SIMULATION_STATE_PHASE_2 self.rover.roverGear = 2 self.rover.roverDirection = ROVER_DIRECTION_FORWARD self.rover.updateMotorCommand() self.newCommand.emit(tuple(self.rover.commandMsg)) self.simTimer.start(16000) elif self.simState == SIMULATION_STATE_PHASE_2: self.simState = SIMULATION_STATE_PHASE_3 self.rover.roverDirection = ROVER_DIRECTION_BACKWARD self.rover.updateMotorCommand() self.newCommand.emit(tuple(self.rover.commandMsg)) self.simTimer.start(6100) elif self.simState == SIMULATION_STATE_PHASE_3: self.simState = SIMULATION_STATE_PHASE_4 self.rover.roverDirection = ROVER_DIRECTION_LEFT self.rover.updateMotorCommand() self.newCommand.emit(tuple(self.rover.commandMsg)) self.simTimer.start(3500) elif self.simState == SIMULATION_STATE_PHASE_4: self.simState = SIMULATION_STATE_PHASE_5 self.rover.roverDirection = ROVER_DIRECTION_BACKWARD self.rover.updateMotorCommand() self.newCommand.emit(tuple(self.rover.commandMsg)) self.simTimer.start(8300) elif self.simState == SIMULATION_STATE_PHASE_5: self.rover.roverGear = 0 self.rover.roverDirection = ROVER_DIRECTION_STOP self.rover.updateMotorCommand() self.newCommand.emit(tuple(self.rover.commandMsg)) def start(self): self.mainWidget.show() self.wiflyReceiverThread.start() self.wiflySenderThread.start() self.appStart.emit()
class PreXoverLabel(QGraphicsSimpleTextItem): """ Attributes: is_fwd (bool): Description """ _XO_FONT = styles.XOVER_LABEL_FONT _XO_BOLD = styles.XOVER_LABEL_FONT_BOLD _FM = QFontMetrics(_XO_FONT) def __init__(self, is_fwd: bool, pre_xover_item: 'PreXoverItem'): """ Args: is_fwd: Description pre_xover_item: Description """ super(QGraphicsSimpleTextItem, self).__init__(pre_xover_item) self.is_fwd = is_fwd self._tbr = None self._outline = QGraphicsRectItem(self) self.setFont(self._XO_FONT) self.setBrush(getBrushObj('#666666')) # end def def resetItem(self, is_fwd: bool, color: str): """ Args: is_fwd: Description color: Description """ self.resetTransform() self.is_fwd = is_fwd self.color = color # end def def setTextAndStyle(self, text: str, outline: bool = False): """ Args: text: Description outline: Default is ``False`` """ str_txt = str(text) self._tbr = tBR = self._FM.tightBoundingRect(str_txt) half_label_H = tBR.height() / 2.0 half_label_W = tBR.width() / 2.0 labelX = BASE_WIDTH/2.0 - half_label_W if str_txt == '1': # adjust for the number one labelX -= tBR.width() labelY = half_label_H if self.is_fwd else (BASE_WIDTH - tBR.height())/2 self.setPos(labelX, labelY) self.setText(str_txt) if outline: self.setFont(self._XO_BOLD) self.setBrush(getBrushObj('#ff0000')) else: self.setFont(self._XO_FONT) self.setBrush(getBrushObj('#666666')) if outline: r = QRectF(self._tbr).adjusted(-half_label_W, 0, half_label_W, half_label_H) self._outline.setRect(r) self._outline.setPen(getPenObj('#ff0000', 0.25)) self._outline.setY(2*half_label_H) self._outline.show() else: self._outline.hide()
class NodeTemplateItem(): ''' This represents one node template on the diagram. A node template can be on many diagrams This class creates the rectangle graphics item and the text graphics item and adds them to the scene. ''' def __init__(self, scene, x, y, nodeTemplateDict=None, NZID=None): self.scene = scene self.logMsg = None self.x = x self.y = y self.nodeTemplateDict = nodeTemplateDict # self.name = self.nodeTemplateDict.get("name", "") THIS HAS BEEN REPLACED BY THE name FUNCTION - SEE BELOW self.diagramType = "Node Template" self.displayText = None self.model = self.scene.parent.model self.gap = 100 self.relList = [] # assign a unique key if it doesn't already have one if NZID == None: self.NZID = str(uuid.uuid4()) else: self.NZID = NZID # init graphics objects to none self.TNode = None self.TNtext = None # draw the node template on the diagram self.drawIt() def name(self, ): return self.nodeTemplateDict.get("name", "") def getX(self, ): return self.TNode.boundingRect().x() def getY(self, ): return self.TNode.boundingRect().y() def getHeight(self, ): return self.TNode.boundingRect().height() def getWidth(self, ): return self.TNode.boundingRect().width() def getRelList(self, ): '''return a list of all relationitems that are inbound or outbound from this node template. do not include self referencing relationships ''' return [ diagramItem for key, diagramItem in self.scene.parent.itemDict.items() if diagramItem.diagramType == "Relationship Template" and ( diagramItem.startNZID == self.NZID or diagramItem.endNZID == self.NZID) ] def getPoint(self, offset=None): ''' This function is used by the template diagram to calculate the location to drop a node template on the diagram ''' if offset is None: return QPointF(self.x, self.y) else: return QPointF(self.x + offset, self.y + offset) def getFormat(self, ): ''' determine if the Node Template has a template format or should use the project default format ''' # get the node Template custom format customFormat = self.nodeTemplateDict.get("TNformat", None) if not customFormat is None: # get the template custom format self.nodeFormat = TNodeFormat(formatDict=customFormat) else: # get the project default format self.nodeFormat = TNodeFormat( formatDict=self.model.modelData["TNformat"]) def clearItem(self, ): if (not self.TNode is None and not self.TNode.scene() is None): self.TNode.scene().removeItem(self.TNode) if (not self.TNtext is None and not self.TNtext.scene() is None): self.TNtext.scene().removeItem(self.TNtext) def drawIt(self, ): # get current format as it may have changed self.getFormat() # create the qgraphicsItems if they don't exist if self.TNode is None: # create the rectangle self.TNode = QGraphicsRectItem(QRectF( self.x, self.y, self.nodeFormat.formatDict["nodeWidth"], self.nodeFormat.formatDict["nodeHeight"]), parent=None) self.TNode.setZValue(NODELAYER) self.TNode.setFlag(QGraphicsItem.ItemIsMovable, True) self.TNode.setFlag(QGraphicsItem.ItemSendsGeometryChanges, True) self.TNode.setFlag(QGraphicsItem.ItemIsSelectable, True) self.TNode.setSelected(True) self.TNode.setData(1, self.NZID) # get with self.INode.data(1) self.TNode.setData(ITEMTYPE, NODETEMPLATE) # create the text box self.TNtext = QGraphicsTextItem("", parent=None) self.TNtext.setPos(self.x, self.y) self.TNtext.setFlag(QGraphicsItem.ItemIsMovable, True) self.TNtext.setFlag(QGraphicsItem.ItemIsSelectable, False) self.TNtext.setData(NODEID, self.NZID) self.TNtext.setData(ITEMTYPE, NODETEMPLATETEXT) self.TNtext.setZValue(NODELAYER) # save the location self.x = self.TNode.sceneBoundingRect().x() self.y = self.TNode.sceneBoundingRect().y() # generate the html and resize the rectangle self.formatItem() # add the graphics items to the scene self.scene.addItem(self.TNode) self.scene.addItem(self.TNtext) else: # generate the html and resize the rectangle self.formatItem() def formatItem(self, ): # configure the formatting aspects of the qgraphics item pen = self.nodeFormat.pen() brush = self.nodeFormat.brush() self.TNode.setBrush(brush) self.TNode.setPen(pen) # generate the HTML genHTML = self.generateHTML() self.TNtext.prepareGeometryChange() # print("before html bounding rectangle width:{}".format(self.TNtext.boundingRect().width())) # print("before html text width:{}".format(self.TNtext.textWidth())) self.TNtext.setTextWidth( -1 ) # reset the width to unkonwn so it will calculate a new width based on the new html self.TNtext.setHtml(genHTML) # print("after html bounding rectangle width:{}".format(self.TNtext.boundingRect().width())) # print("after html text width:{}".format(self.TNtext.textWidth())) # make sure minimum width of 120 if self.TNtext.boundingRect().width() < 120: self.TNtext.setTextWidth(120) else: self.TNtext.setTextWidth( self.TNtext.boundingRect().width() ) # you have to do a setTextWidth to get the html to render correctly. # set the rectangle item to the same size as the formatted html self.TNode.prepareGeometryChange() currentRect = self.TNode.rect() # insure minimum height of 120 if self.TNtext.boundingRect().height() < 120: currentRect.setHeight(120) else: currentRect.setHeight(self.TNtext.boundingRect().height()) currentRect.setWidth(self.TNtext.boundingRect().width()) self.TNode.setRect(currentRect) def generateHTML(self, ): ''' Generate the HTML that formats the node template data inside the rectangle ''' # generate the html prefix = "<!DOCTYPE html><html><body>" # head = "<head><style>table, th, td {border: 1px solid black; border-collapse: collapse;}</style></head>" suffix = "</body></html>" # blankRow = "<tr><td><left>{}</left></td><td><left>{}</left></td><td><left>{}</left></td><td><left>{}</left></td></tr>".format("", "", "", "") name = "<center><b>{}</b></center>".format( self.nodeTemplateDict.get("name", "")) lbls = self.genLblHTML() props = self.genPropHTML() genHTML = "{}{}<hr>{}<br><hr>{}{}".format(prefix, name, lbls, props, suffix) # print("{} html: {}".format(self.name(), genHTML)) return genHTML def genLblHTML(self): # html = '<table width="90%">' html = '<table style="width:90%;border:1px solid black;">' if len(self.nodeTemplateDict.get("labels", [])) > 0: for lbl in self.nodeTemplateDict.get("labels", []): if lbl[NODEKEY] == Qt.Checked: nk = "NK" else: nk = " " if lbl[REQUIRED] == Qt.Checked: rq = "R" else: rq = "" html = html + '<tr align="left"><td width="15%"><left>{}</left></td><td width="65%"><left>{}</left></td><td width="10%"><left>{}</left></td><td width="10%"><left>{}</left></td></tr>'.format( nk, lbl[LABEL], "", rq) html = html + "</table>" else: html = '<tr align="left"><td width="15%"><left>{}</left></td><td width="65%"><left>{}</left></td><td width="10%"><left>{}</left></td><td width="10%"><left>{}</left></td></tr>'.format( " ", "NO{}LABELS".format(" "), "", "") html = html + "</table>" return html def genPropHTML(self): # PROPERTY, DATATYPE, PROPREQ, DEFAULT, EXISTS, UNIQUE, PROPNODEKEY html = '<table style="width:90%;border:1px solid black;">' if len(self.nodeTemplateDict.get("properties", [])) > 0: for prop in self.nodeTemplateDict.get("properties", []): if prop[PROPNODEKEY] == Qt.Checked: nk = "NK" else: nk = " " if prop[PROPREQ] == Qt.Checked: rq = "R" else: rq = "" if prop[EXISTS] == Qt.Checked: ex = "E" else: ex = "" if prop[UNIQUE] == Qt.Checked: uq = "U" else: uq = "" html = html + '<tr align="left"><td width="15%"><left>{}</left></td><td width="65%"><left>{}</left></td><td width="10%"><left>{}</left></td><td width="10%"><left>{}</left></td><td width="10%"><left>{}</left></td></tr>'.format( nk, prop[PROPERTY], rq, ex, uq) html = html + "</table>" else: html = html + '<tr align="left"><td width="15%"><left>{}</left></td><td width="65%"><left>{}</left></td><td width="10%"><left>{}</left></td><td width="10%"><left>{}</left></td></tr>'.format( " ", "NO{}PROPERTIES".format(" "), "", "", "") html = html + "</table>" return html def moveIt(self, dx, dy): ''' Move the node rectangle and the node textbox to the delta x,y coordinate. ''' # print("before moveIt: sceneboundingrect {} ".format( self.INode.sceneBoundingRect())) self.TNode.moveBy(dx, dy) self.x = self.TNode.sceneBoundingRect().x() self.y = self.TNode.sceneBoundingRect().y() self.TNtext.moveBy(dx, dy) # print("after moveIt: sceneboundingrect {} ".format( self.INode.sceneBoundingRect())) # now redraw all the relationships self.drawRels() def drawRels(self, ): '''Redraw all the relationship lines connected to the Node Template Rectangle''' # get a list of the relationship items connected to this node template self.relList = self.getRelList() # assign the correct inbound/outbound side for the rel for rel in self.relList: if rel.endNodeItem.NZID != rel.startNodeItem.NZID: # ignore bunny ears rel.assignSide() # get a set of all the nodes and sides involved nodeSet = set() for rel in self.relList: if rel.endNodeItem.NZID != rel.startNodeItem.NZID: # ignore bunny ears nodeSet.add((rel.endNodeItem, rel.inboundSide)) nodeSet.add((rel.startNodeItem, rel.outboundSide)) # tell each node side to assign rel locations for nodeSide in nodeSet: nodeSide[0].assignPoint(nodeSide[1]) ############################################ # now tell them all to redraw for rel in self.relList: rel.drawIt2() def calcOffset(self, index, totRels): offset = [-60, -40, -20, 0, 20, 40, 60] offsetStart = [3, 2, 2, 1, 1, 0, 0] if totRels > 7: totRels = 7 return offset[offsetStart[totRels - 1] + index] def assignPoint(self, side): # go through all the rels on a side and assign their x,y coord for that side self.relList = self.getRelList() sideList = [ rel for rel in self.relList if ((rel.startNZID == self.NZID and rel.outboundSide == side) or ( rel.endNZID == self.NZID and rel.inboundSide == side)) ] totRels = len(sideList) if totRels > 0: if side == R: # calc center of the side x = self.x + self.getWidth() y = self.y + self.getHeight() / 2 # sort the rels connected to this side by the y value sideList.sort(key=self.getSortY) # assign each of them a position on the side starting in the center and working out in both directions for index, rel in enumerate(sideList): if rel.startNZID == self.NZID: rel.outboundPoint = QPointF( x, y + (self.calcOffset(index, totRels))) if rel.endNZID == self.NZID: rel.inboundPoint = QPointF( x, y + (self.calcOffset(index, totRels))) elif side == L: x = self.x y = self.y + self.getHeight() / 2 sideList.sort(key=self.getSortY) for index, rel in enumerate(sideList): if rel.startNZID == self.NZID: rel.outboundPoint = QPointF( x, y + (self.calcOffset(index, totRels))) if rel.endNZID == self.NZID: rel.inboundPoint = QPointF( x, y + (self.calcOffset(index, totRels))) elif side == TOP: x = self.x + self.getWidth() / 2 y = self.y sideList.sort(key=self.getSortX) for index, rel in enumerate(sideList): if rel.startNZID == self.NZID: rel.outboundPoint = QPointF( x + (self.calcOffset(index, totRels)), y) if rel.endNZID == self.NZID: rel.inboundPoint = QPointF( x + (self.calcOffset(index, totRels)), y) elif side == BOTTOM: x = self.x + self.getWidth() / 2 y = self.y + self.getHeight() sideList.sort(key=self.getSortX) for index, rel in enumerate(sideList): if rel.startNZID == self.NZID: rel.outboundPoint = QPointF( x + (self.calcOffset(index, totRels)), y) if rel.endNZID == self.NZID: rel.inboundPoint = QPointF( x + (self.calcOffset(index, totRels)), y) else: print("error, no side") def getSortY(self, rel): # if this node is the start node then return the end node's Y if rel.startNZID == self.NZID: return rel.endNodeItem.TNode.sceneBoundingRect().center().y() # if this node is the end node then return the start node's Y if rel.endNZID == self.NZID: return rel.startNodeItem.TNode.sceneBoundingRect().center().y() # this should never happen return 0 def getSortX(self, rel): # if this node is the start node then return the end node's X if rel.startNZID == self.NZID: return rel.endNodeItem.TNode.sceneBoundingRect().center().x() # if this node is the end node then return the start node's X if rel.endNZID == self.NZID: return rel.startNodeItem.TNode.sceneBoundingRect().center().x() # this should never happen return 0 def getObjectDict(self, ): ''' This function returns a dictionary with all the data that represents this node template item. The dictionary is added to the Instance Diagram dictionary.''' objectDict = {} objectDict["NZID"] = self.NZID objectDict["name"] = self.nodeTemplateDict.get("name", "") objectDict["displayText"] = self.displayText objectDict["x"] = self.TNode.sceneBoundingRect().x() objectDict["y"] = self.TNode.sceneBoundingRect().y() objectDict["diagramType"] = self.diagramType objectDict["labels"] = self.nodeTemplateDict.get("labels", []) objectDict["properties"] = self.nodeTemplateDict.get("properties", []) return objectDict def setLogMethod(self, logMethod=None): if logMethod is None: if self.logMsg is None: self.logMsg = self.noLog else: self.logMsg = logMethod def noLog(self, msg): return
class PreXoverLabel(QGraphicsSimpleTextItem): """Summary Attributes: is_fwd (TYPE): Description """ _XO_FONT = styles.XOVER_LABEL_FONT _XO_BOLD = styles.XOVER_LABEL_FONT_BOLD _FM = QFontMetrics(_XO_FONT) def __init__(self, is_fwd, color, pre_xover_item): """Summary Args: is_fwd (TYPE): Description color (TYPE): Description pre_xover_item (TYPE): Description """ super(QGraphicsSimpleTextItem, self).__init__(pre_xover_item) self.is_fwd = is_fwd self._color = color self._tbr = None self._outline = QGraphicsRectItem(self) self.setFont(self._XO_FONT) self.setBrush(getBrushObj('#666666')) # end def def resetItem(self, is_fwd, color): """Summary Args: is_fwd (TYPE): Description color (TYPE): Description Returns: TYPE: Description """ self.is_fwd = is_fwd self._color = color # end def def setTextAndStyle(self, text, outline=False): """Summary Args: text (TYPE): Description outline (bool, optional): Description Returns: TYPE: Description """ str_txt = str(text) self._tbr = tBR = self._FM.tightBoundingRect(str_txt) half_label_H = tBR.height() / 2.0 half_label_W = tBR.width() / 2.0 labelX = BASE_WIDTH / 2.0 - half_label_W if str_txt == '1': # adjust for the number one labelX -= tBR.width() labelY = half_label_H if self.is_fwd else (BASE_WIDTH - tBR.height()) / 2 self.setPos(labelX, labelY) self.setText(str_txt) if outline: self.setFont(self._XO_BOLD) self.setBrush(getBrushObj('#ff0000')) else: self.setFont(self._XO_FONT) self.setBrush(getBrushObj('#666666')) if outline: r = QRectF(self._tbr).adjusted(-half_label_W, 0, half_label_W, half_label_H) self._outline.setRect(r) self._outline.setPen(getPenObj('#ff0000', 0.25)) self._outline.setY(2 * half_label_H) self._outline.show() else: self._outline.hide()
class ZoomableScene(QGraphicsScene): def __init__(self, parent=None): super().__init__(parent) self.noise_area = None self.ones_area = None self.zeros_area = None self.ones_arrow = None self.zeros_arrow = None self.selection_area = ROI(0, 0, 0, 0, fillcolor=constants.SELECTION_COLOR, opacity=constants.SELECTION_OPACITY) self.addItem(self.selection_area) def draw_noise_area(self, y, h): x = self.sceneRect().x() w = self.sceneRect().width() if self.ones_area is not None: self.ones_area.hide() if self.zeros_area is not None: self.zeros_area.hide() if self.noise_area is None or self.noise_area.scene() != self: roi = ROI(x, y, w, h, fillcolor=constants.NOISE_COLOR, opacity=constants.NOISE_OPACITY) # roi.setPen(QPen(constants.NOISE_COLOR, Qt.FlatCap)) self.noise_area = roi self.addItem(self.noise_area) else: self.noise_area.show() self.noise_area.setY(y) self.noise_area.height = h def draw_sep_area(self, y_mid): x = self.sceneRect().x() y = self.sceneRect().y() h = self.sceneRect().height() w = self.sceneRect().width() if self.noise_area is not None: self.noise_area.hide() if self.ones_area is None: self.ones_area = QGraphicsRectItem(x, y, w, h / 2 + y_mid) self.ones_area.setBrush(constants.ONES_AREA_COLOR) self.ones_area.setOpacity(constants.SEPARATION_OPACITY) self.ones_area.setPen(QPen(constants.TRANSPARENT_COLOR, Qt.FlatCap)) self.addItem(self.ones_area) else: self.ones_area.show() self.ones_area.setRect(x, y, w, h / 2 + y_mid) start = y + h / 2 + y_mid if self.zeros_area is None: self.zeros_area = QGraphicsRectItem(x, start, w, (y + h) - start) self.zeros_area.setBrush(constants.ZEROS_AREA_COLOR) self.zeros_area.setOpacity(constants.SEPARATION_OPACITY) self.zeros_area.setPen(QPen(constants.TRANSPARENT_COLOR, Qt.FlatCap)) self.addItem(self.zeros_area) else: self.zeros_area.show() self.zeros_area.setRect(x, start, w, (y + h) - start) def clear(self): self.noise_area = None self.ones_area = None self.zeros_area = None self.zeros_arrow = None self.ones_arrow = None self.selection_area = None super().clear() def dragEnterEvent(self, event: QGraphicsSceneDragDropEvent): event.accept() def dragMoveEvent(self, event: QGraphicsSceneDragDropEvent): event.accept()
class PaintArea(QGraphicsView): def __init__(self, width=10, parent=None): QGraphicsView.__init__(self, parent) self._frame = None self._instructions = None self.setScene(QGraphicsScene(self)) self._items = self.scene().createItemGroup([]) self.setMouseTracking(True) self.pen = QPen(Qt.black, width, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin) self.painting = False self.setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform) self.viewport().setCursor(self.getCursor()) self.updateScene() def updateScene(self): if self._frame: self.scene().setBackgroundBrush(Qt.gray) oldCanvas = self.canvas() self.setSceneRect(QRectF(self.contentsRect())) self.centerScene() self.scaleItems(oldCanvas, self.canvas()) def centerScene(self): self.centerFrame() self.centerInstructions() def scaleItems(self, oldCanvas, newCanvas): pass def canvas(self): if self._frame: return self._frame.rect() return QRectF(self.contentsRect()) def fitInstructions(self): textSize = self._instructions.document().size() factor = min(self.canvas().size().width() / textSize.width(), self.canvas().size().height() / textSize.height()) f = self._instructions.font() f.setPointSizeF(f.pointSizeF() * factor) self._instructions.setFont(f) def centerInstructions(self): if self._instructions: self.fitInstructions() size = self.size() textSize = self._instructions.document().size() self._instructions.setPos((size.width() - textSize.width()) / 2.0, (size.height() - textSize.height()) / 2.0) def setInstructions(self, text): if self._instructions: self._instructions.setPlainText(text) else: self._instructions = self.scene().addText(text, QFont('Arial', 10, QFont.Bold)) self._instructions.setZValue(-1) self._instructions.setDefaultTextColor(QColor(220, 220, 220)) self._text = text self.centerInstructions() def setFrame(self, width, height): if self._frame: self._frame.setRect(0, 0, width, height) else: self.addFrame(QRectF(0, 0, width, height)) self.centerScene() def addFrame(self, rect): self._frame = QGraphicsRectItem(rect) self._frame.setPen(QPen(Qt.NoPen)) self._frame.setBrush(Qt.white) self._frame.setZValue(-2) self.scene().addItem(self._frame) def centerFrame(self): if self._frame: rect = self._frame.rect() size = self.contentsRect() factor = min((size.width() + 1) / rect.width(), (size.height() + 1) / rect.height()) w, h = rect.width() * factor, rect.height() * factor self._frame.setRect(size.x() + (size.width() - w) / 2.0, size.y() + (size.height() - h) / 2.0, w, h) def resizeEvent(self, event): self.updateScene() def setBrushSize(self, size): self.pen.setWidth(size) self.viewport().setCursor(self.getCursor()) def render(self, painter): if self._instructions: self.scene().removeItem(self._instructions) self.scene().render(painter, source=self.scene().itemsBoundingRect()) if self._instructions: self.scene().addItem(self._instructions) def getLines(self): items = [item for item in self.scene().items() if item.group() == self._items] return self.canvas(), items def clear(self): for item in self.scene().items(): if item.group() == self._items: self._items.removeFromGroup(item) def getCursor(self): antialiasing_margin = 1 size = self.pen.width() pixmap = QPixmap(size + antialiasing_margin * 2, size + antialiasing_margin * 2) pixmap.fill(Qt.transparent) painter = QPainter(pixmap) painter.setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform) painter.drawEllipse(QRectF(QPointF(antialiasing_margin, antialiasing_margin), QSizeF(size, size))) painter.end() return QCursor(pixmap) def addLine(self, start, end): if start == end: delta = QPointF(.0001, 0) end = start - delta line = self.scene().addLine(QLineF(start, end), self.pen) self._items.addToGroup(line) def drawPoint(self, pos): delta = QPointF(.0001, 0) line = self.scene().addLine(QLineF(pos, pos - delta), self.pen) self._items.addToGroup(line) def mousePressEvent(self, event): self.start = QPointF(self.mapToScene(event.pos())) self.painting = True self.addLine(self.start, self.start) def mouseReleaseEvent(self, event): self.painting = False def mouseMoveEvent(self, event): pos = QPointF(self.mapToScene(event.pos())) if self.painting: self.addLine(self.start, pos) self.start = pos
class MapScene(QGraphicsScene): selectedObjectItemsChanged = pyqtSignal() ## # Constructor. ## def __init__(self, parent): super().__init__(parent) self.mMapDocument = None self.mSelectedTool = None self.mActiveTool = None self.mObjectSelectionItem = None self.mUnderMouse = False self.mCurrentModifiers = Qt.NoModifier, self.mDarkRectangle = QGraphicsRectItem() self.mDefaultBackgroundColor = Qt.darkGray self.mLayerItems = QVector() self.mObjectItems = QMap() self.mObjectLineWidth = 0.0 self.mSelectedObjectItems = QSet() self.mLastMousePos = QPointF() self.mShowTileObjectOutlines = False self.mHighlightCurrentLayer = False self.mGridVisible = False self.setBackgroundBrush(self.mDefaultBackgroundColor) tilesetManager = TilesetManager.instance() tilesetManager.tilesetChanged.connect(self.tilesetChanged) tilesetManager.repaintTileset.connect(self.tilesetChanged) prefs = preferences.Preferences.instance() prefs.showGridChanged.connect(self.setGridVisible) prefs.showTileObjectOutlinesChanged.connect(self.setShowTileObjectOutlines) prefs.objectTypesChanged.connect(self.syncAllObjectItems) prefs.highlightCurrentLayerChanged.connect(self.setHighlightCurrentLayer) prefs.gridColorChanged.connect(self.update) prefs.objectLineWidthChanged.connect(self.setObjectLineWidth) self.mDarkRectangle.setPen(QPen(Qt.NoPen)) self.mDarkRectangle.setBrush(Qt.black) self.mDarkRectangle.setOpacity(darkeningFactor) self.addItem(self.mDarkRectangle) self.mGridVisible = prefs.showGrid() self.mObjectLineWidth = prefs.objectLineWidth() self.mShowTileObjectOutlines = prefs.showTileObjectOutlines() self.mHighlightCurrentLayer = prefs.highlightCurrentLayer() # Install an event filter so that we can get key events on behalf of the # active tool without having to have the current focus. QCoreApplication.instance().installEventFilter(self) ## # Destructor. ## def __del__(self): if QCoreApplication.instance(): QCoreApplication.instance().removeEventFilter(self) ## # Returns the map document this scene is displaying. ## def mapDocument(self): return self.mMapDocument ## # Sets the map this scene displays. ## def setMapDocument(self, mapDocument): if (self.mMapDocument): self.mMapDocument.disconnect() if (not self.mSelectedObjectItems.isEmpty()): self.mSelectedObjectItems.clear() self.selectedObjectItemsChanged.emit() self.mMapDocument = mapDocument if (self.mMapDocument): renderer = self.mMapDocument.renderer() renderer.setObjectLineWidth(self.mObjectLineWidth) renderer.setFlag(RenderFlag.ShowTileObjectOutlines, self.mShowTileObjectOutlines) self.mMapDocument.mapChanged.connect(self.mapChanged) self.mMapDocument.regionChanged.connect(self.repaintRegion) self.mMapDocument.tileLayerDrawMarginsChanged.connect(self.tileLayerDrawMarginsChanged) self.mMapDocument.layerAdded.connect(self.layerAdded) self.mMapDocument.layerRemoved.connect(self.layerRemoved) self.mMapDocument.layerChanged.connect(self.layerChanged) self.mMapDocument.objectGroupChanged.connect(self.objectGroupChanged) self.mMapDocument.imageLayerChanged.connect(self.imageLayerChanged) self.mMapDocument.currentLayerIndexChanged.connect(self.currentLayerIndexChanged) self.mMapDocument.tilesetTileOffsetChanged.connect(self.tilesetTileOffsetChanged) self.mMapDocument.objectsInserted.connect(self.objectsInserted) self.mMapDocument.objectsRemoved.connect(self.objectsRemoved) self.mMapDocument.objectsChanged.connect(self.objectsChanged) self.mMapDocument.objectsIndexChanged.connect(self.objectsIndexChanged) self.mMapDocument.selectedObjectsChanged.connect(self.updateSelectedObjectItems) self.refreshScene() ## # Returns whether the tile grid is visible. ## def isGridVisible(self): return self.mGridVisible ## # Returns the set of selected map object items. ## def selectedObjectItems(self): return QSet(self.mSelectedObjectItems) ## # Sets the set of selected map object items. This translates to a call to # MapDocument.setSelectedObjects. ## def setSelectedObjectItems(self, items): # Inform the map document about the newly selected objects selectedObjects = QList() #selectedObjects.reserve(items.size()) for item in items: selectedObjects.append(item.mapObject()) self.mMapDocument.setSelectedObjects(selectedObjects) ## # Returns the MapObjectItem associated with the given \a mapObject. ## def itemForObject(self, object): return self.mObjectItems[object] ## # Enables the selected tool at this map scene. # Therefore it tells that tool, that this is the active map scene. ## def enableSelectedTool(self): if (not self.mSelectedTool or not self.mMapDocument): return self.mActiveTool = self.mSelectedTool self.mActiveTool.activate(self) self.mCurrentModifiers = QApplication.keyboardModifiers() if (self.mCurrentModifiers != Qt.NoModifier): self.mActiveTool.modifiersChanged(self.mCurrentModifiers) if (self.mUnderMouse): self.mActiveTool.mouseEntered() self.mActiveTool.mouseMoved(self.mLastMousePos, Qt.KeyboardModifiers()) def disableSelectedTool(self): if (not self.mActiveTool): return if (self.mUnderMouse): self.mActiveTool.mouseLeft() self.mActiveTool.deactivate(self) self.mActiveTool = None ## # Sets the currently selected tool. ## def setSelectedTool(self, tool): self.mSelectedTool = tool ## # QGraphicsScene.drawForeground override that draws the tile grid. ## def drawForeground(self, painter, rect): if (not self.mMapDocument or not self.mGridVisible): return offset = QPointF() # Take into account the offset of the current layer layer = self.mMapDocument.currentLayer() if layer: offset = layer.offset() painter.translate(offset) prefs = preferences.Preferences.instance() self.mMapDocument.renderer().drawGrid(painter, rect.translated(-offset), prefs.gridColor()) ## # Override for handling enter and leave events. ## def event(self, event): x = event.type() if x==QEvent.Enter: self.mUnderMouse = True if (self.mActiveTool): self.mActiveTool.mouseEntered() elif x==QEvent.Leave: self.mUnderMouse = False if (self.mActiveTool): self.mActiveTool.mouseLeft() else: pass return super().event(event) def keyPressEvent(self, event): if (self.mActiveTool): self.mActiveTool.keyPressed(event) if (not (self.mActiveTool and event.isAccepted())): super().keyPressEvent(event) def mouseMoveEvent(self, mouseEvent): self.mLastMousePos = mouseEvent.scenePos() if (not self.mMapDocument): return super().mouseMoveEvent(mouseEvent) if (mouseEvent.isAccepted()): return if (self.mActiveTool): self.mActiveTool.mouseMoved(mouseEvent.scenePos(), mouseEvent.modifiers()) mouseEvent.accept() def mousePressEvent(self, mouseEvent): super().mousePressEvent(mouseEvent) if (mouseEvent.isAccepted()): return if (self.mActiveTool): mouseEvent.accept() self.mActiveTool.mousePressed(mouseEvent) def mouseReleaseEvent(self, mouseEvent): super().mouseReleaseEvent(mouseEvent) if (mouseEvent.isAccepted()): return if (self.mActiveTool): mouseEvent.accept() self.mActiveTool.mouseReleased(mouseEvent) ## # Override to ignore drag enter events. ## def dragEnterEvent(self, event): event.ignore() ## # Sets whether the tile grid is visible. ## def setGridVisible(self, visible): if (self.mGridVisible == visible): return self.mGridVisible = visible self.update() def setObjectLineWidth(self, lineWidth): if (self.mObjectLineWidth == lineWidth): return self.mObjectLineWidth = lineWidth if (self.mMapDocument): self.mMapDocument.renderer().setObjectLineWidth(lineWidth) # Changing the line width can change the size of the object items if (not self.mObjectItems.isEmpty()): for item in self.mObjectItems: item[1].syncWithMapObject() self.update() def setShowTileObjectOutlines(self, enabled): if (self.mShowTileObjectOutlines == enabled): return self.mShowTileObjectOutlines = enabled if (self.mMapDocument): self.mMapDocument.renderer().setFlag(RenderFlag.ShowTileObjectOutlines, enabled) if (not self.mObjectItems.isEmpty()): self.update() ## # Sets whether the current layer should be highlighted. ## def setHighlightCurrentLayer(self, highlightCurrentLayer): if (self.mHighlightCurrentLayer == highlightCurrentLayer): return self.mHighlightCurrentLayer = highlightCurrentLayer self.updateCurrentLayerHighlight() ## # Refreshes the map scene. ## def refreshScene(self): self.mLayerItems.clear() self.mObjectItems.clear() self.removeItem(self.mDarkRectangle) self.clear() self.addItem(self.mDarkRectangle) if (not self.mMapDocument): self.setSceneRect(QRectF()) return self.updateSceneRect() map = self.mMapDocument.map() self.mLayerItems.resize(map.layerCount()) if (map.backgroundColor().isValid()): self.setBackgroundBrush(map.backgroundColor()) else: self.setBackgroundBrush(self.mDefaultBackgroundColor) layerIndex = 0 for layer in map.layers(): layerItem = self.createLayerItem(layer) layerItem.setZValue(layerIndex) self.addItem(layerItem) self.mLayerItems[layerIndex] = layerItem layerIndex += 1 tileSelectionItem = TileSelectionItem(self.mMapDocument) tileSelectionItem.setZValue(10000 - 2) self.addItem(tileSelectionItem) self.mObjectSelectionItem = ObjectSelectionItem(self.mMapDocument) self.mObjectSelectionItem.setZValue(10000 - 1) self.addItem(self.mObjectSelectionItem) self.updateCurrentLayerHighlight() ## # Repaints the specified region. The region is in tile coordinates. ## def repaintRegion(self, region, layer): renderer = self.mMapDocument.renderer() margins = self.mMapDocument.map().drawMargins() for r in region.rects(): boundingRect = QRectF(renderer.boundingRect(r)) self.update(QRectF(renderer.boundingRect(r).adjusted(-margins.left(), -margins.top(), margins.right(), margins.bottom()))) boundingRect.translate(layer.offset()) self.update(boundingRect) def currentLayerIndexChanged(self): self.updateCurrentLayerHighlight() # New layer may have a different offset, affecting the grid if self.mGridVisible: self.update() ## # Adapts the scene, layers and objects to new map size, orientation or # background color. ## def mapChanged(self): self.updateSceneRect() for item in self.mLayerItems: tli = item if type(tli) == TileLayerItem: tli.syncWithTileLayer() for item in self.mObjectItems.values(): item.syncWithMapObject() map = self.mMapDocument.map() if (map.backgroundColor().isValid()): self.setBackgroundBrush(map.backgroundColor()) else: self.setBackgroundBrush(self.mDefaultBackgroundColor) def tilesetChanged(self, tileset): if (not self.mMapDocument): return if (contains(self.mMapDocument.map().tilesets(), tileset)): self.update() def tileLayerDrawMarginsChanged(self, tileLayer): index = self.mMapDocument.map().layers().indexOf(tileLayer) item = self.mLayerItems.at(index) item.syncWithTileLayer() def layerAdded(self, index): layer = self.mMapDocument.map().layerAt(index) layerItem = self.createLayerItem(layer) self.addItem(layerItem) self.mLayerItems.insert(index, layerItem) z = 0 for item in self.mLayerItems: item.setZValue(z) z += 1 def layerRemoved(self, index): self.mLayerItems.remove(index) ## # A layer has changed. This can mean that the layer visibility, opacity or # offset changed. ## def layerChanged(self, index): layer = self.mMapDocument.map().layerAt(index) layerItem = self.mLayerItems.at(index) layerItem.setVisible(layer.isVisible()) multiplier = 1 if (self.mHighlightCurrentLayer and self.mMapDocument.currentLayerIndex() < index): multiplier = opacityFactor layerItem.setOpacity(layer.opacity() * multiplier) layerItem.setPos(layer.offset()) # Layer offset may have changed, affecting the scene rect and grid self.updateSceneRect() if self.mGridVisible: self.update() ## # When an object group has changed it may mean its color or drawing order # changed, which affects all its objects. ## def objectGroupChanged(self, objectGroup): self.objectsChanged(objectGroup.objects()) self.objectsIndexChanged(objectGroup, 0, objectGroup.objectCount() - 1) ## # When an image layer has changed, it may change size and it may look # differently. ## def imageLayerChanged(self, imageLayer): index = self.mMapDocument.map().layers().indexOf(imageLayer) item = self.mLayerItems.at(index) item.syncWithImageLayer() item.update() ## # When the tile offset of a tileset has changed, it can affect the bounding # rect of all tile layers and tile objects. It also requires a full repaint. ## def tilesetTileOffsetChanged(self, tileset): self.update() for item in self.mLayerItems: tli = item if type(tli) == TileLayerItem: tli.syncWithTileLayer() for item in self.mObjectItems: cell = item.mapObject().cell() if (not cell.isEmpty() and cell.tile.tileset() == tileset): item.syncWithMapObject() ## # Inserts map object items for the given objects. ## def objectsInserted(self, objectGroup, first, last): ogItem = None # Find the object group item for the object group for item in self.mLayerItems: ogi = item if type(ogi)==ObjectGroupItem: if (ogi.objectGroup() == objectGroup): ogItem = ogi break drawOrder = objectGroup.drawOrder() for i in range(first, last+1): object = objectGroup.objectAt(i) item = MapObjectItem(object, self.mMapDocument, ogItem) if (drawOrder == ObjectGroup.DrawOrder.TopDownOrder): item.setZValue(item.y()) else: item.setZValue(i) self.mObjectItems.insert(object, item) ## # Removes the map object items related to the given objects. ## def objectsRemoved(self, objects): for o in objects: i = self.mObjectItems.find(o) self.mSelectedObjectItems.remove(i) # python would not force delete QGraphicsItem self.removeItem(i) self.mObjectItems.erase(o) ## # Updates the map object items related to the given objects. ## def objectsChanged(self, objects): for object in objects: item = self.itemForObject(object) item.syncWithMapObject() ## # Updates the Z value of the objects when appropriate. ## def objectsIndexChanged(self, objectGroup, first, last): if (objectGroup.drawOrder() != ObjectGroup.DrawOrder.IndexOrder): return for i in range(first, last+1): item = self.itemForObject(objectGroup.objectAt(i)) item.setZValue(i) def updateSelectedObjectItems(self): objects = self.mMapDocument.selectedObjects() items = QSet() for object in objects: item = self.itemForObject(object) if item: items.insert(item) self.mSelectedObjectItems = items self.selectedObjectItemsChanged.emit() def syncAllObjectItems(self): for item in self.mObjectItems: item.syncWithMapObject() def createLayerItem(self, layer): layerItem = None tl = layer.asTileLayer() if tl: layerItem = TileLayerItem(tl, self.mMapDocument) else: og = layer.asObjectGroup() if og: drawOrder = og.drawOrder() ogItem = ObjectGroupItem(og) objectIndex = 0 for object in og.objects(): item = MapObjectItem(object, self.mMapDocument, ogItem) if (drawOrder == ObjectGroup.DrawOrder.TopDownOrder): item.setZValue(item.y()) else: item.setZValue(objectIndex) self.mObjectItems.insert(object, item) objectIndex += 1 layerItem = ogItem else: il = layer.asImageLayer() if il: layerItem = ImageLayerItem(il, self.mMapDocument) layerItem.setVisible(layer.isVisible()) return layerItem def updateSceneRect(self): mapSize = self.mMapDocument.renderer().mapSize() sceneRect = QRectF(0, 0, mapSize.width(), mapSize.height()) margins = self.mMapDocument.map().computeLayerOffsetMargins() sceneRect.adjust(-margins.left(), -margins.top(), margins.right(), margins.bottom()) self.setSceneRect(sceneRect) self.mDarkRectangle.setRect(sceneRect) def updateCurrentLayerHighlight(self): if (not self.mMapDocument): return currentLayerIndex = self.mMapDocument.currentLayerIndex() if (not self.mHighlightCurrentLayer or currentLayerIndex == -1): self.mDarkRectangle.setVisible(False) # Restore opacity for all layers for i in range(self.mLayerItems.size()): layer = self.mMapDocument.map().layerAt(i) self.mLayerItems.at(i).setOpacity(layer.opacity()) return # Darken layers below the current layer self.mDarkRectangle.setZValue(currentLayerIndex - 0.5) self.mDarkRectangle.setVisible(True) # Set layers above the current layer to half opacity for i in range(1, self.mLayerItems.size()): layer = self.mMapDocument.map().layerAt(i) if currentLayerIndex < i: _x = opacityFactor else: _x = 1 multiplier = _x self.mLayerItems.at(i).setOpacity(layer.opacity() * multiplier) def eventFilter(self, object, event): x = event.type() if x==QEvent.KeyPress or x==QEvent.KeyRelease: keyEvent = event newModifiers = keyEvent.modifiers() if (self.mActiveTool and newModifiers != self.mCurrentModifiers): self.mActiveTool.modifiersChanged(newModifiers) self.mCurrentModifiers = newModifiers else: pass return False