class PolymaskGeneratorWindow(QWidget): def __init__(self, splines=[]): super().__init__() self.setMinimumSize(1628, 742) # 1600 plus margins self.setMaximumSize(1628, 742) self.setGeometry(100, 100, 1628, 742) layout = QGridLayout(self) self.setLayout(layout) self.setWindowTitle("Polymask Generator") panelStyleSheet = """ .QWidget { border: 2px solid black; } """ right_panel = QSplitter(self) right_panel.setChildrenCollapsible(False) generate_panel = QWidget(right_panel) generate_panel.setStyleSheet(panelStyleSheet) generate_layout = QVBoxLayout(generate_panel) generate_panel.setLayout(generate_layout) control_polygon_panel = QWidget(right_panel) control_polygon_panel.setStyleSheet(panelStyleSheet) control_polygon_layout = QVBoxLayout(control_polygon_panel) control_polygon_panel.setLayout(control_polygon_layout) control_splines_panel = QWidget(right_panel) control_splines_panel.setStyleSheet(panelStyleSheet) control_splines_layout = QVBoxLayout(control_splines_panel) control_splines_panel.setLayout(control_splines_layout) saveButton = QPushButton("Save", right_panel) saveButton.clicked.connect(self.save) generate_layout.addWidget(saveButton) generateButton = QPushButton("Show filled polygons", right_panel) generateButton.clicked.connect(self.generateTriangles) generate_layout.addWidget(generateButton) backgroundButton = QPushButton("Select background image", right_panel) backgroundButton.clicked.connect(self.setBackground) generate_layout.addWidget(backgroundButton) colorGrid = QWidget(right_panel) colorGrid.setStyleSheet(""" .QWidget { border: 0px solid black; } """) colorLayout = QGridLayout(colorGrid) colorLayout.setColumnStretch(1, 1) colorLayout.setSpacing(1) wnd = self splineColorButton = QPushButton("Set spline color", colorGrid) splineColorButton.clicked.connect( lambda: wnd.setSplineColor("splineColor")) colorLayout.addWidget(splineColorButton) splineColorButton = QPushButton("Set selected spline color", colorGrid) splineColorButton.clicked.connect( lambda: wnd.setSplineColor("selectedSplineColor")) colorLayout.addWidget(splineColorButton, 0, 1) splineColorButton = QPushButton("Set control point color", colorGrid) splineColorButton.clicked.connect( lambda: wnd.setSplineColor("CPColor")) colorLayout.addWidget(splineColorButton, 1, 0) splineColorButton = QPushButton("Set selected control point color", colorGrid) splineColorButton.clicked.connect( lambda: wnd.setSplineColor("selectedCPColor")) colorLayout.addWidget(splineColorButton, 1, 1) colorGrid.setLayout(colorLayout) generate_layout.addWidget(colorGrid) self.cp_list = QListWidget() self.cp_list.setFixedHeight(100) self.cp_list.currentItemChanged.connect(self.currentPointChanged) control_polygon_layout.addWidget(self.cp_list) addBeforeButton = QPushButton("Add Controlpoint before selected", right_panel) addBeforeButton.clicked.connect(self.addBefore) control_polygon_layout.addWidget(addBeforeButton) addAfterButton = QPushButton("Add Controlpoint after selected", right_panel) addAfterButton.clicked.connect(self.addAfter) control_polygon_layout.addWidget(addAfterButton) removeButton = QPushButton("Remove selected Controlpoint", right_panel) removeButton.clicked.connect(self.removeCurrent) control_polygon_layout.addWidget(removeButton) self.curve_list = QListWidget() self.curve_list.setFixedHeight(100) splines = self.set_control_points(splines) self.curve_list.currentItemChanged.connect(self.currentSplineChanged) control_splines_layout.addWidget(self.curve_list) addSplineButton = QPushButton("Add New Spline", right_panel) addSplineButton.clicked.connect(self.addSpline) control_splines_layout.addWidget(addSplineButton) removeSplineButton = QPushButton("Remove selected Spline", right_panel) removeSplineButton.clicked.connect(self.removeSpline) control_splines_layout.addWidget(removeSplineButton) right_panel.setOrientation(Qt.Vertical) right_panel.addWidget(generate_panel) right_panel.addWidget(control_polygon_panel) right_panel.addWidget(control_splines_panel) self.polymaskGenerator = PolymaskGenerator(self, self.winId(), splines, self.dataChanged) layout.addWidget(self.polymaskGenerator) layout.setColumnStretch(0, 4) self.cp_list.setCurrentRow(0) self.curve_list.setCurrentRow(0) layout.addWidget(right_panel, 0, 1) layout.setColumnStretch(1, 1) def set_control_points(self, splines): self.cp_list.clear() self.curve_list.clear() if len(splines) == 0: controlPoints = [ ar([-0.25, -0.25]), ar([-0.25, 0.25]), ar([0.25, 0.25]), ar([0.25, -0.25]) ] splines = [controlPoints] idx = 0 for npcp in splines[0]: cp = npcp.tolist() if len(cp) < 2: print("Error: controlPoint size less then 2") continue self.cp_list.addItem(self.pointToString(cp)) idx += 1 for idx in range(len(splines)): self.curve_list.addItem("Spline " + str(idx)) return splines def pointToString(self, point): return "( " + "{:.4f}".format(point[0]) + ", " + "{:.4f}".format( point[1]) + " )" def save(self): if self.generateTriangles(): self.saveFunction(self.polymaskGenerator.fill, self.polymaskGenerator.splines) self.close() def addBefore(self): self.polymaskGenerator.addBefore(self.cp_list.currentRow()) def addAfter(self): self.polymaskGenerator.addAfter(self.cp_list.currentRow()) def removeCurrent(self): self.polymaskGenerator.removeCurrent(self.cp_list.currentRow()) def generateTriangles(self): return self.polymaskGenerator.generateTriangles() def set(self, saveFunction, splines): self.saveFunction = saveFunction if len(splines) > 0: self.set_control_points(splines) self.polymaskGenerator.splines = splines self.cp_list.setCurrentRow(0) self.curve_list.setCurrentRow(0) def dataChanged(self, type, data): if type == PolymaskChangeEvent.SelectionChanged: if self.curve_list.currentRow() != data["s_idx"]: self.changeSpline(data["s_idx"]) self.cp_list.setCurrentRow(data["idx"]) elif type == PolymaskChangeEvent.ItemChanged: self.cp_list.item(data["idx"]).setText( self.pointToString(data["value"])) elif type == PolymaskChangeEvent.ItemAdded: self.cp_list.insertItem(data["idx"], self.pointToString(data["value"])) elif type == PolymaskChangeEvent.ItemRemoved: self.cp_list.takeItem(data["idx"]) def changeSpline(self, idx): self.curve_list.setCurrentRow(idx) self.cp_list.clear() for s in self.polymaskGenerator.splines[idx]: self.cp_list.addItem(self.pointToString(s.tolist())) def currentPointChanged(self): self.polymaskGenerator.currentPointChanged(self.cp_list.currentRow()) def closeEvent(self, event): self.saveFunction = None event.accept() def addSpline(self): points = [[-0.25, -0.25], [-0.25, 0.25], [0.25, 0.25], [0.25, -0.25]] self.curve_list.addItem("Spline " + str(len(self.polymaskGenerator.splines))) self.polymaskGenerator.addSpline(points) def removeSpline(self): current = self.curve_list.currentRow() if current >= 0: self.curve_list.takeItem(current) self.polymaskGenerator.removeSpline(current, self.curve_list.currentRow()) def currentSplineChanged(self): self.polymaskGenerator.currentSplineChanged( self.curve_list.currentRow()) def setBackground(self): fname = QFileDialog.getOpenFileName( self, 'Select Background Image', "", "Image files(*.jpg *.png *.gif *.bmp);;All Files (*)") self.polymaskGenerator.setBackground(fname[0]) def setSplineColor(self, obj): color = QColorDialog().getColor() self.polymaskGenerator.setSplineColor(obj, color.red(), color.green(), color.blue())
class SimulationGui(QMainWindow): """ class for the graphical user interface """ # TODO enable closing plot docks by right-clicking their name runSimulation = pyqtSignal() stopSimulation = pyqtSignal() playbackTimeChanged = pyqtSignal() regimeFinished = pyqtSignal() finishedRegimeBatch = pyqtSignal(bool) def __init__(self): # constructor of the base class QMainWindow.__init__(self) QCoreApplication.setOrganizationName("RST") QCoreApplication.setOrganizationDomain("https://tu-dresden.de/rst") QCoreApplication.setApplicationVersion( pkg_resources.require("PyMoskito")[0].version) QCoreApplication.setApplicationName(globals()["__package__"]) # load settings self._settings = QSettings() self._read_settings() # initialize logger self._logger = logging.getLogger(self.__class__.__name__) # Create Simulation Backend self.guiProgress = None self.cmdProgress = None self.sim = SimulatorInteractor(self) self.runSimulation.connect(self.sim.run_simulation) self.stopSimulation.connect(self.sim.stop_simulation) self.sim.simulation_finalized.connect(self.new_simulation_data) self.currentDataset = None self.interpolator = None # sim setup viewer self.targetView = SimulatorView(self) self.targetView.setModel(self.sim.target_model) self.targetView.expanded.connect(self.target_view_changed) self.targetView.collapsed.connect(self.target_view_changed) # sim results viewer self.result_view = QTreeView() # the docking area allows to rearrange the user interface at runtime self.area = pg.dockarea.DockArea() # Window properties icon_size = QSize(25, 25) self.setCentralWidget(self.area) self.resize(1000, 700) self.setWindowTitle("PyMoskito") res_path = get_resource("mosquito.png") icon = QIcon(res_path) self.setWindowIcon(icon) # create docks self.propertyDock = pg.dockarea.Dock("Properties") self.animationDock = pg.dockarea.Dock("Animation") self.regimeDock = pg.dockarea.Dock("Regimes") self.dataDock = pg.dockarea.Dock("Data") self.logDock = pg.dockarea.Dock("Log") self.plotDockPlaceholder = pg.dockarea.Dock("Placeholder") # arrange docks self.area.addDock(self.animationDock, "right") self.area.addDock(self.regimeDock, "left", self.animationDock) self.area.addDock(self.propertyDock, "bottom", self.regimeDock) self.area.addDock(self.dataDock, "bottom", self.propertyDock) self.area.addDock(self.plotDockPlaceholder, "bottom", self.animationDock) self.area.addDock(self.logDock, "bottom", self.dataDock) self.non_plotting_docks = list(self.area.findAll()[1].keys()) # add widgets to the docks self.propertyDock.addWidget(self.targetView) if not vtk_available: self._logger.error( "loading vtk failed with:{}".format(vtk_error_msg)) # check if there is a registered visualizer available_vis = get_registered_visualizers() self._logger.info("found visualizers: {}".format( [name for cls, name in available_vis])) if available_vis: # instantiate the first visualizer self._logger.info("loading visualizer '{}'".format( available_vis[0][1])) self.animationLayout = QVBoxLayout() if issubclass(available_vis[0][0], MplVisualizer): self.animationWidget = QWidget() self.visualizer = available_vis[0][0](self.animationWidget, self.animationLayout) self.animationDock.addWidget(self.animationWidget) elif issubclass(available_vis[0][0], VtkVisualizer): if vtk_available: # vtk window self.animationFrame = QFrame() self.vtkWidget = QVTKRenderWindowInteractor( self.animationFrame) self.animationLayout.addWidget(self.vtkWidget) self.animationFrame.setLayout(self.animationLayout) self.animationDock.addWidget(self.animationFrame) self.vtk_renderer = vtkRenderer() self.vtkWidget.GetRenderWindow().AddRenderer( self.vtk_renderer) self.visualizer = available_vis[0][0](self.vtk_renderer) self.vtkWidget.Initialize() else: self._logger.warning("visualizer depends on vtk which is " "not available on this system!") elif available_vis: raise NotImplementedError else: self.visualizer = None # regime window self.regime_list = QListWidget(self) self.regime_list.setSelectionMode(QAbstractItemView.ExtendedSelection) self.regimeDock.addWidget(self.regime_list) self.regime_list.itemDoubleClicked.connect(self.regime_dclicked) self._regimes = [] self.regime_file_name = "" self.actDeleteRegimes = QAction(self.regime_list) self.actDeleteRegimes.setText("&Delete Selected Regimes") # TODO shortcut works always, not only with focus on the regime list # self.actDeleteRegimes.setShortcutContext(Qt.WindowShortcut) self.actDeleteRegimes.setShortcut(QKeySequence(Qt.Key_Delete)) self.actDeleteRegimes.triggered.connect(self.remove_regime_items) self.actSave = QAction(self) self.actSave.setText('Save Results As') self.actSave.setIcon(QIcon(get_resource("save.png"))) self.actSave.setDisabled(True) self.actSave.setShortcut(QKeySequence.Save) self.actSave.triggered.connect(self.export_simulation_data) self.actLoadRegimes = QAction(self) self.actLoadRegimes.setText("Load Regimes from File") self.actLoadRegimes.setIcon(QIcon(get_resource("load.png"))) self.actLoadRegimes.setDisabled(False) self.actLoadRegimes.setShortcut(QKeySequence.Open) self.actLoadRegimes.triggered.connect(self.load_regime_dialog) self.actExitOnBatchCompletion = QAction(self) self.actExitOnBatchCompletion.setText("&Exit On Batch Completion") self.actExitOnBatchCompletion.setCheckable(True) self.actExitOnBatchCompletion.setChecked( self._settings.value("control/exit_on_batch_completion") == "True") self.actExitOnBatchCompletion.changed.connect( self.update_exit_on_batch_completion_setting) # regime management self.runningBatch = False self._current_regime_index = None self._current_regime_name = None self._regimes = [] self.regimeFinished.connect(self.run_next_regime) self.finishedRegimeBatch.connect(self.regime_batch_finished) # data window self.dataList = QListWidget(self) self.dataDock.addWidget(self.dataList) self.dataList.itemDoubleClicked.connect(self.create_plot) # actions for simulation control self.actSimulateCurrent = QAction(self) self.actSimulateCurrent.setText("&Simulate Current Regime") self.actSimulateCurrent.setIcon(QIcon(get_resource("simulate.png"))) self.actSimulateCurrent.setShortcut(QKeySequence("F5")) self.actSimulateCurrent.triggered.connect(self.start_simulation) self.actSimulateAll = QAction(self) self.actSimulateAll.setText("Simulate &All Regimes") self.actSimulateAll.setIcon(QIcon(get_resource("execute_regimes.png"))) self.actSimulateAll.setShortcut(QKeySequence("F6")) self.actSimulateAll.setDisabled(True) self.actSimulateAll.triggered.connect(self.start_regime_execution) # actions for animation control self.actAutoPlay = QAction(self) self.actAutoPlay.setText("&Autoplay Simulation") self.actAutoPlay.setCheckable(True) self.actAutoPlay.setChecked( self._settings.value("control/autoplay_animation") == "True") self.actAutoPlay.changed.connect(self.update_autoplay_setting) self.actPlayPause = QAction(self) self.actPlayPause.setText("Play Animation") self.actPlayPause.setIcon(QIcon(get_resource("play.png"))) self.actPlayPause.setDisabled(True) self.actPlayPause.setShortcut(QKeySequence(Qt.Key_Space)) self.actPlayPause.triggered.connect(self.play_animation) self.actStop = QAction(self) self.actStop.setText("Stop") self.actStop.setIcon(QIcon(get_resource("stop.png"))) self.actStop.setDisabled(True) self.actStop.triggered.connect(self.stop_animation) self.actSlow = QAction(self) self.actSlow.setText("Slowest") self.actSlow.setIcon(QIcon(get_resource("slow.png"))) self.actSlow.setDisabled(False) self.actSlow.triggered.connect(self.set_slowest_playback_speed) self.actFast = QAction(self) self.actFast.setText("Fastest") self.actFast.setIcon(QIcon(get_resource("fast.png"))) self.actFast.setDisabled(False) self.actFast.triggered.connect(self.set_fastest_playback_speed) self.speedControl = QSlider(Qt.Horizontal, self) self.speedControl.setMaximumSize(200, 25) self.speedControl.setTickPosition(QSlider.TicksBothSides) self.speedControl.setDisabled(False) self.speedControl.setMinimum(0) self.speedControl.setMaximum(12) self.speedControl.setValue(6) self.speedControl.setTickInterval(6) self.speedControl.setSingleStep(2) self.speedControl.setPageStep(3) self.speedControl.valueChanged.connect(self.update_playback_speed) self.timeSlider = QSlider(Qt.Horizontal, self) self.timeSlider.setMinimum(0) self.timeSliderRange = 1000 self.timeSlider.setMaximum(self.timeSliderRange) self.timeSlider.setTickInterval(1) self.timeSlider.setTracking(True) self.timeSlider.setDisabled(True) self.timeSlider.valueChanged.connect(self.update_playback_time) self.playbackTime = .0 self.playbackGain = 1 self.currentStepSize = .0 self.currentEndTime = .0 self.playbackTimer = QTimer() self.playbackTimer.timeout.connect(self.increment_playback_time) self.playbackTimeChanged.connect(self.update_gui) self.playbackTimeout = 33 # in [ms] -> 30 fps self.actResetCamera = QAction(self) self.actResetCamera.setText("Reset Camera") self.actResetCamera.setIcon(QIcon(get_resource("reset_camera.png"))) self.actResetCamera.setDisabled(True) if available_vis: self.actResetCamera.setEnabled(self.visualizer.can_reset_view) self.actResetCamera.triggered.connect(self.reset_camera_clicked) # postprocessing self.actPostprocessing = QAction(self) self.actPostprocessing.setText("Launch Postprocessor") self.actPostprocessing.setIcon(QIcon(get_resource("processing.png"))) self.actPostprocessing.setDisabled(False) self.actPostprocessing.triggered.connect(self.postprocessing_clicked) self.actPostprocessing.setShortcut(QKeySequence("F7")) self.postprocessor = None # toolbar self.toolbarSim = QToolBar("Simulation") self.toolbarSim.setContextMenuPolicy(Qt.PreventContextMenu) self.toolbarSim.setMovable(False) self.toolbarSim.setIconSize(icon_size) self.addToolBar(self.toolbarSim) self.toolbarSim.addAction(self.actLoadRegimes) self.toolbarSim.addAction(self.actSave) self.toolbarSim.addSeparator() self.toolbarSim.addAction(self.actSimulateCurrent) self.toolbarSim.addAction(self.actSimulateAll) self.toolbarSim.addSeparator() self.toolbarSim.addAction(self.actPlayPause) self.toolbarSim.addAction(self.actStop) self.toolbarSim.addWidget(self.timeSlider) self.toolbarSim.addSeparator() self.toolbarSim.addAction(self.actSlow) self.toolbarSim.addWidget(self.speedControl) self.toolbarSim.addAction(self.actFast) self.toolbarSim.addSeparator() self.toolbarSim.addAction(self.actPostprocessing) self.toolbarSim.addAction(self.actResetCamera) self.postprocessor = None # log dock self.logBox = QPlainTextEdit(self) self.logBox.setReadOnly(True) self.logDock.addWidget(self.logBox) # init logger for logging box self.textLogger = PlainTextLogger(logging.INFO) self.textLogger.set_target_cb(self.logBox.appendPlainText) logging.getLogger().addHandler(self.textLogger) # menu bar fileMenu = self.menuBar().addMenu("&File") fileMenu.addAction(self.actLoadRegimes) fileMenu.addAction(self.actSave) fileMenu.addAction("&Quit", self.close) editMenu = self.menuBar().addMenu("&Edit") editMenu.addAction(self.actDeleteRegimes) simMenu = self.menuBar().addMenu("&Simulation") simMenu.addAction(self.actSimulateCurrent) simMenu.addAction(self.actSimulateAll) simMenu.addAction(self.actExitOnBatchCompletion) simMenu.addAction(self.actPostprocessing) animMenu = self.menuBar().addMenu("&Animation") animMenu.addAction(self.actPlayPause) animMenu.addAction("&Increase Playback Speed", self.increment_playback_speed, QKeySequence(Qt.CTRL + Qt.Key_Plus)) animMenu.addAction("&Decrease Playback Speed", self.decrement_playback_speed, QKeySequence(Qt.CTRL + Qt.Key_Minus)) animMenu.addAction("&Reset Playback Speed", self.reset_playback_speed, QKeySequence(Qt.CTRL + Qt.Key_0)) animMenu.addAction(self.actAutoPlay) animMenu.addAction(self.actResetCamera) helpMenu = self.menuBar().addMenu("&Help") helpMenu.addAction("&Online Documentation", self.show_online_docs) helpMenu.addAction("&About", self.show_info) # status bar self.status = QStatusBar(self) self.setStatusBar(self.status) self.statusLabel = QLabel("Ready.") self.statusBar().addPermanentWidget(self.statusLabel) self.timeLabel = QLabel("current time: 0.0") self.statusBar().addPermanentWidget(self.timeLabel) self._logger.info("Simulation GUI is up and running.") def _read_settings(self): # add default settings if none are present if not self._settings.contains("path/simulation_results"): self._settings.setValue( "path/simulation_results", os.path.join(os.path.curdir, "results", "simulation")) if not self._settings.contains("path/postprocessing_results"): self._settings.setValue( "path/postprocessing_results", os.path.join(os.path.curdir, "results", "postprocessing")) if not self._settings.contains("path/metaprocessing_results"): self._settings.setValue( "path/metaprocessing_results", os.path.join(os.path.curdir, "results", "metaprocessing")) if not self._settings.contains("control/autoplay_animation"): self._settings.setValue("control/autoplay_animation", "False") if not self._settings.contains("control/exit_on_batch_completion"): self._settings.setValue("control/exit_on_batch_completion", "False") def _write_settings(self): """ Store the application state. """ pass @pyqtSlot() def update_autoplay_setting(self): self._settings.setValue("control/autoplay_animation", str(self.actAutoPlay.isChecked())) @pyqtSlot() def update_exit_on_batch_completion_setting(self, state=None): if state is None: state = self.actExitOnBatchCompletion.isChecked() self._settings.setValue("control/exit_on_batch_completion", str(state)) def set_visualizer(self, vis): self.visualizer = vis self.vtkWidget.Initialize() @pyqtSlot() def play_animation(self): """ play the animation """ self._logger.debug("Starting Playback") # if we are at the end, start from the beginning if self.playbackTime == self.currentEndTime: self.timeSlider.setValue(0) self.actPlayPause.setText("Pause Animation") self.actPlayPause.setIcon(QIcon(get_resource("pause.png"))) self.actPlayPause.triggered.disconnect(self.play_animation) self.actPlayPause.triggered.connect(self.pause_animation) self.playbackTimer.start(self.playbackTimeout) @pyqtSlot() def pause_animation(self): """ pause the animation """ self._logger.debug("Pausing Playback") self.playbackTimer.stop() self.actPlayPause.setText("Play Animation") self.actPlayPause.setIcon(QIcon(get_resource("play.png"))) self.actPlayPause.triggered.disconnect(self.pause_animation) self.actPlayPause.triggered.connect(self.play_animation) def stop_animation(self): """ Stop the animation if it is running and reset the playback time. """ self._logger.debug("Stopping Playback") if self.actPlayPause.text() == "Pause Animation": # animation is playing -> stop it self.playbackTimer.stop() self.actPlayPause.setText("Play Animation") self.actPlayPause.setIcon(QIcon(get_resource("play.png"))) self.actPlayPause.triggered.disconnect(self.pause_animation) self.actPlayPause.triggered.connect(self.play_animation) self.timeSlider.setValue(0) @pyqtSlot() def start_simulation(self): """ start the simulation and disable start button """ if self._current_regime_index is None: regime_name = "" else: regime_name = str( self.regime_list.item(self._current_regime_index).text()) self.statusLabel.setText("simulating {}".format(regime_name)) self._logger.info("Simulating: {}".format(regime_name)) self.actSimulateCurrent.setIcon( QIcon(get_resource("stop_simulation.png"))) self.actSimulateCurrent.setText("Abort &Simulation") self.actSimulateCurrent.triggered.disconnect(self.start_simulation) self.actSimulateCurrent.triggered.connect(self.stop_simulation) if not self.runningBatch: self.actSimulateAll.setDisabled(True) self.guiProgress = QProgressBar(self) self.sim.simulationProgressChanged.connect(self.guiProgress.setValue) self.statusBar().addWidget(self.guiProgress) self.runSimulation.emit() @pyqtSlot() def stop_simulation(self): self.stopSimulation.emit() def export_simulation_data(self, ok): """ Query the user for a custom name and export the current simulation results. :param ok: unused parameter from QAction.triggered() Signal """ self._save_data() def _save_data(self, file_path=None): """ Save the current simulation results. If *fie_name* is given, the result will be saved to the specified location, making automated exporting easier. Args: file_path(str): Absolute path of the target file. If `None` the use will be asked for a storage location. """ regime_name = self._regimes[self._current_regime_index]["Name"] if file_path is None: # get default path path = self._settings.value("path/simulation_results") # create canonic file name suggestion = self._simfile_name(regime_name) else: path = os.path.dirname(file_path) suggestion = os.path.basename(file_path) # check if path exists otherwise create it if not os.path.isdir(path): box = QMessageBox() box.setText("Export Folder does not exist yet.") box.setInformativeText("Do you want to create it? \n" "{}".format(os.path.abspath(path))) box.setStandardButtons(QMessageBox.Ok | QMessageBox.No) box.setDefaultButton(QMessageBox.Ok) ret = box.exec_() if ret == QMessageBox.Ok: os.makedirs(path) else: path = os.path.abspath(os.path.curdir) file_path = None # If no path was given, present the default and let the user choose if file_path is None: dialog = QFileDialog(self) dialog.setAcceptMode(QFileDialog.AcceptSave) dialog.setFileMode(QFileDialog.AnyFile) dialog.setDirectory(path) dialog.setNameFilter("PyMoskito Results (*.pmr)") dialog.selectFile(suggestion) if dialog.exec_(): file_path = dialog.selectedFiles()[0] else: self._logger.warning("Export Aborted") return -1 # ask whether this should act as new default path = os.path.abspath(os.path.dirname(file_path)) if path != self._settings.value("path/simulation_results"): box = QMessageBox() box.setText("Use this path as new default?") box.setInformativeText("{}".format(path)) box.setStandardButtons(QMessageBox.Yes | QMessageBox.No) box.setDefaultButton(QMessageBox.Yes) ret = box.exec_() if ret == QMessageBox.Yes: self._settings.setValue("path/simulation_results", path) self.currentDataset.update({"regime name": regime_name}) with open(file_path, "wb") as f: pickle.dump(self.currentDataset, f, protocol=4) self.statusLabel.setText("results saved to {}".format(file_path)) self._logger.info("results saved to {}".format(file_path)) def _simfile_name(self, regime_name): """ Create a canonical name for a simulation result file """ suggestion = (time.strftime("%Y%m%d-%H%M%S") + "_" + regime_name + ".pmr") return suggestion def load_regime_dialog(self): regime_path = os.path.join(os.curdir) dialog = QFileDialog(self) dialog.setFileMode(QFileDialog.ExistingFile) dialog.setDirectory(regime_path) dialog.setNameFilter("Simulation Regime files (*.sreg)") if dialog.exec_(): file = dialog.selectedFiles()[0] self.load_regimes_from_file(file) def load_regimes_from_file(self, file_name): """ load simulation regime from file :param file_name: """ self.regime_file_name = os.path.split(file_name)[-1][:-5] self._logger.info("loading regime file: {0}".format( self.regime_file_name)) with open(file_name.encode(), "r") as f: self._regimes += yaml.load(f) self._update_regime_list() if self._regimes: self.actSimulateAll.setDisabled(False) self._logger.info("loaded {} regimes".format(len(self._regimes))) self.statusBar().showMessage( "loaded {} regimes.".format(len(self._regimes)), 1000) return def _update_regime_list(self): self.regime_list.clear() for reg in self._regimes: self._logger.debug("adding '{}' to regime list".format( reg["Name"])) self.regime_list.addItem(reg["Name"]) def remove_regime_items(self): if self.regime_list.currentRow() >= 0: # flag all selected files as invalid items = self.regime_list.selectedItems() for item in items: del self._regimes[self.regime_list.row(item)] self.regime_list.takeItem(self.regime_list.row(item)) @pyqtSlot(QListWidgetItem) def regime_dclicked(self, item): """ Apply the selected regime to the current target. """ self.apply_regime_by_name(str(item.text())) def apply_regime_by_name(self, regime_name): """ Apply the regime given by `regime_name` und update the regime index. Returns: bool: `True` if successful, `False` if errors occurred. """ # get regime idx try: idx = list(map(itemgetter("Name"), self._regimes)).index(regime_name) except ValueError as e: self._logger.error( "apply_regime_by_name(): Error no regime called " "'{0}'".format(regime_name)) return False # apply return self._apply_regime_by_idx(idx) def _apply_regime_by_idx(self, index=0): """ Apply the given regime. Args: index(int): Index of the regime in the `RegimeList` . Returns: bool: `True` if successful, `False` if errors occurred. """ if index >= len(self._regimes): self._logger.error("applyRegime: index error! ({})".format(index)) return False reg_name = self._regimes[index]["Name"] self.statusBar().showMessage("regime {} applied.".format(reg_name), 1000) self._logger.info("applying regime '{}'".format(reg_name)) self._current_regime_index = index self._current_regime_name = reg_name return self.sim.set_regime(self._regimes[index]) @pyqtSlot() def start_regime_execution(self): """ Simulate all regimes in the regime list. """ self.actSimulateAll.setText("Stop Simulating &All Regimes") self.actSimulateAll.setIcon(QIcon(get_resource("stop_batch.png"))) self.actSimulateAll.triggered.disconnect(self.start_regime_execution) self.actSimulateAll.triggered.connect(self.stop_regime_excecution) self.runningBatch = True self._current_regime_index = -1 self.regimeFinished.emit() def run_next_regime(self): """ Execute the next regime in the regime batch. """ # are we finished? if self._current_regime_index == len(self._regimes) - 1: self.finishedRegimeBatch.emit(True) return suc = self._apply_regime_by_idx(self._current_regime_index + 1) if not suc: self.finishedRegimeBatch.emit(False) return self.start_simulation() @pyqtSlot() def stop_regime_excecution(self): """ Stop the batch process. """ self.stopSimulation.emit() self.finishedRegimeBatch.emit(False) def regime_batch_finished(self, status): self.runningBatch = False self.actSimulateAll.setDisabled(False) self.actSave.setDisabled(True) self.actSimulateAll.setText("Simulate &All Regimes") self.actSimulateAll.setIcon(QIcon(get_resource("execute_regimes.png"))) self.actSimulateAll.triggered.disconnect(self.stop_regime_excecution) self.actSimulateAll.triggered.connect(self.start_regime_execution) if status: self.statusLabel.setText("All regimes have been simulated") self._logger.info("All Regimes have been simulated") else: self._logger.error("Batch simulation has been aborted") if self._settings.value("control/exit_on_batch_completion") == "True": self._logger.info("Shutting down SimulationGUI") self.close() @pyqtSlot(str, dict, name="new_simulation_data") def new_simulation_data(self, status, data): """ Slot to be called when the simulation interface has completed the current job and new data is available. Args: status (str): Status of the simulation, either - `finished` : Simulation has been finished successfully or - `failed` : Simulation has failed. data (dict): Dictionary, holding the simulation data. """ self._logger.info("Simulation {}".format(status)) self.statusLabel.setText("Simulation {}".format(status)) self.actSimulateCurrent.setText("&Simulate Current Regime") self.actSimulateCurrent.setIcon(QIcon(get_resource("simulate.png"))) self.actSimulateCurrent.triggered.disconnect(self.stop_simulation) self.actSimulateCurrent.triggered.connect(self.start_simulation) self.actPlayPause.setDisabled(False) self.actStop.setDisabled(False) self.actSave.setDisabled(False) self.speedControl.setDisabled(False) self.timeSlider.setDisabled(False) self.sim.simulationProgressChanged.disconnect( self.guiProgress.setValue) self.statusBar().removeWidget(self.guiProgress) self.stop_animation() self.currentDataset = data if data: self._read_results() self._update_data_list() self._update_plots() if self._settings.value("control/autoplay_animation") == "True": self.actPlayPause.trigger() if self.runningBatch: regime_name = self._regimes[self._current_regime_index]["Name"] file_name = self._simfile_name(regime_name) self._save_data( os.path.join(self._settings.value("path/simulation_results"), file_name)) self.regimeFinished.emit() else: self.actSimulateAll.setDisabled(False) def _read_results(self): state = self.currentDataset["results"]["Solver"] self.interpolator = interp1d(self.currentDataset["results"]["time"], state, axis=0, bounds_error=False, fill_value=(state[0], state[-1])) self.currentStepSize = 1.0 / self.currentDataset["simulation"][ "measure rate"] self.currentEndTime = self.currentDataset["simulation"]["end time"] self.validData = True def increment_playback_speed(self): self.speedControl.setValue(self.speedControl.value() + self.speedControl.singleStep()) def decrement_playback_speed(self): self.speedControl.setValue(self.speedControl.value() - self.speedControl.singleStep()) def reset_playback_speed(self): self.speedControl.setValue( (self.speedControl.maximum() - self.speedControl.minimum()) / 2) def set_slowest_playback_speed(self): self.speedControl.setValue(self.speedControl.minimum()) def set_fastest_playback_speed(self): self.speedControl.setValue(self.speedControl.maximum()) def update_playback_speed(self, val): """ adjust playback time to slider value :param val: """ maximum = self.speedControl.maximum() self.playbackGain = 10**(3.0 * (val - maximum / 2) / maximum) @pyqtSlot() def increment_playback_time(self): """ go one time step forward in playback """ if self.playbackTime == self.currentEndTime: self.pause_animation() return increment = self.playbackGain * self.playbackTimeout / 1000 self.playbackTime = min(self.currentEndTime, self.playbackTime + increment) pos = int(self.playbackTime / self.currentEndTime * self.timeSliderRange) self.timeSlider.blockSignals(True) self.timeSlider.setValue(pos) self.timeSlider.blockSignals(False) self.playbackTimeChanged.emit() def update_playback_time(self): """ adjust playback time to slider value """ self.playbackTime = self.timeSlider.value( ) / self.timeSliderRange * self.currentEndTime self.playbackTimeChanged.emit() return def update_gui(self): """ updates the graphical user interface, including: - timestamp - visualisation - time cursor in diagrams """ if not self.validData: return self.timeLabel.setText("current time: %4f" % self.playbackTime) # update time cursor in plots self._update_time_cursors() # update state of rendering if self.visualizer: state = self.interpolator(self.playbackTime) self.visualizer.update_scene(state) if isinstance(self.visualizer, MplVisualizer): pass elif isinstance(self.visualizer, VtkVisualizer): self.vtkWidget.GetRenderWindow().Render() def _update_data_list(self): self.dataList.clear() for module_name, results in self.currentDataset["results"].items(): if not isinstance(results, np.ndarray): continue if len(results.shape) == 1: self.dataList.insertItem(0, module_name) elif len(results.shape) == 2: for col in range(results.shape[1]): self.dataList.insertItem( 0, self._build_entry_name(module_name, (col, ))) elif len(results.shape) == 3: for col in range(results.shape[1]): for der in range(results.shape[2]): self.dataList.insertItem( 0, self._build_entry_name(module_name, (col, der))) def _build_entry_name(self, module_name, idx): """ Construct an identifier for a given entry of a module. Args: module_name (str): name of the module the entry belongs to. idx (tuple): Index of the entry. Returns: str: Identifier to use for display. """ # save the user from defining 1d entries via tuples if len(idx) == 1: m_idx = idx[0] else: m_idx = idx mod_settings = self.currentDataset["modules"] info = mod_settings.get(module_name, {}).get("output_info", None) if info: if m_idx in info: return ".".join([module_name, info[m_idx]["Name"]]) return ".".join([module_name] + [str(i) for i in idx]) def _get_index_from_suffix(self, module_name, suffix): info = self.currentDataset["modules"].get(module_name, {}).get("output_info", None) idx = next((i for i in info if info[i]["Name"] == suffix), None) return idx def _get_units(self, entry): """ Return the unit that corresponds to a given entry. If no information is available, None is returned. Args: entry (str): Name of the entry. This can either be "Model.a.b" where a and b are numbers or if information is available "Model.Signal" where signal is the name of that part. Returns: """ args = entry.split(".") module_name = args.pop(0) info = self.currentDataset["modules"].get(module_name, {}).get("output_info", None) if info is None: return None if len(args) == 1: try: idx = int(args[0]) except ValueError: idx = next((i for i in info if info[i]["Name"] == args[0]), None) else: idx = (int(a) for a in args) return info[idx]["Unit"] def create_plot(self, item): """ Creates a plot widget based on the given item. If a plot for this item is already open no new plot is created but the existing one is raised up again. Args: item(Qt.ListItem): Item to plot. """ title = str(item.text()) if title in self.non_plotting_docks: self._logger.error("Title '{}' not allowed for a plot window since" "it would shadow on of the reserved " "names".format(title)) # check if plot has already been opened if title in self.area.findAll()[1]: self.area.docks[title].raiseDock() return # collect data data = self._get_data_by_name(title) t = self.currentDataset["results"]["time"] unit = self._get_units(title) if "." in title: name = title.split(".")[1] else: name = title # create plot widget widget = pg.PlotWidget(title=title) widget.showGrid(True, True) widget.plot(x=t, y=data) widget.getPlotItem().getAxis("bottom").setLabel(text="Time", units="s") widget.getPlotItem().getAxis("left").setLabel(text=name, units=unit) # add a time line time_line = pg.InfiniteLine(self.playbackTime, angle=90, movable=False, pen=pg.mkPen("#FF0000", width=2.0)) widget.getPlotItem().addItem(time_line) # create dock container and add it to dock area dock = pg.dockarea.Dock(title, closable=True) dock.addWidget(widget) self.area.addDock(dock, "above", self.plotDockPlaceholder) def _get_data_by_name(self, name): tmp = name.split(".") module_name = tmp[0] if len(tmp) == 1: data = np.array(self.currentDataset["results"][module_name]) elif len(tmp) == 2: try: idx = int(tmp[1]) except ValueError: idx = self._get_index_from_suffix(module_name, tmp[1]) finally: data = self.currentDataset["results"][module_name][..., idx] elif len(tmp) == 3: idx = int(tmp[1]) der = int(tmp[2]) data = self.currentDataset["results"][module_name][..., idx, der] else: raise ValueError("Format not supported") return data def _update_time_cursors(self): """ Update the time lines of all plot windows """ for title, dock in self.area.findAll()[1].items(): if title in self.non_plotting_docks: continue for widget in dock.widgets: for item in widget.getPlotItem().items: if isinstance(item, pg.InfiniteLine): item.setValue(self.playbackTime) def _update_plots(self): """ Update the data in all plot windows """ for title, dock in self.area.findAll()[1].items(): if title in self.non_plotting_docks: continue if not self.dataList.findItems(dock.name(), Qt.MatchExactly): # no data for this plot -> remove it dock.close() continue for widget in dock.widgets: for item in widget.getPlotItem().items: if isinstance(item, pg.PlotDataItem): x_data = self.currentDataset["results"]["time"] y_data = self._get_data_by_name(dock.name()) item.setData(x=x_data, y=y_data) @pyqtSlot(QModelIndex) def target_view_changed(self, index): self.targetView.resizeColumnToContents(0) def postprocessing_clicked(self): """ starts the post- and metaprocessing application """ self._logger.info("launching postprocessor") self.statusBar().showMessage("launching postprocessor", 1000) if self.postprocessor is None: self.postprocessor = PostProcessor() self.postprocessor.show() def reset_camera_clicked(self): """ reset camera in vtk window """ self.visualizer.reset_camera() self.vtkWidget.GetRenderWindow().Render() def show_info(self): icon_lic = open(get_resource("license.txt"), "r").read() text = "This application was build using PyMoskito ver. {} .<br />" \ "PyMoskito is free software distributed under GPLv3. <br />" \ "It is developed by members of the " \ "<a href=\'https://tu-dresden.de/ing/elektrotechnik/rst'>" \ "Institute of Control Theory</a>" \ " at the <a href=\'https://tu-dresden.de'>" \ "Dresden University of Technology</a>. <br />" \ "".format(pkg_resources.require("PyMoskito")[0].version) \ + "<br />" + icon_lic box = QMessageBox.about(self, "PyMoskito", text) def show_online_docs(self): webbrowser.open("https://pymoskito.readthedocs.org") def closeEvent(self, QCloseEvent): self._logger.info("Close Event received, shutting down.") logging.getLogger().removeHandler(self.textLogger) super().closeEvent(QCloseEvent)
class ContentTwoListGUI(QWidget): def __init__(self, working_directory, _option_gui, parent=None): super(QWidget, self).__init__(parent) self.cfg = HF.LittleHelpers.load_config(ROOTDIR) self.option_gui = _option_gui # ============================ Different options available ============================ if self.option_gui == 'dcm2niix': if os.path.isdir(self.cfg["folders"]["dicom"]): self.working_dir = self.cfg["folders"]["dicom"] else: self.working_dir = os.getcwd() options = { 'folderbox_title': "Directory (DICOM-files)", 'str_labelDir': 'DICOM DIR: {}'.format(self.working_dir), 'runBTN_label': 'Run processing' } elif self.option_gui == "displayNiftiFiles": if not working_directory: HF.msg_box( text='Please provide a valid folder. Terminating this GUI.', title='No folder provided') self.close() return else: self.working_dir = working_directory options = { 'folderbox_title': "Directory (nifti-files)", 'str_labelDir': 'subjects\' DIR: {}'.format(self.working_dir), 'runBTN_label': 'View files' } else: HF.msg_box( text= 'Please provide a valid option such as "dcm2niix" or "displayNiftiFiles". ' 'Terminating the GUI', title='Wrong input as option') self.close() return # Create general layout self.tot_layout = QVBoxLayout(self) self.mid_layout = QHBoxLayout(self) # ============================ Create upper of GUI, i.e. working directory ============================ self.label_folderbox = QGroupBox(options["folderbox_title"]) self.HBoxUpperTwoListGUI = QVBoxLayout(self.label_folderbox) self.label_workingdir = QLabel(options["str_labelDir"]) self.HBoxUpperTwoListGUI.addWidget(self.label_workingdir) self.btn_workingdir = QPushButton('Change working \ndirectory') self.btn_workingdir.setFixedSize(150, 40) self.btn_workingdir.setDisabled(True) if self.option_gui == "dcm2niix": self.btn_workingdir.setEnabled(True) self.btn_workingdir.clicked.connect(self.change_workingdir) self.btn_savedir = QPushButton('Save directory \nto config file') self.btn_savedir.setFixedSize(150, 40) self.btn_savedir.setDisabled(True) if self.option_gui == "dcm2niix": self.btn_savedir.setEnabled(True) self.btn_savedir.setToolTip( HF.LittleHelpers.split_lines(setToolTips.saveDirButton())) self.btn_savedir.clicked.connect(self.save_cfg_dicomdir) hlay_upper = QHBoxLayout() hlay_upper.addWidget(self.btn_workingdir) hlay_upper.addWidget(self.btn_savedir) hlay_upper.addStretch(1) self.HBoxUpperTwoListGUI.addLayout(hlay_upper) # ==================== Create Content for Lists, i.e. input/output ==================== self.listboxInputGUITwoList = QGroupBox( 'Available items in working directory') self.listboxInput = QVBoxLayout(self.listboxInputGUITwoList) self.mInput = QListWidget() self.listboxInput.addWidget(self.mInput) self.mButtonToAvailable = QPushButton("<<") self.mBtnMoveToAvailable = QPushButton(">") self.mBtnMoveToSelected = QPushButton("<") self.mButtonToSelected = QPushButton(">>") self.mBtnUp = QPushButton("Up") self.mBtnDown = QPushButton("Down") self.listboxOutputGUITwoLIst = QGroupBox('Items to process') self.listboxOutput = QVBoxLayout(self.listboxOutputGUITwoLIst) self.mOutput = QListWidget() self.listboxOutput.addWidget(self.mOutput) # First column (Left side) vlay = QVBoxLayout() vlay.addStretch() vlay.addWidget(self.mBtnMoveToAvailable) vlay.addWidget(self.mBtnMoveToSelected) vlay.addStretch() vlay.addWidget(self.mButtonToAvailable) vlay.addWidget(self.mButtonToSelected) vlay.addStretch() # Second column (Right side) vlay2 = QVBoxLayout() vlay2.addStretch() vlay2.addWidget(self.mBtnUp) vlay2.addWidget(self.mBtnDown) vlay2.addStretch() # ==================== Lower part of GUI, i.e. Preferences/Start estimation ==================== self.btn_preferences = QPushButton("Preferences") self.btn_preferences.setDisabled(True) self.btn_preferences.clicked.connect(self.settings_show) if self.option_gui == "dcm2niix": self.btn_preferences.setEnabled(True) self.btn_run_command = QPushButton(options["runBTN_label"]) if self.option_gui == "dcm2niix": self.btn_run_command.setToolTip(setToolTips.run_dcm2niix()) else: self.btn_run_command.setToolTip( setToolTips.run_CheckRegistration()) self.btn_run_command.clicked.connect(self.start_process) hlay_bottom = QHBoxLayout() hlay_bottom.addStretch(1) hlay_bottom.addWidget(self.btn_preferences) hlay_bottom.addWidget(self.btn_run_command) hlay_bottom.addStretch() # ==================== Set all contents to general Layout ======================= self.mid_layout.addWidget(self.listboxInputGUITwoList) self.mid_layout.addLayout(vlay) self.mid_layout.addWidget(self.listboxOutputGUITwoLIst) self.mid_layout.addLayout(vlay2) self.tot_layout.addWidget(self.label_folderbox) self.tot_layout.addLayout(self.mid_layout) self.tot_layout.addLayout(hlay_bottom) try: self.mInput.clear() if self.option_gui == 'dcm2niix': items = HF.list_folders(self.working_dir, prefix='') else: items = HF.list_files_in_folder(inputdir=self.working_dir, contains='', suffix='nii') self.addAvailableItems(items) except FileExistsError: print('{} without any valid files/folders, continuing ...'.format( self.working_dir)) self.update_buttons_status() self.connections() def addAvailableItems(self, items): """adds the available Items in the directory to read from into list; error message is dropped if 0 available""" if len(items) == 0: buttonReply = QMessageBox.question( self, 'No files/folders in the selected directory', 'There are no subjects available in the current working directory ({}). ' 'Do you want to change to a different one?'.format( self.working_dir), QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) if buttonReply == QMessageBox.Yes: self.change_workingdir() else: self.mInput.addItems(items) def change_workingdir(self): """A new window appears in which the working directory can be set; besides, data is stored in the preferences file so that they will be loaded automatically next time""" self.working_dir = QFileDialog.getExistingDirectory( self, directory=self.working_dir, caption='Change working directory') self.label_workingdir.setText('Working DIR: {}'.format( self.working_dir)) self.mInput.clear() items = HF.list_folders(self.working_dir, prefix='') self.addAvailableItems(items) if self.option_gui == "dcm2niix": self.cfg["folders"]["dicom"] = self.working_dir HF.LittleHelpers.save_config(self.cfg["folders"]["rootdir"], self.cfg) def save_cfg_dicomdir(self): """Function intended to save the DICOM directory once button is pressed""" self.cfg["folders"]["dicom"] = self.working_dir HF.LittleHelpers.save_config(self.cfg["folders"]["rootdir"], self.cfg) HF.LittleHelpers.msg_box( text="Folder changed in the configuration file to {}".format( self.working_dir), title='Changed folder') def settings_show(self): """Opens a new GUI in which the settings for the transformation con be changed and saved to config file""" self.settingsGUI = GuiSettingsDCM2NII() self.settingsGUI.show() def start_process(self): """starts the process linked to the module selected; that is in case of dcm2nii it runs the extraction of nifti- files from the DICOM folder or in case of displayN4corr it displays all nifti files available in the folder""" input = [] [ input.append(self.mOutput.item(x).text()) for x in range(self.mOutput.count()) ] if not input: HF.LittleHelpers.msg_box( text="At least one folder with data must be selected!", title='No directory selected') elif len(input) != 0 and self.option_gui == "dcm2niix": print('in total, {} folders were selected'.format(len(input))) preprocDCM2NII.PreprocessDCM(input) elif len(input) != 0 and self.option_gui == "displayNiftiFiles": input_with_path = [] [ input_with_path.extend( glob.glob(self.working_dir + '/**/' + x, recursive=True)) for x in input ] viewer = 'itk-snap' # to-date, only one viewer is available. May be changed in a future HF.LittleHelpers.load_imageviewer(viewer, input_with_path) @QtCore.pyqtSlot() def update_buttons_status(self): self.mBtnUp.setDisabled(not bool(self.mOutput.selectedItems()) or self.mOutput.currentRow() == 0) self.mBtnDown.setDisabled( not bool(self.mOutput.selectedItems()) or self.mOutput.currentRow() == (self.mOutput.count() - 1)) self.mBtnMoveToAvailable.setDisabled( not bool(self.mInput.selectedItems()) or self.mOutput.currentRow() == 0) self.mBtnMoveToSelected.setDisabled( not bool(self.mOutput.selectedItems())) self.mButtonToAvailable.setDisabled( not bool(self.mOutput.selectedItems())) self.mButtonToSelected.setDisabled( not bool(self.mInput.selectedItems())) def connections(self): self.mInput.itemSelectionChanged.connect(self.update_buttons_status) self.mOutput.itemSelectionChanged.connect(self.update_buttons_status) self.mBtnMoveToAvailable.clicked.connect( self.on_mBtnMoveToAvailable_clicked) self.mBtnMoveToSelected.clicked.connect( self.on_mBtnMoveToSelected_clicked) self.mButtonToAvailable.clicked.connect( self.on_mButtonToAvailable_clicked) self.mButtonToSelected.clicked.connect( self.on_mButtonToSelected_clicked) self.mBtnUp.clicked.connect(self.on_mBtnUp_clicked) self.mBtnDown.clicked.connect(self.on_mBtnDown_clicked) @QtCore.pyqtSlot() def on_mBtnMoveToAvailable_clicked(self): self.mOutput.addItem(self.mInput.takeItem(self.mInput.currentRow())) @QtCore.pyqtSlot() def on_mBtnMoveToSelected_clicked(self): self.mInput.addItem(self.mOutput.takeItem(self.mOutput.currentRow())) @QtCore.pyqtSlot() def on_mButtonToAvailable_clicked(self): while self.mOutput.count() > 0: self.mInput.addItem(self.mOutput.takeItem(0)) @QtCore.pyqtSlot() def on_mButtonToSelected_clicked(self): while self.mInput.count() > 0: self.mOutput.addItem(self.mInput.takeItem(0)) @QtCore.pyqtSlot() def on_mBtnUp_clicked(self): row = self.mOutput.currentRow() currentItem = self.mOutput.takeItem(row) self.mOutput.insertItem(row - 1, currentItem) self.mOutput.setCurrentRow(row - 1) @QtCore.pyqtSlot() def on_mBtnDown_clicked(self): row = self.mOutput.currentRow() currentItem = self.mOutput.takeItem(row) self.mOutput.insertItem(row + 1, currentItem) self.mOutput.setCurrentRow(row + 1) def addSelectedItems(self, items): self.mOutput.addItems(items) def closeEvent(self, event): """saves the settings found here as a yaml file and closes the GUI""" event.accept()
class App(QMainWindow): def __init__(self): super().__init__() self.instance = vlc.Instance('--novideo') #self.mediaplayer = self.instance.media_player_new() self.medialist = self.instance.media_list_new() self.mediaplayer = self.instance.media_list_player_new() self.title = 'Player' self.left = 500 self.top = 200 self.width = 600 self.height = 400 self.color = 0 # 0- toggle to dark 1- toggle to light self.initUI() def initUI(self): self.addControls() self.setWindowTitle(self.title) self.setGeometry(self.left, self.top, self.width, self.height) self.toggleColors() #self.show() self.showFullScreen() self.Wellcome() def addControls(self): wid = QWidget(self) self.setCentralWidget(wid) # Status self.status = QLabel() self.status.setAlignment(QtCore.Qt.AlignCenter) self.status.setStyleSheet('color: #1DB954') self.status.setText('Xin chào') # List self.result_label = QLabel() self.result_label.setAlignment(QtCore.Qt.AlignCenter) self.result_label.setStyleSheet('color: #1DB954') self.result_label.setText('Danh sách phát') # Search self.searchInput = QLineEdit() self.searchInput.setFixedHeight(22) #self.searchInput.setText("am tham ben em") self.searchBtn = QPushButton('Tìm kiếm') self.searchBtn.setFixedHeight(40) self.searchBtn.clicked.connect(self.Search) self.voicesearchBtn = QPushButton('Tìm bằng giọng nói') self.voicesearchBtn.setFixedHeight(40) self.voicesearchBtn.clicked.connect(self.VoiceSearch) self.listAudio = QListWidget() self.listAudio.setFixedHeight(125) self.current_vol = 60 self.mediaplayer.get_media_player().audio_set_volume(self.current_vol) self.voldown = QPushButton('-') self.volup = QPushButton('+') self.volup.setFixedHeight(50) self.volup.setFixedWidth(50) self.voldown.setFixedHeight(50) self.voldown.setFixedWidth(50) self.playbutton = QPushButton('Phát/Tạm dừng') # play button self.playbutton.setFixedHeight(50) self.stopbutton = QPushButton('Phát lại') # Stop button self.stopbutton.setFixedHeight(50) self.nextbutton = QPushButton('Tiếp theo') # Next button self.nextbutton.setFixedHeight(50) self.nextbutton2 = QPushButton('Tiếp theo') # Next button self.nextbutton2.setFixedHeight(50) self.command = QPushButton('Ra lệnh bằng giọng nói') # Next button self.command.setFixedHeight(40) self.shortcut = QShortcut(QKeySequence("Space"), self) self.shortcut.activated.connect(self.excCommand) self.exit = QPushButton('Thoát') # Next button self.exit.setFixedHeight(40) # Add button layouts mainLayout = QVBoxLayout() controls = QHBoxLayout() controls.addWidget(self.voldown) controls.addWidget(self.playbutton) controls.addWidget(self.nextbutton2) controls.addWidget(self.stopbutton) controls.addWidget(self.volup) # Add to vertical layout mainLayout.addWidget(self.status) mainLayout.addWidget(self.searchInput) mainLayout.addWidget(self.result_label) mainLayout.addWidget(self.listAudio) mainLayout.addWidget(self.searchBtn) mainLayout.addWidget(self.voicesearchBtn) mainLayout.addLayout(controls) mainLayout.addWidget(self.command) mainLayout.addWidget(self.exit) wid.setLayout(mainLayout) self.playbutton.clicked.connect(self.PlayPause) self.stopbutton.clicked.connect(self.Stop) self.nextbutton2.clicked.connect(self.Next) self.volup.clicked.connect(self.volUp) self.voldown.clicked.connect(self.volDown) self.command.clicked.connect(self.excCommand) self.exit.clicked.connect(self.exitApp) self.statusBar() def Wellcome(self): t2s("Xin chào tôi là Pi") def exitApp(self): exit() def setVolume(self, Volume): self.mediaplayer.get_media_player().audio_set_volume(Volume) def volUp(self): if self.current_vol < 100: self.current_vol = self.current_vol + 20 self.mediaplayer.get_media_player().audio_set_volume( self.current_vol) def volDown(self): if self.current_vol > 20: self.current_vol = self.current_vol - 20 self.mediaplayer.get_media_player().audio_set_volume( self.current_vol) def PlayPause(self): if self.mediaplayer.is_playing(): self.mediaplayer.pause() self.playbutton.setText("Phát") else: self.mediaplayer.play() self.playbutton.setText("Tạm dừng") def Pause(self): if self.mediaplayer.is_playing(): self.mediaplayer.pause() self.playbutton.setText("Phát") def Stop(self): self.mediaplayer.stop() self.mediaplayer.play() self.playbutton.setText("Tạm dừng") def setMediaList(self): self.mediaplayer.set_media_list(self.medialist) def addMediaPlayerUrl(self, url): self.medialist.add_media(self.instance.media_new(url)) def resetMediaPlayList(self): self.medialist = self.instance.media_list_new() self.mediaplayer = self.instance.media_list_player_new() def getListSearch(self): self.status.setText('Đang tìm') if self.searchInput.text() == '': self.status.setText('Nhập từ khóa hoặc tìm bằng giọng nói') t2s('Vui lòng nhập từ khóa hoặc tìm kiếm bằng giọng nói') else: print('keyword:', self.searchInput.text()) results = searchYoutube(self.searchInput.text(), offset=1, mode="json", max_results=5).result() results = json.loads(results) results = results["search_result"] #print(results) if len(results) == 0: if self.fornext: self.listAudio.clear() #self.resetMediaPlayList() self.idx_audio = self.idx_audio + 1 self.listAudio.insertItem( 0, 'Tiếp theo: ' + self.list_title[self.idx_audio]) self.listAudio.repaint() else: self.status.setText('Không có kết quả hoặc lỗi') t2s('Tôi không thể tìm kiếm Hãy thử lại') #pass else: #tìm kiếm mới self.listAudio_text = {} self.listAudio.clear() self.list_title = [e["title"] for e in results] print("list_title:", self.list_title[0]) for i, item in enumerate(results): itemlink = item['link'] self.listAudio_text[self.list_title[i]] = itemlink self.status.setText('Kết quả cho: "{}"'.format( self.searchInput.text())) for i, item in enumerate(results): #nếu search gợi ý cho next thì chỉ hiển thị 1 bài tiếp theo if self.fornext: self.listAudio.insertItem( i + 1, 'Tiếp theo: ' + self.list_title[i]) self.idx_audio = 0 self.listAudio.repaint() break else: self.listAudio.insertItem( i + 1, str(i + 1) + ': ' + self.list_title[i]) self.listAudio.repaint() self.listAudio.repaint() print("end search") def Search(self): self.Stop() self.resetMediaPlayList() self.fornext = False t2s("Tôi sẽ tìm những kết quả cho") t2s(self.searchInput.text()) self.getListSearch() try: _ = len(self.listAudio_text) t2s("Đây là kết quả Chọn bài muốn phát") self.idx_audio = select_by_speech() self.fornext = True self.selectAndPlaySongByIndex() except: self.status.setText('Không có kết quả hoặc lỗi. Thử lại') t2s('Không có kết quả hoặc lỗi Hãy thử lại') def VoiceSearch(self): self.Stop() t2s('Chức năng tìm kiếm bằng giọng nói sẵn sàng') self.status.setText('Tìm kiếm giọng nói') keyword = getVoiceKeyWord() # có xác nhận key self.status.setText('Tìm kiếm cho: "{}"'.format(keyword)) self.searchInput.setText(keyword) self.Search() def selectAndPlaySongByIndex(self): title_audio = list(self.listAudio_text.keys())[self.idx_audio] link_audio = list(self.listAudio_text.values())[self.idx_audio] link_audio = getLinkAudio(link_audio) t2s('Bắt đầu phát') t2s(title_audio) self.addMediaPlayerUrl(link_audio) #self.setMediaList() next_key = next_keyword(self.list_title) self.searchInput.setText(next_key) self.getListSearch() title_audio2 = list(self.listAudio_text.keys())[self.idx_audio] link_audio2 = list(self.listAudio_text.values())[self.idx_audio] link_audio2 = getLinkAudio(link_audio2) self.addMediaPlayerUrl(link_audio2) self.setMediaList() print("=" * 20) print("hiện tại:", title_audio) print("tiếp theo", title_audio2) print("=" * 20) self.PlayPause() self.status.setText('Đang phát: {}'.format(title_audio)) def Next(self): self.Pause() self.status.setText("Tiếp theo") t2s("Tiếp theo") self.resetMediaPlayList() #self.idx_audio += 1 self.selectAndPlaySongByIndex() print("sau next:", self.medialist.count()) def excCommand(self): self.Pause() command = listen_command() if command == 0: self.PlayPause() elif command == 1: self.Next() elif command == 2: self.Stop() t2s("Phát lại") elif command == 3: self.VoiceSearch() elif command == 4: self.volUp() t2s("đã tăng") self.PlayPause() elif command == 6: self.volDown() t2s("đã giảm") self.PlayPause() elif command == 5: t2s('Bạn chắc chắn muốn thoát không') t2s('Xác nhận bằng cách nói ok') cf_exit = recognize('') if cf_exit == 'yes' or cf_exit == 'ok' or cf_exit == 'oke': t2s("Chào bạn hẹn gặp lại bạn hihi") exit() else: print('no exit') def toggleColors(self): app.setStyle("Fusion") palette = QPalette() palette.setColor(QPalette.Window, QColor(41, 41, 41)) palette.setColor(QPalette.WindowText, Qt.white) palette.setColor(QPalette.Base, QColor(41, 41, 41)) palette.setColor(QPalette.AlternateBase, QColor(53, 53, 53)) palette.setColor(QPalette.ToolTipBase, Qt.white) palette.setColor(QPalette.ToolTipText, Qt.white) palette.setColor(QPalette.Text, Qt.white) palette.setColor(QPalette.Button, QColor(29, 185, 84)) palette.setColor(QPalette.ButtonText, Qt.white) palette.setColor(QPalette.BrightText, Qt.red) palette.setColor(QPalette.Link, QColor(235, 101, 54)) palette.setColor(QPalette.Highlight, Qt.white) palette.setColor(QPalette.HighlightedText, QColor(29, 185, 84)) app.setPalette(palette)
class MainWindow(QMainWindow): htmlReady = pyqtSignal(str) def __init__(self, app): QMainWindow.__init__(self) self.install_directory = os.getcwd() self.app = app self.book = None self.last_book = "" self.filename = "" self._part_is_new = False self.tread_running = False self.initTheme() self.createUi() self.createMenus() self.createStatusBar() self.readSettings() self.text_edit.textChanged.connect(self.textChanged) def initTheme(self): settings = QSettings(QSettings.IniFormat, QSettings.UserScope, QCoreApplication.organizationName(), QCoreApplication.applicationName()) self.theme = settings.value("theme", "Fusion") hilite_color = settings.value( "hiliteColor", self.palette().highlight().color().name()) self.changeStyle(self.theme, hilite_color) def showEvent(self, event): if self.last_book: self.loadBook(self.last_book) def changeStyle(self, theme, hilite_color): self.theme = theme if theme == "DarkFusion": QApplication.setStyle(DarkFusion(hilite_color)) else: QApplication.setStyle(QStyleFactory.create(theme)) pal = self.app.palette() pal.setColor(QPalette.Highlight, QColor(hilite_color)) self.app.setPalette(pal) def createUi(self): self.content = Expander("Content", ":/images/parts.svg") self.images = Expander("Images", ":/images/images.svg") self.settings = Expander("Settings", ":/images/settings.svg") self.setWindowTitle(QCoreApplication.applicationName() + " " + QCoreApplication.applicationVersion()) vbox = QVBoxLayout() vbox.addWidget(self.content) vbox.addWidget(self.images) vbox.addWidget(self.settings) vbox.addStretch() self.content_list = QListWidget() self.content_list.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Fixed) content_box = QVBoxLayout() content_box.addWidget(self.content_list) self.item_edit = QLineEdit() self.item_edit.setMaximumHeight(0) self.item_edit.editingFinished.connect(self.editItemFinished) self.item_anim = QPropertyAnimation(self.item_edit, "maximumHeight".encode("utf-8")) content_box.addWidget(self.item_edit) button_layout = QHBoxLayout() plus_button = FlatButton(":/images/plus.svg") self.edit_button = FlatButton(":/images/edit.svg") self.trash_button = FlatButton(":/images/trash.svg") self.up_button = FlatButton(":/images/up.svg") self.down_button = FlatButton(":/images/down.svg") self.trash_button.enabled = False self.up_button.enabled = False self.down_button.enabled = False button_layout.addWidget(plus_button) button_layout.addWidget(self.up_button) button_layout.addWidget(self.down_button) button_layout.addWidget(self.edit_button) button_layout.addWidget(self.trash_button) content_box.addLayout(button_layout) self.content.addLayout(content_box) plus_button.clicked.connect(self.addPart) self.trash_button.clicked.connect(self.dropPart) self.up_button.clicked.connect(self.partUp) self.down_button.clicked.connect(self.partDown) self.edit_button.clicked.connect(self.editPart) self.image_list = QListWidget() self.image_list.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Fixed) image_box = QVBoxLayout() image_box.addWidget(self.image_list) image_button_layout = QHBoxLayout() image_plus_button = FlatButton(":/images/plus.svg") self.image_trash_button = FlatButton(":/images/trash.svg") self.image_trash_button.enabled = False image_button_layout.addWidget(image_plus_button) image_button_layout.addWidget(self.image_trash_button) image_box.addLayout(image_button_layout) self.images.addLayout(image_box) image_plus_button.clicked.connect(self.addImage) self.image_trash_button.clicked.connect(self.dropImage) scroll_content = QWidget() scroll_content.setLayout(vbox) scroll = QScrollArea() scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) scroll.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) scroll.setWidget(scroll_content) scroll.setWidgetResizable(True) scroll.setMaximumWidth(200) scroll.setMinimumWidth(200) self.navigationdock = QDockWidget("Navigation", self) self.navigationdock.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea) self.navigationdock.setWidget(scroll) self.navigationdock.setObjectName("Navigation") self.addDockWidget(Qt.LeftDockWidgetArea, self.navigationdock) self.splitter = QSplitter() self.text_edit = MarkdownEdit() self.text_edit.setFont(QFont("Courier", 11)) self.preview = QWebEngineView() self.preview.setMinimumWidth(300) self.setWindowTitle(QCoreApplication.applicationName()) self.splitter.addWidget(self.text_edit) self.splitter.addWidget(self.preview) self.setCentralWidget(self.splitter) self.content.expanded.connect(self.contentExpanded) self.images.expanded.connect(self.imagesExpanded) self.settings.expanded.connect(self.settingsExpanded) self.settings.clicked.connect(self.openSettings) self.content_list.currentItemChanged.connect(self.partSelectionChanged) self.image_list.currentItemChanged.connect(self.imageSelectionChanged) self.image_list.itemDoubleClicked.connect(self.insertImage) self.text_edit.undoAvailable.connect(self.undoAvailable) self.text_edit.redoAvailable.connect(self.redoAvailable) self.text_edit.copyAvailable.connect(self.copyAvailable) QApplication.clipboard().dataChanged.connect(self.clipboardDataChanged) def undoAvailable(self, value): self.undo_act.setEnabled(value) def redoAvailable(self, value): self.redo_act.setEnabled(value) def copyAvailable(self, value): self.copy_act.setEnabled(value) self.cut_act.setEnabled(value) def clipboardDataChanged(self): md = QApplication.clipboard().mimeData() self.paste_act.setEnabled(md.hasText()) def openSettings(self): dlg = Settings(self.book) dlg.exec() if dlg.saved: self.setWindowTitle(QCoreApplication.applicationName() + " - " + self.book.name) def addPart(self): self.item_edit.setText("") self.item_edit.setFocus() self.item_anim.setStartValue(0) self.item_anim.setEndValue(23) self.item_anim.start() self._part_is_new = True def addItem(self): text = self.item_edit.text() if text: if not self.book.getPart(text): self.book.addPart(text) self.loadBook(self.last_book) def updateItem(self): text = self.item_edit.text() if text: if not self.book.getPart(text): self.book.updatePart( self.content_list.currentItem().data(1).name, text) self.loadBook(self.last_book) def editItemFinished(self): if self._part_is_new: self.addItem() else: self.updateItem() self.item_anim.setStartValue(23) self.item_anim.setEndValue(0) self.item_anim.start() def editPart(self): item = self.content_list.currentItem().data(1).name self.item_edit.setText(item) self.item_edit.setFocus() self.item_anim.setStartValue(0) self.item_anim.setEndValue(23) self.item_anim.start() self._part_is_new = False def dropPart(self): item = self.content_list.currentItem().data(1).name msgBox = QMessageBox() msgBox.setText("You are about to delete the part <i>" + item + "</i>") msgBox.setInformativeText("Do you really want to delete the item?") msgBox.setStandardButtons(QMessageBox.Yes | QMessageBox.Cancel) msgBox.setDefaultButton(QMessageBox.Cancel) ret = msgBox.exec() if ret == QMessageBox.Yes: self.book.dropPart(item) self.loadBook(self.last_book) def addImage(self): fileName = "" dialog = QFileDialog() dialog.setFileMode(QFileDialog.AnyFile) dialog.setNameFilter("Image Files(*.png *.jpg *.bmp *.gif);;All (*)") dialog.setWindowTitle("Load Image") dialog.setOption(QFileDialog.DontUseNativeDialog, True) dialog.setAcceptMode(QFileDialog.AcceptOpen) if dialog.exec_(): fileName = dialog.selectedFiles()[0] del dialog if not fileName: return base = os.path.basename(fileName) if not os.path.exists( os.path.join(self.book.source_path, "images", base)): copy(fileName, os.path.join(self.book.source_path, "images")) item = QListWidgetItem() item.setText(Path(fileName).name) item.setData( 1, os.path.join(self.book.source_path, "images", Path(fileName).name)) self.image_list.addItem(item) def dropImage(self): item = self.image_list.currentItem() image = item.data(1) filename = os.path.join(self.book.source_path, "parts", image) os.remove(filename) self.loadImages() def loadImages(self): self.image_list.clear() for root, dir, files in os.walk( os.path.join(self.book.source_path, "images")): for file in files: filename = os.path.join(self.book.source_path, "images", Path(file).name) item = QListWidgetItem() item.setToolTip("Doubleclick image to insert into text") item.setText(Path(file).name) item.setData(1, filename) self.image_list.addItem(item) def partUp(self): pos = self.content_list.currentRow() item = self.content_list.takeItem(pos) self.content_list.insertItem(pos - 1, item) self.content_list.setCurrentRow(pos - 1) self.book.partUp(item.data(1).name) def partDown(self): pos = self.content_list.currentRow() item = self.content_list.takeItem(pos) self.content_list.insertItem(pos + 1, item) self.content_list.setCurrentRow(pos + 1) self.book.partDown(item.data(1).name) def partSelectionChanged(self, item): if item: part = item.data(1) self.filename = os.path.join(self.book.source_path, "parts", part.src) with open(self.filename, "r") as f: self.text_edit.setText(f.read()) self.trash_button.enabled = True self.up_button.enabled = self.content_list.currentRow() > 0 self.down_button.enabled = self.content_list.currentRow( ) < self.content_list.count() - 1 self.edit_button.enabled = True else: self.text_edit.setText("") self.trash_button.enabled = False self.up_button.enabled = False self.down_button.enabled = False self.edit_button.enabled = False def imageSelectionChanged(self, item): if item: self.image_trash_button.enabled = True else: self.image_trash_button.enabled = False def contentExpanded(self, value): if value: self.images.setExpanded(False) self.settings.setExpanded(False) def imagesExpanded(self, value): if value: self.content.setExpanded(False) self.settings.setExpanded(False) def appearanceExpanded(self, value): if value: self.content.setExpanded(False) self.images.setExpanded(False) self.settings.setExpanded(False) def settingsExpanded(self, value): if value: self.content.setExpanded(False) self.images.setExpanded(False) def closeEvent(self, event): self.writeSettings() event.accept() def createMenus(self): new_icon = QIcon(QPixmap(":/images/new.svg")) open_icon = QIcon(QPixmap(":/images/open.svg")) book_icon = QIcon(QPixmap(":/images/book.svg")) bold_icon = QIcon(QPixmap(":/images/bold.svg")) italic_icon = QIcon(QPixmap(":/images/italic.svg")) image_icon = QIcon(QPixmap(":/images/image.svg")) table_icon = QIcon(QPixmap(":/images/table.svg")) new_act = QAction(new_icon, "&New", self) new_act.setShortcuts(QKeySequence.New) new_act.setStatusTip("Create a new ebook project") new_act.triggered.connect(self.newFile) new_act.setToolTip("Create new ebook project") open_act = QAction(open_icon, "&Open", self) open_act.setShortcuts(QKeySequence.Open) open_act.setStatusTip("Open an existing ebook project") open_act.triggered.connect(self.open) open_act.setToolTip("Open an existing ebook project") book_act = QAction(book_icon, "&Create Book", self) book_act.setShortcuts(QKeySequence.SaveAs) book_act.setStatusTip("Create an ebook") book_act.triggered.connect(self.create) book_act.setToolTip("Create an ebook") pdf_act = QAction("Create &PDF", self) pdf_act.setStatusTip("Create PDF") pdf_act.setToolTip("Create PDF") pdf_act.triggered.connect(self.pdfExport) settings_act = QAction("&Settings", self) settings_act.setStatusTip("Open settings dialog") settings_act.triggered.connect(self.settingsDialog) settings_act.setToolTip("Open settings dialog") exit_act = QAction("E&xit", self) exit_act.setShortcuts(QKeySequence.Quit) exit_act.setStatusTip("Exit the application") exit_act.triggered.connect(self.close) self.undo_act = QAction("Undo", self) self.undo_act.setShortcut(QKeySequence.Undo) self.undo_act.setEnabled(False) self.undo_act.triggered.connect(self.doUndo) self.redo_act = QAction("Redo", self) self.redo_act.setShortcut(QKeySequence.Redo) self.redo_act.setEnabled(False) self.undo_act.triggered.connect(self.doRedo) self.cut_act = QAction("Cu&t", self) self.cut_act.setShortcut(QKeySequence.Cut) self.cut_act.triggered.connect(self.doCut) self.cut_act.setEnabled(False) self.copy_act = QAction("&Copy", self) self.copy_act.setShortcut(QKeySequence.Copy) self.copy_act.triggered.connect(self.doCopy) self.copy_act.setEnabled(False) self.paste_act = QAction("&Paste", self) self.paste_act.setShortcut(QKeySequence.Paste) self.paste_act.triggered.connect(self.doPaste) self.paste_act.setEnabled(False) bold_act = QAction(bold_icon, "Bold", self) bold_act.setShortcut(Qt.CTRL + Qt.Key_B) bold_act.triggered.connect(self.bold) italic_act = QAction(italic_icon, "Italic", self) italic_act.setShortcut(Qt.CTRL + Qt.Key_I) italic_act.triggered.connect(self.italic) image_act = QAction(image_icon, "Image", self) image_act.setShortcut(Qt.CTRL + Qt.Key_G) image_act.triggered.connect(self.insertImage) image_act.setToolTip("Insert an image") table_act = QAction(table_icon, "Table", self) table_act.setShortcut(Qt.CTRL + Qt.Key_T) table_act.triggered.connect(self.insertTable) table_act.setToolTip("Insert a table") about_act = QAction("&About", self) about_act.triggered.connect(self.about) about_act.setStatusTip("Show the application's About box") file_menu = self.menuBar().addMenu("&File") file_menu.addAction(new_act) file_menu.addAction(open_act) file_menu.addAction(book_act) file_menu.addAction(pdf_act) file_menu.addSeparator() file_menu.addAction(settings_act) file_menu.addSeparator() file_menu.addAction(exit_act) edit_menu = self.menuBar().addMenu("&Edit") edit_menu.addAction(self.undo_act) edit_menu.addAction(self.redo_act) edit_menu.addSeparator() edit_menu.addAction(self.cut_act) edit_menu.addAction(self.copy_act) edit_menu.addAction(self.paste_act) format_menu = self.menuBar().addMenu("&Format") format_menu.addAction(bold_act) format_menu.addAction(italic_act) insert_menu = self.menuBar().addMenu("&Insert") insert_menu.addAction(image_act) insert_menu.addAction(table_act) help_menu = self.menuBar().addMenu("&Help") help_menu.addAction(about_act) file_tool_bar = self.addToolBar("File") file_tool_bar.addAction(new_act) file_tool_bar.addAction(open_act) file_tool_bar.addAction(book_act) format_tool_bar = self.addToolBar("Format") format_tool_bar.addAction(bold_act) format_tool_bar.addAction(italic_act) insert_toolbar = self.addToolBar("Insert") insert_toolbar.addAction(image_act) insert_toolbar.addAction(table_act) def doUndo(self): self.text_edit.undo() def doRedo(self): self.text_edit.redo() def doCut(self): self.text_edit.cut() def doCopy(self): self.text_edit.copy() def doPaste(self): self.text_edit.paste() def insertImage(self): if not self.book: QMessageBox.warning(self, QCoreApplication.applicationName(), "You have to load or create a book first!") return if not self.filename: QMessageBox.warning( self, QCoreApplication.applicationName(), "You have to select part from the book content first!") return if self.image_list.count() == 0: QMessageBox.warning( self, QCoreApplication.applicationName(), "You have to add an image to the image list first!") return if not self.image_list.currentItem(): QMessageBox.warning( self, QCoreApplication.applicationName(), "You have to select an image from the image list first!") return item = self.image_list.currentItem() filename = item.text() cursor = self.text_edit.textCursor() pos = cursor.position() base = filename.split(".")[0].replace("_", "-") cursor.insertText("![" + base + "](../images/" + filename + " \"" + base + "\")") cursor.setPosition(pos) self.text_edit.setTextCursor(cursor) def insertTable(self): cursor = self.text_edit.textCursor() pos = cursor.position() cursor.insertText( "| alignLeft | alignCenter | unAligned | alignRight |\n" "| :--- | :---: | --- | ---: |\n" "| cell a | cell b | cell c | cell d |\n" "| cell e | cell f | cell g | cell h |\n") cursor.setPosition(pos) self.text_edit.setTextCursor(cursor) def createStatusBar(self): self.statusBar().showMessage("Ready") def about(self): QMessageBox.about( self, "About " + QCoreApplication.applicationName(), "EbookCreator\nVersion: " + QCoreApplication.applicationVersion() + "\n(C) Copyright 2019 Olaf Japp. All rights reserved.\n\nThis program is provided AS IS with NO\nWARRANTY OF ANY KIND, INCLUDING THE\nWARRANTY OF DESIGN, MERCHANTABILITY AND\nFITNESS FOR A PATICULAR PURPOSE." ) def newFile(self): dlg = ProjectWizard(self.install_directory, parent=self) dlg.loadBook.connect(self.loadBook) dlg.show() def open(self): fileName = "" dialog = QFileDialog() dialog.setFileMode(QFileDialog.AnyFile) dialog.setNameFilter("EbookCreator (book.qml);;All (*)") dialog.setWindowTitle("Load Ebook") dialog.setOption(QFileDialog.DontUseNativeDialog, True) dialog.setAcceptMode(QFileDialog.AcceptOpen) dialog.setDirectory(os.path.join(self.install_directory, "sources")) if dialog.exec_(): fileName = dialog.selectedFiles()[0] del dialog if not fileName: return self.loadBook(fileName) def writeSettings(self): settings = QSettings(QSettings.IniFormat, QSettings.UserScope, QCoreApplication.organizationName(), QCoreApplication.applicationName()) settings.setValue("geometry", self.saveGeometry()) settings.setValue("lastBook", self.last_book) def readSettings(self): settings = QSettings(QSettings.IniFormat, QSettings.UserScope, QCoreApplication.organizationName(), QCoreApplication.applicationName()) geometry = settings.value("geometry", QByteArray()) self.last_book = settings.value("lastBook") if not geometry: availableGeometry = QApplication.desktop().availableGeometry(self) self.resize(availableGeometry.width() / 3, availableGeometry.height() / 2) self.move((availableGeometry.width() - self.width()) / 2, (availableGeometry.height() - self.height()) / 2) else: self.restoreGeometry(geometry) def bold(self): if not self.filename: QMessageBox.warning( self, QCoreApplication.applicationName(), "You have to select part from the book content first!") return cursor = self.text_edit.textCursor() pos = cursor.position() if not cursor.hasSelection(): cursor.select(QTextCursor.WordUnderCursor) cursor.insertText("**" + cursor.selectedText() + "**") cursor.setPosition(pos + 2) self.text_edit.setTextCursor(cursor) def italic(self): if not self.filename: QMessageBox.warning( self, QCoreApplication.applicationName(), "You have to select part from the book content first!") return cursor = self.text_edit.textCursor() pos = cursor.position() if not cursor.hasSelection(): cursor.select(QTextCursor.WordUnderCursor) cursor.insertText("*" + cursor.selectedText() + "*") cursor.setPosition(pos + 1) self.text_edit.setTextCursor(cursor) def create(self): filename = "" dialog = QFileDialog() dialog.setFileMode(QFileDialog.AnyFile) dialog.setNameFilter("ePub3 (*.epub);;All (*)") dialog.setWindowTitle("Create Ebook") dialog.setOption(QFileDialog.DontUseNativeDialog, True) dialog.setAcceptMode(QFileDialog.AcceptSave) dialog.setDirectory(self.install_directory) dialog.setDefaultSuffix("epub") if dialog.exec_(): filename = dialog.selectedFiles()[0] del dialog if not filename: return QApplication.setOverrideCursor(Qt.WaitCursor) createEpub(filename, self.book, self) QApplication.restoreOverrideCursor() def loadStatusChanged(self, status): if status == 1: self.book = self.component.create() if self.book is not None: self.book.setFilename(self.last_book) self.book.setWindow(self) else: for error in self.component.errors(): print(error.toString()) return self.content_list.clear() for part in self.book.parts: item = QListWidgetItem() item.setText(part.name) item.setData(1, part) self.content_list.addItem(item) self.loadImages() self.setWindowTitle(QCoreApplication.applicationName() + " - " + self.book.name) self.content.setExpanded(True) self.content_list.setCurrentRow(0) elif status == 3: for error in self.component.errors(): print(error.toString()) return def loadBook(self, filename): self.last_book = filename self.filename = "" engine = QQmlEngine() self.component = QQmlComponent(engine) self.component.statusChanged.connect(self.loadStatusChanged) self.component.loadUrl(QUrl.fromLocalFile(filename)) def settingsDialog(self): dlg = SettingsDialog(self.theme, self.palette().highlight().color().name(), parent=self) dlg.exec() if dlg.theme != self.theme or dlg.hilite_color != self.palette( ).highlight().color().name(): settings = QSettings(QSettings.IniFormat, QSettings.UserScope, QCoreApplication.organizationName(), QCoreApplication.applicationName()) settings.setValue("theme", dlg.theme) settings.setValue("hiliteColor", dlg.hilite_color) msgBox = QMessageBox() msgBox.setText("Please restart the app to change the theme!") msgBox.exec() def textChanged(self): if self.filename: with open(self.filename, "w") as f: f.write(self.text_edit.toPlainText()) self.lock = Lock() with self.lock: if not self.tread_running: self.tread_running = True self.htmlReady.connect(self.previewReady) thread = Thread(target=self.createHtml, args=(self.text_edit.toPlainText(), )) thread.daemon = True thread.start() def previewReady(self, html): self.preview.setHtml( html, baseUrl=QUrl( Path(os.path.join(self.book.source_path, "parts", "index.html")).as_uri())) self.htmlReady.disconnect() with self.lock: self.tread_running = False def createHtml(self, text): html = "<html>\n<head>\n" html += "<link href=\"../css/pastie.css\" rel=\"stylesheet\" type=\"text/css\"/>\n" html += "<link href=\"../css/stylesheet.css\" rel=\"stylesheet\" type=\"text/css\"/>\n" html += "</head>\n<body>\n" html += markdown(text, html4tags=False, extras=[ "fenced-code-blocks", "wiki-tables", "tables", "header-ids" ]) html += "\n</body>\n</html>" html = addLineNumbers(html) self.htmlReady.emit(html) def pdfExport(self): p = PdfExport("test.pdf", self.book, self.statusBar())
class EventSelectionWindow(QMainWindow): def __init__(self, main_window): super().__init__() self.main_window = main_window # Defining some variables of the window self.title_window = "Event Selection" # Setting the window appropriately self.setWindowTitle(self.title_window) self.set_position() self.palette_main_window = self.palette() self.palette_main_window.setColor(QPalette.Window, Qt.black) # Initiate the sub-widgets self.init_window() def init_window(self): # Read the available labels self.labels = list() with open('../config/classes.txt') as file: for cnt, line in enumerate(file): self.labels.append(line.rstrip()) # Read the available second labels self.second_labels = list() with open('../config/second_classes.txt') as file: for cnt, line in enumerate(file): self.second_labels.append(line.rstrip()) # Read the available third labels self.third_labels = list() with open('../config/third_classes.txt') as file: for cnt, line in enumerate(file): self.third_labels.append(line.rstrip()) self.list_widget = QListWidget() self.list_widget.clicked.connect(self.clicked) for item_nbr, element in enumerate(self.labels): self.list_widget.insertItem(item_nbr,element) self.list_widget_second = QListWidget() self.list_widget_second.clicked.connect(self.clicked) for item_nbr, element in enumerate(self.second_labels): self.list_widget_second.insertItem(item_nbr,element) self.list_widget_third = QListWidget() self.list_widget_third.clicked.connect(self.clicked) for item_nbr, element in enumerate(self.third_labels): self.list_widget_third.insertItem(item_nbr,element) # Layout the different widgets central_display = QWidget(self) self.setCentralWidget(central_display) final_layout = QHBoxLayout() final_layout.addWidget(self.list_widget) final_layout.addWidget(self.list_widget_second) final_layout.addWidget(self.list_widget_third) central_display.setLayout(final_layout) self.to_second = False self.to_third = False self.first_label = None self.second_label = None def clicked(self, qmodelindex): print("clicked") def set_position(self): self.xpos_window = self.main_window.pos().x()+self.main_window.frameGeometry().width()//4 self.ypos_window = self.main_window.pos().y()+self.main_window.frameGeometry().height()//4 self.width_window = self.main_window.frameGeometry().width()//2 self.height_window = self.main_window.frameGeometry().height()//2 self.setGeometry(self.xpos_window, self.ypos_window, self.width_window, self.height_window) def keyPressEvent(self, event): if event.key() == Qt.Key_Return: if not self.to_second and not self.to_third: self.first_label = self.list_widget.currentItem().text() self.list_widget_second.setFocus() self.to_second=True elif self.to_second: self.second_label = self.list_widget_second.currentItem().text() self.to_second=False self.to_third=True self.list_widget_third.setFocus() elif self.to_third: position = self.main_window.media_player.media_player.position() self.main_window.list_manager.add_event(Event(self.first_label,self.main_window.half,ms_to_time(position),self.second_label, position, self.list_widget_third.currentItem().text())) self.main_window.list_display.display_list(self.main_window.list_manager.create_text_list()) self.first_label = None self.second_label = None self.to_third=False # Save path_label = self.main_window.media_player.get_last_label_file() self.main_window.list_manager.save_file(path_label, self.main_window.half) self.hide() self.list_widget_second.setCurrentRow(-1) self.list_widget_third.setCurrentRow(-1) self.main_window.setFocus() if event.key() == Qt.Key_Escape: self.to_second=False self.to_third=False self.first_label = None self.second_label = None self.list_widget_second.setCurrentRow(-1) self.list_widget_third.setCurrentRow(-1) self.hide() self.main_window.setFocus()
class repairshopStack(QWidget): def __init__(self): super(repairshopStack, self).__init__() self.leftlist = QListWidget() self.leftlist.setFixedWidth(250) self.leftlist.insertItem(0, 'Order Stock') self.leftlist.insertItem(2, 'View Shop Stock') self.leftlist.insertItem(4, 'Manufacturer Stock') self.stack1 = QWidget() self.stack3 = QWidget() self.stack5 = QWidget() self.stack1UI() self.stack3UI() self.stack5UI() self.Stack = QStackedWidget(self) self.Stack.addWidget(self.stack1) self.Stack.addWidget(self.stack3) self.Stack.addWidget(self.stack5) hbox = QHBoxLayout(self) hbox.addWidget(self.leftlist) hbox.addWidget(self.Stack) self.setLayout(hbox) self.leftlist.currentRowChanged.connect(self.display) self.setGeometry(500, 350, 200, 200) self.setWindowTitle('Stock Management') self.show() def stack1UI(self): # ADD STOCK UI layout = QFormLayout() self.mes = QLabel() self.ok = QPushButton('Order Stock', self) clear = QPushButton('Clear', self) self.stock_name = QLineEdit() layout.addRow("Stock Name", self.stock_name) self.stock_count = QLineEdit() layout.addRow("Quantity", self.stock_count) layout.addWidget(self.ok) layout.addWidget(clear) layout.addWidget(self.mes) self.ok.clicked.connect(self.on_click) # clear the data from the screen clear.clicked.connect(self.stock_name.clear) clear.clicked.connect(self.stock_count.clear) self.stack1.setLayout(layout) def on_click(self): # update car stock # remove quantity from Manufacturer stock stock_name_dec = self.stock_name.text().replace(' ', '_').lower() stock_count_dec = -(int(self.stock_count.text())) # add quantity to Shop stock stock_name_inc = self.stock_name.text().replace(' ', '_').lower() stock_count_inc = (int(self.stock_count.text())) if (stock_count_dec and stock_count_inc > 0): mp.del_shop_stock(stock_name_dec, stock_count_dec) message = mp.add_shop_stock(stock_name_inc, stock_count_inc) self.mes.setText(message) else: self.mes.setText('Invalid quantity') def stack3UI(self): # VIEW SHOP STOCK TAB layout = QVBoxLayout() self.srb = QPushButton() self.srb.setText("Get Search Result.") self.View = QTableWidget() self.lbl3 = QLabel() self.lbl_conf_text = QLabel() self.lbl_conf_text.setText("Enter the search keyword:") self.conf_text = QLineEdit() self.View.setColumnCount(3) self.View.setColumnWidth(0, 250) self.View.setColumnWidth(1, 250) self.View.setColumnWidth(2, 200) self.View.insertRow(0) self.View.setItem(0, 0, QTableWidgetItem('Stock Name')) self.View.setItem(0, 1, QTableWidgetItem('Quantity')) self.View.setItem(0, 2, QTableWidgetItem('Cost(Per Unit)')) layout.addWidget(self.View) layout.addWidget(self.lbl_conf_text) layout.addWidget(self.conf_text) layout.addWidget(self.srb) layout.addWidget(self.lbl3) self.srb.clicked.connect(self.show_search) self.stack3.setLayout(layout) def show_search(self): # checks database for stock items if self.View.rowCount() > 1: for i in range(1, self.View.rowCount()): self.View.removeRow(1) x_act = mp.show_shop_stock() x = [] if self.conf_text.text() != '': for i in range(0, len(x_act)): a = list(x_act[i]) if self.conf_text.text().lower() in a[0].lower(): x.append(a) else: x = mp.show_shop_stock() if len(x) != 0: for i in range(1, len(x) + 1): self.View.insertRow(i) a = list(x[i - 1]) self.View.setItem( i, 0, QTableWidgetItem(a[0].replace('_', ' ').upper())) self.View.setItem(i, 1, QTableWidgetItem(str(a[1]))) self.View.setItem(i, 2, QTableWidgetItem(str(a[2]))) self.View.setRowHeight(i, 50) self.lbl3.setText('Viewing Stock Database.') else: self.lbl3.setText('No valid information in database.') def stack5UI(self): # VIEW MANUFACTURER STOCK TAB layout_manu = QVBoxLayout() self.srb5 = QPushButton() self.srb5.setText("Get Search Result.") self.View5 = QTableWidget() self.lbl5 = QLabel() self.lbl_conf_text5 = QLabel() self.lbl_conf_text5.setText("Enter the search keyword:") self.conf_text5 = QLineEdit() self.View5.setColumnCount(3) self.View5.setColumnWidth(0, 250) self.View5.setColumnWidth(1, 250) self.View5.setColumnWidth(2, 200) self.View5.insertRow(0) self.View5.setItem(0, 0, QTableWidgetItem('Stock Name')) self.View5.setItem(0, 1, QTableWidgetItem('Quantity')) self.View5.setItem(0, 2, QTableWidgetItem('Cost(Per Unit)')) layout_manu.addWidget(self.View5) layout_manu.addWidget(self.lbl_conf_text5) layout_manu.addWidget(self.conf_text5) layout_manu.addWidget(self.srb5) layout_manu.addWidget(self.lbl5) self.srb5.clicked.connect(self.show_manu_search) self.stack5.setLayout(layout_manu) def show_manu_search(self): # checks database for stock items if self.View5.rowCount() > 1: for i in range(1, self.View5.rowCount()): self.View5.removeRow(1) x_act = mp.show_manu_order_stock() x = [] if self.conf_text5.text() != '': for i in range(0, len(x_act)): a = list(x_act[i]) if self.conf_text5.text().lower() in a[0].lower(): x.append(a) else: x = mp.show_manu_order_stock() if len(x) != 0: for i in range(1, len(x) + 1): self.View5.insertRow(i) a = list(x[i - 1]) self.View5.setItem( i, 0, QTableWidgetItem(a[0].replace('_', ' ').upper())) self.View5.setItem(i, 1, QTableWidgetItem(str(a[1]))) self.View5.setItem(i, 2, QTableWidgetItem(str(a[2]))) self.View5.setRowHeight(i, 50) self.lbl5.setText('Viewing Manufacturer Stock Database.') else: self.lbl5.setText('No valid information in database.') def display(self, i): self.Stack.setCurrentIndex(i)
class Gui(QMainWindow): mainText = None mast = None tenants = None inc = None rowStore = None Gen = None gen = None mastInput = None keyNum = 1 def __init__(self): super().__init__() self.initUI() def initUI(self): app_icon = QIcon() app_icon.addFile("key.png", QSize(256, 256)) self.setWindowIcon(app_icon) # open openFile = QAction('Open', self) openFile.setShortcut('Ctrl+O') openFile.setStatusTip('Open new File') openFile.triggered.connect(self.fileOpen) # save saveFile = QAction('Save', self) saveFile.setShortcut('Ctrl+S') saveFile.setStatusTip('Save new File') saveFile.triggered.connect(self.fileSave) printAction = QAction("Print", self) printAction.triggered.connect(self.printSetup) # exit exitAction = QAction('Exit', self) exitAction.triggered.connect(self.closeEvent) # menu object menubar = self.menuBar() # file drop down fileMenu = menubar.addMenu('&File') fileMenu.addAction(openFile) fileMenu.addAction(saveFile) fileMenu.addAction(printAction) fileMenu.addAction(exitAction) # widgets grid = QGridLayout() horiz = QVBoxLayout() bigHoriz = QHBoxLayout() horizLayout = QHBoxLayout() window = QWidget() window.setLayout(bigHoriz) leftPane = QFormLayout() bigHoriz.addLayout(leftPane) bigHoriz.addLayout(horiz) self.setCentralWidget(window) btn = QPushButton('Generate', self) btn.clicked.connect(lambda: self.runGen()) clearBtn = QPushButton("Clear", self) clearBtn.clicked.connect(self.clearList) self.mainText = QListWidget(self) self.mainText.itemSelectionChanged.connect(self.listItemClicked) self.mainText.setFont( QFontDatabase.systemFont(QFontDatabase.FixedFont)) self.mastInput = [] i = 0 while i < 6: t = QLineEdit() t.setMaxLength(1) t.setAlignment(Qt.AlignHCenter) t.textChanged.connect(self.textInputed) self.mastInput.append(t) i = i + 1 for e in self.mastInput: horizLayout.addWidget(e) self.mast = QLineEdit() self.tenants = QLineEdit() self.inc = QLineEdit() self.title = QLineEdit() self.title.setMinimumWidth(200) self.desc = QLineEdit() self.address = QLineEdit() self.contact = QLineEdit() self.phone = QLineEdit() self.email = QLineEdit() self.notes = QTextEdit() self.keyway = QLineEdit() label = QLabel("Master Cuts") incLabel = QLabel("Increment") tenantLabel = QLabel("Tenants") # add widgets to layouts leftPane.addRow(QLabel("Title"), self.title) leftPane.addRow(QLabel("Description"), self.desc) leftPane.addRow(QLabel("Keyway"), self.keyway) leftPane.addRow(QLabel("Address"), self.address) leftPane.addRow(QLabel("contact"), self.contact) leftPane.addRow(QLabel("Phone"), self.phone) leftPane.addRow(QLabel("Email"), self.email) leftPane.addRow(QLabel("Notes"), self.notes) grid.addWidget(incLabel, 3, 0) grid.addWidget(tenantLabel, 2, 0) grid.addWidget(label, 1, 0) grid.addWidget(btn, 0, 0) horiz.addWidget(self.mainText) horiz.addLayout(grid) # horiz.addLayout(horizLayout) grid.addWidget(clearBtn, 0, 1) grid.addWidget(self.tenants, 2, 1) grid.addWidget(self.inc, 3, 1) grid.addLayout(horizLayout, 1, 1) # window properties self.setGeometry(300, 300, 500, 425) self.setWindowTitle('PySchlageGen') self.show() def textInputed(self, string): if len(string) == 1: self.focusNextChild() def getGen(self): return self.gen def runGen(self): self.mainText.clear() self.keyNum = 1 text = self.mast.text() mastCuts = [] try: for e in self.mastInput: if e.text(): mastCuts.append(int(e.text())) tenants = int(self.tenants.text()) inc = int(self.inc.text()) mastCuts = list(map(int, mastCuts)) self.gen = schlageGen() self.gen.addMasterKey(mastCuts) output = self.gen.genSystem(tenants, inc) self.displayKeys(output) except: pass def displayKeys(self, output): i = 0 for o in output: if self.keyNum < 10: f = str(self.keyNum) + ": " elif self.keyNum < 100: f = str(self.keyNum) + ": " elif self.keyNum < 1000: f = str(self.keyNum) + ": " else: f = str(self.keyNum) + ":" for e in o: f = f + str(e) + " " item = QListWidgetItem(f) self.mainText.insertItem(i, item) i = i + 1 self.keyNum = self.keyNum + 1 def formatText(self, flist, space=True, inj=" "): out = "" for e in flist[:-1]: out = out + str(e) if space: out = out + inj out = out + str(flist[-1]) return out def printSetup(self): if self.gen != None: printSetup = PrintSetup(self) printSetup.exec() else: QMessageBox.about(self, "Error","Please generate a system before printing.") def clearList(self): self.title.clear() self.desc.clear() self.address.clear() self.keyway.clear() self.contact.clear() self.phone.clear() self.email.clear() self.keyNum = 1 self.notes.clear() self.mainText.clear() self.tenants.clear() self.inc.clear() for e in self.mastInput: e.clear() def listItemClicked(self): item = self.mainText.currentItem() flags = item.flags() if flags & Qt.ItemIsEnabled: if self.rowStore != None: self.mainText.takeItem(self.rowStore + 1) self.mainText.takeItem(self.rowStore + 1) tenCuts = self.gen.getSystem()[int(item.text().split(":")[0]) - 1] tenCuts = list(map(int, tenCuts)) output = self.gen.bittingCalc(tenCuts) row = self.mainText.currentRow() self.rowStore = row flags = item.flags() flags ^= Qt.ItemIsEnabled item = QListWidgetItem(" " + self.formatText(output[0])) item.setFlags(flags) item2 = QListWidgetItem(" " + self.formatText(output[1])) item2.setFlags(flags) self.mainText.insertItem(row + 1, item) self.mainText.insertItem(row + 2, item2) def fileOpen(self): self.clearList() home = expanduser("~") fname = QFileDialog.getOpenFileName(self, 'Open file', home, "*.mks") data = None if fname[0] != '': with open(fname[0], 'r') as infile: data = infile.read() sys = data.split("`") self.gen = schlageGen() master = list(map(int, sys[0])) self.gen.addMasterKey(master) del sys[0] self.inc.setText(str(sys[0])) del sys[0] self.title.setText(str(sys[0])) del sys[0] self.desc.setText(str(sys[0])) del sys[0] self.keyway.setText(str(sys[0])) del sys[0] self.address.setText(str(sys[0])) del sys[0] self.contact.setText(str(sys[0])) del sys[0] self.phone.setText(str(sys[0])) del sys[0] self.email.setText(str(sys[0])) del sys[0] self.notes.setPlainText(str(sys[0])) del sys[0] self.gen.setTenants(sys) self.displayKeys(sys) i = 0 while i < len(master): self.mastInput[i].setText(str(master[i])) i = i + 1 self.tenants.setText(str(len(sys))) def fileSave(self): if self.gen != None: home = expanduser("~") fname = QFileDialog.getSaveFileName(self, 'Open file', home, "*.mks") if fname[0]: with open(fname[0], "w") as thefile: thefile.write("%s`" % self.formatText( self.gen.getMasterKey(), False)) thefile.write("%s`" % self.inc.text()) thefile.write("%s`" % self.title.text()) thefile.write("%s`" % self.desc.text()) thefile.write("%s`" % self.keyway.text()) thefile.write("%s`" % self.address.text()) thefile.write("%s`" % self.contact.text()) thefile.write("%s`" % self.phone.text()) thefile.write("%s`" % self.email.text()) thefile.write("%s`" % self.notes.toPlainText()) for e in self.gen.getSystem()[:-1]: thefile.write("%s`" % self.formatText(e, False)) thefile.write("%s" % self.formatText( self.gen.getSystem()[-1], False)) else: QMessageBox.about(self, "Error","Please generate a system before saving.") def closeEvent(self, event): reply = QMessageBox.question(self, 'Message', "Are you sure to quit?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.Yes: sys.exit() else: if event: event.ignore()
class App(QMainWindow): game_filter_positions = { "nhl": ['C', 'LW', 'RW', 'D'], "nfl": ['QB', 'RB', 'WR', 'TE', 'K', 'DEF'] } game_projection_columns = { "nhl": { 'name': 'Name', 'posn_display': 'POS', 'team': 'Team', 'csh_rank': 'CSH', 'preseason_rank': 'Preseason', 'current_rank': 'Current', 'GP': 'GP', 'fantasy_score': 'Score' }, "nfl": { 'name': 'Name', 'posn_display': 'POS', 'team': 'Team', 'Bye': 'Bye', 'fan_points': 'fan_points', 'overall_rank': 'Rank', 'fp_rank': 'FP_rank', 'position_rank': 'FP_Pos' } } # ['name', 'position', 'player_id', 'GP', 'Bye', 'fan_points', # 'overall_rank', 'percent_rostered', 'pass_yds', 'pass_td', 'pass_int', # 'pass_sack', 'rush_attempts', 'rush_yards', 'rush_tds', # 'receiving_targets', 'receiving_receptions', 'receiving_yards', # 'receiving_tds', 'team'] def __init__(self): super().__init__() self.title = "Yahoo Draft Board" self.left = 10 self.top = 10 self.width = 3200 self.height = 1000 self.main_widget = QWidget(self) self.oauth = OAuth2(None, None, from_file='oauth2.json') league_id = "403.l.41177" self.game_type = "nhl" self.game_year = 2020 self.league = None self.setup_menubar() self.my_draft_picks = None self.label_num_picks_before_pick = QLabel( "Number picks until your turn: 4") self.positional_filter_checkboxes = None # runnable to grab draft picks from yahoo self.draft_monitor = None self.draft_complete = False self.draft_results = [] self.position_filtering_layout = QHBoxLayout() self.draft_list_widget = QListWidget() self.projection_table = QTableView() self.projection_table.setSortingEnabled(True) self.pause_draft_button = QPushButton("Pause") self.pause_draft_button.clicked.connect(self.pause_pressed) self.team_combo_box = QComboBox() self.draft_status_label = QLabel("Status: Running") self.roster_table = QTableView() self.roster_table_model = None # self.league_changed("403.l.41177", "nhl", 2020) self.league_changed("406.l.246660", "nfl", 2021) self.initUI() def setup_menubar(self): menuBar = QMenuBar(self) menuBar.setNativeMenuBar(False) self.setMenuBar(menuBar) leagueMenu = menuBar.addMenu("&League") years = [2021, 2020, 2019, 2018] fantasy_games = ['nhl', 'nfl'] for year in years: year_menu = leagueMenu.addMenu(str(year)) for game in fantasy_games: game_menu = year_menu.addMenu(game) gm = yfa.Game(self.oauth, game) ids = gm.league_ids(year) for id in ids: lg = gm.to_league(id) lg_action = QAction(lg.settings()['name'], self) lg_action.league_id = id lg_action.game_type = game lg_action.year = year game_menu.addAction(lg_action) game_menu.triggered[QAction].connect(self.league_selected) def league_selected(self, q): print("league selected") if not (q.league_id == self.league_id and q.year == self.game_year): self.league_changed(q.league_id, q.game_type, q.year) def get_scraped_draft_results(self): scraped_draft_results = None draft_scrape_filename = f".cache/gui_draft/draft-scrape-{self.league_id}-{self.game_type}-{self.game_year}.pkl" if os.path.exists(draft_scrape_filename): with open(draft_scrape_filename, "rb") as f: scraped_draft_results = pickle.load(f) else: scraped_draft_results = retrieve_draft_order(self.league) with open(draft_scrape_filename, "wb") as f: pickle.dump(scraped_draft_results, f) return scraped_draft_results def league_changed(self, league_id, game_type, year): print( f"League changed, id: {league_id} - type: {game_type} - year:{year}" ) if self.draft_monitor is not None and self.league_id != league_id: self.draft_monitor.stop_flag = True print("Draft thread cancelled") self.league_id = league_id self.game_type = game_type self.game_year = year self.league = yfa.league.League(self.oauth, self.league_id) self.my_draft_picks = self._get_draft_picks(self.league.team_key()) self.projections_df = self.retrieve_player_projections() # figure out if we have keepers scraped_draft_results = None if "nfl" == self.game_type: # for keeper leagues, would be defined in a screen scrape. lets do that and simulate them scraped_draft_results = self.get_scraped_draft_results() # assign keepers to teams for team, keepers in scraped_draft_results['keepers'].items(): for keeper in keepers: if 'team' in keeper.keys(): self.projections_df.loc[ self.projections_df.name.str. contains(keeper['name']) & (self.projections_df.team == keeper['team'].upper()), ['draft_fantasy_key', 'is_keeper']] = team, True else: self.projections_df.loc[ self.projections_df.name.str. contains(keeper['name']), ['draft_fantasy_key', 'is_keeper']] = team, True base_columns = App.game_projection_columns[self.game_type].copy() # add league specifc scoring cats for hockey if self.game_type == "nhl": for stat in self.league.stat_categories(): if stat['position_type'] == 'P': base_columns[stat['display_name']] = stat['display_name'] self.projections_model = PandasModel( self.projections_df, column_headers=base_columns, valid_positions=App.game_filter_positions[self.game_type], highlight_personal_ranking_differences=self.game_type == "nhl") self.team_name_by_id = { team['team_key']: team['name'] for team in self.league.teams() } self.team_key_by_name = { team['name']: team['team_key'] for team in self.league.teams() } self.build_filter_panel() self.projections_model.update_filters(self._position_filter_list()) self.draft_complete = False self.draft_results = [] self.projection_table.setModel(self.projections_model) self.pause_draft_button = QPushButton("Pause") self.pause_draft_button.clicked.connect(self.pause_pressed) self.team_combo_box.clear() for team in self.league.teams(): self.team_combo_box.addItem(team['name'], team['team_key']) self.team_combo_box.setCurrentIndex( self.team_combo_box.findData(self.league.team_key())) self.roster_table_model = DraftedRosterModel( self.draft_results, self.league.team_key(), self.projections_df, keepers=scraped_draft_results['keepers'] if scraped_draft_results else None) self.roster_table.setModel(self.roster_table_model) self.draft_list_widget.clear() self.update_picks_until_next_player_pick(0) the_keepers = scraped_draft_results.get( 'keepers', None) if scraped_draft_results else None self.draft_monitor = DraftMonitor(self.league, keepers=the_keepers) self.draft_monitor.register_draft_listener(self) QThreadPool.globalInstance().start(self.draft_monitor) def build_filter_panel(self): ''' builds the position checkbox filters depends on game_type ''' # clear any checkboxes for i in reversed(range(self.position_filtering_layout.count())): self.position_filtering_layout.itemAt(i).widget().close() self.position_filtering_layout.takeAt(i) self.positional_filter_checkboxes = [] for position in self.game_filter_positions[self.game_type]: box = QCheckBox(position) box.setChecked(True) box.clicked.connect(self.filter_checked) self.positional_filter_checkboxes.append(box) self.position_filtering_layout.addWidget(box) all_button = QPushButton('All') all_button.pressed.connect(self.filter_all_selected) self.position_filtering_layout.addWidget(all_button) none_button = QPushButton('None') none_button.pressed.connect(self.filter_none_selected) self.position_filtering_layout.addWidget(none_button) def retrieve_player_projections(self): projections = None if "nhl" == self.game_type: projections = retrieve_yahoo_rest_of_season_projections( self.league.league_id) scoring_stats = [ stat['display_name'] for stat in self.league.stat_categories() if stat['position_type'] == 'P' ] produce_csh_ranking(projections, scoring_stats, projections.index, ranking_column_name='fantasy_score') projections['fantasy_score'] = round(projections['fantasy_score'], 3) projections.reset_index(inplace=True) projections.sort_values("fantasy_score", ascending=False, inplace=True, ignore_index=True) projections['csh_rank'] = projections.index + 1 projections['rank_diff'] = projections['csh_rank'] - projections[ 'current_rank'] else: projections = pd.read_csv( f"{self.game_year}-{self.league_id}-predictions-merged.csv", converters={ "position": lambda x: x.strip("[]").replace('"', "").replace("'", ""). replace(" ", "").split(",") }) projections.sort_values('overall_rank', inplace=True) projections['draft_fantasy_key'] = -1 projections['is_keeper'] = False # use this for rendering eligible positions projections['posn_display'] = projections['position'].apply( lambda x: ", ".join(x)) return projections def draft_status_changed(self, is_paused): self.draft_status_label.setText( "Status: Paused" if is_paused else "Status: Running") def _get_draft_picks(self, team_key): scraped = self.get_scraped_draft_results() if 'predraft' == scraped['status']: return scraped['draft_picks'].get('team_key', []) else: return [ int(key['number']) for key in scraped['draft_picks'][team_key] ] def initUI(self): self.setWindowTitle(self.title) self.setCentralWidget(self.main_widget) self.layout_screen() self.show() def pause_pressed(self): print("Pause pressed") if self.draft_monitor: self.draft_monitor.toggle_paused() if "Pause" == self.pause_draft_button.text(): self.pause_draft_button.setText("Resume") else: self.pause_draft_button.setText("Pause") def handle_team_roster_changed(self, index): print(f"Do something with the selected item: {index.data()}") self.roster_table_model.specify_team_key( self.team_key_by_name[index.data()]) # self.roster_table.setModel(DraftedRosterModel(self.draft_results,self.team_key_by_name[index.data()])) def _set_projection_table_widths(self, table): table.setColumnWidth(0, 140) table.setColumnWidth(1, 60) table.setColumnWidth(2, 60) table.setColumnWidth(3, 50) table.setColumnWidth(4, 50) table.setColumnWidth(5, 50) table.setColumnWidth(6, 50) table.setColumnWidth(7, 60) table.setColumnWidth(8, 50) table.setColumnWidth(9, 50) table.setColumnWidth(10, 50) table.setColumnWidth(11, 50) table.setColumnWidth(12, 50) table.setColumnWidth(13, 50) table.setColumnWidth(14, 50) def hide_drafted_click(self): cbutton = self.sender() self.projections_model.set_hide_drafted(cbutton.isChecked()) def _position_filter_list(self): return { checkbox.text() for checkbox in self.positional_filter_checkboxes if checkbox.isChecked() } def filter_all_selected(self): print("All button pressed") for checkbox in self.positional_filter_checkboxes: checkbox.setChecked(True) self.projections_model.update_filters(self._position_filter_list()) def filter_none_selected(self): print("None pressed") for checkbox in self.positional_filter_checkboxes: checkbox.setChecked(False) self.projections_model.update_filters(self._position_filter_list()) def filter_checked(self, state): print(f"filter was checked: {self.sender().text()}") self.projections_model.update_filters(self._position_filter_list()) def layout_screen(self): layout = QHBoxLayout() left_layout = QVBoxLayout() second_filter_layout = QHBoxLayout() hide_drafted = QCheckBox('Hide drafted') hide_drafted.toggled.connect(self.hide_drafted_click) second_filter_layout.addWidget(hide_drafted) name_filter_layout = QFormLayout() name_filter_text = QLineEdit() name_filter_text.setMaximumWidth(400) name_filter_layout.addRow(QLabel("Name filter: "), name_filter_text) second_filter_layout.addLayout(name_filter_layout) filtering_layout = QVBoxLayout() filtering_layout.addLayout(self.position_filtering_layout) filtering_layout.addLayout(second_filter_layout) second_filter_layout.addStretch(1) # this displays list of eligible players, and their projections self._set_projection_table_widths(self.projection_table) projection_layout = QVBoxLayout() projection_layout.addWidget(self.projection_table) # projection_layout.addWidget(QTableView()) roster_layout = QVBoxLayout() roster_layout.addWidget(self.draft_status_label) roster_team_selection = QHBoxLayout() # set up draft pause button roster_team_selection.addWidget(self.pause_draft_button) roster_team_selection.addWidget(QLabel("Team: ")) self.team_combo_box.view().pressed.connect( self.handle_team_roster_changed) roster_team_selection.addWidget(self.team_combo_box) roster_layout.addLayout(roster_team_selection) game_selection_layout = QHBoxLayout() game_selection_layout.addWidget(self.label_num_picks_before_pick) game_selection_layout.addStretch(1) roster_layout.addWidget(self.roster_table) roster_layout.addWidget(self.draft_list_widget) left_layout.addLayout(game_selection_layout) left_layout.addLayout(filtering_layout) left_layout.addLayout(projection_layout) layout.addLayout(left_layout, stretch=2) layout.addLayout(roster_layout) self.main_widget.setLayout(layout) def player_drafted(self, draft_entry): self.draft_results.append(draft_entry) print(f"de: {draft_entry}") player_id = int(draft_entry['player_key'].split('.')[-1]) draft_position = int(draft_entry['pick']) player_name = None draft_value = 'n/a' try: player_name = self.projections_df[self.projections_df.player_id == player_id]['name'].values[0] # figure out draft position relative to csh_rank ranking try: csh_rank = self.projections_df[self.projections_df.player_id == player_id]['csh_rank'].values[0] draft_value = draft_position - csh_rank except KeyError: pass except IndexError: player_name = f"Unknown({player_id})" team_id = int(draft_entry['team_key'].split('.')[-1]) self.projections_model.player_drafted(player_id, team_id) self.roster_table_model.player_drafted(draft_entry) try: next_draft = next(x for x in self.my_draft_picks if x > draft_position) self.label_num_picks_before_pick.setText( f"Number picks until your turn: {next_draft-draft_position}") except StopIteration: pass self.draft_list_widget.insertItem( 0, f"{draft_position}. {player_name} - {self.team_name_by_id[draft_entry['team_key']]} - ({draft_value})" ) def update_picks_until_next_player_pick(self, current_draft_position): try: next_draft = next(x for x in self.my_draft_picks if x > current_draft_position) self.label_num_picks_before_pick.setText( f"Number picks until your turn: {next_draft-current_draft_position}" ) except StopIteration: pass
class App(QDialog): def __init__(self): super().__init__() self.title = "CSH Draft Board" self.left = 10 self.top = 10 self.width = 3200 self.height = 1000 self.projection_columns = { 'name': 'Name', 'posn_display': 'POS', 'team': 'Team', 'csh_rank': 'CSH', 'preseason_rank': 'Preseason', 'current_rank': 'Current', 'GP': 'GP', 'fantasy_score': 'Score' } # lg_id = "403.l.18782" lg_id = "403.l.41177" self.manager = ManagerBot(league_id=lg_id) my_team_key = self.manager.lg.team_key() for cat in self.manager.stat_categories: self.projection_columns[cat] = cat self.my_draft_picks = self._get_draft_picks(my_team_key) self.label_num_picks_before_pick = QLabel( "Number picks until your turn: 4") self.projections_df = self.retrieve_player_projections() # use this for rendering eligible positions self.projections_df['posn_display'] = self.projections_df[ 'position'].apply(lambda x: ", ".join(x)) self.projections_model = PandasModel( self.projections_df, column_headers=self.projection_columns) self.team_name_by_id = { team['team_key']: team['name'] for team in self.manager.lg.teams() } self.team_key_by_name = { team['name']: team['team_key'] for team in self.manager.lg.teams() } self.positional_filter_checkboxes = { pos: QCheckBox(pos) for pos in ['C', 'LW', 'RW', 'D'] } for checkbox in self.positional_filter_checkboxes.values(): checkbox.setChecked(True) self.last_draft_count = 0 self.draft_supplier = None self.draft_complete = False self.draft_results = [] self.draft_list_widget = QListWidget() self.pause_draft_button = QPushButton("Pause") self.pause_draft_button.clicked.connect(self.pause_pressed) self.team_combo_box = QComboBox() self.draft_status_label = QLabel("Status: Running") for team in self.manager.lg.teams(): self.team_combo_box.addItem(team['name'], team['team_key']) self.team_combo_box.setCurrentIndex( self.team_combo_box.findData(my_team_key)) self.roster_table = QTableView() self.roster_table_model = DraftedRosterModel(self.draft_results, my_team_key, self.projections_df) self.roster_table.setModel(self.roster_table_model) self.initUI() def retrieve_player_projections(self): projections = retrieve_yahoo_rest_of_season_projections( self.manager.lg.league_id) produce_csh_ranking(projections, self.manager.stat_categories, projections.index, ranking_column_name='fantasy_score') projections['fantasy_score'] = round(projections['fantasy_score'], 3) projections.reset_index(inplace=True) projections.sort_values("fantasy_score", ascending=False, inplace=True, ignore_index=True) projections['csh_rank'] = projections.index + 1 projections['draft_fantasy_key'] = -1 return projections def draft_status_changed(self, status): if status: self.draft_status_label.setText("Status: Paused") else: self.draft_status_label.setText("Status: Running") def _get_draft_picks(self, team_key): draft_results = self.manager.lg.draft_results() # find first pick which matches team_key draft_position = None for draft_info in draft_results: if draft_info['team_key'] == team_key: draft_position = int(draft_info['pick']) assert draft_info[ 'round'] == 1, "Didnt find team key in first round of draft" break # num teams in league n_teams = len(self.manager.lg.teams()) #num rounds in draft n_rounds = int(len(draft_results) / n_teams) return generate_snake_draft_picks(n_teams=n_teams, n_rounds=n_rounds, draft_position=draft_position) def initUI(self): self.setWindowTitle(self.title) self.layout_screen() self.show() def pause_pressed(self): print("Pause pressed") if self.draft_supplier: self.draft_supplier.toggle_paused() if "Pause" == self.pause_draft_button.text(): self.pause_draft_button.setText("Resume") else: self.pause_draft_button.setText("Pause") def handle_team_roster_changed(self, index): print(f"Do something with the selected item: {index.data()}") self.roster_table_model.specify_team_key( self.team_key_by_name[index.data()]) # self.roster_table.setModel(DraftedRosterModel(self.draft_results,self.team_key_by_name[index.data()])) def _set_projection_table_widths(self, table): table.setColumnWidth(0, 140) table.setColumnWidth(1, 60) table.setColumnWidth(2, 60) table.setColumnWidth(3, 50) table.setColumnWidth(4, 50) table.setColumnWidth(5, 50) table.setColumnWidth(6, 50) table.setColumnWidth(7, 60) table.setColumnWidth(8, 50) table.setColumnWidth(9, 50) table.setColumnWidth(10, 50) table.setColumnWidth(11, 50) table.setColumnWidth(12, 50) table.setColumnWidth(13, 50) table.setColumnWidth(14, 50) def hide_drafted_click(self): cbutton = self.sender() self.projections_model.set_hide_drafted(cbutton.isChecked()) # self.projections_model.show_drafted = not cbutton.isChecked() # self.projections_model.modelReset.emit() def _position_filter_list(self): return { posn for posn, checkbox in self.positional_filter_checkboxes.items() if checkbox.isChecked() } def filter_all_selected(self): print("All button pressed") for checkbox in self.positional_filter_checkboxes.values(): checkbox.setChecked(True) self.projections_model.update_filters(self._position_filter_list()) def filter_none_selected(self): print("None pressed") for checkbox in self.positional_filter_checkboxes.values(): checkbox.setChecked(False) self.projections_model.update_filters(self._position_filter_list()) def filter_checked(self, state): print(f"filter was checked: {self.sender().text()}") self.projections_model.update_filters(self._position_filter_list()) def layout_screen(self): layout = QHBoxLayout() left_layout = QVBoxLayout() # filtering position_filtering_layout = QHBoxLayout() for posn, checkbox in self.positional_filter_checkboxes.items(): checkbox.clicked.connect(self.filter_checked) position_filtering_layout.addWidget(checkbox) # position_filtering_layout.addWidget(QCheckBox('G')) all_button = QPushButton('All') all_button.pressed.connect(self.filter_all_selected) position_filtering_layout.addWidget(all_button) none_button = QPushButton('None') none_button.pressed.connect(self.filter_none_selected) position_filtering_layout.addWidget(none_button) second_filter_layout = QHBoxLayout() hide_drafted = QCheckBox('Hide drafted') hide_drafted.toggled.connect(self.hide_drafted_click) second_filter_layout.addWidget(hide_drafted) name_filter_layout = QFormLayout() name_filter_text = QLineEdit() name_filter_text.setMaximumWidth(400) name_filter_layout.addRow(QLabel("Name filter: "), name_filter_text) second_filter_layout.addLayout(name_filter_layout) filtering_layout = QVBoxLayout() filtering_layout.addLayout(position_filtering_layout) filtering_layout.addLayout(second_filter_layout) second_filter_layout.addStretch(1) # this displays list of eligible players, and their projections projection_table = QTableView() projection_table.setModel(self.projections_model) self._set_projection_table_widths(projection_table) projection_layout = QVBoxLayout() projection_layout.addWidget(projection_table) projection_layout.addWidget(QTableView()) roster_layout = QVBoxLayout() roster_layout.addWidget(self.draft_status_label) roster_team_selection = QHBoxLayout() # set up draft pause button roster_team_selection.addWidget(self.pause_draft_button) roster_team_selection.addWidget(QLabel("Team: ")) self.team_combo_box.view().pressed.connect( self.handle_team_roster_changed) roster_team_selection.addWidget(self.team_combo_box) roster_layout.addLayout(roster_team_selection) # this sets row headers of roster to num spots for each position # roster_makeup_index = 0 # for posn, num_spots in self.manager.lg.roster_makeup().items(): # if posn != 'IR': # for _ in range(num_spots): # self.roster_table.setVerticalHeaderItem(roster_makeup_index, QTableWidgetItem(posn)) # roster_makeup_index += 1 roster_layout.addWidget(self.roster_table) roster_layout.addWidget(self.draft_list_widget) left_layout.addWidget(self.label_num_picks_before_pick) left_layout.addLayout(filtering_layout) left_layout.addLayout(projection_layout) layout.addLayout(left_layout, stretch=2) layout.addLayout(roster_layout) self.setLayout(layout) def register_draft_supplier(self, draft_supplier): self.draft_supplier = draft_supplier def player_drafted(self, draft_entry): self.draft_results.append(draft_entry) player_id = int(draft_entry['player_key'].split('.')[-1]) draft_position = int(draft_entry['pick']) player_name = None draft_value = 'n/a' try: player_name = self.projections_df[self.projections_df.player_id == player_id]['name'].values[0] # figure out draft position relative to csh_rank ranking csh_rank = self.projections_df[self.projections_df.player_id == player_id]['csh_rank'].values[0] draft_value = draft_position - csh_rank except IndexError: player_name = f"Unknown({player_id})" team_id = int(draft_entry['team_key'].split('.')[-1]) print(f"Player1 drafted was: {player_id}") self.projections_model.player_drafted(player_id, team_id) self.roster_table_model.player_drafted(draft_entry) next_draft = next(x for x in self.my_draft_picks if x > draft_position) self.label_num_picks_before_pick.setText( f"Number picks until your turn: {next_draft-draft_position}") # self.my_draft_picks # "Number picks until your turn: 4" # self.label_num_picks_before_pick # 6743 mcdavid self.draft_list_widget.insertItem( 0, f"{draft_position}. {player_name} - {self.team_name_by_id[draft_entry['team_key']]} - ({draft_value})" )
class SelectInitCoordinate(QDockWidget): def __init__(self, vp_eng, *__args): super().__init__(*__args) self.vp_eng = None self.point_selected = None self.list_widget = None self.main_layout = None self.main_widget = None self.reset(vp_eng) # set UI # self.xy_button = QPushButton("Use XY") # self.xy_button.clicked.connect(lambda: self.extract_side("XY", self.vp_eng.coordinates[self.point_selected[0]], self.vp_eng.coordinates[self.point_selected[1]])) # self.xz_button = QPushButton("Use XZ") # self.xz_button.clicked.connect(lambda: self.extract_side("XZ", self.vp_eng.coordinates[self.point_selected[0]], self.vp_eng.coordinates[self.point_selected[1]])) # self.yz_button = QPushButton("Use YZ") # self.yz_button.clicked.connect(lambda: self.extract_side("YZ", self.vp_eng.coordinates[self.point_selected[0]], self.vp_eng.coordinates[self.point_selected[1]])) # self.hBox = QHBoxLayout() # self.hBox.addWidget(self.xy_button) # self.hBox.addWidget(self.xz_button) # self.hBox.addWidget(self.yz_button) # self.main_layout.addLayout(self.hBox) def reset(self, vp_eng): self.vp_eng = vp_eng self.point_selected = [] # init UI self.list_widget = QListWidget() self.list_widget.itemClicked.connect(self.item_click) self.setWidget(self.list_widget) self.add_layer() self.add_layer() self.add_layer() # self.main_layout = QVBoxLayout() # self.main_layout.addWidget(self.list_widget) # # self.main_widget = QWidget() # self.main_widget.setLayout(self.main_layout) # # self.setWidget(self.main_widget) def add_layer(self): index = self.list_widget.count() widget = LayerWidget(index, self.point_selected) item = QListWidgetItem() item.setData(Qt.UserRole, index) self.list_widget.insertItem(index, item) self.list_widget.setItemWidget(item, widget) self.vp_eng.coordinates.append([0, 0]) item.setSizeHint(widget.sizeHint()) def on_point_selected(self, coordinate): widget = self.list_widget.itemWidget(self.list_widget.currentItem()) widget.set_coordinate(coordinate) def extract_side(self, axis, point0, point1): xvpoint = self.vp_eng.vpoints[0] yvpoint = self.vp_eng.vpoints[1] zvpoint = self.vp_eng.vpoints[2] basename = self.window().input_file.name if axis == "XY": vpoint1 = xvpoint vpoint2 = yvpoint img_out = "output/" + basename + "/" + basename + "(top).png" elif axis == "XZ": vpoint1 = xvpoint vpoint2 = zvpoint img_out = "output/" + basename + "/" + basename + "(right).png" elif axis == "YZ": vpoint1 = yvpoint vpoint2 = zvpoint img_out = "output/" + basename + "/" + basename + "(left).png" L1 = line(point0, vpoint1) L2 = line(point1, vpoint2) R1 = [round(i) for i in intersection(L1, L2)] if R1: print("Intersection 1 detected:", R1) else: print("No single intersection point detected") L1 = line(point0, vpoint2) L2 = line(point1, vpoint1) R2 = [round(i) for i in intersection(L1, L2)] if R1: print("Intersection 2 detected:", R2) else: print("No single intersection point detected") all_points = [point0, point1, R1, R2] ordered_points = rearrange_point(all_points) engine = FixPerspective(self.window().input_file.base, img_out, ordered_points, 400, 400) engine.show() @pyqtSlot() def item_click(self): self.vp_eng.set_current_wizard(Wizard.ADD_POINT) index = self.list_widget.currentItem().data(Qt.UserRole) self.window().centralWidget().graphics_view.set_coordinate_index( index, self.on_point_selected)
class ManagerDialog(QWidget): def __init__(self, parent=None): QWidget.__init__(self, parent) # remember the parent for future use self.parent = parent self.dict = None # main_window settings self.setWindowRole("QDialog") self.setWindowModality(Qt.ApplicationModal) self.setWindowFlags(self.windowFlags() & QtCore.Qt.CustomizeWindowHint) self.setWindowFlags(self.windowFlags() | Qt.Window | Qt.WindowMaximizeButtonHint | Qt.WindowCloseButtonHint) self.setWindowFlags(self.windowFlags() & ~QtCore.Qt.WindowMinimizeButtonHint) self.resize(800, 600) # layouts self._layout = QtWidgets.QVBoxLayout(self) self._buttonlayout = QtWidgets.QHBoxLayout() self._layout.addLayout(self._buttonlayout) # item list widget self._itemscroll = QListWidget() def moveUp(self, subDictName, id): """ move the item one position up """ if id: i = self.dict[subDictName].index(id) row = self._getIndexinList(id) if i > 0 and row > 0: j = 1 item = self._itemscroll.item(row - j) # skip hidden items while i - j > 0 and item.isHidden(): j += 1 item = self._itemscroll.item(row - j) if not item or item.isHidden(): return # update dict tmp = self.dict[subDictName][i] for k in range(0, j): self.dict[subDictName][i - k] = self.dict[subDictName][i - k - 1] self.dict[subDictName][i - j] = tmp widget = self.createWidget(id) #move item widgets scroll = self._itemscroll.cursor() item = self._itemscroll.takeItem(row) item.setSizeHint(widget.sizeHint()) self._itemscroll.insertItem(row - j, item) self._itemscroll.setItemWidget(item, widget) self._itemscroll.setCursor(scroll) self.updateAfterMovement() def moveDown(self, subDictName, id): """ move the item one position down """ if id: i = self.dict[subDictName].index(id) row = self._getIndexinList(id) dictLength = len(self.dict[subDictName]) if i < dictLength - 1 and row < dictLength - 1: j = 1 item = self._itemscroll.item(row + j) # skip hidden items while i + j < dictLength and item.isHidden(): j += 1 item = self._itemscroll.item(row + j) if not item or item.isHidden(): return # update dict tmp = self.dict[subDictName][i] for k in range(0, j): self.dict[subDictName][i + k] = self.dict[subDictName][i + k + 1] self.dict[subDictName][i + j] = tmp widget = self.createWidget(id) #move item widgets scroll = self._itemscroll.cursor() item = self._itemscroll.takeItem(row) item.setSizeHint(widget.sizeHint()) self._itemscroll.insertItem(row + j, item) self._itemscroll.setItemWidget(item, widget) self._itemscroll.setCursor(scroll) self.updateAfterMovement() def createWidget(self, id): """ abstract method returning a widget for one entry in the list """ raise NotImplementedError("Please Implement this method") def updateAfterMovement(self): """ abstract method """ raise NotImplementedError("Please Implement this method")
class stackedExample(QWidget): def __init__(self): super(stackedExample, self).__init__() self.leftlist = QListWidget() self.leftlist.setFixedWidth(250) self.leftlist.insertItem(0, 'Add Stock') self.leftlist.insertItem(1, 'Manage Stock') self.leftlist.insertItem(2, 'View Stock') self.leftlist.insertItem(3, 'View Transaction History') self.stack1 = QWidget() self.stack2 = QWidget() self.stack3 = QWidget() self.stack4 = QWidget() self.stack1UI() self.stack2UI() self.stack3UI() self.stack4UI() self.Stack = QStackedWidget(self) self.Stack.addWidget(self.stack1) self.Stack.addWidget(self.stack2) self.Stack.addWidget(self.stack3) self.Stack.addWidget(self.stack4) hbox = QHBoxLayout(self) hbox.addWidget(self.leftlist) hbox.addWidget(self.Stack) self.setLayout(hbox) self.leftlist.currentRowChanged.connect(self.display) # self.setGeometry(500, 350, 200, 200) self.setGeometry(500, 350, 200, 200) self.setWindowTitle('Quant') self.show() def stack1UI(self): layout = QFormLayout() self.ok = QPushButton('Add Stock', self) cancel = QPushButton('Cancel', self) self.stock_name = QLineEdit() layout.addRow("Stock Name", self.stock_name) self.stock_count = QLineEdit() layout.addRow("Quantity", self.stock_count) self.stock_cost = QLineEdit() layout.addRow("Cost of Stock (per item)", self.stock_cost) layout.addWidget(self.ok) layout.addWidget(cancel) self.ok.clicked.connect(self.on_click) cancel.clicked.connect(self.stock_name.clear) cancel.clicked.connect(self.stock_cost.clear) cancel.clicked.connect(self.stock_count.clear) self.stack1.setLayout(layout) def on_click(self): now = datetime.datetime.now() stock_name_inp = self.stock_name.text().replace(' ', '_').lower() stock_count_inp = int(self.stock_count.text()) stock_cost_inp = int(self.stock_cost.text()) # print(stock_name_inp,stock_count_inp,stock_cost_inp) stock_add_date_time = now.strftime("%Y-%m-%d %H:%M") d = mp.insert_prod(stock_name_inp, stock_count_inp, stock_cost_inp, stock_add_date_time) print(d) # Need to add the above details to table def stack2UI(self): layout = QHBoxLayout() layout.setGeometry(QRect(0, 300, 1150, 500)) tabs = QTabWidget() self.tab1 = QWidget() self.tab2 = QWidget() self.tab3 = QWidget() tabs.addTab(self.tab1, 'Add Quantity') tabs.addTab(self.tab2, 'Reduce Quantity') tabs.addTab(self.tab3, 'Delete Stock') self.tab1UI() self.tab2UI() self.tab3UI() layout.addWidget(tabs) self.stack2.setLayout(layout) def tab1UI(self): layout = QFormLayout() self.ok_add = QPushButton('Add Stock', self) cancel = QPushButton('Cancel', self) self.stock_name_add = QLineEdit() layout.addRow("Stock Name", self.stock_name_add) self.stock_count_add = QLineEdit() layout.addRow("Quantity to add", self.stock_count_add) layout.addWidget(self.ok_add) layout.addWidget(cancel) self.tab1.setLayout(layout) self.ok_add.clicked.connect( self.call_add) # need to write function to add quantity cancel.clicked.connect(self.stock_name_add.clear) cancel.clicked.connect(self.stock_count_add.clear) def tab2UI(self): layout = QFormLayout() self.ok_red = QPushButton('Reduce Stock', self) cancel = QPushButton('Cancel', self) self.stock_name_red = QLineEdit() layout.addRow("Stock Name", self.stock_name_red) self.stock_count_red = QLineEdit() layout.addRow("Quantity to reduce", self.stock_count_red) layout.addWidget(self.ok_red) layout.addWidget(cancel) self.tab2.setLayout(layout) self.ok_red.clicked.connect( self.call_red) # need to write function to reduce quantity cancel.clicked.connect(self.stock_name_red.clear) cancel.clicked.connect(self.stock_count_red.clear) def tab3UI(self): layout = QFormLayout() self.ok_del = QPushButton('Delete Stock', self) cancel = QPushButton('Cancel', self) self.stock_name_del = QLineEdit() layout.addRow("Stock Name", self.stock_name_del) layout.addWidget(self.ok_del) layout.addWidget(cancel) self.tab3.setLayout(layout) self.ok_del.clicked.connect( self.call_del) # need to write function to delete stock cancel.clicked.connect(self.stock_name_del.clear) def call_del(self): now = datetime.datetime.now() stock_del_date_time = now.strftime("%Y-%m-%d %H:%M") stock_name = self.stock_name_del.text().replace(' ', '_').lower() mp.remove_stock(stock_name, stock_del_date_time) def call_red(self): now = datetime.datetime.now() stock_red_date_time = now.strftime("%Y-%m-%d %H:%M") stock_name = self.stock_name_red.text().replace(' ', '_').lower() try: stock_val = -(int(self.stock_count_red.text())) print(stock_val) print(type(stock_val)) mp.update_quantity(stock_name, stock_val, stock_red_date_time) except Exception: print('Exception') def call_add(self): now = datetime.datetime.now() stock_call_add_date_time = now.strftime("%Y-%m-%d %H:%M") stock_name = self.stock_name_add.text().replace(' ', '_').lower() stock_val = int(self.stock_count_add.text()) mp.update_quantity(stock_name, stock_val, stock_call_add_date_time) def stack3UI(self): table = mp.show_stock() print('show') print(table) layout = QVBoxLayout() self.srb = QPushButton() self.srb.setText("Get Search Result.") self.View = QTableWidget() self.lbl3 = QLabel() self.lbl_conf_text = QLabel() self.lbl_conf_text.setText("Enter the search keyword:") self.conf_text = QLineEdit() self.View.setColumnCount(3) self.View.setColumnWidth(0, 250) self.View.setColumnWidth(1, 250) self.View.setColumnWidth(2, 200) self.View.insertRow(0) self.View.setItem(0, 0, QTableWidgetItem('Stock Name')) self.View.setItem(0, 1, QTableWidgetItem('Quantity')) self.View.setItem(0, 2, QTableWidgetItem('Cost(Per Unit)')) layout.addWidget(self.View) layout.addWidget(self.lbl_conf_text) layout.addWidget(self.conf_text) layout.addWidget(self.srb) layout.addWidget(self.lbl3) self.srb.clicked.connect(self.show_search) self.stack3.setLayout(layout) def show_search(self): if self.View.rowCount() > 1: for i in range(1, self.View.rowCount()): self.View.removeRow(1) x_act = mp.show_stock() x = [] if self.conf_text.text() != '': for i in range(0, len(x_act)): a = list(x_act[i]) if self.conf_text.text().lower() in a[0].lower(): x.append(a) else: x = mp.show_stock() if len(x) != 0: for i in range(1, len(x) + 1): self.View.insertRow(i) a = list(x[i - 1]) self.View.setItem( i, 0, QTableWidgetItem(a[0].replace('_', ' ').upper())) self.View.setItem(i, 1, QTableWidgetItem(str(a[1]))) self.View.setItem(i, 2, QTableWidgetItem(str(a[2]))) self.View.setRowHeight(i, 50) self.lbl3.setText('Viewing Stock Database.') else: self.lbl3.setText('No valid information in database.') def stack4UI(self): layout = QVBoxLayout() self.srt = QPushButton() self.srt.setText("Get Transaction History.") self.Trans = QTableWidget() self.lbl4 = QLabel() self.lbl_trans_text = QLabel() self.lbl_trans_text.setText("Enter the search keyword:") self.trans_text = QLineEdit() self.Trans.setColumnCount(6) self.Trans.setColumnWidth(0, 150) self.Trans.setColumnWidth(1, 150) self.Trans.setColumnWidth(2, 150) self.Trans.setColumnWidth(3, 100) self.Trans.setColumnWidth(4, 100) self.Trans.setColumnWidth(5, 500) self.Trans.insertRow(0) self.Trans.setItem(0, 0, QTableWidgetItem('Transaction ID')) self.Trans.setItem(0, 1, QTableWidgetItem('Stock Name')) self.Trans.setItem(0, 2, QTableWidgetItem('Transaction Type')) self.Trans.setItem(0, 3, QTableWidgetItem('Date')) self.Trans.setItem(0, 4, QTableWidgetItem('Time')) self.Trans.setItem(0, 5, QTableWidgetItem('Transaction Specific')) self.Trans.setRowHeight(0, 50) layout.addWidget(self.Trans) layout.addWidget(self.lbl_trans_text) layout.addWidget(self.trans_text) layout.addWidget(self.srt) layout.addWidget(self.lbl4) self.srt.clicked.connect(self.show_trans_history) self.stack4.setLayout(layout) def show_trans_history(self): if self.Trans.rowCount() > 1: for i in range(1, self.Trans.rowCount()): self.Trans.removeRow(1) path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'transaction.txt') if os.path.exists(path): tsearch = open(path, 'r') x_c = tsearch.readlines() tsearch.close() x = [] if self.trans_text.text() != '': key = self.trans_text.text() for i in range(0, len(x_c)): a = x_c[i].split(" ") name = a[0] action = a[-2] if (key.lower() in name.lower()) or (key.lower() in action.lower()): x.append(a) else: x = x_c.copy() for i in range(0, len(x)): x.sort(key=lambda a: a[4]) # print(x) tid = 1900001 for i in range(1, len(x) + 1): self.Trans.insertRow(i) a = x[i - 1].split(" ") if a[-2] == 'UPDATE': p = 'Quantity of Stock Changed from ' + a[1] + ' to ' + a[2] elif a[-2] == 'INSERT': p = 'Stock added with Quantity : ' + a[ 1] + ' and Cost(Per Unit in Rs.) : ' + a[2] elif a[-2] == 'REMOVE': p = 'Stock information deleted.' else: p = 'None' self.Trans.setItem(i, 0, QTableWidgetItem(str(tid))) self.Trans.setItem(i, 1, QTableWidgetItem(a[0].replace('_', ' '))) self.Trans.setItem(i, 2, QTableWidgetItem(a[-2])) self.Trans.setItem(i, 3, QTableWidgetItem(a[3])) self.Trans.setItem(i, 4, QTableWidgetItem(a[4])) self.Trans.setItem(i, 5, QTableWidgetItem(p)) self.Trans.setRowHeight(i, 50) tid += 1 self.lbl4.setText('Transaction History.') else: self.lbl4.setText('No valid information found.') def display(self, i): self.Stack.setCurrentIndex(i)
class ProgrammingDialog(QDialog): def __init__(self, name, proList=None): super(ProgrammingDialog, self).__init__() self.name = name self.list = QListWidget() if proList is not None: self.list.addItems(proList) self.list.setCurrentRow(0) vbox = QVBoxLayout() for text, slot in (("Add", self.add), ("Edit", self.edit), ("Remove", self.remove), ("Sort", self.sort), ("Close", self.close)): buttons = QPushButton(text) buttons.clicked.connect(slot) vbox.addWidget(buttons) hbox = QHBoxLayout() hbox.addWidget(self.list) hbox.addLayout(vbox) self.setLayout(hbox) self.setWindowTitle("PyQt5 Simple List Project") def add(self): row = self.list.currentRow() title = "Add {0}".format(self.name) string, ok = QInputDialog.getText(self, title, title) if ok and string is not None: self.list.insertItem(row, string) def edit(self): row = self.list.currentRow() item = self.list.item(row) if item is not None: title = "Edit {0}".format(self.name) string, ok = QInputDialog.getText(self, title, title, QLineEdit.Normal, item.text()) if ok and string is not None: item.setText(string) def remove(self): row = self.list.currentRow() item = self.list.item(row) if item is None: return reply = QMessageBox.question( self, "Remove {0}".format(self.name), "Remove {0} '{1}'?".format(self.name, str(item.text())), QMessageBox.Yes | QMessageBox.No) if reply == QMessageBox.Yes: item = self.list.takeItem(row) del item def sort(self): self.list.sortItems() def close(self): self.accept()
class SimulationGui(QMainWindow): """ class for the graphical user interface """ # TODO enable closing plot docks by right-clicking their name runSimulation = pyqtSignal() stopSimulation = pyqtSignal() playbackTimeChanged = pyqtSignal() regimeFinished = pyqtSignal() finishedRegimeBatch = pyqtSignal(bool) def __init__(self): # constructor of the base class QMainWindow.__init__(self) QCoreApplication.setOrganizationName("RST") QCoreApplication.setOrganizationDomain("https://tu-dresden.de/rst") QCoreApplication.setApplicationVersion( pkg_resources.require("PyMoskito")[0].version) QCoreApplication.setApplicationName(globals()["__package__"]) # load settings self._settings = QSettings() self._read_settings() # initialize logger self._logger = logging.getLogger(self.__class__.__name__) # Create Simulation Backend self.guiProgress = None self.cmdProgress = None self.sim = SimulatorInteractor(self) self.runSimulation.connect(self.sim.run_simulation) self.stopSimulation.connect(self.sim.stop_simulation) self.sim.simulation_finalized.connect(self.new_simulation_data) self.currentDataset = None self.interpolator = None # sim setup viewer self.targetView = SimulatorView(self) self.targetView.setModel(self.sim.target_model) self.targetView.expanded.connect(self.target_view_changed) self.targetView.collapsed.connect(self.target_view_changed) # sim results viewer self.result_view = QTreeView() # the docking area allows to rearrange the user interface at runtime self.area = pg.dockarea.DockArea() # Window properties icon_size = QSize(25, 25) self.setCentralWidget(self.area) self.resize(1000, 700) self.setWindowTitle("PyMoskito") res_path = get_resource("mosquito.png") icon = QIcon(res_path) self.setWindowIcon(icon) # create docks self.propertyDock = pg.dockarea.Dock("Properties") self.animationDock = pg.dockarea.Dock("Animation") self.regimeDock = pg.dockarea.Dock("Regimes") self.dataDock = pg.dockarea.Dock("Data") self.logDock = pg.dockarea.Dock("Log") self.plotDockPlaceholder = pg.dockarea.Dock("Placeholder") # arrange docks self.area.addDock(self.animationDock, "right") self.area.addDock(self.regimeDock, "left", self.animationDock) self.area.addDock(self.propertyDock, "bottom", self.regimeDock) self.area.addDock(self.dataDock, "bottom", self.propertyDock) self.area.addDock(self.plotDockPlaceholder, "bottom", self.animationDock) self.area.addDock(self.logDock, "bottom", self.dataDock) self.non_plotting_docks = list(self.area.findAll()[1].keys()) # add widgets to the docks self.propertyDock.addWidget(self.targetView) if not vtk_available: self._logger.error("loading vtk failed with:{}".format(vtk_error_msg)) # check if there is a registered visualizer available_vis = get_registered_visualizers() self._logger.info("found visualizers: {}".format( [name for cls, name in available_vis])) if available_vis: # instantiate the first visualizer self._logger.info("loading visualizer '{}'".format(available_vis[0][1])) self.animationLayout = QVBoxLayout() if issubclass(available_vis[0][0], MplVisualizer): self.animationWidget = QWidget() self.visualizer = available_vis[0][0](self.animationWidget, self.animationLayout) self.animationDock.addWidget(self.animationWidget) elif issubclass(available_vis[0][0], VtkVisualizer): if vtk_available: # vtk window self.animationFrame = QFrame() self.vtkWidget = QVTKRenderWindowInteractor( self.animationFrame) self.animationLayout.addWidget(self.vtkWidget) self.animationFrame.setLayout(self.animationLayout) self.animationDock.addWidget(self.animationFrame) self.vtk_renderer = vtkRenderer() self.vtkWidget.GetRenderWindow().AddRenderer( self.vtk_renderer) self.visualizer = available_vis[0][0](self.vtk_renderer) self.vtkWidget.Initialize() else: self._logger.warning("visualizer depends on vtk which is " "not available on this system!") elif available_vis: raise NotImplementedError else: self.visualizer = None # regime window self.regime_list = QListWidget(self) self.regime_list.setSelectionMode(QAbstractItemView.ExtendedSelection) self.regimeDock.addWidget(self.regime_list) self.regime_list.itemDoubleClicked.connect(self.regime_dclicked) self._regimes = [] self.regime_file_name = "" self.actDeleteRegimes = QAction(self.regime_list) self.actDeleteRegimes.setText("&Delete Selected Regimes") # TODO shortcut works always, not only with focus on the regime list # self.actDeleteRegimes.setShortcutContext(Qt.WindowShortcut) self.actDeleteRegimes.setShortcut(QKeySequence(Qt.Key_Delete)) self.actDeleteRegimes.triggered.connect(self.remove_regime_items) self.actSave = QAction(self) self.actSave.setText('Save Results As') self.actSave.setIcon(QIcon(get_resource("save.png"))) self.actSave.setDisabled(True) self.actSave.setShortcut(QKeySequence.Save) self.actSave.triggered.connect(self.export_simulation_data) self.actLoadRegimes = QAction(self) self.actLoadRegimes.setText("Load Regimes from File") self.actLoadRegimes.setIcon(QIcon(get_resource("load.png"))) self.actLoadRegimes.setDisabled(False) self.actLoadRegimes.setShortcut(QKeySequence.Open) self.actLoadRegimes.triggered.connect(self.load_regime_dialog) self.actExitOnBatchCompletion = QAction(self) self.actExitOnBatchCompletion.setText("&Exit On Batch Completion") self.actExitOnBatchCompletion.setCheckable(True) self.actExitOnBatchCompletion.setChecked( self._settings.value("control/exit_on_batch_completion") == "True" ) self.actExitOnBatchCompletion.changed.connect( self.update_exit_on_batch_completion_setting) # regime management self.runningBatch = False self._current_regime_index = None self._current_regime_name = None self._regimes = [] self.regimeFinished.connect(self.run_next_regime) self.finishedRegimeBatch.connect(self.regime_batch_finished) # data window self.dataList = QListWidget(self) self.dataDock.addWidget(self.dataList) self.dataList.itemDoubleClicked.connect(self.create_plot) # actions for simulation control self.actSimulateCurrent = QAction(self) self.actSimulateCurrent.setText("&Simulate Current Regime") self.actSimulateCurrent.setIcon(QIcon(get_resource("simulate.png"))) self.actSimulateCurrent.setShortcut(QKeySequence("F5")) self.actSimulateCurrent.triggered.connect(self.start_simulation) self.actSimulateAll = QAction(self) self.actSimulateAll.setText("Simulate &All Regimes") self.actSimulateAll.setIcon(QIcon(get_resource("execute_regimes.png"))) self.actSimulateAll.setShortcut(QKeySequence("F6")) self.actSimulateAll.setDisabled(True) self.actSimulateAll.triggered.connect(self.start_regime_execution) # actions for animation control self.actAutoPlay = QAction(self) self.actAutoPlay.setText("&Autoplay Simulation") self.actAutoPlay.setCheckable(True) self.actAutoPlay.setChecked( self._settings.value("control/autoplay_animation") == "True" ) self.actAutoPlay.changed.connect(self.update_autoplay_setting) self.actPlayPause = QAction(self) self.actPlayPause.setText("Play Animation") self.actPlayPause.setIcon(QIcon(get_resource("play.png"))) self.actPlayPause.setDisabled(True) self.actPlayPause.setShortcut(QKeySequence(Qt.Key_Space)) self.actPlayPause.triggered.connect(self.play_animation) self.actStop = QAction(self) self.actStop.setText("Stop") self.actStop.setIcon(QIcon(get_resource("stop.png"))) self.actStop.setDisabled(True) self.actStop.triggered.connect(self.stop_animation) self.actSlow = QAction(self) self.actSlow.setText("Slowest") self.actSlow.setIcon(QIcon(get_resource("slow.png"))) self.actSlow.setDisabled(False) self.actSlow.triggered.connect(self.set_slowest_playback_speed) self.actFast = QAction(self) self.actFast.setText("Fastest") self.actFast.setIcon(QIcon(get_resource("fast.png"))) self.actFast.setDisabled(False) self.actFast.triggered.connect(self.set_fastest_playback_speed) self.speedControl = QSlider(Qt.Horizontal, self) self.speedControl.setMaximumSize(200, 25) self.speedControl.setTickPosition(QSlider.TicksBothSides) self.speedControl.setDisabled(False) self.speedControl.setMinimum(0) self.speedControl.setMaximum(12) self.speedControl.setValue(6) self.speedControl.setTickInterval(6) self.speedControl.setSingleStep(2) self.speedControl.setPageStep(3) self.speedControl.valueChanged.connect(self.update_playback_speed) self.timeSlider = QSlider(Qt.Horizontal, self) self.timeSlider.setMinimum(0) self.timeSliderRange = 1000 self.timeSlider.setMaximum(self.timeSliderRange) self.timeSlider.setTickInterval(1) self.timeSlider.setTracking(True) self.timeSlider.setDisabled(True) self.timeSlider.valueChanged.connect(self.update_playback_time) self.playbackTime = .0 self.playbackGain = 1 self.currentStepSize = .0 self.currentEndTime = .0 self.playbackTimer = QTimer() self.playbackTimer.timeout.connect(self.increment_playback_time) self.playbackTimeChanged.connect(self.update_gui) self.playbackTimeout = 33 # in [ms] -> 30 fps self.actResetCamera = QAction(self) self.actResetCamera.setText("Reset Camera") self.actResetCamera.setIcon(QIcon(get_resource("reset_camera.png"))) self.actResetCamera.setDisabled(True) if available_vis: self.actResetCamera.setEnabled(self.visualizer.can_reset_view) self.actResetCamera.triggered.connect(self.reset_camera_clicked) # postprocessing self.actPostprocessing = QAction(self) self.actPostprocessing.setText("Launch Postprocessor") self.actPostprocessing.setIcon(QIcon(get_resource("processing.png"))) self.actPostprocessing.setDisabled(False) self.actPostprocessing.triggered.connect(self.postprocessing_clicked) self.actPostprocessing.setShortcut(QKeySequence("F7")) self.postprocessor = None # toolbar self.toolbarSim = QToolBar("Simulation") self.toolbarSim.setContextMenuPolicy(Qt.PreventContextMenu) self.toolbarSim.setMovable(False) self.toolbarSim.setIconSize(icon_size) self.addToolBar(self.toolbarSim) self.toolbarSim.addAction(self.actLoadRegimes) self.toolbarSim.addAction(self.actSave) self.toolbarSim.addSeparator() self.toolbarSim.addAction(self.actSimulateCurrent) self.toolbarSim.addAction(self.actSimulateAll) self.toolbarSim.addSeparator() self.toolbarSim.addAction(self.actPlayPause) self.toolbarSim.addAction(self.actStop) self.toolbarSim.addWidget(self.timeSlider) self.toolbarSim.addSeparator() self.toolbarSim.addAction(self.actSlow) self.toolbarSim.addWidget(self.speedControl) self.toolbarSim.addAction(self.actFast) self.toolbarSim.addSeparator() self.toolbarSim.addAction(self.actPostprocessing) self.toolbarSim.addAction(self.actResetCamera) self.postprocessor = None # log dock self.logBox = QPlainTextEdit(self) self.logBox.setReadOnly(True) self.logDock.addWidget(self.logBox) # init logger for logging box self.textLogger = PlainTextLogger(logging.INFO) self.textLogger.set_target_cb(self.logBox.appendPlainText) logging.getLogger().addHandler(self.textLogger) # menu bar fileMenu = self.menuBar().addMenu("&File") fileMenu.addAction(self.actLoadRegimes) fileMenu.addAction(self.actSave) fileMenu.addAction("&Quit", self.close) editMenu = self.menuBar().addMenu("&Edit") editMenu.addAction(self.actDeleteRegimes) simMenu = self.menuBar().addMenu("&Simulation") simMenu.addAction(self.actSimulateCurrent) simMenu.addAction(self.actSimulateAll) simMenu.addAction(self.actExitOnBatchCompletion) simMenu.addAction(self.actPostprocessing) animMenu = self.menuBar().addMenu("&Animation") animMenu.addAction(self.actPlayPause) animMenu.addAction("&Increase Playback Speed", self.increment_playback_speed, QKeySequence(Qt.CTRL + Qt.Key_Plus)) animMenu.addAction("&Decrease Playback Speed", self.decrement_playback_speed, QKeySequence(Qt.CTRL + Qt.Key_Minus)) animMenu.addAction("&Reset Playback Speed", self.reset_playback_speed, QKeySequence(Qt.CTRL + Qt.Key_0)) animMenu.addAction(self.actAutoPlay) animMenu.addAction(self.actResetCamera) helpMenu = self.menuBar().addMenu("&Help") helpMenu.addAction("&Online Documentation", self.show_online_docs) helpMenu.addAction("&About", self.show_info) # status bar self.status = QStatusBar(self) self.setStatusBar(self.status) self.statusLabel = QLabel("Ready.") self.statusBar().addPermanentWidget(self.statusLabel) self.timeLabel = QLabel("current time: 0.0") self.statusBar().addPermanentWidget(self.timeLabel) self._logger.info("Simulation GUI is up and running.") def _read_settings(self): # add default settings if none are present if not self._settings.contains("path/simulation_results"): self._settings.setValue("path/simulation_results", os.path.join(os.path.curdir, "results", "simulation")) if not self._settings.contains("path/postprocessing_results"): self._settings.setValue("path/postprocessing_results", os.path.join(os.path.curdir, "results", "postprocessing")) if not self._settings.contains("path/metaprocessing_results"): self._settings.setValue("path/metaprocessing_results", os.path.join(os.path.curdir, "results", "metaprocessing")) if not self._settings.contains("control/autoplay_animation"): self._settings.setValue("control/autoplay_animation", "False") if not self._settings.contains("control/exit_on_batch_completion"): self._settings.setValue("control/exit_on_batch_completion", "False") def _write_settings(self): """ Store the application state. """ pass @pyqtSlot() def update_autoplay_setting(self): self._settings.setValue("control/autoplay_animation", str(self.actAutoPlay.isChecked())) @pyqtSlot() def update_exit_on_batch_completion_setting(self, state=None): if state is None: state = self.actExitOnBatchCompletion.isChecked() self._settings.setValue("control/exit_on_batch_completion", str(state)) def set_visualizer(self, vis): self.visualizer = vis self.vtkWidget.Initialize() @pyqtSlot() def play_animation(self): """ play the animation """ self._logger.debug("Starting Playback") # if we are at the end, start from the beginning if self.playbackTime == self.currentEndTime: self.timeSlider.setValue(0) self.actPlayPause.setText("Pause Animation") self.actPlayPause.setIcon(QIcon(get_resource("pause.png"))) self.actPlayPause.triggered.disconnect(self.play_animation) self.actPlayPause.triggered.connect(self.pause_animation) self.playbackTimer.start(self.playbackTimeout) @pyqtSlot() def pause_animation(self): """ pause the animation """ self._logger.debug("Pausing Playback") self.playbackTimer.stop() self.actPlayPause.setText("Play Animation") self.actPlayPause.setIcon(QIcon(get_resource("play.png"))) self.actPlayPause.triggered.disconnect(self.pause_animation) self.actPlayPause.triggered.connect(self.play_animation) def stop_animation(self): """ Stop the animation if it is running and reset the playback time. """ self._logger.debug("Stopping Playback") if self.actPlayPause.text() == "Pause Animation": # animation is playing -> stop it self.playbackTimer.stop() self.actPlayPause.setText("Play Animation") self.actPlayPause.setIcon(QIcon(get_resource("play.png"))) self.actPlayPause.triggered.disconnect(self.pause_animation) self.actPlayPause.triggered.connect(self.play_animation) self.timeSlider.setValue(0) @pyqtSlot() def start_simulation(self): """ start the simulation and disable start button """ if self._current_regime_index is None: regime_name = "" else: regime_name = str(self.regime_list.item( self._current_regime_index).text()) self.statusLabel.setText("simulating {}".format(regime_name)) self._logger.info("Simulating: {}".format(regime_name)) self.actSimulateCurrent.setIcon(QIcon( get_resource("stop_simulation.png"))) self.actSimulateCurrent.setText("Abort &Simulation") self.actSimulateCurrent.triggered.disconnect(self.start_simulation) self.actSimulateCurrent.triggered.connect(self.stop_simulation) if not self.runningBatch: self.actSimulateAll.setDisabled(True) self.guiProgress = QProgressBar(self) self.sim.simulationProgressChanged.connect(self.guiProgress.setValue) self.statusBar().addWidget(self.guiProgress) self.runSimulation.emit() @pyqtSlot() def stop_simulation(self): self.stopSimulation.emit() def export_simulation_data(self, ok): """ Query the user for a custom name and export the current simulation results. :param ok: unused parameter from QAction.triggered() Signal """ self._save_data() def _save_data(self, file_path=None): """ Save the current simulation results. If *fie_name* is given, the result will be saved to the specified location, making automated exporting easier. Args: file_path(str): Absolute path of the target file. If `None` the use will be asked for a storage location. """ regime_name = self._regimes[self._current_regime_index]["Name"] if file_path is None: # get default path path = self._settings.value("path/simulation_results") # create canonic file name suggestion = self._simfile_name(regime_name) else: path = os.path.dirname(file_path) suggestion = os.path.basename(file_path) # check if path exists otherwise create it if not os.path.isdir(path): box = QMessageBox() box.setText("Export Folder does not exist yet.") box.setInformativeText("Do you want to create it? \n" "{}".format(os.path.abspath(path))) box.setStandardButtons(QMessageBox.Ok | QMessageBox.No) box.setDefaultButton(QMessageBox.Ok) ret = box.exec_() if ret == QMessageBox.Ok: os.makedirs(path) else: path = os.path.abspath(os.path.curdir) file_path = None # If no path was given, present the default and let the user choose if file_path is None: dialog = QFileDialog(self) dialog.setAcceptMode(QFileDialog.AcceptSave) dialog.setFileMode(QFileDialog.AnyFile) dialog.setDirectory(path) dialog.setNameFilter("PyMoskito Results (*.pmr)") dialog.selectFile(suggestion) if dialog.exec_(): file_path = dialog.selectedFiles()[0] else: self._logger.warning("Export Aborted") return -1 # ask whether this should act as new default path = os.path.abspath(os.path.dirname(file_path)) if path != self._settings.value("path/simulation_results"): box = QMessageBox() box.setText("Use this path as new default?") box.setInformativeText("{}".format(path)) box.setStandardButtons(QMessageBox.Yes | QMessageBox.No) box.setDefaultButton(QMessageBox.Yes) ret = box.exec_() if ret == QMessageBox.Yes: self._settings.setValue("path/simulation_results", path) self.currentDataset.update({"regime name": regime_name}) with open(file_path, "wb") as f: pickle.dump(self.currentDataset, f, protocol=4) self.statusLabel.setText("results saved to {}".format(file_path)) self._logger.info("results saved to {}".format(file_path)) def _simfile_name(self, regime_name): """ Create a canonical name for a simulation result file """ suggestion = (time.strftime("%Y%m%d-%H%M%S") + "_" + regime_name + ".pmr") return suggestion def load_regime_dialog(self): regime_path = os.path.join(os.curdir) dialog = QFileDialog(self) dialog.setFileMode(QFileDialog.ExistingFile) dialog.setDirectory(regime_path) dialog.setNameFilter("Simulation Regime files (*.sreg)") if dialog.exec_(): file = dialog.selectedFiles()[0] self.load_regimes_from_file(file) def load_regimes_from_file(self, file_name): """ load simulation regime from file :param file_name: """ self.regime_file_name = os.path.split(file_name)[-1][:-5] self._logger.info("loading regime file: {0}".format(self.regime_file_name)) with open(file_name.encode(), "r") as f: self._regimes += yaml.load(f) self._update_regime_list() if self._regimes: self.actSimulateAll.setDisabled(False) self._logger.info("loaded {} regimes".format(len(self._regimes))) self.statusBar().showMessage("loaded {} regimes.".format(len(self._regimes)), 1000) return def _update_regime_list(self): self.regime_list.clear() for reg in self._regimes: self._logger.debug("adding '{}' to regime list".format(reg["Name"])) self.regime_list.addItem(reg["Name"]) def remove_regime_items(self): if self.regime_list.currentRow() >= 0: # flag all selected files as invalid items = self.regime_list.selectedItems() for item in items: del self._regimes[self.regime_list.row(item)] self.regime_list.takeItem(self.regime_list.row(item)) @pyqtSlot(QListWidgetItem) def regime_dclicked(self, item): """ Apply the selected regime to the current target. """ self.apply_regime_by_name(str(item.text())) def apply_regime_by_name(self, regime_name): """ Apply the regime given by `regime_name` und update the regime index. Returns: bool: `True` if successful, `False` if errors occurred. """ # get regime idx try: idx = list(map(itemgetter("Name"), self._regimes)).index(regime_name) except ValueError as e: self._logger.error("apply_regime_by_name(): Error no regime called " "'{0}'".format(regime_name)) return False # apply return self._apply_regime_by_idx(idx) def _apply_regime_by_idx(self, index=0): """ Apply the given regime. Args: index(int): Index of the regime in the `RegimeList` . Returns: bool: `True` if successful, `False` if errors occurred. """ if index >= len(self._regimes): self._logger.error("applyRegime: index error! ({})".format(index)) return False reg_name = self._regimes[index]["Name"] self.statusBar().showMessage("regime {} applied.".format(reg_name), 1000) self._logger.info("applying regime '{}'".format(reg_name)) self._current_regime_index = index self._current_regime_name = reg_name return self.sim.set_regime(self._regimes[index]) @pyqtSlot() def start_regime_execution(self): """ Simulate all regimes in the regime list. """ self.actSimulateAll.setText("Stop Simulating &All Regimes") self.actSimulateAll.setIcon(QIcon(get_resource("stop_batch.png"))) self.actSimulateAll.triggered.disconnect(self.start_regime_execution) self.actSimulateAll.triggered.connect(self.stop_regime_excecution) self.runningBatch = True self._current_regime_index = -1 self.regimeFinished.emit() def run_next_regime(self): """ Execute the next regime in the regime batch. """ # are we finished? if self._current_regime_index == len(self._regimes) - 1: self.finishedRegimeBatch.emit(True) return suc = self._apply_regime_by_idx(self._current_regime_index + 1) if not suc: self.finishedRegimeBatch.emit(False) return self.start_simulation() @pyqtSlot() def stop_regime_excecution(self): """ Stop the batch process. """ self.stopSimulation.emit() self.finishedRegimeBatch.emit(False) def regime_batch_finished(self, status): self.runningBatch = False self.actSimulateAll.setDisabled(False) self.actSave.setDisabled(True) self.actSimulateAll.setText("Simulate &All Regimes") self.actSimulateAll.setIcon(QIcon(get_resource("execute_regimes.png"))) self.actSimulateAll.triggered.disconnect(self.stop_regime_excecution) self.actSimulateAll.triggered.connect(self.start_regime_execution) if status: self.statusLabel.setText("All regimes have been simulated") self._logger.info("All Regimes have been simulated") else: self._logger.error("Batch simulation has been aborted") if self._settings.value("control/exit_on_batch_completion") == "True": self._logger.info("Shutting down SimulationGUI") self.close() @pyqtSlot(str, dict, name="new_simulation_data") def new_simulation_data(self, status, data): """ Slot to be called when the simulation interface has completed the current job and new data is available. Args: status (str): Status of the simulation, either - `finished` : Simulation has been finished successfully or - `failed` : Simulation has failed. data (dict): Dictionary, holding the simulation data. """ self._logger.info("Simulation {}".format(status)) self.statusLabel.setText("Simulation {}".format(status)) self.actSimulateCurrent.setText("&Simulate Current Regime") self.actSimulateCurrent.setIcon(QIcon(get_resource("simulate.png"))) self.actSimulateCurrent.triggered.disconnect(self.stop_simulation) self.actSimulateCurrent.triggered.connect(self.start_simulation) self.actPlayPause.setDisabled(False) self.actStop.setDisabled(False) self.actSave.setDisabled(False) self.speedControl.setDisabled(False) self.timeSlider.setDisabled(False) self.sim.simulationProgressChanged.disconnect(self.guiProgress.setValue) self.statusBar().removeWidget(self.guiProgress) self.stop_animation() self.currentDataset = data if data: self._read_results() self._update_data_list() self._update_plots() if self._settings.value("control/autoplay_animation") == "True": self.actPlayPause.trigger() if self.runningBatch: regime_name = self._regimes[self._current_regime_index]["Name"] file_name = self._simfile_name(regime_name) self._save_data(os.path.join( self._settings.value("path/simulation_results"), file_name)) self.regimeFinished.emit() else: self.actSimulateAll.setDisabled(False) def _read_results(self): state = self.currentDataset["results"]["Solver"] self.interpolator = interp1d(self.currentDataset["results"]["time"], state, axis=0, bounds_error=False, fill_value=(state[0], state[-1])) self.currentStepSize = 1.0/self.currentDataset["simulation"][ "measure rate"] self.currentEndTime = self.currentDataset["simulation"]["end time"] self.validData = True def increment_playback_speed(self): self.speedControl.setValue(self.speedControl.value() + self.speedControl.singleStep()) def decrement_playback_speed(self): self.speedControl.setValue(self.speedControl.value() - self.speedControl.singleStep()) def reset_playback_speed(self): self.speedControl.setValue((self.speedControl.maximum() - self.speedControl.minimum())/2) def set_slowest_playback_speed(self): self.speedControl.setValue(self.speedControl.minimum()) def set_fastest_playback_speed(self): self.speedControl.setValue(self.speedControl.maximum()) def update_playback_speed(self, val): """ adjust playback time to slider value :param val: """ maximum = self.speedControl.maximum() self.playbackGain = 10**(3.0 * (val - maximum / 2) / maximum) @pyqtSlot() def increment_playback_time(self): """ go one time step forward in playback """ if self.playbackTime == self.currentEndTime: self.pause_animation() return increment = self.playbackGain * self.playbackTimeout / 1000 self.playbackTime = min(self.currentEndTime, self.playbackTime + increment) pos = int(self.playbackTime / self.currentEndTime * self.timeSliderRange) self.timeSlider.blockSignals(True) self.timeSlider.setValue(pos) self.timeSlider.blockSignals(False) self.playbackTimeChanged.emit() def update_playback_time(self): """ adjust playback time to slider value """ self.playbackTime = self.timeSlider.value()/self.timeSliderRange*self.currentEndTime self.playbackTimeChanged.emit() return def update_gui(self): """ updates the graphical user interface, including: - timestamp - visualisation - time cursor in diagrams """ if not self.validData: return self.timeLabel.setText("current time: %4f" % self.playbackTime) # update time cursor in plots self._update_time_cursors() # update state of rendering if self.visualizer: state = self.interpolator(self.playbackTime) self.visualizer.update_scene(state) if isinstance(self.visualizer, MplVisualizer): pass elif isinstance(self.visualizer, VtkVisualizer): self.vtkWidget.GetRenderWindow().Render() def _update_data_list(self): self.dataList.clear() for module_name, results in self.currentDataset["results"].items(): if not isinstance(results, np.ndarray): continue if len(results.shape) == 1: self.dataList.insertItem(0, module_name) elif len(results.shape) == 2: for col in range(results.shape[1]): self.dataList.insertItem( 0, self._build_entry_name(module_name, (col, )) ) elif len(results.shape) == 3: for col in range(results.shape[1]): for der in range(results.shape[2]): self.dataList.insertItem( 0, self._build_entry_name(module_name, (col, der)) ) def _build_entry_name(self, module_name, idx): """ Construct an identifier for a given entry of a module. Args: module_name (str): name of the module the entry belongs to. idx (tuple): Index of the entry. Returns: str: Identifier to use for display. """ # save the user from defining 1d entries via tuples if len(idx) == 1: m_idx = idx[0] else: m_idx = idx mod_settings = self.currentDataset["modules"] info = mod_settings.get(module_name, {}).get("output_info", None) if info: if m_idx in info: return ".".join([module_name, info[m_idx]["Name"]]) return ".".join([module_name] + [str(i) for i in idx]) def _get_index_from_suffix(self, module_name, suffix): info = self.currentDataset["modules"].get(module_name, {}).get( "output_info", None) idx = next((i for i in info if info[i]["Name"] == suffix), None) return idx def _get_units(self, entry): """ Return the unit that corresponds to a given entry. If no information is available, None is returned. Args: entry (str): Name of the entry. This can either be "Model.a.b" where a and b are numbers or if information is available "Model.Signal" where signal is the name of that part. Returns: """ args = entry.split(".") module_name = args.pop(0) info = self.currentDataset["modules"].get(module_name, {}).get( "output_info", None) if info is None: return None if len(args) == 1: try: idx = int(args[0]) except ValueError: idx = next((i for i in info if info[i]["Name"] == args[0]), None) else: idx = (int(a) for a in args) return info[idx]["Unit"] def create_plot(self, item): """ Creates a plot widget based on the given item. If a plot for this item is already open no new plot is created but the existing one is raised up again. Args: item(Qt.ListItem): Item to plot. """ title = str(item.text()) if title in self.non_plotting_docks: self._logger.error("Title '{}' not allowed for a plot window since" "it would shadow on of the reserved " "names".format(title)) # check if plot has already been opened if title in self.area.findAll()[1]: self.area.docks[title].raiseDock() return # collect data data = self._get_data_by_name(title) t = self.currentDataset["results"]["time"] unit = self._get_units(title) if "." in title: name = title.split(".")[1] else: name = title # create plot widget widget = pg.PlotWidget(title=title) widget.showGrid(True, True) widget.plot(x=t, y=data) widget.getPlotItem().getAxis("bottom").setLabel(text="Time", units="s") widget.getPlotItem().getAxis("left").setLabel(text=name, units=unit) # add a time line time_line = pg.InfiniteLine(self.playbackTime, angle=90, movable=False, pen=pg.mkPen("#FF0000", width=2.0)) widget.getPlotItem().addItem(time_line) # create dock container and add it to dock area dock = pg.dockarea.Dock(title, closable=True) dock.addWidget(widget) self.area.addDock(dock, "above", self.plotDockPlaceholder) def _get_data_by_name(self, name): tmp = name.split(".") module_name = tmp[0] if len(tmp) == 1: data = np.array(self.currentDataset["results"][module_name]) elif len(tmp) == 2: try: idx = int(tmp[1]) except ValueError: idx = self._get_index_from_suffix(module_name, tmp[1]) finally: data = self.currentDataset["results"][module_name][..., idx] elif len(tmp) == 3: idx = int(tmp[1]) der = int(tmp[2]) data = self.currentDataset["results"][module_name][..., idx, der] else: raise ValueError("Format not supported") return data def _update_time_cursors(self): """ Update the time lines of all plot windows """ for title, dock in self.area.findAll()[1].items(): if title in self.non_plotting_docks: continue for widget in dock.widgets: for item in widget.getPlotItem().items: if isinstance(item, pg.InfiniteLine): item.setValue(self.playbackTime) def _update_plots(self): """ Update the data in all plot windows """ for title, dock in self.area.findAll()[1].items(): if title in self.non_plotting_docks: continue if not self.dataList.findItems(dock.name(), Qt.MatchExactly): # no data for this plot -> remove it dock.close() continue for widget in dock.widgets: for item in widget.getPlotItem().items: if isinstance(item, pg.PlotDataItem): x_data = self.currentDataset["results"]["time"] y_data = self._get_data_by_name(dock.name()) item.setData(x=x_data, y=y_data) @pyqtSlot(QModelIndex) def target_view_changed(self, index): self.targetView.resizeColumnToContents(0) def postprocessing_clicked(self): """ starts the post- and metaprocessing application """ self._logger.info("launching postprocessor") self.statusBar().showMessage("launching postprocessor", 1000) if self.postprocessor is None: self.postprocessor = PostProcessor() self.postprocessor.show() def reset_camera_clicked(self): """ reset camera in vtk window """ self.visualizer.reset_camera() self.vtkWidget.GetRenderWindow().Render() def show_info(self): icon_lic = open(get_resource("license.txt"), "r").read() text = "This application was build using PyMoskito ver. {} .<br />" \ "PyMoskito is free software distributed under GPLv3. <br />" \ "It is developed by members of the " \ "<a href=\'https://tu-dresden.de/ing/elektrotechnik/rst'>" \ "Institute of Control Theory</a>" \ " at the <a href=\'https://tu-dresden.de'>" \ "Dresden University of Technology</a>. <br />" \ "".format(pkg_resources.require("PyMoskito")[0].version) \ + "<br />" + icon_lic box = QMessageBox.about(self, "PyMoskito", text) def show_online_docs(self): webbrowser.open("https://pymoskito.readthedocs.org") def closeEvent(self, QCloseEvent): self._logger.info("Close Event received, shutting down.") logging.getLogger().removeHandler(self.textLogger) super().closeEvent(QCloseEvent)
class IRSGUI(QWidget): """Image Retrieval System GUI""" def __init__(self, args): super(IRSGUI, self).__init__() self.args = args self.state = _STATE(self.args) self._setup() self._layout() self._connect() self._init_dataset() self.setStyleSheet(self.args.style) def _init_dataset(self): self.dataset = Dataset(self.args.dataset) self._images() def _connect(self): self.select.clicked.connect(self._slot_search) self.edit.textChanged.connect(self._slot_edit) self.rarea.itemDoubleClicked.connect(self._slot_rarea_dc) self.rarea.itemClicked.connect(self._slot_rarea) def _setup(self): self.select = QPushButton() self.select.setIcon(QIcon(QPixmap(self.args.search))) self.select.setStyleSheet(self.args.style) self.edit = DndEdit() self.edit.setDragEnabled(True) self.edit.setStyleSheet(self.args.style) self.edit.setMaximumWidth(self.args.lineEdit[1]) self.edit.setMinimumWidth(self.args.lineEdit[0]) self.rarea = QListWidget() self.rarea.setIconSize( QSize(self.args.image_size[0], self.args.image_size[1])) self.rarea.setResizeMode(QListView.Adjust) self.rarea.setFixedWidth(self.args.fix) self.rarea.verticalScrollBar().setStyleSheet(self.args.style) self.rarea.setViewMode(QListView.IconMode) self.rarea.setMovement(QListView.Static) self.rarea.setSpacing(self.args.space) self.rarea.setStyleSheet(self.args.style) def _layout(self): hbox = QHBoxLayout() hbox.setContentsMargins(self.args.margins[0], self.args.margins[1], self.args.margins[2], self.args.margins[3]) hbox.addStretch(1) hbox.addWidget(self.edit) hbox.addWidget((self.select)) hbox.addStretch(1) self.vbox = QVBoxLayout(self) self.vbox.addLayout(hbox) self.vbox.addWidget(self.hline()) self.hbox = QHBoxLayout() self.hbox.addStretch(1) self.hbox.addWidget(self.rarea) self.hbox.addStretch(1) # self.vbox.addWidget(self.rarea) self.vbox.addLayout(self.hbox) def hline(self): hline = QFrame() hline.setFrameShape(QFrame.HLine) hline.setFrameShadow(QFrame.Sunken) return hline def _create_image(self, image): im = QLabel() im.setFixedSize(self.args.image_size[0], self.args.image_size[1]) pix = QPixmap(image) im.setPixmap(pix) im.setScaledContents(True) return im def _create_icon(self, image): pix = QPixmap(image) item = ImageItem( QIcon( pix.scaled( QSize(self.args.image_size[0], self.args.image_size[1]))), image) item.setSizeHint( QSize(self.args.image_size[0], self.args.image_size[1])) return item def _slot_edit(self): text = self.edit.text() if self.edit.legal(text): if os.path.isfile(text): self.IRetrieval = text self.rarea.clear() self.state.reset() rank_images = self.state.retrieval.metric(self.IRetrieval) if rank_images is None: return self.state.rank_images = rank_images self.rarea.addItem(self._create_icon(self.IRetrieval)) self._rank_images(rank_images) def _slot_search(self): image = QFileDialog.getOpenFileName( directory='.', filter='Images (*.png *.jpeg *.bmp *.jpg)', parent=self, caption='select a picture to search') if image[0] != '': self.edit.setText(image[0]) def _slot_rarea(self, imItem): self.IRetrieval = imItem.image dialog = ImageDetails(self, imItem.image, self.args) dialog.exec_() def _slot_rarea_dc(self, imItem): pass def slot_detail_search(self): if hasattr(self, 'IRetrieval'): rank_images = self.state.retrieval.metric(self.IRetrieval) if rank_images is None: return self.rarea.clear() self.state.reset() self.state.rank_images = rank_images self._rank_images(rank_images) def _rank_images(self, rank_images): for idx in range(self.args.top): if idx >= len(rank_images): break self.rarea.addItem( self._create_icon(self.dataset.absolute(rank_images[idx]))) self.state.rank_index += 1 def _images(self): for idx in range(self.args.start_number): if idx >= len(self.dataset): break self.rarea.insertItem( idx, self._create_icon(self.dataset.absolute(self.dataset[idx]))) self.state.index += 1 def resizeEvent(self, QResizeEvent): pass def wheelEvent(self, QWheelEvent): para = QWheelEvent.angleDelta().y() if para < 0: if self.state.is_retrieval: index = self.state.rank_index dataset = self.state.rank_images else: index = self.state.index dataset = self.dataset incre = self.args.incre if (index + self.args.incre) > len(dataset): incre = len(dataset) - index - 1 if incre <= 0: return for incre_idx in range(incre): self.rarea.addItem( self._create_icon( self.dataset.absolute(dataset[index + incre_idx]))) if self.state.is_retrieval: self.state.rank_index += 1 else: self.state.index += 1
class MainWindow(QMainWindow): def __init__(self, parent=None): super(MainWindow, self).__init__(parent) self.title = 'To The Beat' self.can_start = {'vid_chooser_list': False, 'music_file_textbox': False, 'output_file_textbox': False, 'isRendering': False} self.vids = [] self.was_canceled = False self.initUI() def initUI(self): self.setWindowTitle(self.title) self.central_widget = QWidget() self.layout = QVBoxLayout() self.tabs = QTabWidget() self.inputTab = QWidget() self.inputLayout = QVBoxLayout() self.inputTab.setLayout(self.inputLayout) self.optionsTab = QWidget() self.optionsGrid = QGridLayout() self.optionsLayout = QVBoxLayout() self.optionsTab.setLayout(self.optionsLayout) self.tabs.addTab(self.inputTab, 'Input') self.tabs.addTab(self.optionsTab, 'Options') # Video chooser self.vid_chooser_list = QListWidget() self.vid_chooser_list.setSelectionMode(QAbstractItemView.ExtendedSelection) self.vid_chooser_list.setIconSize(QSize(320/3, 240/3)) self.vid_chooser_list.setVerticalScrollMode(QAbstractItemView.ScrollPerPixel) self.vid_chooser_model = self.vid_chooser_list.model() self.vid_chooser_model.rowsInserted.connect(self.changeVidChooserBtnState) self.vid_chooser_model.rowsRemoved.connect(self.changeVidChooserBtnState) self.vid_add_btn = QPushButton('Add') self.vid_add_btn.clicked.connect(self.addVideos) self.vid_rm_btn = QPushButton('Remove') self.vid_rm_btn.clicked.connect(self.removeVideos) self.vid_up_btn = QPushButton('Up') self.vid_up_btn.clicked.connect(self.moveVideosUp) self.vid_down_btn = QPushButton('Down') self.vid_down_btn.clicked.connect(self.moveVideosDown) self.vid_chooser_btn_layout = QVBoxLayout() self.vid_chooser_btn_layout.setContentsMargins(0, 0, 0, 0) self.vid_chooser_btn_layout.setSpacing(0) self.vid_chooser_btn_layout.addWidget(self.vid_add_btn) self.vid_chooser_btn_layout.addWidget(self.vid_rm_btn) self.vid_chooser_btn_layout.addWidget(self.vid_up_btn) self.vid_chooser_btn_layout.addWidget(self.vid_down_btn) self.vid_chooser_btn_layout.addStretch(0) self.vid_chooser_layout = QHBoxLayout() self.vid_chooser_layout.addWidget(self.vid_chooser_list) self.vid_chooser_layout.addLayout(self.vid_chooser_btn_layout) self.vid_chooser_group_box = QGroupBox('Video files') self.vid_chooser_group_box.setLayout(self.vid_chooser_layout) # Music chooser self.music_file_textbox = QLineEdit() self.music_file_textbox.textChanged.connect(self.musicFileTextboxChanged) self.music_file_browse_btn = QPushButton('Browse') self.music_file_browse_btn.clicked.connect(self.browseMusicFile) self.music_chooser_layout = QHBoxLayout() self.music_chooser_layout.addWidget(self.music_file_textbox) self.music_chooser_layout.addWidget(self.music_file_browse_btn) self.music_chooser_group_box = QGroupBox('Music') self.music_chooser_group_box.setLayout(self.music_chooser_layout) # Output vid chooser self.output_file_textbox = QLineEdit() self.output_file_textbox.textChanged.connect(self.outputFileTextboxChanged) self.output_file_browse_btn = QPushButton('Browse') self.output_file_browse_btn.clicked.connect(self.browseOutputFile) self.output_chooser_layout = QHBoxLayout() self.output_chooser_layout.addWidget(self.output_file_textbox) self.output_chooser_layout.addWidget(self.output_file_browse_btn) self.output_chooser_group_box = QGroupBox('Output') self.output_chooser_group_box.setLayout(self.output_chooser_layout) # OPTIONS split_beat_tooltip = 'Change this option to change when in the music the video cuts to another clip. Multiples of 3 and 4 work best for most music.' self.split_beat_spinbox = QSpinBox() self.split_beat_spinbox.setMinimum(1) self.split_beat_spinbox.setValue(4) self.split_beat_spinbox.setPrefix('Cut every ') self.split_beat_spinbox.setSuffix(' beats') self.split_beat_spinbox.setToolTip(split_beat_tooltip) sep_tooltip = 'Clips from the same video must be at least this many seconds apart.' self.sep_spinbox = QSpinBox() self.sep_spinbox.setMinimum(1) self.sep_spinbox.setValue(5) self.sep_spinbox.setSuffix(' seconds') self.sep_spinbox.setToolTip(sep_tooltip) # TODO: Add a button in this combobox that says 'Custom resolution', and it pops up with a dialog box allowing you to input custom resolution resolution_tooltip = 'Sets the output resolution of the video. Select "Custom resolution" to set your own. Higher resolutions will take longer to render.' self.resolution_combobox = QComboBox() self.resolution_combobox.addItems([ '1280 x 720 (720p)', '1920 x 1080 (1080p)', '3840 x 2160 (4K)', 'Custom resolution' ]) self.resolution_combobox.insertSeparator(3) self.resolution_combobox.setToolTip(resolution_tooltip) fps_tooltip = 'Sets the output frames per second of the video. The default value of 30fps should work well for most videos.' self.fps_spinbox = QSpinBox() self.fps_spinbox.setMinimum(1) self.fps_spinbox.setValue(30) self.fps_spinbox.setSuffix(' fps') self.fps_spinbox.setToolTip(fps_tooltip) preset_tooltip = 'Sets the FFmpeg render preset. Faster presets will result in faster render times but bigger file sizes.' self.preset_combobox = QComboBox() self.preset_combobox.addItems([ 'ultrafast', 'superfast', 'veryfast', 'faster', 'fast', 'medium', 'slow', 'slower', 'veryslow' ]) self.preset_combobox.setCurrentIndex(0) #ultrafast self.preset_combobox.setToolTip(preset_tooltip) self.options = [ [QLabel('Beat to cut at'), self.split_beat_spinbox], [QLabel('Minimum separation time'), self.sep_spinbox], [QLabel('Frames per second'), self.fps_spinbox], [QLabel('Resolution'), self.resolution_combobox], [QLabel('FFmpeg render preset'), self.preset_combobox] ] for r, row in enumerate(self.options): for c, widget in enumerate(row): self.optionsGrid.addWidget(widget, r, c) self.optionsLayout.addWidget(QLabel('Hover over an option to learn more about it')) self.optionsLayout.addLayout(self.optionsGrid) self.optionsLayout.addStretch() # Start button (create video?) # Maybe make button bigger vertically so it's more obvious? self.start_btn = QPushButton('Start') self.start_btn.clicked.connect(self.start) self.changeVidChooserBtnState() self.stop_btn = QPushButton('Cancel') self.stop_btn.clicked.connect(self.stop) self.stop_btn.hide() self.progress_bar = QProgressBar() self.progress_bar.setMaximum(100) self.progress_bar.setValue(0) self.progress_bar.hide() # Add everything to the main layout self.inputLayout.addWidget(self.vid_chooser_group_box) self.inputLayout.addWidget(self.music_chooser_group_box) self.layout.addWidget(self.tabs) self.layout.addWidget(self.output_chooser_group_box) self.layout.addWidget(self.start_btn) self.layout.addWidget(self.stop_btn) self.layout.addWidget(self.progress_bar) self.central_widget.setLayout(self.layout) self.setCentralWidget(self.central_widget) self.show() def checkCanStart(self): if self.can_start['vid_chooser_list'] and self.can_start['music_file_textbox'] and self.can_start['output_file_textbox'] and not self.can_start['isRendering']: self.start_btn.setEnabled(True) else: self.start_btn.setEnabled(False) def changeVidChooserBtnState(self): if len(self.vid_chooser_list) > 0: self.vid_rm_btn.setEnabled(True) self.vid_up_btn.setEnabled(True) self.vid_down_btn.setEnabled(True) self.can_start['vid_chooser_list'] = True else: self.vid_rm_btn.setEnabled(False) self.vid_up_btn.setEnabled(False) self.vid_down_btn.setEnabled(False) self.can_start['vid_chooser_list'] = False self.checkCanStart() def musicFileTextboxChanged(self): if len(self.music_file_textbox.text()) > 0: self.can_start['music_file_textbox'] = True else: self.can_start['music_file_textbox'] = False self.checkCanStart() def outputFileTextboxChanged(self): if len(self.output_file_textbox.text()) > 0: self.can_start['output_file_textbox'] = True else: self.can_start['output_file_textbox'] = False self.checkCanStart() def addVideos(self): file_names = QFileDialog.getOpenFileNames(self, 'Select video files', '', ( 'Video and Image files (*.mp4 *.avi *.mov *.flv *.wmv *.png *.jpg *.bpm *.tiff *.gif *.webp)' ))[0] QApplication.setOverrideCursor(Qt.WaitCursor) with tempfile.TemporaryDirectory() as dir: for i, name in enumerate(file_names): thumbnail_name = f'{dir}/{str(uuid.uuid4())}.jpg' tothebeat.createThumbnail(name, thumbnail_name) short_name = name.split('/')[-1] self.vid_chooser_list.addItem(QListWidgetItem(QIcon(thumbnail_name), short_name)) self.vids.append(name) QApplication.restoreOverrideCursor() def removeVideos(self): for selected_widget in self.vid_chooser_list.selectedItems(): index = self.vid_chooser_list.row(selected_widget) self.vid_chooser_list.takeItem(index) self.vids.pop(index) def moveVideosUp(self): selected_rows = [self.vid_chooser_list.row(selected_widget) for selected_widget in self.vid_chooser_list.selectedItems()] selected_rows.sort() if 0 not in selected_rows: for row in selected_rows: widget = self.vid_chooser_list.takeItem(row) self.vid_chooser_list.insertItem(row-1, widget) widget.setSelected(True) self.vids.insert(row-1, self.vids.pop(row)) def moveVideosDown(self): selected_rows = [self.vid_chooser_list.row(selected_widget) for selected_widget in self.vid_chooser_list.selectedItems()] selected_rows.sort(reverse=True) if len(self.vid_chooser_list)-1 not in selected_rows: for row in selected_rows: widget = self.vid_chooser_list.takeItem(row) self.vid_chooser_list.insertItem(row+1, widget) widget.setSelected(True) self.vids.insert(row+1, self.vids.pop(row)) def browseMusicFile(self): file_name = QFileDialog.getOpenFileName(self, 'Select a music file', '', 'Audio files (*.3gp *.aa *.aac *.aax *.act *.aiff *.amr *.ape *.au *.awb *.dct *.dss *.dvf *.flac *.gsm *.iklax *.ivs *.m4a *.m4b *.m4p *.mmf *.mp3 *.mpc *.msv *.nmf *.nsf *.ogg *.oga *.mogg *.opus *.ra *.rm *.raw *.sln *.tt *.vox *.wav *.webm *.wma *.wv)')[0] self.music_file_textbox.setText(file_name) def browseOutputFile(self): file_name = QFileDialog.getSaveFileName(self, 'Save output video as...', '', 'Video files (*.mp4 *.avi *.mov *.flv *.wmv)')[0] self.output_file_textbox.setText(file_name) def start(self): self.output_file_name = self.output_file_textbox.text() resolution = self.resolution_combobox.currentText().split('(')[0].strip().split('x') resolution_w = int(resolution[0].strip()) resolution_h = int(resolution[1].strip()) sep = self.sep_spinbox.value() fps = self.fps_spinbox.value() split_every_n_beat = self.split_beat_spinbox.value() preset = self.preset_combobox.currentText() self.progress_bar.show() self.progress_bar.setValue(0) self.render_thread = RenderVideoThread( self.music_file_textbox.text(), self.output_file_name, resolution_w, resolution_h, sep=sep, fps=fps, split_every_n_beat=split_every_n_beat, preset=preset, vids=self.vids ) self.render_thread.setProgress.connect(self.setProgress) self.render_thread.showErrorMessage.connect(self.showErrorMessage) self.render_thread.canceled.connect(self.canceled) self.render_thread.finished.connect(self.done) self.render_thread.start() #self.stop_btn.clicked.connect(self.render_thread.terminate) self.stop_btn.show() self.can_start['isRendering'] = True self.checkCanStart() def stop(self): ans = QMessageBox.warning(None, 'Cancel render', 'Are you sure you want to cancel the rendering of this video?', QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if ans == QMessageBox.Yes: self.render_thread.stop(canceled=True) self.render_thread.wait() def setProgress(self, progress): self.progress_bar.setValue(progress) def showErrorMessage(self, error_log_path): QMessageBox.critical(self, 'Something went wrong!', f'The video could not be rendered for some reason. A log of the error can be found at: {error_log_path}') self.render_thread.stop() self.render_thread.wait() def canceled(self): self.was_canceled = True QMessageBox.information(self, 'Canceled', 'The rendering of the video has been successfully canceled.') def done(self): if os.path.isfile(self.output_file_name) and not self.was_canceled: self.progress_bar.setValue(100) QMessageBox.information(self, 'Render complete!', f'Video has been successfully rendered to {self.output_file_name}!') self.was_canceled = False self.progress_bar.hide() self.progress_bar.setValue(0) self.stop_btn.hide() self.can_start['isRendering'] = False self.checkCanStart()
class manufacturerStack(QWidget): def __init__(self): super(manufacturerStack, self).__init__() self.leftlist = QListWidget() self.leftlist.setFixedWidth(250) self.leftlist.insertItem(0, 'Add Stock') self.leftlist.insertItem(1, 'Manage Stock') self.leftlist.insertItem(2, 'View Stock') self.stack1 = QWidget() self.stack2 = QWidget() self.stack3 = QWidget() self.stack1UI() self.stack2UI() self.stack3UI() self.Stack = QStackedWidget(self) self.Stack.addWidget(self.stack1) self.Stack.addWidget(self.stack2) self.Stack.addWidget(self.stack3) hbox = QHBoxLayout(self) hbox.addWidget(self.leftlist) hbox.addWidget(self.Stack) self.setLayout(hbox) self.leftlist.currentRowChanged.connect(self.display) self.setGeometry(500, 350, 200, 200) self.setWindowTitle('Stock Management') self.show() def stack1UI(self): # ADD STOCK UI layout = QFormLayout() self.add_mes = QLabel() self.ok = QPushButton('Add Stock', self) clear = QPushButton('Clear', self) self.stock_name = QLineEdit() layout.addRow("Stock Name", self.stock_name) self.stock_count = QLineEdit() layout.addRow("Quantity", self.stock_count) self.stock_cost = QLineEdit() layout.addRow("Cost of Stock (per item)", self.stock_cost) layout.addWidget(self.ok) layout.addWidget(clear) layout.addWidget(self.add_mes) self.ok.clicked.connect(self.on_click) # clear the data from the screen clear.clicked.connect(self.stock_name.clear) clear.clicked.connect(self.stock_cost.clear) clear.clicked.connect(self.stock_count.clear) self.stack1.setLayout(layout) def on_click( self ): # Add user input data to the database when ADD STOCK button is pressed stock_name_inp = self.stock_name.text().replace(' ', '_').lower() stock_count_inp = int(self.stock_count.text()) stock_cost_inp = int(self.stock_cost.text()) if (stock_count_inp and stock_cost_inp > 0): message = mp.insert_prod(stock_name_inp, stock_count_inp, stock_cost_inp) self.add_mes.setText(message) else: self.add_mes.setText('Invalid input') def stack2UI(self): # MANAGE STOCK UI with 3 tab options layout = QHBoxLayout() layout.setGeometry(QRect(0, 300, 1150, 500)) tabs = QTabWidget() self.tab1 = QWidget() self.tab2 = QWidget() self.tab3 = QWidget() tabs.addTab(self.tab1, 'Add Quantity') tabs.addTab(self.tab2, 'Reduce Quantity') tabs.addTab(self.tab3, 'Delete Stock') self.tab1UI() self.tab2UI() self.tab3UI() layout.addWidget(tabs) self.stack2.setLayout(layout) def tab1UI(self): #ADD QUANTITY TAB layout = QFormLayout() self.mes1 = QLabel() self.ok_add = QPushButton('Add Stock', self) clear = QPushButton('Clear', self) self.stock_name_add = QLineEdit() layout.addRow("Stock Name", self.stock_name_add) self.stock_count_add = QLineEdit() layout.addRow("Quantity to add", self.stock_count_add) layout.addWidget(self.ok_add) layout.addWidget(clear) layout.addWidget(self.mes1) self.tab1.setLayout(layout) self.ok_add.clicked.connect(self.call_add) clear.clicked.connect(self.stock_name_add.clear) clear.clicked.connect(self.stock_count_add.clear) def tab2UI(self): # REDUCE QUANTITY TAB layout = QFormLayout() self.mes2 = QLabel() self.ok_red = QPushButton('Reduce Stock', self) clear = QPushButton('Clear', self) self.stock_name_red = QLineEdit() layout.addRow("Stock Name", self.stock_name_red) self.stock_count_red = QLineEdit() layout.addRow("Quantity to reduce", self.stock_count_red) layout.addWidget(self.ok_red) layout.addWidget(clear) layout.addWidget(self.mes2) self.tab2.setLayout(layout) self.ok_red.clicked.connect(self.call_red) clear.clicked.connect(self.stock_name_red.clear) clear.clicked.connect(self.stock_count_red.clear) def tab3UI(self): # DELETE STOCK TAB layout = QFormLayout() self.mes3 = QLabel() self.ok_del = QPushButton('Delete Stock', self) clear = QPushButton('Clear', self) self.stock_name_del = QLineEdit() layout.addRow("Stock Name", self.stock_name_del) layout.addWidget(self.ok_del) layout.addWidget(clear) layout.addWidget(self.mes3) self.tab3.setLayout(layout) self.ok_del.clicked.connect(self.call_del) clear.clicked.connect(self.stock_name_del.clear) def call_del(self): # deletes the stock item that is passed # needs a check test to see if item is in database # needs user notification if item was deleted successfully or item not found stock_name = self.stock_name_del.text().replace(' ', '_').lower() message = mp.remove_stock(stock_name) self.mes3.setText(message) def call_red(self): # reduces the stock item quantity it is passed # needs a check test to see if item is in database # needs user notification if item quantity was reduced successfully or item not found stock_name = self.stock_name_red.text().replace(' ', '_').lower() stock_val = int(self.stock_count_red.text()) if (stock_val > 0): message = mp.dec_manu_quantity(stock_name, stock_val) self.mes2.setText(message) else: self.mes2.setText('Invalid quantity') def call_add(self): # adds the stock item quantity that it is passed # needs a check test to see if item is in database # needs user notification if item quantity was increased successfully or item not found stock_name = self.stock_name_add.text().replace(' ', '_').lower() stock_val = int(self.stock_count_add.text()) if (stock_val > 0): message = mp.inc_manu_quantity(stock_name, stock_val) self.mes1.setText(message) else: self.mes1.setText('Invalid quantity') def stack3UI(self): # VIEW STOCK TAB layout = QVBoxLayout() self.srb = QPushButton() self.srb.setText("Get Search Result.") self.View = QTableWidget() self.lbl3 = QLabel() self.lbl_conf_text = QLabel() self.lbl_conf_text.setText("Enter the search keyword:") self.conf_text = QLineEdit() self.View.setColumnCount(3) self.View.setColumnWidth(0, 250) self.View.setColumnWidth(1, 250) self.View.setColumnWidth(2, 200) self.View.insertRow(0) self.View.setItem(0, 0, QTableWidgetItem('Stock Name')) self.View.setItem(0, 1, QTableWidgetItem('Quantity')) self.View.setItem(0, 2, QTableWidgetItem('Cost(Per Unit)')) layout.addWidget(self.View) layout.addWidget(self.lbl_conf_text) layout.addWidget(self.conf_text) layout.addWidget(self.srb) layout.addWidget(self.lbl3) self.srb.clicked.connect(self.show_search) self.stack3.setLayout(layout) def show_search(self): # checks database for stock items if self.View.rowCount() > 1: for i in range(1, self.View.rowCount()): self.View.removeRow(1) x_act = mp.show_stock() x = [] if self.conf_text.text() != '': for i in range(0, len(x_act)): a = list(x_act[i]) if self.conf_text.text().lower() in a[0].lower(): x.append(a) else: x = mp.show_stock() if len(x) != 0: for i in range(1, len(x) + 1): self.View.insertRow(i) a = list(x[i - 1]) self.View.setItem( i, 0, QTableWidgetItem(a[0].replace('_', ' ').upper())) self.View.setItem(i, 1, QTableWidgetItem(str(a[1]))) self.View.setItem(i, 2, QTableWidgetItem(str(a[2]))) self.View.setRowHeight(i, 50) self.lbl3.setText('Viewing Stock Database.') else: self.lbl3.setText('No valid information in database.') def display(self, i): self.Stack.setCurrentIndex(i)
class BlackScholesUI(QDialog): def __init__(self, parent=None): super(BlackScholesUI, self).__init__(parent) self.originalPalette = QApplication.palette() self.outputs = ['price', 'delta', 'vega', 'theta', 'rho', 'omega',\ 'gamma', 'vanna', 'charm', 'vomma', 'veta', 'speed', \ 'zomma', 'color', 'ultima', 'dualDelta', 'dualGamma'] self.optionsList = [] self.addOptionSelector() self.addOptionInputs() self.greeksView() self.sweepInput() self.sweepOutput() self.optionsDisplay() self.plotWindow() leftLayout = QVBoxLayout() leftLayout.addWidget(self.optionsSelection) leftLayout.addWidget(self.optionsInput) leftLayout.addWidget(self.greeksBox) rightLayout = QGridLayout() rightLayout.addLayout(self.inputBox, 0, 0, 1, 4) rightLayout.addWidget(self.outputSelectList, 1, 0, 5, 4) MATLABButton = QPushButton("Export to MATLAB") rightLayout.addWidget(MATLABButton, 6, 0, 1, 2) plotButton = QPushButton("Plot Sweep Outputs") rightLayout.addWidget(plotButton, 6, 2, 1, 2) plotButton.clicked.connect(self.onPlotSweepButtonClicked) bottomLayout = QGridLayout() bottomLayout.addWidget(self.optionsBox, 0, 0, 2, 4) mainLayout = QGridLayout() mainLayout.addLayout(leftLayout, 0, 0, 3, 4) mainLayout.addLayout(rightLayout, 0, 9, 3, -1) mainLayout.addLayout(bottomLayout, 3, 0, -1, 4) mainLayout.addWidget(self.plotGroupBox, 0, 4, 3, 5) mainLayout.setColumnStretch(0, 1) mainLayout.setColumnStretch(1, 1) mainLayout.setColumnStretch(2, 1) mainLayout.setColumnStretch(3, 2) mainLayout.setColumnStretch(4, 2) mainLayout.setColumnStretch(5, 2) mainLayout.setColumnStretch(6, 1) mainLayout.setColumnStretch(7, 1) mainLayout.setColumnStretch(8, 1) self.setLayout(mainLayout) self.setFixedSize(1440, 900) self.setWindowTitle("Black-Scholes Calculations") QApplication.setStyle(QStyleFactory.create('Fusion')) def addOptionSelector(self): self.optionsSelection = QGroupBox() addOptionButton = QPushButton("Add Option") addOptionButton.clicked.connect(self.onOptionsAddClicked) self.optionsTypeBox = QComboBox() self.optionsTypeBox.addItems( ['Long Call', 'Long Put', 'Short Call', 'Short Put']) layout = QHBoxLayout() layout.addWidget(addOptionButton) layout.addWidget(self.optionsTypeBox) layout.addStretch(1) self.optionsSelection.setLayout(layout) def onOptionsAddClicked(self): marketPrice = None vol = None K = None expDay = None S0 = None q = 0 r = None try: K = float(self.kEdit.text()) except ValueError: msgBox = QMessageBox() msgBox.setIcon(QMessageBox.Information) msgBox.setWindowTitle("Error") msgBox.setText("Please Enter a Strike Price") msgBox.exec_() try: expDay = str(self.TEdit.text()) except ValueError: msgBox = QMessageBox() msgBox.setIcon(QMessageBox.Information) msgBox.setWindowTitle("Error") msgBox.setText("Please Enter an Expiration") msgBox.exec_() try: S0 = float(self.S0Edit.text()) except ValueError: msgBox = QMessageBox() msgBox.setIcon(QMessageBox.Information) msgBox.setWindowTitle("Error") msgBox.setText("Please Enter a Stock Price") msgBox.exec_() try: q = float(self.qEdit.text()) except ValueError: msgBox = QMessageBox() msgBox.setIcon(QMessageBox.Information) msgBox.setWindowTitle("Error") msgBox.setText("Please Enter a Dividend Yield") msgBox.exec_() try: r = float(self.rEdit.text()) except ValueError: msgBox = QMessageBox() msgBox.setIcon(QMessageBox.Information) msgBox.setWindowTitle("Error") msgBox.setText("Please Enter a Risk Free Rate") msgBox.exec_() try: vol = float(self.volEdit.text()) except ValueError: try: marketPrice = float(self.mpEdit.text()) except ValueError: msgBox = QMessageBox() msgBox.setIcon(QMessageBox.Information) msgBox.setWindowTitle("Error") msgBox.setText("Please Enter a Market Price or Volatility") msgBox.exec_() if 'Long' in self.optionsTypeBox.currentText(): ls = 'Long' elif 'Short' in self.optionsTypeBox.currentText(): ls = 'Short' if 'Call' in self.optionsTypeBox.currentText(): otype = 'Call' elif 'Put' in self.optionsTypeBox.currentText(): otype = 'Put' if K and expDay and S0 and (q is not None) and r and (vol or marketPrice): if marketPrice is None: opt = option(otype=otype, K=K, expDay=expDay, S0=S0, vol=vol, q=q, r=r, ls=ls) elif vol is None: opt = option(otype=otype, K=K, expDay=expDay, S0=S0, marketPrice=marketPrice, q=q, r=r, ls=ls) self.optionsList.append(opt) self.updateOptionsDisplay() def updateOptionsDisplay(self): for i, opt in enumerate(self.optionsList): self.optionsTable.setItem( i + 1, 0, QTableWidgetItem(opt.ls + ' ' + opt.otype)) self.optionsTable.setItem(i + 1, 1, QTableWidgetItem(str(opt.K))) self.optionsTable.setItem(i + 1, 2, QTableWidgetItem(opt.expDayStr)) self.optionsTable.setItem(i + 1, 3, QTableWidgetItem(str(opt.S0))) def addOptionInputs(self): self.optionsInput = QGroupBox('Option Inputs') kLabel = QLabel("&Strike:") self.kEdit = QLineEdit() kLabel.setBuddy(self.kEdit) TLabel = QLabel("&Expiration Date (YYYY-MM-DD):") self.TEdit = QLineEdit() TLabel.setBuddy(self.TEdit) S0Label = QLabel("&Underlying Price:") self.S0Edit = QLineEdit() S0Label.setBuddy(self.S0Edit) volLabel = QLabel("&Volatility:") self.volEdit = QLineEdit() volLabel.setBuddy(self.volEdit) mpLabel = QLabel("&Option Market Price:") self.mpEdit = QLineEdit() mpLabel.setBuddy(self.mpEdit) qLabel = QLabel("&Dividend Yield:") self.qEdit = QLineEdit() qLabel.setBuddy(self.qEdit) rLabel = QLabel("&Risk Free Rate:") self.rEdit = QLineEdit() rLabel.setBuddy(self.rEdit) layout = QFormLayout() layout.addRow(kLabel, self.kEdit) layout.addRow(TLabel, self.TEdit) layout.addRow(S0Label, self.S0Edit) layout.addRow(volLabel, self.volEdit) layout.addRow(mpLabel, self.mpEdit) layout.addRow(qLabel, self.qEdit) layout.addRow(rLabel, self.rEdit) self.optionsInput.setLayout(layout) def greeksView(self): self.greeksBox = QGroupBox("Option Info") self.greeksTable = QTableWidget(11, 4) self.greeksTable.setItem(0, 0, QTableWidgetItem('Market Price')) self.greeksTable.setItem(1, 0, QTableWidgetItem('Volatility')) self.greeksTable.setItem(2, 0, QTableWidgetItem('Years To Expiration')) self.greeksTable.setItem(3, 0, QTableWidgetItem('Dividend Yield')) self.greeksTable.setItem(4, 0, QTableWidgetItem('Risk Free Rate')) self.greeksTable.setItem(5, 0, QTableWidgetItem('Delta')) self.greeksTable.setItem(6, 0, QTableWidgetItem('Vega')) self.greeksTable.setItem(7, 0, QTableWidgetItem('Theta')) self.greeksTable.setItem(8, 0, QTableWidgetItem('Rho')) self.greeksTable.setItem(9, 0, QTableWidgetItem('Omega')) self.greeksTable.setItem(10, 0, QTableWidgetItem('Gamma')) self.greeksTable.setItem(0, 2, QTableWidgetItem('Vanna')) self.greeksTable.setItem(1, 2, QTableWidgetItem('Charm')) self.greeksTable.setItem(2, 2, QTableWidgetItem('Vomma')) self.greeksTable.setItem(3, 2, QTableWidgetItem('Veta')) self.greeksTable.setItem(4, 2, QTableWidgetItem('Speed')) self.greeksTable.setItem(5, 2, QTableWidgetItem('Zomma')) self.greeksTable.setItem(6, 2, QTableWidgetItem('Color')) self.greeksTable.setItem(7, 2, QTableWidgetItem('Ultima')) self.greeksTable.setItem(8, 2, QTableWidgetItem('Dual Delta')) self.greeksTable.setItem(9, 2, QTableWidgetItem('Dual Gamma')) layout = QHBoxLayout() layout.setContentsMargins(5, 5, 5, 5) layout.addWidget(self.greeksTable) self.greeksBox.setLayout(layout) def plotWindow(self): self.plotGroupBox = QTabWidget() self.plotGroupBox.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Ignored) self.tab1 = FigureCanvas(Figure(figsize=(4, 8))) self.plot1ax = self.tab1.figure.add_subplot(111) self.tab2 = FigureCanvas(Figure(figsize=(4, 8))) self.plot2ax = self.tab2.figure.add_subplot(111) self.plotGroupBox.addTab(self.tab1, "&Time Evolution of Value") self.plotGroupBox.addTab(self.tab2, "&Sweep Results") #self.plotGroupBox.addTab(tab3, "&Profit Calculator") #self.plotGroupBox.addTab(tab4, "&Delta Hedging") def optionsDisplay(self): self.optionsBox = QGroupBox("Options") self.optionsTable = QTableWidget(10, 4) self.optionsTable.setItem(0, 0, QTableWidgetItem('Option Type')) self.optionsTable.setItem(0, 1, QTableWidgetItem('Strike')) self.optionsTable.setItem(0, 2, QTableWidgetItem('Expiration')) self.optionsTable.setItem(0, 3, QTableWidgetItem('Underlying Price')) self.optionsTable.setSelectionMode(2) self.optionsTable.cellClicked.connect(self.onOptionTableClicked) layout = QHBoxLayout() layout.setContentsMargins(1, 1, 1, 1) layout.addWidget(self.optionsTable) self.optionsBox.setLayout(layout) def onOptionTableClicked(self, row, column): self.selected = self.optionsTable.selectedItems() fulltime = [] halftime = [] expiry = [] usedRows = [] for item in self.selected: if item.row() != 0 and item.row() <= len( self.optionsList) and item.row() not in usedRows: usedRows.append(item.row()) opt = self.optionsList[item.row() - 1] self.greeksTable.setItem( 0, 1, QTableWidgetItem(str(opt.marketPrice))) self.greeksTable.setItem(1, 1, QTableWidgetItem(str(opt.vol))) self.greeksTable.setItem(2, 1, QTableWidgetItem(str(opt.T))) self.greeksTable.setItem(3, 1, QTableWidgetItem(str(opt.q))) self.greeksTable.setItem(4, 1, QTableWidgetItem(str(opt.r))) self.greeksTable.setItem( 5, 1, QTableWidgetItem( str( opt.delta(S0=opt.S0, K=opt.K, vol=opt.vol, r=opt.r, T=opt.T, q=opt.q)))) self.greeksTable.setItem( 6, 1, QTableWidgetItem( str( opt.vega(S0=opt.S0, K=opt.K, vol=opt.vol, r=opt.r, T=opt.T, q=opt.q)))) self.greeksTable.setItem( 7, 1, QTableWidgetItem( str( opt.theta(S0=opt.S0, K=opt.K, vol=opt.vol, r=opt.r, T=opt.T, q=opt.q)))) self.greeksTable.setItem( 8, 1, QTableWidgetItem( str( opt.rho(S0=opt.S0, K=opt.K, vol=opt.vol, r=opt.r, T=opt.T, q=opt.q)))) self.greeksTable.setItem( 9, 1, QTableWidgetItem( str( opt.omega(S0=opt.S0, K=opt.K, vol=opt.vol, r=opt.r, T=opt.T, q=opt.q)))) self.greeksTable.setItem( 10, 1, QTableWidgetItem( str( opt.gamma(S0=opt.S0, K=opt.K, vol=opt.vol, r=opt.r, T=opt.T, q=opt.q)))) self.greeksTable.setItem( 0, 3, QTableWidgetItem( str( opt.vanna(S0=opt.S0, K=opt.K, vol=opt.vol, r=opt.r, T=opt.T, q=opt.q)))) self.greeksTable.setItem( 1, 3, QTableWidgetItem( str( opt.charm(S0=opt.S0, K=opt.K, vol=opt.vol, r=opt.r, T=opt.T, q=opt.q)))) self.greeksTable.setItem( 2, 3, QTableWidgetItem( str( opt.vomma(S0=opt.S0, K=opt.K, vol=opt.vol, r=opt.r, T=opt.T, q=opt.q)))) self.greeksTable.setItem( 3, 3, QTableWidgetItem( str( opt.veta(S0=opt.S0, K=opt.K, vol=opt.vol, r=opt.r, T=opt.T, q=opt.q)))) self.greeksTable.setItem( 4, 3, QTableWidgetItem( str( opt.speed(S0=opt.S0, K=opt.K, vol=opt.vol, r=opt.r, T=opt.T, q=opt.q)))) self.greeksTable.setItem( 5, 3, QTableWidgetItem( str( opt.zomma(S0=opt.S0, K=opt.K, vol=opt.vol, r=opt.r, T=opt.T, q=opt.q)))) self.greeksTable.setItem( 6, 3, QTableWidgetItem( str( opt.color(S0=opt.S0, K=opt.K, vol=opt.vol, r=opt.r, T=opt.T, q=opt.q)))) self.greeksTable.setItem( 7, 3, QTableWidgetItem( str( opt.ultima(S0=opt.S0, K=opt.K, vol=opt.vol, r=opt.r, T=opt.T, q=opt.q)))) self.greeksTable.setItem( 8, 3, QTableWidgetItem( str( opt.dualDelta(S0=opt.S0, K=opt.K, vol=opt.vol, r=opt.r, T=opt.T, q=opt.q)))) self.greeksTable.setItem( 9, 3, QTableWidgetItem( str( opt.dualGamma(S0=opt.S0, K=opt.K, vol=opt.vol, r=opt.r, T=opt.T, q=opt.q)))) self.plot1ax.clear() toSweep = {'S0': (opt.S0 * 0.8, opt.S0 * 1.2, 50)} toGrab = ('price') fulltime.append(opt.sweep(toSweep, toGrab)) self.plot1ax.plot(fulltime[0]['S0'], self.sumprice(fulltime), label='Now') saveT = opt.T opt.T /= 2 halftime.append(opt.sweep(toSweep, toGrab)) self.plot1ax.plot(halftime[0]['S0'], self.sumprice(halftime), label='Half Time') opt.T = 1e-6 expiry.append(opt.sweep(toSweep, toGrab)) opt.T = saveT self.plot1ax.plot(expiry[0]['S0'], self.sumprice(expiry), label='Expiration') self.plot1ax.set_xlabel('Stock Price') self.plot1ax.set_ylabel('Option Price') self.plot1ax.legend() self.tab1.draw() def sumprice(self, outs): for i in range(len(outs)): outs[i]['price'] = np.array(outs[i]['price']) if i == 0: if outs[0]['ls'] == 'Long': price = outs[0]['price'] elif outs[0]['ls'] == 'Short': price = -outs[0]['price'] else: if outs[i]['ls'] == 'Long': price += outs[i]['price'] elif outs[i]['ls'] == 'Short': price -= outs[i]['price'] return price def sweepInput(self): self.sweepBox = QGroupBox("Sweep Variables") toSweepLabel = QLabel("Inputs to Sweep") minLabel = QLabel("Min") maxLabel = QLabel("Max") stepsLabel = QLabel("Steps") inputs = [ 'None', 'Stock Price', 'Years to Expiration', 'Volatility', 'Dividend Yield', 'Risk Free Rate' ] self.inputSelect1 = QComboBox() self.inputSelect1.addItems(inputs) self.inputSelect2 = QComboBox() self.inputSelect2.addItems(inputs) self.min1 = QLineEdit() self.min2 = QLineEdit() self.max1 = QLineEdit() self.max2 = QLineEdit() self.steps1 = QLineEdit() self.steps2 = QLineEdit() self.inputBox = QGridLayout() self.inputBox.addWidget(toSweepLabel, 0, 0, 1, 1) self.inputBox.addWidget(minLabel, 0, 1, 1, 2) self.inputBox.addWidget(maxLabel, 0, 2, 1, 3) self.inputBox.addWidget(stepsLabel, 0, 3, 1, 4) self.inputBox.addWidget(self.inputSelect1, 1, 0, 2, 1) self.inputBox.addWidget(self.inputSelect2, 2, 0, 3, 1) self.inputBox.addWidget(self.min1, 1, 1, 2, 2) self.inputBox.addWidget(self.min2, 2, 1, 3, 2) self.inputBox.addWidget(self.max1, 1, 2, 2, 3) self.inputBox.addWidget(self.max2, 2, 2, 3, 3) self.inputBox.addWidget(self.steps1, 1, 3, 2, 4) self.inputBox.addWidget(self.steps2, 2, 3, 3, 4) def sweepOutput(self): self.outputSelectList = QListWidget() for i, out in enumerate(self.outputs): self.outputSelectList.insertItem(i, out) self.outputSelectList.setSelectionMode(1) def onPlotSweepButtonClicked(self): toSweep = {} if len(self.outputSelectList.selectedItems()) < 1: msgBox = QMessageBox() msgBox.setIcon(QMessageBox.Information) msgBox.setWindowTitle("Error") msgBox.setText("Select At Least One Output") msgBox.exec_() return toGrab = self.outputSelectList.selectedItems() toGrab = [str(g.text()) for g in toGrab] if str(self.inputSelect1.currentText()) == 'Stock Price': is1 = 'S0' if str(self.inputSelect1.currentText()) == 'Years to Expiration': is1 = 'T' if str(self.inputSelect1.currentText()) == 'Volatility': is1 = 'vol' if str(self.inputSelect1.currentText()) == 'Dividend Yield': is1 = 'q' if str(self.inputSelect1.currentText()) == 'Risk Free Rate': is1 = 'r' if str(self.inputSelect2.currentText()) == 'Stock Price': is2 = 'S0' if str(self.inputSelect2.currentText()) == 'Years to Expiration': is2 = 'T' if str(self.inputSelect2.currentText()) == 'Volatility': is2 = 'vol' if str(self.inputSelect2.currentText()) == 'Dividend Yield': is2 = 'q' if str(self.inputSelect2.currentText()) == 'Risk Free Rate': is2 = 'r' if str(self.inputSelect1.currentText()) != 'None' and bool( self.min1.text()) and bool(self.max1.text()) and bool( self.steps1.text()): min1 = float(self.min1.text()) max1 = float(self.max1.text()) steps1 = int(self.steps1.text()) toSweep[is1] = (min1, max1, steps1) if str(self.inputSelect2.currentText()) != 'None' and bool( self.min2.text()) and bool(self.max2.text()) and bool( self.steps2.text()): min2 = float(self.min2.text()) max2 = float(self.max2.text()) steps2 = int(self.steps2.text()) toSweep[is2] = (min2, max2, steps2) if len(toSweep) == 0: msgBox = QMessageBox() msgBox.setIcon(QMessageBox.Information) msgBox.setWindowTitle("Error") msgBox.setText("Select At Least One Input") msgBox.exec_() return if len(self.selected) > 1 and toGrab[0] != 'price' or len( self.selected) > 1 and len(toGrab) > 1: msgBox = QMessageBox() msgBox.setIcon(QMessageBox.Information) msgBox.setWindowTitle("Error") msgBox.setText( "Output Must be Only Price if More than One Option Selected") msgBox.exec_() return if len(toSweep) == 1: out = [] usedRows = [] self.tab2.figure.clf() self.plot2ax = self.tab2.figure.add_subplot(111) if len(self.selected) > 1: for item in self.selected: if item.row() != 0 and item.row() <= len( self.optionsList) and item.row() not in usedRows: opt = self.optionsList[item.row() - 1] usedRows.append(item.row()) out.append(opt.sweep(toSweep, toGrab)) self.plot2ax.clear() self.plot2ax.plot(out[0][is1], self.sumprice(out)) self.plot2ax.set_xlabel( str(self.inputSelect1.currentText())) self.plot2ax.set_ylabel(str(toGrab[0])) self.tab2.draw() else: for item in self.selected: if item.row() != 0 and item.row() <= len(self.optionsList): opt = self.optionsList[item.row() - 1] out = opt.sweep(toSweep, toGrab) self.plot2ax.clear() self.plot2ax.plot(out[is1], out[toGrab[0]]) self.plot2ax.set_xlabel( str(self.inputSelect1.currentText())) self.plot2ax.set_ylabel(str(toGrab[0])) self.tab2.draw() elif len(toSweep) == 2: parula_map = LinearSegmentedColormap.from_list('parula', parula()) out = [] usedRows = [] self.tab2.figure.clf() self.plot2ax = self.tab2.figure.add_subplot(111, projection='3d') if len(self.selected) > 1: for item in self.selected: if item.row() != 0 and item.row() <= len( self.optionsList) and item.row() not in usedRows: opt = self.optionsList[item.row() - 1] usedRows.append(item.row()) out.append(opt.sweep(toSweep, toGrab)) self.plot2ax.clear() self.plot2ax.plot_surface(out[0][is1], out[0][is2], self.sumprice(out), cmap=parula_map) self.plot2ax.set_xlabel( str(self.inputSelect1.currentText())) self.plot2ax.set_ylabel( str(self.inputSelect2.currentText())) self.plot2ax.set_zlabel(str(toGrab[0])) self.tab2.draw() else: for item in self.selected: if item.row() != 0 and item.row() <= len(self.optionsList): opt = self.optionsList[item.row() - 1] out = opt.sweep(toSweep, toGrab) self.plot2ax.clear() self.plot2ax.plot_surface(out[is1], out[is2], out[toGrab[0]], cmap=parula_map) self.plot2ax.set_xlabel( str(self.inputSelect1.currentText())) self.plot2ax.set_ylabel( str(self.inputSelect2.currentText())) self.plot2ax.set_zlabel(str(toGrab[0])) self.tab2.draw()
class StringListDlg(QDialog): acceptedList = pyqtSignal('QStringList') def __init__(self, name, stringlist=None, parent=None): super(StringListDlg, self).__init__(parent) self.name = name self.listWidget = QListWidget() if stringlist is not None: self.listWidget.addItems(stringlist) self.listWidget.setCurrentRow(0) buttonLayout = QVBoxLayout() for text, slot in (("&Add...", self.add), ("&Edit...", self.edit), ("&Remove...", self.remove), ("&Up", self.up), ("&Down", self.down), ("&Sort", self.listWidget.sortItems), ("Close", self.accept)): button = QPushButton(text) if not MAC: button.setFocusPolicy(Qt.NoFocus) if text == "Close": buttonLayout.addStretch() buttonLayout.addWidget(button) button.clicked.connect(slot) layout = QHBoxLayout() layout.addWidget(self.listWidget) layout.addLayout(buttonLayout) self.setLayout(layout) self.setWindowTitle("Edit {} List".format(self.name)) def add(self): row = self.listWidget.currentRow() title = "Add {}".format(self.name) string, ok = QInputDialog.getText(self, title, title) if ok and string: self.listWidget.insertItem(row, string) def edit(self): row = self.listWidget.currentRow() item = self.listWidget.item(row) if item is not None: title = "Edit {}".format(self.name) string, ok = QInputDialog.getText(self, title, title, QLineEdit.Normal, item.text()) if ok and string: item.setText(string) def remove(self): row = self.listWidget.currentRow() item = self.listWidget.item(row) if item is None: return reply = QMessageBox.question( self, "Remove {}".format(self.name), "Remove {} `{}'?".format(self.name, item.text()), QMessageBox.Yes | QMessageBox.No) if reply == QMessageBox.Yes: item = self.listWidget.takeItem(row) del item def up(self): row = self.listWidget.currentRow() if row >= 1: item = self.listWidget.takeItem(row) self.listWidget.insertItem(row - 1, item) self.listWidget.setCurrentItem(item) def down(self): row = self.listWidget.currentRow() if row < self.listWidget.count() - 1: item = self.listWidget.takeItem(row) self.listWidget.insertItem(row + 1, item) self.listWidget.setCurrentItem(item) def reject(self): self.accept() def accept(self): self.stringlist = [] for row in range(self.listWidget.count()): self.stringlist.append(self.listWidget.item(row).text()) self.acceptedList['QStringList'].emit(self.stringlist) # self.emit(SIGNAL("acceptedList(QStringList)"), self.stringlist) QDialog.accept(self)
class Window(QWidget): def __init__(self): super().__init__() self.load_image('res\\niceimage.jpg') self.root_layout = QHBoxLayout() self.control_view = self.build_control_view() self.view_layout = self.build_view_layout() self.root_layout.addLayout(self.view_layout, stretch=65) self.root_layout.addLayout(self.control_view, stretch=35) self.cmap = None self.setWindowTitle('ImgProc') self.setLayout(self.root_layout) self.show() @staticmethod def build_labeled_slider(text, min, max, init_value, c_slider=None): h_layout = QHBoxLayout() if c_slider is not None: slider = c_slider else: slider = QSlider(orientation=Qt.Horizontal) slider.setRange(min, max) slider.setValue(init_value) label = QLabel(text) value_label = QLabel(str(init_value)) h_layout.addWidget(label, stretch=10) h_layout.addWidget(slider, stretch=80) h_layout.addWidget(value_label, stretch=10) return h_layout, value_label, slider def build_control_view(self): control_view = QVBoxLayout() h_layout = QHBoxLayout() v_layout = QVBoxLayout() v_layout.setAlignment(Qt.AlignTop) s1, self.hue_slider_label, self.hue_slider = \ Window.build_labeled_slider('H: ', 0, 360, 0) s2, self.saturation_slider_label, self.saturation_slider = \ Window.build_labeled_slider('S: ', 0, 100, 50) s3, self.value_slider_label, self.value_slider = \ Window.build_labeled_slider('V: ', 0, 100, 50) s4, self.sigma_slider_label, self.sigma_slider = Window.build_labeled_slider( 'SIGMA: ', 0, 20, 0) self.hue_slider.valueChanged[int].connect(self.on_image_hsv_change) self.saturation_slider.valueChanged[int].connect( self.on_image_hsv_change) self.value_slider.valueChanged[int].connect(self.on_image_hsv_change) self.sigma_slider.valueChanged[int].connect(self.on_gaus_sigma_change) colors_info_layout = QVBoxLayout() self.rgb_label = QLabel(ColorsFormats.RGB.format(0, 0, 0)) self.hsv_label = QLabel(ColorsFormats.HSV.format(0, 0, 0)) self.lab_label = QLabel(ColorsFormats.LAB.format(0, 0, 0)) self.cells_count_label = QLabel('Cells found: 0') self.l_hist_button = QPushButton('Show hist') self.l_hist_button.clicked.connect(self.show_hist) colors_info_layout.addWidget(self.rgb_label) colors_info_layout.addWidget(self.hsv_label) colors_info_layout.addWidget(self.lab_label) colors_info_layout.addWidget(self.cells_count_label) v_layout.addLayout(s1) v_layout.addLayout(s2) v_layout.addLayout(s3) v_layout.addLayout(s4) v_layout.addLayout(colors_info_layout) v_layout.addWidget(self.l_hist_button) self.operations_list_widget = QListWidget() self.operations_list_widget.itemClicked.connect( self.pipeline_item_clicked) self.operations_list_widget.setDragEnabled(True) l1 = QListWidgetItem('Sobel') l1.setCheckState(Qt.Checked) l2 = QListWidgetItem('Otsu') l2.setCheckState(Qt.Checked) l3 = QListWidgetItem('Gabor') l3.setData(Qt.UserRole, {'theta': 0}) l3.setCheckState(Qt.Checked) l4 = QListWidgetItem('Canny') # l4.setData(Qt.UserRole, {'min_treshold': 80}, {'max_treshold': 120}) l4.setCheckState(Qt.Checked) l5 = QListWidgetItem('CompCount') l5.setData(Qt.UserRole, {'ksize': 0}) l5.setCheckState(Qt.Checked) l6 = QListWidgetItem('Harris') # l4.setData(Qt.UserRole, {'min_treshold': 80}, {'max_treshold': 120}) l6.setCheckState(Qt.Checked) l7 = QListWidgetItem('Fertness') # l4.setData(Qt.UserRole, {'min_treshold': 80}, {'max_treshold': 120}) l7.setCheckState(Qt.Checked) self.operations_list_widget.insertItem(1, l1) self.operations_list_widget.insertItem(2, l2) self.operations_list_widget.insertItem(3, l3) self.operations_list_widget.insertItem(4, l4) self.operations_list_widget.insertItem(5, l5) self.operations_list_widget.insertItem(6, l6) self.operations_list_widget.insertItem(7, l7) h_layout.addLayout(v_layout, stretch=50) h_layout.addWidget(self.operations_list_widget, stretch=50) h_pipeline = QHBoxLayout() buttons_layout = QVBoxLayout() buttons_layout.setAlignment(Qt.AlignTop) self.run_button = QPushButton('run') self.run_button.clicked.connect(self.run_pipeline) self.open_image_button = QPushButton('open') self.open_image_button.clicked.connect(self.open_image) self.clear_pipeline_button = QPushButton('clear') self.reset_image_button = QPushButton('reset') self.reset_image_button.clicked.connect(self.reset_image) self.clear_pipeline_button.clicked.connect(self.clear_pipeline) buttons_layout.addWidget(self.run_button, stretch=5) buttons_layout.addWidget(self.clear_pipeline_button, stretch=5) buttons_layout.addWidget(self.reset_image_button, stretch=5) buttons_layout.addWidget(self.open_image_button, stretch=5) self.pipeline_operations_widget = QListWidget() self.pipeline_operations_widget.setAcceptDrops(True) self.pipeline_operations_widget.setDragEnabled(True) h_pipeline.addWidget(self.pipeline_operations_widget) h_pipeline.addLayout(buttons_layout) control_view.addLayout(h_layout, stretch=30) control_view.addLayout(h_pipeline, stretch=70) return control_view def reset_image(self): self.hsv_adjust_image = self.image self.show_image(self.image) def clear_pipeline(self): self.pipeline_operations_widget.clear() def run_pipeline(self): self.cmap = None for index in range(self.pipeline_operations_widget.count()): item = self.pipeline_operations_widget.item(index) if item.checkState() == Qt.Checked: if item.text() == 'Sobel': self.show_image(T.sobel_filter(self.shown_image).numpy()) elif item.text() == 'Otsu': self.show_image( T.otsu_binarization(self.shown_image).numpy()) elif item.text() == 'Gabor': self.show_image( T.gabor_filter(self.shown_image, item.data( Qt.UserRole)['theta']).numpy()) elif item.text() == 'Canny': self.show_image( T.canny_edge_detection(self.shown_image).numpy()) elif item.text() == 'CompCount': self.cmap = 'YlGnBu' count, img = T.components_count(self.shown_image, dilate_ksize=3, erode_ksize=7) self.cells_count_label.setText(f'Cells found: {count}') self.show_image(img.numpy()) elif item.text() == 'Fertness': self.show_image( T.local_features_detection(self.shown_image, fertness=True)) elif item.text() == 'Harris': self.show_image( T.local_features_detection(self.shown_image, fertness=False)) def pipeline_item_clicked(self, item): if item.text() == 'Gabor': gaborDialog = GaborDialog(item.data(Qt.UserRole)['theta']) gaborDialog.exec_() item.setData(Qt.UserRole, {'theta': gaborDialog.theta}) def build_view_layout(self): self.image_figure = Figure() self.image_canvas = FigureCanvas(self.image_figure) self.image_canvas.mpl_connect('motion_notify_event', self.mouse_moved_on_image) self.hist_figure = Figure() self.hist_canvas = FigureCanvas(self.hist_figure) view_layout = QVBoxLayout() info_layout = QHBoxLayout() info_layout.addWidget(self.hist_canvas, stretch=30) info_layout.addWidget(QWidget(), stretch=40) view_layout.addWidget(self.image_canvas, stretch=80) view_layout.addLayout(info_layout, stretch=20) self.show_image(self.image) return view_layout def open_image(self): path, f = QFileDialog.getOpenFileName(self, 'Select image', filter="Images (*.png *.jpg);") print(f, path) self.load_image(path) self.show_image(self.image) # TODO check for image channels (must be 3) def load_image(self, path): self.image = np.array(Image.open(path)) self.hsv_adjust_image = self.image[:, :, :3] self.image = self.image[:, :, :3] def show_hist(self): l_comp = T.cielab_L_component(self.shown_image).numpy() self.hist_figure.clear() axs = self.hist_figure.add_subplot(111) axs.hist(l_comp.flatten(), bins=100) self.hist_canvas.draw() def mouse_moved_on_image(self, mouse_event): if not mouse_event.inaxes: return x, y = mouse_event.xdata, mouse_event.ydata r, g, b = map(int, self.shown_image[int(y), int(x)]) h, s, v = T.rgb_to_hsv((r, g, b)) L, la, lb = T.rgb_to_cielab((r, g, b)) self.rgb_label.setText(ColorsFormats.RGB.format(r, g, b)) self.hsv_label.setText(ColorsFormats.HSV.format(h, s, v)) self.lab_label.setText(ColorsFormats.LAB.format(L, la, lb)) def on_gaus_sigma_change(self, value): val = self.sigma_slider.value() val = (val / self.sigma_slider.maximum()) * 10 self.sigma_slider_label.setText(str(val)) if val == 0.0: self.show_image(self.hsv_adjust_image) return self.show_image( T.gaussian_filter(self.hsv_adjust_image, val, 5).numpy()) def on_image_hsv_change(self, value): h = self.hue_slider.value() s = self.saturation_slider.value() v = self.value_slider.value() self.hue_slider_label.setText(str(h)) self.saturation_slider_label.setText(str(s)) self.value_slider_label.setText(str(v)) s = s / 100.0 + 0.5 v = v / 100.0 + 0.5 self.hsv_adjust_image = T.transform_hsv(self.image, h, s, v).numpy() self.show_image(self.hsv_adjust_image) def show_image(self, image): self.image_figure.clear() axs = self.image_figure.add_subplot(111) axs.imshow(image, interpolation='none') self.shown_image = image self.image_canvas.draw()
class IngredientsDialogue(QDialog, QWidget): """Class representing second window of program which displays ingredients and allows users to input ingredients and specify a maximum time for recipes displayed. """ def __init__(self) -> None: """Initialize an instance of the ingredients window """ super().__init__() # Set up the screen background self.recipes_dialogue = None self.setStyleSheet("background-color: rgb(240, 225, 204)") self.setWindowIcon(QIcon('visuals/L_C_Icon.PNG')) # Initialized all widgets needed self.lbl_list = QLabel("Your List", self) self.lbl_list.setFont(QFont('Georgia', 12, QFont.Bold)) self.lbl_list.setStyleSheet('color: rgb(211, 104, 80)') self.lbl_list.setFixedSize(200, 25) self.lbl_list.move(80, 100) self.lbl_max_ingredients = QLabel("(10 ingredients max.)", self) self.lbl_max_ingredients.setFont(QFont('Georgia', 9)) self.lbl_max_ingredients.setStyleSheet('color: rgb(35, 87, 77)') self.lbl_max_ingredients.move(57, 125) self.lbl_max_ingredients.resize(200, 20) self.lbl_max_time = QLabel("Maximum cooking time in minutes (optional):", self) self.lbl_max_time.setFont(QFont('Georgia', 12, QFont.Bold)) self.lbl_max_time.setStyleSheet('color: rgb(211, 104, 80)') self.lbl_max_time.setFixedSize(250, 80) self.lbl_max_time.setWordWrap(True) self.lbl_all_ingredients = QLabel("All Ingredients", self) self.lbl_all_ingredients.setFont(QFont('Georgia', 12, QFont.Bold)) self.lbl_all_ingredients.setStyleSheet('color: rgb(211, 104, 80)') self.lbl_all_ingredients.setFixedSize(200, 25) self.lbl_all_ingredients.move(375, 100) self.lbl_select_items = QLabel("Select your ingredients!", self) self.lbl_select_items.setFont(QFont('Georgia', 17, QFont.Bold)) self.lbl_select_items.setStyleSheet('color: rgb(210, 146, 68)') self.lbl_select_items.setFixedSize(400, 40) self.lbl_select_items.move(180, 40) self.time_selected = QSpinBox(self) self.time_selected.setStyleSheet('color: rgb(35, 87, 77)') self.time_selected.setFont(QFont('Georgia', 10)) self.time_selected.setRange(0, 50000) self.all_ingredients = QListWidget() self.all_ingredients.setFont(QFont('Georgia', 10)) self.all_ingredients.setStyleSheet('color: rgb(35, 87, 77)') self.line_edit = None self.user_input = None self.disabled_color2 = QGraphicsColorizeEffect() self.disabled_color2.setColor(QColor.fromRgb(240, 225, 204)) self.disabled_color3 = QGraphicsColorizeEffect() self.disabled_color3.setColor(QColor.fromRgb(240, 225, 204)) self.disabled_color4 = QGraphicsColorizeEffect() self.disabled_color4.setColor(QColor.fromRgb(240, 225, 204)) self.disabled_color5 = QGraphicsColorizeEffect() self.disabled_color5.setColor(QColor.fromRgb(240, 225, 204)) self.disabled_color6 = QGraphicsColorizeEffect() self.disabled_color6.setColor(QColor.fromRgb(240, 225, 204)) self.disabled_color7 = QGraphicsColorizeEffect() self.disabled_color7.setColor(QColor.fromRgb(240, 225, 204)) self.disabled_color8 = QGraphicsColorizeEffect() self.disabled_color8.setColor(QColor.fromRgb(240, 225, 204)) self.disabled_color9 = QGraphicsColorizeEffect() self.disabled_color9.setColor(QColor.fromRgb(240, 225, 204)) self.disabled_color10 = QGraphicsColorizeEffect() self.disabled_color10.setColor(QColor.fromRgb(240, 225, 204)) # Sets up all the line edits self.ingredient1 = QLineEdit(self) self.ingredient1.setStyleSheet( 'background-color: rgb(224, 182, 157); color: rgb(35, 87, 77)') self.ingredient1.setFont(QFont('Georgia', 10)) self.ingredient2 = QLineEdit(self) self.ingredient2.setStyleSheet( 'background-color: rgb(224, 182, 157); color: rgb(35, 87, 77)') self.ingredient2.setFont(QFont('Georgia', 10)) self.ingredient2.setGraphicsEffect(self.disabled_color2) self.ingredient3 = QLineEdit(self) self.ingredient3.setStyleSheet( 'background-color: rgb(224, 182, 157); color: rgb(35, 87, 77)') self.ingredient3.setFont(QFont('Georgia', 10)) self.ingredient3.setGraphicsEffect(self.disabled_color3) self.ingredient4 = QLineEdit(self) self.ingredient4.setStyleSheet( 'background-color: rgb(224, 182, 157); color: rgb(35, 87, 77)') self.ingredient4.setFont(QFont('Georgia', 10)) self.ingredient4.setGraphicsEffect(self.disabled_color4) self.ingredient5 = QLineEdit(self) self.ingredient5.setStyleSheet( 'background-color: rgb(224, 182, 157); color: rgb(35, 87, 77)') self.ingredient5.setFont(QFont('Georgia', 10)) self.ingredient5.setGraphicsEffect(self.disabled_color5) self.ingredient6 = QLineEdit(self) self.ingredient6.setStyleSheet( 'background-color: rgb(224, 182, 157); color: rgb(35, 87, 77)') self.ingredient6.setFont(QFont('Georgia', 10)) self.ingredient6.setGraphicsEffect(self.disabled_color6) self.ingredient7 = QLineEdit(self) self.ingredient7.setStyleSheet( 'background-color: rgb(224, 182, 157); color: rgb(35, 87, 77)') self.ingredient7.setFont(QFont('Georgia', 10)) self.ingredient7.setGraphicsEffect(self.disabled_color7) self.ingredient8 = QLineEdit(self) self.ingredient8.setStyleSheet( 'background-color: rgb(224, 182, 157); color: rgb(35, 87, 77)') self.ingredient8.setFont(QFont('Georgia', 10)) self.ingredient8.setGraphicsEffect(self.disabled_color8) self.ingredient9 = QLineEdit(self) self.ingredient9.setStyleSheet( 'background-color: rgb(224, 182, 157); color: rgb(35, 87, 77)') self.ingredient9.setFont(QFont('Georgia', 10)) self.ingredient9.setGraphicsEffect(self.disabled_color9) self.ingredient10 = QLineEdit(self) self.ingredient10.setStyleSheet( 'background-color: rgb(224, 182, 157); color: rgb(35, 87, 77)') self.ingredient10.setFont(QFont('Georgia', 10)) self.ingredient10.setGraphicsEffect(self.disabled_color10) # Gets all the ingredients from the data self.ingredient = [self.ingredient1, self.ingredient2, self.ingredient3, self.ingredient4, self.ingredient5, self.ingredient6, self.ingredient7, self.ingredient8, self.ingredient9, self.ingredient10] self.disabled_color = [self.disabled_color2, self.disabled_color3, self.disabled_color4, self.disabled_color5, self.disabled_color6, self.disabled_color7, self.disabled_color8, self.disabled_color9, self.disabled_color10] data = data_reading.read_recipes(data_reading.RECIPES_FILE) data_reading.clean_ingredients(data) self.clean = sorted(list(data_reading.get_ingredients(data))) for i in range(len(self.clean)): self.all_ingredients.insertItem(i, self.clean[i]) # Sets up the screen with all the needed elements self.title = "Look and Cook" self.left = 500 self.top = 200 self.width = 700 self.height = 700 self.init_window() self.center() self.setFixedSize(700, 700) def init_window(self) -> None: """Open the second window on the user's screen with the provided dimensions. """ # Sets up screen self.setGeometry(self.left, self.top, self.width, self.height) self.setWindowTitle(self.title) # Moves the labels to the right position self.lbl_max_time.move(250, self.height - 230) self.time_selected.move(550, self.height - 200) # Creates an autocomplete system to use when typing the ingredients completer = QCompleter(sorted(self.clean)) completer.setFilterMode(Qt.MatchContains) completer.setCaseSensitivity(Qt.CaseInsensitive) # Places all the line edits on the window self.ingredient1.setCompleter(completer) self.ingredient1.move(50, 160) self.ingredient1.setFixedSize(160, 30) height = 200 for x in self.ingredient[1:]: x.setCompleter(completer) x.move(50, height) x.setFixedSize(160, 30) x.setDisabled(True) height += 40 # Places list in the middle on the window vbox = QVBoxLayout() vbox.setContentsMargins(250, 0, 120, 120) self.all_ingredients.setFixedSize(400, 300) vbox.addWidget(self.all_ingredients) self.all_ingredients.itemDoubleClicked.connect(self.add_ingredient) self.setLayout(vbox) # Creates an add_item item button add_item = QPushButton("Add", self) add_item.setGeometry((self.width // 2) - 50, self.height // 2 + 200, 70, 70) add_item.move(50, self.height - 120) add_item.setFont(QFont('Georgia', 8, QFont.Bold)) add_item.setStyleSheet('border-radius: 35; background-color: rgb(211, 104, 80); ' 'color: rgb(240, 225, 204)') add_item.clicked.connect(self.add_item) # Creates an remove_item item button remove_item = QPushButton("Remove", self) remove_item.setGeometry((self.width // 2) - 50, self.height // 2 + 200, 70, 70) remove_item.move(140, self.height - 120) remove_item.setFont(QFont('Georgia', 8, QFont.Bold)) remove_item.setStyleSheet('border-radius: 35; background-color: rgb(211, 104, 80); ' 'color: rgb(240, 225, 204)') remove_item.clicked.connect(self.remove_item) # Creates an clear list item button clear_list = QPushButton("Clear List", self) clear_list.setGeometry((self.width // 2) - 50, self.height // 2 + 200, 190, 70) clear_list.move(250, self.height - 120) clear_list.setFont(QFont('Georgia', 12, weight=QtGui.QFont.Bold)) clear_list.setStyleSheet('border-radius: 35; background-color: rgb(211, 104, 80); ' 'color: rgb(240, 225, 204)') clear_list.clicked.connect(self.clear_list) # Creates a submit button submit = QPushButton("Find Recipes", self) submit.setGeometry((self.width // 2) - 50, self.height // 2 + 200, 190, 70) submit.move(460, self.height - 120) submit.setFont(QFont('Georgia', 12, weight=QtGui.QFont.Bold)) submit.setStyleSheet('border-radius: 35; background-color: rgb(210, 146, 68); ' 'color: rgb(240, 225, 204)') submit.clicked.connect(self.submit) # Displays everything on the window self.show() def center(self) -> None: """Function to center second window on the provided desktop screen. """ qr = self.frameGeometry() cp = QDesktopWidget().availableGeometry().center() qr.moveCenter(cp) self.move(qr.topLeft()) # frameGm = self.frameGeometry() # screen = QApplication.desktop().screenNumber(QApplication.desktop().cursor().pos()) # centerPoint = QApplication.desktop().screenGeometry(screen).center() # frameGm.moveCenter(centerPoint) # self.move(frameGm.topLeft()) resolution = QDesktopWidget().screenGeometry() self.move((resolution.width() / 2) - (self.frameSize().width() / 2), (resolution.height() / 2) - (self.frameSize().height() / 2)) def add_item(self) -> None: """Enables search box for user to input a new ingredient.""" for x in range(len(self.ingredient) - 1, 0, -1): if self.ingredient[x - 1].isEnabled(): self.ingredient[x - 1].setCursorPosition(0) self.ingredient[x].setDisabled(False) self.disabled_color[x - 1].setEnabled(False) def remove_item(self) -> None: """Removes the last ingredient of the list.""" for x in range(len(self.ingredient))[::-1][:-1]: if self.ingredient[x].isEnabled(): self.ingredient[x].setDisabled(True) self.ingredient[x].clear() self.disabled_color[x - 1].setEnabled(True) return def clear_list(self) -> None: """Clears all ingredients in user's list.""" for x in self.ingredient: x.clear() if x != self.ingredient1: x.setDisabled(True) self.time_selected.setValue(0) def add_ingredient(self, item: QListWidgetItem) -> None: """Add the double clicked ingredient to the ingredients list.""" for x in self.ingredient: if x.isEnabled() and x.text() == '': x.setText(item.text()) return def submit(self) -> None: """Create button to submit user input and proceed to the third page, which displays the recipes. If there is an ingredient listed twice in the input, raise an error pop up that says "Ingredient [duplicated ingredient] appears more than once" or "Ingredients [duplicated ingredients] appear more than once" if there are multiple duplicated ingredients. If there are remaining unfilled ingredient boxes, raise an error pop up that says "Did not fill all the needed information". If the user has inputted an invalid ingredient that does not appear in the list of ingredients at the center, raise an error pop up that says "The ingredient [invalid ingredient] is invalid" or "The ingredients [invalid ingredients] are invalid" if there are more than 1 invalid ingredients. """ ingredients_set = set() duplicates = '' count = 0 for x in self.ingredient: if x.isEnabled() and x.text() in ingredients_set: duplicates += x.text() count += 1 else: ingredients_set.add(x.text()) if len(duplicates) != 0: # This occurs when the user inputs the same ingredient twice contains_duplicates = QMessageBox() contains_duplicates.setWindowTitle("Error! - Duplicates") contains_duplicates.setWindowIcon(QIcon('visuals/L&C Icon.PNG')) if count == 1: # Only one ingredient appears more than once contains_duplicates.setText( f'Sorry, the ingredient {duplicates} appears more than once.') else: # Muliple ingredients contains_duplicates.setText( f'Sorry, the ingredients {duplicates} appear more than once.') contains_duplicates.setIcon(QMessageBox.Critical) x = contains_duplicates.exec_() elif any([y.isEnabled() and y.text() == '' for y in self.ingredient]): # Checks if there are any empty textboxes warning = QMessageBox() warning.setWindowTitle("Error!") warning.setWindowIcon(QIcon('visuals/L&C Icon.PNG')) warning.setText('Sorry, you did not fill all the necessary information. Please check ' 'that your ingredients list is correct and there are no empty spots.') warning.setIcon(QMessageBox.Critical) x = warning.exec_() elif not all([z.text() in self.clean for z in self.ingredient if z.isEnabled()]): # Checks if there are any items that are not valid invalid_ingredient = '' count = 0 for x in self.ingredient: if x.text() not in self.clean and x.isEnabled(): invalid_ingredient += x.text() + ', ' count += 1 invalid_ingredient = invalid_ingredient.strip(', ') invalid = QMessageBox() invalid.setWindowTitle("Error! - Invalid Ingredient") invalid.setWindowIcon(QIcon('visuals/L&C Icon.PNG')) if count == 1: # One ingredient invalid.setText(f"Sorry, the ingredient '{invalid_ingredient}' is invalid.") else: # More than one invalid.setText(f"Sorry, the ingredients '{invalid_ingredient}' are invalid.") invalid.setIcon(QMessageBox.Critical) x = invalid.exec_() else: # If everything is correct self.hide() user_input = [u.text() for u in self.ingredient if u.isEnabled()] # Goes to the next dialogue self.recipes_dialogue = \ RecipesDialogue(user_input, int(self.time_selected.text()), self) self.recipes_dialogue.show()
class TradeShares(QDialog): def __init__(self): super().__init__() self.title = "Trade shares" self.top = 200 self.left = 200 self.width = 400 self.height = 300 # Set the window title and geometry. self.setWindowTitle(self.title) self.setGeometry(self.left, self.top, self.width, self.height) # Setting the layout self.main_layout = QVBoxLayout() self.setLayout(self.main_layout) self.UI_trading_layout() def UI_trading_layout(self): # Layout contaning the below buttons layout and shares list. self.horizontal_layout = QHBoxLayout() # Layout containing line edit and buttons. self.vertical_layout_buttons = QVBoxLayout() # Create the list of securities. self.stocks_list = QListWidget() for i, stock in enumerate(securities_objects): self.stocks_list.insertItem(i, stock.name) # Create the line edit self.line_edit_volume = QLineEdit() self.line_edit_volume.setPlaceholderText("Amount") self.line_edit_volume.setValidator(QIntValidator(1, 100_000_000)) # Buttons self.buy_button = QPushButton(f"Buy") self.buy_button.clicked.connect(self.buy_shares) self.sell_button = QPushButton(f"Sell") self.sell_button.clicked.connect(self.sell_shares) self.close_button = QPushButton(f"Close") self.close_button.clicked.connect(self.close) # Status line self.status_line = QLabel(f"") # Populate the vertical layout with buttons. self.vertical_layout_buttons.addWidget(self.line_edit_volume) self.vertical_layout_buttons.addWidget(self.buy_button) self.vertical_layout_buttons.addWidget(self.sell_button) self.vertical_layout_buttons.addWidget(self.close_button) # Populate the horizontal layout. self.horizontal_layout.addWidget(self.stocks_list) self.horizontal_layout.addLayout(self.vertical_layout_buttons) # Populate the veritcal layout. self.main_layout.addLayout(self.horizontal_layout) self.main_layout.addWidget(self.status_line) def UI_change_status_line(self, string): def change_status_line(string): self.status_line.setText(string) time.sleep(2) self.status_line.setText(f"") status_line_thread = threading.Thread(target=change_status_line, args=(string, )) status_line_thread.start() def buy_shares(self): selection = self.stocks_list.currentItem().text() volume = int(self.line_edit_volume.text()) # Find the selcted share's object. for share in securities_objects: if share.name == selection: total_price = volume * share.price if patrik.check_balance(total_price) == False: self.UI_change_status_line(f"Not enough funds.") elif selection in patrik.holdings: patrik.holdings[selection] += volume self.UI_change_status_line(f"Transaction OK.") else: self.UI_change_status_line(f"Transaction OK.") patrik.holdings[selection] = volume def sell_shares(self): selection = self.stocks_list.currentItem().text() volume = int(self.line_edit_volume.text()) # Find the selcted share's object. for share in securities_objects: if share.name == selection: total_price = volume * share.price if selection in patrik.holdings: if volume < patrik.holdings[selection]: patrik.holdings[selection] -= volume self.UI_change_status_line(f"Transaction OK.") elif volume == patrik.holdings[selection]: del patrik.holdings[selection] self.UI_change_status_line(f"Transaction OK.") elif volume > patrik.holdings[selection]: self.UI_change_status_line( f"You only own {patrik.holdings[selection]} shares, not {volume}." ) else: self.UI_change_status_line(f"You don't own these shares")
class Window(QWidget): def __init__(self, programming=None): super(Window, self).__init__() self.li = [] if programming is not None: self.li = programming self.InitUi() def InitUi(self): self.listwidget = QListWidget() # li=['java','c++', 'c#'] # self.listwidget.insertItem(0,'C++') # self.listwidget.insertItem(1,'PYthon') # self.listwidget.insertItem(2,'C#') self.listwidget.addItems(self.li) self.listwidget.setCurrentRow(0) self.vbox = QVBoxLayout() self.vbox.addWidget(self.listwidget) self.vbox2 = QVBoxLayout() for text, slot in (("Add", self.add), ("Edit", self.edit), ("Remove", self.remove), ("Sort", self.sort), ("exit", self.exit)): btn = QPushButton(text) btn.clicked.connect(slot) self.vbox2.addWidget(btn) groubox = QGroupBox() vbox3 = QVBoxLayout() wizardbtn = QPushButton("Launch wizard") vbox3.addWidget(wizardbtn) groubox.setLayout(vbox3) wizardbtn.clicked.connect(self.wizartBtnClicked) self.vbox2.addWidget(groubox) self.hbox = QHBoxLayout() self.hbox.addWidget(self.listwidget) self.hbox.addLayout(self.vbox2) self.setLayout(self.hbox) self.wizardwin = QWizard() def wizartBtnClicked(self): self.wizardwin.open() def sort(self): self.listwidget.sortItems() def add(self): string, ok = QInputDialog.getText(self, 'Add Language', '추가할 언어를 입력하세요') if ok and string is not None: row = self.listwidget.currentRow() self.listwidget.insertItem(row, string) def edit(self): row = self.listwidget.currentRow() item = self.listwidget.item(row) # string, ok=QInputDialog.getText(self,'Edit Language','Enter new Language',QLineEdit.Normal,item.text()) string, ok = QInputDialog.getText(self, 'Edit Language', f"{item.text()}(을)를 무엇으로 수정하시겠나요?") if ok and string is not None: item.setText(string) def exit(self): self.close() def remove(self): row = self.listwidget.currentRow() item = self.listwidget.takeItem(row) del (item)
class App(QMainWindow): def __init__(self): super().__init__() self.title = "Weather" self.setWindowIcon(QtGui.QIcon('weather.ico')) self.initUI() def initUI(self): self.setWindowTitle(self.title) self.setGeometry(50, 130, 640, 500) self.label = QLabel('Welcome, ' + username, self) self.label.setFont(QFont("Verdana", 20, QFont.Bold)) self.label.setFixedWidth(320) self.label.setAlignment(QtCore.Qt.AlignCenter) self.label.move(160, 30) self.label2 = QLabel('WEATHER FORECAST', self) self.label2.setFont(QFont("Verdana", 18, QFont.Bold)) self.label2.setFixedWidth(320) self.label2.setAlignment(QtCore.Qt.AlignCenter) self.label2.move(160, 90) self.label3 = QLabel('Enter a city', self) self.label3.setFont(QFont("Verdana", 12)) self.label3.setFixedWidth(160) self.label3.setAlignment(QtCore.Qt.AlignCenter) self.label3.move(60, 180) self.label4 = QLabel('Days of forecast', self) self.label4.setFont(QFont("Verdana", 12)) self.label4.setFixedWidth(160) self.label4.setAlignment(QtCore.Qt.AlignCenter) self.label4.move(60, 210) self.city_name = QLineEdit(self) self.city_name.resize(120, 25) self.city_name.move(350, 180) self.num_days = QSpinBox(self) self.num_days.setRange(1, 5) self.num_days.setStyleSheet('QComboBox {background-color: white;}') self.num_days.resize(120, 25) self.num_days.move(350, 210) self.button1 = QPushButton('Get Forecast', self) self.button1.setFont(QFont("Verdana", 10, QFont.Bold)) self.button1.setStyleSheet( 'QPushButton {background-color: red; color: white;}') self.button1.resize(120, 30) self.button1.move(260, 280) def get_forecast(): url = "https://community-open-weather-map.p.rapidapi.com/forecast/daily" self.city = str(self.city_name.text()) self.days = str(self.num_days.value()) querystring = {"q": self.city, "cnt": self.days, "units": "metric"} headers = { 'x-rapidapi-host': "community-open-weather-map.p.rapidapi.com", 'x-rapidapi-key': "78034bcf5emsh6d33f4776102e77p10adaajsn7041714f6122" } response = requests.request("GET", url, headers=headers, params=querystring) self.data = response.json() #print(self.data) self.frcst = '' if self.data['cod'] == '404': self.error = QMessageBox() self.error.setWindowTitle('Warning') self.error.setIcon(QMessageBox.Warning) self.error.setStandardButtons(QMessageBox.Ok) self.error.setText( "City Not Found! \n Please Enter A Valid City") self.error.setGeometry(300, 300, 100, 100) self.error.exec_() else: self.label5 = QLabel('CITY FOUND', self) self.label5.setFont(QFont("Verdana", 14, QFont.Bold)) self.label5.setFixedWidth(440) self.label5.setAlignment(QtCore.Qt.AlignCenter) self.label5.move(100, 320) self.label5.show() for day in self.data['list']: self.date = str( datetime.datetime.fromtimestamp( day['dt']).strftime('%d-%m-%y')) self.weather = str(day['weather'][0]['description']) self.min_temp = float(day['temp']['min']) self.max_temp = float(day['temp']['max']) self.hum = int(day['humidity']) self.text = 'DATE:' self.text2 = 'Weather:' self.text3 = 'Temp min:' self.text4 = 'Temp max:' self.text5 = 'Humidity:' self.d = '{0} {1}'.format(self.text, self.date) self.w = '{0} {1}'.format(self.text2, self.weather) self.mi = '{0} {1}'.format(self.text3, self.min_temp) self.ma = '{0} {1}'.format(self.text4, self.max_temp) self.h = '{0} {1}{2}'.format(self.text5, self.hum, '%') self.frcst += (self.d + "\n" + self.w + '\n' + self.mi + "\n" + self.ma + "\n" + self.h + '\n\n') def open_forecast(): self.msg = QListWidget() self.msg.setWindowTitle("Weather Forecast") self.msg.setGeometry(0, 0, 280, 500) self.msg.move(690, 100) self.msg.insertItem(0, "Forecast for " + self.city.title() + '\n') self.msg.insertItem(1, self.frcst) self.msg.show() self.button1.clicked.connect(get_forecast) self.button2 = QPushButton('Show Forecast', self) self.button2.setFont(QtGui.QFont("Verdana", 10, QFont.Bold)) self.button2.setStyleSheet( 'QPushButton {background-color: blue; color: white;}') self.button2.resize(120, 30) self.button2.move(260, 360) self.button2.clicked.connect(open_forecast) self.button3 = QPushButton('Exit', self) self.button3.clicked.connect(QCoreApplication.instance().quit) self.button3.setFont(QtGui.QFont("Verdana", 10, QFont.Bold)) self.button3.setStyleSheet( 'QPushButton {background-color: black; color: white;}') self.button3.resize(80, 30) self.button3.move(280, 460) self.show()
class SelectCellDialog(QDialog): ''' Dialog used to select cells or trials for assigning protocols or types. ''' selected = pyqtSignal(tuple) def __init__(self, parent=None): ''' Initialize and build the window. Parameters ---------- parent: QWidget Parent window. Attributes ---------- items: list of QListWidgetItem Items to display in QListWidget. included: list of int Included cell numbers. excluded: list of int Excluded cell numbers. ''' super().__init__(parent) self.incLW = QListWidget(self) self.excLW = QListWidget(self) self.incLb = QLabel("Included") self.excLb = QLabel("Excluded") incVB = QVBoxLayout() excVB = QVBoxLayout() incVB.addWidget(self.incLb) incVB.addWidget(self.incLW) excVB.addWidget(self.excLb) excVB.addWidget(self.excLW) excAllBtn = QPushButton(">>", self) excBtn = QPushButton('>', self) incBtn = QPushButton('<', self) incAllBtn = QPushButton("<<", self) btnVB = QVBoxLayout() btnVB.addWidget(excAllBtn) btnVB.addWidget(excBtn) btnVB.addWidget(incBtn) btnVB.addWidget(incAllBtn) selectHB = QHBoxLayout() selectHB.addLayout(incVB) selectHB.addLayout(btnVB) selectHB.addLayout(excVB) acceptBtn = QPushButton("OK", self) acceptBtn.setDefault(True) cancelBtn = QPushButton("Cancel", self) btnHB = QHBoxLayout() btnHB.addWidget(acceptBtn) btnHB.addWidget(cancelBtn) topVB = QVBoxLayout(self) topVB.addLayout(selectHB) topVB.addLayout(btnHB) self.items = [] self.included = [] self.excluded = [] # key binding incAllBtn.clicked.connect(self.includeAll) excAllBtn.clicked.connect(self.excludeAll) incBtn.clicked.connect(self.include) excBtn.clicked.connect(self.exclude) acceptBtn.clicked.connect(self.finish) cancelBtn.clicked.connect(self.reject) def start(self, inc, exc): ''' Overload dialog open function, initializing items before showing the dialog. Parameters ---------- inc: list of int Predefined included cell numbers. exc: list of int Predefined excluded cell numbers. ''' self.included = sorted(inc) self.excluded = sorted(exc) self.incLW.clear() self.excLW.clear() self.items = [None] * (max(self.included + self.excluded)) for c in self.included: self.items[c - 1] = QListWidgetItem(str(c)) self.incLW.addItem(self.items[c - 1]) for c in self.excluded: self.items[c - 1] = QListWidgetItem(str(c)) self.excLW.addItem(self.items[c - 1]) super().open() def finish(self): ''' Finish selection, raise selectd signal with (inc, exc) as parameter. Signals ------- selected: Signalling selection is finished and return selection results. ''' self.accept() self.selected.emit((self.included, self.excluded)) def include(self): ''' Move the selected items from excluded list to included list. ''' for item in self.excLW.selectedItems(): c = int(item.text()) i = bisect.bisect_left(self.excluded, c) self.excLW.takeItem(i) self.excluded.pop(i) j = bisect.bisect_left(self.included, c) self.included.insert(j, c) self.incLW.insertItem(j, item) self.incLW.update() self.excLW.update() def exclude(self): ''' Move the selected items from included list to excluded list. ''' for item in self.incLW.selectedItems(): c = int(item.text()) i = bisect.bisect_left(self.included, c) self.incLW.takeItem(i) self.included.pop(i) j = bisect.bisect_left(self.excluded, c) self.excluded.insert(j, c) self.excLW.insertItem(j, item) self.incLW.update() self.excLW.update() def includeAll(self): ''' Move all items from excluded list ot included list. ''' while len(self.excluded): item = self.excLW.takeItem(0) c = self.excluded.pop(0) j = bisect.bisect_left(self.included, c) self.included.insert(j, c) self.incLW.insertItem(j, item) self.excLW.update() self.incLW.update() def excludeAll(self): ''' Move all items from included list ot excluded list. ''' while len(self.included): item = self.incLW.takeItem(0) c = self.included.pop(0) j = bisect.bisect_left(self.excluded, c) self.excluded.insert(j, c) self.excLW.insertItem(j, item) self.incLW.update() self.excLW.update() def changeTarget(self, target): ''' Change display included or excluded subject. ''' self.incLb.setText("Included " + target) self.excLb.setText("Excluded " + target)
class PowerBIThemeGeneratorWindow(QMainWindow): _powerBIThemeGenerator = None _pbiFilePath = None _horizontalLayoutTabVisualsTop = None _horizontalLayoutWelcomeScreen = None _groupBoxSelectedVisualPropertiesTree = None _listWidgetReportPages = None _listWidgetReportPageVisuals = None _verticalLayoutVisualsPropertiesTab = None _verticalLayoutMainWindow = None _treeWidgetSelectedVisualProperties = None _tabWidgetMainWindow = None _tabGeneralProperties = None _generalProperties = {} def __init__(self): super().__init__() # Windows Settings self.setWindowTitle(AppInfo.Name + ' - ' + AppInfo.Version) self.setGeometry(100, 100, 960, 720) # Creating central widget element self.centralWidget = QWidget(self) self.setCentralWidget(self.centralWidget) # Creating status bar self.statusBar = QStatusBar(self) self.setStatusBar(self.statusBar) # Creating menu bar self._createMenuBar() # Creating main window layout and adding widgets to them self._verticalLayoutMainWindow = QVBoxLayout(self.centralWidget) self._verticalLayoutMainWindow.addWidget(self._tabWidgetMainWindow) # for testing # self.__testOpenFileMethod() # end for testing self._horizontalLayoutWelcomeScreen = QHBoxLayout() self._createTabs() self.show() def _createMenuBar(self): # Create menu bar menuBar = self.menuBar() # Create root menu such as File, Edit, Help etc. menuBarFile = menuBar.addMenu('&File') menuBarHelp = menuBar.addMenu('&Help') # Create actions for Menu actionOpenPowerBIFile = QAction('Open &Power BI File', self) actionOpenPowerBIFile.setShortcut('Ctrl+O') actionOpenPowerBIFile.setStatusTip('Import Power BI file from which you want to export the visual settings to ' 'create theme') # actionReloadPowerBIFile = QAction('&Reload Power BI File', self) # actionReloadPowerBIFile.setShortcut('Ctrl+R') # actionReloadPowerBIFile.setStatusTip('Click to reload Power BI file if there are changes made in Power BI # file') actionGenerateTheme = QAction('&Generate Theme', self) actionGenerateTheme.setShortcut('Ctrl+G') actionGenerateTheme.setStatusTip('Click to generate theme and save theme file') actionQuit = QAction('&Quit', self) actionQuit.setShortcut('Ctrl+Q') actionQuit.setStatusTip('Click to quit application') actionAbout = QAction('&About', self) actionAbout.setStatusTip('Click to see detail application information and useful links') # Add actions to menu menuBarFile.addAction(actionOpenPowerBIFile) # menuBarFile.addAction(actionReloadPowerBIFile) menuBarFile.addAction(actionGenerateTheme) menuBarFile.addSeparator() menuBarFile.addAction(actionQuit) menuBarHelp.addAction(actionAbout) # Add global events i.e. what will happen when option is selected from menu bar actionOpenPowerBIFile.triggered.connect(self._openPowerBIFileDialog) actionQuit.triggered.connect(self.close) actionGenerateTheme.triggered.connect(self.generateTheme) actionAbout.triggered.connect(self._showAboutDialog) def _openPowerBIFileDialog(self): try: directory = '/' if self._pbiFilePath is not None: directory = self._pbiFilePath.split('/')[0:-1] directory = "/".join(directory) self._pbiFilePath = QFileDialog.getOpenFileName( self, caption='Select Power BI File', directory=directory, filter='Power BI Files(*.pbix)' )[0] if self._pbiFilePath is not '' and not None: if self._pbiFilePath.split('/')[-1].split('.')[-1] == 'pbix': self._powerBIThemeGenerator = PowerBIThemeGenerator(self._pbiFilePath) else: raise ValueError('Invalid Power BI File') self._reportVisualData = self._powerBIThemeGenerator.modifiedDataStructure() self._populateTabVisualProperties() except Exception as e: ShowErrorDialog(LogException(e)) def __testOpenFileMethod(self): self._pbiFilePath = 'G:/Power BI Reports/Theme Template.pbix' self._powerBIThemeGenerator = PowerBIThemeGenerator(self._pbiFilePath) self._reportVisualData = self._powerBIThemeGenerator.modifiedDataStructure() def _createTabs(self): # Creating tabs self._tabWidgetMainWindow = QTabWidget(self.centralWidget) self._tabGeneralProperties = QWidget() self._tabVisualProperties = QWidget() self._tabWidgetMainWindow.addTab(self._tabGeneralProperties, 'General Properties') self._tabWidgetMainWindow.addTab(self._tabVisualProperties, 'Visuals Properties') tabBarMainWindow: QTabBar = self._tabWidgetMainWindow.tabBar() tabBarMainWindow.setTabToolTip(0, 'Add optional general properties of the theme') tabBarMainWindow.setTabToolTip(1, 'All the Power BI File visuals related properties will be visible here') self._verticalLayoutVisualsPropertiesTab = QVBoxLayout(self._tabVisualProperties) self._verticalLayoutMainWindow.addWidget(self._tabWidgetMainWindow) self._populateTabGeneralProperties() self._populateTabVisualProperties() def _populateTabVisualProperties(self): if self._pbiFilePath is None: self._showWelcomeScreenTabVisualProperties() self._verticalLayoutVisualsPropertiesTab.addLayout(self._horizontalLayoutWelcomeScreen) else: self._clearTabVisualProperties() self._horizontalLayoutTabVisualsTop = QHBoxLayout() self._verticalLayoutVisualsPropertiesTab.insertLayout(0, self._horizontalLayoutTabVisualsTop) self._createReportPageList() # self._create_extra_options() def _populateTabGeneralProperties(self): try: dataColors = [] def __showMessageBoxInvalidHexColor(hexColor): messageBoxInvalidColor = QMessageBox() messageBoxInvalidColor.setIcon(QMessageBox.Critical) messageBoxInvalidColor.setWindowTitle('Invalid Hex Color Code') messageBoxInvalidColor.setText('"' + hexColor + '"' + ' is not valid hex color code') messageBoxInvalidColor.exec_() def __validateDataColors(): try: dataColors = [dataColor.strip() for dataColor in lineEditDataColors.text().split(',')] for dataColor in dataColors: if ColorUtil.IsValidHexColor(dataColor) is False and dataColor is not '': dataColors.remove(dataColor) __showMessageBoxInvalidHexColor(dataColor) elif dataColor is '': dataColors.remove(dataColor) lineEditDataColors.setText(','.join(dataColors)) except Exception as e: ShowErrorDialog(LogException(e)) def __showColorPickerDialog(): try: sender = self.sender().objectName() color: QColor = QColorDialog().getColor() if color.isValid(): hexColor = color.name() buttonBackgroundColor = 'background-color: ' + hexColor if sender == 'push_button_data_color': dataColors.append(hexColor) self._generalProperties['dataColors'] = dataColors lineEditDataColors.setText(','.join(dataColors)) elif sender == 'push_button_background_color': pushButtonBackgroundColorPicker.setStyleSheet(buttonBackgroundColor) lineEditBackgroundColor.setText(hexColor) self._generalProperties['background'] = hexColor elif sender == 'push_button_foreground_color': pushButtonForegroundColorPicker.setStyleSheet(buttonBackgroundColor) lineEditForegroundColor.setText(hexColor) self._generalProperties['foreground'] = hexColor elif sender == 'push_button_table_accent_color': pushButtonTableAccentColorPicker.setStyleSheet(buttonBackgroundColor) lineEditTableAccent.setText(hexColor) self._generalProperties['tableAccent'] = hexColor except Exception as e: ShowErrorDialog(LogException(e)) def __lineEditEditingFinished(): try: sender = self.sender().objectName() if sender == 'line_edit_data_color': __validateDataColors() nonlocal dataColors dataColors = lineEditDataColors.text().split(",") self._generalProperties['dataColors'] = dataColors return elif sender == 'line_edit_background_color': textColor = lineEditBackgroundColor.text() elif sender == 'line_edit_foreground_color': textColor = lineEditForegroundColor.text() elif sender == 'line_edit_table_accent_color': textColor = lineEditTableAccent.text() elif sender == 'line_edit_theme_name': self._generalProperties['name'] = lineEditThemeName.text() return validColor = ColorUtil.IsValidHexColor(textColor) if validColor is False and textColor is not '': __showMessageBoxInvalidHexColor(textColor) if sender == 'line_edit_background_color': lineEditBackgroundColor.setText('') elif sender == 'line_edit_foreground_color': lineEditForegroundColor.setText('') elif sender == 'line_edit_table_accent_color': lineEditTableAccent.setText('') else: buttonBackgroundColor = 'background-color: ' + textColor if sender == 'line_edit_background_color': self._generalProperties['background'] = textColor pushButtonBackgroundColorPicker.setStyleSheet(buttonBackgroundColor) elif sender == 'line_edit_foreground_color': pushButtonForegroundColorPicker.setStyleSheet(buttonBackgroundColor) self._generalProperties['foreground'] = textColor elif sender == 'line_edit_table_accent_color': pushButtonTableAccentColorPicker.setStyleSheet(buttonBackgroundColor) self._generalProperties['tableAccent'] = textColor except Exception as e: ShowErrorDialog(LogException(e)) labelThemeName = QLabel('Theme Name: ') lineEditThemeName = QLineEdit() lineEditThemeName.setObjectName('line_edit_theme_name') lineEditThemeName.editingFinished.connect(__lineEditEditingFinished) horizontalLayoutDataColor = QHBoxLayout() labelDataColors = QLabel('Data Colors: ') labelDataColors.setToolTip('These colors will be visible in color picker and will apply as data colors in ' 'charts') lineEditDataColors = QLineEdit() lineEditDataColors.editingFinished.connect(__lineEditEditingFinished) lineEditDataColors.setObjectName('line_edit_data_color') pushButtonDataColorPicker = QPushButton() pushButtonDataColorPicker.setToolTip('Click to pick colors for Data Colors') pushButtonDataColorPicker.setObjectName('push_button_data_color') pushButtonDataColorPicker.clicked.connect(__showColorPickerDialog) horizontalLayoutDataColor.addWidget(lineEditDataColors) horizontalLayoutDataColor.addWidget(pushButtonDataColorPicker) # TO DO: Add generate colors button horizontalLayoutBackgroundColor = QHBoxLayout() labelBackgroundColor = QLabel('Background Color: ') labelBackgroundColor.setToolTip('Applies to button fill and combo chart label background. How these colors ' 'are used depends on the specific visual style that\'s applied.') lineEditBackgroundColor = QLineEdit() lineEditBackgroundColor.setObjectName('line_edit_background_color') lineEditBackgroundColor.editingFinished.connect(__lineEditEditingFinished) pushButtonBackgroundColorPicker = QPushButton() pushButtonBackgroundColorPicker.setToolTip('Click to pick color for Background Color') pushButtonBackgroundColorPicker.setObjectName('push_button_background_color') pushButtonBackgroundColorPicker.clicked.connect(__showColorPickerDialog) horizontalLayoutBackgroundColor.addWidget(lineEditBackgroundColor) horizontalLayoutBackgroundColor.addWidget(pushButtonBackgroundColorPicker) horizontalLayoutForegroundColor = QHBoxLayout() labelForegroundColor = QLabel('Foreground Color: ') labelForegroundColor.setToolTip('Applies to textbox text, KPI goal text, multi-row card text, card value ' 'text, gauge callout text, vertical slicer element text, and table and ' 'matrix total and values text.') lineEditForegroundColor = QLineEdit() lineEditForegroundColor.setObjectName('line_edit_foreground_color') lineEditForegroundColor.editingFinished.connect(__lineEditEditingFinished) pushButtonForegroundColorPicker = QPushButton() pushButtonForegroundColorPicker.setToolTip('Click to pick color for Foreground Color') pushButtonForegroundColorPicker.setObjectName('push_button_foreground_color') pushButtonForegroundColorPicker.clicked.connect(__showColorPickerDialog) horizontalLayoutForegroundColor.addWidget(lineEditForegroundColor) horizontalLayoutForegroundColor.addWidget(pushButtonForegroundColorPicker) horizontalLayoutTableAccent = QHBoxLayout() labelTableAccent = QLabel('Table Accent: ') labelTableAccent.setToolTip('Table and matrix visuals apply these styles by default.') lineEditTableAccent = QLineEdit() lineEditTableAccent.setObjectName('line_edit_table_accent_color') lineEditTableAccent.editingFinished.connect(__lineEditEditingFinished) pushButtonTableAccentColorPicker = QPushButton() pushButtonTableAccentColorPicker.setToolTip('Click to pick color for Table Accent Color') pushButtonTableAccentColorPicker.setObjectName('push_button_table_accent_color') pushButtonTableAccentColorPicker.clicked.connect(__showColorPickerDialog) horizontalLayoutTableAccent.addWidget(lineEditTableAccent) horizontalLayoutTableAccent.addWidget(pushButtonTableAccentColorPicker) formLayoutGeneralProperties = QFormLayout(self._tabGeneralProperties) formLayoutGeneralProperties.addRow(labelThemeName, lineEditThemeName) formLayoutGeneralProperties.addRow(labelDataColors, horizontalLayoutDataColor) formLayoutGeneralProperties.addRow(labelBackgroundColor, horizontalLayoutBackgroundColor) formLayoutGeneralProperties.addRow(labelForegroundColor, horizontalLayoutForegroundColor) formLayoutGeneralProperties.addRow(labelTableAccent, horizontalLayoutTableAccent) except Exception as e: ShowErrorDialog(LogException(e)) def _showWelcomeScreenTabVisualProperties(self): welcomeLabel = QLabel('Go to File menu or press Ctrl+O to select Power BI File') self._horizontalLayoutWelcomeScreen.addStretch() self._horizontalLayoutWelcomeScreen.addWidget(welcomeLabel) self._horizontalLayoutWelcomeScreen.addStretch() def _removeWelcomeScreenFromTabVisualProperties(self): self._deleteLayout(self._horizontalLayoutWelcomeScreen) def _clearTabVisualProperties(self): self._removeWelcomeScreenFromTabVisualProperties() self._deleteLayout(self._verticalLayoutVisualsPropertiesTab) self._listWidgetReportPages = None self._listWidgetReportPageVisuals = None self._treeWidgetSelectedVisualProperties = None def _deleteLayout(self, layout): if layout is not None: while layout.count(): item = layout.takeAt(0) widget = item.widget() if widget is not None: widget.deleteLater() else: self._deleteLayout(item.layout()) # sip.delete(layout) def _createReportPageList(self): try: def __reportPageListValueSelected(item): try: self._createReportPageVisualsList(item.GetMetaData()['reportPageSection']) except Exception as e: ShowErrorDialog(LogException(e)) if self._listWidgetReportPages is not None: self._listWidgetReportPages.clear() groupBoxReportPageList = QGroupBox(self.centralWidget) groupBoxReportPageList.setTitle("Report Pages") verticalLayoutReportPageList = QVBoxLayout(groupBoxReportPageList) self._listWidgetReportPages = QListWidget() i = 0 for reportSection in self._powerBIThemeGenerator.get_report_sections(): reportListWidgetItem = CQListWidgetItemReportPages(str(reportSection['displayName'])) reportListWidgetItem.SetMetaData(reportSection) reportListWidgetItem.setToolTip(reportSection['name']) self._listWidgetReportPages.insertItem(i, reportListWidgetItem) i += 1 verticalLayoutReportPageList.addWidget(self._listWidgetReportPages) self._horizontalLayoutTabVisualsTop.addWidget(groupBoxReportPageList) # __reportPageListValueSelected(self._listWidgetReportPages.item(0)) self._listWidgetReportPages.itemClicked.connect(__reportPageListValueSelected) except Exception as e: ShowErrorDialog(LogException(e)) def _createReportPageVisualsList(self, reportPageSection=None): try: # print(self._reportVisualData[reportPageSection]) # print('inside report page visual list', reportPageSection) def __updateStateInDataStructure(item): vizProperties = item.GetVisualProperties() if item.checkState() == Qt.Checked: (self._reportVisualData[vizProperties['report_section']] .get('visuals')[vizProperties['visual_index']]['__selected']) = True else: (self._reportVisualData[vizProperties['report_section']] .get('visuals')[vizProperties['visual_index']]['__selected']) = False def __getVisualProperties(item: CQListWidgetItemReportPageVisuals): try: __updateStateInDataStructure(item) self._createSelectedVisualPropertiesTree(item.GetVisualProperties()) except Exception as e: ShowErrorDialog(LogException(e)) def __selectDeselectAll(): try: if self._listWidgetReportPageVisuals is not None: for i in range(self._listWidgetReportPageVisuals.count()): item = self._listWidgetReportPageVisuals.item(i) if self.sender().objectName() == 'button_select_all': item.setCheckState(Qt.Checked) else: item.setCheckState(Qt.Unchecked) __updateStateInDataStructure(item) except Exception as e: ShowErrorDialog(LogException(e)) groupBoxReportPageVisualsList = QGroupBox(self.centralWidget) groupBoxReportPageVisualsList.setTitle("Visuals in Selected Report Page") # groupBoxReportPageVisualsList.setToolTip("All visuals present in the selected page will be visible below") verticalLayoutReportPageVisualsList = QVBoxLayout(groupBoxReportPageVisualsList) horizontalLayoutSelectDeselectButton = QHBoxLayout() pushButtonSelectAll = QPushButton('Select All') pushButtonSelectAll.setObjectName('button_select_all') pushButtonDeselectAll = QPushButton('Deselect All') pushButtonDeselectAll.setObjectName('button_deselect_all') horizontalLayoutSelectDeselectButton.addWidget(pushButtonSelectAll) horizontalLayoutSelectDeselectButton.addWidget(pushButtonDeselectAll) verticalLayoutReportPageVisualsList.addLayout(horizontalLayoutSelectDeselectButton) if self._listWidgetReportPageVisuals is None: self._listWidgetReportPageVisuals = QListWidget() verticalLayoutReportPageVisualsList.addWidget(self._listWidgetReportPageVisuals) self._horizontalLayoutTabVisualsTop.addWidget(groupBoxReportPageVisualsList) self._listWidgetReportPageVisuals.itemClicked.connect(__getVisualProperties, Qt.UniqueConnection) else: self._listWidgetReportPageVisuals.clear() i = 0 for visualIndex, visual in enumerate(self._reportVisualData[reportPageSection].get('visuals')): visualType = visual['visual_type'] visualTitle = visual.get('objects').get('title') # print(visualType, visualTitle) if visualTitle is None or visualTitle.get('text') is None: listDisplayText = str(visualType) else: listDisplayText = str(visualType) + " - " + visualTitle.get('text') pageVisualsList = CQListWidgetItemReportPageVisuals(listDisplayText) pageVisualsList.setFlags(pageVisualsList.flags() | Qt.ItemIsUserCheckable) if visual['__selected'] is False: pageVisualsList.setCheckState(Qt.Unchecked) else: pageVisualsList.setCheckState(Qt.Checked) pageVisualsList.SetVisualProperties(visual, reportPageSection, visualIndex) # print(pageVisualsList.GetVisualProperties()) self._listWidgetReportPageVisuals.insertItem(i, pageVisualsList) i += 1 pushButtonSelectAll.clicked.connect(__selectDeselectAll) pushButtonDeselectAll.clicked.connect(__selectDeselectAll) # self._listWidgetReportPageVisuals.setCurrentRow(0) # __getVisualProperties(self._listWidgetReportPageVisuals.item(0)) except Exception as e: ShowErrorDialog(LogException(e)) def _createSelectedVisualPropertiesTree(self, visualProperties=None): try: def __getVisualProperty(item: CQTreeWidgetItemVisualProperties): try: visualProperties = item.GetVisualProperties() if visualProperties is not None: visualProperty = (self._reportVisualData[visualProperties['report_section']] .get('visuals')[visualProperties['visual_index']].get('objects') .get(str(item.data(0, 0)))) return visualProperty except Exception as e: ShowErrorDialog(LogException(e)) def __updateStateInDataStructure(item: CQTreeWidgetItemVisualProperties): try: visualProperty = __getVisualProperty(item) if visualProperty is not None: if item.checkState(0) == Qt.Unchecked: visualProperty['__selected'] = False else: visualProperty['__selected'] = True except Exception as e: ShowErrorDialog(LogException(e)) def __updateWildCardState(state, item=None): try: visualProperty = __getVisualProperty(item) if visualProperty is not None: if state == Qt.Checked: visualProperty['__wildcard'] = True # print(visualProperty) elif visualProperty.get('__wildcard') is not None and state == Qt.Unchecked: visualProperty.pop('__wildcard') except Exception as e: ShowErrorDialog(LogException(e)) def __treeWidgetVisualsPropertiesClicked(item: CQTreeWidgetItemVisualProperties): try: __updateStateInDataStructure(item) except Exception as e: ShowErrorDialog(LogException(e)) def __selectDeselectAll(): try: if self._treeWidgetSelectedVisualProperties is not None: for i in range(self._treeWidgetSelectedVisualProperties.topLevelItemCount()): item = self._treeWidgetSelectedVisualProperties.topLevelItem(i) if self.sender().objectName() == 'button_select_all': item.setCheckState(0, Qt.Checked) else: item.setCheckState(0, Qt.Unchecked) __updateStateInDataStructure(item) except Exception as e: ShowErrorDialog(LogException(e)) self._groupBoxSelectedVisualPropertiesTree = QGroupBox(self.centralWidget) self._groupBoxSelectedVisualPropertiesTree.setTitle("Selected Visual Properties") verticalLayoutSelectedVisualPropertiesTree = QVBoxLayout(self._groupBoxSelectedVisualPropertiesTree) horizontalLayoutSelectDeselectButton = QHBoxLayout() pushButtonSelectAll = QPushButton('Select All') pushButtonSelectAll.setObjectName('button_select_all') pushButtonDeselectAll = QPushButton('Deselect All') pushButtonDeselectAll.setObjectName('button_deselect_all') horizontalLayoutSelectDeselectButton.addWidget(pushButtonSelectAll) horizontalLayoutSelectDeselectButton.addWidget(pushButtonDeselectAll) verticalLayoutSelectedVisualPropertiesTree.addLayout(horizontalLayoutSelectDeselectButton) if self._treeWidgetSelectedVisualProperties is None: self._treeWidgetSelectedVisualProperties = QTreeWidget() verticalLayoutSelectedVisualPropertiesTree.addWidget(self._treeWidgetSelectedVisualProperties) self._verticalLayoutVisualsPropertiesTab.insertWidget(1, self._groupBoxSelectedVisualPropertiesTree) self._treeWidgetSelectedVisualProperties.itemClicked.connect(__treeWidgetVisualsPropertiesClicked) else: self._treeWidgetSelectedVisualProperties.clear() self._treeWidgetSelectedVisualProperties.setHeaderLabels(['Property', 'Value', 'Wild Card']) treeHeader = self._treeWidgetSelectedVisualProperties.header() treeHeader.setSectionResizeMode(QtWidgets.QHeaderView.ResizeToContents) # treeHeader.setStretchLastSection(False) visualObjects = visualProperties['visual_properties'].get('objects') for object, objectValues in visualObjects.items(): if len(objectValues.keys()) > 1: # greater than 1 because one default key is __selected parent = CQTreeWidgetItemVisualProperties(self._treeWidgetSelectedVisualProperties, [str(object)]) parent.SetVisualProperties(visualProperties) wildCardCheckBox = QCheckBox() wildCardCheckBox.stateChanged.connect(partial(__updateWildCardState, item=parent)) self._treeWidgetSelectedVisualProperties.setItemWidget(parent, 2, wildCardCheckBox) if objectValues['__selected'] is False: parent.setCheckState(0, Qt.Unchecked) else: parent.setCheckState(0, Qt.Checked) for objectKey, objectValue in objectValues.items(): if objectKey is not '__selected': mObjectV = None if type(objectValue) is dict and objectValue.get('solid') is not None: mObjectV = objectValue.get('solid').get('color') else: mObjectV = objectValue CQTreeWidgetItemVisualProperties(parent, [str(objectKey), str(mObjectV)]) else: objectValues['__selected'] = False pushButtonSelectAll.clicked.connect(__selectDeselectAll) pushButtonDeselectAll.clicked.connect(__selectDeselectAll) except Exception as e: ShowErrorDialog(LogException(e)) def generateTheme(self): try: correctVisualData = True correctWildcardData = True themeName = self._generalProperties.get('name') themeData = { 'name': themeName if themeName is not None and len(themeName.strip()) > 0 else 'My Theme', } if self._generalProperties.get('dataColors') is not None: dataColors = list(filter(None, self._generalProperties.get('dataColors'))) else: dataColors = [] background = self._generalProperties.get('background') foreground = self._generalProperties.get('foreground') tableAccent = self._generalProperties.get('tableAccent') if dataColors is not None and dataColors is not "": themeData['dataColors'] = dataColors if background is not None and background is not "": themeData['background'] = background if foreground is not None and foreground is not "": themeData['foreground'] = foreground if tableAccent is not None and tableAccent is not "": themeData['tableAccent'] = tableAccent selectedVisualsList = [[], [], []] # page name, visual type and visual data or visual objects wildCardPropertiesList = [[], [], []] # page name, property type and property data visualObjectIndex = 0 themeData['visualStyles'] = {} if self._pbiFilePath is not None: for reportPage in self._reportVisualData: for visual in self._reportVisualData[reportPage].get('visuals'): pageName = self._reportVisualData[reportPage]['reportPageDisplayName'] if visual['__selected'] is True: selectedVisualsList[0].append(pageName) selectedVisualsList[1].append(visual['visual_type']) selectedVisualsList[2].append({}) visualObjects = visual.get('objects') for object in visualObjects: if visualObjects[object]['__selected'] is True: selectedVisualsList[2][visualObjectIndex][object] = [visualObjects[object]] if visualObjects[object].get('__wildcard') is not None: # wildCardObjects[object] = [visualObjects[object]] wildCardPropertiesList[0].append(pageName) wildCardPropertiesList[1].append(object) wildCardPropertiesList[2].append(visualObjects[object]) visualObjectIndex += 1 if len(selectedVisualsList[1]) > 0: if len(selectedVisualsList[1]) != len(set(selectedVisualsList[1])): message = 'Multiple visual of same type selected from different report pages. See details below.' \ ' Please select one visual type for only once.' detailMessage = '' seen = set() repeatedVisuals = [] for selectedVisual in selectedVisualsList[1]: if selectedVisual in seen: repeatedVisuals.append(selectedVisual) else: seen.add(selectedVisual) for i, selectedVisual in enumerate(selectedVisualsList[1]): if selectedVisual in repeatedVisuals: detailMessage += selectedVisual + ' is selected in report page'\ + selectedVisualsList[0][i] + '\n' correctVisualData = False self._showDialogAfterThemeGeneration(message, detailMessage=detailMessage) else: # adding wild card data if len(wildCardPropertiesList[1]) > 0: if len(wildCardPropertiesList[1]) != len(set(wildCardPropertiesList[1])): message = 'Multiple wild card properties of same types are selected. Please only select '\ 'unique properties for wild card from all the visuals. As a result this' \ 'properties will not be added as wild card property in them file. ' \ 'See details below' detailMessage = '' seen = set() repeatedWildCardProperties = [] for wildCardProperty in wildCardPropertiesList[1]: if wildCardProperty in seen: repeatedWildCardProperties.append(wildCardProperty) else: seen.add(wildCardProperty) for i, wildCardProperty in enumerate(wildCardPropertiesList[1]): if wildCardProperty in repeatedWildCardProperties: detailMessage += wildCardProperty + 'is selected in report page' +\ wildCardPropertiesList[0][i] correctWildcardData = False self._showDialogAfterThemeGeneration(message, detailMessage=detailMessage) else: themeData['visualStyles']['*'] = { '*': {} } for i in range(len(wildCardPropertiesList[1])): themeData['visualStyles']['*']['*'][wildCardPropertiesList[1][i]] = [wildCardPropertiesList[2][i]] # adding visual data for i in range(len(selectedVisualsList[1])): themeData['visualStyles'][selectedVisualsList[1][i]] = { "*": selectedVisualsList[2][i] } if correctVisualData is True and correctWildcardData is True: self._saveThemeFile(themeData, 'C:/Users/bjadav/Desktop/', themeData['name']) except Exception as e: ShowErrorDialog(LogException(e)) def _showDialogAfterThemeGeneration(self, message, messageIcon=QMessageBox.Information, detailMessage=None): try: messageBoxThemeGeneration = QMessageBox() messageBoxThemeGeneration.setIcon(messageIcon) messageBoxThemeGeneration.setText(message) if detailMessage is not None: messageBoxThemeGeneration.setInformativeText(detailMessage) messageBoxThemeGeneration.setWindowTitle("Theme Generation Information") messageBoxThemeGeneration.setStandardButtons(QMessageBox.Ok) messageBoxThemeGeneration.exec_() except Exception as e: ShowErrorDialog(LogException(e)) def _saveThemeFile(self, themeData, saveFileDirectory, fileName): try: _themeData = json.loads(json.dumps(themeData)) # Removing internal keys such as __selected from final output for visualType, visualProperties in _themeData['visualStyles'].items(): for propertyType, propertyValue in visualProperties['*'].items(): if propertyValue[0].get('__selected') is not None: propertyValue[0].pop('__selected') if propertyValue[0].get('__wildcard') is not None: propertyValue[0].pop('__wildcard') initialSaveFilePath = saveFileDirectory + fileName + '.json' saveFile = QFileDialog.getSaveFileName(self, 'Save Theme File', initialSaveFilePath, filter='JSON file(*.json)')[0] if saveFile is not '': with open(saveFile, 'w') as themeFile: json.dump(_themeData, themeFile, indent=4) message = 'Successfully generated theme' self._showDialogAfterThemeGeneration(message) except Exception as e: ShowErrorDialog(LogException(e)) def _showAboutDialog(self): try: dialogAbout = QDialog() dialogAbout.resize(320, 180) dialogButtonBoxAbout = QDialogButtonBox(dialogAbout) dialogButtonBoxAbout.setLayoutDirection(Qt.LeftToRight) dialogButtonBoxAbout.setAutoFillBackground(False) dialogButtonBoxAbout.setOrientation(Qt.Horizontal) dialogButtonBoxAbout.setStandardButtons(QtWidgets.QDialogButtonBox.Ok) dialogButtonBoxAbout.setCenterButtons(True) dialogButtonBoxAbout.accepted.connect(dialogAbout.accept) verticalLayoutAboutDialog = QVBoxLayout(dialogAbout) dialogAbout.setWindowTitle('About ' + AppInfo.Name) labelPowerBIThemeGeneratorFont = QFont() labelPowerBIThemeGeneratorFont.setPointSize(16) labelPowerBIThemeGeneratorFont.setFamily('Calibri') labelPowerBIThemeGenerator = QLabel(AppInfo.Name) labelPowerBIThemeGenerator.setFont(labelPowerBIThemeGeneratorFont) labelVersionFont = QFont() labelVersionFont.setPointSize(8) labelVersion = QLabel(AppInfo.Version) labelVersion.setContentsMargins(0, 0, 0, 0) labelCreatedBy = QLabel('Created By ' + AppInfo.Author) link = '<a href="' + AppInfo.GitHubRepoIssuesURL + '">Click here to report bugs</a>' labelBugs = QLabel(link) labelBugs.setOpenExternalLinks(True) verticalLayoutAboutDialog.addWidget(labelPowerBIThemeGenerator) verticalLayoutAboutDialog.addWidget(labelVersion) verticalLayoutAboutDialog.addWidget(labelCreatedBy) verticalLayoutAboutDialog.addWidget(labelBugs) verticalLayoutAboutDialog.addStretch() verticalLayoutAboutDialog.addWidget(dialogButtonBoxAbout) dialogAbout.exec_() except Exception as e: ShowErrorDialog(LogException(e))
class CityListDlg(QDialog): citieslist_signal = pyqtSignal([list]) citiesdict_signal = pyqtSignal([dict]) def __init__(self, citylist, accurate_url, appid, trans_cities_dict, parent=None): super(CityListDlg, self).__init__(parent) self.settings = QSettings() self.citylist = citylist self.trans_cities_dict = trans_cities_dict self.accurate_url = accurate_url self.appid = appid self.listWidget = QListWidget() self.listWidget.itemDoubleClicked.connect(self.translate) cities_list = [] for i in self.citylist: cities_list.append(self.trans_cities_dict.get(i, i)) self.listWidget.addItems(cities_list) buttonLayout = QVBoxLayout() self.buttonBox = QDialogButtonBox() self.buttonBox.setOrientation(Qt.Horizontal) self.buttonBox.setStandardButtons(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) self.buttonBox.rejected.connect(self.reject) self.buttonBox.accepted.connect(self.accept) layoutT = QVBoxLayout() layout = QHBoxLayout() layout.addWidget(self.listWidget) layout.addLayout(buttonLayout) for text, slot in ((self.tr("&Add..."), self.add), (self.tr("&Remove..."), self.remove), (self.tr("&Up"), self.up), (self.tr("&Down"), self.down), (self.tr("De&fault"), self.default), (self.tr("&Sort"), self.listWidget.sortItems)): button = QPushButton(text) buttonLayout.addWidget(button) button.clicked.connect(slot) self.translate_button = QPushButton( QCoreApplication.translate('Button', '&Translate', 'Edit cities dialogue')) buttonLayout.addWidget(self.translate_button) self.translate_button.clicked.connect(self.translate) buttonLayout.addWidget(self.buttonBox) self.status = QLabel() layoutT.addLayout(layout) layoutT.addWidget(self.status) self.setLayout(layoutT) self.setWindowTitle( QCoreApplication.translate('Window title', 'Cities', 'Cities list dialogue')) self.checklength() def add(self): self.status.setText('') lista = [] newitem = '' self.citytoadd = '' self.countrytoadd = '' self._idtoadd = '' dialog = searchcity.SearchCity(self.accurate_url, self.appid, self) dialog.id_signal.connect(self.addcity) dialog.city_signal.connect(self.addcity) dialog.country_signal.connect(self.addcity) if dialog.exec_() == 1: newitem = (self.citytoadd + '_' + self.countrytoadd + '_' + self._idtoadd) for row in range(self.listWidget.count()): lista.append(self.listWidget.item(row).text()) if newitem in lista: self.status.setText( QCoreApplication.translate( 'Status bar message', 'The city already exists in the list', 'Cities list dialogue')) return else: self.listWidget.addItem(newitem) self.checklength() def addcity(self, what): self.status.setText('') if what[0] == 'ID': self._idtoadd = what[1] elif what[0] == 'City': self.citytoadd = what[1] elif what[0] == 'Country': self.countrytoadd = what[1] def remove(self): self.status.setText('') if self.listWidget.count() == 1: self.status.setText( QCoreApplication.translate( 'Message when trying to remove the last and unique city in the list', 'This is the default city !', 'Cities list dialogue')) return row = self.listWidget.currentRow() item = self.listWidget.item(row) if item is None: return message = self.tr('The city "{0}" has been removed').format( self.listWidget.item(row).text()) item = self.listWidget.takeItem(row) del item self.status.setText(message) def up(self): self.status.setText('') row = self.listWidget.currentRow() if row >= 1: item = self.listWidget.takeItem(row) self.listWidget.insertItem(row - 1, item) self.listWidget.setCurrentItem(item) def down(self): self.status.setText('') row = self.listWidget.currentRow() if row < self.listWidget.count() - 1: item = self.listWidget.takeItem(row) self.listWidget.insertItem(row + 1, item) self.listWidget.setCurrentItem(item) def default(self): self.status.setText('') row = self.listWidget.currentRow() if row >= 1: item = self.listWidget.takeItem(row) self.listWidget.insertItem(0, item) self.listWidget.setCurrentItem(item) def checklength(self): if self.listWidget.count() == 1: # After adding the first city the entry is not activated self.listWidget.setCurrentRow(0) if self.listWidget.count() > 0: self.translate_button.setEnabled(True) self.listWidget.setMinimumWidth( self.listWidget.sizeHintForColumn(0)) else: self.translate_button.setEnabled(False) def translate(self): city = self.listWidget.currentItem().text() dialog = citytranslate.CityTranslate(city, self.trans_cities_dict, self) dialog.city_signal.connect(self.current_translation) if dialog.exec_() == 1: row = self.listWidget.currentRow() item = self.listWidget.takeItem(row) del item self.listWidget.insertItem(row, self.current_translated_city) self.listWidget.setCurrentRow(row) def current_translation(self, translated_city): for city, translated in translated_city.items(): if translated == '': translated = city self.trans_cities_dict[city] = translated self.current_translated_city = translated def accept(self): listtosend = [] for row in range(self.listWidget.count()): city = self.find_city_key(self.listWidget.item(row).text()) listtosend.append(city) if self.listWidget.count() == 0: return self.citieslist_signal[list].emit(listtosend) self.citiesdict_signal[dict].emit(self.trans_cities_dict) QDialog.accept(self) def find_city_key(self, city): for key, value in self.trans_cities_dict.items(): if value == city: return key return city
class Ui(QWidget): def setupUi(self, Main): Main.setObjectName("Main") Main.setFixedSize(900, 500) self.width = 900 self.height = 500 self.setFixedSize(self.width, self.height) '''MENU ON THE MAIN WINDOW''' self.menu = QStackedLayout() self.mainMenu = QWidget() self.howToMenu = QWidget() self.mainMenuUi() self.howToMenuUi() self.menu.addWidget(self.mainMenu) self.menu.addWidget(self.howToMenu) '''MENU ON THE HOWTO WINDOW''' #self.howToMenuMenu = QStackedLayout() #self.howToOverView = QWidget() #self.howToLevel = QWidget() #self.howToTapeMeasure = QWidget() #self.howToTheodolite = QWidget() #self. overViewUi() #self. levelUi() #self.tapeMeasureUi() #self. theodoliteUi() #self.howToMenuMenu.addWidget(self.howToOverView ) #self.howToMenuMenu.addWidget(self.howToLevel ) #self.howToMenuMenu.addWidget(self.howToTapeMeasure) #self.howToMenuMenu.addWidget(self.howToTheodolite ) def mainMenuUi(self): self.mainMenu.setFixedSize(self.width, self.height) self.mainMenuText = QLabel(self.mainMenu) self.mainMenuText.setGeometry(QRect(30, 120, 480, 200)) self.mainMenuText.setStyleSheet("font: 14pt Century Gothic") self.mainMenuText.setAlignment(Qt.AlignLeading | Qt.AlignLeft | Qt.AlignTop) self.mainMenuText.setText( "Welcome to the Surveying Traverse Calculator!") self.howToButton = QPushButton("HOW TO DO A TRAVERSE", self.mainMenu) self.howToButton.setGeometry(140, 180, 200, 30) def howToMenuUi(self): self.howToMenu_layout = QGridLayout() self.howToMenu.setFixedSize(self.width, self.height) self.menuButton1 = QPushButton("Back to main menu") self.menuButton1.setGeometry(QRect(10, 10, 200, 30)) self.howToTitle = QLabel() self.howToTitle.setGeometry(QRect(10, 50, self.width, 40)) self.howToTitle.setStyleSheet("font: 14pt Century Gothic") self.howToTitle.setAlignment(Qt.AlignLeading | Qt.AlignLeft | Qt.AlignVCenter) self.howToTitle.setText("How to Do a Traverse") self.howToSteps = QListWidget() self.howToSteps.setGeometry(QRect(10, 100, 200, 80)) self.howToSteps.insertItem(0, "OVERVIEW") self.howToSteps.insertItem(1, "LEVEL") self.howToSteps.insertItem(2, "TAPE MEASURE") self.howToSteps.insertItem(3, "THEODOLITE") self.howToSteps.currentRowChanged.connect(self.display_traverse) self.overview_container = QWidget() self.level_container = QWidget() self.tape_measure_container = QWidget() self.theodolite_container = QWidget() self.overViewUi() self.levelUi() self.tapeMeasureUi() self.theodoliteUi() self.traverse_action = QStackedWidget() self.traverse_action.addWidget(self.overview_container) self.traverse_action.addWidget(self.level_container) self.traverse_action.addWidget(self.tape_measure_container) self.traverse_action.addWidget(self.theodolite_container) self.traverse_action.setCurrentIndex(0) self.howToMenu_left_layout = QVBoxLayout() self.howToMenu_left_layout.addWidget(self.menuButton1) self.howToMenu_left_layout.addWidget(self.howToTitle) self.howToMenu_left_layout.addWidget(self.howToSteps) self.howToMenu_layout.addLayout(self.howToMenu_left_layout, 0, 0, 1, 1) self.howToMenu_layout.addWidget(self.traverse_action, 0, 1, 1, 1) self.howToMenu.setLayout(self.howToMenu_layout) def overViewUi(self): self.overview_layout = QVBoxLayout() self.overview_button = QPushButton('Overview') self.overview_layout.addWidget(self.overview_button) self.overview_container.setLayout(self.overview_layout) def levelUi(self): self.level_layout = QVBoxLayout() self.level_button = QPushButton('Level') self.level_layout.addWidget(self.level_button) self.level_container.setLayout(self.level_layout) def tapeMeasureUi(self): self.tape_measure_layout = QVBoxLayout() self.tape_measure_button = QPushButton('Tape measure') self.tape_measure_layout.addWidget(self.tape_measure_button) self.tape_measure_container.setLayout(self.tape_measure_layout) def theodoliteUi(self): self.theodolite_layout = QVBoxLayout() self.theodolite_button = QPushButton('Theodolite') self.theodolite_layout.addWidget(self.theodolite_button) self.theodolite_container.setLayout(self.theodolite_layout) def display_traverse(self, index): self.traverse_action.setCurrentIndex(index)
class TabMain_gui(QWidget): def __init__(self, caller, *args, **kwargs): QWidget.__init__(self) self.caller = caller ###-- Initialize self.loadIcons() self.initGlobalButtons() self.initBody() ###-- Compose layout mainVertical = QVBoxLayout() mainVertical.setSpacing(10) mainVertical.addLayout(self.globalButtons) mainVertical.addWidget(self.body) ###-- Set Layout self.setLayout(mainVertical) def initGlobalButtons(self): ###-- Global Buttons globalButtons = QHBoxLayout() self.button_startAll = QPushButton("Start All Masternodes") globalButtons.addWidget(self.button_startAll) self.button_getAllStatus = QPushButton("Get Status of All Masternodes") globalButtons.addWidget(self.button_getAllStatus) self.button_sweepAllRewards = QPushButton("Sweep All Rewards") globalButtons.addWidget(self.button_sweepAllRewards) self.globalButtons = globalButtons def initBody(self): ###-- CENTRAL PART self.body = QGroupBox() self.body.setTitle("My Masternodes") # masternode list self.myList = QListWidget() self.myList.setUpdatesEnabled(True) self.myList.setDragDropMode(QAbstractItemView.InternalMove) self.myList.setDefaultDropAction(Qt.MoveAction) self.current_mn = {} self.mnLed = {} self.mnLabel = {} self.mnBalance = {} self.btn_details = {} self.mnStatusLabel = {} self.mnStatusProgress = {} self.btn_remove = {} self.btn_edit = {} self.btn_start = {} self.btn_rewards = {} for masternode in self.caller.masternode_list: name = masternode['name'] self.insert_mn_list(name, masternode['ip'], masternode['port'], isHardware=masternode['isHardware']) vBox = QVBoxLayout() vBox.addWidget(self.myList) self.button_addMasternode = QPushButton("New Masternode") vBox.addWidget(self.button_addMasternode) vBox.stretch(1) self.body.setLayout(vBox) def insert_mn_list(self, name, ip, port, row=None, isHardware=True): mnRow = QWidget() mnRow.alias = name if isHardware: mnRow.setToolTip("Drag rows to re-order.") else: mnRow.setToolTip("EXTERNAL MASTERNODE - Drag rows to re-order.") mnRowLayout = QHBoxLayout() ##--- Led self.mnLed[name] = QLabel() self.mnLed[name].setPixmap(self.caller.ledGrayV_icon) mnRowLayout.addWidget(self.mnLed[name]) ##--- Label & Balance self.mnLabel[name] = QLabel() if isHardware: self.mnLabel[name].setText("%s [<i>%s</i>]" % (name, ip)) else: self.mnLabel[name].setText( "<span style='color: orange'>%s</span> [<i>%s</i>]" % (name, ip)) mnRowLayout.addWidget(self.mnLabel[name]) self.mnBalance[name] = QLabel() mnRowLayout.addWidget(self.mnBalance[name]) self.mnBalance[name].hide() mnRowLayout.addStretch(1) ##--- Status Label self.mnStatusLabel[name] = QLabel() mnRowLayout.addWidget(self.mnStatusLabel[name]) self.mnStatusLabel[name].hide() ##--- Rank bar self.mnStatusProgress[name] = QProgressBar() self.mnStatusProgress[name].setMaximumHeight(15) self.mnStatusProgress[name].setMaximumWidth(40) self.mnStatusProgress[name].setTextVisible(False) mnRowLayout.addWidget(self.mnStatusProgress[name]) self.mnStatusProgress[name].hide() ##--- Details button self.btn_details[name] = QToolButton() self.btn_details[name].setIcon(self.details_icon) self.btn_details[name].setToolTip( 'Check status details of masternode "%s"' % name) mnRowLayout.addWidget(self.btn_details[name]) self.btn_details[name].hide() ##--- Rewards button self.btn_rewards[name] = QPushButton() self.btn_rewards[name].setToolTip('Transfer rewards from "%s"' % name) self.btn_rewards[name].setIcon(self.rewards_icon) self.btn_rewards[name].alias = name if not isHardware: self.btn_rewards[name].setDisabled(True) self.btn_rewards[name].setToolTip( "EXTERNAL MN: unable to move rewards with SPMT") mnRowLayout.addWidget(self.btn_rewards[name]) ##--- Start button self.btn_start[name] = QPushButton() self.btn_start[name].setToolTip('Start masternode "%s"' % name) self.btn_start[name].setIcon(self.startMN_icon) self.btn_start[name].alias = name if not isHardware: self.btn_start[name].setDisabled(True) self.btn_start[name].setToolTip( "EXTERNAL MN: unable to start with SPMT") mnRowLayout.addWidget(self.btn_start[name]) ##--- Edit button self.btn_edit[name] = QPushButton() self.btn_edit[name].setToolTip('Edit masternode "%s"' % name) self.btn_edit[name].setIcon(self.editMN_icon) self.btn_edit[name].alias = name if not isHardware: self.btn_edit[name].setDisabled(True) self.btn_edit[name].setToolTip( "EXTERNAL MN: to edit, delete entry and load new 'masternode.conf'" ) mnRowLayout.addWidget(self.btn_edit[name]) ##--- Remove button self.btn_remove[name] = QPushButton() self.btn_remove[name].setToolTip('Delete masternode "%s"' % name) self.btn_remove[name].setIcon(self.removeMN_icon) self.btn_remove[name].alias = name mnRowLayout.addWidget(self.btn_remove[name]) ##--- Three Dots threeDots = QLabel() threeDots.setPixmap( self.threeDots_icon.scaledToHeight(20, Qt.SmoothTransformation)) mnRowLayout.addWidget(threeDots) ##--- Set Row Layout mnRow.setLayout(mnRowLayout) ##--- Append Row self.current_mn[name] = QListWidgetItem() #self.current_mn[name].setFlags(Qt.ItemIsSelectable) self.current_mn[name].setSizeHint(mnRow.sizeHint()) if row is not None: self.myList.insertItem(row, self.current_mn[name]) else: self.myList.addItem(self.current_mn[name]) self.myList.setItemWidget(self.current_mn[name], mnRow) def loadIcons(self): self.removeMN_icon = QIcon( os.path.join(self.caller.imgDir, 'icon_delete.png')) self.editMN_icon = QIcon( os.path.join(self.caller.imgDir, 'icon_edit.png')) self.startMN_icon = QIcon( os.path.join(self.caller.imgDir, 'icon_rocket.png')) self.rewards_icon = QIcon( os.path.join(self.caller.imgDir, 'icon_money.png')) self.details_icon = QIcon( os.path.join(self.caller.imgDir, 'icon_search.png')) self.ledgerImg = QPixmap(os.path.join(self.caller.imgDir, 'ledger.png')) self.threeDots_icon = QPixmap( os.path.join(self.caller.imgDir, 'icon_3dots.png'))
class StartWindow(QWidget): def __init__(self, model=None): super().__init__() # zwraca klase rodzica i wywoluje jego konstruktor self.model = model self.interface() def interface(self): #1-liniowe pola edycyjne self.featuresEdt = QLineEdit() self.featuresEdt.setText("1") self.sizeSetEdt = QLineEdit() self.sizeSetEdt.setText("10") self.paramsEdt = QLineEdit() self.paramsEdt.setText("1") # przyciski self.addFeaturesBtn = QPushButton("&AddFeatures", self) self.createSetBtn = QPushButton("&Create", self) self.addParamsBtn = QPushButton("Add&Params", self) self.findParams = QPushButton("&FindParams", self) self.resetBtn = QPushButton("&Reset", self) self.addFeaturesBtn.clicked.connect(self.addFeaturesClick) self.createSetBtn.clicked.connect(self.createSetClick) self.addParamsBtn.clicked.connect(self.addParamsClick) self.findParams.clicked.connect(self.findParamsClick) self.resetBtn.clicked.connect(self.resetClick) # etykiety self.featuresLabel = QLabel("Features: ", self) self.setLabel = QLabel("Set: ", self) self.paramsLabel = QLabel("Params: ", self) self.matchingLabel = QLabel("Maching: ", self) # ListWidget self.featuresListWidget = QListWidget() self.setWidget = QListWidget() self.paramsListWidget = QListWidget() self.findParamsListWidget = QListWidget() # uklad tabelaryczny: self.tab_layout = QGridLayout() self.tab_layout.addWidget(self.featuresEdt, 0, 0) self.tab_layout.addWidget(self.sizeSetEdt, 0, 1) self.tab_layout.addWidget(self.paramsEdt, 0, 2) self.tab_layout.addWidget(self.resetBtn, 0, 3) self.tab_layout.addWidget(self.addFeaturesBtn, 1, 0) self.tab_layout.addWidget(self.createSetBtn, 1, 1) self.tab_layout.addWidget(self.addParamsBtn, 1, 2) self.tab_layout.addWidget(self.findParams, 1, 3) self.tab_layout.addWidget(self.featuresLabel, 2, 0) self.tab_layout.addWidget(self.setLabel, 2, 1) self.tab_layout.addWidget(self.paramsLabel, 2, 2) self.tab_layout.addWidget(self.matchingLabel, 2, 3) self.tab_layout.addWidget(self.featuresListWidget, 3, 0) self.tab_layout.addWidget(self.setWidget, 3, 1) self.tab_layout.addWidget(self.paramsListWidget, 3, 2) self.tab_layout.addWidget(self.findParamsListWidget, 3, 3) # przypisanie utworzonego ukladu do okna self.setLayout(self.tab_layout) self.showMaximized() self.setWindowTitle("Projekt - dopasowanie zbiorow") self.show() def addFeaturesClick(self): self.model.listoffeatures.addToList(self.featuresEdt.text()) #print(self.model.listoffeatures.list) l = len(self.model.listoffeatures.list) - 1 self.featuresListWidget.insertItem(l, self.model.listoffeatures.list[-1]) def createSetClick(self): self.setWidget.clear() self.model.set.createSet(int(self.sizeSetEdt.text()), model.listoffeatures.list) print(model.set.set) for a in model.set.set: self.setWidget.addItem(str(a)) def addParamsClick(self): self.model.listOfParams.addToList(self.paramsEdt.text()) # print(self.model.listoffeatures.list) l = len(self.model.listOfParams.list) - 1 self.paramsListWidget.insertItem(l, self.model.listOfParams.list[-1]) print(self.model.listOfParams.list) def findParamsClick(self): self.model.set.findParams(self.model.listOfParams.list) for i, a in enumerate(self.model.set.listofResults): self.findParamsListWidget.insertItem(i, str(a)) def resetClick(self): self.model.resetmodel() self.clearLayout(self.tab_layout) def clearLayout(self, layout): self.featuresListWidget.clear() self.setWidget.clear() self.paramsListWidget.clear() self.findParamsListWidget.clear()
class composite_gui(QMainWindow): def __init__(self): super(composite_gui, self).__init__() self.title = 'Shadow Compositing' self.left, self.top = 400, 400 self.width, self.height = 1720, 1080 self.cutout_count = 0 self.cur_ao_name = 'user_ao.png' self.init_ui() def init_ui(self): self.setWindowTitle(self.title) # self.setGeometry(self.left,self.top, self.width, self.height) # self.setFixedSize(self.width, self.height) self.setAcceptDrops(True) self.set_menu() # cutout layer self.cutout_layer = [] # shadow layer self.shadow_layer = [] # center widget wid = QWidget(self) self.setCentralWidget(wid) # canvas self.canvas = QLabel(self) self.canvas_img = np.ones((720, 680, 3)) self.set_img(self.to_qt_img(self.canvas_img), self.canvas) # self.read_img('imgs/x.jpg', self.canvas, (1024, 1024)) self.ibl = ibl_widget(self) self.light_list = QListWidget(self) self.light_list.itemClicked.connect(self.light_item_clicked) self.touch_widget = painter_widget(256, 256, self) # sliders self.shadow_intensity_label = QLabel('intensity', self) self.shadow_intensity_slider = QSlider(Qt.Horizontal) self.shadow_intensity_slider.valueChanged.connect( self.shadow_intensity_change) self.shadow_intensity_slider.setValue(99) self.size_label = QLabel('size', self) self.size_slider = QSlider(Qt.Horizontal) self.size_slider.valueChanged.connect(self.shadow_size_change) self.scale_label = QLabel('scale', self) self.scale_slider = QSlider(Qt.Horizontal) self.scale_slider.valueChanged.connect(self.shadow_scale_change) # buttons self.save_btn = QPushButton("save", self) self.save_btn.move(1300, self.ibl.pos().y() + self.ibl.height() + 10) self.save_btn.clicked.connect(self.save_result) # layouts self.canvas_group = QGroupBox("canvas", self) canvas_layout = QtWidgets.QHBoxLayout() canvas_layout.addWidget(self.canvas) self.canvas_group.setLayout(canvas_layout) control_group = QGroupBox('control', self) light_control_layout = QtWidgets.QVBoxLayout() widget_layout = QtWidgets.QHBoxLayout() widget_layout.addWidget(self.ibl) widget_layout.addWidget(self.touch_widget) light_control_layout.addLayout(widget_layout) light_control_layout.addWidget(self.light_list) light_control_layout.addWidget(self.shadow_intensity_label) light_control_layout.addWidget(self.shadow_intensity_slider) light_control_layout.addWidget(self.size_label) light_control_layout.addWidget(self.size_slider) light_control_layout.addWidget(self.scale_label) light_control_layout.addWidget(self.scale_slider) light_control_layout.addWidget(self.save_btn) control_group.setLayout(light_control_layout) grid = QGridLayout() grid.addWidget(self.canvas_group, 0, 0) grid.addWidget(control_group, 0, 1) wid.setLayout(grid) self.setFocusPolicy(Qt.StrongFocus) # init smooth mask h, w = 256, 256 mask, padding = np.zeros((h, w, 1)), 5 mask[padding:h - padding, padding:w - padding] = 1.0 self.soft_mask = cv2.GaussianBlur(mask, (padding * 4 + 1, padding * 4 + 1), 0) self.soft_mask = self.soft_mask[:, :, np.newaxis] self.init_state() self.show() def init_state(self): # self.add_light() self.show_shadow = True self.update_ao = True def set_menu(self): main_menu = self.menuBar() file_menu = main_menu.addMenu('File') load_canvas_button = QAction(QIcon(":/images/open.png"), 'load canvas', self) load_canvas_button.triggered.connect(self.load_canvas) load_cutout_button = QAction(QIcon(":/images/open.png"), 'load cutout', self) load_cutout_button.triggered.connect(self.load_cutout) file_menu.addAction(load_canvas_button) file_menu.addAction(load_cutout_button) def dragEnterEvent(self, e): e.accept() def dropEvent(self, e): e.setDropAction(Qt.MoveAction) e.accept() def dragMoveEvent(self, e): e.accept() self.cur_cutout.move(e.pos() - self.cur_cutout_offset) self.render_layers() """ Utilities """ def to_qt_img(self, np_img): if np_img.dtype != np.uint8: np_img = np.clip(np_img, 0.0, 1.0) np_img = np_img * 255.0 np_img = np_img.astype(np.uint8) h, w, c = np_img.shape # bytesPerLine = 3 * w return QImage(np_img.data, w, h, QImage.Format_RGB888) def read_img(self, file, label, size=None): if not os.path.exists(file): print('cannot find file', file) return img = plt.imread(file) if len(img.shape) == 2: img = np.repeat(img[:, :, np.newaxis], 3, axis=2) h, w, _ = img.shape self.canvas_img = img[:, :, :3] if size is not None: self.canvas_img = cv2.resize(img, (size[0], size[1])) if self.canvas_img.dtype == np.uint8: self.canvas_img = self.canvas_img / 255.0 # print('self canvas: {}, {}'.format(np.min(self.canvas_img), np.max(self.canvas_img))) self.set_img(self.to_qt_img(self.canvas_img.copy()), label) def set_img(self, img, label): pixmap = QPixmap(img) label.setPixmap(pixmap) w, h = img.width(), img.height() label.adjustSize() def load_file(self): dir_path = os.path.dirname(os.path.realpath(__file__)) # test_folder = '/home/ysheng/Documents/paper_project/adobe/failed_case/geenral_net' test_folder = '/home/ysheng/Documents/paper_project/adobe/soft_shadow/paper_demo/supplementary/videos/mask' fname = QFileDialog.getOpenFileName(self, 'Open file', os.path.join(test_folder)) self.cur_ao_name = os.path.basename(fname[0]) return fname[0] def add_cutout(self, filename): cutout_label = drag_img(self) cutout_label.set_id(self.cutout_count) self.cutout_count += 1 # self.read_img(filename, cutout_label) cutout_label.read_img(filename) cutout_label.show() self.cutout_layer.append(cutout_label) def get_cutout_label(self, id): for label in self.cutout_layer: if label.get_id() == id: return label print('cannot find the label') return None def set_cur_label(self, id, offset_pos): self.cur_cutout = self.get_cutout_label(id) self.cur_cutout_offset = offset_pos def composite_region(self, canvas_xy, canvas_wh, xy, wh): canvas_xxyy = [ canvas_xy[0] + canvas_wh[0], canvas_xy[1] + canvas_wh[1] ] xxyy = [xy[0] + wh[0], xy[1] + wh[1]] # com_xy, com_xxyy = canvas_xy, canvas_xxyy com_xy, com_xxyy = [canvas_xy[0], canvas_xy[1]], [canvas_xy[0], canvas_xy[1]] com_xy[0] = max(canvas_xy[0], xy[0]) com_xy[1] = max(canvas_xy[1], xy[1]) com_xxyy[0] = min(canvas_xxyy[0], xxyy[0]) com_xxyy[1] = min(canvas_xxyy[1], xxyy[1]) # print('canvas xy: {}, xy: {}'.format(canvas_xy, xy)) # print('canvas xxyy: {}, xxyy: {}'.format(canvas_xxyy, xxyy)) # print('com xy: {}, com xxyy: {}'.format(com_xy, com_xxyy)) canvas_region = (com_xy[1] - canvas_xy[1], com_xxyy[1] - canvas_xy[1], com_xy[0] - canvas_xy[0], com_xxyy[0] - canvas_xy[0]) widget_region = (com_xy[1] - xy[1], com_xxyy[1] - xy[1], com_xy[0] - xy[0], com_xxyy[0] - xy[0]) return canvas_region, widget_region def composite_layer_result(self, cur_canvas, xy, wh, composite_img, composite_operator='lerp'): """ input: canvas image, composite image top-left corner xy, width and height, ... outout: alpha blending compsite result """ tmp = cur_canvas cutout_img = composite_img canvas_region, widget_region = self.composite_region( [self.canvas.pos().x(), self.canvas.pos().y()], [self.canvas.width(), self.canvas.height()], xy, wh) # print('canvas region: {}, h: {}, w: {}, widget region: {}, h: {}, w: {}'.format(canvas_region, # canvas_region[1], # cur_canvas.shape[1], # widget_region, # wh[1], # wh[0])) if composite_operator == 'lerp': mask = cutout_img[widget_region[0]:widget_region[1], widget_region[2]:widget_region[3], 3:] mask = np.repeat(mask, 3, axis=2) tmp[canvas_region[0]:canvas_region[1], canvas_region[2]:canvas_region[3], :] = \ (1.0 - mask) * tmp[canvas_region[0]:canvas_region[1], canvas_region[2]:canvas_region[3], :] + \ mask * cutout_img[widget_region[0]:widget_region[1], widget_region[2]:widget_region[3], :3] else: tmp[canvas_region[0]:canvas_region[1], canvas_region[2]:canvas_region[3], :] = \ tmp[canvas_region[0]:canvas_region[1], canvas_region[2]:canvas_region[3],:] * composite_img[widget_region[0]:widget_region[1], widget_region[2]:widget_region[3],:] return tmp def render_cutout(self, cur_canvas): tmp = cur_canvas canvas_h, canvas_w, _ = tmp.shape # composite result with cutout for cutout in self.cutout_layer: xy = (cutout.pos().x(), cutout.pos().y()) wh = (cutout.width(), cutout.height()) tmp = self.composite_layer_result(tmp, xy, wh, cutout.get_img()) return tmp def render_shadow(self, cur_canvas): """ Render shadow to canvas """ if len(self.cutout_layer ) == 0 or not self.show_shadow or self.ibl.get_light_num() == 0: return cur_canvas # h x w ibl_np = self.ibl.get_ibl_numpy() ibl_np = ibl_np[:128, :] ibl_np = cv2.resize(ibl_np, (32, 16)) ibl_np = cv2.flip(ibl_np, 0) ibl_np = np.transpose( np.expand_dims(cv2.resize(ibl_np, (32, 16), cv2.INTER_LINEAR), axis=2), (2, 0, 1)) if np.sum(ibl_np) > 1e-3: ibl_np = ibl_np * 30.0 / np.sum(ibl_np) ibl_np = np.repeat(ibl_np[np.newaxis, :, :, :], len(self.cutout_layer), axis=0) # convert to predict format if self.update_ao: mask_input = [] for cutout in self.cutout_layer: cur_curout = cutout.get_img() # h x w x 4 cur_curout = cv2.resize(cur_curout, (256, 256)) cur_curout = cur_curout[:, :, -1:] cur_curout = np.transpose(cur_curout, (2, 0, 1)) mask_input.append(cur_curout) ao_pred = evaluation.net_pred_touch(np.array(mask_input), ibl_np) ao_pred = ao_pred[0] ao_pred = np.transpose(ao_pred, (1, 2, 0)) save_pred = ao_pred.copy() cv2.normalize(save_pred, save_pred, 0.0, 1.0, cv2.NORM_MINMAX) save_pred = np.repeat(save_pred, 3, axis=2) plt.imsave('AO_pred.png', save_pred) self.touch_widget.update_pred(save_pred) self.update_ao = False mask_input = [] for cutout in self.cutout_layer: cur_curout = cutout.get_img() # h x w x 4 cur_curout = cv2.resize(cur_curout, (256, 256)) cur_curout = cur_curout[:, :, -1] cur_touch = self.touch_widget.get_img() * 1.3 save_user_ao = self.touch_widget.get_img() plt.imsave( self.cur_ao_name, np.clip(np.repeat(save_user_ao[:, :, np.newaxis], 3, axis=2), 0.0, 1.0)) print('cur cutout: {}, cur touch: {} {} {}'.format( cur_curout.shape, cur_touch.shape, cur_touch.min(), cur_touch.max())) inputs = np.concatenate( (cur_curout[:, :, np.newaxis], cur_touch[:, :, np.newaxis]), axis=2) cur_curout = np.transpose(inputs, (2, 0, 1)) mask_input.append(cur_curout) # b x c x h x w mask_input = np.array(mask_input) shadow_pred = evaluation.net_render_np(mask_input, ibl_np) tmp = cur_canvas for i, shadow in enumerate(shadow_pred): shadow = np.transpose(shadow, (1, 2, 0)) # print('shadow shape: ', shadow.shape) cv2.normalize(shadow[:, :, 0], shadow[:, :, 0], 0.0, 1.0, cv2.NORM_MINMAX) plt.imsave("net_shadow.png", np.repeat(shadow, 3, axis=2)) h, w = shadow.shape[0], shadow.shape[1] shadow = self.soft_shadow_boundary( shadow) * self.cur_shadow_itensity_fract shadow_out = np.zeros((h, w, 3)) shadow_out[:, :, 0] = shadow_out[:, :, 1] = shadow_out[:, :, 2] = 1.0 - shadow[:, :, 0] shadow_out = cv2.resize( shadow_out, (self.cutout_layer[i].width(), self.cutout_layer[i].height())) # composite shadow with canvas xy, wh = (self.cutout_layer[i].pos().x(), self.cutout_layer[i].pos().y()), ( self.cutout_layer[i].width(), self.cutout_layer[i].height()) tmp = self.composite_layer_result(tmp, xy, wh, shadow_out, 'prod') return tmp def soft_shadow_boundary(self, shadow_img): return np.clip(np.multiply(self.soft_mask, shadow_img), 0.0, 1.0) def keyPressEvent(self, event): if event.key() == Qt.Key_A: print('Pressed A') self.add_light() if event.key() == Qt.Key_Space: self.show_shadow = self.show_shadow ^ True print('press space, toggle show shadow ', self.show_shadow) self.render_layers() if event.key() == Qt.Key_T: print("Pressed T") self.update_ao = True if event.key() == Qt.Key_C: print("Clear layers") self.cutout_count = 0 self.cutout_layer.clear() #################### Actions ############################## @pyqtSlot() def load_canvas(self): canvas_file = self.load_file() print('load file', canvas_file) self.read_img(canvas_file, self.canvas, (1024, 1024)) @pyqtSlot() def load_cutout(self): cutout_file = self.load_file() if not os.path.exists(cutout_file): return print('load file', cutout_file) self.add_cutout(cutout_file) self.render_layers() @pyqtSlot() def render_layers(self): if len(self.cutout_layer) == 0: return self.canvas_img.copy() # print('canvas shape: ', self.canvas_img.shape) shadow_canvas = self.render_shadow( self.canvas_img.copy()) # render to cutout layers shadow_canvas = self.render_cutout( shadow_canvas) # composite cutout with canvas self.set_img(self.to_qt_img(shadow_canvas), self.canvas) return shadow_canvas @pyqtSlot() def save_result(self): # get save file name dir_path = os.path.dirname(os.path.realpath(__file__)) save_fname = QFileDialog.getSaveFileName( self, 'Open file', os.path.join(dir_path, 'output')) print(save_fname) # layer results out_img = self.render_layers() if plt.imsave(save_fname[0], out_img): print('file {} saved succeed'.format(save_fname[0])) else: print('file {} save fail'.format(save_fname[0])) # shadow layers white_back = np.ones(self.canvas_img.shape) shadow_layer = self.render_shadow(white_back) shadow_out_fanme = 'shadow_' + os.path.basename(save_fname[0]) if plt.imsave( os.path.join(os.path.dirname(save_fname[0]), shadow_out_fanme), shadow_layer): print('file {} saved succeed'.format(save_fname[0])) else: print('file {} save fail'.format(save_fname[0])) def light_item_clicked(self, item): # self.ibl.set_cur_ibl() cur_ibl = self.light_list.currentRow() self.update_list(cur_ibl) @pyqtSlot() def add_light(self): self.ibl.add_light() self.update_list(self.ibl.get_light_num() - 1) @pyqtSlot() def update_list(self, cur_ibl): self.light_list.clear() light_num = self.ibl.get_light_num() for i in range(light_num): self.light_list.insertItem(i, 'light {}'.format(i)) self.light_list.setCurrentRow(cur_ibl) self.ibl.set_cur_light(cur_ibl) if cur_ibl >= 0: radius, scale = self.ibl.get_cur_light_state() self.set_slider_state(radius, scale) else: self.set_slider_state(0.008, 0) def set_slider_state(self, radius, scale): size_value = (radius - SIZE_MIN) / (1 - SIZE_MIN) * 99.0 self.size_slider.setValue(int(size_value)) scale_value = scale * 99.0 self.scale_slider.setValue(scale_value) @pyqtSlot() def shadow_intensity_change(self): self.cur_shadow_itensity_fract = self.shadow_intensity_slider.value( ) / 99.0 self.render_layers() @pyqtSlot() def shadow_scale_change(self): fract = self.scale_slider.value() / 99.0 self.ibl.set_cur_scale(fract) @pyqtSlot() def shadow_size_change(self): fract = self.size_slider.value() / 99.0 radius = (1.0 - fract) * SIZE_MIN + fract * SIZE_MAX self.ibl.set_cur_size(radius)
class StringListDlg(QDialog): def __init__(self, name, stringlist=None, parent=None): super(StringListDlg, self).__init__(parent) self.name = name self.listWidget = QListWidget() if stringlist is not None: self.listWidget.addItems(stringlist) self.listWidget.setCurrentRow(0) buttonLayout = QVBoxLayout() for text, slot in (("&Add...", self.add), ("&Edit...", self.edit), ("&Remove...", self.remove), ("&Up", self.up), ("&Down", self.down), ("&Sort", self.listWidget.sortItems), ("Close", self.accept)): button = QPushButton(text) if not MAC: button.setFocusPolicy(Qt.NoFocus) if text == "Close": buttonLayout.addStretch() buttonLayout.addWidget(button) button.clicked.connect(slot) # self.connect(button, SIGNAL("clicked()"), slot) layout = QHBoxLayout() layout.addWidget(self.listWidget) layout.addLayout(buttonLayout) self.setLayout(layout) self.setWindowTitle("Edit {0} List".format(self.name)) def add(self): row = self.listWidget.currentRow() title = "Add {0}".format(self.name) string, ok = QInputDialog.getText(self, title, title) if ok and string is not None: self.listWidget.insertItem(row, string) def edit(self): row = self.listWidget.currentRow() item = self.listWidget.item(row) if item is not None: title = "Edit {0}".format(self.name) string, ok = QInputDialog.getText(self, title, title, QLineEdit.Normal, item.text()) if ok and string is not None: item.setText(string) def remove(self): row = self.listWidget.currentRow() item = self.listWidget.item(row) if item is None: return reply = QMessageBox.question(self, "Remove {0}".format( self.name), "Remove {0} `{1}'?".format( self.name, unicode(item.text())), QMessageBox.Yes|QMessageBox.No) if reply == QMessageBox.Yes: item = self.listWidget.takeItem(row) del item def up(self): row = self.listWidget.currentRow() if row >= 1: item = self.listWidget.takeItem(row) self.listWidget.insertItem(row - 1, item) self.listWidget.setCurrentItem(item) def down(self): row = self.listWidget.currentRow() if row < self.listWidget.count() - 1: item = self.listWidget.takeItem(row) self.listWidget.insertItem(row + 1, item) self.listWidget.setCurrentItem(item) def reject(self): self.accept() def accept(self): self.stringlist = QStringListModel().stringList() for row in range(self.listWidget.count()): self.stringlist.append(self.listWidget.item(row).text()) #self.stringlist.acceptedList.emit(self.stringlist) QDialog.accept(self)
class CityListDlg(QDialog): citieslist_signal = pyqtSignal([list]) def __init__(self, citylist, accurate_url, appid, parent=None): super(CityListDlg, self).__init__(parent) self.citylist = citylist self.accurate_url = accurate_url self.appid = appid self.listWidget = QListWidget() self.listWidget.addItems(self.citylist) buttonLayout = QVBoxLayout() self.buttonBox = QDialogButtonBox() self.buttonBox.setOrientation(Qt.Horizontal) self.buttonBox.setStandardButtons(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) self.buttonBox.rejected.connect(self.reject) self.buttonBox.accepted.connect(self.accept) layoutT = QVBoxLayout() layout = QHBoxLayout() layout.addWidget(self.listWidget) layout.addLayout(buttonLayout) for text, slot in ((self.tr("&Add..."), self.add), (self.tr("&Remove..."), self.remove), (self.tr("&Up"), self.up), (self.tr("&Down"), self.down), (self.tr("De&fault"), self.default), (self.tr("&Sort"), self.listWidget.sortItems)): button = QPushButton(text) buttonLayout.addWidget(button) button.clicked.connect(slot) buttonLayout.addWidget(self.buttonBox) self.status = QLabel() layoutT.addLayout(layout) layoutT.addWidget(self.status) self.setLayout(layoutT) self.checklength() def add(self): self.status.setText('') lista = [] newitem = '' self.citytoadd = '' self.countrytoadd = '' self._idtoadd = '' dialog = searchcity.SearchCity(self.accurate_url, self.appid, self) dialog.id_signal.connect(self.addcity) dialog.city_signal.connect(self.addcity) dialog.country_signal.connect(self.addcity) if dialog.exec_() == 1: newitem = (self.citytoadd + '_' + self.countrytoadd + '_' + self._idtoadd) for row in range(self.listWidget.count()): lista.append(self.listWidget.item(row).text()) if newitem in lista: self.status.setText(QCoreApplication.translate('Status bar message', 'The city already exists in the list', 'Cities list dialogue')) return else: self.listWidget.addItem(newitem) self.checklength() def addcity(self, what): self.status.setText('') if what[0] == 'ID': self._idtoadd = what[1] elif what[0] == 'City': self.citytoadd = what[1] elif what[0] == 'Country': self.countrytoadd = what[1] def remove(self): self.status.setText('') if self.listWidget.count() == 0: self.status.setText(self.tr('The list is empty')) return row = self.listWidget.currentRow() item = self.listWidget.item(row) if item is None: return message = self.tr('The city "{0}" has been removed').format( self.listWidget.item(row).text()) item = self.listWidget.takeItem(row) del item self.status.setText(message) def up(self): self.status.setText('') row = self.listWidget.currentRow() if row >= 1: item = self.listWidget.takeItem(row) self.listWidget.insertItem(row - 1, item) self.listWidget.setCurrentItem(item) def down(self): self.status.setText('') row = self.listWidget.currentRow() if row < self.listWidget.count() - 1: item = self.listWidget.takeItem(row) self.listWidget.insertItem(row + 1, item) self.listWidget.setCurrentItem(item) def default(self): self.status.setText('') row = self.listWidget.currentRow() if row >= 1: item = self.listWidget.takeItem(row) self.listWidget.insertItem(0, item) self.listWidget.setCurrentItem(item) def checklength(self): listtosend = [] for row in range(self.listWidget.count()): listtosend.append(self.listWidget.item(row).text()) if len(listtosend) == 0: return self.listWidget.setMinimumWidth(self.listWidget.sizeHintForColumn(0)) def accept(self): listtosend = [] for row in range(self.listWidget.count()): listtosend.append(self.listWidget.item(row).text()) self.citieslist_signal[list].emit(listtosend) QDialog.accept(self)
class CityListDlg(QDialog): citieslist_signal = pyqtSignal([list]) citiesdict_signal = pyqtSignal([dict]) def __init__( self, citylist, accurate_url, appid, trans_cities_dict, parent=None ): super(CityListDlg, self).__init__(parent) self.settings = QSettings() self.citylist = citylist self.trans_cities_dict = trans_cities_dict self.accurate_url = accurate_url self.appid = appid self.listWidget = QListWidget() self.listWidget.itemDoubleClicked.connect(self.translate) cities_list = [] for i in self.citylist: cities_list.append(self.trans_cities_dict.get(i, i)) self.listWidget.addItems(cities_list) buttonLayout = QVBoxLayout() self.buttonBox = QDialogButtonBox() self.buttonBox.setOrientation(Qt.Horizontal) self.buttonBox.setStandardButtons( QDialogButtonBox.Ok | QDialogButtonBox.Cancel ) self.buttonBox.rejected.connect(self.reject) self.buttonBox.accepted.connect(self.accept) layoutT = QVBoxLayout() layout = QHBoxLayout() layout.addWidget(self.listWidget) layout.addLayout(buttonLayout) for text, slot in ((self.tr("&Add..."), self.add), (self.tr("&Remove..."), self.remove), (self.tr("&Up"), self.up), (self.tr("&Down"), self.down), (self.tr("De&fault"), self.default), (self.tr("&Sort"), self.listWidget.sortItems)): button = QPushButton(text) buttonLayout.addWidget(button) button.clicked.connect(slot) self.translate_button = QPushButton( QCoreApplication.translate( 'Button', '&Translate', 'Edit cities name' ) ) buttonLayout.addWidget(self.translate_button) self.translate_button.clicked.connect(self.translate) buttonLayout.addWidget(self.buttonBox) self.status = QLabel() layoutT.addLayout(layout) layoutT.addWidget(self.status) self.setLayout(layoutT) self.setWindowTitle( QCoreApplication.translate( 'Window title', 'Cities', 'Cities list dialogue' ) ) self.checklength() def add(self): self.status.setText('') lista = [] newitem = '' self.citytoadd = '' self.countrytoadd = '' self._idtoadd = '' dialog = searchcity.SearchCity(self.accurate_url, self.appid, self) dialog.id_signal.connect(self.addcity) dialog.city_signal.connect(self.addcity) dialog.country_signal.connect(self.addcity) if dialog.exec_() == 1: newitem = ( self.citytoadd + '_' + self.countrytoadd + '_' + self._idtoadd ) for row in range(self.listWidget.count()): lista.append(self.listWidget.item(row).text()) if newitem in lista: self.status.setText( QCoreApplication.translate( 'Status bar message', 'The city already exists in the list', 'Cities list dialogue' ) ) return else: self.listWidget.addItem(newitem) self.checklength() self.status.setText( 'ℹ ' + QCoreApplication.translate( 'Status bar message', 'Toggle cities with mouse scroll on the weather window', 'Cities list dialogue' ) ) def addcity(self, what): self.status.setText('') if what[0] == 'ID': self._idtoadd = what[1] elif what[0] == 'City': self.citytoadd = what[1] elif what[0] == 'Country': self.countrytoadd = what[1] def remove(self): self.status.setText('') if self.listWidget.count() == 1: self.status.setText( QCoreApplication.translate( 'Message when trying to remove the' 'last and unique city in the list', 'This is the default city !', 'Cities list dialogue' ) ) return row = self.listWidget.currentRow() item = self.listWidget.item(row) if item is None: return message = self.tr('The city "{0}" has been removed').format( self.listWidget.item(row).text()) item = self.listWidget.takeItem(row) del item self.status.setText(message) def up(self): self.status.setText('') row = self.listWidget.currentRow() if row >= 1: item = self.listWidget.takeItem(row) self.listWidget.insertItem(row - 1, item) self.listWidget.setCurrentItem(item) def down(self): self.status.setText('') row = self.listWidget.currentRow() if row < self.listWidget.count() - 1: item = self.listWidget.takeItem(row) self.listWidget.insertItem(row + 1, item) self.listWidget.setCurrentItem(item) def default(self): self.status.setText('') row = self.listWidget.currentRow() if row >= 1: item = self.listWidget.takeItem(row) self.listWidget.insertItem(0, item) self.listWidget.setCurrentItem(item) def checklength(self): if self.listWidget.count() == 1: # After adding the first city the entry is not activated self.listWidget.setCurrentRow(0) if self.listWidget.count() > 0: self.translate_button.setEnabled(True) self.listWidget.setMinimumWidth( self.listWidget.sizeHintForColumn(0) ) else: self.translate_button.setEnabled(False) def translate(self): city = self.listWidget.currentItem().text() dialog = citytranslate.CityTranslate( city, self.trans_cities_dict, self ) dialog.city_signal.connect(self.current_translation) if dialog.exec_() == 1: row = self.listWidget.currentRow() item = self.listWidget.takeItem(row) del item self.listWidget.insertItem(row, self.current_translated_city) self.listWidget.setCurrentRow(row) def current_translation(self, translated_city): for city, translated in translated_city.items(): if translated == '': translated = city self.trans_cities_dict[city] = translated self.current_translated_city = translated def accept(self): listtosend = [] for row in range(self.listWidget.count()): city = self.find_city_key(self.listWidget.item(row).text()) listtosend.append(city) if self.listWidget.count() == 0: return self.citieslist_signal[list].emit(listtosend) self.citiesdict_signal[dict].emit(self.trans_cities_dict) QDialog.accept(self) def find_city_key(self, city): for key, value in self.trans_cities_dict.items(): if value == city: return key return city