def write(self, ds_out):
     if out.version() >= QtCore.QDataStream.Qt_4_4:
         isCompressed = 1
         if self._svgBuffers:
             svgBuffers = self._svgBuffers
         else:
             svgBuffers = {}     # QHash<int, QByteArray>
         for key, v in self._svgFiles.items():
             buf = QtCore.QByteArray()
             f = QtCore.QFile(v)
             if f.open(QtCore.QIODevice.ReadOnly):
                 buf = f.readAll()
             buf = QtCore.qCompress(buf)
             svgBuffers[key] = buf
         out << self._svgFiles << isCompressed << svgBuffers
         if self._addedPixmaps:
             out << 1 << self._addedPixmaps
         else:
             out << 0
     else:
         buf = QtCore.QByteArray()
         if self._svgBuffers:
             buf = self._svgBuffers[self.default_key]
         if buf.isEmpty():
             svgFile = self._svgFiles[self.default_key]
             if not svgFile.isEmpty():
                 f = QtCore.QFile(svgFile)
                 if f.open(QtCore.QIODevice.ReadOnly):
                     buf = f.readAll()
         buf = QtCore.qCompress(buf)
         out << buf
         # 4.3 has buggy handling of added pixmaps, so don't write any
         out << 0
     return True
Пример #2
0
 def handleMessage(self):
     socket = self._server.nextPendingConnection()
     if socket.waitForReadyRead(self._timeout):
         self.messageAvailable.emit(
             socket.readAll().data().decode('utf-8'))
         socket.disconnectFromServer()
     else:
         QtCore.qDebug(socket.errorString())
    def _loadDataForModeAndState(self, renderer, mode, state):
        """Load SVG data to renderer.
        """
        # First, try to load from a buffer if available.
        if (mode, state) in self._svgBuffers:
            buf = self._svgBuffers[(mode, state)]
        elif self.default_key in self._svgBuffers:
            buf = self._svgBuffers[self.default_key]
        else:
            buf = QtCore.QByteArray()

        if buf:
            buf = QtCore.qUncompress(buf)
            renderer.load(buf)
        else:
            # If no buffer is available, load from file
            if (mode, state) in self._svgFiles:
                svgFile = self._svgFiles[(mode, state)]
                renderer.load(self._replace_in_stream(svgFile))
            elif self.default_key in self._svgFiles:
                svgFile = self._svgFiles[self.default_key]
                if mode == QtGui.QIcon.Disabled:
                    renderer.load(self._replace_in_stream(svgFile, 'disabled'))
                else:
                    renderer.load(self._replace_in_stream(svgFile))
    def read(self, ds_in):

        self._svgBuffers = {}    # QHash<int, QByteArray>

        if ds_in.version() >= QtCore.QDataStream.Qt_4_4:
            nfiles = ds_in.readInt()
            fileNames = {}
            for i in range(nfiles):
                fileNames[i] = ds_in.readString()
            isCompressed = ds_in.readBool()
            key = ds_in.readInt()
            self._svgBuffers[key] = QtCore.QByteArray()
            ds_in >> self._svgBuffers[key]
            if not isCompressed:
                for key, v in self._svgBuffers.items():
                    self._svgBuffers[key] = QtCore.qCompress(v)
            hasAddedPixmaps = ds_in.readInt()
            if hasAddedPixmaps:
                npixmaps = ds_in.readInt()
                self._addedPixmaps = {}
                for i in range(npixmaps):
                    pm = QtGui.QPixmap()
                    ds_in >> pm
                    self._addedPixmaps[i] = pm
        else:
            pixmap = QtGui.QPixmap()
            data = QtCore.QByteArray()

            ds_in >> data
            if not data.isEmpty():
                data = QtCore.qUncompress(data)
                if not data.isEmpty():
                    self._svgBuffers[self.default_key] = data
            num_entries = ds_in.readInt()
            for i in range(num_entries):
                if ds_in.atEnd():
                    return False
                ds_in >> pixmap
                mode = ds_in.readUInt32()
                state = ds_in.readUInt32()
                # The pm list written by 4.3 is buggy and/or useless, so ignore
                # self._addPixmap(pixmap, QIcon.Mode(mode), QIcon.State(state))

        return True
Пример #5
0
class LoadView(QtWidgets.QWidget):
    sig_loading_finished = QtCore.Signal()

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

        self.browse_button = QtWidgets.QPushButton("User Dirs", self)
        self.browse_button.clicked.connect(self.show_directory_manager)

        self.co_button = QtWidgets.QPushButton("Co-Add", self)
        self.load_button = QtWidgets.QPushButton("Load", self)

        self.spinbox = QtWidgets.QSpinBox(self)
        self.spinbox.setRange(0, 99999)
        self.spinbox.setValue(0)
        self.last_spinbox_val = 0

        self.grid = QtWidgets.QVBoxLayout()
        self.grid.addWidget(self.spinbox)
        self.grid.addWidget(self.load_button)
        self.grid.addWidget(self.co_button)
        self.grid.addWidget(self.browse_button)
        self.setLayout(self.grid)

    def show_directory_manager(self):
        manageuserdirectories.ManageUserDirectories.openManageUserDirectories()

    def disable_buttons(self):
        self.spinbox.setEnabled(False)
        self.load_button.setEnabled(False)
        self.co_button.setEnabled(False)

    def enable_buttons(self):
        self.spinbox.setEnabled(True)
        self.load_button.setEnabled(True)
        self.co_button.setEnabled(True)
        self.sig_loading_finished.emit()

    def on_load_clicked(self, slot):
        self.load_button.clicked.connect(slot)

    def unreg_on_load_clicked(self, slot):
        try:
            self.load_button.clicked.disconnect(slot)
        except TypeError:
            return

    def on_co_add_clicked(self, slot):
        self.co_button.clicked.connect(slot)

    def unreg_on_co_add_clicked(self, slot):
        try:
            self.co_button.clicked.disconnect(slot)
        except TypeError:
            return

    def on_spinbox_changed(self, slot):
        self.spinbox.valueChanged.connect(slot)

    def unreg_on_spinbox_changed(self, slot):
        try:
            self.spinbox.valueChanged.disconnect(slot)
        except TypeError:
            return

    def on_loading_finished(self, slot):
        self.sig_loading_finished.connect(slot)

    def unreg_on_loading_finished(self, slot):
        try:
            self.sig_loading_finished.disconnect(slot)
        except TypeError:
            return

    def warning(self, msg):
        message_box.warning(msg)
Пример #6
0
    try:
        logf = "crash.log"

        fh = open(logf, 'a')
        fh.write(str(msg)+'\n')
        fh.write('\n'.join(traceback.format_stack()))
        fh.close()
    except:
        print("Failed to write crash log:")
        traceback.print_exc()

    if msgType == QtCore.QtFatalMsg:
        try:
            sys.exit()
            QtCore.QCoreApplication.instance().processEvents()
        except:
            pass

if 'PyQt4' in sys.modules:
    QtCore.qInstallMsgHandler(messageHandler)
else:
    def qt5_messageHandler(msgType, context, msg):
        """ Handle error messages from Qt.

            @param QtMsType msgType: message type
            @param QMesageLogContrxt context: message context
            @param str msg: error message from Qt
        """
        messageHandler(msgType, msg)
    QtCore.qInstallMessageHandler(qt5_messageHandler)
Пример #7
0
class FFTView(QtWidgets.QWidget):
    """
    creates the layout for the FFT GUI
    """
    # signals
    buttonSignal = QtCore.Signal()
    tableClickSignal = QtCore.Signal(object, object)
    phaseCheckSignal = QtCore.Signal()

    def __init__(self, parent=None):
        super(FFTView, self).__init__(parent)
        self.grid = QtWidgets.QGridLayout(self)

        # add splitter for resizing
        splitter = QtWidgets.QSplitter(QtCore.Qt.Vertical)

        # make table
        self.FFTTable = QtWidgets.QTableWidget(self)
        self.FFTTable.resize(800, 800)
        self.FFTTable.setRowCount(6)
        self.FFTTable.setColumnCount(2)
        self.FFTTable.setColumnWidth(0, 300)
        self.FFTTable.setColumnWidth(1, 300)
        self.FFTTable.verticalHeader().setVisible(False)
        self.FFTTable.horizontalHeader().setStretchLastSection(True)
        self.FFTTable.setHorizontalHeaderLabels(
            ("FFT Property;Value").split(";"))
        # populate table
        options = ['test']

        table_utils.setRowName(self.FFTTable, 0, "Workspace")
        self.ws = table_utils.addComboToTable(self.FFTTable, 0, options)
        self.Im_box_row = 1
        table_utils.setRowName(self.FFTTable, self.Im_box_row,
                               "Imaginary Data")
        self.Im_box = table_utils.addCheckBoxToTable(self.FFTTable, True,
                                                     self.Im_box_row)

        table_utils.setRowName(self.FFTTable, 2, "Imaginary Workspace")
        self.Im_ws = table_utils.addComboToTable(self.FFTTable, 2, options)

        self.shift_box_row = 3
        table_utils.setRowName(self.FFTTable, self.shift_box_row, "Auto shift")
        self.shift_box = table_utils.addCheckBoxToTable(
            self.FFTTable, True, self.shift_box_row)

        table_utils.setRowName(self.FFTTable, 4, "Shift")
        self.shift = table_utils.addDoubleToTable(self.FFTTable, 0.0, 4)
        self.FFTTable.hideRow(4)

        table_utils.setRowName(self.FFTTable, 5, "Use Raw data")
        self.Raw_box = table_utils.addCheckBoxToTable(self.FFTTable, True, 5)

        self.FFTTable.resizeRowsToContents()
        # make advanced table options
        self.advancedLabel = QtWidgets.QLabel("\n Advanced Options")
        self.FFTTableA = QtWidgets.QTableWidget(self)
        self.FFTTableA.resize(800, 800)
        self.FFTTableA.setRowCount(4)
        self.FFTTableA.setColumnCount(2)
        self.FFTTableA.setColumnWidth(0, 300)
        self.FFTTableA.setColumnWidth(1, 300)
        self.FFTTableA.verticalHeader().setVisible(False)
        self.FFTTableA.horizontalHeader().setStretchLastSection(True)
        self.FFTTableA.setHorizontalHeaderLabels(
            ("Advanced Property;Value").split(";"))

        table_utils.setRowName(self.FFTTableA, 0, "Apodization Function")
        options = ["Lorentz", "Gaussian", "None"]
        self.apodization = table_utils.addComboToTable(self.FFTTableA, 0,
                                                       options)

        table_utils.setRowName(self.FFTTableA, 1,
                               "Decay Constant (micro seconds)")
        self.decay = table_utils.addDoubleToTable(self.FFTTableA, 4.4, 1)

        table_utils.setRowName(self.FFTTableA, 2, "Negative Padding")
        self.negativePadding = table_utils.addCheckBoxToTable(
            self.FFTTableA, True, 2)

        table_utils.setRowName(self.FFTTableA, 3, "Padding")
        self.padding = table_utils.addSpinBoxToTable(self.FFTTableA, 1, 3)
        self.FFTTableA.resizeRowsToContents()

        # make button
        self.button = QtWidgets.QPushButton('Calculate FFT', self)
        self.button.setStyleSheet("background-color:lightgrey")
        # connects
        self.FFTTable.cellClicked.connect(self.tableClick)
        self.button.clicked.connect(self.buttonClick)
        self.ws.currentIndexChanged.connect(self.phaseCheck)
        # add to layout
        self.FFTTable.setMinimumSize(40, 158)
        self.FFTTableA.setMinimumSize(40, 127)
        table_utils.setTableHeaders(self.FFTTable)
        table_utils.setTableHeaders(self.FFTTableA)

        # add to layout
        splitter.addWidget(self.FFTTable)
        splitter.addWidget(self.advancedLabel)
        splitter.addWidget(self.FFTTableA)
        self.grid.addWidget(splitter)
        self.grid.addWidget(self.button)

    def getLayout(self):
        return self.grid

    def addItems(self, options):
        self.ws.clear()
        self.ws.addItems(options)
        self.Im_ws.clear()
        self.Im_ws.addItems(options)
        self.phaseQuadChanged()

    def removeIm(self, pattern):
        index = self.Im_ws.findText(pattern)
        self.Im_ws.removeItem(index)

    def removeRe(self, pattern):
        index = self.ws.findText(pattern)
        self.ws.removeItem(index)

    # connect signals
    def phaseCheck(self):
        self.phaseCheckSignal.emit()

    def tableClick(self, row, col):
        self.tableClickSignal.emit(row, col)

    def buttonClick(self):
        self.buttonSignal.emit()

    # responses to commands
    def activateButton(self):
        self.button.setEnabled(True)

    def deactivateButton(self):
        self.button.setEnabled(False)

    def setPhaseBox(self):
        self.FFTTable.setRowHidden(8, "PhaseQuad" not in self.workspace)

    def changed(self, box, row):
        self.FFTTable.setRowHidden(row, box.checkState() == QtCore.Qt.Checked)

    def changedHideUnTick(self, box, row):
        self.FFTTable.setRowHidden(row, box.checkState() != QtCore.Qt.Checked)

    def phaseQuadChanged(self):
        # hide complex ws
        self.FFTTable.setRowHidden(2, "PhaseQuad" in self.workspace)

    def set_raw_checkbox_state(self, state):
        if state:
            self.Raw_box.setCheckState(QtCore.Qt.Checked)
        else:
            self.Raw_box.setCheckState(QtCore.Qt.Unchecked)

    def setup_raw_checkbox_changed(self, slot):
        self.FFTTable.itemChanged.connect(self.raw_checkbox_changed)
        self.signal_raw_option_changed = slot

    def raw_checkbox_changed(self, table_item):
        if table_item == self.Raw_box:
            self.signal_raw_option_changed()

    def getImBoxRow(self):
        return self.Im_box_row

    def getShiftBoxRow(self):
        return self.shift_box_row

    def getImBox(self):
        return self.Im_box

    def getShiftBox(self):
        return self.shift_box

    def warning_popup(self, message):
        warning(message, parent=self)

    @property
    def workspace(self):
        return str(self.ws.currentText())

    @workspace.setter
    def workspace(self, name):
        index = self.ws.findText(name)
        if index == -1:
            return
        self.ws.setCurrentIndex(index)

    @property
    def imaginary_workspace(self):
        return str(self.Im_ws.currentText())

    @imaginary_workspace.setter
    def imaginary_workspace(self, name):
        index = self.Im_ws.findText(name)
        if index == -1:
            return
        self.Im_ws.setCurrentIndex(index)

    @property
    def imaginary_data(self):
        return self.Im_box.checkState() == QtCore.Qt.Checked

    @imaginary_data.setter
    def imaginary_data(self, value):
        if value:
            self.Im_box.setCheckState(QtCore.Qt.Checked)
        else:
            self.Im_box.setCheckState(QtCore.Qt.Unchecked)

    @property
    def auto_shift(self):
        return self.shift_box.checkState() == QtCore.Qt.Checked

    @property
    def use_raw_data(self):
        return self.Raw_box.checkState() == QtCore.Qt.Checked

    @property
    def apodization_function(self):
        return str(self.apodization.currentText())

    @property
    def decay_constant(self):
        return float(self.decay.text())

    @property
    def negative_padding(self):
        return self.negativePadding.checkState() == QtCore.Qt.Checked

    @property
    def padding_value(self):
        return int(self.padding.text())
Пример #8
0
    def create_kline_page(self, MainWindow):
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setEnabled(True)
        self.centralwidget.setObjectName(_fromUtf8("centralwidget"))
        self.horizontalLayout = QtWidgets.QHBoxLayout(self.centralwidget)
        self.horizontalLayout.setObjectName(_fromUtf8("horizontalLayout"))
        self.gridLayout = QtWidgets.QGridLayout()
        self.gridLayout.setContentsMargins(
            -1, -1, -1, 0)  # I have no idea how layouts work
        self.gridLayout.setHorizontalSpacing(0)
        self.gridLayout.setObjectName(_fromUtf8("gridLayout"))
        self.dateEdit_2 = QtWidgets.QDateEdit(self.centralwidget)  #End Date
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed,
                                           QtWidgets.QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(
            self.dateEdit_2.sizePolicy().hasHeightForWidth())
        self.dateEdit_2.setSizePolicy(sizePolicy)
        self.dateEdit_2.setObjectName(_fromUtf8("dateEdit_2"))
        self.gridLayout.addWidget(self.dateEdit_2, 4, 1, 1, 1)
        self.label = QtWidgets.QLabel(self.centralwidget)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed,
                                           QtWidgets.QSizePolicy.Minimum)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(
            self.label.sizePolicy().hasHeightForWidth())
        self.label.setSizePolicy(sizePolicy)
        self.label.setObjectName(_fromUtf8("label"))
        self.gridLayout.addWidget(self.label, 3, 0, 1, 1)
        self.dateEdit = QtWidgets.QDateEdit(self.centralwidget)  #Start date
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed,
                                           QtWidgets.QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(
            self.dateEdit.sizePolicy().hasHeightForWidth())
        self.dateEdit.setSizePolicy(sizePolicy)
        self.dateEdit.setObjectName(_fromUtf8("dateEdit"))
        self.gridLayout.addWidget(self.dateEdit, 4, 0, 1, 1)
        self.label_2 = QtWidgets.QLabel(self.centralwidget)  #Shows "enddate"
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed,
                                           QtWidgets.QSizePolicy.Preferred)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(
            self.label_2.sizePolicy().hasHeightForWidth())
        self.label_2.setSizePolicy(sizePolicy)
        self.label_2.setObjectName(_fromUtf8("label_2"))
        self.gridLayout.addWidget(self.label_2, 3, 1, 1, 1)
        self.verticalLayout_3 = QtWidgets.QVBoxLayout()
        self.verticalLayout_3.setObjectName(_fromUtf8("verticalLayout_3"))
        self.treeWidget = QtWidgets.QTreeWidget(
            self.centralwidget)  #Displays stock according to business type
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed,
                                           QtWidgets.QSizePolicy.Minimum)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(2)
        sizePolicy.setHeightForWidth(
            self.treeWidget.sizePolicy().hasHeightForWidth())
        self.treeWidget.setSizePolicy(sizePolicy)
        self.treeWidget.setObjectName(_fromUtf8("treeWidget"))
        self.treeWidget.headerItem().setText(0, _fromUtf8("历史数据"))
        self.verticalLayout_3.addWidget(self.treeWidget)
        self.gridLayout.addLayout(self.verticalLayout_3, 1, 0, 1, 3)
        self.commandLinkButton = QtWidgets.QCommandLinkButton(
            self.centralwidget)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed,
                                           QtWidgets.QSizePolicy.Preferred)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(
            self.commandLinkButton.sizePolicy().hasHeightForWidth())
        self.commandLinkButton.setSizePolicy(sizePolicy)
        self.commandLinkButton.setText(_fromUtf8(""))
        self.commandLinkButton.setObjectName(_fromUtf8("commandLinkButton"))
        self.gridLayout.addWidget(self.commandLinkButton, 4, 2, 1, 1)
        self.comboBox = QtWidgets.QComboBox(
            self.centralwidget)  #Combobox for Selecting type of graph
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed,
                                           QtWidgets.QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(
            self.comboBox.sizePolicy().hasHeightForWidth())
        self.comboBox.setSizePolicy(sizePolicy)
        self.comboBox.setObjectName(_fromUtf8("comboBox"))
        self.gridLayout.addWidget(self.comboBox, 3, 2, 1, 1)
        self.treeWidget_2 = QtWidgets.QTreeWidget(self.centralwidget)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed,
                                           QtWidgets.QSizePolicy.Maximum)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(1)
        sizePolicy.setHeightForWidth(
            self.treeWidget_2.sizePolicy().hasHeightForWidth())
        self.treeWidget_2.setSizePolicy(
            sizePolicy)  #Shows what graphs are selected
        self.treeWidget_2.setObjectName(_fromUtf8("treeWidget_2"))
        self.treeWidget_2.headerItem().setText(0, _fromUtf8("绘图项"))
        self.gridLayout.addWidget(self.treeWidget_2, 5, 0, 1, 3)
        self.gridLayout.setColumnStretch(0, 60)
        self.gridLayout.setColumnStretch(1, 20)
        self.gridLayout.setColumnStretch(2, 20)
        self.horizontalLayout.addLayout(self.gridLayout)
        self.verticalLayout = QtWidgets.QVBoxLayout()
        self.verticalLayout.setObjectName(_fromUtf8("verticalLayout"))
        self.widget = QtWidgets.QWidget(self.centralwidget)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred,
                                           QtWidgets.QSizePolicy.Preferred)
        sizePolicy.setHorizontalStretch(1)
        sizePolicy.setVerticalStretch(1)
        sizePolicy.setHeightForWidth(
            self.widget.sizePolicy().hasHeightForWidth())
        self.widget.setSizePolicy(sizePolicy)
        self.widget.setObjectName(_fromUtf8("widget"))
        self.widget = QWebEngineView(
        )  #This is for displaying html content generated by pyecharts
        self.verticalLayout.addWidget(self.widget)
        self.horizontalLayout.addLayout(self.verticalLayout)
        self.horizontalLayout.setStretch(0, 1)
        self.horizontalLayout.setStretch(1, 15)
        MainWindow.setCentralWidget(self.centralwidget)

        self.menubar = QtWidgets.QToolBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 900, 23))
        self.menubar.setObjectName(_fromUtf8("menubar"))
        #self.menubar.setNativeMenuBar(False)
        self.combobox = QtWidgets.QComboBox()
        self.menubar.addWidget(self.combobox)
        self.combobox.addItems(["K线", "复权", "分笔数据", "历史分钟", "十大股东"])
        self.comboBox.setFixedSize(55, 40)

        MainWindow.addToolBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName(_fromUtf8("statusbar"))
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)
Пример #9
0
class ThreadUpdater(QtCore.QObject):
    """General timer that will call functions on an interval.

    DEBUG Types:

        * PRINT_ERROR ["print"]: If an error occurs print the traceback to stderr.
        * HIDE_ERROR ["hide"]: If an error occurs ignore it and do not display the error.
        * RAISE_ERROR ["raise"]: If an error occurs actually raise the error. This will crash the updater.

    Args:
         timeout (float/int)[1/30]: Interval in which to run the update functions (in seconds).
         debug_type (str)[PRINT_ERROR]: Control to manage how errors are handled.
         parent (QtCore.QObject)[None]: Parent QObject.
    """

    starting = QtCore.Signal()  # Signal to start the timer in the main thread.
    stopping = QtCore.Signal()  # Signal to stop the timer in the main thread.
    creating = QtCore.Signal(
    )  # Signal to create the timer in the main thread.

    class DebugTypes:
        PRINT_ERROR = 'print'  # Print to stderr
        HIDE_ERROR = 'hide'  # Do nothing and hide the error
        RAISE_ERROR = 'raise'  # Crash the update function and raise the error

    PRINT_ERROR = DebugTypes.PRINT_ERROR
    HIDE_ERROR = DebugTypes.HIDE_ERROR
    RAISE_ERROR = DebugTypes.RAISE_ERROR

    DEFAULT_DEBUG_TYPE = PRINT_ERROR

    def __init__(self,
                 timeout=1 / 30,
                 debug_type=None,
                 parent=None,
                 init_later=False,
                 **kwargs):
        """Initialize the ThreadUpdater.

        Args:
             timeout (float/int)[1/30]: Interval in which to run the update functions (in seconds).
             debug_type (str)[DEFAULT_DEBUG_TYPE]: Control to manage how errors are handled.
             parent (QtCore.QObject)[None]: Parent QObject.
             init_later (bool)[False]: Manually initialize this object later with `init()`.
        """
        super().__init__(parent)

        if debug_type is None:
            debug_type = self.DEFAULT_DEBUG_TYPE

        # Lock and update variables
        self._lock = threading.RLock()
        self._latest_call = OrderedDict()
        self._every_call = OrderedDict()
        self._always_call = OrderedDict()
        self._delay_call = []

        # Control variables
        self._timeout = timeout
        self.debug_type = debug_type
        self._running = False
        self._tmr = None

        # Try to initialize later so thread variables can be set as fast as possible.
        if not init_later:
            self.init()

    def init(self, *args, **kwargs):
        """Initialize here; Try to make creating the object in __init__ as fast as possible and with little complexity.

        This is to
        reduce the chance that two threads create the global MAIN_UPDATER at the same time. Yes, I've seen this and it
        was problematic.
        """
        # Move to main thread before connecting the signals, so signals run in the main thread
        if not is_main_thread():
            self.moveToThread(QtWidgets.QApplication.instance().thread())

        # Connect the signals
        self.starting.connect(self.start)
        self.stopping.connect(self.stop)
        self.creating.connect(self.create_timer)

        # Create the timer
        self.create_timer()
        return self

    @contextlib.contextmanager
    def handle_error(self, func=None):
        """Context manager to handle exceptions if the unknown update functions cause an error.

        Change how the errors are handled with the "debug_type" variable.
          * PRINT_ERROR ["print"]: If an error occurs print the traceback to stderr.
          * HIDE_ERROR ["hide"]: If an error occurs ignore it and do not display the error.
          * RAISE_ERROR ["raise"]: If an error occurs actually raise the error. This will crash the updater.
        """
        if self.debug_type == self.RAISE_ERROR:
            yield  # If this errors it will crash the updater and raise teh real error.
        elif self.debug_type == self.HIDE_ERROR:
            try:
                yield
            except Exception:
                pass
        else:  # self.debug_type == self.PRINT_ERROR:
            try:
                yield
            except Exception:
                traceback.print_exc()
                print('Error in {}'.format(func.__name__), file=sys.stderr)

    @contextlib.contextmanager
    def restart_on_change(self, restart=None):
        """Context manager to stop and restart the timer if it is running."""
        if restart is None:
            restart = self.is_running()
        if restart:
            self.stop()
            yield
            self.start()
        else:
            yield

    def get_timeout(self):
        """Return the update timer interval in seconds."""
        return self._timeout

    def set_timeout(self, value):
        """Set the update timer interval in seconds."""
        with self.restart_on_change(self.is_running()):
            self._timeout = value
            try:
                self._tmr.setInterval(int(self.get_timeout() * 1000))
            except (AttributeError, RuntimeError, Exception):
                pass

    def create_timer(self):
        """Actually create the timer."""
        # Check to run this function in the main thread.
        if not is_main_thread():
            self.creating.emit()
            return

        self.stop(set_state=False)

        self._tmr = QtCore.QTimer()
        self._tmr.setSingleShot(False)
        self._tmr.setInterval(int(self.get_timeout() * 1000))
        self._tmr.timeout.connect(self.run_update)

    def is_running(self):
        """Return if running."""
        return self._running

    def stop(self, set_state=True):
        """Stop the updater timer."""
        # Check to run this function in the main thread.
        if not is_main_thread():
            self.stopping.emit()
            return

        try:
            self._tmr.stop()
        except:
            pass
        if set_state:
            self._running = False

    def start(self):
        """Start the updater timer."""
        # Check to run this function in the main thread.
        if not is_main_thread():
            self.starting.emit()
            return

        self.stop(set_state=False)
        self._running = True
        if self._tmr is None:
            self.create_timer()  # Should be in main thread
        self._tmr.start()

    def ensure_running(self):
        """If the updater is not running send a safe signal to start it."""
        if not self.is_running():
            self._running = True
            self.starting.emit()

    def register_continuous(self, func, *args, **kwargs):
        """Register a function to be called on every update continuously."""
        with self._lock:
            self._always_call[func] = (args, kwargs)
        self.ensure_running()

    def unregister_continuous(self, func):
        """Unregister a function to be called on every update continuously."""
        with self._lock:
            try:
                self._always_call.pop(func, None)
            except:
                pass

    def call_latest(self, func, *args, **kwargs):
        """Call the most recent values for this function in the main thread on the next update call."""
        with self._lock:
            self._latest_call[func] = (args, kwargs)
        self.ensure_running()

    def now_call_latest(self, func, *args, **kwargs):
        """Call the latest value in the main thread. If this is the main thread call now."""
        if is_main_thread():
            func(*args, **kwargs)
        else:
            self.call_latest(func, *args, **kwargs)

    def call_in_main(self, func, *args, **kwargs):
        """Call this function in the main thread on the next update call."""
        with self._lock:
            try:
                self._every_call[func].append((args, kwargs))
            except (KeyError, IndexError, Exception):
                self._every_call[func] = [(args, kwargs)]
        self.ensure_running()

    def now_call_in_main(self, func, *args, **kwargs):
        """Call in the main thread. If this is the main thread call now."""
        if is_main_thread():
            func(*args, **kwargs)
        else:
            self.call_in_main(func, *args, **kwargs)

    def delay(self, seconds, func, *args, **kwargs):
        """Call the given function after the given number of seconds has passed.

        This will not be accurate unless your timeout is at a high rate (lower timeout number).

        Args:
            seconds (float/int): Number of seconds to wait until calling the function.
            func (callable): Function to call.
            *args (tuple): Positional arguments to pass into the function.
            **kwargs (dict): Keyword arguments to pass into the function.
        """
        now = time.time()  # Note: this is before the lock
        with self._lock:
            self._delay_call.append(
                DelayedFunc(now, seconds, func, args, kwargs))
        self.ensure_running()

    def run_update(self):
        """Run the stored function calls to update the GUI items in the main thread.

        This function should not be called directly. Call `ThreadUpdater.start()` to run this function on a timer in
        the main thread.
        """
        # Collect the items using the thread safe lock
        with self._lock:
            always = self._always_call.copy()
            latest, self._latest_call = self._latest_call, OrderedDict()
            main, self._every_call = self._every_call, OrderedDict()
            delayed = [
                self._delay_call.pop(i)
                for i in reversed(range(len(self._delay_call)))
                if self._delay_call[i].can_run()
            ]

        # Start running the functions
        for delayed_func in delayed:
            with self.handle_error(delayed_func.func):
                delayed_func.func(*delayed_func.args, **delayed_func.kwargs)

        for func, (args, kwargs) in always.items():
            with self.handle_error(func):
                func(*args, **kwargs)

        for func, (args, kwargs) in latest.items():
            with self.handle_error(func):
                func(*args, **kwargs)

        for func, li in main.items():
            for args, kwargs in li:
                with self.handle_error(func):
                    func(*args, **kwargs)
