class View(QWidget): def __init__(self, buffer, view_info): super(View, self).__init__() self.buffer = buffer # Init widget attributes. if get_emacs_func_result("eaf-emacs-not-use-reparent-technology", []): self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.NoDropShadowWindowHint) else: self.setWindowFlags(Qt.FramelessWindowHint) self.setAttribute(Qt.WA_X11DoNotAcceptFocus, True) self.setContentsMargins(0, 0, 0, 0) self.installEventFilter(self) # Init attributes. self.view_info = view_info (self.buffer_id, self.emacs_xid, self.x, self.y, self.width, self.height) = view_info.split(":") self.x = int(self.x) self.y = int(self.y) self.width = int(self.width) self.height = int(self.height) # Build QGraphicsView. self.layout = QVBoxLayout(self) self.layout.setSpacing(0) self.layout.setContentsMargins(0, 0, 0, 0) self.graphics_view = QGraphicsView(buffer, self) # Remove border from QGraphicsView. self.graphics_view.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.graphics_view.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.graphics_view.setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform | QPainter.TextAntialiasing) self.graphics_view.setFrameStyle(QFrame.NoFrame) # Fill background color. self.graphics_view.setBackgroundBrush(QBrush(buffer.background_color)) # Add graphics view. self.layout.addWidget(self.graphics_view) # NOTE: show function must start before resize to trigger *first* resizeEvent after show. self.show() # Resize after show to trigger fit view operation. self.resize(self.width, self.height) self.buffer.aspect_ratio_change.connect(self.adjust_aspect_ratio) def resizeEvent(self, event): # Fit content to view rect just when buffer fit_to_view option is enable. if self.buffer.fit_to_view: if event.oldSize().isValid(): self.graphics_view.fitInView( self.graphics_view.scene().sceneRect(), Qt.KeepAspectRatio) QWidget.resizeEvent(self, event) def adjust_aspect_ratio(self): widget_width = self.width widget_height = self.height if self.buffer.aspect_ratio == 0: self.buffer.buffer_widget.resize(self.width, self.height) self.layout.setContentsMargins(0, 0, 0, 0) else: view_height = widget_height * ( 1 - 2 * self.buffer.vertical_padding_ratio) view_width = view_height * self.buffer.aspect_ratio horizontal_padding = (widget_width - view_width) / 2 vertical_padding = self.buffer.vertical_padding_ratio * widget_height self.buffer.buffer_widget.resize(view_width, view_height) self.layout.setContentsMargins(horizontal_padding, vertical_padding, horizontal_padding, vertical_padding) def eventFilter(self, obj, event): # import time # print(time.time(), event.type()) if event.type() in [QEvent.ShortcutOverride]: eval_in_emacs('eaf-activate-emacs-window', [self.buffer_id]) # Focus emacs buffer when user click view. event_type = [ QEvent.MouseButtonPress, QEvent.MouseButtonRelease, QEvent.MouseButtonDblClick ] if platform.system() != "Darwin": event_type += [QEvent.Wheel] if event.type() in event_type: focus_emacs_buffer(self.buffer_id) # Stop mouse event. return True return False def showEvent(self, event): # NOTE: we must reparent after widget show, otherwise reparent operation maybe failed. self.reparent() if platform.system() in ["Windows", "Darwin"]: eval_in_emacs('eaf-activate-emacs-window', []) # Make graphics view at left-top corner after show. self.graphics_view.verticalScrollBar().setValue(0) self.graphics_view.horizontalScrollBar().setValue(0) def reparent(self): # print("Reparent: ", self.buffer.url) qwindow = self.windowHandle() if not get_emacs_func_result("eaf-emacs-not-use-reparent-technology", []): qwindow.setParent(QWindow.fromWinId(int(self.emacs_xid))) qwindow.setPosition(QPoint(self.x, self.y)) def try_show_top_view(self): if get_emacs_func_result("eaf-emacs-not-use-reparent-technology", []): self.setWindowFlag(Qt.WindowStaysOnTopHint, True) self.show() def try_hide_top_view(self): if get_emacs_func_result("eaf-emacs-not-use-reparent-technology", []): self.setWindowFlag(Qt.WindowStaysOnTopHint, False) self.hide() def destroy_view(self): # print("Destroy: ", self.buffer.url) self.destroy()
class SlideViewer(QWidget): eventSignal = pyqtSignal(PyQt5.QtCore.QEvent) def __init__(self, parent: QWidget = None, viewer_top_else_left=True): super().__init__(parent) self.init_view() self.init_labels(word_wrap=viewer_top_else_left) self.init_layout(viewer_top_else_left) def init_view(self): self.scene = MyGraphicsScene() self.view = QGraphicsView() self.view.setScene(self.scene) self.view.setTransformationAnchor(QGraphicsView.NoAnchor) self.view.viewport().installEventFilter(self) self.rubber_band = QRubberBand(QRubberBand.Rectangle, self) self.mouse_press_view = QPoint() self.view.horizontalScrollBar().sliderMoved.connect( self.on_view_changed) self.view.verticalScrollBar().sliderMoved.connect(self.on_view_changed) self.scale_initializer_deffered_function = None self.slide_view_params = None self.slide_helper = None def init_labels(self, word_wrap): # word_wrap = True self.level_downsample_label = QLabel() self.level_downsample_label.setWordWrap(word_wrap) self.level_size_label = QLabel() self.level_size_label.setWordWrap(word_wrap) self.selected_rect_label = QLabel() self.selected_rect_label.setWordWrap(word_wrap) self.mouse_pos_scene_label = QLabel() self.mouse_pos_scene_label.setWordWrap(word_wrap) self.view_rect_scene_label = QLabel() self.view_rect_scene_label.setWordWrap(word_wrap) self.labels_layout = QVBoxLayout() self.labels_layout.setAlignment(Qt.AlignTop) self.labels_layout.addWidget(self.level_downsample_label) self.labels_layout.addWidget(self.level_size_label) self.labels_layout.addWidget(self.mouse_pos_scene_label) # self.labels_layout.addWidget(self.selected_rect_label) self.labels_layout.addWidget(self.view_rect_scene_label) def init_layout(self, viewer_top_else_left=True): main_layout = QVBoxLayout( self) if viewer_top_else_left else QHBoxLayout(self) main_layout.addWidget(self.view, ) main_layout.addLayout(self.labels_layout) # main_layout.setContentsMargins(0, 0, 0, 0) self.setLayout(main_layout) """ If you want to start view frome some point at some level, specify <level> and <level_rect> params. level_rect : rect in dimensions of slide at level=level. If None - fits the whole size of slide """ def load(self, slide_view_params: SlideViewParams, preffered_rects_count=2000, zoom_step=1.15): self.zoom_step = zoom_step self.slide_view_params = slide_view_params self.slide_helper = SlideHelper(slide_view_params.slide_path) self.slide_graphics = SlideGraphicsGroup(slide_view_params, preffered_rects_count) self.scene.clear() self.scene.addItem(self.slide_graphics) if self.slide_view_params.level == -1 or self.slide_view_params.level is None: self.slide_view_params.level = self.slide_helper.get_max_level() self.slide_graphics.update_visible_level(self.slide_view_params.level) self.scene.setSceneRect( self.slide_helper.get_rect_for_level(self.slide_view_params.level)) def scale_initializer_deffered_function(): self.view.resetTransform() # print("size when loading: ", self.view.viewport().size()) if self.slide_view_params.level_rect: # self.view.fitInView(QRectF(*self.slide_view_params.level_rect), Qt.KeepAspectRatioByExpanding) self.view.fitInView(QRectF(*self.slide_view_params.level_rect), Qt.KeepAspectRatio) # print("after fit: ", self.get_current_view_scene_rect()) else: start_margins = QMarginsF(200, 200, 200, 200) start_image_rect_ = self.slide_helper.get_rect_for_level( self.slide_view_params.level) self.view.fitInView(start_image_rect_ + start_margins, Qt.KeepAspectRatio) self.scale_initializer_deffered_function = scale_initializer_deffered_function def eventFilter(self, qobj: 'QObject', event: QEvent): self.eventSignal.emit(event) event_processed = False # print("size when event: ", event, event.type(), self.view.viewport().size()) if isinstance(event, QShowEvent): """ we need it deffered because fitInView logic depends on current viewport size. Expecting at this point widget is finally resized before being shown at first """ if self.scale_initializer_deffered_function: # TODO labels start to occupy some space after view was already fitted, and labels will reduce size of viewport # self.update_labels() self.scale_initializer_deffered_function() self.on_view_changed() self.scale_initializer_deffered_function = None elif isinstance(event, QWheelEvent): event_processed = self.process_viewport_wheel_event(event) # we handle wheel event to prevent GraphicsView interpret it as scrolling elif isinstance(event, QMouseEvent): event_processed = self.process_mouse_event(event) return event_processed def process_viewport_wheel_event(self, event: QWheelEvent): # print("size when wheeling: ", self.view.viewport().size()) zoom_in = self.zoom_step zoom_out = 1 / zoom_in zoom_ = zoom_in if event.angleDelta().y() > 0 else zoom_out self.update_scale(event.pos(), zoom_) event.accept() self.on_view_changed() return True def process_mouse_event(self, event: QMouseEvent): if self.slide_helper is None: return False if event.button() == Qt.MiddleButton: if event.type() == QEvent.MouseButtonPress: self.slide_graphics.update_grid_visibility( not self.slide_graphics.slide_view_params.grid_visible) # items=self.scene.items() # QMessageBox.information(None, "Items", str(items)) return True # self.update_scale(QPoint(), 1.15) elif event.button() == Qt.LeftButton: if event.type() == QEvent.MouseButtonPress: self.mouse_press_view = QPoint(event.pos()) self.rubber_band.setGeometry( QRect(self.mouse_press_view, QSize())) self.rubber_band.show() return True elif event.type() == QEvent.MouseButtonRelease: self.rubber_band.hide() self.remember_selected_rect_params() self.slide_graphics.update_selected_rect_0_level( self.slide_view_params.selected_rect_0_level) self.update_labels() self.scene.invalidate() return True elif event.type() == QEvent.MouseMove: self.mouse_pos_scene_label.setText( "mouse_scene: " + point_to_str(self.view.mapToScene(event.pos()))) if not self.mouse_press_view.isNull(): self.rubber_band.setGeometry( QRect(self.mouse_press_view, event.pos()).normalized()) return True return False def remember_selected_rect_params(self): pos_scene = self.view.mapToScene(self.rubber_band.pos()) rect_scene = self.view.mapToScene( self.rubber_band.rect()).boundingRect() downsample = self.slide_helper.get_downsample_for_level( self.slide_view_params.level) selected_qrectf_0_level = QRectF(pos_scene * downsample, rect_scene.size() * downsample) self.slide_view_params.selected_rect_0_level = selected_qrectf_0_level.getRect( ) def update_scale(self, mouse_pos: QPoint, zoom): old_mouse_pos_scene = self.view.mapToScene(mouse_pos) old_view_scene_rect = self.view.mapToScene( self.view.viewport().rect()).boundingRect() old_level = self.get_best_level_for_scale( self.get_current_view_scale()) old_level_downsample = self.slide_helper.get_downsample_for_level( old_level) new_level = self.get_best_level_for_scale( self.get_current_view_scale() * zoom) new_level_downsample = self.slide_helper.get_downsample_for_level( new_level) level_scale_delta = 1 / (new_level_downsample / old_level_downsample) r = old_view_scene_rect.topLeft() m = old_mouse_pos_scene new_view_scene_rect_top_left = (m - (m - r) / zoom) * level_scale_delta new_view_scene_rect = QRectF( new_view_scene_rect_top_left, old_view_scene_rect.size() * level_scale_delta / zoom) new_scale = self.get_current_view_scale( ) * zoom * new_level_downsample / old_level_downsample transform = QTransform().scale(new_scale, new_scale).translate( -new_view_scene_rect.x(), -new_view_scene_rect.y()) new_rect = self.slide_helper.get_rect_for_level(new_level) self.scene.setSceneRect(new_rect) self.slide_view_params.level = new_level self.reset_view_transform() self.view.setTransform(transform, False) self.slide_graphics.update_visible_level(new_level) self.update_labels() def get_best_level_for_scale(self, scale): scene_width = self.scene.sceneRect().size().width() candidates = [0] for level in self.slide_helper.get_levels(): w, h = self.slide_helper.get_level_size(level) if scene_width * scale <= w: candidates.append(level) best_level = max(candidates) return best_level def update_labels(self): level_downsample = self.slide_helper.get_downsample_for_level( self.slide_view_params.level) level_size = self.slide_helper.get_level_size( self.slide_view_params.level) self.level_downsample_label.setText( "level, downsample: {}, {:.0f}".format( self.slide_view_params.level, level_downsample)) self.level_size_label.setText( "level_size: ({}, {})".format(*level_size)) self.view_rect_scene_label.setText( "view_scene: ({:.0f},{:.0f},{:.0f},{:.0f})".format( *self.get_current_view_scene_rect().getRect())) if self.slide_view_params.selected_rect_0_level: self.selected_rect_label.setText( "selected rect (0-level): ({:.0f},{:.0f},{:.0f},{:.0f})". format(*self.slide_view_params.selected_rect_0_level)) def on_view_changed(self): if self.scale_initializer_deffered_function is None and self.slide_view_params: self.slide_view_params.level_rect = self.get_current_view_scene_rect( ).getRect() self.update_labels() def reset_view_transform(self): self.view.resetTransform() self.view.horizontalScrollBar().setValue(0) self.view.verticalScrollBar().setValue(0) def get_current_view_scene_rect(self): return self.view.mapToScene(self.view.viewport().rect()).boundingRect() def get_current_view_scale(self): scale = self.view.transform().m11() return scale
class CarlaPatchbayW(QFrame, PluginEditParentMeta, HostWidgetMeta): #class CarlaPatchbayW(QFrame, PluginEditParentMeta, HostWidgetMeta, metaclass=PyQtMetaClass): def __init__(self, parent, host, doSetup = True, onlyPatchbay = True, is3D = False): QFrame.__init__(self, parent) self.host = host if False: # kdevelop likes this :) host = CarlaHostMeta() self.host = host # ------------------------------------------------------------- self.fLayout = QGridLayout(self) self.fLayout.setContentsMargins(0, 0, 0, 0) self.fLayout.setSpacing(1) self.setLayout(self.fLayout) self.fView = QGraphicsView(self) self.fKeys = PixmapKeyboardHArea(self) self.fPeaksIn = DigitalPeakMeter(self) self.fPeaksOut = DigitalPeakMeter(self) self.fPeaksCleared = True self.fPeaksIn.setColor(DigitalPeakMeter.BLUE) self.fPeaksIn.setChannels(2) self.fPeaksIn.setOrientation(DigitalPeakMeter.VERTICAL) self.fPeaksIn.setFixedWidth(25) self.fPeaksOut.setColor(DigitalPeakMeter.GREEN) self.fPeaksOut.setChannels(2) self.fPeaksOut.setOrientation(DigitalPeakMeter.VERTICAL) self.fPeaksOut.setFixedWidth(25) self.fLayout.addWidget(self.fPeaksIn, 0, 0) self.fLayout.addWidget(self.fView, 0, 1) # self.fViewWidget if is3D else self.fLayout.addWidget(self.fPeaksOut, 0, 2) self.fLayout.addWidget(self.fKeys, 1, 0, 1, 0) # ------------------------------------------------------------- # Internal stuff self.fParent = parent self.fPluginCount = 0 self.fPluginList = [] self.fExternalPatchbay = False self.fIsOnlyPatchbay = onlyPatchbay self.fSelectedPlugins = [] self.fCanvasWidth = 0 self.fCanvasHeight = 0 # ------------------------------------------------------------- # Set-up Canvas Preview self.fMiniCanvasPreview = self.fParent.ui.miniCanvasPreview self.fMiniCanvasPreview.setRealParent(self) self.fMovingViaMiniCanvas = False # ------------------------------------------------------------- # Set-up Canvas self.scene = patchcanvas.PatchScene(self, self.fView) self.fView.setScene(self.scene) self.fView.setRenderHint(QPainter.Antialiasing, bool(parent.fSavedSettings[CARLA_KEY_CANVAS_ANTIALIASING] == patchcanvas.ANTIALIASING_FULL)) if parent.fSavedSettings[CARLA_KEY_CANVAS_USE_OPENGL] and hasGL: # and not is3D: self.fViewWidget = QGLWidget(self) self.fView.setViewport(self.fViewWidget) self.fView.setRenderHint(QPainter.HighQualityAntialiasing, parent.fSavedSettings[CARLA_KEY_CANVAS_HQ_ANTIALIASING]) self.setupCanvas() QTimer.singleShot(100, self.slot_restoreScrollbarValues) # ------------------------------------------------------------- # Connect actions to functions parent.ui.act_settings_show_meters.toggled.connect(self.slot_showCanvasMeters) parent.ui.act_settings_show_keyboard.toggled.connect(self.slot_showCanvasKeyboard) self.fView.horizontalScrollBar().valueChanged.connect(self.slot_horizontalScrollBarChanged) self.fView.verticalScrollBar().valueChanged.connect(self.slot_verticalScrollBarChanged) self.scene.scaleChanged.connect(self.slot_canvasScaleChanged) self.scene.sceneGroupMoved.connect(self.slot_canvasItemMoved) self.scene.pluginSelected.connect(self.slot_canvasPluginSelected) self.fMiniCanvasPreview.miniCanvasMoved.connect(self.slot_miniCanvasMoved) self.fKeys.keyboard.noteOn.connect(self.slot_noteOn) self.fKeys.keyboard.noteOff.connect(self.slot_noteOff) # ------------------------------------------------------------- # Load Settings settings = QSettings() showMeters = settings.value("ShowMeters", False, type=bool) self.fParent.ui.act_settings_show_meters.setChecked(showMeters) self.fPeaksIn.setVisible(showMeters) self.fPeaksOut.setVisible(showMeters) showKeyboard = settings.value("ShowKeyboard", not(MACOS or WINDOWS), type=bool) self.fParent.ui.act_settings_show_keyboard.setChecked(showKeyboard) self.fKeys.setVisible(showKeyboard) # ------------------------------------------------------------- # Connect actions to functions (part 2) host.PluginAddedCallback.connect(self.slot_handlePluginAddedCallback) host.PluginRemovedCallback.connect(self.slot_handlePluginRemovedCallback) host.NoteOnCallback.connect(self.slot_handleNoteOnCallback) host.NoteOffCallback.connect(self.slot_handleNoteOffCallback) host.PatchbayClientAddedCallback.connect(self.slot_handlePatchbayClientAddedCallback) host.PatchbayClientRemovedCallback.connect(self.slot_handlePatchbayClientRemovedCallback) host.PatchbayClientRenamedCallback.connect(self.slot_handlePatchbayClientRenamedCallback) host.PatchbayClientDataChangedCallback.connect(self.slot_handlePatchbayClientDataChangedCallback) host.PatchbayPortAddedCallback.connect(self.slot_handlePatchbayPortAddedCallback) host.PatchbayPortRemovedCallback.connect(self.slot_handlePatchbayPortRemovedCallback) host.PatchbayPortRenamedCallback.connect(self.slot_handlePatchbayPortRenamedCallback) host.PatchbayConnectionAddedCallback.connect(self.slot_handlePatchbayConnectionAddedCallback) host.PatchbayConnectionRemovedCallback.connect(self.slot_handlePatchbayConnectionRemovedCallback) if not doSetup: return parent.ui.act_plugins_enable.triggered.connect(self.slot_pluginsEnable) parent.ui.act_plugins_disable.triggered.connect(self.slot_pluginsDisable) parent.ui.act_plugins_volume100.triggered.connect(self.slot_pluginsVolume100) parent.ui.act_plugins_mute.triggered.connect(self.slot_pluginsMute) parent.ui.act_plugins_wet100.triggered.connect(self.slot_pluginsWet100) parent.ui.act_plugins_bypass.triggered.connect(self.slot_pluginsBypass) parent.ui.act_plugins_center.triggered.connect(self.slot_pluginsCenter) parent.ui.act_plugins_panic.triggered.connect(self.slot_pluginsDisable) parent.ui.act_canvas_show_internal.triggered.connect(self.slot_canvasShowInternal) parent.ui.act_canvas_show_external.triggered.connect(self.slot_canvasShowExternal) parent.ui.act_canvas_arrange.setEnabled(False) # TODO, later parent.ui.act_canvas_arrange.triggered.connect(self.slot_canvasArrange) parent.ui.act_canvas_refresh.triggered.connect(self.slot_canvasRefresh) parent.ui.act_canvas_zoom_fit.triggered.connect(self.slot_canvasZoomFit) parent.ui.act_canvas_zoom_in.triggered.connect(self.slot_canvasZoomIn) parent.ui.act_canvas_zoom_out.triggered.connect(self.slot_canvasZoomOut) parent.ui.act_canvas_zoom_100.triggered.connect(self.slot_canvasZoomReset) parent.ui.act_canvas_print.triggered.connect(self.slot_canvasPrint) parent.ui.act_canvas_save_image.triggered.connect(self.slot_canvasSaveImage) parent.ui.act_settings_configure.triggered.connect(self.slot_configureCarla) # ----------------------------------------------------------------- def getPluginEditDialog(self, pluginId): if pluginId >= self.fPluginCount: return None pedit = self.fPluginList[pluginId] if pedit is None: return None if False: pedit = DummyPluginEdit(self, 0) return pedit # ----------------------------------------------------------------- @pyqtSlot(int, str) def slot_handlePluginAddedCallback(self, pluginId, pluginName): if self.fIsOnlyPatchbay: pedit = PluginEdit(self, self.host, pluginId) else: pedit = DummyPluginEdit(self, pluginId) self.fPluginList.append(pedit) self.fPluginCount += 1 if self.fIsOnlyPatchbay and not self.fParent.isProjectLoading(): self.host.set_active(pluginId, True) @pyqtSlot(int) def slot_handlePluginRemovedCallback(self, pluginId): patchcanvas.handlePluginRemoved(pluginId) if pluginId in self.fSelectedPlugins: self.clearSideStuff() pedit = self.getPluginEditDialog(pluginId) self.fPluginCount -= 1 self.fPluginList.pop(pluginId) if pedit is not None: pedit.close() del pedit # push all plugins 1 slot back for i in range(pluginId, self.fPluginCount): pedit = self.fPluginList[i] pedit.setPluginId(i) @pyqtSlot(int, int, int, int) def slot_handleNoteOnCallback(self, pluginId, channel, note, velocity): if pluginId in self.fSelectedPlugins: self.fKeys.keyboard.sendNoteOn(note, False) @pyqtSlot(int, int, int) def slot_handleNoteOffCallback(self, pluginId, channel, note): if pluginId in self.fSelectedPlugins: self.fKeys.keyboard.sendNoteOff(note, False) # ----------------------------------------------------------------- # HostWidgetMeta methods def removeAllPlugins(self): patchcanvas.handleAllPluginsRemoved() for pedit in self.fPluginList: if pedit is None: break pedit.close() del pedit self.fPluginCount = 0 self.fPluginList = [] self.clearSideStuff() def engineStarted(self): pass def engineStopped(self): patchcanvas.clear() def idleFast(self): if self.fPluginCount == 0: return for pluginId in self.fSelectedPlugins: self.fPeaksCleared = False if self.fPeaksIn.isVisible(): self.fPeaksIn.displayMeter(1, self.host.get_input_peak_value(pluginId, True)) self.fPeaksIn.displayMeter(2, self.host.get_input_peak_value(pluginId, False)) if self.fPeaksOut.isVisible(): self.fPeaksOut.displayMeter(1, self.host.get_output_peak_value(pluginId, True)) self.fPeaksOut.displayMeter(2, self.host.get_output_peak_value(pluginId, False)) return if self.fPeaksCleared: return self.fPeaksCleared = True self.fPeaksIn.displayMeter(1, 0.0, True) self.fPeaksIn.displayMeter(2, 0.0, True) self.fPeaksOut.displayMeter(1, 0.0, True) self.fPeaksOut.displayMeter(2, 0.0, True) def idleSlow(self): for pedit in self.fPluginList: if pedit is None: break pedit.idleSlow() def projectLoadingStarted(self): self.fView.setEnabled(False) def projectLoadingFinished(self): self.fView.setEnabled(True) QTimer.singleShot(1000, self.slot_canvasRefresh) def saveSettings(self, settings): settings.setValue("ShowMeters", self.fParent.ui.act_settings_show_meters.isChecked()) settings.setValue("ShowKeyboard", self.fParent.ui.act_settings_show_keyboard.isChecked()) settings.setValue("HorizontalScrollBarValue", self.fView.horizontalScrollBar().value()) settings.setValue("VerticalScrollBarValue", self.fView.verticalScrollBar().value()) def showEditDialog(self, pluginId): pedit = self.getPluginEditDialog(pluginId) if pedit: pedit.show() # ----------------------------------------------------------------- # PluginEdit callbacks def editDialogVisibilityChanged(self, pluginId, visible): pass def editDialogPluginHintsChanged(self, pluginId, hints): pass def editDialogParameterValueChanged(self, pluginId, parameterId, value): pass def editDialogProgramChanged(self, pluginId, index): pass def editDialogMidiProgramChanged(self, pluginId, index): pass def editDialogNotePressed(self, pluginId, note): if pluginId in self.fSelectedPlugins: self.fKeys.keyboard.sendNoteOn(note, False) def editDialogNoteReleased(self, pluginId, note): if pluginId in self.fSelectedPlugins: self.fKeys.keyboard.sendNoteOff(note, False) def editDialogMidiActivityChanged(self, pluginId, onOff): pass # ----------------------------------------------------------------- def clearSideStuff(self): self.scene.clearSelection() self.fSelectedPlugins = [] self.fKeys.keyboard.allNotesOff(False) self.fKeys.setEnabled(False) self.fPeaksCleared = True self.fPeaksIn.displayMeter(1, 0.0, True) self.fPeaksIn.displayMeter(2, 0.0, True) self.fPeaksOut.displayMeter(1, 0.0, True) self.fPeaksOut.displayMeter(2, 0.0, True) def setupCanvas(self): pOptions = patchcanvas.options_t() pOptions.theme_name = self.fParent.fSavedSettings[CARLA_KEY_CANVAS_THEME] pOptions.auto_hide_groups = self.fParent.fSavedSettings[CARLA_KEY_CANVAS_AUTO_HIDE_GROUPS] pOptions.use_bezier_lines = self.fParent.fSavedSettings[CARLA_KEY_CANVAS_USE_BEZIER_LINES] pOptions.antialiasing = self.fParent.fSavedSettings[CARLA_KEY_CANVAS_ANTIALIASING] pOptions.eyecandy = self.fParent.fSavedSettings[CARLA_KEY_CANVAS_EYE_CANDY] pFeatures = patchcanvas.features_t() pFeatures.group_info = False pFeatures.group_rename = False pFeatures.port_info = False pFeatures.port_rename = False pFeatures.handle_group_pos = True patchcanvas.setOptions(pOptions) patchcanvas.setFeatures(pFeatures) patchcanvas.init("Carla2", self.scene, canvasCallback, False) tryCanvasSize = self.fParent.fSavedSettings[CARLA_KEY_CANVAS_SIZE].split("x") if len(tryCanvasSize) == 2 and tryCanvasSize[0].isdigit() and tryCanvasSize[1].isdigit(): self.fCanvasWidth = int(tryCanvasSize[0]) self.fCanvasHeight = int(tryCanvasSize[1]) else: self.fCanvasWidth = CARLA_DEFAULT_CANVAS_SIZE_WIDTH self.fCanvasHeight = CARLA_DEFAULT_CANVAS_SIZE_HEIGHT patchcanvas.setCanvasSize(0, 0, self.fCanvasWidth, self.fCanvasHeight) patchcanvas.setInitialPos(self.fCanvasWidth / 2, self.fCanvasHeight / 2) self.fView.setSceneRect(0, 0, self.fCanvasWidth, self.fCanvasHeight) self.themeData = [self.fCanvasWidth, self.fCanvasHeight, patchcanvas.canvas.theme.canvas_bg, patchcanvas.canvas.theme.rubberband_brush, patchcanvas.canvas.theme.rubberband_pen.color()] def updateCanvasInitialPos(self): x = self.fView.horizontalScrollBar().value() + self.width()/4 y = self.fView.verticalScrollBar().value() + self.height()/4 patchcanvas.setInitialPos(x, y) # ----------------------------------------------------------------- @pyqtSlot(bool) def slot_showCanvasMeters(self, yesNo): self.fPeaksIn.setVisible(yesNo) self.fPeaksOut.setVisible(yesNo) @pyqtSlot(bool) def slot_showCanvasKeyboard(self, yesNo): self.fKeys.setVisible(yesNo) # ----------------------------------------------------------------- @pyqtSlot() def slot_miniCanvasCheckAll(self): self.slot_miniCanvasCheckSize() self.slot_horizontalScrollBarChanged(self.fView.horizontalScrollBar().value()) self.slot_verticalScrollBarChanged(self.fView.verticalScrollBar().value()) @pyqtSlot() def slot_miniCanvasCheckSize(self): self.fMiniCanvasPreview.setViewSize(float(self.width()) / self.fCanvasWidth, float(self.height()) / self.fCanvasHeight) @pyqtSlot(int) def slot_horizontalScrollBarChanged(self, value): if self.fMovingViaMiniCanvas: return maximum = self.fView.horizontalScrollBar().maximum() if maximum == 0: xp = 0 else: xp = float(value) / maximum self.fMiniCanvasPreview.setViewPosX(xp) self.updateCanvasInitialPos() @pyqtSlot(int) def slot_verticalScrollBarChanged(self, value): if self.fMovingViaMiniCanvas: return maximum = self.fView.verticalScrollBar().maximum() if maximum == 0: yp = 0 else: yp = float(value) / maximum self.fMiniCanvasPreview.setViewPosY(yp) self.updateCanvasInitialPos() @pyqtSlot() def slot_restoreScrollbarValues(self): settings = QSettings() self.fView.horizontalScrollBar().setValue(settings.value("HorizontalScrollBarValue", self.fView.horizontalScrollBar().maximum()/2, type=int)) self.fView.verticalScrollBar().setValue(settings.value("VerticalScrollBarValue", self.fView.verticalScrollBar().maximum()/2, type=int)) # ----------------------------------------------------------------- @pyqtSlot(float) def slot_canvasScaleChanged(self, scale): self.fMiniCanvasPreview.setViewScale(scale) @pyqtSlot(int, int, QPointF) def slot_canvasItemMoved(self, group_id, split_mode, pos): self.fMiniCanvasPreview.update() @pyqtSlot(list) def slot_canvasPluginSelected(self, pluginList): self.fKeys.keyboard.allNotesOff(False) self.fKeys.setEnabled(len(pluginList) != 0) # and self.fPluginCount > 0 self.fSelectedPlugins = pluginList @pyqtSlot(float, float) def slot_miniCanvasMoved(self, xp, yp): self.fMovingViaMiniCanvas = True self.fView.horizontalScrollBar().setValue(xp * self.fView.horizontalScrollBar().maximum()) self.fView.verticalScrollBar().setValue(yp * self.fView.verticalScrollBar().maximum()) self.fMovingViaMiniCanvas = False self.updateCanvasInitialPos() # ----------------------------------------------------------------- @pyqtSlot(int) def slot_noteOn(self, note): for pluginId in self.fSelectedPlugins: self.host.send_midi_note(pluginId, 0, note, 100) pedit = self.getPluginEditDialog(pluginId) pedit.noteOn(0, note, 100) @pyqtSlot(int) def slot_noteOff(self, note): for pluginId in self.fSelectedPlugins: self.host.send_midi_note(pluginId, 0, note, 0) pedit = self.getPluginEditDialog(pluginId) pedit.noteOff(0, note) # ----------------------------------------------------------------- @pyqtSlot() def slot_pluginsEnable(self): if not self.host.is_engine_running(): return for i in range(self.fPluginCount): self.host.set_active(i, True) @pyqtSlot() def slot_pluginsDisable(self): if not self.host.is_engine_running(): return for i in range(self.fPluginCount): self.host.set_active(i, False) @pyqtSlot() def slot_pluginsVolume100(self): if not self.host.is_engine_running(): return for i in range(self.fPluginCount): pedit = self.fPluginList[i] if pedit is None: break if pedit.getHints() & PLUGIN_CAN_VOLUME: pedit.setParameterValue(PARAMETER_VOLUME, 1.0) self.host.set_volume(i, 1.0) @pyqtSlot() def slot_pluginsMute(self): if not self.host.is_engine_running(): return for i in range(self.fPluginCount): pedit = self.fPluginList[i] if pedit is None: break if pedit.getHints() & PLUGIN_CAN_VOLUME: pedit.setParameterValue(PARAMETER_VOLUME, 0.0) self.host.set_volume(i, 0.0) @pyqtSlot() def slot_pluginsWet100(self): if not self.host.is_engine_running(): return for i in range(self.fPluginCount): pedit = self.fPluginList[i] if pedit is None: break if pedit.getHints() & PLUGIN_CAN_DRYWET: pedit.setParameterValue(PARAMETER_DRYWET, 1.0) self.host.set_drywet(i, 1.0) @pyqtSlot() def slot_pluginsBypass(self): if not self.host.is_engine_running(): return for i in range(self.fPluginCount): pedit = self.fPluginList[i] if pedit is None: break if pedit.getHints() & PLUGIN_CAN_DRYWET: pedit.setParameterValue(PARAMETER_DRYWET, 0.0) self.host.set_drywet(i, 0.0) @pyqtSlot() def slot_pluginsCenter(self): if not self.host.is_engine_running(): return for i in range(self.fPluginCount): pedit = self.fPluginList[i] if pedit is None: break if pedit.getHints() & PLUGIN_CAN_BALANCE: pedit.setParameterValue(PARAMETER_BALANCE_LEFT, -1.0) pedit.setParameterValue(PARAMETER_BALANCE_RIGHT, 1.0) self.host.set_balance_left(i, -1.0) self.host.set_balance_right(i, 1.0) if pedit.getHints() & PLUGIN_CAN_PANNING: pedit.setParameterValue(PARAMETER_PANNING, 0.0) self.host.set_panning(i, 0.0) # ----------------------------------------------------------------- @pyqtSlot() def slot_configureCarla(self): dialog = CarlaSettingsW(self, self.host, True, hasGL) if not dialog.exec_(): return self.fParent.loadSettings(False) patchcanvas.clear() self.setupCanvas() self.fParent.updateContainer(self.themeData) self.slot_miniCanvasCheckAll() if self.host.is_engine_running(): self.host.patchbay_refresh(self.fExternalPatchbay) # ----------------------------------------------------------------- @pyqtSlot(int, int, int, str) def slot_handlePatchbayClientAddedCallback(self, clientId, clientIcon, pluginId, clientName): pcSplit = patchcanvas.SPLIT_UNDEF pcIcon = patchcanvas.ICON_APPLICATION if clientIcon == PATCHBAY_ICON_PLUGIN: pcIcon = patchcanvas.ICON_PLUGIN if clientIcon == PATCHBAY_ICON_HARDWARE: pcIcon = patchcanvas.ICON_HARDWARE elif clientIcon == PATCHBAY_ICON_CARLA: pass elif clientIcon == PATCHBAY_ICON_DISTRHO: pcIcon = patchcanvas.ICON_DISTRHO elif clientIcon == PATCHBAY_ICON_FILE: pcIcon = patchcanvas.ICON_FILE patchcanvas.addGroup(clientId, clientName, pcSplit, pcIcon) QTimer.singleShot(0, self.fMiniCanvasPreview.update) if pluginId < 0: return if pluginId >= self.fPluginCount: print("sorry, can't map this plugin to canvas client", pluginId, self.fPluginCount) return patchcanvas.setGroupAsPlugin(clientId, pluginId, bool(self.host.get_plugin_info(pluginId)['hints'] & PLUGIN_HAS_CUSTOM_UI)) @pyqtSlot(int) def slot_handlePatchbayClientRemovedCallback(self, clientId): #if not self.fEngineStarted: return patchcanvas.removeGroup(clientId) QTimer.singleShot(0, self.fMiniCanvasPreview.update) @pyqtSlot(int, str) def slot_handlePatchbayClientRenamedCallback(self, clientId, newClientName): patchcanvas.renameGroup(clientId, newClientName) QTimer.singleShot(0, self.fMiniCanvasPreview.update) @pyqtSlot(int, int, int) def slot_handlePatchbayClientDataChangedCallback(self, clientId, clientIcon, pluginId): pcIcon = patchcanvas.ICON_APPLICATION if clientIcon == PATCHBAY_ICON_PLUGIN: pcIcon = patchcanvas.ICON_PLUGIN if clientIcon == PATCHBAY_ICON_HARDWARE: pcIcon = patchcanvas.ICON_HARDWARE elif clientIcon == PATCHBAY_ICON_CARLA: pass elif clientIcon == PATCHBAY_ICON_DISTRHO: pcIcon = patchcanvas.ICON_DISTRHO elif clientIcon == PATCHBAY_ICON_FILE: pcIcon = patchcanvas.ICON_FILE patchcanvas.setGroupIcon(clientId, pcIcon) QTimer.singleShot(0, self.fMiniCanvasPreview.update) if pluginId < 0: return if pluginId >= self.fPluginCount: print("sorry, can't map this plugin to canvas client", pluginId, self.fPluginCount) return patchcanvas.setGroupAsPlugin(clientId, pluginId, bool(self.host.get_plugin_info(pluginId)['hints'] & PLUGIN_HAS_CUSTOM_UI)) @pyqtSlot(int, int, int, str) def slot_handlePatchbayPortAddedCallback(self, clientId, portId, portFlags, portName): isAlternate = False if (portFlags & PATCHBAY_PORT_IS_INPUT): portMode = patchcanvas.PORT_MODE_INPUT else: portMode = patchcanvas.PORT_MODE_OUTPUT if (portFlags & PATCHBAY_PORT_TYPE_AUDIO): portType = patchcanvas.PORT_TYPE_AUDIO_JACK elif (portFlags & PATCHBAY_PORT_TYPE_CV): isAlternate = True portType = patchcanvas.PORT_TYPE_AUDIO_JACK elif (portFlags & PATCHBAY_PORT_TYPE_MIDI): portType = patchcanvas.PORT_TYPE_MIDI_JACK else: portType = patchcanvas.PORT_TYPE_NULL patchcanvas.addPort(clientId, portId, portName, portMode, portType, isAlternate) QTimer.singleShot(0, self.fMiniCanvasPreview.update) @pyqtSlot(int, int) def slot_handlePatchbayPortRemovedCallback(self, groupId, portId): #if not self.fEngineStarted: return patchcanvas.removePort(groupId, portId) QTimer.singleShot(0, self.fMiniCanvasPreview.update) @pyqtSlot(int, int, str) def slot_handlePatchbayPortRenamedCallback(self, groupId, portId, newPortName): patchcanvas.renamePort(groupId, portId, newPortName) QTimer.singleShot(0, self.fMiniCanvasPreview.update) @pyqtSlot(int, int, int, int, int) def slot_handlePatchbayConnectionAddedCallback(self, connectionId, groupOutId, portOutId, groupInId, portInId): patchcanvas.connectPorts(connectionId, groupOutId, portOutId, groupInId, portInId) QTimer.singleShot(0, self.fMiniCanvasPreview.update) @pyqtSlot(int, int, int) def slot_handlePatchbayConnectionRemovedCallback(self, connectionId, portOutId, portInId): #if not self.fEngineStarted: return patchcanvas.disconnectPorts(connectionId) QTimer.singleShot(0, self.fMiniCanvasPreview.update) # ----------------------------------------------------------------- @pyqtSlot() def slot_canvasArrange(self): patchcanvas.arrange() @pyqtSlot() def slot_canvasShowInternal(self): self.fExternalPatchbay = False self.fParent.ui.act_canvas_show_internal.blockSignals(True) self.fParent.ui.act_canvas_show_external.blockSignals(True) self.fParent.ui.act_canvas_show_internal.setChecked(True) self.fParent.ui.act_canvas_show_external.setChecked(False) self.fParent.ui.act_canvas_show_internal.blockSignals(False) self.fParent.ui.act_canvas_show_external.blockSignals(False) self.slot_canvasRefresh() @pyqtSlot() def slot_canvasShowExternal(self): self.fExternalPatchbay = True self.fParent.ui.act_canvas_show_internal.blockSignals(True) self.fParent.ui.act_canvas_show_external.blockSignals(True) self.fParent.ui.act_canvas_show_internal.setChecked(False) self.fParent.ui.act_canvas_show_external.setChecked(True) self.fParent.ui.act_canvas_show_internal.blockSignals(False) self.fParent.ui.act_canvas_show_external.blockSignals(False) self.slot_canvasRefresh() @pyqtSlot() def slot_canvasRefresh(self): patchcanvas.clear() if self.host.is_engine_running(): self.host.patchbay_refresh(self.fExternalPatchbay) for pedit in self.fPluginList: if pedit is None: break pedit.reloadAll() QTimer.singleShot(1000 if self.fParent.fSavedSettings[CARLA_KEY_CANVAS_EYE_CANDY] else 0, self.fMiniCanvasPreview.update) @pyqtSlot() def slot_canvasZoomFit(self): self.scene.zoom_fit() @pyqtSlot() def slot_canvasZoomIn(self): self.scene.zoom_in() @pyqtSlot() def slot_canvasZoomOut(self): self.scene.zoom_out() @pyqtSlot() def slot_canvasZoomReset(self): self.scene.zoom_reset() @pyqtSlot() def slot_canvasPrint(self): self.scene.clearSelection() self.fExportPrinter = QPrinter() dialog = QPrintDialog(self.fExportPrinter, self) if dialog.exec_(): painter = QPainter(self.fExportPrinter) painter.save() painter.setRenderHint(QPainter.Antialiasing) painter.setRenderHint(QPainter.TextAntialiasing) self.scene.render(painter) painter.restore() @pyqtSlot() def slot_canvasSaveImage(self): newPath = QFileDialog.getSaveFileName(self, self.tr("Save Image"), filter=self.tr("PNG Image (*.png);;JPEG Image (*.jpg)")) if config_UseQt5: newPath = newPath[0] if not newPath: return self.scene.clearSelection() if newPath.lower().endswith((".jpg",)): imgFormat = "JPG" elif newPath.lower().endswith((".png",)): imgFormat = "PNG" else: # File-dialog may not auto-add the extension imgFormat = "PNG" newPath += ".png" self.fExportImage = QImage(self.scene.sceneRect().width(), self.scene.sceneRect().height(), QImage.Format_RGB32) painter = QPainter(self.fExportImage) painter.save() painter.setRenderHint(QPainter.Antialiasing) # TODO - set true, cleanup this painter.setRenderHint(QPainter.TextAntialiasing) self.scene.render(painter) self.fExportImage.save(newPath, imgFormat, 100) painter.restore() # ----------------------------------------------------------------- def resizeEvent(self, event): QFrame.resizeEvent(self, event) self.slot_miniCanvasCheckSize()
class View(QWidget): trigger_focus_event = QtCore.pyqtSignal(str) def __init__(self, emacs_xid, buffer, view_info): super(View, self).__init__() self.buffer = buffer self.emacs_xid = emacs_xid # Init widget attributes. self.setWindowFlags(Qt.FramelessWindowHint) self.setAttribute(Qt.WA_X11DoNotAcceptFocus, True) self.setContentsMargins(0, 0, 0, 0) self.installEventFilter(self) # Init attributes. self.view_info = view_info (self.buffer_id, self.x, self.y, self.width, self.height) = view_info.split(":") self.x = int(self.x) self.y = int(self.y) self.width = int(self.width) self.height = int(self.height) # Build QGraphicsView. self.layout = QVBoxLayout(self) self.layout.setContentsMargins(0, 0, 0, 0) self.graphics_view = QGraphicsView(buffer, self) self.graphics_view.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.graphics_view.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.graphics_view.setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform | QPainter.TextAntialiasing) # Remove damn border from QGraphicsView. self.graphics_view.setFrameStyle(0) self.graphics_view.setStyleSheet( "QGraphicsView {background: transparent; border: 3px; outline: none;}" ) self.layout.addWidget(self.graphics_view) # NOTE: show function must start before resize to trigger *first* resizeEvent after show. self.show() # Resize after show to trigger fit view operation. self.resize(self.width, self.height) def resizeEvent(self, event): # Fit content to view rect just when buffer fit_to_view option is enable. if self.buffer.fit_to_view: if event.oldSize().isValid(): self.graphics_view.fitInView( self.graphics_view.scene().sceneRect(), Qt.KeepAspectRatio) QWidget.resizeEvent(self, event) def eventFilter(self, obj, event): # Focus emacs buffer when user click view. if event.type() in [ QEvent.MouseButtonPress, QEvent.MouseButtonRelease, QEvent.MouseMove, QEvent.MouseButtonDblClick, QEvent.Wheel ]: self.trigger_focus_event.emit("{0},{1}".format( event.globalX(), event.globalY())) return False def showEvent(self, event): # NOTE: we must reparent after widget show, otherwise reparent operation maybe failed. self.reparent() # Make graphics view at left-top corner after show. self.graphics_view.verticalScrollBar().setValue(0) self.graphics_view.horizontalScrollBar().setValue(0) def reparent(self): xlib_display = get_xlib_display() view_xid = self.winId().__int__() view_xwindow = xlib_display.create_resource_object("window", view_xid) emacs_xwindow = xlib_display.create_resource_object( "window", self.emacs_xid) view_xwindow.reparent(emacs_xwindow, self.x, self.y) xlib_display.sync() def handle_destroy(self): self.destroy()
class View(QWidget): trigger_focus_event = QtCore.pyqtSignal(str) def __init__(self, buffer, view_info): super(View, self).__init__() self.buffer = buffer # Init widget attributes. self.setWindowFlags(Qt.FramelessWindowHint) self.setAttribute(Qt.WA_X11DoNotAcceptFocus, True) self.setContentsMargins(0, 0, 0, 0) self.installEventFilter(self) # Init attributes. self.view_info = view_info (self.buffer_id, self.emacs_xid, self.x, self.y, self.width, self.height) = view_info.split(":") self.emacs_xid = int(self.emacs_xid) self.x = int(self.x) self.y = int(self.y) self.width = int(self.width) self.height = int(self.height) # Build QGraphicsView. self.layout = QVBoxLayout(self) self.layout.setSpacing(0) self.layout.setContentsMargins(0, 0, 0, 0) self.graphics_view = QGraphicsView(buffer, self) # Set background color. # When fit_to_view is True, QGraphicsView will fill color around app view. # We fill color with buffer's attribute "background_color". if hasattr(self.buffer, "background_color") and self.buffer.background_color: self.graphics_view.setBackgroundBrush( QBrush(self.buffer.background_color)) # Remove border from QGraphicsView. self.graphics_view.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.graphics_view.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.graphics_view.setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform | QPainter.TextAntialiasing) self.graphics_view.setFrameStyle(QFrame.NoFrame) # Add graphics view. self.layout.addWidget(self.graphics_view) # NOTE: show function must start before resize to trigger *first* resizeEvent after show. self.show() # Resize after show to trigger fit view operation. self.resize(self.width, self.height) self.buffer.aspect_ratio_change.connect(self.adjust_aspect_ratio) def resizeEvent(self, event): # Fit content to view rect just when buffer fit_to_view option is enable. if self.buffer.fit_to_view: if event.oldSize().isValid(): self.graphics_view.fitInView( self.graphics_view.scene().sceneRect(), Qt.KeepAspectRatio) QWidget.resizeEvent(self, event) def adjust_aspect_ratio(self): widget_width = self.width widget_height = self.height if self.buffer.aspect_ratio == 0: self.buffer.buffer_widget.resize(self.width, self.height) self.layout.setContentsMargins(0, 0, 0, 0) else: view_height = widget_height * ( 1 - 2 * self.buffer.vertical_padding_ratio) view_width = view_height * self.buffer.aspect_ratio horizontal_padding = (widget_width - view_width) / 2 vertical_padding = self.buffer.vertical_padding_ratio * widget_height self.buffer.buffer_widget.resize(view_width, view_height) self.layout.setContentsMargins(horizontal_padding, vertical_padding, horizontal_padding, vertical_padding) def eventFilter(self, obj, event): # When we press Alt + Tab in operating system. # Emacs window cannot get the focus normally if mouse in EAF buffer area. # # So we use wmctrl activate on Emacs window after Alt + Tab operation. # # NOTE: turn off this behavior under i3 window manager. if event.type() in [QEvent.ShortcutOverride]: import os if os.environ.get("DESKTOP_SESSION") != "i3": if not activate_emacs_window(): self.buffer.message_to_emacs.emit( "You need install tool 'wmctrl' to activate Emacs window, make Emacs input correctly after Alt + Tab operation." ) # Focus emacs buffer when user click view. if event.type() in [ QEvent.MouseButtonPress, QEvent.MouseButtonRelease, QEvent.MouseButtonDblClick, QEvent.Wheel ]: self.trigger_focus_event.emit(self.buffer_id) # Stop mouse event. return True return False def showEvent(self, event): # NOTE: we must reparent after widget show, otherwise reparent operation maybe failed. self.reparent() # Make graphics view at left-top corner after show. self.graphics_view.verticalScrollBar().setValue(0) self.graphics_view.horizontalScrollBar().setValue(0) def reparent(self): qwindow = self.windowHandle() qwindow.setParent(QWindow.fromWinId(self.emacs_xid)) qwindow.setPosition(QPoint(self.x, self.y)) def destroy_view(self): self.destroy()
class View(QWidget): trigger_focus_event = QtCore.pyqtSignal(str) def __init__(self, emacs_xid, buffer, view_info): super(View, self).__init__() self.buffer = buffer self.emacs_xid = emacs_xid # Init widget attributes. self.setWindowFlags(Qt.FramelessWindowHint) self.setAttribute(Qt.WA_X11DoNotAcceptFocus, True) self.setContentsMargins(0, 0, 0, 0) self.installEventFilter(self) # Init attributes. self.view_info = view_info (self.buffer_id, self.x, self.y, self.width, self.height) = view_info.split(":") self.x = int(self.x) self.y = int(self.y) self.width = int(self.width) self.height = int(self.height) # Build QGraphicsView. self.layout = QVBoxLayout(self) self.layout.setContentsMargins(0, 0, 0, 0) self.graphics_view = QGraphicsView(buffer, self) self.graphics_view.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.graphics_view.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.graphics_view.setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform | QPainter.TextAntialiasing) # Remove damn border from QGraphicsView. self.graphics_view.setFrameStyle(0) self.graphics_view.setStyleSheet("QGraphicsView {background: transparent; border: 3px; outline: none;}") self.layout.addWidget(self.graphics_view) # NOTE: show function must start before resize to trigger *first* resizeEvent after show. self.show() # Resize after show to trigger fit view operation. self.resize(self.width, self.height) def resizeEvent(self, event): # Fit content to view rect just when buffer fit_to_view option is enable. if self.buffer.fit_to_view: if event.oldSize().isValid(): self.graphics_view.fitInView(self.graphics_view.scene().sceneRect(), Qt.KeepAspectRatio) QWidget.resizeEvent(self, event) def eventFilter(self, obj, event): # Focus emacs buffer when user click view. if event.type() in [QEvent.MouseButtonPress, QEvent.MouseButtonRelease, QEvent.MouseMove, QEvent.MouseButtonDblClick, QEvent.Wheel]: # Send mouse event to applicatin view. self.trigger_focus_event.emit("{0},{1}".format(event.globalX(), event.globalY())) # Stop mouse event. return True return False def showEvent(self, event): # NOTE: we must reparent after widget show, otherwise reparent operation maybe failed. self.reparent() # Make graphics view at left-top corner after show. self.graphics_view.verticalScrollBar().setValue(0) self.graphics_view.horizontalScrollBar().setValue(0) def reparent(self): xlib_display = get_xlib_display() view_xid = self.winId().__int__() view_xwindow = xlib_display.create_resource_object("window", view_xid) emacs_xwindow = xlib_display.create_resource_object("window", self.emacs_xid) view_xwindow.reparent(emacs_xwindow, self.x, self.y) xlib_display.sync() def handle_destroy(self): self.destroy()
class View(QWidget): trigger_focus_event = QtCore.pyqtSignal(str) def __init__(self, buffer, view_info): super(View, self).__init__() self.buffer = buffer # Init widget attributes. self.setWindowFlags(Qt.FramelessWindowHint) self.setAttribute(Qt.WA_X11DoNotAcceptFocus, True) self.setContentsMargins(0, 0, 0, 0) self.installEventFilter(self) # Init attributes. self.view_info = view_info (self.buffer_id, self.emacs_xid, self.x, self.y, self.width, self.height) = view_info.split(":") self.emacs_xid = int(self.emacs_xid) self.x = int(self.x) self.y = int(self.y) self.width = int(self.width) self.height = int(self.height) # Build QGraphicsView. self.layout = QVBoxLayout(self) self.layout.setSpacing(0) self.layout.setContentsMargins(0, 0, 0, 0) self.graphics_view = QGraphicsView(buffer, self) self.graphics_view.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.graphics_view.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.graphics_view.setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform | QPainter.TextAntialiasing) # Remove damn border from QGraphicsView. self.graphics_view.setFrameStyle(QFrame.NoFrame) self.layout.addWidget(self.graphics_view) # NOTE: show function must start before resize to trigger *first* resizeEvent after show. self.show() # Resize after show to trigger fit view operation. self.resize(self.width, self.height) self.buffer.aspect_ratio_change.connect(self.adjust_aspect_ratio) def resizeEvent(self, event): # Fit content to view rect just when buffer fit_to_view option is enable. if self.buffer.fit_to_view: if event.oldSize().isValid(): self.graphics_view.fitInView( self.graphics_view.scene().sceneRect(), Qt.KeepAspectRatio) QWidget.resizeEvent(self, event) def adjust_aspect_ratio(self): widget_width = self.width widget_height = self.height if self.buffer.aspect_ratio == 0: self.buffer.buffer_widget.resize(self.width, self.height) self.layout.setContentsMargins(0, 0, 0, 0) else: view_height = widget_height * ( 1 - 2 * self.buffer.vertical_padding_ratio) view_width = view_height * self.buffer.aspect_ratio horizontal_padding = (widget_width - view_width) / 2 vertical_padding = self.buffer.vertical_padding_ratio * widget_height self.buffer.buffer_widget.resize(view_width, view_height) self.layout.setContentsMargins(horizontal_padding, vertical_padding, horizontal_padding, vertical_padding) def eventFilter(self, obj, event): # Focus emacs buffer when user click view. if event.type() in [ QEvent.MouseButtonPress, QEvent.MouseButtonRelease, QEvent.MouseButtonDblClick, QEvent.Wheel ]: self.trigger_focus_event.emit(self.buffer_id) # Stop mouse event. return True return False def showEvent(self, event): # NOTE: we must reparent after widget show, otherwise reparent operation maybe failed. self.reparent() # Make graphics view at left-top corner after show. self.graphics_view.verticalScrollBar().setValue(0) self.graphics_view.horizontalScrollBar().setValue(0) def reparent(self): qwindow = self.windowHandle() qwindow.setParent(QWindow.fromWinId(self.emacs_xid)) qwindow.setPosition(QPoint(self.x, self.y)) def destroy_view(self): self.destroy()
class CarlaPatchbayW(QFrame, PluginEditParentMeta, HostWidgetMeta): #class CarlaPatchbayW(QFrame, PluginEditParentMeta, HostWidgetMeta, metaclass=PyQtMetaClass): def __init__(self, parent, host, doSetup=True, onlyPatchbay=True, is3D=False): QFrame.__init__(self, parent) self.host = host if False: # kdevelop likes this :) host = CarlaHostMeta() self.host = host # ------------------------------------------------------------- self.fLayout = QGridLayout(self) self.fLayout.setContentsMargins(0, 0, 0, 0) self.fLayout.setSpacing(1) self.setLayout(self.fLayout) self.fView = QGraphicsView(self) self.fKeys = PixmapKeyboardHArea(self) self.fPeaksIn = DigitalPeakMeter(self) self.fPeaksOut = DigitalPeakMeter(self) self.fPeaksCleared = True self.fPeaksIn.setColor(DigitalPeakMeter.BLUE) self.fPeaksIn.setChannels(2) self.fPeaksIn.setOrientation(DigitalPeakMeter.VERTICAL) self.fPeaksIn.setFixedWidth(25) self.fPeaksOut.setColor(DigitalPeakMeter.GREEN) self.fPeaksOut.setChannels(2) self.fPeaksOut.setOrientation(DigitalPeakMeter.VERTICAL) self.fPeaksOut.setFixedWidth(25) self.fLayout.addWidget(self.fPeaksIn, 0, 0) self.fLayout.addWidget(self.fView, 0, 1) # self.fViewWidget if is3D else self.fLayout.addWidget(self.fPeaksOut, 0, 2) self.fLayout.addWidget(self.fKeys, 1, 0, 1, 0) # ------------------------------------------------------------- # Internal stuff self.fParent = parent self.fPluginCount = 0 self.fPluginList = [] self.fExternalPatchbay = False self.fIsOnlyPatchbay = onlyPatchbay self.fSelectedPlugins = [] self.fCanvasWidth = 0 self.fCanvasHeight = 0 # ------------------------------------------------------------- # Set-up Canvas Preview self.fMiniCanvasPreview = self.fParent.ui.miniCanvasPreview self.fMiniCanvasPreview.setRealParent(self) self.fMovingViaMiniCanvas = False # ------------------------------------------------------------- # Set-up Canvas self.scene = patchcanvas.PatchScene(self, self.fView) self.fView.setScene(self.scene) self.fView.setRenderHint( QPainter.Antialiasing, bool(parent.fSavedSettings[CARLA_KEY_CANVAS_ANTIALIASING] == patchcanvas.ANTIALIASING_FULL)) if parent.fSavedSettings[ CARLA_KEY_CANVAS_USE_OPENGL] and hasGL: # and not is3D: self.fViewWidget = QGLWidget(self) self.fView.setViewport(self.fViewWidget) self.fView.setRenderHint( QPainter.HighQualityAntialiasing, parent.fSavedSettings[CARLA_KEY_CANVAS_HQ_ANTIALIASING]) self.setupCanvas() QTimer.singleShot(100, self.slot_restoreScrollbarValues) # ------------------------------------------------------------- # Connect actions to functions parent.ui.act_settings_show_meters.toggled.connect( self.slot_showCanvasMeters) parent.ui.act_settings_show_keyboard.toggled.connect( self.slot_showCanvasKeyboard) self.fView.horizontalScrollBar().valueChanged.connect( self.slot_horizontalScrollBarChanged) self.fView.verticalScrollBar().valueChanged.connect( self.slot_verticalScrollBarChanged) self.scene.scaleChanged.connect(self.slot_canvasScaleChanged) self.scene.sceneGroupMoved.connect(self.slot_canvasItemMoved) self.scene.pluginSelected.connect(self.slot_canvasPluginSelected) self.fMiniCanvasPreview.miniCanvasMoved.connect( self.slot_miniCanvasMoved) self.fKeys.keyboard.noteOn.connect(self.slot_noteOn) self.fKeys.keyboard.noteOff.connect(self.slot_noteOff) # ------------------------------------------------------------- # Load Settings settings = QSettings() showMeters = settings.value("ShowMeters", False, type=bool) self.fParent.ui.act_settings_show_meters.setChecked(showMeters) self.fPeaksIn.setVisible(showMeters) self.fPeaksOut.setVisible(showMeters) showKeyboard = settings.value("ShowKeyboard", not (MACOS or WINDOWS), type=bool) self.fParent.ui.act_settings_show_keyboard.setChecked(showKeyboard) self.fKeys.setVisible(showKeyboard) # ------------------------------------------------------------- # Connect actions to functions (part 2) host.PluginAddedCallback.connect(self.slot_handlePluginAddedCallback) host.PluginRemovedCallback.connect( self.slot_handlePluginRemovedCallback) host.NoteOnCallback.connect(self.slot_handleNoteOnCallback) host.NoteOffCallback.connect(self.slot_handleNoteOffCallback) host.PatchbayClientAddedCallback.connect( self.slot_handlePatchbayClientAddedCallback) host.PatchbayClientRemovedCallback.connect( self.slot_handlePatchbayClientRemovedCallback) host.PatchbayClientRenamedCallback.connect( self.slot_handlePatchbayClientRenamedCallback) host.PatchbayClientDataChangedCallback.connect( self.slot_handlePatchbayClientDataChangedCallback) host.PatchbayPortAddedCallback.connect( self.slot_handlePatchbayPortAddedCallback) host.PatchbayPortRemovedCallback.connect( self.slot_handlePatchbayPortRemovedCallback) host.PatchbayPortRenamedCallback.connect( self.slot_handlePatchbayPortRenamedCallback) host.PatchbayConnectionAddedCallback.connect( self.slot_handlePatchbayConnectionAddedCallback) host.PatchbayConnectionRemovedCallback.connect( self.slot_handlePatchbayConnectionRemovedCallback) if not doSetup: return parent.ui.act_plugins_enable.triggered.connect(self.slot_pluginsEnable) parent.ui.act_plugins_disable.triggered.connect( self.slot_pluginsDisable) parent.ui.act_plugins_volume100.triggered.connect( self.slot_pluginsVolume100) parent.ui.act_plugins_mute.triggered.connect(self.slot_pluginsMute) parent.ui.act_plugins_wet100.triggered.connect(self.slot_pluginsWet100) parent.ui.act_plugins_bypass.triggered.connect(self.slot_pluginsBypass) parent.ui.act_plugins_center.triggered.connect(self.slot_pluginsCenter) parent.ui.act_plugins_panic.triggered.connect(self.slot_pluginsDisable) parent.ui.act_canvas_show_internal.triggered.connect( self.slot_canvasShowInternal) parent.ui.act_canvas_show_external.triggered.connect( self.slot_canvasShowExternal) parent.ui.act_canvas_arrange.setEnabled(False) # TODO, later parent.ui.act_canvas_arrange.triggered.connect(self.slot_canvasArrange) parent.ui.act_canvas_refresh.triggered.connect(self.slot_canvasRefresh) parent.ui.act_canvas_zoom_fit.triggered.connect( self.slot_canvasZoomFit) parent.ui.act_canvas_zoom_in.triggered.connect(self.slot_canvasZoomIn) parent.ui.act_canvas_zoom_out.triggered.connect( self.slot_canvasZoomOut) parent.ui.act_canvas_zoom_100.triggered.connect( self.slot_canvasZoomReset) parent.ui.act_canvas_print.triggered.connect(self.slot_canvasPrint) parent.ui.act_canvas_save_image.triggered.connect( self.slot_canvasSaveImage) parent.ui.act_settings_configure.triggered.connect( self.slot_configureCarla) # ----------------------------------------------------------------- def getPluginEditDialog(self, pluginId): if pluginId >= self.fPluginCount: return None pedit = self.fPluginList[pluginId] if pedit is None: return None if False: pedit = DummyPluginEdit(self, 0) return pedit # ----------------------------------------------------------------- @pyqtSlot(int, str) def slot_handlePluginAddedCallback(self, pluginId, pluginName): if self.fIsOnlyPatchbay: pedit = PluginEdit(self, self.host, pluginId) else: pedit = DummyPluginEdit(self, pluginId) self.fPluginList.append(pedit) self.fPluginCount += 1 if self.fIsOnlyPatchbay and not self.fParent.isProjectLoading(): self.host.set_active(pluginId, True) @pyqtSlot(int) def slot_handlePluginRemovedCallback(self, pluginId): patchcanvas.handlePluginRemoved(pluginId) if pluginId in self.fSelectedPlugins: self.clearSideStuff() pedit = self.getPluginEditDialog(pluginId) self.fPluginCount -= 1 self.fPluginList.pop(pluginId) if pedit is not None: pedit.close() del pedit # push all plugins 1 slot back for i in range(pluginId, self.fPluginCount): pedit = self.fPluginList[i] pedit.setPluginId(i) @pyqtSlot(int, int, int, int) def slot_handleNoteOnCallback(self, pluginId, channel, note, velocity): if pluginId in self.fSelectedPlugins: self.fKeys.keyboard.sendNoteOn(note, False) @pyqtSlot(int, int, int) def slot_handleNoteOffCallback(self, pluginId, channel, note): if pluginId in self.fSelectedPlugins: self.fKeys.keyboard.sendNoteOff(note, False) # ----------------------------------------------------------------- # HostWidgetMeta methods def removeAllPlugins(self): patchcanvas.handleAllPluginsRemoved() for pedit in self.fPluginList: if pedit is None: break pedit.close() del pedit self.fPluginCount = 0 self.fPluginList = [] self.clearSideStuff() def engineStarted(self): pass def engineStopped(self): patchcanvas.clear() def idleFast(self): if self.fPluginCount == 0: return for pluginId in self.fSelectedPlugins: self.fPeaksCleared = False if self.fPeaksIn.isVisible(): self.fPeaksIn.displayMeter( 1, self.host.get_input_peak_value(pluginId, True)) self.fPeaksIn.displayMeter( 2, self.host.get_input_peak_value(pluginId, False)) if self.fPeaksOut.isVisible(): self.fPeaksOut.displayMeter( 1, self.host.get_output_peak_value(pluginId, True)) self.fPeaksOut.displayMeter( 2, self.host.get_output_peak_value(pluginId, False)) return if self.fPeaksCleared: return self.fPeaksCleared = True self.fPeaksIn.displayMeter(1, 0.0, True) self.fPeaksIn.displayMeter(2, 0.0, True) self.fPeaksOut.displayMeter(1, 0.0, True) self.fPeaksOut.displayMeter(2, 0.0, True) def idleSlow(self): for pedit in self.fPluginList: if pedit is None: break pedit.idleSlow() def projectLoadingStarted(self): self.fView.setEnabled(False) def projectLoadingFinished(self): self.fView.setEnabled(True) QTimer.singleShot(1000, self.slot_canvasRefresh) def saveSettings(self, settings): settings.setValue("ShowMeters", self.fParent.ui.act_settings_show_meters.isChecked()) settings.setValue( "ShowKeyboard", self.fParent.ui.act_settings_show_keyboard.isChecked()) settings.setValue("HorizontalScrollBarValue", self.fView.horizontalScrollBar().value()) settings.setValue("VerticalScrollBarValue", self.fView.verticalScrollBar().value()) def showEditDialog(self, pluginId): pedit = self.getPluginEditDialog(pluginId) if pedit: pedit.show() # ----------------------------------------------------------------- # PluginEdit callbacks def editDialogVisibilityChanged(self, pluginId, visible): pass def editDialogPluginHintsChanged(self, pluginId, hints): pass def editDialogParameterValueChanged(self, pluginId, parameterId, value): pass def editDialogProgramChanged(self, pluginId, index): pass def editDialogMidiProgramChanged(self, pluginId, index): pass def editDialogNotePressed(self, pluginId, note): if pluginId in self.fSelectedPlugins: self.fKeys.keyboard.sendNoteOn(note, False) def editDialogNoteReleased(self, pluginId, note): if pluginId in self.fSelectedPlugins: self.fKeys.keyboard.sendNoteOff(note, False) def editDialogMidiActivityChanged(self, pluginId, onOff): pass # ----------------------------------------------------------------- def clearSideStuff(self): self.scene.clearSelection() self.fSelectedPlugins = [] self.fKeys.keyboard.allNotesOff(False) self.fKeys.setEnabled(False) self.fPeaksCleared = True self.fPeaksIn.displayMeter(1, 0.0, True) self.fPeaksIn.displayMeter(2, 0.0, True) self.fPeaksOut.displayMeter(1, 0.0, True) self.fPeaksOut.displayMeter(2, 0.0, True) def setupCanvas(self): pOptions = patchcanvas.options_t() pOptions.theme_name = self.fParent.fSavedSettings[ CARLA_KEY_CANVAS_THEME] pOptions.auto_hide_groups = self.fParent.fSavedSettings[ CARLA_KEY_CANVAS_AUTO_HIDE_GROUPS] pOptions.use_bezier_lines = self.fParent.fSavedSettings[ CARLA_KEY_CANVAS_USE_BEZIER_LINES] pOptions.antialiasing = self.fParent.fSavedSettings[ CARLA_KEY_CANVAS_ANTIALIASING] pOptions.eyecandy = self.fParent.fSavedSettings[ CARLA_KEY_CANVAS_EYE_CANDY] pFeatures = patchcanvas.features_t() pFeatures.group_info = False pFeatures.group_rename = False pFeatures.port_info = False pFeatures.port_rename = False pFeatures.handle_group_pos = True patchcanvas.setOptions(pOptions) patchcanvas.setFeatures(pFeatures) patchcanvas.init("Carla2", self.scene, canvasCallback, False) tryCanvasSize = self.fParent.fSavedSettings[ CARLA_KEY_CANVAS_SIZE].split("x") if len(tryCanvasSize) == 2 and tryCanvasSize[0].isdigit( ) and tryCanvasSize[1].isdigit(): self.fCanvasWidth = int(tryCanvasSize[0]) self.fCanvasHeight = int(tryCanvasSize[1]) else: self.fCanvasWidth = CARLA_DEFAULT_CANVAS_SIZE_WIDTH self.fCanvasHeight = CARLA_DEFAULT_CANVAS_SIZE_HEIGHT patchcanvas.setCanvasSize(0, 0, self.fCanvasWidth, self.fCanvasHeight) patchcanvas.setInitialPos(self.fCanvasWidth / 2, self.fCanvasHeight / 2) self.fView.setSceneRect(0, 0, self.fCanvasWidth, self.fCanvasHeight) self.themeData = [ self.fCanvasWidth, self.fCanvasHeight, patchcanvas.canvas.theme.canvas_bg, patchcanvas.canvas.theme.rubberband_brush, patchcanvas.canvas.theme.rubberband_pen.color() ] def updateCanvasInitialPos(self): x = self.fView.horizontalScrollBar().value() + self.width() / 4 y = self.fView.verticalScrollBar().value() + self.height() / 4 patchcanvas.setInitialPos(x, y) # ----------------------------------------------------------------- @pyqtSlot(bool) def slot_showCanvasMeters(self, yesNo): self.fPeaksIn.setVisible(yesNo) self.fPeaksOut.setVisible(yesNo) @pyqtSlot(bool) def slot_showCanvasKeyboard(self, yesNo): self.fKeys.setVisible(yesNo) # ----------------------------------------------------------------- @pyqtSlot() def slot_miniCanvasCheckAll(self): self.slot_miniCanvasCheckSize() self.slot_horizontalScrollBarChanged( self.fView.horizontalScrollBar().value()) self.slot_verticalScrollBarChanged( self.fView.verticalScrollBar().value()) @pyqtSlot() def slot_miniCanvasCheckSize(self): self.fMiniCanvasPreview.setViewSize( float(self.width()) / self.fCanvasWidth, float(self.height()) / self.fCanvasHeight) @pyqtSlot(int) def slot_horizontalScrollBarChanged(self, value): if self.fMovingViaMiniCanvas: return maximum = self.fView.horizontalScrollBar().maximum() if maximum == 0: xp = 0 else: xp = float(value) / maximum self.fMiniCanvasPreview.setViewPosX(xp) self.updateCanvasInitialPos() @pyqtSlot(int) def slot_verticalScrollBarChanged(self, value): if self.fMovingViaMiniCanvas: return maximum = self.fView.verticalScrollBar().maximum() if maximum == 0: yp = 0 else: yp = float(value) / maximum self.fMiniCanvasPreview.setViewPosY(yp) self.updateCanvasInitialPos() @pyqtSlot() def slot_restoreScrollbarValues(self): settings = QSettings() self.fView.horizontalScrollBar().setValue( settings.value("HorizontalScrollBarValue", self.fView.horizontalScrollBar().maximum() / 2, type=int)) self.fView.verticalScrollBar().setValue( settings.value("VerticalScrollBarValue", self.fView.verticalScrollBar().maximum() / 2, type=int)) # ----------------------------------------------------------------- @pyqtSlot(float) def slot_canvasScaleChanged(self, scale): self.fMiniCanvasPreview.setViewScale(scale) @pyqtSlot(int, int, QPointF) def slot_canvasItemMoved(self, group_id, split_mode, pos): self.fMiniCanvasPreview.update() @pyqtSlot(list) def slot_canvasPluginSelected(self, pluginList): self.fKeys.keyboard.allNotesOff(False) self.fKeys.setEnabled( len(pluginList) != 0) # and self.fPluginCount > 0 self.fSelectedPlugins = pluginList @pyqtSlot(float, float) def slot_miniCanvasMoved(self, xp, yp): self.fMovingViaMiniCanvas = True self.fView.horizontalScrollBar().setValue( xp * self.fView.horizontalScrollBar().maximum()) self.fView.verticalScrollBar().setValue( yp * self.fView.verticalScrollBar().maximum()) self.fMovingViaMiniCanvas = False self.updateCanvasInitialPos() # ----------------------------------------------------------------- @pyqtSlot(int) def slot_noteOn(self, note): for pluginId in self.fSelectedPlugins: self.host.send_midi_note(pluginId, 0, note, 100) pedit = self.getPluginEditDialog(pluginId) pedit.noteOn(0, note, 100) @pyqtSlot(int) def slot_noteOff(self, note): for pluginId in self.fSelectedPlugins: self.host.send_midi_note(pluginId, 0, note, 0) pedit = self.getPluginEditDialog(pluginId) pedit.noteOff(0, note) # ----------------------------------------------------------------- @pyqtSlot() def slot_pluginsEnable(self): if not self.host.is_engine_running(): return for i in range(self.fPluginCount): self.host.set_active(i, True) @pyqtSlot() def slot_pluginsDisable(self): if not self.host.is_engine_running(): return for i in range(self.fPluginCount): self.host.set_active(i, False) @pyqtSlot() def slot_pluginsVolume100(self): if not self.host.is_engine_running(): return for i in range(self.fPluginCount): pedit = self.fPluginList[i] if pedit is None: break if pedit.getHints() & PLUGIN_CAN_VOLUME: pedit.setParameterValue(PARAMETER_VOLUME, 1.0) self.host.set_volume(i, 1.0) @pyqtSlot() def slot_pluginsMute(self): if not self.host.is_engine_running(): return for i in range(self.fPluginCount): pedit = self.fPluginList[i] if pedit is None: break if pedit.getHints() & PLUGIN_CAN_VOLUME: pedit.setParameterValue(PARAMETER_VOLUME, 0.0) self.host.set_volume(i, 0.0) @pyqtSlot() def slot_pluginsWet100(self): if not self.host.is_engine_running(): return for i in range(self.fPluginCount): pedit = self.fPluginList[i] if pedit is None: break if pedit.getHints() & PLUGIN_CAN_DRYWET: pedit.setParameterValue(PARAMETER_DRYWET, 1.0) self.host.set_drywet(i, 1.0) @pyqtSlot() def slot_pluginsBypass(self): if not self.host.is_engine_running(): return for i in range(self.fPluginCount): pedit = self.fPluginList[i] if pedit is None: break if pedit.getHints() & PLUGIN_CAN_DRYWET: pedit.setParameterValue(PARAMETER_DRYWET, 0.0) self.host.set_drywet(i, 0.0) @pyqtSlot() def slot_pluginsCenter(self): if not self.host.is_engine_running(): return for i in range(self.fPluginCount): pedit = self.fPluginList[i] if pedit is None: break if pedit.getHints() & PLUGIN_CAN_BALANCE: pedit.setParameterValue(PARAMETER_BALANCE_LEFT, -1.0) pedit.setParameterValue(PARAMETER_BALANCE_RIGHT, 1.0) self.host.set_balance_left(i, -1.0) self.host.set_balance_right(i, 1.0) if pedit.getHints() & PLUGIN_CAN_PANNING: pedit.setParameterValue(PARAMETER_PANNING, 0.0) self.host.set_panning(i, 0.0) # ----------------------------------------------------------------- @pyqtSlot() def slot_configureCarla(self): dialog = CarlaSettingsW(self, self.host, True, hasGL) if not dialog.exec_(): return self.fParent.loadSettings(False) patchcanvas.clear() self.setupCanvas() self.fParent.updateContainer(self.themeData) self.slot_miniCanvasCheckAll() if self.host.is_engine_running(): self.host.patchbay_refresh(self.fExternalPatchbay) # ----------------------------------------------------------------- @pyqtSlot(int, int, int, str) def slot_handlePatchbayClientAddedCallback(self, clientId, clientIcon, pluginId, clientName): pcSplit = patchcanvas.SPLIT_UNDEF pcIcon = patchcanvas.ICON_APPLICATION if clientIcon == PATCHBAY_ICON_PLUGIN: pcIcon = patchcanvas.ICON_PLUGIN if clientIcon == PATCHBAY_ICON_HARDWARE: pcIcon = patchcanvas.ICON_HARDWARE elif clientIcon == PATCHBAY_ICON_CARLA: pass elif clientIcon == PATCHBAY_ICON_DISTRHO: pcIcon = patchcanvas.ICON_DISTRHO elif clientIcon == PATCHBAY_ICON_FILE: pcIcon = patchcanvas.ICON_FILE patchcanvas.addGroup(clientId, clientName, pcSplit, pcIcon) QTimer.singleShot(0, self.fMiniCanvasPreview.update) if pluginId < 0: return if pluginId >= self.fPluginCount: print("sorry, can't map this plugin to canvas client", pluginId, self.fPluginCount) return patchcanvas.setGroupAsPlugin( clientId, pluginId, bool( self.host.get_plugin_info(pluginId)['hints'] & PLUGIN_HAS_CUSTOM_UI)) @pyqtSlot(int) def slot_handlePatchbayClientRemovedCallback(self, clientId): #if not self.fEngineStarted: return patchcanvas.removeGroup(clientId) QTimer.singleShot(0, self.fMiniCanvasPreview.update) @pyqtSlot(int, str) def slot_handlePatchbayClientRenamedCallback(self, clientId, newClientName): patchcanvas.renameGroup(clientId, newClientName) QTimer.singleShot(0, self.fMiniCanvasPreview.update) @pyqtSlot(int, int, int) def slot_handlePatchbayClientDataChangedCallback(self, clientId, clientIcon, pluginId): pcIcon = patchcanvas.ICON_APPLICATION if clientIcon == PATCHBAY_ICON_PLUGIN: pcIcon = patchcanvas.ICON_PLUGIN if clientIcon == PATCHBAY_ICON_HARDWARE: pcIcon = patchcanvas.ICON_HARDWARE elif clientIcon == PATCHBAY_ICON_CARLA: pass elif clientIcon == PATCHBAY_ICON_DISTRHO: pcIcon = patchcanvas.ICON_DISTRHO elif clientIcon == PATCHBAY_ICON_FILE: pcIcon = patchcanvas.ICON_FILE patchcanvas.setGroupIcon(clientId, pcIcon) QTimer.singleShot(0, self.fMiniCanvasPreview.update) if pluginId < 0: return if pluginId >= self.fPluginCount: print("sorry, can't map this plugin to canvas client", pluginId, self.fPluginCount) return patchcanvas.setGroupAsPlugin( clientId, pluginId, bool( self.host.get_plugin_info(pluginId)['hints'] & PLUGIN_HAS_CUSTOM_UI)) @pyqtSlot(int, int, int, str) def slot_handlePatchbayPortAddedCallback(self, clientId, portId, portFlags, portName): isAlternate = False if (portFlags & PATCHBAY_PORT_IS_INPUT): portMode = patchcanvas.PORT_MODE_INPUT else: portMode = patchcanvas.PORT_MODE_OUTPUT if (portFlags & PATCHBAY_PORT_TYPE_AUDIO): portType = patchcanvas.PORT_TYPE_AUDIO_JACK elif (portFlags & PATCHBAY_PORT_TYPE_CV): isAlternate = True portType = patchcanvas.PORT_TYPE_AUDIO_JACK elif (portFlags & PATCHBAY_PORT_TYPE_MIDI): portType = patchcanvas.PORT_TYPE_MIDI_JACK else: portType = patchcanvas.PORT_TYPE_NULL patchcanvas.addPort(clientId, portId, portName, portMode, portType, isAlternate) QTimer.singleShot(0, self.fMiniCanvasPreview.update) @pyqtSlot(int, int) def slot_handlePatchbayPortRemovedCallback(self, groupId, portId): #if not self.fEngineStarted: return patchcanvas.removePort(groupId, portId) QTimer.singleShot(0, self.fMiniCanvasPreview.update) @pyqtSlot(int, int, str) def slot_handlePatchbayPortRenamedCallback(self, groupId, portId, newPortName): patchcanvas.renamePort(groupId, portId, newPortName) QTimer.singleShot(0, self.fMiniCanvasPreview.update) @pyqtSlot(int, int, int, int, int) def slot_handlePatchbayConnectionAddedCallback(self, connectionId, groupOutId, portOutId, groupInId, portInId): patchcanvas.connectPorts(connectionId, groupOutId, portOutId, groupInId, portInId) QTimer.singleShot(0, self.fMiniCanvasPreview.update) @pyqtSlot(int, int, int) def slot_handlePatchbayConnectionRemovedCallback(self, connectionId, portOutId, portInId): #if not self.fEngineStarted: return patchcanvas.disconnectPorts(connectionId) QTimer.singleShot(0, self.fMiniCanvasPreview.update) # ----------------------------------------------------------------- @pyqtSlot() def slot_canvasArrange(self): patchcanvas.arrange() @pyqtSlot() def slot_canvasShowInternal(self): self.fExternalPatchbay = False self.fParent.ui.act_canvas_show_internal.blockSignals(True) self.fParent.ui.act_canvas_show_external.blockSignals(True) self.fParent.ui.act_canvas_show_internal.setChecked(True) self.fParent.ui.act_canvas_show_external.setChecked(False) self.fParent.ui.act_canvas_show_internal.blockSignals(False) self.fParent.ui.act_canvas_show_external.blockSignals(False) self.slot_canvasRefresh() @pyqtSlot() def slot_canvasShowExternal(self): self.fExternalPatchbay = True self.fParent.ui.act_canvas_show_internal.blockSignals(True) self.fParent.ui.act_canvas_show_external.blockSignals(True) self.fParent.ui.act_canvas_show_internal.setChecked(False) self.fParent.ui.act_canvas_show_external.setChecked(True) self.fParent.ui.act_canvas_show_internal.blockSignals(False) self.fParent.ui.act_canvas_show_external.blockSignals(False) self.slot_canvasRefresh() @pyqtSlot() def slot_canvasRefresh(self): patchcanvas.clear() if self.host.is_engine_running(): self.host.patchbay_refresh(self.fExternalPatchbay) for pedit in self.fPluginList: if pedit is None: break pedit.reloadAll() QTimer.singleShot( 1000 if self.fParent.fSavedSettings[CARLA_KEY_CANVAS_EYE_CANDY] else 0, self.fMiniCanvasPreview.update) @pyqtSlot() def slot_canvasZoomFit(self): self.scene.zoom_fit() @pyqtSlot() def slot_canvasZoomIn(self): self.scene.zoom_in() @pyqtSlot() def slot_canvasZoomOut(self): self.scene.zoom_out() @pyqtSlot() def slot_canvasZoomReset(self): self.scene.zoom_reset() @pyqtSlot() def slot_canvasPrint(self): self.scene.clearSelection() self.fExportPrinter = QPrinter() dialog = QPrintDialog(self.fExportPrinter, self) if dialog.exec_(): painter = QPainter(self.fExportPrinter) painter.save() painter.setRenderHint(QPainter.Antialiasing) painter.setRenderHint(QPainter.TextAntialiasing) self.scene.render(painter) painter.restore() @pyqtSlot() def slot_canvasSaveImage(self): newPath = QFileDialog.getSaveFileName( self, self.tr("Save Image"), filter=self.tr("PNG Image (*.png);;JPEG Image (*.jpg)")) if config_UseQt5: newPath = newPath[0] if not newPath: return self.scene.clearSelection() if newPath.lower().endswith((".jpg", )): imgFormat = "JPG" elif newPath.lower().endswith((".png", )): imgFormat = "PNG" else: # File-dialog may not auto-add the extension imgFormat = "PNG" newPath += ".png" self.fExportImage = QImage(self.scene.sceneRect().width(), self.scene.sceneRect().height(), QImage.Format_RGB32) painter = QPainter(self.fExportImage) painter.save() painter.setRenderHint( QPainter.Antialiasing) # TODO - set true, cleanup this painter.setRenderHint(QPainter.TextAntialiasing) self.scene.render(painter) self.fExportImage.save(newPath, imgFormat, 100) painter.restore() # ----------------------------------------------------------------- def resizeEvent(self, event): QFrame.resizeEvent(self, event) self.slot_miniCanvasCheckSize()
class analysis(QMainWindow): def __init__(self): super().__init__() self.initUI() self.show() def initUI(self): # 设置菜单并获取日期列表 self.loadMenu() # 加载初始化数据 self.load_initial_data() #设置提示信息字体 ## # 整体布局为水平布局 self.centralWidget = QWidget(self) self.upest = QHBoxLayout() self.setScrollArea() self.setPie() # 右 self.centralWidget.setLayout(self.upest) self.setCentralWidget(self.centralWidget) #图标 self.setIcon() #页面设置 self.SetWindoww() def loadMenu(self): #从数据库读取日期信息 if not path.isfile('./data/date_list'): system('copy nul date_list') print('not exist') else: print('exists!') with open('./data/date_list', 'r') as f: self.date_list = [x.strip() for x in f.readlines()] print(self.date_list) #self.date_list = ['2019/5/16','2019/5/17'] menu = self.menuBar() item = menu.addMenu('&日期选择') for i in self.date_list: ac = QAction(i, self) ac.triggered.connect(self.redraw) item.addAction(ac) def load_initial_data(self): def filter_date(date_list, date): return [x for x in date_list if x['phase'][0] == date] self.colors = [ '#90ed7d', '#f7a35c', '#f15c80', '#7199cf', '#4fc4aa', '#ffff10' ] #self.colors = ['#90ed7d', '#f7a35c', '#f15c80'] if not self.date_list: self.initial_timeline = [] self.initial_pie = {} self.date_list = ['没有任务记录~'] return with open('./data/tasks', 'r') as f: tasks_to_show = filter_date( [loads(x.strip('\n')) for x in f.readlines()], self.date_list[-1]) #print(tasks_to_show) #从数据库获取最新一天的数据 # tasks_to_show = [ # {'title': '程设', 'phase': ('2019/5/31', '0:00', '0:30'), 'tags': '学习'}, # {'title': '打游戏', 'phase': ('2019/5/31', '0:30', '1:00'), 'tags': '娱乐'}, # {'title': '打篮球', 'phase': ('2019/5/31', '18:00', '19:15'), 'tags': '运动'} # ] self.initial_timeline = tasks_to_show #self.initial_timeline = [] self.initial_pie = [(x['tags'], self.time_len([x['phase'][1], x['phase'][2]])) for x in tasks_to_show] print('inital pie data is {}'.format(self.initial_pie)) #self.initial_pie = [] test = [{ 'title': '程设', 'phase': ('2019/5/28', '8:00', '10:50'), 'tags': '测试0' }, { 'title': '打游戏', 'phase': ('2019/5/28', '1:30', '4:20'), 'tags': '测试1' }, { 'title': '打篮球', 'phase': ('2019/5/28', '18:00', '19:15'), 'tags': '测试2' }] #self.test = {x['tags']:self.time_len([x['phase'][1], x['phase'][2]]) for x in test} self.test = test def time_len(self, time_points): #计算两个字符串格式日期的时间间隔 按分钟计 t1 = time_points[0].split(':') t2 = time_points[1].split(':') t1 = [int(x) for x in t1] t2 = [int(x) for x in t2] return 60 * (t2[0] - 1 - t1[0]) + 60 + t2[1] - t1[1] def time_transform(self, tbegin, tend): t1 = tbegin.split(':') t2 = tend.split(':') t1 = [int(x) for x in t1] t2 = [int(x) for x in t2] return t1[0] * 60 + t1[1], t2[0] * 60 + t2[1] def redraw(self): def filter_date(date_list, date): return [x for x in date_list if x['phase'][0] == date] date_to_show = self.sender().text() #从数据库提取指定日期的任务列表,每个元素是一个字典 tasks_to_show = [{ 'title': '程设', 'phase': ('2019/5/17', '8:00', '10:20'), 'tags': '你好' }, { 'title': '打游戏', 'phase': ('2019/5/17', '1:30', '4:20'), 'tags': '娱乐' }, { 'title': '打篮球', 'phase': ('2019/5/17', '18:00', '19:15'), 'tags': '运动' }] with open('./data/tasks', 'r') as f: tasks_to_show = filter_date( [loads(x.strip('\n')) for x in f.readlines()], date_to_show) #draw new pie pie_data = [(x['tags'], self.time_len([x['phase'][1], x['phase'][2]])) for x in tasks_to_show] print('pie data is {}'.format(pie_data)) self.draw_pie(pie_data, date_to_show) self.draw_timeline(tasks_to_show) #draw new timeline def draw_pie(self, data, today): # if not data: # return def sum_data(data): res = {} for x in data: res[x[0]] = 0 for x in data: res[x[0]] += x[1] return res data = sum_data(data) print('new pie data is {}'.format(data)) self.F = MyFigure(width=3, height=2) values = [x for x in data.values()] labels = [x for x in data.keys()] #colors = ['#7199cf', '#4fc4aa', '#ffff10'] colors_sample = [] for i in range(len(values)): colors_sample.append(self.colors[i % len(self.colors)]) print(colors_sample) # 设置饼图的凸出显示 explode = [0 for i in range(len(values))] self.F.axes.pie(values, labels=labels, colors=colors_sample, explode=explode, shadow=False) self.F.fig.suptitle(today) # graphicscene是一个展示2D图形的场景,setSceneRect的效果是设置场景中可见的矩形范围 # 参数a b c d; a是可见范围左上角坐标的横坐标 b是纵坐标; c是可见范围的横向距离,d是纵向距离 # graphicscene.setSceneRect(QtCore.QRectF(101, 300, 590, 100)) graphicscene = QGraphicsScene() graphicscene.addWidget(self.F) graphicscene.setSceneRect(QRectF(101, 300, 590, 100)) self.graphicview.setScene(graphicscene) def draw_timeline(self, data): def sort_func(elem): t = elem['phase'][1].split(':') t = [int(x) for x in t] return 60 * t[0] + t[1] for i in range(self.vlay_2.count()): self.vlay_2.itemAt(i).widget().deleteLater() if not data: temp = QLabel("还没有任务完成~", self) temp.setStyleSheet( 'background-color: red; margin: 0px; font-size:20px;color:black' ) temp.setFixedHeight(60) temp.setFixedWidth(345) self.vlay_2.addWidget(temp) self.vlay_2.addWidget(QLabel()) return #数据项x格式 {'title': '程设', 'phase': ('2019/5/17', '8:00', '10:50'), 'tags': '学习'}, data.sort(key=sort_func) print(data) index = -1 head_time = 0 for x in data: index += 1 temp = QLabel(x['title'], self) temp.setToolTip('标签:{}\n时间:{} ~ {}'.format(x['tags'], x['phase'][1], x['phase'][2])) begin_point, end_point = self.time_transform( x['phase'][1], x['phase'][2]) if head_time != begin_point: blank = QLabel('none', self) #blank.setStyleSheet('margin: 0px; font-size:20px;color:black') #blank.setStyleSheet('background-color: purple; margin: 0px; font-size:20px;color:black') blank.setFixedWidth(345) if begin_point <= head_time: print('error!!!') blank.setFixedHeight(2 * (begin_point - head_time)) #blank.setFixedHeight(100) self.vlay_2.addWidget(blank) head_time = end_point print(index) else: head_time = end_point temp.setStyleSheet( "background-color: {}; margin: 0px; font-size:20px;color:black" .format(self.colors[index % len(self.colors)])) #temp.setStyleSheet("background-color: rgb(144,237,15); margin: 0px; font-size:20px;color:black") temp.setFixedWidth(345) temp.setFixedHeight(2 * (end_point - begin_point)) self.vlay_2.addWidget(temp) self.vlay_2.addWidget(QLabel()) def setScrollArea(self): self.scrollArea = QScrollArea(self) self.upest.addWidget(self.scrollArea) self.scrollArea.resize(1000, 1000) self.scrollcontent = QWidget() self.setMinimumHeight(10) ###important self.lay1 = QHBoxLayout(self) self.lay1.setContentsMargins(0, 0, 0, 0) self.lay1.setSpacing(0) self.vlay_1 = QVBoxLayout(self) self.vlay_1.setContentsMargins(0, 0, 0, 0) self.vlay_1.setSpacing(0) self.vlay_2 = QVBoxLayout(self) self.vlay_2.setContentsMargins(0, 0, 0, 0) self.vlay_2.setSpacing(0) for i in range(24): temp = QLabel("{}:00~{}:00".format(str(i), str(i + 1))) if i % 2 == 0: temp.setStyleSheet( 'background-color: rgb(240, 248, 255); margin: 0px; font-size:20px;color:black' ) # temp.setStyleSheet('margin: 0px; border:2px solid red; border-left: 0px; font-size:20px;color:black') else: #rgb(232, 234, 212) temp.setStyleSheet( 'background-color: rgb(230, 230, 250); margin: 0px; font-size:20px;color:black' ) temp.setFixedHeight(120) temp.setFixedWidth(200) self.vlay_1.addWidget(temp) self.vlay_1.addWidget(QLabel()) self.draw_timeline(self.initial_timeline) self.lay1.addLayout(self.vlay_1) self.lay1.addLayout(self.vlay_2) self.scrollcontent.setLayout(self.lay1) self.scrollArea.setWidget(self.scrollcontent) def setPie(self): self.graphicview = QGraphicsView() self.graphicview.horizontalScrollBar().setVisible(False) self.draw_pie(self.initial_pie, self.date_list[-1]) self.graphicview.show() self.upest.addWidget(self.graphicview) def setIcon(self): self.setWindowIcon(QIcon('./images/others/icon.png')) def SetWindoww(self): self.move(403, 36) #self.resize(1200,900) self.setFixedSize(1200, 900) self.setWindowTitle('觅时') # if __name__ == '__main__': # app = QApplication(argv) # w = main() # exit(app.exec_())