Beispiel #1
0
    def get_table(self, list_name, table_name):
        """
        Returns a QTableWidget showing an interaction parameter matrix for a given composition
        :param list_name: str, name of component list of the corresponding table
        :param table_name: str, name of table (could be K, alpha, A, B, C. epsilon, sigma, gamma dep on mixing rule)
        :return: QTableWidget, table containing the correct parameters
        """
        composition = self.component_lists[list_name]["Names"]
        matrix_data = self.settings["Parameters"][list_name]["Coefficient matrices"][table_name]
        size = len(composition)

        table = QTableWidget(size, size)

        # Insert coefficients into table
        for row in range(size):
            table.setVerticalHeaderItem(row, QTableWidgetItem(composition[row]))

        for col in range(size):
            table.setHorizontalHeaderItem(col, QTableWidgetItem(composition[col]))

        for i in range(size):
            for j in range(size):

                if i == j:
                    item = QTableWidgetItem("-")
                    # Not editable
                    item.setFlags(QtCore.Qt.NoItemFlags)

                else:
                    item = QTableWidgetItem(str(matrix_data[i][j]))

                item.setTextAlignment(QtCore.Qt.AlignCenter)

                table.blockSignals(True)
                table.setItem(i, j, item)
                table.blockSignals(False)

                if table_name in ["VDW K", "CPA K", "CPA Epsilon", "SAFT-VR Mie Epsilon",
                                  "SAFT-VR Mie Sigma", "SAFT-VR Mie Gamma"]:
                    # Matrix is symmetric, so these items can't be edited
                    if i >= j:
                        item.setFlags(QtCore.Qt.NoItemFlags)

        header = table.horizontalHeader()
        header.setSectionResizeMode(QHeaderView.ResizeToContents)

        return table
Beispiel #2
0
class VideoTagEditor(QWidget):
    def __init__(self, db):
        super(VideoTagEditor, self).__init__()

        self.path = None
        self.db = db
        self.initUi()

    def initUi(self):
        self.setLayout(QVBoxLayout())

        self.video = BasicVideoWidget()
        self.layout().addWidget(self.video, 1)

        self.row2 = QWidget()
        self.row2.setLayout(QHBoxLayout())
        self.layout().addWidget(self.row2)

        self.playPauseButton = QPushButton('▶')
        self.playPauseButton.clicked.connect(self.playPause)
        self.row2.layout().addWidget(self.playPauseButton)

        self.seeker = SeekSlider(self.video)
        self.seeker.setSingleStep(1000)
        #~ self.seeker.valueChanged.connect(self.video.seek)
        self.row2.layout().addWidget(self.seeker)

        self.posLabel = PositionLabel(self.video)
        self.row2.layout().addWidget(self.posLabel)

        startSet = QPushButton('<')
        startSet.clicked.connect(self.rowSetStart)
        self.layout().addWidget(startSet)

        endSet = QPushButton('>')
        endSet.clicked.connect(self.rowSetEnd)
        self.layout().addWidget(endSet)

        self.endRow = QWidget()
        self.endRow.setLayout(QHBoxLayout())
        self.layout().addWidget(self.endRow)

        self.table = QTableWidget()
        self.table.setColumnCount(3)
        self.table.setHorizontalHeaderLabels(
            ['Tags', 'Start time', 'End time'])
        self.table.itemChanged.connect(self._itemChanged)
        self.table.itemDoubleClicked.connect(self._itemDClicked)
        self.layout().addWidget(self.table)

        self.addRowButton = QPushButton('Add')
        self.addRowButton.clicked.connect(self.addRow)
        self.endRow.layout().addWidget(self.addRowButton)

        self.delRowButton = QPushButton('Remove')
        self.delRowButton.clicked.connect(self.delRow)
        self.endRow.layout().addWidget(self.delRowButton)

    def setFile(self, path):
        self.path = path

        if self.video.state() == QMediaPlayer.PlayingState:
            self.video.pause()

        self.video.load(path)

        self.loadTags()

    @Slot()
    def playPause(self):
        if self.video.state() != QMediaPlayer.PlayingState:
            self.video.play()
            self.playPauseButton.setText('▋▋')
        else:
            self.video.pause()
            self.playPauseButton.setText('▶')

    def chooseTags(self, row):
        row = self.table.currentRow()
        taglist = self.table.item(row, 0).text().split(' ')

        dialog = TagChooserDialog(self.db)
        dialog.setTags(list(taglist))
        dialog.exec_()
        taglist = dialog.selectedTags()
        self.table.item(row, 0).setText(' '.join(taglist))
        #~ self.saveTags()

    @Slot()
    def rowSetStart(self):
        ms = self.video.position()
        n = self.table.currentRow()
        self.table.item(n, 1).setText('%d' % (ms / 1000))
        #~ self.saveTags()

    @Slot()
    def rowSetEnd(self):
        ms = self.video.position()
        n = self.table.currentRow()
        self.table.item(n, 2).setText('%d' % (ms / 1000))
        #~ self.saveTags()

    @Slot()
    def delRow(self):
        self.table.removeRow(self.table.currentRow())
        self.saveTags()

    @Slot()
    def addRow(self):
        n = self.table.rowCount()
        self.table.insertRow(n)
        for col in range(self.table.columnCount()):
            self.table.setItem(n, col, QTableWidgetItem())

    def loadTags(self):
        blocking = self.table.signalsBlocked()
        try:
            self.table.blockSignals(True)
            tags = self.db.find_tags_by_file(self.path)
            for t in tags:
                for start, end in self.db.get_extras_for_file(self.path, t):
                    n = self.table.rowCount()
                    self.table.insertRow(n)
                    self.table.setItem(n, 0, QTableWidgetItem(t))
                    self.table.setItem(n, 1,
                                       QTableWidgetItem('%d' % (start / 1000)))
                    self.table.setItem(n, 2,
                                       QTableWidgetItem('%d' % (end / 1000)))
        finally:
            self.table.blockSignals(blocking)

    def saveTags(self):
        duration = self.video.duration() / 1000

        with self.db:
            self.db.remove_file(self.path)
            for i in range(self.table.rowCount()):
                tags = self.table.item(i, 0).text().split(' ')
                tags = [t for t in tags if t]
                if not tags:
                    continue
                start = int(self.table.item(i, 1).text() or 0) * 1000
                end = int(self.table.item(i, 2).text() or duration) * 1000
                for tag in tags:
                    self.db.tag_file(self.path, tag, start, end)

    def _itemChanged(self, qitem):
        self.saveTags()
        pass

    def _itemDClicked(self, item):
        if item.column() != 0:
            pass
        self.chooseTags(item.row())
Beispiel #3
0
class Labels(QWidget):
    """
    Attributes
    ----------
    chan_name : list of str
        list of all the labels (with the user-defined changes)
    """
    def __init__(self, parent):
        super().__init__()
        self.parent = parent
        self.filename = None
        self.chan_name = None  # None when dataset is not loaded

        self.create()

    def create(self):

        self.idx_load = QPushButton('Load')
        self.idx_load.clicked.connect(self.load_labels)
        self.idx_load.setToolTip('Load file with a list of channels (separated by , or ; or tabs or spaces).')
        self.idx_save = QPushButton('Save')
        self.idx_save.clicked.connect(self.save_labels)
        self.idx_save.setEnabled(False)

        # cancel is equal to setting labels to what they were
        self.idx_cancel = QPushButton('Cancel')
        self.idx_cancel.clicked.connect(self.update)
        self.idx_apply = QPushButton('Apply')
        self.idx_apply.clicked.connect(self.apply)
        self.idx_apply.setToolTip('Changes will take effect. This will reset the channel groups and traces.')

        layout_0 = QHBoxLayout()
        layout_0.addWidget(self.idx_load)
        layout_0.addWidget(self.idx_save)

        layout_1 = QHBoxLayout()
        layout_1.addWidget(self.idx_cancel)
        layout_1.addWidget(self.idx_apply)

        self.table = QTableWidget()

        self.table.horizontalHeader().setStretchLastSection(True)
        self.table.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.table.setColumnCount(2)
        self.table.setHorizontalHeaderLabels(['Current Labels', 'New Labels'])

        self.table.cellChanged.connect(self.check_labels)

        layout = QVBoxLayout()
        layout.addLayout(layout_0)
        layout.addWidget(self.table)
        layout.addLayout(layout_1)

        self.setLayout(layout)

        self.setEnabled(False)

    def update(self, checked=False, labels=None, custom_labels=None):
        """Use this function when we make changes to the list of labels or when
        we load a new dataset.

        Parameters
        ----------
        checked : bool
            argument from clicked.connect
        labels : list of str
            list of labels in the dataset (default)
        custom_labels : list of str
            list of labels from a file
        """
        if labels is not None:
            self.setEnabled(True)
            self.chan_name = labels

        self.table.blockSignals(True)
        self.table.clearContents()
        self.table.setRowCount(len(self.chan_name))

        for i, label in enumerate(self.chan_name):
            old_label = QTableWidgetItem(label)
            old_label.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)

            if custom_labels is not None and i < len(custom_labels) and custom_labels[i]:  # it's not empty string or None
                label_txt = custom_labels[i]
            else:
                label_txt = label
            new_label = QTableWidgetItem(label_txt)

            self.table.setItem(i, 0, old_label)
            self.table.setItem(i, 1, new_label)

        self.table.blockSignals(False)

    def check_labels(self):

        # read new labels first
        labels = self._read_labels()

        # disable apply, if there are duplicates
        if len(labels) == len(set(labels)):
            self.idx_apply.setEnabled(True)
        else:
            self.idx_apply.setEnabled(False)

        # mark duplicates in red
        self.table.blockSignals(True)
        for i, label in enumerate(labels):
            if labels.count(label) > 1:
                self.table.item(i, 1).setBackground(QColor('red'))
            else:
                self.table.item(i, 1).setBackground(QColor('white'))
        self.table.blockSignals(False)

    def load_labels(self, checked=False, test_name=None):
        if self.filename is not None:
            filename = self.filename
        elif self.parent.info.filename is not None:
            filename = Path(self.parent.info.filename)
        else:
            filename = None

        if test_name:
            filename = test_name
        else:
            filename, _ = QFileDialog.getOpenFileName(self,
                                                      'Open Labels',
                                                      str(filename.parent),
                                                      'Comma-separated values (*.csv);; Text file (*.txt);; All Files(*.*)')
        if filename == '':
            return

        self.filename = Path(filename)

        with self.filename.open() as f:
            text = f.read()

        labels = split(', |,|; |;|\t|\n| ', text)
        labels = [label.strip() for label in labels]
        self.update(custom_labels=labels)

    def save_labels(self):
        """Save labels modified by the user.

        TODO
        ----
        Save labels modified by the user
        """
        pass

    def apply(self):

        self.chan_name = self._read_labels()
        self.parent.info.dataset.header['chan_name'] = self.chan_name

        self.parent.channels.reset()
        self.parent.channels.update()
        self.parent.traces.reset()

    def reset(self):
        self.table.blockSignals(True)
        self.table.clearContents()
        self.table.blockSignals(False)

        self.setEnabled(False)

    def _read_labels(self):

        labels = []
        for i in range(self.table.rowCount()):
            labels.append(self.table.item(i, 1).text())

        return labels