Пример #10
0
class VnaSelector(QtWidgets.QWidget):
    enableStateToggled = QtCore.Signal(bool)

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

        # --- Setup UI Elements --- #
        self.verticalLayout = QtWidgets.QVBoxLayout(
            self)  # primary widget layout
        self.verticalLayout.setContentsMargins(
            0, 0, 0,
            0)  # normally this will be embedded in another application

        self.checkBox_SweepNew = QtWidgets.QCheckBox("Sweep New", self)
        self.checkBox_SweepNew.setLayoutDirection(QtCore.Qt.RightToLeft)
        self.checkBox_RawData = QtWidgets.QCheckBox("Raw Data", self)
        self.checkBox_RawData.setLayoutDirection(QtCore.Qt.RightToLeft)
        self.label_ports = QtWidgets.QLabel("ports 1,2:")
        self.spinBox_port1 = QtWidgets.QSpinBox(self)
        self.spinBox_port2 = QtWidgets.QSpinBox(self)
        for port in (self.spinBox_port1, self.spinBox_port2):
            port.setMinimum(1)
            port.setMaximum(2)
        self.spinBox_port1.setValue(1)
        self.spinBox_port2.setValue(2)

        self.label_analyzerList = QtWidgets.QLabel("Select Analyzer", self)
        self.comboBox_analyzer = QtWidgets.QComboBox(self)
        self.hlay_analyzerList = QtWidgets.QHBoxLayout()
        self.hlay_analyzerList.addWidget(self.label_analyzerList)
        self.hlay_analyzerList.addWidget(self.comboBox_analyzer)

        self.label_visaString = QtWidgets.QLabel("Visa String", self)
        self.lineEdit_visaString = QtWidgets.QLineEdit(self)

        self.row1 = QtWidgets.QHBoxLayout()
        self.row1.addLayout(self.hlay_analyzerList)
        self.row1.addWidget(self.label_visaString)
        self.row1.addWidget(self.lineEdit_visaString)
        self.row1.insertStretch(-1)

        self.label_channel = QtWidgets.QLabel("Channel:")
        self.spinBox_channel = QtWidgets.QSpinBox()
        self.spinBox_channel.setMinimum(1)
        self.spinBox_channel.setMaximum(256)

        self.btn_controlVna = QtWidgets.QPushButton("Set VNA State")

        self.row2 = QtWidgets.QHBoxLayout()
        self.row2.addWidget(self.label_channel)
        self.row2.addWidget(self.spinBox_channel)
        self.row2.addWidget(qt.QVLine())
        self.row2.addWidget(self.checkBox_SweepNew)
        self.row2.addWidget(qt.QVLine())
        self.row2.addWidget(self.checkBox_RawData)
        self.row2.addWidget(qt.QVLine())
        self.row2.addWidget(self.label_ports)
        self.row2.addWidget(self.spinBox_port1)
        self.row2.addWidget(self.spinBox_port2)
        self.row2.addWidget(qt.QVLine())
        self.row2.addWidget(self.btn_controlVna)
        self.row2.insertStretch(-1)

        self.verticalLayout.addLayout(self.row1)
        self.verticalLayout.addLayout(self.row2)

        self.comboBox_analyzer.currentIndexChanged.connect(
            self.update_selected_analyzer)
        for key, val in analyzers.items():
            self.comboBox_analyzer.addItem(key)
        # --- End Setup UI Elements --- #

        self.btn_controlVna.clicked.connect(self.control_vna)
        self.btn_controlVna.setEnabled(False)

    def setEnabled(self, enabled):
        super(VnaSelector, self).setEnabled(enabled)
        self.enableStateToggled.emit(enabled)

    def update_selected_analyzer(self):
        cls = analyzers[self.comboBox_analyzer.currentText()]
        self.lineEdit_visaString.setText(cls.DEFAULT_VISA_ADDRESS)
        self.spinBox_port2.setMaximum(cls.NPORTS)
        self.spinBox_channel.setMaximum(cls.NCHANNELS)

    def get_analyzer(self):
        nwa = analyzers[self.comboBox_analyzer.currentText()](
            self.lineEdit_visaString.text())
        nwa.set_measurement_parameters(port1=self.port1,
                                       port2=self.port2,
                                       sweep=self.sweep_new,
                                       channel=self.channel,
                                       raw_data=self.raw_data)
        return nwa

    @property
    def port1(self):
        return self.spinBox_port1.value()

    @port1.setter
    def port1(self, val):
        self.spinBox_port1.setValue(val)

    @property
    def port2(self):
        return self.spinBox_port2.value()

    @port2.setter
    def port2(self, val):
        self.spinBox_port2.setValue(val)

    @property
    def sweep_new(self):
        return self.checkBox_SweepNew.isChecked()

    @sweep_new.setter
    def sweep_new(self, val):
        self.checkBox_SweepNew.setChecked(val)

    @property
    def raw_data(self):
        return self.checkBox_RawData.isChecked()

    @raw_data.setter
    def raw_data(self, val):
        self.checkBox_RawData.setChecked(val)

    @property
    def channel(self):
        return self.spinBox_channel.value()

    @channel.setter
    def channel(self, val):
        self.spinBox_channel.setValue(val)

    def control_vna(self):
        qt.warnMissingFeature()
Пример #11
0
class PrePostTask(QtCore.QObject, Fysom, metaclass=TaskMetaclass):
    """ Represents a task that creates the necessary conditions for a different task
        and reverses its own actions afterwards.
    """

    sigPreExecStart = QtCore.Signal()
    sigPreExecFinish = QtCore.Signal()
    sigPostExecStart = QtCore.Signal()
    sigPostExecFinish = QtCore.Signal()
    sigStateChanged = QtCore.Signal(object)

    requiredModules = []

    def __init__(self, name, runner, references, config, **kwargs):
        """ Create a PrePostTask.
          @param str name: unique name of the task
          @param object runner: TaskRunner that manages this task
          @param dict references: contains references to all required modules
          @param dict config: configuration parameter dictionary
        """
        _default_callbacks = {'onprerun': self._pre, 'onpostrun': self._post}
        _stateList = {
            'initial':
            'stopped',
            'events': [{
                'name': 'prerun',
                'src': 'stopped',
                'dst': 'paused'
            }, {
                'name': 'postrun',
                'src': 'paused',
                'dst': 'stopped'
            }],
            'callbacks':
            _default_callbacks
        }
        if 'PyQt5' in sys.modules:
            super().__init__(cfg=_stateList, **kwargs)
        else:
            QtCore.QObject.__init__(self)
            Fysom.__init__(self, _stateList)
        self.lock = Mutex()
        self.name = name
        self.runner = runner
        self.ref = references
        self.config = config

    @property
    def log(self):
        """
        Returns a logger object
        """
        return logging.getLogger("{0}.{1}".format(self.__module__,
                                                  self.__class__.__name__))

    def onchangestate(self, e):
        """ Fysom callback for all state transitions.
          @param object e: Fysom state transition description

          This just emits a signal so external components can react.
        """
        self.sigStateChanged.emit(e)

    @abc.abstractmethod
    def preExecute(self):
        """ This method contains any action that should be done before some task.
            It needs to be overwritten in every subclass.
        """
        pass

    @abc.abstractmethod
    def postExecute(self):
        """ This method needs to undo any actions in preExecute() after a task has been finished.
            It needs to be overwritten in every subclass.
        """
        pass

    def _pre(self, e):
        """ Actually call preExecute with the appropriate safeguards amd emit singals before and afterwards.

          @param object e: Fysom state transition description
        """
        self.sigPreExecStart.emit()
        try:
            self.preExecute()
        except Exception as e:
            self.log.exception('Exception during task {0}. {1}'.format(
                self.name, e))

        self.sigPreExecFinish.emit()

    def _post(self, e):
        """ Actually call postExecute with the appropriate safeguards amd emit singals before and afterwards.

          @param object e: Fysom state transition description
        """
        self.sigPostExecStart.emit()
        try:
            self.postExecute()
        except Exception as e:
            self.log.exception('Exception during task {0}. {1}'.format(
                self.name, e))

        self.sigPostExecFinish.emit()
Пример #12
0
class QudiKernelLogic(GenericLogic):
    """ Logic module providing a Jupyer-compatible kernel connected via ZMQ."""
    _modclass = 'QudiKernelLogic'
    _modtype = 'logic'
    _out = {'kernel': 'QudiKernelLogic'}

    sigStartKernel = QtCore.Signal(str)
    sigStopKernel = QtCore.Signal(int)

    def __init__(self, **kwargs):
        """ Create logic object
          @param dict kwargs: additional parameters as a dict
        """
        super().__init__(**kwargs)
        self.kernellist = dict()
        self.modules = set()

    def on_activate(self, e):
        """ Prepare logic module for work.

          @param object e: Fysom state change notification
        """
        logging.basicConfig(format='%(asctime)s %(levelname)s: %(message)s',
                            datefmt='%Y-%m-%d %I:%M:%S %p',
                            level=logging.DEBUG)

        self.kernellist = dict()
        self.modules = set()
        self._manager.sigModulesChanged.connect(self.updateModuleList)
        self.sigStartKernel.connect(self.updateModuleList,
                                    QtCore.Qt.QueuedConnection)

    def on_deactivate(self, e):
        """ Deactivate module.

          @param object e: Fysom state change notification
        """
        while len(self.kernellist) > 0:
            self.stopKernel(tuple(self.kernellist.keys())[0])
            QtCore.QCoreApplication.processEvents()
            time.sleep(0.05)

    def startKernel(self, config, external=None):
        """Start a qudi inprocess jupyter kernel.
          @param dict config: connection information for kernel
          @param callable external: function to call on exit of kernel

          @return str: uuid of the started kernel
        """
        realconfig = netobtain(config)
        self.log.info('Start {0}'.format(realconfig))
        mythread = self.getModuleThread()
        kernel = QZMQKernel(realconfig)
        kernel.moveToThread(mythread)
        kernel.user_global_ns.update({
            'pg': pg,
            'np': np,
            'config': self._manager.tree['defined'],
            'manager': self._manager
        })
        kernel.sigShutdownFinished.connect(self.cleanupKernel)
        self.log.info('Kernel is {0}'.format(kernel.engine_id))
        QtCore.QMetaObject.invokeMethod(kernel, 'connect_kernel')
        self.kernellist[kernel.engine_id] = kernel
        self.log.info('Finished starting Kernel {0}'.format(kernel.engine_id))
        self.sigStartKernel.emit(kernel.engine_id)
        return kernel.engine_id

    def stopKernel(self, kernelid):
        """Tell kernel to close all sockets and stop hearteat thread.
          @param str kernelid: uuid of kernel to be stopped
        """
        realkernelid = netobtain(kernelid)
        self.log.info('Stopping {0}'.format(realkernelid))
        kernel = self.kernellist[realkernelid]
        QtCore.QMetaObject.invokeMethod(kernel, 'shutdown')

    def cleanupKernel(self, kernelid, external=None):
        """Remove kernel reference and tell rpyc client for that kernel to exit.

          @param str kernelid: uuid of kernel reference to remove
          @param callable external: reference to rpyc client exit function
        """
        self.log.info('Cleanup kernel {0}'.format(kernelid))
        del self.kernellist[kernelid]
        if external is not None:
            try:
                external.exit()
            except:
                self.log.warning('External qudikernel starter did not exit')

    def updateModuleList(self):
        """Remove non-existing modules from namespace,
            add new modules to namespace, update reloaded modules
        """
        currentModules = set()
        newNamespace = dict()
        for base in ['hardware', 'logic', 'gui']:
            for module in self._manager.tree['loaded'][base]:
                currentModules.add(module)
                newNamespace[module] = self._manager.tree['loaded'][base][
                    module]
        discard = self.modules - currentModules
        for kernel in self.kernellist:
            self.kernellist[kernel].user_global_ns.update(newNamespace)
        for module in discard:
            for kernel in self.kernellist:
                self.kernellist[kernel].user_global_ns.pop(module, None)
        self.modules = currentModules
Пример #13
0
class FFTView(QtWidgets.QWidget):
    """
    creates the layout for the FFT GUI
    """
    # signals
    buttonSignal = QtCore.Signal()
    tableClickSignal = QtCore.Signal(object, object)
    phaseCheckSignal = QtCore.Signal()

    def __init__(self, parent=None):
        super(FFTView, self).__init__(parent)
        self.grid = QtWidgets.QGridLayout(self)

        # add splitter for resizing
        splitter = QtWidgets.QSplitter(QtCore.Qt.Vertical)

        # make table
        self.FFTTable = QtWidgets.QTableWidget(self)
        self.FFTTable.resize(800, 800)
        self.FFTTable.setRowCount(9)
        self.FFTTable.setColumnCount(2)
        self.FFTTable.setColumnWidth(0, 300)
        self.FFTTable.setColumnWidth(1, 300)
        self.FFTTable.verticalHeader().setVisible(False)
        self.FFTTable.horizontalHeader().setStretchLastSection(True)
        self.FFTTable.setHorizontalHeaderLabels(
            ("FFT Property;Value").split(";"))
        # populate table
        options = ['test']

        table_utils.setRowName(self.FFTTable, 0, "Workspace")
        self.ws = table_utils.addComboToTable(self.FFTTable, 0, options)
        self.Im_box_row = 1
        table_utils.setRowName(self.FFTTable, self.Im_box_row,
                               "Imaginary Data")
        self.Im_box = table_utils.addCheckBoxToTable(self.FFTTable, True,
                                                     self.Im_box_row)

        table_utils.setRowName(self.FFTTable, 2, "Imaginary Workspace")
        self.Im_ws = table_utils.addComboToTable(self.FFTTable, 2, options)

        self.shift_box_row = 3
        table_utils.setRowName(self.FFTTable, self.shift_box_row, "Auto shift")
        self.shift_box = table_utils.addCheckBoxToTable(
            self.FFTTable, True, self.shift_box_row)

        table_utils.setRowName(self.FFTTable, 4, "Shift")
        self.shift = table_utils.addDoubleToTable(self.FFTTable, 0.0, 4)
        self.FFTTable.hideRow(4)

        table_utils.setRowName(self.FFTTable, 5, "Use Raw data")
        self.Raw_box = table_utils.addCheckBoxToTable(self.FFTTable, True, 5)

        table_utils.setRowName(self.FFTTable, 6, "First Good Data")
        self.x0 = table_utils.addDoubleToTable(self.FFTTable, 0.1, 6)
        self.FFTTable.hideRow(6)

        table_utils.setRowName(self.FFTTable, 7, "Last Good Data")
        self.xN = table_utils.addDoubleToTable(self.FFTTable, 15.0, 7)
        self.FFTTable.hideRow(7)

        table_utils.setRowName(self.FFTTable, 8, "Construct Phase Table")
        self.phaseTable_box = table_utils.addCheckBoxToTable(
            self.FFTTable, True, 8)
        self.FFTTable.hideRow(8)

        self.FFTTable.resizeRowsToContents()
        # make advanced table options
        self.advancedLabel = QtWidgets.QLabel("\n Advanced Options")
        self.FFTTableA = QtWidgets.QTableWidget(self)
        self.FFTTableA.resize(800, 800)
        self.FFTTableA.setRowCount(4)
        self.FFTTableA.setColumnCount(2)
        self.FFTTableA.setColumnWidth(0, 300)
        self.FFTTableA.setColumnWidth(1, 300)
        self.FFTTableA.verticalHeader().setVisible(False)
        self.FFTTableA.horizontalHeader().setStretchLastSection(True)
        self.FFTTableA.setHorizontalHeaderLabels(
            ("Advanced Property;Value").split(";"))

        table_utils.setRowName(self.FFTTableA, 0, "Apodization Function")
        options = ["Lorentz", "Gaussian", "None"]
        self.apodization = table_utils.addComboToTable(self.FFTTableA, 0,
                                                       options)

        table_utils.setRowName(self.FFTTableA, 1,
                               "Decay Constant (micro seconds)")
        self.decay = table_utils.addDoubleToTable(self.FFTTableA, 4.4, 1)

        table_utils.setRowName(self.FFTTableA, 2, "Negative Padding")
        self.negativePadding = table_utils.addCheckBoxToTable(
            self.FFTTableA, True, 2)

        table_utils.setRowName(self.FFTTableA, 3, "Padding")
        self.padding = table_utils.addSpinBoxToTable(self.FFTTableA, 1, 3)
        self.FFTTableA.resizeRowsToContents()

        # make button
        self.button = QtWidgets.QPushButton('Calculate FFT', self)
        self.button.setStyleSheet("background-color:lightgrey")
        # connects
        self.FFTTable.cellClicked.connect(self.tableClick)
        self.button.clicked.connect(self.buttonClick)
        self.ws.currentIndexChanged.connect(self.phaseCheck)
        # add to layout
        self.FFTTable.setMinimumSize(40, 158)
        self.FFTTableA.setMinimumSize(40, 127)
        table_utils.setTableHeaders(self.FFTTable)
        table_utils.setTableHeaders(self.FFTTableA)

        # add to layout
        splitter.addWidget(self.FFTTable)
        splitter.addWidget(self.advancedLabel)
        splitter.addWidget(self.FFTTableA)
        self.grid.addWidget(splitter)
        self.grid.addWidget(self.button)

    def getLayout(self):
        return self.grid

# add data to view

    def addItems(self, options):
        self.ws.clear()
        self.ws.addItems(options)
        self.ws.addItem("PhaseQuad")
        self.Im_ws.clear()
        self.Im_ws.addItems(options)
        self.phaseQuadChanged()

    def removeIm(self, pattern):
        index = self.Im_ws.findText(pattern)
        self.Im_ws.removeItem(index)

    def removeRe(self, pattern):
        index = self.ws.findText(pattern)
        self.ws.removeItem(index)

    def setReTo(self, name):
        index = self.ws.findText(name)
        if index == -1:
            return
        self.ws.setCurrentIndex(index)

    def setImTo(self, name):
        index = self.Im_ws.findText(name)
        if index == -1:
            return
        self.Im_ws.setCurrentIndex(index)

    # connect signals
    def phaseCheck(self):
        self.phaseCheckSignal.emit()

    def tableClick(self, row, col):
        self.tableClickSignal.emit(row, col)

    def buttonClick(self):
        self.buttonSignal.emit()

    def getInputWS(self):
        return self.ws.currentText()

    def getInputImWS(self):
        return self.Im_ws.currentText()

    # responses to commands
    def activateButton(self):
        self.button.setEnabled(True)

    def deactivateButton(self):
        self.button.setEnabled(False)

    def setPhaseBox(self):
        self.FFTTable.setRowHidden(8, "PhaseQuad" not in self.getWS())

    def changed(self, box, row):
        self.FFTTable.setRowHidden(row, box.checkState() == QtCore.Qt.Checked)

    def changedHideUnTick(self, box, row):
        self.FFTTable.setRowHidden(row, box.checkState() != QtCore.Qt.Checked)

    def phaseQuadChanged(self):
        # show axis
        self.FFTTable.setRowHidden(6, "PhaseQuad" not in self.getWS())
        self.FFTTable.setRowHidden(7, "PhaseQuad" not in self.getWS())
        # hide complex ws
        self.FFTTable.setRowHidden(2, "PhaseQuad" in self.getWS())

    # these are for getting inputs
    def getRunName(self):
        if mantid.AnalysisDataService.doesExist("MuonAnalysis_1"):
            tmpWS = mantid.AnalysisDataService.retrieve("MuonAnalysis_1")
        else:
            tmpWS = mantid.AnalysisDataService.retrieve("MuonAnalysis")
        return tmpWS.getInstrument().getName() + str(
            tmpWS.getRunNumber()).zfill(8)

    def initFFTInput(self, run=None):
        inputs = {}
        inputs['InputWorkspace'] = "__ReTmp__"  #
        inputs['Real'] = 0  # always zero
        out = str(self.ws.currentText()).replace(";", "; ")
        if run is None:
            run = self.getRunName()
        inputs['OutputWorkspace'] = run + ";" + out + ";FFT"
        inputs["AcceptXRoundingErrors"] = True
        return inputs

    def addFFTComplex(self, inputs):
        inputs["InputImagWorkspace"] = "__ImTmp__"
        inputs["Imaginary"] = 0  # always zero

    def addFFTShift(self, inputs):
        inputs['AutoShift'] = False
        inputs['Shift'] = float(self.shift.text())

    def addRaw(self, inputs, key):
        inputs[key] += "_Raw"

    def getFFTRePhase(self, inputs):
        inputs['InputWorkspace'] = "__ReTmp__"
        inputs['Real'] = 0  # always zero

    def getFFTImPhase(self, inputs):
        inputs['InputImagWorkspace'] = "__ReTmp__"
        inputs['Imaginary'] = 1

    def initAdvanced(self):
        inputs = {}
        inputs["ApodizationFunction"] = str(self.apodization.currentText())
        inputs["DecayConstant"] = float(self.decay.text())
        inputs["NegativePadding"] = self.negativePadding.checkState()
        inputs["Padding"] = int(self.padding.text())
        return inputs

    def ReAdvanced(self, inputs):
        inputs['InputWorkspace'] = str(self.ws.currentText()).replace(
            ";", "; ")
        inputs['OutputWorkspace'] = "__ReTmp__"

    def ImAdvanced(self, inputs):
        inputs['InputWorkspace'] = str(self.Im_ws.currentText()).replace(
            ";", "; ")
        inputs['OutputWorkspace'] = "__ImTmp__"

    def RePhaseAdvanced(self, inputs):
        inputs['InputWorkspace'] = "__phaseQuad__"
        inputs['OutputWorkspace'] = "__ReTmp__"

    # get methods (from the GUI)
    def getWS(self):
        return str(self.ws.currentText()).replace(";", "; ")

    def isAutoShift(self):
        return self.shift_box.checkState() == QtCore.Qt.Checked

    def isComplex(self):
        return self.Im_box.checkState() == QtCore.Qt.Checked

    def isRaw(self):
        return self.Raw_box.checkState() == QtCore.Qt.Checked

    def set_raw_checkbox_state(self, state):
        if state:
            self.Raw_box.setCheckState(QtCore.Qt.Checked)
        else:
            self.Raw_box.setCheckState(QtCore.Qt.Unchecked)

    def setup_raw_checkbox_changed(self, slot):
        self.FFTTable.itemChanged.connect(self.raw_checkbox_changed)
        self.signal_raw_option_changed = slot

    def raw_checkbox_changed(self, table_item):
        if table_item == self.Raw_box:
            self.signal_raw_option_changed()

    def getImBoxRow(self):
        return self.Im_box_row

    def getShiftBoxRow(self):
        return self.shift_box_row

    def getImBox(self):
        return self.Im_box

    def getShiftBox(self):
        return self.shift_box

    def getFirstGoodData(self):
        return float(self.x0.text())

    def getLastGoodData(self):
        return (self.xN.text())

    def isNewPhaseTable(self):
        return self.phaseTable_box.checkState() == QtCore.Qt.Checked

    def isPhaseBoxShown(self):
        return self.FFTTable.isRowHidden(8)

    def warning_popup(self, message):
        warning(message, parent=self)
