示例#1
0
class QTiffStackView(QWidget):
    #the view which the user of the videoviewer sees.
    #This class contains relevant 'client side' attributes e.g. buttons to get a frame, a slide bar and a timer. These attributes submit requests to the QTiffStackController to give the next frame etc. The controller returns either the requested frame or an error message

    def __init__(self):
        super(QTiffStackView, self).__init__()
        #add the image display. This is a subclass of QLabel, where paintEvent is overriden.
        self.frame_view = FrameView()

        #self.frame_view.setSizePolicy(QSizePolicy.Expanding,QSizePolicy.Expanding)

        #add the slide bar which allows the user to manual flick through

        self.slideBar = QSlider(Qt.Horizontal)
        self.slideBar.setTickPosition(QSlider.TicksAbove)
        self.slideBar.setTracking(True)
        self.slideBar.setTickInterval(100)

        #add a counter which displays the frame which is currently displayed
        self.counter = QSpinBox()
        self.counter.setSingleStep(1)
        self.counter.setRange(self.slideBar.minimum(), self.slideBar.maximum())

        #self explanatory
        self.play = QPushButton('Play')

        #when play button is pressed the timer takes control of the displaying of frames
        self.frametimer = QTimer()

        frame_rate = 30
        self.frametimer.setInterval(frame_rate)

        #Add a sublayout to align the slidebar and frame counter next to eachother
        slidelyt = QHBoxLayout()
        slidelyt.addWidget(self.slideBar)
        slidelyt.addWidget(self.counter)

        #Add the main layout for the widget
        lyt = QVBoxLayout()

        lyt.addWidget(self.frame_view)
        lyt.addLayout(slidelyt)
        lyt.addWidget(self.play)

        self.setLayout(lyt)

    def updateRanges(self, maximum):

        assert type(maximum) == int

        self.slideBar.setMaximum(maximum)
        self.counter.setRange(self.slideBar.minimum(), self.slideBar.maximum())