Beispiel #4
0
class FS3MainWindow(QMainWindow, FORM_CLASS):
    """
    FS3MainWindow
    Handles the creation and display of the window.
    Makes use of the fs3.ui QT ui file.
    """
    resized = pyqtSignal()

    def __init__(self, parent=None):
        super(FS3MainWindow, self).__init__(parent)
        self.setupUi(self)
        self.mainWindowSplitter.setStretchFactor(1, 10)
        self.setWindowTitle('FS3 -- FieldStats3')
        self.iconPath = ":/plugins/FS3/FS3Icon.png"
        self.setWindowIcon(QIcon(self.iconPath))

        self.fieldGetterInst = LayerFieldGetter()
        self.grapher = Grapher(self.graphTypeBox)
        self.currentProject = QgsProject.instance()
        self.currentLayer = None
        self.allFields = None
        self.emptyCellDict = {}  #{feature_id:cell}

        self.percentile25Update()
        self.currentDecimalPrecision = 0

        self.error = QErrorMessage()

        ### Tabs
        self.tabFields.currentChanged.connect(self.graphTabLoaded)
        self.dataTableLayout = QVBoxLayout()
        self.tableWidget = QTableWidget()

        ### Data Table Widget Connection
        self.tableWidget.cellChanged.connect(self.attributeCellChanged)
        self.horizontalHeader = self.tableWidget.horizontalHeader()
        self.horizontalHeader.sectionClicked.connect(self.handleDataSortSignal)
        self.statisticLayout = QVBoxLayout()
        self.statisticTable = QTableWidget()
        self.uniqueLayout = QVBoxLayout()
        self.uniqueTable = QTableWidget()
        self.uniqueTable.verticalHeader().hide()
        self.graphLayout = QHBoxLayout()
        self.graphView = QWebView()
        self.uniqueHHeader = self.uniqueTable.horizontalHeader()
        self.uniqueHHeader.sectionClicked.connect(self.handleUniqueSortSignal)

        ### Window resized
        self.resizeTimer = QTimer()
        self.resizeTimer.setSingleShot(True)
        self.resizeTimer.timeout.connect(self.windowTimeout)
        self.resized.connect(self.windowResized)

        ###Background Color Brush
        self.backgroundBrush = QColor.fromRgb(230, 230, 250)
        self.defaultBrush = QColor.fromRgbF(0, 0, 0, 0)

        #Refresh for the connecters
        self.refresh()

        ###pyqtSlot connectors (PROJECT)
        self.currentProject.layersAdded.connect(self.refresh)
        self.currentProject.layersRemoved.connect(self.refresh)

        ### pyqtSlot connectors (BUTTONS)
        # Percentile
        self.percentile25.clicked.connect(self.percentile25Update)
        self.percentile10.clicked.connect(self.percentile10Update)
        self.percentile5.clicked.connect(self.percentile5Update)
        self.percentileHighEnd.clicked.connect(self.percentileHighEndUpdate)
        self.percentilesLineEdit.textChanged.connect(
            self.percentileTextChanged)
        # Limit to Selected
        self.limitToSelected.stateChanged.connect(self.handleLimitSelected)
        # Toggle Edit Mode
        self.editModeCheck.stateChanged.connect(self.handleEditModeChecked)
        # Decimal Selector
        self.numberOfDecimalsBox.valueChanged.connect(
            self.handleDecimalChanged)

        ### Layer Combo Box and Field List Widget
        self.selectLayerComboBox.currentIndexChanged \
                        .connect(self.refreshFields)
        self.selectFieldListWidget.itemSelectionChanged \
                        .connect(self.refreshAttributes)

        ### Handles graph stuffs
        self.graphTypeBox.currentIndexChanged.connect(self.refreshAttributes)

        self.openGraphSettings.clicked.connect(self.grapher.openGraphOptions)
        self.grapher.optionsWindow.applyButton.clicked.connect(
            self.refreshGraph)

        self.pngExportButton.clicked.connect(self.exportToPNG)
        self.htmlOpenButton.clicked.connect(self.openHTML)

    @pyqtSlot()
    def openGraphOptions(self):
        """
        openGraphOptions
        Opens the Graph Options UI
        """
        self.grapher.openGraphOptions()

    def refresh(self):
        """
        refresh
        Reloads data
        """
        self.refreshLayers()
        self.refreshFields()
        self.refreshAttributes()

    @pyqtSlot()
    def percentile25Update(self):
        """
        percentile25Update
        Fills LineEdit with 25th percentile numbers
        """
        self.percentilesLineEdit.setText("25, 50, 75, 100")

    @pyqtSlot()
    def percentile10Update(self):
        """
        percentile10Update
        Fills LineEdit with 10th percentile numbers
        """
        percentile10Str = ", ".join(str(x * 10) for x in range(1, 11))
        self.percentilesLineEdit.setText(percentile10Str)

    @pyqtSlot()
    def percentile5Update(self):
        """
        percentile5Update
        Fills LineEdit with 5th percentile numbers
        """
        percentile5Str = ", ".join(str(x * 5) for x in range(1, 21))
        self.percentilesLineEdit.setText(percentile5Str)

    @pyqtSlot()
    def percentileHighEndUpdate(self):
        """
        percentileHighEndUpdate
        Fills LineEdit with High End (50, 80, 95) percentile numbers
        """
        self.percentilesLineEdit.setText("50, 80, 95")

    @pyqtSlot()
    def percentileTextChanged(self):
        """
        percentileTextChanged
        Tokenize percentile display
        """
        try:
            percentileList = self.percentilesLineEdit.text().split(', ')
            for percentile in percentileList:
                # Ensure the user has entered a valid percentile
                if float(percentile) < 0 or float(percentile) > 100:
                    return
            self.refreshAttributes()
        except ValueError:
            # The user is either still entering text
            # Or has entered an invalid input
            return

    @pyqtSlot()
    def handleLimitSelected(self):
        """
        handleLimitSelected
        Limit to selected checkbox
        """
        self.refreshAttributes()

    @pyqtSlot()
    def handleEditModeChecked(self):
        """
        handleEditModeChecked
        Edit Mode trigger within plugin UI
        """
        # Edit box was checked
        # Check state
        if self.editModeCheck.isChecked():
            # Check if the layer is already in edit mode
            if self.currentLayer.isEditable():
                # Our work here is already done
                return
            # Else set the layer to editable
            editMessage = QCoreApplication.translate(
                "FS3MainWindow",
                "FS3 Performance may be slow while in edit mode.")
            editMessage += QCoreApplication.translate(
                "FS3MainWindow",
                "\nPlease ensure it is disabled when not in use.")
            self.error.showMessage(editMessage)
            self.currentLayer.startEditing()
        else:
            # Else the checkbox is unchecked
            # Check state of the layer
            if not self.currentLayer.isEditable():
                # Our work here is already done
                return
            # Else set the layer to noneditable
            self.currentLayer.commitChanges()

    @pyqtSlot()
    def editingStartedQGIS(self):
        """
        editingStartedQGIS
        Handles events when in Edit Mode
        """
        if self.editModeCheck.isChecked():
            # The checkbox is already checked, return
            return
        self.editModeCheck.setChecked(True)

    @pyqtSlot()
    def editingStoppedQGIS(self):
        """
        editingStoppedQGIS
        Handles events when out of Edit Mode
        """
        if not self.editModeCheck.isChecked():
            # The checkbox is already unchecked, return
            return
        self.editModeCheck.setChecked(False)

    @pyqtSlot('int', 'int')
    def attributeCellChanged(self, row, column):
        """
        attributeCellChanged
        Handles updates case any change were made in Edit Mode
        @param row Row of data table cell that was changed
        @param column Column of data table cell that was changed
        """
        newValue = self.tableWidget.item(row, column)
        if self.currentLayer.isEditable():
            # Make the change then commit the change
            fieldname = self.tableWidget.horizontalHeaderItem(column).text()
            for fid, cell in self.emptyCellDict.items():
                feature = self.currentLayer.getFeature(fid)
                fieldIndex = feature.fieldNameIndex(fieldname)
                if cell == newValue:
                    success = self.currentLayer.changeAttributeValue(
                        fid, fieldIndex, newValue.text())

                    if success:
                        # Update was successful, commit changes
                        successCommit = self.currentLayer.commitChanges()
                        if not successCommit:
                            commitError = str(
                                (len(self.currentLayer.commitErrors())))
                            commitError += '\n' + str(
                                (self.currentLayer.commitErrors()[0]))
                            self.error.showMessage(commitError)
                        else:
                            #Else the operation was a success
                            self.currentLayer.startEditing()
                    else:
                        # Update failed, report error
                        updateError = QCoreApplication.translate(
                            "FS3MainWindow", "Attribute update failed")
                        self.error.showMessage(updateError)

    @pyqtSlot()
    def handleDataSortSignal(self):
        """
        handleDataSortSignal
        Handles propper sorting of statistic data
        """
        #Recolor the table
        for i in range(0, self.tableWidget.columnCount()):
            for j in range(0, self.tableWidget.rowCount()):
                cell = self.tableWidget.item(j, i)
                if (j % 2) == 0:
                    #This is an even row, color it
                    cell.setBackground(self.backgroundBrush)
                else:
                    #This is an odd row, default its color
                    cell.setBackground(self.defaultBrush)

    @pyqtSlot()
    def handleUniqueSortSignal(self):
        """
        handleUniqueSortSignal
        Handles propper sorting of unique data
        """
        #Recolor the table
        for i in range(0, self.uniqueTable.columnCount()):
            for j in range(0, self.uniqueTable.rowCount()):
                cell = self.uniqueTable.item(j, i)
                if (j % 2) == 0:
                    #This is an even row, color it
                    cell.setBackground(self.backgroundBrush)
                else:
                    #This is an odd row, default its color
                    cell.setBackground(self.defaultBrush)

    @pyqtSlot()
    def handleSelectionChanged(self):
        """
        handleSelectionChanged
        Update on a new selection
        """
        #If there are selected layers in QGIS
        self.refreshAttributes()

    @pyqtSlot()
    def handleDecimalChanged(self):
        """
        handleDecimalChanged
        Handles Decimal selection box
        """
        self.currentDecimalPrecision = self.numberOfDecimalsBox.value()
        self.refreshAttributes()

    def refreshLayers(self):
        """
        refreshLayers
        Reload the layers coboBox with the current content of the layers list
        """
        self.selectLayerComboBox.clear()

        layers = self.fieldGetterInst.getVectorLayers()
        self.selectLayerComboBox.insertItems(0, layers)

    @pyqtSlot()
    def refreshFields(self):
        """
        refreshFields
        Reload the fields coboBox with the current content of the field lists
        """
        self.selectFieldListWidget.clear()

        layer = self.fieldGetterInst.getSingleLayer \
                (self.selectLayerComboBox.currentText())
        if layer != None:
            self.currentLayer = layer
            self.allFields = self.currentLayer.fields()
            self.selectFieldListWidget.insertItem(
                0, QCoreApplication.translate("FS3MainWindow", "All"))
            self.selectFieldListWidget.insertItems \
                (1, self.fieldGetterInst.getAllFields(layer))

            # 3 is Extended, 2 is Multi, check the documentation
            self.selectFieldListWidget.setSelectionMode(3)

            # Connect the current layer to a pyqtSlot
            self.currentLayer.selectionChanged.connect(
                self.handleSelectionChanged)

            #Listen for editing mode enabled and disabled
            self.currentLayer.editingStarted.connect(self.editingStartedQGIS)
            self.currentLayer.editingStopped.connect(self.editingStoppedQGIS)

            # Update grapher layer
            self.grapher.setData(self.currentLayer)

    @pyqtSlot()
    def refreshAttributes(self):
        """
        refreshAttributes
        Refresh the table content with coresponding Layer & field selection
        """
        self.tableWidget.setSortingEnabled(False)
        self.emptyCellDict.clear()
        self.tableWidget.blockSignals(True)
        self.tableWidget.clear()

        # Get selected fields form list widget
        selectedFields = self.selectFieldListWidget.selectedItems()
        fields = [field.text() for field in selectedFields]

        # If the field is not set yet (Layer was swapped)
        # Return until the refresh is ready
        if len(fields) is 0:
            return

        if self.limitToSelected.isChecked():
            if not self.currentLayer.getSelectedFeatures().isClosed():
                # If there are features selected, get them
                features = self.currentLayer.getSelectedFeatures()
            else:
                # Else get all features
                features = self.currentLayer.getFeatures()
        else:
            features = self.currentLayer.getFeatures()

        # Determine length
        total = 0
        for feature in features:
            total += 1
        self.tableWidget.setRowCount(total)

        if self.limitToSelected.isChecked():
            if not self.currentLayer.getSelectedFeatures().isClosed():
                # If there are features selected, get them
                features = self.currentLayer.getSelectedFeatures()
            else:
                # Else get all features
                features = self.currentLayer.getFeatures()
        else:
            features = self.currentLayer.getFeatures()

        # Data to pass to statistical calculations
        statValues = []
        for i in range(len(fields)):
            statValues.append([])

        # for each row
        row = 0
        for feature in features:
            if QCoreApplication.translate("FS3MainWindow", "All") in fields:
                attributes = feature.attributes()
                self.tableWidget.setColumnCount(len(attributes))

                col = 0
                # for each column value
                for attribute in attributes:
                    #Set the precision of numeric fields
                    if isinstance(attribute, float):
                        attribute = decimalRound(attribute,
                                                 self.currentDecimalPrecision)
                    if attribute == NULL:
                        cell = MyTableWidgetItem("")
                        self.tableWidget.setItem(row, col, cell)
                        self.emptyCellDict[feature.id()] = cell
                    else:
                        cell = MyTableWidgetItem(str(attribute))
                        self.tableWidget.setItem(row, col, cell)
                        self.emptyCellDict[feature.id()] = cell
                    col += 1

            else:
                self.tableWidget.setColumnCount(len(fields))

                col = 0
                for field in fields:
                    fieldIndex = feature.fieldNameIndex(field)
                    attribute = feature.attributes()[fieldIndex]
                    if isinstance(attribute, float):
                        attribute = decimalRound(attribute,
                                                 self.currentDecimalPrecision)
                    statValues[col].append(attribute)
                    if attribute == NULL:
                        cell = MyTableWidgetItem("")
                        self.tableWidget.setItem(row, col, cell)
                        self.emptyCellDict[feature.id()] = cell
                    else:
                        cell = MyTableWidgetItem(str(attribute))
                        self.tableWidget.setItem(row, col, cell)
                        self.emptyCellDict[feature.id()] = cell
                    col += 1

            row += 1
            featureInst = feature

        if QCoreApplication.translate("FS3MainWindow", "All") in fields:
            fields = self.fieldGetterInst.getAllFields(self.currentLayer)
            self.tableWidget.setHorizontalHeaderLabels(fields)
        else:
            self.tableWidget.setHorizontalHeaderLabels(fields)

            statistics = []
            data = []
            uniquenesses = []
            for i in range(len(fields)):
                fieldIndex = featureInst.fieldNameIndex(fields[i])
                uniquenesses.append(statValues[i])
                if self.allFields.at(fieldIndex).isNumeric():
                    #Generate numeric statistics
                    statistics.append(
                        self.createNumericalStatistics(statValues[i]))
                    data.append(statValues[i])
                else:
                    #Generate character statistics
                    statistics.append(
                        self.createCharacterStatistics(statValues[i]))
                    data.append(statValues[i])

            uniqueCalculation = self.createUniqueness(uniquenesses)
            self.grapher.setData(self.currentLayer, data, uniqueCalculation,
                                 self.limitToSelected.isChecked(), fields)

            self.refreshUnique(fields, uniqueCalculation)

            self.refreshStatistics(fields, statistics)

            self.refreshGraph()

        self.dataTableLayout.addWidget(self.tableWidget)
        self.dataTab.setLayout(self.dataTableLayout)
        self.tableWidget.blockSignals(False)
        self.tableWidget.setSortingEnabled(True)
        self.handleDataSortSignal()

    def createNumericalStatistics(self, inputArray):
        """
        createNumericalStatistics
        Methods that instantiates Numerical Statistics and initializes them
        @param inputArray Data from selected field(s) that statistics should be run on
        @return numericalStatistics Finished Statistics Object
        """
        percentileArray = []
        try:
            percentileArray = self.percentilesLineEdit.text().split(', ')
            for percentile in percentileArray:
                if float(percentile) < 0 or float(percentile) > 100:
                    raise ValueError
        except ValueError:
            self.error.showMessage(
                QCoreApplication.translate(
                    "FS3MainWindow", "Invalid Value for Percentile Detected!"))
            percentileArray = []
        originalSize = len(inputArray)
        emptyCellsRemoved = removeEmptyCells(inputArray)
        numericalStatistics = FS3NumericalStatistics()
        numericalStatistics.initialize(emptyCellsRemoved, percentileArray,
                                       originalSize)
        numericalStatistics.roundNumericStatistics(
            self.currentDecimalPrecision)
        return numericalStatistics

    def createCharacterStatistics(self, inputArray):
        """
        createNumericalStatistics
        Methods that instantiates Character Statistics and initializes them
        @param inputArray Data from selected field(s) that statistics should be run on
        @return characterStatistics Finished Statistics Object
        """
        percentileArray = []
        try:
            percentileArray = self.percentilesLineEdit.text().split(', ')
            for percentile in percentileArray:
                if float(percentile) < 0 or float(percentile) > 100:
                    raise ValueError
        except ValueError:
            self.error.showMessage(
                QCoreApplication.translate(
                    "FS3MainWindow", "Invalid Value for Percentile Detected!"))
            percentileArray = []
        originalSize = len(inputArray)
        emptyCellsRemoved = removeEmptyCells(inputArray)
        characterStatistics = FS3CharacterStatistics()
        characterStatistics.initialize(emptyCellsRemoved, percentileArray,
                                       originalSize)
        characterStatistics.roundCharacterStatistics(
            self.currentDecimalPrecision)
        return characterStatistics

    def createUniqueness(self, inputArray):
        """
        createUniqueness
        Method that instantiates Uniqueness class and initializes it
        @param inputArray Data from selected field(s) that uniqueness should be run on
        @return uniqueness FS3Uniqueness object with calculated uniqueness
        """
        uniqueness = FS3Uniqueness()
        uniqueness.initialize(inputArray)
        uniqueness.roundUniqueness(self.currentDecimalPrecision)
        return uniqueness

    def refreshStatistics(self, fields, stats):
        """
        refreshStatistics
        Method that updates the statistic table
        @param fields Fields that are selected
        @param stats The current stats that are displayed
        """
        numAndCharStats = False
        verticalHeaders = stats[0].statName
        for i in range(len(stats)):
            for j in range(len(stats)):
                if isinstance(stats[i], FS3NumericalStatistics):
                    if isinstance(stats[j], FS3CharacterStatistics):
                        numAndCharStats = True
                        verticalHeaders = stats[i].statName + stats[j].statName
                        break

        self.statisticTable.clear()
        self.statisticTable.setRowCount(stats[0].statCount +
                                        stats[0].statCount * numAndCharStats)
        self.statisticTable.setColumnCount(len(fields))
        self.statisticTable.setVerticalHeaderLabels(verticalHeaders)
        self.statisticTable.setHorizontalHeaderLabels(fields)

        col = 0
        for stat in stats:
            #See if the field is numeric
            if isinstance(stat, FS3NumericalStatistics):
                emptyData = stat.totalItemCount - stat.itemCount
                row = 0
                self.statisticTable.setItem(
                    row, col, QTableWidgetItem(str(stat.totalItemCount)))
                row += 1
                self.statisticTable.setItem(
                    row, col, QTableWidgetItem(str(stat.itemCount)))
                row += 1
                self.statisticTable.setItem(row, col,
                                            QTableWidgetItem(str(emptyData)))
                row += 1
                self.statisticTable.setItem(
                    row, col, QTableWidgetItem(str(stat.maxValue)))
                row += 1
                self.statisticTable.setItem(
                    row, col, QTableWidgetItem(str(stat.minValue)))
                row += 1
                self.statisticTable.setItem(
                    row, col, QTableWidgetItem(str(stat.meanValue)))
                row += 1
                self.statisticTable.setItem(
                    row, col, QTableWidgetItem(str(stat.medianValue)))
                row += 1
                self.statisticTable.setItem(
                    row, col, QTableWidgetItem(str(stat.modeValue)))
                row += 1
                self.statisticTable.setItem(
                    row, col, QTableWidgetItem(str(stat.sumValue)))
                row += 1
                self.statisticTable.setItem(
                    row, col, QTableWidgetItem(str(stat.stdDevValue)))
                row += 1
                self.statisticTable.setItem(
                    row, col, QTableWidgetItem(str(stat.coeffVarValue)))
                row += 1
                for percentile in stat.percentiles:
                    self.statisticTable.setItem(
                        row, col, QTableWidgetItem(str(percentile)))
                    row += 1
            else:
                # is character statistics
                row = 0
                if numAndCharStats:
                    row = stat.statCount

                emptyData = stat.totalItemCount - stat.itemCount
                self.statisticTable.setItem(
                    row, col, QTableWidgetItem(str(stat.totalItemCount)))
                row += 1
                self.statisticTable.setItem(
                    row, col, QTableWidgetItem(str(stat.itemCount)))
                row += 1
                self.statisticTable.setItem(row, col,
                                            QTableWidgetItem(str(emptyData)))
                row += 1
                self.statisticTable.setItem(
                    row, col, QTableWidgetItem(str(stat.maxLength)))
                row += 1
                self.statisticTable.setItem(
                    row, col, QTableWidgetItem(str(stat.minLength)))
                row += 1
                self.statisticTable.setItem(
                    row, col, QTableWidgetItem(str(stat.meanLength)))
                row += 1
                self.statisticTable.setItem(
                    row, col, QTableWidgetItem(str(stat.medianLength)))
                row += 1
                self.statisticTable.setItem(
                    row, col, QTableWidgetItem(str(stat.modeLength)))
                row += 1
                self.statisticTable.setItem(
                    row, col, QTableWidgetItem(str(stat.sumLength)))
                row += 1
                self.statisticTable.setItem(
                    row, col, QTableWidgetItem(str(stat.stdDevLength)))
                row += 1
                self.statisticTable.setItem(
                    row, col, QTableWidgetItem(str(stat.coeffVarLength)))
                row += 1
                for percentile in stat.percentiles:
                    self.statisticTable.setItem(
                        row, col, QTableWidgetItem(str(percentile)))
                    row += 1
            col += 1

        self.statisticLayout.addWidget(self.statisticTable)
        self.statisticsTab.setLayout(self.statisticLayout)

        self.handleStatisticsColor()

    def handleStatisticsColor(self):
        """
        handleStatisticsColor
        Method implements zebra colors with table rows
        """
        for i in range(0, self.statisticTable.columnCount()):
            for j in range(0, self.statisticTable.rowCount()):
                cell = self.statisticTable.item(j, i)
                if cell is None:
                    cell = MyTableWidgetItem("")
                    self.statisticTable.setItem(j, i, cell)
                if (j % 2) == 0:
                    #This is an even row, color it
                    cell.setBackground(self.backgroundBrush)
                else:
                    #This is an odd row, default its color
                    cell.setBackground(self.defaultBrush)

    def refreshUnique(self, fields, unique):
        """
        refreshUnique
        Method that updates the unique table
        @param fields Currently selected fields
        @param unique Currently displayed unique data
        """
        #Start by clearing the layout
        self.uniqueTable.setSortingEnabled(False)
        row = 0
        col = 0
        self.uniqueTable.clear()
        self.uniqueTable.setRowCount(unique.totalValues)
        self.uniqueTable.setColumnCount(unique.statCount)
        horizontalHeaders = unique.statName
        #Append the names of the fields to the value field
        horizontalHeaders[0] += ' (['
        for field in fields:
            horizontalHeaders[0] += field + '] , ['
        horizontalHeaders[0] = horizontalHeaders[0][:-5]
        horizontalHeaders[0] += '])'
        self.uniqueTable.setHorizontalHeaderLabels(horizontalHeaders)
        for value in unique.uniqueValues:
            cell = MyTableWidgetItem(str(value))
            self.uniqueTable.setItem(row, col, cell)
            row += 1
        row = 0
        col += 1
        for occurance in unique.uniqueNumOccur:
            cell = MyTableWidgetItem(str(occurance))
            self.uniqueTable.setItem(row, col, cell)
            row += 1
        row = 0
        col += 1
        for percent in unique.uniquePercent:
            cell = MyTableWidgetItem(str(percent))
            self.uniqueTable.setItem(row, col, cell)
            row += 1
        self.uniqueTable.setSortingEnabled(True)
        self.handleUniqueSortSignal()
        self.uniqueLayout.addWidget(self.uniqueTable)
        self.uniqueTab.setLayout(self.uniqueLayout)

    @pyqtSlot()
    def refreshGraph(self):
        """
        refreshGraph
        Refreshes the graph on data change
        """
        plot_path = self.grapher.makeGraph()
        self.graphView.load(QUrl.fromLocalFile(plot_path))
        self.graphLayout.addWidget(self.graphView)
        self.graphFrame.setLayout(self.graphLayout)
        self.graphView.show()

    def resizeEvent(self, event):
        self.resized.emit()
        return super(FS3MainWindow, self).resizeEvent(event)

    @pyqtSlot()
    def windowResized(self):
        self.resizeTimer.start(250)

    def windowTimeout(self):
        currentTab = self.tabFields.currentWidget()
        if currentTab == self.graphTab:
            #Refresh the attributes to create a new graph
            self.refreshAttributes()

    @pyqtSlot()
    def graphTabLoaded(self):
        currentTab = self.tabFields.currentWidget()
        if currentTab == self.graphTab:
            #Refresh the attributes to create a new graph
            self.refreshAttributes()

    @pyqtSlot()
    def exportToPNG(self):
        # Attempt to pull a file location from the grapher options
        path = self.grapher.optionsWindow.pngExportEdit.text()
        if (path is None) or (path == ''):
            # The user has not entered a path yet
            pngError = QCoreApplication.translate(
                "FS3MainWindow", "Error: No export path detected!\n")
            pngError += QCoreApplication.translate(
                "FS3MainWindow", "Please open the graph settings window ")
            pngError += QCoreApplication.translate(
                "FS3MainWindow", "and set an export path for the image.")
            self.error.showMessage(pngError)
            return
        #If a filepath can be found, attempt to save the image to it
        path += '/FS3Graph.png'
        success = self.graphFrame.grab().save(path, format='PNG', quality=100)
        if not success:
            #Cannot write file to that filepath
            pngError = 'Error: Cannot write file to given filepath!\n'
            pngError += 'Please ensure the filepath was entered correctly '
            pngError += 'and it is a writable directory'
            self.error.showMessage(pngError)
            return

    @pyqtSlot()
    def openHTML(self):
        # Pull the path of the temp directory
        directory = tempfile.gettempdir()
        path = directory + '/temp_plot_name.html'
        webbrowser.open('file://' + os.path.realpath(path))