Пример #14
0
    def __init__(self, config=None, filename=None, output=None):
        # see labelme/config/default_config.yaml for valid configuration
        if config is None:
            config = get_config()
        self._config = config

        super(MainWindow, self).__init__()
        self.setWindowTitle(__appname__)

        # Whether we need to save or not.
        self.dirty = False

        self._noSelectionSlot = False

        # Main widgets and related state.
        self.labelDialog = LabelDialog(
            parent=self,
            labels=self._config['labels'],
            sort_labels=self._config['sort_labels'],
            show_text_field=self._config['show_label_text_field'],
        )

        self.labelList = LabelQListWidget()
        self.lastOpenDir = None

        self.labelList.itemActivated.connect(self.labelSelectionChanged)
        self.labelList.itemSelectionChanged.connect(self.labelSelectionChanged)
        self.labelList.itemDoubleClicked.connect(self.editLabel)
        # Connect to itemChanged to detect checkbox changes.
        self.labelList.itemChanged.connect(self.labelItemChanged)
        self.labelList.setDragDropMode(
            QtWidgets.QAbstractItemView.InternalMove)
        self.labelList.setParent(self)

        listLayout = QtWidgets.QVBoxLayout()
        listLayout.setContentsMargins(0, 0, 0, 0)
        self.editButton = QtWidgets.QToolButton()
        self.editButton.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
        listLayout.addWidget(self.editButton)  # 0, Qt.AlignCenter)
        listLayout.addWidget(self.labelList)
        self.labelListContainer = QtWidgets.QWidget()
        self.labelListContainer.setLayout(listLayout)

        self.flag_dock = self.flag_widget = None
        self.flag_dock = QtWidgets.QDockWidget('Flags', self)
        self.flag_dock.setObjectName('Flags')
        self.flag_widget = QtWidgets.QListWidget()
        if config['flags']:
            self.loadFlags({k: False for k in config['flags']})
        self.flag_dock.setWidget(self.flag_widget)
        self.flag_widget.itemChanged.connect(self.setDirty)

        self.uniqLabelList = EscapableQListWidget()
        self.uniqLabelList.setToolTip(
            "Select label to start annotating for it. "
            "Press 'Esc' to deselect.")
        if self._config['labels']:
            self.uniqLabelList.addItems(self._config['labels'])
            self.uniqLabelList.sortItems()
        self.labelsdock = QtWidgets.QDockWidget(u'Label List', self)
        self.labelsdock.setObjectName(u'Label List')
        self.labelsdock.setWidget(self.uniqLabelList)

        self.dock = QtWidgets.QDockWidget('Polygon Labels', self)
        self.dock.setObjectName('Labels')
        self.dock.setWidget(self.labelListContainer)

        self.fileListWidget = QtWidgets.QListWidget()
        self.fileListWidget.itemSelectionChanged.connect(
            self.fileSelectionChanged)
        filelistLayout = QtWidgets.QVBoxLayout()
        filelistLayout.setContentsMargins(0, 0, 0, 0)
        filelistLayout.addWidget(self.fileListWidget)
        fileListContainer = QtWidgets.QWidget()
        fileListContainer.setLayout(filelistLayout)
        self.filedock = QtWidgets.QDockWidget(u'File List', self)
        self.filedock.setObjectName(u'Files')
        self.filedock.setWidget(fileListContainer)

        self.zoomWidget = ZoomWidget()
        self.colorDialog = ColorDialog(parent=self)

        self.canvas = self.labelList.canvas = Canvas(
            epsilon=self._config['epsilon'],
        )
        self.canvas.zoomRequest.connect(self.zoomRequest)

        scrollArea = QtWidgets.QScrollArea()
        scrollArea.setWidget(self.canvas)
        scrollArea.setWidgetResizable(True)
        self.scrollBars = {
            Qt.Vertical: scrollArea.verticalScrollBar(),
            Qt.Horizontal: scrollArea.horizontalScrollBar(),
        }
        self.canvas.scrollRequest.connect(self.scrollRequest)

        self.canvas.newShape.connect(self.newShape)
        self.canvas.shapeMoved.connect(self.setDirty)
        self.canvas.selectionChanged.connect(self.shapeSelectionChanged)
        self.canvas.drawingPolygon.connect(self.toggleDrawingSensitive)

        self.setCentralWidget(scrollArea)

        self.addDockWidget(Qt.RightDockWidgetArea, self.flag_dock)
        self.addDockWidget(Qt.RightDockWidgetArea, self.labelsdock)
        self.addDockWidget(Qt.RightDockWidgetArea, self.dock)
        self.addDockWidget(Qt.RightDockWidgetArea, self.filedock)

        # Actions
        action = functools.partial(newAction, self)
        shortcuts = self._config['shortcuts']
        quit = action('&Quit', self.close, shortcuts['quit'], 'quit',
                      'Quit application')
        open_ = action('&Open', self.openFile, shortcuts['open'], 'open',
                       'Open image or label file')
        opendir = action('&Open Dir', self.openDirDialog,
                         shortcuts['open_dir'], 'open', u'Open Dir')
        openNextImg = action('&Next Image', self.openNextImg,
                             shortcuts['open_next'], 'next', u'Open Next')

        openPrevImg = action('&Prev Image', self.openPrevImg,
                             shortcuts['open_prev'], 'prev', u'Open Prev')
        save = action('&Save', self.saveFile, shortcuts['save'], 'save',
                      'Save labels to file', enabled=False)
        saveAs = action('&Save As', self.saveFileAs, shortcuts['save_as'],
                        'save-as', 'Save labels to a different file',
                        enabled=False)
        close = action('&Close', self.closeFile, shortcuts['close'], 'close',
                       'Close current file')
        color1 = action('Polygon &Line Color', self.chooseColor1,
                        shortcuts['edit_line_color'], 'color_line',
                        'Choose polygon line color')
        color2 = action('Polygon &Fill Color', self.chooseColor2,
                        shortcuts['edit_fill_color'], 'color',
                        'Choose polygon fill color')

        createMode = action(
            'Create Polygons',
            lambda: self.toggleDrawMode(False, createMode='polygon'),
            shortcuts['create_polygon'],
            'objects',
            'Start drawing polygons',
            enabled=True,
        )
        createRectangleMode = action(
            'Create Rectangle',
            lambda: self.toggleDrawMode(False, createMode='rectangle'),
            shortcuts['create_rectangle'],
            'objects',
            'Start drawing rectangles',
            enabled=True,
        )
        createLineMode = action(
            'Create Line',
            lambda: self.toggleDrawMode(False, createMode='line'),
            shortcuts['create_line'],
            'objects',
            'Start drawing lines',
            enabled=True,
        )
        createPointMode = action(
            'Create Point',
            lambda: self.toggleDrawMode(False, createMode='point'),
            shortcuts['create_point'],
            'objects',
            'Start drawing points',
            enabled=True,
        )
        editMode = action('Edit Polygons', self.setEditMode,
                          shortcuts['edit_polygon'], 'edit',
                          'Move and edit polygons', enabled=True)

        delete = action('Delete Polygon', self.deleteSelectedShape,
                        shortcuts['delete_polygon'], 'cancel',
                        'Delete', enabled=False)
        copy = action('Duplicate Polygon', self.copySelectedShape,
                      shortcuts['duplicate_polygon'], 'copy',
                      'Create a duplicate of the selected polygon',
                      enabled=False)
        undoLastPoint = action('Undo last point', self.canvas.undoLastPoint,
                               shortcuts['undo_last_point'], 'undo',
                               'Undo last drawn point', enabled=False)
        addPoint = action('Add Point to Edge', self.canvas.addPointToEdge,
                          None, 'edit', 'Add point to the nearest edge',
                          enabled=False)

        undo = action('Undo', self.undoShapeEdit, shortcuts['undo'], 'undo',
                      'Undo last add and edit of shape', enabled=False)

        hideAll = action('&Hide\nPolygons',
                         functools.partial(self.togglePolygons, False),
                         icon='eye', tip='Hide all polygons', enabled=False)
        showAll = action('&Show\nPolygons',
                         functools.partial(self.togglePolygons, True),
                         icon='eye', tip='Show all polygons', enabled=False)

        help = action('&Tutorial', self.tutorial, icon='help',
                      tip='Show tutorial page')

        zoom = QtWidgets.QWidgetAction(self)
        zoom.setDefaultWidget(self.zoomWidget)
        self.zoomWidget.setWhatsThis(
            "Zoom in or out of the image. Also accessible with"
            " %s and %s from the canvas." %
            (fmtShortcut('%s,%s' % (shortcuts['zoom_in'],
                                    shortcuts['zoom_out'])),
             fmtShortcut("Ctrl+Wheel")))
        self.zoomWidget.setEnabled(False)

        zoomIn = action('Zoom &In', functools.partial(self.addZoom, 10),
                        shortcuts['zoom_in'], 'zoom-in',
                        'Increase zoom level', enabled=False)
        zoomOut = action('&Zoom Out', functools.partial(self.addZoom, -10),
                         shortcuts['zoom_out'], 'zoom-out',
                         'Decrease zoom level', enabled=False)
        zoomOrg = action('&Original size',
                         functools.partial(self.setZoom, 100),
                         shortcuts['zoom_to_original'], 'zoom',
                         'Zoom to original size', enabled=False)
        fitWindow = action('&Fit Window', self.setFitWindow,
                           shortcuts['fit_window'], 'fit-window',
                           'Zoom follows window size', checkable=True,
                           enabled=False)
        fitWidth = action('Fit &Width', self.setFitWidth,
                          shortcuts['fit_width'], 'fit-width',
                          'Zoom follows window width',
                          checkable=True, enabled=False)
        # Group zoom controls into a list for easier toggling.
        zoomActions = (self.zoomWidget, zoomIn, zoomOut, zoomOrg,
                       fitWindow, fitWidth)
        self.zoomMode = self.MANUAL_ZOOM
        self.scalers = {
            self.FIT_WINDOW: self.scaleFitWindow,
            self.FIT_WIDTH: self.scaleFitWidth,
            # Set to one to scale to 100% when loading files.
            self.MANUAL_ZOOM: lambda: 1,
        }

        edit = action('&Edit Label', self.editLabel, shortcuts['edit_label'],
                      'edit', 'Modify the label of the selected polygon',
                      enabled=False)
        self.editButton.setDefaultAction(edit)

        shapeLineColor = action(
            'Shape &Line Color', self.chshapeLineColor, icon='color-line',
            tip='Change the line color for this specific shape', enabled=False)
        shapeFillColor = action(
            'Shape &Fill Color', self.chshapeFillColor, icon='color',
            tip='Change the fill color for this specific shape', enabled=False)

        labels = self.dock.toggleViewAction()
        labels.setText('Show/Hide Label Panel')

        # Lavel list context menu.
        labelMenu = QtWidgets.QMenu()
        addActions(labelMenu, (edit, delete))
        self.labelList.setContextMenuPolicy(Qt.CustomContextMenu)
        self.labelList.customContextMenuRequested.connect(
            self.popLabelListMenu)

        # Store actions for further handling.
        self.actions = struct(
            save=save, saveAs=saveAs, open=open_, close=close,
            lineColor=color1, fillColor=color2,
            delete=delete, edit=edit, copy=copy,
            undoLastPoint=undoLastPoint, undo=undo,
            addPoint=addPoint,
            createMode=createMode, editMode=editMode,
            createRectangleMode=createRectangleMode,
            createLineMode=createLineMode,
            createPointMode=createPointMode,
            shapeLineColor=shapeLineColor, shapeFillColor=shapeFillColor,
            zoom=zoom, zoomIn=zoomIn, zoomOut=zoomOut, zoomOrg=zoomOrg,
            fitWindow=fitWindow, fitWidth=fitWidth,
            zoomActions=zoomActions,
            fileMenuActions=(open_, opendir, save, saveAs, close, quit),
            tool=(),
            editMenu=(edit, copy, delete, None, undo, undoLastPoint,
                      None, color1, color2),
            # menu shown at right click
            menu=(
                createMode,
                createRectangleMode,
                createLineMode,
                createPointMode,
                editMode,
                edit,
                copy,
                delete,
                shapeLineColor,
                shapeFillColor,
                undo,
                undoLastPoint,
                addPoint,
            ),
            onLoadActive=(
                close,
                createMode,
                createRectangleMode,
                createLineMode,
                createPointMode,
                editMode,
            ),
            onShapesPresent=(saveAs, hideAll, showAll),
        )

        self.canvas.edgeSelected.connect(self.actions.addPoint.setEnabled)

        self.menus = struct(
            file=self.menu('&File'),
            edit=self.menu('&Edit'),
            view=self.menu('&View'),
            help=self.menu('&Help'),
            recentFiles=QtWidgets.QMenu('Open &Recent'),
            labelList=labelMenu,
        )

        addActions(self.menus.file, (open_, openNextImg, openPrevImg, opendir,
                                     self.menus.recentFiles,
                                     save, saveAs, close, None, quit))
        addActions(self.menus.help, (help,))
        addActions(self.menus.view, (
            labels, None,
            hideAll, showAll, None,
            zoomIn, zoomOut, zoomOrg, None,
            fitWindow, fitWidth))

        self.menus.file.aboutToShow.connect(self.updateFileMenu)

        # Custom context menu for the canvas widget:
        addActions(self.canvas.menus[0], self.actions.menu)
        addActions(self.canvas.menus[1], (
            action('&Copy here', self.copyShape),
            action('&Move here', self.moveShape)))

        self.tools = self.toolbar('Tools')
        # Menu buttons on Left
        self.actions.tool = (
            open_,
            opendir,
            openNextImg,
            openPrevImg,
            save,
            None,
            createMode,
            editMode,
            copy,
            delete,
            undo,
            None,
            zoomIn,
            zoom,
            zoomOut,
            fitWindow,
            fitWidth,
        )

        self.statusBar().showMessage('%s started.' % __appname__)
        self.statusBar().show()

        # Application state.
        self.image = QtGui.QImage()
        self.imagePath = None
        if self._config['auto_save'] and output is not None:
            warnings.warn('If `auto_save` argument is True, `output` argument '
                          'is ignored and output filename is automatically '
                          'set as IMAGE_BASENAME.json.')
        self.labeling_once = output is not None
        self.output = output
        self.recentFiles = []
        self.maxRecent = 7
        self.lineColor = None
        self.fillColor = None
        self.otherData = None
        self.zoom_level = 100
        self.fit_window = False

        if filename is not None and os.path.isdir(filename):
            self.importDirImages(filename, load=False)
        else:
            self.filename = filename

        # XXX: Could be completely declarative.
        # Restore application settings.
        self.settings = QtCore.QSettings('labelme', 'labelme')
        # FIXME: QSettings.value can return None on PyQt4
        self.recentFiles = self.settings.value('recentFiles', []) or []
        size = self.settings.value('window/size', QtCore.QSize(600, 500))
        position = self.settings.value('window/position', QtCore.QPoint(0, 0))
        self.resize(size)
        self.move(position)
        # or simply:
        # self.restoreGeometry(settings['window/geometry']
        self.restoreState(
            self.settings.value('window/state', QtCore.QByteArray()))
        self.lineColor = QtGui.QColor(
            self.settings.value('line/color', Shape.line_color))
        self.fillColor = QtGui.QColor(
            self.settings.value('fill/color', Shape.fill_color))
        Shape.line_color = self.lineColor
        Shape.fill_color = self.fillColor

        # Populate the File menu dynamically.
        self.updateFileMenu()
        # Since loading the file may take some time,
        # make sure it runs in the background.
        if self.filename is not None:
            self.queueEvent(functools.partial(self.loadFile, self.filename))

        # Callbacks:
        self.zoomWidget.valueChanged.connect(self.paintCanvas)

        self.populateModeActions()
    def __init__(self, parent, shortcuts: ClientGUIShortcuts.ShortcutSet):

        ClientGUIScrolledPanels.EditPanel.__init__(self, parent)

        self._name = QW.QLineEdit(self)

        self._shortcuts_panel = ClientGUIListCtrl.BetterListCtrlPanel(self)

        self._shortcuts = ClientGUIListCtrl.BetterListCtrl(
            self._shortcuts_panel,
            CGLC.COLUMN_LIST_SHORTCUTS.ID,
            20,
            data_to_tuples_func=self._ConvertSortTupleToPrettyTuple,
            delete_key_callback=self.RemoveShortcuts,
            activation_callback=self.EditShortcuts)

        self._shortcuts_panel.SetListCtrl(self._shortcuts)

        self._shortcuts_panel.AddImportExportButtons(
            (ClientGUIShortcuts.ShortcutSet, ),
            self._AddShortcutSet,
            custom_get_callable=self._GetSelectedShortcutSet)

        self._shortcuts.setMinimumSize(QC.QSize(360, 480))

        self._add = QW.QPushButton('add', self)
        self._add.clicked.connect(self.AddShortcut)

        self._edit = QW.QPushButton('edit', self)
        self._edit.clicked.connect(self.EditShortcuts)

        self._remove = QW.QPushButton('remove', self)
        self._remove.clicked.connect(self.RemoveShortcuts)

        #

        name = shortcuts.GetName()

        self._name.setText(name)

        self._this_is_custom = True

        if name in ClientGUIShortcuts.SHORTCUTS_RESERVED_NAMES:

            self._this_is_custom = False

            self._name.setEnabled(False)

        self._shortcuts.AddDatas(shortcuts)

        self._shortcuts.Sort()

        #

        action_buttons = QP.HBoxLayout()

        QP.AddToLayout(action_buttons, self._add,
                       CC.FLAGS_CENTER_PERPENDICULAR)
        QP.AddToLayout(action_buttons, self._edit,
                       CC.FLAGS_CENTER_PERPENDICULAR)
        QP.AddToLayout(action_buttons, self._remove,
                       CC.FLAGS_CENTER_PERPENDICULAR)

        vbox = QP.VBoxLayout()

        QP.AddToLayout(vbox,
                       ClientGUICommon.WrapInText(self._name, self, 'name: '),
                       CC.FLAGS_EXPAND_SIZER_PERPENDICULAR)

        if name in ClientGUIShortcuts.shortcut_names_to_descriptions:

            description_text = ClientGUIShortcuts.shortcut_names_to_descriptions[
                name]

            description = ClientGUICommon.BetterStaticText(
                self, description_text, description_text)

            description.setWordWrap(True)

            QP.AddToLayout(vbox, description, CC.FLAGS_EXPAND_PERPENDICULAR)

        QP.AddToLayout(vbox, self._shortcuts_panel, CC.FLAGS_EXPAND_BOTH_WAYS)
        QP.AddToLayout(vbox, action_buttons, CC.FLAGS_ON_RIGHT)

        self.widget().setLayout(vbox)
class MouseShortcutButton(QW.QPushButton):

    valueChanged = QC.Signal()

    def __init__(self, parent):

        self._shortcut = ClientGUIShortcuts.Shortcut(
            ClientGUIShortcuts.SHORTCUT_TYPE_MOUSE,
            ClientGUIShortcuts.SHORTCUT_MOUSE_LEFT,
            ClientGUIShortcuts.SHORTCUT_PRESS_TYPE_PRESS, [])

        self._press_instead_of_release = True

        QW.QPushButton.__init__(self, parent)

        self._SetShortcutString()

    def _ProcessMouseEvent(self, event):

        self.setFocus(QC.Qt.OtherFocusReason)

        shortcut = ClientGUIShortcuts.ConvertMouseEventToShortcut(event)

        if shortcut is not None:

            self._shortcut = shortcut

            self._SetShortcutString()

            self.valueChanged.emit()

    def _SetShortcutString(self):

        display_string = self._shortcut.ToString()

        self.setText(display_string)

    def mousePressEvent(self, event):

        if self._press_instead_of_release:

            self._ProcessMouseEvent(event)

    def mouseReleaseEvent(self, event):

        if not self._press_instead_of_release:

            self._ProcessMouseEvent(event)

    def mouseDoubleClickEvent(self, event):

        self._ProcessMouseEvent(event)

    def wheelEvent(self, event):

        self._ProcessMouseEvent(event)

    def GetValue(self) -> ClientGUIShortcuts.Shortcut:

        return self._shortcut

    def SetPressInsteadOfRelease(self, press_instead_of_release):

        self._press_instead_of_release = press_instead_of_release

        if self._shortcut.IsAppropriateForPressRelease():

            self._shortcut = self._shortcut.Duplicate()

            if self._press_instead_of_release:

                self._shortcut.shortcut_press_type = ClientGUIShortcuts.SHORTCUT_PRESS_TYPE_PRESS

            else:

                self._shortcut.shortcut_press_type = ClientGUIShortcuts.SHORTCUT_PRESS_TYPE_RELEASE

            self._SetShortcutString()

            self.valueChanged.emit()

    def SetValue(self, shortcut: ClientGUIShortcuts.Shortcut):

        self._shortcut = shortcut.Duplicate()

        self._SetShortcutString()

        self.valueChanged.emit()
    def __init__(self, parent, all_shortcuts):

        ClientGUIScrolledPanels.EditPanel.__init__(self, parent)

        help_button = ClientGUICommon.BetterBitmapButton(
            self,
            CC.global_pixmaps().help, self._ShowHelp)
        help_button.setToolTip('Show help regarding editing shortcuts.')

        reserved_panel = ClientGUICommon.StaticBox(
            self, 'built-in hydrus shortcut sets')

        self._reserved_shortcuts = ClientGUIListCtrl.BetterListCtrl(
            reserved_panel,
            CGLC.COLUMN_LIST_SHORTCUT_SETS.ID,
            6,
            data_to_tuples_func=self._GetTuples,
            activation_callback=self._EditReserved)

        self._reserved_shortcuts.setMinimumSize(QC.QSize(320, 200))

        self._edit_reserved_button = ClientGUICommon.BetterButton(
            reserved_panel, 'edit', self._EditReserved)
        self._restore_defaults_button = ClientGUICommon.BetterButton(
            reserved_panel, 'restore defaults', self._RestoreDefaults)

        #

        custom_panel = ClientGUICommon.StaticBox(self, 'custom user sets')

        self._custom_shortcuts = ClientGUIListCtrl.BetterListCtrl(
            custom_panel,
            CGLC.COLUMN_LIST_SHORTCUT_SETS.ID,
            6,
            data_to_tuples_func=self._GetTuples,
            delete_key_callback=self._Delete,
            activation_callback=self._EditCustom)

        self._add_button = ClientGUICommon.BetterButton(
            custom_panel, 'add', self._Add)
        self._edit_custom_button = ClientGUICommon.BetterButton(
            custom_panel, 'edit', self._EditCustom)
        self._delete_button = ClientGUICommon.BetterButton(
            custom_panel, 'delete', self._Delete)

        if not HG.client_controller.new_options.GetBoolean('advanced_mode'):

            custom_panel.hide()

        #

        reserved_shortcuts = [
            shortcuts for shortcuts in all_shortcuts if shortcuts.GetName() in
            ClientGUIShortcuts.SHORTCUTS_RESERVED_NAMES
        ]
        custom_shortcuts = [
            shortcuts for shortcuts in all_shortcuts if shortcuts.GetName()
            not in ClientGUIShortcuts.SHORTCUTS_RESERVED_NAMES
        ]

        self._reserved_shortcuts.AddDatas(reserved_shortcuts)

        self._reserved_shortcuts.Sort()

        self._original_custom_names = set()

        for shortcuts in custom_shortcuts:

            self._custom_shortcuts.AddDatas((shortcuts, ))

            self._original_custom_names.add(shortcuts.GetName())

        self._custom_shortcuts.Sort()

        #

        button_hbox = QP.HBoxLayout()

        QP.AddToLayout(button_hbox, self._edit_reserved_button,
                       CC.FLAGS_CENTER_PERPENDICULAR)
        QP.AddToLayout(button_hbox, self._restore_defaults_button,
                       CC.FLAGS_CENTER_PERPENDICULAR)

        reserved_panel.Add(self._reserved_shortcuts,
                           CC.FLAGS_EXPAND_SIZER_BOTH_WAYS)
        reserved_panel.Add(button_hbox, CC.FLAGS_ON_RIGHT)

        #

        button_hbox = QP.HBoxLayout()

        QP.AddToLayout(button_hbox, self._add_button,
                       CC.FLAGS_CENTER_PERPENDICULAR)
        QP.AddToLayout(button_hbox, self._edit_custom_button,
                       CC.FLAGS_CENTER_PERPENDICULAR)
        QP.AddToLayout(button_hbox, self._delete_button,
                       CC.FLAGS_CENTER_PERPENDICULAR)

        custom_panel_message = 'Custom shortcuts are advanced. They apply to the media viewer and must be turned on to take effect.'

        st = ClientGUICommon.BetterStaticText(custom_panel,
                                              custom_panel_message)
        st.setWordWrap(True)

        custom_panel.Add(st, CC.FLAGS_EXPAND_PERPENDICULAR)
        custom_panel.Add(self._custom_shortcuts,
                         CC.FLAGS_EXPAND_SIZER_BOTH_WAYS)
        custom_panel.Add(button_hbox, CC.FLAGS_ON_RIGHT)

        #

        vbox = QP.VBoxLayout()

        QP.AddToLayout(vbox, help_button, CC.FLAGS_ON_RIGHT)
        QP.AddToLayout(vbox, reserved_panel, CC.FLAGS_EXPAND_BOTH_WAYS)
        QP.AddToLayout(vbox, custom_panel, CC.FLAGS_EXPAND_BOTH_WAYS)

        self.widget().setLayout(vbox)
Пример #18
0
class BasicMonitor(QTableWidget):
    """
    基础监控

    headerDict中的值对应的字典格式如下
    {'chinese': u'中文名', 'cellType': BasicCell}

    """
    signal = QtCore.Signal(type(Event()))

    # ----------------------------------------------------------------------
    def __init__(self, mainEngine=None, eventEngine=None, parent=None):
        """Constructor"""
        super(BasicMonitor, self).__init__(parent)

        self.mainEngine = mainEngine
        self.eventEngine = eventEngine

        # 保存表头标签用
        self.headerDict = OrderedDict()  # 有序字典,key是英文名,value是对应的配置字典
        self.headerList = []  # 对应self.headerDict.keys()

        # 保存相关数据用
        self.dataDict = {}  # 字典,key是字段对应的数据,value是保存相关单元格的字典
        self.dataKey = ''  # 字典键对应的数据字段

        # 监控的事件类型
        self.eventType = ''

        # 列宽调整状态(只在第一次更新数据时调整一次列宽)
        self.columnResized = False

        # 字体
        self.font = None
        self.mouseStatus = False

        # 保存数据对象到单元格
        self.saveData = False

        # 默认不允许根据表头进行排序,需要的组件可以开启
        self.sorting = False

        self.daylinewidget = None
        self.search = searchcode()
        self.stockItemText = None

        # 设置悬停显示
        self.setMouseTracking(True)
        self.itemEntered.connect(self.handleItemClicked)

    def mousePressEvent(self, e: QtGui.QMouseEvent):
        print("press")
        code = self.search.findbychinese(self.stockItemText)
        if (len(code) == 0):
            return
        print(code[0])
        self.mouseStatus = True
        if (e.button() == Qt.LeftButton):
            self.daylineshow(code[0], "left")
        elif (e.button() == Qt.RightButton):
            self.daylineshow(code[0], "right")
        elif (e.button() == Qt.MidButton):
            self.mouseStatus = False

    def mouseReleaseEvent(self, e: QtGui.QMouseEvent):
        print("release")
        self.daylinehide()
        self.mouseStatus = False

    def handleItemClicked(self, item):
        self.stockItemText = item.text()
        print(item.text())

    # ----------------------------------------------------------------------
    def setEventType(self, eventType):
        """设置监控的事件类型"""
        self.eventType = eventType

    def registerEvent(self):
        """注册GUI更新相关的事件监听"""
        self.signal.connect(self.updateEvent)
        self.eventEngine.register(self.eventType, self.signal.emit)

    # ----------------------------------------------------------------------
    def updateEvent(self, event):
        """收到事件更新"""
        data = event.dict_['data']
        self.updateData(data)

    def daylineinit(self):
        url = 'http://image.sinajs.cn/newchart/min/n/sh000001.gif'
        req = requests.get(url)

        photo = QPixmap()
        photo.loadFromData(req.content)

        label = QLabel()
        label.setPixmap(photo)

        widget = QWidget()
        layout = QVBoxLayout()
        widget.setLayout(layout)
        layout.addWidget(label)

        return widget

    def daylineshow(self, code, str):
        if (code[0] == '6'):
            if (str == "left"):
                url = 'http://image.sinajs.cn/newchart/min/n/sh' + code + '.gif'
            elif (str == "right"):
                url = 'http://image.sinajs.cn/newchart/daily/n/sh' + code + '.gif'
        else:
            if (str == "left"):
                url = 'http://image.sinajs.cn/newchart/min/n/sz' + code + '.gif'
            elif (str == "right"):
                url = 'http://image.sinajs.cn/newchart/daily/n/sz' + code + '.gif'
        # url = url.match("sh000001","300279")
        print(url)
        req = requests.get(url)

        photo = QPixmap()
        photo.loadFromData(req.content)
        label = QLabel()

        label.setPixmap(photo)
        label.setWindowOpacity(0.2)

        self.daylinewidget = QWidget()

        self.daylinewidget.setAutoFillBackground(True)
        palette = QPalette()
        palette.setColor(QPalette.Background, QColor(128, 128, 128, 50))
        self.daylinewidget.setPalette(palette)
        self.daylinewidget.resize(50, 50)
        label.setPalette(palette)
        layout = QVBoxLayout()
        self.daylinewidget.setLayout(layout)
        layout.addWidget(label)
        self.daylinewidget.setGeometry(500, 300, 50, 50)
        self.daylinewidget.show()

    def daylinehide(self):
        if (self.mouseStatus == True):
            self.daylinewidget.hide()
