def update_label_with_slider(label: QLabel, slider: QSlider): if label.display_as_percentage: min_value = slider.minimum() percentage = (slider.value() - min_value) / (slider.maximum() - min_value) label.setText(f"{percentage * 100: 3.0f}%") else: label.setText(str(slider.value()))
class Controller(QWidget): def __init__(self, parent=None): super(Controller, self).__init__(parent) self.resourcePath = os.path.normpath(os.path.join(fileDir, "resource")).replace( "\\", "/") self.setWindowFlags(Qt.Window | Qt.FramelessWindowHint) self.setAttribute(Qt.WA_TranslucentBackground) self.setAttribute(Qt.WA_Hover) self.setMouseTracking(True) self.setAcceptDrops(True) layout = QVBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) self.fillColor = QColor(127, 127, 127, 2) self.penColor = QColor(127, 127, 127, 2) self.onTop = False self.visible = False self.drawDrag = False self.isPaused = False self.lastMove = datetime.now() self.lastButton = Qt.MouseButton.NoButton self.setupWidget() self.setupRightClick() if parent: self.player = parent self.setupSignal() self.toggleVisibility(False) def setParent(self, parent): self.player = parent return super(Controller, self).setParent(parent) def setupWidget(self): # Top self.pinBtn = ButtonIcon(icon=f"{self.resourcePath}/pin.svg", iconsize=15) self.closeBtn = ButtonIcon(icon=f"{self.resourcePath}/cancel.svg", iconsize=15) self.topLayout = QHBoxLayout() self.topLayout.setContentsMargins(5, 5, 5, 5) self.topLayout.addWidget(self.pinBtn) self.topLayout.addStretch() self.topLayout.addWidget(self.closeBtn) # Middle self.playBtn = ButtonIcon(icon=f"{self.resourcePath}/play.svg", iconsize=100) self.volumeSlider = QSlider(Qt.Vertical, self) self.volumeSlider.setMaximum(100) self.volumeSlider.setValue(100) self.volumeBtn = ButtonIcon(icon=f"{self.resourcePath}/speaker.svg", iconsize=15) self.volumeLayout = QVBoxLayout() self.volumeLayout.setContentsMargins(0, 0, 0, 0) for w in (self.volumeSlider, self.volumeBtn): self.volumeLayout.addWidget(w) self.playLayout = QHBoxLayout() self.playLayout.addStretch() self.playLayout.addWidget(self.playBtn) self.playLayout.addStretch() self.playLayout.addLayout(self.volumeLayout) # Bottom self.addBtn = ButtonIcon(icon=f"{self.resourcePath}/plus.svg", iconsize=15) self.timeSlider = TimeSlider(Qt.Horizontal, self) self.volumeSlider.setStyleSheet(self.timeSlider.qss()) self.repeatBtn = ButtonIcon(icon=f"{self.resourcePath}/replay.svg", iconsize=15) self.listBtn = ButtonIcon(icon=f"{self.resourcePath}/list.svg", iconsize=15) self.bottomLayout = QHBoxLayout() self.bottomLayout.setContentsMargins(0, 0, 0, 0) self.bottomLayout.addWidget(self.addBtn) self.bottomLayout.addWidget(self.listBtn) self.bottomLayout.addWidget(self.timeSlider) self.bottomLayout.addWidget(self.repeatBtn) self.layout().addLayout(self.topLayout) self.layout().addStretch() self.layout().addLayout(self.playLayout) self.layout().addStretch() self.layout().addLayout(self.bottomLayout) self.timer = QTimer() self.timer.setInterval(50) self.timer.timeout.connect(self.toggleCursor) self.timer.start() self.opacFX = [] for w in (self.pinBtn, self.closeBtn, self.playBtn, self.volumeSlider, self.volumeBtn, self.addBtn, self.repeatBtn, self.listBtn): w.setFocusProxy(self) fx = QGraphicsOpacityEffect(w) fx.setOpacity(0) w.setGraphicsEffect(fx) self.opacFX.append(fx) self.timeSlider.setHeight(1) self.timeSlider.setFocusProxy(self) def setupRightClick(self): self.popMenu = QMenu(self) self.openAct = QAction('Open File', self) self.fullAct = QAction('Fullscreen', self) self.atopAct = QAction('Pin on Top', self) self.listAct = QAction('Playlist', self) self.helpAct = QAction('Help', self) self.exitAct = QAction('Exit', self) for act in (self.openAct, self.fullAct, self.listAct, self.helpAct): self.popMenu.addAction(act) self.popMenu.addSeparator() self.popMenu.addAction(self.exitAct) # Initial self.fullAct.setCheckable(True) self.listAct.setCheckable(True) # Temp self.listAct.setDisabled(True) self.helpAct.setDisabled(True) def opacityAnimation(self, end, duration, callback): anims = [] for fx in self.opacFX: ani = QPropertyAnimation(fx, b"opacity") ani.setStartValue(fx.opacity()) ani.setEndValue(end) a = max(fx.opacity(), end) i = min(fx.opacity(), end) ani.setDuration((a - i) * duration) ani.stateChanged.connect(callback) anims.append(ani) return anims def heightAnimation(self, target, height, duration, callback): ani = QPropertyAnimation(target, b"Height") ani.setStartValue(target.getHeight()) ani.setEndValue(height) a = max(target.getHeight(), height) i = min(target.getHeight(), height) ani.setDuration((a - i) / a * duration) # ani.finished.connect(callback) return ani def setupSignal(self): # Controller self.closeBtn.clicked.connect(self.player.close) self.playBtn.clicked.connect(self.togglePlay) self.volumeSlider.valueChanged.connect(self.player.setVolume) self.addBtn.clicked.connect(self.openFile) self.timeSlider.sliderMoved.connect(self.seek) # Player self.player.stateChanged.connect(self.onStateChanged) self.player.lengthChanged.connect(self.onLengthChanged) self.player.timeChanged.connect(self.onTimeChanged) # Right click self.openAct.triggered.connect(self.openFile) self.fullAct.triggered.connect(self.toggleFullscreen) self.exitAct.triggered.connect(self.player.close) def event(self, event): if event.type() == QEvent.Type.Enter: self.lastMove = datetime.now() self.toggleVisibility(True) elif event.type() == QEvent.Type.Leave: self.toggleVisibility(False) return super(Controller, self).event(event) def paintEvent(self, event): s = self.size() qp = QPainter() qp.begin(self) qp.setRenderHint(QPainter.Antialiasing, True) qp.setPen(self.penColor) qp.setBrush(self.fillColor) qp.drawRect(0, 0, s.width(), s.height()) if self.drawDrag: pen = QPen(Qt.white, 5) pen.setCapStyle(Qt.RoundCap) qp.setPen(pen) qp.setBrush(self.fillColor) outerWidth = s.width() - 60 outerHeight = s.height() - 60 ow = int(s.width() / 2 - outerWidth / 2) oh = int(s.height() / 2 - outerHeight / 2) qp.drawRoundedRect(ow, oh, outerWidth, outerHeight, 5, 5) qp.setBrush(Qt.white) thickness = 12 length = 50 roundness = thickness / 2 vS = int(s.width() / 2 - thickness / 2) vE = int(s.height() / 2 - length / 2) qp.drawRoundedRect(vS, vE, thickness, length, roundness, roundness) hS = int(s.width() / 2 - length / 2) hE = int(s.height() / 2 - thickness / 2) qp.drawRoundedRect(hS, hE, length, thickness, roundness, roundness) qp.end() def mousePressEvent(self, event): self.lastButton = event.button() if event.button() == Qt.MouseButton.LeftButton: self.startPos = event.pos() self.lastClick = datetime.now() elif event.button() == Qt.MouseButton.MiddleButton: self.startFrame = self.timeSlider.value() self.startPos = event.pos() return super(Controller, self).mousePressEvent(event) def mouseReleaseEvent(self, event): if event.button() == Qt.MouseButton.LeftButton: if hasattr(self, "startPos"): if (datetime.now() - self.lastClick).microseconds / 1000 < 300: self.togglePlay() delattr(self, "startPos") elif event.button() == Qt.MouseButton.RightButton: self.onRightClick(event.pos()) elif self.lastButton == Qt.MouseButton.MiddleButton: if hasattr(self, "startPos"): delattr(self, "startPos") if hasattr(self, "startFrame"): delattr(self, "startFrame") self.lastButton = None return super(Controller, self).mouseReleaseEvent(event) def mouseMoveEvent(self, event): self.lastMove = datetime.now() self.toggleVisibility(True) if self.lastButton == Qt.MouseButton.LeftButton: if not self.player.isFullScreen(): if hasattr(self, "startPos"): delta = event.pos() - self.startPos self.move(self.pos() + delta) self.player.move(self.player.pos() + delta) elif self.lastButton == Qt.MouseButton.MiddleButton: if hasattr(self, "startFrame"): self.player.pause() delta = event.pos() - self.startPos percent = delta.x() / self.width() m = self.timeSlider.maximum() final = int(self.startFrame + (percent * m)) self.timeSlider.setValue(final) self.seek() return super(Controller, self).mouseMoveEvent(event) def mouseDoubleClickEvent(self, event): if event.button() == Qt.MouseButton.LeftButton: self.toggleFullscreen() return super(Controller, self).mouseDoubleClickEvent(event) def wheelEvent(self, event): increment = int(self.volumeSlider.maximum() / 10) if (event.angleDelta().y()) > 0: self.volumeSlider.setValue(self.volumeSlider.value() + increment) elif (event.angleDelta().y()) < 0: self.volumeSlider.setValue(self.volumeSlider.value() - increment) return super(Controller, self).wheelEvent(event) def dragEnterEvent(self, event): self.drawDrag = True self.update() if event.mimeData().hasUrls(): event.accept() elif event.mimeData().hasText(): event.accept() else: event.ignore() def dragLeaveEvent(self, event): self.drawDrag = False self.update() return super(Controller, self).dragLeaveEvent(event) def dropEvent(self, event): self.drawDrag = False self.update() if event.mimeData().hasUrls(): url = event.mimeData().urls()[0].toString() self.player.createMedia(url) self.player.play() elif event.mimeData().hasText(): url = event.mimeData().text() if os.path.isfile(url): self.player.createMedia(url) self.player.play() def keyPressEvent(self, event): self.lastMove = datetime.now() self.toggleVisibility(True) if event.key() in [Qt.Key_Left, Qt.Key_A, Qt.Key_Less, Qt.Key_Comma]: self.player.pause() self.timeSlider.setValue(self.timeSlider.value() - 1) self.seek() elif event.key() in [ Qt.Key_Right, Qt.Key_D, Qt.Key_Greater, Qt.Key_Period ]: self.player.pause() self.timeSlider.setValue(self.timeSlider.value() + 1) self.seek() elif event.key() in [Qt.Key_Up, Qt.Key_Plus]: self.volumeSlider.setValue(self.volumeSlider.value() + 5) elif event.key() in [Qt.Key_Down, Qt.Key_Minus]: self.volumeSlider.setValue(self.volumeSlider.value() - 5) elif event.key() in [Qt.Key_Space]: self.togglePlay() elif event.key() in [Qt.Key_Return, Qt.Key_Enter, Qt.Key_O]: self.openFile() elif event.key() in [Qt.Key_F11, Qt.Key_F]: self.toggleFullscreen() elif event.key() in [Qt.Key_Tab, Qt.Key_L]: print("Open Playlist") elif event.key() in [Qt.Key_Slash, Qt.Key_Question]: print("Open Help") elif event.key() in [Qt.Key_Menu]: self.onRightClick(QPoint(0, 0)) elif event.key() in [Qt.Key_Escape]: self.player.close() elif QKeySequence(event.key() + int(event.modifiers())) == QKeySequence("Ctrl+V"): url = QApplication.clipboard().text() if "youtube.com" in url.lower(): video = pafy.new(url) best = video.getbest() playurl = best.url while not playurl: pass else: playurl = url self.player.createMedia(playurl) self.player.play() return super(Controller, self).keyPressEvent(event) def onLengthChanged(self, length): self.timeSlider.setMaxTime(length) self.timeSlider.setMaximum(int(length / 1000 * self.player.fps)) def onTimeChanged(self, pos): sliderPos = int(pos * self.timeSlider.maximum()) self.timeSlider.setValue(sliderPos) def onStateChanged(self, state): if state == 'NothingSpecial': return elif state == 'Opening': return elif state == 'Buffering': return elif state == 'Playing': self.playBtn.changeIcon(f"{self.resourcePath}/pause.svg") elif state == 'Paused': self.playBtn.changeIcon(f"{self.resourcePath}/play.svg") elif state == 'Stopped': self.playBtn.changeIcon(f"{self.resourcePath}/replay.svg") mediaPath = self.player.media.get_mrl() self.player.createMedia(mediaPath) self.player.pause() self.slide(.99) elif state == 'Ended': self.timeSlider.setValue(self.timeSlider.maximum()) elif state == 'Error': return def onRightClick(self, point): self.fullAct.setChecked(self.player.isFullScreen()) self.popMenu.exec_(self.mapToGlobal(point)) def openFile(self): fileName, _ = QFileDialog.getOpenFileName(self, "Open Movie", fileDir, "All (*.*)") if fileName != '': self.player.createMedia(fileName) self.player.play() def toggleWidget(self, state): if (self.sender().endValue() > 0 and state == QAbstractAnimation.State.Running): self.sender().targetObject().parent().show() elif (self.sender().endValue() <= 0 and state == QAbstractAnimation.State.Stopped): self.sender().targetObject().parent().hide() def toggleVisibility(self, visible=True): if self.visible == visible: return self.visible = visible if visible: self.setCursor(Qt.ArrowCursor) self.move(self.player.pos().x() + self.player.gripSize, self.player.pos().y() + self.player.gripSize) self.resize(self.player.width() - (self.player.gripSize * 2), self.player.height() - (self.player.gripSize * 2)) self.anims = self.opacityAnimation(1 if visible else 0, 300, self.toggleWidget) self.anims += [ self.heightAnimation(w, 20 if visible else 1, 300, self.toggleWidget) for w in (self.addBtn, self.timeSlider, self.listBtn, self.repeatBtn) ] for a in self.anims: a.start() self.timeSlider.setTipVisibility(visible) def toggleOnTop(self): # self.onTop = not self.onTop self.player.onTop(not self.player.isOnTop()) self.player.update() self.setWindowFlag(Qt.WindowStaysOnBottomHint, not self.player.isOnTop()) self.player.update() def toggleCursor(self): if (datetime.now() - self.lastMove).seconds >= 5: self.setCursor(Qt.BlankCursor) self.toggleVisibility(False) else: self.setCursor(Qt.ArrowCursor) def toggleFullscreen(self): if self.player.isFullScreen(): self.player.showNormal() else: self.player.showFullScreen() self.player.resizeController() def togglePlay(self): if not self.player.media: self.openFile() if self.player.isPlaying(): self.player.pause() else: self.player.play() def seek(self): self.player.setPosition(self.timeSlider.value() / self.timeSlider.maximum()) def slide(self, pos): self.player.setPosition(pos)
class MainWindow(QDialog): def onModeIndexChanged(self): self.OpenGLWidget.setMode(self.modeComboBox.currentIndex()) # onModeIndexChanged def onAlphaChanged(self): self.OpenGLWidget.setAlphaTest(self.alphaComboBox.currentIndex(), self.alphaSlider.value() / 100) # onAlphaChanged def onBlendChange(self): self.OpenGLWidget.setBlendTest(self.blendSComboBox.currentIndex(), self.blendDComboBox.currentIndex()) # onBlendChange def onSpinPointsChanged(self): self.OpenGLWidget.setPointsAmount(self.amountSpinBox.value()) self.amountSlider.setValue(self.amountSpinBox.value()) # onSpinPointsChanged def onSliderPointsChanged(self): self.OpenGLWidget.setPointsAmount(self.amountSpinBox.value()) self.amountSpinBox.setValue(self.amountSlider.value()) # onSliderPointsChanged def onScissorChanged(self): self.OpenGLWidget.setScissor(self.scissorX.value(), self.scissorY.value(), self.scissorW.value(), self.scissorH.value()) # onScissorChanged def __init__(self, parent=None): super(MainWindow, self).__init__(parent) self.setFixedSize(1200, 620) self.setWindowTitle("Vlasov Roman lr1-2") # OpenGL widget glWidgetW = 800 glWidgetH = 600 self.OpenGLWidget = OpenGLView() self.OpenGLWidget.setFixedSize(glWidgetW, glWidgetH) self.OpenGLWidget.setMode(0) # mode self.modeLabel = QLabel("Mode: ") self.modeComboBox = QComboBox() modes = { 0: "GL_POINTS", 1: "GL_LINES", 2: "GL_LINE_STRIP", 3: "GL_LINE_LOOP", 4: "GL_TRIANGLES", 5: "GL_TRIANGLE_STRIP", 6: "GL_TRIANGLE_FAN", 7: "GL_QUADS", 8: "GL_QUAD_STRIP", 9: "GL_POLYGON" } for mode in modes: self.modeComboBox.addItem(modes.get(mode)) self.modeComboBox.currentIndexChanged.connect(self.onModeIndexChanged) # number of points self.amountLabel = QLabel("Number of points: ") self.amountSpinBox = QSpinBox() self.amountSpinBox.setMinimum(4) self.amountSpinBox.setMaximum(100) self.amountSpinBox.valueChanged.connect(self.onSpinPointsChanged) self.amountSlider = QSlider(QtCore.Qt.Horizontal) self.amountSlider.setMinimum(4) self.amountSlider.setMaximum(100) self.amountSlider.valueChanged.connect(self.onSliderPointsChanged) self.amountMinLabel = QLabel("Min: 4") self.amountMinLabel.setFixedWidth(40) self.amountMaxLabel = QLabel("Max: 100") self.amountMaxLabel.setFixedWidth(60) line1 = QFrame() line1.setFrameShape(QFrame.HLine) layoutAmount = QHBoxLayout() layoutAmount.addWidget(self.amountMinLabel) layoutAmount.addWidget(self.amountSpinBox) layoutAmount.addWidget(self.amountMaxLabel) # GL_SCISSOR_TEST self.scissorLabel = QLabel("GL_SCISSOR_TEST") self.xLabel = QLabel("x") self.yLabel = QLabel("y") self.wLabel = QLabel("width") self.hLabel = QLabel("height") line2 = QFrame() line2.setFrameShape(QFrame.HLine) self.scissorX = QSlider(QtCore.Qt.Horizontal) self.scissorY = QSlider(QtCore.Qt.Horizontal) self.scissorW = QSlider(QtCore.Qt.Horizontal) self.scissorH = QSlider(QtCore.Qt.Horizontal) self.scissorX.setMinimum(0) self.scissorY.setMinimum(0) self.scissorW.setMinimum(0) self.scissorH.setMinimum(0) self.scissorX.setMaximum(glWidgetW) self.scissorY.setMaximum(glWidgetH) self.scissorW.setMaximum(glWidgetW) self.scissorH.setMaximum(glWidgetH) self.scissorX.valueChanged.connect(self.onScissorChanged) self.scissorY.valueChanged.connect(self.onScissorChanged) self.scissorW.valueChanged.connect(self.onScissorChanged) self.scissorH.valueChanged.connect(self.onScissorChanged) self.scissorH.setValue(self.scissorH.maximum()) self.scissorW.setValue(self.scissorW.maximum()) # GL_ALPHA_TEST self.alphaTestLabel = QLabel("GL_ALPHA_TEST") self.alphaLabel = QLabel("Function: ") self.alphaValueLabel = QLabel("Value: ") line3 = QFrame() line3.setFrameShape(QFrame.HLine) self.alphaComboBox = QComboBox() alpha = { 0: "GL_NEVER", 1: "GL_LESS", 2: "GL_EQUAL", 3: "GL_LEQUAL", 4: "GL_GREATER", 5: "GL_NOTEQUAL", 6: "GL_GEQUAL", 7: "GL_ALWAYS" } for mode in alpha: self.alphaComboBox.addItem(alpha.get(mode)) self.alphaComboBox.setCurrentIndex(7) self.alphaSlider = QSlider(QtCore.Qt.Horizontal) self.alphaSlider.setMinimum(0) self.alphaSlider.setMaximum(100) self.alphaComboBox.currentIndexChanged.connect(self.onAlphaChanged) self.alphaSlider.valueChanged.connect(self.onAlphaChanged) # GL_BLEND self.blendTestLabel = QLabel("GL_BLEND") self.sfactorLabel = QLabel("sfactor: ") self.dfactorLabel = QLabel("dfactor: ") line4 = QFrame() line4.setFrameShape(QFrame.HLine) self.blendSComboBox = QComboBox() self.blendDComboBox = QComboBox() sfactor = { 0: "GL_ZERO", 1: "GL_ONE", 2: "GL_DST_COLOR", 3: "GL_ONE_MINUS_DST_COLOR", 4: "GL_SRC_ALPHA", 5: "GL_ONE_MINUS_SRC_ALPHA", 6: "GL_DST_ALPHA", 7: "GL_ONE_MINUS_DST_ALPHA", 8: "GL_SRC_ALPHA_SATURATE" } dfactor = { 0: "GL_ZERO", 1: "GL_ONE", 2: "GL_SRC_COLOR", 3: "GL_ONE_MINUS_SRC_COLOR", 4: "GL_SRC_ALPHA", 5: "GL_ONE_MINUS_SRC_ALPHA", 6: "GL_DST_ALPHA", 7: "GL_ONE_MINUS_DST_ALPHA" } for mode in sfactor: self.blendSComboBox.addItem(sfactor.get(mode)) for mode in dfactor: self.blendDComboBox.addItem(dfactor.get(mode)) self.blendSComboBox.currentIndexChanged.connect(self.onBlendChange) self.blendDComboBox.currentIndexChanged.connect(self.onBlendChange) self.blendSComboBox.setCurrentIndex(1) # tools panel layout layoutTools = QVBoxLayout() layoutTools.addWidget(self.amountLabel) layoutTools.addLayout(layoutAmount) layoutTools.addWidget(self.amountSlider) layoutTools.addWidget(line1) layoutTools.addWidget(self.modeLabel) layoutTools.addWidget(self.modeComboBox) layoutTools.addWidget(line2) layoutTools.addWidget(self.scissorLabel) layoutTools.addWidget(self.xLabel) layoutTools.addWidget(self.scissorX) layoutTools.addWidget(self.yLabel) layoutTools.addWidget(self.scissorY) layoutTools.addWidget(self.wLabel) layoutTools.addWidget(self.scissorW) layoutTools.addWidget(self.hLabel) layoutTools.addWidget(self.scissorH) layoutTools.addWidget(line3) layoutTools.addWidget(self.alphaTestLabel) layoutTools.addWidget(self.alphaLabel) layoutTools.addWidget(self.alphaComboBox) layoutTools.addWidget(self.alphaValueLabel) layoutTools.addWidget(self.alphaSlider) layoutTools.addWidget(line4) layoutTools.addWidget(self.blendTestLabel) layoutTools.addWidget(self.sfactorLabel) layoutTools.addWidget(self.blendSComboBox) layoutTools.addWidget(self.dfactorLabel) layoutTools.addWidget(self.blendDComboBox) verticalSpacer = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding) layoutTools.addItem(verticalSpacer) # window layout layout = QHBoxLayout() layout.addWidget(self.OpenGLWidget) layout.addLayout(layoutTools) self.setLayout(layout)
class ApplicationWindow(QMainWindow): def __init__(self, parent=None): QMainWindow.__init__(self, parent) self.column_names = ["Column A", "Column B", "Column C"] # Central widget self._main = QWidget() self.setCentralWidget(self._main) # Main menu bar self.menu = self.menuBar() self.menu_file = self.menu.addMenu("File") exit = QAction("Exit", self, triggered=qApp.quit) self.menu_file.addAction(exit) self.menu_about = self.menu.addMenu("&About") about = QAction("About Qt", self, shortcut=QKeySequence(QKeySequence.HelpContents), triggered=qApp.aboutQt) self.menu_about.addAction(about) # Figure (Left) self.fig = Figure(figsize=(5, 3)) self.canvas = FigureCanvas(self.fig) # Sliders (Left) self.slider_azim = QSlider(minimum=0, maximum=360, orientation=Qt.Horizontal) self.slider_elev = QSlider(minimum=0, maximum=360, orientation=Qt.Horizontal) self.slider_azim_layout = QHBoxLayout() self.slider_azim_layout.addWidget(QLabel("{}".format(self.slider_azim.minimum()))) self.slider_azim_layout.addWidget(self.slider_azim) self.slider_azim_layout.addWidget(QLabel("{}".format(self.slider_azim.maximum()))) self.slider_elev_layout = QHBoxLayout() self.slider_elev_layout.addWidget(QLabel("{}".format(self.slider_elev.minimum()))) self.slider_elev_layout.addWidget(self.slider_elev) self.slider_elev_layout.addWidget(QLabel("{}".format(self.slider_elev.maximum()))) # Table (Right) self.table = QTableWidget() header = self.table.horizontalHeader() header.setSectionResizeMode(QHeaderView.Stretch) # ComboBox (Right) self.combo = QComboBox() self.combo.addItems(["Wired", "Surface", "Triangular Surface", "Sphere"]) # Right layout rlayout = QVBoxLayout() rlayout.setContentsMargins(1, 1, 1, 1) rlayout.addWidget(QLabel("Plot type:")) rlayout.addWidget(self.combo) rlayout.addWidget(self.table) # Left layout llayout = QVBoxLayout() rlayout.setContentsMargins(1, 1, 1, 1) llayout.addWidget(self.canvas, 88) llayout.addWidget(QLabel("Azimuth:"), 1) llayout.addLayout(self.slider_azim_layout, 5) llayout.addWidget(QLabel("Elevation:"), 1) llayout.addLayout(self.slider_elev_layout, 5) # Main layout layout = QHBoxLayout(self._main) layout.addLayout(llayout, 70) layout.addLayout(rlayout, 30) # Signal and Slots connections self.combo.currentTextChanged.connect(self.combo_option) self.slider_azim.valueChanged.connect(self.rotate_azim) self.slider_elev.valueChanged.connect(self.rotate_elev) # Initial setup self.plot_wire() self._ax.view_init(30, 30) self.slider_azim.setValue(30) self.slider_elev.setValue(30) self.fig.canvas.mpl_connect("button_release_event", self.on_click) # Matplotlib slot method def on_click(self, event): azim, elev = self._ax.azim, self._ax.elev self.slider_azim.setValue(azim + 180) self.slider_elev.setValue(elev + 180) # Utils methods def set_table_data(self, X, Y, Z): for i in range(len(X)): self.table.setItem(i, 0, QTableWidgetItem("{:.2f}".format(X[i]))) self.table.setItem(i, 1, QTableWidgetItem("{:.2f}".format(Y[i]))) self.table.setItem(i, 2, QTableWidgetItem("{:.2f}".format(Z[i]))) def set_canvas_table_configuration(self, row_count, data): self.fig.set_canvas(self.canvas) self._ax = self.canvas.figure.add_subplot(projection="3d") self._ax.set_xlabel(self.column_names[0]) self._ax.set_ylabel(self.column_names[1]) self._ax.set_zlabel(self.column_names[2]) self.table.setRowCount(row_count) self.table.setColumnCount(3) self.table.setHorizontalHeaderLabels(self.column_names) self.set_table_data(data[0], data[1], data[2]) # Plot methods def plot_wire(self): # Data self.X, self.Y, self.Z = axes3d.get_test_data(0.03) self.set_canvas_table_configuration(len(self.X[0]), (self.X[0], self.Y[0], self.Z[0])) self._ax.plot_wireframe(self.X, self.Y, self.Z, rstride=10, cstride=10, cmap="viridis") self.canvas.draw() def plot_surface(self): # Data self.X, self.Y = np.meshgrid(np.linspace(-6, 6, 30), np.linspace(-6, 6, 30)) self.Z = np.sin(np.sqrt(self.X ** 2 + self.Y ** 2)) self.set_canvas_table_configuration(len(self.X[0]), (self.X[0], self.Y[0], self.Z[0])) self._ax.plot_surface(self.X, self.Y, self.Z, rstride=1, cstride=1, cmap="viridis", edgecolor="none") self.canvas.draw() def plot_triangular_surface(self): # Data radii = np.linspace(0.125, 1.0, 8) angles = np.linspace(0, 2 * np.pi, 36, endpoint=False)[..., np.newaxis] self.X = np.append(0, (radii * np.cos(angles)).flatten()) self.Y = np.append(0, (radii * np.sin(angles)).flatten()) self.Z = np.sin(-self.X * self.Y) self.set_canvas_table_configuration(len(self.X), (self.X, self.Y, self.Z)) self._ax.plot_trisurf(self.X, self.Y, self.Z, linewidth=0.2, antialiased=True) self.canvas.draw() def plot_sphere(self): # Data u = np.linspace(0, 2 * np.pi, 100) v = np.linspace(0, np.pi, 100) self.X = 10 * np.outer(np.cos(u), np.sin(v)) self.Y = 10 * np.outer(np.sin(u), np.sin(v)) self.Z = 9 * np.outer(np.ones(np.size(u)), np.cos(v)) self.set_canvas_table_configuration(len(self.X), (self.X[0], self.Y[0], self.Z[0])) self._ax.plot_surface(self.X, self.Y, self.Z) self.canvas.draw() # Slots @Slot() def combo_option(self, text): if text == "Wired": self.plot_wire() elif text == "Surface": self.plot_surface() elif text == "Triangular Surface": self.plot_triangular_surface() elif text == "Sphere": self.plot_sphere() @Slot() def rotate_azim(self, value): self._ax.view_init(self._ax.elev, value) self.fig.set_canvas(self.canvas) self.canvas.draw() @Slot() def rotate_elev(self, value): self._ax.view_init(value, self._ax.azim) self.fig.set_canvas(self.canvas) self.canvas.draw()
class _InputHeader(QGroupBox): def __init__(self, parent, header): super().__init__(parent) self.header = header self.header_index, _ = self.parent().table.model_c.metadata_c.get_header_meta(self.header["name"]) self.setStyleSheet(styles.filter_line_edit) # LAYOUTS self.grid = QGridLayout() self.v_layout = QVBoxLayout() self.h_layout = QHBoxLayout() # FIELDS self.header_label = QLabel(f'{self.header["name"]} : ', self) self.header_label.setFixedWidth(100) self.input = QLineEdit(self) self.input.setMaxLength(self.header["data_type"]["max_value"]) self.input.setPlaceholderText(str(self.parent().table.model_c.get_common_values(header["name"]))) self.exact = QCheckBox("Exact", self) self.exact.setToolTip(f"Value in '{self.header['name']}' must be exacly '{self.input.text()}'") self.exact.stateChanged.connect(self.exact_checked) # SLIDER SET UP self.slider = None if self.header["data_type"]["type"] == "int": self.slider = QSlider(Qt.Horizontal, self) (self.slider.min_v, self.slider.max_v) = self.parent().table.get_min_max(self.header["name"]) self.slider.setMinimum(self.slider.min_v) self.slider.setMaximum(self.slider.max_v) self.slider.valueChanged.connect(self.slider_value_changed) self.slider.hint = QLabel(f'{self.slider.minimum()} - {self.slider.value()}', self) # SET UP self.h_layout.addWidget(self.input) self.h_layout.addWidget(self.exact) self.v_layout.addLayout(self.h_layout) if self.slider is not None: self.v_layout.addWidget(self.slider.hint) self.v_layout.addWidget(self.slider) self.grid.addWidget(self.header_label, 0, 0) self.grid.addLayout(self.v_layout, 0, 1) self.setLayout(self.grid) def get_condition(self): if self.input.text() == "" and self.slider is None: return None if self.slider is not None: if self.input.text() == "" and \ (self.slider.value() == self.slider.maximum() or self.slider.value() == self.slider.minimum()): return None return { "header_name": self.header["name"], "value": self.input.text(), "exact": self.exact.isChecked(), "header_meta": { "index": self.header_index, "meta": self.header }, "value_additional_info": { "contains": not self.exact.isChecked(), "min_value": self.slider.min_v if self.slider is not None else "", "max_value": self.slider.value() if self.slider is not None else "" } } def exact_checked(self): if self.slider is None: return self.slider.hide() if self.exact.isChecked() else self.slider.show() def slider_value_changed(self): self.slider.hint.setText(f'{self.slider.minimum()} - {self.slider.value()}')
class ImageSeriesToolbar(QWidget): def __init__(self, name, parent=None): super(ImageSeriesToolbar, self).__init__(parent) self.ims = HexrdConfig().imageseries(name) self.slider = None self.frame = None self.layout = None self.widget = None self.show = False self.name = name self.create_widget() self.set_range() self.setup_connections() def setup_connections(self): self.slider.valueChanged.connect(self.val_changed) self.slider.valueChanged.connect(self.frame.setValue) self.frame.valueChanged.connect(self.slider.setSliderPosition) def create_widget(self): self.slider = QSlider(Qt.Horizontal, self.parent()) self.frame = QSpinBox(self.parent()) self.widget = QWidget(self.parent()) self.layout = QGridLayout(self.widget) self.layout.addWidget(self.slider, 0, 0, 1, 9) self.layout.addWidget(self.frame, 0, 9, 1, 1) self.widget.setLayout(self.layout) if self.ims and len(self.ims) > 1: self.show = True self.widget.setVisible(self.show) def set_range(self, current_tab=False): if self.ims: size = len(self.ims) - 1 if (not size or not current_tab) and self.show: self.show = False elif size and not self.show and current_tab: self.show = True self.widget.setVisible(self.show) self.slider.setMinimumWidth(self.parent().width() / 2) if not size == self.slider.maximum(): self.slider.setMaximum(size) self.frame.setMaximum(size) self.slider.setValue(0) self.frame.setValue(self.slider.value()) else: self.show = False self.widget.setVisible(self.show) def update_range(self, current_tab): self.ims = HexrdConfig().imageseries(self.name) self.set_range(current_tab) if self.slider.value() != HexrdConfig().current_imageseries_idx: self.val_changed(self.slider.value()) def update_name(self, name): if not name == self.name: self.name = name def set_visible(self, b=False): self.widget.setVisible(b and len(self.ims) > 1) def val_changed(self, pos): self.parent().change_ims_image(pos)