class App(QWidget):
    def __init__(self):
        super().__init__()
        self.title = 'CSA Program Chapter Tracker'
        self.left = 50
        self.top = 75
        self.width = 1350
        self.height = 700
        self.init_UI()

    def init_UI(self):
        self.setWindowTitle(self.title)
        self.setGeometry(self.left, self.top, self.width, self.height)

        self.create_student_table()
        self.create_record_table()
        self.recordTableWidget.setEditTriggers(
            QAbstractItemView.NoEditTriggers)
        self.create_calendar()

        # l is label
        self.lname = QLabel("Name", self)
        self.lnumber = QLabel("Student ID", self)
        self.lgrade = QLabel("Grade", self)
        self.lhours = QLabel("Hours", self)
        self.lcsa = QLabel("CSA", self)
        self.lstart = QLabel(self.my_calendar.selectedDate().toString(), self)
        self.lend = QLabel(self.my_calendar.selectedDate().toString(), self)

        self.lname.move(102.5, 540)
        self.lnumber.move(252.5, 540)
        self.lgrade.move(425, 540)
        self.lhours.move(562.5, 540)
        self.lcsa.move(710, 540)
        self.lstart.move(1025, 350)
        self.lend.move(1180, 350)

        self.lstart.setFrameStyle(QFrame.Panel | QFrame.Raised)
        self.lend.setFrameStyle(QFrame.Panel | QFrame.Raised)

        self.lstart.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        self.lstart.setAlignment(Qt.AlignCenter)
        self.lend.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        self.lend.setAlignment(Qt.AlignCenter)
        self.lstart.resize(self.lstart.sizeHint().width() + 6,
                           self.lstart.sizeHint().height() + 3)
        self.lend.resize(self.lend.sizeHint().width() + 6,
                         self.lend.sizeHint().height() + 3)

        # tb is textbox
        self.tbname = QLineEdit(self)
        self.tbnumber = QLineEdit(self)
        self.tbhours = QLineEdit(self)

        # b is combobox
        self.bgrade = QComboBox(self)
        self.bcsa = QComboBox(self)
        self.btimespan = QComboBox(self)

        self.bgrade.addItem("9th")
        self.bgrade.addItem("10th")
        self.bgrade.addItem("11th")
        self.bgrade.addItem("12th")

        self.bcsa.addItem("N/A")
        self.bcsa.addItem("CSA Community")
        self.bcsa.addItem("CSA Service")
        self.bcsa.addItem("CSA Achievement")

        self.btimespan.addItem("Weeks")
        self.btimespan.addItem("Months")
        self.btimespan.addItem("Custom")

        self.bgrade.move(368, 570)
        self.bcsa.move(652.5, 570)
        self.btimespan.move(815, 105)

        self.tbname.move(50, 570)
        self.tbnumber.move(200, 570)
        self.tbhours.move(510, 570)

        self.tbname.resize(140, 25)
        self.tbnumber.resize(165, 25)
        self.tbhours.resize(140, 25)

        self.bgrade.resize(140, 25)
        self.bcsa.resize(155, 25)

        self.bgrade.currentText()

        self.add_button = QPushButton('Add', self)
        self.add_button.setToolTip('Add student to database with given data')
        self.add_button.move(810, 567.5)
        self.add_button.clicked.connect(self.add_student)

        self.delete_button = QPushButton('Delete', self)
        self.delete_button.setToolTip('Delete selected rows from database')
        self.delete_button.move(810, 245)
        self.delete_button.clicked.connect(self.remove_students)

        self.print_button = QPushButton('Compile Data', self)
        self.print_button.setToolTip(
            'Compile the current student data as a pdf')
        self.print_button.move(810, 65)
        self.print_button.resize(self.print_button.sizeHint())
        self.print_button.clicked.connect(self.compile_pdf)

        self.generate_button = QPushButton('Generate Student', self)
        self.generate_button.setToolTip('Generate a random student')
        self.generate_button.move(810, 605)
        self.generate_button.resize(self.generate_button.sizeHint())
        self.generate_button.clicked.connect(self.generate_students)

        self.start_date_button = QPushButton('Set Start', self)
        self.start_date_button.setToolTip(
            'Set the start date for the data to compile at')
        self.start_date_button.move(1030, 315)
        self.start_date_button.resize(self.start_date_button.sizeHint())
        self.start_date_button.clicked.connect(self.set_start_date)

        self.end_date_button = QPushButton('Set End', self)
        self.end_date_button.setToolTip(
            'Set the end date for the data to compile to')
        self.end_date_button.move(1185, 315)
        self.end_date_button.resize(self.end_date_button.sizeHint())
        self.end_date_button.clicked.connect(self.set_end_date)

        self.btimespan.resize(self.print_button.sizeHint())

        self.layout = QVBoxLayout()
        self.layout.setContentsMargins(50, 70, 550, 200)

        self.layout.addWidget(self.recordTableWidget)
        self.layout.addWidget(self.studentTableWidget)
        self.layout.setSpacing(29)
        self.layout.setStretch(0, 3)
        self.layout.setStretch(1, 5)

        self.setLayout(self.layout)
        self.edit = True

        self.show()

    def create_student_table(self):
        self.studentTableWidget = QTableWidget()

        row_count = c.execute("SELECT COUNT(*) FROM studentdata").fetchone()[0]
        self.studentTableWidget.setRowCount(row_count)
        self.studentTableWidget.setColumnCount(5)
        self.studentTableWidget.setShowGrid(True)
        self.studentTableWidget.move(0, 0)
        self.studentTableWidget.setHorizontalHeaderLabels(
            ['Name', 'Student ID', 'Grade Level', 'Hours', 'CSA'])

        self.header = self.studentTableWidget.horizontalHeader()
        self.header.setSectionResizeMode(0, QHeaderView.Stretch)
        self.header.setSectionResizeMode(1, QHeaderView.ResizeToContents)
        self.header.setSectionResizeMode(2, QHeaderView.ResizeToContents)
        self.header.setSectionResizeMode(3, QHeaderView.ResizeToContents)
        self.header.setSectionResizeMode(4, QHeaderView.ResizeToContents)

        self.populate_student_data()
        self.studentTableWidget.itemChanged.connect(self.log_change)

    def create_record_table(self):
        self.recordTableWidget = QTableWidget()

        row_count = c.execute("SELECT COUNT(*) FROM recorddata").fetchone()[0]
        self.recordTableWidget.setRowCount(row_count)
        self.recordTableWidget.setColumnCount(4)
        self.recordTableWidget.setShowGrid(True)
        self.recordTableWidget.setHorizontalHeaderLabels(
            ['Date', 'Name', 'Student ID', 'Hours Added'])

        self.header = self.recordTableWidget.horizontalHeader()
        self.header.setSectionResizeMode(0, QHeaderView.Stretch)
        self.header.setSectionResizeMode(1, QHeaderView.ResizeToContents)
        self.header.setSectionResizeMode(3, QHeaderView.ResizeToContents)
        self.header.setSectionResizeMode(2, QHeaderView.ResizeToContents)

        self.populate_record_data()

    def create_calendar(self):
        self.my_calendar = QCalendarWidget(self)
        self.my_calendar.move(1000, 70)
        self.my_calendar.resize(300, 200)
        self.ldate = QLabel(self)
        self.ldate.move(1100, 280)
        self.ldate.setFrameStyle(QFrame.Panel | QFrame.Sunken)
        self.ldate.setLineWidth(2)
        self.my_calendar.clicked[QDate].connect(self.on_date_selection)

        self.ldate.setText(self.my_calendar.selectedDate().toString())
        self.ldate.resize(self.ldate.sizeHint())

    def on_date_selection(self, date):
        self.ldate.setText(date.toString())
        self.ldate.resize(self.ldate.sizeHint())

    def populate_student_data(self):
        c.execute("SELECT * FROM studentdata")
        student_data = c.fetchall()
        self.edit = False
        self.studentTableWidget.setRowCount(0)
        self.studentTableWidget.setRowCount(len(student_data))
        for i in range(len(student_data)):
            self.studentTableWidget.setItem(
                i, 0, QTableWidgetItem(str(student_data[i][0])))
            self.studentTableWidget.setItem(
                i, 1, QTableWidgetItem(str(student_data[i][1])))
            self.studentTableWidget.setItem(
                i, 2, QTableWidgetItem(str(student_data[i][2])))
            self.studentTableWidget.setItem(
                i, 3, QTableWidgetItem(str(student_data[i][3])))
            self.studentTableWidget.setItem(
                i, 4, QTableWidgetItem(str(student_data[i][4])))
        self.edit = True

    def populate_record_data(self):
        c.execute("SELECT * FROM recorddata")
        record_data = c.fetchall()[::-1]
        self.recordTableWidget.setRowCount(0)
        self.recordTableWidget.setRowCount(len(record_data))
        for i in range(len(record_data)):
            self.recordTableWidget.setItem(
                i, 0, QTableWidgetItem(str(record_data[i][0])))
            self.recordTableWidget.setItem(
                i, 1, QTableWidgetItem(str(record_data[i][1])))
            self.recordTableWidget.setItem(
                i, 2, QTableWidgetItem(str(record_data[i][2])))
            self.recordTableWidget.setItem(
                i, 3, QTableWidgetItem(str(record_data[i][3])))

    def log_change(self, item):
        if self.edit:
            cols = {0: "name", 1: "number", 2: "grade", 3: "hours", 4: "csa"}
            c.execute("SELECT * FROM studentdata")
            student_data = c.fetchall()
            student_number = student_data[item.row()][1]
            student_name = student_data[item.row()][0]
            old_hours = student_data[item.row()][3]

            if item.column() == 3:
                hours = item.text()
                if hours.isdigit() == False:
                    try:
                        hours = eval(hours)
                        c.execute(
                            "UPDATE studentdata SET hours = ? WHERE number = ?",
                            (
                                hours,
                                student_number,
                            ))
                        self.update_csa(float(hours), student_number, item)

                        c.execute("INSERT INTO recorddata VALUES (?, ?, ?, ?)", (datetime.now().strftime("%B %d, %Y %I:%M %p"), \
                        student_name, student_number, str('%.3f' % (float(hours)-old_hours)).rstrip('0').rstrip('.'),))
                        self.populate_record_data()
                    except Exception as e:
                        value_error = QMessageBox()
                        value_error.setIcon(QMessageBox.Critical)
                        value_error.setInformativeText(
                            'Please enter a valid expression or number')
                        value_error.setWindowTitle("Error: Value")
                        value_error.exec_()
                else:
                    c.execute(
                        "UPDATE studentdata SET hours = ? WHERE number = ?", (
                            hours,
                            student_number,
                        ))
                    self.update_csa(float(hours), student_number, item)

                    c.execute("INSERT INTO recorddata VALUES (?, ?, ?, ?)", (datetime.now().strftime("%B %d, %Y %I:%M %p"), \
                    student_name, student_number, str('%.3f' % (float(hours)-old_hours)).rstrip('0').rstrip('.'),))
                    self.populate_record_data()

            else:
                try:
                    c.execute(
                        "UPDATE studentdata SET {} = ? WHERE number = ?".
                        format(cols[item.column()]), (
                            item.text(),
                            student_number,
                        ))
                    if item.column() == 0 or item.column() == 1:
                        c.execute(
                            "UPDATE recorddata SET {} = ? WHERE number = ?".
                            format(cols[item.column()]), (
                                item.text(),
                                student_number,
                            ))
                        self.populate_record_data()
                except Exception as e:
                    insert_error = QMessageBox()
                    insert_error.setIcon(QMessageBox.Critical)
                    insert_error.setInformativeText(
                        'Please check your Student ID is unique')
                    insert_error.setWindowTitle("Error: Values")
                    insert_error.exec_()
                    replace_list = [
                        '/' * (i + 1) for i in range(len(student_data))
                    ]
                    replace_existing = [
                        i[1] for i in student_data if i[1] in replace_list
                    ]
                    replace_new = [
                        i for i in replace_list if i not in replace_existing
                    ]
                    replace_new.sort(key=len)
                    c.execute(
                        "UPDATE studentdata SET number = ? WHERE number = ?", (
                            replace_new[0],
                            student_number,
                        ))
                    c.execute(
                        "UPDATE recorddata SET number = ? WHERE number = ?", (
                            replace_new[0],
                            student_number,
                        ))
                    self.studentTableWidget.setItem(
                        item.row(), item.column(),
                        QTableWidgetItem(replace_new[0]))

            conn.commit()

    def update_csa(self, hours, student_number, item):
        csa_switcher = {
            50: "CSA Community",
            200: "CSA Service",
            500: "CSA Achievement"
        }
        adjusted_hours = 500 if hours >= 500 else 200 if hours >= 200 else 50 if hours >= 50 else 0
        csa_award = csa_switcher.get(adjusted_hours, "N/A")
        c.execute("UPDATE studentdata SET csa = ? WHERE number = ?",
                  (csa_award, student_number))
        conn.commit()

        try:
            self.studentTableWidget.blockSignals(True)
            self.studentTableWidget.setItem(item.row(), 4,
                                            QTableWidgetItem(str(csa_award)))
            self.studentTableWidget.setItem(
                item.row(), 3,
                QTableWidgetItem(str('%.3f' % hours).rstrip('0').rstrip('.')))
            self.studentTableWidget.blockSignals(False)
        except Exception as e:
            print(e)

        c.execute("SELECT * FROM studentdata")

    @pyqtSlot()
    def add_student(self):
        # i is input
        iname = self.tbname.text()
        inumber = self.tbnumber.text()
        igrade = str(self.bgrade.currentText())
        ihours = self.tbhours.text()
        icsa = str(self.bcsa.currentText())
        try:
            for i in range(5):
                if [iname, inumber, igrade, ihours, icsa][i] == '':
                    raise Exception
            int(ihours)
            c.execute('''INSERT INTO studentdata VALUES (?, ?, ?, ?, ?)''', (
                iname,
                inumber,
                igrade,
                ihours,
                icsa,
            ))
            c.execute('''INSERT INTO recorddata VALUES (?, ?, ?, ?)''', (
                datetime.now().strftime("%B %d, %Y %I:%M %p"),
                iname,
                inumber,
                ihours,
            ))
            conn.commit()

            self.populate_student_data()
            self.populate_record_data()
        except Exception as e:

            insert_error = QMessageBox()
            insert_error.setIcon(QMessageBox.Critical)
            insert_error.setInformativeText(
                'Please check your Student ID is unique, all inputs are filled out, and hours is a number'
            )
            insert_error.setWindowTitle("Error: Values")
            insert_error.exec_()

    @pyqtSlot()
    def remove_students(self):
        selected = self.studentTableWidget.selectedItems()
        selected_dict = {}
        for item in selected:
            if item.row() in selected_dict:
                selected_dict[item.row()] += 1
            else:
                selected_dict[item.row()] = 1

        c.execute('''SELECT * FROM studentdata''')
        student_data = c.fetchall()
        numbers = ()

        for key in reversed(list(selected_dict.keys())):
            if selected_dict[key] == 5:
                self.studentTableWidget.removeRow(key)
                numbers += (student_data[key][1], )

        c.execute(
            "DELETE FROM studentdata WHERE number IN ({})".format(", ".join(
                "?" * len(numbers))), numbers)
        c.execute(
            "DELETE FROM recorddata WHERE number IN ({})".format(", ".join(
                "?" * len(numbers))), numbers)
        self.populate_record_data()

        conn.commit()

    @pyqtSlot()
    def compile_pdf(self):
        pdf = FPDF()
        pdf.add_page()
        pdf.set_font("courier", size=12)

        if self.btimespan.currentText() == "Weeks":
            start_range = (datetime.now() - timedelta(7))
            end_range = datetime.now()
            txt = "Last Week Report"
        elif self.btimespan.currentText() == "Months":
            start_range = (datetime.now() - timedelta(30))
            end_range = datetime.now()
            txt = "Last Week Report"
        else:
            start_range = datetime.strptime(self.lstart.text(), "%a %b %d %Y")
            end_range = datetime.strptime(self.lend.text(), "%a %b %d %Y")
            txt = "Custom Report: %s - %s" % (start_range.strftime(
                "%B %d, %Y"), end_range.strftime("%B %d, %Y"))

        pdf.cell(200, 10, txt=txt, ln=1, align="C")
        c.execute('''SELECT * FROM recorddata''')
        datafetch = c.fetchall()
        record_data = [record for record in datafetch if datetime.strptime(record[0], ("%B %d, %Y %I:%M %p")) >= start_range and\
             datetime.strptime(record[0], ("%B %d, %Y %I:%M %p")) <= end_range][::-1]

        if record_data == []:
            insert_error = QMessageBox()
            insert_error.setIcon(QMessageBox.Critical)
            insert_error.setInformativeText('No data to Compile')
            insert_error.setWindowTitle("Error: Data")
            insert_error.exec_()
        else:
            field1_max = len(
                max([str(record[0]) for record in record_data], key=len))
            field2_max = len(
                max([str(record[1]) for record in record_data], key=len))
            field3_max = len(
                max([str(record[2]) for record in record_data], key=len))
            field4_max = len(
                max([str(record[3]) for record in record_data], key=len))

            field_maxes = [field1_max, field2_max, field3_max, field4_max]
            xpos = 200
            ypos = 15
            yline = 35
            iconst = 2.725
            sconst = 2.4
            spacer = 0 if sum(field_maxes) > 75 else int(
                (75 - sum(field_maxes)) / 3)
            line_count = 0
            page_lines = 17

            if spacer == 0:
                pdf.set_font("courier", size=9)
                spacer = 0 if sum(field_maxes) > 100 else int(
                    (100 - sum(field_maxes)) / 3)
                iconst *= 0.75
                sconst *= 0.75

            for record in record_data:
                txt = ''
                for i in range(len(list(record))):
                    txt += str(
                        record[i]) + ' ' * int(field_maxes[i] -
                                               len(str(record[i])) + spacer)

                pdf.cell(xpos, ypos, txt=txt, ln=1, align="L")
                pdf.line(0, yline, 400, yline)
                yline += 15
                line_count += 1

                newx = 0
                if line_count % page_lines == 0:
                    yline = 10
                    title_space = 25 if line_count == page_lines else 10
                    for i in field_maxes[0:3]:
                        newx += (i) * iconst + (spacer) * sconst
                        pdf.line(newx, title_space, newx, 20 + 15 * page_lines)
                elif line_count == len(record_data):
                    title_space = 25 if line_count < page_lines else 10
                    footer_space = 20 if line_count < page_lines else 10
                    for i in field_maxes[0:3]:
                        newx += (i) * iconst + (spacer) * sconst
                        pdf.line(newx, title_space, newx,
                                 footer_space + 15 * (line_count % page_lines))

            if len(record_data) > page_lines:
                pdf.line(0, yline, 400, yline)

            c.execute('''SELECT name, number FROM studentdata''')
            student_data = c.fetchall()
            for student in student_data:
                if student[1] in [record[2] for record in record_data]:
                    total_user_hours = str(
                        round(
                            sum([
                                record[3] for record in record_data
                                if record[2] == student[1]
                            ]), 3))
                    txt = ' ' * int(
                        sum(field_maxes) + spacer * 3 - len(total_user_hours) -
                        len(student[0]) -
                        2) + student[0] + ': ' + total_user_hours
                    pdf.cell(xpos, ypos, txt=txt, ln=1, align="L")

            total_hours = str(
                round(sum([record[3] for record in record_data]), 3))
            txt = ' ' * int(
                sum(field_maxes) + spacer * 3 - len(total_hours) -
                13) + "Total Hours: " + total_hours

            pdf.cell(xpos, ypos, txt=txt, ln=1, align="L")

            pdf.output("community_service_program.pdf")

    #generate random student with hours added on a random date
    @pyqtSlot()
    def generate_students(self):
        start_dt = date.today().replace(day=1, month=1).toordinal()
        end_dt = date.today().toordinal()
        random_day = date.fromordinal(random.randint(
            start_dt, end_dt)).strftime("%B %d, %Y %I:%M %p")

        random_name = fake.name()
        random_id = fake.ssn()
        random_grade = random.choice(['9th', '10th', '11th', '12th'])
        random_hours = random.randint(1, 500)
        csa_award = 'CSA Community' if random_hours >= 500 else (
            'CSA Service' if random_hours >= 200 else
            ('CSA Achievement' if random_hours >= 50 else 'N/A'))
        c.execute('''INSERT INTO studentdata VALUES (?, ?, ?, ?, ?)''', (
            random_name,
            str(random_id),
            random_grade,
            random_hours,
            csa_award,
        ))
        c.execute('''INSERT INTO recorddata VALUES (?, ?, ?, ?)''', (
            str(random_day),
            random_name,
            str(random_id),
            random_hours,
        ))
        conn.commit()

        self.populate_record_data()
        self.populate_student_data()

    @pyqtSlot()
    def set_start_date(self):
        if datetime.strptime(
                self.lend.text(), "%a %b %d %Y") < datetime.strptime(
                    self.my_calendar.selectedDate().toString(), "%a %b %d %Y"):
            self.lend.setText(self.my_calendar.selectedDate().toString())
            self.lend.resize(self.lend.sizeHint())
        self.lstart.setText(self.my_calendar.selectedDate().toString())
        self.lstart.resize(self.lstart.sizeHint().width() + 6,
                           self.lstart.sizeHint().height() + 3)

    @pyqtSlot()
    def set_end_date(self):
        if datetime.strptime(self.my_calendar.selectedDate().toString(),
                             "%a %b %d %Y") < datetime.strptime(
                                 self.lstart.text(), "%a %b %d %Y"):
            date_error = QMessageBox()
            date_error.setIcon(QMessageBox.Critical)
            date_error.setInformativeText(
                'End date cannot be before start date')
            date_error.setWindowTitle("Error: Date")
            date_error.exec_()
        else:
            self.lend.setText(self.my_calendar.selectedDate().toString())
            self.lend.resize(self.lend.sizeHint().width() + 6,
                             self.lend.sizeHint().height() + 3)
Beispiel #6
0
class Labels(QWidget):
    """
    Attributes
    ----------
    chan_name : list of str
        list of all the labels (with the user-defined changes)
    """
    def __init__(self, parent):
        super().__init__()
        self.parent = parent
        self.filename = None
        self.chan_name = None  # None when dataset is not loaded

        self.create()

    def create(self):

        self.idx_load = QPushButton('Load')
        self.idx_load.clicked.connect(self.load_labels)
        self.idx_load.setToolTip('Load file with a list of channels (separated by , or ; or tabs or spaces).')
        self.idx_save = QPushButton('Save')
        self.idx_save.clicked.connect(self.save_labels)
        self.idx_save.setEnabled(False)

        # cancel is equal to setting labels to what they were
        self.idx_cancel = QPushButton('Cancel')
        self.idx_cancel.clicked.connect(self.update)
        self.idx_apply = QPushButton('Apply')
        self.idx_apply.clicked.connect(self.apply)
        self.idx_apply.setToolTip('Changes will take effect. This will reset the channel groups and traces.')

        layout_0 = QHBoxLayout()
        layout_0.addWidget(self.idx_load)
        layout_0.addWidget(self.idx_save)

        layout_1 = QHBoxLayout()
        layout_1.addWidget(self.idx_cancel)
        layout_1.addWidget(self.idx_apply)

        self.table = QTableWidget()

        self.table.horizontalHeader().setStretchLastSection(True)
        self.table.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.table.setColumnCount(2)
        self.table.setHorizontalHeaderLabels(['Current Labels', 'New Labels'])

        self.table.cellChanged.connect(self.check_labels)

        layout = QVBoxLayout()
        layout.addLayout(layout_0)
        layout.addWidget(self.table)
        layout.addLayout(layout_1)

        self.setLayout(layout)

        self.setEnabled(False)

    def update(self, checked=False, labels=None, custom_labels=None):
        """Use this function when we make changes to the list of labels or when
        we load a new dataset.

        Parameters
        ----------
        checked : bool
            argument from clicked.connect
        labels : list of str
            list of labels in the dataset (default)
        custom_labels : list of str
            list of labels from a file
        """
        if labels is not None:
            self.setEnabled(True)
            self.chan_name = labels

        self.table.blockSignals(True)
        self.table.clearContents()
        self.table.setRowCount(len(self.chan_name))

        for i, label in enumerate(self.chan_name):
            old_label = QTableWidgetItem(label)
            old_label.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)

            if custom_labels is not None and i < len(custom_labels) and custom_labels[i]:  # it's not empty string or None
                label_txt = custom_labels[i]
            else:
                label_txt = label
            new_label = QTableWidgetItem(label_txt)

            self.table.setItem(i, 0, old_label)
            self.table.setItem(i, 1, new_label)

        self.table.blockSignals(False)

    def check_labels(self):

        # read new labels first
        labels = self._read_labels()

        # disable apply, if there are duplicates
        if len(labels) == len(set(labels)):
            self.idx_apply.setEnabled(True)
        else:
            self.idx_apply.setEnabled(False)

        # mark duplicates in red
        self.table.blockSignals(True)
        for i, label in enumerate(labels):
            if labels.count(label) > 1:
                 self.table.item(i, 1).setBackground(QColor('red'))
            else:
                 self.table.item(i, 1).setBackground(QColor('white'))
        self.table.blockSignals(False)

    def load_labels(self, checked=False, test_name=None):
        if self.filename is not None:
            filename = self.filename
        elif self.parent.info.filename is not None:
            filename = Path(self.parent.info.filename)
        else:
            filename = None

        if test_name:
            filename = test_name
        else:
            filename, _ = QFileDialog.getOpenFileName(self,
                                                      'Open Labels',
                                                      str(filename.parent),
                                                      'Comma-separated values (*.csv);; Text file (*.txt);; All Files(*.*)')
        if filename == '':
            return

        self.filename = Path(filename)

        with self.filename.open() as f:
            text = f.read()

        labels = split(', |,|; |;|\t|\n| ',text)
        labels  = [label.strip() for label in labels]
        self.update(custom_labels=labels)

    def save_labels(self):
        """Save labels modified by the user.

        TODO
        ----
        Save labels modified by the user
        """
        pass

    def apply(self):

        self.chan_name = self._read_labels()
        self.parent.info.dataset.header['chan_name'] = self.chan_name

        self.parent.channels.reset()
        self.parent.traces.reset()

    def reset(self):
        self.table.blockSignals(True)
        self.table.clearContents()
        self.table.blockSignals(False)

        self.setEnabled(False)

    def _read_labels(self):

        labels = []
        for i in range(self.table.rowCount()):
            labels.append(self.table.item(i, 1).text())

        return labels