Пример #19
0
    def style_widgets(self):
        icon_size = QtCore.QSize(17, 17)
        self.clear_btn.setIcon(
            QtGui.QIcon(os.path.join(icons_path, 'reset_dark.ico')))
        self.clear_btn.setIconSize(icon_size)

        self.add_btn.setIcon(QtGui.QIcon(os.path.join(icons_path, 'open.ico')))
        self.add_btn.setIconSize(icon_size)

        self.delete_btn.setIcon(
            QtGui.QIcon(os.path.join(icons_path, 'delete.png')))
        self.delete_btn.setIconSize(QtCore.QSize(12, 14))

        self.move_up_btn.setIcon(
            QtGui.QIcon(os.path.join(icons_path, 'arrow_up.ico')))
        self.move_up_btn.setIconSize(icon_size)

        self.move_down_btn.setIcon(
            QtGui.QIcon(os.path.join(icons_path, 'arrow_down.ico')))
        self.move_down_btn.setIconSize(icon_size)

        def modify_btn_to_icon_size(btn):
            button_height = 25
            button_width = 25
            btn.setMinimumHeight(button_height)
            btn.setMaximumHeight(button_height)
            btn.setMinimumWidth(button_width)
            btn.setMaximumWidth(button_width)

        modify_btn_to_icon_size(self.add_btn)
        modify_btn_to_icon_size(self.delete_btn)
        modify_btn_to_icon_size(self.clear_btn)
        modify_btn_to_icon_size(self.move_up_btn)
        modify_btn_to_icon_size(self.move_down_btn)

        step_txt_width = 70
        self.scale_step_msb.setMaximumWidth(step_txt_width)
        self.scale_step_msb.setMinimumWidth(step_txt_width)
        self.offset_step_msb.setMaximumWidth(step_txt_width)
        self.waterfall_separation_msb.setMaximumWidth(step_txt_width)

        self.scale_step_msb.setMaximum(10.0)
        self.scale_step_msb.setMinimum(0.01)
        self.scale_step_msb.setValue(0.01)

        self.offset_step_msb.setMaximum(100000.0)
        self.offset_step_msb.setMinimum(0.01)
        self.offset_step_msb.setValue(100.0)

        self.waterfall_separation_msb.setMaximum(100000.0)
        self.waterfall_separation_msb.setMinimum(0.01)
        self.waterfall_separation_msb.setValue(100.0)

        self.set_as_bkg_btn.setStyleSheet('font-size: 11px')
        self.waterfall_btn.setMaximumWidth(step_txt_width)
        self.waterfall_reset_btn.setMaximumWidth(step_txt_width)

        self.set_as_bkg_btn.setMinimumHeight(40)
        self.set_as_bkg_btn.setMaximumHeight(40)

        self.overlay_header_btn.setStyleSheet("border-radius: 0px")
Пример #20
0
class OverlayWidget(QtWidgets.QWidget):
    color_btn_clicked = QtCore.Signal(int, QtWidgets.QWidget)
    show_cb_state_changed = QtCore.Signal(int, bool)
    name_changed = QtCore.Signal(int, str)
    scale_sb_value_changed = QtCore.Signal(int, float)
    offset_sb_value_changed = QtCore.Signal(int, float)

    def __init__(self):
        super(OverlayWidget, self).__init__()

        self._layout = QtWidgets.QHBoxLayout()
        self._layout.setContentsMargins(5, 5, 5, 5)
        self._layout.setSpacing(5)

        self.button_widget = QtWidgets.QWidget(self)
        self.button_widget.setObjectName('overlay_control_widget')
        self._button_layout = QtWidgets.QVBoxLayout(self.button_widget)
        self._button_layout.setContentsMargins(0, 0, 0, 0)
        self._button_layout.setSpacing(6)

        self.add_btn = FlatButton()
        self.delete_btn = FlatButton()
        self.clear_btn = FlatButton()
        self.move_up_btn = FlatButton()
        self.move_down_btn = FlatButton()

        self._button_layout.addWidget(self.add_btn)
        self._button_layout.addWidget(self.delete_btn)
        self._button_layout.addWidget(HorizontalLine())
        self._button_layout.addWidget(HorizontalLine())
        self._button_layout.addSpacerItem(VerticalSpacerItem())
        self._button_layout.addWidget(self.clear_btn)
        self._button_layout.addSpacerItem(VerticalSpacerItem())
        self._button_layout.addWidget(HorizontalLine())
        self._button_layout.addWidget(HorizontalLine())
        self._button_layout.addWidget(self.move_up_btn)
        self._button_layout.addWidget(self.move_down_btn)
        self._layout.addWidget(self.button_widget)

        self.parameter_widget = QtWidgets.QWidget(self)
        self._parameter_layout = QtWidgets.QVBoxLayout()
        self._parameter_layout.setContentsMargins(0, 0, 0, 0)
        self._parameter_layout.setSpacing(5)

        self.scale_step_msb = DoubleMultiplySpinBoxAlignRight()
        self.offset_step_msb = DoubleMultiplySpinBoxAlignRight()

        self._step_gb = QtWidgets.QWidget()
        self._step_layout = QtWidgets.QVBoxLayout()
        self._step_layout.setContentsMargins(0, 0, 0, 0)
        self._step_layout.setSpacing(4)
        self._step_layout.addWidget(QtWidgets.QLabel('Scale Step'))
        self._step_layout.addWidget(self.scale_step_msb)
        self._step_layout.addWidget(QtWidgets.QLabel('Offset Step'))
        self._step_layout.addWidget(self.offset_step_msb)
        self._step_gb.setLayout(self._step_layout)
        self._parameter_layout.addWidget(self._step_gb)
        self._parameter_layout.addWidget(HorizontalLine())

        self._waterfall_gb = QtWidgets.QWidget()
        self.waterfall_separation_msb = DoubleMultiplySpinBoxAlignRight()
        self.waterfall_btn = FlatButton('Waterfall')
        self.waterfall_reset_btn = FlatButton('Reset')

        self._waterfall_layout = QtWidgets.QVBoxLayout()
        self._waterfall_layout.setContentsMargins(0, 0, 0, 0)
        self._waterfall_layout.setSpacing(4)
        self._waterfall_layout.addWidget(self.waterfall_btn)
        self._waterfall_layout.addWidget(self.waterfall_separation_msb)
        self._waterfall_layout.addWidget(self.waterfall_reset_btn)
        self._waterfall_gb.setLayout(self._waterfall_layout)
        self._parameter_layout.addWidget(self._waterfall_gb)
        self._parameter_layout.addWidget(HorizontalLine())

        self._parameter_layout.addItem(VerticalSpacerItem())

        self.set_as_bkg_btn = CheckableFlatButton('Set as\nBackground')
        self._background_layout = QtWidgets.QHBoxLayout()
        self._background_layout.setContentsMargins(0, 0, 0, 0)
        self._background_layout.addSpacerItem(HorizontalSpacerItem())
        self._background_layout.addWidget(self.set_as_bkg_btn)
        self._parameter_layout.addLayout(self._background_layout)

        self.parameter_widget.setLayout(self._parameter_layout)

        self.overlay_tw = ListTableWidget(columns=5)
        self.overlay_tw.setObjectName('overlay_table_widget')
        self.overlay_tw.setHorizontalHeaderLabels(
            ['', '', 'Name', 'Scale', 'Offset'])
        self.overlay_tw.horizontalHeader().setVisible(True)
        self.overlay_tw.horizontalHeader().setStretchLastSection(False)
        self.overlay_tw.horizontalHeader().setResizeMode(
            2, QtWidgets.QHeaderView.Stretch)
        self.overlay_tw.horizontalHeader().setResizeMode(
            3, QtWidgets.QHeaderView.ResizeToContents)
        self.overlay_tw.horizontalHeader().setResizeMode(
            4, QtWidgets.QHeaderView.ResizeToContents)

        self.overlay_tw.setColumnWidth(0, 20)
        self.overlay_tw.setColumnWidth(1, 25)
        self.overlay_tw.cellChanged.connect(self.label_editingFinished)
        self.overlay_tw.setItemDelegate(NoRectDelegate())

        self._layout.addWidget(self.overlay_tw, 10)
        self._layout.addWidget(self.parameter_widget, 0)

        # label for alternative view:
        self.overlay_header_btn = FlatButton('Overlay')
        self.overlay_header_btn.setEnabled(False)
        self.overlay_header_btn.setVisible(False)
        self._main_layout = QtWidgets.QVBoxLayout()
        self._main_layout.setContentsMargins(0, 0, 0, 0)
        self._main_layout.addWidget(self.overlay_header_btn)
        self._main_layout.addLayout(self._layout)
        self.setLayout(self._main_layout)
        self.style_widgets()
        self.add_tooltips()

        self.show_cbs = []
        self.color_btns = []
        self.scale_sbs = []
        self.offset_sbs = []

    def style_widgets(self):
        icon_size = QtCore.QSize(17, 17)
        self.clear_btn.setIcon(
            QtGui.QIcon(os.path.join(icons_path, 'reset_dark.ico')))
        self.clear_btn.setIconSize(icon_size)

        self.add_btn.setIcon(QtGui.QIcon(os.path.join(icons_path, 'open.ico')))
        self.add_btn.setIconSize(icon_size)

        self.delete_btn.setIcon(
            QtGui.QIcon(os.path.join(icons_path, 'delete.png')))
        self.delete_btn.setIconSize(QtCore.QSize(12, 14))

        self.move_up_btn.setIcon(
            QtGui.QIcon(os.path.join(icons_path, 'arrow_up.ico')))
        self.move_up_btn.setIconSize(icon_size)

        self.move_down_btn.setIcon(
            QtGui.QIcon(os.path.join(icons_path, 'arrow_down.ico')))
        self.move_down_btn.setIconSize(icon_size)

        def modify_btn_to_icon_size(btn):
            button_height = 25
            button_width = 25
            btn.setMinimumHeight(button_height)
            btn.setMaximumHeight(button_height)
            btn.setMinimumWidth(button_width)
            btn.setMaximumWidth(button_width)

        modify_btn_to_icon_size(self.add_btn)
        modify_btn_to_icon_size(self.delete_btn)
        modify_btn_to_icon_size(self.clear_btn)
        modify_btn_to_icon_size(self.move_up_btn)
        modify_btn_to_icon_size(self.move_down_btn)

        step_txt_width = 70
        self.scale_step_msb.setMaximumWidth(step_txt_width)
        self.scale_step_msb.setMinimumWidth(step_txt_width)
        self.offset_step_msb.setMaximumWidth(step_txt_width)
        self.waterfall_separation_msb.setMaximumWidth(step_txt_width)

        self.scale_step_msb.setMaximum(10.0)
        self.scale_step_msb.setMinimum(0.01)
        self.scale_step_msb.setValue(0.01)

        self.offset_step_msb.setMaximum(100000.0)
        self.offset_step_msb.setMinimum(0.01)
        self.offset_step_msb.setValue(100.0)

        self.waterfall_separation_msb.setMaximum(100000.0)
        self.waterfall_separation_msb.setMinimum(0.01)
        self.waterfall_separation_msb.setValue(100.0)

        self.set_as_bkg_btn.setStyleSheet('font-size: 11px')
        self.waterfall_btn.setMaximumWidth(step_txt_width)
        self.waterfall_reset_btn.setMaximumWidth(step_txt_width)

        self.set_as_bkg_btn.setMinimumHeight(40)
        self.set_as_bkg_btn.setMaximumHeight(40)

        self.overlay_header_btn.setStyleSheet("border-radius: 0px")

    def add_tooltips(self):
        self.add_btn.setToolTip('Loads Overlay(s) from file(s)')
        self.delete_btn.setToolTip('Removes currently selected overlay')
        self.clear_btn.setToolTip('Removes all overlays')

    def add_overlay(self, name, color):
        current_rows = self.overlay_tw.rowCount()
        self.overlay_tw.setRowCount(current_rows + 1)
        self.overlay_tw.blockSignals(True)

        show_cb = QtWidgets.QCheckBox()
        show_cb.setChecked(True)
        show_cb.stateChanged.connect(partial(self.show_cb_changed, show_cb))
        show_cb.setStyleSheet("background-color: transparent")
        self.overlay_tw.setCellWidget(current_rows, 0, show_cb)
        self.show_cbs.append(show_cb)

        color_button = FlatButton()
        color_button.setStyleSheet("background-color: " + color)
        color_button.clicked.connect(
            partial(self.color_btn_click, color_button))
        self.overlay_tw.setCellWidget(current_rows, 1, color_button)
        self.color_btns.append(color_button)

        name_item = QtWidgets.QTableWidgetItem(name)
        name_item.setFlags(name_item.flags() & ~QtCore.Qt.ItemIsEditable)
        self.overlay_tw.setItem(current_rows, 2,
                                QtWidgets.QTableWidgetItem(name))

        scale_sb = DoubleSpinBoxAlignRight()
        scale_sb.setMinimum(-9999999)
        scale_sb.setMaximum(9999999)
        scale_sb.setValue(1)
        scale_sb.setSingleStep(self.scale_step_msb.value())
        scale_sb.valueChanged.connect(partial(self.scale_sb_callback,
                                              scale_sb))
        self.overlay_tw.setCellWidget(current_rows, 3, scale_sb)
        self.scale_sbs.append(scale_sb)

        offset_sb = DoubleSpinBoxAlignRight()
        offset_sb.setMinimum(-9999999)
        offset_sb.setMaximum(9999999)
        offset_sb.setValue(0)
        offset_sb.setSingleStep(self.offset_step_msb.value())
        offset_sb.valueChanged.connect(
            partial(self.offset_sb_callback, offset_sb))
        self.overlay_tw.setCellWidget(current_rows, 4, offset_sb)
        self.offset_sbs.append(offset_sb)

        self.overlay_tw.setColumnWidth(0, 20)
        self.overlay_tw.setColumnWidth(1, 25)
        self.overlay_tw.setRowHeight(current_rows, 25)

        self.overlay_tw.horizontalHeader().setResizeMode(
            3, QtWidgets.QHeaderView.ResizeToContents)
        self.overlay_tw.horizontalHeader().setResizeMode(
            4, QtWidgets.QHeaderView.ResizeToContents)

        self.select_overlay(current_rows)
        self.overlay_tw.blockSignals(False)

    def select_overlay(self, ind):
        if self.overlay_tw.rowCount() > 0:
            self.overlay_tw.selectRow(ind)

    def get_selected_overlay_row(self):
        selected = self.overlay_tw.selectionModel().selectedRows()
        try:
            row = selected[0].row()
        except IndexError:
            row = -1
        return row

    def remove_overlay(self, ind):
        self.overlay_tw.blockSignals(True)
        self.overlay_tw.removeRow(ind)
        self.overlay_tw.blockSignals(False)
        del self.show_cbs[ind]
        del self.color_btns[ind]
        del self.offset_sbs[ind]
        del self.scale_sbs[ind]

        if self.overlay_tw.rowCount() > ind:
            self.select_overlay(ind)
        else:
            self.select_overlay(self.overlay_tw.rowCount() - 1)

    def move_overlay_up(self, ind):
        new_ind = ind - 1
        self.overlay_tw.blockSignals(True)
        self.overlay_tw.insertRow(new_ind)
        self.overlay_tw.setCellWidget(new_ind, 0,
                                      self.overlay_tw.cellWidget(ind + 1, 0))
        self.overlay_tw.setCellWidget(new_ind, 1,
                                      self.overlay_tw.cellWidget(ind + 1, 1))
        self.overlay_tw.setCellWidget(new_ind, 3,
                                      self.overlay_tw.cellWidget(ind + 1, 3))
        self.overlay_tw.setCellWidget(new_ind, 4,
                                      self.overlay_tw.cellWidget(ind + 1, 4))
        self.overlay_tw.setItem(new_ind, 2,
                                self.overlay_tw.takeItem(ind + 1, 2))
        self.overlay_tw.setCurrentCell(new_ind, 2)
        self.overlay_tw.removeRow(ind + 1)
        self.overlay_tw.setRowHeight(new_ind, 25)
        self.overlay_tw.blockSignals(False)

        self.color_btns.insert(new_ind, self.color_btns.pop(ind))
        self.show_cbs.insert(new_ind, self.show_cbs.pop(ind))
        self.scale_sbs.insert(new_ind, self.scale_sbs.pop(ind))
        self.offset_sbs.insert(new_ind, self.offset_sbs.pop(ind))

    def move_overlay_down(self, ind):
        new_ind = ind + 2
        self.overlay_tw.blockSignals(True)
        self.overlay_tw.insertRow(new_ind)
        self.overlay_tw.setCellWidget(new_ind, 0,
                                      self.overlay_tw.cellWidget(ind, 0))
        self.overlay_tw.setCellWidget(new_ind, 1,
                                      self.overlay_tw.cellWidget(ind, 1))
        self.overlay_tw.setCellWidget(new_ind, 3,
                                      self.overlay_tw.cellWidget(ind, 3))
        self.overlay_tw.setCellWidget(new_ind, 4,
                                      self.overlay_tw.cellWidget(ind, 4))
        self.overlay_tw.setItem(new_ind, 2, self.overlay_tw.takeItem(ind, 2))
        self.overlay_tw.setCurrentCell(new_ind, 2)
        self.overlay_tw.setRowHeight(new_ind, 25)
        self.overlay_tw.removeRow(ind)
        self.overlay_tw.blockSignals(False)

        self.color_btns.insert(ind + 1, self.color_btns.pop(ind))
        self.show_cbs.insert(ind + 1, self.show_cbs.pop(ind))
        self.scale_sbs.insert(ind + 1, self.scale_sbs.pop(ind))
        self.offset_sbs.insert(ind + 1, self.offset_sbs.pop(ind))

    def color_btn_click(self, button):
        self.color_btn_clicked.emit(self.color_btns.index(button), button)

    def show_cb_changed(self, checkbox):
        self.show_cb_state_changed.emit(self.show_cbs.index(checkbox),
                                        checkbox.isChecked())

    def show_cb_set_checked(self, ind, state):
        checkbox = self.show_cbs[ind]
        checkbox.setChecked(state)

    def show_cb_is_checked(self, ind):
        checkbox = self.show_cbs[ind]
        return checkbox.isChecked()

    def label_editingFinished(self, row, col):
        label_item = self.overlay_tw.item(row, col)
        self.name_changed.emit(row, str(label_item.text()))

    def scale_sb_callback(self, scale_sb):
        self.scale_sb_value_changed.emit(self.scale_sbs.index(scale_sb),
                                         scale_sb.value())

    def offset_sb_callback(self, offset_sb):
        self.offset_sb_value_changed.emit(self.offset_sbs.index(offset_sb),
                                          offset_sb.value())