class  livestream(QWidget):
    i = 0
    def __init__(self,qnd,images = None,annotations_on = True,annotate_coords = None,threshold_switch = False):
        QWidget.__init__(self)
        
        
        self.threshold_switch = threshold_switch
        self.video = images #frames buffer
        self.videobox = Label()
        if annotations_on and annotate_coords is not None:
            self.coords = annotate_coords

            self.videobox.switch = annotations_on
            self.videobox.activecoord = self.coords[0]

        
        
        if self.video is not None:
            self.videobox.activeframe = self.video[0]
            
            self.videobox.maxintens = self.video.shape[0]
            
        else:
            self.videobox.activeframe = np.loadtxt(os.getcwd() + '/defaultimage.txt')
            print(self.videobox.activeframe.shape)
            self.videobox.maxintens = np.max(self.videobox.activeframe)


        self.videobox.setGeometry(QtCore.QRect(70, 80, 310, 310))
        self.videobox.h = 310
        self.videobox.w = 310
        
        self.lyt = QVBoxLayout()
        self.lyt.addWidget(self.videobox,5)
        
        
        self.setLayout(self.lyt)
        
        
        
        self.sl = QSlider(Qt.Horizontal)
        
        self.sl.setMinimum(0.0)
        if self.video is not None:
            self.sl.setMaximum(self.video.shape[0])
            self.sl.valueChanged.connect(self.whenslidechanges)
        self.sl.setTickPosition(QSlider.TicksAbove)
        self.sl.setTracking(True)
        self.sl.setTickInterval(100)
        

        
        self.frame_counter = QDoubleSpinBox()
        if images is not None:
            self.frame = images[0]
            self.frame_counter.valueChanged.connect(self.video_time_update)
        self.frame_counter.setSingleStep(1)
        self.frame_counter.setRange(self.sl.minimum(),self.sl.maximum())
        self.frame_counter.valueChanged.connect(self.sl.setValue)

        
        self.video_time = QDoubleSpinBox()
        self.video_time.setSingleStep(30)
        self.video_time.setRange(self.sl.minimum(),30*self.sl.maximum())
        self.frameratetimer = QTimer()
        self.frameratetimer.setInterval(30)
        if self.video is not None:
            self.frameratetimer.timeout.connect(self.update_display)
        
        
        self.play_button = QPushButton('Play Video')
        self.play_button.clicked.connect(self.frameratetimer.start)
        
        self.stop_button = QPushButton('Stop Video')
        self.stop_button.clicked.connect(self.frameratetimer.stop)

        if self.video is not None:
            self.sl.valueChanged.connect(self.whenslidechanges)
       
        self.lyt.addWidget(self.play_button,0)
        self.lyt.addWidget(self.stop_button,1)
        self.lyt.addWidget(self.sl,2)
        self.lyt.addWidget(self.frame_counter,3)
        self.lyt.addWidget(self.video_time,4)
        
        self.show()
    
    def assign_images(self,images,centres = None):
    
        '''#first disconnect signals from eachother so nothing should change whilst video data is being updated
        self.sl.valueChanged.disconnect(self.video_time_update)
        self.frameratetimer.timeout.disconnect(self.update_display)
        self.frame_counter.valueChanged.disconnect(self.whenslidechanges)
        '''
        
        self.video = images
        self.coords = centres
        self.videobox.activeframe = self.video[0]
        if self.coords is not None:
            self.videobox.activecoord = self.coords[0]

        #readjust slider and ticker values to dimensions of video
        
        self.sl.setMaximum(len(self.video)-1)
        self.frame_counter.setRange(self.sl.minimum(),self.sl.maximum())
        self.video_time.setRange(self.sl.minimum(),30*self.sl.maximum())
        
        
        
        
        #connect slider and timer etc.
    
        self.sl.valueChanged.connect(self.whenslidechanges)
        self.frameratetimer.timeout.connect(self.update_display)
        self.frame_counter.valueChanged.connect(self.video_time_update)
        
        self.videobox.maxintens = np.max(self.video)
        self.videobox.update()
        
        
    def update_display(self):
        
        if self.threshold_switch:
            frame = self.video[livestream.i]
            threshold = threshold_otsu(frame)
            
            mask = np.zeros_like(frame)
            mask[frame > threshold] = 1
            self.videobox.maxintens = 1
            self.videobox.activeframe = mask
        else:
            #if threshold switch is off display usual video, so change active frame source and reset maximum intensity for passing to qimage2ndarray
            self.videobox.activeframe = self.video[livestream.i]
            self.videobox.maxintens = np.max(self.video)
            
        try:
            self.videobox.activecoord = self.coords[livestream.i]

            if not self.videobox.switch:
                
            
                self.videobox.switch = True
                
        except:
            self.videobox.activecoord = None
            self.videobox.switch = False
            
            
        self.videobox.update()
        self.frame_counter.setValue(float(livestream.i))
        
        livestream.i+=1
       
    def whenslidechanges(self):
        
        if self.frameratetimer.isActive():
            self.frameratetimer.stop()
            
            livestream.i = self.sl.value()
        
            self.update_display()
            livestream.i -=1
            
            self.frameratetimer.start()
        else:
            
            livestream.i = self.sl.value()
        
            self.update_display()
            livestream.i -=1
    
    def video_time_update(self):
        self.video_time.setValue(30*self.frame_counter.value())
        
        
    def turn_on_threshold(self,threshold_switch):
        self.threshold_switch = threshold_switch
        self.update_display()