class layer_widget(QWidget):

	changed = pyqtSignal()

	def combo_changed(self):
		self.save_model()
		self.emit_change()

	def tab_changed(self, x,y):
		self.save_model()
		self.emit_change()
		
	def emit_change(self):
		if self.optics_window!=False:
			if self.optics_window.isVisible()==True:
				self.optics_window.update()
			
		self.changed.emit()
		

	def sync_to_electrical_mesh(self):
		tot=0
		for i in range(0,len(self.model)):
			if yes_no(self.model[i][COLUMN_DEVICE])==True:
				tot=tot+float(self.model[i][COLUMN_THICKNES])

		lines=[]
		if inp_load_file(lines,os.path.join(os.getcwd(),"mesh_y.inp"))==True:
			mesh_layers=int(inp_search_token_value(lines, "#mesh_layers"))
			if mesh_layers==1:
				inp_update_token_value(os.path.join(os.getcwd(),"mesh_y.inp"), "#mesh_layer_length0", str(tot),1)

	def layer_type_edit(self):
		for i in range(0,self.tab.rowCount()):
			if tab_get_value(self.tab,i,3).lower()=="active layer" and tab_get_value(self.tab,i,4).startswith("dos")==False:
				tab_set_value(self.tab,i,4,epitay_get_next_dos())
				tab_set_value(self.tab,i,5,epitay_get_next_pl())

				mat_dir=os.path.join(get_materials_path(),tab_get_value(self.tab,i,2))
				
				new_file=tab_get_value(self.tab,i,4)+".inp"
				if inp_isfile(new_file)==False:
					inp_copy_file(new_file,os.path.join(mat_dir,"dos.inp"))

				new_file=tab_get_value(self.tab,i,5)+".inp"
				if inp_isfile(new_file)==False:
					inp_copy_file(new_file,os.path.join(mat_dir,"pl.inp"))

			if tab_get_value(self.tab,i,3).lower()!="active layer" and tab_get_value(self.tab,i,4).startswith("dos")==True:
				tab_set_value(self.tab,i,4,tab_get_value(self.tab,i,3))
				tab_set_value(self.tab,i,5,"none")

		self.save_model()
		self.emit_change()


	def rebuild_mat_list(self):
		self.material_files=[]
		mat=find_materials()
		for i in range(0,len(mat)):
			self.material_files.append(mat[i])
			scan_remove_file(os.path.join(get_materials_path(),mat[i]))			
			scan_item_add(os.path.join("materials",mat[i],"fit.inp"),"#wavelength_shift_alpha","Absorption spectrum wavelength shift",1)
			scan_item_add(os.path.join("materials",mat[i],"fit.inp"),"#n_mul","Refractive index spectrum multiplier",1)
			scan_item_add(os.path.join("materials",mat[i],"fit.inp"),"#alpha_mul","Absorption spectrum multiplier",1)

	def callback_view_materials(self):
		dialog=gpvdm_open(get_materials_path())
		dialog.show_inp_files=False
		ret=dialog.window.exec_()

		if ret==QDialog.Accepted:
			if os.path.isfile(os.path.join(dialog.get_filename(),"mat.inp"))==True:
				self.mat_window=materials_main(dialog.get_filename())
				self.mat_window.show()
			else:
				plot_gen([dialog.get_filename()],[],"auto")

	def on_move_down(self):
		tab_move_down(self.tab)
		self.save_model()
		self.emit_change()

	def callback_optics_sim(self, widget, data=None):
		help_window().help_set_help(["optics.png",_("<big><b>The optical simulation window</b></big><br>Use this window to perform optical simulations.  Click on the play button to run a simulation."),"play.png",_("Click on the play button to run an optical simulation.  The results will be displayed in the tabs to the right.")])

		if self.optics_window==False:
			self.optics_window=class_optical()

		if self.optics_window.isVisible()==True:
			self.optics_window.hide()
		else:
			self.optics_window.show()


	def __init__(self):
		QWidget.__init__(self)
		self.rebuild_mat_list()
		self.doping_window=False
		self.cost_window=False
		self.optics_window=False

		self.main_vbox=QVBoxLayout()

		self.toolbar=QToolBar()
		self.toolbar.setIconSize(QSize(32, 32))

		self.tb_add = QAction(QIcon(os.path.join(get_image_file_path(),"add.png")), _("Add device layer"), self)
		self.tb_add.triggered.connect(self.on_add_item_clicked)
		self.toolbar.addAction(self.tb_add)

		self.tb_remove = QAction(QIcon(os.path.join(get_image_file_path(),"minus.png")), _("Delete device layer"), self)
		self.tb_remove.triggered.connect(self.on_remove_item_clicked)
		self.toolbar.addAction(self.tb_remove)


		self.tb_remove= QAction(QIcon(os.path.join(get_image_file_path(),"down.png")), _("Move device layer"), self)
		self.tb_remove.triggered.connect(self.on_move_down)
		self.toolbar.addAction(self.tb_remove)

		self.tb_doping = QAction(QIcon(os.path.join(get_image_file_path(),"doping.png")), _("Doping"), self)
		self.tb_doping.triggered.connect(self.callback_doping)
		self.toolbar.addAction(self.tb_doping)

		self.optics_button = QAction(QIcon(os.path.join(get_image_file_path(),"optics.png")), _("Optical simulation"), self)
		self.optics_button.triggered.connect(self.callback_optics_sim)
		self.toolbar.addAction(self.optics_button)

		self.tb_open = QAction(QIcon(os.path.join(get_image_file_path(),"organic_material.png")), _("Look at the materials database"), self)
		self.tb_open.triggered.connect(self.callback_view_materials)
		self.toolbar.addAction(self.tb_open)

		self.cost = QAction(QIcon(os.path.join(get_image_file_path(),"cost.png")), _("Calculate the cost of the solar cell"), self)
		self.cost.triggered.connect(self.callback_cost)
		self.toolbar.addAction(self.cost)
		
		self.main_vbox.addWidget(self.toolbar)
	
		self.tab = QTableWidget()
		self.tab.resizeColumnsToContents()

		self.tab.verticalHeader().setVisible(False)
		self.create_model()

		self.tab.cellChanged.connect(self.tab_changed)
		
		self.main_vbox.addWidget(self.tab)

		self.setLayout(self.main_vbox)




	def create_model(self):
		self.tab.clear()
		self.tab.setColumnCount(6)
		if enable_betafeatures()==False:
			self.tab.setColumnHidden(5, True)
			self.tab.setColumnHidden(4, True)

		self.tab.setSelectionBehavior(QAbstractItemView.SelectRows)
		self.tab.setHorizontalHeaderLabels([_("Layer name"), _("Thicknes"), _("Optical material"), _("Layer type"), _("DoS Layer"),_("PL Layer")])

		self.tab.setRowCount(epitaxy_get_layers())

		for i in range(0,epitaxy_get_layers()):
			thick=epitaxy_get_width(i)
			material=epitaxy_get_mat_file(i)
			dos_layer=epitaxy_get_electrical_layer(i)
			pl_file=epitaxy_get_pl_file(i)
			name=epitaxy_get_name(i)

			self.add_row(i,thick,material,dos_layer,pl_file,name)
		return

	def add_row(self,i,thick,material,dos_layer,pl_file,name):
		self.tab.blockSignals(True)

		dos_file=""
		
		if dos_layer.startswith("dos")==True:
			dos_file="active layer"
		else:
			dos_file=dos_layer

		item1 = QTableWidgetItem(str(name))
		self.tab.setItem(i,0,item1)

		item2 = QTableWidgetItem(str(thick))
		self.tab.setItem(i,1,item2)

		combobox = QComboBox()

		#combobox.setEditable(True)

		for a in self.material_files:
			combobox.addItem(str(a))
		self.tab.setCellWidget(i,2, combobox)
		combobox.setCurrentIndex(combobox.findText(material))

		p=combobox.palette()
		p.setColor(QPalette.Active, QPalette.Button, Qt.white);
		p.setColor(QPalette.Inactive, QPalette.Button, Qt.white);
		combobox.setPalette(p)
		
		#item3 = QTableWidgetItem(str(dos_file))
		#self.tab.setItem(i,3,item3)
		combobox_layer_type = QComboBox()
		#combobox.setEditable(True)

		combobox_layer_type.addItem("contact")
		combobox_layer_type.addItem("active layer")
		combobox_layer_type.addItem("other")

		self.tab.setCellWidget(i,3, combobox_layer_type)
		combobox_layer_type.setCurrentIndex(combobox_layer_type.findText(str(dos_file).lower()))

		item3 = QTableWidgetItem(str(dos_layer))
		self.tab.setItem(i,4,item3)

		item3 = QTableWidgetItem(str(pl_file))
		self.tab.setItem(i,5,item3)


		scan_item_add("epitaxy.inp","#layer_material_file"+str(i),_("Material for ")+name,2)
		scan_item_add("epitaxy.inp","#layer_width"+str(i),_("Layer width ")+name,1)

		combobox.currentIndexChanged.connect(self.combo_changed)
		combobox_layer_type.currentIndexChanged.connect(self.layer_type_edit)

		self.tab.blockSignals(False)

	def clean_dos_files(self):
		files=inp_lsdir()
		tab=[]
		for i in range(0,self.tab.rowCount()):
			tab.append(str(tab_get_value(self.tab,i, 4))+".inp")

		for i in range(0,len(files)):
			if files[i].startswith("dos") and files[i].endswith(".inp"):
				disk_file=files[i]
				if disk_file not in tab:
					inp_remove_file(disk_file)

	def on_remove_item_clicked(self):
		tab_remove(self.tab)
		self.save_model()
		self.changed.emit()

	def change_active_layer_thickness(self,obj):
		thickness=obj.get_data("refresh")
		count=0
		for item in self.model:
			if str2bool(item[COLUMN_DEVICE])==True:
				count=count+1

		if count==1:
			for item in self.model:
				if str2bool(item[COLUMN_DEVICE])==True:
					item[COLUMN_THICKNES]=str(thickness)
					self.save_model()
					self.refresh(False)
					return

	def on_add_item_clicked(self):
		row=tab_insert_row(self.tab)

		self.add_row(row,"100e-9","pcbm","other","none","layer"+str(row))
		self.save_model()
		self.changed.emit()

	def save_model(self):

		thick=[]
		mat_file=[]
		dos_file=[]
		pl_file=[]
		name=[]

		for i in range(0,self.tab.rowCount()):
			name.append(str(tab_get_value(self.tab,i, 0)))
			thick.append(str(tab_get_value(self.tab,i, 1)))
			mat_file.append(str(tab_get_value(self.tab,i, 2)))
			dos_file.append(str(tab_get_value(self.tab,i, 4)))
			pl_file.append(str(tab_get_value(self.tab,i, 5)))

		ret=epitaxy_load_from_arrays(name,thick,mat_file,dos_file,pl_file)
		if ret==False:
			error_dlg(self,_("Error in epitaxy, check the input values."))

		epitaxy_save()
		self.clean_dos_files()
		#self.sync_to_electrical_mesh()

	def callback_doping(self):
		help_window().help_set_help(["doping.png",_("<big><b>Doping window</b></big>\nUse this window to add doping to the simulation")])

		if self.doping_window==False:
			self.doping_window=doping_window()

		if self.doping_window.isVisible()==True:
			self.doping_window.hide()
		else:
			self.doping_window.show()

	def callback_cost(self):
		help_window().help_set_help(["cost.png",_("<big><b>Costs window</b></big>\nUse this window to calculate the cost of the solar cell and the energy payback time.")])

		if self.cost_window==False:
			self.cost_window=cost()

		if self.cost_window.isVisible()==True:
			self.cost_window.hide()
		else:
			self.cost_window.show()
Beispiel #8
0
class CompositionGroupBox(QGroupBox):

    with importlib_resources.open_binary('LiquidDiffract.resources',
                                         'pt_data.npy') as fp:
        _element_dict = np.load(fp, allow_pickle=True).item()

    def __init__(self, *args):
        super(CompositionGroupBox, self).__init__(*args)
        self.setTitle('Composition')
        self.setAlignment(Qt.AlignLeft)
        self.setStyleSheet(
            'GroupBox::title{subcontrol-origin: margin; subcontrol-position: top left;}'
        )

        self.create_widgets()
        self.style_widgets()
        self.create_layout()
        self.create_signals()

    def create_widgets(self):

        self.add_element_btn = QPushButton("Add")
        self.delete_element_btn = QPushButton("Delete")

        self.density_lbl = QLabel("Density:")
        self.density_input = QLineEdit("1.0")

        self.mass_density_label = QLabel('g/cm<sup>3</sup>')
        self.mass_density = QLineEdit('')

        self.composition_table = QTableWidget()

    def style_widgets(self):

        self.density_lbl.setAlignment(Qt.AlignVCenter | Qt.AlignRight)

        self.density_input.setAlignment(Qt.AlignRight)
        self.density_input.setValidator(QDoubleValidator())
        self.density_input.setMaximumWidth(100)

        self.mass_density.setAlignment(Qt.AlignRight)
        self.mass_density.setMaximumWidth(100)

        self.mass_density.setReadOnly(True)
        self.mass_density.isReadOnly()

        self.composition_table.setColumnCount(4)
        self.composition_table.horizontalHeader().setVisible(True)
        self.composition_table.verticalHeader().setVisible(False)
        self.composition_table.setContentsMargins(0, 0, 0, 0)
        self.composition_table.horizontalHeader().setStretchLastSection(False)
        self.composition_table.setHorizontalHeaderLabels(
            ['Element', 'Z', 'Charge', 'n'])
        self.composition_table.horizontalHeaderItem(0).setToolTip(
            'Atomic element ')
        self.composition_table.horizontalHeaderItem(1).setToolTip(
            'Atomic number ')
        self.composition_table.horizontalHeaderItem(2).setToolTip('Charge ')
        self.composition_table.horizontalHeaderItem(3).setToolTip(
            'Proportion of compound ')

        # Set the alignment to the headers
        self.composition_table.horizontalHeaderItem(0).setTextAlignment(
            Qt.AlignLeft)
        self.composition_table.horizontalHeaderItem(1).setTextAlignment(
            Qt.AlignHCenter)
        self.composition_table.horizontalHeaderItem(2).setTextAlignment(
            Qt.AlignHCenter)
        self.composition_table.horizontalHeaderItem(3).setTextAlignment(
            Qt.AlignHCenter)

        self.composition_table.setColumnWidth(0, 71)
        self.composition_table.setColumnWidth(1, 66)
        self.composition_table.setColumnWidth(2, 66)
        self.composition_table.setColumnWidth(3, 66)

        self.composition_table.setVerticalScrollBarPolicy(
            Qt.ScrollBarAlwaysOff)

        self.composition_table.horizontalHeader().setResizeMode(
            QHeaderView.Stretch)
        self.composition_table.setSizePolicy(QSizePolicy.Ignored,
                                             QSizePolicy.Minimum)
        self.composition_table.setSizeAdjustPolicy(
            QAbstractScrollArea.AdjustToContents)

        self.composition_table.setItemDelegate(
            utility.ValidatedItemDelegate(self))

    def create_layout(self):
        self.main_layout = QVBoxLayout()
        self.main_layout.setContentsMargins(10, 10, 10, 7)
        self.main_layout.setSpacing(5)

        self.button_layout = QHBoxLayout()
        self.button_layout.setSpacing(15)
        self.button_layout.addWidget(self.add_element_btn)
        self.button_layout.addWidget(self.delete_element_btn)

        self.density_layout = QGridLayout()
        self.density_layout.addWidget(self.density_lbl, 0, 0)
        self.density_layout.addWidget(self.density_input, 0, 1)
        self.density_layout.addWidget(QLabel('atoms/A<sup>3</sup>'), 0, 2)
        self.density_layout.addWidget(self.mass_density, 1, 1)
        self.density_layout.addWidget(self.mass_density_label, 1, 2)

        self.main_layout.addLayout(self.button_layout)
        self.main_layout.addWidget(self.composition_table)
        self.main_layout.addLayout(self.density_layout)

        self.setLayout(self.main_layout)

    def create_signals(self):
        self.delete_element_btn.clicked.connect(self.delete_row)
        self.add_element_btn.clicked.connect(self.add_row)
        self.density_input.textChanged.connect(self.update_mass_density)
        self.composition_table.cellChanged.connect(self.update_mass_density)

    def add_row(self):
        _row_position = self.composition_table.rowCount()
        self.composition_table.insertRow(_row_position)
        _element_editor = QComboBox()
        _element_editor.setStyleSheet('QComboBox {border: 0px ;} ')
        for index, element in enumerate(CompositionGroupBox._element_dict):
            _element_editor.insertItem(index, element)
        # Could use a default style? e.g.:
        # _element_editor.setStyle(QStyleFactory.create('Cleanlooks'))
        # Block composition_table signals while setting the cells
        self.composition_table.blockSignals(True)
        self.composition_table.setCellWidget(_row_position, 0, _element_editor)
        _element_editor.setCurrentIndex(30)
        # No need to set default here
        # self.composition_table.setItem(_row_position , 0, QTableWidgetItem('Ga'))
        self.composition_table.setItem(_row_position, 1,
                                       QTableWidgetItem('31'))
        self.composition_table.setItem(_row_position, 2, QTableWidgetItem('0'))
        self.composition_table.setItem(_row_position, 3, QTableWidgetItem('1'))
        # Re-enable signals
        self.composition_table.blockSignals(False)

        self.composition_table.item(
            _row_position, 1).setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
        self.composition_table.item(
            _row_position, 1).setTextAlignment(Qt.AlignLeft | Qt.AlignVCenter)
        self.composition_table.item(_row_position,
                                    1).setTextAlignment(Qt.AlignHCenter
                                                        | Qt.AlignVCenter)
        self.composition_table.item(_row_position,
                                    2).setTextAlignment(Qt.AlignHCenter
                                                        | Qt.AlignVCenter)
        self.composition_table.item(_row_position,
                                    3).setTextAlignment(Qt.AlignHCenter
                                                        | Qt.AlignVCenter)

        # Create Signal
        _element_editor.currentIndexChanged.connect(self.update_cb_val)

        self.update_mass_density()

    def delete_row(self):
        # Selects last row if none selected by user
        _selected_row = self.composition_table.currentRow()
        if _selected_row == -1:
            _selected_row = self.composition_table.rowCount() - 1
        else:
            pass
        self.composition_table.removeRow(_selected_row)
        self.update_mass_density()

    def update_cb_val(self):
        _cb_widget = self.sender()
        _current_row = self.composition_table.indexAt(_cb_widget.pos()).row()
        _new_element = str(_cb_widget.currentText())
        _new_Z_val = str(CompositionGroupBox._element_dict[_new_element])
        self.composition_table.item(_current_row, 1).setText(_new_Z_val)
        self.update_mass_density()

    def get_composition_dict(self):
        '''Return composition dictionary'''
        # Form of dictionary is col 0 is key, val is tuple(col1,col2,col3)
        _composition_dict = {}
        _key_list = list(CompositionGroupBox._element_dict.keys())
        _val_list = list(CompositionGroupBox._element_dict.values())
        for _row_index in range(self.composition_table.rowCount()):
            _Z = np.int(self.composition_table.item(_row_index, 1).text())
            _charge = np.int(self.composition_table.item(_row_index, 2).text())
            _n = np.int(self.composition_table.item(_row_index, 3).text())
            _key = str(_key_list[_val_list.index(_Z)])
            _dict_entry = {_key: [_Z, _charge, _n]}
            _composition_dict.update(_dict_entry)

        # This code snippet convert _n from integer number of atoms in the
        # formula unit to a fraction of the total - e.g. SiO2 from Si=1 >> 1/3
        # _n_total = sum([_composition_dict[_el][2] for _el in _composition_dict])
        # for _el in _composition_dict:
        #     _composition_dict[_el][2] /= _n_total
        #     _composition_dict[_el] = tuple(_composition_dict[_el])
        # print(_composition_dict)
        return _composition_dict

    def update_mass_density(self):
        _composition = self.get_composition_dict()
        # Handle errors caused by QDoubleValidator allowing intermediate
        # values to pass e.g. '.', ' ' etc.
        try:
            _atomic_density = np.float(self.density_input.text())
        except ValueError:
            _atomic_density = 0.0
        _mass_density = core.conv_density(_atomic_density, _composition)
        self.mass_density.setText('{0:.3f}'.format(_mass_density))
Beispiel #9
0
class HexEditor(QWidget):
    def __init__(self):
        QWidget.__init__(self)
        self.setLayout(QVBoxLayout())
        self.layout().setContentsMargins(0, 0, 0, 0)
        self.layout().setSpacing(0)

        self.data = bytearray()
        self.datatable = QTableWidget()
        self.datatable.cellChanged.connect(self._cell_changed)
        self.datatable.horizontalHeader().setStretchLastSection(True)
        self.row_size = 16
        self.read_only = False
        self.redraw_table()
        self.layout().addWidget(self.datatable)

    def set_bytes(self, bs):
        self.data = bytearray(bs)
        self.redraw_table()

    def get_bytes(self):
        return bytes(self.data)

    def setReadOnly(self, ro):
        self.read_only = ro
        self.redraw_table()

    def _redraw_strcol(self, row):
        start = self.row_size * row
        end = start + self.row_size
        data = self.data[start:end]
        print_data = printable_data(data, include_newline=False)
        item = QTableWidgetItem(print_data)
        item.setFlags(item.flags() ^ Qt.ItemIsEditable)
        self.datatable.setItem(row, self.str_col, item)

    def redraw_table(self, length=None):
        with DisableUpdates(self.datatable):
            oldsig = self.datatable.blockSignals(True)
            self.row_size = length or self.row_size
            self.datatable.setColumnCount(self.row_size + 1)
            self.datatable.setRowCount(0)
            self.str_col = self.row_size

            self.datatable.horizontalHeader().hide()
            self.datatable.verticalHeader().hide()

            rows = int(len(self.data) / self.row_size)
            if len(self.data) % self.row_size > 0:
                rows += 1
            self.datatable.setRowCount(rows)

            for i in range(rows * self.row_size):
                row = i / self.row_size
                col = i % self.row_size
                if i < len(self.data):
                    dataval = "%02x" % self.data[i]
                    item = QTableWidgetItem(dataval)
                    if self.read_only:
                        item.setFlags(item.flags() ^ Qt.ItemIsEditable)
                else:
                    item = QTableWidgetItem("")
                    item.setFlags(item.flags() ^ Qt.ItemIsEditable)
                self.datatable.setItem(row, col, item)

            for row in range(rows):
                self._redraw_strcol(row)
            self.datatable.blockSignals(oldsig)
            self.datatable.resizeColumnsToContents()
            self.datatable.resizeRowsToContents()

    @classmethod
    def _format_hex(cls, n):
        return ("%02x" % n).upper()

    @pyqtSlot(int, int)
    def _cell_changed(self, row, col):
        oldsig = self.datatable.blockSignals(True)
        if col == self.str_col:
            return
        if len(self.data) == 0:
            return

        data_ind = self.row_size * row + col
        if data_ind >= len(self.data):
            return

        data_text = self.datatable.item(row, col).text()
        try:
            data_val = int(data_text, 16)
            if data_val < 0x0 or data_val > 0xff:
                raise Exception()
        except Exception as e:
            item = QTableWidgetItem(self._format_hex(self.data[data_ind]))
            self.datatable.setItem(row, col, item)
            self.datatable.blockSignals(oldsig)
            return

        if data_text != self._format_hex(data_val):
            self.datatable.setItem(
                row, col, QTableWidgetItem(self._format_hex(data_val)))

        self.data[data_ind] = data_val
        self._redraw_strcol(row)
        self.datatable.blockSignals(oldsig)
Beispiel #10
0
class update_window(QWidget):
	got_updates = pyqtSignal()

	def __init__(self):
		QWidget.__init__(self)
		self.setMinimumWidth(1000)
		self.vbox=QVBoxLayout()

		self.setWindowTitle("Download updates (https://www.gpvdm.com)")

		toolbar=QToolBar()

		toolbar.setToolButtonStyle( Qt.ToolButtonTextUnderIcon)
		toolbar.setIconSize(QSize(42, 42))

		self.tb_update = QAction(icon_get("update"), _("Download updates"), self)
		self.tb_update.triggered.connect(self.download_updates)
		toolbar.addAction(self.tb_update)
	
		self.vbox.addWidget(toolbar)

		self.tab = QTableWidget()
		self.tab.resizeColumnsToContents()

		self.tab.verticalHeader().setVisible(False)


		
		self.tab.setColumnCount(5)
		self.tab.setSelectionBehavior(QAbstractItemView.SelectRows)
		#self.tab.setColumnWidth(3, 200)

		self.tab.verticalHeader().setVisible(False)
		
		#self.select_param_window=select_param(self.tab)
		#self.select_param_window.set_save_function(self.save_combo)

		#self.create_model()

		#self.tab.cellChanged.connect(self.tab_changed)

		self.vbox.addWidget(self.tab)

		self.status_bar=QStatusBar()
		self.vbox.addWidget(self.status_bar)		

		self.setLayout(self.vbox)
		self.update=update_cache("materials")
		self.show_updates()

		self.got_updates.connect(self.show_updates)
		self.update_check()

	def show_updates(self):
		self.tab.blockSignals(True)
		self.tab.clear()
		self.tab.setRowCount(0)
		self.tab.setColumnWidth(0, 300)
		self.tab.setColumnWidth(1, 300)
		self.tab.setHorizontalHeaderLabels([_("File"),_("Description"), _("Size"), _("md5"), _("status")])

		for i in range(0,len(self.update.file_list)):
			pos = self.tab.rowCount()
			self.tab.insertRow(pos)
			self.tab.setItem(pos,0,QTableWidgetItem(self.update.file_list[i].file_name))
			self.tab.setItem(pos,1,QTableWidgetItem(str(self.update.file_list[i].description)))
			self.tab.setItem(pos,2,QTableWidgetItem(str(self.update.file_list[i].size)))
			self.tab.setItem(pos,3,QTableWidgetItem(str(self.update.file_list[i].md5)))
			self.tab.setItem(pos,4,QTableWidgetItem(str(self.update.file_list[i].status)))


		self.tab.blockSignals(False)

	def thread_get_updates(self):
		self.status_bar.showMessage("Checking for updates.....")
		self.update.updates_get()
		self.status_bar.showMessage("Done..")
		self.got_updates.emit()

	def thread_download_updates(self):
		self.status_bar.showMessage("Downloading updates.....")
		self.update.updates_get()
		self.update.updates_download()
		self.update.updates_install(get_materials_path())
		self.status_bar.showMessage("Done..")
		self.got_updates.emit()


	def update_check(self):
		p = Thread(target=self.thread_get_updates)
		p.daemon = True
		p.start()

	def download_updates(self):
		p = Thread(target=self.thread_download_updates)
		p.daemon = True
		p.start()