Пример #21
0
class LoggedQuantity(QtCore.QObject):
    """
    **LoggedQuantity** objects are containers that wrap settings. These settings
    may be a number (integer or float) or a string and occasionally small arrays
    of them.
    
    These objects emit signals when changed and can be connected bidirectionally
    to Qt Widgets.
    
    In ScopeFoundry we represent the values in an object called a
    `LoggedQuantity`. A :class:`LoggedQuantity` is a class that contains a
    value, a `bool`, `float`, `int`, `str` etc that is part of an application's
    state. In the case of microscope and equipment control, these also can
    represent the state of a piece of hardware. These are very useful objects
    because the are the central location of the value contained within. All
    graphical interface views will be guaranteed to be consistent with the `LQ`
    state. The data of these quantities will also be saved in datafiles created
    by ScopeFoundry.
    
    """

    # signal sent when value has been updated
    updated_value = QtCore.Signal((float,),(int,),(bool,), (), (str,),) 
    # signal sent when value has been updated, sends text representation
    updated_text_value = QtCore.Signal(str) 
    # emits the index of the value in self.choices
    updated_choice_index_value = QtCore.Signal(int)
     
    # signal sent when min max range updated
    updated_min_max = QtCore.Signal((float,float),(int,int), (),)
    # signal sent when read only (ro) status has changed 
    updated_readonly = QtCore.Signal((bool,), (),) 
    
    def __init__(self, name, dtype=float, 
                 hardware_read_func=None, hardware_set_func=None, 
                 initial=0, fmt="%g", si=False,
                 ro = False, # read only flag
                 unit = None,
                 spinbox_decimals = 2,
                 spinbox_step=0.1,
                 vmin=-1e12, vmax=+1e12, choices=None,
                 reread_from_hardware_after_write = False,
                 description = None
                 ):
        QtCore.QObject.__init__(self)
        
        self.name = name
        self.dtype = dtype
        self.val = dtype(initial)
        self.hardware_read_func = hardware_read_func
        self.hardware_set_func = hardware_set_func
        self.fmt = fmt # string formatting string. This is ignored if dtype==str
        if self.dtype == str:
            self.fmt = "%s"
        self.si   = si # will use pyqtgraph SI Spinbox if True
        self.unit = unit
        self.vmin = vmin
        self.vmax = vmax
        # choices should be tuple [ ('name', val) ... ] or simple list [val, val, ...]
        self.choices = self._expand_choices(choices) 
        self.ro = ro # Read-Only
        self.is_array = False
        self.description = description
        
        self.log = get_logger_from_class(self)
        
        if self.dtype == int:
            self.spinbox_decimals = 0
        else:
            self.spinbox_decimals = spinbox_decimals
        self.reread_from_hardware_after_write = reread_from_hardware_after_write
        
        if self.dtype == int:
            self.spinbox_step = 1
        else:
            self.spinbox_step = spinbox_step
        
        self.oldval = None
        
        self._in_reread_loop = False # flag to prevent reread from hardware loops
        
        self.widget_list = []
        self.listeners = []
        
        # threading lock
        #self.lock = threading.Lock()
        #self.lock = DummyLock()
        self.lock = QLock(mode=0) # mode 0 is non-reentrant lock
        
    def coerce_to_type(self, x):
        """
        Force x to dtype of the LQ
        
        =============  ==================================
        **Arguments**  **Description**
        *x*            value of type str, bool, int, etc.
        =============  ==================================
        
        :returns: Same value, *x* of the same type as its respective logged
        quantity
        
        """
        if self.dtype==bool and isinstance(x, str):
            return str2bool(x)       
        return self.dtype(x)
        
    def _expand_choices(self, choices):
        if choices is None:
            return None
        expanded_choices = []
        for c in choices:
            if isinstance(c, tuple):
                name, val = c
                expanded_choices.append( ( str(name), self.dtype(val) ) )
            else:
                expanded_choices.append( ( str(c), self.dtype(c) ) )
        return expanded_choices
    
    def __str__(self):
        return "{} = {}".format(self.name, self.val)
    
    def __repr__(self):
        return "LQ: {} = {}".format(self.name, self.val)

    
    def read_from_hardware(self, send_signal=True):
        self.log.debug("{}: read_from_hardware send_signal={}".format(self.name, send_signal))
        if self.hardware_read_func:        
            with self.lock:
                self.oldval = self.val
                val = self.hardware_read_func()
            self.update_value(new_val=val, update_hardware=False, send_signal=send_signal)
        else:
            self.log.warn("{} read_from_hardware called when not connected to hardware".format(self.name))
        return self.val
    
    def write_to_hardware(self, reread_hardware=None):
        if reread_hardware is None:
            # if undefined, default to stored reread_from_hardware_after_write bool
            reread_hardware = self.reread_from_hardware_after_write
        # Read from Hardware
        if self.has_hardware_write():
            with self.lock:
                self.hardware_set_func(self.val)
            if reread_hardware:
                self.read_from_hardware(send_signal=False)

    def value(self):
        "return stored value"
        return self.val

    @QtCore.Slot(str)
    @QtCore.Slot(float)
    @QtCore.Slot(int)
    @QtCore.Slot(bool)
    @QtCore.Slot()
    def update_value(self, new_val=None, update_hardware=True, send_signal=True, reread_hardware=None):
        """
        Update stored value with new_val
         
        Change value of LQ and emit signals to inform listeners of change 
        
        if *update_hardware* is true: call connected hardware write function
        
        =============== =================================================================================================================
        **Arguments:**  **Description:**
        new_val         New value for the LoggedQuantity to store
        update_hardware calls hardware_set_func if defined (default True)
        send_signal     sends out QT signals on upon change (default True)
        reread_hardware read from hardware after writing to hardware to ensure change (defaults to self.reread_from_hardware_after_write)
        =============== =================================================================================================================
        
        :returns: None
        
        """
        
        # use a thread lock during update_value to avoid another thread
        # calling update_value during the update_value
        
        if reread_hardware is None:
            # if undefined, default to stored reread_from_hardware_after_write bool
            reread_hardware = self.reread_from_hardware_after_write
        
        with self.lock:
            
            # sometimes a the sender is a textbox that does not send its new value,
            # grab the text() from it instead
            if new_val is None:
                new_val = self.sender().text()
    
            self.oldval = self.coerce_to_type(self.val)
            new_val = self.coerce_to_type(new_val)
            
            self.log.debug("{}: update_value {} --> {}    sender={}".format(self.name, self.oldval, new_val, self.sender()))
    
            # check for equality of new vs old, do not proceed if they are same
            if self.same_values(self.oldval, new_val):
                self.log.debug("{}: same_value so returning {} {}".format(self.name, self.oldval, new_val))
                return
            else:
                pass
                
            # actually change internal state value
            self.val = new_val
        
        
        # Read from Hardware
        if update_hardware and self.hardware_set_func:
            self.hardware_set_func(self.val)
            if reread_hardware:
                self.read_from_hardware(send_signal=False)
        # Send Qt Signals
        if send_signal:
            self.send_display_updates()
            
    def send_display_updates(self, force=False):
        """
        Emit updated_value signals if value has changed.
        
        =============  =============================================
        **Arguments**  **Description**
        *force*        will emit signals regardless of value change. 
        =============  =============================================
        
        :returns: None
        
        """
        #self.log.debug("send_display_updates: {} force={}".format(self.name, force))
        if (not self.same_values(self.oldval, self.val)) or (force):
            self.updated_value[()].emit()
            
            str_val = self.string_value()
            self.updated_value[str].emit(str_val)
            self.updated_text_value.emit(str_val)
                
            if self.dtype in [float, int]:
                self.updated_value[float].emit(self.val)
                self.updated_value[int].emit(int(self.val))
            self.updated_value[bool].emit(bool(self.val))
            
            if self.choices is not None:
                choice_vals = [c[1] for c in self.choices]
                if self.val in choice_vals:
                    self.updated_choice_index_value.emit(choice_vals.index(self.val) )
            self.oldval = self.val
        else:
            # no updates sent
            pass
    
    def same_values(self, v1, v2):
        """ 
        Compares two values of the LQ type, used in update_value

        =============  ====================
        **Arguments**  **Description**
        v1             value 1
        v2             value 2
        =============  ====================
        
        :returns: Boolean value (True or False)

        """
        return v1 == v2
    
    def string_value(self):
        if self.dtype == str:
            return self.val
        else:
            return self.fmt % self.val

    def ini_string_value(self):
        """
        :returns: A string showing the logged quantity value.
        """
        return str(self.val)

    
    def update_choice_index_value(self, new_choice_index, **kwargs):
        self.update_value(self.choices[new_choice_index][1], **kwargs)
    
    def add_listener(self, func, argtype=(), **kwargs):
        """
        Connect 'func' as a listener (Qt Slot) for the 
        updated_value signal.
        By default 'func' should take no arguments,
        but argtype can define the data type that it should accept.
        but should be limited to those supported by LoggedQuantity 
        (i.e. int, float, str)
        \*\*kwargs are passed to the connect function
        appends the 'func' to the 'listeners' list
        """
        
        #    --> This is now handled by redefining sys.excepthook handle in ScopeFoundry.base_app
        # Wraps func in a try block to absorb the Exception to avoid crashing PyQt5 >5.5
        # see https://riverbankcomputing.com/pipermail/pyqt/2016-March/037134.html
        #         def wrapped_func(func):
        #             def f(*args):
        #                 try:
        #                     func(*args)
        #                 except Exception as err:
        #                     print "Exception on listener:"
        
        self.updated_value[argtype].connect(func, **kwargs)
        self.listeners.append(func)

    def connect_bidir_to_widget(self, widget):
        # DEPRECATED
        return self.connect_to_widget(widget)

    def connect_to_widget(self, widget):
        """
        Creates Qt signal-slot connections between LQ and the QtWidget *widget*
        
        connects updated_value signal to the appropriate slot depending on 
        the type of widget 
        
        Makes a bidirectional connection to a QT widget, ie when LQ is updated,
        widget gets a signal and when widget is updated, the LQ receives a
        signal and update_value() slot is called.
        
        Handles many types of widgets:
         * QDoubleSpinBox
         * QCheckBox
         * QLineEdit
         * QComboBox
         * pyqtgraph.widgets.SpinBox.SpinBox 

        =============  ====================================================================
        **Arguments**  **Description**
        widget         The name of the Qt GUI Object, examples of which are listed above.
                       For example, if you have a QDoubleSpinBox in the gui which you 
                       renamed int_value_doubleSpinBox in the Qt Designer Object Inspector, 
                       use self.ui.int_value_doubleSpinBox
        =============  ====================================================================
        
        :returns: None

        """

        if type(widget) == QtWidgets.QDoubleSpinBox:

            widget.setKeyboardTracking(False)
            if self.vmin is not None:
                widget.setMinimum(self.vmin)
            if self.vmax is not None:
                widget.setMaximum(self.vmax)
            if self.unit is not None:
                widget.setSuffix(" "+self.unit)
            widget.setDecimals(self.spinbox_decimals)
            widget.setSingleStep(self.spinbox_step)
            widget.setValue(self.val)
            #events
            def update_widget_value(x):
                """
                block signals from widget when value is set via lq.update_value.
                This prevents signal-slot loops between widget and lq
                """
                try:
                    widget.blockSignals(True)
                    widget.setValue(x)
                finally:
                    widget.blockSignals(False)                    
            #self.updated_value[float].connect(widget.setValue)
            self.updated_value[float].connect(update_widget_value)
            #if not self.ro:
            widget.valueChanged[float].connect(self.update_value)
                
        elif type(widget) == QtWidgets.QSlider:
            self.vrange = self.vmax - self.vmin
            def transform_to_slider(x):
                pct = 100*(x-self.vmin)/self.vrange
                return int(pct)
            def transform_from_slider(x):
                val = self.vmin + (x*self.vrange/100)
                return val
            def update_widget_value(x):
                """
                block signals from widget when value is set via lq.update_value.
                This prevents signal-slot loops between widget and lq
                """
                try:
                    widget.blockSignals(True)
                    widget.setValue(transform_to_slider(x))
                finally:
                    widget.blockSignals(False)
                    
            def update_spinbox(x):
                self.update_value(transform_from_slider(x))    
            if self.vmin is not None:
                widget.setMinimum(transform_to_slider(self.vmin))
            if self.vmax is not None:
                widget.setMaximum(transform_to_slider(self.vmax))
            widget.setSingleStep(1)
            widget.setValue(transform_to_slider(self.val))
            self.updated_value[float].connect(update_widget_value)
            widget.valueChanged[int].connect(update_spinbox)

                
        elif type(widget) == QtWidgets.QCheckBox:

            def update_widget_value(x):
                try:
                    widget.blockSignals(True)
                    widget.setChecked(x)
                finally:
                    widget.blockSignals(False)                    

            self.updated_value[bool].connect(update_widget_value)
            widget.toggled[bool].connect(self.update_value) # another option is stateChanged signal
            if self.ro:
                #widget.setReadOnly(True)
                widget.setEnabled(False)
                
        elif type(widget) == QtWidgets.QLineEdit:
            self.updated_text_value[str].connect(widget.setText)
            if self.ro:
                widget.setReadOnly(True)  # FIXME
            def on_edit_finished():
                self.log.debug(self.name + " qLineEdit on_edit_finished")
                try:
                    widget.blockSignals(True)
                    self.update_value(widget.text())
                finally:
                    widget.blockSignals(False)
            widget.editingFinished.connect(on_edit_finished)
            
        elif type(widget) == QtWidgets.QPlainTextEdit:
            self.updated_text_value[str].connect(widget.document().setPlainText)
            # TODO Read only
            def on_widget_textChanged():
                try:
                    widget.blockSignals(True)
                    self.update_value(widget.toPlainText())
                finally:
                    widget.blockSignals(False)
            widget.textChanged.connect(on_widget_textChanged)
            
        elif type(widget) == QtWidgets.QComboBox:
            # need to have a choice list to connect to a QComboBox
            assert self.choices is not None 
            widget.clear() # removes all old choices
            for choice_name, choice_value in self.choices:
                widget.addItem(choice_name, choice_value)
            self.updated_choice_index_value[int].connect(widget.setCurrentIndex)
            widget.currentIndexChanged.connect(self.update_choice_index_value)
            
        elif type(widget) == pyqtgraph.widgets.SpinBox.SpinBox:
            #widget.setFocusPolicy(QtCore.Qt.StrongFocus)
            suffix = self.unit
            if self.unit is None:
                suffix = ""
            if self.dtype == int:
                integer = True
                minStep=1
                step=1
            else:
                integer = False
                minStep=.1
                step=.1
            opts = dict(
                        suffix=suffix,
                        siPrefix=True,
                        dec=True,
                        step=step,
                        minStep=minStep,
                        bounds=[self.vmin, self.vmax],
                        int=integer)
            if self.si:
                del opts['step']
                del opts['minStep']
            
            widget.setOpts(**opts)
                      
            if self.ro:
                widget.setEnabled(False)
                widget.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons)
                widget.setReadOnly(True)
            #widget.setDecimals(self.spinbox_decimals)
            if not self.si:
                widget.setSingleStep(self.spinbox_step)
            #self.updated_value[float].connect(widget.setValue)
            #if not self.ro:
                #widget.valueChanged[float].connect(self.update_value)
            def update_widget_value(x):
                """
                block signals from widget when value is set via lq.update_value.
                This prevents signal loops
                """
                try:
                    widget.blockSignals(True)
                    widget.setValue(x)
                finally:
                    widget.blockSignals(False)                    
            self.updated_value[float].connect(update_widget_value)
            def on_widget_update(_widget):
                self.update_value(_widget.value())
            widget.sigValueChanged.connect(on_widget_update)

        elif type(widget) == QtWidgets.QLabel:
            self.updated_text_value.connect(widget.setText)
        elif type(widget) == QtWidgets.QProgressBar:
            def set_progressbar(x, widget=widget):
                self.log.debug("set_progressbar {}".format(x))
                widget.setValue(int(x))
            self.updated_value.connect(set_progressbar)
        else:
            raise ValueError("Unknown widget type")
        
        self.send_display_updates(force=True)
        #self.widget = widget
        self.widget_list.append(widget)
        self.change_readonly(self.ro)
        
        
    def connect_to_lq(self, lq):
        self.updated_value.connect(lq.update_value)
        lq.updated_value.connect(self.update_value)
        
    
    def change_choice_list(self, choices):
        #widget = self.widget
        with self.lock:
            self.choices = self._expand_choices(choices)
            
            for widget in self.widget_list:
                if type(widget) == QtWidgets.QComboBox:
                    # need to have a choice list to connect to a QComboBox
                    assert self.choices is not None 
                    widget.clear() # removes all old choices
                    for choice_name, choice_value in self.choices:
                        widget.addItem(choice_name, choice_value)
                else:
                    raise RuntimeError("Invalid widget type.")
    
    def change_min_max(self, vmin=-1e12, vmax=+1e12):
        # TODO  setRange should be a slot for the updated_min_max signal
        with self.lock:
            self.vmin = vmin
            self.vmax = vmax
            for widget in self.widget_list: # may not work for certain widget types
                widget.setRange(vmin, vmax)
            self.updated_min_max.emit(vmin,vmax)
        
    def change_readonly(self, ro=True):
        with self.lock:
            self.ro = ro
            for widget in self.widget_list:
                if type(widget) in [QtWidgets.QDoubleSpinBox, pyqtgraph.widgets.SpinBox.SpinBox]:
                    widget.setReadOnly(self.ro)    
                #TODO other widget types
            self.updated_readonly.emit(self.ro)
            
    def change_unit(self, unit):
        with self.lock:
            self.unit = unit
            for widget in self.widget_list:
                if type(widget) == QtWidgets.QDoubleSpinBox:
                    if self.unit is not None:
                        widget.setSuffix(" "+self.unit)
                         
                elif type(widget) == pyqtgraph.widgets.SpinBox.SpinBox:
                    #widget.setFocusPolicy(QtCore.Qt.StrongFocus)
                    suffix = self.unit
                    if self.unit is None:
                        suffix = ""
                    opts = dict(
                                suffix=suffix)
                     
                    widget.setOpts(**opts)
    
    def is_connected_to_hardware(self):
        """
        :returns: True if either self.hardware_read_func or 
        self.hardware_set_func are defined. False if None.
        """
        return (self.hardware_read_func is not None) or (self.hardware_set_func is not None)
    
    def has_hardware_read(self):
        return self.hardware_read_func is not None
    
    def has_hardware_write(self):
        return self.hardware_set_func is not None

    def connect_to_hardware(self, read_func=None, write_func=None):
        if read_func is not None:
            assert callable(read_func) 
            self.hardware_read_func = read_func
        if write_func is not None:
            assert callable(write_func)
            self.hardware_set_func = write_func
    
    def disconnect_from_hardware(self, dis_read=True, dis_write=True):
        if dis_read:
            self.hardware_read_func = None
        if dis_write:
            self.hardware_set_func = None
Пример #22
0
class InterruptableTask(QtCore.QObject, Fysom, metaclass=TaskMetaclass):
    """ This class represents a task in a module that can be safely executed by checking preconditions
        and pausing other tasks that are being executed as well.
        The task can also be paused, given that the preconditions for pausing are met.

        State diagram for InterruptableTask:

        stopped -> starting -----------> running ---------> finishing -*
           ^          |            _______|   ^_________               |
           |<---------*            v                   |               v
           |                   pausing -> paused -> resuming           |
           |                      |                    |               |
           ^                      v                    v               |
           |-------------<--------|----------<---------|--------<-------

        Each state has a transition state that allow for checks, synchronizatuion and for parts of the task
        to influence its own execution via signals.
        This also allows the TaskRunner to be informed about what the task is doing and ensuring that a task
        is executed in the correct thread.
        """
    sigDoStart = QtCore.Signal()
    sigStarted = QtCore.Signal()
    sigNextTaskStep = QtCore.Signal()
    sigDoPause = QtCore.Signal()
    sigPaused = QtCore.Signal()
    sigDoResume = QtCore.Signal()
    sigResumed = QtCore.Signal()
    sigDoFinish = QtCore.Signal()
    sigFinished = QtCore.Signal()
    sigStateChanged = QtCore.Signal(object)

    prePostTasks = {}
    pauseTasks = {}
    requiredModules = []

    def __init__(self, name, runner, references, config, **kwargs):
        """ Create an Interruptable task.
          @param str name: unique task name
          @param object runner: reference to the TaskRunner managing this task
          @param dict references: a dictionary of all required modules
          @param dict config: configuration dictionary
        """
        default_callbacks = {
            'onrun': self._start,
            'onpause': self._pause,
            'onresume': self._resume,
            'onfinish': self._finish
        }
        _stateDict = {
            'initial':
            'stopped',
            'events': [{
                'name': 'run',
                'src': 'stopped',
                'dst': 'starting'
            }, {
                'name': 'startingFinished',
                'src': 'starting',
                'dst': 'running'
            }, {
                'name': 'pause',
                'src': 'running',
                'dst': 'pausing'
            }, {
                'name': 'pausingFinished',
                'src': 'pausing',
                'dst': 'paused'
            }, {
                'name': 'finish',
                'src': 'running',
                'dst': 'finishing'
            }, {
                'name': 'finishingFinished',
                'src': 'finishing',
                'dst': 'stopped'
            }, {
                'name': 'resume',
                'src': 'paused',
                'dst': 'resuming'
            }, {
                'name': 'resumingFinished',
                'src': 'resuming',
                'dst': 'running'
            }, {
                'name': 'abort',
                'src': 'pausing',
                'dst': 'stopped'
            }, {
                'name': 'abort',
                'src': 'starting',
                'dst': 'stopped'
            }, {
                'name': 'abort',
                'src': 'resuming',
                'dst': 'stopped'
            }],
            'callbacks':
            default_callbacks
        }
        if 'PyQt5' in sys.modules:
            super().__init__(cfg=_stateDict, **kwargs)
        else:
            QtCore.QObject.__init__(self)
            Fysom.__init__(self, _stateDict)

        self.lock = Mutex()
        self.name = name
        self.interruptable = False
        self.success = False
        self.runner = runner
        self.ref = references
        self.config = config

        self.sigDoStart.connect(self._doStart, QtCore.Qt.QueuedConnection)
        self.sigDoPause.connect(self._doPause, QtCore.Qt.QueuedConnection)
        self.sigDoResume.connect(self._doResume, QtCore.Qt.QueuedConnection)
        self.sigDoFinish.connect(self._doFinish, QtCore.Qt.QueuedConnection)
        self.sigNextTaskStep.connect(self._doTaskStep,
                                     QtCore.Qt.QueuedConnection)

    @property
    def log(self):
        """
        Returns a logger object
        """
        return logging.getLogger("{0}.{1}".format(self.__module__,
                                                  self.__class__.__name__))

    def onchangestate(self, e):
        """ Fysom callback for state transition.

          @param object e: Fysom state transition description
        """
        self.sigStateChanged.emit(e)

    def _start(self, e):
        """
          @param object e: Fysom state transition description

          @return bool: True if task was started, False otherwise
        """
        self.result = TaskResult()
        if self.checkStartPrerequisites():
            #print('_run', QtCore.QThread.currentThreadId(), self.current)
            self.sigDoStart.emit()
            #print('_runemit', QtCore.QThread.currentThreadId(), self.current)
            return True
        else:
            return False

    def _doStart(self):
        """ Starting prerequisites were met, now do the actual start action.
        """
        try:
            #print('dostart', QtCore.QThread.currentThreadId(), self.current)
            self.runner.pausePauseTasks(self)
            self.runner.preRunPPTasks(self)
            self.startTask()
            self.startingFinished()
            self.sigStarted.emit()
            self.sigNextTaskStep.emit()
        except Exception as e:
            self.log.exception('Exception during task {0}. {1}'.format(
                self.name, e))
            self.result.update(None, False)

    def _doTaskStep(self):
        """ Check for state transitions to pause or stop and execute one step of the task work function.
        """
        try:
            if self.runTaskStep():
                if self.isstate('pausing') and self.checkPausePrerequisites():
                    self.sigDoPause.emit()
                elif self.isstate('finishing'):
                    self.sigDoFinish.emit()
                else:
                    self.sigNextTaskStep.emit()
            else:
                self.finish()
                self.sigDoFinish.emit()
        except Exception as e:
            self.log.exception('Exception during task step {0}. {1}'.format(
                self.name, e))
            self.result.update(None, False)
            self.finish()
            self.sigDoFinish.emit()

    def _pause(self, e):
        """ This does nothing, it is up to the TaskRunner to check that pausing is allowed and triger the next step.
        """
        pass

    def _doPause(self):
        """ Prerequisites for pausing were checked by Task runner and met, so execute the actual pausing action.
        """
        try:
            self.pauseTask()
            self.runner.postRunPPTasks(self)
            self.pausingFinished()
            self.sigPaused.emit()
        except Exception as e:
            self.log.exception('Exception while pausing task {}. '
                               '{}'.format(self.name, e))
            self.result.update(None, False)

    def _resume(self, e):
        """ Trigger resuming action.
        """
        self.sigDoResume.emit()

    def _doResume(self):
        """ Actually execute resuming action.
        """
        try:
            self.runner.preRunPPTasks(self)
            self.resumeTask()
            self.resumingFinished()
            self.sigResumed.emit()
            self.sigNextTaskStep.emit()
        except Exception as e:
            self.log.exception('Exception while resuming task {}. '
                               '{}'.format(self.name, e))
            self.result.update(None, False)

    def _finish(self, e):
        """ Do nothing, it is up to the TaskRunner to trigger the next step.
        """
        pass

    def _doFinish(self):
        """ Actually finish execution.
        """
        self.cleanupTask()
        self.runner.resumePauseTasks(self)
        self.runner.postRunPPTasks(self)
        self.finishingFinished()
        self.sigFinished.emit()

    def checkStartPrerequisites(self):
        """ Check whether this task can be started by checking if all tasks to be paused are either stopped or can be paused.
            Also check custom prerequisites.

          @return bool: True if task can be stated, False otherwise
        """
        for task in self.prePostTasks:
            if not (isinstance(self.prePostTasks[task], PrePostTask)
                    and self.prePostTasks[task].can('prerun')):
                self.log(
                    'Cannot start task {0} as pre/post task {1} is not in a state to run.'
                    .format(self.name, task),
                    msgType='error')
                return False
        for task in self.pauseTasks:
            if not (isinstance(self.pauseTasks[task], InterruptableTask) and
                    (self.pauseTasks[task].can('pause')
                     or self.pauseTasks[task].isstate('stopped'))):
                self.log(
                    'Cannot start task {0} as interruptable task {1} is not stopped or able to pause.'
                    .format(self.name, task),
                    msgType='error')
                return False
        if not self.checkExtraStartPrerequisites():
            return False
        return True

    def checkExtraStartPrerequisites(self):
        """ If your task has extra prerequisites that are not covered by
            checking if a certain task can be paused, overwrite this function
            when sub-classing.

        @return bool: return True if task can be started, False otherwise
        """
        return True

    def checkPausePrerequisites(self):
        """ Check if task is allowed to pause based on external state."""

        try:
            return self.checkExtraPausePrerequisites()
        except Exception as e:
            self.log.exception('Exception while checking pause '
                               'prerequisites for task {}. {}'.format(
                                   self.name, e))
            return False

    def checkExtraPausePrerequisites(self):
        """ If yout task has prerequisites for pausing, overwrite this function when subclassing and put the check here.

          @return bool: return True if task can be paused right now, False otherwise
        """
        return True

    def canPause(self):
        """ Check if task can pause based on its own state only.
        """
        return self.interruptable and self.can(
            'pause') and self.checkPausePrerequisites()

    @abc.abstractmethod
    def startTask(self):
        """ Implement the operation to start your task here.
        """
        pass

    @abc.abstractmethod
    def runTaskStep(self):
        """ Implement one work step of your task here.
          @return bool: True if the task should continue running, False if it should finish.
        """
        return False

    @abc.abstractmethod
    def pauseTask(self):
        """ Implement the operations necessary to pause your task here.
        """
        pass

    @abc.abstractmethod
    def resumeTask(self):
        """ Implement the operations necessary to resume your task from being paused here.
        """
        pass

    @abc.abstractmethod
    def cleanupTask(self):
        """ If your task leaves behind any undesired state, take care to remove it in this function.
            It is called after a task has finished.
        """
        pass
Пример #23
0
class ArrayLQ(LoggedQuantity):
    updated_shape = QtCore.Signal(str)
    
    def __init__(self, name, dtype=float, 
                 hardware_read_func=None, hardware_set_func=None, 
                 initial=[], fmt="%g", si=True,
                 ro = False,
                 unit = None,
                 vmin=-1e12, vmax=+1e12, choices=None):
        QtCore.QObject.__init__(self)
        
        self.name = name
        self.dtype = dtype
        if self.dtype == str:
            self.val = np.array(initial, dtype=object)
        else:
            self.val = np.array(initial, dtype=dtype)
        self.hardware_read_func = hardware_read_func
        self.hardware_set_func = hardware_set_func
        self.fmt = fmt # % string formatting string. This is ignored if dtype==str
        if self.dtype == str:
            self.fmt = "%s"
        self.unit = unit
        self.vmin = vmin
        self.vmax = vmax
        self.ro = ro # Read-Only
        
        self.log = get_logger_from_class(self)

        if self.dtype == int:
            self.spinbox_decimals = 0
        else:
            self.spinbox_decimals = 2
        self.reread_from_hardware_after_write = False
        
        self.oldval = None
        
        self._in_reread_loop = False # flag to prevent reread from hardware loops
        
        self.widget_list = []
        self.listeners = []

        # threading lock
        self.lock = QLock(mode=0) # mode 0 is non-reentrant lock
        
        self.is_array = True
        
        self._tableView = None
        
    
        

    def same_values(self, v1, v2):
        if v1.shape == v2.shape:
            return np.all(v1 == v2)
            self.log.debug("same_values %s %s" % (v2-v1, np.all(v1 == v2)))        
        else:
            return False
            



    def change_shape(self, newshape):
        #TODO
        pass
 
    def string_value (self):
        return json.dumps(self.val.tolist())
    
    def ini_string_value(self):
        return json.dumps(self.val.tolist())
    
    def coerce_to_type(self, x):
        #print type(x)
        if type(x) in (unicode, str):
            x = json.loads(x)
            #print repr(x)
        return np.array(x, dtype=self.dtype)
    
    def send_display_updates(self, force=False):
        with self.lock:            
            self.log.debug(self.name + ' send_display_updates')
            #print "send_display_updates: {} force={}".format(self.name, force)
            if force or np.any(self.oldval != self.val):
                
                #print "send display updates", self.name, self.val, self.oldval
                str_val = self.string_value()
                self.updated_value[str].emit(str_val)
                self.updated_text_value.emit(str_val)
                    
                #self.updated_value[float].emit(self.val)
                #if self.dtype != float:
                #    self.updated_value[int].emit(self.val)
                #self.updated_value[bool].emit(self.val)
                self.updated_value[()].emit()
                
                self.oldval = self.val
            else:
                pass
                #print "\t no updates sent", (self.oldval != self.val) , (force), self.oldval, self.val
    
    @property
    def array_tableView(self):
        if self._tableView == None:
            self._tableView  = self.create_tableView()
            self._tableView.setWindowTitle(self.name)
        return self._tableView
    
    def create_tableView(self, **kwargs):
        widget = QtWidgets.QTableView()
        #widget.horizontalHeader().hide()
        #widget.verticalHeader().hide()
        model = ArrayLQ_QTableModel(self, transpose=(len(self.val.shape) == 1), **kwargs)
        widget.setModel(model)
        return widget
Пример #24
0
class MeasurementDialog(QtWidgets.QDialog):
    measurements_available = QtCore.Signal(object)

    def __init__(self, nwa, parent=None):
        super(MeasurementDialog, self).__init__(parent)
        self.setWindowTitle("MeasurementDialog")
        self.horizontalLayout_main = QtWidgets.QHBoxLayout(self)

        self.verticalLayout_left = QtWidgets.QVBoxLayout()

        self.groupBox_options = QtWidgets.QGroupBox("Options", self)
        self.lineEdit_namePrefix = QtWidgets.QLineEdit(self)
        self.label_namePrefix = QtWidgets.QLabel("Name Prefix:")
        self.horizontalLayout_namePrefix = QtWidgets.QHBoxLayout()
        self.horizontalLayout_namePrefix.addWidget(self.label_namePrefix)
        self.horizontalLayout_namePrefix.addWidget(self.lineEdit_namePrefix)
        self.label_timeout = QtWidgets.QLabel("Timeout (ms)", self)
        self.spinBox_timeout = QtWidgets.QSpinBox(self)
        self.spinBox_timeout.setMinimum(100)
        self.spinBox_timeout.setMaximum(600000)
        try:
            self.spinBox_timeout.setValue(nwa.resource.timeout)
        except:
            self.spinBox_timeout.setValue(3000)
        self.spinBox_timeout.setSingleStep(1000)
        self.horizontalLayout_timeout = QtWidgets.QHBoxLayout()
        self.horizontalLayout_timeout.addWidget(self.label_timeout)
        self.horizontalLayout_timeout.addWidget(self.spinBox_timeout)
        self.checkBox_sweepNew = QtWidgets.QCheckBox("Sweep New",
                                                     self.groupBox_options)
        self.checkBox_autoTimeOut = QtWidgets.QCheckBox(
            "Auto Timeout", self.groupBox_options)
        self.horizonatlLayout_sweep = QtWidgets.QHBoxLayout()
        self.horizonatlLayout_sweep.addWidget(self.checkBox_sweepNew)
        self.horizonatlLayout_sweep.addWidget(self.checkBox_autoTimeOut)
        self.label_channel = QtWidgets.QLabel("Channel", self.groupBox_options)
        self.spinBox_channel = QtWidgets.QSpinBox(self.groupBox_options)
        self.horizontalLayout_channel = QtWidgets.QHBoxLayout()
        self.horizontalLayout_channel.addWidget(self.label_channel)
        self.horizontalLayout_channel.addWidget(self.spinBox_channel)

        self.verticalLayout_options = QtWidgets.QVBoxLayout(
            self.groupBox_options)
        self.verticalLayout_options.addLayout(self.horizontalLayout_namePrefix)
        self.verticalLayout_options.addLayout(self.horizontalLayout_timeout)
        self.verticalLayout_options.addLayout(self.horizonatlLayout_sweep)
        self.verticalLayout_options.addLayout(self.horizontalLayout_channel)
        self.verticalLayout_left.addWidget(self.groupBox_options)

        self.groupBox_snp = QtWidgets.QGroupBox("Get N-Port Network", self)
        self.verticalLayout_snp = QtWidgets.QVBoxLayout(self.groupBox_snp)
        self.label_ports = QtWidgets.QLabel("Ports:", self.groupBox_snp)
        self.lineEdit_ports = QtWidgets.QLineEdit(self.groupBox_snp)
        self.btn_measureSnp = QtWidgets.QPushButton("Measure Network",
                                                    self.groupBox_snp)
        self.horizontalLayout_nports = QtWidgets.QHBoxLayout()
        self.horizontalLayout_nports.addWidget(self.label_ports)
        self.horizontalLayout_nports.addWidget(self.lineEdit_ports)
        self.verticalLayout_snp.addWidget(self.btn_measureSnp)
        self.verticalLayout_snp.addLayout(self.horizontalLayout_nports)

        self.spacerItem = QtWidgets.QSpacerItem(
            20, 40, QtWidgets.QSizePolicy.Minimum,
            QtWidgets.QSizePolicy.Expanding)
        self.verticalLayout_snp.addItem(self.spacerItem)
        self.verticalLayout_left.addWidget(self.groupBox_snp)

        self.groupBox_traces = QtWidgets.QGroupBox("Available Traces", self)
        self.listWidget_traces = QtWidgets.QListWidget(self.groupBox_traces)
        self.listWidget_traces.setSelectionMode(
            QtWidgets.QAbstractItemView.ExtendedSelection)
        self.btn_updateTraces = QtWidgets.QPushButton("Update",
                                                      self.groupBox_traces)
        self.btn_measureTraces = QtWidgets.QPushButton("Measure Traces",
                                                       self.groupBox_traces)
        self.horizontalLayout_tracesButtons = QtWidgets.QHBoxLayout()
        self.horizontalLayout_tracesButtons.addWidget(self.btn_updateTraces)
        self.horizontalLayout_tracesButtons.addWidget(self.btn_measureTraces)

        self.verticalLayout_traces = QtWidgets.QVBoxLayout(
            self.groupBox_traces)
        self.verticalLayout_traces.addWidget(self.listWidget_traces)
        self.verticalLayout_traces.addLayout(
            self.horizontalLayout_tracesButtons)

        self.horizontalLayout_main.addLayout(self.verticalLayout_left)
        self.horizontalLayout_main.addWidget(self.groupBox_traces)

        self.nwa = nwa

        self.btn_updateTraces.clicked.connect(self.update_traces)
        self.btn_measureSnp.clicked.connect(self.measure_snp)
        self.btn_measureTraces.clicked.connect(self.measure_traces)

        if self.nwa.NCHANNELS:
            self.spinBox_channel.setValue(1)
            self.spinBox_channel.setMinimum(1)
            self.spinBox_channel.setMaximum(self.nwa.NCHANNELS)
        else:
            self.spinBox_channel.setEnabled(False)

        self.lineEdit_ports.setText(",".join(
            [str(port + 1) for port in range(self.nwa.nports)]))
        self.spinBox_timeout.valueChanged.connect(self.set_timeout)

    def set_timeout(self):
        self.nwa.resource.timeout = self.spinBox_timeout.value()

    def measure_traces(self):
        items = self.listWidget_traces.selectedItems()
        if len(items) < 1:
            print("nothing to measure")
            return

        traces = []
        for item in items:
            traces.append(item.trace)

        ntwks = self.nwa.get_traces(
            traces, name_prefix=self.lineEdit_namePrefix.text())
        self.measurements_available.emit(ntwks)

    def measure_snp(self):
        ports = self.lineEdit_ports.text().replace(" ", "").split(",")
        try:
            ports = [int(port) for port in ports]
        except Exception:
            qt.error_popup("Ports must be a comma separated list of integers")
            return

        kwargs = {
            "ports": ports,
            "channel": self.spinBox_channel.value(),
            "sweep": self.checkBox_sweepNew.isChecked(),
            "name": self.lineEdit_namePrefix.text()
        }
        if self.checkBox_autoTimeOut.isChecked():
            kwargs["timeout"] = self.spinBox_timeout.value()

        ntwk = self.nwa.get_snp_network(**kwargs)
        self.measurements_available.emit(ntwk)

    def update_traces(self):
        traces = self.nwa.get_list_of_traces()
        self.listWidget_traces.clear()
        for trace in traces:
            item = QtWidgets.QListWidgetItem()
            item.setText(trace["label"])
            item.trace = trace
            self.listWidget_traces.addItem(item)
