class settingPage(QDialog): settingSignal = Signal(dict) def __init__(self): super().__init__() self.settingDict = {'tableRefresh': 0, # 0: 开启, 1: 关闭 'tableRefreshFPS': 0, # 0: 60FPS, 1: 30FPS, 2: 20FPS, 3: 10FPS 'graphRefreshFPS': 1, # 0: 60FPS, 1: 30FPS, 2: 20FPS, 3: 10FPS } self.setWindowTitle('设置') self.resize(300, 200) layout = QGridLayout() self.setLayout(layout) if os.path.exists('config'): # 导入已存在的设置 with open('config', 'r') as cfg: for line in cfg: if '=' in line: try: cfgName, cfgValue = line.strip().replace(' ', '').split('=') self.settingDict[cfgName] = cfgValue except Exception as e: print(str(e)) self.settingSignal.emit(self.settingDict) # 发射默认配置给主界面 layout.addWidget(label('表格进度跟随鼠标'), 0, 0, 1, 1) self.tableRefreshCombox = QComboBox() self.tableRefreshCombox.addItems(['开启', '关闭']) self.tableRefreshCombox.setCurrentIndex(int(self.settingDict['tableRefresh'])) self.tableRefreshCombox.currentIndexChanged.connect(self.changeSetting) layout.addWidget(self.tableRefreshCombox, 0, 1, 1, 1) layout.addWidget(label('限制表格刷新率'), 1, 0, 1, 1) self.tableRefreshFPSCombox = QComboBox() self.tableRefreshFPSCombox.addItems(['60FPS (有点吃配置)', '30FPS (推荐)', '20FPS', '10FPS']) self.tableRefreshFPSCombox.setCurrentIndex(int(self.settingDict['tableRefreshFPS'])) self.tableRefreshFPSCombox.currentIndexChanged.connect(self.changeSetting) layout.addWidget(self.tableRefreshFPSCombox, 1, 1, 1, 1) layout.addWidget(label('限制波形图刷新率'), 2, 0, 1, 1) self.graphRefreshFPSCombox = QComboBox() self.graphRefreshFPSCombox.addItems(['60FPS (比较吃配置)', '30FPS (推荐)', '20FPS', '10FPS']) self.graphRefreshFPSCombox.setCurrentIndex(int(self.settingDict['graphRefreshFPS'])) self.graphRefreshFPSCombox.currentIndexChanged.connect(self.changeSetting) layout.addWidget(self.graphRefreshFPSCombox, 2, 1, 1, 1) def changeSetting(self): self.settingDict['tableRefresh'] = self.tableRefreshCombox.currentIndex() self.settingDict['tableRefreshFPS'] = self.tableRefreshFPSCombox.currentIndex() self.settingDict['graphRefreshFPS'] = self.graphRefreshFPSCombox.currentIndex() try: with open('config', 'w') as cfg: # 尝试更新配置文件 for cfgName, cfgValue in self.settingDict.items(): cfg.write('%s=%s\n' % (cfgName, cfgValue)) except Exception as e: print(str(e)) self.settingSignal.emit(self.settingDict) # 发射修改后的配置参数给主界面
class NameEditDialog(QDialog): sig_name_edit = Signal(str, int) def __init__(self): super().__init__() self.cmb_option = QComboBox(self) self.cmb_option.addItems(['접두', '접미']) self.cmb_option.setStyleSheet('font: 10pt \'맑은 고딕\'') self.cmb_option.setMinimumWidth(70) self.cmb_option.setMaximumWidth(100) self.line_text = NPLine() self.line_text.setStyleSheet('font: 10pt \'맑은 고딕\'') self.btn_ok = NPButton('실행', 10, self) self.btn_ok.clicked.connect(self.emit_sig_name_edit) box_h = QHBoxLayout() box_h.addWidget(self.line_text) box_h.addWidget(self.cmb_option) box_h.addWidget(self.btn_ok) box_h.setStretchFactor(self.cmb_option, 1) box_h.setStretchFactor(self.line_text, 4) box_h.setStretchFactor(self.btn_ok, 1) # box_h.setContentsMargins(3, 3, 3, 3) self.setLayout(box_h) self.setWindowTitle('선택한 이미지 파일 표제어 일괄 변경') self.setWindowIcon(QIcon('icon.png')) self.setWindowFlag(Qt.WindowContextHelpButtonHint, False) # self.resize(300, 40) def emit_sig_name_edit(self): self.sig_name_edit.emit(self.line_text.text(), self.cmb_option.currentIndex())
class MyDialog(QDialog): def __init__(self, parent=None): super().__init__() self.resize(400, 50) self.engine = 'dot' self.comboBox = QComboBox(self) self.engines = [('dot', 'for drawing directed graphs'), ('neato', 'for drawing undirected graphs'), ('twopi', 'for radial layouts of graphs'), ('circo', 'for circular layout of graphs'), ('fdp', 'for drawing undirected graphs'), ('sfdp', 'for drawing large undirected graphs'), ('patchwork', 'for squarified tree maps'), ('osage', 'for array-based layouts')] for name, desc in self.engines: self.comboBox.addItem(name) self.comboBox.activated[str].connect(self.my_method) def my_method(self, engine_name): index = self.comboBox.currentIndex() self.setWindowTitle(self.engines[index][1]) self.engine = engine_name print(self.engine)
class ThresholdingGui(QWidget): image: np.ndarray titles = ['Original Image', 'THRESH_BINARY + cv2.THRESH_OTSU'] def __init__(self): super(ThresholdingGui, self).__init__() self.setWindowTitle('Otsu\'s Thresholding') open_image_btn = QPushButton('Open Image', self) open_image_btn.clicked.connect(self.open_image) self.method_combobox = QComboBox() for title in self.titles: self.method_combobox.addItem(title) self.method_combobox.currentIndexChanged.connect(self.update_preview) self.threshold_label = QLabel('Threshold calculated: -') self.image_label = QLabel() self.image = np.tile( np.arange(256, dtype=np.uint8).repeat(2), (512, 1)) q_img = QImage(self.image.data, 512, 512, 512, QImage.Format_Indexed8) self.image_label.setPixmap(QPixmap.fromImage(q_img)) # Create layout and add widgets layout = QVBoxLayout() layout.addWidget(open_image_btn) layout.addWidget(self.method_combobox) layout.addWidget(self.threshold_label) layout.addWidget(self.image_label) # Set dialog layout self.setLayout(layout) def open_image(self): image_path, _ = QFileDialog.getOpenFileName( self, "Load Image", filter="Image Files (*.tiff *.png *.jpeg *.jpg *.bmp)") if image_path: self.image = cv2.imread(image_path, 0) self.update_preview() def update_preview(self): method_idx = self.method_combobox.currentIndex() if method_idx == 0: ret, th = '-', self.image elif method_idx == 1: ret, th = cv2.threshold(self.image, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) self.threshold_label.setText(f"Threshold calculated: {ret}") image_h, image_w = th.shape q_img = QImage(th.data, image_w, image_h, image_w, QImage.Format_Indexed8) self.image_label.setPixmap(QPixmap.fromImage(q_img))
def setModelData(self, editor: QComboBox, model: QAbstractItemModel, index: QModelIndex) -> None: selectedIndex: int = editor.currentIndex() mi = QPersistentModelIndex() if selectedIndex is not None and selectedIndex >= 0: mi = QPersistentModelIndex( self.__timeLabelModel.index(selectedIndex)) model.setData(index, mi, Qt.EditRole)
class MyWindow(QWidget): def __init__(self): super().__init__() vbox1 = QVBoxLayout() self.my_lineedit = QLineEdit("What type of photo are you looking for?") self.my_lineedit.setMinimumWidth(250) self.my_lineedit.selectAll() self.my_btn = QPushButton("Submit") self.my_lbl = QLabel('') self.my_btn.clicked.connect(self.on_submit) self.my_lineedit.returnPressed.connect(self.on_submit) vbox1.addWidget(self.my_lineedit) vbox1.addWidget(self.my_btn) vbox1.addWidget(self.my_lbl) self.setLayout(vbox1) gbox1 = QGroupBox('Photo Search') gbox1.setLayout(vbox1) self.my_list = [ "Pick a filter", 'None', 'Sepia', 'Negative', 'Grayscale', 'Thumbnail' ] # 0 1 2 3 4 5 self.my_combo_box = QComboBox() self.my_combo_box.addItems(self.my_list) self.my_label = QLabel("") vbox2 = QVBoxLayout() vbox2.addWidget(self.my_combo_box) vbox2.addWidget(self.my_label) self.setLayout(vbox2) self.my_combo_box.currentIndexChanged.connect(self.update_ui) gbox2 = QGroupBox("Filters") gbox2.setLayout(vbox2) mbox = QVBoxLayout() mbox.addWidget(gbox1) mbox.addWidget(gbox2) self.setLayout(mbox) self.setWindowTitle("CST 205 App") @Slot() def on_submit(self): your_photo = self.my_lineedit.text() self.my_lbl.setText(f'Your photo is {your_photo}') @Slot() def update_ui(self): my_text = self.my_combo_box.currentText() my_index = self.my_combo_box.currentIndex() self.my_label.setText(f'You chose {self.my_list[my_index]}.')
class MainWindow(QMainWindow): def __init__(self): super(MainWindow, self).__init__() centralWidget = QWidget() self.setCentralWidget(centralWidget) layout = QFormLayout(centralWidget) textLayout = QHBoxLayout() self.text = QLineEdit('Hello, PySide2') self.text.setClearButtonEnabled(True) textLayout.addWidget(self.text) self.sayButton = QPushButton('Say') textLayout.addWidget(self.sayButton) self.text.returnPressed.connect(self.sayButton.animateClick) self.sayButton.clicked.connect(self.say) layout.addRow('Text:', textLayout) self.voiceCombo = QComboBox() layout.addRow('Voice:', self.voiceCombo) self.volumeSlider = QSlider(Qt.Horizontal) self.volumeSlider.setMinimum(0) self.volumeSlider.setMaximum(100) self.volumeSlider.setValue(100) layout.addRow('Volume:', self.volumeSlider) self.engine = None engineNames = QTextToSpeech.availableEngines() if len(engineNames) > 0: engineName = engineNames[0] self.engine = QTextToSpeech(engineName) self.engine.stateChanged.connect(self.stateChanged) self.setWindowTitle( 'QTextToSpeech Example ({})'.format(engineName)) self.voices = [] for voice in self.engine.availableVoices(): self.voices.append(voice) self.voiceCombo.addItem(voice.name()) else: self.setWindowTitle('QTextToSpeech Example (no engines available)') self.sayButton.setEnabled(False) def say(self): self.sayButton.setEnabled(False) self.engine.setVoice(self.voices[self.voiceCombo.currentIndex()]) self.engine.setVolume(float(self.volumeSlider.value()) / 100) self.engine.say(self.text.text()) def stateChanged(self, state): if (state == QTextToSpeech.State.Ready): self.sayButton.setEnabled(True)
class StateDialog(QDialog): def __init__(self, parent=None): super().__init__(parent) self.setWindowTitle('State Editor') vbox = QVBoxLayout(self) layout = QGridLayout() self._line_name = QLineEdit() layout.addWidget(QLabel('Name'), 0, 0) layout.addWidget(self._line_name, 0, 1) self._combo_state_type = QComboBox() self._combo_state_type.addItems(["Normal", "Final", "Initial"]) layout.addWidget(QLabel('Type'), 1, 0) layout.addWidget(self._combo_state_type, 1, 1) vbox.addLayout(layout) vbox.addWidget(QLabel('Comment')) self._comment = QTextEdit() vbox.addWidget(self._comment) hbox = QHBoxLayout() hbox.addStretch(1) btn_save = QPushButton('Save') hbox.addWidget(btn_save) btn_cancel = QPushButton('Cancel') hbox.addWidget(btn_cancel) vbox.addLayout(hbox) btn_save.clicked.connect(self.accept) btn_cancel.clicked.connect(self.reject) def get_name(self): return self._line_name.text().strip() def get_comment(self): return self._comment.toPlainText().strip() def get_state_type(self): states_map = { 0: StateType.NORMAL, 1: StateType.FINAL, 2: StateType.INITIAL } return states_map.get(self._combo_state_type.currentIndex())
def _test_dropdown(dropdown: QComboBox, level: Level, level_attr: str, expected_change, index_change=+1): # WHEN the combobox has been changed using the header editor old_index = dropdown.currentIndex() old_attr_value = getattr(level, level_attr) new_index = old_index + index_change assert new_index > 0 dropdown.setCurrentIndex(new_index) dropdown.activated.emit(new_index) new_attr_value = getattr(level, level_attr) # THEN the level attribute should have changed assert new_attr_value == old_attr_value + expected_change
class NewFileDialog(QDialog): def __init__(self): super(NewFileDialog, self).__init__() self.comboBox = QComboBox() self.comboBox.addItem("Assembly file") self.comboBox.addItem("C file") self.lineEdit = QLineEdit() self.lineEdit.setPlaceholderText("Enter file name...") self.setStyleSheet("background-color: #2D2D30; color: white;") self.setWindowIcon(QIcon(main.resource_path("resources/app_icon.ico"))) self.hbox = QHBoxLayout() self.vbox = QVBoxLayout() self.create = QPushButton("Create") self.cancel = QPushButton("Cancel") self.setWindowTitle("New file") self.result = None self.initGUI() def initGUI(self): self.vbox.addWidget(self.lineEdit) self.vbox.addWidget(self.comboBox) self.hbox.addWidget(self.create) self.hbox.addWidget(self.cancel) self.create.setDefault(True) self.vbox.addLayout(self.hbox) self.setLayout(self.vbox) self.setFixedSize(self.sizeHint()) self.create.clicked.connect(self.createButtonClicked) self.cancel.clicked.connect(self.cancelButtonClicked) def createButtonClicked(self): extension = ".S" if self.comboBox.currentIndex() == 0 else ".c" self.result = "{}{}".format(self.lineEdit.text().strip(), extension) self.close() def cancelButtonClicked(self): self.result = None self.close()
def saveAndCloseSettings(modeSelector: QtWidgets.QComboBox, plainAppearance: QtWidgets.QCheckBox, algorithmSelector: QtWidgets.QComboBox, settingsWindow, levelSelector: QtWidgets.QComboBox, create_subfolder: QtWidgets.QCheckBox, parent, autoUpdate: QtWidgets.QCheckBox): global settings, forceClose if (algorithmSelector.currentIndex() == 0): settings['default_algorithm'] = "Deflated" elif (algorithmSelector.currentIndex() == 1): settings['default_algorithm'] = "BZIP2" elif (algorithmSelector.currentIndex() == 2): settings['default_algorithm'] = "LZMA" else: settings['default_algorithm'] = "Without Compression" settings["create_subdir"] = create_subfolder.isChecked() if (modeSelector.currentIndex() == 0): settings['mode'] = 'light' elif (modeSelector.currentIndex() == 1): settings['mode'] = 'dark' else: settings['mode'] = 'auto' settings["plainAppearance"] = plainAppearance.isChecked() settings["autoCheckForUpdates"] = autoUpdate.isChecked() parent.loadStyleSheet() settings["default_level"] = levelSelector.currentIndex() + 1 forceClose = True settingsWindow.close() saveSettings(silent=True, create_subdir=settings['create_subdir'], default_level=settings['default_level'], default_algorithm=settings['default_algorithm'], mode=settings['mode'], autoCheckForUpdates=settings["autoCheckForUpdates"])
class NGL_HKLViewer(QWidget): def __init__(self, parent=None): super(NGL_HKLViewer, self).__init__(parent) self.verbose = 0 self.UseOSbrowser = False self.jscriptfname = "" self.devmode = False for e in sys.argv: if "verbose" in e: self.verbose = e.split("verbose=")[1] if "UseOSbrowser" in e: self.UseOSbrowser = e.split("UseOSbrowser=")[1] if "jscriptfname" in e: self.jscriptfname = e.split("jscriptfname=")[1] if "devmode" in e: self.devmode = True self.zmq_context = None self.bufsize = 20000 self.originalPalette = QApplication.palette() self.openFileNameButton = QPushButton("Load reflection file") self.openFileNameButton.setDefault(True) self.openFileNameButton.clicked.connect(self.OpenReflectionsFile) self.debugbutton = QPushButton("Debug") self.debugbutton.clicked.connect(self.DebugInteractively) self.settingsbtn = QPushButton("Settings") self.settingsbtn.clicked.connect(self.SettingsDialog) self.mousemoveslider = QSlider(Qt.Horizontal) self.mousemoveslider.setMinimum(0) self.mousemoveslider.setMaximum(300) self.mousemoveslider.setValue(0) self.mousemoveslider.sliderReleased.connect( self.onFinalMouseSensitivity) self.mousemoveslider.valueChanged.connect(self.onMouseSensitivity) self.mousesensitxtbox = QLineEdit('') self.mousesensitxtbox.setReadOnly(True) self.fontspinBox = QDoubleSpinBox() self.fontspinBox.setSingleStep(1) self.fontspinBox.setRange(4, 50) self.font = QFont() self.font.setFamily(self.font.defaultFamily()) self.fontspinBox.setValue(self.font.pointSize()) #self.fontspinBox.setValue(self.font.pixelSize()) self.fontspinBox.valueChanged.connect(self.onFontsizeChanged) self.Fontsize_labeltxt = QLabel() self.Fontsize_labeltxt.setText("Font size:") self.cameraPerspectCheckBox = QCheckBox() self.cameraPerspectCheckBox.setText("Perspective camera") self.cameraPerspectCheckBox.clicked.connect(self.onCameraPerspect) self.cameraPerspectCheckBox.setCheckState(Qt.Unchecked) self.settingsform = SettingsForm(self) self.MillerComboBox = QComboBox() self.MillerComboBox.activated.connect(self.onMillerComboSelchange) #self.MillerComboBox.setSizeAdjustPolicy(QComboBox.AdjustToContents) self.MillerLabel = QLabel() self.MillerLabel.setText("Selected HKL Scene") self.HKLnameedit = QLineEdit('') self.HKLnameedit.setReadOnly(True) self.textInfo = QTextEdit() self.textInfo.setLineWrapMode(QTextEdit.NoWrap) self.textInfo.setReadOnly(True) labels = [ "Label", "Type", "no. of HKLs", "Span of HKLs", "Min Max data", "Min Max sigmas", "d_min, d_max", "Symmetry unique", "Anomalous" ] self.millertable = QTableWidget(0, len(labels)) self.millertable.setHorizontalHeaderLabels(labels) self.millertable.horizontalHeader().setDefaultAlignment(Qt.AlignLeft) # don't allow editing this table self.millertable.setEditTriggers(QTableWidget.NoEditTriggers) self.createExpansionBox() self.createFileInfoBox() self.CreateSliceTabs() self.createRadiiScaleGroupBox() self.createBinsBox() self.CreateFunctionTabs() mainLayout = QGridLayout() mainLayout.addWidget(self.FileInfoBox, 0, 0) mainLayout.addWidget(self.MillerLabel, 1, 0) mainLayout.addWidget(self.MillerComboBox, 2, 0) mainLayout.addWidget(self.functionTabWidget, 3, 0) mainLayout.addWidget(self.settingsbtn, 4, 0, 1, 1) #import code, traceback; code.interact(local=locals(), banner="".join( traceback.format_stack(limit=10) ) ) if self.UseOSbrowser == False: self.BrowserBox = QWebEngineView() mainLayout.addWidget(self.BrowserBox, 0, 1, 5, 3) self.BrowserBox.setUrl("https://cctbx.github.io/") #self.BrowserBox.setUrl("https://webglreport.com/") #self.BrowserBox.loadFinished.connect(self.onLoadFinished) mainLayout.setColumnStretch(2, 1) mainLayout.setRowStretch(0, 1) mainLayout.setRowStretch(1, 0) mainLayout.setRowStretch(2, 1) mainLayout.setRowStretch(3, 1) mainLayout.setColumnStretch(4, 0) self.setLayout(mainLayout) self.setWindowTitle("HKL-Viewer") self.cctbxproc = None self.LaunchCCTBXPython() self.out = None self.err = None self.comboviewwidth = 0 self.hklscenes_arrays = [] self.array_infotpls = [] self.matching_arrays = [] self.bin_infotpls = None self.bin_opacities = None self.html_url = "" self.spacegroups = [] self.info = [] self.infostr = "" self.fileisvalid = False self.NewFileLoaded = False self.NewHKLscenes = False self.updatingNbins = False self.binstableitemchanges = False self.show() def SettingsDialog(self): self.settingsform.show() def update(self): if self.cctbxproc: if self.cctbxproc.stdout: print(self.cctbxproc.stdout.read().decode("utf-8")) if self.cctbxproc.stderr: print(self.cctbxproc.stderr.read().decode("utf-8")) if self.out: print(self.out.decode("utf-8")) if self.err: print(self.err.decode("utf-8")) if self.zmq_context: try: msg = self.socket.recv( flags=zmq.NOBLOCK ) #To empty the socket from previous messages msgstr = msg.decode() self.infodict = eval(msgstr) #print("received from cctbx: " + str(self.infodict)) if self.infodict: if self.infodict.get("hklscenes_arrays"): self.hklscenes_arrays = self.infodict.get( "hklscenes_arrays", []) if self.infodict.get("array_infotpls"): self.array_infotpls = self.infodict.get( "array_infotpls", []) if self.infodict.get("bin_data_label"): self.BinDataComboBox.setCurrentText( self.infodict["bin_data_label"]) if self.infodict.get("bin_infotpls"): self.bin_infotpls = self.infodict["bin_infotpls"] self.nbins = len(self.bin_infotpls) self.updatingNbins = True self.Nbins_spinBox.setValue(self.nbins) self.updatingNbins = False self.binstable.clearContents() self.binstable.setRowCount(self.nbins) for row, bin_infotpl in enumerate(self.bin_infotpls): for col, elm in enumerate(bin_infotpl): # only allow changing the last column with opacity values if col != 3: item = QTableWidgetItem(str(elm)) else: item = QTableWidgetItem() item.setFlags(Qt.ItemIsUserCheckable | Qt.ItemIsEnabled) item.setCheckState(Qt.Checked) item.setFlags(item.flags() ^ Qt.ItemIsEditable) self.binstable.setItem(row, col, item) if self.bin_opacities: self.update_table_opacities() if self.infodict.get("bin_opacities"): self.bin_opacities = self.infodict["bin_opacities"] if self.binstable.rowCount() > 0: self.update_table_opacities() if self.infodict.get("html_url"): self.html_url = self.infodict["html_url"] if self.UseOSbrowser == False: self.BrowserBox.setUrl(self.html_url) # workaround for background colour bug in chromium # https://bugreports.qt.io/browse/QTBUG-41960 self.BrowserBox.page().setBackgroundColor( QColor(100, 100, 100, 1.0)) if self.infodict.get("spacegroups"): self.spacegroups = self.infodict.get("spacegroups", []) self.SpaceGroupComboBox.clear() self.SpaceGroupComboBox.addItems(self.spacegroups) if self.infodict.get("merge_data"): self.mergedata = self.infodict["merge_data"] currentinfostr = "" if self.infodict.get("info"): currentinfostr = self.infodict.get("info", []) if self.infodict.get("NewFileLoaded"): self.NewFileLoaded = self.infodict.get( "NewFileLoaded", False) if self.infodict.get("NewHKLscenes"): self.NewHKLscenes = self.infodict.get( "NewHKLscenes", False) self.fileisvalid = True #print("ngl_hkl_infodict: " + str(ngl_hkl_infodict)) if currentinfostr: #print(currentinfostr) self.infostr += currentinfostr + "\n" # display no more than self.bufsize bytes of text self.infostr = self.infostr[-self.bufsize:] self.textInfo.setPlainText(self.infostr) self.textInfo.verticalScrollBar().setValue( self.textInfo.verticalScrollBar().maximum()) if self.NewFileLoaded and self.NewHKLscenes: #if self.mergedata == True : val = Qt.CheckState.Checked #if self.mergedata == None : val = Qt.CheckState.PartiallyChecked #if self.mergedata == False : val = Qt.CheckState.Unchecked #self.mergecheckbox.setCheckState(val ) #print("got hklscenes: " + str(self.hklscenes_arrays)) self.MillerComboBox.clear() self.MillerComboBox.addItems( [e[3] for e in self.hklscenes_arrays]) self.MillerComboBox.setCurrentIndex( -1) # unselect the first item in the list self.comboviewwidth = 0 for e in self.hklscenes_arrays: self.comboviewwidth = max( self.comboviewwidth, self.MillerComboBox.fontMetrics().width(e[3])) self.MillerComboBox.view().setMinimumWidth( self.comboviewwidth) self.millertable.clearContents() self.millertable.setRowCount(len( self.hklscenes_arrays)) for n, millarr in enumerate(self.array_infotpls): for m, elm in enumerate(millarr): self.millertable.setItem( n, m, QTableWidgetItem(str(elm))) self.functionTabWidget.setDisabled(True) self.NewFileLoaded = False if self.NewHKLscenes: self.BinDataComboBox.clear() self.BinDataComboBox.addItems( ["Resolution"] + [e[3] for e in self.hklscenes_arrays]) self.BinDataComboBox.view().setMinimumWidth( self.comboviewwidth) #self.BinDataComboBox.setCurrentIndex(-1) # unselect the first item in the list self.NewHKLscenes = False except Exception as e: errmsg = str(e) if "Resource temporarily unavailable" not in errmsg: print(errmsg + traceback.format_exc(limit=10)) pass def onFinalMouseSensitivity(self): val = self.mousemoveslider.value() / 100.0 self.NGL_HKL_command( 'NGL_HKLviewer.viewer.NGL.mouse_sensitivity = %f' % val) def onMouseSensitivity(self): val = self.mousemoveslider.value() / 100.0 self.mousesensitxtbox.setText("%2.2f" % val) def onFontsizeChanged(self, val): font = app.font() font.setPointSize(val) app.setFont(font) self.settingsform.setFixedSize(self.settingsform.sizeHint()) def onCameraPerspect(self, val): if self.cameraPerspectCheckBox.isChecked(): self.NGL_HKL_command("NGL_HKLviewer.camera_type = perspective") else: self.NGL_HKL_command("NGL_HKLviewer.camera_type = orthographic") def MergeData(self): if self.mergecheckbox.checkState() == Qt.CheckState.Checked: self.NGL_HKL_command('NGL_HKLviewer.mergedata = True') if self.mergecheckbox.checkState() == Qt.CheckState.PartiallyChecked: self.NGL_HKL_command('NGL_HKLviewer.mergedata = None') if self.mergecheckbox.checkState() == Qt.CheckState.Unchecked: self.NGL_HKL_command('NGL_HKLviewer.mergedata = False') def ExpandToP1(self): if self.expandP1checkbox.isChecked(): self.NGL_HKL_command('NGL_HKLviewer.viewer.expand_to_p1 = True') else: self.NGL_HKL_command('NGL_HKLviewer.viewer.expand_to_p1 = False') def ExpandAnomalous(self): if self.expandAnomalouscheckbox.isChecked(): self.NGL_HKL_command( 'NGL_HKLviewer.viewer.expand_anomalous = True') else: self.NGL_HKL_command( 'NGL_HKLviewer.viewer.expand_anomalous = False') def showSysAbsent(self): if self.sysabsentcheckbox.isChecked(): self.NGL_HKL_command( 'NGL_HKLviewer.viewer.show_systematic_absences = True') else: self.NGL_HKL_command( 'NGL_HKLviewer.viewer.show_systematic_absences = False') def showMissing(self): if self.missingcheckbox.isChecked(): self.NGL_HKL_command('NGL_HKLviewer.viewer.show_missing = True') else: self.NGL_HKL_command('NGL_HKLviewer.viewer.show_missing = False') def showOnlyMissing(self): if self.onlymissingcheckbox.isChecked(): self.NGL_HKL_command( 'NGL_HKLviewer.viewer.show_only_missing = True') else: self.NGL_HKL_command( 'NGL_HKLviewer.viewer.show_only_missing = False') def showSlice(self): if self.showslicecheckbox.isChecked(): self.NGL_HKL_command('NGL_HKLviewer.viewer.slice_mode = True') if self.expandP1checkbox.isChecked(): self.NGL_HKL_command("""NGL_HKLviewer.viewer { expand_to_p1 = True inbrowser = False } """) if self.expandAnomalouscheckbox.isChecked(): self.NGL_HKL_command("""NGL_HKLviewer.viewer { expand_anomalous = True inbrowser = False } """) else: self.NGL_HKL_command("""NGL_HKLviewer.viewer { slice_mode = False inbrowser = True } """) def onSliceComboSelchange(self, i): rmin = self.array_infotpls[self.MillerComboBox.currentIndex()][3][0][i] rmax = self.array_infotpls[self.MillerComboBox.currentIndex()][3][1][i] self.sliceindexspinBox.setRange(rmin, rmax) self.NGL_HKL_command("NGL_HKLviewer.viewer.slice_axis = %s" % self.sliceaxis[i]) def onSliceIndexChanged(self, val): self.sliceindex = val self.NGL_HKL_command("NGL_HKLviewer.viewer.slice_index = %d" % self.sliceindex) def onBindataComboSelchange(self, i): if self.BinDataComboBox.currentText(): if self.BinDataComboBox.currentIndex() > 0: bin_scene_label = str(self.BinDataComboBox.currentIndex() - 1) else: bin_scene_label = "Resolution" self.NGL_HKL_command("NGL_HKLviewer.bin_scene_label = %s" % bin_scene_label) def update_table_opacities(self, allalpha=None): bin_opacitieslst = eval(self.bin_opacities) self.binstable_isready = False for binopacity in bin_opacitieslst: if not allalpha: alpha = float(binopacity.split(",")[0]) else: alpha = allalpha bin = int(binopacity.split(",")[1]) item = QTableWidgetItem() item.setFlags(Qt.ItemIsUserCheckable | Qt.ItemIsEnabled) if alpha < 0.5: item.setCheckState(Qt.Unchecked) else: item.setCheckState(Qt.Checked) item.setFlags(item.flags() ^ Qt.ItemIsEditable) self.binstable.setItem(bin, 3, item) self.binstable_isready = True def SetOpaqueAll(self): if self.binstableitemchanges: return bin_opacitieslst = eval(self.bin_opacities) nbins = len(bin_opacitieslst) sum = 0 for binopacity in bin_opacitieslst: sum += float(binopacity.split(",")[0]) if sum >= nbins: self.OpaqueAllCheckbox.setCheckState(Qt.Checked) if sum == 0: self.OpaqueAllCheckbox.setCheckState(Qt.Unchecked) if sum > 0.0 and sum < nbins: self.OpaqueAllCheckbox.setCheckState(Qt.PartiallyChecked) def onBinsTableItemChanged(self, item): row = item.row() column = item.column() try: if item.checkState() == Qt.Unchecked: newval = 0 else: newval = 1.0 if column == 3 and self.binstable_isready: # changing opacity assert (newval <= 1.0 and newval >= 0.0) bin_opacitieslst = eval(self.bin_opacities) bin_opacitieslst[row] = str(newval) + ', ' + str(row) self.bin_opacities = str(bin_opacitieslst) self.SetOpaqueAll() self.NGL_HKL_command( 'NGL_HKLviewer.viewer.NGL.bin_opacities = "%s"' % self.bin_opacities) except Exception as e: print(str(e)) #self.binstable.currentItem().setText( self.currentSelectedBinsTableVal) def onBinsTableItemSelectionChanged(self): row = self.binstable.currentItem().row() column = self.binstable.currentItem().column() self.currentSelectedBinsTableVal = self.binstable.currentItem().text() #print( "in itemSelectionChanged " + self.currentSelectedBinsTableVal) def onOpaqueAll(self): self.binstableitemchanges = True bin_opacitieslst = eval(self.bin_opacities) nbins = len(bin_opacitieslst) bin_opacitieslst = [] self.binstable_isready = False if self.OpaqueAllCheckbox.isChecked(): for i in range(nbins): bin_opacitieslst.append("1.0, %d" % i) else: for i in range(nbins): bin_opacitieslst.append("0.0, %d" % i) self.bin_opacities = str(bin_opacitieslst) self.NGL_HKL_command('NGL_HKLviewer.viewer.NGL.bin_opacities = "%s"' % self.bin_opacities) self.binstableitemchanges = False self.binstable_isready = True """ def onLoadFinished(self, val): pass #print("web page finished loading now") def onBinsTableitemActivated(self, item): row = item.row() column = item.column() currentval = item.text() #print( "in itemActivated " + currentval) def onBinsTableCellentered(self, row, col): pass #print( "in Cellentered " + self.binstable.currentItem().text() ) def onBinsTableCellPressed(self, row, col): pass #print( "in CellPressed " + self.binstable.currentItem().text() ) """ def onNbinsChanged(self, val): self.nbins = val if not self.updatingNbins: # avoid possible endless loop to cctbx self.NGL_HKL_command("NGL_HKLviewer.nbins = %d" % self.nbins) def onRadiiScaleChanged(self, val): self.radii_scale = val self.NGL_HKL_command(""" NGL_HKLviewer.viewer { nth_power_scale_radii = %f scale = %f } """ % (self.nth_power_scale, self.radii_scale)) def onPowerScaleChanged(self, val): self.nth_power_scale = val self.NGL_HKL_command(""" NGL_HKLviewer.viewer { nth_power_scale_radii = %f scale = %f } """ % (self.nth_power_scale, self.radii_scale)) def onManualPowerScale(self): if self.ManualPowerScalecheckbox.isChecked(): self.NGL_HKL_command( 'NGL_HKLviewer.viewer.nth_power_scale_radii = %f' % self.nth_power_scale) self.power_scale_spinBox.setEnabled(True) else: self.NGL_HKL_command( 'NGL_HKLviewer.viewer.nth_power_scale_radii = -1.0') self.power_scale_spinBox.setEnabled(False) self.nth_power_scale = -1.0 def OpenReflectionsFile(self): options = QFileDialog.Options() fileName, filtr = QFileDialog.getOpenFileName( self, "Load reflections file", "", "All Files (*);;MTZ Files (*.mtz);;CIF (*.cif)", "", options) if fileName: self.HKLnameedit.setText(fileName) #self.infostr = "" self.textInfo.setPlainText("") self.fileisvalid = False self.NGL_HKL_command('NGL_HKLviewer.filename = "%s"' % fileName) self.MillerComboBox.clear() self.BinDataComboBox.clear() def createExpansionBox(self): self.SpaceGroupComboBox = QComboBox() self.SpaceGroupComboBox.activated.connect(self.SpacegroupSelchange) self.SpacegroupLabel = QLabel() self.SpacegroupLabel.setText("Space Subgroups") self.mergecheckbox = QCheckBox() self.mergecheckbox.setText("Merge data") #self.mergecheckbox.setTristate (True) self.mergecheckbox.clicked.connect(self.MergeData) self.expandP1checkbox = QCheckBox() self.expandP1checkbox.setText("Expand to P1") self.expandP1checkbox.clicked.connect(self.ExpandToP1) self.expandAnomalouscheckbox = QCheckBox() self.expandAnomalouscheckbox.setText("Show Friedel pairs") self.expandAnomalouscheckbox.clicked.connect(self.ExpandAnomalous) self.sysabsentcheckbox = QCheckBox() self.sysabsentcheckbox.setText("Show Systematic Absences") self.sysabsentcheckbox.clicked.connect(self.showSysAbsent) self.missingcheckbox = QCheckBox() self.missingcheckbox.setText("Show Missing") self.missingcheckbox.clicked.connect(self.showMissing) self.onlymissingcheckbox = QCheckBox() self.onlymissingcheckbox.setText("Only Show Missing") self.onlymissingcheckbox.clicked.connect(self.showOnlyMissing) self.ExpansionBox = QGroupBox("Expansions") layout = QGridLayout() layout.addWidget(self.SpacegroupLabel, 0, 0) layout.addWidget(self.SpaceGroupComboBox, 0, 1) #layout.addWidget(self.mergecheckbox, 1, 0) layout.addWidget(self.expandP1checkbox, 1, 0) layout.addWidget(self.expandAnomalouscheckbox, 1, 1) layout.addWidget(self.sysabsentcheckbox, 2, 0) layout.addWidget(self.missingcheckbox, 3, 0) layout.addWidget(self.onlymissingcheckbox, 3, 1) layout.setRowStretch(0, 0) layout.setRowStretch(1, 0) layout.setRowStretch(2, 0) layout.setRowStretch(3, 1) self.ExpansionBox.setLayout(layout) def CreateSliceTabs(self): self.showslicecheckbox = QCheckBox() self.showslicecheckbox.setText("Show Slice") self.showslicecheckbox.clicked.connect(self.showSlice) self.sliceindexspinBox = QDoubleSpinBox() self.sliceindex = 0 self.sliceindexspinBox.setValue(self.sliceindex) self.sliceindexspinBox.setDecimals(0) self.sliceindexspinBox.setSingleStep(1) self.sliceindexspinBox.setRange(0, 20) self.sliceindexspinBox.valueChanged.connect(self.onSliceIndexChanged) self.SliceLabelComboBox = QComboBox() self.SliceLabelComboBox.activated.connect(self.onSliceComboSelchange) self.sliceaxis = ["h", "k", "l"] self.SliceLabelComboBox.addItems(self.sliceaxis) self.sliceTabWidget = QTabWidget() tab1 = QWidget() layout1 = QGridLayout() layout1.addWidget(self.showslicecheckbox, 0, 0, 1, 1) layout1.addWidget(self.SliceLabelComboBox, 0, 1, 1, 1) layout1.addWidget(self.sliceindexspinBox, 0, 2, 1, 1) tab1.setLayout(layout1) tab2 = QWidget() layout2 = QGridLayout() self.hvec_spinBox = QDoubleSpinBox(self.sliceTabWidget) self.hvecval = 2.0 self.hvec_spinBox.setValue(self.hvecval) self.hvec_spinBox.setDecimals(2) self.hvec_spinBox.setSingleStep(0.5) self.hvec_spinBox.setRange(-100.0, 10.0) self.hvec_spinBox.valueChanged.connect(self.onHvecChanged) self.hvec_Label = QLabel() self.hvec_Label.setText("H") layout2.addWidget(self.hvec_Label, 0, 0, 1, 1) layout2.addWidget(self.hvec_spinBox, 0, 1, 1, 1) self.kvec_spinBox = QDoubleSpinBox(self.sliceTabWidget) self.kvecval = 0.0 self.kvec_spinBox.setValue(self.kvecval) self.kvec_spinBox.setDecimals(2) self.kvec_spinBox.setSingleStep(0.5) self.kvec_spinBox.setRange(-100.0, 100.0) self.kvec_spinBox.valueChanged.connect(self.onKvecChanged) self.kvec_Label = QLabel() self.kvec_Label.setText("K") layout2.addWidget(self.kvec_Label, 1, 0, 1, 1) layout2.addWidget(self.kvec_spinBox, 1, 1, 1, 1) self.lvec_spinBox = QDoubleSpinBox(self.sliceTabWidget) self.lvecval = 0.0 self.lvec_spinBox.setValue(self.lvecval) self.lvec_spinBox.setDecimals(2) self.lvec_spinBox.setSingleStep(0.5) self.lvec_spinBox.setRange(-100.0, 100.0) self.lvec_spinBox.valueChanged.connect(self.onLvecChanged) self.lvec_Label = QLabel() self.lvec_Label.setText("L") layout2.addWidget(self.lvec_Label, 2, 0, 1, 1) layout2.addWidget(self.lvec_spinBox, 2, 1, 1, 1) self.hkldist_spinBox = QDoubleSpinBox(self.sliceTabWidget) self.hkldistval = 0.0 self.hkldist_spinBox.setValue(self.hkldistval) self.hkldist_spinBox.setDecimals(2) self.hkldist_spinBox.setSingleStep(0.5) self.hkldist_spinBox.setRange(-100.0, 100.0) self.hkldist_spinBox.valueChanged.connect(self.onHKLdistChanged) self.hkldist_Label = QLabel() self.hkldist_Label.setText("Distance from Origin") layout2.addWidget(self.hkldist_Label, 3, 0, 1, 1) layout2.addWidget(self.hkldist_spinBox, 3, 1, 1, 1) self.clipwidth_spinBox = QDoubleSpinBox(self.sliceTabWidget) self.clipwidthval = 0.5 self.clipwidth_spinBox.setValue(self.clipwidthval) self.clipwidth_spinBox.setDecimals(2) self.clipwidth_spinBox.setSingleStep(0.05) self.clipwidth_spinBox.setRange(0.0, 100.0) self.clipwidth_spinBox.valueChanged.connect(self.onClipwidthChanged) self.clipwidth_Label = QLabel() self.clipwidth_Label.setText("Clip Plane Width") layout2.addWidget(self.clipwidth_Label, 4, 0, 1, 1) layout2.addWidget(self.clipwidth_spinBox, 4, 1, 1, 1) self.ClipBox = QGroupBox("Normal Vector to Clip Plane") self.ClipBox.setLayout(layout2) layout3 = QGridLayout() self.ClipPlaneChkBox = QCheckBox(self.sliceTabWidget) self.ClipPlaneChkBox.setText( "Use clip plane normal to HKL vector pointing out") self.ClipPlaneChkBox.clicked.connect(self.onClipPlaneChkBox) layout3.addWidget(self.ClipPlaneChkBox, 0, 0) layout3.addWidget(self.ClipBox, 1, 0) tab2.setLayout(layout3) self.sliceTabWidget.addTab(tab1, "Explicit Slicing") self.sliceTabWidget.addTab(tab2, "Clip Plane Slicing") self.ClipBox.setDisabled(True) def onClipPlaneChkBox(self): if self.ClipPlaneChkBox.isChecked(): self.ClipBox.setDisabled(False) philstr = """NGL_HKLviewer.normal_clip_plane { h = %s k = %s l = %s hkldist = %s clipwidth = %s } NGL_HKLviewer.viewer.NGL.fixorientation = %s """ %(self.hvecval, self.kvecval, self.lvecval, self.hkldistval, self.clipwidthval, \ str(self.fixedorientcheckbox.isChecked()) ) self.NGL_HKL_command(philstr) else: self.ClipBox.setDisabled(True) self.NGL_HKL_command( "NGL_HKLviewer.normal_clip_plane.clipwidth = None") def onClipwidthChanged(self, val): self.clipwidthval = val self.NGL_HKL_command("NGL_HKLviewer.normal_clip_plane.clipwidth = %f" % self.clipwidthval) def onHKLdistChanged(self, val): self.hkldistval = val self.NGL_HKL_command("NGL_HKLviewer.normal_clip_plane.hkldist = %f" % self.hkldistval) def onHvecChanged(self, val): self.hvecval = val self.NGL_HKL_command("NGL_HKLviewer.normal_clip_plane.h = %f" % self.hvecval) def onKvecChanged(self, val): self.kvecval = val self.NGL_HKL_command("NGL_HKLviewer.normal_clip_plane.k = %f" % self.kvecval) def onLvecChanged(self, val): self.lvecval = val self.NGL_HKL_command("NGL_HKLviewer.normal_clip_plane.l = %f" % self.lvecval) def onFixedorient(self): self.NGL_HKL_command('NGL_HKLviewer.viewer.NGL.fixorientation = %s' \ %str(self.fixedorientcheckbox.isChecked())) def onMillerComboSelchange(self, i): self.NGL_HKL_command("NGL_HKLviewer.scene_id = %d" % i) #self.MillerComboBox.setCurrentIndex(i) if self.MillerComboBox.currentText(): self.functionTabWidget.setEnabled(True) self.expandAnomalouscheckbox.setEnabled(True) # don' allow anomalous expansion for data that's already anomalous for arrayinfo in self.array_infotpls: isanomalous = arrayinfo[-1] label = arrayinfo[0] if isanomalous and label == self.MillerComboBox.currentText( )[:len(label)]: self.expandAnomalouscheckbox.setDisabled(True) else: self.functionTabWidget.setDisabled(True) self.SpaceGroupComboBox.clear() self.SpaceGroupComboBox.addItems(self.spacegroups) # need to supply issymunique flag in infotuple #if self.hklscenes_arrays[ i ][6] == 0: # self.mergecheckbox.setEnabled(True) #else: # self.mergecheckbox.setEnabled(False) def createFileInfoBox(self): self.FileInfoBox = QGroupBox("Reflection File Information") layout = QGridLayout() layout.addWidget(self.openFileNameButton, 0, 0, 1, 2) if self.devmode: layout.addWidget(self.debugbutton, 0, 2, 1, 1) layout.addWidget(self.HKLnameedit, 1, 0, 1, 3) layout.addWidget(self.millertable, 2, 0, 1, 3) layout.addWidget(self.textInfo, 3, 0, 1, 3) #layout.setColumnStretch(1, 2) self.FileInfoBox.setLayout(layout) def createRadiiScaleGroupBox(self): self.RadiiScaleGroupBox = QGroupBox("Radii Size of HKL Spheres") self.ManualPowerScalecheckbox = QCheckBox() self.ManualPowerScalecheckbox.setText( "Manual Power Scaling of Sphere Radii") self.ManualPowerScalecheckbox.clicked.connect(self.onManualPowerScale) self.power_scale_spinBox = QDoubleSpinBox(self.RadiiScaleGroupBox) self.nth_power_scale = 0.5 self.power_scale_spinBox.setValue(self.nth_power_scale) self.power_scale_spinBox.setDecimals(2) self.power_scale_spinBox.setSingleStep(0.05) self.power_scale_spinBox.setRange(0.0, 1.0) self.power_scale_spinBox.valueChanged.connect(self.onPowerScaleChanged) self.power_scale_spinBox.setEnabled(False) self.powerscaleLabel = QLabel() self.powerscaleLabel.setText("Power scale Factor") self.radii_scale_spinBox = QDoubleSpinBox(self.RadiiScaleGroupBox) self.radii_scale = 1.0 self.radii_scale_spinBox.setValue(self.radii_scale) self.radii_scale_spinBox.setDecimals(1) self.radii_scale_spinBox.setSingleStep(0.1) self.radii_scale_spinBox.setRange(0.2, 2.0) self.radii_scale_spinBox.valueChanged.connect(self.onRadiiScaleChanged) self.radiiscaleLabel = QLabel() self.radiiscaleLabel.setText("Linear Scale Factor") layout = QGridLayout() layout.addWidget(self.ManualPowerScalecheckbox, 1, 0, 1, 2) layout.addWidget(self.powerscaleLabel, 2, 0, 1, 2) layout.addWidget(self.power_scale_spinBox, 2, 1, 1, 2) layout.addWidget(self.radiiscaleLabel, 3, 0, 1, 2) layout.addWidget(self.radii_scale_spinBox, 3, 1, 1, 2) layout.setColumnStretch(0, 1) layout.setColumnStretch(1, 0) self.RadiiScaleGroupBox.setLayout(layout) def createBinsBox(self): self.binstable = QTableWidget(0, 4) self.binstable_isready = False labels = [ "no. of HKLs", "lower bin value", "upper bin value", "opacity" ] self.binstable.setHorizontalHeaderLabels(labels) self.binstable.horizontalHeader().setDefaultAlignment(Qt.AlignLeft) self.bindata_labeltxt = QLabel() self.bindata_labeltxt.setText("Data binned:") self.Nbins_spinBox = QSpinBox() self.Nbins_spinBox.setSingleStep(1) self.Nbins_spinBox.setRange(1, 40) self.Nbins_spinBox.valueChanged.connect(self.onNbinsChanged) self.Nbins_labeltxt = QLabel() self.Nbins_labeltxt.setText("Number of bins:") self.OpaqueAllCheckbox = QCheckBox() #self.OpaqueAllCheckbox.setTristate() self.OpaqueAllCheckbox.setText("Show all data in bins") self.OpaqueAllCheckbox.clicked.connect(self.onOpaqueAll) self.binstable.itemChanged.connect(self.onBinsTableItemChanged) self.binstable.itemSelectionChanged.connect( self.onBinsTableItemSelectionChanged) self.BinDataComboBox = QComboBox() self.BinDataComboBox.activated.connect(self.onBindataComboSelchange) self.BinsGroupBox = QGroupBox("Bins") layout = QGridLayout() layout.addWidget(self.bindata_labeltxt, 0, 0) layout.addWidget(self.BinDataComboBox, 0, 1) layout.addWidget(self.Nbins_labeltxt, 0, 2) layout.addWidget(self.Nbins_spinBox, 0, 3) layout.addWidget(self.OpaqueAllCheckbox, 1, 2) layout.addWidget(self.binstable, 2, 0, 1, 4) layout.setColumnStretch(0, 0) layout.setColumnStretch(1, 2) layout.setColumnStretch(3, 1) self.BinsGroupBox.setLayout(layout) def DebugInteractively(self): import code, traceback code.interact(local=locals(), banner="".join(traceback.format_stack(limit=10))) def CreateFunctionTabs(self): self.functionTabWidget = QTabWidget() tab1 = QWidget() layout1 = QGridLayout() layout1.addWidget(self.ExpansionBox, 0, 0) layout1.setRowStretch(0, 0) tab1.setLayout(layout1) tab2 = QWidget() layout2 = QGridLayout() self.fixedorientcheckbox = QCheckBox(self.sliceTabWidget) self.fixedorientcheckbox.setText( "Fix orientation but allow zoom and translation") self.fixedorientcheckbox.clicked.connect(self.onFixedorient) layout2.addWidget(self.fixedorientcheckbox, 0, 0) layout2.addWidget(self.sliceTabWidget, 1, 0) tab2.setLayout(layout2) tab3 = QWidget() layout3 = QGridLayout() layout3.addWidget(self.RadiiScaleGroupBox, 0, 0) tab3.setLayout(layout3) tab4 = QWidget() layout4 = QGridLayout() layout4.addWidget(self.BinsGroupBox, 0, 0) tab4.setLayout(layout4) self.functionTabWidget.addTab(tab1, "Expand") self.functionTabWidget.addTab(tab2, "Slice") self.functionTabWidget.addTab(tab3, "Size") self.functionTabWidget.addTab(tab4, "Bins") self.functionTabWidget.setDisabled(True) def SpacegroupSelchange(self, i): self.NGL_HKL_command("NGL_HKLviewer.spacegroup_choice = %d" % i) def find_free_port(self): import socket s = socket.socket() s.bind(('', 0)) # Bind to a free port provided by the host. port = s.getsockname()[1] s.close() return port def LaunchCCTBXPython(self): self.sockport = self.find_free_port() self.zmq_context = zmq.Context() self.socket = self.zmq_context.socket(zmq.PAIR) self.socket.bind("tcp://127.0.0.1:%s" % self.sockport) try: msg = self.socket.recv( flags=zmq.NOBLOCK) #To empty the socket from previous messages except Exception as e: pass cmdargs = 'cctbx.python.bat -i -c "from crys3d.hklview import cmdlineframes;' \ + ' myHKLview = cmdlineframes.HKLViewFrame(useGuiSocket=%s, high_quality=True,' %self.sockport \ + ' jscriptfname = \'%s\', ' %self.jscriptfname \ + ' verbose=%s, UseOSBrowser= %s )"\n' %(self.verbose, str(self.UseOSbrowser)) self.cctbxproc = subprocess.Popen(cmdargs, shell=True, stdin=subprocess.PIPE, stdout=sys.stdout, stderr=sys.stderr) #time.sleep(1) def NGL_HKL_command(self, cmdstr): #print("sending:\n" + cmdstr) self.socket.send(bytes(cmdstr, "utf-8"))
class DeepGraphs(cutter.CutterDockWidget): def __init__(self, parent, action): super(DeepGraphs, self).__init__(parent, action) self.setObjectName("DeepGraphs") self.setWindowTitle("Deep graphs") content = QWidget() self.setWidget(content) # Create layout layout = QVBoxLayout(content) content.setLayout(layout) self.output_path = QLineEdit(content) self.output_path.setText("PATH") layout.addWidget(self.output_path) layout.setAlignment(self.output_path, Qt.AlignHCenter) self.combo = QComboBox(content) self.combo.addItem("Graphviz dot") self.combo.addItem("Graph Modelling Language (gml)") self.combo.addItem("Json") layout.addWidget(self.combo) layout.setAlignment(self.combo, Qt.AlignHCenter) # TODO: This graph is not available due to an issue on radare2 (see closed issue #13590). It will be available as soon as Cutter updates its radare2 version. deep_button = QPushButton(content) deep_button.setText("Deep callgraph") deep_button.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum) deep_button.setMaximumHeight(50) deep_button.setMaximumWidth(200) layout.addWidget(deep_button) layout.setAlignment(deep_button, Qt.AlignHCenter) global_button = QPushButton(content) global_button.setText("Global callgraph") global_button.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum) global_button.setMaximumHeight(50) global_button.setMaximumWidth(200) layout.addWidget(global_button) layout.setAlignment(global_button, Qt.AlignHCenter) function_button = QPushButton(content) function_button.setText("Function callgraph") function_button.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum) function_button.setMaximumHeight(50) function_button.setMaximumWidth(200) layout.addWidget(function_button) layout.setAlignment(function_button, Qt.AlignHCenter) global_data_x_button = QPushButton(content) global_data_x_button.setText("Global data references") global_data_x_button.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum) global_data_x_button.setMaximumHeight(50) global_data_x_button.setMaximumWidth(200) layout.addWidget(global_data_x_button) layout.setAlignment(global_data_x_button, Qt.AlignHCenter) function_data_x_button = QPushButton(content) function_data_x_button.setText("Function data references") function_data_x_button.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum) function_data_x_button.setMaximumHeight(50) function_data_x_button.setMaximumWidth(200) layout.addWidget(function_data_x_button) layout.setAlignment(function_data_x_button, Qt.AlignHCenter) global_refs_button = QPushButton(content) global_refs_button.setText("Global references") global_refs_button.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum) global_refs_button.setMaximumHeight(50) global_refs_button.setMaximumWidth(200) layout.addWidget(global_refs_button) layout.setAlignment(global_refs_button, Qt.AlignHCenter) xrefs_button = QPushButton(content) xrefs_button.setText("Function xrefs") xrefs_button.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum) xrefs_button.setMaximumHeight(50) xrefs_button.setMaximumWidth(200) layout.addWidget(xrefs_button) layout.setAlignment(xrefs_button, Qt.AlignHCenter) imports_button = QPushButton(content) imports_button.setText("Imports refs") imports_button.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum) imports_button.setMaximumHeight(50) imports_button.setMaximumWidth(200) layout.addWidget(imports_button) layout.setAlignment(imports_button, Qt.AlignHCenter) QObject.connect(deep_button, SIGNAL("clicked()"), self.generate_callgraph) QObject.connect(global_button, SIGNAL("clicked()"), self.generate_global_callgraph) QObject.connect(function_button, SIGNAL("clicked()"), self.generate_local_callgraph) QObject.connect(global_data_x_button, SIGNAL("clicked()"), self.generate_global_data_references) QObject.connect(function_data_x_button, SIGNAL("clicked()"), self.generate_local_data_references) QObject.connect(global_refs_button, SIGNAL("clicked()"), self.generate_global_refs) QObject.connect(xrefs_button, SIGNAL("clicked()"), self.generate_xrefs) QObject.connect(imports_button, SIGNAL("clicked()"), self.generate_imports_graph) self.show() def generate_callgraph(self): # TODO: get user settings for the .dot graph instead of using this hardcoded settings self.graph_dot = """digraph code { rankdir=LR; outputorder=edgesfirst; graph [bgcolor=azure fontname="Courier" splines="curved"]; node [fillcolor=white style=filled fontname="Courier New Bold" fontsize=14 shape=box]; edge [arrowhead="normal" style=bold weight=2];""" self.used_nodes = [] function_name = cutter.cmd('afi.') function_name = function_name.replace("\n", "") self.functions_list = cutter.cmdj('aflmj') self.get_calls(function_name) self.graph_dot += '\n}' def add_node(self, name): if name not in self.used_nodes: self.used_nodes.append(name) self.graph_dot += '\n"' + name + '" [label="' + name + '"];' def add_edge(self, name1, name2): self.graph_dot += '\n"' + name1 + '" -> "' + name2 + '" [color="#61afef"];' def get_calls(self, name): self.add_node(name) for function in self.functions_list: if function['name'] == name: for call in function['calls']: self.add_node(call['name']) self.add_edge(name, call['name']) if call['name'][:3] != 'sym': self.get_calls(call['name']) def output_callgraph(self): output = 'deep_callgraph_' + cutter.cmd( 'afi.') + self.get_output_format_extension( self.combo.currentIndex()) file = open(output, "w") file.write(self.graph_dot) file.close() def generate_global_callgraph(self): # agC name = self.get_output_name("global_callgraph") command = "agC" + self.get_output_format_cmd(self.combo.currentIndex()) cutter.cmd(command + " > " + name) def generate_local_callgraph(self): # agc function_name = self.get_output_name("callgraph", function=True) command = "agc" + self.get_output_format_cmd(self.combo.currentIndex()) command += " > " command += function_name cutter.cmd(command) def generate_global_data_references(self): # agA name = self.get_output_name("global_data_references") command = "agA" + self.get_output_format_cmd(self.combo.currentIndex()) cutter.cmd(command + " > " + name) def generate_local_data_references(self): # aga function_name = self.get_output_name("data_references", function=True) command = "aga" + self.get_output_format_cmd(self.combo.currentIndex()) command += " > " command += function_name cutter.cmd(command) def generate_global_refs(self): # agR name = self.get_output_name("global_references") command = "agR" + self.get_output_format_cmd(self.combo.currentIndex()) cutter.cmd(command + " > " + name) def generate_xrefs(self): # agx function_name = self.get_output_name("xrefs", function=True) command = "agx" + self.get_output_format_cmd(self.combo.currentIndex()) command += " > " command += function_name cutter.cmd(command) def generate_imports_graph(self): # agi name = self.get_output_name("imports_refs") command = "agi" + self.get_output_format_cmd(self.combo.currentIndex()) cutter.cmd(command + " > " + name) def get_output_name(self, graph_name, function=False): if not function: name = graph_name + "." + self.get_output_format_extension( self.combo.currentIndex()) return self.append_to_path(name) else: function_name = cutter.cmd('afi.') function_name = function_name.replace("\n", "") function_name += "_" function_name += graph_name function_name += "." function_name += self.get_output_format_extension( self.combo.currentIndex()) return self.append_to_path(function_name) def append_to_path(self, name): path = self.output_path.text() if path != "PATH": if path[-1:] != '/': path += '/' name = str(path) + name return name def get_output_format_extension(self, index): if index == 0: return "dot" elif index == 1: return "gml" elif index == 2: return "json" else: return "none" def get_output_format_cmd(self, index): if index == 0: return "d" elif index == 1: return "g" elif index == 2: return "j" else: return ""
def refresh_if_needed(combo: QtWidgets.QComboBox, func): if combo.currentIndex() == 0: func(0)
class QSettingsWindow(QDialog): def __init__(self, game: Game): super(QSettingsWindow, self).__init__() self.game = game self.setModal(True) self.setWindowTitle("Settings") self.setWindowIcon(CONST.ICONS["Settings"]) self.setMinimumSize(600, 250) self.initUi() def initUi(self): self.layout = QGridLayout() self.categoryList = QListView() self.right_layout = QStackedLayout() self.categoryList.setMaximumWidth(175) self.categoryModel = QStandardItemModel(self.categoryList) difficulty = QStandardItem("Difficulty") difficulty.setIcon(CONST.ICONS["Missile"]) difficulty.setEditable(False) difficulty.setSelectable(True) generator = QStandardItem("Mission Generator") generator.setIcon(CONST.ICONS["Generator"]) generator.setEditable(False) generator.setSelectable(True) cheat = QStandardItem("Cheat Menu") cheat.setIcon(CONST.ICONS["Cheat"]) cheat.setEditable(False) cheat.setSelectable(True) self.categoryList.setIconSize(QSize(32, 32)) self.categoryModel.appendRow(difficulty) self.categoryModel.appendRow(generator) self.categoryModel.appendRow(cheat) self.categoryList.setSelectionBehavior(QAbstractItemView.SelectRows) self.categoryList.setModel(self.categoryModel) self.categoryList.selectionModel().setCurrentIndex(self.categoryList.indexAt(QPoint(1,1)), QItemSelectionModel.Select) self.categoryList.selectionModel().selectionChanged.connect(self.onSelectionChanged) self.initDifficultyLayout() self.initGeneratorLayout() self.initCheatLayout() self.right_layout.addWidget(self.difficultyPage) self.right_layout.addWidget(self.generatorPage) self.right_layout.addWidget(self.cheatPage) self.layout.addWidget(self.categoryList, 0, 0, 1, 1) self.layout.addLayout(self.right_layout, 0, 1, 5, 1) self.setLayout(self.layout) def init(self): pass def initDifficultyLayout(self): self.difficultyPage = QWidget() self.difficultyLayout = QGridLayout() self.difficultyLayout.setAlignment(Qt.AlignTop) self.difficultyPage.setLayout(self.difficultyLayout) self.playerCoalitionSkill = QComboBox() self.enemyCoalitionSkill = QComboBox() self.enemyAASkill = QComboBox() for skill in CONST.SKILL_OPTIONS: self.playerCoalitionSkill.addItem(skill) self.enemyCoalitionSkill.addItem(skill) self.enemyAASkill.addItem(skill) self.playerCoalitionSkill.setCurrentIndex(CONST.SKILL_OPTIONS.index(self.game.settings.player_skill)) self.enemyCoalitionSkill.setCurrentIndex(CONST.SKILL_OPTIONS.index(self.game.settings.enemy_skill)) self.enemyAASkill.setCurrentIndex(CONST.SKILL_OPTIONS.index(self.game.settings.enemy_vehicle_skill)) self.playerCoalitionSkill.currentIndexChanged.connect(self.applySettings) self.enemyCoalitionSkill.currentIndexChanged.connect(self.applySettings) self.enemyAASkill.currentIndexChanged.connect(self.applySettings) self.difficultyLayout.addWidget(QLabel("Player coalition skill"), 0, 0) self.difficultyLayout.addWidget(self.playerCoalitionSkill, 0, 1, Qt.AlignRight) self.difficultyLayout.addWidget(QLabel("Enemy skill"), 1, 0) self.difficultyLayout.addWidget(self.enemyCoalitionSkill, 1, 1, Qt.AlignRight) self.difficultyLayout.addWidget(QLabel("Enemy AA and vehicles skill"), 2, 0) self.difficultyLayout.addWidget(self.enemyAASkill, 2, 1, Qt.AlignRight) self.difficultyLabel = QComboBox() [self.difficultyLabel.addItem(t) for t in CONST.LABELS_OPTIONS] self.difficultyLabel.setCurrentIndex(CONST.LABELS_OPTIONS.index(self.game.settings.labels)) self.difficultyLabel.currentIndexChanged.connect(self.applySettings) self.difficultyLayout.addWidget(QLabel("In Game Labels"), 3, 0) self.difficultyLayout.addWidget(self.difficultyLabel, 3, 1, Qt.AlignRight) self.noNightMission = QCheckBox() self.noNightMission.setChecked(self.game.settings.night_disabled) self.noNightMission.toggled.connect(self.applySettings) self.difficultyLayout.addWidget(QLabel("No night missions"), 4, 0) self.difficultyLayout.addWidget(self.noNightMission, 4, 1, Qt.AlignRight) self.mapVisibiitySelection = QComboBox() self.mapVisibiitySelection.addItem("All", ForcedOptions.Views.All) if self.game.settings.map_coalition_visibility == ForcedOptions.Views.All: self.mapVisibiitySelection.setCurrentIndex(0) self.mapVisibiitySelection.addItem("Fog of War", ForcedOptions.Views.Allies) if self.game.settings.map_coalition_visibility == ForcedOptions.Views.Allies: self.mapVisibiitySelection.setCurrentIndex(1) self.mapVisibiitySelection.addItem("Allies Only", ForcedOptions.Views.OnlyAllies) if self.game.settings.map_coalition_visibility == ForcedOptions.Views.OnlyAllies: self.mapVisibiitySelection.setCurrentIndex(2) self.mapVisibiitySelection.addItem("Own Aircraft Only", ForcedOptions.Views.MyAircraft) if self.game.settings.map_coalition_visibility == ForcedOptions.Views.MyAircraft: self.mapVisibiitySelection.setCurrentIndex(3) self.mapVisibiitySelection.addItem("Map Only", ForcedOptions.Views.OnlyMap) if self.game.settings.map_coalition_visibility == ForcedOptions.Views.OnlyMap: self.mapVisibiitySelection.setCurrentIndex(4) self.mapVisibiitySelection.currentIndexChanged.connect(self.applySettings) self.difficultyLayout.addWidget(QLabel("Map visibility options"), 5, 0) self.difficultyLayout.addWidget(self.mapVisibiitySelection, 5, 1, Qt.AlignRight) self.ext_views = QCheckBox() self.ext_views.setChecked(self.game.settings.external_views_allowed) self.ext_views.toggled.connect(self.applySettings) self.difficultyLayout.addWidget(QLabel("Allow external views"), 6, 0) self.difficultyLayout.addWidget(self.ext_views, 6, 1, Qt.AlignRight) def initGeneratorLayout(self): self.generatorPage = QWidget() self.generatorLayout = QVBoxLayout() self.generatorLayout.setAlignment(Qt.AlignTop) self.generatorPage.setLayout(self.generatorLayout) self.gameplay = QGroupBox("Gameplay") self.gameplayLayout = QGridLayout(); self.gameplayLayout.setAlignment(Qt.AlignTop) self.gameplay.setLayout(self.gameplayLayout) self.supercarrier = QCheckBox() self.supercarrier.setChecked(self.game.settings.supercarrier) self.supercarrier.toggled.connect(self.applySettings) self.generate_marks = QCheckBox() self.generate_marks.setChecked(self.game.settings.generate_marks) self.generate_marks.toggled.connect(self.applySettings) if not hasattr(self.game.settings, "include_jtac_if_available"): self.game.settings.include_jtac_if_available = True self.include_jtac_if_available = QCheckBox() self.include_jtac_if_available.setChecked(self.game.settings.include_jtac_if_available) self.include_jtac_if_available.toggled.connect(self.applySettings) self.gameplayLayout.addWidget(QLabel("Use Supercarrier Module"), 0, 0) self.gameplayLayout.addWidget(self.supercarrier, 0, 1, Qt.AlignRight) self.gameplayLayout.addWidget(QLabel("Put Objective Markers on Map"), 1, 0) self.gameplayLayout.addWidget(self.generate_marks, 1, 1, Qt.AlignRight) self.gameplayLayout.addWidget(QLabel("Include JTAC (If available)"), 2, 0) self.gameplayLayout.addWidget(self.include_jtac_if_available, 2, 1, Qt.AlignRight) self.performance = QGroupBox("Performance") self.performanceLayout = QGridLayout() self.performanceLayout.setAlignment(Qt.AlignTop) self.performance.setLayout(self.performanceLayout) self.smoke = QCheckBox() self.smoke.setChecked(self.game.settings.perf_smoke_gen) self.smoke.toggled.connect(self.applySettings) self.red_alert = QCheckBox() self.red_alert.setChecked(self.game.settings.perf_red_alert_state) self.red_alert.toggled.connect(self.applySettings) self.arti = QCheckBox() self.arti.setChecked(self.game.settings.perf_artillery) self.arti.toggled.connect(self.applySettings) self.moving_units = QCheckBox() self.moving_units.setChecked(self.game.settings.perf_moving_units) self.moving_units.toggled.connect(self.applySettings) self.infantry = QCheckBox() self.infantry.setChecked(self.game.settings.perf_infantry) self.infantry.toggled.connect(self.applySettings) self.ai_parking_start = QCheckBox() self.ai_parking_start.setChecked(self.game.settings.perf_ai_parking_start) self.ai_parking_start.toggled.connect(self.applySettings) self.destroyed_units = QCheckBox() self.destroyed_units.setChecked(self.game.settings.perf_destroyed_units) self.destroyed_units.toggled.connect(self.applySettings) self.culling = QCheckBox() self.culling.setChecked(self.game.settings.perf_culling) self.culling.toggled.connect(self.applySettings) self.culling_distance = QSpinBox() self.culling_distance.setMinimum(50) self.culling_distance.setMaximum(10000) self.culling_distance.setValue(self.game.settings.perf_culling_distance) self.culling_distance.valueChanged.connect(self.applySettings) self.performanceLayout.addWidget(QLabel("Smoke visual effect on frontline"), 0, 0) self.performanceLayout.addWidget(self.smoke, 0, 1, alignment=Qt.AlignRight) self.performanceLayout.addWidget(QLabel("SAM starts in RED alert mode"), 1, 0) self.performanceLayout.addWidget(self.red_alert, 1, 1, alignment=Qt.AlignRight) self.performanceLayout.addWidget(QLabel("Artillery strikes"), 2, 0) self.performanceLayout.addWidget(self.arti, 2, 1, alignment=Qt.AlignRight) self.performanceLayout.addWidget(QLabel("Moving ground units"), 3, 0) self.performanceLayout.addWidget(self.moving_units, 3, 1, alignment=Qt.AlignRight) self.performanceLayout.addWidget(QLabel("Generate infantry squads along vehicles"), 4, 0) self.performanceLayout.addWidget(self.infantry, 4, 1, alignment=Qt.AlignRight) self.performanceLayout.addWidget(QLabel("AI planes parking start (AI starts in flight if disabled)"), 5, 0) self.performanceLayout.addWidget(self.ai_parking_start, 5, 1, alignment=Qt.AlignRight) self.performanceLayout.addWidget(QLabel("Include destroyed units carcass"), 6, 0) self.performanceLayout.addWidget(self.destroyed_units, 6, 1, alignment=Qt.AlignRight) self.performanceLayout.addWidget(QHorizontalSeparationLine(), 7, 0, 1, 2) self.performanceLayout.addWidget(QLabel("Culling of distant units enabled"), 8, 0) self.performanceLayout.addWidget(self.culling, 8, 1, alignment=Qt.AlignRight) self.performanceLayout.addWidget(QLabel("Culling distance (km)"), 9, 0) self.performanceLayout.addWidget(self.culling_distance, 9, 1, alignment=Qt.AlignRight) self.generatorLayout.addWidget(self.gameplay) self.generatorLayout.addWidget(QLabel("Disabling settings below may improve performance, but will impact the overall quality of the experience.")) self.generatorLayout.addWidget(self.performance) def initCheatLayout(self): self.cheatPage = QWidget() self.cheatLayout = QGridLayout() self.cheatPage.setLayout(self.cheatLayout) self.moneyCheatBox = QGroupBox("Money Cheat") self.moneyCheatBox.setAlignment(Qt.AlignTop) self.moneyCheatBoxLayout = QGridLayout() self.moneyCheatBox.setLayout(self.moneyCheatBoxLayout) self.cheat25M = QPushButton("Cheat +25M") self.cheat50M = QPushButton("Cheat +50M") self.cheat100M = QPushButton("Cheat +100M") self.cheat200M = QPushButton("Cheat +200M") self.cheat500M = QPushButton("Cheat +500M") self.cheat1000M = QPushButton("Cheat +1000M") self.cheat25M.clicked.connect(lambda: self.cheatMoney(25)) self.cheat50M.clicked.connect(lambda: self.cheatMoney(50)) self.cheat100M.clicked.connect(lambda: self.cheatMoney(100)) self.cheat200M.clicked.connect(lambda: self.cheatMoney(200)) self.cheat500M.clicked.connect(lambda: self.cheatMoney(500)) self.cheat1000M.clicked.connect(lambda: self.cheatMoney(1000)) self.moneyCheatBoxLayout.addWidget(self.cheat25M, 0, 0) self.moneyCheatBoxLayout.addWidget(self.cheat50M, 0, 1) self.moneyCheatBoxLayout.addWidget(self.cheat100M, 1, 0) self.moneyCheatBoxLayout.addWidget(self.cheat200M, 1, 1) self.moneyCheatBoxLayout.addWidget(self.cheat500M, 2, 0) self.moneyCheatBoxLayout.addWidget(self.cheat1000M, 2, 1) self.cheatLayout.addWidget(self.moneyCheatBox, 0, 0) def cheatMoney(self, amount): self.game.budget += amount self.game.informations.append(Information("CHEATER", "You are a cheater and you should feel bad", self.game.turn)) GameUpdateSignal.get_instance().updateGame(self.game) def applySettings(self): self.game.settings.player_skill = CONST.SKILL_OPTIONS[self.playerCoalitionSkill.currentIndex()] self.game.settings.enemy_skill = CONST.SKILL_OPTIONS[self.enemyCoalitionSkill.currentIndex()] self.game.settings.enemy_vehicle_skill = CONST.SKILL_OPTIONS[self.enemyAASkill.currentIndex()] self.game.settings.labels = CONST.LABELS_OPTIONS[self.difficultyLabel.currentIndex()] self.game.settings.night_disabled = self.noNightMission.isChecked() self.game.settings.map_coalition_visibility = self.mapVisibiitySelection.currentData() self.game.settings.external_views_allowed = self.ext_views.isChecked() self.game.settings.generate_marks = self.generate_marks.isChecked() self.game.settings.include_jtac_if_available = self.include_jtac_if_available.isChecked() print(self.game.settings.map_coalition_visibility) self.game.settings.supercarrier = self.supercarrier.isChecked() self.game.settings.perf_red_alert_state = self.red_alert.isChecked() self.game.settings.perf_smoke_gen = self.smoke.isChecked() self.game.settings.perf_artillery = self.arti.isChecked() self.game.settings.perf_moving_units = self.moving_units.isChecked() self.game.settings.perf_infantry = self.infantry.isChecked() self.game.settings.perf_ai_parking_start = self.ai_parking_start.isChecked() self.game.settings.perf_destroyed_units = self.destroyed_units.isChecked() self.game.settings.perf_culling = self.culling.isChecked() self.game.settings.perf_culling_distance = int(self.culling_distance.value()) GameUpdateSignal.get_instance().updateGame(self.game) def onSelectionChanged(self): index = self.categoryList.selectionModel().currentIndex().row() self.right_layout.setCurrentIndex(index)
class MainWindow(QMainWindow): # Main window def __init__(self): super().__init__() self.setWindowTitle = 'DD烤肉机' self.resize(1870, 820) self.mainWidget = QWidget() self.mainLayout = QGridLayout() # Grid layout self.mainLayout.setSpacing(10) self.mainWidget.setLayout(self.mainLayout) self.duration = 60000 self.bitrate = 2000 self.fps = 60 self.initProcess = InitProcess() self.previewSubtitle = PreviewSubtitle() self.dnldWindow = YoutubeDnld() self.exportWindow = exportSubtitle() self.videoDecoder = VideoDecoder() self.exportWindow.exportArgs.connect(self.exportSubtitle) self.stack = QStackedWidget() self.stack.setFixedWidth(1300) self.mainLayout.addWidget(self.stack, 0, 0, 10, 8) buttonWidget = QWidget() buttonLayout = QHBoxLayout() buttonWidget.setLayout(buttonLayout) self.playButton = QPushButton('从本地打开') self.playButton.clicked.connect(self.open) self.playButton.setFixedWidth(400) self.playButton.setFixedHeight(75) self.dnldButton = QPushButton('Youtube下载器') self.dnldButton.clicked.connect(self.popDnld) self.dnldButton.setFixedWidth(400) self.dnldButton.setFixedHeight(75) buttonLayout.addWidget(self.playButton) buttonLayout.addWidget(self.dnldButton) self.stack.addWidget(buttonWidget) self.videoPath = '' self.videoWidth = 1920 self.videoHeight = 1080 self.globalInterval = 200 self.setPlayer() self.setSubtitle() self.setToolBar() self.setCentralWidget(self.mainWidget) self.playStatus = False self.volumeStatus = True self.volumeValue = 100 self.subSelectedTxt = '' self.subReplayTime = 1 self.clipBoard = [] self.grabKeyboard() self.show() def setPlayer(self): self.playerWidget = QGraphicsVideoItem() self.scene = QGraphicsScene() self.view = QGraphicsView(self.scene) self.view.resize(1280, 730) self.scene.addItem(self.playerWidget) self.stack.addWidget(self.view) self.player = QMediaPlayer(self, QMediaPlayer.VideoSurface) self.player.setVideoOutput(self.playerWidget) self.view.installEventFilter(self) self.view.show() self.srtTextItemDict = {0: QGraphicsTextItem(), 1: QGraphicsTextItem(), 2: QGraphicsTextItem(), 3: QGraphicsTextItem(), 4: QGraphicsTextItem()} for _, srtTextItem in self.srtTextItemDict.items(): self.scene.addItem(srtTextItem) def setSubtitle(self): self.subtitleDict = {x: {-1: [100, '']} for x in range(5)} self.subTimer = QTimer() self.subTimer.setInterval(100) self.subtitle = QTableWidget() self.subtitle.setAutoScroll(False) self.subtitle.setEditTriggers(QAbstractItemView.NoEditTriggers) self.mainLayout.addWidget(self.subtitle, 0, 8, 10, 12) self.subtitle.setColumnCount(5) self.subtitle.selectRow(0) self.subtitle.setHorizontalHeaderLabels(['%s' % (i + 1) for i in range(5)]) self.subtitle.setVerticalHeaderLabels([cnt2Time2(i, self.globalInterval) for i in range(self.subtitle.rowCount())]) for index in range(5): self.subtitle.setColumnWidth(index, 130) self.subtitle.setHorizontalScrollMode(QAbstractItemView.ScrollPerPixel) self.subtitle.setEditTriggers(QAbstractItemView.DoubleClicked) self.subtitle.horizontalHeader().sectionClicked.connect(self.addSubtitle) self.subtitle.doubleClicked.connect(self.releaseKeyboard) self.subtitle.cellChanged.connect(self.subEdit) self.subtitle.verticalHeader().sectionClicked.connect(self.subHeaderClick) self.subtitle.setContextMenuPolicy(Qt.CustomContextMenu) self.subtitle.customContextMenuRequested.connect(self.popTableMenu) self.initSubtitle() def initSubtitle(self): self.initProcess.show() self.subtitle.cellChanged.disconnect(self.subEdit) for x in range(self.subtitle.columnCount()): for y in range(self.subtitle.rowCount()): self.subtitle.setSpan(y, x, 1, 1) self.subtitle.setRowCount(self.duration // self.globalInterval + 1) for x in range(self.subtitle.columnCount()): for y in range(self.subtitle.rowCount()): self.subtitle.setItem(y, x, QTableWidgetItem('')) self.subtitle.item(y, x).setBackground(QBrush(QColor('#232629'))) self.subtitle.setVerticalHeaderLabels([cnt2Time2(i, self.globalInterval) for i in range(self.subtitle.rowCount())]) self.subtitle.cellChanged.connect(self.subEdit) self.initProcess.hide() def addSubtitle(self, index): subtitlePath = QFileDialog.getOpenFileName(self, "请选择字幕", None, "字幕文件 (*.srt *.vtt *.ass *.ssa)")[0] if subtitlePath: self.initProcess.show() self.subtitle.cellChanged.disconnect(self.subEdit) if subtitlePath.endswith('.ass') or subtitlePath.endswith('.ssa'): p = subprocess.Popen(['utils/ffmpeg.exe', '-y', '-i', subtitlePath, 'temp_sub.srt']) p.wait() subtitlePath = 'temp_sub.srt' subData = {} with open(subtitlePath, 'r', encoding='utf-8') as f: f = f.readlines() subText = '' YoutubeAutoSub = False for l in f: if '<c>' in l: YoutubeAutoSub = True break for cnt, l in enumerate(f): if '<c>' in l: lineData = l.split('c>') if len(lineData) > 3: subText, start, _ = lineData[0].split('<') start = calSubTime(start[:-1]) // self.globalInterval * self.globalInterval if start not in self.subtitleDict[index]: end = calSubTime(lineData[-3][1:-2]) // self.globalInterval * self.globalInterval for i in range(len(lineData) // 2): subText += lineData[i * 2 + 1][:-2] subData[start] = [end - start, subText] else: subText, start, _ = lineData[0].split('<') start = calSubTime(start[:-1]) // self.globalInterval * self.globalInterval if start not in self.subtitleDict[index]: subText += lineData[1][:-2] subData[start] = [self.globalInterval, subText] elif '-->' in l and f[cnt + 2].strip() and '<c>' not in f[cnt + 2]: subText = f[cnt + 2][:-1] start = calSubTime(l[:12]) // self.globalInterval * self.globalInterval if start not in self.subtitleDict[index]: end = calSubTime(l[17:29]) // self.globalInterval * self.globalInterval subData[start] = [end - start, subText] if '-->' in l and f[cnt + 1].strip() and not YoutubeAutoSub: start = calSubTime(l[:12]) // self.globalInterval * self.globalInterval if start not in self.subtitleDict[index]: end = calSubTime(l[17:29]) // self.globalInterval * self.globalInterval delta = end - start if delta > 10: if '<b>' in f[cnt + 1]: subData[start] = [delta, f[cnt + 1].split('<b>')[1].split('<')[0]] else: subData[start] = [delta, f[cnt + 1][:-1]] self.subtitleDict[index].update(subData) maxRow = 0 for _, v in self.subtitleDict.items(): startMax = max(v.keys()) rowCount = (startMax + v[startMax][0]) // self.globalInterval if rowCount > maxRow: maxRow = rowCount if maxRow < self.duration // self.globalInterval + 1: maxRow = self.duration // self.globalInterval else: self.duration = maxRow * self.globalInterval self.subtitle.setRowCount(maxRow) self.subtitle.setVerticalHeaderLabels([cnt2Time2(i, self.globalInterval) for i in range(self.subtitle.rowCount())]) for start, rowData in subData.items(): startRow = start // self.globalInterval endRow = startRow + rowData[0] // self.globalInterval for row in range(startRow, endRow): self.subtitle.setItem(row, index, QTableWidgetItem(rowData[1])) self.subtitle.item(row, index).setBackground(QBrush(QColor('#35545d'))) self.subtitle.setSpan(startRow, index, endRow - startRow, 1) self.refreshComboBox() self.subtitle.cellChanged.connect(self.subEdit) self.initProcess.hide() def subTimeOut(self): fontColor = self.previewSubtitle.fontColor fontSize = (self.previewSubtitle.fontSize + 5) / 2.5 fontBold = self.previewSubtitle.bold fontItalic = self.previewSubtitle.italic fontShadowOffset = self.previewSubtitle.shadowOffset for _, srtTextItem in self.srtTextItemDict.items(): srtTextItem.setDefaultTextColor(fontColor) font = QFont() font.setFamily("微软雅黑") font.setPointSize(fontSize) font.setBold(fontBold) font.setItalic(fontItalic) srtTextItem.setFont(font) srtTextShadow = QGraphicsDropShadowEffect() srtTextShadow.setOffset(fontShadowOffset) srtTextItem.setGraphicsEffect(srtTextShadow) try: selected = self.subtitle.selectionModel().selection().indexes() for x, i in enumerate(selected): if self.subtitle.item(i.row(), x): txt = self.subtitle.item(i.row(), x).text() if txt: self.srtTextItemDict[x].setPlainText('#%s:' % (x + 1) + txt) txtSize = self.srtTextItemDict[x].boundingRect().size() posY = self.playerWidget.size().height() - txtSize.height() * (x + 1) posX = (self.playerWidget.size().width() - txtSize.width()) / 2 self.srtTextItemDict[x].setPos(posX, posY) else: self.srtTextItemDict[x].setPlainText('') else: self.srtTextItemDict[x].setPlainText('') except: pass def subHeaderClick(self, index): if self.player.duration(): position = index * self.globalInterval self.player.setPosition(position) self.videoSlider.setValue(position * 1000 // self.player.duration()) self.setTimeLabel() def subEdit(self, row, index): repeat = self.subtitle.rowSpan(row, index) self.setSubtitleDict(row, index, repeat, self.subtitle.item(row, index).text()) self.subtitle.cellChanged.disconnect(self.subEdit) for cnt in range(repeat): if self.subtitle.item(row + cnt, index).text(): self.subtitle.item(row, index).setBackground(QBrush(QColor('#35545d'))) else: self.subtitle.item(row, index).setBackground(QBrush(QColor('#232629'))) self.subtitle.cellChanged.connect(self.subEdit) def setSubtitleDict(self, row, index, num, text): self.subtitleDict[index][row * self.globalInterval] = [num * self.globalInterval, text] def popTableMenu(self, pos): self.subtitle.cellChanged.disconnect(self.subEdit) pos = QPoint(pos.x() + 55, pos.y() + 30) menu = QMenu() copy = menu.addAction('复制') paste = menu.addAction('粘贴') setSpan = menu.addAction('合并') clrSpan = menu.addAction('拆分') addSub = menu.addAction('导入字幕') cutSub = menu.addAction('裁剪字幕') action = menu.exec_(self.subtitle.mapToGlobal(pos)) selected = self.subtitle.selectionModel().selection().indexes() yList = [selected[0].row(), selected[-1].row()] xSet = set() for i in range(len(selected)): xSet.add(selected[i].column()) if action == copy: for x in xSet: self.clipBoard = [] for y in range(yList[0], yList[1] + 1): if self.subtitle.item(y, x): self.clipBoard.append(self.subtitle.item(y, x).text()) else: self.clipBoard.append('') break elif action == paste: self.subtitle.cellChanged.connect(self.subEdit) for x in xSet: for cnt, text in enumerate(self.clipBoard): self.subtitle.setItem(yList[0] + cnt, x, QTableWidgetItem(text)) self.subtitleDict[x][(yList[0] + cnt) * self.globalInterval] = [self.globalInterval, text] self.subtitle.cellChanged.disconnect(self.subEdit) elif action == setSpan: for x in xSet: if not self.subtitle.item(yList[0], x): firstItem = '' else: firstItem = self.subtitle.item(yList[0], x).text() for y in range(yList[0], yList[1] + 1): self.subtitle.setSpan(y, x, 1, 1) self.subtitle.setItem(y, x, QTableWidgetItem(firstItem)) self.subtitle.item(y, x).setBackground(QBrush(QColor('#35545d'))) if y * self.globalInterval in self.subtitleDict[x]: del self.subtitleDict[x][y * self.globalInterval] for x in xSet: self.subtitle.setSpan(yList[0], x, yList[1] - yList[0] + 1, 1) self.setSubtitleDict(yList[0], x, yList[1] - yList[0] + 1, firstItem) elif action == clrSpan: for x in xSet: if not self.subtitle.item(yList[0], x): firstItem = '' else: firstItem = self.subtitle.item(yList[0], x).text() for cnt, y in enumerate(range(yList[0], yList[1] + 1)): self.subtitle.setSpan(y, x, 1, 1) if not cnt: self.subtitle.setItem(yList[0], x, QTableWidgetItem(firstItem)) if firstItem: self.subtitle.item(y, x).setBackground(QBrush(QColor('#35545d'))) else: self.subtitle.item(y, x).setBackground(QBrush(QColor('#232629'))) else: self.subtitle.setItem(y, x, QTableWidgetItem('')) self.subtitle.item(y, x).setBackground(QBrush(QColor('#232629'))) self.setSubtitleDict(yList[0], x, yList[1] - yList[0] + 1, firstItem) break elif action == addSub: self.subtitle.cellChanged.connect(self.subEdit) for x in xSet: self.addSubtitle(x) self.subtitle.cellChanged.disconnect(self.subEdit) elif action == cutSub: for x in xSet: start = yList[0] * self.globalInterval end = yList[1] * self.globalInterval self.exportSubWindow(start, end, x + 1) self.subtitle.cellChanged.connect(self.subEdit) def setToolBar(self): ''' menu bar, file menu, play menu, tool bar. ''' toolBar = QToolBar() self.setContextMenuPolicy(Qt.NoContextMenu) self.addToolBar(toolBar) fileMenu = self.menuBar().addMenu('&文件') openAction = QAction(QIcon.fromTheme('document-open'), '&打开...', self, shortcut=QKeySequence.Open, triggered=self.open) fileMenu.addAction(openAction) downloadAction = QAction(QIcon.fromTheme('document-open'), '&Youtube下载器', self, triggered=self.popDnld) fileMenu.addAction(downloadAction) exitAction = QAction(QIcon.fromTheme('application-exit'), '&退出', self, shortcut='Ctrl+Q', triggered=self.close) fileMenu.addAction(exitAction) playMenu = self.menuBar().addMenu('&功能') self.playIcon = self.style().standardIcon(QStyle.SP_MediaPlay) self.pauseIcon = self.style().standardIcon(QStyle.SP_MediaPause) self.playAction = toolBar.addAction(self.playIcon, '播放') self.playAction.triggered.connect(self.mediaPlay) self.volumeIcon = self.style().standardIcon(QStyle.SP_MediaVolume) self.volumeMuteIcon = self.style().standardIcon(QStyle.SP_MediaVolumeMuted) self.volumeAction = toolBar.addAction(self.volumeIcon, '静音') self.volumeAction.triggered.connect(self.volumeMute) previewAction = QAction(QIcon.fromTheme('document-open'), '&设置预览字幕', self, triggered=self.popPreview) playMenu.addAction(previewAction) decodeMenu = self.menuBar().addMenu('&输出') decodeAction = QAction(QIcon.fromTheme('document-open'), '&输出字幕及视频', self, triggered=self.decode) decodeMenu.addAction(decodeAction) self.volSlider = Slider() self.volSlider.setOrientation(Qt.Horizontal) self.volSlider.setMinimum(0) self.volSlider.setMaximum(100) self.volSlider.setFixedWidth(120) self.volSlider.setValue(self.player.volume()) self.volSlider.setToolTip(str(self.volSlider.value())) self.volSlider.pointClicked.connect(self.setVolume) toolBar.addWidget(self.volSlider) self.videoPositionEdit = LineEdit('00:00') self.videoPositionEdit.setAlignment(Qt.AlignRight) self.videoPositionEdit.setFixedWidth(75) self.videoPositionEdit.setFont(QFont('Timers', 14)) self.videoPositionEdit.clicked.connect(self.mediaPauseOnly) self.videoPositionEdit.editingFinished.connect(self.mediaPlayOnly) self.videoPositionLabel = QLabel(' / 00:00 ') self.videoPositionLabel.setFont(QFont('Timers', 14)) toolBar.addWidget(QLabel(' ')) toolBar.addWidget(self.videoPositionEdit) toolBar.addWidget(self.videoPositionLabel) self.timer = QTimer() self.timer.setInterval(100) self.videoSlider = Slider() self.videoSlider.setEnabled(False) self.videoSlider.setOrientation(Qt.Horizontal) self.videoSlider.setMinimum(0) self.videoSlider.setMaximum(1000) self.videoSlider.setFixedWidth(1000) self.videoSlider.sliderMoved.connect(self.timeStop) self.videoSlider.sliderReleased.connect(self.timeStart) self.videoSlider.pointClicked.connect(self.videoSliderClick) toolBar.addWidget(self.videoSlider) toolBar.addWidget(QLabel(' ')) self.globalIntervalComBox = QComboBox() self.globalIntervalComBox.addItems(['间隔 100ms', '间隔 200ms', '间隔 500ms', '间隔 1s']) self.globalIntervalComBox.setCurrentIndex(1) self.globalIntervalComBox.currentIndexChanged.connect(self.setGlobalInterval) toolBar.addWidget(self.globalIntervalComBox) toolBar.addWidget(QLabel(' ')) self.subEditComBox = QComboBox() self.refreshComboBox() toolBar.addWidget(self.subEditComBox) toolBar.addWidget(QLabel(' ')) moveForward = QPushButton('- 1') moveForward.setFixedWidth(50) toolBar.addWidget(moveForward) toolBar.addWidget(QLabel(' ')) moveAfterward = QPushButton('+ 1') moveAfterward.setFixedWidth(50) toolBar.addWidget(moveAfterward) toolBar.addWidget(QLabel(' ')) clearSub = QPushButton('清空') clearSub.setFixedWidth(50) toolBar.addWidget(clearSub) toolBar.addWidget(QLabel(' ')) outputSub = QPushButton('裁剪') outputSub.setFixedWidth(50) toolBar.addWidget(outputSub) moveForward.clicked.connect(self.moveForward) moveAfterward.clicked.connect(self.moveAfterward) clearSub.clicked.connect(self.clearSub) outputSub.clicked.connect(self.exportSubWindow) def setGlobalInterval(self, index): if not self.playStatus: self.mediaPlay() self.globalInterval = {0: 100, 1: 200, 2: 500, 3: 1000}[index] self.initSubtitle() self.initProcess.show() self.subtitle.cellChanged.disconnect(self.subEdit) for index, subData in self.subtitleDict.items(): for start, rowData in subData.items(): startRow = start // self.globalInterval deltaRow = rowData[0] // self.globalInterval if deltaRow: endRow = startRow + deltaRow for row in range(startRow, endRow): self.subtitle.setItem(row, index, QTableWidgetItem(rowData[1])) if row >= 0: self.subtitle.item(row, index).setBackground(QBrush(QColor('#35545d'))) self.subtitle.setSpan(startRow, index, endRow - startRow, 1) self.subtitle.cellChanged.connect(self.subEdit) self.initProcess.hide() def moveForward(self): self.initProcess.show() self.subtitle.cellChanged.disconnect(self.subEdit) index = self.subEditComBox.currentIndex() for y in range(self.subtitle.rowCount()): self.subtitle.setSpan(y, index, 1, 1) self.subtitle.setItem(y, index, QTableWidgetItem('')) self.subtitle.item(y, index).setBackground(QBrush(QColor('#232629'))) tmpDict = self.subtitleDict[index] self.subtitleDict[index] = {} for start, rowData in tmpDict.items(): self.subtitleDict[index][start - self.globalInterval] = rowData for start, rowData in self.subtitleDict[index].items(): startRow = start // self.globalInterval endRow = startRow + rowData[0] // self.globalInterval for row in range(startRow, endRow): self.subtitle.setItem(row, index, QTableWidgetItem(rowData[1])) self.subtitle.item(row, index).setBackground(QBrush(QColor('#35545d'))) self.subtitle.setSpan(startRow, index, endRow - startRow, 1) self.subtitle.cellChanged.connect(self.subEdit) self.initProcess.hide() def moveAfterward(self): self.initProcess.show() self.subtitle.cellChanged.disconnect(self.subEdit) index = self.subEditComBox.currentIndex() for y in range(self.subtitle.rowCount()): self.subtitle.setSpan(y, index, 1, 1) self.subtitle.setItem(y, index, QTableWidgetItem('')) self.subtitle.item(y, index).setBackground(QBrush(QColor('#232629'))) tmpDict = self.subtitleDict[index] self.subtitleDict[index] = {} for start, rowData in tmpDict.items(): self.subtitleDict[index][start + self.globalInterval] = rowData for start, rowData in self.subtitleDict[index].items(): startRow = start // self.globalInterval endRow = startRow + rowData[0] // self.globalInterval for row in range(startRow, endRow): self.subtitle.setItem(row, index, QTableWidgetItem(rowData[1])) self.subtitle.item(row, index).setBackground(QBrush(QColor('#35545d'))) self.subtitle.setSpan(startRow, index, endRow - startRow, 1) self.subtitle.cellChanged.connect(self.subEdit) self.initProcess.hide() def clearSub(self): index = self.subEditComBox.currentIndex() reply = QMessageBox.information(self, '清空字幕', '清空第 %s 列字幕条?' % (index + 1), QMessageBox.Yes | QMessageBox.No) if reply == QMessageBox.Yes: self.initProcess.show() self.subtitle.cellChanged.disconnect(self.subEdit) self.subtitleDict[index] = {0: [self.globalInterval, '']} for i in range(self.subtitle.rowCount()): self.subtitle.setSpan(i, index, 1, 1) self.subtitle.setItem(i, index, QTableWidgetItem('')) self.subtitle.item(i, index).setBackground(QBrush(QColor('#232629'))) self.subtitle.setHorizontalHeaderItem(index, QTableWidgetItem('%s' % (index + 1))) self.subtitle.cellChanged.connect(self.subEdit) self.initProcess.hide() def exportSubWindow(self, start=0, end=0, index=None): self.releaseKeyboard() self.exportWindow.hide() self.exportWindow.show() start = '00:00.0' if not start else self.splitTime(start) end = self.splitTime(self.duration) if not end else self.splitTime(end) if not index: index = self.subEditComBox.currentIndex() + 1 self.exportWindow.setDefault(start, end, index) def exportSubtitle(self, exportArgs): start = calSubTime2(exportArgs[0]) end = calSubTime2(exportArgs[1]) subStart = calSubTime2(exportArgs[2]) index = exportArgs[3] - 1 subData = self.subtitleDict[index] rowList = sorted(subData.keys()) exportRange = [] for t in rowList: if t >= start and t <= end: exportRange.append(t) subNumber = 1 with open(exportArgs[-1], 'w', encoding='utf-8') as exportFile: for t in exportRange: text = subData[t][1] if text: start = ms2Time(t + subStart) end = ms2Time(t + subStart + subData[t][0]) exportFile.write('%s\n%s --> %s\n%s\n\n' % (subNumber, start, end, text)) subNumber += 1 QMessageBox.information(self, '导出字幕', '导出完成', QMessageBox.Yes) self.exportWindow.hide() def refreshComboBox(self): self.subEditComBox.clear() for i in range(self.subtitle.columnCount()): self.subEditComBox.addItem('字幕 ' + str(i + 1)) def open(self): self.videoPath = QFileDialog.getOpenFileName(self, "请选择视频文件", None, "MP4格式 (*.mp4);;所有文件(*.*)")[0] if self.videoPath: cmd = ['utils/ffmpeg.exe', '-i', self.videoPath] p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) p.wait() for l in p.stdout.readlines(): l = l.decode('utf8') if 'Duration' in l: self.duration = calSubTime(l.split(' ')[3][:-1]) if 'Stream' in l and 'DAR' in l: self.videoWidth, self.videoHeight = map(int, l.split(' [')[0].split(' ')[-1].split('x')) args = l.split(',') for cnt, arg in enumerate(args): if 'kb' in arg: self.bitrate = int(arg.split('kb')[0]) self.fps = int(args[cnt + 1].split('fps')[0]) break break self.initProcess.show() self.subtitle.cellChanged.disconnect(self.subEdit) self.subtitle.setRowCount(self.duration // self.globalInterval + 1) self.subtitle.setVerticalHeaderLabels([cnt2Time2(i, self.globalInterval) for i in range(self.subtitle.rowCount())]) self.subtitle.cellChanged.connect(self.subEdit) self.initProcess.hide() url = QUrl.fromLocalFile(self.videoPath) self.stack.setCurrentIndex(1) self.playerWidget.setSize(QSizeF(1280, 720)) self.player.setMedia(url) self.playStatus = True self.videoSlider.setEnabled(True) self.mediaPlay() self.timer.start() self.timer.timeout.connect(self.timeOut) self.subTimer.start() self.subTimer.timeout.connect(self.subTimeOut) def popDnld(self): self.releaseKeyboard() self.dnldWindow.hide() self.dnldWindow.show() def popPreview(self): self.releaseKeyboard() self.previewSubtitle.hide() self.previewSubtitle.show() def decode(self): self.releaseKeyboard() self.videoDecoder.setDefault(self.videoPath, self.videoWidth, self.videoHeight, self.duration, self.bitrate, self.fps, self.subtitleDict) self.videoDecoder.hide() self.videoDecoder.show() def mediaPlay(self): if self.playStatus: self.player.play() self.grabKeyboard() self.timeStart() self.playStatus = False self.playAction.setIcon(self.pauseIcon) self.playAction.setText('暂停') else: self.player.pause() self.timeStop() self.playStatus = True self.playAction.setIcon(self.playIcon) self.playAction.setText('播放') def mediaPlayOnly(self): self.grabKeyboard() try: timeText = self.videoPositionEdit.text().split(':') m, s = timeText[:2] if not m: m = '00' if not s: s = '00' if len(m) > 3: m = m[:3] if len(s) > 2: s = s[:2] if m.isdigit(): m = int(m) if s.isdigit(): s = int(s) if s > 60: s = 60 total_m = self.player.duration() // 60000 if m > total_m: m = total_m self.player.setPosition(m * 60000 + s * 1000) self.videoSlider.setValue(self.player.position() * 1000 / self.player.duration()) except: pass self.videoPositionEdit.setReadOnly(True) self.timeStart() def mediaPauseOnly(self): self.releaseKeyboard() self.videoPositionEdit.setReadOnly(False) self.player.pause() self.timeStop() self.playStatus = True self.playAction.setIcon(self.playIcon) self.playAction.setText('播放') def splitTime(self, playTime): playTime = playTime // 1000 m = str(playTime // 60) s = playTime % 60 s = ('0%s' % s)[-2:] if len(m) > 2: t = '%3s:%2s' % (m, s) else: t = '%2s:%2s' % (m, s) return t def timeOut(self): row = self.player.position() // self.globalInterval self.subtitle.selectRow(row) self.subtitle.verticalScrollBar().setValue(row - 10) if self.dnldWindow.isHidden() or self.exportWindow.isHidden() or self.videoDecoder.isHidden(): self.grabKeyboard() try: self.videoSlider.setValue(self.player.position() * 1000 / self.player.duration()) self.setTimeLabel() except: pass def timeStop(self): self.timer.stop() def timeStart(self): self.timer.start() def videoSliderClick(self, p): self.videoSlider.setValue(p.x()) self.player.setPosition(p.x() * self.player.duration() // 1000) self.setTimeLabel() def setVolume(self, p): self.volumeValue = p.x() if self.volumeValue > 100: self.volumeValue = 100 if self.volumeValue < 0: self.volumeValue = 0 self.volSlider.setValue(self.volumeValue) self.player.setVolume(self.volumeValue) self.volSlider.setToolTip(str(self.volSlider.value())) if self.volumeValue: self.volumeStatus = True self.volumeAction.setIcon(self.volumeIcon) else: self.volumeStatus = False self.volumeAction.setIcon(self.volumeMuteIcon) def volumeMute(self): if self.volumeStatus: self.volumeStatus = False self.old_volumeValue = self.player.volume() self.player.setVolume(0) self.volSlider.setValue(0) self.volumeAction.setIcon(self.volumeMuteIcon) else: self.volumeStatus = True self.player.setVolume(self.old_volumeValue) self.volSlider.setValue(self.old_volumeValue) self.volumeAction.setIcon(self.volumeIcon) def setTimeLabel(self): now = self.player.position() total = self.player.duration() now = self.splitTime(now) total = self.splitTime(total) self.videoPositionEdit.setText(now) self.videoPositionLabel.setText(' / %s ' % total) def eventFilter(self, obj, event): if obj == self.view: if event.type() == QEvent.MouseButtonPress: self.mediaPlay() return QMainWindow.eventFilter(self, obj, event) def keyPressEvent(self, QKeyEvent): key = QKeyEvent.key() if key == Qt.Key_Left: if self.videoSlider.isEnabled(): self.player.setPosition(self.player.position() - 5000) self.videoSlider.setValue(self.player.position() * 1000 / self.player.duration()) self.setTimeLabel() elif key == Qt.Key_Right: if self.videoSlider.isEnabled(): self.player.setPosition(self.player.position() + 5000) self.videoSlider.setValue(self.player.position() * 1000 / self.player.duration()) self.setTimeLabel() elif key == Qt.Key_Up: self.volumeValue += 10 if self.volumeValue > 100: self.volumeValue = 100 self.volSlider.setValue(self.volumeValue) self.player.setVolume(self.volumeValue) elif key == Qt.Key_Down: self.volumeValue -= 10 if self.volumeValue < 0: self.volumeValue = 0 self.volSlider.setValue(self.volumeValue) self.player.setVolume(self.volumeValue) elif key == Qt.Key_Space: self.mediaPlay()
class Ui_MainWindow(object): def setupUi(self, MainWindow): def fun(spoiler): if spoiler.isOpened(): spoiler.close() else: spoiler.open() # app = QApplication(sys.argv) w = QMainWindow() cw = QWidget() mainLayout = QHBoxLayout() cw.setLayout(mainLayout) gridLayout = QGridLayout() spacerItem1 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) gridLayout.addItem(spacerItem1, 0, 0, 1, 1) pushButton1 = QPushButton() pushButton1.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Preferred) pushButton1.setText("") # gridLayout.addWidget(pushButton1, 0, 1, 1, 1) self.label1 = QLabel() self.label1.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum) self.label1.setObjectName("") gridLayout.addWidget(self.label1, 0, 2, 1, 1) datePicker1 = DatePicker() gridLayout.addWidget(datePicker1, 0, 1, 1, 1) datePicker1.selectionChanged.connect(lambda: self.__setLableDate__( self.label1, datePicker1.selectedDate())) spacerItem2 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) gridLayout.addItem(spacerItem2, 0, 3, 1, 1) self.label2 = QLabel() self.label2.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum) self.label2.setObjectName("") gridLayout.addWidget(self.label2, 0, 4, 1, 1) datePicker2 = DatePicker() gridLayout.addWidget(datePicker2, 0, 5, 1, 1) datePicker2.selectionChanged.connect(lambda: self.__setLableDate__( self.label2, datePicker2.selectedDate())) pushButton2 = QPushButton() pushButton2.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Preferred) pushButton2.setText("") #gridLayout.addWidget(pushButton2, 0, 5, 1, 1) spacerItem3 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) gridLayout.addItem(spacerItem3, 0, 6, 1, 1) groupBox = QGroupBox() groupBox.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Maximum) verticalLayout = QVBoxLayout(groupBox) radioButton1 = QRadioButton(groupBox) radioButton1.setText("Subscribers") verticalLayout.addWidget(radioButton1) radioButton2 = QRadioButton(groupBox) radioButton1.setText("PatientsServices") verticalLayout.addWidget(radioButton2) export_btn = QPushButton() export_btn.setText("export") gridLayout.addWidget(export_btn) export_btn.clicked.connect(lambda: self.onExport_btn()) gridLayout.addWidget(groupBox, 1, 1, 1, 2, Qt.AlignTop) self.comboBox = QComboBox() self.h_values = [ "all", "مستشفي الصفوة الدولي", "مختبر مصراتة المركزي", "مستشفى الحكمه الحديث" ] self.comboBox.addItems(self.h_values) if self.comboBox.currentIndex() == 1: print("kln") self.comboBox.activated.connect(self.oncomboBoxChanged) gridLayout.addWidget(self.comboBox, 1, 4, 1, 2, Qt.AlignTop) btn = QPushButton("Click me") btn.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum) spoiler = Spoiler(Spoiler.Orientation.VERTICAL) spoiler.setContentLayout(gridLayout) mainLayout.addWidget(btn) #self.searchGridLayout.addWidget(spoiler) # mainLayout.setAlignment(Qt.AlignRight) MainWindow.setObjectName(_fromUtf8("MainWindow")) MainWindow.resize(716, 392) self.centralwidget = QtWidgets.QWidget(MainWindow) self.centralwidget.setObjectName(_fromUtf8("centralwidget")) self.verticalLayout = QtWidgets.QVBoxLayout(self.centralwidget) self.verticalLayout.setObjectName(_fromUtf8("verticalLayout")) self.tabWidget = QtWidgets.QTabWidget(self.centralwidget) self.tabWidget.setTabsClosable(False) self.tabWidget.setObjectName(_fromUtf8("tabWidget")) self.InsertTab = QtWidgets.QWidget() self.InsertTab.setObjectName(_fromUtf8("InsertTab")) self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.InsertTab) self.verticalLayout_2.setObjectName(_fromUtf8("verticalLayout_2")) spacerItem = QtWidgets.QSpacerItem(20, 109, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) self.verticalLayout_2.addItem(spacerItem) self.insertGridLayout = QtWidgets.QGridLayout() self.insertGridLayout.setObjectName(_fromUtf8("insertGridLayout")) self.runBtn = QtWidgets.QPushButton(self.InsertTab) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth( self.runBtn.sizePolicy().hasHeightForWidth()) self.runBtn.setSizePolicy(sizePolicy) self.runBtn.setMouseTracking(False) self.runBtn.setAutoFillBackground(False) self.runBtn.setStyleSheet(_fromUtf8("border: none")) self.runBtn.setText(_fromUtf8("")) icon = QtGui.QIcon() icon.addPixmap(QtGui.QPixmap(_fromUtf8("icons/run.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.runBtn.setIcon(icon) self.runBtn.setIconSize(QtCore.QSize(64, 64)) self.runBtn.setObjectName(_fromUtf8("runBtn")) self.insertGridLayout.addWidget(self.runBtn, 0, 3, 2, 1) spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) self.combo = QComboBox() self.values = [ "مستشفي الصفوة الدولي", "مختبر مصراتة المركزي", "مستشفى الحكمه الحديث", "مستشفى المدينه السكنيه" ] self.combo.addItems(self.values) if self.combo.currentIndex() == 1: print("kln") self.combo.activated.connect(self.oncomboChanged) self.insertGridLayout.addWidget(self.combo, 1, 4, 1, 2, Qt.AlignTop) self.insertGridLayout.addItem(spacerItem1, 0, 5, 1, 1) self.excelLabel = QtWidgets.QLabel(self.InsertTab) self.excelLabel.setText(_fromUtf8("")) self.excelLabel.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignTrailing | QtCore.Qt.AlignVCenter) self.excelLabel.setObjectName(_fromUtf8("excelLabel")) self.insertGridLayout.addWidget(self.excelLabel, 1, 0, 1, 3) self.sqlBtn = QtWidgets.QPushButton(self.InsertTab) self.sqlBtn.setMinimumSize(QtCore.QSize(200, 0)) self.sqlBtn.setObjectName(_fromUtf8("sqlBtn")) self.insertGridLayout.addWidget(self.sqlBtn, 0, 4, 1, 1) self.excelBtn = QtWidgets.QPushButton(self.InsertTab) self.excelBtn.setMinimumSize(QtCore.QSize(200, 0)) self.excelBtn.setObjectName(_fromUtf8("excelBtn")) self.insertGridLayout.addWidget(self.excelBtn, 0, 2, 1, 1) spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) self.insertGridLayout.addItem(spacerItem2, 0, 1, 1, 1) self.sqlLabel = QtWidgets.QLabel(self.InsertTab) self.sqlLabel.setText(_fromUtf8("")) self.sqlLabel.setObjectName(_fromUtf8("sqlLabel")) self.insertGridLayout.addWidget(self.sqlLabel, 1, 4, 1, 2) self.verticalLayout_2.addLayout(self.insertGridLayout) spacerItem3 = QtWidgets.QSpacerItem(20, 108, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) self.verticalLayout_2.addItem(spacerItem3) self.tabWidget.addTab(self.InsertTab, _fromUtf8("")) # self.filterBtn = QtWidgets.QPushButton(self.searchTab) # self.filterBtn.setStyleSheet(_fromUtf8("border: none")) # icon2 = QtGui.QIcon() # icon2.addPixmap(QtGui.QPixmap(_fromUtf8("icons/filter_icon.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) # self.filterBtn.setIcon(icon2) # self.filterBtn.setObjectName(_fromUtf8("filterBtn")) # self.searchGridLayout.addWidget(self.filterBtn, 0, 3, 1, 1) # self.searchGridLayout.addWidget(spoiler) self.export_btn = QPushButton() self.export_btn.setText("export") #self.gridLayout.addWidget(export_btn) self.export_btn.clicked.connect(lambda: self.onExport_btnSubscribers()) #self.exprt_btn = QtWidgets.QPushButton() #self.exprt_btn.setText("export") #self.exprt_btn.setObjectName(_fromUtf8("exprt_btn")) #self.searchGridLayout1.addWidget(self.exprt_btn, 0, 0, 2, 1) self.verticalLayout.addWidget(self.tabWidget) MainWindow.setCentralWidget(self.centralwidget) self.statusbar = QtWidgets.QStatusBar(MainWindow) self.statusbar.setObjectName(_fromUtf8("statusbar")) MainWindow.setStatusBar(self.statusbar) self.retranslateUi(MainWindow) self.tabWidget.setCurrentIndex(1) QtCore.QMetaObject.connectSlotsByName(MainWindow) self.excelBtn.clicked.connect(MainWindow, QtCore.SLOT("onExcelBtnClick()")) self.sqlBtn.clicked.connect(MainWindow, QtCore.SLOT("onSqlBtnClick()")) self.runBtn.clicked.connect(MainWindow, QtCore.SLOT("onRunBtnClick()")) self.excelFileName = None self.sqlFileName = "/home/eamon/Desktop/test.sqlite" self.sqlLabel.setText(self.sqlFileName) conn = sqlite3.connect("C:\\Users\\PC WORLD\\Downloads\\test.sqlite") result = conn.execute("SELECT * FROM Subscribers") conn.close() def retranslateUi(self, MainWindow): MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow", None)) self.tabWidget.setToolTip( _translate( "MainWindow", "<html><head/><body><p><span style=\" font-size:18pt;\">Search</span></p></body></html>", None)) self.sqlBtn.setText(_translate("MainWindow", "select sql file", None)) self.excelBtn.setText( _translate("MainWindow", "select exel file", None)) self.tabWidget.setTabText(self.tabWidget.indexOf(self.InsertTab), _translate("MainWindow", "INSERT", None)) def oncomboChanged(self, index): name = self.values[index] print(name) def oncomboBoxChanged(self, index): h_name = self.h_values[index] print(h_name) def onExport_btn(self): db = sqlite3.connect("/home/eamon/Desktop/test.sqlite") if self.h_values[self.comboBox.currentIndex()] != "all": date1 = (self.label1.text()) date2 = (self.label2.text()) format_date1 = "%Y-%m-%d" out_date1 = datetime.strptime(date1, format_date1) print(out_date1) format_date2 = "%Y-%m-%d" out_date2 = datetime.strptime(date2, format_date2) print(out_date2) df1 = pd.read_sql_query( "SELECT * FROM PatientsServices WHERE HospitalName='%s' and date BETWEEN '%s' AND '%s'" % (self.h_values[self.comboBox.currentIndex()], out_date1, out_date2), db) # df.to_excel('output.xlsx') # Create a Pandas Excel writer using XlsxWriter as the engine. writer = pd.ExcelWriter( self.h_values[self.comboBox.currentIndex()] + '.xlsx', engine='xlsxwriter') # Convert the dataframe to an XlsxWriter Excel object. df1.to_excel(writer, sheet_name='Sheet2') # Close the Pandas Excel writer and output the Excel file. writer.save() print(writer) else: date1 = (self.label1.text()) date2 = (self.label2.text()) format_date1 = "%Y-%m-%d" out_date1 = datetime.strptime(date1, format_date1) print(out_date1) format_date2 = "%Y-%m-%d" out_date2 = datetime.strptime(date2, format_date2) print(out_date2) df = pd.read_sql_query( "SELECT * FROM PatientsServices WHERE WorkId='%s' and date BETWEEN '%s' AND '%s'" % (self.searchLE1.text(), out_date1, out_date2), db) # df.to_excel('output.xlsx') # Create a Pandas Excel writer using XlsxWriter as the engine. writer = pd.ExcelWriter('pandas_simple.xlsx', engine='xlsxwriter') # Convert the dataframe to an XlsxWriter Excel object. df.to_excel(writer, sheet_name='Sheet2') # Close the Pandas Excel writer and output the Excel file. writer.save() print(writer) def onExport_btnSubscribers(self): db = sqlite3.connect("/home/eamon/Desktop/test.sqlite") df1 = pd.read_sql_query("SELECT * FROM Subscribers ", db) # df.to_excel('output.xlsx') # Create a Pandas Excel writer using XlsxWriter as the engine. writer = pd.ExcelWriter('السقف.xlsx', engine='xlsxwriter') # Convert the dataframe to an XlsxWriter Excel object. df1.to_excel(writer, sheet_name='Subscribers1') # Close the Pandas Excel writer and output the Excel file. writer.save() print(writer)
class VOGView(AbstractView): def __init__(self, name: str = "VOG_NONE", log_handlers: [StreamHandler] = None): self._logger = getLogger(__name__) if log_handlers: for h in log_handlers: self._logger.addHandler(h) self._logger.debug("Initializing") super().__init__(name) """ Min size for the VOG window """ self._subwindow_height = 222 self._subwindow_width = 518 """ min and max sizes for the configuration popup (width, height) """ self._popup_min = (229, 409) self._popup_max = (300, 409) """ Set configuration value display area""" self._config_frame = EasyFrame() self._config_vertical_layout = QVBoxLayout(self._config_frame) self._config_label = QLabel(self._config_frame) self._config_label.setAlignment(Qt.AlignCenter) self._config_val_line_edit = QLineEdit(self._config_frame) self._config_val_line_edit.setAlignment(Qt.AlignCenter) self._config_vertical_layout.addWidget(self._config_label) self._config_vertical_layout.addWidget(self._config_val_line_edit) """ Set preset button selection area. """ self._nhtsa_button = ClickAnimationButton() self._eblindfold_button = ClickAnimationButton() self._direct_control_button = ClickAnimationButton() """ Set open duration, close duration, and debounce time settings display area. """ self._input_box_frame = EasyFrame() self._input_box_grid_layout = QGridLayout(self._input_box_frame) self._input_box_grid_layout.setContentsMargins(0, 6, 0, 6) self._open_dur_label = QLabel(self._input_box_frame) self._open_dur_line_edit = QLineEdit(self._input_box_frame) self._open_dur_line_edit.setFixedWidth(80) self._open_inf_check_box = QCheckBox(self._input_box_frame) self._close_dur_label = QLabel(self._input_box_frame) self._close_dur_line_edit = QLineEdit(self._input_box_frame) self._close_dur_line_edit.setFixedWidth(80) self._close_inf_check_box = QCheckBox(self._input_box_frame) self._debounce_label = QLabel(self._input_box_frame) self._debounce_time_line_edit = QLineEdit(self._input_box_frame) self._debounce_time_line_edit.setFixedWidth(80) self._input_box_grid_layout.addWidget(self._open_dur_label, 0, 0, 1, 1) self._input_box_grid_layout.addWidget(self._open_dur_line_edit, 0, 1, 1, 1) self._input_box_grid_layout.addWidget(self._open_inf_check_box, 0, 2, 1, 1) self._input_box_grid_layout.addWidget(self._close_dur_label, 1, 0, 1, 1) self._input_box_grid_layout.addWidget(self._close_dur_line_edit, 1, 1, 1, 1) self._input_box_grid_layout.addWidget(self._close_inf_check_box, 1, 2, 1, 1) self._input_box_grid_layout.addWidget(self._debounce_label, 2, 0, 1, 1) self._input_box_grid_layout.addWidget(self._debounce_time_line_edit, 2, 1, 1, 1) """ Set button mode setting display area. """ self._button_mode_frame = EasyFrame() self._button_mode_horiz_layout = QGridLayout(self._button_mode_frame) self._button_mode_horiz_layout.setContentsMargins(0, 6, 0, 6) self._button_mode_label = QLabel(self._button_mode_frame) self._button_mode_selector = QComboBox(self._button_mode_frame) self._button_mode_selector.addItem("") self._button_mode_selector.addItem("") self._button_mode_horiz_layout.addWidget(self._button_mode_label, 0, 0, 1, 1) self._button_mode_horiz_layout.addWidget(self._button_mode_selector, 0, 1, 1, 1) """ Set control mode setting display area. """ self._control_mode_label = QLabel(self._button_mode_frame) self._control_mode_selector = QComboBox(self._button_mode_frame) self._control_mode_selector.addItem("") self._control_mode_selector.addItem("") self._button_mode_horiz_layout.addWidget(self._control_mode_label, 1, 0, 1, 1) self._button_mode_horiz_layout.addWidget(self._control_mode_selector, 1, 1, 1, 1) """ Set upload button selection area. """ self._upload_settings_button = ClickAnimationButton() """ Set manual control selection area. """ self._manual_control_button_frame = EasyFrame() self._manual_control_button_layout = QHBoxLayout( self._manual_control_button_frame) self._manual_control_open_button = ClickAnimationButton() self._manual_control_close_button = ClickAnimationButton() self._manual_control_button_layout.addWidget( self._manual_control_open_button) self._manual_control_button_layout.addWidget( self._manual_control_close_button) self._manual_control_button_layout.setMargin(0) """ device settings display """ self._dev_sets_frame = EasyFrame() self._dev_sets_layout = QVBoxLayout(self._dev_sets_frame) self.config_button = ClickAnimationButton() self.config_button.clicked.connect(self._config_button_handler) self._config_win = ConfigPopUp() self._config_win.setMinimumSize(self._popup_min[0], self._popup_min[1]) self._config_win.setMaximumSize(self._popup_max[0], self._popup_max[1]) self._config_win.setLayout(self._dev_sets_layout) """ Add widgets to layout. """ self._dev_sets_layout.addWidget(self._config_frame) self._dev_sets_layout.addWidget(self._input_box_frame) self._dev_sets_layout.addWidget(self._button_mode_frame) self._dev_sets_layout.addWidget(self._nhtsa_button) self._dev_sets_layout.addWidget(self._eblindfold_button) self._dev_sets_layout.addWidget(self._direct_control_button) self._dev_sets_layout.addWidget(self._upload_settings_button) self.layout().addWidget(self.config_button, 0, 0, Qt.AlignTop | Qt.AlignRight) self.config_button.setFixedSize(30, 25) self.layout().addWidget(self._manual_control_button_frame, 0, 0, Qt.AlignBottom | Qt.AlignLeft) self._manual_control_open_button.setFixedSize(70, 25) self._manual_control_close_button.setFixedSize(70, 25) self.layout().setMargin(0) self._strings = dict() self._lang_enum = LangEnum.ENG self.setMinimumSize(self._subwindow_width, self._subwindow_height) self.resize(self._subwindow_width, self._subwindow_height) self._logger.debug("Initialized") def add_graph(self, graph) -> None: """ Add Graph to view. :return None: """ self._logger.debug("running") self.layout().addWidget(graph, 0, 0) self.config_button.raise_() self._manual_control_button_frame.raise_() self._logger.debug("done") def _config_button_handler(self) -> None: """ handles the config button :return None: """ self._logger.debug("running") self._config_win.exec_() self._logger.debug("done") def set_config_val_line_edit_handler(self, func: classmethod) -> None: """ Sets the config val line handler. :param func: classmethod that handles the config val line. :return None: """ self._logger.debug("running") self._config_val_line_edit.textChanged.connect(func) self._logger.debug("done") def set_nhtsa_button_handler(self, func: classmethod) -> None: """ Sets NHTSA button press handler. :param func: classmethod that handles the NHTSA button. :return None: """ self._logger.debug("running") self._nhtsa_button.clicked.connect(func) self._logger.debug("done") def set_eblindfold_button_handler(self, func: classmethod) -> None: """ Sets eBlindfold button press handler. :param func: classmethod that handles the eBlindfold button. :return None: """ self._logger.debug("running") self._eblindfold_button.clicked.connect(func) self._logger.debug("done") def set_direct_control_button_handler(self, func: classmethod) -> None: """ Sets Direct Control button press handler. :param func: classmethod that handles the Direct Control button. :return None: """ self._logger.debug("running") self._direct_control_button.clicked.connect(func) self._logger.debug("done") def set_open_dur_line_edit_handler(self, func: classmethod) -> None: """ Sets open duration line edit handler. :param func: classmethod that handles the line edit. :return None: """ self._logger.debug("running") self._open_dur_line_edit.textChanged.connect(func) self._logger.debug("done") def set_close_dur_line_edit_handler(self, func: classmethod) -> None: """ Sets close duration line edit handler. :param func: classmethod that handles the line edit. :return None: """ self._logger.debug("running") self._close_dur_line_edit.textChanged.connect(func) self._logger.debug("done") def set_open_inf_check_box_handler(self, func: classmethod) -> None: """ Sets open INF checkbox handler. :param func: classmethod that handles the checkbox, requires bool param. :return None: """ self._logger.debug("running") self._open_inf_check_box.stateChanged.connect(func) self._logger.debug("done") def set_close_inf_check_box_handler(self, func: classmethod) -> None: """ Sets close INF checkbox handler. :param func: classmethod that handles the checkbox, requires bool param. :return None: """ self._logger.debug("running") self._close_inf_check_box.stateChanged.connect(func) self._logger.debug("done") def set_debounce_time_line_edit_handler(self, func: classmethod) -> None: """ Sets debounce time line edit handler. :param func: classmethod that handles the line edit. :return None: """ self._logger.debug("running") self._debounce_time_line_edit.textChanged.connect(func) self._logger.debug("done") def set_button_mode_selector_handler(self, func: classmethod) -> None: """ Sets button mode combo box handler. :param func: classmethod that handles the combo. :return None: """ self._logger.debug("running") self._button_mode_selector.currentIndexChanged.connect(func) self._logger.debug("done") def set_control_mode_selector_handler(self, func: classmethod) -> None: """ Sets button mode combo box handler. :param func: classmethod that handles the combo. :return None: """ self._logger.debug("running") self._control_mode_selector.currentIndexChanged.connect(func) self._logger.debug("done") def set_upload_settings_button_handler(self, func: classmethod) -> None: """ Sets upload settings button handler. :param func: classmethod that handles the button. :return None: """ self._logger.debug("running") self._upload_settings_button.clicked.connect(func) self._logger.debug("done") def set_manual_control_open_button_handler(self, func: classmethod) -> None: """ Sets manual open button handler. :param func: classmethod that handles the button. :return None: """ self._logger.debug("running") self._manual_control_open_button.clicked.connect(func) self._logger.debug("done") def set_manual_control_close_button_handler(self, func: classmethod) -> None: """ Sets manual close button handler. :param func: classmethod that handles the button. :return None: """ self._logger.debug("running") self._manual_control_close_button.clicked.connect(func) self._logger.debug("done") @property def config_text(self) -> str: """ Get the string value found in the config text box. :return str: string value in the text box. """ return self._config_val_line_edit.text() @config_text.setter def config_text(self, val: str) -> None: """ Set the string value found in the config text box. :param val: string value to set the config text box. :return None: """ self._logger.debug("running") self._config_val_line_edit.setText(val) self._logger.debug("done") @property def open_duration(self) -> str: """ Get the string value found in the open duration text box. :return str: string value in the text box. """ return self._open_dur_line_edit.text() @open_duration.setter def open_duration(self, val: str) -> None: """ Set the string value found in the open duration text box. :param val: string value to set the open duration text box. :return None: """ self._logger.debug("running") self._open_dur_line_edit.setText(val) self._logger.debug("done") @property def close_duration(self) -> str: """ Get the string value found in the close duration text box. :return str: string value in the text box. """ return self._close_dur_line_edit.text() @close_duration.setter def close_duration(self, val: str) -> None: """ Set the string value found in the close duration text box. :param val: string value to set the close duration text box. :return None: """ self._logger.debug("running") self._close_dur_line_edit.setText(val) self._logger.debug("done") @property def debounce_val(self) -> str: """ Get the string value found in the debounce time text box. :return str: string value in the text box. """ return self._debounce_time_line_edit.text() @debounce_val.setter def debounce_val(self, val: str) -> None: """ Set the string value found in the debounce time text box. :param val: string value to set the debounce text box. :return None: """ self._logger.debug("running") self._debounce_time_line_edit.setText(val) self._logger.debug("done") @property def open_inf_check_box(self) -> bool: """ Get the check box state. :return bool: returns true if the box is checked. """ return self._open_inf_check_box.isChecked() @open_inf_check_box.setter def open_inf_check_box(self, val: bool) -> None: """ Set the check box state. :param val: bool value to set the check box. :return None: """ self._logger.debug("running") self._open_inf_check_box.setChecked(val) self._logger.debug("done") @property def close_inf_check_box(self) -> bool: """ Get the check box state. :return bool: returns true if the box is checked. """ return self._close_inf_check_box.isChecked() @close_inf_check_box.setter def close_inf_check_box(self, val: bool) -> None: """ Set the check box state. :param val: bool value to set the check box. :return None: """ self._logger.debug("running") self._close_inf_check_box.setChecked(val) self._logger.debug("done") @property def button_mode(self) -> int: """ Get index of button mode. :return int: index of current button mode. """ return self._button_mode_selector.currentIndex() @button_mode.setter def button_mode(self, val: int) -> None: """ Set index of button mode. :return None: """ self._logger.debug("running") self._button_mode_selector.setCurrentIndex(val) self._logger.debug("done") @property def control_mode(self) -> int: """ Get index of button mode. :return int: index of current button mode. """ return self._control_mode_selector.currentIndex() @control_mode.setter def control_mode(self, val: int) -> None: """ Set index of button mode. :return None: """ self._logger.debug("running") self._control_mode_selector.setCurrentIndex(val) self._logger.debug("done") def set_open_dur_err(self, err: bool) -> None: """ Set this text entry to error style depending on err. :param err: If this text entry needs to be error styel :return None: """ self._logger.debug("running") if err: self._open_dur_line_edit.setStyleSheet(tab_line_edit_error_style) else: self._open_dur_line_edit.setStyleSheet( tab_line_edit_compliant_style) self._logger.debug("done") def set_close_dur_err(self, err: bool) -> None: """ Set this text entry to error style depending on err. :param err: If this text entry needs to be error styel :return None: """ self._logger.debug("running") if err: self._close_dur_line_edit.setStyleSheet(tab_line_edit_error_style) else: self._close_dur_line_edit.setStyleSheet( tab_line_edit_compliant_style) self._logger.debug("done") def set_debounce_err(self, err: bool) -> None: """ Set this text entry to error style depending on err. :param err: If this text entry needs to be error styel :return None: """ self._logger.debug("running") if err: self._debounce_time_line_edit.setStyleSheet( tab_line_edit_error_style) else: self._debounce_time_line_edit.setStyleSheet( tab_line_edit_compliant_style) self._logger.debug("done") @property def language(self) -> LangEnum: """ Get the current language setting :return LangEnum: The current language enumerator being used """ return self._lang_enum @language.setter def language(self, lang: LangEnum) -> None: """ Set the language for this view object and reload the text and tooltips. :param lang: the language to use. :return None: """ self._logger.debug("running") self._strings = strings[lang] self._set_texts() self._set_tooltips() self._logger.debug("done") def set_upload_button(self, is_active: bool) -> None: """ Set upload button activity to is_active. :param is_active: Whether this button should be active. :return None: """ self._logger.debug("running") self._upload_settings_button.setEnabled(is_active) self._logger.debug("done") def _set_texts(self) -> None: """ Set text fields of view object. :return None: """ self._logger.debug("running") self._config_label.setText(self._strings[StringsEnum.CONFIG_LABEL]) self._config_val_line_edit.setText( self._strings[StringsEnum.CONFIG_LABEL]) self._nhtsa_button.setText(self._strings[StringsEnum.NHTSA_LABEL]) self._eblindfold_button.setText( self._strings[StringsEnum.EBLIND_LABEL]) self._direct_control_button.setText( self._strings[StringsEnum.DCON_LABEL]) self._open_dur_label.setText( self._strings[StringsEnum.OPEN_DURATION_LABEL]) self._open_inf_check_box.setText(self._strings[StringsEnum.INF_LABEL]) self._close_dur_label.setText( self._strings[StringsEnum.CLOSE_DURATION_LABEL]) self._close_inf_check_box.setText(self._strings[StringsEnum.INF_LABEL]) self._debounce_label.setText(self._strings[StringsEnum.DEBOUNCE_LABEL]) self._button_mode_label.setText( self._strings[StringsEnum.BUTTON_MODE_LABEL]) self._button_mode_selector.setItemText( 0, self._strings[StringsEnum.HOLD_VAL_LABEL]) self._button_mode_selector.setItemText( 1, self._strings[StringsEnum.CLICK_VAL_LABEL]) self._control_mode_label.setText( self._strings[StringsEnum.CONTROL_MODE_LABEL]) self._control_mode_selector.setItemText( 0, self._strings[StringsEnum.LENS_VAL_LABEL]) self._control_mode_selector.setItemText( 1, self._strings[StringsEnum.TRIAL_VAL_LABEL]) self._upload_settings_button.setText( self._strings[StringsEnum.UPLOAD_BUTTON_LABEL]) self._manual_control_open_button.setText( self._strings[StringsEnum.MANUAL_OPEN_LABEL]) self._manual_control_close_button.setText( self._strings[StringsEnum.MANUAL_CLOSE_LABEL]) self.config_button.setText(self._strings[StringsEnum.CONFIG_TAB_LABEL]) self.config_button.setText("...") self._config_win.setWindowTitle( self.get_name() + " " + self._strings[StringsEnum.CONFIG_TAB_LABEL]) self._logger.debug("done") def _set_tooltips(self) -> None: """ Set tooltip text fields of view object. :return None: """ self._logger.debug("running") self._config_label.setToolTip( self._strings[StringsEnum.CONFIG_LABEL_TOOLTIP]) self._config_val_line_edit.setToolTip( self._strings[StringsEnum.CONFIG_LABEL_TOOLTIP]) self._open_dur_label.setToolTip( self._strings[StringsEnum.OPEN_DURATION_TOOLTIP]) self._close_dur_label.setToolTip( self._strings[StringsEnum.CLOSE_DURATION_TOOLTIP]) self._debounce_label.setToolTip( self._strings[StringsEnum.DEBOUNCE_TOOLTIP]) self._button_mode_label.setToolTip( self._strings[StringsEnum.BUTTON_MODE_TOOLTIP]) self._control_mode_label.setToolTip( self._strings[StringsEnum.CONTROL_MODE_TOOLTIP]) self._upload_settings_button.setToolTip( self._strings[StringsEnum.UPLOAD_BUTTON_TOOLTIP]) self._manual_control_open_button.setToolTip( self._strings[StringsEnum.MANUAL_OPEN_TOOLTIP]) self._manual_control_close_button.setToolTip( self._strings[StringsEnum.MANUAL_CLOSE_TOOLTIP]) self.config_button.setToolTip( self._strings[StringsEnum.CONFIG_TAB_TOOLTIP]) self._logger.debug("done")
class MixedDistributionChart(QDialog): def __init__(self, parent=None, show_mode=True, toolbar=False, use_animation=False): flags = Qt.Window | Qt.WindowTitleHint | Qt.CustomizeWindowHint | Qt.WindowCloseButtonHint super().__init__(parent=parent, f=flags) self.setWindowTitle(self.tr("Mixed Distribution Chart")) self.figure = plt.figure(figsize=(4, 3)) self.axes = self.figure.subplots() self.canvas = FigureCanvas(self.figure) self.toolbar = NavigationToolbar(self.canvas, self) self.main_layout = QGridLayout(self) self.main_layout.addWidget(self.toolbar, 0, 0, 1, 2) self.main_layout.addWidget(self.canvas, 1, 0, 1, 2) if not toolbar: self.toolbar.hide() self.supported_scales = [("log-linear", self.tr("Log-linear")), ("log", self.tr("Log")), ("phi", self.tr("φ")), ("linear", self.tr("Linear"))] self.scale_label = QLabel(self.tr("Scale")) self.scale_combo_box = QComboBox() self.scale_combo_box.addItems([name for key, name in self.supported_scales]) self.scale_combo_box.currentIndexChanged.connect(self.update_chart) self.main_layout.addWidget(self.scale_label, 2, 0) self.main_layout.addWidget(self.scale_combo_box, 2, 1) self.interval_label = QLabel(self.tr("Interval [ms]")) self.interval_input = QSpinBox() self.interval_input.setRange(0, 10000) self.interval_input.setValue(30) self.interval_input.valueChanged.connect(self.update_animation) self.main_layout.addWidget(self.interval_label, 3, 0) self.main_layout.addWidget(self.interval_input, 3, 1) self.repeat_check_box = QCheckBox(self.tr("Repeat")) self.repeat_check_box.setChecked(False) self.repeat_check_box.stateChanged.connect(self.update_animation) self.save_button = QPushButton(self.tr("Save")) self.save_button.clicked.connect(self.save_animation) self.main_layout.addWidget(self.repeat_check_box, 4, 0) self.main_layout.addWidget(self.save_button, 4, 1) self.show_mode = show_mode self.animation = None self.last_model = None self.last_result = None if not use_animation: self.interval_label.setVisible(False) self.interval_input.setVisible(False) self.repeat_check_box.setVisible(False) self.save_button.setVisible(False) self.normal_msg = QMessageBox(parent=self) self.file_dialog = QFileDialog(parent=self) @property def scale(self) -> str: index = self.scale_combo_box.currentIndex() key, name = self.supported_scales[index] return key @property def transfer(self) -> typing.Callable: if self.scale == "log-linear": return lambda classes_φ: convert_φ_to_μm(classes_φ) elif self.scale == "log": return lambda classes_φ: np.log(convert_φ_to_μm(classes_φ)) elif self.scale == "phi": return lambda classes_φ: classes_φ elif self.scale == "linear": return lambda classes_φ: convert_φ_to_μm(classes_φ) @property def xlabel(self) -> str: if self.scale == "log-linear": return self.tr("Grain-size [μm]") elif self.scale == "log": return self.tr("Ln(grain-size in μm)") elif self.scale == "phi": return self.tr("Grain-size [φ]") elif self.scale == "linear": return self.tr("Grain-size [μm]") @property def ylabel(self) -> str: return self.tr("Frequency") @property def xlog(self) -> bool: if self.scale == "log-linear": return True else: return False @property def interval(self) -> float: return self.interval_input.value() @property def repeat(self) -> bool: return self.repeat_check_box.isChecked() def show_demo(self): demo_model = get_demo_view_model() self.show_model(demo_model) def update_chart(self): if self.last_model is not None: self.show_model(self.last_model) elif self.last_result is not None: self.show_result(self.last_result) def update_animation(self): if self.last_result is not None: self.show_result(self.last_result) def show_model(self, model: SSUViewModel, quick=False): if self.animation is not None: self.animation._stop() self.animation = None if not quick: self.last_result = None self.last_model = model self.interval_label.setEnabled(False) self.interval_input.setEnabled(False) self.repeat_check_box.setEnabled(False) self.save_button.setEnabled(False) self.axes.clear() x = self.transfer(model.classes_φ) if self.xlog: self.axes.set_xscale("log") self.axes.set_title(model.title) self.axes.set_xlabel(self.xlabel) self.axes.set_ylabel(self.ylabel) self.target = self.axes.plot(x, model.target, c="#ffffff00", marker=".", ms=8, mfc="black", mew=0.0, label=self.tr("Target"))[0] # scatter can not be modified from the tool bar # self.target = self.axes.scatter(x, model.target, c="black", s=1) self.axes.set_xlim(x[0], x[-1]) self.axes.set_ylim(0.0, round(np.max(model.target)*1.2, 2)) self.mixed = self.axes.plot(x, model.mixed, c="black", label=self.tr("Mixed"))[0] self.components = [self.axes.plot(x, distribution*fraction, c=plt.get_cmap()(i), label=model.component_prefix+str(i+1))[0] for i, (distribution, fraction) in enumerate(zip(model.distributions, model.fractions))] if self.show_mode: modes = [self.transfer(model.classes_φ[np.unravel_index(np.argmax(distribution), distribution.shape)]) for distribution in model.distributions] colors = [plt.get_cmap()(i) for i in range(model.n_components)] self.vlines = self.axes.vlines(modes, 0.0, round(np.max(model.target)*1.2, 2), colors=colors) if model.n_components < 6: self.axes.legend(loc="upper left") self.figure.tight_layout() self.canvas.draw() else: self.mixed.set_ydata(model.mixed) for comp, distribution, fraction in zip(self.components, model.distributions, model.fractions): comp.set_ydata(distribution*fraction) if self.show_mode: modes = [self.transfer(model.classes_φ[np.unravel_index(np.argmax(distribution), distribution.shape)]) for distribution in model.distributions] self.vlines.set_offsets(modes) self.canvas.draw() def show_result(self, result: SSUResult): if self.animation is not None: self.animation._stop() self.animation = None self.last_model = None self.last_result = result self.interval_label.setEnabled(True) self.interval_input.setEnabled(True) self.repeat_check_box.setEnabled(True) self.save_button.setEnabled(True) models = iter(result.view_models) first = next(models) x = self.transfer(first.classes_φ) self.axes.cla() if self.xlog: self.axes.set_xscale("log") self.axes.set_title(first.title) self.axes.set_xlabel(self.xlabel) self.axes.set_ylabel(self.ylabel) self.target = self.axes.plot(x, first.target, c="#ffffff00", marker=".", ms=8, mfc="black", mew=0.0)[0] self.axes.set_xlim(x[0], x[-1]) self.axes.set_ylim(0.0, round(np.max(first.target)*1.2, 2)) self.figure.tight_layout() # self.canvas.draw() colors = [plt.get_cmap()(i) for i in range(first.n_components)] def init(): model = first self.mixed = self.axes.plot(x, model.mixed, c="black")[0] self.components = [self.axes.plot(x, distribution*fraction, c=plt.get_cmap()(i))[0] for i, (distribution, fraction) in enumerate(zip(model.distributions, model.fractions))] if self.show_mode: modes = [self.transfer(model.classes_φ[np.unravel_index(np.argmax(distribution), distribution.shape)]) for distribution in model.distributions] self.vlines = self.axes.vlines(modes, 0.0, round(np.max(model.target)*1.2, 2), colors=colors) return self.mixed, self.vlines, *self.components def animate(current): model = current self.mixed.set_ydata(current.mixed) for line, distribution, fraction in zip(self.components, model.distributions, model.fractions): line.set_ydata(distribution*fraction) if self.show_mode: self.vlines.remove() modes = [self.transfer(model.classes_φ[np.unravel_index(np.argmax(distribution), distribution.shape)]) for distribution in model.distributions] self.vlines = self.axes.vlines(modes, 0.0, round(np.max(model.target)*1.2, 2), colors=colors) return self.mixed, self.vlines, *self.components self.animation = FuncAnimation(self.figure, animate, frames=models, init_func=init, interval=self.interval, blit=True, repeat=self.repeat, repeat_delay=3.0, save_count=result.n_iterations) def save_animation(self): if self.last_result is not None: filename, format_str = self.file_dialog.getSaveFileName(self, self.tr("Save the animation of this SSU result"), None, self.tr("MPEG-4 Video File (*.mp4);;Graphics Interchange Format (*.gif)")) if filename is None or filename == "": return progress = QProgressDialog(self) progress.setRange(0, 100) progress.setLabelText(self.tr("Saving Animation [{0} Frames]").format(self.last_result.n_iterations)) canceled = False def save_callback(i, n): if progress.wasCanceled(): nonlocal canceled canceled = True raise StopIteration() progress.setValue((i+1)/n*100) QCoreApplication.processEvents() self.show_result(self.last_result) # plt.rcParams["savefig.dpi"] = 120.0 if "*.gif" in format_str: if not ImageMagickWriter.isAvailable(): self.normal_msg.setWindowTitle(self.tr("Error")) self.normal_msg.setText(self.tr("ImageMagick is not installed, please download and install it from its offical website (https://imagemagick.org/index.php).")) self.normal_msg.exec_() else: self.animation.save(filename, writer="imagemagick", fps=30, progress_callback=save_callback) elif "*.mp4" in format_str: if not FFMpegWriter.isAvailable(): self.normal_msg.setWindowTitle(self.tr("Error")) self.normal_msg.setText(self.tr("FFMpeg is not installed, please download and install it from its offical website (https://ffmpeg.org/).")) self.normal_msg.exec_() else: self.animation.save(filename, writer="ffmpeg", fps=30, progress_callback=save_callback) # plt.rcParams["savefig.dpi"] = 300.0 if not canceled: progress.setValue(100)
class Window(QWidget): def __init__(self): super(Window, self).__init__() self.renderArea = RenderArea() self.shapeComboBox = QComboBox() self.shapeComboBox.addItem("Polygon", RenderArea.Polygon) self.shapeComboBox.addItem("Rectangle", RenderArea.Rect) self.shapeComboBox.addItem("Rounded Rectangle", RenderArea.RoundedRect) self.shapeComboBox.addItem("Ellipse", RenderArea.Ellipse) self.shapeComboBox.addItem("Pie", RenderArea.Pie) self.shapeComboBox.addItem("Chord", RenderArea.Chord) self.shapeComboBox.addItem("Path", RenderArea.Path) self.shapeComboBox.addItem("Line", RenderArea.Line) self.shapeComboBox.addItem("Polyline", RenderArea.Polyline) self.shapeComboBox.addItem("Arc", RenderArea.Arc) self.shapeComboBox.addItem("Points", RenderArea.Points) self.shapeComboBox.addItem("Text", RenderArea.Text) self.shapeComboBox.addItem("Pixmap", RenderArea.Pixmap) shapeLabel = QLabel("&Shape:") shapeLabel.setBuddy(self.shapeComboBox) self.penWidthSpinBox = QSpinBox() self.penWidthSpinBox.setRange(0, 20) self.penWidthSpinBox.setSpecialValueText("0 (cosmetic pen)") penWidthLabel = QLabel("Pen &Width:") penWidthLabel.setBuddy(self.penWidthSpinBox) self.penStyleComboBox = QComboBox() self.penStyleComboBox.addItem("Solid", Qt.SolidLine) self.penStyleComboBox.addItem("Dash", Qt.DashLine) self.penStyleComboBox.addItem("Dot", Qt.DotLine) self.penStyleComboBox.addItem("Dash Dot", Qt.DashDotLine) self.penStyleComboBox.addItem("Dash Dot Dot", Qt.DashDotDotLine) self.penStyleComboBox.addItem("None", Qt.NoPen) penStyleLabel = QLabel("&Pen Style:") penStyleLabel.setBuddy(self.penStyleComboBox) self.penCapComboBox = QComboBox() self.penCapComboBox.addItem("Flat", Qt.FlatCap) self.penCapComboBox.addItem("Square", Qt.SquareCap) self.penCapComboBox.addItem("Round", Qt.RoundCap) penCapLabel = QLabel("Pen &Cap:") penCapLabel.setBuddy(self.penCapComboBox) self.penJoinComboBox = QComboBox() self.penJoinComboBox.addItem("Miter", Qt.MiterJoin) self.penJoinComboBox.addItem("Bevel", Qt.BevelJoin) self.penJoinComboBox.addItem("Round", Qt.RoundJoin) penJoinLabel = QLabel("Pen &Join:") penJoinLabel.setBuddy(self.penJoinComboBox) self.brushStyleComboBox = QComboBox() self.brushStyleComboBox.addItem("Linear Gradient", Qt.LinearGradientPattern) self.brushStyleComboBox.addItem("Radial Gradient", Qt.RadialGradientPattern) self.brushStyleComboBox.addItem("Conical Gradient", Qt.ConicalGradientPattern) self.brushStyleComboBox.addItem("Texture", Qt.TexturePattern) self.brushStyleComboBox.addItem("Solid", Qt.SolidPattern) self.brushStyleComboBox.addItem("Horizontal", Qt.HorPattern) self.brushStyleComboBox.addItem("Vertical", Qt.VerPattern) self.brushStyleComboBox.addItem("Cross", Qt.CrossPattern) self.brushStyleComboBox.addItem("Backward Diagonal", Qt.BDiagPattern) self.brushStyleComboBox.addItem("Forward Diagonal", Qt.FDiagPattern) self.brushStyleComboBox.addItem("Diagonal Cross", Qt.DiagCrossPattern) self.brushStyleComboBox.addItem("Dense 1", Qt.Dense1Pattern) self.brushStyleComboBox.addItem("Dense 2", Qt.Dense2Pattern) self.brushStyleComboBox.addItem("Dense 3", Qt.Dense3Pattern) self.brushStyleComboBox.addItem("Dense 4", Qt.Dense4Pattern) self.brushStyleComboBox.addItem("Dense 5", Qt.Dense5Pattern) self.brushStyleComboBox.addItem("Dense 6", Qt.Dense6Pattern) self.brushStyleComboBox.addItem("Dense 7", Qt.Dense7Pattern) self.brushStyleComboBox.addItem("None", Qt.NoBrush) brushStyleLabel = QLabel("&Brush Style:") brushStyleLabel.setBuddy(self.brushStyleComboBox) otherOptionsLabel = QLabel("Other Options:") self.antialiasingCheckBox = QCheckBox("&Antialiasing") self.transformationsCheckBox = QCheckBox("&Transformations") self.shapeComboBox.activated.connect(self.shapeChanged) self.penWidthSpinBox.valueChanged.connect(self.penChanged) self.penStyleComboBox.activated.connect(self.penChanged) self.penCapComboBox.activated.connect(self.penChanged) self.penJoinComboBox.activated.connect(self.penChanged) self.brushStyleComboBox.activated.connect(self.brushChanged) self.antialiasingCheckBox.toggled.connect(self.renderArea.setAntialiased) self.transformationsCheckBox.toggled.connect(self.renderArea.setTransformed) mainLayout = QGridLayout() mainLayout.setColumnStretch(0, 1) mainLayout.setColumnStretch(3, 1) mainLayout.addWidget(self.renderArea, 0, 0, 1, 4) mainLayout.setRowMinimumHeight(1, 6) mainLayout.addWidget(shapeLabel, 2, 1, Qt.AlignRight) mainLayout.addWidget(self.shapeComboBox, 2, 2) mainLayout.addWidget(penWidthLabel, 3, 1, Qt.AlignRight) mainLayout.addWidget(self.penWidthSpinBox, 3, 2) mainLayout.addWidget(penStyleLabel, 4, 1, Qt.AlignRight) mainLayout.addWidget(self.penStyleComboBox, 4, 2) mainLayout.addWidget(penCapLabel, 5, 1, Qt.AlignRight) mainLayout.addWidget(self.penCapComboBox, 5, 2) mainLayout.addWidget(penJoinLabel, 6, 1, Qt.AlignRight) mainLayout.addWidget(self.penJoinComboBox, 6, 2) mainLayout.addWidget(brushStyleLabel, 7, 1, Qt.AlignRight) mainLayout.addWidget(self.brushStyleComboBox, 7, 2) mainLayout.setRowMinimumHeight(8, 6) mainLayout.addWidget(otherOptionsLabel, 9, 1, Qt.AlignRight) mainLayout.addWidget(self.antialiasingCheckBox, 9, 2) mainLayout.addWidget(self.transformationsCheckBox, 10, 2) self.setLayout(mainLayout) self.shapeChanged() self.penChanged() self.brushChanged() self.antialiasingCheckBox.setChecked(True) self.setWindowTitle("Basic Drawing") def shapeChanged(self): shape = self.shapeComboBox.itemData(self.shapeComboBox.currentIndex(), IdRole) self.renderArea.setShape(shape) def penChanged(self): width = self.penWidthSpinBox.value() style = Qt.PenStyle(self.penStyleComboBox.itemData( self.penStyleComboBox.currentIndex(), IdRole)) cap = Qt.PenCapStyle(self.penCapComboBox.itemData( self.penCapComboBox.currentIndex(), IdRole)) join = Qt.PenJoinStyle(self.penJoinComboBox.itemData( self.penJoinComboBox.currentIndex(), IdRole)) self.renderArea.setPen(QPen(Qt.blue, width, style, cap, join)) def brushChanged(self): style = Qt.BrushStyle(self.brushStyleComboBox.itemData( self.brushStyleComboBox.currentIndex(), IdRole)) if style == Qt.LinearGradientPattern: linearGradient = QLinearGradient(0, 0, 100, 100) linearGradient.setColorAt(0.0, Qt.white) linearGradient.setColorAt(0.2, Qt.green) linearGradient.setColorAt(1.0, Qt.black) self.renderArea.setBrush(QBrush(linearGradient)) elif style == Qt.RadialGradientPattern: radialGradient = QRadialGradient(50, 50, 50, 70, 70) radialGradient.setColorAt(0.0, Qt.white) radialGradient.setColorAt(0.2, Qt.green) radialGradient.setColorAt(1.0, Qt.black) self.renderArea.setBrush(QBrush(radialGradient)) elif style == Qt.ConicalGradientPattern: conicalGradient = QConicalGradient(50, 50, 150) conicalGradient.setColorAt(0.0, Qt.white) conicalGradient.setColorAt(0.2, Qt.green) conicalGradient.setColorAt(1.0, Qt.black) self.renderArea.setBrush(QBrush(conicalGradient)) elif style == Qt.TexturePattern: self.renderArea.setBrush(QBrush(QPixmap(':/images/brick.png'))) else: self.renderArea.setBrush(QBrush(Qt.green, style))
class JumpEditor(CustomDialog): def __init__(self, parent: Optional[QWidget], jump: Jump): super(JumpEditor, self).__init__(parent, "Jump Editor") self.jump = jump self.screen_spinner = Spinner(parent=self, maximum=MAX_SCREEN_INDEX, base=10) position_layout = QFormLayout() position_layout.addRow("Jump on screen:", self.screen_spinner) level_group_box = QGroupBox("Level position") level_group_box.setLayout(position_layout) self.exit_action = QComboBox(self) self.exit_action.addItems(JUMP_ACTIONS) self.exit_horizontal = Spinner(parent=self, maximum=MAX_HORIZ_POSITION, base=10) self.exit_vertical = QComboBox(self) self.exit_vertical.addItems(VERT_POSITIONS) exit_layout = QFormLayout() exit_layout.addRow("Exit action:", self.exit_action) exit_layout.addRow("Exit position x:", self.exit_horizontal) exit_layout.addRow("Exit position y:", self.exit_vertical) exit_group_box = QGroupBox("Exit options") exit_group_box.setLayout(exit_layout) button_box = QDialogButtonBox() self.ok_button = button_box.addButton(QDialogButtonBox.Ok) self.ok_button.pressed.connect(self.on_ok) button_box.addButton(QDialogButtonBox.Cancel).pressed.connect( self.close) main_layout = QVBoxLayout() main_layout.addWidget(level_group_box) main_layout.addWidget(exit_group_box) main_layout.addWidget(button_box) self.setLayout(main_layout) self._set_widget_values() def _set_widget_values(self): self.screen_spinner.setValue(self.jump.screen_index) self.exit_action.setCurrentIndex(self.jump.exit_action) self.exit_horizontal.setValue(self.jump.exit_horizontal) self.exit_vertical.setCurrentIndex(self.jump.exit_vertical) @staticmethod def edit_jump(parent: Optional[QWidget], jump: Jump): jump_editor = JumpEditor(parent, jump) jump_editor.exec_() return jump_editor.jump def on_ok(self): self.jump = Jump.from_properties( self.screen_spinner.value(), self.exit_action.currentIndex(), self.exit_horizontal.value(), self.exit_vertical.currentIndex(), ) self.close()
class customDesigner(QWidget): def __init__(self): super().__init__() self.fileName = None self.filters = "Text files (*.txt)" self.customList = QComboBox() self.customList.addItems(["Layer", "Loss Function", "Optimizer"]) self.customList.currentIndexChanged[int].connect(self.editorText) self.newButton = QPushButton("New") self.newButton.clicked.connect(self.newAction) self.saveButton = QPushButton("Save") self.saveButton.clicked.connect(self.saveAction) self.openButton = QPushButton("Open") self.openButton.clicked.connect(self.openAction) self.init_GUI() self.set_styling() def init_GUI(self): self.main_layout = QGridLayout() self.hbox = QHBoxLayout() self.TextEditor = QTextEdit() self.TextEditor.setText("class customLayer(Layer):\n\tpass") self.hbox.addWidget(self.customList) self.hbox.addWidget(self.newButton) self.hbox.addWidget(self.saveButton) self.hbox.addWidget(self.openButton) self.main_layout.addLayout(self.hbox, 0, 0) self.main_layout.addWidget(self.TextEditor, 1, 0) self.setLayout(self.main_layout) self.set_styling() def newAction(self): response = self.msgApp("Confirmation", "Do you like to save the current file?") if response == "Y": self.saveAction() print(self.customList.currentIndex) self.editorText(self.customList.currentIndex()) def saveAction(self): if self.fileName == None or self.fileName == '': self.fileName, self.filterName = QFileDialog.getSaveFileName( self, filter=self.filters) if (self.fileName != ''): file = open(self.fileName, 'w') file.write(self.TextEditor.toPlainText()) def openAction(self): response = self.msgApp("Confirmation", "Do you like to save the current file?") if response == "Y": self.saveAction() self.fileName, self.filterName = QFileDialog.getOpenFileName(self) self.TextEditor.setText(open(self.fileName).read()) def msgApp(self, title, msg): userInfo = QMessageBox.question(self, title, msg, QMessageBox.Yes | QMessageBox.No) if userInfo == QMessageBox.Yes: return "Y" if userInfo == QMessageBox.No: return "N" self.close() def editorText(self, index): if (index == 0): self.TextEditor.setText("class customLayer(Layer):\n\tpass") if (index == 1): self.TextEditor.setText("class customLossFunction():\n\tpass") if (index == 2): self.TextEditor.setText("class customOptimizer():\n\tpass") def set_styling(self): pass
class PlanesWidget(ToolWidget): def __init__(self, image, parent=None): super(PlanesWidget, self).__init__(parent) self.chan_combo = QComboBox() self.chan_combo.addItems( [self.tr('Luminance'), self.tr('Red'), self.tr('Green'), self.tr('Blue'), self.tr('RGB Norm')]) self.plane_spin = QSpinBox() self.plane_spin.setPrefix(self.tr('Bit ')) self.plane_spin.setRange(0, 7) self.filter_combo = QComboBox() self.filter_combo.addItems([self.tr('Disabled'), self.tr('Median'), self.tr('Gaussian')]) self.image = image self.viewer = ImageViewer(self.image, self.image) self.planes = None self.preprocess() self.chan_combo.currentIndexChanged.connect(self.preprocess) self.plane_spin.valueChanged.connect(self.process) self.filter_combo.currentIndexChanged.connect(self.process) top_layout = QHBoxLayout() top_layout.addWidget(QLabel(self.tr('Channel:'))) top_layout.addWidget(self.chan_combo) top_layout.addWidget(QLabel(self.tr('Plane:'))) top_layout.addWidget(self.plane_spin) top_layout.addWidget(QLabel(self.tr('Filter:'))) top_layout.addWidget(self.filter_combo) top_layout.addStretch() main_layout = QVBoxLayout() main_layout.addLayout(top_layout) main_layout.addWidget(self.viewer) self.setLayout(main_layout) def preprocess(self): channel = self.chan_combo.currentIndex() if channel == 0: img = cv.cvtColor(self.image, cv.COLOR_BGR2GRAY) elif channel == 4: b, g, r = cv.split(self.image.astype(np.float64)) img = cv.sqrt(cv.pow(b, 2) + cv.pow(g, 2) + cv.pow(r, 2)).astype(np.uint8) else: img = self.image[:, :, 3 - channel] self.planes = [normalize_mat(cv.bitwise_and(np.full_like(img, 2**b), img), to_bgr=True) for b in range(8)] # rows, cols = img.shape # bits = 8 # data = [np.binary_repr(img[i][j], width=bits) for i in range(rows) for j in range(cols)] # self.planes = [ # (np.array([int(i[b]) for i in data], dtype=np.uint8) * 2 ** (bits - b - 1)).reshape( # (rows, cols)) for b in range(bits)] self.process() def process(self): plane = self.planes[self.plane_spin.value()] if self.filter_combo.currentIndex() == 1: plane = cv.medianBlur(plane, 3) elif self.filter_combo.currentIndex() == 2: plane = cv.GaussianBlur(plane, (3, 3), 0) self.viewer.update_processed(plane)
class SettingsDialog(CustomDialog): def __init__(self, parent=None): super(SettingsDialog, self).__init__(parent, "Settings") mouse_box = QGroupBox("Mouse", self) mouse_box.setLayout(QVBoxLayout()) scroll_layout = QHBoxLayout() label = QLabel("Scroll objects with mouse wheel:") label.setToolTip( "Select an object and scroll up and down to change its type.") self._scroll_check_box = QCheckBox("Enabled") self._scroll_check_box.setChecked(SETTINGS["object_scroll_enabled"]) self._scroll_check_box.toggled.connect(self._update_settings) scroll_layout.addWidget(label) scroll_layout.addStretch(1) scroll_layout.addWidget(self._scroll_check_box) resize_layout = QHBoxLayout() self.lmb_radio = QRadioButton("Left Mouse Button") rmb_radio = QRadioButton("Right Mouse Button") self.lmb_radio.setChecked(SETTINGS["resize_mode"] == RESIZE_LEFT_CLICK) rmb_radio.setChecked(SETTINGS["resize_mode"] == RESIZE_RIGHT_CLICK) self.lmb_radio.toggled.connect(self._update_settings) radio_group = QButtonGroup() radio_group.addButton(self.lmb_radio) radio_group.addButton(rmb_radio) resize_layout.addWidget(QLabel("Object resize mode:")) resize_layout.addStretch(1) resize_layout.addWidget(self.lmb_radio) resize_layout.addWidget(rmb_radio) mouse_box.layout().addLayout(scroll_layout) mouse_box.layout().addLayout(resize_layout) # emulator command self.emulator_command_input = QLineEdit(self) self.emulator_command_input.setPlaceholderText("Path to emulator") self.emulator_command_input.setText(SETTINGS["instaplay_emulator"]) self.emulator_command_input.textChanged.connect(self._update_settings) self.emulator_path_button = QPushButton(icon("folder.svg"), "", self) self.emulator_path_button.pressed.connect(self._get_emulator_path) self.command_arguments_input = QLineEdit(self) self.command_arguments_input.setPlaceholderText("%f") self.command_arguments_input.setText(SETTINGS["instaplay_arguments"]) self.command_arguments_input.textEdited.connect(self._update_settings) self.command_label = QLabel() command_box = QGroupBox("Emulator", self) command_layout = QVBoxLayout(command_box) command_layout.addWidget(QLabel('Emulator command or "path to exe":')) command_input_layout = QHBoxLayout() command_input_layout.addWidget(self.emulator_command_input) command_input_layout.addWidget(self.emulator_path_button) command_layout.addLayout(command_input_layout) command_layout.addWidget( QLabel("Command arguments (%f will be replaced with rom path):")) command_layout.addWidget(self.command_arguments_input) command_layout.addWidget(QLabel("Command used to play the rom:")) command_layout.addWidget(self.command_label) command_layout.addWidget(HorizontalLine()) command_layout.addWidget( QLabel("Power up of Mario when playing level:")) self.powerup_combo_box = QComboBox() for name, x, y, value, p_wing in POWERUPS: powerup_icon = self._load_from_png(x, y) self.powerup_combo_box.addItem(powerup_icon, name) self.powerup_combo_box.currentIndexChanged.connect( self._update_settings) self.powerup_combo_box.setCurrentIndex(SETTINGS["default_powerup"]) command_layout.addWidget(self.powerup_combo_box) # ---------------------- layout = QVBoxLayout(self) layout.addWidget(mouse_box) layout.addWidget(command_box) self.update() def update(self): self.command_label.setText( f" > {SETTINGS['instaplay_emulator']} {SETTINGS['instaplay_arguments']}" ) def _update_settings(self, _): SETTINGS["instaplay_emulator"] = self.emulator_command_input.text() SETTINGS["instaplay_arguments"] = self.command_arguments_input.text() if self.lmb_radio.isChecked(): SETTINGS["resize_mode"] = RESIZE_LEFT_CLICK else: SETTINGS["resize_mode"] = RESIZE_RIGHT_CLICK SETTINGS["object_scroll_enabled"] = self._scroll_check_box.isChecked() SETTINGS["default_powerup"] = self.powerup_combo_box.currentIndex() self.update() def _get_emulator_path(self): path_to_emulator, _ = QFileDialog.getOpenFileName( self, caption="Select emulator executable") if not path_to_emulator: return self.emulator_command_input.setText(path_to_emulator) @staticmethod def _load_from_png(x: int, y: int) -> QIcon: image = png.copy(QRect(x * 16, y * 16, 16, 16)) mask = image.createMaskFromColor( QColor(*MASK_COLOR).rgb(), Qt.MaskOutColor) image.setAlphaChannel(mask) pixmap = QPixmap.fromImage(image) icon = QIcon(pixmap) return icon def on_exit(self): save_settings() super(SettingsDialog, self).on_exit()
class Window(QDialog): def __init__(self, parent=None): super(Window, self).__init__(parent) self.iconGroupBox = QGroupBox() self.iconLabel = QLabel() self.iconComboBox = QComboBox() self.showIconCheckBox = QCheckBox() self.messageGroupBox = QGroupBox() self.typeLabel = QLabel() self.durationLabel = QLabel() self.durationWarningLabel = QLabel() self.titleLabel = QLabel() self.bodyLabel = QLabel() self.typeComboBox = QComboBox() self.durationSpinBox = QSpinBox() self.titleEdit = QLineEdit() self.bodyEdit = QTextEdit() self.showMessageButton = QPushButton() self.minimizeAction = QAction() self.maximizeAction = QAction() self.restoreAction = QAction() self.quitAction = QAction() self.trayIcon = QSystemTrayIcon() self.trayIconMenu = QMenu() self.createIconGroupBox() self.createMessageGroupBox() self.iconLabel.setMinimumWidth(self.durationLabel.sizeHint().width()) self.createActions() self.createTrayIcon() self.showMessageButton.clicked.connect(self.showMessage) self.showIconCheckBox.toggled.connect(self.trayIcon.setVisible) self.iconComboBox.currentIndexChanged.connect(self.setIcon) self.trayIcon.messageClicked.connect(self.messageClicked) self.trayIcon.activated.connect(self.iconActivated) self.mainLayout = QVBoxLayout() self.mainLayout.addWidget(self.iconGroupBox) self.mainLayout.addWidget(self.messageGroupBox) self.setLayout(self.mainLayout) self.iconComboBox.setCurrentIndex(1) self.trayIcon.show() self.setWindowTitle("Systray") self.resize(400, 300) def setVisible(self, visible): self.minimizeAction.setEnabled(visible) self.maximizeAction.setEnabled(not self.isMaximized()) self.restoreAction.setEnabled(self.isMaximized() or not visible) super().setVisible(visible) def closeEvent(self, event): if not event.spontaneous() or not self.isVisible(): return if self.trayIcon.isVisible(): QMessageBox.information(self, "Systray", "The program will keep running in the system tray. " "To terminate the program, choose <b>Quit</b> in the context " "menu of the system tray entry.") self.hide() event.ignore() @Slot(int) def setIcon(self, index): icon = self.iconComboBox.itemIcon(index) self.trayIcon.setIcon(icon) self.setWindowIcon(icon) self.trayIcon.setToolTip(self.iconComboBox.itemText(index)) @Slot(str) def iconActivated(self, reason): if reason == QSystemTrayIcon.Trigger: pass if reason == QSystemTrayIcon.DoubleClick: self.iconComboBox.setCurrentIndex( (self.iconComboBox.currentIndex() + 1) % self.iconComboBox.count() ) if reason == QSystemTrayIcon.MiddleClick: self.showMessage() @Slot() def showMessage(self): self.showIconCheckBox.setChecked(True) selectedIcon = self.typeComboBox.itemData(self.typeComboBox.currentIndex()) msgIcon = QSystemTrayIcon.MessageIcon(selectedIcon) if selectedIcon == -1: # custom icon icon = QIcon(self.iconComboBox.itemIcon(self.iconComboBox.currentIndex())) self.trayIcon.showMessage( self.titleEdit.text(), self.bodyEdit.toPlainText(), icon, self.durationSpinBox.value() * 1000, ) else: self.trayIcon.showMessage( self.titleEdit.text(), self.bodyEdit.toPlainText(), msgIcon, self.durationSpinBox.value() * 1000, ) @Slot() def messageClicked(self): QMessageBox.information(None, "Systray", "Sorry, I already gave what help I could.\n" "Maybe you should try asking a human?") def createIconGroupBox(self): self.iconGroupBox = QGroupBox("Tray Icon") self.iconLabel = QLabel("Icon:") self.iconComboBox = QComboBox() self.iconComboBox.addItem(QIcon(":/images/bad.png"), "Bad") self.iconComboBox.addItem(QIcon(":/images/heart.png"), "Heart") self.iconComboBox.addItem(QIcon(":/images/trash.png"), "Trash") self.showIconCheckBox = QCheckBox("Show icon") self.showIconCheckBox.setChecked(True) iconLayout = QHBoxLayout() iconLayout.addWidget(self.iconLabel) iconLayout.addWidget(self.iconComboBox) iconLayout.addStretch() iconLayout.addWidget(self.showIconCheckBox) self.iconGroupBox.setLayout(iconLayout) def createMessageGroupBox(self): self.messageGroupBox = QGroupBox("Balloon Message") self.typeLabel = QLabel("Type:") self.typeComboBox = QComboBox() self.typeComboBox.addItem("None", QSystemTrayIcon.NoIcon) self.typeComboBox.addItem( self.style().standardIcon(QStyle.SP_MessageBoxInformation), "Information", QSystemTrayIcon.Information, ) self.typeComboBox.addItem( self.style().standardIcon(QStyle.SP_MessageBoxWarning), "Warning", QSystemTrayIcon.Warning, ) self.typeComboBox.addItem( self.style().standardIcon(QStyle.SP_MessageBoxCritical), "Critical", QSystemTrayIcon.Critical, ) self.typeComboBox.addItem(QIcon(), "Custom icon", -1) self.typeComboBox.setCurrentIndex(1) self.durationLabel = QLabel("Duration:") self.durationSpinBox = QSpinBox() self.durationSpinBox.setRange(5, 60) self.durationSpinBox.setSuffix(" s") self.durationSpinBox.setValue(15) self.durationWarningLabel = QLabel("(some systems might ignore this hint)") self.durationWarningLabel.setIndent(10) self.titleLabel = QLabel("Title:") self.titleEdit = QLineEdit("Cannot connect to network") self.bodyLabel = QLabel("Body:") self.bodyEdit = QTextEdit() self.bodyEdit.setPlainText("Don't believe me. Honestly, I don't have a clue." "\nClick this balloon for details.") self.showMessageButton = QPushButton("Show Message") self.showMessageButton.setDefault(True) messageLayout = QGridLayout() messageLayout.addWidget(self.typeLabel, 0, 0) messageLayout.addWidget(self.typeComboBox, 0, 1, 1, 2) messageLayout.addWidget(self.durationLabel, 1, 0) messageLayout.addWidget(self.durationSpinBox, 1, 1) messageLayout.addWidget(self.durationWarningLabel, 1, 2, 1, 3) messageLayout.addWidget(self.titleLabel, 2, 0) messageLayout.addWidget(self.titleEdit, 2, 1, 1, 4) messageLayout.addWidget(self.bodyLabel, 3, 0) messageLayout.addWidget(self.bodyEdit, 3, 1, 2, 4) messageLayout.addWidget(self.showMessageButton, 5, 4) messageLayout.setColumnStretch(3, 1) messageLayout.setRowStretch(4, 1) self.messageGroupBox.setLayout(messageLayout) def createActions(self): self.minimizeAction = QAction("Minimize", self) self.minimizeAction.triggered.connect(self.hide) self.maximizeAction = QAction("Maximize", self) self.maximizeAction.triggered.connect(self.showMaximized) self.restoreAction = QAction("Restore", self) self.restoreAction.triggered.connect(self.showNormal) self.quitAction = QAction("Quit", self) self.quitAction.triggered.connect(qApp.quit) def createTrayIcon(self): self.trayIconMenu = QMenu(self) self.trayIconMenu.addAction(self.minimizeAction) self.trayIconMenu.addAction(self.maximizeAction) self.trayIconMenu.addAction(self.restoreAction) self.trayIconMenu.addSeparator() self.trayIconMenu.addAction(self.quitAction) self.trayIcon = QSystemTrayIcon(self) self.trayIcon.setContextMenu(self.trayIconMenu)
class HeaderEditor(CustomDialog): header_change: SignalInstance = Signal() def __init__(self, parent: Optional[QWindow], level_ref: LevelRef): super(HeaderEditor, self).__init__(parent, "Level Header Editor") self.level: Level = level_ref.level # enables undo self.header_change.connect(level_ref.save_level_state) main_layout = QVBoxLayout(self) tab_widget = QTabWidget(self) main_layout.addWidget(tab_widget) # level settings self.length_dropdown = QComboBox() self.length_dropdown.addItems(STR_LEVEL_LENGTHS) self.length_dropdown.activated.connect(self.on_combo) self.music_dropdown = QComboBox() self.music_dropdown.addItems(MUSIC_ITEMS) self.music_dropdown.activated.connect(self.on_combo) self.time_dropdown = QComboBox() self.time_dropdown.addItems(TIMES) self.time_dropdown.activated.connect(self.on_combo) self.v_scroll_direction_dropdown = QComboBox() self.v_scroll_direction_dropdown.addItems(SCROLL_DIRECTIONS) self.v_scroll_direction_dropdown.activated.connect(self.on_combo) self.level_is_vertical_cb = QCheckBox("Level is Vertical") self.level_is_vertical_cb.clicked.connect(self.on_check_box) self.pipe_ends_level_cb = QCheckBox("Pipe ends Level") self.pipe_ends_level_cb.clicked.connect(self.on_check_box) check_box_layout = QHBoxLayout() check_box_layout.setContentsMargins(0, 0, 0, 0) check_box_layout.addWidget(self.level_is_vertical_cb) check_box_layout.addWidget(self.pipe_ends_level_cb) check_box_widget = QWidget() check_box_widget.setLayout(check_box_layout) form = QFormLayout() form.setFormAlignment(Qt.AlignCenter) form.addRow("Level length: ", self.length_dropdown) form.addRow("Music: ", self.music_dropdown) form.addRow("Time: ", self.time_dropdown) form.addRow("Scroll direction: ", self.v_scroll_direction_dropdown) form.addWidget(check_box_widget) widget = QWidget() widget.setLayout(form) tab_widget.addTab(widget, "Level") # player settings self.x_position_dropdown = QComboBox() self.x_position_dropdown.addItems(STR_X_POSITIONS) self.x_position_dropdown.activated.connect(self.on_combo) self.y_position_dropdown = QComboBox() self.y_position_dropdown.addItems(STR_Y_POSITIONS) self.y_position_dropdown.activated.connect(self.on_combo) self.action_dropdown = QComboBox() self.action_dropdown.addItems(ACTIONS) self.action_dropdown.activated.connect(self.on_combo) form = QFormLayout() form.setFormAlignment(Qt.AlignCenter) form.addRow("Starting X: ", self.x_position_dropdown) form.addRow("Starting Y: ", self.y_position_dropdown) form.addRow("Action: ", self.action_dropdown) widget = QWidget() widget.setLayout(form) tab_widget.addTab(widget, "Mario") # graphic settings self.object_palette_spinner = Spinner(self, maximum=7) self.object_palette_spinner.valueChanged.connect(self.on_spin) self.enemy_palette_spinner = Spinner(self, maximum=3) self.enemy_palette_spinner.valueChanged.connect(self.on_spin) self.graphic_set_dropdown = QComboBox() self.graphic_set_dropdown.addItems(GRAPHIC_SETS) self.graphic_set_dropdown.activated.connect(self.on_combo) form = QFormLayout() form.setFormAlignment(Qt.AlignCenter) form.addRow("Object Palette: ", self.object_palette_spinner) form.addRow("Enemy Palette: ", self.enemy_palette_spinner) form.addRow("Graphic Set: ", self.graphic_set_dropdown) widget = QWidget() widget.setLayout(form) tab_widget.addTab(widget, "Graphics") # next area settings self.level_pointer_spinner = Spinner(self, maximum=SPINNER_MAX_VALUE) self.level_pointer_spinner.valueChanged.connect(self.on_spin) self.enemy_pointer_spinner = Spinner(self, maximum=SPINNER_MAX_VALUE) self.enemy_pointer_spinner.valueChanged.connect(self.on_spin) self.next_area_object_set_dropdown = QComboBox() self.next_area_object_set_dropdown.addItems(OBJECT_SET_ITEMS) self.next_area_object_set_dropdown.activated.connect(self.on_combo) level_select_button = QPushButton("Set from Level") level_select_button.pressed.connect(self._set_jump_destination) form = QFormLayout() form.setFormAlignment(Qt.AlignCenter) form.addRow("Address of Objects: ", self.level_pointer_spinner) form.addRow("Address of Enemies: ", self.enemy_pointer_spinner) form.addRow("Object Set: ", self.next_area_object_set_dropdown) form.addRow(QLabel("")) form.addRow(level_select_button) widget = QWidget() widget.setLayout(form) tab_widget.addTab(widget, "Jump Destination") self.header_bytes_label = QLabel() main_layout.addWidget(self.header_bytes_label, alignment=Qt.AlignCenter) self.update() def update(self): length_index = LEVEL_LENGTHS.index(self.level.length) self.length_dropdown.setCurrentIndex(length_index) self.music_dropdown.setCurrentIndex(self.level.music_index) self.time_dropdown.setCurrentIndex(self.level.time_index) self.v_scroll_direction_dropdown.setCurrentIndex( self.level.scroll_type) self.level_is_vertical_cb.setChecked(self.level.is_vertical) self.pipe_ends_level_cb.setChecked(self.level.pipe_ends_level) self.x_position_dropdown.setCurrentIndex(self.level.start_x_index) self.y_position_dropdown.setCurrentIndex(self.level.start_y_index) self.action_dropdown.setCurrentIndex(self.level.start_action) self.object_palette_spinner.setValue(self.level.object_palette_index) self.enemy_palette_spinner.setValue(self.level.enemy_palette_index) self.graphic_set_dropdown.setCurrentIndex(self.level.graphic_set) self.level_pointer_spinner.setValue(self.level.next_area_objects) self.enemy_pointer_spinner.setValue(self.level.next_area_enemies) self.next_area_object_set_dropdown.setCurrentIndex( self.level.next_area_object_set) self.header_bytes_label.setText(" ".join( f"{number:0=#4X}"[2:] for number in self.level.header_bytes)) def _set_jump_destination(self): level_selector = LevelSelector(self) level_was_selected = level_selector.exec_() == QDialog.Accepted if not level_was_selected: return self.next_area_object_set_dropdown.setCurrentIndex( level_selector.object_set) self.level.next_area_object_set = level_selector.object_set self.level_pointer_spinner.setValue(level_selector.object_data_offset) self.level.next_area_objects = level_selector.object_data_offset self.enemy_pointer_spinner.setValue(level_selector.enemy_data_offset) self.level.next_area_enemies = level_selector.enemy_data_offset - 1 self.header_change.emit() def on_spin(self, _): if self.level is None: return spinner = self.sender() self.level.data_changed.connect(self.header_change.emit) if spinner == self.object_palette_spinner: new_index = self.object_palette_spinner.value() self.level.object_palette_index = new_index elif spinner == self.enemy_palette_spinner: new_index = self.enemy_palette_spinner.value() self.level.enemy_palette_index = new_index elif spinner == self.level_pointer_spinner: new_offset = self.level_pointer_spinner.value() self.level.next_area_objects = new_offset elif spinner == self.enemy_pointer_spinner: new_offset = self.enemy_pointer_spinner.value() self.level.next_area_enemies = new_offset self.level.data_changed.disconnect(self.header_change.emit) self.update() def on_combo(self, _): dropdown = self.sender() self.level.data_changed.connect(self.header_change.emit) if dropdown == self.length_dropdown: new_length = LEVEL_LENGTHS[self.length_dropdown.currentIndex()] self.level.length = new_length elif dropdown == self.music_dropdown: new_music = self.music_dropdown.currentIndex() self.level.music_index = new_music elif dropdown == self.time_dropdown: new_time = self.time_dropdown.currentIndex() self.level.time_index = new_time elif dropdown == self.v_scroll_direction_dropdown: new_scroll = self.v_scroll_direction_dropdown.currentIndex() self.level.scroll_type = new_scroll elif dropdown == self.x_position_dropdown: new_x = self.x_position_dropdown.currentIndex() self.level.start_x_index = new_x elif dropdown == self.y_position_dropdown: new_y = self.y_position_dropdown.currentIndex() self.level.start_y_index = new_y elif dropdown == self.action_dropdown: new_action = self.action_dropdown.currentIndex() self.level.start_action = new_action elif dropdown == self.graphic_set_dropdown: new_gfx_set = self.graphic_set_dropdown.currentIndex() self.level.graphic_set = new_gfx_set elif dropdown == self.next_area_object_set_dropdown: new_object_set = self.next_area_object_set_dropdown.currentIndex() self.level.next_area_object_set = new_object_set self.level.data_changed.disconnect(self.header_change.emit) self.update() def on_check_box(self, _): checkbox = self.sender() self.level.data_changed.connect(self.header_change.emit) if checkbox == self.pipe_ends_level_cb: self.level.pipe_ends_level = self.pipe_ends_level_cb.isChecked() elif checkbox == self.level_is_vertical_cb: self.level.is_vertical = self.level_is_vertical_cb.isChecked() self.level.data_changed.disconnect(self.header_change.emit) self.update()
class EMMAResolverPanel(QDialog): def __init__(self, parent=None): super().__init__(parent=parent, f=Qt.Window) self.setWindowTitle(self.tr("EMMA Resolver")) self.SUPPORTED_DISTS = \ [(DistributionType.Nonparametric, self.tr("Nonparametric")), (DistributionType.Normal, self.tr("Normal")), (DistributionType.Weibull, self.tr("Weibull")), (DistributionType.SkewNormal, self.tr("Skew Normal"))] self.init_ui() self.normal_msg = QMessageBox(self) self.__dataset = None # type: GrainSizeDataset self.__result_list = [] # type: list[EMMAResult] self.neural_setting = NNResolverSettingWidget(parent=self) self.neural_setting.setting = NNResolverSetting(min_niter=800, max_niter=1200, tol=1e-6, ftol=1e-8, lr=5e-2) self.load_dialog = LoadDatasetDialog(parent=self) self.load_dialog.dataset_loaded.connect(self.on_dataset_loaded) self.file_dialog = QFileDialog(parent=self) self.emma_result_chart = EMMAResultChart(toolbar=True) self.emma_summary_chart = EMMASummaryChart(toolbar=True) def init_ui(self): self.setAttribute(Qt.WA_StyledBackground, True) self.main_layout = QGridLayout(self) # self.main_layout.setContentsMargins(0, 0, 0, 0) # control group self.control_group = QGroupBox(self.tr("Control")) self.control_layout = QGridLayout(self.control_group) self.main_layout.addWidget(self.control_group, 0, 0) self.load_dataset_button = QPushButton(qta.icon("fa.database"), self.tr("Load Dataset")) self.load_dataset_button.clicked.connect(self.on_load_clicked) self.control_layout.addWidget(self.load_dataset_button, 0, 0, 1, 2) self.configure_button = QPushButton(qta.icon("fa.gears"), self.tr("Configure Algorithm")) self.configure_button.clicked.connect(self.on_configure_clicked) self.control_layout.addWidget(self.configure_button, 1, 0, 1, 2) self.n_samples_label = QLabel(self.tr("N<sub>samples</sub>")) self.n_samples_display = QLabel(self.tr("Unknown")) self.control_layout.addWidget(self.n_samples_label, 2, 0) self.control_layout.addWidget(self.n_samples_display, 2, 1) self.distribution_label = QLabel(self.tr("Distribution Type")) self.distribution_combo_box = QComboBox() self.distribution_combo_box.addItems( [name for _, name in self.SUPPORTED_DISTS]) self.control_layout.addWidget(self.distribution_label, 3, 0) self.control_layout.addWidget(self.distribution_combo_box, 3, 1) self.min_n_members_label = QLabel("Minimum N<sub>members</sub>") self.min_n_members_input = QSpinBox() self.min_n_members_input.setRange(1, 10) self.control_layout.addWidget(self.min_n_members_label, 4, 0) self.control_layout.addWidget(self.min_n_members_input, 4, 1) self.max_n_members_label = QLabel("Maximum N<sub>members</sub>") self.max_n_members_input = QSpinBox() self.max_n_members_input.setRange(1, 10) self.max_n_members_input.setValue(10) self.control_layout.addWidget(self.max_n_members_label, 5, 0) self.control_layout.addWidget(self.max_n_members_input, 5, 1) self.perform_button = QPushButton(qta.icon("fa.play-circle"), self.tr("Perform")) self.perform_button.clicked.connect(self.on_perform_clicked) self.perform_button.setEnabled(False) self.perform_with_customized_ems_button = QPushButton( qta.icon("fa.play-circle"), self.tr("Perform With Customized EMs")) self.perform_with_customized_ems_button.clicked.connect( self.on_perform_with_customized_ems) self.perform_with_customized_ems_button.setEnabled(False) self.control_layout.addWidget(self.perform_button, 6, 0, 1, 2) self.control_layout.addWidget(self.perform_with_customized_ems_button, 7, 0, 1, 2) self.progress_bar = QProgressBar() self.progress_bar.setFormat(self.tr("EMMA Progress")) self.control_layout.addWidget(self.progress_bar, 8, 0, 1, 2) self.result_group = QGroupBox(self.tr("Result")) self.result_layout = QGridLayout(self.result_group) self.main_layout.addWidget(self.result_group, 0, 1) self.result_list_widget = QListWidget() self.result_layout.addWidget(self.result_list_widget, 0, 0, 1, 2) self.remove_result_button = QPushButton(qta.icon("mdi.delete"), self.tr("Remove")) self.remove_result_button.clicked.connect(self.on_remove_clicked) self.show_result_button = QPushButton(qta.icon("fa.area-chart"), self.tr("Show")) self.show_result_button.clicked.connect(self.on_show_clicked) self.load_dump_button = QPushButton(qta.icon("fa.database"), self.tr("Load Dump")) self.load_dump_button.clicked.connect(self.on_load_dump_clicked) self.save_button = QPushButton(qta.icon("fa.save"), self.tr("Save")) self.save_button.clicked.connect(self.on_save_clicked) self.remove_result_button.setEnabled(False) self.show_result_button.setEnabled(False) self.save_button.setEnabled(False) self.result_layout.addWidget(self.remove_result_button, 1, 0) self.result_layout.addWidget(self.show_result_button, 1, 1) self.result_layout.addWidget(self.load_dump_button, 2, 0) self.result_layout.addWidget(self.save_button, 2, 1) def show_message(self, title: str, message: str): self.normal_msg.setWindowTitle(title) self.normal_msg.setText(message) self.normal_msg.exec_() def show_info(self, message: str): self.show_message(self.tr("Info"), message) def show_warning(self, message: str): self.show_message(self.tr("Warning"), message) def show_error(self, message: str): self.show_message(self.tr("Error"), message) def on_dataset_loaded(self, dataset: GrainSizeDataset): self.__dataset = dataset self.n_samples_display.setText(str(self.__dataset.n_samples)) self.perform_button.setEnabled(True) self.perform_with_customized_ems_button.setEnabled(True) def on_load_clicked(self): self.load_dialog.show() def on_configure_clicked(self): self.neural_setting.show() @property def distribution_type(self) -> DistributionType: distribution_type, _ = self.SUPPORTED_DISTS[ self.distribution_combo_box.currentIndex()] return distribution_type @property def n_members_list(self): min_n = self.min_n_members_input.value() max_n = self.max_n_members_input.value() if min_n > max_n: min_n, max_n = max_n, min_n return list(range(min_n, max_n + 1, 1)) @property def n_results(self) -> int: return len(self.__result_list) @property def selected_index(self): indexes = self.result_list_widget.selectedIndexes() if len(indexes) == 0: return 0 else: return indexes[0].row() @property def selected_result(self): if self.n_results == 0: None else: return self.__result_list[self.selected_index] def on_perform_clicked(self): if self.__dataset is None: self.show_error(self.tr("Dataset has not been loaded.")) return self.perform_button.setEnabled(False) self.perform_with_customized_ems_button.setEnabled(False) resolver = EMMAResolver() resolver_setting = self.neural_setting.setting results = [] n_members_list = self.n_members_list self.progress_bar.setRange(0, len(n_members_list)) self.progress_bar.setValue(0) self.progress_bar.setFormat(self.tr("Performing EMMA [%v/%m]")) QCoreApplication.processEvents() for i, n_members in enumerate(n_members_list): result = resolver.try_fit(self.__dataset, self.distribution_type, n_members, resolver_setting) results.append(result) self.progress_bar.setValue(i + 1) QCoreApplication.processEvents() self.add_results(results) self.progress_bar.setFormat(self.tr("Finished")) self.perform_button.setEnabled(True) self.perform_with_customized_ems_button.setEnabled(True) if len(results) > 1: self.emma_summary_chart.show_distances(results) self.emma_summary_chart.show() def on_perform_with_customized_ems(self): if self.__dataset is None: self.show_error(self.tr("Dataset has not been loaded.")) return filename, _ = self.file_dialog.getOpenFileName( self, self. tr("Choose a excel file which contains the customized EMs at the first sheet" ), None, f"{self.tr('Microsoft Excel')} (*.xlsx)") if filename is None or filename == "": return try: wb = openpyxl.load_workbook(filename, read_only=True, data_only=True) ws = wb[wb.sheetnames[0]] raw_data = [[value for value in row] for row in ws.values] classes_μm = np.array(raw_data[0][1:], dtype=np.float64) classes_φ = convert_μm_to_φ(classes_μm) em_distributions = [ np.array(row[1:], dtype=np.float64) for row in raw_data[1:] ] except Exception as e: self.show_error( self.tr( "Error raised while loading the customized EMs.\n {0}"). format(e.__str__())) return if len(classes_μm) < 10: self.show_error( self.tr("The length of grain-size classes is too less.")) return for i in range(len(classes_μm) - 1): if classes_μm[i + 1] <= classes_μm[i]: self.show_error( self.tr("The grain-size classes is not incremental.")) return if np.any(np.isnan(classes_μm)): self.show_error( self.tr( "There is at least one nan value in grain-size classes.")) return if len(em_distributions) > 10: self.show_error( self. tr("There are more than 10 customized EMs in the first sheet, please check." )) return for distribution in em_distributions: if len(classes_μm) != len(distribution): self.show_error( self. tr("Some distributions of customized EMs have different length with the grain-size classes." )) return if np.any(np.isnan(distribution)): self.show_error( self. tr("There is at least one nan value in the frequceny distributions of EMs." )) return if abs(np.sum(distribution) - 1.0) > 0.05: self.show_error( self. tr("The sum of some distributions of customized EMs are not equal to 1." )) return self.perform_button.setEnabled(False) self.perform_with_customized_ems_button.setEnabled(False) resolver = EMMAResolver() resolver_setting = self.neural_setting.setting self.progress_bar.setRange(0, 1) self.progress_bar.setValue(0) self.progress_bar.setFormat(self.tr("Performing EMMA [%v/%m]")) QCoreApplication.processEvents() try: result = resolver.try_fit_with_fixed_ems(self.__dataset, classes_φ, em_distributions, resolver_setting) self.progress_bar.setValue(1) self.add_results([result]) self.progress_bar.setFormat(self.tr("Finished")) except Exception as e: self.show_error( self.tr("Error raised while fitting.\n {0}").format( e.__str__())) self.progress_bar.setFormat(self.tr("Failed")) QCoreApplication.processEvents() self.perform_button.setEnabled(True) self.perform_with_customized_ems_button.setEnabled(True) def get_distribution_name(self, distribution_type: DistributionType): if distribution_type == DistributionType.Nonparametric: return self.tr("Nonparametric") elif distribution_type == DistributionType.Normal: return self.tr("Normal") elif distribution_type == DistributionType.Weibull: return self.tr("Weibull") elif distribution_type == DistributionType.SkewNormal: return self.tr("Skew Normal") elif distribution_type == DistributionType.Customized: return self.tr("Customized") else: raise NotImplementedError(distribution_type) def get_result_name(self, result: EMMAResult): return f"[{self.get_distribution_name(result.distribution_type)}]-[{result.n_members} EM(s)]" def add_results(self, results: typing.List[EMMAResult]): if self.n_results == 0: self.remove_result_button.setEnabled(True) self.show_result_button.setEnabled(True) self.save_button.setEnabled(True) self.__result_list.extend(results) self.result_list_widget.addItems( [self.get_result_name(result) for result in results]) def on_remove_clicked(self): if self.n_results == 0: return else: index = self.selected_index self.__result_list.pop(index) self.result_list_widget.takeItem(index) if self.n_results == 0: self.remove_result_button.setEnabled(False) self.show_result_button.setEnabled(False) self.save_button.setEnabled(False) def on_show_clicked(self): result = self.selected_result if result is None: return else: self.emma_result_chart.show_result(result) self.emma_result_chart.show() def on_load_dump_clicked(self): filename, _ = self.file_dialog.getOpenFileName( self, self.tr("Select the dump file of the EMMA result(s)"), None, f"{self.tr('Binary Dump')} (*.dump)") if filename is None or filename == "": return with open(filename, "rb") as f: results = pickle.load(f) invalid = False if isinstance(results, list): for result in results: if not isinstance(result, EMMAResult): invalid = True break else: invalid = True if invalid: self.show_error( self.tr("The dump file does not contain any EMMA result.")) return else: self.add_results(results) def save_result_excel(self, filename: str, result: EMMAResult): # get the mode size of each end-members modes = [(i, result.dataset.classes_μm[np.unravel_index( np.argmax(result.end_members[i]), result.end_members[i].shape)]) for i in range(result.n_members)] # sort them by mode size modes.sort(key=lambda x: x[1]) wb = openpyxl.Workbook() prepare_styles(wb) ws = wb.active ws.title = self.tr("README") description = \ """ This Excel file was generated by QGrain ({0}). Please cite: Liu, Y., Liu, X., Sun, Y., 2021. QGrain: An open-source and easy-to-use software for the comprehensive analysis of grain size distributions. Sedimentary Geology 423, 105980. https://doi.org/10.1016/j.sedgeo.2021.105980 It contanins three sheets: 1. The first sheet is the dataset which was used to perform the EMMA algorithm. 2. The second sheet is used to put the distributions of all end-members. 3. The third sheet is the end-member fractions of all samples. This EMMA algorithm was implemented by QGrian, using the famous machine learning framework, PyTorch. EMMA algorithm details N_samples: {1}, Distribution Type: {2}, N_members: {3}, N_iterations: {4}, Spent Time: {5} s, Computing Device: {6}, Distance: {7}, Minimum N_iterations: {8}, Maximum N_iterations: {9}, Learning Rate: {10}, eps: {11}, tol: {12}, ftol: {13} """.format(QGRAIN_VERSION, result.dataset.n_samples, result.distribution_type.name, result.n_members, result.n_iterations, result.time_spent, result.resolver_setting.device, result.resolver_setting.distance, result.resolver_setting.min_niter, result.resolver_setting.max_niter, result.resolver_setting.lr, result.resolver_setting.eps, result.resolver_setting.tol, result.resolver_setting.ftol) def write(row, col, value, style="normal_light"): cell = ws.cell(row + 1, col + 1, value=value) cell.style = style lines_of_desc = description.split("\n") for row, line in enumerate(lines_of_desc): write(row, 0, line, style="description") ws.column_dimensions[column_to_char(0)].width = 200 ws = wb.create_sheet(self.tr("Dataset")) write(0, 0, self.tr("Sample Name"), style="header") ws.column_dimensions[column_to_char(0)].width = 16 for col, value in enumerate(result.dataset.classes_μm, 1): write(0, col, value, style="header") ws.column_dimensions[column_to_char(col)].width = 10 for row, sample in enumerate(result.dataset.samples, 1): if row % 2 == 0: style = "normal_dark" else: style = "normal_light" write(row, 0, sample.name, style=style) for col, value in enumerate(sample.distribution, 1): write(row, col, value, style=style) QCoreApplication.processEvents() ws = wb.create_sheet(self.tr("End-members")) write(0, 0, self.tr("End-member"), style="header") ws.column_dimensions[column_to_char(0)].width = 16 for col, value in enumerate(result.dataset.classes_μm, 1): write(0, col, value, style="header") ws.column_dimensions[column_to_char(col)].width = 10 for row, (index, _) in enumerate(modes, 1): if row % 2 == 0: style = "normal_dark" else: style = "normal_light" write(row, 0, f"EM{row}", style=style) for col, value in enumerate(result.end_members[index], 1): write(row, col, value, style=style) QCoreApplication.processEvents() ws = wb.create_sheet(self.tr("Fractions")) write(0, 0, self.tr("Sample Name"), style="header") ws.column_dimensions[column_to_char(0)].width = 16 for i in range(result.n_members): write(0, i + 1, f"EM{i+1}", style="header") ws.column_dimensions[column_to_char(i + 1)].width = 10 for row, fractions in enumerate(result.fractions, 1): if row % 2 == 0: style = "normal_dark" else: style = "normal_light" write(row, 0, result.dataset.samples[row - 1].name, style=style) for col, (index, _) in enumerate(modes, 1): write(row, col, fractions[index], style=style) QCoreApplication.processEvents() wb.save(filename) wb.close() def on_save_clicked(self): if self.n_results == 0: self.show_warning( self.tr("There is not an EMMA result in the list.")) return filename, _ = self.file_dialog.getSaveFileName( self, self.tr("Choose a filename to save the EMMA result(s) in list"), None, f"{self.tr('Binary Dump')} (*.dump);;{self.tr('Microsoft Excel')} (*.xlsx)" ) if filename is None or filename == "": return _, ext = os.path.splitext(filename) if ext == ".dump": with open(filename, "wb") as f: pickle.dump(self.__result_list, f) self.show_info(self.tr("All results in list has been saved.")) elif ext == ".xlsx": try: result = self.selected_result self.save_result_excel(filename, result) self.show_info(self.tr("The selected result has been saved.")) except Exception as e: self.show_error( self.tr( "Error raised while saving it to Excel file.\n {0}" ).format(e.__str__())) return
class Window(QWidget): def __init__(self): super(Window, self).__init__() self.proxyModel = SortFilterProxyModel() self.proxyModel.setDynamicSortFilter(True) self.sourceGroupBox = QGroupBox("Original Model") self.proxyGroupBox = QGroupBox("Sorted/Filtered Model") self.sourceView = QTreeView() self.sourceView.setRootIsDecorated(False) self.sourceView.setAlternatingRowColors(True) self.proxyView = QTreeView() self.proxyView.setRootIsDecorated(False) self.proxyView.setAlternatingRowColors(True) self.proxyView.setModel(self.proxyModel) self.proxyView.setSortingEnabled(True) self.sortCaseSensitivityCheckBox = QCheckBox("Case sensitive sorting") self.filterCaseSensitivityCheckBox = QCheckBox("Case sensitive filter") self.filterPatternLineEdit = QLineEdit() self.filterPatternLabel = QLabel("&Filter pattern:") self.filterPatternLabel.setBuddy(self.filterPatternLineEdit) self.filterSyntaxComboBox = QComboBox() self.filterSyntaxComboBox.addItem("Regular expression", QRegExp.RegExp) self.filterSyntaxComboBox.addItem("Wildcard", QRegExp.Wildcard) self.filterSyntaxComboBox.addItem("Fixed string", QRegExp.FixedString) self.filterSyntaxLabel = QLabel("Filter &syntax:") self.filterSyntaxLabel.setBuddy(self.filterSyntaxComboBox) self.filterColumnComboBox = QComboBox() self.filterColumnComboBox.addItem("Subject") self.filterColumnComboBox.addItem("Sender") self.filterColumnComboBox.addItem("Date") self.filterColumnLabel = QLabel("Filter &column:") self.filterColumnLabel.setBuddy(self.filterColumnComboBox) self.filterPatternLineEdit.textChanged.connect( self.filterRegExpChanged) self.filterSyntaxComboBox.currentIndexChanged.connect( self.filterRegExpChanged) self.filterColumnComboBox.currentIndexChanged.connect( self.filterColumnChanged) self.filterCaseSensitivityCheckBox.toggled.connect( self.filterRegExpChanged) self.sortCaseSensitivityCheckBox.toggled.connect(self.sortChanged) sourceLayout = QHBoxLayout() sourceLayout.addWidget(self.sourceView) self.sourceGroupBox.setLayout(sourceLayout) proxyLayout = QGridLayout() proxyLayout.addWidget(self.proxyView, 0, 0, 1, 3) proxyLayout.addWidget(self.filterPatternLabel, 1, 0) proxyLayout.addWidget(self.filterPatternLineEdit, 1, 1, 1, 2) proxyLayout.addWidget(self.filterSyntaxLabel, 2, 0) proxyLayout.addWidget(self.filterSyntaxComboBox, 2, 1, 1, 2) proxyLayout.addWidget(self.filterColumnLabel, 3, 0) proxyLayout.addWidget(self.filterColumnComboBox, 3, 1, 1, 2) proxyLayout.addWidget(self.filterCaseSensitivityCheckBox, 4, 0, 1, 2) proxyLayout.addWidget(self.sortCaseSensitivityCheckBox, 4, 2) self.proxyGroupBox.setLayout(proxyLayout) mainLayout = QVBoxLayout() mainLayout.addWidget(self.sourceGroupBox) mainLayout.addWidget(self.proxyGroupBox) self.setLayout(mainLayout) self.setWindowTitle("Basic Sort/Filter Model") self.resize(500, 450) self.proxyView.sortByColumn(SENDER, Qt.AscendingOrder) self.filterColumnComboBox.setCurrentIndex(SENDER) self.filterPatternLineEdit.setText("Andy|Grace") self.filterCaseSensitivityCheckBox.setChecked(True) self.sortCaseSensitivityCheckBox.setChecked(True) def setSourceModel(self, model): self.proxyModel.setSourceModel(model) self.sourceView.setModel(model) def filterRegExpChanged(self): syntax_nr = self.filterSyntaxComboBox.itemData( self.filterSyntaxComboBox.currentIndex()) syntax = QRegExp.PatternSyntax(syntax_nr) if self.filterCaseSensitivityCheckBox.isChecked(): caseSensitivity = Qt.CaseSensitive else: caseSensitivity = Qt.CaseInsensitive regExp = QRegExp(self.filterPatternLineEdit.text(), caseSensitivity, syntax) self.proxyModel.setFilterRegExp(regExp) def filterColumnChanged(self): self.proxyModel.setFilterKeyColumn( self.filterColumnComboBox.currentIndex()) def sortChanged(self): if self.sortCaseSensitivityCheckBox.isChecked(): caseSensitivity = Qt.CaseSensitive else: caseSensitivity = Qt.CaseInsensitive self.proxyModel.setSortCaseSensitivity(caseSensitivity)
class PcaWidget(ToolWidget): def __init__(self, image, parent=None): super(PcaWidget, self).__init__(parent) self.component_combo = QComboBox() self.component_combo.addItems([self.tr(f"#{i + 1}") for i in range(3)]) self.distance_radio = QRadioButton(self.tr("Distance")) self.distance_radio.setToolTip(self.tr("Distance from the closest point on selected component")) self.project_radio = QRadioButton(self.tr("Projection")) self.project_radio.setToolTip(self.tr("Projection onto the selected principal component")) self.crossprod_radio = QRadioButton(self.tr("Cross product")) self.crossprod_radio.setToolTip(self.tr("Cross product between input and selected component")) self.distance_radio.setChecked(True) self.last_radio = self.distance_radio self.invert_check = QCheckBox(self.tr("Invert")) self.invert_check.setToolTip(self.tr("Output bitwise complement")) self.equalize_check = QCheckBox(self.tr("Equalize")) self.equalize_check.setToolTip(self.tr("Apply histogram equalization")) rows, cols, chans = image.shape x = np.reshape(image, (rows * cols, chans)).astype(np.float32) mu, ev, ew = cv.PCACompute2(x, np.array([])) p = np.reshape(cv.PCAProject(x, mu, ev), (rows, cols, chans)) x0 = image.astype(np.float32) - mu self.output = [] for i, v in enumerate(ev): cross = np.cross(x0, v) distance = np.linalg.norm(cross, axis=2) / np.linalg.norm(v) project = p[:, :, i] self.output.extend([norm_mat(distance, to_bgr=True), norm_mat(project, to_bgr=True), norm_img(cross)]) table_data = [ [mu[0, 2], mu[0, 1], mu[0, 0]], [ev[0, 2], ev[0, 1], ev[0, 0]], [ev[1, 2], ev[1, 1], ev[1, 0]], [ev[2, 2], ev[2, 1], ev[2, 0]], [ew[2, 0], ew[1, 0], ew[0, 0]], ] table_widget = QTableWidget(5, 4) table_widget.setHorizontalHeaderLabels([self.tr("Element"), self.tr("Red"), self.tr("Green"), self.tr("Blue")]) table_widget.setItem(0, 0, QTableWidgetItem(self.tr("Mean vector"))) table_widget.setItem(1, 0, QTableWidgetItem(self.tr("Eigenvector 1"))) table_widget.setItem(2, 0, QTableWidgetItem(self.tr("Eigenvector 2"))) table_widget.setItem(3, 0, QTableWidgetItem(self.tr("Eigenvector 3"))) table_widget.setItem(4, 0, QTableWidgetItem(self.tr("Eigenvalues"))) for i in range(len(table_data)): modify_font(table_widget.item(i, 0), bold=True) for j in range(len(table_data[i])): table_widget.setItem(i, j + 1, QTableWidgetItem(str(table_data[i][j]))) # item = QTableWidgetItem() # item.setBackgroundColor(QColor(mu[0, 2], mu[0, 1], mu[0, 0])) # table_widget.setItem(0, 4, item) # table_widget.resizeRowsToContents() # table_widget.resizeColumnsToContents() table_widget.setEditTriggers(QAbstractItemView.NoEditTriggers) table_widget.setSelectionMode(QAbstractItemView.SingleSelection) table_widget.setMaximumHeight(190) self.viewer = ImageViewer(image, image, None) self.process() self.component_combo.currentIndexChanged.connect(self.process) self.distance_radio.clicked.connect(self.process) self.project_radio.clicked.connect(self.process) self.crossprod_radio.clicked.connect(self.process) self.invert_check.stateChanged.connect(self.process) self.equalize_check.stateChanged.connect(self.process) top_layout = QHBoxLayout() top_layout.addWidget(QLabel(self.tr("Component:"))) top_layout.addWidget(self.component_combo) top_layout.addWidget(QLabel(self.tr("Mode:"))) top_layout.addWidget(self.distance_radio) top_layout.addWidget(self.project_radio) top_layout.addWidget(self.crossprod_radio) top_layout.addWidget(self.invert_check) top_layout.addWidget(self.equalize_check) top_layout.addStretch() bottom_layout = QHBoxLayout() bottom_layout.addWidget(table_widget) main_layout = QVBoxLayout() main_layout.addLayout(top_layout) main_layout.addWidget(self.viewer) main_layout.addLayout(bottom_layout) self.setLayout(main_layout) def process(self): index = 3 * self.component_combo.currentIndex() if self.distance_radio.isChecked(): output = self.output[index] self.last_radio = self.distance_radio elif self.project_radio.isChecked(): output = self.output[index + 1] self.last_radio = self.project_radio elif self.crossprod_radio.isChecked(): output = self.output[index + 2] self.last_radio = self.crossprod_radio else: self.last_radio.setChecked(True) return if self.invert_check.isChecked(): output = cv.bitwise_not(output) if self.equalize_check.isChecked(): output = equalize_img(output) self.viewer.update_processed(output)
class QLiberationPreferences(QFrame): def __init__(self): super(QLiberationPreferences, self).__init__() self.saved_game_dir = "" self.dcs_install_dir = "" self.install_dir_ignore_warning = False self.dcs_install_dir = liberation_install.get_dcs_install_directory() self.saved_game_dir = liberation_install.get_saved_game_dir() self.edit_dcs_install_dir = QLineEdit(self.dcs_install_dir) self.edit_saved_game_dir = QLineEdit(self.saved_game_dir) self.edit_dcs_install_dir.setMinimumWidth(300) self.edit_saved_game_dir.setMinimumWidth(300) self.browse_saved_game = QPushButton("Browse...") self.browse_saved_game.clicked.connect(self.on_browse_saved_games) self.browse_install_dir = QPushButton("Browse...") self.browse_install_dir.clicked.connect( self.on_browse_installation_dir) self.themeSelect = QComboBox() [self.themeSelect.addItem(y["themeName"]) for x, y in THEMES.items()] self.initUi() def initUi(self): main_layout = QVBoxLayout() layout = QGridLayout() layout.addWidget( QLabel("<strong>DCS saved game directory:</strong>"), 0, 0, alignment=Qt.AlignLeft, ) layout.addWidget(self.edit_saved_game_dir, 1, 0, alignment=Qt.AlignRight) layout.addWidget(self.browse_saved_game, 1, 1, alignment=Qt.AlignRight) layout.addWidget( QLabel("<strong>DCS installation directory:</strong>"), 2, 0, alignment=Qt.AlignLeft, ) layout.addWidget(self.edit_dcs_install_dir, 3, 0, alignment=Qt.AlignRight) layout.addWidget(self.browse_install_dir, 3, 1, alignment=Qt.AlignRight) layout.addWidget(QLabel("<strong>Theme (Requires Restart)</strong>"), 4, 0) layout.addWidget(self.themeSelect, 4, 1, alignment=Qt.AlignRight) self.themeSelect.setCurrentIndex(get_theme_index()) main_layout.addLayout(layout) main_layout.addStretch() self.setLayout(main_layout) def on_browse_saved_games(self): saved_game_dir = str( QFileDialog.getExistingDirectory( self, "Select DCS Saved Game Directory")) if saved_game_dir: self.saved_game_dir = saved_game_dir self.edit_saved_game_dir.setText(saved_game_dir) def on_browse_installation_dir(self): install_dir = str( QFileDialog.getExistingDirectory( self, "Select DCS Installation Directory")) if install_dir: self.dcs_install_dir = install_dir self.edit_dcs_install_dir.setText(install_dir) def apply(self): print("Applying changes") self.saved_game_dir = self.edit_saved_game_dir.text() self.dcs_install_dir = self.edit_dcs_install_dir.text() set_theme_index(self.themeSelect.currentIndex()) if not os.path.isdir(self.saved_game_dir): error_dialog = QMessageBox.critical( self, "Wrong DCS Saved Games directory.", self.saved_game_dir + " is not a valid directory", QMessageBox.StandardButton.Ok, ) error_dialog.exec_() return False if self.install_dir_ignore_warning and self.dcs_install_dir == "": warning_dialog = QMessageBox.warning( self, "The DCS Installation directory was not set", "You set an empty DCS Installation directory! " "<br/><br/>Without this directory, DCS Liberation can not replace the MissionScripting.lua for you and will not work properly. " "In this case, you need to edit the MissionScripting.lua yourself. The easiest way to do it is to replace the original file (<dcs_installation_directory>/Scripts/MissionScripting.lua) with the file in dcs-liberation distribution (<dcs_liberation_installation>/resources/scripts/MissionScripting.lua)." "<br/><br/>You can find more information on how to manually change this file in the Liberation Wiki (Page: Dedicated Server Guide) on GitHub.</p>" "<br/><br/>Are you sure that you want to leave the installation directory empty?" "<br/><br/><strong>This is only recommended for expert users!</strong>", QMessageBox.StandardButton.Yes, QMessageBox.StandardButton.No, ) if warning_dialog == QMessageBox.No: return False elif not os.path.isdir(self.dcs_install_dir): error_dialog = QMessageBox.critical( self, "Wrong DCS installation directory.", self.dcs_install_dir + " is not a valid directory. DCS Liberation requires the installation directory to replace the MissionScripting.lua" "<br/><br/>If you ignore this Error, DCS Liberation can not work properly and needs your attention. " "In this case, you need to edit the MissionScripting.lua yourself. The easiest way to do it is to replace the original file (<dcs_installation_directory>/Scripts/MissionScripting.lua) with the file in dcs-liberation distribution (<dcs_liberation_installation>/resources/scripts/MissionScripting.lua)." "<br/><br/>You can find more information on how to manually change this file in the Liberation Wiki (Page: Dedicated Server Guide) on GitHub.</p>" "<br/><br/><strong>This is only recommended for expert users!</strong>", QMessageBox.StandardButton.Ignore, QMessageBox.StandardButton.Ok, ) if error_dialog == QMessageBox.Ignore: self.install_dir_ignore_warning = True return False elif not os.path.isdir(os.path.join( self.dcs_install_dir, "Scripts")) and os.path.isfile( os.path.join(self.dcs_install_dir, "bin", "DCS.exe")): error_dialog = QMessageBox.critical( self, "Wrong DCS installation directory.", self.dcs_install_dir + " is not a valid DCS installation directory", QMessageBox.StandardButton.Ok, ) error_dialog.exec_() return False liberation_install.setup(self.saved_game_dir, self.dcs_install_dir) liberation_install.save_config() liberation_theme.save_theme_config() return True
class MonteCarloTab(NewAnalysisTab): def __init__(self, parent=None): super(MonteCarloTab, self).__init__(parent) self.parent: LCAResultsSubTab = parent self.layout.addLayout(get_header_layout('Monte Carlo Simulation')) self.scenario_label = QLabel("Scenario:") self.include_box = QGroupBox("Include uncertainty for:", self) grid = QGridLayout() self.include_tech = QCheckBox("Technosphere", self) self.include_tech.setChecked(True) self.include_bio = QCheckBox("Biosphere", self) self.include_bio.setChecked(True) self.include_cf = QCheckBox("Characterization Factors", self) self.include_cf.setChecked(True) self.include_parameters = QCheckBox("Parameters", self) self.include_parameters.setChecked(True) grid.addWidget(self.include_tech, 0, 0) grid.addWidget(self.include_bio, 0, 1) grid.addWidget(self.include_cf, 1, 0) grid.addWidget(self.include_parameters, 1, 1) self.include_box.setLayout(grid) self.add_MC_ui_elements() self.table = LCAResultsTable() self.table.table_name = 'MonteCarlo_' + self.parent.cs_name self.plot = MonteCarloPlot(self.parent) self.plot.hide() self.plot.plot_name = 'MonteCarlo_' + self.parent.cs_name self.layout.addWidget(self.plot) self.export_widget = self.build_export(has_plot=True, has_table=True) self.layout.addWidget(self.export_widget) self.layout.setAlignment(QtCore.Qt.AlignTop) self.connect_signals() def connect_signals(self): self.button_run.clicked.connect(self.calculate_mc_lca) # signals.monte_carlo_ready.connect(self.update_mc) # self.combobox_fu.currentIndexChanged.connect(self.update_plot) self.combobox_methods.currentIndexChanged.connect( # ignore the index and send the cs_name instead lambda x: self.update_mc(cs_name=self.parent.cs_name)) # signals # self.radio_button_biosphere.clicked.connect(self.button_clicked) # self.radio_button_technosphere.clicked.connect(self.button_clicked) if self.using_presamples: self.scenario_box.currentIndexChanged.connect( self.parent.update_scenario_data) self.parent.update_scenario_box_index.connect( lambda index: self.set_combobox_index(self.scenario_box, index )) def add_MC_ui_elements(self): layout_mc = QVBoxLayout() # H-LAYOUT start simulation self.button_run = QPushButton('Run Simulation') self.label_iterations = QLabel('Iterations:') self.iterations = QLineEdit('10') self.iterations.setFixedWidth(40) self.iterations.setValidator(QtGui.QIntValidator(1, 1000)) self.label_seed = QLabel('Random seed:') self.label_seed.setToolTip( 'Seed value (integer) for the random number generator. ' 'Use this for reproducible samples.') self.seed = QLineEdit('') self.seed.setFixedWidth(30) self.hlayout_run = QHBoxLayout() self.hlayout_run.addWidget(self.scenario_label) self.hlayout_run.addWidget(self.scenario_box) self.hlayout_run.addWidget(self.button_run) self.hlayout_run.addWidget(self.label_iterations) self.hlayout_run.addWidget(self.iterations) self.hlayout_run.addWidget(self.label_seed) self.hlayout_run.addWidget(self.seed) self.hlayout_run.addWidget(self.include_box) self.hlayout_run.addStretch(1) layout_mc.addLayout(self.hlayout_run) # self.label_running = QLabel('Running a Monte Carlo simulation. Please allow some time for this. ' # 'Please do not run another simulation at the same time.') # self.layout_mc.addWidget(self.label_running) # self.label_running.hide() # # buttons for all FUs or for all methods # self.radio_button_all_fu = QRadioButton("For all functional units") # self.radio_button_all_methods = QRadioButton("Technosphere flows") # # self.radio_button_biosphere.setChecked(True) # self.radio_button_technosphere.setChecked(False) # # self.label_for_all_fu = QLabel('For all functional units') # self.combobox_fu = QRadioButton() # self.hlayout_fu = QHBoxLayout() # FU selection # self.label_fu = QLabel('Choose functional unit') # self.combobox_fu = QComboBox() # self.hlayout_fu = QHBoxLayout() # # self.hlayout_fu.addWidget(self.label_fu) # self.hlayout_fu.addWidget(self.combobox_fu) # self.hlayout_fu.addStretch() # self.layout_mc.addLayout(self.hlayout_fu) # method selection self.method_selection_widget = QWidget() self.label_methods = QLabel('Choose LCIA method') self.combobox_methods = QComboBox() self.hlayout_methods = QHBoxLayout() self.hlayout_methods.addWidget(self.label_methods) self.hlayout_methods.addWidget(self.combobox_methods) self.hlayout_methods.addStretch() self.method_selection_widget.setLayout(self.hlayout_methods) layout_mc.addWidget(self.method_selection_widget) self.method_selection_widget.hide() self.layout.addLayout(layout_mc) def build_export(self, has_table: bool = True, has_plot: bool = True) -> QWidget: """Construct the export layout but set it into a widget because we want to hide it.""" export_layout = super().build_export(has_table, has_plot) export_widget = QWidget() export_widget.setLayout(export_layout) # Hide widget until MC is calculated export_widget.hide() return export_widget @QtCore.Slot(name="calculateMcLca") def calculate_mc_lca(self): self.method_selection_widget.hide() self.plot.hide() self.export_widget.hide() iterations = int(self.iterations.text()) seed = None if self.seed.text(): print('SEED: ', self.seed.text()) try: seed = int(self.seed.text()) except ValueError: traceback.print_exc() QMessageBox.warning( self, 'Warning', 'Seed value must be an integer number or left empty.') self.seed.setText('') return includes = { "technosphere": self.include_tech.isChecked(), "biosphere": self.include_bio.isChecked(), "cf": self.include_cf.isChecked(), "parameters": self.include_parameters.isChecked(), } try: self.parent.mc.calculate(iterations=iterations, seed=seed, **includes) self.update_mc() except InvalidParamsError as e: # This can occur if uncertainty data is missing or otherwise broken # print(e) traceback.print_exc() QMessageBox.warning(self, 'Could not perform Monte Carlo simulation', str(e)) # a threaded way for this - unfortunatley this crashes as: # pypardsio_solver is used for the 'spsolve' and 'factorized' functions. Python crashes on windows if multiple # instances of PyPardisoSolver make calls to the Pardiso library # worker_thread = WorkerThread() # print('Created local worker_thread') # worker_thread.set_mc(self.parent.mc, iterations=iterations) # print('Passed object to thread.') # worker_thread.start() # self.label_running.show() # # thread = NewCSMCThread() #self.parent.mc # thread.calculation_finished.connect( # lambda x: print('Calculation finished.')) # thread.start() # # give us a thread and start it # thread = QtCore.QThread() # thread.start() # # # create a worker and move it to our extra thread # worker = Worker() # worker.moveToThread(thread) # self.parent.mct.start() # self.parent.mct.run(iterations=iterations) # self.parent.mct.finished() # objThread = QtCore.QThread() # obj = QObjectMC() # self.parent.cs_name # obj.moveToThread(objThread) # obj.finished.connect(objThread.quit) # objThread.started.connect(obj.long_running) # # objThread.finished.connect(app.exit) # objThread.finished.connect( # lambda x: print('Finished Thread!') # ) # objThread.start() # objThread = QtCore.QThread() # obj = SomeObject() # obj.moveToThread(objThread) # obj.finished.connect(objThread.quit) # objThread.started.connect(obj.long_running) # objThread.finished.connect( # lambda x: print('Finished Thread!') # ) # objThread.start() # self.method_selection_widget.show() # self.plot.show() # self.export_widget.show() def configure_scenario(self): super().configure_scenario() self.scenario_label.setVisible(self.using_presamples) def update_tab(self): self.update_combobox(self.combobox_methods, [str(m) for m in self.parent.mc.methods]) # self.update_combobox(self.combobox_methods, [str(m) for m in self.parent.mct.mc.methods]) def update_mc(self, cs_name=None): # act = self.combobox_fu.currentText() # activity_index = self.combobox_fu.currentIndex() # act_key = self.parent.mc.activity_keys[activity_index] # if cs_name != self.parent.cs_name: # relevant if several CS are open at the same time # return # self.label_running.hide() self.method_selection_widget.show() self.export_widget.show() method_index = self.combobox_methods.currentIndex() method = self.parent.mc.methods[method_index] # data = self.parent.mc.get_results_by(act_key=act_key, method=method) self.df = self.parent.mc.get_results_dataframe(method=method) self.update_table() self.update_plot(method=method) filename = '_'.join([ str(x) for x in [self.parent.cs_name, 'Monte Carlo results', str(method)] ]) self.plot.plot_name, self.table.table_name = filename, filename def update_plot(self, method): idx = self.layout.indexOf(self.plot) self.plot.figure.clf() self.plot.deleteLater() self.plot = MonteCarloPlot(self.parent) self.layout.insertWidget(idx, self.plot) self.plot.plot(self.df, method=method) self.plot.show() if self.layout.parentWidget(): self.layout.parentWidget().updateGeometry() def update_table(self): self.table.sync(self.df)
class ExportFileDialog(QFileDialog): """ Create a custom Export-File Dialog with options like BOM etc. """ def __init__(self,*args,**kwargs): super(ExportFileDialog,self).__init__(*args,**kwargs) self.mainWindow = self.parent() self.setWindowTitle("Export nodes to CSV") self.setAcceptMode(QFileDialog.AcceptSave) self.setOption(QFileDialog.DontUseNativeDialog) #self.setFilter("CSV Files (*.csv)") self.setDefaultSuffix("csv") self.optionBOM = QCheckBox("Use a BOM",self) self.optionBOM.setCheckState(Qt.CheckState.Checked) self.optionLinebreaks = QCheckBox("Remove line breaks",self) self.optionLinebreaks.setCheckState(Qt.CheckState.Checked) self.optionSeparator = QComboBox(self) self.optionSeparator.insertItems(0, [";","\\t",","]) self.optionSeparator.setEditable(True) #self.optionLinebreaks.setCheckState(Qt.CheckState.Checked) self.optionWide = QCheckBox("Convert to wide format (experimental feature)",self) self.optionWide.setCheckState(Qt.CheckState.Unchecked) # if none or all are selected, export all # if one or more are selected, export selective self.optionAll = QComboBox(self) self.optionAll.insertItems(0, ['All nodes (faster for large datasets, ordered by internal ID)','Selected nodes (ordered like shown in nodes view)']) if self.mainWindow.tree.noneOrAllSelected(): self.optionAll.setCurrentIndex(0) else: self.optionAll.setCurrentIndex(1) layout = self.layout() row = layout.rowCount() layout.addWidget(QLabel('Options'),row,0) options = QHBoxLayout() options.addWidget(self.optionBOM) options.addWidget(self.optionLinebreaks) options.addWidget(QLabel('Separator')) options.addWidget(self.optionSeparator) options.addStretch(1) layout.addLayout(options,row,1,1,2) layout.addWidget(QLabel('Post processing'),row+1,0) layout.addWidget(self.optionWide,row+1,1,1,2) layout.addWidget(QLabel('Export mode'),row+2,0) layout.addWidget(self.optionAll,row+2,1,1,2) self.setLayout(layout) if self.exec_(): if os.path.isfile(self.selectedFiles()[0]): os.remove(self.selectedFiles()[0]) output = open(self.selectedFiles()[0], 'w', newline='', encoding='utf8') if self.optionBOM.isChecked() and not self.optionWide.isChecked(): output.write('\ufeff') try: if self.optionAll.currentIndex() == 0: self.exportAllNodes(output) else: self.exportSelectedNodes(output) finally: output.close() if self.optionWide.isChecked(): self.convertToWideFormat(self.selectedFiles()[0]) def exportSelectedNodes(self,output): progress = ProgressBar("Exporting data...", self.mainWindow) #indexes = self.mainWindow.tree.selectionModel().selectedRows() #if child nodes should be exported as well, uncomment this line an comment the previous one indexes = self.mainWindow.tree.selectedIndexesAndChildren() progress.setMaximum(len(indexes)) try: delimiter = self.optionSeparator.currentText() delimiter = delimiter.encode('utf-8').decode('unicode_escape') writer = csv.writer(output, delimiter=delimiter, quotechar='"', quoting=csv.QUOTE_ALL, doublequote=True, lineterminator='\r\n') #headers row = [str(val) for val in self.mainWindow.tree.treemodel.getRowHeader()] if self.optionLinebreaks.isChecked(): row = [val.replace('\n', ' ').replace('\r',' ') for val in row] writer.writerow(row) #rows for no in range(len(indexes)): if progress.wasCanceled: break row = [str(val) for val in self.mainWindow.tree.treemodel.getRowData(indexes[no])] if self.optionLinebreaks.isChecked(): row = [val.replace('\n', ' ').replace('\r',' ') for val in row] writer.writerow(row) progress.step() finally: progress.close() def exportAllNodes(self,output): progress = ProgressBar("Exporting data...", self.mainWindow) progress.setMaximum(Node.query.count()) try: delimiter = self.optionSeparator.currentText() delimiter = delimiter.encode('utf-8').decode('unicode_escape') writer = csv.writer(output, delimiter=delimiter, quotechar='"', quoting=csv.QUOTE_ALL, doublequote=True, lineterminator='\r\n') #headers row = ["level", "id", "parent_id", "object_id", "object_type", "query_status", "query_time", "query_type"] for key in self.mainWindow.tree.treemodel.customcolumns: row.append(key) if self.optionLinebreaks.isChecked(): row = [val.replace('\n', ' ').replace('\r',' ') for val in row] writer.writerow(row) #rows page = 0 while True: allnodes = Node.query.offset(page * 5000).limit(5000) if allnodes.count() == 0: break for node in allnodes: if progress.wasCanceled: break row = [node.level, node.id, node.parent_id, node.objectid, node.objecttype, node.querystatus, node.querytime, node.querytype] for key in self.mainWindow.tree.treemodel.customcolumns: row.append(node.getResponseValue(key)) if self.optionLinebreaks.isChecked(): row = [str(val).replace('\n', ' ').replace('\r',' ') for val in row] writer.writerow(row) # step the Bar progress.step() if progress.wasCanceled: break else: page += 1 finally: progress.close() def convertToWideFormat(self,filename): progress = ProgressBar("Converting data...", self.mainWindow) try: #Separate levels def flattenTable(fulltable,levelcol,idcol,parentidcol,countchildren,removeempty): fulltable[[levelcol]] = fulltable[[levelcol]].astype(int) levels = dict(list(fulltable.groupby(levelcol))) minlevel = fulltable.level.min() for level, data in sorted(levels.items()): #First level is the starting point for the following merges if level == minlevel: #data = data[[idcol,'object_id','object_type']] data = data.add_prefix('level_{}-'.format(level)) flattable = data else: #Aggregate object types and join them for col_countchildren in countchildren: children = data[parentidcol].groupby([data[parentidcol],data[col_countchildren]]).count() children = children.unstack(col_countchildren) children['total'] = children.sum(axis=1) children = children.add_prefix('level_{}-children-{}-'.format(level-1,col_countchildren)) leftkey = 'level_{}-id'.format(level-1) flattable = merge(flattable,children,how='left',left_on=leftkey,right_index=True) flattable[children.columns.values.tolist()] = flattable[children.columns.values.tolist()].fillna(0).astype(int) #Join data data['childnumber'] = data.groupby(parentidcol).cumcount() leftkey = 'level_{}-{}'.format(level-1,idcol) rightkey = 'level_{}-{}'.format(level,parentidcol) data = data.drop([levelcol],axis=1) data = data.add_prefix('level_{}-'.format(level)) flattable = merge(flattable,data,how="outer",left_on=leftkey,right_on=rightkey) if removeempty: flattable = flattable.dropna(axis=1,how='all') return flattable try: #delimiter delimiter = self.optionSeparator.currentText() delimiter = delimiter.encode('utf-8').decode('unicode_escape') #open data = read_csv(filename, sep=delimiter,encoding='utf-8',dtype=str) #convert newdata = flattenTable(data,'level','id','parent_id',['object_type','query_status','query_type'],False) #save outfile = open(filename, 'w',newline='',encoding='utf8') try: if self.optionBOM.isChecked(): outfile.write('\ufeff') #UTF8 BOM newdata.to_csv(outfile,sep=delimiter,index=False,encoding="utf-8") finally: outfile.close() except Exception as e: self.mainWindow.logmessage(e) finally: progress.close()