示例#3
0
class ColorbarEditor(EasyDialog):
    NAME = _("Colorbar editor")
    HELP_BODY = _("Click a triangle to change its color. <br>"
    "Drag triangles to move. <br>"
    "Click in an empty area to add a new color. <br>"
    "Right click a triangle to remove. <br>"
    "Right click axis or region, click View All, to zoom. <br>"
    "Mouse wheel zoom in/out when cursor on axis or region. <br>"
    "After zoom out, drag the region to move along the axis. <br>"
    "Right click the colorbar to select different colormap. <br>"
    "One of the four bars can be enabled by set Orientation. <br>"
    "The bar widgets can be resized by the move the splitter. <br>")

    def __init__(self, parent=None):
        EasyDialog.__init__(self, parent=parent, set_tree=True, set_db=True)

        self.dob = None
        self.clip_min = 0
        self.clip_max = 1
        self.setup_page()

    def setup_page(self):
        vbox = QVBoxLayout()
        btnWidget = QWidget(self)
        btnWidget.setLayout(vbox)

        text = _("Object")
        geom = ['Point', 'Line', 'Tsurface', 'Gsurface', 'Cube']
        self.grabob = self.create_grabob(text, geom=geom)
        vbox.addWidget(self.grabob)

        text = _("Property")
        self.prop = self.create_combobox(text)

        btn_load_property = QPushButton(_('Load'))
        btn_load_property.clicked.connect(self.load_property)
        hbox = QHBoxLayout()
        hbox.addWidget(self.prop)
        hbox.addWidget(btn_load_property)
        vbox.addLayout(hbox)

        lbl_orientation = QLabel(_('Orientation'))
        rb_top = QRadioButton('Top')
        rb_bottom = QRadioButton('Bottom')
        rb_left = QRadioButton('Left')
        rb_right = QRadioButton('Right')
        hbox = QHBoxLayout()
        hbox.addWidget(lbl_orientation)
        hbox.addWidget(rb_top)
        hbox.addWidget(rb_bottom)
        hbox.addWidget(rb_left)
        hbox.addWidget(rb_right)
        vbox.addLayout(hbox)

        lbl_clip_min = QLabel(_('Clip minimum'))
        lbl_clip_max = QLabel(_('Clip maximum'))
        self.le_clip_min = QLineEdit('0')
        self.le_clip_max = QLineEdit('1')
        hbox = QHBoxLayout()
        hbox.addWidget(lbl_clip_min)
        hbox.addWidget(self.le_clip_min)
        hbox.addWidget(lbl_clip_max)
        hbox.addWidget(self.le_clip_max)
        vbox.addLayout(hbox)

        opacity = QLabel(_('Opacity'))
        self.opacity = QSlider(Qt.Horizontal)
        self.opacity.setTracking(False)
        self.opacity.setTickPosition(QSlider.TicksBelow)
        self.opacity.setSingleStep(1)
        self.opacity.setRange(0, 255)
        self.opacity.setValue(255)
        self.opacity.valueChanged.connect(self.opacity_changed)
        hbox = QHBoxLayout()
        hbox.addWidget(opacity)
        hbox.addWidget(self.opacity)
        vbox.addLayout(hbox)

        action = self.create_action()
        vbox.addWidget(action)

        hlut_right = HistogramLUTWidget(orientation='right',
                                        gradients=customGradients)
        hlut_left = HistogramLUTWidget(orientation='left',
                                       gradients=customGradients)
        hlut_bottom = HistogramLUTWidget(orientation='bottom',
                                         gradients=customGradients)
        hlut_top = HistogramLUTWidget(orientation='top',
                                      gradients=customGradients)
        lbl_hlut_help = QLabel("You can activate any one of the four.")

        split1 = QSplitter(Qt.Vertical)
        split1.addWidget(hlut_top)
        split1.addWidget(lbl_hlut_help)
        split1.addWidget(hlut_bottom)
        # split1.setStretchFactor(0, 0)
        # split1.setStretchFactor(1, 1)
        # split1.setStretchFactor(2, 0)
        # split1.setSizes([50, 400, 50])

        split2 = QSplitter(Qt.Horizontal)
        split2.addWidget(hlut_left)
        split2.addWidget(split1)
        split2.addWidget(hlut_right)

        split3 = QSplitter(Qt.Vertical)
        split3.addWidget(btnWidget)
        split3.addWidget(split2)
        self.layout.addWidget(split3)

        # self.le_clip_min.editingFinished.connect(self.clip_changed)
        # self.le_clip_max.editingFinished.connect(self.clip_changed)

        self.hlut_list = [hlut_top, hlut_bottom, hlut_left, hlut_right]

        rb_top.toggled.connect(lambda:self.set_orientation(rb_top))
        rb_bottom.toggled.connect(lambda:self.set_orientation(rb_bottom))
        rb_left.toggled.connect(lambda:self.set_orientation(rb_left))
        rb_right.toggled.connect(lambda:self.set_orientation(rb_right))
        rb_right.setChecked(True)

    def opacity_changed(self):
        """
        Potentially can use non-constant opacity e.g. user can define
        any opacity gradient, linear interpolate on the color gradient
        ticks and set Alpha in the RGBA.
        """
        opacity = self.opacity.value()
        gradient = self.hlut_active.gradient.saveState()
        set_gradient_alpha(gradient, opacity)
        prop_name = self.prop_name
        self.dob.set_gradient(prop_name, gradient)
        self.dob.make_colormap(prop_name)
        self.dob.update_plots_by_prop()

    def set_orientation(self, rb):
        if rb.isChecked():
            if rb.text() == "Top":
                index = 0
            elif rb.text() == "Bottom":
                index = 1
            elif rb.text() == "Left":
                index = 2
            elif rb.text() == "Right":
                index = 3
            else:
                raise ValueError("Unknown value")

            self.hlut_active = self.hlut_list[index]
            self.hlut_active.setEnabled(True)
            for i in range(len(self.hlut_list)):
                if i != index:
                    self.hlut_list[i].setEnabled(False)

            self.hlut_active.sigLevelChangeFinished.connect(self.level_changed)
            self.hlut_active.sigLevelChangeFinished.connect(self.apply)
            # self.hlut_active.sigLookupTableChanged.connect(self.apply)

            if self.dob is not None:
                self.load_hlut()

    def level_changed(self):
        """
        Level is changed in the hlut by mouse dragging, now sync textbox.
        """
        self.clip_min, self.clip_max = self.hlut_active.getLevels()
        self.le_clip_min.setText(str(self.clip_min))
        self.le_clip_max.setText(str(self.clip_max))

    def clip_changed(self):
        """
        Clip is changed in the textbox by user typing, now sync hlut.
        """
        self.clip_min = float(self.le_clip_min.text())
        self.clip_max = float(self.le_clip_max.text())
        self.hlut_active.setLevels(self.clip_min, self.clip_max)

    def apply(self):
        if self.dob is None:
            logger.warning('No data object is loaded yet')
            return
        self.clip_changed()
        prop_name = self.prop_name
        # save to dob for updating plots of the object
        clip = self.hlut_active.getLevels()
        self.dob.set_clip(prop_name, clip)
        # save the colorbar/gradient as a dictionary
        gradient = self.hlut_active.gradient.saveState()
        opacity = self.opacity.value()
        set_gradient_alpha(gradient, opacity)
        self.dob.set_gradient(prop_name, gradient)
        self.dob.make_colormap(prop_name)
        self.dob.update_plots_by_prop()

        # TODO handle points multiple properties

    def grab_object_rc(self):
        """ Used when dialog is brought up by right click in tree. """
        geom = ['Point', 'Line', 'Tsurface', 'Gsurface', 'Cube']
        self.dob = self.treebase.grab_object(geom)
        self.grabob.lineedit.edit.setText(self.dob.name)
        self.propList = list(self.dob.prop.keys())
        self.prop.combobox.clear()
        self.prop.combobox.addItems(self.propList)
        self.grab_property()

    def load_object(self):
        self.dob = self.object  # from EasyDialog grab_object
        self.propList = list(self.dob.prop.keys())
        self.prop.combobox.clear()
        self.prop.combobox.addItems(self.propList)
        self.grab_property()

    def grab_property(self):
        prop_name = self.dob.current_property
        index = self.propList.index(prop_name)
        self.prop.combobox.setCurrentIndex(index)

    def load_property(self):
        """
        """
        object_name = self.grabob.lineedit.edit.text()
        self.prop_name = self.prop.combobox.currentText()
        self.dob = self.database[object_name]
        self.load_hlut()

    def load_hlut(self):
        prop_name = self.prop_name
        cg = self.dob.prop[prop_name]['colorGradient']
        if cg is not None:
            self.hlut_active.gradient.restoreState(cg)

        self.clip_min, self.clip_max = self.dob.prop[prop_name]['colorClip']
        self.hlut_active.setLevels(self.clip_min, self.clip_max)

        # Assume constant alpha, so use the first value
        alpha = cg['ticks'][0][1][3]
        self.opacity.setValue(alpha)