Пример #25
0
class LQRange(QtCore.QObject):
    """
    LQRange is a collection of logged quantities that describe a
    numpy.linspace array inputs
    Four LQ's are defined, min, max, num, step
    and are connected by signals/slots that keep the quantities
    in sync.
    LQRange.array is the linspace array and is kept upto date
    with changes to the 4 LQ's
    """
    updated_range = QtCore.Signal((),)# (float,),(int,),(bool,), (), (str,),) # signal sent when value has been updated
    
    def __init__(self, min_lq,max_lq,step_lq, num_lq, center_lq=None, span_lq=None):
        QtCore.QObject.__init__(self)
        self.log = get_logger_from_class(self)

        self.min = min_lq
        self.max = max_lq
        self.num = num_lq
        self.step = step_lq
        self.center = center_lq
        self.span = span_lq
        
        assert self.num.dtype == int
        
        self._array_valid = False # Internal _array invalid, must be computed on next request
        
        self._array = None #np.linspace(self.min.val, self.max.val, self.num.val)
        
        #step = self._array[1]-self._array[0]
        step = self.compute_step(self.min.val, self.max.val, self.num.val)
        self.step.update_value(step)
        
        self.num.updated_value[int].connect(self.recalc_with_new_num)
        self.min.updated_value.connect(self.recalc_with_new_min_max)
        self.max.updated_value.connect(self.recalc_with_new_min_max)
        self.step.updated_value.connect(self.recalc_with_new_step)
        
        if self.center and self.span:
            self.center.updated_value.connect(self.recalc_with_new_center_span)
            self.span.updated_value.connect(self.recalc_with_new_center_span)


    @property
    def array(self):
        if self._array_valid:
            return self._array
        else:
            self._array = np.linspace(self.min.val, self.max.val, self.num.val)
            self._array_valid = True
            return self._array

    def compute_step(self, xmin, xmax, num):
        delta = xmax - xmin
        if num > 1:
            return delta/(num-1)
        else:
            return delta

    def recalc_with_new_num(self, new_num):
        self.log.debug("recalc_with_new_num {}".format( new_num))
        self._array_valid = False
        self._array = None
        #self._array = np.linspace(self.min.val, self.max.val, int(new_num))
        new_step = self.compute_step(self.min.val, self.max.val, int(new_num))
        self.log.debug( "    new_step inside new_num {}".format( new_step))
        self.step.update_value(new_step)#, send_signal=True, update_hardware=False)
        self.step.send_display_updates(force=True)
        self.updated_range.emit()
        
    def recalc_with_new_min_max(self, x):
        self._array_valid = False
        self._array = None
        #self._array = np.linspace(self.min.val, self.max.val, self.num.val)
        #step = self._array[1]-self._array[0]
        step = self.compute_step(self.min.val, self.max.val, self.num.val)
        self.step.update_value(step)#, send_signal=True, update_hardware=False)
        if self.center:
            self.span.update_value(0.5*(self.max.val-self.min.val) + self.min.val)
        if self.span:
            self.span.update_value(self.max.val-self.min.val)
        self.updated_range.emit()
        
    def recalc_with_new_step(self,new_step):
        #print "-->recalc_with_new_step"
        if self.num.val > 1:
            #old_step = self._array[1]-self._array[0]
            old_step = self.compute_step(self.min.val, self.max.val, self.num.val)
        else:
            old_step = np.nan
        sdiff = np.abs(old_step - new_step)
        #print "step diff", sdiff
        if sdiff < 10**(-self.step.spinbox_decimals):
            #print "steps close enough, no more recalc"
            return
        else:
            self._array_valid = False
            self._array = None
            new_num = int((((self.max.val - self.min.val)/new_step)+1))
            #self._array = np.linspace(self.min.val, self.max.val, new_num)
            #new_step1 = self._array[1]-self._array[0]
            new_step1 = self.compute_step(self.min.val, self.max.val, new_num)
            
            #print "recalc_with_new_step", new_step, new_num, new_step1
            #self.step.val = new_step1
            #self.num.val = new_num
            #self.step.update_value(new_step1, send_signal=False)
            #if np.abs(self.step.val - new_step1)/self.step.val > 1e-2:
            self.step.val = new_step1
            self.num.update_value(new_num)
            #self.num.send_display_updates(force=True)
            #self.step.update_value(new_step1)

            #print "sending step display Updates"
            #self.step.send_display_updates(force=True)
            self.updated_range.emit()
            
    def recalc_with_new_center_span(self,x):
        C = self.center.val
        S = self.span.val
        self.min.update_value( C - 0.5*S)
        self.max.update_value( C + 0.5*S)
Пример #26
0
class ResultsTabView(QtWidgets.QWidget, ui_fitting_tab):
    # Public signals
    function_selection_changed = QtCore.Signal()
    results_name_edited = QtCore.Signal()
    output_results_requested = QtCore.Signal()

    def __init__(self, parent=None):
        super(ResultsTabView, self).__init__(parent)
        self._init_layout()
        self._init_signals()

    def set_output_results_button_enabled(self, on):
        """
        Set the status of the output results button
        :param on: If True then enable the button otherwise disable it
        """
        self.output_results_table_btn.setEnabled(on)

    def results_table_name(self):
        """Return the name of the output table."""
        return self.results_name_editor.text()

    def set_results_table_name(self, name):
        """Set the name of the output table.

        :param name: A new name for the table
        """
        self.results_name_editor.setText(name)

    def set_fit_function_names(self, names):
        """Set a new list of function names for the function selector. This blocks
        signals from the widget during the update

        :param names: A list of strings specifying function names used in known fits
        """
        function_selector = self.fit_function_selector
        original_selection = function_selector.currentText()
        function_selector.blockSignals(True)
        function_selector.clear()
        function_selector.addItems(names)
        if original_selection in names:
            self.set_selected_fit_function(original_selection)
        function_selector.blockSignals(False)

    def selected_result_workspaces(self):
        """
        :return: The list of selected workspaces and their positions in the list
        """
        return self.fit_selector_presenter.get_selected_items_and_positions()

    def fit_result_workspaces(self):
        """
        :return: The current state of the workspace list selector
        """
        return self.fit_selector_presenter.model

    def set_fit_result_workspaces(self, workspace_list_state):
        """Set the map of workspaces

        :param workspace_list_state: Dictionary containing the updated
        state for the workspace list selector
        """
        self.fit_selector_presenter.update_model(workspace_list_state)

    def set_selected_fit_function(self, function):
        """
        Set the fit function in the QComboBox
        """
        if PYQT4:
            self.fit_function_selector.setCurrentIndex(
                self.fit_function_selector.findText(function))
        else:
            self.fit_function_selector.setCurrentText(function)

    def selected_fit_function(self):
        """Return the text of the selected item in the function
        selection box"""
        return self.fit_function_selector.currentText()

    def selected_log_values(self):
        """
        :return: A list of selected log values and their positions in the list
        """
        return self.log_selector_presenter.get_selected_items()

    def log_values(self):
        """
        :return: The current state of the log list selector
        """
        return self.log_selector_presenter.model

    def set_log_values(self, logs_list_state):
        """Set the map of log values and selected status

        :param logs_list_state: Dictionary containing the updated
        state for the workspace list selector
        """
        self.log_selector_presenter.update_model(logs_list_state)

    def show_warning(self, msg):
        """
        Display a warning on that something went wrong
        :param msg: The message to include
        """
        warning(msg, self)

    # Private methods
    def _init_layout(self):
        """Setup the layout of the view"""
        self.setupUi(self)

        self.log_selector_presenter = _create_empty_list_selector(
            self, LOG_SELECTOR_COL0_WIDTH)
        self.log_value_layout.addWidget(self.log_selector_presenter.view,
                                        *FIT_SELECTOR_GRID_POS)
        self.fit_selector_presenter = _create_empty_list_selector(
            self, FIT_SELECTOR_COL0_WIDTH)
        self.fit_layout.addWidget(self.fit_selector_presenter.view,
                                  *LOG_SELECTOR_GRID_POS)

    def _init_signals(self):
        """Connect internal signals to external notifiers"""
        self.fit_function_selector.currentIndexChanged.connect(
            self.function_selection_changed)
        self.results_name_editor.editingFinished.connect(
            self.results_name_edited)
        self.output_results_table_btn.clicked.connect(
            self.output_results_requested)
Пример #27
0
class Parameter(QtCore.QObject):
    changed = QtCore.Signal(object)

    def __init__(self, name=None, label=None, label_above=False, **kwargs):
        super().__init__()
        self.name = name
        self.label = label
        self._label_above = label_above
        self.__options = kwargs if kwargs else {}
        if _have_qt:
            self._createWithLabel()

    def _attachTo(self, obj):
        if self.name:
            obj._par_name_dict[self.name] = self

    def setVisible(self, val):
        if _have_qt:
            self._widget._visible = val
            self._widget.setVisible(val)

    def isVisible(self):
        return self._widget._visible if hasattr(self._widget,
                                                "_visible") else True

    def _createWithLabel(self):
        if self.label:
            self._widget = QtWidgets.QWidget()
            arrange = ArrangeV if self._label_above else ArrangeH
            layout = arrange(QtWidgets.QLabel(self.label),
                             self._createWidget())
            layout.setContentsMargins(0, 0, 0, 0)
            self._widget.setLayout(layout)
        else:
            self._widget = self._createWidget()

    def _createWidget(self):
        raise NotImplementedError(
            "Parameter class must overload _createWidget!")

    def getOption(self, name):
        if not name in self.__options:
            return False
        else:
            return self.__options[name]

    def setValue(self, val=None):
        self.changed.emit(val)

    def getWidget(self):
        return self._widget

    def __getstate__(self):
        return (self.name, self.label, self.__options, self._label_above)

    def __setstate__(self, state):
        Parameter.__init__(self,
                           name=state[0],
                           label=state[1],
                           label_above=state[3])
        self.__options = state[2]
Пример #28
0
class Canvas(QtWidgets.QWidget):

    zoomRequest = QtCore.Signal(int, QtCore.QPoint)
    scrollRequest = QtCore.Signal(int, int)
    newShape = QtCore.Signal()
    selectionChanged = QtCore.Signal(bool)
    shapeMoved = QtCore.Signal()
    drawingPolygon = QtCore.Signal(bool)
    edgeSelected = QtCore.Signal(bool)

    CREATE, EDIT = 0, 1

    # polygon, rectangle, line, or point
    _createMode = 'polygon'

    _fill_drawing = False

    def __init__(self, *args, **kwargs):
        self.epsilon = kwargs.pop('epsilon', 10.0)
        super(Canvas, self).__init__(*args, **kwargs)
        # Initialise local state.
        self.mode = self.EDIT
        self.shapes = []
        self.shapesBackups = []
        self.current = None
        self.selectedShape = None  # save the selected shape here
        self.selectedShapeCopy = None
        self.lineColor = QtGui.QColor(0, 0, 255)
        # self.line represents:
        #   - createMode == 'polygon': edge from last point to current
        #   - createMode == 'rectangle': diagonal line of the rectangle
        #   - createMode == 'line': the line
        #   - createMode == 'point': the point
        self.line = Shape(line_color=self.lineColor)
        self.prevPoint = QtCore.QPoint()
        self.prevMovePoint = QtCore.QPoint()
        self.offsets = QtCore.QPoint(), QtCore.QPoint()
        self.scale = 1.0
        self.pixmap = QtGui.QPixmap()
        self.visible = {}
        self._hideBackround = False
        self.hideBackround = False
        self.hShape = None
        self.hVertex = None
        self.hEdge = None
        self.movingShape = False
        self._painter = QtGui.QPainter()
        self._cursor = CURSOR_DEFAULT
        # Menus:
        self.menus = (QtWidgets.QMenu(), QtWidgets.QMenu())
        # Set widget options.
        self.setMouseTracking(True)
        self.setFocusPolicy(QtCore.Qt.WheelFocus)

    def fillDrawing(self):
        return self._fill_drawing

    def setFillDrawing(self, value):
        self._fill_drawing = value

    @property
    def createMode(self):
        return self._createMode

    @createMode.setter
    def createMode(self, value):
        if value not in ['polygon', 'rectangle', 'circle',
           'line', 'point', 'linestrip']:
            raise ValueError('Unsupported createMode: %s' % value)
        self._createMode = value

    def storeShapes(self):
        shapesBackup = []
        for shape in self.shapes:
            shapesBackup.append(shape.copy())
        if len(self.shapesBackups) >= 10:
            self.shapesBackups = self.shapesBackups[-9:]
        self.shapesBackups.append(shapesBackup)

    @property
    def isShapeRestorable(self):
        if len(self.shapesBackups) < 2:
            return False
        return True

    def restoreShape(self):
        if not self.isShapeRestorable:
            return
        self.shapesBackups.pop()  # latest
        shapesBackup = self.shapesBackups.pop()
        self.shapes = shapesBackup
        self.storeShapes()
        self.repaint()

    def enterEvent(self, ev):
        self.overrideCursor(self._cursor)

    def leaveEvent(self, ev):
        self.restoreCursor()

    def focusOutEvent(self, ev):
        self.restoreCursor()

    def isVisible(self, shape):
        return self.visible.get(shape, True)

    def drawing(self):
        return self.mode == self.CREATE

    def editing(self):
        return self.mode == self.EDIT

    def setEditing(self, value=True):
        self.mode = self.EDIT if value else self.CREATE
        if not value:  # Create
            self.unHighlight()
            self.deSelectShape()

    def unHighlight(self):
        if self.hShape:
            self.hShape.highlightClear()
        self.hVertex = self.hShape = None

    def selectedVertex(self):
        return self.hVertex is not None

    def mouseMoveEvent(self, ev):
        """Update line with last point and current coordinates."""
        try:
            if QT5:
                pos = self.transformPos(ev.pos())
            else:
                pos = self.transformPos(ev.posF())
        except AttributeError:
            return

        self.prevMovePoint = pos
        self.restoreCursor()

        # Polygon drawing.
        if self.drawing():
            self.line.shape_type = self.createMode

            self.overrideCursor(CURSOR_DRAW)
            if not self.current:
                return

            color = self.lineColor
            if self.outOfPixmap(pos):
                # Don't allow the user to draw outside the pixmap.
                # Project the point to the pixmap's edges.
                pos = self.intersectionPoint(self.current[-1], pos)
            elif len(self.current) > 1 and self.createMode == 'polygon' and\
                    self.closeEnough(pos, self.current[0]):
                # Attract line to starting point and
                # colorise to alert the user.
                pos = self.current[0]
                color = self.current.line_color
                self.overrideCursor(CURSOR_POINT)
                self.current.highlightVertex(0, Shape.NEAR_VERTEX)
            if self.createMode in ['polygon', 'linestrip']:
                self.line[0] = self.current[-1]
                self.line[1] = pos
            elif self.createMode == 'rectangle':
                self.line.points = [self.current[0], pos]
                self.line.close()
            elif self.createMode == 'circle':
                self.line.points = [self.current[0], pos]
                self.line.shape_type = "circle"
            elif self.createMode == 'line':
                self.line.points = [self.current[0], pos]
                self.line.close()
            elif self.createMode == 'point':
                self.line.points = [self.current[0]]
                self.line.close()
            self.line.line_color = color
            self.repaint()
            self.current.highlightClear()
            return

        # Polygon copy moving.
        if QtCore.Qt.RightButton & ev.buttons():
            if self.selectedShapeCopy and self.prevPoint:
                self.overrideCursor(CURSOR_MOVE)
                self.boundedMoveShape(self.selectedShapeCopy, pos)
                self.repaint()
            elif self.selectedShape:
                self.selectedShapeCopy = self.selectedShape.copy()
                self.repaint()
            return

        # Polygon/Vertex moving.
        self.movingShape = False
        if QtCore.Qt.LeftButton & ev.buttons():
            if self.selectedVertex():
                self.boundedMoveVertex(pos)
                self.repaint()
                self.movingShape = True
            elif self.selectedShape and self.prevPoint:
                self.overrideCursor(CURSOR_MOVE)
                self.boundedMoveShape(self.selectedShape, pos)
                self.repaint()
                self.movingShape = True
            return

        # Just hovering over the canvas, 2 posibilities:
        # - Highlight shapes
        # - Highlight vertex
        # Update shape/vertex fill and tooltip value accordingly.
        self.setToolTip("Image")
        for shape in reversed([s for s in self.shapes if self.isVisible(s)]):
            # Look for a nearby vertex to highlight. If that fails,
            # check if we happen to be inside a shape.
            index = shape.nearestVertex(pos, self.epsilon / self.scale)
            index_edge = shape.nearestEdge(pos, self.epsilon / self.scale)
            if index is not None:
                if self.selectedVertex():
                    self.hShape.highlightClear()
                self.hVertex = index
                self.hShape = shape
                self.hEdge = index_edge
                shape.highlightVertex(index, shape.MOVE_VERTEX)
                self.overrideCursor(CURSOR_POINT)
                self.setToolTip("Click & drag to move point")
                self.setStatusTip(self.toolTip())
                self.update()
                break
            elif shape.containsPoint(pos):
                if self.selectedVertex():
                    self.hShape.highlightClear()
                self.hVertex = None
                self.hShape = shape
                self.hEdge = index_edge
                self.setToolTip(
                    "Click & drag to move shape '%s'" % shape.label)
                self.setStatusTip(self.toolTip())
                self.overrideCursor(CURSOR_GRAB)
                self.update()
                break
        else:  # Nothing found, clear highlights, reset state.
            if self.hShape:
                self.hShape.highlightClear()
                self.update()
            self.hVertex, self.hShape, self.hEdge = None, None, None
        self.edgeSelected.emit(self.hEdge is not None)

    def addPointToEdge(self):
        if (self.hShape is None and
                self.hEdge is None and
                self.prevMovePoint is None):
            return
        shape = self.hShape
        index = self.hEdge
        point = self.prevMovePoint
        shape.insertPoint(index, point)
        shape.highlightVertex(index, shape.MOVE_VERTEX)
        self.hShape = shape
        self.hVertex = index
        self.hEdge = None

    def mousePressEvent(self, ev):
        if QT5:
            pos = self.transformPos(ev.pos())
        else:
            pos = self.transformPos(ev.posF())
        if ev.button() == QtCore.Qt.LeftButton:
            if self.drawing():
                if self.current:
                    # Add point to existing shape.
                    if self.createMode == 'polygon':
                        self.current.addPoint(self.line[1])
                        self.line[0] = self.current[-1]
                        if self.current.isClosed():
                            self.finalise()
                    elif self.createMode in ['rectangle', 'circle', 'line']:
                        assert len(self.current.points) == 1
                        self.current.points = self.line.points
                        self.finalise()
                    elif self.createMode == 'linestrip':
                        self.current.addPoint(self.line[1])
                        self.line[0] = self.current[-1]
                        if int(ev.modifiers()) == QtCore.Qt.ControlModifier:
                            self.finalise()
                elif not self.outOfPixmap(pos):
                    # Create new shape.
                    self.current = Shape(shape_type=self.createMode)
                    self.current.addPoint(pos)
                    if self.createMode == 'point':
                        self.finalise()
                    else:
                        if self.createMode == 'circle':
                            self.current.shape_type = 'circle'
                        self.line.points = [pos, pos]
                        self.setHiding()
                        self.drawingPolygon.emit(True)
                        self.update()
            else:
                self.selectShapePoint(pos)
                self.prevPoint = pos
                self.repaint()
        elif ev.button() == QtCore.Qt.RightButton and self.editing():
            self.selectShapePoint(pos)
            self.prevPoint = pos
            self.repaint()

    def mouseReleaseEvent(self, ev):
        if ev.button() == QtCore.Qt.RightButton:
            menu = self.menus[bool(self.selectedShapeCopy)]
            self.restoreCursor()
            if not menu.exec_(self.mapToGlobal(ev.pos()))\
               and self.selectedShapeCopy:
                # Cancel the move by deleting the shadow copy.
                self.selectedShapeCopy = None
                self.repaint()
        elif ev.button() == QtCore.Qt.LeftButton and self.selectedShape:
            self.overrideCursor(CURSOR_GRAB)
        if self.movingShape:
            self.storeShapes()
            self.shapeMoved.emit()

    def endMove(self, copy=False):
        assert self.selectedShape and self.selectedShapeCopy
        shape = self.selectedShapeCopy
        # del shape.fill_color
        # del shape.line_color
        if copy:
            self.shapes.append(shape)
            self.selectedShape.selected = False
            self.selectedShape = shape
            self.repaint()
        else:
            shape.label = self.selectedShape.label
            self.deleteSelected()
            self.shapes.append(shape)
        self.storeShapes()
        self.selectedShapeCopy = None

    def hideBackroundShapes(self, value):
        self.hideBackround = value
        if self.selectedShape:
            # Only hide other shapes if there is a current selection.
            # Otherwise the user will not be able to select a shape.
            self.setHiding(True)
            self.repaint()

    def setHiding(self, enable=True):
        self._hideBackround = self.hideBackround if enable else False

    def canCloseShape(self):
        return self.drawing() and self.current and len(self.current) > 2

    def mouseDoubleClickEvent(self, ev):
        # We need at least 4 points here, since the mousePress handler
        # adds an extra one before this handler is called.
        if self.canCloseShape() and len(self.current) > 3:
            self.current.popPoint()
            self.finalise()

    def selectShape(self, shape):
        self.deSelectShape()
        shape.selected = True
        self.selectedShape = shape
        self.setHiding()
        self.selectionChanged.emit(True)
        self.update()

    def selectShapePoint(self, point):
        """Select the first shape created which contains this point."""
        self.deSelectShape()
        if self.selectedVertex():  # A vertex is marked for selection.
            index, shape = self.hVertex, self.hShape
            shape.highlightVertex(index, shape.MOVE_VERTEX)
            return
        for shape in reversed(self.shapes):
            if self.isVisible(shape) and shape.containsPoint(point):
                shape.selected = True
                self.selectedShape = shape
                self.calculateOffsets(shape, point)
                self.setHiding()
                self.selectionChanged.emit(True)
                return

    def calculateOffsets(self, shape, point):
        rect = shape.boundingRect()
        x1 = rect.x() - point.x()
        y1 = rect.y() - point.y()
        x2 = (rect.x() + rect.width() - 1) - point.x()
        y2 = (rect.y() + rect.height() - 1) - point.y()
        self.offsets = QtCore.QPoint(x1, y1), QtCore.QPoint(x2, y2)

    def boundedMoveVertex(self, pos):
        index, shape = self.hVertex, self.hShape
        point = shape[index]
        if self.outOfPixmap(pos):
            pos = self.intersectionPoint(point, pos)
        shape.moveVertexBy(index, pos - point)

    def boundedMoveShape(self, shape, pos):
        if self.outOfPixmap(pos):
            return False  # No need to move
        o1 = pos + self.offsets[0]
        if self.outOfPixmap(o1):
            pos -= QtCore.QPoint(min(0, o1.x()), min(0, o1.y()))
        o2 = pos + self.offsets[1]
        if self.outOfPixmap(o2):
            pos += QtCore.QPoint(min(0, self.pixmap.width() - o2.x()),
                                 min(0, self.pixmap.height() - o2.y()))
        # XXX: The next line tracks the new position of the cursor
        # relative to the shape, but also results in making it
        # a bit "shaky" when nearing the border and allows it to
        # go outside of the shape's area for some reason.
        # self.calculateOffsets(self.selectedShape, pos)
        dp = pos - self.prevPoint
        if dp:
            shape.moveBy(dp)
            self.prevPoint = pos
            return True
        return False

    def deSelectShape(self):
        if self.selectedShape:
            self.selectedShape.selected = False
            self.selectedShape = None
            self.setHiding(False)
            self.selectionChanged.emit(False)
            self.update()

    def deleteSelected(self):
        if self.selectedShape:
            shape = self.selectedShape
            self.shapes.remove(self.selectedShape)
            self.storeShapes()
            self.selectedShape = None
            self.update()
            return shape

    def copySelectedShape(self):
        if self.selectedShape:
            shape = self.selectedShape.copy()
            self.deSelectShape()
            self.shapes.append(shape)
            self.storeShapes()
            shape.selected = True
            self.selectedShape = shape
            self.boundedShiftShape(shape)
            return shape

    def boundedShiftShape(self, shape):
        # Try to move in one direction, and if it fails in another.
        # Give up if both fail.
        point = shape[0]
        offset = QtCore.QPoint(2.0, 2.0)
        self.calculateOffsets(shape, point)
        self.prevPoint = point
        if not self.boundedMoveShape(shape, point - offset):
            self.boundedMoveShape(shape, point + offset)

    def paintEvent(self, event):
        if not self.pixmap:
            return super(Canvas, self).paintEvent(event)

        p = self._painter
        p.begin(self)
        p.setRenderHint(QtGui.QPainter.Antialiasing)
        p.setRenderHint(QtGui.QPainter.HighQualityAntialiasing)
        p.setRenderHint(QtGui.QPainter.SmoothPixmapTransform)

        p.scale(self.scale, self.scale)
        p.translate(self.offsetToCenter())

        p.drawPixmap(0, 0, self.pixmap)
        Shape.scale = self.scale
        for shape in self.shapes:
            if (shape.selected or not self._hideBackround) and \
                    self.isVisible(shape):
                shape.fill = shape.selected or shape == self.hShape
                shape.paint(p)
        if self.current:
            self.current.paint(p)
            self.line.paint(p)
        if self.selectedShapeCopy:
            self.selectedShapeCopy.paint(p)

        if (self.fillDrawing() and self.createMode == 'polygon' and
                self.current is not None and len(self.current.points) >= 2):
            drawing_shape = self.current.copy()
            drawing_shape.addPoint(self.line[1])
            drawing_shape.fill = True
            drawing_shape.fill_color.setAlpha(64)
            drawing_shape.paint(p)

        p.end()

    def transformPos(self, point):
        """Convert from widget-logical coordinates to painter-logical ones."""
        return point / self.scale - self.offsetToCenter()

    def offsetToCenter(self):
        s = self.scale
        area = super(Canvas, self).size()
        w, h = self.pixmap.width() * s, self.pixmap.height() * s
        aw, ah = area.width(), area.height()
        x = (aw - w) / (2 * s) if aw > w else 0
        y = (ah - h) / (2 * s) if ah > h else 0
        return QtCore.QPoint(x, y)

    def outOfPixmap(self, p):
        w, h = self.pixmap.width(), self.pixmap.height()
        return not (0 <= p.x() < w and 0 <= p.y() < h)

    def finalise(self):
        assert self.current
        self.current.close()
        self.shapes.append(self.current)
        self.storeShapes()
        self.current = None
        self.setHiding(False)
        self.newShape.emit()
        self.update()

    def closeEnough(self, p1, p2):
        # d = distance(p1 - p2)
        # m = (p1-p2).manhattanLength()
        # print "d %.2f, m %d, %.2f" % (d, m, d - m)
        # divide by scale to allow more precision when zoomed in
        return labelme.utils.distance(p1 - p2) < (self.epsilon / self.scale)

    def intersectionPoint(self, p1, p2):
        # Cycle through each image edge in clockwise fashion,
        # and find the one intersecting the current line segment.
        # http://paulbourke.net/geometry/lineline2d/
        size = self.pixmap.size()
        points = [(0, 0),
                  (size.width() - 1, 0),
                  (size.width() - 1, size.height() - 1),
                  (0, size.height() - 1)]
        x1, y1 = p1.x(), p1.y()
        x2, y2 = p2.x(), p2.y()
        d, i, (x, y) = min(self.intersectingEdges((x1, y1), (x2, y2), points))
        x3, y3 = points[i]
        x4, y4 = points[(i + 1) % 4]
        if (x, y) == (x1, y1):
            # Handle cases where previous point is on one of the edges.
            if x3 == x4:
                return QtCore.QPoint(x3, min(max(0, y2), max(y3, y4)))
            else:  # y3 == y4
                return QtCore.QPoint(min(max(0, x2), max(x3, x4)), y3)
        return QtCore.QPoint(x, y)

    def intersectingEdges(self, point1, point2, points):
        """Find intersecting edges.

        For each edge formed by `points', yield the intersection
        with the line segment `(x1,y1) - (x2,y2)`, if it exists.
        Also return the distance of `(x2,y2)' to the middle of the
        edge along with its index, so that the one closest can be chosen.
        """
        (x1, y1) = point1
        (x2, y2) = point2
        for i in range(4):
            x3, y3 = points[i]
            x4, y4 = points[(i + 1) % 4]
            denom = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1)
            nua = (x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)
            nub = (x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)
            if denom == 0:
                # This covers two cases:
                #   nua == nub == 0: Coincident
                #   otherwise: Parallel
                continue
            ua, ub = nua / denom, nub / denom
            if 0 <= ua <= 1 and 0 <= ub <= 1:
                x = x1 + ua * (x2 - x1)
                y = y1 + ua * (y2 - y1)
                m = QtCore.QPoint((x3 + x4) / 2, (y3 + y4) / 2)
                d = labelme.utils.distance(m - QtCore.QPoint(x2, y2))
                yield d, i, (x, y)

    # These two, along with a call to adjustSize are required for the
    # scroll area.
    def sizeHint(self):
        return self.minimumSizeHint()

    def minimumSizeHint(self):
        if self.pixmap:
            return self.scale * self.pixmap.size()
        return super(Canvas, self).minimumSizeHint()

    def wheelEvent(self, ev):
        if QT5:
            mods = ev.modifiers()
            delta = ev.angleDelta()
            if QtCore.Qt.ControlModifier == int(mods):
                # with Ctrl/Command key
                # zoom
                self.zoomRequest.emit(delta.y(), ev.pos())
            else:
                # scroll
                self.scrollRequest.emit(delta.x(), QtCore.Qt.Horizontal)
                self.scrollRequest.emit(delta.y(), QtCore.Qt.Vertical)
        else:
            if ev.orientation() == QtCore.Qt.Vertical:
                mods = ev.modifiers()
                if QtCore.Qt.ControlModifier == int(mods):
                    # with Ctrl/Command key
                    self.zoomRequest.emit(ev.delta(), ev.pos())
                else:
                    self.scrollRequest.emit(
                        ev.delta(),
                        QtCore.Qt.Horizontal
                        if (QtCore.Qt.ShiftModifier == int(mods))
                        else QtCore.Qt.Vertical)
            else:
                self.scrollRequest.emit(ev.delta(), QtCore.Qt.Horizontal)
        ev.accept()

    def keyPressEvent(self, ev):
        key = ev.key()
        if key == QtCore.Qt.Key_Escape and self.current:
            self.current = None
            self.drawingPolygon.emit(False)
            self.update()
        elif key == QtCore.Qt.Key_Return and self.canCloseShape():
            self.finalise()

    def setLastLabel(self, text):
        assert text
        self.shapes[-1].label = text
        self.shapesBackups.pop()
        self.storeShapes()
        return self.shapes[-1]

    def undoLastLine(self):
        assert self.shapes
        self.current = self.shapes.pop()
        self.current.setOpen()
        if self.createMode in ['polygon', 'linestrip']:
            self.line.points = [self.current[-1], self.current[0]]
        elif self.createMode in ['rectangle', 'line', 'circle']:
            self.current.points = self.current.points[0:1]
        elif self.createMode == 'point':
            self.current = None
        self.drawingPolygon.emit(True)

    def undoLastPoint(self):
        if not self.current or self.current.isClosed():
            return
        self.current.popPoint()
        if len(self.current) > 0:
            self.line[0] = self.current[-1]
        else:
            self.current = None
            self.drawingPolygon.emit(False)
        self.repaint()

    def loadPixmap(self, pixmap):
        self.pixmap = pixmap
        self.shapes = []
        self.repaint()

    def loadShapes(self, shapes, replace=True):
        if replace:
            self.shapes = list(shapes)
        else:
            self.shapes.extend(shapes)
        self.storeShapes()
        self.current = None
        self.repaint()

    def setShapeVisible(self, shape, value):
        self.visible[shape] = value
        self.repaint()

    def overrideCursor(self, cursor):
        self.restoreCursor()
        self._cursor = cursor
        QtWidgets.QApplication.setOverrideCursor(cursor)

    def restoreCursor(self):
        QtWidgets.QApplication.restoreOverrideCursor()

    def resetState(self):
        self.restoreCursor()
        self.pixmap = None
        self.shapesBackups = []
        self.update()
