def split_frame(self, frame, orientation): """ Split the given frame either horizontally or vertically. :param frame: frame to split :param orientation: orientation along which the frame will be split """ opposite_orientation = Qt.Vertical if orientation == Qt.Horizontal else Qt.Horizontal # get opposite orientation parent_splitter = frame.splitter # get reference to the splitter wherein the frame is embedded (maybe none) splitter = None # variable to hold the splitter that will be used to embed this frame after splitting # if the frame is not embedded in a splitter or the orientation of the embedding splitter does not match a new splitter has to be created if parent_splitter is None or parent_splitter.orientation() == opposite_orientation: splitter = QSplitter(orientation) # create new splitter if parent_splitter is None: # if the frame was not embedded into a splitter the new splitter is the base splitter self.addWidget(splitter) self._base_splitter = splitter splitter.addWidget(frame) else: # if the frame was embedded into a differently oriented splitter, the new splitter will take its place splitter_index = frame.splitter_index # get the correct position of the new splitter within the parent splitter # get the sizes of all the widgets within the parent splitter if orientation == Qt.Horizontal: sizes = [parent_splitter.widget(i).geometry().height() for i in range(parent_splitter.count())] else: sizes = [parent_splitter.widget(i).geometry().width() for i in range(parent_splitter.count())] splitter.addWidget(frame) # add the frame to the new splitter parent_splitter.insertWidget(splitter_index, splitter) # add the splitter to the parent splitter parent_splitter.setSizes(sizes) # restore the dimensions of the widgets within the parent splitter frame.splitter = splitter # store the reference to the newly created splitter within the frame elif parent_splitter.orientation() == orientation: splitter = parent_splitter # if the orientation of the existing splitter matches just use it to proceed # store the sizes of all the existing widgets in the splitter # half the size of the frame to be split and add an additional size which is also half of the frames current dimension geometry = frame.geometry() if orientation == Qt.Horizontal: sizes = [splitter.widget(i).geometry().height() for i in range(splitter.count())] sizes[frame.splitter_index] = geometry.width() / 2 sizes.insert(frame.splitter_index + 1, geometry.width() / 2) else: sizes = [splitter.widget(i).geometry().width() for i in range(splitter.count())] sizes[frame.splitter_index] = geometry.height() / 2 sizes.insert(frame.splitter_index + 1, geometry.height() / 2) insert_index = frame.splitter_index + 1 # get the index where the new frame should be inserted into the splitter new_frame = self._add_modular_frame_() # create the new frame new_frame.splitter = splitter # set the splitter of the new frame splitter.insertWidget(insert_index, new_frame) # insert the new frame into the splitter splitter.setSizes(sizes) # setup the proper dimensions of all the widgets in the splitter
class ChartLayoutManager: def __init__(self, parent=None): self.parent = parent # Create default main chart pane self.chart_panes = [ ChartPane(ChartView(), ChartAxisView()), ] # Create main time axis pane self.time_axis_pane = ChartTimePane(TimeAxisView()) # Create chart pane container self.container = QSplitter(parent) self.init_layout(self.container) def init_layout(self, container): container.setOrientation(Qt.Vertical) container.setChildrenCollapsible(False) container.setHandleWidth(2) container.setContentsMargins(0, 0, 0, 0) # init chart panes for chart_pane in self.chart_panes: container.addWidget(chart_pane.create(self.parent)) def del_last_pane(self, checked=False): count = self.container.count() if count > 1: self.chart_panes.pop(count - 1) self.container.widget(count - 1).deleteLater() def del_pane(self, index): if index == 0: return False self.chart_panes.pop(index) self.container.widget(index).deleteLater() def add_pane(self, pane=None): if not pane: pane = ChartPane(ChartView(), ChartAxisView()) self.chart_panes.append(pane) self.container.addWidget(pane.create(self.parent)) def get_chart_panes(self) -> QSplitter: return self.container def get_time_axis(self) -> QWidget: return self.time_axis_pane.create(self.parent)
class QuadView(QWidget): def __init__(self, parent, view1, view2, view3, view4=None): QWidget.__init__(self, parent) self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.installEventFilter(self) self.dockableContainer = [] self.layout = QVBoxLayout() self.setLayout(self.layout) self.layout.setContentsMargins(0, 0, 0, 0) self.layout.setSpacing(0) self.splitVertical = QSplitter(Qt.Vertical, self) self.layout.addWidget(self.splitVertical) self.splitHorizontal1 = QSplitter(Qt.Horizontal, self.splitVertical) self.splitHorizontal1.setObjectName("splitter1") self.splitHorizontal2 = QSplitter(Qt.Horizontal, self.splitVertical) self.splitHorizontal2.setObjectName("splitter2") self.splitHorizontal1.splitterMoved.connect(self.horizontalSplitterMoved) self.splitHorizontal2.splitterMoved.connect(self.horizontalSplitterMoved) self.imageView2D_1 = view1 self.imageView2D_2 = view2 self.imageView2D_3 = view3 self.dock1_ofSplitHorizontal1 = ImageView2DDockWidget(self.imageView2D_1) self.dock1_ofSplitHorizontal1.connectHud() self.dockableContainer.append(self.dock1_ofSplitHorizontal1) self.dock1_ofSplitHorizontal1.onDockButtonClicked.connect( lambda arg=self.dock1_ofSplitHorizontal1: self.on_dock(arg) ) self.dock1_ofSplitHorizontal1.onMaxButtonClicked.connect( lambda arg=self.dock1_ofSplitHorizontal1: self.on_max(arg) ) self.dock1_ofSplitHorizontal1.onMinButtonClicked.connect( lambda arg=self.dock1_ofSplitHorizontal1: self.on_min(arg) ) self.splitHorizontal1.addWidget(self.dock1_ofSplitHorizontal1) self.dock2_ofSplitHorizontal1 = ImageView2DDockWidget(self.imageView2D_2) self.dock2_ofSplitHorizontal1.onDockButtonClicked.connect( lambda arg=self.dock2_ofSplitHorizontal1: self.on_dock(arg) ) self.dock2_ofSplitHorizontal1.onMaxButtonClicked.connect( lambda arg=self.dock2_ofSplitHorizontal1: self.on_max(arg) ) self.dock2_ofSplitHorizontal1.onMinButtonClicked.connect( lambda arg=self.dock2_ofSplitHorizontal1: self.on_min(arg) ) self.dock2_ofSplitHorizontal1.connectHud() self.dockableContainer.append(self.dock2_ofSplitHorizontal1) self.splitHorizontal1.addWidget(self.dock2_ofSplitHorizontal1) self.dock1_ofSplitHorizontal2 = ImageView2DDockWidget(self.imageView2D_3) self.dock1_ofSplitHorizontal2.onDockButtonClicked.connect( lambda arg=self.dock1_ofSplitHorizontal2: self.on_dock(arg) ) self.dock1_ofSplitHorizontal2.onMaxButtonClicked.connect( lambda arg=self.dock1_ofSplitHorizontal2: self.on_max(arg) ) self.dock1_ofSplitHorizontal2.onMinButtonClicked.connect( lambda arg=self.dock1_ofSplitHorizontal2: self.on_min(arg) ) self.dock1_ofSplitHorizontal2.connectHud() self.dockableContainer.append(self.dock1_ofSplitHorizontal2) self.splitHorizontal2.addWidget(self.dock1_ofSplitHorizontal2) self.dock2_ofSplitHorizontal2 = ImageView2DDockWidget(view4) self.dockableContainer.append(self.dock2_ofSplitHorizontal2) self.splitHorizontal2.addWidget(self.dock2_ofSplitHorizontal2) # this is a hack: with 0 ms it does not work... QTimer.singleShot(250, self._resizeEqual) def _resizeEqual(self): if not all([dock.isVisible() for dock in self.dockableContainer]): return w, h = ( self.size().width() - self.splitHorizontal1.handleWidth(), self.size().height() - self.splitVertical.handleWidth(), ) self.splitVertical.setSizes([h // 2, h // 2]) if self.splitHorizontal1.count() == 2 and self.splitHorizontal2.count() == 2: # docks = [self.imageView2D_1, self.imageView2D_2, self.imageView2D_3, self.testView4] docks = [] for splitter in [self.splitHorizontal1, self.splitHorizontal2]: for i in range(splitter.count()): docks.append(splitter.widget(i).graphicsView) w1 = [docks[i].minimumSize().width() for i in [0, 2]] w2 = [docks[i].minimumSize().width() for i in [1, 3]] wLeft = max(w1) wRight = max(w2) if wLeft > wRight and wLeft > w // 2: wRight = w - wLeft elif wRight >= wLeft and wRight > w // 2: wLeft = w - wRight else: wLeft = w // 2 wRight = w // 2 self.splitHorizontal1.setSizes([wLeft, wRight]) self.splitHorizontal2.setSizes([wLeft, wRight]) def eventFilter(self, obj, event): if event.type() in [QEvent.WindowActivate, QEvent.Show]: self._synchronizeSplitter() return False def _synchronizeSplitter(self): sizes1 = self.splitHorizontal1.sizes() sizes2 = self.splitHorizontal2.sizes() if len(sizes1) > 0 and sizes1[0] > 0: self.splitHorizontal2.setSizes(sizes1) elif len(sizes2) > 0 and sizes2[0] > 0: self.splitHorizontal1.setSizes(sizes2) def resizeEvent(self, event): QWidget.resizeEvent(self, event) self._synchronizeSplitter() def horizontalSplitterMoved(self, x, y): if self.splitHorizontal1.count() != 2 or self.splitHorizontal2.count() != 2: return sizes = self.splitHorizontal1.sizes() # What. Nr2 if self.splitHorizontal2.closestLegalPosition(x, y) < self.splitHorizontal2.closestLegalPosition(x, y): sizeLeft = self.splitHorizontal1.closestLegalPosition(x, y) else: sizeLeft = self.splitHorizontal2.closestLegalPosition(x, y) sizeRight = sizes[0] + sizes[1] - sizeLeft sizes = [sizeLeft, sizeRight] self.splitHorizontal1.setSizes(sizes) self.splitHorizontal2.setSizes(sizes) def addStatusBar(self, bar): self.statusBar = bar self.layout.addLayout(self.statusBar) def setGrayScaleToQuadStatusBar(self, gray): self.quadViewStatusBar.setGrayScale(gray) def setMouseCoordsToQuadStatusBar(self, x, y, z): self.quadViewStatusBar.setMouseCoords(x, y, z) def ensureMaximized(self, axis): """ Maximize the view for the given axis if it isn't already maximized. """ axisDict = { 0: self.dock2_ofSplitHorizontal1, # x 1: self.dock1_ofSplitHorizontal2, # y 2: self.dock1_ofSplitHorizontal1, } # z if not axisDict[axis]._isMaximized: self.switchMinMax(axis) def ensureMinimized(self, axis): """ Minimize the view for the given axis if it isn't already minimized. """ axisDict = { 0: self.dock2_ofSplitHorizontal1, # x 1: self.dock1_ofSplitHorizontal2, # y 2: self.dock1_ofSplitHorizontal1, } # z if axisDict[axis]._isMaximized: self.switchMinMax(axis) def switchMinMax(self, axis): """Switch an AxisViewWidget between from minimized to maximized and vice versa. Keyword arguments: axis -- the axis which is represented by the widget (no default) either string or integer 'x' - 0 'y' - 1 'z' - 2 """ # TODO: get the mapping information from where it is set! if this is not # done properly - do it properly if type(axis) == str: axisDict = { "x": self.dock2_ofSplitHorizontal1, # x "y": self.dock1_ofSplitHorizontal2, # y "z": self.dock1_ofSplitHorizontal1, } # z elif type(axis) == int: axisDict = { 0: self.dock2_ofSplitHorizontal1, # x 1: self.dock1_ofSplitHorizontal2, # y 2: self.dock1_ofSplitHorizontal1, } # z dockWidget = axisDict.pop(axis) for dWidget in list(axisDict.values()): if dWidget._isMaximized: dWidget.graphicsView._hud.maximizeButtonClicked.emit() dockWidget.graphicsView._hud.maximizeButtonClicked.emit() def switchXMinMax(self): self.switchMinMax("x") def switchYMinMax(self): self.switchMinMax("y") def switchZMinMax(self): self.switchMinMax("z") def on_dock(self, dockWidget): if dockWidget._isDocked: dockWidget.undockView() self.on_min(dockWidget) dockWidget.minimizeView() else: dockWidget.dockView() def on_max(self, dockWidget): dockWidget.setVisible(True) for dock in self.dockableContainer: if not dockWidget == dock: dock.setVisible(False) # Force sizes to be updated now QApplication.processEvents() # On linux, the vertical splitter doesn't seem to refresh unless we do so manually # Presumably, this is a QT bug. self.splitVertical.refresh() # Viewport doesn't update automatically... view = dockWidget.graphicsView view.viewport().setGeometry(view.rect()) def on_min(self, dockWidget): for dock in self.dockableContainer: dock.setVisible(True) # Force sizes to be updated now QApplication.processEvents() self._resizeEqual() # Viewports don't update automatically... for dock in self.dockableContainer: view = dock.graphicsView if hasattr(view, "viewport"): view.viewport().setGeometry(view.rect())
class _s_CentralWidget(QWidget): ############################################################################### # CentralWidget SIGNALS ############################################################################### """ splitterCentralRotated() """ splitterCentralRotated = pyqtSignal() ############################################################################### def __init__(self, parent=None): super(_s_CentralWidget, self).__init__(parent) self.parent = parent #This variables are used to save the splitter sizes before hide self._splitterMainSizes = None self._splitterAreaSizes = None self.lateralPanel = None hbox = QHBoxLayout(self) hbox.setContentsMargins(0, 0, 0, 0) hbox.setSpacing(0) #Create Splitters to divide the UI in: MainPanel, Explorer, Misc self._splitterArea = QSplitter(Qt.Horizontal) self._splitterMain = QSplitter(Qt.Vertical) #Create scrollbar for follow mode self.scrollBar = QScrollBar(Qt.Vertical, self) self.scrollBar.setFixedWidth(20) self.scrollBar.setToolTip('Follow Mode: Scroll the Editors together') self.scrollBar.hide() self.scrollBar.valueChanged[int].connect(self.move_follow_scrolls) #Add to Main Layout hbox.addWidget(self.scrollBar) hbox.addWidget(self._splitterArea) def insert_central_container(self, container): self.mainContainer = container self._splitterMain.insertWidget(0, container) def insert_lateral_container(self, container): self.lateralPanel = LateralPanel(container) self._splitterArea.insertWidget(0, self.lateralPanel) def insert_bottom_container(self, container): self.misc = container self._splitterMain.insertWidget(1, container) def showEvent(self, event): #Show Event QWidget.showEvent(self, event) #Avoid recalculate the panel sizes if they are already loaded if self._splitterArea.count() == 2: return #Rearrange widgets on Window self._splitterArea.insertWidget(0, self._splitterMain) if not event.spontaneous(): self.change_misc_visibility() if bin(settings.UI_LAYOUT)[-1] == '1': self.splitter_central_rotate() if bin(settings.UI_LAYOUT >> 1)[-1] == '1': self.splitter_misc_rotate() if bin(settings.UI_LAYOUT >> 2)[-1] == '1': self.splitter_central_orientation() qsettings = QSettings(resources.SETTINGS_PATH, QSettings.IniFormat) #Lists of sizes as list of QVariant- heightList = [QVariant, QVariant] heightList = list(qsettings.value("window/central/mainSize", [(self.height() / 3) * 2, self.height() / 3])) widthList = list(qsettings.value("window/central/areaSize", [(self.width() / 6) * 5, self.width() / 6])) self._splitterMainSizes = [int(heightList[0]), int(heightList[1])] self._splitterAreaSizes = [int(widthList[0]), int(widthList[1])] #Set the sizes to splitters #self._splitterMain.setSizes(self._splitterMainSizes) self._splitterMain.setSizes(self._splitterMainSizes) self._splitterArea.setSizes(self._splitterAreaSizes) self.misc.setVisible( qsettings.value("window/show_misc", False, type=bool)) def change_misc_visibility(self, on_start=False): if self.misc.isVisible(): self._splitterMainSizes = self._splitterMain.sizes() self.misc.hide() widget = self.mainContainer.get_actual_widget() if widget: widget.setFocus() else: self.misc.show() self.misc.gain_focus() def change_main_visibility(self): if self.mainContainer.isVisible(): self.mainContainer.hide() else: self.mainContainer.show() def change_explorer_visibility(self, force_hide=False): if self.lateralPanel.isVisible() or force_hide: self._splitterAreaSizes = self._splitterArea.sizes() self.lateralPanel.hide() else: self.lateralPanel.show() def splitter_central_rotate(self): w1, w2 = self._splitterArea.widget(0), self._splitterArea.widget(1) self._splitterArea.insertWidget(0, w2) self._splitterArea.insertWidget(1, w1) self.splitterCentralRotated.emit() def splitter_central_orientation(self): if self._splitterArea.orientation() == Qt.Horizontal: self._splitterArea.setOrientation(Qt.Vertical) else: self._splitterArea.setOrientation(Qt.Horizontal) def splitter_misc_rotate(self): w1, w2 = self._splitterMain.widget(0), self._splitterMain.widget(1) self._splitterMain.insertWidget(0, w2) self._splitterMain.insertWidget(1, w1) def splitter_misc_orientation(self): if self._splitterMain.orientation() == Qt.Horizontal: self._splitterMain.setOrientation(Qt.Vertical) else: self._splitterMain.setOrientation(Qt.Horizontal) def get_area_sizes(self): if self.lateralPanel.isVisible(): self._splitterAreaSizes = self._splitterArea.sizes() return self._splitterAreaSizes def get_main_sizes(self): if self.misc.isVisible(): self._splitterMainSizes = self._splitterMain.sizes() return self._splitterMainSizes def enable_follow_mode_scrollbar(self, val): if val: editorWidget = self.mainContainer.get_actual_editor() maxScroll = editorWidget.verticalScrollBar().maximum() position = editorWidget.verticalScrollBar().value() self.scrollBar.setMaximum(maxScroll) self.scrollBar.setValue(position) self.scrollBar.setVisible(val) def move_follow_scrolls(self, val): widget = self.mainContainer._tabMain.currentWidget() diff = widget._sidebarWidget.highest_line - val s1 = self.mainContainer._tabMain.currentWidget().verticalScrollBar() s2 = self.mainContainer._tabSecondary.\ currentWidget().verticalScrollBar() s1.setValue(val) s2.setValue(val + diff)
class QuadView(QWidget): def __init__(self, parent, view1, view2, view3, view4=None): QWidget.__init__(self, parent) self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.installEventFilter(self) self.dockableContainer = [] self.layout = QVBoxLayout() self.setLayout(self.layout) self.layout.setContentsMargins(0, 0, 0, 0) self.layout.setSpacing(0) self.splitVertical = QSplitter(Qt.Vertical, self) self.layout.addWidget(self.splitVertical) self.splitHorizontal1 = QSplitter(Qt.Horizontal, self.splitVertical) self.splitHorizontal1.setObjectName("splitter1") self.splitHorizontal2 = QSplitter(Qt.Horizontal, self.splitVertical) self.splitHorizontal2.setObjectName("splitter2") self.splitHorizontal1.splitterMoved.connect( self.horizontalSplitterMoved) self.splitHorizontal2.splitterMoved.connect( self.horizontalSplitterMoved) self.imageView2D_1 = view1 self.imageView2D_2 = view2 self.imageView2D_3 = view3 self.dock1_ofSplitHorizontal1 = ImageView2DDockWidget( self.imageView2D_1) self.dock1_ofSplitHorizontal1.connectHud() self.dockableContainer.append(self.dock1_ofSplitHorizontal1) self.dock1_ofSplitHorizontal1.onDockButtonClicked.connect( lambda arg=self.dock1_ofSplitHorizontal1: self.on_dock(arg)) self.dock1_ofSplitHorizontal1.onMaxButtonClicked.connect( lambda arg=self.dock1_ofSplitHorizontal1: self.on_max(arg)) self.dock1_ofSplitHorizontal1.onMinButtonClicked.connect( lambda arg=self.dock1_ofSplitHorizontal1: self.on_min(arg)) self.splitHorizontal1.addWidget(self.dock1_ofSplitHorizontal1) self.dock2_ofSplitHorizontal1 = ImageView2DDockWidget( self.imageView2D_2) self.dock2_ofSplitHorizontal1.onDockButtonClicked.connect( lambda arg=self.dock2_ofSplitHorizontal1: self.on_dock(arg)) self.dock2_ofSplitHorizontal1.onMaxButtonClicked.connect( lambda arg=self.dock2_ofSplitHorizontal1: self.on_max(arg)) self.dock2_ofSplitHorizontal1.onMinButtonClicked.connect( lambda arg=self.dock2_ofSplitHorizontal1: self.on_min(arg)) self.dock2_ofSplitHorizontal1.connectHud() self.dockableContainer.append(self.dock2_ofSplitHorizontal1) self.splitHorizontal1.addWidget(self.dock2_ofSplitHorizontal1) self.dock1_ofSplitHorizontal2 = ImageView2DDockWidget( self.imageView2D_3) self.dock1_ofSplitHorizontal2.onDockButtonClicked.connect( lambda arg=self.dock1_ofSplitHorizontal2: self.on_dock(arg)) self.dock1_ofSplitHorizontal2.onMaxButtonClicked.connect( lambda arg=self.dock1_ofSplitHorizontal2: self.on_max(arg)) self.dock1_ofSplitHorizontal2.onMinButtonClicked.connect( lambda arg=self.dock1_ofSplitHorizontal2: self.on_min(arg)) self.dock1_ofSplitHorizontal2.connectHud() self.dockableContainer.append(self.dock1_ofSplitHorizontal2) self.splitHorizontal2.addWidget(self.dock1_ofSplitHorizontal2) self.dock2_ofSplitHorizontal2 = ImageView2DDockWidget(view4) self.dockableContainer.append(self.dock2_ofSplitHorizontal2) self.splitHorizontal2.addWidget(self.dock2_ofSplitHorizontal2) #this is a hack: with 0 ms it does not work... QTimer.singleShot(250, self._resizeEqual) def _resizeEqual(self): if not all([dock.isVisible() for dock in self.dockableContainer]): return w, h = self.size().width() - self.splitHorizontal1.handleWidth( ), self.size().height() - self.splitVertical.handleWidth() self.splitVertical.setSizes([h // 2, h // 2]) if self.splitHorizontal1.count() == 2 and self.splitHorizontal2.count( ) == 2: #docks = [self.imageView2D_1, self.imageView2D_2, self.imageView2D_3, self.testView4] docks = [] for splitter in [self.splitHorizontal1, self.splitHorizontal2]: for i in range(splitter.count()): docks.append(splitter.widget(i).graphicsView) w1 = [docks[i].minimumSize().width() for i in [0, 2]] w2 = [docks[i].minimumSize().width() for i in [1, 3]] wLeft = max(w1) wRight = max(w2) if wLeft > wRight and wLeft > w // 2: wRight = w - wLeft elif wRight >= wLeft and wRight > w // 2: wLeft = w - wRight else: wLeft = w // 2 wRight = w // 2 self.splitHorizontal1.setSizes([wLeft, wRight]) self.splitHorizontal2.setSizes([wLeft, wRight]) def eventFilter(self, obj, event): if (event.type() in [QEvent.WindowActivate, QEvent.Show]): self._synchronizeSplitter() return False def _synchronizeSplitter(self): sizes1 = self.splitHorizontal1.sizes() sizes2 = self.splitHorizontal2.sizes() if len(sizes1) > 0 and sizes1[0] > 0: self.splitHorizontal2.setSizes(sizes1) elif len(sizes2) > 0 and sizes2[0] > 0: self.splitHorizontal1.setSizes(sizes2) def resizeEvent(self, event): QWidget.resizeEvent(self, event) self._synchronizeSplitter() def horizontalSplitterMoved(self, x, y): if self.splitHorizontal1.count() != 2 or self.splitHorizontal2.count( ) != 2: return sizes = self.splitHorizontal1.sizes() #What. Nr2 if self.splitHorizontal2.closestLegalPosition( x, y) < self.splitHorizontal2.closestLegalPosition(x, y): sizeLeft = self.splitHorizontal1.closestLegalPosition(x, y) else: sizeLeft = self.splitHorizontal2.closestLegalPosition(x, y) sizeRight = sizes[0] + sizes[1] - sizeLeft sizes = [sizeLeft, sizeRight] self.splitHorizontal1.setSizes(sizes) self.splitHorizontal2.setSizes(sizes) def addStatusBar(self, bar): self.statusBar = bar self.layout.addLayout(self.statusBar) def setGrayScaleToQuadStatusBar(self, gray): self.quadViewStatusBar.setGrayScale(gray) def setMouseCoordsToQuadStatusBar(self, x, y, z): self.quadViewStatusBar.setMouseCoords(x, y, z) def ensureMaximized(self, axis): """ Maximize the view for the given axis if it isn't already maximized. """ axisDict = { 0: self.dock2_ofSplitHorizontal1, # x 1: self.dock1_ofSplitHorizontal2, # y 2: self.dock1_ofSplitHorizontal1 } # z if not axisDict[axis]._isMaximized: self.switchMinMax(axis) def ensureMinimized(self, axis): """ Minimize the view for the given axis if it isn't already minimized. """ axisDict = { 0: self.dock2_ofSplitHorizontal1, # x 1: self.dock1_ofSplitHorizontal2, # y 2: self.dock1_ofSplitHorizontal1 } # z if axisDict[axis]._isMaximized: self.switchMinMax(axis) def switchMinMax(self, axis): """Switch an AxisViewWidget between from minimized to maximized and vice versa. Keyword arguments: axis -- the axis which is represented by the widget (no default) either string or integer 'x' - 0 'y' - 1 'z' - 2 """ #TODO: get the mapping information from where it is set! if this is not #done properly - do it properly if type(axis) == str: axisDict = { 'x': self.dock2_ofSplitHorizontal1, # x 'y': self.dock1_ofSplitHorizontal2, # y 'z': self.dock1_ofSplitHorizontal1 } # z elif type(axis) == int: axisDict = { 0: self.dock2_ofSplitHorizontal1, # x 1: self.dock1_ofSplitHorizontal2, # y 2: self.dock1_ofSplitHorizontal1 } # z dockWidget = axisDict.pop(axis) for dWidget in list(axisDict.values()): if dWidget._isMaximized: dWidget.graphicsView._hud.maximizeButtonClicked.emit() dockWidget.graphicsView._hud.maximizeButtonClicked.emit() def switchXMinMax(self): self.switchMinMax('x') def switchYMinMax(self): self.switchMinMax('y') def switchZMinMax(self): self.switchMinMax('z') def on_dock(self, dockWidget): if dockWidget._isDocked: dockWidget.undockView() self.on_min(dockWidget) dockWidget.minimizeView() else: dockWidget.dockView() def on_max(self, dockWidget): dockWidget.setVisible(True) for dock in self.dockableContainer: if not dockWidget == dock: dock.setVisible(False) # Force sizes to be updated now QApplication.processEvents() # On linux, the vertical splitter doesn't seem to refresh unless we do so manually # Presumably, this is a QT bug. self.splitVertical.refresh() # Viewport doesn't update automatically... view = dockWidget.graphicsView view.viewport().setGeometry(view.rect()) def on_min(self, dockWidget): for dock in self.dockableContainer: dock.setVisible(True) # Force sizes to be updated now QApplication.processEvents() self._resizeEqual() # Viewports don't update automatically... for dock in self.dockableContainer: view = dock.graphicsView if hasattr(view, 'viewport'): view.viewport().setGeometry(view.rect())
class MainWindow(QMainWindow): KEY_WINDOW_SIZE = 'main_window/size' KEY_WINDOW_MAXIMIZED = 'main_window/maximized' KEY_WINDOW_POSITION = 'main_window/position' KEY_H_SPLITTER_STATE = 'main_window/h_splitter_state' def __init__(self): super(MainWindow, self).__init__() self.setWindowTitle('Mojuru') app_icon = QIcon('images/mojuru_logo.png') self.setWindowIcon(app_icon) reload_modules_action = QAction('Reload MainWindow', self) reload_modules_action.setShortcut('ctrl+shift+alt+r') reload_modules_action.triggered.connect(self.reload_central_widget) self.addAction(reload_modules_action) quit_action = QAction('Quit', self) quit_action.setShortcut('ctrl+q') quit_action.triggered.connect(self.on_quit) self.addAction(quit_action) self.vertical_splitter = QSplitter(Qt.Vertical, self) self.setCentralWidget(self.vertical_splitter) self.load_central_widget() self.file_menu = self.menuBar().addMenu(self.tr('&File')) self.file_menu.addAction(quit_action) self.file_menu.addSeparator() self.module_menu = self.menuBar().addMenu(self.tr('&Modules')) self.module_menu.addAction(reload_modules_action) self.module_menu.addSeparator() Alter.invoke_all('main_window_init', self) #restore main window state size = ModuleManager.core['settings'].Settings.value( self.KEY_WINDOW_SIZE, QSize(600, 400)) maximized = ModuleManager.core['settings'].Settings.value( self.KEY_WINDOW_MAXIMIZED, False) position = ModuleManager.core['settings'].Settings.value( self.KEY_WINDOW_POSITION, QPoint(0,0)) if maximized == 'true': self.showMaximized() else: self.resize(size) self.move(position) def closeEvent(self, event): self.on_quit() def save_state(self): ModuleManager.core['settings'].Settings.set_value( self.KEY_WINDOW_SIZE, self.size()) ModuleManager.core['settings'].Settings.set_value( self.KEY_WINDOW_MAXIMIZED, self.isMaximized()) ModuleManager.core['settings'].Settings.set_value( self.KEY_WINDOW_POSITION, self.pos()) ModuleManager.core['settings'].Settings.set_value( self.KEY_H_SPLITTER_STATE, self.horizontal_splitter.saveState()) def on_quit(self): self.save_state() self.close() def load_central_widget(self): self.populate_central_widget() self.connect_widgets() def populate_central_widget(self): self.vertical_widgets = collections.OrderedDict() self.horizontal_splitter = QSplitter( Qt.Horizontal, self.vertical_splitter) self.horizontal_widgets = collections.OrderedDict() self.vertical_widgets["horizontal_splitter"] = self.horizontal_splitter Alter.invoke_all( 'main_window_add_vertical_widget', self.vertical_widgets, self ) for widget in self.vertical_widgets.values(): self.vertical_splitter.addWidget(widget) Alter.invoke_all( 'main_window_add_horizontal_widget', self.horizontal_widgets, self.vertical_splitter ) for widget in self.horizontal_widgets.values(): self.horizontal_splitter.addWidget(widget) #restore horizontal splitter state state = ModuleManager.core['settings'].Settings.value( self.KEY_H_SPLITTER_STATE, None ) if state: self.horizontal_splitter.restoreState(state) def connect_widgets(self): Alter.invoke_all( 'main_window_connect_widgets', self.vertical_widgets, self.horizontal_widgets ) def reload_central_widget(self): self.save_state() for index in range(self.vertical_splitter.count()): widget = self.vertical_splitter.widget(index) widget.hide() widget.setParent(None) del widget Alter.clear() ModuleManager.reload_all_modules('core') ModuleManager.reload_all_modules('custom') self.load_central_widget()
class MSplitter(QFrame, MHierarchicalElement): VERTICAL = 'vertical' HORIZONTAL = 'horizontal' def __init__(self, parent_window): super().__init__() self.setObjectName("main_frame") # Construct top-level window elements self.main_layout = QVBoxLayout() self.setLayout(self.main_layout) self.parent_container = None #self.set_parent_he(parent_window) self.main_splitter = QSplitter() self.main_splitter.setObjectName("main_splitter") self.main_splitter.show() self.main_layout.setContentsMargins(0, 0, 0, 0) #self.header_frame = MHeaderBar(self) #self.main_layout.addWidget(self.header_frame) self.content = self.main_splitter self.main_layout.addWidget(self.main_splitter) self.show() self.setStyleSheet( self.styleSheet() + "QFrame#main_frame{background-color:rgb(200,200,200)}\r\n" "QSplitter::handle#main_splitter" "{" " border: 2px solid rgb(50,50,50);" " background-color:rgb(100,100,100)" "}" "QSplitter::handle:pressed#main_splitter" "{" " border: 2px solid rgb(100,100,100);" " background-color:rgb(200,100,20)" "}") self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) self.orientation = None def set_sizes(self, one, two): self.main_splitter.setSizes([one, two]) def get_sizes(self): return self.main_splitter.sizes() def add_content(self, container, location=None): # if not (type(container) is MContainer): # raise TypeError("Expected type %s, got %s" % (str(MWindow), type(container))) if location is None: self.main_splitter.addWidget(container) elif location is "top": self.main_splitter.setOrientation(Qt.Vertical) self.main_splitter.insertWidget(0, container) self.orientation = self.VERTICAL elif location is "left": self.main_splitter.setOrientation(Qt.Horizontal) self.main_splitter.insertWidget(0, container) self.orientation = self.HORIZONTAL elif location is "right": self.main_splitter.setOrientation(Qt.Horizontal) self.main_splitter.insertWidget(1, container) self.orientation = self.HORIZONTAL elif location is "bottom": self.main_splitter.setOrientation(Qt.Vertical) self.main_splitter.insertWidget(1, container) self.orientation = self.VERTICAL container.set_parent_he(self.get_parent_he()) self.updateGeometry() def get_orientation(self): return self.orientation def get_position(self): return self.main_splitter.sizes() def get_num_widgets(self): return self.main_splitter.count() def get_item_at(self, index): return self.main_splitter.widget(index) # # def get_parent_container(self): # return self.parent_container # # def set_parent_container(self, win): # # if type(win) is MWindow or win is None: # # # Remove self from old parent # if self.parent_container is not None: # self.parent_container._remove_child_container(self) # # # Add self to new parent # if (win is not None): # win._add_child_container(self) # # # Set local reference to parent # self.parent_container = win # # else: # raise TypeError("Parent window must be type %s, not %s" % (str(type(MWindow)), str(type(win)))) def show_drop_regions(self): pass def hide_drop_regions(self): pass
class CodecTab(QScrollArea): # BUG: codec_frame should have height 210 but has 480. # WORKAROUND: manually set height to 210 height. # SEE: https://forum.qt.io/topic/42055/qwidget-height-returns-incorrect-value-in-5-3/7 FRAME_HEIGHT = 210 def __init__(self, parent, context, commands): super(QWidget, self).__init__(parent) self._context = context self._logger = context.logger() self._commands = commands self._next_frame_id = 1 self._frames = QSplitter(Qt.Vertical) self._frames.setChildrenCollapsible(False) self._frames.setSizePolicy( QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)) self._frames.setContentsMargins(0, 0, 0, 0) self._main_frame = QFrame(self) self._main_frame_layout = QVBoxLayout() self._main_frame_layout.addWidget(self._frames) self._main_frame_layout.addWidget(VSpacer(self)) self._main_frame.setLayout(self._main_frame_layout) self.newFrame("", "") self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn) self.setWidgetResizable(True) self.setWidget(self._main_frame) def newFrame(self, text, title, previous_frame=None, status=None, msg=None): try: # BUG: Setting complex default values is not possible in python # WORKAROUND: Set default value to None and set real default later. if status is None: status = StatusWidget.DEFAULT if previous_frame and previous_frame.hasNext(): next_frame = previous_frame.next() next_frame.setTitle(title) finished = False if status == StatusWidget.ERROR: while not finished: next_frame.flashStatus(status, msg) # Display error only for the first frame. msg = None finished = not next_frame.hasNext() next_frame = next_frame.next() else: next_frame.setInputText(text, msg is not None and len(msg) > 0) next_frame.flashStatus(status, msg) previous_frame.focusInputText() else: new_frame = CodecFrame(self, self._context, self._next_frame_id, self, self._commands, previous_frame, text) self._next_frame_id += 1 if self._frames.count() > 0: new_frame.flashStatus(status, msg) new_frame.setTitle(title) new_frame.setContentsMargins(0, 0, 0, 0) new_frame.layout().setContentsMargins(0, 0, 0, 0) self._frames.addWidget(new_frame) # BUG: QSplitter does not allow frames to be wider than the surrounding area (here: QScrollArea). # WORKAROUND: Set a fixed size for codec frames and disable handles which prevents users from # trying to resize the codec frames. new_frame.setFixedHeight(self.FRAME_HEIGHT) self._frames.handle(self._frames.count() - 1).setEnabled(False) if previous_frame: previous_frame.focusInputText() else: new_frame.focusInputText() except Exception as e: self._logger.error("Unknown error: {}".format(str(e))) def removeFrames(self, frame): if frame: if frame.previous(): frame.previous().setNext(None) frames_to_remove = [frame] while frame.next(): frames_to_remove.append(frame.next()) frame = frame.next() for frame_to_remove in reversed(frames_to_remove): frame_to_remove.deleteLater() def getFocussedFrame(self): widget = self._frames.focusWidget() while widget: if isinstance(widget, CodecFrame): return widget widget = widget.parent() return self._frames.widget(0)
class AnalysisWindow(QMainWindow): """ The main window for analysing and processing data. Attributes ---------- cs : :class:`~cued_datalogger.api.channel.ChannelSet` The ChannelSet containing all the data. Data is accessed through this ChannelSet. menubar : :class:`PyQt5.QtWidgets.QMenuBar` toolbox : :class:`~cued_datalogger.api.toolbox.MasterToolbox` The widget containing local tools and operations. Contains four toolboxes: :attr:`time_toolbox`, :attr:`frequency_toolbox`, :attr:`sonogram_toolbox`, :attr:`circle_fit_toolbox`. time_toolbox : :class:`~cued_datalogger.analysis.time_domain.TimeToolbox` frequency_toolbox : :class:`~cued_datalogger.analysis.frequency_domain.FrequencyToolbox` sonogram_toolbox : :class:`~cued_datalogger.analysis.sonogram.SonogramToolbox` circle_fit_toolbox : :class:`~cued_datalogger.analysis.circle_fit.CircleFitToolbox` display_tabwidget : :class:`~cued_datalogger.analysis_window.AnalysisDisplayTabWidget` The central widget for display. freqdomain_widget : :class`~cued_datalogger.analysis.frequency_domain.FrequencyDomainWidget` sonogram_widget : :class:`~cued_datalogger.analysis.sonogram.SonogramDisplayWidget` circle_widget : :class:`~cued_datalogger.analysis.circle_fit.CircleFitWidget` global_master_toolbox : :class:`~cued_datalogger.api.toolbox.MasterToolbox` The master toolbox containing the :attr:`global_toolbox`. global_toolbox : :class:`~cued_datalogger.api.toolbox.Toolbox` The widget containing global tools and operations. Has five tabs, containing: <acquisition window launcher>, :attr:`channel_select_widget`, :attr:`channel_metadata_widget`, :attr:`addon_widget`, :attr:`import_widget`. acquisition_window : :class:`~cued_datalogger.acquisition_window.AcquisitionWindow` channel_select_widget : :class:`~cued_datalogger.api.channel.ChannelSelectWidget` channel_metadata_widget : :class:`~cued_datalogger.api.channel.ChannelMetadataWidget` addon_widget : :class:`~cued_datalogger.api.addons.AddonManager` import_widget : :class:`~cued_datalogger.api.file_import.DataImportWidget` """ def __init__(self): super().__init__() self.setWindowTitle('AnalysisWindow') self.create_test_channelset() self._init_ui() self.setFocus() self.showMaximized() def _init_ui(self): # Add the drop-down menu self.menubar = self.menuBar() self.menubar.addMenu(ProjectMenu(self)) # # Create the main widget self.splitter = QSplitter(self) self.splitter.setHandleWidth(5) self.splitter.setChildrenCollapsible(False) self.setCentralWidget(self.splitter) # Create the analysis tools tab widget self._init_display_tabwidget() # Create the toolbox self._init_toolbox() self.display_tabwidget.currentChanged.connect(self.toolbox.set_toolbox) self.display_tabwidget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) # Create the global toolbox self._init_global_master_toolbox() # Configure self.update_channelset() self.goto_time_series() # Add the widgets self.splitter.addWidget(self.toolbox) self.splitter.addWidget(self.display_tabwidget) self.splitter.addWidget(self.global_master_toolbox) def _init_display_tabwidget(self): """Create the display tabwidget.""" self.display_tabwidget = QTabWidget(self) self.timedomain_widget = TimeDomainWidget() self.freqdomain_widget = FrequencyDomainWidget() self.sonogram_widget = SonogramDisplayWidget() self.circle_widget = CircleFitWidget() # Create the tabs self.display_tabwidget.addTab(self.timedomain_widget, "Time Domain") self.display_tabwidget.addTab(self.freqdomain_widget, "Frequency Domain") self.display_tabwidget.addTab(self.sonogram_widget, "Sonogram") self.display_tabwidget.addTab(self.circle_widget, "Circle Fit") self.display_tabwidget.currentChanged.connect( lambda: self.set_selected_channels(self.channel_select_widget. selected_channels())) def _init_toolbox(self): """Create the master toolbox""" self.toolbox = MasterToolbox(self) self.toolbox.sig_collapsed_changed.connect(self._update_splitter) # # Time toolbox self.time_toolbox = TimeToolbox(self.toolbox) self.time_toolbox.sig_convert_to_fft.connect( self.goto_frequency_spectrum) self.time_toolbox.sig_convert_to_sonogram.connect(self.goto_sonogram) # # Frequency toolbox self.frequency_toolbox = FrequencyToolbox(self.toolbox) self.frequency_toolbox.sig_calculate_transfer_function.connect( self.freqdomain_widget.calculate_transfer_function) self.frequency_toolbox.sig_convert_to_circle_fit.connect( self.goto_circle_fit) self.frequency_toolbox.sig_plot_frequency_spectrum.connect( lambda: self.freqdomain_widget.update_plot(False)) self.frequency_toolbox.sig_plot_transfer_function.connect( lambda: self.freqdomain_widget.update_plot(True)) self.frequency_toolbox.sig_plot_type_changed.connect( self.freqdomain_widget.set_plot_type) self.frequency_toolbox.sig_show_coherence.connect( self.freqdomain_widget.set_show_coherence) # # Sonogram toolbox self.sonogram_toolbox = SonogramToolbox(self.toolbox) self.sonogram_toolbox.sig_contour_spacing_changed.connect( self.sonogram_widget.update_contour_spacing) self.sonogram_toolbox.sig_num_contours_changed.connect( self.sonogram_widget.update_num_contours) self.sonogram_toolbox.sig_window_overlap_fraction_changed.connect( self.sonogram_widget.update_window_overlap_fraction) self.sonogram_toolbox.sig_window_width_changed.connect( self.sonogram_widget.update_window_width) # # Circle Fit toolbox self.circle_fit_toolbox = CircleFitToolbox(self.toolbox) self.circle_fit_toolbox.sig_show_transfer_fn.connect( self.circle_widget.show_transfer_fn) self.circle_fit_toolbox.sig_construct_transfer_fn.connect( self.circle_widget.construct_transfer_fn) self.toolbox.add_toolbox(self.time_toolbox) self.toolbox.add_toolbox(self.frequency_toolbox) self.toolbox.add_toolbox(self.sonogram_toolbox) self.toolbox.add_toolbox(self.circle_fit_toolbox) self.toolbox.set_toolbox(0) def _init_global_master_toolbox(self): self.global_master_toolbox = MasterToolbox() self.global_master_toolbox.sig_collapsed_changed.connect( self._update_splitter) self.global_toolbox = Toolbox('right', self.global_master_toolbox) # # Acquisition Window self.acquisition_window = None self.device_config = DevConfigUI() self.device_config.config_button.setText('Open AcquisitionWindow') self.device_config.config_button.clicked.connect( self.open_acquisition_window) self.global_toolbox.addTab(self.device_config, 'Acquisition Window') # # Channel Selection self.channel_select_widget = ChannelSelectWidget(self.global_toolbox) self.channel_select_widget.sig_channel_selection_changed.connect( self.set_selected_channels) self.global_toolbox.addTab(self.channel_select_widget, 'Channel Selection') # # Channel Metadata self.channel_metadata_widget = ChannelMetadataWidget( self.global_toolbox) self.global_toolbox.addTab(self.channel_metadata_widget, 'Channel Metadata') self.channel_metadata_widget.metadataChange.connect( self.update_channelset) # # Addon Manager self.addon_widget = AddonManager(self) self.global_toolbox.addTab(self.addon_widget, 'Addon Manager') # # Import self.import_widget = DataImportWidget(self) self.import_widget.sig_extend_channelset.connect( self.extend_channelset) self.import_widget.sig_replace_channelset.connect( self.replace_channelset) self.global_toolbox.addTab(self.import_widget, 'Import Files') # # Export self.export_widget = DataExportWidget(self) self.global_toolbox.addTab(self.export_widget, 'Export Files') self.global_master_toolbox.add_toolbox(self.global_toolbox) def _update_splitter(self): # Get the current sizes of everything sizes = self.splitter.sizes() # Adjust the size of the sender to be equal to its size hint for i in range(self.splitter.count()): if self.sender() == self.splitter.widget(i): sizes[i] = self.sender().sizeHint().width() # Set the sizes self.splitter.setSizes(sizes) self.splitter.setStretchFactor(0, 0) self.splitter.setStretchFactor(1, 1) self.splitter.setStretchFactor(2, 0) #---------------------- Acquisition window methods ------------------------ def open_acquisition_window(self): if not self.acquisition_window: recType, configs = self.device_config.read_device_config() if not any([c is None for c in configs]): self.acquisition_window = AcquisitionWindow( self, recType, configs) else: self.acquisition_window = AcquisitionWindow(self) self.acquisition_window.sig_closed.connect( self.close_acquisition_window) self.acquisition_window.sig_transfer_function_data_saved.connect( self.receive_data) self.acquisition_window.sig_time_series_data_saved.connect( self.receive_data) self.acquisition_window.show() def receive_data(self, received_cs): self.replace_channelset(received_cs) if self.cs.channels[0].is_dataset("transfer_function"): self.goto_transfer_function() else: self.goto_time_series() def auto_change_tab(self): current_widget = self.display_tabwidget.currentWidget() if (current_widget is self.circle_widget and self.cs.channels[0].is_dataset("transfer_function")): # Remain here pass elif (current_widget is self.sonogram_widget and self.cs.channels[0].is_dataset("sonogram")): # Remain here pass elif (current_widget is self.freqdomain_widget and self.cs.channels[0].is_dataset("spectrum")): # Remain here # Switch the plot type self.frequency_toolbox.set_plot_spectrum() elif (current_widget is self.freqdomain_widget and self.cs.channels[0].is_dataset("transfer_function")): # Remain here # Switch the plot type self.frequency_toolbox.set_plot_transfer_function() else: if self.cs.channels[0].is_dataset("transfer_function"): self.display_tabwidget.setCurrentWidget(self.freqdomain_widget) self.frequency_toolbox.set_plot_transfer_function() elif self.cs.channels[0].is_dataset("spectrum"): self.display_tabwidget.setCurrentWidget(self.freqdomain_widget) self.frequency_toolbox.set_plot_spectrum() elif self.cs.channels[0].is_dataset("sonogram"): self.display_tabwidget.setCurrentWidget(self.sonogram_widget) else: self.display_tabwidget.setCurrentWidget(self.timedomain_widget) def close_acquisition_window(self): self.acquisition_window.sig_closed.disconnect() self.acquisition_window = None #---------------------------- Tab methods --------------------------------- def goto_time_series(self, switch_to_tab=True): if switch_to_tab: self.display_tabwidget.setCurrentWidget(self.timedomain_widget) self.timedomain_widget.set_selected_channels( self.channel_select_widget.selected_channels()) def goto_frequency_spectrum(self, switch_to_tab=True): if switch_to_tab: # Switch to frequency domain tab self.display_tabwidget.setCurrentWidget(self.freqdomain_widget) self.freqdomain_widget.set_selected_channels( self.channel_select_widget.selected_channels()) self.freqdomain_widget.calculate_spectrum() self.frequency_toolbox.set_plot_spectrum() def goto_transfer_function(self, switch_to_tab=True): if switch_to_tab: self.display_tabwidget.setCurrentWidget(self.freqdomain_widget) self.freqdomain_widget.set_selected_channels( self.channel_select_widget.selected_channels()) # TODO: calculate TF function if none is found self.freqdomain_widget.calculate_transfer_function() self.frequency_toolbox.set_plot_transfer_function() def goto_sonogram(self, switch_to_tab=True): if switch_to_tab: self.display_tabwidget.setCurrentWidget(self.sonogram_widget) self.sonogram_widget.set_selected_channels( self.channel_select_widget.selected_channels()) self.sonogram_widget.calculate_sonogram() def goto_circle_fit(self, switch_to_tab=True): if switch_to_tab: self.display_tabwidget.setCurrentWidget(self.circle_widget) self.circle_widget.set_selected_channels( self.channel_select_widget.selected_channels()) #------------------ ChannelSet methods ------------------------------------ def create_test_channelset(self): self.cs = ChannelSet(5) t = np.arange(0, 0.5, 1 / 5000) for i, channel in enumerate(self.cs.channels): self.cs.set_channel_metadata(i, {'sample_rate': 5000}) self.cs.add_channel_dataset(i, 'time_series', np.sin(t * 2 * np.pi * 100 * (i + 1))) channel.name = "Channel {}".format(i) self.cs.add_channel_dataset( i, 'time_series', np.sin(t * 2 * np.pi * 100 * (i + 1)) * np.exp(-t / t[-1])) def extend_channelset(self, cs): if isinstance(cs, ChannelSet): self.cs.channels.extend(cs.channels) self.update_channelset() self.auto_change_tab() else: print("Failed to extend ChannelSet: {} not a ChannelSet".format( type(cs))) def replace_channelset(self, cs): if isinstance(cs, ChannelSet): self.cs = cs self.update_channelset() self.auto_change_tab() else: print("Failed to replace ChannelSet: {} not a ChannelSet".format( type(cs))) def set_selected_channels(self, channels): self.display_tabwidget.currentWidget().set_selected_channels(channels) if self.display_tabwidget.currentWidget() is self.sonogram_widget: self.sonogram_toolbox.set_selected_channels(channels) def update_channelset(self): self.channel_select_widget.set_channel_set(self.cs) self.channel_metadata_widget.set_channel_set(self.cs) self.export_widget.set_channel_set(self.cs)
class MainWindow(QMainWindow): def __init__(self, *args): QMainWindow.__init__(self, *args) self.setWindowIcon(QIcon(str(images_path.joinpath('qtifm512.png')))) # load config self.config = Config() self.config.load() self.move(self.config.mainwindow_x, self.config.mainwindow_y) self.resize(self.config.mainwindow_witdh, self.config.mainwindow_height) # Actions self.exit_action = QAction(_('Exit'), self) self.exit_action.setMenuRole(QAction.QuitRole) self.exit_action.setShortcut('Ctrl+Q') self.about_action = QAction(_('About'), self) self.about_action.setMenuRole(QAction.AboutRole) self.new_action = QAction(QIcon.fromTheme('document-new'), _('New')) self.new_action.setShortcut('Ctrl+N') self.open_action = QAction(QIcon.fromTheme('document-open'), _('Open...')) self.open_action.setShortcut('Ctrl+O') self.save_action = QAction(QIcon.fromTheme('document-save'), _('Save')) self.save_action.setShortcut('Ctrl+S') self.saveas_action = QAction(QIcon.fromTheme('document-save-as'), _('Save As...')) self.saveas_action.setShortcut('Shift+Ctrl+S') self.clear_recent_files_action = QAction(_('Clear Items')) self.settings_action = QAction(_('Settings')) self.find_next_action = QAction(QIcon.fromTheme('down'), _('Find Next')) self.find_next_action.setShortcut('F3') self.find_previous_action = QAction(QIcon.fromTheme('up'), _('Find Previous')) self.find_previous_action.setShortcut('Ctrl+F3') self.normal_size_action = QAction(QIcon.fromTheme('zoom-original'), _('Normal Size')) self.normal_size_action.setShortcut('Ctrl+0') self.zoom_in_action = QAction(QIcon.fromTheme('zoom-in'), _('Zoom In')) self.zoom_in_action.setShortcut('Ctrl++') self.zoom_out_action = QAction(QIcon.fromTheme('zoom-out'), _('Zoom Out')) self.zoom_out_action.setShortcut('Ctrl+-') # Menu Bar file_menu = self.menuBar().addMenu(_('File')) file_menu.addAction(self.new_action) file_menu.addAction(self.open_action) self.recent_files_menu = file_menu.addMenu(_('Open recent')) file_menu.addAction(self.save_action) file_menu.addAction(self.saveas_action) file_menu.addSeparator() file_menu.addAction(self.settings_action) file_menu.addSeparator() file_menu.addAction(self.exit_action) help_menu = self.menuBar().addMenu(_('Help')) help_menu.addAction(self.about_action) # Widgets self.splitter = QSplitter(Qt.Horizontal) self.splitter.setHandleWidth(5) self.editor = Editor(self, self.config.editor_dark_theme) self.map_view = MapView(self, self.config) self.find_edit = QLineEdit() self.find_edit.setFixedWidth(200) # Tool bar tool_bar = self.addToolBar('Edit') tool_bar.setFloatable(False) tool_bar.setMovable(False) tool_bar.addAction(self.new_action) tool_bar.addAction(self.open_action) tool_bar.addAction(self.save_action) tool_bar.addAction(self.saveas_action) tool_bar.addSeparator() tool_bar.addWidget(QLabel(_('Find:') + ' ')) tool_bar.addWidget(self.find_edit) tool_bar.addAction(self.find_next_action) tool_bar.addAction(self.find_previous_action) tool_bar.addSeparator() tool_bar.addAction(self.normal_size_action) tool_bar.addAction(self.zoom_in_action) tool_bar.addAction(self.zoom_out_action) # Connects self.new_action.triggered.connect(self.editor.new_file) self.open_action.triggered.connect(self.editor.open_file) self.save_action.triggered.connect(self.editor.save_file) self.saveas_action.triggered.connect(self.editor.save_file_as) self.about_action.triggered.connect(self.show_about_dialog) self.clear_recent_files_action.triggered.connect( self.editor.clear_recent_files) self.settings_action.triggered.connect(self.show_settings) self.exit_action.triggered.connect(self.close) self.normal_size_action.triggered.connect(self.map_view.normal_size) self.zoom_in_action.triggered.connect(self.map_view.zoom_in) self.zoom_out_action.triggered.connect(self.map_view.zoom_out) self.find_next_action.triggered.connect(self.find_next) self.find_previous_action.triggered.connect(self.find_previous) self.editor.map_changed_signal.connect(self.map_view.create_maps) self.editor.map_cleared_signal.connect(self.map_view.clear_maps) self.map_view.map_view_changed_signal.connect(self.enable_map_actions) self.find_edit.textChanged.connect(self.find_edit_text_changed) self.find_edit.returnPressed.connect(self.find_next) # Layout central_widget = QWidget() self.setCentralWidget(central_widget) central_layout = QVBoxLayout() central_layout.setContentsMargins(0, 0, 0, 0) # left, top, right, bottom central_widget.setLayout(central_layout) central_layout.addWidget(self.splitter) self.statusBar().setSizeGripEnabled(False) self.statusBar().addWidget(self.editor.cursor_position_label, 1) self.statusBar().addWidget(self.editor.editor_modified_label) self.statusBar().addWidget(self.map_view.zoom_factor_label) self.splitter.addWidget(self.editor) self.splitter.addWidget(self.map_view) if len(self.config.mainwindow_splitter_sizes) > 0: self.splitter.setSizes(self.config.mainwindow_splitter_sizes) if self.config.editor_last_file is not None: self.editor.open_path(self.config.editor_last_file, check_modified=False) self.find_next_action.setEnabled(False) self.find_previous_action.setEnabled(False) @pyqtSlot() def enable_map_actions(self): self.zoom_in_action.setEnabled(self.map_view.zoom_in_allowed()) self.zoom_out_action.setEnabled(self.map_view.zoom_out_allowed()) self.normal_size_action.setEnabled(self.map_view.valid) self.map_view.update_zoom_factor_status() @pyqtSlot() def show_about_dialog(self): dialog = AboutDialog(self) dialog.exec_() @pyqtSlot() def show_settings(self): dialog = SettingsDialog(self) dialog.ifm_command_edit.setText(self.config.map_ifm_command) dialog.fig2dev_command_edit.setText(self.config.map_fig2dev_command) dialog.magnifcation_factor_edit.setValue( self.config.map_fig2dev_magnification_factor) dialog.dark_theme_check.setChecked(self.config.editor_dark_theme) dialog.helvetica_check.setChecked( self.config.map_ifm_helvetica_as_default) dialog.image_per_map_check.setChecked( self.config.map_ifm_create_image_per_map) dark_theme = self.config.editor_dark_theme result = dialog.exec_() if result == QDialog.Accepted: self.config.map_ifm_command = dialog.ifm_command_edit.text().strip( ) self.config.map_fig2dev_command = dialog.fig2dev_command_edit.text( ).strip() self.config.map_fig2dev_magnification_factor = dialog.magnifcation_factor_edit.value( ) self.config.editor_dark_theme = dialog.dark_theme_check.isChecked() self.config.map_ifm_helvetica_as_default = dialog.helvetica_check.isChecked( ) self.config.map_ifm_create_image_per_map = dialog.image_per_map_check.isChecked( ) if self.config.editor_dark_theme != dark_theme: self.editor.reset_highlighter(self.config.editor_dark_theme) @pyqtSlot() def find_edit_text_changed(self): flag = len(self.find_edit.text()) > 0 self.find_next_action.setEnabled(flag) self.find_previous_action.setEnabled(flag) @pyqtSlot() def find_next(self): text = self.find_edit.text() if len(text) > 0: self.editor.find(text) @pyqtSlot() def find_previous(self): text = self.find_edit.text() if len(text) > 0: self.editor.find(text, QTextDocument.FindBackward) def closeEvent(self, event): if self.editor.abort_if_modified(_('Exit')): event.ignore() else: event.accept() self.config.mainwindow_witdh = self.width() self.config.mainwindow_height = self.height() self.config.mainwindow_x = self.x() self.config.mainwindow_y = self.y() self.config.mainwindow_splitter_sizes = [] for i in range(0, self.splitter.count()): self.config.mainwindow_splitter_sizes.append( self.splitter.sizes()[i]) self.config.editor_last_file = self.editor.current_file self.config.save()
class PanelContainer(QWidget): def __init__(self, panelWin): super(QWidget, self).__init__() self.panelWindow = panelWin self.panelCount = 0 self.mainLayout = QGridLayout(self) self.mainLayout.setContentsMargins(0, 0, 0, 0) self.splitter = QSplitter(self) self.containerParent = 0 self.mainLayout.addWidget(self.splitter, 0, 0) def splitter(self): return self.splitter def addPanel(self, panel=None): if panel: panel.setParent(self) panel.setContainer(self) self.splitter.addWidget(panel) self.panelCount += 1 self.updatePanelSignals(panel) else: panel = self.createPanel() self.splitter.addWidget(panel) return panel def addPanelSplit(self, panel, direction): panel = PanelWidget(self) if 0 else panel # Store original size origSize = panel.size() # reparent the panel panel.setParent(self) panel.setContainer(self) # set orientation and add the panel self.splitter.setOrientation(direction) self.splitter.addWidget(panel) # add another panel for split panel = self.createPanel() self.splitter.addWidget(panel) sizes = list() origSize *= 0.5 if direction == Qt.Horizontal: sizes.append(origSize.width()) sizes.append(origSize.width()) else: sizes.append(origSize.height()) sizes.append(origSize.height()) self.splitter.setSizes(sizes) self.panelCount += 1 def addContainer(self, child): child = PanelContainer(self) if 0 else child self.splitter.addWidget(child) child.setParentContainer(self) def insertContainer(self, child, index): child = PanelContainer(self) if 0 else child self.splitter.insertWidget(index, child) child.setParentContainer(self) def setParentContainer(self, parent): self.containerParent = parent def parentContainer(self): return self.containerParent def childContainers(self): # childContainers = list() # for index in range(0, self.splitter.count()): # container = self.splitter.widget(index) # if container: # childContainers.append(container) return self.sortedChildren() def panels(self): # panels = list() # for index in range(0, self.splitter.count()): # panel = self.splitter.widget(index) # if panel: # panels.append(panel) return self.sortedChildren() def sortedChildren(self): return (self.splitter.widget(index) for index in range(self.splitter.count())) def numberOfPanels(self): return self.panelCount def createPanel(self): panel = PanelWidget(self) panel.createMenu(WorkbenchWidget.panelNames()) self.connectPanelSignals(panel) self.panelCount += 1 return panel def connectPanelSignals(self, panel): panel = PanelWidget(self) if 0 else panel panel.panelSplit.connect(lambda: self.panelWindow.splitPanel()) panel.panelFloat.connect(lambda : self.panelWindow.floatPanel()) panel.panelClosed.connect(lambda : self.panelWindow.closePanel()) panel.panelMenuTriggered.connect(lambda : self.panelWindow.closePanel()) panel.tabClosed.connect(lambda : self.panelWindow.closePanel()) def updatePanelSignals(self, panel): panel = PanelWidget(self) if 0 else panel panel.panelSplit.disconnect() panel.panelFloat.disconnect() panel.panelClosed.disconnect() panel.panelMenuTriggered.disconnect() panel.tabClosed.disconnect()
class MainWindow(QMainWindow): KEY_WINDOW_SIZE = 'main_window/size' KEY_WINDOW_MAXIMIZED = 'main_window/maximized' KEY_WINDOW_POSITION = 'main_window/position' KEY_H_SPLITTER_STATE = 'main_window/h_splitter_state' def __init__(self): super(MainWindow, self).__init__() self.setWindowTitle('Mojuru') app_icon = QIcon('images/mojuru_logo.png') self.setWindowIcon(app_icon) reload_modules_action = QAction('Reload MainWindow', self) reload_modules_action.setShortcut('ctrl+shift+alt+r') reload_modules_action.triggered.connect(self.reload_central_widget) self.addAction(reload_modules_action) quit_action = QAction('Quit', self) quit_action.setShortcut('ctrl+q') quit_action.triggered.connect(self.on_quit) self.addAction(quit_action) self.vertical_splitter = QSplitter(Qt.Vertical, self) self.setCentralWidget(self.vertical_splitter) self.load_central_widget() self.file_menu = self.menuBar().addMenu(self.tr('&File')) self.file_menu.addAction(quit_action) self.file_menu.addSeparator() self.module_menu = self.menuBar().addMenu(self.tr('&Modules')) self.module_menu.addAction(reload_modules_action) self.module_menu.addSeparator() Alter.invoke_all('main_window_init', self) #restore main window state size = ModuleManager.core['settings'].Settings.value( self.KEY_WINDOW_SIZE, QSize(600, 400)) maximized = ModuleManager.core['settings'].Settings.value( self.KEY_WINDOW_MAXIMIZED, False) position = ModuleManager.core['settings'].Settings.value( self.KEY_WINDOW_POSITION, QPoint(0,0)) if maximized == 'true': self.showMaximized() else: self.resize(size) self.move(position) def closeEvent(self, event): self.on_quit() def save_state(self): ModuleManager.core['settings'].Settings.set_value( self.KEY_WINDOW_SIZE, self.size()) ModuleManager.core['settings'].Settings.set_value( self.KEY_WINDOW_MAXIMIZED, self.isMaximized()) ModuleManager.core['settings'].Settings.set_value( self.KEY_WINDOW_POSITION, self.pos()) ModuleManager.core['settings'].Settings.set_value( self.KEY_H_SPLITTER_STATE, self.horizontal_splitter.saveState()) def on_quit(self): self.save_state() self.close() def load_central_widget(self): self.populate_central_widget() self.connect_widgets() def populate_central_widget(self): self.vertical_widgets = collections.OrderedDict() self.horizontal_splitter = QSplitter( Qt.Horizontal, self.vertical_splitter) self.horizontal_widgets = collections.OrderedDict() self.vertical_widgets["horizontal_splitter"] = self.horizontal_splitter Alter.invoke_all( 'main_window_add_vertical_widget', self.vertical_widgets, self ) for widget in self.vertical_widgets.values(): self.vertical_splitter.addWidget(widget) Alter.invoke_all( 'main_window_add_horizontal_widget', self.horizontal_widgets, self.vertical_splitter ) for widget in self.horizontal_widgets.values(): self.horizontal_splitter.addWidget(widget) #restore horizontal splitter state state = ModuleManager.core['settings'].Settings.value( self.KEY_H_SPLITTER_STATE, None ) if state: self.horizontal_splitter.restoreState(state) def connect_widgets(self): Alter.invoke_all( 'main_window_connect_widgets', self.vertical_widgets, self.horizontal_widgets ) def reload_central_widget(self): self.save_state() for index in range(self.vertical_splitter.count()): widget = self.vertical_splitter.widget(index) widget.hide() widget.setParent(None) del widget Alter.clear() ModuleManager.reload_all_modules('core') ModuleManager.reload_all_modules('custom') self.load_central_widget() def add_action(self, name, callback, **kwargs): """ Ajoute une action au context menu et au widget lui même. Créer une fonction à la volé pour fournir des arguments aux fonctions de rappel. """ action = QAction(name, self) if 'icon' in kwargs: action.setIcon(kwargs['icon']) if 'shortcut' in kwargs: action.setShortcut(kwargs['shortcut']) action.setShortcutContext(Qt.WidgetWithChildrenShortcut) action.triggered.connect(self.__wrapper(callback, **kwargs)) self.addAction(action) if 'menu' in kwargs: kwargs['menu'].addAction(action) def add_separator(self, menu): """Simple abstraction of self.context_menu.addSeparator()""" menu.addSeparator() def __wrapper(self, callback, **kwargs): def __new_function(): """ __new_function représente la forme de tous les callbacks connecté à une action pour pouvoir utiliser les raccourcis en même temps que le menu contextuel. """ args = [ kwargs['instance'] if 'instance' in kwargs else self ] callback(*args) return __new_function
class WebTab(QWidget): class SavedTab(object): def __init__(self, webTab=None): self.title = '' self.url = QUrl() self.icon = QIcon() self.history = QByteArray() self.isPinned = False self.zoomLevel = 1 self.parentTab = -1 self.childTabs = [] self.sessionData = {} if webTab: self.setWebTab(webTab) def __getstate__(self): result = dict(self.__dict__) result['url'] = result['url'].toEncoded() data = QByteArray() ds = QDataStream(data, QIODevice.WriteOnly) ds.writeQVariant(self.icon) result['icon'] = data.data() return result def __setstate__(self, state): for key, val in state.items(): if key == 'url': self.__dict__[key] = QUrl.fromEncoded(val) elif key == 'icon': ds = QDataStream(QByteArray(val)) self.__dict__[key] = ds.readQVariant() else: self.__dict__[key] = val def setWebTab(self, webTab): self.title = webTab.title() self.url = webTab.url() self.icon = webTab.icon() self.history = webTab.historyData() self.isPinned = webTab.isPinned() self.zoomLevel = webTab.zoomLevel() if webTab.parentTab(): self.parentTab = webTab.parentTab().tabIndex() else: self.parentTab = -1 self.childTabs = [ tab.tabIndex() for tab in webTab.childTabs() ] self.sessionData = webTab.sessionData() def isValid(self): return not self.url.isEmpty() or not self.history.isEmpty() def clear(self): self.title = '' self.url = QUrl() self.icon = QIcon() self.history = QByteArray() self.isPinned = False self.zoomLevel = 1 self.parentTab = -1 self.childTabs = [] self.sessionData = {} # type AddChildBehavior AppendChild = 0 PrependChild = 1 s_addChildBehavior = AppendChild def __init__(self, parent=None): super(WebTab, self).__init__(parent) self.setObjectName('webtab') self._tabBar = None self._window = None self._parentTab = None self._childTabs = [] self._sessionData = {} self._savedTab = self.SavedTab() self._isPinned = False self._isCurrentTab = False self._webView = TabbedWebView(self) self._webView.setPage(WebPage()) self._webView.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding) self.setFocusProxy(self._webView) self._locationBar = LocationBar(self) self._locationBar.setWebView(self._webView) self._tabIcon = TabIcon(self) self._tabIcon.setWebTab(self) self._layout = QVBoxLayout(self) self._layout.setContentsMargins(0, 0, 0, 0) self._layout.setSpacing(0) self._layout.addWidget(self._webView) viewWidget = QWidget(self) viewWidget.setLayout(self._layout) self._splitter = QSplitter(Qt.Vertical, self) self._splitter.setChildrenCollapsible(False) self._splitter.addWidget(viewWidget) layout = QVBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) layout.addWidget(self._splitter) self.setLayout(layout) self._notificationWidget = QWidget(self) self._notificationWidget.setAutoFillBackground(True) pal = self._notificationWidget.palette() pal.setColor(QPalette.Window, pal.window().color().darker(110)) self._notificationWidget.setPalette(pal) nlayout = QVBoxLayout(self._notificationWidget) nlayout.setSizeConstraint(QLayout.SetMinAndMaxSize) nlayout.setContentsMargins(0, 0, 0, 0) nlayout.setSpacing(1) self._webView.showNotification.connect(self.showNotification) self._webView.loadFinished.connect(self.loadFinished) self._webView.titleChanged.connect(self.titleWasChanged) self._webView.titleChanged.connect(self.titleChanged) self._webView.iconChanged.connect(self.iconChanged) self._webView.backgroundActivityChanged.connect(self.backgroundActivityChanged) self._webView.loadStarted.connect(lambda: self.loadingChanged.emit(True)) self._webView.loadFinished.connect(lambda: self.loadingChanged.emit(False)) def pageChanged(page): page.audioMutedChanged.connect(self.playingChanged) page.recentlyAudibleChanged.connect(self.mutedChanged) pageChanged(self._webView.page()) self._webView.pageChanged.connect(pageChanged) def tabIconResized(): if self._tabBar: self._tabBar.update() self._tabIcon.resized.connect(tabIconResized) def browserWindow(self): ''' @return BrowserWindow ''' return self._window def webView(self): ''' @return TabbedWebView ''' return self._webView def locationBar(self): ''' @return LocationBar ''' return self._locationBar def tabIcon(self): ''' @return TabIcon ''' return self._tabIcon def parentTab(self): ''' @return WebTab ''' return self._parentTab def setParentTab(self, tab): if self._isPinned or self._parentTab == tab: return if tab and tab.isPinned(): return if self._parentTab: index = self._parentTab._childTabs.index(self) if index >= 0: self._parentTab._childTabs.pop(index) self._parentTab.childTabRemoved.emit(self, index) self._parentTab = tab if tab: self._parentTab = None tab.addChildTab(self) else: self.parentTabChanged.emit(self._parentTab) def addChildTab(self, tab, index=-1): if self._isPinned or not tab or tab.isPinned(): return oldParent = tab._parentTab tab._parentTab = self if oldParent: index = oldParent._childTabs.index(tab) if index >= 0: oldParent._childTabs.pop(index) oldParent.childTabRemoved.emit(tab, index) if index < 0 or index > len(self._childTabs): index = 0 if self.addChildBehavior() == self.AppendChild: index = len(self._childTabs) else: # PrependChild index = 0 self._childTabs.insert(index, tab) self.childTabAdded.emit(tab, index) tab.parentTabChanged.emit(self) def childTabs(self): ''' @return QVector<WebTab*> ''' return self._childTabs def sessionData(self): ''' @return {} ''' return self._sessionData def setSessionData(self, key, value): self._sessionData[key] = value def url(self): if self.isRestored(): if self._webView.url().isEmpty() and self._webView.isLoading(): return self._webView.page().requestedUrl() return self._webView.url() else: return self._savedTab.url def title(self, allowEmpty=False): if self.isRestored(): return self._webView.title(allowEmpty) else: return self._savedTab.title def icon(self, allowNull=False): if self.isRestored(): return self._webView.icon(allowNull) if allowNull or not self._savedTab.icon.isNull(): return self._savedTab.icon return IconProvider.emptyWebIcon() def history(self): ''' @return QWebEngineHistory ''' return self._webView.history() def zoomLevel(self): return self._webView.zoomLevel() def setZoomLevel(self, level): self._webView.setZoomLevel(level) def detach(self): assert(self._window) assert(self._tabBar) # Remove from tab tree self.removeFromTabTree() # Remove icon from tab self._tabBar.setTabButton(self.tabIndex(), self._tabBar.iconButtonPosition(), None) self._tabIcon.setParent(self) # Remove the tab from tabbar self._window.tabWidget().removeTab(self.tabIndex()) self.setParent(None) # Remove the locationbar from window self._locationBar.setParent(self) # Detach TabbedWindow self._webView.setBrowserWindow(None) if self._isCurrentTab: self._isCurrentTab = False self.currentTabChanged.emit(self._isCurrentTab) self._tabBar.currentChanged.disconnect(self.onCurrentChanged) self._window = None self._tabBar = None def onCurrentChanged(self, index): wasCurrent = self._isCurrentTab self._isCurrentTab = index == self.tabIndex() if wasCurrent != self._isCurrentTab: self.currentTabChanged.emit(self._isCurrentTab) def attach(self, window): self._window = window self._tabBar = self._window.tabWidget().tabBar() self._webView.setBrowserWindow(self._window) self._locationBar.setBrowserWindow(self._window) self._tabBar.setTabText(self.tabIndex(), self.title()) self._tabBar.setTabButton(self.tabIndex(), self._tabBar.iconButtonPosition(), self._tabIcon) QTimer.singleShot(0, self._tabIcon.updateIcon) self.onCurrentChanged(self._tabBar.currentIndex()) self._tabBar.currentChanged.connect(self.onCurrentChanged) def historyData(self): ''' @return QByteArray ''' if self.isRestored(): historyArray = QByteArray() stream = QDataStream(historyArray, QIODevice.WriteOnly) history = self._webView.history() stream << history return historyArray else: return self._savedTab.history def stop(self): self._webView.stop() def reload(self): self._webView.reload() def load(self, request): ''' @param: requset LoadRequest ''' if self.isRestored(): self.tabActivated() QTimer.singleShot(0, lambda: self.load(request)) else: self._webView.load(request) def unload(self): self._savedTab = self.SavedTab(self) self.restoredChanged.emit(self.isRestored()) self._webView.setPage(WebPage()) self._webView.setFocus() def isLoading(self): return self._webView.isloading() def isPinned(self): return self._isPinned def setPinned(self, state): if self._isPinned == state: return if state: self.removeFromTabTree() self._isPinned = state self.pinnedChanged.emit(self._isPinned) def togglePinned(self): assert(self._tabBar) assert(self._window) self.setPinned(not self.isPinned()) self._window.tabWidget().pinUnPinTab(self.tabIndex(), self.title()) def isMuted(self): return self._webView.page().isAudioMuted() def isPlaying(self): return self._webView.page().recentlyAudible() def setMuted(self, muted): self._webView.page().setAudioMuted(muted) def toggleMuted(self): self.setMuted(not self.isMuted()) def backgroundActivity(self): return self._webView.backgroundActivity() def tabIndex(self): index = -1 if self._tabBar: index = self._tabBar.tabWidget().indexOf(self) return index def isCurrentTab(self): return self._isCurrentTab def makeCurrentTab(self): if self._tabBar: self._tabBar.tabWidget().setCurrentIndex(self.tabIndex()) def closeTab(self): if self._tabBar: self._tabBar.tabWidget().closeTab(self.tabIndex()) def moveTab(self, to): if self._tabBar: self._tabBar.tabWidget().moveTab(self.tabIndex(), to) def haveInspector(self): return self._splitter.count() > 1 and self._splitter.widget(1).inherits('WebInspector') def showWebInspector(self, inspectElement=False): if not WebInspector.isEnabled() or self.haveInspector(): return inspector = WebInspector(self) inspector.setView(self._webView) if inspectElement: inspector.inspectElement() height = inspector.sizeHint().height() self._splitter.addWidget(inspector) self._splitter.setSizes((self._splitter.height() - height, height)) def toggleWebInspector(self): if not self.haveInspector(): self.showWebInspector() else: self._splitter.widget(1).destroy() # TODO: del? def showSearchToolBar(self, searchText=''): index = 1 toolBar = None if self._layout.count() == 1: toolBar = SearchToolBar(self._webView, self) self._layout.insertWidget(index, toolBar) if self._layout.count() == 2: assert(isinstance(self._layout.itemAt(index).widget(), SearchToolBar)) toolBar = self._layout.itemAt(index).widget() assert(toolBar) if not searchText: toolBar.setText(searchText) toolBar.focusSearchLine() def isRestored(self): return not self._savedTab.isValid() def restoreTab(self, tab): ''' @param: tab SavedTab ''' assert(self._tabBar) self.setPinned(tab.isPinned) self._sessionData = tab.sessionData if not self.isPinned() and gVar.appSettings.loadTabsOnActivation: self._savedTab = tab self.restoredChanged.emit(self.isRestored()) index = self.tabIndex() self._tabBar.setTabText(index, tab.title) self._locationBar.showUrl(tab.url) self._tabIcon.updateIcon() else: # This is called only on restore session and restoring tabs # immediately crashes QtWebEngine, waiting after initialization is # complete fixes it QTimer.singleShot(1000, lambda: self.p_restoreTab(tab)) def p_restoreTab(self, tab): ''' @param: tab SavedTab ''' self.p_restoreTabByUrl(tab.url, tab.history, tab.zoomLevel) def p_restoreTabByUrl(self, url, history, zoomLevel): self._webView.load(url) # Restoring history of internal pages crashes QtWebEngine 5.8 blacklistedSchemes = ['view-source', 'chrome'] if (url.scheme() not in blacklistedSchemes): stream = QDataStream(history) stream >> self._webView.history() self._webView.setZoomLevel(zoomLevel) self._webView.setFocus() def tabActivated(self): if self.isRestored(): return def _onTabActivated(): if self.isRestored(): return self.p_restoreTab(self._savedTab) self._savedTab.clear() self.restoredChanged.emit(self.isRestored()) QTimer.singleShot(0, _onTabActivated) def addChildBehavior(self): ''' @return AddChildBehavior ''' return self.s_addChildBehavior def setAddChildBehavior(self, behavior): self.s_addChildBehavior = behavior # Q_SLOTS @pyqtSlot(QWidget) def showNotification(self, notif): self._notificationWidget.setParent(self) self._notificationWidget.raise_() self._notificationWidget.setFixedWidth(self.width()) self._notificationWidget.layout().addWidget(notif) self._notificationWidget.show() notif.show() @pyqtSlot() def loadFinished(self): self.titleWasChanged(self._webView.title()) # Q_SIGNALS titleChanged = pyqtSignal(str) # title iconChanged = pyqtSignal(QIcon) # icon pinnedChanged = pyqtSignal(bool) # pinned restoredChanged = pyqtSignal(bool) # restored currentTabChanged = pyqtSignal(bool) # current loadingChanged = pyqtSignal(bool) # loading mutedChanged = pyqtSignal(bool) # muted playingChanged = pyqtSignal(bool) # playing backgroundActivityChanged = pyqtSignal(bool) # activity parentTabChanged = pyqtSignal('PyQt_PyObject') # WebTab* childTabAdded = pyqtSignal('PyQt_PyObject', int) # WebTab*, index childTabRemoved = pyqtSignal('PyQt_PyObject', int) # WebTab*, index def titleWasChanged(self, title): if not self._tabBar or not self._window or not title: return if self._isCurrentTab: self._window.setWindowTitle('%s - Demo' % title) self._tabBar.setTabText(self.tabIndex(), title) # override def resizeEvent(self, event): QWidget.resizeEvent(self, event) self._notificationWidget.setFixedWidth(self.width()) def removeFromTabTree(self): parentTab = self._parentTab parentIndex = -1 if parentTab: parentIndex = parentTab._childTabs.index(self) self.setParentTab(None) idx = 0 while self._childTabs: child = self._childTabs[0] child.setParentTab(None) if parentTab: parentTab.addChildTab(child, parentIndex + idx) idx += 1
class MainWindow(QMainWindow): KEY_WINDOW_SIZE = 'main_window/size' KEY_WINDOW_MAXIMIZED = 'main_window/maximized' KEY_WINDOW_POSITION = 'main_window/position' KEY_H_SPLITTER_STATE = 'main_window/h_splitter_state' def __init__(self): super(MainWindow, self).__init__() self.setWindowTitle('Mojuru') app_icon = QIcon('images/mojuru_logo.png') self.setWindowIcon(app_icon) reload_modules_action = QAction('Reload MainWindow', self) reload_modules_action.setShortcut('ctrl+shift+alt+r') reload_modules_action.triggered.connect(self.reload_central_widget) self.addAction(reload_modules_action) quit_action = QAction('Quit', self) quit_action.setShortcut('ctrl+q') quit_action.triggered.connect(self.on_quit) self.addAction(quit_action) self.vertical_splitter = QSplitter(Qt.Vertical, self) self.setCentralWidget(self.vertical_splitter) self.load_central_widget() self.file_menu = self.menuBar().addMenu(self.tr('&File')) self.file_menu.addAction(quit_action) self.file_menu.addSeparator() self.module_menu = self.menuBar().addMenu(self.tr('&Modules')) self.module_menu.addAction(reload_modules_action) self.module_menu.addSeparator() Alter.invoke_all('main_window_init', self) #restore main window state size = ModuleManager.core['settings'].Settings.value( self.KEY_WINDOW_SIZE, QSize(600, 400)) maximized = ModuleManager.core['settings'].Settings.value( self.KEY_WINDOW_MAXIMIZED, False) position = ModuleManager.core['settings'].Settings.value( self.KEY_WINDOW_POSITION, QPoint(0, 0)) if maximized == 'true': self.showMaximized() else: self.resize(size) self.move(position) def closeEvent(self, event): self.on_quit() def save_state(self): ModuleManager.core['settings'].Settings.set_value( self.KEY_WINDOW_SIZE, self.size()) ModuleManager.core['settings'].Settings.set_value( self.KEY_WINDOW_MAXIMIZED, self.isMaximized()) ModuleManager.core['settings'].Settings.set_value( self.KEY_WINDOW_POSITION, self.pos()) ModuleManager.core['settings'].Settings.set_value( self.KEY_H_SPLITTER_STATE, self.horizontal_splitter.saveState()) def on_quit(self): self.save_state() self.close() def load_central_widget(self): self.populate_central_widget() self.connect_widgets() def populate_central_widget(self): self.vertical_widgets = collections.OrderedDict() self.horizontal_splitter = QSplitter(Qt.Horizontal, self.vertical_splitter) self.horizontal_widgets = collections.OrderedDict() self.vertical_widgets["horizontal_splitter"] = self.horizontal_splitter Alter.invoke_all('main_window_add_vertical_widget', self.vertical_widgets, self) for widget in self.vertical_widgets.values(): self.vertical_splitter.addWidget(widget) Alter.invoke_all('main_window_add_horizontal_widget', self.horizontal_widgets, self.vertical_splitter) for widget in self.horizontal_widgets.values(): self.horizontal_splitter.addWidget(widget) #restore horizontal splitter state state = ModuleManager.core['settings'].Settings.value( self.KEY_H_SPLITTER_STATE, None) if state: self.horizontal_splitter.restoreState(state) def connect_widgets(self): Alter.invoke_all('main_window_connect_widgets', self.vertical_widgets, self.horizontal_widgets) def reload_central_widget(self): self.save_state() for index in range(self.vertical_splitter.count()): widget = self.vertical_splitter.widget(index) widget.hide() widget.setParent(None) del widget Alter.clear() ModuleManager.reload_all_modules('core') ModuleManager.reload_all_modules('custom') self.load_central_widget()
class DanaBrowseWindow(QMainWindow): def __init__(self, args): super(DanaBrowseWindow, self).__init__() #Leeo la configuracion self.configFile = args.configFile self.secure = args.secure self.cubeFile = args.cubeFile self.sysExclude = args.sysExclude self.maxLevel = 1 #para poder modificarlo luego self.dictionary = DataDict(defFile=args.configFile, secure=args.secure, sysExclude=args.sysExclude) #TODO variables asociadas del diccionario. Reevaluar al limpiar self.baseModel = self.dictionary.baseModel self.configData = self.dictionary.configData self.conn = self.dictionary.conn if self.dictionary.isEmpty: self.newConfigData() #self.dictionary._cargaModelo(self.dictionary.baseModel) self.setupView() self.cubeMgr = None # necesito mas adelante que este definida if config.DEBUG: print('inicializacion completa') #CHANGE here self.queryView = TableBrowse(None) self.dictMenu = self.menuBar().addMenu("&Conexiones") self.dictMenu.addAction("&New ...", self.newConnection, "Ctrl+N") self.dictMenu.addAction("&Modify ...", self.modConnection, "Ctrl+M") self.dictMenu.addAction("&Delete ...", self.delConnection, "Ctrl+D") self.dictMenu.addAction("&Save Config File", self.saveConfigFile, "Ctrl+S") self.dictMenu.addAction("E&xit", self.close, "Ctrl+Q") self.queryMenu = self.menuBar().addMenu('Consulta de &datos') self.queryMenu.addAction("Cerrar", self.hideDatabrowse) self.queryMenu.setEnabled(False) self.cubeMenu = self.menuBar().addMenu("C&ubo") self.cubeMenu.addAction("&Salvar", self.saveCubeFile, "Ctrl+S") #self.cubeMenu.addAction("&Restaurar", self.restoreCubeFile, "Ctrl+M") self.cubeMenu.addAction("S&alir", self.hideCube, "Ctrl+C") self.cubeMenu.addSeparator() self.cubeMenu.addAction("Ver &ejemplo de datos del cubo", self.previewCube, "Ctrl+E") self.cubeMenu.setEnabled(False) #self.queryModel = self.queryView.baseModel self.querySplitter = QSplitter(Qt.Vertical) self.querySplitter.addWidget(self.view) #self.querySplitter.addWidget(self.queryView) self.configSplitter = QSplitter(Qt.Horizontal) self.configSplitter.addWidget(self.querySplitter) self.setCentralWidget(self.configSplitter) self.setWindowTitle("Visualizador de base de datos") """ estas funciones son para soportar para los decoradores keep position y tal """ def model(self): return self.baseModel def isExpanded(self, idx): return self.view.isExpanded(idx) def setExpanded(self, idx, state): return self.view.setExpanded(idx, state) def setupView(self): self.view = QTreeView(self) self.view.setContextMenuPolicy(Qt.CustomContextMenu) self.view.customContextMenuRequested.connect(self.openContextMenu) self.view.doubleClicked.connect(self.test) self.view.setModel(self.baseModel) #self.view.resizeColumnToContents(0) self.view.setEditTriggers(QAbstractItemView.NoEditTriggers) self.view.expandAll() for m in range(self.baseModel.columnCount()): self.view.resizeColumnToContents(m) self.view.collapseAll() self.view.expandToDepth(0) #self.view.setHeaderHidden(True) #self.view.setSortingEnabled(True) #self.view.setRootIsDecorated(False) self.view.setAlternatingRowColors(True) #self.view.sortByColumn(0, Qt.AscendingOrder) def newConfigData(self): self.configData = dict() self.configData['Conexiones'] = dict() self.editConnection(None) if self.configData['Conexiones']: self.saveConfigFile() print(self.configData) self.dictionary._cargaModelo(confData=self.configData['Conexiones'] ) #self.dictionary.baseModel) else: QMessageBox.critical( self, "Error Fatal", "No se ha encontrado una conexión valida.\nFin de proceso") self.close() def saveConfigFile(self): dump_config(self.configData, getConfigFileName(self.configFile), secure=self.secure) #TODO de momento def closeEvent(self, event): self.close() def close(self): if self.cubeMgr: self.saveConfigFile() for conid in self.conn: if self.conn[conid] is None: continue if self.conn[conid].closed: self.conn[conid].close() self.saveConfigFile() sys.exit() def newConnection(self): confName = self.editConnection(None) # esta claro que sobran parametros self.dictionary.appendConnection(confName) def modConnection(self, nombre=None): if nombre is None: selDialog = SelectConnectionDlg(self.configData['Conexiones']) if selDialog.exec_(): confName = selDialog.conexion else: return else: confName = nombre self.editConnection(confName) self.updateModel(confName) @waiting_effects def updateModel(self, nombre=None): self.dictionary.updateModel(nombre) def delConnection(self, nombre=None): if nombre is None: selDialog = SelectConnectionDlg(self.configData['Conexiones']) if selDialog.exec_(): confName = selDialog.conexion else: return else: confName = nombre self.dictionary.dropConnection(confName) def editConnection(self, nombre=None): attr_list = ('driver', 'dbname', 'dbhost', 'dbuser', 'dbpass', 'dbport', 'debug') if nombre is None: datos = [None for k in range(len(attr_list) + 1)] else: datos = [ nombre, ] + dict2row(self.configData['Conexiones'][nombre], attr_list) datos[1] = DRIVERS.index(datos[1]) #contexto context = ( ( 'Nombre', QLineEdit, { 'setReadOnly': True } if nombre is not None else None, None, ), # driver ( "Driver ", QComboBox, None, DRIVERS, ), ( "DataBase Name", QLineEdit, None, None, ), ( "Host", QLineEdit, None, None, ), ( "User", QLineEdit, None, None, ), ( "Password", QLineEdit, { 'setEchoMode': QLineEdit.Password }, None, ), ( "Port", QLineEdit, None, None, ), ( "Debug", QCheckBox, None, None, )) parmDialog = ConnectionSheetDlg('Edite la conexion', context, datos, self) if parmDialog.exec_(): #TODO deberia verificar que se han cambiado los datos #datos[1]=DRIVERS[datos[1]] self.configData['Conexiones'][datos[0]] = row2dict( datos[1:], attr_list) return datos[0] @keep_tree_layout() def openContextMenu(self, position): """ """ item = None indexes = self.view.selectedIndexes() if len(indexes) > 0: index = indexes[0] item = self.baseModel.itemFromIndex(index) menu = QMenu() if item: item.setMenuActions(menu, self) action = menu.exec_(self.view.viewport().mapToGlobal(position)) #getContextMenu(item,action,self) @waiting_effects def databrowse(self, confName, schema, table, iters=0): #print(confName,schema,table,self.dictionary.conn[confName]) self.queryView.reconnect( self.queryView.getConnection(self.dictionary, confName, schema, table, iters)) self.queryView.executeNewScript( self.queryView.generateSQL(confName, schema, table, iters, pFilter=None)) if self.queryView.isHidden(): self.queryView.show() self.queryMenu.setEnabled(True) if self.querySplitter.count( ) == 1: #de momento parece un modo sencillo de no multiplicar en exceso self.querySplitter.addWidget(self.queryView) def hideDatabrowse(self): self.queryView.hide() self.queryMenu.setEnabled(False) def prepareNewCube(self, confName, schema, table): # aqui tiene que venir un dialogo para seleccionar nombre del cubo maxLevel = self.maxLevel parmDlg = GenerationSheetDlg('Parámetros de generación', table, maxLevel) if parmDlg.exec_(): kname = parmDlg.data[0] maxLevel = parmDlg.data[1] infox = info2cube(self.dictionary, confName, schema, table, maxLevel) if kname != table: infox[kname] = infox.pop(table) return infox def cubebrowse(self, confName, schema, table): infox = self.prepareNewCube(confName, schema, table) if self.cubeMgr and not self.cubeMgr.isHidden(): self.hideCube() self.cubeMgr = CubeMgr(self, confName, schema, table, self.dictionary, rawCube=infox, cubeFile=self.cubeFile) self.cubeMgr.expandToDepth(1) #if self.configSplitter.count() == 1: #de momento parece un modo sencillo de no multiplicar en exceso self.configSplitter.addWidget(self.cubeMgr) self.cubeMgr.show() self.cubeMenu.setEnabled(True) def saveCubeFile(self): self.cubeMgr.saveCubeFile() def restoreCubeFile(self): self.cubeMgr.restoreConfigFile() def hideCube(self): self.cubeMgr.saveCubeFile() self.cubeMgr.hide() self.cubeMenu.setEnabled(False) def test(self, index): return print(index.row(), index.column()) item = self.baseModel.itemFromIndex(index) print(item.text(), item.model()) def refreshTable(self): self.baseModel.emitModelReset() def previewCube(self): startItem = self.cubeMgr.model().item(0, 0) conName = self.cubeMgr.defaultConnection self.queryView.reconnect( self.queryView.getConnection(self.dictionary, confName=conName)) query = self.cubeMgr.getPreviewQuery(startItem) self.queryView.executeNewScript(query) if self.queryView.isHidden(): self.queryView.show() self.queryMenu.setEnabled(True) if self.querySplitter.count( ) == 1: #de momento parece un modo sencillo de no multiplicar en exceso self.querySplitter.addWidget(self.queryView)