Beispiel #11
0
class layer_widget(QWidgetSavePos):
    def combo_changed(self):
        self.save_model()
        self.emit_change()
        self.emit_structure_changed()

    def callback_tab_selection_changed(self):
        #self.tab_changed(0,0)
        self.emit_change()

    def tab_changed(self, x, y):
        self.save_model()
        self.emit_structure_changed()

    def emit_change(self):
        global_object_run("gl_force_redraw")

    def emit_structure_changed(
            self):  #This will emit when there has been an edit
        global_object_run("mesh_update")
        global_object_run("optics_force_redraw")
        global_object_run("gl_force_redraw")

    def layer_type_edit(self):
        for i in range(0, self.tab.rowCount()):
            if tab_get_value(self.tab, i,
                             3).lower() == "active layer" and tab_get_value(
                                 self.tab, i, 4).startswith("dos") == False:
                tab_set_value(self.tab, i, 4, epitay_get_next_dos())
                tab_set_value(self.tab, i, 5, epitay_get_next_pl())

                mat_dir = os.path.join(get_materials_path(),
                                       tab_get_value(self.tab, i, 2))

                new_file = tab_get_value(self.tab, i, 4) + ".inp"
                if inp_isfile(new_file) == False:
                    dos_path = os.path.join(mat_dir, "dos.inp")
                    if os.path.isfile(dos_path) == False:
                        dos_path = os.path.join(get_default_material_path(),
                                                "dos.inp")
                    inp_copy_file(new_file, dos_path)

                new_file = tab_get_value(self.tab, i, 5) + ".inp"
                if inp_isfile(new_file) == False:
                    inp_copy_file(new_file, os.path.join(mat_dir, "pl.inp"))

            if tab_get_value(self.tab, i,
                             3).lower() != "active layer" and tab_get_value(
                                 self.tab, i, 4).startswith("dos") == True:
                tab_set_value(self.tab, i, 4, tab_get_value(self.tab, i, 3))
                tab_set_value(self.tab, i, 5, "none")

            if tab_get_value(self.tab, i, 3).lower() == "other":
                tab_set_value(self.tab, i, 4, tab_get_value(self.tab, i, 3))

            if tab_get_value(self.tab, i, 3).lower() == "contact":
                tab_set_value(self.tab, i, 4, tab_get_value(self.tab, i, 3))

        self.save_model()
        self.emit_change()
        global_object_run("dos_update")
        global_object_run("pl_update")

    def on_move_down(self):
        tab_move_down(self.tab)
        self.save_model()
        self.emit_change()
        self.emit_structure_changed()

    def on_move_up(self):
        tab_move_up(self.tab)
        self.save_model()
        self.emit_change()
        self.emit_structure_changed()

    def __init__(self):
        QWidgetSavePos.__init__(self, "layer_widget")

        self.setWindowTitle(_("Layer editor") + " https://www.gpvdm.com")
        self.setWindowIcon(icon_get("layers"))
        self.resize(800, 500)

        self.cost_window = False

        self.main_vbox = QVBoxLayout()

        self.toolbar = QToolBar()
        self.toolbar.setIconSize(QSize(32, 32))

        self.tb_add = QAction(icon_get("list-add"), _("Add device layer"),
                              self)
        self.tb_add.triggered.connect(self.on_add_item_clicked)
        self.toolbar.addAction(self.tb_add)

        self.tb_remove = QAction(icon_get("list-remove"),
                                 _("Delete device layer"), self)
        self.tb_remove.triggered.connect(self.on_remove_item_clicked)
        self.toolbar.addAction(self.tb_remove)

        self.tb_down = QAction(icon_get("go-down"), _("Move device layer"),
                               self)
        self.tb_down.triggered.connect(self.on_move_down)
        self.toolbar.addAction(self.tb_down)

        self.tb_up = QAction(icon_get("go-up"), _("Move device layer"), self)
        self.tb_up.triggered.connect(self.on_move_up)
        self.toolbar.addAction(self.tb_up)

        self.main_vbox.addWidget(self.toolbar)

        self.tab = QTableWidget()
        #self.tab.resizeColumnsToContents()

        self.tab.verticalHeader().setVisible(False)
        self.create_model()

        self.tab.cellChanged.connect(self.tab_changed)
        self.tab.itemSelectionChanged.connect(
            self.callback_tab_selection_changed)
        self.main_vbox.addWidget(self.tab)

        self.setLayout(self.main_vbox)

        self.tab.itemSelectionChanged.connect(self.layer_selection_changed)

    def create_model(self):
        self.tab.clear()
        self.tab.setColumnCount(6)
        if enable_betafeatures() == False:
            self.tab.setColumnHidden(5, True)
            self.tab.setColumnHidden(4, True)

        self.tab.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.tab.setHorizontalHeaderLabels([
            _("Layer name"),
            _("Thicknes"),
            _("Optical material"),
            _("Layer type"),
            _("DoS Layer"),
            _("PL Layer")
        ])
        self.tab.setColumnWidth(2, 250)
        self.tab.setRowCount(epitaxy_get_layers())

        for i in range(0, epitaxy_get_layers()):
            thick = epitaxy_get_width(i)
            material = epitaxy_get_mat_file(i)
            dos_layer = epitaxy_get_electrical_layer(i)
            pl_file = epitaxy_get_pl_file(i)
            name = epitaxy_get_name(i)

            self.add_row(i, thick, material, dos_layer, pl_file, name)
        return

    def add_row(self, i, thick, material, dos_layer, pl_file, name):
        self.tab.blockSignals(True)

        dos_file = ""

        if dos_layer.startswith("dos") == True:
            dos_file = "active layer"
        else:
            dos_file = dos_layer

        item1 = QTableWidgetItem(str(name))
        self.tab.setItem(i, 0, item1)

        item2 = QTableWidgetItem(str(thick))
        self.tab.setItem(i, 1, item2)

        combobox = gpvdm_select()
        combobox.setText(material)
        combobox.button.clicked.connect(self.callback_material_select)

        self.tab.setCellWidget(i, 2, combobox)
        #		combobox.setCurrentIndex(combobox.findText(material))

        #p=combobox.palette()
        #p.setColor(QPalette.Active, QPalette.Button, Qt.white);
        #p.setColor(QPalette.Inactive, QPalette.Button, Qt.white);
        #combobox.setPalette(p)

        #item3 = QTableWidgetItem(str(dos_file))
        #self.tab.setItem(i,3,item3)
        combobox_layer_type = QComboBoxLang()
        #combobox.setEditable(True)

        combobox_layer_type.addItemLang("contact", _("contact"))
        combobox_layer_type.addItemLang("active layer", _("active layer"))
        combobox_layer_type.addItemLang("other", _("other"))

        self.tab.setCellWidget(i, 3, combobox_layer_type)
        combobox_layer_type.setValue_using_english(str(dos_file).lower())

        item3 = QTableWidgetItem(str(dos_layer))
        self.tab.setItem(i, 4, item3)

        item3 = QTableWidgetItem(str(pl_file))
        self.tab.setItem(i, 5, item3)

        #combobox.currentIndexChanged.connect(self.combo_changed)
        combobox_layer_type.currentIndexChanged.connect(self.layer_type_edit)

        self.tab.blockSignals(False)

    def callback_material_select(self):
        self.mat_select = materials_select()
        self.mat_select.init(self.tab)
        self.mat_select.set_save_function(self.combo_changed)
        self.mat_select.show()

    def clean_dos_files(self):
        files = inp_lsdir("sim.gpvdm")
        tab = []
        for i in range(0, self.tab.rowCount()):
            tab.append(str(tab_get_value(self.tab, i, 4)) + ".inp")

        for i in range(0, len(files)):
            if files[i].startswith("dos") and files[i].endswith(".inp"):
                disk_file = files[i]
                if disk_file not in tab:
                    inp_remove_file(disk_file)

    def on_remove_item_clicked(self):
        tab_remove(self.tab)
        self.save_model()
        self.emit_change()

    def change_active_layer_thickness(self, obj):
        thickness = obj.get_data("refresh")
        count = 0
        for item in self.model:
            if str2bool(item[COLUMN_DEVICE]) == True:
                count = count + 1

        if count == 1:
            for item in self.model:
                if str2bool(item[COLUMN_DEVICE]) == True:
                    item[COLUMN_THICKNES] = str(thickness)
                    self.save_model()
                    self.refresh(False)
                    return

    def on_add_item_clicked(self):
        row = tab_insert_row(self.tab)

        self.add_row(row, "100e-9", "pcbm", "other", "none",
                     "layer" + str(row))
        self.save_model()
        #self.changed.emit()
        self.emit_change()

    def save_model(self):

        thick = []
        mat_file = []
        dos_file = []
        pl_file = []
        name = []
        for i in range(0, self.tab.rowCount()):
            name.append(str(tab_get_value(self.tab, i, 0)))
            thick.append(str(tab_get_value(self.tab, i, 1)))
            mat_file.append(str(tab_get_value(self.tab, i, 2)))
            dos_file.append(str(tab_get_value(self.tab, i, 4)))
            pl_file.append(str(tab_get_value(self.tab, i, 5)))

        ret = epitaxy_load_from_arrays(name, thick, mat_file, dos_file,
                                       pl_file)
        if ret == False:
            error_dlg(self, _("Error in epitaxy, check the input values."))

        epitaxy_save(get_sim_path())
        self.clean_dos_files()
        epitaxy_mesh_update()

    def layer_selection_changed(self):
        a = self.tab.selectionModel().selectedRows()

        if len(a) > 0:
            y = a[0].row()
        else:
            y = -1

        if global_isobject("display_set_selected_layer") == True:
            global_object_get("display_set_selected_layer")(y)
        global_object_run("gl_force_redraw")
Beispiel #12
0
class update_window(QWidget):
    got_updates = pyqtSignal()

    def __init__(self):
        QWidget.__init__(self)
        self.setMinimumWidth(1000)
        self.setMinimumHeight(800)

        self.vbox = QVBoxLayout()

        self.setWindowTitle(
            _("Download extra materials") + " (https://www.gpvdm.com)")

        toolbar = QToolBar()

        toolbar.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)
        toolbar.setIconSize(QSize(42, 42))

        self.tb_update = QAction_lock("update", _("Download extra\nmaterials"),
                                      self, "update")
        self.tb_update.clicked.connect(self.download_updates)
        toolbar.addAction(self.tb_update)

        spacer = QWidget()
        spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        toolbar.addWidget(spacer)

        #self.progress=progress_class()
        #self.progress.spinner.stop()
        #self.progress.spinner.hide()
        #self.progress.set_text(_("Connecting to server"))
        #self.progress.hide_time()
        #toolbar.addWidget(self.progress)

        self.vbox.addWidget(toolbar)

        self.tab = QTableWidget()
        self.tab.resizeColumnsToContents()

        self.tab.verticalHeader().setVisible(False)

        self.tab.setColumnCount(6)
        self.tab.setSelectionBehavior(QAbstractItemView.SelectRows)
        #self.tab.setColumnWidth(3, 200)

        self.tab.verticalHeader().setVisible(False)

        #self.select_param_window=select_param(self.tab)
        #self.select_param_window.set_save_function(self.save_combo)

        #self.create_model()

        #self.tab.cellChanged.connect(self.tab_changed)

        self.vbox.addWidget(self.tab)

        self.status_bar = QStatusBar()
        self.vbox.addWidget(self.status_bar)

        self.setLayout(self.vbox)
        self.update = update_cache()
        self.show_updates()
        self.update.update_progress.connect(self.update_progress)
        self.got_updates.connect(self.show_updates)
        self.update_check()
        self.setWindowIcon(icon_get("update"))

        self.timer = QTimer()
        self.timer.setSingleShot(False)
        self.timer.timeout.connect(self.update_ui)
        self.timer.start(500)

    def update_progress(self, line, percent):
        if self.isVisible() == True:
            if line != -1:
                self.tab.setItem(
                    line, 5,
                    QTableWidgetItem(
                        str(self.update.file_list[line].get_status())))
                self.tab.setItem(
                    line, 4,
                    QTableWidgetItem(str(
                        self.update.file_list[line].md5_disk)))
                self.tab.selectRow(line)

        #self.progress.set_fraction(percent)
        #self.progress.set_text(self.update.get_progress_text())
        process_events()

    def update_ui(self):
        if self.isVisible() == True:
            if self.update.updates_avaliable() == True:
                self.tb_update.setEnabled(True)
            else:
                self.tb_update.setEnabled(False)

            #print("write")

    def show_updates(self):
        self.tab.blockSignals(True)
        self.tab.clear()
        self.tab.setRowCount(0)
        self.tab.setColumnWidth(0, 50)
        self.tab.setColumnWidth(1, 200)
        self.tab.setColumnWidth(2, 200)
        self.tab.setColumnWidth(5, 300)
        self.tab.setHorizontalHeaderLabels([
            _("ID"),
            _("File"),
            _("Description"),
            _("Size"),
            _("md5"),
            _("status")
        ])

        for i in range(0, len(self.update.file_list)):
            pos = self.tab.rowCount()
            self.tab.insertRow(pos)
            self.tab.setItem(pos, 0, QTableWidgetItem(str(i)))
            self.tab.setItem(
                pos, 1, QTableWidgetItem(self.update.file_list[i].file_name))
            self.tab.setItem(
                pos, 2, QTableWidgetItem(str(self.update.file_list[i].text)))
            self.tab.setItem(
                pos, 3,
                QTableWidgetItem(sizeof_fmt(self.update.file_list[i].size)))
            self.tab.setItem(
                pos, 4,
                QTableWidgetItem(str(self.update.file_list[i].md5_disk)))
            self.tab.setItem(
                pos, 5,
                QTableWidgetItem(str(self.update.file_list[i].get_status())))
            self.tab.setItem(
                pos, 6,
                QTableWidgetItem(str(self.update.file_list[i].md5_disk)))
            #self.tab.setItem(pos,4,QTableWidgetItem(str(self.update.file_list[i].get_status())))
            #self.tab.setItem(pos,5,QTableWidgetItem(str(self.update.file_list[i].ver)))

        self.tab.blockSignals(False)
        self.status_bar.showMessage("")

    def thread_get_updates(self):
        self.update.updates_get()
        self.got_updates.emit()

    def thread_download_updates(self):
        self.update.updates_get()
        self.update.updates_download()
        self.update.updates_install()
        self.got_updates.emit()

    def update_check(self):
        self.status_bar.showMessage("Checking for updates.....")
        self.update_check_thread = Thread(target=self.thread_get_updates)
        self.update_check_thread.daemon = True
        self.update_check_thread.start()

    def download_updates(self):
        if get_lock().is_trial() == True:
            self.trial = trial(
                override_text=
                "<br><br><br><br>Please buy gpvdm to get access to the full materials database.<br><br><br>",
                show_text=False,
                title_font_size=14)
            self.trial.title_text.setAlignment(Qt.AlignCenter)
            ret = self.trial.run()
            if ret == QDialog.Accepted:
                msgBox = msg_dlg()
                msgBox.setText("Thank you for buying gpvdm")
                reply = msgBox.exec_()

            return

        self.status_bar.showMessage("Downloading updates.....")
        p = Thread(target=self.thread_download_updates)
        p.daemon = True
        p.start()
Beispiel #13
0
class AdvSetTab(QWidget):
    def __init__(self, gui):
        super(AdvSetTab, self).__init__()

        self._ui = gui
        self._config = self._ui.config
        self._tran = self._ui.tran
        self._parser = self._ui.configparser
        self.setObjectName("advtab")

        # main layout of this tab
        self._layout = QGridLayout(self)
        self._layout.setObjectName('advtab_layout')

        # setup the new button
        self._button_new = QPushButton(self)
        self._button_new.setObjectName('advtab_button_new')
        self._button_new.setText(
            self._tran.get_text(self._button_new.objectName()))
        self._button_new.clicked.connect(self._new_setting)

        # setup the delete button
        self._button_del = QPushButton(self)
        self._button_del.setObjectName('advtab_button_delete')
        self._button_del.setText(
            self._tran.get_text(self._button_del.objectName()))
        self._button_del.clicked.connect(self._delete_setting)

        # setup the delete all
        self._button_del_all = QPushButton(self)
        self._button_del_all.setObjectName('advtab_button_delete_all')
        self._button_del_all.setText(
            self._tran.get_text(self._button_del_all.objectName()))
        self._button_del_all.clicked.connect(self._delete_config)

        # setup the reset all button
        self._button_res_all = QPushButton(self)
        self._button_res_all.setObjectName('advtab_button_reset_all')
        self._button_res_all.setText(
            self._tran.get_text(self._button_res_all.objectName()))
        self._button_res_all.clicked.connect(self._reset_config)

        # setup the option table
        self._table = QTableWidget(0, 2)
        self._table.setObjectName('advtab_table')
        self._table.setHorizontalHeaderLabels(
            [self._tran.get_text('option'),
             self._tran.get_text('value')])
        self._table.horizontalHeader().setSectionResizeMode(
            QHeaderView.Stretch)
        self._table.setSelectionBehavior(QAbstractItemView.SelectRows)
        self._table.setSelectionMode(QAbstractItemView.SingleSelection)
        self._table.setShowGrid(False)
        self._table.setGeometry(0, 0, 800, 400)
        self._table.itemChanged.connect(self._changed_setting)

        # add the elements to the top level layout of the tab
        self._layout.addWidget(self._button_new, 0, 0, 1, 1)
        self._layout.addWidget(self._button_del, 0, 1, 1, 1)
        self._layout.addWidget(self._button_del_all, 0, 2, 1, 1)
        self._layout.addWidget(self._button_res_all, 0, 3, 1, 1)
        self._layout.addWidget(self._table, 1, 0, 4, 4)

        # set the array with all elements that have tooltips
        self._tooltips = [
            self._button_new,
            self._button_del,
            self._button_del_all,
            self._button_res_all,
        ]

    def _changed_setting(self):
        """
        Update the config with the values from the ui

        :return:
        """
        # run through the table
        for a in range(self._table.rowCount()):
            # get key and value of the current row
            _key = self._table.item(a, 0).text() if self._table.item(a,
                                                                     0) else ""
            _value = self._table.item(a, 1).text() if self._table.item(
                a, 1) else ""
            # update the config
            self._config[_key] = _value
        # make the default config visible in the ui, toggle tooltips, ...
        self.init()

    def _new_setting(self):
        """
        Add a new setting to the config

        :return: None
        """
        # get the number of rows in the table
        _rows = self._table.rowCount()
        # create a wizard to get the setting name and setting value from the user
        _wizard = OptionWizard(self._tran.get_text('option_wizard_title'),
                               self._tran)
        # open the wizard
        _wizard.exec_()
        # only if the user has entered a setting name
        if _wizard.setting_name():
            # create a new item holding the entered data
            self._table.insertRow(_rows)
            _key = QTableWidgetItem(_wizard.setting_name())
            # make the new item editable and selectable
            _flags = _key.flags()
            _flags |= Qt.ItemIsSelectable
            _flags &= Qt.ItemIsEditable
            _key.setFlags(_flags)
            # add the new item to the table
            self._table.setItem(_rows, 0, _key)
            self._table.setItem(_rows, 1,
                                QTableWidgetItem(_wizard.setting_value()))
            # add the new setting to the config
            self._config[_wizard.setting_name()] = _wizard.setting_value()
        # make the default config visible in the ui, toggle tooltips, ...
        self.init()

    def _delete_setting(self):
        """
        Remove the selected setting from config

        :return: None
        """
        # get the index of the selected row
        try:
            _row = self._table.selectedItems()[0].row()
        except KeyError:
            return
        # delete the options from config
        del self._config[self._table.selectedItems()[0].text()]
        # remove the row from the table
        self._table.removeRow(_row)
        # make the default config visible in the ui, toggle tooltips, ...
        self.init()

    def _delete_config(self):
        """
        Remove all setting from config

        :return: None
        """
        # clear the config
        self.config = {}
        # make the default config visible in the ui, toggle tooltips, ...
        self.init()

    def _reset_config(self):
        """
        Reset the config to default state

        :return: None
        """
        # reset config to default
        self._config = cc_con.DEFAULT_CONFIG
        # make the default config visible in the ui, toggle tooltips, ...
        self.init()

    def toggle_tooltips(self, onoff):
        """
        If "onoff" is either "True" or "true", then show tooltips for this tab, otherwise dont

        :param onoff: whether to show verbose tooltips
        :return: None
        """
        if onoff == "True" or onoff == "true":
            # run through the list with elements that have tooltips...
            for ele in self._tooltips:
                # and activate the tooltip for each element
                ele.setToolTip(
                    self._tran.get_text(ele.objectName() + '_tooltip'))
        else:
            # run through the list with elements that have tooltips...
            for ele in self._tooltips:
                # and disable the tooltip for each element
                ele.setToolTip("")

    def lock(self):
        """
        Disable the ui components, so that the user cannot change settings while the program is running

        :return: None
        """
        self._toggle_ui_components(False)

    def unlock(self):
        """
        Enable the ui components, so that the user can change settings

        :return: None
        """
        self._toggle_ui_components(True)

    def _toggle_ui_components(self, onoff):
        """
        Enable/Disable ui components (DO NOT USE THIS DIRECTLY; use _lock()/_unlock())

        :param onoff: True to enable, False to disable
        :return: None
        """
        # enable/disable buttons
        self._button_del.setEnabled(onoff)
        self._button_del_all.setEnabled(onoff)
        self._button_new.setEnabled(onoff)
        self._button_res_all.setEnabled(onoff)
        # enable/disable editing of the items in the table
        for a in range(0, self._table.rowCount()):
            if onoff:
                self._table.setEnabled(True)
            else:
                self._table.setEnabled(False)

    def init(self):
        """
        What to do, when this tab is activated: fill the table with the contents of the config, ...

        :return: None
        """
        # prevent sending signals: otherwise this caused the itemChanged Signal of the table to fire which in turn
        # invokes this method, ... -> infinitely deep recursion
        self._table.blockSignals(True)
        # delete all rows from the table
        while self._table.rowCount() > 0:
            self._table.removeRow(0)
        # fill the table with the contents of the config
        _i = 0
        for key, value in self._config.items():
            self._table.insertRow(_i)
            self._table.setItem(_i, 0, QTableWidgetItem(key))
            self._table.setItem(_i, 1, QTableWidgetItem(value))
            _i += 1
        # toggle tooltips if necessary
        if cc_con.SHOW_VERB_TOOL in self._config:
            self.toggle_tooltips(self._config[cc_con.SHOW_VERB_TOOL])
        # if the options for showing tooltips has not been set they are shown per default
        else:
            self.toggle_tooltips("True")
        self._table.blockSignals(False)

    def config(self):
        """
        Return the internal configuration of this tab

        :return: A dictionary containing the configuration
        """
        return self._config
