def __init__(self, parent=None): QGraphicsView.__init__(self, parent) self.scene = QGraphicsScene(self) self.scene.setBackgroundBrush(Qt.yellow) self.setScene(self.scene) center_rect = QGraphicsRectItem(-10, -10, 20, 20) self.scene.addItem(center_rect) rect = QGraphicsRectItem(0, 0, 100, 100) rect.setPos(-100, -100) self.scene.addItem(rect) rect2 = QGraphicsRectItem(0, 0, 100, 100) self.scene.addItem(rect2) text = self.scene.addText("Hello WOrld") # self.scene.addLine(QLineF(0, 10, -20, -20), QPen(Qt.black)) rect2.moveBy(50, 50) rect2.setRotation(50) rect2.moveBy(-50, -50) print(self.scene.width(), self.scene.height()) self.resize(400, 400)
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 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 create_scene_tile(self, x: int, y: int, tile) -> QGraphicsItem: x, y = self.pos_to_scene(x, y) width = 2 * qt_drawings.tile_size if tile.is_horizontal else qt_drawings.tile_size height = qt_drawings.tile_size if tile.is_horizontal else 2 * qt_drawings.tile_size item = QGraphicsRectItem(0, 0, width, height) item.setPos(x, y) item.setBrush(qt_drawings.tile_to_brush(tile)) item.setPen(qt_drawings.black_pen) self.scene.addItem(item) return item
def display_current(self): """Create a rectangle showing the current window.""" if self.idx_current in self.scene.items(): self.scene.removeItem(self.idx_current) item = QGraphicsRectItem(0, CURR['pos0'], self.parent.value('window_length'), CURR['pos1']) # it's necessary to create rect first, and then move it item.setPos(self.parent.value('window_start'), 0) item.setPen(QPen(Qt.lightGray)) item.setBrush(QBrush(Qt.lightGray)) item.setZValue(-10) self.scene.addItem(item) self.idx_current = item
class AnimatedClock(): updateTimer = None calDataLock = threading.Lock() calDataUpdated = False curCalendars = None def __init__(self, scene, widthClkTextArea, heightClkTextArea, borders, updateSecs): self.masterScene = scene self.widthClkTextArea = widthClkTextArea self.heightClkTextArea = heightClkTextArea self.borders = borders self.updateSecs = updateSecs # Background self.textBkgd = QGraphicsRectItem(0, 0, self.widthClkTextArea, self.heightClkTextArea) self.textBkgd.setPos(self.borders[3], self.borders[0]) self.textBkgd.setBrush(QColor("light green")) self.textBkgd.setZValue(10) scene.addItem(self.textBkgd) # Text Item self.textItem = QGraphicsTextItem() self.textItem.setFont(QFont("Segoe UI", 80)) self.textItem.setDefaultTextColor(QColor("black")) self.textItem.setPos(QPointF(self.borders[3]+10,self.borders[0]+5)) self.textItem.setHtml("<B>Clock</B>") self.textItem.setZValue(20) self.textItem.setTextWidth(self.widthClkTextArea-20) scene.addItem(self.textItem) def start(self): self.updateTimer = QTimer() self.updateTimer.setInterval(self.updateSecs * 1000) self.updateTimer.timeout.connect(self.updateClock) self.updateTimer.start() def stop (self): if self.updateTimer != None: self.updateTimer.stop() def updateClock(self): localtime = time.localtime() # dateString = time.strftime("%a %d %b %Y", localtime) timeString = time.strftime("%H:%M:%S", localtime) self.textItem.setHtml(timeString) # self.textItem.setTextWidth(self.widthCalTextArea-20) self.textItem.update()
def on_actItem_Rect(self): # 添加一个矩形 _triggered rect = QRectF(-50, -25, 100, 50) item = QGraphicsRectItem(rect) # x,y 为左上角的图元局部坐标,图元中心点为0,0 item.rect = rect item.setFlags(QGraphicsItem.ItemIsMovable | QGraphicsItem.ItemIsSelectable | QGraphicsItem.ItemIsFocusable) item.brush = Qt.yellow item.setBrush(QBrush(item.brush)) item.style = Qt.SolidLine #item.setTransform(QTransform()) self.view.frontZ = self.view.frontZ + 1 item.setZValue(self.view.frontZ) item.setPos(-50 + (QtCore.qrand() % 100), -50 + (QtCore.qrand() % 100)) self.view.seqNum = self.view.seqNum + 1 item.setData(self.view.ItemId, self.view.seqNum) item.setData(self.view.ItemDesciption, "矩形") self.scene.addItem(item) self.scene.clearSelection() item.setSelected(True)
def add_game_grid(self): ''' Adds an QGraphicsItem for each square in the game. Qt uses QGraphicsItems to draw objects in the QGraphicsScene. QGraphicsRectItem is a subclass of QGraphicsItem, and is useful for easily drawing rectangular items. This method should only be called once, otherwise it creates duplicates! ''' x = 0 y = 0 for i in range(self.game.get_width()): for j in range(self.game.get_height()): new_square = QGraphicsRectItem(0, 0, self.square_size, self.square_size) new_square.setPos(x, y) self.square_graphics.append(new_square) self.scene.addItem(new_square) y += self.square_size x += self.square_size y = 0
def __init__(self, scene): super(Setbuttons, self).__init__() self.scene = scene self.buttons = {} # Register of buttons as {name: button} buttonParent = QGraphicsRectItem() buttonParent.persistent = True self.playButton, self.acceptButton, self.passButton = None, None, None self.challengeButton, self.exchangeButton, self.quitButton = None, None, None self.nextButton, self.backButton, self.endButton, self.newButton = None, None, None, None self.setup_buttons(buttonParent) self.set_button_pos() # self.reset_tips() self.scene.addItem(buttonParent) buttonParent.setScale(0.75) buttonParent.setPos(200, 200) buttonParent.setZValue(65)
def __init__(self, scene): super(Setbuttons, self).__init__() self.scene = scene self.buttons = {} # Register of buttons as {name: button} self.setupButton, self.checkButton, self.loadButton, self.saveButton = None, None, None, None self.clearButton, self.playButton, self.printButton, self.solveButton = None, None, None, None self.printButton, self.solveButton, self.stepButton, self.quitButton = None, None, None, None self.hintButton, self.restartButton = None, None buttonParent = QGraphicsRectItem() self.playButton = None self.checkButton = None self.setup_buttons(buttonParent) self.set_button_pos() self.reset_tips() buttonParent.setScale(0.75) buttonParent.setPos(Cons.BUTTON_X, Cons.BUTTON_Y) buttonParent.setZValue(65) self.scene.addItem(buttonParent)
def display_markers(self): """Add markers on top of first plot.""" for item in self.idx_markers: self.scene.removeItem(item) self.idx_markers = [] window_start = self.parent.value('window_start') window_length = self.parent.value('window_length') window_end = window_start + window_length y_distance = self.parent.value('y_distance') markers = [] if self.parent.info.markers is not None: if self.parent.value('marker_show'): markers = self.parent.info.markers for mrk in markers: if window_start <= mrk['end'] and window_end >= mrk['start']: mrk_start = max((mrk['start'], window_start)) mrk_end = min((mrk['end'], window_end)) color = QColor(self.parent.value('marker_color')) item = QGraphicsRectItem(mrk_start, 0, mrk_end - mrk_start, len(self.idx_label) * y_distance) item.setPen(color) item.setBrush(color) item.setZValue(-9) self.scene.addItem(item) item = TextItem_with_BG(color.darker(200)) item.setText(mrk['name']) item.setPos(mrk['start'], len(self.idx_label) * self.parent.value('y_distance')) item.setFlag(QGraphicsItem.ItemIgnoresTransformations) item.setRotation(-90) self.scene.addItem(item) self.idx_markers.append(item)
def __init__(self, simu, f, motion_manager): """AircraftItem constructor, creates the ellipse and adds to the scene""" super().__init__(None) self.setZValue(radarview.PLOT_Z_VALUE) # instance variables self.motion_manager = motion_manager self.flight = f self.simulation = simu # list to store the comet elements as ellipses self.comete = [] # create the tooltip tooltip = f.type.name + ' ' + f.call_sign + ' ' + f.qfu time = self.simulation.t for (idx, ti) in enumerate(range(time - 4, time + 1)): if idx < 4: item = QGraphicsRectItem() item.setPen(DEP_PEN if self.flight.type == traffic.Movement.DEP else ARR_PEN) else: item = QGraphicsEllipseItem() item.setBrush(DEP_BRUSH if self.flight.type == traffic.Movement.DEP else ARR_BRUSH) item.setToolTip(tooltip) self.comete.append(item) self.addToGroup(item) position = self.flight.get_position(ti) item.setPos(position.x, position.y) # compute the width of each element self.update_size() # connect to ask_inspection_signal in order to toggle_highlight this item self.motion_manager.radarView.ask_inspection_signal.connect( self.toggle_highlight)
class Square(QGraphicsView): def __init__(self, x, y, a, b): self.x = x self.y = y self.a = a self.b = b self.square = QGraphicsRectItem(self.x, self.y, self.a, self.b) def draw_square(self, scene): scene.addItem(self.square) def move_square(self, m, n): self.square.setPos(m, n) def rotate_square(self, r): self.square.setRotation(r) def scale_square(self, s): self.square.setScale((s + 50) / 100) def recolor_square(self, r, g, b): self.square.setBrush(QColor(r, g, b))
class ForcedXoverNode3(QGraphicsRectItem): """ This is a QGraphicsRectItem to allow actions and also a QGraphicsSimpleTextItem to allow a label to be drawn """ def __init__(self, virtual_helix_item, xover_item, strand3p, idx): super(ForcedXoverNode3, self).__init__(virtual_helix_item) self._vhi = virtual_helix_item self._xover_item = xover_item self._idx = idx self._is_on_top = virtual_helix_item.isStrandOnTop(strand3p) self._is_drawn_5_to_3 = strand3p.strandSet().isDrawn5to3() self._strand_type = strand3p.strandSet().strandType() self._partner_virtual_helix = virtual_helix_item self._blank_thing = QGraphicsRectItem(_blankRect, self) self._blank_thing.setBrush(QBrush(Qt.white)) self._path_thing = QGraphicsPathItem(self) self.configurePath() self.setPen(_NO_PEN) self._label = None self.setPen(_NO_PEN) self.setBrush(_NO_BRUSH) self.setRect(_rect) self.setZValue(styles.ZENDPOINTITEM + 1) # end def def updateForFloatFromVHI(self, virtual_helix_item, strand_type, idx_x, idx_y): """ """ self._vhi = virtual_helix_item self.setParentItem(virtual_helix_item) self._strand_type = strand_type self._idx = idx_x self._is_on_top = self._is_drawn_5_to_3 = True if idx_y == 0 else False self.updatePositionAndAppearance(is_from_strand=False) # end def def updateForFloatFromStrand(self, virtual_helix_item, strand3p, idx): """ """ self._vhi = virtual_helix_item self._strand = strand3p self.setParentItem(virtual_helix_item) self._idx = idx self._is_on_top = virtual_helix_item.isStrandOnTop(strand3p) self._is_drawn_5_to_3 = strand3p.strandSet().isDrawn5to3() self._strand_type = strand3p.strandSet().strandType() self.updatePositionAndAppearance() # end def def strandType(self): return self._strand_type # end def def configurePath(self): self._path_thing.setBrush(QBrush(styles.RED_STROKE)) path = PPR3 if self._is_drawn_5_to_3 else PPL3 offset = -_BASE_WIDTH if self._is_drawn_5_to_3 else _BASE_WIDTH self._path_thing.setPath(path) self._path_thing.setPos(offset, 0) offset = -_BASE_WIDTH if self._is_drawn_5_to_3 else 0 self._blank_thing.setPos(offset, 0) self._blank_thing.show() self._path_thing.show() # end def def refreshXover(self): self._xover_item.refreshXover() # end def def setPartnerVirtualHelix(self, virtual_helix_item): self._partner_virtual_helix = virtual_helix_item # end def def idx(self): return self._idx # end def def virtualHelixItem(self): return self._vhi # end def def point(self): return self._vhi.upperLeftCornerOfBaseType(self._idx, self._strand_type) # end def def floatPoint(self): pt = self.pos() return pt.x(), pt.y() # end def def isOnTop(self): return self._is_on_top # end def def isDrawn5to3(self): return self._is_drawn_5_to_3 # end def def updatePositionAndAppearance(self, is_from_strand=True): """ Sets position by asking the VirtualHelixItem Sets appearance by choosing among pre-defined painterpaths (from normalstrandgraphicsitem) depending on drawing direction. """ self.setPos(*self.point()) n5 = self._xover_item._node5 if is_from_strand: from_strand, from_idx = (n5._strand, n5._idx) if n5 != self else (None, None) if self._strand.canInstallXoverAt(self._idx, from_strand, from_idx): self.configurePath() # We can only expose a 5' end. But on which side? is_left = True if self._is_drawn_5_to_3 else False self._updateLabel(is_left) else: self.hideItems() else: self.hideItems() # end def def updateConnectivity(self): is_left = True if self._is_drawn_5_to_3 else False self._updateLabel(is_left) # end def def remove(self): """ Clean up this joint """ scene = self.scene() scene.removeItem(self._label) self._label = None scene.removeItem(self._path_thing) self._path_thing = None scene.removeItem(self._blank_thing) self._blank_thing = None scene.removeItem(self) # end def def _updateLabel(self, is_left): """ Called by updatePositionAndAppearance during init, or later by updateConnectivity. Updates drawing and position of the label. """ lbl = self._label if self._idx != None: bw = _BASE_WIDTH num = self._partner_virtual_helix.number() tBR = _FM.tightBoundingRect(str(num)) half_label_h = tBR.height()/2.0 half_label_w = tBR.width()/2.0 # determine x and y positions label_x = bw/2.0 - half_label_w if self._is_on_top: label_y = -0.25*half_label_h - 0.5 - 0.5*bw else: label_y = 2*half_label_h + 0.5 + 0.5*bw # adjust x for left vs right label_x_offset = 0.25*bw if is_left else -0.25*bw label_x += label_x_offset # adjust x for numeral 1 if num == 1: label_x -= half_label_w/2.0 # create text item if lbl == None: lbl = QGraphicsSimpleTextItem(str(num), self) lbl.setPos(label_x, label_y) lbl.setBrush(_ENAB_BRUSH) lbl.setFont(_TO_HELIX_NUM_FONT) self._label = lbl lbl.setText( str(self._partner_virtual_helix.number()) ) lbl.show() # end if # end def def hideItems(self): if self._label: self._label.hide() if self._blank_thing: self._path_thing.hide() if self._blank_thing: self._blank_thing.hide()
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)
def mouseMoveEvent(self, event): """When normal selection, update power spectrum with current selection. Otherwise, show the range of the new marker. """ if not self.scene: return if self.idx_sel in self.scene.items(): self.scene.removeItem(self.idx_sel) self.idx_sel = None chk_marker = self.parent.notes.action['new_bookmark'].isChecked() chk_event = self.parent.notes.action['new_event'].isChecked() if chk_marker or chk_event: xy_scene = self.mapToScene(event.pos()) y_distance = self.parent.value('y_distance') pos = QRectF(self.sel_xy[0], 0, xy_scene.x() - self.sel_xy[0], len(self.idx_label) * y_distance) item = QGraphicsRectItem(pos.normalized()) item.setPen(NoPen) if chk_marker: color = QColor(self.parent.value('annot_bookmark_color')) elif chk_event: eventtype = self.parent.notes.idx_eventtype.currentText() color = convert_name_to_color(eventtype) item.setBrush(QBrush(color.lighter(115))) item.setZValue(-10) self.scene.addItem(item) self.idx_sel = item return xy_scene = self.mapToScene(event.pos()) pos = QRectF(self.sel_xy[0], self.sel_xy[1], xy_scene.x() - self.sel_xy[0], xy_scene.y() - self.sel_xy[1]) self.idx_sel = QGraphicsRectItem(pos.normalized()) self.idx_sel.setPen(QPen(QColor(LINE_COLOR), LINE_WIDTH)) self.scene.addItem(self.idx_sel) if self.idx_info in self.scene.items(): self.scene.removeItem(self.idx_info) duration = '{0:0.2f}s'.format(abs(xy_scene.x() - self.sel_xy[0])) # get y-size, based on scaling too y = abs(xy_scene.y() - self.sel_xy[1]) scale = self.parent.value('y_scale') * self.chan_scale[self.sel_chan] height = '{0:0.3f}uV'.format(y / scale) item = TextItem_with_BG() item.setText(duration + ' ' + height) item.setPos(self.sel_xy[0], self.sel_xy[1]) self.scene.addItem(item) self.idx_info = item trial = 0 time = self.parent.traces.data.axis['time'][trial] beg_win = min((self.sel_xy[0], xy_scene.x())) end_win = max((self.sel_xy[0], xy_scene.x())) time_of_interest = time[(time >= beg_win) & (time < end_win)] if len(time_of_interest) > MINIMUM_N_SAMPLES: data = self.parent.traces.data(trial=trial, chan=self.chan[self.sel_chan], time=time_of_interest) n_data = len(data) n_pad = (power(2, ceil(log2(n_data))) - n_data) / 2 data = pad(data, (int(ceil(n_pad)), int(floor(n_pad))), 'constant') self.parent.spectrum.display(data)
def _set_square(disk: QGraphicsRectItem, x0, y0, r): """Helper to center `square` on `x0`, `y0` w radius `r`""" disk.setRect(0, 0, 2 * r, 2 * r) disk.setPos(x0 - r, y0 - r)
class ForcedXoverNode3(QGraphicsRectItem): """ This is a QGraphicsRectItem to allow actions and also a QGraphicsSimpleTextItem to allow a label to be drawn Attributes: is_forward (TYPE): Description """ def __init__(self, virtual_helix_item, xover_item, strand3p, idx): """Summary Args: virtual_helix_item (cadnano.gui.views.pathview.virtualhelixitem.VirtualHelixItem): from vhi xover_item (TYPE): Description strand3p (Strand): reference to the 3' strand idx (int): the base index within the virtual helix """ super(ForcedXoverNode3, self).__init__(virtual_helix_item) self._vhi = virtual_helix_item self._xover_item = xover_item self._idx = idx self.is_forward = strand3p.strandSet().isForward() self._is_on_top = self.is_forward self._partner_virtual_helix = virtual_helix_item self._blank_thing = QGraphicsRectItem(_blankRect, self) self._blank_thing.setBrush(QBrush(Qt.white)) self._path_thing = QGraphicsPathItem(self) self.configurePath() self._label = None self.setPen(_NO_PEN) self.setBrush(_NO_BRUSH) self.setRect(_rect) self.setZValue(styles.ZENDPOINTITEM + 1) # end def def updateForFloatFromVHI(self, virtual_helix_item, is_forward, idx_x, idx_y): """ Args: virtual_helix_item (cadnano.gui.views.pathview.virtualhelixitem.VirtualHelixItem): Description is_forward (TYPE): Description idx_x (TYPE): Description idx_y (TYPE): Description """ self._vhi = virtual_helix_item self.setParentItem(virtual_helix_item) self._idx = idx_x self._is_on_top = self.is_forward = True if is_forward else False self.updatePositionAndAppearance(is_from_strand=False) # end def def updateForFloatFromStrand(self, virtual_helix_item, strand3p, idx): """ Args: virtual_helix_item (cadnano.gui.views.pathview.virtualhelixitem.VirtualHelixItem): Description strand3p (Strand): reference to the 3' strand idx (int): the base index within the virtual helix """ self._vhi = virtual_helix_item self._strand = strand3p self.setParentItem(virtual_helix_item) self._idx = idx self._is_on_top = self.is_forward = strand3p.strandSet().isForward() self.updatePositionAndAppearance() # end def def configurePath(self): """Summary Returns: TYPE: Description """ self._path_thing.setBrush(getBrushObj(_PENCIL_COLOR)) path = PPR3 if self.is_forward else PPL3 offset = -_BASE_WIDTH if self.is_forward else _BASE_WIDTH self._path_thing.setPath(path) self._path_thing.setPos(offset, 0) offset = -_BASE_WIDTH if self.is_forward else 0 self._blank_thing.setPos(offset, 0) self._blank_thing.show() self._path_thing.show() # end def def refreshXover(self): """Summary Returns: TYPE: Description """ self._xover_item.refreshXover() # end def def setPartnerVirtualHelix(self, virtual_helix_item): """Summary Args: virtual_helix_item (cadnano.gui.views.pathview.virtualhelixitem.VirtualHelixItem): Description Returns: TYPE: Description """ self._partner_virtual_helix = virtual_helix_item # end def def idx(self): """Summary Returns: TYPE: Description """ return self._idx # end def def virtualHelixItem(self): """Summary Returns: TYPE: Description """ return self._vhi # end def def point(self): """Summary Returns: TYPE: Description """ return self._vhi.upperLeftCornerOfBaseType(self._idx, self.is_forward) # end def def floatPoint(self): """Summary Returns: TYPE: Description """ pt = self.pos() return pt.x(), pt.y() # end def def isForward(self): """Summary Returns: TYPE: Description """ return self.is_forward # end def def updatePositionAndAppearance(self, is_from_strand=True): """ Sets position by asking the VirtualHelixItem Sets appearance by choosing among pre-defined painterpaths (from normalstrandgraphicsitem) depending on drawing direction. Args: is_from_strand (bool, optional): Description """ self.setPos(*self.point()) n5 = self._xover_item._node5 if is_from_strand: from_strand, from_idx = (n5._strand, n5._idx) if n5 != self else (None, None) if self._strand.canInstallXoverAt(self._idx, from_strand, from_idx): self.configurePath() # We can only expose a 5' end. But on which side? is_left = True if self.is_forward else False self._updateLabel(is_left) else: self.hideItems() else: self.hideItems() # end def def remove(self): """Clean up this joint """ scene = self.scene() scene.removeItem(self._label) self._label = None scene.removeItem(self._path_thing) self._path_thing = None scene.removeItem(self._blank_thing) self._blank_thing = None scene.removeItem(self) # end def def _updateLabel(self, is_left): """Called by updatePositionAndAppearance during init. Updates drawing and position of the label. Args: is_left (TYPE): Description """ lbl = self._label if self._idx is not None: bw = _BASE_WIDTH num = self._partner_virtual_helix.idNum() tBR = _FM.tightBoundingRect(str(num)) half_label_h = tBR.height()/2.0 half_label_w = tBR.width()/2.0 # determine x and y positions label_x = bw/2.0 - half_label_w if self._is_on_top: label_y = -0.25*half_label_h - 0.5 - 0.5*bw else: label_y = 2*half_label_h + 0.5 + 0.5*bw # adjust x for left vs right label_x_offset = 0.25*bw if is_left else -0.25*bw label_x += label_x_offset # adjust x for numeral 1 if num == 1: label_x -= half_label_w/2.0 # create text item if lbl is None: lbl = QGraphicsSimpleTextItem(str(num), self) lbl.setPos(label_x, label_y) lbl.setBrush(_ENAB_BRUSH) lbl.setFont(_TO_HELIX_NUM_FONT) self._label = lbl lbl.setText(str(self._partner_virtual_helix.idNum())) lbl.show() # end if # end def def hideItems(self): """Summary Returns: TYPE: Description """ if self._label: self._label.hide() if self._blank_thing: self._path_thing.hide() if self._blank_thing: self._blank_thing.hide()
def mouseMoveEvent(self, event): """When normal selection, update power spectrum with current selection. Otherwise, show the range of the new marker. """ if not self.scene: return if self.event_sel or self.deselect: return if self.sel_xy[0] is None or self.sel_xy[1] is None: return if self.idx_sel in self.scene.items(): self.scene.removeItem(self.idx_sel) self.idx_sel = None chk_marker = self.parent.notes.action['new_bookmark'].isChecked() chk_event = self.parent.notes.action['new_event'].isChecked() if chk_marker or chk_event: xy_scene = self.mapToScene(event.pos()) y_distance = self.parent.value('y_distance') pos = QRectF(self.sel_xy[0], 0, xy_scene.x() - self.sel_xy[0], len(self.idx_label) * y_distance) item = QGraphicsRectItem(pos.normalized()) item.setPen(NoPen) if chk_marker: color = QColor(self.parent.value('annot_bookmark_color')) elif chk_event: eventtype = self.parent.notes.idx_eventtype.currentText() color = convert_name_to_color(eventtype) item.setBrush(QBrush(color.lighter(115))) item.setZValue(-10) self.scene.addItem(item) self.idx_sel = item return xy_scene = self.mapToScene(event.pos()) pos = QRectF(self.sel_xy[0], self.sel_xy[1], xy_scene.x() - self.sel_xy[0], xy_scene.y() - self.sel_xy[1]) self.idx_sel = QGraphicsRectItem(pos.normalized()) self.idx_sel.setPen(QPen(QColor(LINE_COLOR), LINE_WIDTH)) self.scene.addItem(self.idx_sel) if self.idx_info in self.scene.items(): self.scene.removeItem(self.idx_info) duration = '{0:0.3f}s'.format(abs(xy_scene.x() - self.sel_xy[0])) # get y-size, based on scaling too y = abs(xy_scene.y() - self.sel_xy[1]) scale = self.parent.value('y_scale') * self.chan_scale[self.sel_chan] height = '{0:0.3f}uV'.format(y / scale) item = TextItem_with_BG() item.setText(duration + ' ' + height) item.setPos(self.sel_xy[0], self.sel_xy[1]) self.scene.addItem(item) self.idx_info = item trial = 0 time = self.parent.traces.data.axis['time'][trial] beg_win = min((self.sel_xy[0], xy_scene.x())) end_win = max((self.sel_xy[0], xy_scene.x())) time_of_interest = time[(time >= beg_win) & (time < end_win)] if len(time_of_interest) > MINIMUM_N_SAMPLES: data = self.parent.traces.data(trial=trial, chan=self.chan[self.sel_chan], time=time_of_interest) n_data = len(data) n_pad = (power(2, ceil(log2(n_data))) - n_data) / 2 data = pad(data, (int(ceil(n_pad)), int(floor(n_pad))), 'constant') self.parent.spectrum.display(data)
class ForcedXoverNode3(QGraphicsRectItem): """ This is a QGraphicsRectItem to allow actions and also a QGraphicsSimpleTextItem to allow a label to be drawn Attributes: is_forward (TYPE): Description """ def __init__(self, virtual_helix_item, xover_item, strand3p, idx): """Summary Args: virtual_helix_item (cadnano.views.pathview.virtualhelixitem.VirtualHelixItem): from vhi xover_item (TYPE): Description strand3p (Strand): reference to the 3' strand idx (int): the base index within the virtual helix """ super(ForcedXoverNode3, self).__init__(virtual_helix_item) self._vhi = virtual_helix_item self._xover_item = xover_item self._idx = idx self.is_forward = strand3p.strandSet().isForward() self._is_on_top = self.is_forward self._partner_virtual_helix = virtual_helix_item self._blank_thing = QGraphicsRectItem(_blankRect, self) self._blank_thing.setBrush(QBrush(Qt.white)) self._path_thing = QGraphicsPathItem(self) self.configurePath() self._label = None self.setPen(_NO_PEN) self.setBrush(_NO_BRUSH) self.setRect(_rect) self.setZValue(styles.ZENDPOINTITEM + 1) # end def def updateForFloatFromVHI(self, virtual_helix_item, is_forward, idx_x, idx_y): """ Args: virtual_helix_item (cadnano.views.pathview.virtualhelixitem.VirtualHelixItem): Description is_forward (TYPE): Description idx_x (TYPE): Description idx_y (TYPE): Description """ self._vhi = virtual_helix_item self.setParentItem(virtual_helix_item) self._idx = idx_x self._is_on_top = self.is_forward = True if is_forward else False self.updatePositionAndAppearance(is_from_strand=False) # end def def updateForFloatFromStrand(self, virtual_helix_item, strand3p, idx): """ Args: virtual_helix_item (cadnano.views.pathview.virtualhelixitem.VirtualHelixItem): Description strand3p (Strand): reference to the 3' strand idx (int): the base index within the virtual helix """ self._vhi = virtual_helix_item self._strand = strand3p self.setParentItem(virtual_helix_item) self._idx = idx self._is_on_top = self.is_forward = strand3p.strandSet().isForward() self.updatePositionAndAppearance() # end def def configurePath(self): """Summary Returns: TYPE: Description """ self._path_thing.setBrush(getBrushObj(_PENCIL_COLOR)) path = PPR3 if self.is_forward else PPL3 offset = -_BASE_WIDTH if self.is_forward else _BASE_WIDTH self._path_thing.setPath(path) self._path_thing.setPos(offset, 0) offset = -_BASE_WIDTH if self.is_forward else 0 self._blank_thing.setPos(offset, 0) self._blank_thing.show() self._path_thing.show() # end def def refreshXover(self): """Summary Returns: TYPE: Description """ self._xover_item.refreshXover() # end def def setPartnerVirtualHelix(self, virtual_helix_item): """Summary Args: virtual_helix_item (cadnano.views.pathview.virtualhelixitem.VirtualHelixItem): Description Returns: TYPE: Description """ self._partner_virtual_helix = virtual_helix_item # end def def idx(self): """Summary Returns: TYPE: Description """ return self._idx # end def def virtualHelixItem(self): """Summary Returns: TYPE: Description """ return self._vhi # end def def point(self): """Summary Returns: TYPE: Description """ return self._vhi.upperLeftCornerOfBaseType(self._idx, self.is_forward) # end def def floatPoint(self): """Summary Returns: TYPE: Description """ pt = self.pos() return pt.x(), pt.y() # end def def isForward(self): """Summary Returns: TYPE: Description """ return self.is_forward # end def def updatePositionAndAppearance(self, is_from_strand=True): """ Sets position by asking the VirtualHelixItem Sets appearance by choosing among pre-defined painterpaths (from normalstrandgraphicsitem) depending on drawing direction. Args: is_from_strand (bool, optional): Description """ self.setPos(*self.point()) n5 = self._xover_item._node5 if is_from_strand: from_strand, from_idx = (n5._strand, n5._idx) if n5 != self else (None, None) if self._strand.canInstallXoverAt(self._idx, from_strand, from_idx): self.configurePath() # We can only expose a 5' end. But on which side? is_left = True if self.is_forward else False self._updateLabel(is_left) else: self.hideItems() else: self.hideItems() # end def def remove(self): """Clean up this joint """ scene = self.scene() scene.removeItem(self._label) self._label = None scene.removeItem(self._path_thing) self._path_thing = None scene.removeItem(self._blank_thing) self._blank_thing = None scene.removeItem(self) # end def def _updateLabel(self, is_left): """Called by updatePositionAndAppearance during init. Updates drawing and position of the label. Args: is_left (TYPE): Description """ lbl = self._label if self._idx is not None: bw = _BASE_WIDTH num = self._partner_virtual_helix.idNum() tBR = _FM.tightBoundingRect(str(num)) half_label_h = tBR.height()/2.0 half_label_w = tBR.width()/2.0 # determine x and y positions label_x = bw/2.0 - half_label_w if self._is_on_top: label_y = -0.25*half_label_h - 0.5 - 0.5*bw else: label_y = 2*half_label_h + 0.5 + 0.5*bw # adjust x for left vs right label_x_offset = 0.25*bw if is_left else -0.25*bw label_x += label_x_offset # adjust x for numeral 1 if num == 1: label_x -= half_label_w/2.0 # create text item if lbl is None: lbl = QGraphicsSimpleTextItem(str(num), self) lbl.setPos(label_x, label_y) lbl.setBrush(_ENAB_BRUSH) lbl.setFont(_TO_HELIX_NUM_FONT) self._label = lbl lbl.setText(str(self._partner_virtual_helix.idNum())) lbl.show() # end if # end def def hideItems(self): """Summary Returns: TYPE: Description """ if self._label: self._label.hide() if self._blank_thing: self._path_thing.hide() if self._blank_thing: self._blank_thing.hide()
class OverlayGraphics(QGraphicsView): def __init__(self): super(OverlayGraphics, self).__init__() self.setStyleSheet("background:transparent") # ビューの背景透明化 self.overlayScene = QGraphicsScene() self.setScene(self.overlayScene) self.overlayScene.setSceneRect(QRectF(self.rect())) self.createItem() self.luRect = QRect() self.rbRect = QRect() self.isSelected = False self.setTargetMode(False) self.hide() # 初期状態は非表示 def createItem(self): # ターゲットマーカの作成 self.target_circle = QGraphicsEllipseItem( QtCore.QRectF(-10, -10, 20, 20)) self.target_circle.setBrush(QBrush(Qt.red)) self.target_circle.setPen(QPen(Qt.black)) self.overlayScene.addItem(self.target_circle) self.setTargetMode(False) # モーダルの作成:モーダルはターゲット位置に追従する self.pop_rect = QGraphicsRectItem(QtCore.QRectF(0, 0, 100, 60), self.target_circle) self.pop_rect.setBrush(QBrush(Qt.gray)) self.pop_rect.setPen(QPen(Qt.gray)) self.pop_rect.setOpacity(0.8) # 透明度を設定 self.operate_text = QGraphicsSimpleTextItem("", self.pop_rect) self.operate_text.setScale(1.7) self.sub_operate_text = QGraphicsSimpleTextItem("", self.pop_rect) self.sub_operate_text.setScale(1.7) self.setTargetPos(400, 180, DirectionEnum.VERTICAL.value) # オーバレイヤのサイズが変わると呼び出される.シーンのサイズをビューの大きさに追従(-5 はマージン) def resizeEvent(self, event): self.overlayScene.setSceneRect( QRectF(0, 0, self.size().width() - 5, self.size().height() - 5)) # ウィンドウ左上からの位置(画面位置からウインドウ左上の位置を引く) def setTargetPos(self, x_pos, y_pos, direction): self.target_circle.setPos(QPointF(x_pos, y_pos)) self.setPopPos(direction) def setPopTextPos(self, text1, text2): lentext1 = len(text1) lentext2 = len(text2) if lentext2 == 0: self.operate_text.setPos( (self.pop_rect.rect().size().width() / 2) - (lentext1 / 2 * 14), 15) else: self.operate_text.setPos( (self.pop_rect.rect().size().width() / 2) - (lentext1 / 2 * 14), 5) self.sub_operate_text.setPos( (self.pop_rect.rect().size().width() / 2) - (lentext2 / 2 * 14), 30) def setPopPos(self, direction): if direction == DirectionEnum.VERTICAL.value: # モーダルを右に表示しきれない場合に左に表示 if self.target_circle.pos().x() > self.overlayScene.width( ) - self.pop_rect.rect().size().width() * 1.5: self.pop_rect.setPos( -self.pop_rect.rect().size().width() * 1.5, -self.pop_rect.rect().size().height() / 2) else: # 右に表示 self.pop_rect.setPos(self.pop_rect.rect().size().width() / 2, -self.pop_rect.rect().size().height() / 2) else: # モーダルを下に表示しきれない場合に上に表示 if self.target_circle.pos().y() > self.overlayScene.height( ) - self.pop_rect.rect().size().height() * 1.5: self.pop_rect.setPos( -self.pop_rect.rect().size().width() / 2, -self.pop_rect.rect().size().height() * 1.5) else: # 下に表示 self.pop_rect.setPos(-self.pop_rect.rect().size().width() / 2, self.pop_rect.rect().size().height() / 2) def setTargetMode(self, active): self.targetMode = active if active: self.target_circle.setRect(-10, -10, 20, 20) else: # ターゲットを非表示にする self.target_circle.setRect(0, 0, 0, 0) # def targetVisible(self, visible): # if visible: # self.target_circle.setRect(-10, -10, 20, 20) # else: # self.target_circle.setRect(0, 0, 0, 0) def feedbackShow(self, text1, text2, direction): if self.isSelected: self.operate_text.setText(text1) self.sub_operate_text.setText(text2) # ターゲットモードがアクティブでないとき,ターゲットマーカの位置は選択セルに依存 if not self.targetMode: if direction == DirectionEnum.HORIZON.value: x_pos = (self.luRect.left() + self.rbRect.right()) / 2 + 20 y_pos = self.rbRect.bottom( ) - self.rbRect.height() / 2 + 20 else: x_pos = self.rbRect.right() - self.rbRect.width() / 2 + 20 y_pos = (self.luRect.top() + self.rbRect.bottom()) / 2 + 20 self.setTargetPos(x_pos, y_pos, direction) self.setPopTextPos(text1, text2) self.show()
class ForcedXoverNode3(QGraphicsRectItem): """ This is a QGraphicsRectItem to allow actions and also a QGraphicsSimpleTextItem to allow a label to be drawn """ def __init__(self, virtual_helix_item, xover_item, strand3p, idx): super(ForcedXoverNode3, self).__init__(virtual_helix_item) self._vhi = virtual_helix_item self._xover_item = xover_item self._idx = idx self._is_on_top = virtual_helix_item.isStrandOnTop(strand3p) self._is_drawn_5_to_3 = strand3p.strandSet().isDrawn5to3() self._strand_type = strand3p.strandSet().strandType() self._partner_virtual_helix = virtual_helix_item self._blank_thing = QGraphicsRectItem(_blankRect, self) self._blank_thing.setBrush(QBrush(Qt.white)) self._path_thing = QGraphicsPathItem(self) self.configurePath() self.setPen(_NO_PEN) self._label = None self.setPen(_NO_PEN) self.setBrush(_NO_BRUSH) self.setRect(_rect) self.setZValue(styles.ZENDPOINTITEM + 1) # end def def updateForFloatFromVHI(self, virtual_helix_item, strand_type, idx_x, idx_y): """ """ self._vhi = virtual_helix_item self.setParentItem(virtual_helix_item) self._strand_type = strand_type self._idx = idx_x self._is_on_top = self._is_drawn_5_to_3 = True if idx_y == 0 else False self.updatePositionAndAppearance(is_from_strand=False) # end def def updateForFloatFromStrand(self, virtual_helix_item, strand3p, idx): """ """ self._vhi = virtual_helix_item self._strand = strand3p self.setParentItem(virtual_helix_item) self._idx = idx self._is_on_top = virtual_helix_item.isStrandOnTop(strand3p) self._is_drawn_5_to_3 = strand3p.strandSet().isDrawn5to3() self._strand_type = strand3p.strandSet().strandType() self.updatePositionAndAppearance() # end def def strandType(self): return self._strand_type # end def def configurePath(self): self._path_thing.setBrush(QBrush(styles.RED_STROKE)) path = PPR3 if self._is_drawn_5_to_3 else PPL3 offset = -_BASE_WIDTH if self._is_drawn_5_to_3 else _BASE_WIDTH self._path_thing.setPath(path) self._path_thing.setPos(offset, 0) offset = -_BASE_WIDTH if self._is_drawn_5_to_3 else 0 self._blank_thing.setPos(offset, 0) self._blank_thing.show() self._path_thing.show() # end def def refreshXover(self): self._xover_item.refreshXover() # end def def setPartnerVirtualHelix(self, virtual_helix_item): self._partner_virtual_helix = virtual_helix_item # end def def idx(self): return self._idx # end def def virtualHelixItem(self): return self._vhi # end def def point(self): return self._vhi.upperLeftCornerOfBaseType(self._idx, self._strand_type) # end def def floatPoint(self): pt = self.pos() return pt.x(), pt.y() # end def def isOnTop(self): return self._is_on_top # end def def isDrawn5to3(self): return self._is_drawn_5_to_3 # end def def updatePositionAndAppearance(self, is_from_strand=True): """ Sets position by asking the VirtualHelixItem Sets appearance by choosing among pre-defined painterpaths (from normalstrandgraphicsitem) depending on drawing direction. """ self.setPos(*self.point()) n5 = self._xover_item._node5 if is_from_strand: from_strand, from_idx = (n5._strand, n5._idx) if n5 != self else (None, None) if self._strand.canInstallXoverAt(self._idx, from_strand, from_idx): self.configurePath() # We can only expose a 5' end. But on which side? is_left = True if self._is_drawn_5_to_3 else False self._updateLabel(is_left) else: self.hideItems() else: self.hideItems() # end def def updateConnectivity(self): is_left = True if self._is_drawn_5_to_3 else False self._updateLabel(is_left) # end def def remove(self): """ Clean up this joint """ scene = self.scene() scene.removeItem(self._label) self._label = None scene.removeItem(self._path_thing) self._path_thing = None scene.removeItem(self._blank_thing) self._blank_thing = None scene.removeItem(self) # end def def _updateLabel(self, is_left): """ Called by updatePositionAndAppearance during init, or later by updateConnectivity. Updates drawing and position of the label. """ lbl = self._label if self._idx != None: bw = _BASE_WIDTH num = self._partner_virtual_helix.number() tBR = _FM.tightBoundingRect(str(num)) half_label_h = tBR.height() / 2.0 half_label_w = tBR.width() / 2.0 # determine x and y positions label_x = bw / 2.0 - half_label_w if self._is_on_top: label_y = -0.25 * half_label_h - 0.5 - 0.5 * bw else: label_y = 2 * half_label_h + 0.5 + 0.5 * bw # adjust x for left vs right label_x_offset = 0.25 * bw if is_left else -0.25 * bw label_x += label_x_offset # adjust x for numeral 1 if num == 1: label_x -= half_label_w / 2.0 # create text item if lbl == None: lbl = QGraphicsSimpleTextItem(str(num), self) lbl.setPos(label_x, label_y) lbl.setBrush(_ENAB_BRUSH) lbl.setFont(_TO_HELIX_NUM_FONT) self._label = lbl lbl.setText(str(self._partner_virtual_helix.number())) lbl.show() # end if # end def def hideItems(self): if self._label: self._label.hide() if self._blank_thing: self._path_thing.hide() if self._blank_thing: self._blank_thing.hide()
class PhotoViewer(QtWidgets.QGraphicsView): photoClicked = QtCore.pyqtSignal(QtCore.QPoint) def __init__(self, parent): super(PhotoViewer, self).__init__(parent) # all gravestone-denoting polygons self.selection_polygons = [] self.box_creation_mode = False self.box_start_point = None self._box_graphic = None self.line_selection_mode = False self.start_line_select = None self.line_graphic = None self.ctrl_held = False # the polygons currently selected for editing self.selected_polygons = [] self.update_selected = None self._zoom = 0 self._empty = True self.scene = QtWidgets.QGraphicsScene(self) self._photo = QtWidgets.QGraphicsPixmapItem() self.scene.addItem(self._photo) self.setScene(self.scene) self.setTransformationAnchor(QtWidgets.QGraphicsView.AnchorUnderMouse) self.setResizeAnchor(QtWidgets.QGraphicsView.AnchorUnderMouse) self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded) self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded) self.setBackgroundBrush(QtGui.QBrush(QtGui.QColor(30, 30, 30))) self.setFrameShape(QtWidgets.QFrame.NoFrame) def add_selected_polygon(self, polygon): self.selected_polygons.append(polygon) if self.update_selected is not None: self.update_selected() def remove_selected_polygon(self, polygon): self.selected_polygons.remove(polygon) if self.update_selected is not None: self.update_selected(self.selected_polygons) def has_photo(self): return not self._empty def pixmap(self): return self._photo def fitInView(self, scale=True): rect = QtCore.QRectF(self._photo.pixmap().rect()) if not rect.isNull(): self.setSceneRect(rect) if self.has_photo(): unity = self.transform().mapRect(QtCore.QRectF(0, 0, 1, 1)) self.scale(1 / unity.width(), 1 / unity.height()) viewrect = self.viewport().rect() scenerect = self.transform().mapRect(rect) factor = min(viewrect.width() / scenerect.width(), viewrect.height() / scenerect.height()) self.scale(factor, factor) self._zoom = 0 def set_photo(self, pixmap=None): self._zoom = 0 if pixmap and not pixmap.isNull(): self._empty = False self.setDragMode(QtWidgets.QGraphicsView.ScrollHandDrag) self._photo.setPixmap(pixmap) self.selection_polygons = [] self.box_creation_mode = False self.box_start_point = None self._box_graphic = None for poly in self.selected_polygons: self.remove_selected_polygon(poly) else: self._empty = True self.setDragMode(QtWidgets.QGraphicsView.NoDrag) self._photo.setPixmap(QtGui.QPixmap()) self.fitInView() def wheelEvent(self, event): if self.has_photo(): if event.angleDelta().y() > 0: factor = 1.25 self._zoom += 1 else: factor = 0.8 self._zoom -= 1 if self._zoom > 0: self.scale(factor, factor) elif self._zoom == 0: self.fitInView() else: self._zoom = 0 def delete_selected(self): for selected in self.selected_polygons: selected.deselect() self.scene.removeItem(selected) self.selection_polygons.remove(selected) self.selected_polygons = [] def any_selection_nodes_under_mouse(self): for selected in self.selected_polygons: for node in selected._nodes: if node.isUnderMouse(): return True return False def deselect_all(self): for polygon in self.selected_polygons: polygon.deselect() self.selected_polygons = [] if self.update_selected is not None: self.update_selected() def remove_all(self): """ Delete all polygons """ self.deselect_all() for polygon in self.selection_polygons: self.scene.removeItem(polygon) self.selection_polygons.clear() def mousePressEvent(self, event): if not self._photo.isUnderMouse(): return if self.box_creation_mode: photo_click_point = self.mapToScene(event.pos()).toPoint() self.box_start_point = photo_click_point self._box_graphic = QGraphicsRectItem(0, 0, 1, 1) self._box_graphic.setBrush(QBrush(Qt.transparent)) self._box_graphic.setPen( QPen(Qt.blue, 2, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin)) self._box_graphic.setPos(photo_click_point) self.scene.addItem(self._box_graphic) elif self.line_selection_mode and self.start_line_select is None: photo_click_point = self.mapToScene(event.pos()).toPoint() self.start_line_select = photo_click_point self.line_graphic = QGraphicsLineItem() self.line_graphic.setPen( QPen(Qt.green, 4, Qt.DotLine, Qt.RoundCap, Qt.RoundJoin)) self.scene.addItem(self.line_graphic) else: # this is pretty hacky and ugly but it works well if not self.any_selection_nodes_under_mouse( ) and not self.ctrl_held: self.deselect_all() super(PhotoViewer, self).mousePressEvent(event) def mouseReleaseEvent(self, event): if not self._photo.isUnderMouse(): return if self.box_creation_mode and self.box_start_point is not None: photo_click_point = self.mapToScene(event.pos()).toPoint() polygon_coords = [ QPointF(self.box_start_point.x(), self.box_start_point.y()), QPointF(self.box_start_point.x(), photo_click_point.y()), QPointF(photo_click_point.x(), photo_click_point.y()), QPointF(photo_click_point.x(), self.box_start_point.y()) ] selection_polygon = SelectionPolygon(polygon_coords, self) self.add_selection_polygon(selection_polygon) self.scene.removeItem(self._box_graphic) self.box_creation_mode = False self.box_start_point = None self._box_graphic = None self.setDragMode(QtWidgets.QGraphicsView.ScrollHandDrag) elif self.line_selection_mode and self.start_line_select is not None: # line select self.deselect_all() photo_click_point = self.mapToScene(event.pos()).toPoint() self.scene.removeItem(self.line_graphic) for polygon in self.selection_polygons: line = [ np.array([photo_click_point.x(), photo_click_point.y()]), np.array([ self.start_line_select.x(), self.start_line_select.y() ]) ] centroid = polygon.centroid() # max node dist from centroid, used for approximations max_dist = 0.0 for point in polygon.polygon_points: # todo manhattan length is a bad approx of euclid. dist, maybe its not worth the inaccuracy dist = (point - centroid).manhattanLength() if dist > max_dist: max_dist = dist np_centroid = np.array([centroid.x(), centroid.y()]) distance_from_line = np.abs( np.cross(line[1] - line[0], line[0] - np_centroid)) / norm(line[0] - line[1]) if distance_from_line <= max_dist: min_x = min(photo_click_point.x(), self.start_line_select.x()) * .95 max_x = max(photo_click_point.x(), self.start_line_select.x()) * 1.05 min_y = min(photo_click_point.y(), self.start_line_select.y()) * .95 max_y = max(photo_click_point.y(), self.start_line_select.x()) * 1.05 if min_x < centroid.x() < max_x and min_y < centroid.y( ) < max_y: polygon.select() self.start_line_select = None self.line_graphic = None else: super(PhotoViewer, self).mouseReleaseEvent(event) def mouseMoveEvent(self, event): if self.box_creation_mode and self.box_start_point is not None and self._box_graphic is not None: mouse_point = self.mapToScene(event.pos()).toPoint() self._box_graphic.setRect( QRectF(0, 0, mouse_point.x() - self.box_start_point.x(), mouse_point.y() - self.box_start_point.y()).normalized()) elif self.line_selection_mode and self.start_line_select is not None and self.line_graphic is not None: mouse_point = self.mapToScene(event.pos()).toPoint() self.line_graphic.setLine( QLineF(mouse_point, self.start_line_select)) super(PhotoViewer, self).mouseMoveEvent(event) def add_selection_polygon(self, selection_polygon): self.selection_polygons.append(selection_polygon) self.scene.addItem(selection_polygon) def pixmap_width_and_height(self): return self._photo.pixmap().width(), self._photo.pixmap().height()
buttonParent = QGraphicsRectItem() ellipseButton = Button(QPixmap(':/images/ellipse.png'), buttonParent) figure8Button = Button(QPixmap(':/images/figure8.png'), buttonParent) randomButton = Button(QPixmap(':/images/random.png'), buttonParent) tiledButton = Button(QPixmap(':/images/tile.png'), buttonParent) centeredButton = Button(QPixmap(':/images/centered.png'), buttonParent) ellipseButton.setPos(-100, -100) figure8Button.setPos(100, -100) randomButton.setPos(0, 0) tiledButton.setPos(-100, 100) centeredButton.setPos(100, 100) scene.addItem(buttonParent) buttonParent.setScale(0.75) buttonParent.setPos(200, 200) buttonParent.setZValue(65) # States. rootState = QState() ellipseState = QState(rootState) figure8State = QState(rootState) randomState = QState(rootState) tiledState = QState(rootState) centeredState = QState(rootState) # Values. for i, item in enumerate(items): # Ellipse. ellipseState.assignProperty( item, 'pos',
class GameArea(QWidget): #constants of widget RATIO_WITH_SHIPLIST = 11 / 14 RATIO_WITHOUT_SHIPLIST = 1 EPS = 0.3 #signals shipPlaced = pyqtSignal(Ship) def __init__(self, parent=None): super(GameArea, self).__init__(parent) self.__ui = Ui_GameArea() self.__ui.setupUi(self) self.__ratio = self.RATIO_WITH_SHIPLIST self.__scaleFactor = 1 self.controller = Controller() self.controller._accept = self.__accept self.controller._decline = self.__decline self.__gameModel = None self.__shipList = { "boat": ShipListItem(length=1, name="boat", count=4), "destroyer": ShipListItem(length=2, name="destroyer", count=3), "cruiser": ShipListItem(length=3, name="cruiser", count=2), "battleship": ShipListItem(length=4, name="battleship", count=1), } # resources self.__cellImages = {"intact": None, "hit": None, "miss": None} self.__sprites = {"explosion": None, "splash": None} self.__originalTileSize = 0 self.tileSize = 0 self.__shipListImage = QImage() self.__counterImage = QImage() # drawing items self.__placedShips = [] self.__field = [QGraphicsPixmapItem() for _ in range(100)] self.__letters = [QGraphicsTextItem() for _ in range(10)] self.__numbers = [QGraphicsTextItem() for _ in range(10)] for i in range(10): self.__letters[i].setDefaultTextColor(QColor.fromRgb(0, 0, 0)) self.__numbers[i].setDefaultTextColor(QColor.fromRgb(0, 0, 0)) self.__spriteAnimations = [] self.__shipListItem = QGraphicsPixmapItem() self.__ghostShip = QGraphicsPixmapItem( ) # data: 0 - rotation; 1 - ShipListItem self.__placer = QGraphicsRectItem() self.__dragShip = False self.__targetPixmap = None self.__targets = [] self.__scanEffect = None # prepare Qt objects self.__scene = QGraphicsScene() self.__loadResources() self.__initGraphicsView() self.__adjustedToSize = 0 def serviceModel(self, game_model): self.removeModel() self.__gameModel = game_model self.__gameModel.shipKilled.connect(self.__shootAround) def removeModel(self): if self.__gameModel: self.shipKilled.disconnect() self.__gameModel = None def __loadResources(self): if DEBUG_RESOURCE: resourcesPath = os.path.join(os.path.dirname(__file__), DEBUG_RESOURCE) else: resourcesPath = Environment.Resources.path() first = True for imageName in self.__cellImages: image = QImage( os.path.join(resourcesPath, "img", "cells", f"{imageName}.png")) if first: first = False self.__originalTileSize = min(image.width(), image.height()) else: self.__originalTileSize = min(self.__originalTileSize, image.width(), image.height()) self.__cellImages[imageName] = image for spriteName in self.__sprites: image = QImage( os.path.join(resourcesPath, "img", "cells", f"{spriteName}.png")) self.__sprites[spriteName] = image for shipName, ship in self.__shipList.items(): ship.image = QImage( os.path.join(resourcesPath, "img", "ships", f"{shipName}.png")) self.__shipListImage = QImage( os.path.join(resourcesPath, "img", "backgrounds", "shiplist.png")) self.__counterImage = QImage( os.path.join(resourcesPath, "img", "miscellaneous", "shipcounter.png")) self.__targetPixmap = QPixmap( os.path.join(resourcesPath, "img", "cells", "target.png")) def __initGraphicsView(self): self.__ui.graphicsView.setScene(self.__scene) self.__ui.graphicsView.viewport().installEventFilter(self) self.__ui.graphicsView.setRenderHints( QPainter.RenderHint.HighQualityAntialiasing | QPainter.RenderHint.TextAntialiasing | QPainter.RenderHint.SmoothPixmapTransform) self.__ui.graphicsView.horizontalScrollBar().blockSignals(True) self.__ui.graphicsView.verticalScrollBar().blockSignals(True) for cell in self.__field: cell.setData(0, "intact") pixmap = QPixmap.fromImage(self.__cellImages["intact"]) cell.setPixmap(pixmap) cell.setTransformationMode( Qt.TransformationMode.SmoothTransformation) self.__scene.addItem(cell) ca_letters = ["А", "Б", "В", "Г", "Д", "Е", "Ж", "З", "И", "К"] # ca_letters = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J"] ca_numbers = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"] font = QFont("Roboto") font.setPixelSize(int(self.__originalTileSize * 0.8)) for i in range(10): self.__letters[i].setPlainText(ca_letters[i]) self.__letters[i].setFont(font) self.__numbers[i].setPlainText(ca_numbers[i]) self.__numbers[i].setFont(font) self.__scene.addItem(self.__letters[i]) self.__scene.addItem(self.__numbers[i]) self.__shipListItem.setPixmap(QPixmap.fromImage(self.__shipListImage)) self.__shipListItem.setTransformationMode( Qt.TransformationMode.SmoothTransformation) self.__scene.addItem(self.__shipListItem) font.setPixelSize(int(self.__originalTileSize * 0.3)) for _, ship in self.__shipList.items(): t = QTransform().rotate(90) ship.shipItem.setPixmap( QPixmap.fromImage(ship.image).transformed(t)) ship.shipItem.setTransformationMode( Qt.TransformationMode.SmoothTransformation) ship.counterItem.setPixmap(QPixmap.fromImage(self.__counterImage)) ship.counterItem.setTransformationMode( Qt.TransformationMode.SmoothTransformation) ship.counterText.setPlainText(str(ship.count)) ship.counterText.setFont(font) self.__scene.addItem(ship.shipItem) self.__scene.addItem(ship.counterItem) self.__scene.addItem(ship.counterText) self.__ghostShip.setTransformationMode( Qt.TransformationMode.SmoothTransformation) self.__ghostShip.setOpacity(0.7) pen = QPen() pen.setWidth(2) pen.setStyle(Qt.PenStyle.DashLine) pen.setJoinStyle(Qt.PenJoinStyle.RoundJoin) self.__placer.setPen(pen) def __setCell(self, x, y, cell_type): if cell_type not in self.__cellImages.keys(): raise ValueError( f"Type is {cell_type}. Allowed \"intact\", \"miss\", \"hit\"") cellItem = self.__field[y * 10 + x] cellItem.setData(0, cell_type) pixmap = QPixmap.fromImage(self.__cellImages[cell_type]) cellItem.setPixmap(pixmap) def __runAnimation(self, x, y, animation, looped=False): sprite = SpriteItem() sprite.setData(0, QPoint(x, y)) spritePixmap = QPixmap.fromImage(self.__sprites[animation]) sprite.setSpriteMap(spritePixmap, 60, 60, 5) sprite.setScale(self.__scaleFactor) sprite.setPos((x + 1) * self.tileSize, (y + 1) * self.tileSize) sprite.setZValue(10) self.__spriteAnimations.append(sprite) self.__scene.addItem(sprite) def removeAnimation(): self.__scene.removeItem(sprite) self.__spriteAnimations.remove(sprite) sprite.stopAnimation() sprite.startAnimation(100, looped, removeAnimation) def __shootAround(self, ship: Ship): shipCells = [] for i in range(ship.length): shipCells.append(ship.pos + (QPoint(0, i) if ship.vertical else QPoint(i, 0))) cells = [] for cell in shipCells: arounds = [ cell + QPoint(i, j) for i in range(-1, 2) for j in range(-1, 2) ] for candidate in arounds: if 0 <= candidate.x() < 10 and 0 <= candidate.y() < 10: if candidate not in cells: cells.append(candidate) for cell in cells: if cell not in shipCells: self.__setCell(cell.x(), cell.y(), "miss") self.__runAnimation(cell.x(), cell.y(), "splash") log.debug( f"name: {ship.name} | length: {ship.length} | pos: {ship.pos} | vertical: {ship.vertical}" ) def hasHeightForWidth(self): return True def heightForWidth(self, width): return width / self.__ratio def hideShipList(self): self.__ratio = self.RATIO_WITHOUT_SHIPLIST self.__scene.removeItem(self.__shipListItem) for _, ship in self.__shipList.items(): self.__scene.removeItem(ship.shipItem) self.__adjustedToSize = None resize = QResizeEvent(self.size(), self.size()) QApplication.postEvent(self, resize) def getPlacedShips(self): return self.__placedShips def placedShipsCount(self): return len(self.__placedShips) def hideShips(self): for ship in self.__placedShips: self.__scene.removeItem(ship) def removePlacedShips(self): if self.__shipListItem.scene() is None: return for ship in self.__placedShips: shipListItem = ship.data(1) shipListItem.count += 1 shipListItem.counterText.setPlainText(str(shipListItem.count)) self.__scene.removeItem(ship) self.__placedShips.clear() def shuffleShips(self): if self.__shipListItem.scene() is None: return self.removePlacedShips() def findPossibleCells(length, rotation): vertical = rotation.isVertical() if vertical == rotation.isHorizontal(): raise Exception( "Unknown state! Rotation is not horizontal and not vertical." ) # wtf width = 1 if vertical else length height = length if vertical else 1 cells = [] for x in range(10): for y in range(10): if QRect(0, 0, 10, 10) != QRect(0, 0, 10, 10).united( QRect(x, y, width, height)): break if self.__validatePosition(x, y, width, height): cells.append(QPoint(x, y)) return cells shipList = list(self.__shipList.values()) shipList.sort(key=lambda ship: ship.length, reverse=True) for shipItem in shipList: for i in range(shipItem.count): rot = random.choice(list(Rotation)) cells = findPossibleCells(shipItem.length, rot) if not cells: rot = rot.next() cells = findPossibleCells(shipItem.length, rot) if not cells: return cell = random.choice(cells) self.__placeShip(shipItem, cell.x(), cell.y(), rot) def resizeEvent(self, event): size = event.size() if size == self.__adjustedToSize: return self.__adjustedToSize = size nowWidth = size.width() nowHeight = size.height() width = min(nowWidth, nowHeight * self.__ratio) height = min(nowHeight, nowWidth / self.__ratio) h_margin = round((nowWidth - width) / 2) - 2 v_margin = round((nowHeight - height) / 2) - 2 self.setContentsMargins( QMargins(h_margin, v_margin, h_margin, v_margin)) self.__resizeScene() def scanEffect(self, x, y): """ :return: True on success, False otherwise """ if self.__scanEffect: return False def scanFinisedCallback(): self.__scene.removeItem(self.__scanEffect) del self.__scanEffect self.__scanEffect = None target = FadingPixmapItem(self.__targetPixmap) target.setPos((x + 1) * self.tileSize, (y + 1) * self.tileSize) target.setScale(self.__scaleFactor) target.setData(0, (x, y)) self.__targets.append(target) self.__scene.addItem(target) self.__scanEffect = FloatingGradientItem( QRectF(self.tileSize, self.tileSize, self.__originalTileSize * 10, self.__originalTileSize * 10), scanFinisedCallback) self.__scanEffect.setScale(self.__scaleFactor) self.__scanEffect.setBackwards(True) self.__scene.addItem(self.__scanEffect) return True def __resizeScene(self): width = self.__ui.graphicsView.width() height = self.__ui.graphicsView.height() self.__scene.setSceneRect(0, 0, width, height) self.tileSize = min(width / 11, height / (11 / self.__ratio)) self.__scaleFactor = self.tileSize / self.__originalTileSize for i, cell in enumerate(self.__field): x = i % 10 y = i // 10 cell.setScale(self.__scaleFactor) cell.setPos((x + 1) * self.tileSize, (y + 1) * self.tileSize) for sprite in self.__spriteAnimations: pos = sprite.data(0) sprite.setPos((pos.x() + 1) * self.tileSize, (pos.y() + 1) * self.tileSize) sprite.setScale(self.__scaleFactor) for i in range(10): letter = self.__letters[i] letter.setScale(self.__scaleFactor) offsetX = (self.tileSize - letter.boundingRect().width() * self.__scaleFactor) / 2 offsetY = (self.tileSize - letter.boundingRect().height() * self.__scaleFactor) / 2 letter.setPos((i + 1) * self.tileSize + offsetX, offsetY) number = self.__numbers[i] number.setScale(self.__scaleFactor) offsetX = (self.tileSize - number.boundingRect().width() * self.__scaleFactor) / 2 offsetY = (self.tileSize - number.boundingRect().height() * self.__scaleFactor) / 2 number.setPos(offsetX, (i + 1) * self.tileSize + offsetY) xPos = 0 for _, ship in self.__shipList.items(): xPos += (ship.length - 1) xOffset = xPos * self.tileSize ship.shipItem.setScale(self.__scaleFactor) ship.shipItem.setPos(self.tileSize + xOffset, self.tileSize * (12 + self.EPS)) ship.counterItem.setScale(self.__scaleFactor) ship.counterItem.setPos(self.tileSize + xOffset, self.tileSize * (12.65 + self.EPS)) counterSize = ship.counterItem.boundingRect() textSize = ship.counterText.boundingRect() textXOffset = (counterSize.width() - textSize.width()) * self.__scaleFactor / 2 textYOffset = (counterSize.height() - textSize.height()) * self.__scaleFactor / 2 textX = ship.counterItem.pos().x() + textXOffset textY = ship.counterItem.pos().y() + textYOffset ship.counterText.setScale(self.__scaleFactor) ship.counterText.setPos(textX, textY) for ship in self.__placedShips: mapPos = ship.data(2) ship.setPos((mapPos.x() + 1) * self.tileSize, (mapPos.y() + 1) * self.tileSize) ship.setScale(self.__scaleFactor) for target in self.__targets: x, y = target.data(0) target.setPos((x + 1) * self.tileSize, (y + 1) * self.tileSize) target.setScale(self.__scaleFactor) if self.__scanEffect: self.__scanEffect.setPos(self.tileSize, self.tileSize) self.__scanEffect.setScale(self.__scaleFactor) shipListX = self.tileSize shipListY = self.tileSize * (11 + self.EPS) self.__shipListItem.setScale(self.__scaleFactor) self.__shipListItem.setPos(shipListX, shipListY) self.__ghostShip.setScale(self.__scaleFactor) def eventFilter(self, obj, event): if obj is self.__ui.graphicsView.viewport(): if event.type() == QEvent.MouseButtonPress: self.__viewportMousePressEvent(event) elif event.type() == QEvent.MouseButtonRelease: self.__viewportMouseReleaseEvent(event) elif event.type() == QEvent.MouseMove: self.__viewportMouseMoveEvent(event) return super().eventFilter(obj, event) def sceneToMap(self, x, y): x -= self.tileSize y -= self.tileSize x //= self.tileSize y //= self.tileSize return int(x), int(y) def __initGhostShip(self, ship, pos): self.__ghostShip.setPixmap(QPixmap.fromImage(ship.image)) self.__ghostShip.setPos(pos) self.__ghostShip.setRotation(0) width = self.__ghostShip.boundingRect().width() height = self.__ghostShip.boundingRect().height() self.__ghostShip.setOffset(-width / 2, -height / 2) self.__ghostShip.setData(0, Rotation.UP) self.__ghostShip.setData(1, ship) self.__placer.setRect(0, 0, self.tileSize, self.tileSize * ship.length) self.__placer.setZValue(50) self.__scene.addItem(self.__ghostShip) self.__ghostShip.setZValue(100) def __rotateGhostShip(self, rotation=None): rotation = rotation if rotation else self.__ghostShip.data(0).next() self.__ghostShip.setData(0, rotation) self.__ghostShip.setRotation(rotation.value) placerRect = self.__placer.rect() maxSide = max(placerRect.width(), placerRect.height()) minSide = min(placerRect.width(), placerRect.height()) if rotation.isHorizontal(): self.__placer.setRect(0, 0, maxSide, minSide) elif rotation.isVertical(): self.__placer.setRect(0, 0, minSide, maxSide) else: raise Exception( "Unknown state! Rotation is not horizontal and not vertical." ) # wtf self.__validatePlacer() def __ghostShipLongSurface(self): pos = self.__ghostShip.pos() x = pos.x() y = pos.y() rot: Rotation = self.__ghostShip.data(0) length = self.__ghostShip.data(1).length if rot.isHorizontal(): x -= self.tileSize * length / 2 return x, y if rot.isVertical(): y -= self.tileSize * length / 2 return x, y def __validatePosition(self, x, y, width=1, height=1): positionRect = QRectF((x + 1) * self.tileSize, (y + 1) * self.tileSize, self.tileSize * width, self.tileSize * height) isPlacerValid = True for ship in self.__placedShips: shipRect = ship.mapRectToScene(ship.boundingRect()).adjusted( -self.tileSize / 2, -self.tileSize / 2, self.tileSize / 2, self.tileSize / 2) isPlacerValid = not positionRect.intersects(shipRect) if not isPlacerValid: break return isPlacerValid def __validatePlacer(self): sceneX, sceneY = self.__ghostShipLongSurface() x, y = self.sceneToMap(sceneX, sceneY) self.__placer.setPos((x + 1) * self.tileSize, (y + 1) * self.tileSize) permittedArea = QRectF(self.__ui.graphicsView.viewport().geometry()) permittedArea.setTopLeft(QPointF(self.tileSize, self.tileSize)) permittedArea.setBottomRight( QPointF(self.tileSize * 12, self.tileSize * 12)) placerSize = self.__placer.boundingRect() placerRect = QRectF(sceneX, sceneY, placerSize.width(), placerSize.height()) isPlacerValid = False # first validation - ship can be placed inside game field if permittedArea.contains(self.__ghostShip.pos( )) and permittedArea == permittedArea.united(placerRect): if self.__placer.scene() == None: self.__scene.addItem(self.__placer) x, y = self.sceneToMap(sceneX, sceneY) self.__placer.setPos((x + 1) * self.tileSize, (y + 1) * self.tileSize) isPlacerValid = True else: if self.__placer.scene() == self.__scene: self.__scene.removeItem(self.__placer) # second validation - placer does not intersect with other ships if isPlacerValid: isPlacerValid = self.__validatePosition( x, y, placerSize.width() / self.tileSize, placerSize.height() / self.tileSize) # set color of placer pen = self.__placer.pen() if isPlacerValid: pen.setColor(Qt.GlobalColor.darkGreen) else: pen.setColor(Qt.GlobalColor.red) self.__placer.setPen(pen) self.__placer.setData(0, isPlacerValid) def __placeShip(self, shipListItem, x, y, rotation): sceneX, sceneY = (x + 1) * self.tileSize, (y + 1) * self.tileSize shipListItem.count -= 1 shipListItem.counterText.setPlainText(str(shipListItem.count)) pixmap = QPixmap(shipListItem.image).transformed(QTransform().rotate( (rotation.value))) placedShip = QGraphicsPixmapItem(pixmap) placedShip.setData(0, rotation) placedShip.setData(1, shipListItem) placedShip.setData(2, QPoint(x, y)) # position in map coordinates placedShip.setPos(sceneX, sceneY) placedShip.setTransformationMode( Qt.TransformationMode.SmoothTransformation) placedShip.setScale(self.__scaleFactor) self.__placedShips.append(placedShip) self.__scene.addItem(placedShip) def __placeGhostShip(self): isPlacingPermitted = self.__placer.data(0) if isPlacingPermitted: sceneX = self.__placer.pos().x() + self.tileSize / 2 sceneY = self.__placer.pos().y() + self.tileSize / 2 mapX, mapY = self.sceneToMap(sceneX, sceneY) rotation = self.__ghostShip.data(0) if rotation.isVertical(): vertical = True elif rotation.isHorizontal(): vertical = False else: raise Exception( "Unknown state! Rotation is not horizontal and not vertical." ) # wtf shipListItem = self.__ghostShip.data(1) self.__placeShip(shipListItem, mapX, mapY, rotation) log.debug( f"ship \"{shipListItem.name}\"; " f"position ({mapX}, {mapY}); " f"oriented {'vertically' if vertical else 'horizontally'}") self.shipPlaced.emit( Ship(name=shipListItem.name, length=shipListItem.length, pos=QPoint(mapX, mapY), vertical=vertical)) def __viewportMousePressEvent(self, event): if event.button() == Qt.MouseButton.LeftButton: if self.__shipListItem.scene(): # check press on shiplist shipUnderMouse = None for _, ship in self.__shipList.items(): if ship.shipItem.isUnderMouse(): shipUnderMouse = ship break # check press on field rotation = Rotation.RIGHT if shipUnderMouse == None: for ship in self.__placedShips: if ship.isUnderMouse(): rotation = ship.data(0) shipListItem = ship.data(1) if shipListItem.count == 0: shipListItem.shipItem.setGraphicsEffect(None) shipListItem.count += 1 shipListItem.counterText.setPlainText( str(shipListItem.count)) shipUnderMouse = shipListItem self.__placedShips.remove(ship) self.__scene.removeItem(ship) break # if ship grabbed if shipUnderMouse and shipUnderMouse.count > 0: self.__initGhostShip(shipUnderMouse, event.pos()) self.__rotateGhostShip(rotation) self.__dragShip = True x, y = self.sceneToMap(event.pos().x(), event.pos().y()) if x >= 0 and x < 10 and y >= 0 and y < 10: if self.controller.isBot: return self.controller.emitHit(x, y) if event.button() == Qt.MouseButton.RightButton: if self.__dragShip: self.__rotateGhostShip() def __viewportMouseReleaseEvent(self, event): if event.button() == Qt.MouseButton.LeftButton: if self.__dragShip: self.__scene.removeItem(self.__ghostShip) self.__dragShip = False if self.__placer.scene() != None: self.__scene.removeItem(self.__placer) # self.__placeShip() self.__placeGhostShip() self.__placer.setData(0, False) def __viewportMouseMoveEvent(self, event): if self.__dragShip: self.__ghostShip.setPos(event.pos()) permittedArea = QRectF( self.__ui.graphicsView.viewport().geometry()) permittedArea.setTopLeft(QPointF(self.tileSize, self.tileSize)) # stop dragging ship if mouse out of field # if not permittedArea.contains(event.pos()): # self.__scene.removeItem(self.__ghostShip) # self.__dragShip = False self.__validatePlacer() def __accept(self, x, y, hit_type: CellState): log.debug( f" -- ACCEPTED -- hit on point ({x}, {y}) hit type: {hit_type}") cell = self.__field[y * 10 + x] if cell.data(0) != "intact": return if hit_type in [CellState.HIT, CellState.KILLED]: self.__setCell(x, y, 'hit') self.__runAnimation(x, y, "explosion", looped=True) for target in self.__targets: if (x, y) == target.data(0): self.__scene.removeItem(target) self.__targets.remove(target) break elif hit_type in [CellState.MISS]: self.__setCell(x, y, 'miss') self.__runAnimation(x, y, "splash", looped=False) if hit_type == CellState.KILLED: for ship in self.__placedShips: rotation = ship.data(0) length = ship.data(1).length position = ship.data(2) width = 1 if rotation.isVertical() else length height = length if rotation.isVertical() else 1 rect = QRect(position.x(), position.y(), width, height) if rect.contains(x, y): self.__scene.addItem(ship) def __decline(self, x, y): log.debug(f"declined hit on point ({x}, {y})")
def refresh(self): if not self._mdlPlots or not self._mdlOutline or not self._mdlCharacter: return if not self.isVisible(): return LINE_HEIGHT = 18 SPACING = 3 TEXT_WIDTH = self.sldTxtSize.value() CIRCLE_WIDTH = 10 LEVEL_HEIGHT = 12 s = self.scene s.clear() # Get Max Level (max depth) root = self._mdlOutline.rootItem def maxLevel(item, level=0, max=0): if level > max: max = level for c in item.children(): m = maxLevel(c, level + 1) if m > max: max = m return max MAX_LEVEL = maxLevel(root) # Get the list of tracked items (array of references) trackedItems = [] if self.actPlots.isChecked(): trackedItems += self.plotReferences() if self.actCharacters.isChecked(): trackedItems += self.charactersReferences() ROWS_HEIGHT = len(trackedItems) * (LINE_HEIGHT + SPACING ) fm = QFontMetrics(s.font()) max_name = 0 for ref in trackedItems: name = references.title(ref) max_name = max(fm.width(name), max_name) TITLE_WIDTH = max_name + 2 * SPACING # Add Folders and Texts outline = OutlineRect(0, 0, 0, ROWS_HEIGHT + SPACING + MAX_LEVEL * LEVEL_HEIGHT) s.addItem(outline) outline.setPos(TITLE_WIDTH + SPACING, 0) refCircles = [] # a list of all references, to be added later on the lines # A Function to add a rect with centered elided text def addRectText(x, w, parent, text="", level=0, tooltip=""): deltaH = LEVEL_HEIGHT if level else 0 r = OutlineRect(0, 0, w, parent.rect().height()-deltaH, parent, title=text) r.setPos(x, deltaH) txt = QGraphicsSimpleTextItem(text, r) f = txt.font() f.setPointSize(8) fm = QFontMetricsF(f) elidedText = fm.elidedText(text, Qt.ElideMiddle, w) txt.setFont(f) txt.setText(elidedText) txt.setPos(r.boundingRect().center() - txt.boundingRect().center()) txt.setY(0) return r # A function to returns an item's width, by counting its children def itemWidth(item): if item.isFolder(): r = 0 for c in item.children(): r += itemWidth(c) return r or TEXT_WIDTH else: return TEXT_WIDTH def listItems(item, rect, level=0): delta = 0 for child in item.children(): w = itemWidth(child) if child.isFolder(): parent = addRectText(delta, w, rect, child.title(), level, tooltip=child.title()) parent.setToolTip(references.tooltip(references.textReference(child.ID()))) listItems(child, parent, level + 1) else: rectChild = addRectText(delta, TEXT_WIDTH, rect, "", level, tooltip=child.title()) rectChild.setToolTip(references.tooltip(references.textReference(child.ID()))) # Find tracked references in that scene (or parent folders) for ref in trackedItems: result = [] # Tests if POV scenePOV = False # Will hold true of character is POV of the current text, not containing folder if references.type(ref) == references.CharacterLetter: ID = references.ID(ref) c = child while c: if c.POV() == ID: result.append(c.ID()) if c == child: scenePOV = True c = c.parent() # Search in notes/references c = child while c: result += references.findReferencesTo(ref, c, recursive=False) c = c.parent() if result: ref2 = result[0] # Create a RefCircle with the reference c = RefCircle(TEXT_WIDTH / 2, - CIRCLE_WIDTH / 2, CIRCLE_WIDTH, ID=ref2, important=scenePOV) # Store it, with the position of that item, to display it on the line later on refCircles.append((ref, c, rect.mapToItem(outline, rectChild.pos()))) delta += w listItems(root, outline) OUTLINE_WIDTH = itemWidth(root) # Add Tracked items i = 0 itemsRect = s.addRect(0, 0, 0, 0) itemsRect.setPos(0, MAX_LEVEL * LEVEL_HEIGHT + SPACING) # Set of colors for plots (as long as they don't have their own colors) colors = [ "#D97777", "#AE5F8C", "#D9A377", "#FFC2C2", "#FFDEC2", "#D2A0BC", "#7B0F0F", "#7B400F", "#620C3D", "#AA3939", "#AA6C39", "#882D61", "#4C0000", "#4C2200", "#3D0022", ] for ref in trackedItems: if references.type(ref) == references.CharacterLetter: color = self._mdlCharacter.getCharacterByID(references.ID(ref)).color() else: color = QColor(colors[i % len(colors)]) # Rect r = QGraphicsRectItem(0, 0, TITLE_WIDTH, LINE_HEIGHT, itemsRect) r.setPen(QPen(Qt.NoPen)) r.setBrush(QBrush(color)) r.setPos(0, i * LINE_HEIGHT + i * SPACING) r.setToolTip(references.tooltip(ref)) i += 1 # Text name = references.title(ref) txt = QGraphicsSimpleTextItem(name, r) txt.setPos(r.boundingRect().center() - txt.boundingRect().center()) # Line line = PlotLine(0, 0, OUTLINE_WIDTH + SPACING, 0) line.setPos(TITLE_WIDTH, r.mapToScene(r.rect().center()).y()) s.addItem(line) line.setPen(QPen(color, 5)) line.setToolTip(references.tooltip(ref)) # We add the circles / references to text, on the line for ref2, circle, pos in refCircles: if ref2 == ref: circle.setParentItem(line) circle.setPos(pos.x(), 0) # self.view.fitInView(0, 0, TOTAL_WIDTH, i * LINE_HEIGHT, Qt.KeepAspectRatioByExpanding) # KeepAspectRatio self.view.setSceneRect(0, 0, 0, 0)
class AnimatedCalendar(): updatesRunning = False updateTimer = None listUpdateThread = None calDataLock = threading.Lock() calDataUpdated = False curCalendars = None def __init__(self, scene, widthCalTextArea, heightCalTextArea, borders, calFeeds, calUpdateSecs): self.masterScene = scene self.widthCalTextArea = widthCalTextArea self.heightCalTextArea = heightCalTextArea self.borders = borders self.calFeeds = calFeeds self.calendarUpdateSecs = calUpdateSecs # Background self.textBkgd = QGraphicsRectItem(0, 0, self.widthCalTextArea, self.heightCalTextArea) self.textBkgd.setPos(self.borders[3], self.borders[0]) self.textBkgd.setBrush(QColor("light green")) self.textBkgd.setZValue(10) scene.addItem(self.textBkgd) # Text Item self.textItem = QGraphicsTextItem() self.textItem.setFont(QFont("Segoe UI", 24)) self.textItem.setDefaultTextColor(QColor("black")) self.textItem.setPos(QPointF(self.borders[3]+10,self.borders[0]+10)) self.textItem.setHtml("<B>Hello</B>Hello") self.textItem.setZValue(20) self.textItem.setTextWidth(self.widthCalTextArea-20) scene.addItem(self.textItem) def start(self): self.updatesRunning = True QTimer.singleShot(100, self.updateCalendar) self.listUpdateThread = CalendarUpdateThread(self, self.calFeeds, self.calendarUpdateSecs) self.listUpdateThread.start() # print("CalStarted") def stop (self): self.updatesRunning = False if self.updateTimer != None: self.updateTimer.stop() if self.listUpdateThread != None: self.listUpdateThread.stop() def setNewCalendarEntries(self, calendars): with self.calDataLock: self.curCalendars = calendars self.calDataUpdated = True def updateCalendar(self): # print("Update cal") with self.calDataLock: if self.calDataUpdated and self.curCalendars != None: for calEvents in self.curCalendars: calStr = "" lastDay = -1 for anEvent in calEvents: # date, duration, summary, location, UID eventDate = anEvent[0] duration = anEvent[1] summary = anEvent[2] location = anEvent[3] if lastDay != eventDate.day: if lastDay != -1: calStr += "<br/>" calStr += "<b>" + anEvent[0].strftime("%a") + " (" + anEvent[0].strftime("%d %B)") + ")</b><br/>" lastDay = eventDate.day strDurTime = str(duration).rpartition(":")[0] durStr = (str(duration.days) + "day" + ("s" if duration.days != 1 else "")) if duration.days > 0 else strDurTime locStr = "<small>("+location+")</small>" if location != "" else "" calStr += anEvent[0].strftime("%H:%M") + " <small>(" + durStr + ")</small> " + summary + " " + locStr + "<br/>" # print (anEvent) # print(date) self.textItem.setHtml(calStr) self.textItem.setTextWidth(self.widthCalTextArea-20) self.textItem.update() self.calDataUpdated = False if not self.updatesRunning: return self.updateTimer = QTimer() self.updateTimer.setInterval(5000) self.updateTimer.setSingleShot(True) self.updateTimer.timeout.connect(self.updateCalendar) self.updateTimer.start()
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 TextInputWidget(QGraphicsObject): input_entered = pyqtSignal(str) input_cancelled = pyqtSignal(str) font = QFont('monospace', 16) def __init__(self, mode: str = 'number', label: str = '', getter=None, setter=None, parser=str, validator=None, parent: Optional[QGraphicsItem] = None): QGraphicsObject.__init__(self, parent) self.getter: Callable[[], str] = getter self.setter: Callable[[str], None] = setter self.parser: Callable[[str], Any] = parser self.validator: Callable[[Any], bool] = validator self.accept_button = Button("btn_accept", "Accept", parent=self) self.accept_button.set_base_color([ButtonColor.GREEN]) self.accept_button.clicked.connect(self.handle_input_accepted) self.cancel_button = Button("btn_cancel", "Cancel", parent=self) self.cancel_button.set_base_color([ButtonColor.RED]) self.cancel_button.clicked.connect( lambda: self.input_cancelled.emit(self.text_field.toPlainText())) self.accept_button.set_height(12) self.cancel_button.set_height(12) self.hor_padding = 2 self.background_rect = QGraphicsRectItem(parent=self) self.background_rect.setBrush(QBrush(QColor(50, 50, 50, 200))) self.text_field = NumberInputItem(self) self.text_field.setFont(self.font) self.text_field.setTextInteractionFlags(Qt.TextEditable) self.text_field.setDefaultTextColor(Qt.white) self.text_field.text_changed.connect(self.adjust_layout) self.text_field.set_is_number_mode(mode == 'number') if self.getter is not None: self.text_field.setPlainText(str(self.getter())) self.adjust_layout() self.text_field.accepted.connect( lambda: self.accept_button.click_button(artificial_emit=True)) self.text_field.cancelled.connect( lambda: self.cancel_button.click_button(artificial_emit=True)) self.text_label = QGraphicsTextItem(self) self.text_label.setFont(self.font) self.text_label.setPlainText(label) self.text_label.setDefaultTextColor(Qt.white) self.setVisible(False) def paint(self, painter: QPainter, option: QStyleOptionGraphicsItem, widget: Optional[QWidget] = ...) -> None: pass def boundingRect(self) -> QRectF: return self.background_rect.boundingRect().united(self.accept_button.boundingRect())\ .united(self.cancel_button.boundingRect()) def adjust_layout(self): self.accept_button.fit_to_contents() self.cancel_button.fit_to_contents() total_width = 3 * self.hor_padding + self.text_field.boundingRect().width() \ + self.text_label.boundingRect().width() total_width = max( total_width, 3 * self.hor_padding + self.accept_button.boundingRect().width() + self.cancel_button.boundingRect().width()) total_height = self.text_field.boundingRect().height() + self.accept_button.boundingRect().height() \ + 3 * self.hor_padding self.background_rect.setRect(0, 0, total_width, total_height) self.setTransformOriginPoint( QPointF(0.5 * total_width, 0.5 * total_height)) self.text_label.setPos(-0.5 * total_width + self.hor_padding, -0.5 * total_height + self.hor_padding) self.text_field.setPos( self.hor_padding + self.text_label.pos().x() + self.text_label.boundingRect().width(), self.text_label.pos().y()) self.background_rect.setPos(-0.5 * total_width, -0.5 * total_height) self.accept_button.set_width( (total_width - 3 * self.hor_padding) * 0.5) self.cancel_button.set_width( (total_width - 3 * self.hor_padding) * 0.5) self.accept_button.setPos(-0.5 * total_width + self.hor_padding, 2 * self.hor_padding) self.cancel_button.setPos(self.accept_button.pos() + QPointF( self.hor_padding + self.accept_button.boundingRect().width(), 0.0)) self.accept_button.set_disabled( len(self.text_field.toPlainText()) == 0) if self.validator is not None: try: value = self.parser(self.text_field.toPlainText()) is_valid = self.validator(value) except ValueError: is_valid = False self.text_field.set_is_valid(is_valid) self.accept_button.set_disabled(not is_valid) self.update() def get_value(self) -> str: return self.text_field.toPlainText() def set_value(self, text: str): self.text_field.setPlainText(text) self.adjust_layout() self.set_focus() def set_focus(self): self.text_field.setFocus(Qt.PopupFocusReason) cursor = self.text_field.textCursor() cursor.movePosition(QTextCursor.EndOfLine) self.text_field.setTextCursor(cursor) def set_getter_setter_parser_validator(self, getter: Callable[[], Any], setter: Callable[[Any], None], parser: Callable[[str], Any], validator: Callable[[Any], bool]): self.getter = getter self.setter = setter self.parser = parser self.validator = validator if self.getter is not None: self.text_field.setPlainText(str(self.getter())) self.adjust_layout() def set_label(self, label: str): self.text_label.setPlainText(label) self.adjust_layout() def handle_input_accepted(self): self.setter(self.parser(self.text_field.toPlainText())) self.input_entered.emit(self.text_field.toPlainText()) def handle_input_cancelled(self): self.input_cancelled.emit(str(self.getter()))
class MainFunction(QMainWindow): def __init__(self): QMainWindow.__init__(self) self.ui = Ui_MainWindow() self.ui.setupUi(self) self.round = 50 # set range. self.gScene = QGraphicsScene(0, 0, self.ui.graphicsView.width() - 5, self.ui.graphicsView.height() - 5, self.ui.graphicsView) print 'graphics View x %f', self.ui.graphicsView.width() print 'graphics View y %f', self.ui.graphicsView.height() self.ui.graphicsView.setScene(self.gScene) # test circle self.circle = QGraphicsEllipseItem() red = QBrush(Qt.red) pen = QPen(Qt.black) pen.setWidth(6) # test line self.x_line = QGraphicsLineItem(self.gScene.width() / 2, 0, self.gScene.width() / 2, self.gScene.height()) self.gScene.addItem(self.x_line) self.y_line = QGraphicsLineItem(0, self.gScene.width() / 2, self.gScene.height(), self.gScene.width() / 2) self.gScene.addItem(self.y_line) #self.circle2 = DrawCircles(int(self.gScene.width()/2), int(self.gScene.height()/2)) #self.gScene.addItem(self.circle2) print 'gScene View x %f', self.gScene.width() / 2 print 'gScene View y %f', self.gScene.height() / 2 self.circle = self.gScene.addEllipse( self.gScene.width() / 2 - self.round, self.gScene.height() / 2 - self.round, self.round * 2, self.round * 2, pen, red) # check Item argv. self.g_item = QGraphicsRectItem(self.gScene.width() / 2, self.gScene.height() / 2, 100, 100) self.gScene.addItem(self.g_item) self.g1_item = QGraphicsRectItem(self.gScene.width() / 2, self.gScene.height() / 2, 100, 100) self.gScene.addItem(self.g1_item) # self.gScene.addItem(self.circles) self.show() def slot_ok(self): random_x = np.random.random_integers(-300, 300) random_y = np.random.random_integers(-300, 300) # 값 조정은 display로 한다. self.ui.layer_a.display(100) tip = '' self.circle.setPos(float(random_x), float(random_y)) self.g1_item.setPos(float(random_x), float(random_y)) print 'x > %s' % self.g_item.x() print 'circle -> %d' % self.circle.x() print 'circle -> %d' % self.circle.y() if self.g_item.x() != self.circle.x(): tip += ' move x > %f\n' % (self.g_item.x() - self.circle.x()) if self.g_item.y() != self.circle.y(): tip += ' move y > %f' % (self.g_item.y() - self.circle.y()) self.ui.information.setText(tip)
buttonParent = QGraphicsRectItem() ellipseButton = Button(QPixmap(':/images/ellipse.png'), buttonParent) figure8Button = Button(QPixmap(':/images/figure8.png'), buttonParent) randomButton = Button(QPixmap(':/images/random.png'), buttonParent) tiledButton = Button(QPixmap(':/images/tile.png'), buttonParent) centeredButton = Button(QPixmap(':/images/centered.png'), buttonParent) ellipseButton.setPos(-100, -100) figure8Button.setPos(100, -100) randomButton.setPos(0, 0) tiledButton.setPos(-100, 100) centeredButton.setPos(100, 100) scene.addItem(buttonParent) buttonParent.setScale(0.75) buttonParent.setPos(200, 200) buttonParent.setZValue(65) # States. rootState = QState() ellipseState = QState(rootState) figure8State = QState(rootState) randomState = QState(rootState) tiledState = QState(rootState) centeredState = QState(rootState) # Values. for i, item in enumerate(items): # Ellipse. ellipseState.assignProperty(item, 'pos', QPointF(math.cos((i / 63.0) * 6.28) * 250,
def setAxisIntersect(self, intersect): self._axisIntersect = intersect # print "SkeletonsLayer(axis=%d) is updating intersect=%d" % (self._axis, self._axisIntersect) nodes, eIntersected, ePlane = self._3d._skeletons.intersect(self._axis, self._axisIntersect) # update existing items toRemove = [] for node, item in self._node2view.items(): if node.pos[self._axis] != self._axisIntersect: self._scene.removeItem(item) toRemove.append(node) elif node.pointF(self._axis) != item.pos(): item.setPos(self._scene.data2scene.map(node.pointF(self._axis))) if node.isSelected() != item.isSelected(): item.setSelected(node.isSelected()) assert item.isSelected() == node.isSelected() i = 0 newSize = [0, 0] for j in range(3): if j == self._axis: continue newSize[i] = node.shape[j] i += 1 newRectF = QRectF(0, 0, *newSize) newRectF = self._scene.data2scene.mapRect(newRectF) item.setRect( QRectF( old_div(-newRectF.width(), 2.0), old_div(-newRectF.height(), 2.0), newRectF.width(), newRectF.height(), ) ) for r in toRemove: del self._node2view[r] # add new views for nodes for n in nodes: if n in self._node2view: continue pos2D = list(n.pos) del pos2D[self._axis] shape2D = n.shape2D(self._axis) itm = QGraphicsSkeletonNode(shape2D, skeletons=self._3d._skeletons, node=n) itm.setPos(self._scene.data2scene.map(QPointF(*pos2D))) itm.setSelected(n.isSelected()) self._scene.addItem(itm) self._node2view[n] = itm for itm in list(self._edge2view.values()): self._scene.removeItem(itm) self._edge2view = dict() for e in ePlane: l = QLineF(e[0].pointF(), e[1].pointF()) c1 = e[0].color() c2 = e[1].color() assert sys.version_info.major == 2, ( "Alert! This function has not been " "tested under python 3. Please remove this assertion and be wary of any " "strnage behavior you encounter" ) mixColor = QColor((c1.red() + c2.red()) // 2, (c1.green() + c2.green()) // 2, (c1.blue() + c2.blue()) // 2) line = QGraphicsLineItem(self._scene.data2scene.map(l)) line.setPen(QPen(mixColor)) self._scene.addItem(line) self._edge2view[e] = line for theEdge, e in eIntersected: c1 = theEdge[0].color() c2 = theEdge[1].color() assert sys.version_info.major == 2, ( "Alert! This function has not been " "tested under python 3. Please remove this assertion and be wary of any " "strnage behavior you encounter" ) mixColor = QColor((c1.red() + c2.red()) // 2, (c1.green() + c2.green()) // 2, (c1.blue() + c2.blue()) // 2) nodeSize = 6 p = QGraphicsRectItem(old_div(-nodeSize, 2), old_div(-nodeSize, 2), nodeSize, nodeSize) pos2D = list(e) del pos2D[self._axis] p.setPos(self._scene.data2scene.map(QPointF(*pos2D))) p.setPen(QPen(mixColor)) self._scene.addItem(p) self._edge2view[e] = p
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 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]])
class Scene(QGraphicsScene): def __init__(self, parent=None): QGraphicsScene.__init__(self, parent) # set pritisnutih tastera self.keys_pressed = set() # pozadina self.bg = QGraphicsRectItem() self.bg.setRect(-1, -1, SCREEN_WIDTH + 2, SCREEN_HEIGHT + 2) self.bg.setBrush(QBrush(Qt.black)) self.addItem(self.bg) # brojaci za pomeranje protivnika self.left = 1 self.right = 5 # lista protivnika self.enemies = [] # nasumicni indeks protivnika koji puca self.randomEnemyIndex = randint(0, len(self.enemies)) # nasumicna lista indeksa protivnika koji ce imati specijalnu moc self.enemyPowerUps = [] # pravljenje i iscrtavanje protivnika self.threadFinished = False _thread.start_new_thread(self.draw_enemies, ()) while not self.threadFinished: continue self.threadFinished = False # igrac 1 (plavi) self.player1 = Player1() self.player1.setPos(20, 530) self.addItem(self.player1) # igrac 2 (crveni) self.player2 = Player2() self.player2.setPos(589, 530) self.addItem(self.player2) # metak igraca 1 (plavi) self.bullet1 = Bullet1(PLAYER_BULLET_X_OFFSETS, PLAYER_BULLET_Y) self.bullet1.setPos(SCREEN_WIDTH, SCREEN_HEIGHT) self.addItem(self.bullet1) # metak igraca 2 (crveni) self.bullet2 = Bullet2(PLAYER_BULLET_X_OFFSETS, PLAYER_BULLET_Y) self.bullet2.setPos(SCREEN_WIDTH, SCREEN_HEIGHT) self.addItem(self.bullet2) # pobednik self.winner = 0 # power up i power down igraca 1 (plavi) self.player1PowerUp = QGraphicsPixmapItem() self.player1PowerUp.setPixmap(QPixmap("Images/powerUp.png")) self.player1PowerDown = QGraphicsPixmapItem() self.player1PowerDown.setPixmap(QPixmap("Images/powerDown.png")) # power up i power down igraca 2 (crveni) self.player2PowerUp = QGraphicsPixmapItem() self.player2PowerUp.setPixmap(QPixmap("Images/powerUp.png")) self.player2PowerDown = QGraphicsPixmapItem() self.player2PowerDown.setPixmap(QPixmap("Images/powerDown.png")) # protivnicki metak (narandzasti) self.bulletEnemy = BulletEnemy(ENEMY_BULLET_X_OFFSET, ENEMY_BULLET_Y_OFFSET) self.bulletEnemy.setPos(SCREEN_WIDTH, SCREEN_HEIGHT) self.addItem(self.bulletEnemy) # polje za rezultate self.scoreField = QGraphicsRectItem() self.scoreField.setRect(672, -1, 128, SCREEN_HEIGHT + 2) self.scoreField.setBrush(QBrush(Qt.darkGray)) self.addItem(self.scoreField) # trenutni nivo self.level = 1 # iscrtavanje trenutnog nivoa self.levelFont = QFont() self.levelFont.setPixelSize(25) self.levelFont.setBold(1) self.levelField = QGraphicsSimpleTextItem("Level: " + str(self.level)) self.levelField.setBrush(QBrush(Qt.green)) self.levelField.setPos(677, 20) self.levelField.setFont(self.levelFont) self.addItem(self.levelField) self.playerFont = QFont() self.playerFont.setPixelSize(20) self.playerFont.setBold(1) # iscrtavanje broja zivota igraca 1 self.player1Lives = QGraphicsSimpleTextItem("Player 1: ") self.player1Lives.setBrush(QBrush(Qt.blue)) self.player1Lives.setPos(674, 130) self.player1Lives.setFont(self.playerFont) self.addItem(self.player1Lives) self.livesPlayer1 = [ LifePlayer1(680, 175), LifePlayer1(720, 175), LifePlayer1(760, 175) ] for l in self.livesPlayer1: self.addItem(l) # iscrtavanje broja zivota igraca 2 self.player2Lives = QGraphicsSimpleTextItem("Player 2: ") self.player2Lives.setBrush(QBrush(Qt.blue)) self.player2Lives.setPos(674, 330) self.player2Lives.setFont(self.playerFont) self.addItem(self.player2Lives) self.livesPlayer2 = [ LifePlayer2(680, 375), LifePlayer2(720, 375), LifePlayer2(760, 375) ] for l in self.livesPlayer2: self.addItem(l) self.scoreFont = QFont() self.scoreFont.setPixelSize(20) self.scoreFont.setBold(1) # iscrtavanje rezultata igraca 1 self.player1Score = 0 self.player1Scores = QGraphicsSimpleTextItem("Score: \n" + str(self.player1Score)) self.player1Scores.setBrush(QBrush(Qt.blue)) self.player1Scores.setPos(674, 220) self.player1Scores.setFont(self.scoreFont) self.addItem(self.player1Scores) # iscrtavanje rezultata igraca 1 self.player2Score = 0 self.player2Scores = QGraphicsSimpleTextItem("Score: \n" + str(self.player2Score)) self.player2Scores.setBrush(QBrush(Qt.blue)) self.player2Scores.setPos(674, 420) self.player2Scores.setFont(self.scoreFont) self.addItem(self.player2Scores) # tajmer za iscrtavanje slike 60 puta u sekndi (60 fps) self.timer = QBasicTimer() self.timer.start(16, self) # tajmer za pomeranje neprijatelja self.timerEnemy = QTimer() self.timerEnemy.timeout.connect(self.game_update_enemy) self.timerEnemy.start(1000) # icrtavanje scene 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) self.view.setGeometry(560, 240, 0, 0) def keyPressEvent(self, event): self.keys_pressed.add(event.key()) def keyReleaseEvent(self, event): self.keys_pressed.remove(event.key()) def timerEvent(self, event): self.game_update() self.update() def game_update(self): self.player1.game_update(self.keys_pressed) self.player2.game_update(self.keys_pressed) for e in self.enemies: # ako je neprijatelj zavrsio sa padanjem, brisemo ga if e.frames == -1: self.removeItem(e) self.enemies.remove(e) continue # ako je neprijatelj izabran za padanje, nastavlja da pada if e.chosen: e.collapse() # igrac 1 pogodio neprijatelja if e.x() <= self.bullet1.x() <= e.x() + 32: if e.y() <= self.bullet1.y() <= e.y() + 26: if e.powerUp: temp = randint(0, 3) if temp == 0: if self.player1.speed == 2: self.removeItem(self.player1PowerDown) self.player1.speed = 8 self.player1PowerUp.setPos(770, 130) self.addItem(self.player1PowerUp) elif temp == 1: if self.player1.speed == 8: self.removeItem(self.player1PowerUp) self.player1.speed = 2 self.player1PowerDown.setPos(770, 130) self.addItem(self.player1PowerDown) elif temp == 2: if self.player1.lives == 3: pass elif self.player1.lives == 2: self.livesPlayer1.append(LifePlayer1(760, 175)) self.player1.lives += 1 self.addItem(self.livesPlayer1[-1]) elif self.player1.lives == 1: self.livesPlayer1.append(LifePlayer1(720, 175)) self.player1.lives += 1 self.addItem((self.livesPlayer1[-1])) else: pass else: self.player1Score += 100 self.player1Scores.setText("Score: \n" + str(self.player1Score)) self.removeItem(e) self.enemies.remove(e) self.removeItem(self.bullet1) self.bullet1.setPos(SCREEN_WIDTH, SCREEN_HEIGHT) self.addItem(self.bullet1) self.player1Score += 10 self.player1Scores.setText("Score: \n" + str(self.player1Score)) continue # igrac 2 pogodio neprijatelja if e.x() <= self.bullet2.x() <= e.x() + 32: if e.y() <= self.bullet2.y() <= e.y() + 26: if e.powerUp: temp = randint(0, 3) if temp == 0: if self.player2.speed == 2: self.removeItem(self.player2PowerDown) self.player2.speed = 8 self.player2PowerUp.setPos(770, 330) self.addItem(self.player2PowerUp) elif temp == 1: if self.player2.speed == 8: self.removeItem(self.player2PowerUp) self.player2.speed = 2 self.player2PowerDown.setPos(770, 330) self.addItem(self.player2PowerDown) elif temp == 2: if self.player2.lives == 3: pass elif self.player2.lives == 2: self.livesPlayer2.append(LifePlayer2(760, 375)) self.player2.lives += 1 self.addItem(self.livesPlayer2[-1]) elif self.player2.lives == 1: self.livesPlayer2.append(LifePlayer2(720, 375)) self.player2.lives += 1 self.addItem((self.livesPlayer2[-1])) else: pass else: self.player2Score += 100 self.player2Scores.setText("Score: \n" + str(self.player2Score)) self.removeItem(e) self.enemies.remove(e) self.removeItem(self.bullet2) self.bullet2.setPos(SCREEN_WIDTH, SCREEN_HEIGHT) self.addItem(self.bullet2) self.player2Score += 10 self.player2Scores.setText("Score: \n" + str(self.player2Score)) continue # na igraca 1 se obrusio neprijatelj if self.player1.y() <= e.y() + 26 <= self.player1.y() + 53: if self.player1.x() <= e.x( ) <= self.player1.x() + 69 or self.player1.x( ) <= e.x() + 32 <= self.player1.x() + 69: if self.player1.lives > 0: if self.player1.speed == 8: self.removeItem(self.player1PowerUp) elif self.player1.speed == 2: self.removeItem(self.player1PowerDown) self.player1.speed = 4 e.frames = -1 self.player1.lives -= 1 self.removeItem(self.livesPlayer1[-1]) self.livesPlayer1.remove(self.livesPlayer1[-1]) self.player1.setPos(20, 530) if self.player1.lives <= 0: self.removeItem(self.player1) if self.player1.speed == 8: self.removeItem(self.player1PowerUp) elif self.player1.speed == 2: self.removeItem(self.player1PowerDown) if self.player2.lives > 0: self.winner = 2 # na igraca 2 se obrusio neprijatelj if self.player2.y() <= e.y() + 26 <= self.player2.y() + 53: if self.player2.x() <= e.x( ) <= self.player2.x() + 69 or self.player2.x( ) <= e.x() + 32 <= self.player2.x() + 69: if self.player2.lives > 0: if self.player2.speed == 8: self.removeItem(self.player1PowerUp) elif self.player2.speed == 2: self.removeItem(self.player2PowerDown) self.player2.speed = 4 e.frames = -1 self.player2.lives -= 1 self.removeItem(self.livesPlayer2[-1]) self.livesPlayer2.remove(self.livesPlayer2[-1]) self.player2.setPos(589, 530) if self.player2.lives <= 0: self.removeItem(self.player2) if self.player2.speed == 8: self.removeItem(self.player2PowerUp) elif self.player2.speed == 2: self.removeItem(self.player2PowerDown) if self.player1.lives > 0: self.winner = 1 # igraca 1 pogodio laser neprijatelja if self.player1.x() + 69 >= self.bulletEnemy.x() >= self.player1.x(): if self.player1.y() + 53 >= self.bulletEnemy.y() >= self.player1.y( ): if self.player1.lives > 0: if self.player1.speed == 8: self.removeItem(self.player1PowerUp) elif self.player1.speed == 2: self.removeItem(self.player1PowerDown) self.player1.speed = 4 self.bulletEnemy.active = False self.player1.lives -= 1 self.removeItem(self.livesPlayer1[-1]) self.livesPlayer1.remove(self.livesPlayer1[-1]) self.player1.setPos(20, 530) if self.player1.lives <= 0: self.removeItem(self.player1) if self.player2.lives > 0: self.winner = 2 # igraca 2 pogodio laser neprijatelja if self.player2.x() + 69 >= self.bulletEnemy.x() >= self.player2.x(): if self.player2.y() + 53 >= self.bulletEnemy.y() >= self.player2.y( ): if self.player2.lives > 0: if self.player2.speed == 8: self.removeItem(self.player2PowerUp) elif self.player2.speed == 2: self.removeItem(self.player2PowerDown) self.player2.speed = 4 self.bulletEnemy.active = False self.player2.lives -= 1 self.removeItem(self.livesPlayer2[-1]) self.livesPlayer2.remove(self.livesPlayer2[-1]) self.player2.setPos(589, 530) if self.player2.lives <= 0: self.removeItem(self.player2) if self.player1.lives > 0: self.winner = 1 # pomeranje metaka self.bullet1.game_update(self.keys_pressed, self.player1) self.bullet2.game_update(self.keys_pressed, self.player2) try: self.bulletEnemy.game_update(self.enemies[self.randomEnemyIndex]) if not self.bulletEnemy.active: self.randomEnemyIndex = randint(0, len(self.enemies)) except: self.randomEnemyIndex = randint(0, len(self.enemies)) # nasumicno biranje obrusavanja try: if randint(0, 500) == 0: self.enemies[randint(0, len(self.enemies))].chosen = True except: pass # kraj igre if self.player1.lives == 0 and self.player2.lives == 0: self.timer.stop() self.timerEnemy.stop() self.removeItem(self.bg) self.bg.setPos(-128, 0) self.addItem(self.bg) fontWinner = QFont() fontWinner.setPixelSize(40) textWinner = QGraphicsSimpleTextItem() textWinner.setFont(fontWinner) if self.winner == 1: textWinner.setText("WINNER PLAYER 1") textWinner.setBrush(QBrush(Qt.blue)) self.player1.setPos(310, 300) self.addItem(self.player1) elif self.winner == 2: textWinner.setText("WINNER PLAYER 2") textWinner.setBrush(QBrush(Qt.red)) self.player2.setPos(310, 300) self.addItem(self.player2) textWinner.setPos(180, 200) self.addItem(textWinner) # pobedjen nivo if len(self.enemies) == 0: self.timer.stop() self.timerEnemy.stop() if self.player1.lives > 0: self.removeItem(self.player1) self.removeItem(self.bullet1) if self.player2.lives > 0: self.removeItem(self.player2) self.removeItem(self.bullet2) self.removeItem(self.bulletEnemy) self.new_level() # pomeranje neprijatelja def game_update_enemy(self): if 0 < self.right < 9: self.right += 1 if self.right == 9: self.right = 0 self.left = 1 for e in self.enemies: e.move_right() elif 0 < self.left < 9: self.left += 1 if self.left == 9: self.left = 0 self.right = 1 for e in self.enemies: e.move_left() # pravljenje novog nivoa def new_level(self): self.level += 1 self.levelField.setText("Level: " + str(self.level)) self.left = 1 self.right = 5 self.threadFinished = False _thread.start_new_thread(self.draw_enemies, ()) while not self.threadFinished: continue self.threadFinished = False for e in self.enemies: e.collapseSpeed += self.level e.collapseFrames = 700 / e.collapseSpeed if self.player1.lives > 0: self.player1.setPos(20, 530) self.bullet1 = Bullet1(PLAYER_BULLET_X_OFFSETS, PLAYER_BULLET_Y) self.bullet1.setPos(SCREEN_WIDTH, SCREEN_HEIGHT) self.addItem(self.bullet1) self.addItem(self.player1) if self.player2.lives > 0: self.player2.setPos(589, 530) self.bullet2 = Bullet2(PLAYER_BULLET_X_OFFSETS, PLAYER_BULLET_Y) self.bullet2.setPos(SCREEN_WIDTH, SCREEN_HEIGHT) self.addItem(self.bullet2) self.addItem(self.player2) self.bulletEnemy = BulletEnemy(ENEMY_BULLET_X_OFFSET, ENEMY_BULLET_Y_OFFSET) self.bulletEnemy.setPos(SCREEN_WIDTH, SCREEN_HEIGHT) self.bulletEnemy.speed += self.level self.bulletEnemy.frames = 700 / self.bulletEnemy.speed self.addItem(self.bulletEnemy) self.timer.start(16, self) self.timerEnemy.start(1000) def draw_enemies(self): self.threadFinished = False self.enemies = [ Enemy(131, 10), Enemy(173, 10), Enemy(215, 10), Enemy(257, 10), Enemy(299, 10), Enemy(341, 10), Enemy(383, 10), Enemy(425, 10), Enemy(467, 10), Enemy(509, 10), Enemy(131, 50), Enemy(173, 50), Enemy(215, 50), Enemy(257, 50), Enemy(299, 50), Enemy(341, 50), Enemy(383, 50), Enemy(425, 50), Enemy(467, 50), Enemy(509, 50), Enemy(131, 90), Enemy(173, 90), Enemy(215, 90), Enemy(257, 90), Enemy(299, 90), Enemy(341, 90), Enemy(383, 90), Enemy(425, 90), Enemy(467, 90), Enemy(509, 90) ] pool = multiprocessing.Pool(processes=1) result = pool.apply_async(get_enemy_power_ups, (len(self.enemies) - 1, 5)) self.enemyPowerUps = result.get(timeout=1) pool.close() for i in self.enemyPowerUps: self.enemies[i].setPixmap(QPixmap("Images/enemyPowerUp.png")) self.enemies[i].powerUp = True for e in self.enemies: self.addItem(e) self.randomEnemyIndex = randint(0, len(self.enemies)) self.threadFinished = True
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 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()
def refresh(self): if not self._mdlPlots or not self._mdlOutline or not self._mdlCharacter: return if not self.isVisible(): return LINE_HEIGHT = 18 SPACING = 3 TEXT_WIDTH = self.sldTxtSize.value() CIRCLE_WIDTH = 10 LEVEL_HEIGHT = 12 s = self.scene s.clear() # Get Max Level (max depth) root = self._mdlOutline.rootItem def maxLevel(item, level=0, max=0): if level > max: max = level for c in item.children(): m = maxLevel(c, level + 1) if m > max: max = m return max MAX_LEVEL = maxLevel(root) # Get the list of tracked items (array of references) trackedItems = [] if self.actPlots.isChecked(): trackedItems += self.plotReferences() if self.actCharacters.isChecked(): trackedItems += self.charactersReferences() ROWS_HEIGHT = len(trackedItems) * (LINE_HEIGHT + SPACING) fm = QFontMetrics(s.font()) max_name = 0 for ref in trackedItems: name = references.title(ref) max_name = max(fm.width(name), max_name) TITLE_WIDTH = max_name + 2 * SPACING # Add Folders and Texts outline = OutlineRect(0, 0, 0, ROWS_HEIGHT + SPACING + MAX_LEVEL * LEVEL_HEIGHT) s.addItem(outline) outline.setPos(TITLE_WIDTH + SPACING, 0) refCircles = [ ] # a list of all references, to be added later on the lines # A Function to add a rect with centered elided text def addRectText(x, w, parent, text="", level=0, tooltip=""): deltaH = LEVEL_HEIGHT if level else 0 r = OutlineRect(0, 0, w, parent.rect().height() - deltaH, parent, title=text) r.setPos(x, deltaH) txt = QGraphicsSimpleTextItem(text, r) f = txt.font() f.setPointSize(8) fm = QFontMetricsF(f) elidedText = fm.elidedText(text, Qt.ElideMiddle, w) txt.setFont(f) txt.setText(elidedText) txt.setPos(r.boundingRect().center() - txt.boundingRect().center()) txt.setY(0) return r # A function to returns an item's width, by counting its children def itemWidth(item): if item.isFolder(): r = 0 for c in item.children(): r += itemWidth(c) return r or TEXT_WIDTH else: return TEXT_WIDTH def listItems(item, rect, level=0): delta = 0 for child in item.children(): w = itemWidth(child) if child.isFolder(): parent = addRectText(delta, w, rect, child.title(), level, tooltip=child.title()) parent.setToolTip( references.tooltip(references.textReference( child.ID()))) listItems(child, parent, level + 1) else: rectChild = addRectText(delta, TEXT_WIDTH, rect, "", level, tooltip=child.title()) rectChild.setToolTip( references.tooltip(references.textReference( child.ID()))) # Find tracked references in that scene (or parent folders) for ref in trackedItems: result = [] # Tests if POV scenePOV = False # Will hold true of character is POV of the current text, not containing folder if references.type(ref) == references.CharacterLetter: ID = references.ID(ref) c = child while c: if c.POV() == ID: result.append(c.ID()) if c == child: scenePOV = True c = c.parent() # Search in notes/references c = child while c: result += references.findReferencesTo( ref, c, recursive=False) c = c.parent() if result: ref2 = result[0] # Create a RefCircle with the reference c = RefCircle(TEXT_WIDTH / 2, -CIRCLE_WIDTH / 2, CIRCLE_WIDTH, ID=ref2, important=scenePOV) # Store it, with the position of that item, to display it on the line later on refCircles.append( (ref, c, rect.mapToItem(outline, rectChild.pos()))) delta += w listItems(root, outline) OUTLINE_WIDTH = itemWidth(root) # Add Tracked items i = 0 itemsRect = s.addRect(0, 0, 0, 0) itemsRect.setPos(0, MAX_LEVEL * LEVEL_HEIGHT + SPACING) # Set of colors for plots (as long as they don't have their own colors) colors = [ "#D97777", "#AE5F8C", "#D9A377", "#FFC2C2", "#FFDEC2", "#D2A0BC", "#7B0F0F", "#7B400F", "#620C3D", "#AA3939", "#AA6C39", "#882D61", "#4C0000", "#4C2200", "#3D0022", ] for ref in trackedItems: if references.type(ref) == references.CharacterLetter: color = self._mdlCharacter.getCharacterByID( references.ID(ref)).color() else: color = QColor(colors[i % len(colors)]) # Rect r = QGraphicsRectItem(0, 0, TITLE_WIDTH, LINE_HEIGHT, itemsRect) r.setPen(QPen(Qt.NoPen)) r.setBrush(QBrush(color)) r.setPos(0, i * LINE_HEIGHT + i * SPACING) r.setToolTip(references.tooltip(ref)) i += 1 # Text name = references.title(ref) txt = QGraphicsSimpleTextItem(name, r) txt.setPos(r.boundingRect().center() - txt.boundingRect().center()) # Line line = PlotLine(0, 0, OUTLINE_WIDTH + SPACING, 0) line.setPos(TITLE_WIDTH, r.mapToScene(r.rect().center()).y()) s.addItem(line) line.setPen(QPen(color, 5)) line.setToolTip(references.tooltip(ref)) # We add the circles / references to text, on the line for ref2, circle, pos in refCircles: if ref2 == ref: circle.setParentItem(line) circle.setPos(pos.x(), 0) # self.view.fitInView(0, 0, TOTAL_WIDTH, i * LINE_HEIGHT, Qt.KeepAspectRatioByExpanding) # KeepAspectRatio self.view.setSceneRect(0, 0, 0, 0)
def refresh(self): if not self._mdlPlots or not self._mdlOutline or not self._mdlPersos: pass LINE_HEIGHT = 18 SPACING = 3 TEXT_WIDTH = self.sldTxtSize.value() CIRCLE_WIDTH = 10 LEVEL_HEIGHT = 12 s = self.scene s.clear() # Get Max Level (max depth) root = self._mdlOutline.rootItem def maxLevel(item, level=0, max=0): if level > max: max = level for c in item.children(): m = maxLevel(c, level + 1) if m > max: max = m return max MAX_LEVEL = maxLevel(root) # Generate left entries # (As of now, plot only) plotsID = self._mdlPlots.getPlotsByImportance() trackedItems = [] fm = QFontMetrics(s.font()) max_name = 0 for importance in plotsID: for ID in importance: name = self._mdlPlots.getPlotNameByID(ID) ref = references.plotReference(ID, searchable=True) trackedItems.append((ID, ref, name)) max_name = max(fm.width(name), max_name) ROWS_HEIGHT = len(trackedItems) * (LINE_HEIGHT + SPACING ) TITLE_WIDTH = max_name + 2 * SPACING # Add Folders and Texts outline = OutlineRect(0, 0, 0, ROWS_HEIGHT + SPACING + MAX_LEVEL * LEVEL_HEIGHT) s.addItem(outline) outline.setPos(TITLE_WIDTH + SPACING, 0) refCircles = [] # a list of all references, to be added later on the lines # A Function to add a rect with centered elided text def addRectText(x, w, parent, text="", level=0, tooltip=""): deltaH = LEVEL_HEIGHT if level else 0 r = OutlineRect(0, 0, w, parent.rect().height()-deltaH, parent, title=text) r.setPos(x, deltaH) txt = QGraphicsSimpleTextItem(text, r) f = txt.font() f.setPointSize(8) fm = QFontMetricsF(f) elidedText = fm.elidedText(text, Qt.ElideMiddle, w) txt.setFont(f) txt.setText(elidedText) txt.setPos(r.boundingRect().center() - txt.boundingRect().center()) txt.setY(0) return r # A function to returns an item's width, by counting its children def itemWidth(item): if item.isFolder(): r = 0 for c in item.children(): r += itemWidth(c) return r or TEXT_WIDTH else: return TEXT_WIDTH def listItems(item, rect, level=0): delta = 0 for child in item.children(): w = itemWidth(child) if child.isFolder(): parent = addRectText(delta, w, rect, child.title(), level, tooltip=child.title()) parent.setToolTip(references.tooltip(references.textReference(child.ID()))) listItems(child, parent, level + 1) else: rectChild = addRectText(delta, TEXT_WIDTH, rect, "", level, tooltip=child.title()) rectChild.setToolTip(references.tooltip(references.textReference(child.ID()))) # Find tracked references in that scene (or parent folders) for ID, ref, name in trackedItems: result = [] c = child while c: result += references.findReferencesTo(ref, c, recursive=False) c = c.parent() if result: ref2 = result[0] # Create a RefCircle with the reference c = RefCircle(TEXT_WIDTH / 2, - CIRCLE_WIDTH / 2, CIRCLE_WIDTH, ID=ref2) # Store it, with the position of that item, to display it on the line later on refCircles.append((ref, c, rect.mapToItem(outline, rectChild.pos()))) delta += w listItems(root, outline) OUTLINE_WIDTH = itemWidth(root) # Add Plots i = 0 itemsRect = s.addRect(0, 0, 0, 0) itemsRect.setPos(0, MAX_LEVEL * LEVEL_HEIGHT + SPACING) for ID, ref, name in trackedItems: color = randomColor() # Rect r = QGraphicsRectItem(0, 0, TITLE_WIDTH, LINE_HEIGHT, itemsRect) r.setPen(QPen(Qt.NoPen)) r.setBrush(QBrush(color)) r.setPos(0, i * LINE_HEIGHT + i * SPACING) i += 1 # Text txt = QGraphicsSimpleTextItem(name, r) txt.setPos(r.boundingRect().center() - txt.boundingRect().center()) # Line line = PlotLine(0, 0, OUTLINE_WIDTH + SPACING, 0) line.setPos(TITLE_WIDTH, r.mapToScene(r.rect().center()).y()) s.addItem(line) line.setPen(QPen(color, 5)) line.setToolTip(self.tr("Plot: ") + name) # We add the circles / references to text, on the line for ref2, circle, pos in refCircles: if ref2 == ref: circle.setParentItem(line) circle.setPos(pos.x(), 0) # self.view.fitInView(0, 0, TOTAL_WIDTH, i * LINE_HEIGHT, Qt.KeepAspectRatioByExpanding) # KeepAspectRatio self.view.setSceneRect(0, 0, 0, 0)