Пример #29
0
class MultiPlotWidget(QtWidgets.QWidget):
    closeSignal = QtCore.Signal()

    def __init__(self, context, parent=None):
        super(MultiPlotWidget, self).__init__()
        self._context = context
        layout = QtWidgets.QVBoxLayout()
        splitter = QtWidgets.QSplitter(QtCore.Qt.Vertical)
        self.quickEdit = QuickEditWidget(self)
        self.quickEdit.connect_x_range_changed(self._x_range_changed)
        self.quickEdit.connect_y_range_changed(self._y_range_changed)
        self.quickEdit.connect_errors_changed(self._errors_changed)
        self.quickEdit.connect_autoscale_changed(self._autoscale_changed)
        self.quickEdit.connect_plot_selection(self._selection_changed)

        # add some dummy plot
        self.plots = subplot(self._context)
        self.plots.connect_quick_edit_signal(self._update_quick_edit)
        self.plots.connect_rm_subplot_signal(self._update_quick_edit)
        # create GUI layout
        splitter.addWidget(self.plots)
        splitter.addWidget(self.quickEdit.widget)
        layout.addWidget(splitter)
        self.setLayout(layout)

    """ plotting """

    def add_subplot(self, name):
        self.plots.add_subplot(name, len(self.quickEdit.get_subplots()))

        self.quickEdit.add_subplot(name)

    def plot(self, subplotName, ws, specNum=1):
        self.plots.plot(subplotName, ws, specNum=specNum)

    def remove_subplot(self, name):
        self.plots._remove_subplot(name)

    def get_subplots(self):
        return list(self._context.subplots.keys())

    def add_vline_and_annotate(self, subplotName, xvalue, label, color):
        self.add_annotate(subplotName, label)
        self.add_vline(subplotName, xvalue, label.text, color)

    def rm_vline_and_annotate(self, subplotName, name):
        self.rm_annotate(subplotName, name)
        self.rm_vline(subplotName, name)

    def add_annotate(self, subplotName, label):
        self.plots.add_annotate(subplotName, label)

    def add_vline(self, subplotName, xvalue, name, color):
        self.plots.add_vline(subplotName, xvalue, name, color)

    def rm_annotate(self, subplotName, name):
        self.plots.rm_annotate(subplotName, name)

    def rm_vline(self, subplotName, name):
        self.plots.rm_vline(subplotName, name)

    # gets inital values for quickEdit
    def set_all_values(self):
        names = self.quickEdit.get_selection()
        xrange = list(self._context.subplots[names[0]].xbounds)
        yrange = list(self._context.subplots[names[0]].ybounds)
        for name in names:
            xbounds = self._context.subplots[name].xbounds
            ybounds = self._context.subplots[name].ybounds
            if xrange[0] > xbounds[0]:
                xrange[0] = deepcopy(xbounds[0])
            if xrange[1] < xbounds[1]:
                xrange[1] = deepcopy(xbounds[1])
            if yrange[0] > ybounds[0]:
                yrange[0] = deepcopy(ybounds[0])
            if yrange[1] < ybounds[1]:
                yrange[1] = deepcopy(ybounds[1])
        self._context.set_xBounds(xrange)
        self._context.set_yBounds(yrange)
        self._x_range_changed(xrange)
        self._y_range_changed(yrange)
        # get tick boxes correct
        errors = self._check_all_errors(names)
        self.quickEdit.set_errors(errors)
        self._change_errors(errors, names)

    def connectCloseSignal(self, slot):
        self.closeSignal.connect(slot)

    def removeSubplotConnection(self, slot):
        self.plots.connect_rm_subplot_signal(slot)

    def disconnectCloseSignal(selft):
        self.closeSignal.disconnect()

    def removeSubplotDisonnect(self):
        self.plots.disconnect_rm_subplot_signal()

    """ update GUI """

    def _if_empty_close(self):
        if not self._context.subplots:
            self.closeSignal.emit()
            self.close

    def _update_quick_edit(self, subplotName):
        names = self.quickEdit.get_selection()
        if subplotName not in self._context.subplots.keys():
            self.quickEdit.rm_subplot(subplotName)
            self._if_empty_close()
            return
        xrange = self._context.subplots[subplotName].xbounds
        yrange = self._context.subplots[subplotName].ybounds
        if len(names) == 0:
            return
        # if all selected update everyone
        if len(names) > 1:
            self.quickEdit.set_plot_x_range(xrange)
            self.quickEdit.set_plot_y_range(yrange)
        # if changed current selection
        elif names[0] == subplotName:
            self.quickEdit.set_plot_x_range(xrange)
            self.quickEdit.set_plot_y_range(yrange)
        # if a different plot changed
        else:
            pass

    def _selection_changed(self, index):
        names = self.quickEdit.get_selection()
        xrange = self._context.get_xBounds()
        yrange = self._context.get_yBounds()
        errors = True
        if len(names) == 1:
            xrange = self._context.subplots[names[0]].xbounds
            yrange = self._context.subplots[names[0]].ybounds
            errors = self._context.subplots[names[0]].errors
        else:
            errors = self._check_all_errors(names)
        # update values
        self.quickEdit.set_errors(errors)
        self._change_errors(errors, names)
        self.quickEdit.set_plot_x_range(xrange)
        self.quickEdit.set_plot_y_range(yrange)

        # force update of plots if selection is all
        if len(names) > 1:
            self._x_range_changed(xrange)
            self._y_range_changed(yrange)

    def _autoscale_changed(self, state):
        names = self.quickEdit.get_selection()
        self.plots.set_y_autoscale(names, True)

    def _change_errors(self, state, names):
        self.plots.change_errors(state, names)

    def _errors_changed(self, state):
        names = self.quickEdit.get_selection()
        self._change_errors(state, names)

    def _x_range_changed(self, xRange):
        names = self.quickEdit.get_selection()
        if len(names) > 1:
            self._context.set_xBounds(xRange)
        self.plots.set_plot_x_range(names, xRange)
        self.quickEdit.set_plot_x_range(xRange)

    def _y_range_changed(self, yRange):
        names = self.quickEdit.get_selection()
        if len(names) > 1:
            self._context.set_yBounds(yRange)
        self.plots.set_plot_y_range(names, yRange)
        self.quickEdit.set_plot_y_range(yRange)

    def _check_all_errors(self, names):
        for name in names:
            if self._context.subplots[name].errors is False:
                return False
        return True
Пример #30
0
class CounterLogic(GenericLogic):
    """ This logic module gathers data from a hardware counting device.

    @signal sigCounterUpdate: there is new counting data available
    @signal sigCountContinuousNext: used to simulate a loop in which the data
                                    acquisition runs.
    @sigmal sigCountGatedNext: ???

    @return error: 0 is OK, -1 is error
    """
    # Signals and slots are used for communication between objects. A signal is emitted when a particular event occurs.
    # A slot is a function that is called in response to a particular signal.
    # The documentation of the Qt's signals and slots can be found on this link:
    # https://doc.qt.io/qt-5/signalsandslots.html
    sigCounterUpdated = QtCore.Signal()

    sigCountDataNext = QtCore.Signal()

    sigGatedCounterFinished = QtCore.Signal()
    sigGatedCounterContinue = QtCore.Signal(bool)
    sigCountingSamplesChanged = QtCore.Signal(int)
    sigCountLengthChanged = QtCore.Signal(int)
    sigCountFrequencyChanged = QtCore.Signal(float)
    sigSavingStatusChanged = QtCore.Signal(bool)
    sigCountStatusChanged = QtCore.Signal(bool)
    sigCountingModeChanged = QtCore.Signal(CountingMode)

    _modclass = 'CounterLogic'
    _modtype = 'logic'

    ## declare connectors (slow counter hardware corresponding to the counter_logic)
    # Connector() is imported from core module
    counter1 = Connector(interface='SlowCounterInterface')
    savelogic = Connector(interface='SaveLogic')

    # status vars of the counter
    _count_length = StatusVar(
        'count_length', 300)  # counter's shown length is 300 measurements
    _smooth_window_length = StatusVar(
        'smooth_window_length',
        10)  # 10 data points on moving average for smoothing data
    _counting_samples = StatusVar(
        'counting_samples',
        1)  # oversampling parameter (measurements per data point)
    _count_frequency = StatusVar('count_frequency',
                                 50)  # number of measurements per second
    _saving = StatusVar('saving', False)

    # instantiation for counter_logic
    def __init__(self, config, **kwargs):
        """ Create CounterLogic object with connectors.

        @param dict config: module configuration
        @param dict kwargs: optional parameters
        """
        super().__init__(config=config, **kwargs)

        # locking for thread safety
        # Mutex is a mutual exclusion object that synchronizes access to a resource.
        # Mutex is a locking mechanism that makes sure only one thread can acquire the Mutex at a time and enter the
        # critical section. This thread only releases the Mutex when it exits the critical section.
        """
        example of using Mutex():
        wait(mutex);
        ...
        (critical section)
        ...
        signal(mutex);
        """
        self.threadlock = Mutex()

        self.log.debug('The following configuration was found.')

        # checking for the right configuration
        for key in config.keys():
            self.log.debug('{0}: {1}'.format(key, config[key]))

        # in bins (vars explained in comments around line 70)
        self._count_length = 300
        self._smooth_window_length = 10
        self._counting_samples = 1
        # in hertz
        self._count_frequency = 50

        # self._binned_counting = True  # UNUSED?
        # This is the default counting mode - continuous
        self._counting_mode = CountingMode['CONTINUOUS']

        self._saving = False
        return

    def on_activate(self):
        """ Initialisation performed during activation of the module.
        """
        # Connect to hardware and save logic (vars defined in line 67, 68)
        # (explained in SlowCounterInterface and SaveLogic)
        self._counting_device = self.counter1()
        self._save_logic = self.savelogic()

        # Recall saved app-parameters
        if 'counting_mode' in self._statusVariables:
            self._counting_mode = CountingMode[
                self._statusVariables['counting_mode']]

        # function defined around line 169
        constraints = self.get_hardware_constraints()
        number_of_detectors = constraints.max_detectors

        # initialize data arrays (vars explained in comments around line 70)
        # get_channels() defined in bottom of this file
        self.countdata = np.zeros(
            [len(self.get_channels()), self._count_length])
        self.countdata_smoothed = np.zeros(
            [len(self.get_channels()), self._count_length])
        self.rawdata = np.zeros(
            [len(self.get_channels()), self._counting_samples])
        self._already_counted_samples = 0  # The variable is an index for gated counting
        self._data_to_save = []

        # Flag to stop the loop
        self.stopRequested = False

        # time: Return the current time in seconds since the Epoch.
        self._saving_start_time = time.time()

        # connect signals to the counter
        self.sigCountDataNext.connect(self.count_loop_body,
                                      QtCore.Qt.QueuedConnection)
        return

    def on_deactivate(self):
        """ Deinitialisation performed during deactivation of the module.
        """
        # Save parameters to disk
        self._statusVariables['counting_mode'] = self._counting_mode.name

        # Stop measurement
        # lock is used as a synchronization tool, preventing threads outputting at the same time
        if self.module_state() == 'locked':
            self._stopCount_wait(
            )  # function defined in the bottom of this file

        # disconnect the signal from counter
        self.sigCountDataNext.disconnect()
        return

    def get_hardware_constraints(self):
        """
        Retrieve the hardware constraints from the counter device.

        @return SlowCounterConstraints: object with constraints for the counter
        """
        # the get_constraints() function is defined in hardware/national_instruments_x_series.py
        return self._counting_device.get_constraints()

    def set_counting_samples(self,
                             samples=1
                             ):  # function of setting the oversampling number
        """
        Sets the length of the counted bins.
        The counter is stopped first and restarted afterwards.

        @param int samples: oversampling in units of bins (positive int ).

        @return int: oversampling in units of bins.
        """
        # Determine if the counter has to be restarted after setting the parameter
        # param restart: whether the counter restart or not
        # a locked state means the counter is running
        if self.module_state() == 'locked':
            restart = True
        else:
            restart = False

        if samples > 0:
            self._stopCount_wait()
            self._counting_samples = int(samples)
            # if the counter was running, restart it
            if restart:
                self.startCount(
                )  # startCount() redirects to counting mode's start function
        else:
            self.log.warning(
                'counting_samples has to be larger than 0! Command ignored!')
        self.sigCountingSamplesChanged.emit(self._counting_samples)
        return self._counting_samples

    def set_count_length(self, length=300):
        """ Sets the time trace in units of bins.

        @param int length: time trace in units of bins (positive int).

        @return int: length of time trace in units of bins

        This makes sure, the counter is stopped first and restarted afterwards.
        """
        if self.module_state() == 'locked':
            restart = True
        else:
            restart = False

        if length > 0:
            self._stopCount_wait()  # explained in the bottom of the file
            self._count_length = int(length)  # explained in line 70
            # if the counter was running, restart it
            if restart:
                self.startCount()
        else:
            self.log.warning(
                'count_length has to be larger than 0! Command ignored!')
        self.sigCountLengthChanged.emit(self._count_length)
        return self._count_length

    def set_count_frequency(self, frequency=50):
        """ Sets the frequency with which the data is acquired.

        @param float frequency: the desired frequency of counting in Hz

        @return float: the actual frequency of counting in Hz

        This makes sure, the counter is stopped first and restarted afterwards.
        """
        constraints = self.get_hardware_constraints(
        )  # function defined around line 169

        # restart the measurement if it's already running
        if self.module_state() == 'locked':
            restart = True
        else:
            restart = False

        if constraints.min_count_frequency <= frequency <= constraints.max_count_frequency:
            # stop the counter and set to this frequency in range
            self._stopCount_wait()
            self._count_frequency = frequency
            # if the counter was running, restart it
            if restart:
                self.startCount()
        # if the default frequency (50 Hz) is not in range, display error
        else:
            self.log.warning('count_frequency not in range! Command ignored!')
        self.sigCountFrequencyChanged.emit(self._count_frequency)
        return self._count_frequency

    def get_count_length(self):
        """ Returns the currently set length of the counting array.

        @return int: count_length
        """
        return self._count_length  # defined around line 70

    # FIXME: get the frequency from the slow counter hardware
    def get_count_frequency(self):
        """ Returns the currently set frequency of counting (resolution).

        @return float: count_frequency
        """
        return self._count_frequency

    def get_counting_samples(self):
        """ Returns the currently set number of samples counted per readout.

        @return int: counting_samples
        """
        return self._counting_samples

    def get_saving_state(self):
        """ Returns if the data is saved in the moment.

        @return bool: saving state
        """
        return self._saving

    def start_saving(self, resume=False):
        """
        Sets up start-time and initializes data array, if not resuming, and changes saving state.
        If the counter is not running it will be started in order to have data to save.

        @return bool: saving state
        """
        if not resume:
            self._data_to_save = []
            self._saving_start_time = time.time(
            )  # time: Return the current time in seconds since the Epoch.

        self._saving = True

        # If the counter is not running, then it should start running so there is data to save
        if self.module_state() != 'locked':
            self.startCount()

        # the "emit()" sends signal to the slot function, executing the slot codes
        self.sigSavingStatusChanged.emit(self._saving)
        return self._saving

    # save data to file
    def save_data(self, to_file=True, postfix='', save_figure=True):
        """ Save the counter trace data and writes it to a file.

        @param bool to_file: indicate, whether data have to be saved to file
        @param str postfix: an additional tag, which will be added to the filename upon save
        @param bool save_figure: select whether png and pdf should be saved

        @return dict parameters: Dictionary which contains the saving parameters
        """
        # stop saving thus saving state has to be set to False
        self._saving = False
        self._saving_stop_time = time.time()

        # write the parameters:
        # strftime(format[, tuple]) -> string.
        # Convert a time tuple to a string according to a format specification.

        # localtime([seconds]) -> (tm_year,tm_mon,tm_mday,tm_hour,tm_min,tm_sec,tm_wday,tm_yday,tm_isdst)
        # Convert seconds since the Epoch to a time tuple expressing local time
        parameters = OrderedDict()
        parameters['Start counting time'] = time.strftime(
            '%d.%m.%Y %Hh:%Mmin:%Ss', time.localtime(self._saving_start_time))
        parameters['Stop counting time'] = time.strftime(
            '%d.%m.%Y %Hh:%Mmin:%Ss', time.localtime(self._saving_stop_time))
        parameters['Count frequency (Hz)'] = self._count_frequency
        parameters['Oversampling (Samples)'] = self._counting_samples
        parameters[
            'Smooth Window Length (# of events)'] = self._smooth_window_length

        if to_file:
            # If there is a postfix then add separating underscore
            if postfix == '':
                filelabel = 'count_trace'
            else:
                filelabel = 'count_trace_' + postfix

            # prepare the data in a dict or in an OrderedDict:
            header = 'Time (s)'
            for i, detector in enumerate(self.get_channels()):
                header = header + ',Signal{0} (counts/s)'.format(i)

            data = {header: self._data_to_save}
            filepath = self._save_logic.get_path_for_module(
                module_name='Counter')

            if save_figure:
                fig = self.draw_figure(data=np.array(self._data_to_save))
            else:
                fig = None
            self._save_logic.save_data(data,
                                       filepath=filepath,
                                       parameters=parameters,
                                       filelabel=filelabel,
                                       plotfig=fig,
                                       delimiter='\t')
            self.log.info('Counter Trace saved to:\n{0}'.format(filepath))

        self.sigSavingStatusChanged.emit(self._saving)
        return self._data_to_save, parameters

    def draw_figure(self, data):  # data in array format
        """ Draw figure to save with data file.

        @param: nparray data: a numpy array containing counts vs time for all detectors

        @return: fig fig: a matplotlib figure object to be saved to file.
        """
        count_data = data[:, 1:len(self.get_channels(
        )) + 1]  # total counter channels number (all elements in 2nd column)
        time_data = data[:,
                         0]  # all elements in 1st column should be the time data

        # Scale count values using SI prefix (converting units in '', 'k', 'M', 'G')
        prefix = ['', 'k', 'M', 'G']
        prefix_index = 0
        while np.max(count_data) > 1000:
            count_data = count_data / 1000
            prefix_index = prefix_index + 1
        counts_prefix = prefix[prefix_index]

        # Use qudi style
        plt.style.use(self._save_logic.mpl_qd_style)

        # Create figure
        fig, ax = plt.subplots()
        ax.plot(time_data, count_data, linestyle=':', linewidth=0.5)
        ax.set_xlabel('Time (s)')
        ax.set_ylabel('Fluorescence (' + counts_prefix + 'c/s)')
        return fig

    def set_counting_mode(self, mode='CONTINUOUS'):
        """Set the counting mode, to change between continuous and gated counting.
        Possible options are:
            'CONTINUOUS'    = counts continuously
            'GATED'         = bins the counts according to a gate signal
            'FINITE_GATED'  = finite measurement with predefined number of samples

        @return str: counting mode
        """
        constraints = self.get_hardware_constraints()
        if self.module_state() != 'locked':
            if CountingMode[mode] in constraints.counting_mode:
                self._counting_mode = CountingMode[mode]
                self.log.debug('New counting mode: {}'.format(
                    self._counting_mode))
            else:
                self.log.warning(
                    'Counting mode not supported from hardware. Command ignored!'
                )
            self.sigCountingModeChanged.emit(self._counting_mode)
        else:
            self.log.error(
                'Cannot change counting mode while counter is still running.')
        return self._counting_mode

    def get_counting_mode(self):
        """ Retrieve the current counting mode.

        @return str: one of the possible counting options:
                'CONTINUOUS'    = counts continuously
                'GATED'         = bins the counts according to a gate signal
                'FINITE_GATED'  = finite measurement with predefined number of samples
        """
        return self._counting_mode

    # FIXME: Not implemented for self._counting_mode == 'gated'
    def startCount(self):
        """ This is called externally, and is basically a wrapper that
            redirects to the chosen counting mode start function.

            @return error: 0 is OK, -1 is error
        """
        # Sanity checks
        constraints = self.get_hardware_constraints(
        )  # defined around line 169
        if self._counting_mode not in constraints.counting_mode:
            self.log.error(
                'Unknown counting mode "{0}". Cannot start the counter.'
                ''.format(self._counting_mode))
            self.sigCountStatusChanged.emit(False)
            return -1

        with self.threadlock:
            # Lock module
            if self.module_state() != 'locked':
                self.module_state.lock()
            else:
                self.log.warning(
                    'Counter already running. Method call ignored.')
                return 0

            # Set up clock (the function set_up_clock() is defined in hardware/national_instruments_x_series.py)
            clock_status = self._counting_device.set_up_clock(
                clock_frequency=self._count_frequency)
            if clock_status < 0:
                self.module_state.unlock()
                self.sigCountStatusChanged.emit(False)
                return -1

            # Set up counter (the function set_up_counter() is defined in hardware/national_instruments_x_series.py)
            if self._counting_mode == CountingMode['FINITE_GATED']:
                counter_status = self._counting_device.set_up_counter(
                    counter_buffer=self._count_length)
            # elif self._counting_mode == CountingMode['GATED']:
            #
            else:
                counter_status = self._counting_device.set_up_counter()
            if counter_status < 0:
                self._counting_device.close_clock()
                self.module_state.unlock()
                self.sigCountStatusChanged.emit(False)
                return -1

            # initialising the data arrays
            # rawdata: create a num of channels x oversampling, 2 dimensional array with 0's
            self.rawdata = np.zeros(
                [len(self.get_channels()), self._counting_samples])
            # countdata: create a num of channels x count length, 2 dimensional array with 0's
            self.countdata = np.zeros(
                [len(self.get_channels()), self._count_length])
            # countdata_smoothed:
            # create a num of channels x count length, 2 dimensional array with 0's (same size with above)
            self.countdata_smoothed = np.zeros(
                [len(self.get_channels()), self._count_length])
            # _sampling_data: create a num of channels x oversampling num, 2 dimensional array without initializing data
            # empty (Return a new array of given shape and type, without initializing entries.)
            self._sampling_data = np.empty(
                [len(self.get_channels()), self._counting_samples])

            # the sample index for gated counting
            self._already_counted_samples = 0

            # Start data reader loop
            self.sigCountStatusChanged.emit(True)
            self.sigCountDataNext.emit()
            return

    def stopCount(self):
        """ Set a flag to request stopping counting.
        """
        if self.module_state() == 'locked':
            with self.threadlock:
                self.stopRequested = True
        return

    def count_loop_body(self):
        """ This method gets the count data from the hardware for the continuous counting mode (default).

        It runs repeatedly in the logic module event loop by being connected
        to sigCountContinuousNext and emitting sigCountContinuousNext through a queued connection.
        """
        if self.module_state() == 'locked':
            with self.threadlock:
                # check for aborts of the thread in break if necessary
                if self.stopRequested:
                    # close off the actual counter
                    cnt_err = self._counting_device.close_counter()
                    clk_err = self._counting_device.close_clock()
                    if cnt_err < 0 or clk_err < 0:
                        self.log.error(
                            'Could not even close the hardware, giving up.')
                    # switch the state variable off again
                    self.stopRequested = False
                    self.module_state.unlock()
                    self.sigCounterUpdated.emit()
                    return

                # read the current counter value (vars explained around line 466)
                # the function get_counter() defined in hardware/national_instruments_x_series.py
                self.rawdata = self._counting_device.get_counter(
                    samples=self._counting_samples)
                if self.rawdata[0, 0] < 0:
                    self.log.error(
                        'The counting went wrong, killing the counter.')
                    self.stopRequested = True
                else:
                    if self._counting_mode == CountingMode['CONTINUOUS']:
                        self._process_data_continous(
                        )  # defined in the bottom of the file
                    elif self._counting_mode == CountingMode['GATED']:
                        self._process_data_gated(
                        )  # defined in the bottom of the file
                    elif self._counting_mode == CountingMode['FINITE_GATED']:
                        self._process_data_finite_gated(
                        )  # defined in the bottom of the file
                    else:
                        self.log.error(
                            'No valid counting mode set! Can not process counter data.'
                        )

            # call this again from event loop
            self.sigCounterUpdated.emit()
            self.sigCountDataNext.emit()
        return

    def save_current_count_trace(self, name_tag=''):
        """ The currently displayed counttrace will be saved.

        @param str name_tag: optional, personal description that will be
                             appended to the file name

        @return: dict data: Data which was saved
                 str filepath: Filepath
                 dict parameters: Experiment parameters
                 str filelabel: Filelabel

        This method saves the already displayed counts to file and does not
        accumulate them. The counttrace variable will be saved to file with the
        provided name!
        """

        # If there is a postfix then add separating underscore
        if name_tag == '':
            filelabel = 'snapshot_count_trace'
        else:
            filelabel = 'snapshot_count_trace_' + name_tag

        stop_time = self._count_length / self._count_frequency
        time_step_size = stop_time / len(self.countdata)
        x_axis = np.arange(0, stop_time, time_step_size)

        # prepare the data in a dict or in an OrderedDict:
        data = OrderedDict()
        chans = self.get_channels()
        savearr = np.empty((len(chans) + 1, len(x_axis)))
        savearr[0] = x_axis
        datastr = 'Time (s)'

        for i, ch in enumerate(chans):
            savearr[i + 1] = self.countdata[i]
            datastr += ',Signal {0} (counts/s)'.format(i)

        data[datastr] = savearr.transpose()

        # write the parameters:
        parameters = OrderedDict()
        timestr = time.strftime('%d.%m.%Y %Hh:%Mmin:%Ss',
                                time.localtime(time.time()))
        parameters['Saved at time'] = timestr
        parameters['Count frequency (Hz)'] = self._count_frequency
        parameters['Oversampling (Samples)'] = self._counting_samples
        parameters[
            'Smooth Window Length (# of events)'] = self._smooth_window_length

        filepath = self._save_logic.get_path_for_module(module_name='Counter')
        self._save_logic.save_data(data,
                                   filepath=filepath,
                                   parameters=parameters,
                                   filelabel=filelabel,
                                   delimiter='\t')

        self.log.debug('Current Counter Trace saved to: {0}'.format(filepath))
        return data, filepath, parameters, filelabel

    def get_channels(self):
        """ Shortcut for hardware get_counter_channels.

            @return list(str): return list of active counter channel names
        """
        # (function get_counter_channels() defined in hardware/national_instruments_x_series.py)
        return self._counting_device.get_counter_channels()

    def _process_data_continous(self):
        """
        Processes the raw data from the counting device
        @return:
        """
        for i, ch in enumerate(self.get_channels()):
            # remember the new count data in circular array
            self.countdata[i, 0] = np.average(self.rawdata[i])
        # move the array to the left to make space for the new data
        self.countdata = np.roll(self.countdata, -1, axis=1)
        # also move the smoothing array
        self.countdata_smoothed = np.roll(self.countdata_smoothed, -1, axis=1)
        # calculate the median and save it
        window = -int(self._smooth_window_length / 2) - 1
        for i, ch in enumerate(self.get_channels()):
            self.countdata_smoothed[i, window:] = np.median(
                self.countdata[i, -self._smooth_window_length:])

        # save the data if necessary
        if self._saving:
            # if oversampling is necessary
            if self._counting_samples > 1:
                chans = self.get_channels()
                self._sampling_data = np.empty(
                    [len(chans) + 1, self._counting_samples])
                self._sampling_data[
                    0, :] = time.time() - self._saving_start_time
                for i, ch in enumerate(chans):
                    self._sampling_data[i + 1, 0] = self.rawdata[i]

                self._data_to_save.extend(list(self._sampling_data))
            # if we don't want to use oversampling
            else:
                # append tuple to data stream (timestamp, average counts)
                chans = self.get_channels()
                newdata = np.empty((len(chans) + 1, ))
                newdata[0] = time.time() - self._saving_start_time
                for i, ch in enumerate(chans):
                    newdata[i + 1] = self.countdata[i, -1]
                self._data_to_save.append(newdata)
        return

    def _process_data_gated(self):
        """
        Processes the raw data from the counting device
        @return:
        """
        # remember the new count data in circular array
        self.countdata[0] = np.average(self.rawdata[0])
        # move the array to the left to make space for the new data
        self.countdata = np.roll(self.countdata, -1)
        # also move the smoothing array
        self.countdata_smoothed = np.roll(self.countdata_smoothed, -1)
        # calculate the median and save it
        self.countdata_smoothed[-int(self._smooth_window_length / 2) -
                                1:] = np.median(
                                    self.
                                    countdata[-self._smooth_window_length:])

        # save the data if necessary
        if self._saving:
            # if oversampling is necessary
            if self._counting_samples > 1:
                self._sampling_data = np.empty((self._counting_samples, 2))
                # first column is time data, second column is value
                self._sampling_data[:,
                                    0] = time.time() - self._saving_start_time
                self._sampling_data[:, 1] = self.rawdata[0]
                self._data_to_save.extend(list(self._sampling_data))
            # if we don't want to use oversampling
            else:
                # append tuple to data stream (timestamp, average counts)
                self._data_to_save.append(
                    np.array((time.time() - self._saving_start_time,
                              self.countdata[-1])))
        return

    def _process_data_finite_gated(self):
        """
        Processes the raw data from the counting device
        @return:
        """
        if self._already_counted_samples + len(self.rawdata[0]) >= len(
                self.countdata):
            needed_counts = len(self.countdata) - self._already_counted_samples
            self.countdata[0:needed_counts] = self.rawdata[0][0:needed_counts]
            self.countdata = np.roll(self.countdata, -needed_counts)
            self._already_counted_samples = 0
            self.stopRequested = True
        else:
            # replace the first part of the array with the new data:
            self.countdata[0:len(self.rawdata[0])] = self.rawdata[0]
            # roll the array by the amount of data it had been inserted:
            self.countdata = np.roll(self.countdata, -len(self.rawdata[0]))
            # increment the index counter:
            self._already_counted_samples += len(self.rawdata[0])
        return

    def _stopCount_wait(self, timeout=5.0):
        """
        Stops the counter and waits until it actually has stopped.

        @param timeout: float, the max. time in seconds how long the method should wait for the
                        process to stop.

        @return: error code
        """
        self.stopCount()
        start_time = time.time()
        while self.module_state() == 'locked':
            time.sleep(0.1)
            # if the counter is still running after the timeout duration then error returns
            if time.time() - start_time >= timeout:
                self.log.error(
                    'Stopping the counter timed out after {0}s'.format(
                        timeout))
                return -1
        return 0