示例#4
0
class PyDMSlider(QFrame, TextFormatter, PyDMWritableWidget):
    """
    A QSlider with support for Channels and more from PyDM.

    Parameters
    ----------
    parent : QWidget
        The parent widget for the Label
    init_channel : str, optional
        The channel to be used by the widget.
    """
    actionTriggered = Signal(int)
    rangeChanged = Signal(float, float)
    sliderMoved = Signal(float)
    sliderPressed = Signal()
    sliderReleased = Signal()
    valueChanged = Signal(float)

    def __init__(self, parent=None, init_channel=None):
        QFrame.__init__(self, parent)
        PyDMWritableWidget.__init__(self, init_channel=init_channel)
        self.alarmSensitiveContent = True
        self.alarmSensitiveBorder = False
        # Internal values for properties
        self._ignore_mouse_wheel = False
        self._show_limit_labels = True
        self._show_value_label = True
        self._user_defined_limits = False
        self._needs_limit_info = True
        self._minimum = None
        self._maximum = None
        self._user_minimum = -10.0
        self._user_maximum = 10.0
        self._num_steps = 101
        self._orientation = Qt.Horizontal
        # Set up all the internal widgets that make up a PyDMSlider.
        # We'll add all these things to layouts when we call setup_widgets_for_orientation
        label_size_policy = QSizePolicy(QSizePolicy.Maximum, QSizePolicy.Fixed)
        self.low_lim_label = QLabel(self)
        self.low_lim_label.setObjectName("lowLimLabel")
        self.low_lim_label.setSizePolicy(label_size_policy)
        self.low_lim_label.setAlignment(Qt.AlignLeft | Qt.AlignTrailing | Qt.AlignVCenter)
        self.value_label = QLabel(self)
        self.value_label.setObjectName("valueLabel")
        self.value_label.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
        self.high_lim_label = QLabel(self)
        self.high_lim_label.setObjectName("highLimLabel")
        self.high_lim_label.setSizePolicy(label_size_policy)
        self.high_lim_label.setAlignment(Qt.AlignRight | Qt.AlignTrailing | Qt.AlignVCenter)
        self._slider = QSlider(parent=self)
        self._slider.setOrientation(Qt.Horizontal)
        self._orig_wheel_event = self._slider.wheelEvent
        self._slider.sliderMoved.connect(self.internal_slider_moved)
        self._slider.sliderPressed.connect(self.internal_slider_pressed)
        self._slider.sliderReleased.connect(self.internal_slider_released)
        self._slider.valueChanged.connect(self.internal_slider_value_changed)
        # self.vertical_layout.addWidget(self._slider)
        # Other internal variables and final setup steps
        self._slider_position_to_value_map = None
        self._mute_internal_slider_changes = False
        self.setup_widgets_for_orientation(self._orientation)
        self.reset_slider_limits()

    def wheelEvent(self, e):
        #We specifically want to ignore mouse wheel events.
        if self._ignore_mouse_wheel:
            e.ignore()
        else:
            super(PyDMSlider, self).wheelEvent(e)
        return

    def init_for_designer(self):
        """
        Method called after the constructor to tweak configurations for
        when using the widget with the Qt Designer
        """
        self.value = 0.0

    @Property(Qt.Orientation)
    def orientation(self):
        """
        The slider orientation (Horizontal or Vertical)

        Returns
        -------
        int
            Qt.Horizontal or Qt.Vertical
        """
        return self._orientation

    @orientation.setter
    def orientation(self, new_orientation):
        """
        The slider orientation (Horizontal or Vertical)

        Parameters
        ----------
        new_orientation : int
            Qt.Horizontal or Qt.Vertical
        """
        self._orientation = new_orientation
        self.setup_widgets_for_orientation(new_orientation)

    def setup_widgets_for_orientation(self, new_orientation):
        """
        Reconstruct the widget given the orientation.

        Parameters
        ----------
        new_orientation : int
            Qt.Horizontal or Qt.Vertical
        """
        if new_orientation not in (Qt.Horizontal, Qt.Vertical):
            logger.error("Invalid orientation '{0}'. The existing layout will not change.".format(new_orientation))
            return

        layout = None
        if new_orientation == Qt.Horizontal:
            layout = QVBoxLayout()
            layout.setContentsMargins(4, 0, 4, 4)
            label_layout = QHBoxLayout()
            label_layout.addWidget(self.low_lim_label)
            label_layout.addStretch(0)
            label_layout.addWidget(self.value_label)
            label_layout.addStretch(0)
            label_layout.addWidget(self.high_lim_label)
            layout.addLayout(label_layout)
            self._slider.setOrientation(new_orientation)
            layout.addWidget(self._slider)
        elif new_orientation == Qt.Vertical:
            layout = QHBoxLayout()
            layout.setContentsMargins(0, 4, 4, 4)
            label_layout = QVBoxLayout()
            label_layout.addWidget(self.high_lim_label)
            label_layout.addStretch(0)
            label_layout.addWidget(self.value_label)
            label_layout.addStretch(0)
            label_layout.addWidget(self.low_lim_label)
            layout.addLayout(label_layout)
            self._slider.setOrientation(new_orientation)
            layout.addWidget(self._slider)

        if self.layout() is not None:
            # Trick to remove the existing layout by re-parenting it in an empty widget.
            QWidget().setLayout(self.layout())
        self.setLayout(layout)

    def update_labels(self):
        """
        Update the limits and value labels with the correct values.
        """
        def set_label(value, label_widget):
            if value is None:
                label_widget.setText("")
            else:
                label_widget.setText(self.format_string.format(value))

        set_label(self.minimum, self.low_lim_label)
        set_label(self.maximum, self.high_lim_label)
        set_label(self.value, self.value_label)

    def reset_slider_limits(self):
        """
        Reset the limits and adjust the labels properly for the slider.
        """
        logger.debug("Running reset_slider_limits.")
        if self.minimum is None or self.maximum is None:
            self._needs_limit_info = True
            logger.debug("Need both limits before reset_slider_limits can work.")
            self.set_enable_state()
            return
        logger.debug("Has both limits, proceeding.")
        self._needs_limit_info = False
        self._slider.setMinimum(0)
        self._slider.setMaximum(self._num_steps - 1)
        self._slider.setSingleStep(1)
        self._slider.setPageStep(10)
        self._slider_position_to_value_map = np.linspace(self.minimum, self.maximum, num=self._num_steps)
        self.update_labels()
        self.set_slider_to_closest_value(self.value)
        self.rangeChanged.emit(self.minimum, self.maximum)
        self.set_enable_state()

    def find_closest_slider_position_to_value(self, val):
        """
        Find and returns the index for the closest position on the slider
        for a given value.

        Parameters
        ----------
        val : float

        Returns
        -------
        int
        """
        diff = abs(self._slider_position_to_value_map - float(val))
        logger.debug("The closest value to %f is: %f", val, self._slider_position_to_value_map[np.argmin(diff)])
        return np.argmin(diff)

    def set_slider_to_closest_value(self, val):
        """
        Set the value for the slider according to a given value.

        Parameters
        ----------
        val : float
        """
        if val is None or self._needs_limit_info:
            logger.debug("Not setting slider to closest value because we need limits.")
            return
        # When we set the slider to the closest value, it may end up at a slightly
        # different position than val (if val is not in self._slider_position_to_value_map)
        # We don't want that slight difference to get broacast out and put the channel
        # somewhere new.    For example, if the slider can only be at 0.4 or 0.5, but a
        # new value comes in of 0.45, its more important to keep the 0.45 than to change
        # it to where the slider gets set.  Therefore, we mute the internal slider changes
        # so that its valueChanged signal doesn't cause us to emit a signal to PyDM to change
        # the value of the channel.
        logger.debug("Setting slider to closest value.")
        self._mute_internal_slider_changes = True
        self._slider.setValue(self.find_closest_slider_position_to_value(val))
        self._mute_internal_slider_changes = False

    def connection_changed(self, connected):
        """
        Callback invoked when the connection state of the Channel is changed.
        This callback acts on the connection state to enable/disable the widget
        and also trigger the change on alarm severity to ALARM_DISCONNECTED.

        Parameters
        ----------
        connected : int
            When this value is 0 the channel is disconnected, 1 otherwise.
        """
        super(PyDMSlider, self).connection_changed(connected)
        self.set_enable_state()

    def write_access_changed(self, new_write_access):
        """
        Callback invoked when the Channel has new write access value.
        This callback calls check_enable_state so it can act on the widget
        enabling or disabling it accordingly

        Parameters
        ----------
        new_write_access : bool
            True if write operations to the channel are allowed.
        """
        super(PyDMSlider, self).write_access_changed(new_write_access)
        self.set_enable_state()

    def value_changed(self, new_val):
        """
        Callback invoked when the Channel value is changed.

        Parameters
        ----------
        new_val : int or float
            The new value from the channel.
        """
        logger.debug("Slider got a new value = %f", float(new_val))
        PyDMWritableWidget.value_changed(self, new_val)
        if hasattr(self, "value_label"):
            logger.debug("Setting text for value label.")
            self.value_label.setText(self.format_string.format(self.value))
        if not self._slider.isSliderDown():
            self.set_slider_to_closest_value(self.value)

    def ctrl_limit_changed(self, which, new_limit):
        """
        Callback invoked when the Channel receives new control limit
        values.

        Parameters
        ----------
        which : str
            Which control limit was changed. "UPPER" or "LOWER"
        new_limit : float
            New value for the control limit
        """
        logger.debug("%s limit changed to %f", which, new_limit)
        PyDMWritableWidget.ctrl_limit_changed(self, which, new_limit)
        if not self.userDefinedLimits:
            self.reset_slider_limits()

    def update_format_string(self):
        """
        Reconstruct the format string to be used when representing the
        output value.

        Returns
        -------
        format_string : str
            The format string to be used including or not the precision
            and unit
        """
        fs = super(PyDMSlider, self).update_format_string()
        self.update_labels()
        return fs

    def set_enable_state(self):
        """
        Determines wether or not the widget must be enabled or not depending
        on the write access, connection state and presence of limits information
        """
        # Even though by documentation disabling parent QFrame (self), should disable internal
        # slider, in practice it still remains interactive (PyQt 5.12.1). Disabling explicitly, solves
        # the problem.
        should_enable = self._write_access and self._connected and not self._needs_limit_info
        self.setEnabled(should_enable)
        self._slider.setEnabled(should_enable)

    @Slot(int)
    def internal_slider_action_triggered(self, action):
        self.actionTriggered.emit(action)

    @Slot(int)
    def internal_slider_moved(self, val):
        """
        Method invoked when the slider is moved.

        Parameters
        ----------
        val : float
        """
        self.sliderMoved.emit(self.value)

    @Slot()
    def internal_slider_pressed(self):
        """
        Method invoked when the slider is pressed
        """
        self.sliderPressed.emit()

    @Slot()
    def internal_slider_released(self):
        """
        Method invoked when the slider is released
        """
        self.sliderReleased.emit()

    @Slot(int)
    def internal_slider_value_changed(self, val):
        """
        Method invoked when a new value is selected on the slider.
        This will cause the new value to be emitted to the signal
        unless `mute_internal_slider_changes` is True.

        Parameters
        ----------
        val : int
        """        
        # Avoid potential crash if limits are undefined
        if self._slider_position_to_value_map is None:
            return
        if not self._mute_internal_slider_changes:
            self.value = self._slider_position_to_value_map[val]
            self.send_value_signal[float].emit(self.value)

    @Property(bool)
    def tracking(self):
        """
        If tracking is enabled (the default), the slider emits new values
        while the slider is being dragged.  If tracking is disabled, it will
        only emit new values when the user releases the slider.  Tracking can
        cause PyDM to rapidly send new values to the channel.  If you are using
        the slider to control physical hardware, consider whether the device
        you want to control can handle large amounts of changes in a short
        timespan.
        """
        return self._slider.hasTracking()
        
    @tracking.setter
    def tracking(self, checked):
        self._slider.setTracking(checked)
    
    def hasTracking(self):
        """
        An alternative function to get the tracking property, to match what
        Qt provides for QSlider.
        """
        return self.tracking
    
    def setTracking(self, checked):
        """
        An alternative function to set the tracking property, to match what
        Qt provides for QSlider.
        """
        self.tracking = checked

    @Property(bool)
    def ignoreMouseWheel(self):
        """
        If true, the mouse wheel will not change the value of the slider.
        This is useful if you want to put sliders inside a scroll view, and
        don't want to accidentally change the slider as you are scrolling.
        """
        return self._ignore_mouse_wheel
        
    @ignoreMouseWheel.setter
    def ignoreMouseWheel(self, checked):
        self._ignore_mouse_wheel = checked
        if checked:
            self._slider.wheelEvent = self.wheelEvent
        else:
            self._slider.wheelEvent = self._orig_wheel_event

    @Property(bool)
    def showLimitLabels(self):
        """
        Whether or not the high and low limits should be displayed on the slider.

        Returns
        -------
        bool
        """
        return self._show_limit_labels

    @showLimitLabels.setter
    def showLimitLabels(self, checked):
        """
        Whether or not the high and low limits should be displayed on the slider.

        Parameters
        ----------
        checked : bool
        """
        self._show_limit_labels = checked
        if checked:
            self.low_lim_label.show()
            self.high_lim_label.show()
        else:
            self.low_lim_label.hide()
            self.high_lim_label.hide()

    @Property(bool)
    def showValueLabel(self):
        """
        Whether or not the current value should be displayed on the slider.

        Returns
        -------
        bool
        """
        return self._show_value_label

    @showValueLabel.setter
    def showValueLabel(self, checked):
        """
        Whether or not the current value should be displayed on the slider.

        Parameters
        ----------
        checked : bool
        """
        self._show_value_label = checked
        if checked:
            self.value_label.show()
        else:
            self.value_label.hide()

    @Property(QSlider.TickPosition)
    def tickPosition(self):
        """
        Where to draw tick marks for the slider.

        Returns
        -------
        QSlider.TickPosition
        """
        return self._slider.tickPosition()

    @tickPosition.setter
    def tickPosition(self, position):
        """
        Where to draw tick marks for the slider.

        Parameter
        ---------
        position : QSlider.TickPosition
        """
        self._slider.setTickPosition(position)

    @Property(bool)
    def userDefinedLimits(self):
        """
        Wether or not to use limits defined by the user and not from the
        channel

        Returns
        -------
        bool
        """
        return self._user_defined_limits

    @userDefinedLimits.setter
    def userDefinedLimits(self, user_defined_limits):
        """
        Wether or not to use limits defined by the user and not from the
        channel

        Parameters
        ----------
        user_defined_limits : bool
        """
        self._user_defined_limits = user_defined_limits
        self.reset_slider_limits()

    @Property(float)
    def userMinimum(self):
        """
        Lower user defined limit value

        Returns
        -------
        float
        """
        return self._user_minimum

    @userMinimum.setter
    def userMinimum(self, new_min):
        """
        Lower user defined limit value

        Parameters
        ----------
        new_min : float
        """
        self._user_minimum = float(new_min) if new_min is not None else None
        if self.userDefinedLimits:
            self.reset_slider_limits()

    @Property(float)
    def userMaximum(self):
        """
        Upper user defined limit value

        Returns
        -------
        float
        """
        return self._user_maximum

    @userMaximum.setter
    def userMaximum(self, new_max):
        """
        Upper user defined limit value

        Parameters
        ----------
        new_max : float
        """
        self._user_maximum = float(new_max) if new_max is not None else None
        if self.userDefinedLimits:
            self.reset_slider_limits()

    @property
    def minimum(self):
        """
        The current value being used for the lower limit

        Returns
        -------
        float
        """
        if self.userDefinedLimits:
            return self._user_minimum
        return self._lower_ctrl_limit

    @property
    def maximum(self):
        """
        The current value being used for the upper limit

        Returns
        -------
        float
        """
        if self.userDefinedLimits:
            return self._user_maximum
        return self._upper_ctrl_limit

    @Property(int)
    def num_steps(self):
        """
        The number of steps on the slider

        Returns
        -------
        int
        """
        return self._num_steps

    @num_steps.setter
    def num_steps(self, new_steps):
        """
        The number of steps on the slider

        Parameters
        ----------
        new_steps : int
        """
        self._num_steps = int(new_steps)
        self.reset_slider_limits()