Beispiel #14
0
class MainWindow(QMainWindow):
    def __init__(self, *args, **kwargs):
        super(MainWindow, self).__init__()

        self.setWindowTitle('Sign PDF GUI')

        self.author = kwargs['author']
        self.version = kwargs['version']
        self.cliApp = kwargs['cliApp']

        self.title = 'Sign PDF GUI'

        cliAppBasename = os.path.splitext(self.cliApp)[0]

        scriptDir = os.path.dirname(os.path.realpath(__file__))
        iconPath = os.path.sep.join([scriptDir, 'icon', cliAppBasename])
        self.setWindowIcon(QIcon(iconPath))

        self.listPdf = []  # List of pdfs
        self.listImage = []  # List of images

        if 'pdflist' in kwargs.keys() and len(kwargs['pdflist']) > 0:
            n = len(kwargs['pdflist'])
            for i in range(0, n):
                self.listPdf.append(os.path.realpath(kwargs['pdflist'][i]))

        self.left = 10
        self.top = 10
        self.width = 700
        self.height = 480
        self.setGeometry(self.left, self.top, self.width, self.height)

        self.btnWidth = 100
        self.btnHeight = 30
        self.selectAllWidth = 70

        widget = QWidget()
        self.setCentralWidget(widget)

        self.vbox = QVBoxLayout()
        widget.setLayout(self.vbox)

        multiple = False
        threshold = 0.9
        mask = "254,255,254,255,254,255"

        self.otherConfigs = {
            'multiple': multiple,
            'threshold': threshold,
            'mask': mask
        }

        self.createPdfBox()
        self.createPdfTable()
        self.createImageBox()
        self.createImageTable()
        self.createButtonBox()
        self.createStatusBar()

        self.updatePdfTable()

    def createPdfBox(self):

        hbox = QHBoxLayout()
        self.vbox.addLayout(hbox)
        self.vbox.setAlignment(Qt.AlignTop)

        addPdfBtn = QPushButton("Add PDF", self)
        addPdfBtn.setFixedSize(self.btnWidth, self.btnHeight)
        addPdfBtn.clicked.connect(self.addPdfFiles)
        hbox.addWidget(addPdfBtn)

        delPdfBtn = QPushButton("Remove PDF", self)
        delPdfBtn.setFixedSize(self.btnWidth, self.btnHeight)
        delPdfBtn.clicked.connect(self.delPdfFiles)
        hbox.addWidget(delPdfBtn)

        hbox.addStretch(1)

        self.showGrid = QCheckBox("Show grid")
        self.showGrid.stateChanged.connect(self.showGridState)
        hbox.addWidget(self.showGrid)

        self.printCmd = QCheckBox("Print cmd")
        self.printCmd.stateChanged.connect(self.printCmdState)
        hbox.addWidget(self.printCmd)

        otherBtn = QPushButton("Others")
        otherBtn.setFixedSize(self.btnWidth, self.btnHeight)
        otherBtn.clicked.connect(self.otherBtn_clicked)
        hbox.addWidget(otherBtn)

    def createPdfTable(self):

        npdf = len(self.listPdf)

        self.pdfTable = QTableWidget()
        self.pdfTable.setRowCount(npdf)
        self.pdfTable.setColumnCount(2)

        self.pdfTable.setHorizontalHeaderLabels(
            "Select All;PDF Filename".split(";"))

        header = self.pdfTable.horizontalHeader()
        header.setSectionResizeMode(1, QtWidgets.QHeaderView.Stretch)
        header.resizeSection(0, self.selectAllWidth)

        self.vbox.setAlignment(Qt.AlignTop)
        #         self.pdfTable.setMaximumHeight(self.tableMaxHeight)
        self.vbox.addWidget(self.pdfTable)

        # If header is clicked
        self.selectAllPdf = False
        header.sectionClicked.connect(self.onPdfTableHeaderClicked)

    def onPdfTableHeaderClicked(self, logicalIndex):
        n = len(self.listPdf)
        if logicalIndex == 0:  # If the first row is clicked

            if not self.selectAllPdf:  # If self.selectAllPdf is False
                self.selectAllPdf = True
                for r in range(0, n):
                    self.pdfTable.cellWidget(r, 0).findChild(type(
                        QCheckBox())).setChecked(True)

            else:
                self.selectAllPdf = False
                for r in range(0, n):
                    self.pdfTable.cellWidget(r, 0).findChild(type(
                        QCheckBox())).setChecked(False)

    def createImageBox(self):

        hbox = QHBoxLayout()
        self.vbox.addLayout(hbox)

        addImageBtn = QPushButton("Add Image")
        addImageBtn.setFixedSize(self.btnWidth, self.btnHeight)
        addImageBtn.clicked.connect(lambda x: self.addEditImage(False))
        hbox.addWidget(addImageBtn)

        delImageBtn = QPushButton("Remove Image")
        delImageBtn.setFixedSize(self.btnWidth, self.btnHeight)
        delImageBtn.clicked.connect(self.delImageFiles)
        hbox.addWidget(delImageBtn)

        hbox.addStretch(1)

    def delImageFiles(self):

        # Clear the status message
        self.statusBar.clearMessage()

        self.selectAllImage = False

        n = len(self.listImage)
        for r in range(0, n):
            if self.imageTable.cellWidget(r, 0).findChild(type(
                    QCheckBox())).isChecked():

                # Remove the element from the list
                del self.listImage[r]

                # Insert blank in the list so that the number of
                # elements remain the same.
                self.listImage.insert(r, "")

        # Remove the blanks that had been inserted
        while ("" in self.listImage):
            self.listImage.remove("")

        # Update the table
        self.updateImageTable()
        return

    def createImageTable(self):

        n = len(self.listImage)

        self.imageTable = QTableWidget()
        self.imageTable.setRowCount(n)
        self.imageTable.setColumnCount(9)

        self.imageTable.setHorizontalHeaderLabels(
            "Select All;Edit;Image;Template;XValue;YValue;Scale;Page;Rotation".
            split(";"))

        header = self.imageTable.horizontalHeader()
        #         header.setSectionResizeMode(1,QtWidgets.QHeaderView.Stretch)
        header.resizeSection(0, self.selectAllWidth)
        header.resizeSection(1, 46)

        self.vbox.setAlignment(Qt.AlignTop)
        #         self.imageTable.setMaximumHeight(self.tableMaxHeight)
        self.vbox.addWidget(self.imageTable)

        # If header is clicked
        self.selectAllImage = False
        header.sectionClicked.connect(self.onImageTableHeaderClicked)

        self.imageTable.itemChanged.connect(self.itemChangedText)

        return

    # If cell value have changed, then update them on the dictionary
    def itemChangedText(self, item):
        row = item.row()
        col = item.column()

        # Get the 'key' = column header
        key = self.imageTable.horizontalHeaderItem(col).text().lower()

        # Get the 'value' = cell value from the table
        value = str(self.imageTable.item(row, col).text())

        # Update the dictionary
        self.listImage[row].update_info(key, value)
        return

    # If image header has been clicked
    def onImageTableHeaderClicked(self, logicalIndex):
        n = len(self.listImage)
        if logicalIndex == 0:  # If the first row is clicked

            if not self.selectAllImage:  # If self.selectAllImage is False
                self.selectAllImage = True
                for r in range(0, n):
                    self.imageTable.cellWidget(r, 0).findChild(
                        type(QCheckBox())).setChecked(True)

            else:
                self.selectAllImage = False
                for r in range(0, n):
                    self.imageTable.cellWidget(r, 0).findChild(
                        type(QCheckBox())).setChecked(False)

    def updateImageTable(self):
        n = len(self.listImage)

        # Block the signals so that any editing doesn't call any signal
        self.imageTable.blockSignals(True)

        # Clears the table
        self.imageTable.setRowCount(0)

        # Create an empty list of 'Edit' buttons
        self.editImageBtn = []

        for row in range(0, n):

            # Insert a new row
            self.imageTable.insertRow(row)

            # Insert a checkbox in the first column (col = 0)
            qw = QWidget()
            self.checkbox = QCheckBox()
            self.checkbox.setCheckState(Qt.Unchecked)
            tabhlayout = QHBoxLayout(qw)
            tabhlayout.addWidget(self.checkbox)
            tabhlayout.setAlignment(Qt.AlignCenter)
            tabhlayout.setContentsMargins(0, 0, 0, 0)
            self.imageTable.setCellWidget(row, 0, qw)

            # Insert a checkbox in the second column (col = 1)
            # Append to the list of 'Edit' buttons
            self.editImageBtn.append(QPushButton('Edit'))
            self.editImageBtn[row].setStyleSheet('margin:3px')
            self.imageTable.setCellWidget(row, 1, self.editImageBtn[row])
            self.editImageBtn[row].clicked.connect(
                lambda x: self.addEditImage(True))

            for col in range(2, 9):
                # Get the column header in lowercase
                colheader = self.imageTable.horizontalHeaderItem(
                    col).text().lower()

                if colheader == 'image' or colheader == 'template':

                    # If the column header is 'image' or 'template', then
                    # get the file basename and also make the cells read-only
                    itemString = os.path.basename(
                        str(self.listImage[row].info[colheader]))
                    item = QTableWidgetItem(itemString)
                    item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable)

                else:

                    # Just get the value from the dictionary
                    itemString = str(self.listImage[row].info[colheader])
                    item = QTableWidgetItem(itemString)

                # Align and insert the value in the cell
                item.setTextAlignment(Qt.AlignCenter)
                self.imageTable.setItem(row, col, item)

        self.imageTable.blockSignals(False)

    # Add or edit the image info
    def addEditImage(self, editBool):

        # Clear the status message
        self.statusBar.clearMessage()

        dlg = ImageOptionsDialog(self)

        # If edit then
        if editBool:
            # Get the row number 'r'
            buttonClicked = self.sender()
            index = self.imageTable.indexAt(buttonClicked.pos())
            r = index.row()

            # Get the r-th row dictionary
            imageInfo = self.listImage[r].get_dict()

            # Insert the image info in the dialog
            dlg.editInfoDialog(imageInfo)

        if dlg.exec_() == ImageOptionsDialog.Accepted:

            data = dlg.get_output()

            if editBool:
                # If edit, the replace the original content with
                # the new content
                self.listImage[r] = data['image']
                self.listImage[r] = ImageOptions(data)

            else:
                # If not editing old info, then add it to the end
                self.listImage.append(data['image'])
                k = len(self.listImage)
                self.listImage[k - 1] = ImageOptions(data)

            # Update the image table
            self.updateImageTable()

        return

    def createButtonBox(self):

        hbox = QHBoxLayout()
        self.vbox.addLayout(hbox)

        aboutBtn = QPushButton("About")
        aboutBtn.setFixedSize(self.btnWidth, self.btnHeight)
        aboutBtn.clicked.connect(self.aboutBtn_clicked)
        hbox.addWidget(aboutBtn, alignment=Qt.AlignLeft)

        self.helpBtn = QPushButton("Help")
        self.helpBtn.setFixedSize(self.btnWidth, self.btnHeight)
        self.helpBtn.clicked.connect(self.helpBtn_clicked)
        hbox.addWidget(self.helpBtn)

        hbox.addStretch(1)

        self.applyBtn = QPushButton("Apply")
        self.applyBtn.setFixedSize(self.btnWidth, self.btnHeight)
        self.applyBtn.clicked.connect(self.applyBtn_clicked)
        hbox.addWidget(self.applyBtn)

        quitBtn = QPushButton("Quit")
        quitBtn.setFixedSize(self.btnWidth, self.btnHeight)
        quitBtn.clicked.connect(self.close)
        hbox.addWidget(quitBtn)

        return

    def aboutBtn_clicked(self):

        # Clear the status message
        self.statusBar.clearMessage()

        infoMsg = ('Version: ' + self.version + '\n' + 'Author: ' +
                   self.author)

        dlg = MessageDialog(title=self.title + ': About',
                            text='Sign PDF GUI',
                            informativeText=infoMsg,
                            icon=QStyle.SP_MessageBoxInformation)
        dlg.setMinimumWidth(300)
        dlg.exec_()
        return

    def helpBtn_clicked(self):

        cmd = self.cliApp + ' --help'
        self.proc = Popen(shlex.split(cmd), stdout=PIPE, stderr=PIPE)
        stdout, stderr = self.proc.communicate()
        rtn = self.proc.returncode

        if rtn != 0:
            print("Problem getting help text.")
            return rtn

        msg = self.cliApp + ' --help'
        detailedMsg = stdout.decode("utf-8")

        dlg = MessageDialog(
            title=self.title + ': Help',
            icon=QStyle.SP_MessageBoxInformation,
            text=msg,
            detailedText=detailedMsg,
        )
        dlg.setMinimumSize(550, 400)
        dlg.exec_()

        return

    def applyBtn_clicked(self):

        n = len(self.listPdf)

        if n == 0:
            dlg = MessageDialog(title=self.title + ': Error',
                                text='Warning: No pdf file supplied.',
                                icon=QStyle.SP_MessageBoxCritical)
            dlg.exec_()
            return

        self.pd = ProgressDialog()
        self.pd.setMinimum(0)
        self.pd.setMaximum(n)
        self.pd.setFormat("%v/%m")

        self.work = Worker(self)
        self.thread = Thread(target=self.work.run)
        self.thread.start()

        if self.pd.exec_() == ProgressDialog.Rejected:
            self.work.terminate()

    def createStatusBar(self):
        self.statusBar = QStatusBar()
        self.setStatusBar(self.statusBar)
        return

    def addPdfFiles(self):

        # Clear the status message
        self.statusBar.clearMessage()

        options = QFileDialog.Options()
        options |= QFileDialog.DontUseNativeDialog
        files, _ = (QFileDialog.getOpenFileNames(
            self,
            "QFileDialog.getOpenFileNames()",
            "",
            "PDF Files (*.pdf)",
            options=options))
        if files:
            self.listPdf.extend(files)
            self.updatePdfTable()

    def updatePdfTable(self):

        n = len(self.listPdf)
        self.pdfTable.setRowCount(0)  # Clear the table

        for r in range(0, n):
            self.pdfTable.insertRow(r)

            qw = QWidget()
            self.checkbox = QCheckBox()
            self.checkbox.setCheckState(Qt.Unchecked)
            tabhlayout = QHBoxLayout(qw)
            tabhlayout.addWidget(self.checkbox)
            tabhlayout.setAlignment(Qt.AlignCenter)
            tabhlayout.setContentsMargins(0, 0, 0, 0)
            self.pdfTable.setCellWidget(r, 0, qw)

            item = QTableWidgetItem(self.listPdf[r])
            item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable)
            self.pdfTable.setItem(r, 1, item)

    def showGridState(self):
        self.statusBar.clearMessage()
        return

    def printCmdState(self):
        self.statusBar.clearMessage()
        return

    def delPdfFiles(self):

        # Clear the status message
        self.statusBar.clearMessage()

        self.selectAllPdf = False

        n = len(self.listPdf)
        for r in range(0, n):
            if self.pdfTable.cellWidget(r, 0).findChild(type(
                    QCheckBox())).isChecked():

                # Remove the element from the list
                del self.listPdf[r]

                # Insert blank in the list so that the number of
                # elements remain the same.
                self.listPdf.insert(r, "")

        # Remove the blanks that had been inserted
        while ("" in self.listPdf):
            self.listPdf.remove("")

        # Update the table
        self.updatePdfTable()

        return

    def otherBtn_clicked(self):
        dlg = OtherDialog(self.otherConfigs)
        if dlg.exec_() == OtherDialog.Accepted:
            self.otherConfigs = dlg.get_output()
        return