Пример #31
0
    def __init__(self):
        super().__init__()

        # Get FontAwesome 5.x icons by name in various styles by name
        fa5_icon = qta.icon('fa5.flag')
        fa5_button = QtWidgets.QPushButton(fa5_icon, 'Font Awesome! (regular)')

        fa5s_icon = qta.icon('fa5s.flag')
        fa5s_button = QtWidgets.QPushButton(fa5s_icon, 'Font Awesome! (solid)')

        fa5b_icon = qta.icon('fa5b.github')
        fa5b_button = QtWidgets.QPushButton(fa5b_icon,
                                            'Font Awesome! (brands)')

        # Get Elusive icons by name
        asl_icon = qta.icon('ei.asl')
        elusive_button = QtWidgets.QPushButton(asl_icon, 'Elusive Icons!')

        # Get Material Design icons by name
        apn_icon = qta.icon('mdi.access-point-network')
        mdi_button = QtWidgets.QPushButton(apn_icon, 'Material Design Icons!')

        # Rotated
        rot_icon = qta.icon('mdi.access-point-network', rotated=45)
        rot_button = QtWidgets.QPushButton(rot_icon, 'Rotated Icons!')

        # Horizontal flip
        hflip_icon = qta.icon('mdi.account-alert', hflip=True)
        hflip_button = QtWidgets.QPushButton(hflip_icon,
                                             'Horizontally Flipped Icons!')

        # Vertical flip
        vflip_icon = qta.icon('mdi.account-alert', vflip=True)
        vflip_button = QtWidgets.QPushButton(vflip_icon,
                                             'Vertically Flipped Icons!')

        # Styling
        styling_icon = qta.icon('fa5s.music',
                                active='fa5s.balance-scale',
                                color='blue',
                                color_active='orange')
        music_button = QtWidgets.QPushButton(styling_icon, 'Styling')

        # Toggle
        toggle_icon = qta.icon('fa5s.home',
                               selected='fa5s.balance-scale',
                               color_off='black',
                               color_off_active='blue',
                               color_on='orange',
                               color_on_active='yellow')
        toggle_button = QtWidgets.QPushButton(toggle_icon, 'Toggle')
        toggle_button.setCheckable(True)

        iconwidget = qta.IconWidget()
        spin_icon = qta.icon('mdi.loading',
                             color='red',
                             animation=qta.Spin(iconwidget))
        iconwidget.setIcon(spin_icon)
        iconwidget.setIconSize(QtCore.QSize(32, 32))
        iconwidgetholder = QtWidgets.QWidget()
        lo = QtWidgets.QHBoxLayout()
        lo.addWidget(iconwidget)
        lo.addWidget(QtWidgets.QLabel('IconWidget'))
        iconwidgetholder.setLayout(lo)
        iconwidget2 = qta.IconWidget('mdi.web', color='blue')

        # Stack icons
        camera_ban = qta.icon('fa5s.camera',
                              'fa5s.ban',
                              options=[{
                                  'scale_factor': 0.5,
                                  'active': 'fa5s.balance-scale'
                              }, {
                                  'color': 'red',
                                  'opacity': 0.7
                              }])
        stack_button = QtWidgets.QPushButton(camera_ban, 'Stack')
        stack_button.setIconSize(QtCore.QSize(32, 32))

        # Stack and offset icons
        saveall = qta.icon('fa5.save',
                           'fa5.save',
                           options=[{
                               'scale_factor': 0.8,
                               'offset': (0.2, 0.2),
                               'color': 'gray'
                           }, {
                               'scale_factor': 0.8
                           }])
        saveall_button = QtWidgets.QPushButton(saveall, 'Stack, offset')

        # Spin icons
        spin_button = QtWidgets.QPushButton(' Spinning icon')
        spin_icon = qta.icon('fa5s.spinner',
                             color='red',
                             animation=qta.Spin(spin_button))
        spin_button.setIcon(spin_icon)

        # Pulse icons
        pulse_button = QtWidgets.QPushButton(' Pulsing icon')
        pulse_icon = qta.icon('fa5s.spinner',
                              color='green',
                              animation=qta.Pulse(pulse_button))
        pulse_button.setIcon(pulse_icon)

        # Stacked spin icons
        stack_spin_button = QtWidgets.QPushButton('Stack spin')
        options = [{
            'scale_factor': 0.4,
            'animation': qta.Spin(stack_spin_button)
        }, {
            'color': 'blue'
        }]
        stack_spin_icon = qta.icon('ei.asl', 'fa5.square', options=options)
        stack_spin_button.setIcon(stack_spin_icon)
        stack_spin_button.setIconSize(QtCore.QSize(32, 32))

        # Render a label with this font
        label = QtWidgets.QLabel(chr(0xf19c) + ' ' + 'Label')
        label.setFont(qta.font('fa', 16))

        # Layout
        vbox = QtWidgets.QVBoxLayout()
        widgets = [
            fa5_button, fa5s_button, fa5b_button, elusive_button, mdi_button,
            music_button, rot_button, hflip_button, vflip_button,
            toggle_button, stack_button, saveall_button, spin_button,
            pulse_button, stack_spin_button, label, iconwidgetholder,
            iconwidget2
        ]

        for w in widgets:
            vbox.addWidget(w)

        self.setLayout(vbox)
        self.setWindowTitle('Awesome')
        self.show()
Пример #32
0
class BaseSettings(QtCore.QObject):
    _individual_rendering_parameters = True
    # signals defined here because qt has a problem with multiple inheritance and I can't get it running if
    # they are defined in the matching classes...
    individualColormapChanged = QtCore.Signal()
    individualClippingPlaneChanged = QtCore.Signal()
    individualLightChanged = QtCore.Signal()

    def __init__(self):
        super().__init__()
        self._createParameters()
        if _have_qt:
            self._createQtWidget()

    def getSettings(self):
        res = {}
        for group in self._parameters:
            for p in self._parameters[group]:
                res[p.name] = p.getValue()
        return res

    def setSettings(self, settings):
        for group in self._parameters:
            for p in self._parameters[group]:
                if p.name in settings:
                    p.setValue(settings[p.name])

    def __getstate__(self):
        return (self._parameters, )

    def __setstate__(self, state):
        self._parameters = {}
        self._par_name_dict = {}
        for group, items in state[0].items():
            self.addParameters(group, *items)
        if _have_qt:
            self._createQtWidget()

    def _createParameters(self):
        self._parameters = {}
        self._par_name_dict = {}

    @inmain_decorator(True)
    def _createQtWidget(self):
        self.widgets = wid.OptionWidgets()
        for group, params in self._parameters.items():
            for param in params:
                if param.getOption("updateWidgets"):
                    param.changed.connect(
                        lambda *a, **b: self.widgets.update())
            widgets = [par.getWidget() for par in params]
            self.widgets.addGroup(group, *widgets)

    @inmain_decorator(True)
    def updateWidgets(self):
        """Updates setting widgets"""
        self.widgets.update()

    def _attachParameter(self, parameter):
        if parameter.name:
            if hasattr(parameter, "getValue"):
                setattr(self, "get" + parameter.name, parameter.getValue)
            setattr(self, "set" + parameter.name, parameter.setValue)
        parameter._attachTo(self)

    def _connectParameters(self):
        pass

    def addParameters(self, group, *parameters):
        if group not in self._parameters:
            self._parameters[group] = []
        for par in parameters:
            self._parameters[group].append(par)
            self._attachParameter(par)
Пример #33
0
class LayerCommunicator(QtCore.QObject):
    layer_check_changed = QtCore.Signal(object, bool)
Пример #34
0
class ScientificDoubleSpinBox(QtWidgets.QDoubleSpinBox):
    resetValueChanged = QtCore.Signal(float)
    def __init__(self, reset_popup=True, *args, **kwargs):
        self.validator = FloatValidator()
        if 'numFormat' in kwargs:
            self.numFormat = kwargs.pop('numFormat')
        else:
            self.numFormat = 'g'
        
        self.setStrDecimals(6)  # number of decimals displayed
        super().__init__(*args, **kwargs)
        self.cb = QApplication.clipboard()
        self.setKeyboardTracking(False)
        self.setMinimum(-sys.float_info.max)
        self.setMaximum(sys.float_info.max)
        self.setDecimals(int(np.floor(np.log10(sys.float_info.max))))   # big for setting value
        self.setSingleStep(0.1)
        self.setAccelerated(True)
        # self.installEventFilter(self)
        
        if 'value' in kwargs:
            self.setValue(kwargs['value'])
        else:
            self.setValue(0)
            
        self._set_reset_value(self.value())
        
        if reset_popup:
            # Set popup
            self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
            self.customContextMenuRequested.connect(self._popup_menu)
            
            # Set Shortcuts
            shortcut_fcn_pair = [['Ctrl+R', lambda: self._reset()], ['Ctrl+C', lambda: self._copy()],
                                 ['Ctrl+V', lambda: self._paste()]]
            for shortcut, fcn in shortcut_fcn_pair:     # TODO: need to fix hover shortcuts not working
                QShortcut(QtGui.QKeySequence(shortcut), self, activated=fcn, context=QtCore.Qt.WidgetShortcut)
    
    # def eventFilter(self, obj, event):              # event filter to allow hover shortcuts
        # if event.type() == QtCore.QEvent.Enter: 
            # self.setFocus()
            # return True
        # elif event.type() == QtCore.QEvent.Leave:
            # return False
        # else:
            # return super().eventFilter(obj, event)
    
    def _popup_menu(self, event):
        popup_menu = QMenu(self)
        popup_menu.addAction('Reset', lambda: self._reset(), 'Ctrl+R')
        popup_menu.addSeparator()
        popup_menu.addAction('Copy', lambda: self._copy(), 'Ctrl+C')
        popup_menu.addAction('Paste', lambda: self._paste(), 'Ctrl+V')
        popup_menu.addSeparator()
        popup_menu.addAction('Set Reset Value', lambda: self._set_reset_value(self.value()))
        popup_menu.exec_(QtGui.QCursor.pos())
    
    def _reset(self, silent=False):
        self.blockSignals(True)     # needed because shortcut isn't always signalling valueChanged.emit
        self.setValue(self.reset_value)
        self.blockSignals(False)
        if not silent:
            self.valueChanged.emit(self.reset_value)
    
    def setStrDecimals(self, value: int):
        self.strDecimals = value
    
    def _set_reset_value(self, value):
        self.reset_value = value
        self.resetValueChanged.emit(self.reset_value)
    
    def _copy(self):
        self.selectAll()
        cb = self.cb
        cb.clear(mode=cb.Clipboard)
        cb.setText(self.textFromValue(self.value()), mode=cb.Clipboard)
    
    def _paste(self):
        previous_value = self.text()
        if self.fixup(self.cb.text()):
            self.setValue(float(self.fixup(self.cb.text())))
        else:
            self.setValue(float(previous_value))
    
    def keyPressEvent(self, event):
        if event.matches(QtGui.QKeySequence.Paste):
            self._paste()
        
        super(ScientificDoubleSpinBox, self).keyPressEvent(event)   # don't want to overwrite all shortcuts
    
    def validate(self, text, position):
        return self.validator.validate(text, position)

    def fixup(self, text):
        return self.validator.fixup(text)

    def valueFromText(self, text):
        return float(text)

    def textFromValue(self, value):
        """Modified form of the 'g' format specifier."""
        if 'g' in self.numFormat: 
            string = "{:.{dec}{numFormat}}".format(value, dec=self.strDecimals, 
                                                   numFormat=self.numFormat)
        elif 'e' in self.numFormat:
            string = "{:.{dec}{numFormat}}".format(value, dec=self.strDecimals, 
                                                   numFormat=self.numFormat)
        string = re.sub("e(-?)0*(\d+)", r"e\1\2", string.replace("e+", "e"))
        return string
    
    def stepBy(self, steps):
        if self.specialValueText() and self.value() == self.minimum():
            text = self.textFromValue(self.minimum())
        else:    
            text = self.cleanText()
        
        old_val = float(text)
        if self.numFormat == 'g' and OoM(old_val) <= self.strDecimals:    # my own custom g
            val = old_val + self.singleStep()*steps
            new_string = "{:.{dec}f}".format(val, dec=self.strDecimals)
        else:
            old_OoM = OoM(old_val)
            val = old_val + np.power(10, old_OoM)*self.singleStep()*steps
            new_OoM = OoM(val)
            if old_OoM > new_OoM:   # needed to step down by new amount 1E5 -> 9.9E6
                val = old_val + np.power(10, new_OoM)*self.singleStep()*steps
                
            new_string = "{:.{dec}e}".format(val, dec=self.strDecimals)

        self.lineEdit().setText(new_string)
        self.setValue(float(new_string))
Пример #35
0
class WorkbenchNavigationToolbar(NavigationToolbar2QT):

    sig_grid_toggle_triggered = QtCore.Signal()
    sig_active_triggered = QtCore.Signal()
    sig_hold_triggered = QtCore.Signal()

    toolitems = (('Home', 'Reset original view', 'fa.home', 'home',
                  None), ('Pan', 'Pan axes with left mouse, zoom with right',
                          'fa.arrows-alt', 'pan', False),
                 ('Zoom', 'Zoom to rectangle', 'fa.search-plus', 'zoom',
                  False), (None, None, None, None, None),
                 ('Grid', 'Toggle grid on/off', None, 'toggle_grid',
                  False), (None, None, None, None, None),
                 ('Active',
                  'When enabled future plots will overwrite this figure', None,
                  'active',
                  True), ('Hold', 'When enabled this holds this figure open ',
                          None, 'hold', False), (None, None, None, None, None),
                 ('Save', 'Save the figure', 'fa.save', 'save_figure', None),
                 ('Print', 'Print the figure', 'fa.print', 'print_figure',
                  None), (None, None, None, None,
                          None), ('Customize', 'Configure plot options',
                                  'fa.cog', 'edit_parameters', None))

    def _init_toolbar(self):
        for text, tooltip_text, fa_icon, callback, checked in self.toolitems:
            if text is None:
                self.addSeparator()
            else:
                if fa_icon:
                    a = self.addAction(qta.icon(fa_icon), text,
                                       getattr(self, callback))
                else:
                    a = self.addAction(text, getattr(self, callback))
                self._actions[callback] = a
                if checked is not None:
                    a.setCheckable(True)
                    a.setChecked(checked)
                if tooltip_text is not None:
                    a.setToolTip(tooltip_text)

        self.buttons = {}
        # Add the x,y location widget at the right side of the toolbar
        # The stretch factor is 1 which means any resizing of the toolbar
        # will resize this label instead of the buttons.
        if self.coordinates:
            self.locLabel = QtWidgets.QLabel("", self)
            self.locLabel.setAlignment(QtCore.Qt.AlignRight
                                       | QtCore.Qt.AlignTop)
            self.locLabel.setSizePolicy(
                QtWidgets.QSizePolicy(QtWidgets.Expanding,
                                      QtWidgets.QSizePolicy.Ignored))
            labelAction = self.addWidget(self.locLabel)
            labelAction.setVisible(True)

        # reference holder for subplots_adjust window
        self.adj_window = None

        # Adjust icon size or they are too small in PyQt5 by default
        self.setIconSize(QtCore.QSize(24, 24))

    def toggle_grid(self):
        self.sig_grid_toggle_triggered.emit()

    def hold(self, *args):
        self._actions['hold'].setChecked(True)
        self._actions['active'].setChecked(False)
        self.sig_hold_triggered.emit()

    def active(self, *args):
        self._actions['active'].setChecked(True)
        self._actions['hold'].setChecked(False)
        self.sig_active_triggered.emit()

    def print_figure(self):
        printer = QtPrintSupport.QPrinter(
            QtPrintSupport.QPrinter.HighResolution)
        printer.setOrientation(QtPrintSupport.QPrinter.Landscape)
        print_dlg = QtPrintSupport.QPrintDialog(printer)
        if print_dlg.exec_() == QtWidgets.QDialog.Accepted:
            painter = QtGui.QPainter(printer)
            page_size = printer.pageRect()
            pixmap = self.canvas.grab().scaled(page_size.width(),
                                               page_size.height(),
                                               QtCore.Qt.KeepAspectRatio)
            painter.drawPixmap(0, 0, pixmap)
            painter.end()