示例#5
0
class TrapViewer(QWidget):
    i = 0

    def __init__(self, qnd, images, trap_positions=None, labels=None):
        QWidget.__init__(self)

        self.video = images  # This is a file object buffer containing the images

        self.trap_positions = trap_positions
        self.labels = labels
        self.videobox = Label(trap_positions, labels)
        self.videobox.activeframe = images.asarray(key=TrapViewer.i)
        try:
            self.videobox.maxintens = int(images.imagej_metadata['max'])
            self.videobox.maxintens = 15265
            print(images.imagej_metadata)
        except KeyError:
            self.videobox.maxintens = int(np.max(self.videobox.activeframe))

        self.videobox.setGeometry(QtCore.QRect(70, 80, 200, 200))

        self.lyt = QVBoxLayout()
        self.lyt.addWidget(self.videobox, 5)

        self.setLayout(self.lyt)

        self.sl = QSlider(Qt.Horizontal)

        self.sl.setMinimum(0.0)
        self.sl.setMaximum(self.video.imagej_metadata['frames'] - 1)

        self.sl.setTickPosition(QSlider.TicksAbove)
        self.sl.setTracking(True)
        self.sl.setTickInterval(100)

        self.sl.valueChanged.connect(self.whenslidechanges)

        self.frame_counter = QDoubleSpinBox()
        self.frame = self.videobox.activeframe
        self.frame_counter.setSingleStep(1)
        self.frame_counter.setRange(self.sl.minimum(), self.sl.maximum() - 1)
        self.frame_counter.valueChanged.connect(self.sl.setValue)
        self.frame_counter.valueChanged.connect(self.video_time_update)

        self.video_time = QDoubleSpinBox()
        self.video_time.setSingleStep(30)
        self.video_time.setRange(self.sl.minimum(), 30 * self.sl.maximum() - 1)
        self.frameratetimer = QTimer()
        self.frameratetimer.setInterval(50)
        self.frameratetimer.timeout.connect(self.update_display)

        self.play_button = QPushButton('Play Video')
        self.play_button.clicked.connect(self.frameratetimer.start)

        self.stop_button = QPushButton('Stop Video')
        self.stop_button.clicked.connect(self.frameratetimer.stop)
        self.sl.valueChanged.connect(self.whenslidechanges)

        self.lyt.addWidget(self.play_button, 0)
        self.lyt.addWidget(self.stop_button, 1)
        self.lyt.addWidget(self.sl, 2)
        self.lyt.addWidget(self.frame_counter, 3)
        self.lyt.addWidget(self.video_time, 4)

        self.show()

    def update_display(self):

        self.frame = self.video.asarray(key=TrapViewer.i)
        self.videobox.activeframe = self.frame

        self.videobox.update()

        self.frame_counter.setValue(float(TrapViewer.i))

        if TrapViewer.i < self.video.imagej_metadata['frames']:

            TrapViewer.i += 1

    def whenslidechanges(self):

        if self.frameratetimer.isActive():
            self.frameratetimer.stop()

            TrapViewer.i = self.sl.value()

            self.update_display()
            TrapViewer.i -= 1

            self.frameratetimer.start()
        else:

            TrapViewer.i = self.sl.value()

            self.update_display()
            TrapViewer.i -= 1

    def video_time_update(self):
        self.video_time.setValue(30 * self.frame_counter.value())