class AboutDialog(QDialog): def __init__(self, parent=None): super(AboutDialog, self).__init__(parent) self.setupUI() # Add button signal to greetings slot self.okButton.clicked.connect(self.hide) self.show() def setupUI(self): self.resize(431, 260) self.licenseText = QPlainTextEdit(self) self.licenseText.setObjectName(u"licenseText") self.licenseText.setEnabled(True) self.licenseText.setGeometry(QRect(10, 10, 411, 211)) self.licenseText.setFrameShape(QFrame.StyledPanel) self.licenseText.setFrameShadow(QFrame.Sunken) self.licenseText.setUndoRedoEnabled(False) self.licenseText.setTextInteractionFlags(Qt.NoTextInteraction) self.okButton = QPushButton(self) self.okButton.setObjectName(u"okButton") self.okButton.setGeometry(QRect(350, 230, 75, 23)) self.retranslateUi() # setupUi def retranslateUi(self): self.setWindowTitle( QCoreApplication.translate("about", u"About MAEsure", None)) self.licenseText.setPlainText( QCoreApplication.translate( "about", u"MAEsure is a program to measure the surface energy of MAEs via contact angle\n" "Copyright (C) 2021 Raphael Kriegl\n" "\n" "This program is free software: you can redistribute it and/or modify\n" "it under the terms of the GNU General Public License as published by\n" "the Free Software Foundation, either version 3 of the License, or\n" "(at your option) any later version.\n" "\n" "This program is distributed in the hope that it will be useful,\n" "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" "GNU General Public License for more details.\n" "\n" "You should have received a copy of the GNU General Public License\n" "along with this program. If not, see <https://www.gnu.org/licenses/>.", None)) self.licenseText.setPlaceholderText("") self.okButton.setText(QCoreApplication.translate("about", u"OK", None))
class Snippets(QDialog): def __init__(self, parent=None): super(Snippets, self).__init__(parent) # Create widgets self.setWindowModality(Qt.ApplicationModal) self.title = QLabel(self.tr("Snippet Editor")) self.saveButton = QPushButton(self.tr("Save")) self.closeButton = QPushButton(self.tr("Close")) self.clearHotkeyButton = QPushButton(self.tr("Clear Hotkey")) self.setWindowTitle(self.title.text()) self.newFolderButton = QPushButton("New Folder") self.deleteSnippetButton = QPushButton("Delete") self.newSnippetButton = QPushButton("New Snippet") self.edit = QPlainTextEdit() self.edit.setPlaceholderText("python code") self.resetting = False self.columns = 3 self.keySequenceEdit = QKeySequenceEdit(self) self.currentHotkey = QKeySequence() self.currentHotkeyLabel = QLabel("") self.currentFileLabel = QLabel() self.currentFile = "" self.snippetDescription = QLineEdit() self.snippetDescription.setPlaceholderText("optional description") #Set Editbox Size font = getMonospaceFont(self) self.edit.setFont(font) font = QFontMetrics(font) self.edit.setTabStopWidth(4 * font.width(' ')); #TODO, replace with settings API #Files self.files = QFileSystemModel() self.files.setRootPath(snippetPath) self.files.setNameFilters(["*.py"]) #Tree self.tree = QTreeView() self.tree.setModel(self.files) self.tree.setSortingEnabled(True) self.tree.hideColumn(2) self.tree.sortByColumn(0, Qt.AscendingOrder) self.tree.setRootIndex(self.files.index(snippetPath)) for x in range(self.columns): #self.tree.resizeColumnToContents(x) self.tree.header().setSectionResizeMode(x, QHeaderView.ResizeToContents) treeLayout = QVBoxLayout() treeLayout.addWidget(self.tree) treeButtons = QHBoxLayout() treeButtons.addWidget(self.newFolderButton) treeButtons.addWidget(self.newSnippetButton) treeButtons.addWidget(self.deleteSnippetButton) treeLayout.addLayout(treeButtons) treeWidget = QWidget() treeWidget.setLayout(treeLayout) # Create layout and add widgets buttons = QHBoxLayout() buttons.addWidget(self.clearHotkeyButton) buttons.addWidget(self.keySequenceEdit) buttons.addWidget(self.currentHotkeyLabel) buttons.addWidget(self.closeButton) buttons.addWidget(self.saveButton) description = QHBoxLayout() description.addWidget(QLabel(self.tr("Description: "))) description.addWidget(self.snippetDescription) vlayoutWidget = QWidget() vlayout = QVBoxLayout() vlayout.addLayout(description) vlayout.addWidget(self.edit) vlayout.addLayout(buttons) vlayoutWidget.setLayout(vlayout) hsplitter = QSplitter() hsplitter.addWidget(treeWidget) hsplitter.addWidget(vlayoutWidget) hlayout = QHBoxLayout() hlayout.addWidget(hsplitter) self.showNormal() #Fixes bug that maximized windows are "stuck" self.settings = QSettings("Vector35", "Snippet Editor") if self.settings.contains("ui/snippeteditor/geometry"): self.restoreGeometry(self.settings.value("ui/snippeteditor/geometry")) else: self.edit.setMinimumWidth(80 * font.averageCharWidth()) self.edit.setMinimumHeight(30 * font.lineSpacing()) # Set dialog layout self.setLayout(hlayout) # Add signals self.saveButton.clicked.connect(self.save) self.closeButton.clicked.connect(self.close) self.clearHotkeyButton.clicked.connect(self.clearHotkey) self.tree.selectionModel().selectionChanged.connect(self.selectFile) self.newSnippetButton.clicked.connect(self.newFileDialog) self.deleteSnippetButton.clicked.connect(self.deleteSnippet) self.newFolderButton.clicked.connect(self.newFolder) if self.settings.contains("ui/snippeteditor/selected"): selectedName = self.settings.value("ui/snippeteditor/selected") self.tree.selectionModel().select(self.files.index(selectedName), QItemSelectionModel.ClearAndSelect | QItemSelectionModel.Rows) if self.tree.selectionModel().hasSelection(): self.selectFile(self.tree.selectionModel().selection(), None) self.edit.setFocus() cursor = self.edit.textCursor() cursor.setPosition(self.edit.document().characterCount()-1) self.edit.setTextCursor(cursor) else: self.readOnly(True) else: self.readOnly(True) @staticmethod def registerAllSnippets(): for action in list(filter(lambda x: x.startswith("Snippets\\"), UIAction.getAllRegisteredActions())): if action == "Snippets\\Snippet Editor...": continue UIActionHandler.globalActions().unbindAction(action) Menu.mainMenu("Tools").removeAction(action) UIAction.unregisterAction(action) for snippet in includeWalk(snippetPath, ".py"): snippetKeys = None (snippetDescription, snippetKeys, snippetCode) = loadSnippetFromFile(snippet) if not snippetDescription: actionText = "Snippets\\" + os.path.basename(snippet).rstrip(".py") else: actionText = "Snippets\\" + snippetDescription if snippetCode: if snippetKeys == None: UIAction.registerAction(actionText) else: UIAction.registerAction(actionText, snippetKeys) UIActionHandler.globalActions().bindAction(actionText, UIAction(makeSnippetFunction(snippetCode))) Menu.mainMenu("Tools").addAction(actionText, actionText) def clearSelection(self): self.keySequenceEdit.clear() self.currentHotkey = QKeySequence() self.currentHotkeyLabel.setText("") self.currentFileLabel.setText("") self.snippetDescription.setText("") self.edit.setPlainText("") self.currentFile = "" def reject(self): self.settings.setValue("ui/snippeteditor/geometry", self.saveGeometry()) if self.snippetChanged(): question = QMessageBox.question(self, self.tr("Discard"), self.tr("You have unsaved changes, quit anyway?")) if question != QMessageBox.StandardButton.Yes: return self.accept() def newFolder(self): (folderName, ok) = QInputDialog.getText(self, self.tr("Folder Name"), self.tr("Folder Name: ")) if ok and folderName: index = self.tree.selectionModel().currentIndex() selection = self.files.filePath(index) if QFileInfo(selection).isDir(): QDir(selection).mkdir(folderName) else: QDir(snippetPath).mkdir(folderName) def selectFile(self, new, old): if (self.resetting): self.resetting = False return newSelection = self.files.filePath(new.indexes()[0]) self.settings.setValue("ui/snippeteditor/selected", newSelection) if QFileInfo(newSelection).isDir(): self.readOnly(True) self.tree.clearSelection() self.currentFile = "" return if old and old.length() > 0: oldSelection = self.files.filePath(old.indexes()[0]) if not QFileInfo(oldSelection).isDir() and self.snippetChanged(): question = QMessageBox.question(self, self.tr("Discard"), self.tr("Snippet changed. Discard changes?")) if question != QMessageBox.StandardButton.Yes: self.resetting = True self.tree.selectionModel().select(old, QItemSelectionModel.ClearAndSelect | QItemSelectionModel.Rows) return False self.currentFile = newSelection self.loadSnippet() def loadSnippet(self): self.currentFileLabel.setText(QFileInfo(self.currentFile).baseName()) (snippetDescription, snippetKeys, snippetCode) = loadSnippetFromFile(self.currentFile) self.snippetDescription.setText(snippetDescription) if snippetDescription else self.snippetDescription.setText("") self.keySequenceEdit.setKeySequence(snippetKeys) if snippetKeys else self.keySequenceEdit.setKeySequence(QKeySequence("")) self.edit.setPlainText(snippetCode) if snippetCode else self.edit.setPlainText("") self.readOnly(False) def newFileDialog(self): (snippetName, ok) = QInputDialog.getText(self, self.tr("Snippet Name"), self.tr("Snippet Name: ")) if ok and snippetName: if not snippetName.endswith(".py"): snippetName += ".py" index = self.tree.selectionModel().currentIndex() selection = self.files.filePath(index) if QFileInfo(selection).isDir(): path = os.path.join(selection, snippetName) else: path = os.path.join(snippetPath, snippetName) self.readOnly(False) open(path, "w").close() self.tree.setCurrentIndex(self.files.index(path)) log_debug("Snippet %s created." % snippetName) def readOnly(self, flag): self.keySequenceEdit.setEnabled(not flag) self.snippetDescription.setReadOnly(flag) self.edit.setReadOnly(flag) if flag: self.snippetDescription.setDisabled(True) self.edit.setDisabled(True) else: self.snippetDescription.setEnabled(True) self.edit.setEnabled(True) def deleteSnippet(self): selection = self.tree.selectedIndexes()[::self.columns][0] #treeview returns each selected element in the row snippetName = self.files.fileName(selection) question = QMessageBox.question(self, self.tr("Confirm"), self.tr("Confirm deletion: ") + snippetName) if (question == QMessageBox.StandardButton.Yes): log_debug("Deleting snippet %s." % snippetName) self.clearSelection() self.files.remove(selection) self.registerAllSnippets() def snippetChanged(self): if (self.currentFile == "" or QFileInfo(self.currentFile).isDir()): return False (snippetDescription, snippetKeys, snippetCode) = loadSnippetFromFile(self.currentFile) if snippetKeys == None and not self.keySequenceEdit.keySequence().isEmpty(): return True if snippetKeys != None and snippetKeys != self.keySequenceEdit.keySequence().toString(): return True return self.edit.toPlainText() != snippetCode or \ self.snippetDescription.text() != snippetDescription def save(self): log_debug("Saving snippet %s" % self.currentFile) outputSnippet = open(self.currentFile, "w") outputSnippet.write("#" + self.snippetDescription.text() + "\n") outputSnippet.write("#" + self.keySequenceEdit.keySequence().toString() + "\n") outputSnippet.write(self.edit.toPlainText()) outputSnippet.close() self.registerAllSnippets() def clearHotkey(self): self.keySequenceEdit.clear()
class Editor(QWidget): changed = Signal() def __init__(self, parent): super(Editor, self).__init__(parent) self.path = Path(__file__).parent / 'shaders' self.project = None self.programName = None self.vertexShader = '' self.fragmentShader = '' self.colorEditor = QLineEdit() self.colorEditor.setEnabled(False) self.colorEditor.textChanged.connect(self.updateColorHex) self.updateColor((0.5, 0.5, 0.5, 1.0)) self.pickButton = QPushButton('Pick') self.pickButton.setEnabled(False) self.pickButton.clicked.connect(self.pickColor) colorContainer = QWidget() colorContainer.setLayout(QHBoxLayout()) colorContainer.layout().setMargin(0) colorContainer.layout().addWidget(self.colorEditor) colorContainer.layout().addWidget(self.pickButton) self.displayRatioSlider = QSlider(Qt.Horizontal) self.displayRatioSlider.setRange(0, 1000) self.displayRatioSlider.setEnabled(False) self.displayRatioSlider.setSingleStep(1) self.displayRatioSlider.setPageStep(10) self.displayRatioSlider.valueChanged.connect(self.updateDisplayRatio) self.pointSizeSlider = QSlider(Qt.Horizontal) self.pointSizeSlider.setRange(0, 500) self.pointSizeSlider.setEnabled(False) self.pointSizeSlider.setSingleStep(1) self.pointSizeSlider.setPageStep(10) self.pointSizeSlider.valueChanged.connect(self.updatePointSize) self.distanceSlider = QSlider(Qt.Horizontal) self.distanceSlider.setRange(0, 1000) self.distanceSlider.setEnabled(False) self.distanceSlider.setSingleStep(1) self.distanceSlider.setPageStep(10) self.distanceSlider.valueChanged.connect(self.updateDistanceRange) self.vertexEditor = QPlainTextEdit() self.vertexEditor.setStyleSheet( "QPlainTextEdit { background: #393939; color: #b6dede; font: 1rem 'monospace'; }" ) self.vertexEditor.setLineWrapMode(QPlainTextEdit.NoWrap) self.vertexEditor.setWordWrapMode(QTextOption.NoWrap) self.vertexEditor.setTabStopWidth(2) self.vertexEditor.setTabChangesFocus(False) self.vertexEditor.setCenterOnScroll(True) self.vertexEditor.setEnabled(False) self.vertexEditor.textChanged.connect(self.saveVertex) self.fragmentEditor = QPlainTextEdit() self.fragmentEditor.setStyleSheet( "QPlainTextEdit { background: #393939; color: #b6dede; font: 1rem 'monospace'; }" ) self.fragmentEditor.setLineWrapMode(QPlainTextEdit.NoWrap) self.fragmentEditor.setWordWrapMode(QTextOption.NoWrap) self.fragmentEditor.setTabStopWidth(2) self.fragmentEditor.setTabChangesFocus(False) self.fragmentEditor.setCenterOnScroll(True) self.fragmentEditor.setEnabled(False) self.fragmentEditor.textChanged.connect(self.saveFragment) self.setLayout(QVBoxLayout()) self.layout().addWidget(QLabel("Display (%):")) self.layout().addWidget(self.displayRatioSlider) self.layout().addWidget(QLabel("Mask Point size (px):")) self.layout().addWidget(self.pointSizeSlider) self.layout().addWidget(QLabel("Mask Distance range:")) self.layout().addWidget(self.distanceSlider) self.layout().addWidget(QLabel("Clear color:")) self.layout().addWidget(colorContainer) self.layout().addWidget(QLabel("Vertex shader:")) self.layout().addWidget(self.vertexEditor) self.layout().addWidget(QLabel("Fragment shader:")) self.layout().addWidget(self.fragmentEditor) def setProject(self, project): self.project = project if project: self.displayRatioSlider.setValue(project.displayRatio * 1000) self.displayRatioSlider.setEnabled(True) self.pointSizeSlider.setValue(project.maskPointSize * 10) self.pointSizeSlider.setEnabled(True) self.distanceSlider.setValue(project.maskDistanceRange[1] * 10) self.distanceSlider.setEnabled(True) self.updateColor(project.clearColor) self.loadShader(project.cloudShaderName) self.colorEditor.setEnabled(True) self.pickButton.setEnabled(True) self.vertexEditor.setEnabled(True) self.fragmentEditor.setEnabled(True) else: self.displayRatioSlider.setValue(1000) self.displayRatioSlider.setEnabled(False) self.pointSizeSlider.setValue(10) self.pointSizeSlider.setEnabled(False) self.distanceSlider.setValue(10) self.distanceSlider.setEnabled(False) self.updateColor((0.5, 0.5, 0.5, 1.0)) self.clearShader() self.colorEditor.setEnabled(False) self.pickButton.setEnabled(False) self.vertexEditor.setEnabled(False) self.fragmentEditor.setEnabled(False) def parseColorHex(self, value): m = re.compile('#?([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})' ).match(value) if not m: return None return (min(max(int(m.group(1), base=16), 0), 255) / 255.0, min(max(int(m.group(2), base=16), 0), 255) / 255.0, min(max(int(m.group(3), base=16), 0), 255) / 255.0, min(max(int(m.group(4), base=16), 0), 255) / 255.0) def pickColor(self): color = QColor() color.setRgbF(*self.parseColorHex(self.colorEditor.text())) color = QColorDialog.getColor(color, parent=self, title="Background Color", options=QColorDialog.ShowAlphaChannel) if not color.isValid(): return self.updateColor( (color.redF(), color.greenF(), color.blueF(), color.alphaF())) def updateColorHex(self, value): self.updateColor(self.parseColorHex(value)) def updateColor(self, color): def formatChannel(value): return "%02x" % int(min(max(value * 255.0, 0.0), 255.0)) bg = "#%s%s%s" % (formatChannel(color[0]), formatChannel( color[1]), formatChannel(color[2])) fg = "#ffffff" if sum(color) / 3 < 0.5 else "#000000" self.colorEditor.setStyleSheet( "QLineEdit { color: %s; background: %s; }" % (fg, bg)) text = bg + formatChannel(color[3]) if self.colorEditor.text() != text: self.colorEditor.setText(text) if self.project: self.project.setClearColor(color) # self.changed.emit() def updateDisplayRatio(self, value): if self.project: self.project.setDisplayRatio(value / 1000.0) # self.changed.emit() def updatePointSize(self, value): if self.project: self.project.setMaskPointSize(value / 10.0) # self.changed.emit() def updateDistanceRange(self, value): if self.project: self.project.setMaskDistanceRange(value / 10.0) # self.changed.emit() def clearShader(self): self.programName = None self.vertexShader = '' self.fragmentShader = '' self.vertexEditor.setPlainText('') self.fragmentEditor.setPlainText('') def loadShader(self, name): self.programName = name self.vertexShader = '' self.fragmentShader = '' try: with io.open(self.path / (self.programName + '.vs'), 'r') as f: self.vertexShader = f.read() except Exception as e: print(e) try: with io.open(self.path / (self.programName + '.fs'), 'r') as f: self.fragmentShader = f.read() except Exception as e: print(e) self.vertexEditor.setPlainText(self.vertexShader) self.fragmentEditor.setPlainText(self.fragmentShader) def saveVertex(self): value = self.vertexEditor.toPlainText() if not self.programName or self.vertexShader == value: return with io.open(self.path / (self.programName + '.vs'), 'w') as f: f.write(value) self.vertexShader = value self.changed.emit() def saveFragment(self): value = self.fragmentEditor.toPlainText() if not self.programName or self.fragmentShader == value: return with io.open(self.path / (self.programName + '.fs'), 'w') as f: f.write(value) self.fragmentShader = value self.changed.emit()
class BibTeXWidget(QWidget): def __init__(self, parent=None): super().__init__(parent) self._source = None self._table = None self._id = -1 self._inspire_id = None self._doi = None self._bibtex_string = "" self._fetcher = Fetcher() self._fetcher.BatchReady.connect(self._HandleBatchReady) self._fetcher.FetchingFinished.connect(self._HandleFetchingCompleted) self._text_edit = QPlainTextEdit() font = QFont("Monospace") self._text_edit.setFont(font) self._text_edit.setTextInteractionFlags( Qt.TextInteractionFlag.TextSelectableByKeyboard | Qt.TextInteractionFlag.TextSelectableByMouse) self._text_edit.setEnabled(False) self._dowload_inspire = QToolButton() self._dowload_inspire.setIcon(QIcon(icons.INSPIRE)) self._dowload_inspire.clicked.connect(self._FetchINSPIRE) self._dowload_inspire.setEnabled(False) self._dowload_doi = QToolButton() self._dowload_doi.setIcon(QIcon(icons.DOI)) self._dowload_doi.clicked.connect(self._FetchDOI) self._dowload_doi.setEnabled(False) self._SetupUI() def _SetupUI(self): tool_widget = QWidget() tool_layout = QHBoxLayout() tool_layout.setContentsMargins(0, 0, 0, 0) tool_layout.addWidget(self._dowload_inspire) tool_layout.addWidget(self._dowload_doi) tool_layout.addStretch(1) tool_widget.setLayout(tool_layout) layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.addWidget(self._text_edit) layout.addWidget(tool_widget) self.setLayout(layout) def SetTable(self, database_table): if self._table == database_table: return if self._table is not None: self._table.Cleared.disconnect(self.Clear) self._table = database_table self._table.Cleared.connect(self.Clear) def Clear(self): self._text_edit.clear() self._text_edit.setEnabled(False) self._dowload_inspire.setEnabled(False) self._dowload_doi.setEnabled(False) def DisplayItem(self, id_): # NOTE: What if the item gets updated? if self._id == id_: return self._id = id_ self._fetcher.Stop() self.Clear() if self._id == -1: return record = self._table.GetRow(self._id, ("inspire_id", "dois")) self._inspire_id = record["inspire_id"] self._doi = None if record["dois"] == [] else record["dois"][0] has_inspire_id = self._inspire_id is not None has_doi = self._doi is not None self._text_edit.setEnabled(has_inspire_id | has_doi) self._dowload_inspire.setEnabled(has_inspire_id) self._dowload_doi.setEnabled(has_doi) def _FetchINSPIRE(self): self._fetcher.Fetch(InspireBibTeXPlugin, "recid:" + str(self._inspire_id), batch_size=2) def _FetchDOI(self): self._fetcher.Fetch(DOIBibTeXPlugin, self._doi) def _HandleBatchReady(self, batch): self._bibtex_string = batch[0] def _HandleFetchingCompleted(self, records_number): self._text_edit.setPlainText(self._bibtex_string)