Beispiel #15
0
class mainwindow(QWidget):
    # set up signals
    module_selected = pyqtSignal()
    module_loaded = pyqtSignal()

    def __init__(self, parent=None):
        super(mainwindow, self).__init__(parent)

        # this is where we will store the acquisition module later
        self.m = None

        # set up splitter widget
        l = QVBoxLayout()
        self.setLayout(l)
        self.splitter = QSplitter(self)
        l.addWidget(self.splitter)

        # set up module interface widget
        scriptinterfacewidget = QWidget(self.splitter)
        self.txt_acquisition_script = QLineEdit('Path to acquistion script...')
        self.txt_acquisition_script.setReadOnly(True)
        self.btn_browse = QPushButton(QIcon('../icons/folder.png'), '')
        self.btn_browse.setToolTip('Browse')
        l1 = QHBoxLayout()
        for w in [self.txt_acquisition_script, self.btn_browse]:
            l1.addWidget(w)
        self.btn_load = QPushButton(QIcon('tools-wizard.png'), '')
        self.btn_load.setToolTip('Load module')
        self.btn_run = QPushButton(QIcon('../icons/start.png', ), '')
        self.btn_run.setToolTip('Run module')
        self.btn_stopmod = QPushButton(QIcon('../icons/stop.png'), '')
        self.btn_stopmod.setToolTip('Stop module')
        self.btn_forcestopmod = QPushButton(QIcon('../icons/kill.png'), '')
        self.btn_forcestopmod.setToolTip('Kill module')
        self.btn_help = QPushButton(QIcon('../icons/help.png'), '')
        self.btn_help.setToolTip('Show step-by-step help')
        for btn in [
                self.btn_load, self.btn_run, self.btn_stopmod,
                self.btn_forcestopmod, self.btn_help
        ]:
            btn.setIconSize(QSize(32, 32))
        for btn in [self.btn_run, self.btn_stopmod, self.btn_forcestopmod]:
            btn.setEnabled(False)
        l2 = QHBoxLayout()
        for w in [
                self.btn_load, self.btn_run, self.btn_stopmod,
                self.btn_forcestopmod, self.btn_help
        ]:
            l2.addWidget(w)
        # set up parameters table
        self.lbl_params = QLabel('<b>Module parameters:</b>')
        self.tbl_params = QTableWidget()
        self.tbl_params.setColumnCount(2)
        self.tbl_params.setHorizontalHeaderLabels(['Name', 'Value'])
        self.tbl_params.verticalHeader().hide()
        self.tbl_params.horizontalHeader().setStretchLastSection(True)
        self.lbl_readonlyconsole = QLabel('<b>Terminal output:</b>')
        self.readonlyconsole = ReadOnlyConsole()
        l_siw = QVBoxLayout(scriptinterfacewidget)
        for l in [l1, l2]:
            l_siw.addLayout(l)
        for w in [
                self.lbl_params, self.tbl_params, self.lbl_readonlyconsole,
                self.readonlyconsole
        ]:
            l_siw.addWidget(w)

        # set up plotter widget
        self.plotter = Plotter(self.splitter)

        # set up observables widget
        observablesinterfacewidget = QWidget(
            self.splitter
        )  # this widget will contain the observables list and the alarms
        # set up observables table
        self.lbl_observables = QLabel('<b>Observables:</b>',
                                      observablesinterfacewidget)
        self.tbl_observables = QTableWidget(observablesinterfacewidget)
        self.tbl_observables.setColumnCount(2)
        self.tbl_observables.setHorizontalHeaderLabels(['Name', 'Value'])
        self.tbl_observables.verticalHeader().hide()
        self.tbl_observables.horizontalHeader().setStretchLastSection(True)
        # set up alarms table
        self.lbl_alarm = QLabel('<b>Alarms:</b>', observablesinterfacewidget)
        self.tbl_alarms = QTableWidget(observablesinterfacewidget)
        self.tbl_alarms.setColumnCount(3)
        self.tbl_alarms.setHorizontalHeaderLabels(
            ['Condition', 'Action', 'Value'])
        self.tbl_alarms.horizontalHeader().setSectionResizeMode(
            QHeaderView.Stretch)
        self.tbl_alarms.verticalHeader().hide()
        self.btn_addalarm = QPushButton("Add alarm",
                                        observablesinterfacewidget)
        self.btn_addalarm.setEnabled(False)
        # put everything in a layout
        l = QVBoxLayout(observablesinterfacewidget)
        for w in [
                self.lbl_observables, self.tbl_observables, self.lbl_alarm,
                self.tbl_alarms, self.btn_addalarm
        ]:
            l.addWidget(w)

        # make connections
        self.btn_browse.clicked.connect(self.browse_acquisition_script)
        self.btn_load.clicked.connect(self.load_module)
        self.btn_run.clicked.connect(self.run_module)
        self.btn_help.clicked.connect(
            lambda: self.update_step_help(reinit=True))
        self.btn_addalarm.clicked.connect(self.add_alarm)
        self.tbl_alarms.cellChanged.connect(self.alarm_modified)

        # set window size.
        self.setWindowState(Qt.WindowMaximized)

        # set window title
        self.setWindowTitle("MAScriL - Mercury Acquisition Script Launcher")

        # set up step by step help
        self.lbl_step_help = QLabel(self)
        #self.lbl_step_help.setWindowFlag(Qt.ToolTip)
        self.lbl_step_help.setStyleSheet(
            'QLabel {background-color: #FF6666; padding: 5px; padding-bottom: 15px;}'
        )
        self.lbl_step_help.linkActivated.connect(self.on_step_help_link)
        self.step_help_count = 1

        # set up the step help list: message, reference widget, relative position, signal for next step
        self.step_help = [
            ('Select a module', self.btn_browse, 'bottomright',
             self.module_selected),
            ('Load the module', self.btn_load, 'bottomright',
             self.module_loaded),
            ('Set the parameters', self.tbl_params, 'bottomright',
             self.tbl_params.clicked),
            ('Select X and Y values for plotting', self.plotter.yvar,
             'bottomright', self.plotter.yvar.clicked),
            ('Set up the alarms', self.lbl_alarm, 'topleft',
             self.tbl_alarms.clicked),
            ('Run the script', self.btn_run, 'bottomright',
             self.btn_run.clicked)
        ]

        def make_lambda(i):
            return lambda: self.next_step_help(check_step=i + 1,
                                               close_last=True)

        for i, step in enumerate(self.step_help):
            step[3].connect(make_lambda(i))

    @pyqtSlot(QString)
    def on_step_help_link(self, str):
        if str == 'close':
            self.lbl_step_help.hide()
            self.step_help_count = 0
        elif str == 'next':
            self.next_step_help()
        else:
            raise Exception('Unsupported link')

    @pyqtSlot()
    def next_step_help(self, check_step=None, close_last=False):
        # check if we should update the step help
        if self.step_help_count == 0 or (check_step and
                                         check_step != self.step_help_count):
            return

        # update the step help
        self.step_help_count += 1
        if self.step_help_count > len(self.step_help):
            if close_last:
                self.lbl_step_help.hide()
                self.step_help_count = 0
                return
            else:
                self.step_help_count = 1
        self.update_step_help()

    def update_step_help(self, reinit=False):
        if reinit:
            self.step_help_count = 1
        title = 'Step ' + str(self.step_help_count) + ':'
        text = self.step_help[self.step_help_count - 1][0]
        widget = self.step_help[self.step_help_count - 1][1]
        point = self.step_help[self.step_help_count - 1][2]
        self.lbl_step_help.setText(
            '<span style="font-size: 16px; font-family: DejaVu Sans;"><b>&#x2196;</b>'
            + '<a href="close" style="text-decoration: none;">&#x2716;</a>' +
            '<a href="next" style="text-decoration: none;">&#x27a1;</a>' +
            '</span>' +
            '<div style="margin-left: 10px; margin-right: 10px;">' + '<b>' +
            title + '</b><br>' + text + '</div>')
        self.lbl_step_help.adjustSize()
        if point == 'bottomright':
            self.lbl_step_help.move(
                widget.mapTo(self,
                             QPoint(widget.width() - 5,
                                    widget.height() - 5)))
        elif point == 'topleft':
            self.lbl_step_help.move(widget.mapTo(self, QPoint(5, 5)))
        else:
            raise Exception("Invalid position")

        self.lbl_step_help.show()

    def paintEvent(self, event):
        super(mainwindow, self).paintEvent(event)
        if self.step_help_count > 0:
            if self.isActiveWindow():
                self.update_step_help()
            else:
                self.lbl_step_help.hide()

    @pyqtSlot()
    def browse_acquisition_script(self):
        modulespath = os.path.join(os.path.dirname(os.path.realpath(__file__)),
                                   'modules')
        filename, filt = QFileDialog.getOpenFileName(self,
                                                     'Open File',
                                                     directory=modulespath,
                                                     filter='*.py')
        if filename:
            self.txt_acquisition_script.setText(filename)
            self.module_selected.emit()

    @pyqtSlot()
    def load_module(self):
        # check if there is a running module
        if isinstance(self.m, MeasurementBase) and self.m.isRunning():
            QMessageBox.critical(
                self, "Error",
                "Cannot load a new module when the previous one is not done.")
            return

        # tidy up
        self.tbl_params.clearContents()
        self.plotter.clear()
        self.plotter.xvar.clear()
        self.plotter.yvar.clear()
        self.tbl_observables.clearContents()
        self.tbl_alarms.clearContents()
        for w in [
                self.btn_run, self.btn_browse, self.btn_load, self.btn_help,
                self.btn_addalarm
        ]:
            w.setEnabled(False)
        self.tbl_alarms.blockSignals(
            True)  # we will modify the alarms table, so disable signals

        # indicate we are loading
        self.btn_load.setIcon(QIcon('../icons/wait.png'))
        self.repaint()
        self.plotter.repaint()

        # check if we are dealing with a valid module
        module_loaded = False
        filename = str(self.txt_acquisition_script.text())
        mod_name, file_ext = os.path.splitext(os.path.split(filename)[-1])
        try:
            mod = imp.load_source(mod_name, filename)
            if not hasattr(mod, 'Measurement') or not issubclass(
                    getattr(mod, 'Measurement'), MeasurementBase):
                QMessageBox.critical(self, "Error",
                                     "Could not get correct class from file.")
            else:
                module_loaded = True
        except IOError as e:
            QMessageBox.critical(self, "Error",
                                 "Could not load file: " + str(e.args[1]))
        except Exception as e:
            QMessageBox.critical(
                self, "Error",
                "Could not load module: " + traceback.format_exc())

        if module_loaded:
            self.m = getattr(mod, 'Measurement')(redirect_console=True)

            # set up parameter table
            self.tbl_params.setRowCount(len(self.m.params))
            for i, key in enumerate(self.m.params):
                item = QTableWidgetItem(key)
                item.setFlags(item.flags() ^ Qt.ItemIsEditable)
                self.tbl_params.setItem(i, 0, item)
                value = self.m.params[key]
                if isinstance(value, list):
                    value = '[' + ','.join(map(str, value)) + ']'
                elif isinstance(value, np.ndarray):
                    value = '[' + ",".join(map(str, value.tolist())) + ']'
                if isinstance(value, MeasurementParameter):
                    self.tbl_params.setCellWidget(i, 1, value.widget)
                    value.mainwindow = self
                else:
                    self.tbl_params.setItem(i, 1, QTableWidgetItem(str(value)))

            # set up plotter
            self.plotter.set_header(self.m.observables)

            # set up observables table
            self.tbl_observables.setRowCount(len(self.m.observables))
            for i, label in enumerate(self.m.observables):
                for j in [0, 1]:
                    item = QTableWidgetItem(label if j == 0 else '')
                    item.setFlags(item.flags() ^ Qt.ItemIsEditable)
                    self.tbl_observables.setItem(i, j, item)

            # set up alarms table
            self.tbl_alarms.setRowCount(0)
            for i, alarm in enumerate(self.m.alarms):
                self.add_alarm(
                )  # this has the advantage of directly setting up the combobox as well
                self.tbl_alarms.item(i, 0).setText(self.m.alarms[i][0])
                cmb = self.tbl_alarms.cellWidget(i, 1)
                cmb.setCurrentIndex(cmb.findData(self.m.alarms[i][1]))

            # connect signals
            self.m.new_observables_data[list].connect(self.new_data_handler)
            self.m.new_alarm_data[list].connect(self.new_alarm_data_handler)
            self.m.new_console_data[QString].connect(
                self.readonlyconsole.write)
            self.btn_stopmod.clicked.connect(self.m.quit)
            self.btn_stopmod.clicked.connect(self.quit_requested)
            self.btn_forcestopmod.clicked.connect(self.m.terminate)
            self.m.finished.connect(self.module_done)
            self.tbl_alarms.blockSignals(False)

            # activate run button and add alarm button
            for w in [self.btn_run, self.btn_addalarm]:
                w.setEnabled(True)

            self.module_loaded.emit()

        # activate some other buttons regardless of if the module was successfully loaded
        self.btn_load.setIcon(QIcon('tools-wizard.png'))
        for w in [self.btn_browse, self.btn_load, self.btn_help]:
            w.setEnabled(True)

    @pyqtSlot()
    def run_module(self):
        # check if no other module is running and if the module we loaded is valid
        if isinstance(self.m, MeasurementBase) and self.m.isRunning():
            QMessageBox.critical(
                self, "Error",
                "Cannot run a new module when the previous one is not done.")
            return
        elif not isinstance(self.m, MeasurementBase):
            QMessageBox.critical(self, "Error",
                                 "Please load a valid module first.")
            return

        # deactivate request_quit flag in case process has been stopped previously
        # and disconnect all signals
        self.m.flags['quit_requested'] = False

        # update the parameters that do not update automatically
        # (i.e. that are not derived from MeasurementParameter)
        for i in range(self.tbl_params.rowCount()):
            key = str(self.tbl_params.item(i, 0).text())
            if not isinstance(self.m.params[key], MeasurementParameter):
                # TODO: get rid of the python interpretation and cast value to the correct type (python evaluation should be a special kind of MeasurementParameter)
                value = self.tbl_params.item(i, 1).text()
                try:
                    self.m.params[key] = eval(value, {'np': np})
                except Exception as e:
                    QMessageBox.critical(
                        self, "Error", "Parameter '" + key +
                        "' could not be evaluated: " + str(e.args[0]))
                    return

        # disable parameter editing
        self.tbl_params.setEnabled(False)

        # update buttons
        self.btn_load.setEnabled(False)
        self.btn_run.setEnabled(False)
        self.btn_stopmod.setEnabled(True)

        # run the thread
        self.m.start()

    def quit_requested(self):
        self.btn_stopmod.setEnabled(False)
        self.btn_forcestopmod.setEnabled(True)

    def module_done(self):
        # enable parameter editing
        self.tbl_params.setEnabled(True)

        # update buttons
        self.btn_stopmod.setEnabled(False)
        self.btn_forcestopmod.setEnabled(False)
        self.btn_run.setEnabled(True)
        self.btn_load.setEnabled(True)

    @pyqtSlot()
    def add_alarm(self):
        # temporarily block the signals while we set up the new row in the alarms table
        blocked = self.tbl_alarms.signalsBlocked()
        self.tbl_alarms.blockSignals(True)

        # create a new row in the alarms table
        self.tbl_alarms.setRowCount(self.tbl_alarms.rowCount() + 1)
        i = self.tbl_alarms.rowCount() - 1

        # create an editable field for the alarm condition
        self.tbl_alarms.setItem(i, 0, QTableWidgetItem())

        # create a combo box for the alarm action
        cmb = QComboBox()
        for value, text in [(MeasurementBase.ALARM_SHOWVALUE, 'show value'),
                            (MeasurementBase.ALARM_QUIT, 'stop acquisition'),
                            (MeasurementBase.ALARM_CALLCOPS, 'call the cops')]:
            cmb.addItem(text, value)
        self.tbl_alarms.setCellWidget(i, 1, cmb)

        # create a non-editable field for the alarm info
        item = QTableWidgetItem()
        item.setFlags(item.flags() ^ Qt.ItemIsEditable)
        self.tbl_alarms.setItem(i, 2, item)

        # when the QComboBox is changed, we want to emit the cellChanged signal correspondingly
        cmb.currentIndexChanged.connect(
            lambda: self.tbl_alarms.cellChanged.emit(i, 1))

        # unblock the signals, if they were previously unblocked
        self.tbl_alarms.blockSignals(blocked)

    @pyqtSlot(int, int)
    def alarm_modified(self, row, column):
        # update the alarms
        alarms = []
        for i in range(self.tbl_alarms.rowCount()):
            condition = str(self.tbl_alarms.item(i, 0).text())
            action = self.tbl_alarms.cellWidget(i, 1).currentData()
            alarms.append([condition, action])
        self.m.alarms = alarms

    @pyqtSlot(list)
    def new_data_handler(self, data):
        for i, value in enumerate(data):
            self.tbl_observables.item(i, 1).setText(str(value))
        self.plotter.new_data_handler(data)

    @pyqtSlot(list)
    def new_alarm_data_handler(self, data):
        for i, d in enumerate(data):
            action = self.tbl_alarms.cellWidget(i, 1).currentData()
            result_field = self.tbl_alarms.item(i, 2)
            self.tbl_alarms.item(i, 2).setBackground(Qt.white)
            if isinstance(d, Exception):
                result_field.setText('could not evaluate: ' + str(d))
                continue
            if action == MeasurementBase.ALARM_SHOWVALUE:
                result_field.setText(str(d))
            elif action == MeasurementBase.ALARM_QUIT:
                if data[i]:
                    result_field.setText('stopping acquisition...')
                    result_field.setBackground(Qt.red)
            elif action == MeasurementBase.ALARM_CALLCOPS:
                if data[i]:
                    result_field.setText('calling the cops...')
                    result_field.setBackground(Qt.red)
                else:
                    result_field.setText('OK...')
                    result_field.setBackground(Qt.green)
Beispiel #16
0
class OpticalTab(QWidget):
    """The Optical Tab of ErwinJr. This is designed to be a GUI wrapper of
    the class Stratum
    Member variable
        stratum: A class to describe the physics of 1D optical slab waveguide

        customized SIGNAL
            dirty: Show if there's any new changes to qclayers that need to
                   be saved

        plotImag: bool, whether to plot the imaginary part of the refractive
            index

        redActive: bool, whether to plot the active part red

        --- GUI widget ---
        1st column (settingBox):
            wlBox
            mtrlsBox
            rIdxRealBox     rIdxImagBox
            periodBox       repeatBox
            fieldBox        gainCoeffBox
            facetBox[0]     facetBox[1]
            refBox[0]       refBox[1]
            ridgeLengthBox  mirrorLoss(QLabel)

        2nd column (strataBox):
            insertButton    deleteButton
            strataTable
            solveButton     OptimizeButton
            infoBox

        3rd column (figureBox):
            optCanvas

    Member method
        setupActive
    """
    dirty = pyqtSignal()

    def __init__(self, stratum=None, parent=None):
        super(OpticalTab, self).__init__(parent)
        self.stratum = stratum if stratum else OptStrata(3.0)
        self.periods = {}
        self.ridgeLength = 3.0
        self.ridgeLoss = 0.0
        self.Lps = {}
        self.facet = [facetList[0], facetList[0]]
        self.beta = None
        self.select = None
        self.redActive = False
        self.plotImag = True

        self.setAutoFillBackground(True)
        self.setBackgroundRole(QPalette.Window)

        if sys.platform.startswith('win'):
            settingBoxWidth = 200
            strataBoxWidth = 350
        elif sys.platform.startswith('darwin'):
            settingBoxWidth = 350
            strataBoxWidth = 370
        elif sys.platform.startswith('linux'):
            settingBoxWidth = 350
            strataBoxWidth = 350
        else:
            settingBoxWidth = 350
            strataBoxWidth = 375

        opticalLayout = QHBoxLayout()
        opticalLayout.setSpacing(0)
        opticalLayout.addLayout(self._settingBox(settingBoxWidth))
        opticalLayout.addLayout(self._strataBox(strataBoxWidth))
        figureBox = QVBoxLayout()
        self.optCanvas = EJcanvas(xlabel='Position $x$ (μm)',
                                  ylabel='Refractive index $n$')
        self.rIdxAxes = self.optCanvas.axes
        self.modeAxes = self.rIdxAxes.twinx()
        self.modeAxes.set_frame_on(False)
        self.modeAxes.get_yaxis().set_visible(False)
        # self.modeAxis = self.ridxAxis
        # self.modeAxis.set_ylabel("TM Mode $E_y$ (a.u.)", color='C0',
        #                          fontsize=canvasConfig["fontsize"])
        # self.modeAxis.tick_params(axis='y', labelcolor='C0')
        figureBox.addWidget(self.optCanvas)
        opticalLayout.addLayout(figureBox)

        self.setLayout(opticalLayout)
        self.dirty.connect(self.update_xs)
        self.update_xs()
        self.update_Loss()

    def _settingBox(self, width):
        """Return a Qt Layout object containing all setting widgets"""
        settingBox = QVBoxLayout()

        settingBox.addWidget(QLabel('<b><center>Wavelength</center></b>'))
        self.wlBox = QDoubleSpinBox()
        self.wlBox.setDecimals(1)
        self.wlBox.setValue(self.stratum.wl)
        self.wlBox.setSuffix(' μm')
        self.wlBox.setRange(0.0, 100.0)
        self.wlBox.setMaximumWidth(width)
        settingBox.addWidget(self.wlBox)

        mtrlGroupBox = QGroupBox("Custom Material")
        mtrlGroupBox.setMaximumWidth(width)
        mtrlLayout = QGridLayout()
        mtrlLayout.addWidget(QLabel('<center>Material Name</center>'), 0, 0, 1,
                             2)
        self.mtrlsBox = QComboBox()
        self.mtrlsBox.addItems(self.stratum.cstmIndx.keys())
        mtrlLayout.addWidget(self.mtrlsBox, 1, 0, 1, 2)
        mtrlLayout.addWidget(QLabel('<center>index n<sub>eff</sub></center>'),
                             2, 0)
        self.rIdxRealBox = QDoubleSpinBox()
        self.rIdxRealBox.setSingleStep(0.1)
        mtrlLayout.addWidget(self.rIdxRealBox, 3, 0)
        mtrlLayout.addWidget(QLabel('<center>passive loss k</center>'), 2, 1)
        self.rIdxImagBox = QDoubleSpinBox()
        self.rIdxImagBox.setSingleStep(0.1)
        mtrlLayout.addWidget(self.rIdxImagBox, 3, 1)
        mtrlLayout.addWidget(QLabel('<center>Period Length</center>'), 4, 0)
        self.periodBox = QDoubleSpinBox()
        self.periodBox.setSuffix(" Å")
        self.periodBox.setMaximum(19999.99)
        self.repeatBox = QSpinBox()
        self.repeatBox.setMaximum(999)
        mtrlLayout.addWidget(self.periodBox, 5, 0)
        mtrlLayout.addWidget(QLabel('<center>Periods</center>'), 4, 1)
        mtrlLayout.addWidget(self.repeatBox, 5, 1)
        mtrlLayout.addWidget(QLabel('<center>Active property</center>'), 6, 0,
                             1, 2)
        mtrlLayout.addWidget(QLabel('<center>Bias Field</center>'), 7, 0)
        self.fieldBox = QDoubleSpinBox()
        self.fieldBox.setSuffix(' kV/cm')
        self.fieldBox.setEnabled(False)
        self.fieldBox.setDecimals(1)
        self.fieldBox.setMaximum(500)
        mtrlLayout.addWidget(self.fieldBox, 8, 0)
        mtrlLayout.addWidget(QLabel('<center>Gain Coeff.</center>'), 7, 1)
        self.gainCoeffBox = QDoubleSpinBox()
        self.gainCoeffBox.setSuffix(' cm/kA')
        self.gainCoeffBox.setEnabled(False)
        self.gainCoeffBox.setDecimals(1)
        mtrlLayout.addWidget(self.gainCoeffBox, 8, 1)
        # TODO
        self.updateMtrl()
        mtrlLayout.setHorizontalSpacing(1)
        mtrlLayout.setVerticalSpacing(3)
        mtrlGroupBox.setLayout(mtrlLayout)
        settingBox.addWidget(mtrlGroupBox)

        self.wlBox.valueChanged[float].connect(self.input_wl)
        self.mtrlsBox.currentIndexChanged[int].connect(self.updateMtrl)
        self.rIdxRealBox.valueChanged[float].connect(self.input_rIdx)
        self.rIdxImagBox.valueChanged[float].connect(self.input_alpha)
        self.periodBox.valueChanged[float].connect(self.input_period)
        self.repeatBox.valueChanged[int].connect(self.input_repeats)
        self.gainCoeffBox.valueChanged[float].connect(self.input_gainCoeff)

        ridgeBox = QGroupBox("Ridge Geometry")
        ridgeBox.setMaximumWidth(width)
        ridgeLayout = QGridLayout()
        self.facetBox = [None] * 2
        self.refBox = [None] * 2
        for n in (0, 1):
            ridgeLayout.addWidget(QLabel("<center>Facet%d</center>" % (n + 1)),
                                  0, n)
            self.facetBox[n] = QComboBox()
            self.facetBox[n].addItems(facetList)
            self.facetBox[n].setCurrentText(self.facet[n])
            ridgeLayout.addWidget(self.facetBox[n], 1, n)
            self.refBox[n] = QDoubleSpinBox()
            self.refBox[n].setDecimals(1)
            self.refBox[n].setRange(0.0, 100.0)
            self.refBox[n].setSuffix(" %")
            self.refBox[n].setValue(self.facetRefct(n))
            ridgeLayout.addWidget(self.refBox[n], 3, n)
            self.facetBox[n].currentIndexChanged[int].connect(
                partial(self.input_facet, n))
            self.refBox[n].valueChanged[float].connect(
                partial(self.input_ref, n))
            if self.facet[n] == "custom":
                self.refBox[n].setEnabled(True)
                self.refBox[n].blockSignals(False)
            else:
                self.refBox[n].setEnabled(False)
                self.refBox[n].blockSignals(True)
        ridgeLayout.addWidget(QLabel("<center>Reflectivity</center>"), 2, 0, 1,
                              2)

        ridgeLayout.addWidget(QLabel("<center>Ridge Length</center>"), 4, 0)
        self.ridgeLengthBox = QDoubleSpinBox()
        self.ridgeLengthBox.setDecimals(1)
        self.ridgeLengthBox.setRange(0.0, 20.0)
        self.ridgeLengthBox.setSingleStep(1)
        self.ridgeLengthBox.setValue(self.ridgeLength)
        self.ridgeLengthBox.setSuffix(" mm")
        self.ridgeLengthBox.valueChanged[float].connect(self.input_ridgeL)
        ridgeLayout.addWidget(self.ridgeLengthBox, 5, 0)
        ridgeLayout.addWidget(QLabel("<center>Mirror Loss: </center>"), 4, 1)
        self.mirrorLoss = QLabel("<center>0.0 cm<sup>-1</sup></center>")
        ridgeLayout.addWidget(self.mirrorLoss, 5, 1)
        ridgeLayout.setHorizontalSpacing(1)
        ridgeLayout.setVerticalSpacing(3)
        ridgeBox.setLayout(ridgeLayout)
        settingBox.addWidget(ridgeBox)

        settingBox.addStretch()
        return settingBox
        # _settingBox end

    def _strataBox(self, width):
        """Return a Qt Layout object containing all strata table widgets"""
        strataBox = QGridLayout()
        strataBox.setSpacing(5)
        self.insertButton = QPushButton("Insert Strata")
        self.insertButton.clicked.connect(self.insert_strata)
        self.deleteButton = QPushButton("Delete Strata")
        self.deleteButton.clicked.connect(self.delete_strata)
        strataBox.addWidget(self.insertButton, 0, 0)
        strataBox.addWidget(self.deleteButton, 0, 1)
        self.strataTable = QTableWidget()
        self.strataTable.setMaximumWidth(width)
        self.strataTable.setMinimumWidth(width)
        self.strataTable.setMinimumHeight(375)
        self.strataTable.setMaximumHeight(500)
        self.strataTable.setSelectionBehavior(QTableWidget.SelectRows)
        self.strataTable.setSelectionMode(QTableWidget.SingleSelection)
        self.strataTable.itemChanged.connect(self.strataTable_item)
        self.strataTable.itemSelectionChanged.connect(self.strataTable_select)
        self.strataTable_refresh()
        strataBox.addWidget(self.strataTable, 1, 0, 1, 2)
        self.solveButton = QPushButton("Solve Mode")
        self.solveButton.clicked.connect(self.solve)
        self.optimizeButton = QPushButton("Optimize Strata")
        self.optimizeButton.clicked.connect(self.optimizeStrata)
        self.optimizeButton.setEnabled(False)
        strataBox.addWidget(self.solveButton, 2, 0)
        strataBox.addWidget(self.optimizeButton, 2, 1)
        strataBox.addWidget(QLabel("Performance"), 3, 0, 1, 2)
        self.infoBox = QTextEdit()
        self.infoBox.setReadOnly(True)
        self.infoBox.setMinimumWidth(width)
        self.infoBox.setMaximumWidth(width)
        self.infoBox.setMinimumHeight(120)
        strataBox.addWidget(self.infoBox, 4, 0, 1, 2)

        strataBox.setRowStretch(5, 0)
        return strataBox

    @pyqtSlot(float)
    def input_wl(self, wl):
        """SLOT connected to self.wlBox.valueChanged[float]"""
        self.stratum.setWl(wl)
        self.dirty.emit()

    @pyqtSlot(int)
    def updateMtrl(self, idx=0):
        """SLOT connect to mtrlsBox.currentIndexChanged[int]"""
        if self.stratum.cstmIndx:
            self.mtrlsBox.setCurrentIndex(idx)
            mtrl = self.mtrlsBox.currentText()
            mtrlIdx = self.stratum.cstmIndx[mtrl]
            self.rIdxRealBox.setValue(mtrlIdx.real)
            self.rIdxImagBox.setValue(mtrlIdx.imag)
            if (mtrl in self.stratum.cstmPrd
                    and self.stratum.cstmPrd[mtrl][0] > 0):
                self.periodBox.setValue(self.stratum.cstmPrd[mtrl][0])
                self.repeatBox.setValue(self.stratum.cstmPrd[mtrl][1])
            else:
                self.periodBox.setValue(0.0)
                self.repeatBox.setValue(0)
                self.repeatBox.setEnabled(False)
            if mtrl in self.stratum.cstmGain:
                self.gainCoeffBox.setValue(self.stratum.cstmGain[mtrl])
        else:
            self.rIdxRealBox.setValue(1.0)
            self.rIdxRealBox.setEnabled(False)
            self.rIdxImagBox.setValue(0.0)
            self.rIdxImagBox.setEnabled(False)
            self.periodBox.setValue(0.0)
            self.periodBox.setEnabled(False)
            return

    @pyqtSlot(float)
    def input_rIdx(self, value):
        """SLOT connected to self.rIdxRealBox.valueChanged[float]"""
        mtrl = self.mtrlsBox.currentText()
        alpha = self.stratum.cstmIndx[mtrl].imag
        self.stratum.cstmIndx[mtrl] = value + 1j * alpha
        self.dirty.emit()

    @pyqtSlot(float)
    def input_alpha(self, value):
        """SLOT connected to self.rIdxImagBox.valueChanged[float]"""
        mtrl = self.mtrlsBox.currentText()
        neff = self.stratum.cstmIndx[mtrl].real
        self.stratum.cstmIndx[mtrl] = neff + 1j * value
        self.dirty.emit()

    @pyqtSlot(float)
    def input_period(self, value):
        """SLOT connected to self.periodBox.valueChanged[float]"""
        mtrl = self.mtrlsBox.currentText()
        if mtrl not in self.stratum.cstmPrd:
            self.stratum.cstmPrd[mtrl] = [value, 1]
        else:
            self.stratum.cstmPrd[mtrl][0] = value
        if value == 0:
            self.repeatBox.setEnabled(False)
        else:
            self.repeatBox.setEnabled(True)
            self.update_customLength()

    @pyqtSlot(int)
    def input_repeats(self, value):
        """SLOT connected to self.repeatBox.valueChanged[int]"""
        mtrl = self.mtrlsBox.currentText()
        if mtrl not in self.stratum.cstmPrd:
            self.stratum.cstmPrd[mtrl] = [1, value]
        else:
            self.stratum.cstmPrd[mtrl][1] = value
        self.update_customLength()

    @pyqtSlot(float)
    def input_gainCoeff(self, value):
        """SLOT connected to self.gainCoeffBox.valueChanged[float]"""
        mtrl = self.mtrlsBox.currentText()
        self.stratum.cstmGain[mtrl] = value

    def setupActive(self, wl, EField, gaincoeff, rIdx, Lp):
        """Interface to get parameters from quantum tab"""
        self.wlBox.setValue(wl)
        mtrl = 'Active Core'
        # self.stratum.wl = wl
        self.stratum.cstmIndx[mtrl] = rIdx
        if 'Active Core' in self.stratum.cstmPrd:
            self.stratum.cstmPrd[mtrl][0] = Lp
        else:
            self.stratum.cstmPrd[mtrl] = [Lp, 1]
        self.stratum.cstmGain[mtrl] = gaincoeff

        # set up active core
        if self.mtrlsBox.findText(mtrl) == -1:
            self.mtrlsBox.addItem(mtrl)
        self.mtrlsBox.setCurrentText(mtrl)
        self.fieldBox.setValue(EField)
        self.periodBox.setValue(Lp)
        self.gainCoeffBox.setValue(gaincoeff)
        self.update_customLength()
        self.updateMtrl()

    def update_customLength(self):
        length = self.periodBox.value() * self.repeatBox.value() / 1E4  # to um
        for n, mtrl in enumerate(self.stratum.materials):
            if mtrl == self.mtrlsBox.currentText():
                self.stratum.Ls[n] = length
        self.dirty.emit()

    def input_facet(self, n, idx):
        """SLOT as partial(self.input_facet, n) connected to
        facetBox[n].currentIndexChanged(int)"""
        self.facet[n] = facetList[idx]
        self.refBox[n].setValue(self.facetRefct(n) * 100)
        if self.facet[n] == "custom":
            self.refBox[n].setEnabled(True)
            self.refBox[n].blockSignals(False)
        else:
            self.refBox[n].setEnabled(False)
            self.refBox[n].blockSignals(True)
        self.update_Loss()
        self.dirty.emit()

    def input_ref(self, n, ref):
        """SLOT as partial(self.input_facet, n) connected to
        facetBox[n].currentIndexChanged(int)"""
        self.update_Loss()
        self.dirty.emit()

    def facetRefct(self, n):
        """Return the reflectivity of facet n"""
        if self.facet[n] == 'cleaved':
            if self.beta is None:
                return -1
            R = abs((self.beta.real - 1) / (self.beta.real + 1))**2
            self.refBox[n].setValue(100 * R)
            return R
        if self.facet[n] == 'perfect AR':
            return 1E-9
        if self.facet[n] == 'perfect HR':
            return 1
        if self.facet[n] == 'custom':
            return self.refBox[n].value() / 100
        else:
            raise ValueError("Wrong facet %s" % self.facet[n])

    @pyqtSlot(float)
    def input_ridgeL(self, value):
        """SLOT connected to ridgeLengthBox.valueChanged[float]"""
        self.ridgeLength = value
        self.update_Loss()
        self.dirty.emit()

    def update_Loss(self):
        """Update the mirror loss, should be called whenever the facet
        settings are changed"""
        perRunLoss = self.facetRefct(1) * self.facetRefct(0)
        self.alphaM = -log(perRunLoss) / (2 * self.ridgeLength / 10)  # to cm-1
        self.mirrorLoss.setText("<center>%.1f cm<sup>-1</sup></center>" %
                                self.alphaM)

    @pyqtSlot()
    def strataTable_select(self):
        """SLOT connected to SIGNAL strataTable.itemSelectionChanged"""
        row = self.strataTable.currentRow()
        if row < len(self.stratum.materials):
            self.select = row
        else:
            self.select = None
            self.stratTable.clearSelection()
        self.update_canvas()

    @pyqtSlot(QTableWidgetItem)
    def strataTable_item(self, item):
        """SLOT connected to strataTable.itemChanged"""
        row = item.row()
        column = item.column()
        if column in (1, 2, 3):
            try:
                value = float(item.text())
                if value < 0:
                    raise ValueError
            except ValueError:
                # invalid input
                QMessageBox.warning(
                    self, ejError, "This value should be "
                    "a non-negative number")
                self.strataTable_refresh()
                return
            if column == 1:
                # mole fraction
                if value > 1:
                    QMessageBox.warning(
                        self, ejError, "Mole Fraction must be between 0 and 1")
                    self.strataTable.setItem(
                        row, column,
                        QTableWidgetItem("%.2f" % self.stratum.moleFracs[row]))
                    return
                self.stratum.moleFracs[row] = value
            if column == 2:
                # width
                self.stratum.Ls[row] = value
            if column == 3:
                # doping
                self.stratum.dopings[row] = value

        self.stratum.updateIndices()
        self.dirty.emit()

    @pyqtSlot()
    def insert_strata(self):
        """SLOT connected to self.insertButton.clicked()"""
        row = self.strataTable.currentRow()
        if row >= len(self.stratum.materials) or row < 0:
            # Add new lines in the last strata
            row = len(self.stratum.materials) - 1
        elif row == 0:
            row = 1
        # len(self.stratum.materials) is at least 2 for top and substrate
        # s.t. row >= 1 and <= len(self.stratum.materials) - 1
        self.stratum.insert(row)
        self.dirty.emit()
        self.strataTable.selectRow(row)

    @pyqtSlot()
    def delete_strata(self):
        """SLOT connected to self.deleteButton.clicked()"""
        row = self.strataTable.currentRow()
        if row >= len(self.stratum.materials) - 1 or row < 1:
            return
        # s.t. len(self.stratum.materials) > 2 and Ls is not empty
        self.stratum.delete(row)
        self.dirty.emit()
        self.strataTable.selectRow(row)

    def strataTable_refresh(self):
        """Update strataTable content, should be called whenever the strata
        structure is changed"""
        self.strataTable.blockSignals(True)
        self.stratum.updateIndices()
        self.strataTable.clear()
        self.strataTable.setColumnCount(5)
        self.strataTable.setHorizontalHeaderLabels(
            ["Material", "x", "Width", "Doping", "n"])
        self.strataTable.setRowCount(len(self.stratum.materials))
        self.strataTable.setVerticalHeaderLabels(
            str(n + 1) for n in range(len(self.stratum.materials)))

        for q, mtrl in enumerate(self.stratum.materials):
            # material name
            mtrlName = mtrlComboBox()
            mtrlName.addItems(mtrlList)
            mtrlName.addItems(self.stratum.cstmIndx.keys())
            mtrlName.setCurrentText(mtrl)
            mtrlName.currentTextChanged.connect(
                partial(self.strataTable_mtrlChanged, q))
            self.strataTable.setCellWidget(q, 0, mtrlName)

            # Mole Frac
            if mtrl not in Alloy:
                moleFrac = QTableWidgetItem('N/A')
                moleFrac.setFlags(Qt.ItemIsSelectable)
            else:
                moleFrac = QTableWidgetItem("%.2f" % self.stratum.moleFracs[q])
            # moleFrac.setTextAlignment(Qt.AlignCenter)
            self.strataTable.setItem(q, 1, moleFrac)

            # Thickness
            if q == 0 or q == len(self.stratum.materials) - 1:
                # TODO: To check the best default width for substrate and air
                thickness = QTableWidgetItem('1.0' if q == 0 else '3.0')
                thickness.setFlags(Qt.ItemIsSelectable)
            else:
                thickness = QTableWidgetItem("%.2f" % self.stratum.Ls[q])
                if mtrl in self.stratum.cstmIndx:
                    thickness.setFlags(Qt.ItemIsSelectable)
            self.strataTable.setItem(q, 2, thickness)

            # doping
            if mtrl in Dopable:
                doping = QTableWidgetItem("%.1f" % self.stratum.dopings[q])
            else:
                doping = QTableWidgetItem("N/A")
                doping.setFlags(Qt.ItemIsSelectable)
            self.strataTable.setItem(q, 3, doping)

            # refractive index
            if q == 0:
                ridx = self.stratum.index0
            elif q == len(self.stratum.materials) - 1:
                ridx = self.stratum.indexS
            else:
                ridx = self.stratum.indices[q - 1]
            ridx = QTableWidgetItem("%.3f + %.3fi" % (ridx.real, ridx.imag))
            ridx.setFlags(Qt.ItemIsSelectable)
            self.strataTable.setItem(q, 4, ridx)

        self.strataTable.resizeColumnsToContents()
        self.strataTable.blockSignals(False)

    def strataTable_mtrlChanged(self, row, selection):
        """SLOT as partial(self.strataTable_mtrlChanged, q) connected to
        mtrlName.currentTextChanged(str)"""
        self.stratum.materials[row] = selection
        if selection == 'Active Core':
            self.update_customLength()
        self.strataTable.selectRow(row)
        self.dirty.emit()

    @pyqtSlot()
    def update_xs(self):
        """Update position vector for plotting and confinement factor integral,
        also as SLOT to self.dirty SGINAL"""
        self.beta = None
        self.optimizeButton.setEnabled(False)
        # self.stratum.updateIndices()
        self.strataTable_refresh()
        self.xs = np.linspace(-1, sum(self.stratum.Ls[1:]), 5000)
        self.update_canvas()

    @pyqtSlot()
    def solve(self):
        """SLOT connected to self.solveButton.clicked

        Yield
        -----
        Ey : np.ndarray(complex)
            The field to plot, normalized to max(abs(Ey)) = 1

        confinement : float
            The confinement factor in unit 1 (percentage 100*confinement)

        alphaW : float
            The waveguide loss in unit cm-1
        """
        try:
            self.beta = self.stratum.boundModeTM()
        except (TimeoutError, ValueError):
            QMessageBox.warning(self, ejError, "Failed to solve for modes")
            return
        self.Ey, _, _ = self.stratum.populateMode(self.beta, self.xs)
        # TODO
        self.confinement = self.stratum.confinementy(self.beta, self.xs,
                                                     self.Ey)
        self.alphaW = 4 * pi / (self.stratum.wl /
                                1E4) * self.beta.imag  # cm^-1
        self.update_canvas()
        self.update_Loss()
        self.update_info()
        self.optimizeButton.setEnabled(True)
        return self.Ey

    def update_info(self):
        """Update information in the info box"""
        info = ""
        if self.beta is not None:
            info += "Effective refractive index:\n"
            info += "  β = %.3f + (%.3g)i\n" % (self.beta.real, self.beta.imag)
            info += "Waveguide loss:\n"
            info += "  α<sub>w</sub> = %.3f cm<sup>-1</sup>\n" % self.alphaW
            info += "Confinement factor: "
            info += "  Γ = %.1f%%\n" % (self.confinement * 100)
            info += "Threshold gain:\n"
            gth = (self.alphaM + self.alphaW) / self.confinement
            info += " g<sub>th</sub> = %.1f cm<sup>-1</sup>\n" % gth
            try:
                self.jth = gth / self.stratum.cstmGain["Active Core"]
                info += "Threshold current:\n"
                info += "  J<sub>th</sub> = %.1f kA/cm<sup>-2</sup>" % self.jth
            except (AttributeError, ZeroDivisionError, KeyError):
                info += "\nUse the quantum tab to define Active region"
        self.infoBox.setHtml(info.replace("\n", "<br>"))

    def update_canvas(self):
        """Update figure in optCanvas"""
        self.rIdxAxes.clear()
        self.modeAxes.clear()
        nx = self.stratum.populateIndices(self.xs)
        self.rIdxAxes.plot(self.xs, nx.real, 'k', lw=1)
        self.rIdxAxes.set_xlabel('Position (μm)')
        self.rIdxAxes.set_ylabel('Mode Intensity (a.u.) or Refractive Index')
        if self.redActive:
            for ar in self.stratum.populateMtrl(self.xs):
                self.rIdxAxes.plot(self.xs[ar], nx.real[ar], 'r', lw=2)
        if np.max(nx.real) > 5:
            self.rIdxAxes.set_ylim(top=5)
        if self.plotImag:
            self.rIdxAxes.plot(self.xs, nx.imag, 'orange', lw=1)
            if np.max(nx.imag) > 5:
                self.rIdxAxes.set_ylim(top=5)
        # plot select strata
        if self.select is not None:
            if self.select == 0:
                idx = self.xs < 0
            elif self.select == len(self.stratum.Ls) - 1:
                idx = self.xs > sum(self.stratum.Ls[1:-1])
            else:
                lsum = sum(self.stratum.Ls[1:self.select])
                idx = (self.xs >= lsum) & (self.xs <
                                           lsum + self.stratum.Ls[self.select])
            self.rIdxAxes.plot(self.xs[idx], nx.real[idx], 'b', lw=1.5)
        if self.beta is not None:
            self.modeAxes.plot(self.xs, np.abs(self.Ey)**2, color='C0')
        # self.optCanvas.figure.tight_layout()
        self.optCanvas.draw()

    @pyqtSlot()
    def view_redActive(self):
        self.redActive = not self.redActive
        self.update_canvas()

    @pyqtSlot()
    def optimizeStrata(self):
        # TODO use threadRun in QuantumTab
        # TODO: block the button before solve
        nmax = len(self.stratum.Ls) - 2
        toOptimize, lmax, buttonRes = OptimizeInfoDialog(
            self, [
                self.stratum.materials[q + 1] not in self.stratum.cstmIndx
                for q in range(nmax)
            ], nmax, sum(self.stratum.Ls[1:-1])).exec()
        if buttonRes:
            optimizeOptStrata(self.stratum, self.alphaM, toOptimize, lmax)
            self.strataTable_refresh()
            self.solve()
Beispiel #17
0
class AdvSetTab(QWidget):

    def __init__(self, gui):
        super(AdvSetTab, self).__init__()

        self._ui = gui
        self._config = self._ui.config
        self._tran = self._ui.tran
        self._parser = self._ui.configparser
        self.setObjectName("advtab")

        # main layout of this tab
        self._layout = QGridLayout(self)
        self._layout.setObjectName('advtab_layout')

        # setup the new button
        self._button_new = QPushButton(self)
        self._button_new.setObjectName('advtab_button_new')
        self._button_new.setText(self._tran.get_text(self._button_new.objectName()))
        self._button_new.clicked.connect(self._new_setting)

        # setup the delete button
        self._button_del = QPushButton(self)
        self._button_del.setObjectName('advtab_button_delete')
        self._button_del.setText(self._tran.get_text(self._button_del.objectName()))
        self._button_del.clicked.connect(self._delete_setting)

        # setup the delete all
        self._button_del_all = QPushButton(self)
        self._button_del_all.setObjectName('advtab_button_delete_all')
        self._button_del_all.setText(self._tran.get_text(self._button_del_all.objectName()))
        self._button_del_all.clicked.connect(self._delete_config)

        # setup the reset all button
        self._button_res_all = QPushButton(self)
        self._button_res_all.setObjectName('advtab_button_reset_all')
        self._button_res_all.setText(self._tran.get_text(self._button_res_all.objectName()))
        self._button_res_all.clicked.connect(self._reset_config)

        # setup the option table
        self._table = QTableWidget(0, 2)
        self._table.setObjectName('advtab_table')
        self._table.setHorizontalHeaderLabels([self._tran.get_text('option'), self._tran.get_text('value')])
        self._table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
        self._table.setSelectionBehavior(QAbstractItemView.SelectRows)
        self._table.setSelectionMode(QAbstractItemView.SingleSelection)
        self._table.setShowGrid(False)
        self._table.setGeometry(0, 0, 800, 400)
        self._table.itemChanged.connect(self._changed_setting)

        # add the elements to the top level layout of the tab
        self._layout.addWidget(self._button_new, 0, 0, 1, 1)
        self._layout.addWidget(self._button_del, 0, 1, 1, 1)
        self._layout.addWidget(self._button_del_all, 0, 2, 1, 1)
        self._layout.addWidget(self._button_res_all, 0, 3, 1, 1)
        self._layout.addWidget(self._table, 1, 0, 4, 4)

        # set the array with all elements that have tooltips
        self._tooltips = [
            self._button_new, self._button_del,
            self._button_del_all, self._button_res_all,
        ]

    def _changed_setting(self):
        """
        Update the config with the values from the ui

        :return:
        """
        # run through the table
        for a in range(self._table.rowCount()):
            # get key and value of the current row
            _key = self._table.item(a, 0).text() if self._table.item(a, 0) else ""
            _value = self._table.item(a, 1).text() if self._table.item(a, 1) else ""
            # update the config
            self._config[_key] = _value
        # make the default config visible in the ui, toggle tooltips, ...
        self.init()

    def _new_setting(self):
        """
        Add a new setting to the config

        :return: None
        """
        # get the number of rows in the table
        _rows = self._table.rowCount()
        # create a wizard to get the setting name and setting value from the user
        _wizard = OptionWizard(self._tran.get_text('option_wizard_title'), self._tran)
        # open the wizard
        _wizard.exec_()
        # only if the user has entered a setting name
        if _wizard.setting_name():
            # create a new item holding the entered data
            self._table.insertRow(_rows)
            _key = QTableWidgetItem(_wizard.setting_name())
            # make the new item editable and selectable
            _flags = _key.flags()
            _flags |= Qt.ItemIsSelectable
            _flags &= Qt.ItemIsEditable
            _key.setFlags(_flags)
            # add the new item to the table
            self._table.setItem(_rows, 0, _key)
            self._table.setItem(_rows, 1, QTableWidgetItem(_wizard.setting_value()))
            # add the new setting to the config
            self._config[_wizard.setting_name()] = _wizard.setting_value()
        # make the default config visible in the ui, toggle tooltips, ...
        self.init()

    def _delete_setting(self):
        """
        Remove the selected setting from config

        :return: None
        """
        # get the index of the selected row
        try:
            _row = self._table.selectedItems()[0].row()
        except KeyError:
            return
        # delete the options from config
        del self._config[self._table.selectedItems()[0].text()]
        # remove the row from the table
        self._table.removeRow(_row)
        # make the default config visible in the ui, toggle tooltips, ...
        self.init()

    def _delete_config(self):
        """
        Remove all setting from config

        :return: None
        """
        # clear the config
        self.config = {}
        # make the default config visible in the ui, toggle tooltips, ...
        self.init()

    def _reset_config(self):
        """
        Reset the config to default state

        :return: None
        """
        # reset config to default
        self._config = cc_con.DEFAULT_CONFIG
        # make the default config visible in the ui, toggle tooltips, ...
        self.init()

    def toggle_tooltips(self, onoff):
        """
        If "onoff" is either "True" or "true", then show tooltips for this tab, otherwise dont

        :param onoff: whether to show verbose tooltips
        :return: None
        """
        if onoff == "True" or onoff == "true":
            # run through the list with elements that have tooltips...
            for ele in self._tooltips:
                # and activate the tooltip for each element
                ele.setToolTip(self._tran.get_text(ele.objectName() + '_tooltip'))
        else:
            # run through the list with elements that have tooltips...
            for ele in self._tooltips:
                # and disable the tooltip for each element
                ele.setToolTip("")

    def lock(self):
        """
        Disable the ui components, so that the user cannot change settings while the program is running

        :return: None
        """
        self._toggle_ui_components(False)

    def unlock(self):
        """
        Enable the ui components, so that the user can change settings

        :return: None
        """
        self._toggle_ui_components(True)

    def _toggle_ui_components(self, onoff):
        """
        Enable/Disable ui components (DO NOT USE THIS DIRECTLY; use _lock()/_unlock())

        :param onoff: True to enable, False to disable
        :return: None
        """
        # enable/disable buttons
        self._button_del.setEnabled(onoff)
        self._button_del_all.setEnabled(onoff)
        self._button_new.setEnabled(onoff)
        self._button_res_all.setEnabled(onoff)
        # enable/disable editing of the items in the table
        for a in range(0, self._table.rowCount()):
            if onoff:
                self._table.setEnabled(True)
            else:
                self._table.setEnabled(False)

    def init(self):
        """
        What to do, when this tab is activated: fill the table with the contents of the config, ...

        :return: None
        """
        # prevent sending signals: otherwise this caused the itemChanged Signal of the table to fire which in turn
        # invokes this method, ... -> infinitely deep recursion
        self._table.blockSignals(True)
        # delete all rows from the table
        while self._table.rowCount() > 0:
            self._table.removeRow(0)
        # fill the table with the contents of the config
        _i = 0
        for key, value in self._config.items():
            self._table.insertRow(_i)
            self._table.setItem(_i, 0, QTableWidgetItem(key))
            self._table.setItem(_i, 1, QTableWidgetItem(value))
            _i += 1
        # toggle tooltips if necessary
        if cc_con.SHOW_VERB_TOOL in self._config:
            self.toggle_tooltips(self._config[cc_con.SHOW_VERB_TOOL])
        # if the options for showing tooltips has not been set they are shown per default
        else:
            self.toggle_tooltips("True")
        self._table.blockSignals(False)

    def config(self):
        """
        Return the internal configuration of this tab

        :return: A dictionary containing the configuration
        """
        return self._config