Ejemplo n.º 1
0
def map_property(type, attrname, notify=None, on_modified=None):
	def getter(self):
		return getattr(self, attrname)
	def setter(self, val):
		setattr(self, attrname, val)
		if notify: getattr(self, notify).emit()
		if on_modified: getattr(self, on_modified)()
	if notify:
		return pyqtProperty(type, getter, setter, notify)
	else:
		return pyqtProperty(type, getter, setter)
Ejemplo n.º 2
0
    def magic_property(prop_name, prop_type, notify, notify_name):
        """
            Create pyqtProperty object with value captured in the closure
            and a 'notify' signal attached.
            You need to pass both:
                - unbound signal, because pyqtProperty needs it
                - signal name to allow setter to find the *bound* signal
                  and emit it.
        """

        def getter(self):
            value = getattr(self, '_' + prop_name)
            return value

        def setter(self, value):
            setattr(self, '_' + prop_name, value)
            getattr(self, notify_name).emit(value)

        prop = pyqtProperty(
            type=prop_type,
            fget=getter,
            fset=setter,
            notify=notify)

        return prop
Ejemplo n.º 3
0
def dataPyqtProperty( type, origName ):
	name = '_' + origName

	signalName = origName + 'Changed' 
	signal = pyqtSignal( type, name = signalName )

	def getter( self ):
		return getattr( self, name )

	def setter( self, value ):
		if value != getter( self ):
			getattr( self, signalName ).emit( value )

		setattr( self, name, value )

	return pyqtProperty( type, getter, setter, notify = signal ), signal
Ejemplo n.º 4
0
def stylePyqtProperty( type, origName ):
	name = '_' + origName

	signalName = origName + 'Changed' 
	signal = pyqtSignal( type, name = signalName )

	def getter( self ):
		return self.lookupProperty( origName )

	def setter( self, value ):
		print( f'setting {origName} of {self.parent().metaObject().className()} to {value}' )
		if getattr( self, name ) != value:
			setattr( self, name, value )
			self.notify( origName, value )

	return pyqtProperty( type, getter, setter, notify = signal ), signal
Ejemplo n.º 5
0
def simpleProperty(type, name, notify, readonly=False):
    _name = "_" + name
    def getter(obj):
        return getattr(obj, _name)

    def setter(obj, value):
        if getattr(obj, _name) != value:
            qDebug("set %s %s" % (_name, value))
            setattr(obj, _name, value)
            notify.__get__(obj).emit()

    fset = setter
    if readonly:
        fset = None

    return pyqtProperty(type, fget=getter, fset=fset, notify=notify)
Ejemplo n.º 6
0
class HALLabel(QLabel, _HalWidgetBase):
    def __init__(self, parent=None):
        super(HALLabel, self).__init__(parent)
        self._textTemplate = '%f'
        self.istate = False
        self._pin_name = ''
        self._bit_pin_type = True
        self._s32_pin_type = False
        self._float_pin_type = False

    def _hal_init(self):
        if self._pin_name == '':
            pname = self.HAL_NAME_
        else:
            pname = self._pin_name
        if self._bit_pin_type:
            self.hal_pin = self.HAL_GCOMP_.newpin(pname, hal.HAL_BIT,
                                                  hal.HAL_IN)
            self.hal_pin.value_changed.connect(
                lambda data: self._setText(data))
        elif self._float_pin_type:
            self.hal_pin = self.HAL_GCOMP_.newpin(pname, hal.HAL_FLOAT,
                                                  hal.HAL_IN)
            self.hal_pin.value_changed.connect(
                lambda data: self._setText(data))
        elif self._s32_pin_type:
            self.hal_pin = self.HAL_GCOMP_.newpin(pname, hal.HAL_S32,
                                                  hal.HAL_IN)
            self.hal_pin.value_changed.connect(
                lambda data: self._setText(data))

    def _setText(self, data):
        tmpl = lambda s: str(self._textTemplate) % s
        self.setText(tmpl(data))

    # one can connect signals to this widget to
    # feed an input that gets scaled by this widget.
    @pyqtSlot(float)
    @pyqtSlot(int)
    @pyqtSlot(bool)
    def setDisplay(self, data):
        self._setText(data)

    #########################################################################
    # This is how designer can interact with our widget properties.
    # designer will show the pyqtProperty properties in the editor
    # it will use the get set and reset calls to do those actions
    ########################################################################

    def _toggle_properties(self, picked):
        data = ('bit', 's32', 'float')

        for i in data:
            if not i == picked:
                self[i + '_pin_type'] = False

    def set_pin_name(self, value):
        self._pin_name = value

    def get_pin_name(self):
        return self._pin_name

    def reset_pin_name(self):
        self._pin_name = ''

    def set_bit_pin_type(self, value):
        self._bit_pin_type = value
        if value:
            self._toggle_properties('bit')

    def get_bit_pin_type(self):
        return self._bit_pin_type

    def reset_bit_pin_type(self):
        self._bit_pin_type = ''

    def set_s32_pin_type(self, value):
        self._s32_pin_type = value
        if value:
            self._toggle_properties('s32')

    def get_s32_pin_type(self):
        return self._s32_pin_type

    def reset_s32_pin_type(self):
        self._s32_pin_type = ''

    def set_float_pin_type(self, value):
        self._float_pin_type = value
        if value:
            self._toggle_properties('float')

    def get_float_pin_type(self):
        return self._float_pin_type

    def reset_float_pin_type(self):
        self._float_pin_type = ''

    def set_textTemplate(self, data):
        self._textTemplate = data

    def get_textTemplate(self):
        return self._textTemplate

    def reset_textTemplate(self):
        self._textTemplate = '%d'

    # designer will show these properties in this order:
    pin_name = pyqtProperty(str, get_pin_name, set_pin_name, reset_pin_name)
    bit_pin_type = pyqtProperty(bool, get_bit_pin_type, set_bit_pin_type,
                                reset_bit_pin_type)
    s32_pin_type = pyqtProperty(bool, get_s32_pin_type, set_s32_pin_type,
                                reset_s32_pin_type)
    float_pin_type = pyqtProperty(bool, get_float_pin_type, set_float_pin_type,
                                  reset_float_pin_type)
    textTemplate = pyqtProperty(str, get_textTemplate, set_textTemplate,
                                reset_textTemplate)

    ##############################
    # required class boiler code #
    ##############################

    def __getitem__(self, item):
        return getattr(self, item)

    def __setitem__(self, item, value):
        return setattr(self, item, value)
Ejemplo n.º 7
0
class  GCodeGraphics(Lcnc_3dGraphics, _HalWidgetBase):
    def __init__(self, parent=None):
        super( GCodeGraphics, self).__init__(parent)

        self.colors['overlay_background'] = (0.0, 0.0, 0.0)  # blue
        self._overlayColor = QColor(0, 0, 0, 0)

        self.colors['back'] = (0.0, 0.0, 0.75)  # blue
        self._backgroundColor = QColor(0, 0, 0.75, 150)

        self.use_gradient_background = False
        # color1 is the bottom color that blends up to color2
        self.gradient_color1 = (0.,0,.5)
        self.gradient_color2 = (0,.0, 0)

        self.show_overlay = False  # no DRO or DRO overlay
        self._reload_filename = None

        self._view_incr = 20
        self.inhibit_selection = False

    def _hal_init(self):
        STATUS.connect('file-loaded', self.load_program)
        STATUS.connect('reload-display', self.reloadfile)
        STATUS.connect('actual-spindle-speed-changed', self.set_spindle_speed)
        STATUS.connect('metric-mode-changed', lambda w, f: self.set_metric_units(w, f))
        STATUS.connect('graphics-view-changed', self.set_view_signal)

    def set_view_signal(self, w, view, args):
        v = view.lower()
        if v == 'clear':
            self.logger.clear()
        elif v == 'zoom-in':
            self.zoomin()
        elif v == 'zoom-out':
            self.zoomout()
        elif v == 'pan-down':
            self.recordMouse(0,0)
            self.translateOrRotate(0,self._view_incr)
        elif v == 'pan-up':
            self.recordMouse(0,0)
            self.translateOrRotate(0,-self._view_incr)
        elif v == 'pan-right':
            self.recordMouse(0,0)
            self.translateOrRotate(self._view_incr,0)
        elif v == 'pan-left':
            self.recordMouse(0,0)
            self.translateOrRotate(-self._view_incr,0)
        elif v == 'rotate-ccw':
            self.recordMouse(0,0)
            self.rotateOrTranslate(self._view_incr,0)
        elif v == 'rotate-cw':
            self.recordMouse(0,0)
            self.rotateOrTranslate(-self._view_incr,0)
        elif v == 'rotate-up':
            self.recordMouse(0,0)
            self.rotateOrTranslate(0,self._view_incr)
        elif v == 'rotate-down':
            self.recordMouse(0,0)
            self.rotateOrTranslate(0,-self._view_incr)
        elif v == 'overlay-offsets-on':
            self.setShowOffsets(True)
        elif v == 'overlay-offsets-off':
            self.setShowOffsets(False)
        elif v == 'overlay-dro-on':
            self.setdro(True)
        elif v == 'overlay-dro-off':
            self.setdro(False)
        elif v == 'pan-view':
            self.panView(args.get('X'),args.get('Y'))
        elif v == 'rotate-view':
            self.rotateView(args.get('X'),args.get('Y'))
        elif v == 'grid-size':
            self.grid_size = args.get('SIZE')
            self.updateGL()
        elif v == 'alpha-mode-on':
            self.set_alpha_mode(True)
        elif v == 'alpha-mode-off':
            self.set_alpha_mode(False)
        elif v == 'inhibit-selection-on':
            self.inhibit_selection = True
        elif v == 'inhibit-selection-off':
            self.inhibit_selection = False
        elif v == 'dimensions-on':
            self.show_extents_option = True
            self.updateGL()
        elif v == 'dimensions-off':
            self.show_extents_option = False
            self.updateGL()
        else:
            self.set_view(v)

    def load_program(self, g, fname):
        LOG.debug('load the display: {}'.format(fname))
        self._reload_filename = fname
        self.load(fname)
        STATUS.emit('graphics-gcode-properties',self.gcode_properties)
        # reset the current view to standard calculated zoom and position
        self.set_current_view()

    def set_metric_units(self, w, state):
        self.metric_units = state
        self.updateGL()

    def set_spindle_speed(self, w, rate):
        if rate < 1: rate = 1
        self.spindle_speed = rate

    def set_view(self, value):
        view = str(value).lower()
        if self.lathe_option:
            if view not in ['p', 'y', 'y2']:
                return False
        elif view not in ['p', 'x', 'y', 'z', 'z2']:
            return False
        self.current_view = view
        if self.initialised:
            self.set_current_view()

    def reloadfile(self, w):
        LOG.debug('reload the display: {}'.format(self._reload_filename))
        try:
            self.load(self._reload_filename)
            STATUS.emit('graphics-gcode-properties',self.gcode_properties)
        except:
            print('error', self._reload_filename)
            pass



    ####################################################
    # functions that override qt5_graphics
    ####################################################
    def report_gcode_error(self, result, seq, filename):
        error_str = gcode.strerror(result)
        errortext = "G-Code error in " + os.path.basename(filename) + "\n" + "Near line " \
                    + str(seq) + " of\n" + filename + "\n" + error_str + "\n"
        STATUS.emit("graphics-gcode-error", errortext)

    # Override qt5_graphics / glcannon.py function so we can emit a GObject signal
    def update_highlight_variable(self, line):
        self.highlight_line = line
        if line is None:
            line = -1
        STATUS.emit('graphics-line-selected', line)

    def select_fire(self):
        if self.inhibit_selection: return
        if STATUS.is_auto_running(): return
        if not self.select_primed: return
        x, y = self.select_primed
        self.select_primed = None
        self.select(x, y)

    # override user plot -One could add gl commands to plot static objects here
    def user_plot(self):
        return

    def emit_percent(self, f):
        super( GCodeGraphics, self).emit_percent(f)
        STATUS.emit('graphics-loading-progress',f)

    #########################################################################
    # This is how designer can interact with our widget properties.
    # property getter/setters
    #########################################################################

    # VIEW
    def setview(self, view):
        self.current_view = view
        self.set_view(view)
    def getview(self):
        return self.current_view
    def resetview(self):
        self.set_view('p')
    _view = pyqtProperty(str, getview, setview, resetview)

    # DRO
    def setdro(self, state):
        self.enable_dro = state
        self.updateGL()
    def getdro(self):
        return self.enable_dro
    _dro = pyqtProperty(bool, getdro, setdro)

    # DTG
    def setdtg(self, state):
        self.show_dtg = state
        self.updateGL()
    def getdtg(self):
        return self.show_dtg
    _dtg = pyqtProperty(bool, getdtg, setdtg)

    # METRIC
    def setmetric(self, state):
        self.metric_units = state
        self.updateGL()
    def getmetric(self):
        return self.metric_units
    _metric = pyqtProperty(bool, getmetric, setmetric)

    # overlay
    def setoverlay(self, overlay):
        self.show_overlay = overlay
        self.updateGL()
    def getoverlay(self):
        return self.show_overlay
    def resetoverlay(self):
        self.show_overlay(False)
    _overlay = pyqtProperty(bool, getoverlay, setoverlay, resetoverlay)

    # show Offsets
    def setShowOffsets(self, state):
        self.show_offsets = state
        self.updateGL()
    def getShowOffsets(self):
        return self.show_offsets
    _offsets = pyqtProperty(bool, getShowOffsets, setShowOffsets)

    def getOverlayColor(self):
        return self._overlayColor
    def setOverlayColor(self, value):
        self._overlayColor = value
        self.colors['overlay_background'] = (value.redF(), value.greenF(), value.blueF())
        self.updateGL()
    def resetOverlayColor(self):
        self._overlayColor = QColor(0, 0, .75, 150)
    overlay_color = pyqtProperty(QColor, getOverlayColor, setOverlayColor, resetOverlayColor)

    def getBackgroundColor(self):
        return self._backgroundColor
    def setBackgroundColor(self, value):
        self._backgroundColor = value
        #print value.getRgbF()
        self.colors['back'] = (value.redF(), value.greenF(), value.blueF())
        self.gradient_color1 = (value.redF(), value.greenF(), value.blueF())
        self.updateGL()
    def resetBackgroundColor(self):
        self._backgroundColor = QColor(0, 0, 0, 0)
        self.gradient_color1 = QColor(0, 0, 0, 0)
        value = QColor(0, 0, 0, 0)
        self.gradient_color1 = (value.redF(), value.greenF(), value.blueF())
        self.colors['back'] = (value.redF(), value.greenF(), value.blueF())
        self.updateGL()
    background_color = pyqtProperty(QColor, getBackgroundColor, setBackgroundColor, resetBackgroundColor)

    # use gradient background
    def setGradientBackground(self, state):
        self.use_gradient_background = state
        self.updateGL()
    def getGradientBackground(self):
        return self.use_gradient_background
    _use_gradient_background = pyqtProperty(bool, getGradientBackground, setGradientBackground)
class InstanceContainer(QObject, ContainerInterface, PluginObject):
    Version = 2

    ##  Constructor
    #
    #   \param container_id A unique, machine readable/writable ID for this container.
    def __init__(self, container_id, *args, **kwargs):
        super().__init__(parent = None, *args, **kwargs)

        self._id = str(container_id)    # type: str
        self._name = container_id       # type: str
        self._definition = None         # type: DefinitionContainerInterface
        self._metadata = {}
        self._instances = {}            # type: Dict[str, SettingInstance]
        self._read_only = False
        self._dirty = False
        self._path = ""
        self._postponed_emits = []

        self._cached_values = None

    def __hash__(self):
        # We need to re-implement the hash, because we defined the __eq__ operator.
        # According to some, returning the ID is technically not right, as objects with the same value should return
        # the same hash. The way we use it, it is acceptable for objects with the same value to return a different hash.
        return id(self)

    def __deepcopy__(self, memo):
        new_container = self.__class__(self._id)
        new_container._name = self._name
        new_container._definition = self._definition
        new_container._metadata = copy.deepcopy(self._metadata, memo)
        new_container._instances = copy.deepcopy(self._instances, memo)
        for instance in new_container._instances.values(): #Set the back-links of the new instances correctly to the copied container.
            instance._container = new_container
        new_container._read_only = self._read_only
        new_container._dirty = self._dirty
        new_container._path = copy.deepcopy(self._path, memo)
        new_container._cached_values = copy.deepcopy(self._cached_values, memo)
        return new_container

    def __eq__(self, other):
        self._instantiateCachedValues()
        if type(self) != type(other):
            return False  # Type mismatch

        if self._id != other.getId():
            return False  # ID mismatch

        for entry in self._metadata:
            if other.getMetaDataEntry(entry) != self._metadata[entry]:
                return False  # Meta data entry mismatch

        for entry in other.getMetaData():
            if entry not in self._metadata:
                return False  # Other has a meta data entry that this object does not have.

        for key in self._instances:
            if key not in other._instances:
                return False  # This object has an instance that other does not have.
            if self._instances[key] != other._instances[key]:
                return False  # The objects don't match.

        for key in other._instances:
            if key not in self._instances:
                return False  # Other has an instance that this object does not have.
        return True

    def __ne__(self, other):
        return not (self == other)

    ##  For pickle support
    def __getnewargs__(self):
        return (self._id,)

    ##  For pickle support
    def __getstate__(self):
        return self.__dict__

    ##  For pickle support
    def __setstate__(self, state):
        self.__dict__.update(state)

    ##  \copydoc ContainerInterface::getId
    #
    #   Reimplemented from ContainerInterface
    def getId(self) -> str:
        return self._id

    id = pyqtProperty(str, fget = getId, constant = True)

    def setCachedValues(self, cached_values):
        if not self._instances:
            self._cached_values = cached_values
        else:
            Logger.log("w", "Unable set values to be lazy loaded when values are already loaded ")

    @classmethod
    def getLoadingPriority(cls) -> int:
        return 1

    ##  \copydoc ContainerInterface::getPath.
    #
    #   Reimplemented from ContainerInterface
    def getPath(self):
        return self._path

    ##  \copydoc ContainerInterface::setPath
    #
    #   Reimplemented from ContainerInterface
    def setPath(self, path):
        self._path = path

    ##  \copydoc ContainerInterface::getName
    #
    #   Reimplemented from ContainerInterface
    def getName(self) -> str:
        return self._name

    def setName(self, name):
        if name != self._name:
            self._name = name
            self._dirty = True
            self.nameChanged.emit()
            self.pyqtNameChanged.emit()


    # Because we want to expose the properties of InstanceContainer as Qt properties for
    # CURA-3497, the nameChanged signal should be changed to a pyqtSignal. However,
    # pyqtSignal throws TypeError when calling disconnect() when there are no connections.
    # This causes a lot of errors in Cura code when we try to disconnect from nameChanged.
    # Therefore, rather than change the type of nameChanged, we add an extra signal that
    # is used as notify for the property.
    #
    # TODO: Remove this once the Cura code has been refactored to not use nameChanged anymore.
    pyqtNameChanged = pyqtSignal()

    nameChanged = Signal()
    name = pyqtProperty(str, fget = getName, fset = setName, notify = pyqtNameChanged)

    ##  \copydoc ContainerInterface::isReadOnly
    #
    #   Reimplemented from ContainerInterface
    def isReadOnly(self) -> bool:
        return self._read_only

    def setReadOnly(self, read_only):
        if read_only != self._read_only:
            self._read_only = read_only
            self.readOnlyChanged.emit()

    readOnlyChanged = pyqtSignal()
    readOnly = pyqtProperty(bool, fget = isReadOnly, fset = setReadOnly, notify = readOnlyChanged)

    ##  \copydoc ContainerInterface::getMetaData
    #
    #   Reimplemented from ContainerInterface
    def getMetaData(self):
        return self._metadata

    def setMetaData(self, metadata):
        if metadata != self._metadata:
            self._metadata = metadata
            self._dirty = True
            self.metaDataChanged.emit(self)

    metaDataChanged = pyqtSignal(QObject)
    metaData = pyqtProperty("QVariantMap", fget = getMetaData, fset = setMetaData, notify = metaDataChanged)

    ##  \copydoc ContainerInterface::getMetaDataEntry
    #
    #   Reimplemented from ContainerInterface
    def getMetaDataEntry(self, entry, default = None):
        return self._metadata.get(entry, default)

    ##  Add a new entry to the metadata of this container.
    #
    #   \param key \type{str} The key of the new entry.
    #   \param value The value of the new entry.
    #
    #   \note This does nothing if the key already exists.
    def addMetaDataEntry(self, key, value):
        if key not in self._metadata:
            self._metadata[key] = value
            self._dirty = True
            self.metaDataChanged.emit(self)
        else:
            Logger.log("w", "Meta data with key %s was already added.", key)

    ##  Set a metadata entry to a certain value.
    #
    #   \param key The key of the metadata entry to set.
    #   \param value The new value of the metadata.
    #
    #   \note This does nothing if the key is not already added to the metadata.
    def setMetaDataEntry(self, key, value):
        if key in self._metadata:
            self._metadata[key] = value
            self._dirty = True
            self.metaDataChanged.emit(self)
        else:
            Logger.log("w", "Meta data with key %s was not found. Unable to change.", key)

    ##  Check if this container is dirty, that is, if it changed from deserialization.
    def isDirty(self):
        return self._dirty

    def setDirty(self, dirty):
        if self._read_only:
            Logger.log("w", "Tried to set dirty on read-only object.")
        else:
            self._dirty = dirty

    ##  \copydoc ContainerInterface::getProperty
    #
    #   Reimplemented from ContainerInterface
    def getProperty(self, key, property_name):
        self._instantiateCachedValues()
        if key in self._instances:
            try:
                return getattr(self._instances[key], property_name)
            except AttributeError:
                pass

        return None

    ##  \copydoc ContainerInterface::hasProperty
    #
    #   Reimplemented from ContainerInterface.
    def hasProperty(self, key, property_name):
        self._instantiateCachedValues()
        return key in self._instances and hasattr(self._instances[key], property_name)

    ##  Set the value of a property of a SettingInstance.
    #
    #   This will set the value of the specified property on the SettingInstance corresponding to key.
    #   If no instance has been created for the specified key, a new one will be created and inserted
    #   into this instance.
    #
    #   \param key \type{string} The key of the setting to set a property of.
    #   \param property_name \type{string} The name of the property to set.
    #   \param property_value The new value of the property.
    #   \param container The container to use for retrieving values when changing the property triggers property updates. Defaults to None, which means use the current container.
    #   \param set_from_cache Flag to indicate that the property was set from cache. This triggers the behavior that the read_only and setDirty are ignored.
    #
    #   \note If no definition container is set for this container, new instances cannot be created and this method will do nothing.
    def setProperty(self, key, property_name, property_value, container = None, set_from_cache = False):
        if self._read_only and not set_from_cache:
            Logger.log(
                "w",
                "Tried to setProperty [%s] with value [%s] with key [%s] on read-only object [%s]" % (
                    property_name, property_value, key, self.id))
            return
        if key not in self._instances:
            if not self._definition:
                Logger.log("w", "Tried to set value of setting %s that has no instance in container %s and the container has no definition", key, self._name)
                return

            setting_definition = self._definition.findDefinitions(key = key)
            if not setting_definition:
                Logger.log("w", "Tried to set value of setting %s that has no instance in container %s or its definition %s", key, self._name, self._definition.getName())
                return

            instance = SettingInstance(setting_definition[0], self)
            instance.propertyChanged.connect(self.propertyChanged)
            self._instances[instance.definition.key] = instance

        self._instances[key].setProperty(property_name, property_value, container)

        if not set_from_cache:
            self.setDirty(True)

    propertyChanged = Signal()

    ##  Remove all instances from this container.
    def clear(self):
        self._instantiateCachedValues()
        all_keys = self._instances.copy()
        for key in all_keys:
            self.removeInstance(key, postpone_emit=True)
        self.sendPostponedEmits()

    ##  Get all the keys of the instances of this container
    #   \returns list of keys
    def getAllKeys(self):
        if self._cached_values:
            # If we only want the keys and the actual values are still cached, just get the keys from the cache.
            return self._cached_values.keys()
        return [key for key in self._instances]

    ##  Create a new InstanceContainer with the same contents as this container
    #
    #   \param new_id \type{str} The new ID of the container
    #   \param new_name \type{str} The new name of the container. Defaults to None to indicate the name should not change.
    #
    #   \return A new InstanceContainer with the same contents as this container.
    def duplicate(self, new_id: str, new_name: str = None):
        self._instantiateCachedValues()
        new_container = self.__class__(new_id)
        if new_name:
            new_container.setName(new_name)

        new_container.setMetaData(copy.deepcopy(self._metadata))
        new_container.setDefinition(self._definition)

        for instance_id in self._instances:
            instance = self._instances[instance_id]
            for property_name in instance.definition.getPropertyNames():
                if not hasattr(instance, property_name):
                    continue

                new_container.setProperty(instance.definition.key, property_name, getattr(instance, property_name))

        new_container._dirty = True
        new_container._read_only = False
        return new_container

    ##  \copydoc ContainerInterface::serialize
    #
    #   Reimplemented from ContainerInterface
    def serialize(self) -> str:
        self._instantiateCachedValues()
        parser = configparser.ConfigParser(interpolation = None)

        if not self._definition:
            Logger.log("w", "Tried to serialize an instance container without definition, this is not supported")
            return ""

        parser["general"] = {}
        parser["general"]["version"] = str(self.Version)
        parser["general"]["name"] = str(self._name)
        parser["general"]["definition"] = str(self._definition.getId())

        parser["metadata"] = {}
        for key, value in self._metadata.items():
            parser["metadata"][key] = str(value)

        parser["values"] = {}
        for key, instance in sorted(self._instances.items()):
            try:
                parser["values"][key] = str(instance.value)
            except AttributeError:
                pass

        stream = io.StringIO()
        parser.write(stream)
        return stream.getvalue()

    def _readAndValidateSerialized(self, serialized: str) -> configparser.ConfigParser:
        parser = configparser.ConfigParser(interpolation=None)
        parser.read_string(serialized)

        has_general = "general" in parser
        has_version = "version" in parser["general"]
        has_definition = "definition" in parser["general"]

        if not has_general or not has_version or not has_definition:
            exception_string = "Missing the required"
            if not has_general:
                exception_string += " section 'general'"
            if not has_definition:
                exception_string += " property 'definition'"
            if not has_version:
                exception_string += " property 'version'"
            raise InvalidInstanceError(exception_string)
        return parser

    def getConfigurationTypeFromSerialized(self, serialized: str) -> Optional[str]:
        configuration_type = None
        try:
            parser = self._readAndValidateSerialized(serialized)
            configuration_type = parser['metadata'].get('type')
        except Exception as e:
            Logger.log("d", "Could not get configuration type: %s", e)
        return configuration_type

    def getVersionFromSerialized(self, serialized: str) -> Optional[int]:
        configuration_type = self.getConfigurationTypeFromSerialized(serialized)
        # get version
        version = None
        try:
            import UM.VersionUpgradeManager
            version = UM.VersionUpgradeManager.VersionUpgradeManager.getInstance().getFileVersion(configuration_type,
                                                                                                  serialized)
        except Exception as e:
            #Logger.log("d", "Could not get version from serialized: %s", e)
            pass
        return version

    ##  \copydoc ContainerInterface::deserialize
    #
    #   Reimplemented from ContainerInterface
    def deserialize(self, serialized: str) -> str:
        # update the serialized data first
        serialized = super().deserialize(serialized)
        parser = self._readAndValidateSerialized(serialized)

        if int(parser["general"]["version"]) != self.Version:
            raise IncorrectInstanceVersionError("Reported version {0} but expected version {1}".format(int(parser["general"]["version"]), self.Version))

        # Reset old data
        self._metadata = {}
        self._instances = {}

        self._name = parser["general"].get("name", self._id)

        definition_id = parser["general"]["definition"]

        definitions = _containerRegistry.findDefinitionContainers(id = definition_id)
        if not definitions:
            raise DefinitionNotFoundError("Could not find definition {0} required for instance {1}".format(definition_id, self._id))
        self._definition = definitions[0]

        if "metadata" in parser:
            self._metadata = dict(parser["metadata"])

        if "values" in parser:
            self._cached_values = dict(parser["values"])

        self._dirty = False

    ##  Instance containers are lazy loaded. This function ensures that it happened.
    def _instantiateCachedValues(self):
        if not self._cached_values:
            return

        for key, value in self._cached_values.items():
            self.setProperty(key, "value", value, self._definition, set_from_cache=True)

        self._cached_values = None

    ##  Find instances matching certain criteria.
    #
    #   \param kwargs \type{dict} A dictionary of keyword arguments with key-value pairs that should match properties of the instances.
    def findInstances(self, **kwargs) -> List[SettingInstance]:
        self._instantiateCachedValues()
        result = []
        for setting_key, instance in self._instances.items():
            for key, value in kwargs.items():
                if not hasattr(instance, key) or getattr(instance, key) != value:
                    break
            else:
                result.append(instance)

        return result

    ##  Get an instance by key
    #
    def getInstance(self, key: str) -> SettingInstance:
        self._instantiateCachedValues()
        if key in self._instances:
            return self._instances[key]

        return None

    ##  Add a new instance to this container.
    def addInstance(self, instance: SettingInstance) -> None:
        self._instantiateCachedValues()
        key = instance.definition.key
        if key in self._instances:
            return

        instance.propertyChanged.connect(self.propertyChanged)
        instance.propertyChanged.emit(key, "value")
        self._instances[key] = instance

    ##  Remove an instance from this container.
    #   /param postpone_emit postpone emit until calling sendPostponedEmits
    def removeInstance(self, key: str, postpone_emit: bool=False) -> None:
        self._instantiateCachedValues()
        if key not in self._instances:
            return

        instance = self._instances[key]
        del self._instances[key]
        if postpone_emit:
            # postpone, call sendPostponedEmits later. The order matters.
            self._postponed_emits.append((instance.propertyChanged, (key, "validationState")))
            self._postponed_emits.append((instance.propertyChanged, (key, "state")))
            self._postponed_emits.append((instance.propertyChanged, (key, "value")))
            for property_name in instance.definition.getPropertyNames():
                if instance.definition.dependsOnProperty(property_name) == "value":
                    self._postponed_emits.append((instance.propertyChanged, (key, property_name)))
        else:
            # Notify listeners of changed properties for all related properties
            instance.propertyChanged.emit(key, "value")
            instance.propertyChanged.emit(key, "state")  # State is no longer user state, so signal is needed.
            instance.propertyChanged.emit(key, "validationState") # If the value was invalid, it should now no longer be invalid.
            for property_name in instance.definition.getPropertyNames():
                if instance.definition.dependsOnProperty(property_name) == "value":
                    self.propertyChanged.emit(key, property_name)

        self._dirty = True

        instance.updateRelations(self)

    ##  Get the DefinitionContainer used for new instance creation.
    def getDefinition(self) -> DefinitionContainerInterface:
        return self._definition

    ##  Set the DefinitionContainer to use for new instance creation.
    #
    #   Since SettingInstance needs a SettingDefinition to work properly, we need some
    #   way of figuring out what SettingDefinition to use when creating a new SettingInstance.
    def setDefinition(self, definition: DefinitionContainerInterface):
        self._definition = definition

    def __lt__(self, other):
        own_weight = int(self.getMetaDataEntry("weight", 0))
        other_weight = int(other.getMetaDataEntry("weight", 0))

        if own_weight and other_weight:
            return own_weight < other_weight

        return self._name < other.name

    ##  Send postponed emits
    #   These emits are collected from the option postpone_emit.
    #   Note: the option can be implemented for all functions modifying the container.
    def sendPostponedEmits(self):
        while self._postponed_emits:
            signal, signal_arg = self._postponed_emits.pop(0)
            signal.emit(*signal_arg)
Ejemplo n.º 9
0
class MacroTabDialog(QDialog, _HalWidgetBase):
    def __init__(self, parent=None):
        super(MacroTabDialog, self).__init__(parent)
        self.setWindowTitle('Qtvcp Macro Menu')
        self._color = QColor(0, 0, 0, 150)
        self._state = False
        self._request_name = 'MACROTAB'
        self.setWindowModality(Qt.ApplicationModal)
        self.setWindowFlags(self.windowFlags() | Qt.Tool | Qt.Dialog
                            | Qt.WindowStaysOnTopHint
                            | Qt.WindowSystemMenuHint)
        self.setMinimumSize(00, 200)
        self.resize(600, 400)
        # patch class to call our button methods rather then the
        # original methods (Gotta do before instantiation)
        MacroTab.closeChecked = self._close
        MacroTab.runChecked = self._run
        MacroTab.setTitle = self._setTitle
        # ok now instantiate patched class
        self.tab = MacroTab()
        self.tab.setObjectName('macroTabInternal_')
        l = QVBoxLayout()
        self.setLayout(l)
        l.addWidget(self.tab)
        #we need the close button
        self.tab.closeButton.setVisible(True)

    def _hal_init(self):
        x = self.geometry().x()
        y = self.geometry().y()
        w = self.geometry().width()
        h = self.geometry().height()
        geo = '%s %s %s %s' % (x, y, w, h)
        self._default_geometry = [x, y, w, h]
        if self.PREFS_:
            self._geometry_string = self.PREFS_.getpref(
                'MacroTabDialog-geometry', geo, str, 'DIALOG_OPTIONS')
        else:
            self._geometry_string = 'default'
        # gotta call this since we instantiated this out of qtvcp's knowledge
        self.tab._hal_init()
        self.topParent = self.QTVCP_INSTANCE_
        STATUS.connect('dialog-request', self._external_request)

    def _external_request(self, w, message):
        if message['NAME'] == self._request_name:
            self.load_dialog()

    # This method is called instead of MacroTab's closeChecked method
    # we do this so we can use it's buttons to hide our dialog
    # rather then close the MacroTab widget
    def _close(self):
        self.close()

    # This method is called instead of MacroTab's runChecked() method
    # we do this so we can use it's buttons to hide our dialog
    # rather then close the MacroTab widget
    def _run(self):
        self.tab.runMacro()
        self.close()

    def _setTitle(self, string):
        self.setWindowTitle(string)

    def load_dialog(self):
        STATUS.emit('focus-overlay-changed', True, 'Lathe Macro Dialog',
                    self._color)
        self.tab.stack.setCurrentIndex(0)
        self.calculate_placement()
        self.show()
        self.exec_()
        STATUS.emit('focus-overlay-changed', False, None, None)
        record_geometry(self, 'MacroTabDialog-geometry')

    def calculate_placement(self):
        geometry_parsing(self, 'MacroTabDialog-geometry')

    # **********************
    # Designer properties
    # **********************

    @pyqtSlot(bool)
    def setState(self, value):
        self._state = value
        if value:
            self.show()
        else:
            self.hide()

    def getState(self):
        return self._state

    def resetState(self):
        self._state = False

    def getColor(self):
        return self._color

    def setColor(self, value):
        self._color = value

    def resetState(self):
        self._color = QColor(0, 0, 0, 150)

    state = pyqtProperty(bool, getState, setState, resetState)
    overlay_color = pyqtProperty(QColor, getColor, setColor)
Ejemplo n.º 10
0
class LcncDialog(QMessageBox, _HalWidgetBase):
    def __init__(self, parent=None):
        super(LcncDialog, self).__init__(parent)
        self.setTextFormat(Qt.RichText)
        self.setText('<b>Sample Text?</b>')
        self.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
        self.setIcon(QMessageBox.Critical)
        self.setDetailedText('')
        self.OK_TYPE = 1
        self.YN_TYPE = 0
        self._state = False
        self._color = QColor(0, 0, 0, 150)
        self.focus_text = ''
        self._request_name = 'MESSAGE'
        self.hide()

    def _hal_init(self):
        x = self.geometry().x()
        y = self.geometry().y()
        w = self.geometry().width()
        h = self.geometry().height()
        geo = '%s %s %s %s' % (x, y, w, h)
        self._default_geometry = [x, y, w, h]
        if self.PREFS_:
            self._geometry_string = self.PREFS_.getpref(
                'LcncDialog-geometry', geo, str, 'DIALOG_OPTIONS')
        else:
            self._geometry_string = 'default'
        self.topParent = self.QTVCP_INSTANCE_
        STATUS.connect('dialog-request', self._external_request)

    # this processes STATUS called dialog requests
    # We check the cmd to see if it was for us
    # then we check for a id string
    # if all good show the dialog
    # and then send back the dialog response via a general message
    def _external_request(self, w, message):
        if message.get('NAME') == self._request_name:
            t = message.get('TITLE')
            if t:
                self.title = t
            else:
                self.title = 'Entry'
            mess = message.get('MESSAGE') or None
            more = message.get('MORE') or None
            details = message.get('DETAILS') or None
            ok_type = message.get('TYPE')
            if ok_type == None: ok_type = True
            icon = message.get('ICON') or 'INFO'
            pin = message.get('PINNAME') or None
            ftext = message.get('FOCUS_TEXT') or None
            fcolor = message.get('FOCUS_COLOR') or None
            alert = message.get('PLAY_ALERT') or None
            rtrn = self.showdialog(mess, more, details, ok_type, icon, pin,
                                   ftext, fcolor, alert)
            message['RETURN'] = rtrn
            STATUS.emit('general', message)

    def showdialog(self,
                   message,
                   more_info=None,
                   details=None,
                   display_type=1,
                   icon=QMessageBox.Information,
                   pinname=None,
                   focus_text=None,
                   focus_color=None,
                   play_alert=None):

        self.setWindowModality(Qt.ApplicationModal)
        self.setWindowFlags(self.windowFlags() | Qt.Tool
                            | Qt.FramelessWindowHint | Qt.Dialog
                            | Qt.WindowStaysOnTopHint
                            | Qt.WindowSystemMenuHint)
        #Qt.X11BypassWindowManagerHint

        if focus_text is not None:
            self.focus_text = focus_text
        if focus_color is not None:
            color = focus_color
        else:
            color = self._color

        if icon == 'QUESTION': icon = QMessageBox.Question
        elif icon == 'INFO' or isinstance(icon, str):
            icon = QMessageBox.Information
        elif icon == 'WARNING':
            icon = QMessageBox.Warning
        elif icon == 'CRITICAL':
            icon = QMessageBox.Critical
        self.setIcon(icon)

        self.setText('<b>%s</b>' % message)
        if more_info is not None:
            self.setInformativeText(more_info)
        else:
            self.setInformativeText('')
        if details is not None:
            self.setDetailedText(details)
        else:
            self.setDetailedText('')
        if display_type == self.OK_TYPE:
            self.setStandardButtons(QMessageBox.Ok)
        else:
            self.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
        self.buttonClicked.connect(self.msgbtn)
        STATUS.emit('focus-overlay-changed', True, self.focus_text, color)
        if play_alert:
            STATUS.emit('play-sound', play_alert)
        retval = self.exec_()
        STATUS.emit('focus-overlay-changed', False, None, None)
        LOG.debug("Value of pressed button: {}".format(retval))
        if display_type == self.OK_TYPE:
            if retval == QMessageBox.No:
                return False
            else:
                return True
        else:
            return retval

    def showEvent(self, event):
        geom = self.frameGeometry()
        geom.moveCenter(QDesktopWidget().availableGeometry().center())
        self.setGeometry(geom)
        super(LcncDialog, self).showEvent(event)

    def msgbtn(self, i):
        LOG.debug("Button pressed is: {}".format(i.text()))
        return

    # **********************
    # Designer properties
    # **********************
    @pyqtSlot(bool)
    def setState(self, value):
        self._state = value
        if value:
            self.show()
        else:
            self.hide()

    def getState(self):
        return self._state

    def resetState(self):
        self._state = False

    def getColor(self):
        return self._color

    def setColor(self, value):
        self._color = value

    def resetState(self):
        self._color = QColor(0, 0, 0, 150)

    overlay_color = pyqtProperty(QColor, getColor, setColor)
    state = pyqtProperty(bool, getState, setState, resetState)
Ejemplo n.º 11
0
class CalculatorDialog(Calculator, _HalWidgetBase):
    def __init__(self, parent=None):
        super(CalculatorDialog, self).__init__(parent)
        self._color = QColor(0, 0, 0, 150)
        self.play_sound = False
        self._request_name = 'CALCULATOR'
        self.title = 'Calculator Entry'
        self.setWindowFlags(self.windowFlags() | Qt.Tool | Qt.Dialog
                            | Qt.WindowStaysOnTopHint
                            | Qt.WindowSystemMenuHint)

    def _hal_init(self):
        x = self.geometry().x()
        y = self.geometry().y()
        w = self.geometry().width()
        h = self.geometry().height()
        geo = '%s %s %s %s' % (x, y, w, h)
        self._default_geometry = [x, y, w, h]
        if self.PREFS_:
            self._geometry_string = self.PREFS_.getpref(
                'CalculatorDialog-geometry', geo, str, 'DIALOG_OPTIONS')
        else:
            self._geometry_string = 'default'
        if self.PREFS_:
            self.play_sound = self.PREFS_.getpref(
                'CalculatorDialog_play_sound', True, bool, 'DIALOG_OPTIONS')
            self.sound_type = self.PREFS_.getpref(
                'CalculatorDialog_sound_type', 'RING', str, 'DIALOG_OPTIONS')
        else:
            self.play_sound = False
        STATUS.connect('dialog-request', self._external_request)

    # this processes STATUS called dialog requests
    # We check the cmd to see if it was for us
    # then we check for a id string
    # if all good show the dialog
    # and then send back the dialog response via a general message
    def _external_request(self, w, message):
        if message.get('NAME') == self._request_name:
            t = message.get('TITLE')
            if t:
                self.title = t
            else:
                self.title = 'Entry'
            preload = message.get('PRELOAD')
            num = self.showdialog(preload)
            message['RETURN'] = num
            STATUS.emit('general', message)

    def showdialog(self, preload=None):
        STATUS.emit('focus-overlay-changed', True, 'Origin Setting',
                    self._color)
        self.setWindowTitle(self.title)
        if self.play_sound:
            STATUS.emit('play-sound', self.sound_type)
        self.calculate_placement()
        if preload is not None:
            self.display.setText(str(preload))
        retval = self.exec_()
        STATUS.emit('focus-overlay-changed', False, None, None)
        record_geometry(self, 'EntryDialog-geometry')
        LOG.debug("Value of pressed button: {}".format(retval))
        if retval:
            try:
                return float(self.display.text())
            except:
                pass
        return None

    def calculate_placement(self):
        geometry_parsing(self, 'EntryDialog-geometry')

    def getColor(self):
        return self._color

    def setColor(self, value):
        self._color = value

    def resetState(self):
        self._color = QColor(0, 0, 0, 150)

    overlay_color = pyqtProperty(QColor, getColor, setColor)
Ejemplo n.º 12
0
class MDIHistory(QWidget, _HalWidgetBase):
    def __init__(self, parent=None):
        super(MDIHistory, self).__init__(parent)
        self.setMinimumSize(QSize(200, 150))
        self.setWindowTitle("PyQt5 editor test example")

        lay = QVBoxLayout()
        lay.setContentsMargins(0, 0, 0, 0)
        self.setLayout(lay)

        self.list = QListView()
        self.list.setEditTriggers(QListView.NoEditTriggers)
        self.list.activated.connect(self.activated)
        self.list.setAlternatingRowColors(True)
        self.list.selectionChanged = self.selectionChanged
        self.model = QStandardItemModel(self.list)

        self.MDILine = MDILine()
        self.MDILine.soft_keyboard = False
        self.MDILine.line_up = self.line_up
        self.MDILine.line_down = self.line_down

        STATUS.connect('mdi-history-changed', self.reload)

        # add widgets
        lay.addWidget(self.list)
        lay.addWidget(self.MDILine)

        self.fp = os.path.expanduser(INFO.MDI_HISTORY_PATH)
        try:
            open(self.fp, 'r')
        except:
            open(self.fp, 'a+')
            LOG.debug('MDI History file created: {}'.format(self.fp))
        self.reload()
        self.select_row('last')

    def _hal_init(self):
        STATUS.connect('state-off', lambda w: self.setEnabled(False))
        STATUS.connect('state-estop', lambda w: self.setEnabled(False))
        STATUS.connect(
            'interp-idle', lambda w: self.setEnabled(STATUS.machine_is_on(
            ) and (STATUS.is_all_homed() or INFO.NO_HOME_REQUIRED)))
        STATUS.connect('interp-run',
                       lambda w: self.setEnabled(not STATUS.is_auto_mode()))
        STATUS.connect('all-homed',
                       lambda w: self.setEnabled(STATUS.machine_is_on()))

    def reload(self, w=None):
        self.model.clear()
        try:
            with open(self.fp, 'r') as inputfile:
                for line in inputfile:
                    line = line.rstrip('\n')
                    item = QStandardItem(line)
                    self.model.appendRow(item)
            self.list.setModel(self.model)
            self.list.scrollToBottom()
            if self.MDILine.hasFocus():
                self.select_row('last')
        except:
            LOG.debug('File path is not valid: {}'.format(fp))

    def selectionChanged(self, old, new):
        cmd = self.getSelected()
        self.MDILine.setText(cmd)
        selectionModel = self.list.selectionModel()
        if selectionModel.hasSelection():
            self.row = selectionModel.currentIndex().row()

    def getSelected(self):
        selected_indexes = self.list.selectedIndexes()
        selected_rows = [item.row() for item in selected_indexes]
        # iterates each selected row in descending order
        for selected_row in sorted(selected_rows, reverse=True):
            text = self.model.item(selected_row).text()
            return text

    def activated(self):
        cmd = self.getSelected()
        self.MDILine.setText(cmd)
        self.MDILine.submit()
        self.select_row('down')

    def run_command(self):
        self.MDILine.submit()
        self.select_row('last')

    def select_row(self, style):
        style = style.lower()
        selectionModel = self.list.selectionModel()
        parent = QModelIndex()
        self.rows = self.model.rowCount(parent) - 1
        if style == 'last':
            self.row = self.rows
        elif style == 'up':
            if self.row > 0:
                self.row -= 1
            else:
                self.row = 0
        elif style == 'down':
            if self.row < self.rows:
                self.row += 1
            else:
                self.row = self.rows
        else:
            return
        top = self.model.index(self.row, 0, parent)
        bottom = self.model.index(self.row, 0, parent)
        selectionModel.setCurrentIndex(
            top, QItemSelectionModel.Select | QItemSelectionModel.Rows)
        selection = QItemSelection(top, top)
        selectionModel.clearSelection()
        selectionModel.select(selection, QItemSelectionModel.Select)

    def line_up(self):
        self.select_row('up')

    def line_down(self):
        self.select_row('down')

    #########################################################################
    # This is how designer can interact with our widget properties.
    # designer will show the pyqtProperty properties in the editor
    # it will use the get set and reset calls to do those actions
    #########################################################################

    def set_soft_keyboard(self, data):
        self.MDILine.soft_keyboard = data

    def get_soft_keyboard(self):
        return self.MDILine.soft_keyboard

    def reset_soft_keyboard(self):
        self.MDILine.soft_keyboard = False

    # designer will show these properties in this order:
    soft_keyboard_option = pyqtProperty(bool, get_soft_keyboard,
                                        set_soft_keyboard, reset_soft_keyboard)
Ejemplo n.º 13
0
class GcodeEditor(QWidget, _HalWidgetBase):
    percentDone = pyqtSignal(int)

    def __init__(self, parent=None):
        super(GcodeEditor, self).__init__(parent)
        self.load_dialog_code = 'LOAD'
        self.save_dialog_code = 'SAVE'
        STATUS.connect('general', self.returnFromDialog)

        self.isCaseSensitive = 0

        self.setMinimumSize(QSize(300, 200))
        self.setWindowTitle("PyQt5 editor test example")

        lay = QVBoxLayout()
        lay.setContentsMargins(0, 0, 0, 0)
        self.setLayout(lay)

        # make editor
        self.editor = GcodeDisplay(self)

        # class patch editor's function to ours
        # so we get the lines percent update
        self.editor.emit_percent = self.emit_percent

        self.editor.setReadOnly(True)
        self.editor.setModified(False)

        ################################
        # add menubar actions
        ################################

        # Create new action
        newAction = QAction(QIcon.fromTheme('document-new'), 'New', self)
        newAction.setShortcut('Ctrl+N')
        newAction.setStatusTip('New document')
        newAction.triggered.connect(self.newCall)

        # Create open action
        openAction = QAction(QIcon.fromTheme('document-open'), '&Open', self)
        openAction.setShortcut('Ctrl+O')
        openAction.setStatusTip('Open document')
        openAction.triggered.connect(self.openCall)

        # Create save action
        saveAction = QAction(QIcon.fromTheme('document-save'), '&save', self)
        saveAction.setShortcut('Ctrl+S')
        saveAction.setStatusTip('save document')
        saveAction.triggered.connect(self.saveCall)

        # Create exit action
        exitAction = QAction(QIcon.fromTheme('application-exit'), '&Exit',
                             self)
        exitAction.setShortcut('Ctrl+Q')
        exitAction.setStatusTip('Exit application')
        exitAction.triggered.connect(self.exitCall)

        # Create gcode lexer action
        gCodeLexerAction = QAction(QIcon.fromTheme('lexer.png'),
                                   '&Gcode\n lexer', self)
        gCodeLexerAction.setCheckable(1)
        gCodeLexerAction.setShortcut('Ctrl+G')
        gCodeLexerAction.setStatusTip('Set Gcode highlighting')
        gCodeLexerAction.triggered.connect(self.gcodeLexerCall)

        # Create gcode lexer action
        pythonLexerAction = QAction(QIcon.fromTheme('lexer.png'),
                                    '&python\n lexer', self)
        pythonLexerAction.setShortcut('Ctrl+P')
        pythonLexerAction.setStatusTip('Set Python highlighting')
        pythonLexerAction.triggered.connect(self.pythonLexerCall)

        # Create toolbar and add action
        toolBar = QToolBar('File')
        toolBar.addAction(newAction)
        toolBar.addAction(openAction)
        toolBar.addAction(saveAction)
        toolBar.addAction(exitAction)

        toolBar.addSeparator()

        # add lexer actions
        toolBar.addAction(gCodeLexerAction)
        toolBar.addAction(pythonLexerAction)

        toolBar.addSeparator()
        toolBar.addWidget(
            QLabel(
                '<html><head/><body><p><span style=" font-size:20pt; font-weight:600;">Edit Mode</span></p></body></html>'
            ))

        # create a frame for buttons
        box = QHBoxLayout()
        box.addWidget(toolBar)

        self.topMenu = QFrame()
        self.topMenu.setLayout(box)

        # add widgets
        lay.addWidget(self.topMenu)
        lay.addWidget(self.editor)
        lay.addWidget(self.createGroup())

        self.readOnlyMode()

    def createGroup(self):
        self.bottomMenu = QFrame()

        self.searchText = QLineEdit(self)
        self.replaceText = QLineEdit(self)

        toolBar = QToolBar()
        # Create new action
        undoAction = QAction(QIcon.fromTheme('edit-undo'), 'Undo', self)
        undoAction.setStatusTip('Undo')
        undoAction.triggered.connect(self.undoCall)
        toolBar.addAction(undoAction)

        # create redo action
        redoAction = QAction(QIcon.fromTheme('edit-redo'), 'Redo', self)
        redoAction.setStatusTip('Undo')
        redoAction.triggered.connect(self.redoCall)
        toolBar.addAction(redoAction)

        toolBar.addSeparator()

        # create replace action
        replaceAction = QAction(QIcon.fromTheme('edit-find-replace'),
                                'Replace', self)
        replaceAction.triggered.connect(self.replaceCall)
        toolBar.addAction(replaceAction)

        # create find action
        findAction = QAction(QIcon.fromTheme('edit-find'), 'Find', self)
        findAction.triggered.connect(self.findCall)
        toolBar.addAction(findAction)

        # create next action
        nextAction = QAction(QIcon.fromTheme('go-previous'), 'Find Previous',
                             self)
        nextAction.triggered.connect(self.nextCall)
        toolBar.addAction(nextAction)

        toolBar.addSeparator()

        # create case action
        caseAction = QAction(QIcon.fromTheme('edit-case'), 'Aa', self)
        caseAction.setCheckable(1)
        caseAction.triggered.connect(self.caseCall)
        toolBar.addAction(caseAction)

        box = QHBoxLayout()
        box.addWidget(toolBar)
        box.addWidget(self.searchText)
        box.addWidget(self.replaceText)
        box.addStretch(1)
        self.bottomMenu.setLayout(box)

        return self.bottomMenu

    # callback functions built for easy class patching ##########
    # want to refrain from renaming these functions as it will break
    # any class patch user's use
    # we split them like this so a user can intercept the callback
    # but still call the original functionality

    def caseCall(self):
        self.case()

    def case(self):
        self.isCaseSensitive -= 1
        self.isCaseSensitive *= -1
        print self.isCaseSensitive

    def exitCall(self):
        self.exit()

    def exit(self):
        if self.editor.isModified():
            result = self.killCheck()
            if result:
                self.readOnlyMode()

    def findCall(self):
        self.find()

    def find(self):
        self.editor.search(str(self.searchText.text()),
                           re=False,
                           case=self.isCaseSensitive,
                           word=False,
                           wrap=False,
                           fwd=True)

    def gcodeLexerCall(self):
        self.gcodeLexer()

    def gcodeLexer(self):
        self.editor.set_gcode_lexer()

    def nextCall(self):
        self.next()

    def next(self):
        self.editor.search(str(self.searchText.text()), False)
        self.editor.search_Next()

    def newCall(self):
        self.new()

    def new(self):
        if self.editor.isModified():
            result = self.killCheck()
            if result:
                self.editor.new_text()

    def openCall(self):
        self.open()

    def open(self):
        self.getFileName()

    def openReturn(self, f):
        ACTION.OPEN_PROGRAM(f)
        self.editor.setModified(False)

    def pythonLexerCall(self):
        self.pythonLexer()

    def pythonLexer(self):
        self.editor.set_python_lexer()

    def redoCall(self):
        self.redo()

    def redo(self):
        self.editor.redo()

    def replaceCall(self):
        self.replace()

    def replace(self):
        self.editor.replace_text(str(self.replaceText.text()))

    def saveCall(self):
        self.save()

    def save(self):
        self.getSaveFileName()

    def saveReturn(self, fname):
        ACTION.SAVE_PROGRAM(self.editor.text(), fname)
        self.editor.setModified(False)
        ACTION.OPEN_PROGRAM(fname)

    def undoCall(self):
        self.undo()

    def undo(self):
        self.editor.undo()

    # helper functions ############################################

    def _hal_init(self):
        # name the top and bottom frames so it's easier to style
        self.bottomMenu.setObjectName('%sBottomButtonFrame' %
                                      self.objectName())
        self.topMenu.setObjectName('%sTopButtonFrame' % self.objectName())

    def editMode(self):
        self.topMenu.show()
        self.bottomMenu.show()
        self.editor.setReadOnly(False)

    def readOnlyMode(self):
        self.topMenu.hide()
        self.bottomMenu.hide()
        self.editor.setReadOnly(True)

    def getFileName(self):
        mess = {
            'NAME': self.load_dialog_code,
            'ID': '%s__' % self.objectName(),
            'TITLE': 'Load Editor'
        }
        STATUS.emit('dialog-request', mess)

    def getSaveFileName(self):
        mess = {
            'NAME': self.save_dialog_code,
            'ID': '%s__' % self.objectName(),
            'TITLE': 'Save Editor'
        }
        STATUS.emit('dialog-request', mess)

    # process the STATUS return message
    def returnFromDialog(self, w, message):
        if message.get('NAME') == self.load_dialog_code:
            path = message.get('RETURN')
            code = bool(message.get('ID') == '%s__' % self.objectName())
            if path and code:
                self.openReturn(path)
        elif message.get('NAME') == self.save_dialog_code:
            path = message.get('RETURN')
            code = bool(message.get('ID') == '%s__' % self.objectName())
            if path and code:
                self.saveReturn(path)

    def killCheck(self):
        choice = QMessageBox.question(
            self, 'Warning!!',
            "This file has changed since loading...Still want to proceed?",
            QMessageBox.Yes | QMessageBox.No)
        if choice == QMessageBox.Yes:
            return True
        else:
            return False

    def emit_percent(self, percent):
        self.percentDone.emit(percent)

    def select_lineup(self):
        self.editor.select_lineup(None)

    def select_linedown(self):
        self.editor.select_linedown(None)

    def select_line(self, line):
        self.editor.highlight_line(None, line)

    def jump_line(self, jump):
        self.editor.jump_line(jump)

    def get_line(self):
        return self.editor.getCursorPosition()[0] + 1

    # designer recognized getter/setters
    # auto_show_mdi status
    # These adjust the self.editor instance
    def set_auto_show_mdi(self, data):
        self.editor.auto_show_mdi = data

    def get_auto_show_mdi(self):
        return self.editor.auto_show_mdi

    def reset_auto_show_mdi(self):
        self.editor.auto_show_mdi = True

    auto_show_mdi_status = pyqtProperty(bool, get_auto_show_mdi,
                                        set_auto_show_mdi, reset_auto_show_mdi)

    # designer recognized getter/setters
    # auto_show_manual status
    def set_auto_show_manual(self, data):
        self.editor.auto_show_manual = data

    def get_auto_show_manual(self):
        return self.editor.auto_show_manual

    def reset_auto_show_manual(self):
        self.editor.auto_show_manual = True

    auto_show_manual_status = pyqtProperty(bool, get_auto_show_manual,
                                           set_auto_show_manual,
                                           reset_auto_show_manual)
Ejemplo n.º 14
0
class GcodeDisplay(EditorBase, _HalWidgetBase):
    ARROW_MARKER_NUM = 8

    def __init__(self, parent=None):
        super(GcodeDisplay, self).__init__(parent)
        # linuxcnc defaults
        self.idle_line_reset = False
        self._last_filename = None
        self.auto_show_mdi = True
        self.auto_show_manual = False
        self.auto_show_preference = True
        self.last_line = None

    def _hal_init(self):
        self.cursorPositionChanged.connect(self.line_changed)
        if self.auto_show_mdi:
            STATUS.connect('mode-mdi', self.load_mdi)
            STATUS.connect('mdi-history-changed', self.load_mdi)
            STATUS.connect('mode-auto', self.reload_last)
            STATUS.connect('move-text-lineup', self.select_lineup)
            STATUS.connect('move-text-linedown', self.select_linedown)
        if self.auto_show_manual:
            STATUS.connect('mode-manual', self.load_manual)
            STATUS.connect('machine-log-changed', self.load_manual)
        if self.auto_show_preference:
            STATUS.connect('show-preference', self.load_preference)
        STATUS.connect('file-loaded', self.load_program)
        STATUS.connect('line-changed', self.highlight_line)
        STATUS.connect('graphics-line-selected', self.highlight_line)

        if self.idle_line_reset:
            STATUS.connect('interp_idle',
                           lambda w: self.set_line_number(None, 0))

    def load_program(self, w, filename=None):
        if filename is None:
            filename = self._last_filename
        else:
            self._last_filename = filename
        self.load_text(filename)
        #self.zoomTo(6)
        self.setCursorPosition(0, 0)
        self.setModified(False)

    # when switching from MDI to AUTO we need to reload the
    # last (linuxcnc loaded) program.
    def reload_last(self, w):
        self.load_text(STATUS.old['file'])
        self.setCursorPosition(0, 0)

    # With the auto_show__mdi option, MDI history is shown
    def load_mdi(self, w):
        self.load_text(INFO.MDI_HISTORY_PATH)
        self._last_filename = INFO.MDI_HISTORY_PATH
        #print 'font point size', self.font().pointSize()
        #self.zoomTo(10)
        #print 'font point size', self.font().pointSize()
        self.setCursorPosition(self.lines(), 0)

    # With the auto_show__mdi option, MDI history is shown
    def load_manual(self, w):
        if STATUS.is_man_mode():
            self.load_text(INFO.MACHINE_LOG_HISTORY_PATH)
            self.setCursorPosition(self.lines(), 0)

    def load_preference(self, w):
        self.load_text(self.PATHS_.PREFS_FILENAME)
        self.setCursorPosition(self.lines(), 0)

    def load_text(self, filename):
        if filename:
            try:
                fp = os.path.expanduser(filename)
                self.setText(open(fp).read())
                self.last_line = None
                self.ensureCursorVisible()
                self.SendScintilla(QsciScintilla.SCI_VERTICALCENTRECARET)
                return
            except:
                LOG.error('File path is not valid: {}'.format(filename))
        self.setText('')

    def highlight_line(self, w, line):
        if STATUS.is_auto_running():
            if not STATUS.old['file'] == self._last_filename:
                LOG.debug('should reload the display')
                self.load_text(STATUS.old['file'])
                self._last_filename = STATUS.old['file']
            self.emit_percent(line * 100 / self.lines())
        self.markerAdd(line, self.ARROW_MARKER_NUM)
        if self.last_line:
            self.markerDelete(self.last_line, self.ARROW_MARKER_NUM)
        self.setCursorPosition(line, 0)
        self.ensureCursorVisible()
        self.SendScintilla(QsciScintilla.SCI_VERTICALCENTRECARET)
        self.last_line = line

    def emit_percent(self, percent):
        pass

    def set_line_number(self, line):
        STATUS.emit('gcode-line-selected', line)

    def line_changed(self, line, index):
        #LOG.debug('Line changed: {}'.format(line))
        if STATUS.is_auto_running() is False:
            self.markerDeleteAll(-1)
            if STATUS.is_mdi_mode():
                line_text = str(self.text(line)).strip()
                STATUS.emit('mdi-line-selected', line_text,
                            self._last_filename)
            else:
                self.set_line_number(line)

    def select_lineup(self, w):
        line, col = self.getCursorPosition()
        LOG.debug(line)
        self.setCursorPosition(line - 1, 0)
        self.highlight_line(None, line - 1)

    def select_linedown(self, w):
        line, col = self.getCursorPosition()
        LOG.debug(line)
        self.setCursorPosition(line + 1, 0)
        self.highlight_line(None, line + 1)

    def jump_line(self, jump):
        line, col = self.getCursorPosition()
        line = line + jump
        LOG.debug(line)
        if line < 0:
            line = 0
        elif line > self.lines():
            line = self.lines()
        self.setCursorPosition(line, 0)
        self.highlight_line(None, line)

    # designer recognized getter/setters
    # auto_show_mdi status
    def set_auto_show_mdi(self, data):
        self.auto_show_mdi = data

    def get_auto_show_mdi(self):
        return self.auto_show_mdi

    def reset_auto_show_mdi(self):
        self.auto_show_mdi = True

    auto_show_mdi_status = pyqtProperty(bool, get_auto_show_mdi,
                                        set_auto_show_mdi, reset_auto_show_mdi)

    # designer recognized getter/setters
    # auto_show_manual status
    def set_auto_show_manual(self, data):
        self.auto_show_manual = data

    def get_auto_show_manual(self):
        return self.auto_show_manual

    def reset_auto_show_manual(self):
        self.auto_show_manual = True

    auto_show_manual_status = pyqtProperty(bool, get_auto_show_manual,
                                           set_auto_show_manual,
                                           reset_auto_show_manual)
Ejemplo n.º 15
0
class BatteryWidget(QWidget):
    chargeChanged = pyqtSignal(float)

    def __init__(self, parent=None):

        QWidget.__init__(self, parent)

        self._charge = 0

        self.white = QColor(255, 255, 255)
        self.black = QColor(0, 0, 0)
        self.green = QColor(51, 255, 51)
        self.yellow = QColor(255, 255, 51)
        self.red = QColor(255, 0, 0)

        self.setMinimumWidth(110)
        self.setMinimumHeight(75)
        self.setMaximumWidth(110)
        self.setMaximumHeight(75)
        self.data = None

        # Defining sizes of battery
        self.ex_top_left_y = 25  #top left corner, Y position, exterior border (black)
        self.in_top_left_y = self.ex_top_left_y + 5  #top left corner, Y position, interior border (white, filler)
        self.ex_top_left_x = 6  #top left corner, X position, exterior border (black)
        self.in_top_left_x = self.ex_top_left_x + 5  #top left corner, X position, interior border (white, filler)
        self.exterior_width = self.width(
        ) - 15  #Width of the drawing, exterior border (black)
        self.interior_width = self.exterior_width - 10  #Width of the drawing, interior border (white)
        self.exterior_height = self.height(
        ) - 40  #Height of the drawing, exterior border (black)
        self.interior_height = self.exterior_height - 10  #Height of the drawing, interior border (white)
        self.total_width = self.width(
        ) - 20 - self.ex_top_left_x  #Width of the battery bar drawing (green)

    def paintEvent(self, ev):
        """ paint the battery widget"""
        painter = QPainter()
        painter.begin(self)
        painter.setRenderHint(QPainter.Antialiasing)
        # draw positive terminal
        painter.setBrush(self.black)
        rect_p_terminal = QRect(self.width() - 7,
                                self.height() / 2,
                                self.width() / 30,
                                self.height() / 8)
        painter.drawRect(rect_p_terminal)
        # draw battery
        painter.setBrush(self.black)
        rect = QRect(self.ex_top_left_x, self.ex_top_left_y,
                     self.exterior_width, self.exterior_height)
        painter.drawRoundedRect(rect, 10.0, 10.0)
        # draw transparent background
        painter.setBrush(self.white)
        rect_transp = QRect(self.in_top_left_x, self.in_top_left_y,
                            self.interior_width, self.interior_height)
        painter.drawRoundedRect(rect_transp.intersected(rect), 5.0, 5.0)
        # draw charge
        charge = self._charge
        # charge on pixels 0 to 100
        if charge > 100:
            charge = 100
        if charge < 0:
            charge = 0
        rect_charge = QRect(self.in_top_left_x, self.in_top_left_y,
                            self.total_width * charge / 100,
                            self.interior_height)

        if charge > 45:
            color = self.green
        elif charge < 20:
            color = self.red
        else:
            color = self.yellow
        painter.setBrush(color)
        painter.drawRoundedRect(rect_charge, 5.0, 5.0)
        painter.setPen(QPen(QBrush(Qt.black), 1, Qt.SolidLine))
        painter.drawText(self.width() / 2 - (self.width() / 8),
                         self.height() / 2 + 10,
                         str(int(charge)) + '%')
        painter.end()

    def charge(self):
        """ return charge value"""
        return self._charge

    def set_data(self, data):
        """ set 'data'"""
        self.data = data
        if data:
            self.setCharge(data)

    @pyqtSlot(float)
    def setCharge(self, charge):

        if charge != self._charge:
            self._charge = charge
            self.chargeChanged.emit(charge)
            self.update()

    charge = pyqtProperty(float, charge, setCharge)
Ejemplo n.º 16
0
class ContainerStack(QObject, ContainerInterface, PluginObject):
    """A stack of setting containers to handle setting value retrieval."""

    Version = 4  # type: int

    def __init__(self, stack_id: str) -> None:
        """Constructor

        :param stack_id: A unique, machine readable/writable ID.
        """

        super().__init__()
        QQmlEngine.setObjectOwnership(self, QQmlEngine.CppOwnership)

        self._metadata = {
            "id": stack_id,
            "name": stack_id,
            "version": self.Version,
            "container_type": ContainerStack
        }  #type: Dict[str, Any]
        self._containers = []  # type: List[ContainerInterface]
        self._next_stack = None  # type: Optional[ContainerStack]
        self._read_only = False  # type: bool
        self._dirty = False  # type: bool
        self._path = ""  # type: str
        self._postponed_emits = [
        ]  # type: List[Tuple[Signal, ContainerInterface]] # gets filled with 2-tuples: signal, signal_argument(s)

        self._property_changes = {}  # type: Dict[str, Set[str]]
        self._emit_property_changed_queued = False  # type: bool

    def __getnewargs__(self) -> Tuple[str]:
        """For pickle support"""

        return (self.getId(), )

    def __getstate__(self) -> Dict[str, Any]:
        """For pickle support"""

        return self.__dict__

    def __setstate__(self, state: Dict[str, Any]) -> None:
        """For pickle support"""

        self.__dict__.update(state)

    def getId(self) -> str:
        """:copydoc ContainerInterface::getId

        Reimplemented from ContainerInterface
        """

        return cast(str, self._metadata["id"])

    id = pyqtProperty(str, fget=getId, constant=True)

    def getName(self) -> str:
        """:copydoc ContainerInterface::getName

        Reimplemented from ContainerInterface
        """

        return str(self._metadata["name"])

    def setName(self, name: str) -> None:
        """Set the name of this stack.

        :param name: The new name of the stack.
        """

        if name != self.getName():
            self._metadata["name"] = name
            self.nameChanged.emit()
            self.metaDataChanged.emit(self)

    nameChanged = pyqtSignal()
    """Emitted whenever the name of this stack changes."""

    name = pyqtProperty(str, fget=getName, fset=setName, notify=nameChanged)

    def isReadOnly(self) -> bool:
        """:copydoc ContainerInterface::isReadOnly

        Reimplemented from ContainerInterface
        """

        return self._read_only

    def setReadOnly(self, read_only: bool) -> None:
        if read_only != self._read_only:
            self._read_only = read_only
            self.readOnlyChanged.emit()

    readOnlyChanged = pyqtSignal()
    readOnly = pyqtProperty(bool,
                            fget=isReadOnly,
                            fset=setReadOnly,
                            notify=readOnlyChanged)

    def getMetaData(self) -> Dict[str, Any]:
        """:copydoc ContainerInterface::getMetaData

        Reimplemented from ContainerInterface
        """

        return self._metadata

    def setMetaData(self, meta_data: Dict[str, Any]) -> None:
        """Set the complete set of metadata"""

        if meta_data == self.getMetaData():
            return  #Unnecessary.

        #We'll fill a temporary dictionary with all the required metadata and overwrite it with the new metadata.
        #This way it is ensured that at least the required metadata is still there.
        self._metadata = {
            "id": self.getId(),
            "name": self.getName(),
            "version": self.getMetaData().get("version", 0),
            "container_type": ContainerStack
        }

        self._metadata.update(meta_data)
        self.metaDataChanged.emit(self)

    metaDataChanged = pyqtSignal(QObject)
    metaData = pyqtProperty("QVariantMap",
                            fget=getMetaData,
                            fset=setMetaData,
                            notify=metaDataChanged)

    def getMetaDataEntry(self, entry: str, default: Any = None) -> Any:
        """:copydoc ContainerInterface::getMetaDataEntry

        Reimplemented from ContainerInterface
        """

        value = self._metadata.get(entry, None)

        if value is None:
            for container in self._containers:
                value = container.getMetaDataEntry(entry, None)
                if value is not None:
                    break

        if value is None:
            return default
        else:
            return value

    def setMetaDataEntry(self, key: str, value: Any) -> None:
        if key not in self._metadata or self._metadata[key] != value:
            self._metadata[key] = value
            self._dirty = True
            self.metaDataChanged.emit(self)

    def removeMetaDataEntry(self, key: str) -> None:
        if key in self._metadata:
            del self._metadata[key]
            self.metaDataChanged.emit(self)

    def isDirty(self) -> bool:
        return self._dirty

    def setDirty(self, dirty: bool) -> None:
        self._dirty = dirty

    containersChanged = Signal()

    def getProperty(
            self,
            key: str,
            property_name: str,
            context: Optional[PropertyEvaluationContext] = None) -> Any:
        """:copydoc ContainerInterface::getProperty

        Reimplemented from ContainerInterface.

        getProperty will start at the top of the stack and try to get the property
        specified. If that container returns no value, the next container on the
        stack will be tried and so on until the bottom of the stack is reached.
        If a next stack is defined for this stack it will then try to get the
        value from that stack. If no next stack is defined, None will be returned.

        Note that if the property value is a function, this method will return the
        result of evaluating that property with the current stack. If you need the
        actual function, use getRawProperty()
        """

        value = self.getRawProperty(key, property_name, context=context)
        if isinstance(value, SettingFunction):
            if context is not None:
                context.pushContainer(self)
            value = value(self, context)
            if context is not None:
                context.popContainer()

        return value

    def getRawProperty(
            self,
            key: str,
            property_name: str,
            *,
            context: Optional[PropertyEvaluationContext] = None,
            use_next: bool = True,
            skip_until_container: Optional[ContainerInterface] = None) -> Any:
        """Retrieve a property of a setting by key and property name.

        This method does the same as getProperty() except it does not perform any
        special handling of the result, instead the raw stored value is returned.

        :param key: The key to get the property value of.
        :param property_name: The name of the property to get the value of.
        :param use_next: True if the value should be retrieved from the next
        stack if not found in this stack. False if not.
        :param skip_until_container: A container ID to skip to. If set, it will
        be as if all containers above the specified container are empty. If the
        container is not in the stack, it'll try to find it in the next stack.

        :return: The raw property value of the property, or None if not found. Note that
        the value might be a SettingFunction instance.

        """

        containers = self._containers
        if context is not None:
            # if context is provided, check if there is any container that needs to be skipped.
            start_index = context.context.get("evaluate_from_container_index",
                                              0)
            if start_index >= len(self._containers):
                return None
            containers = self._containers[start_index:]
        if property_name not in ["value", "state", "validationState"]:
            # Value, state & validationState can be changed by instanceContainer, the rest cant. Ask the definition
            # right away
            value = containers[-1].getProperty(key, property_name, context)
            if value is not None:
                return value
        else:
            for container in containers:
                if skip_until_container and container.getId(
                ) != skip_until_container:
                    continue  #Skip.
                skip_until_container = None  #When we find the container, stop skipping.

                value = container.getProperty(key, property_name, context)
                if value is not None:
                    return value

        if self._next_stack and use_next:
            return self._next_stack.getRawProperty(
                key,
                property_name,
                context=context,
                use_next=use_next,
                skip_until_container=skip_until_container)
        else:
            return None

    def hasProperty(self, key: str, property_name: str) -> bool:
        """:copydoc ContainerInterface::hasProperty

        Reimplemented from ContainerInterface.

        hasProperty will check if any of the containers in the stack has the
        specified property. If it does, it stops and returns True. If it gets to
        the end of the stack, it returns False.
        """

        for container in self._containers:
            if container.hasProperty(key, property_name):
                return True

        if self._next_stack:
            return self._next_stack.hasProperty(key, property_name)
        return False

    # NOTE: we make propertyChanged and propertiesChanged as queued signals because otherwise, the emits in
    # _emitCollectedPropertyChanges() will be direct calls which modify the dict we are iterating over, and then
    # everything crashes.
    propertyChanged = Signal(Signal.Queued)
    propertiesChanged = Signal(Signal.Queued)

    def serialize(self,
                  ignored_metadata_keys: Optional[Set[str]] = None) -> str:
        """:copydoc ContainerInterface::serialize

        Reimplemented from ContainerInterface

        TODO: Expand documentation here, include the fact that this should _not_ include all containers
        """

        parser = configparser.ConfigParser(interpolation=None,
                                           empty_lines_in_values=False)

        parser["general"] = {}
        parser["general"]["version"] = str(self._metadata["version"])
        parser["general"]["name"] = str(self.getName())
        parser["general"]["id"] = str(self.getId())

        if ignored_metadata_keys is None:
            ignored_metadata_keys = set()
        ignored_metadata_keys |= {"id", "name", "version", "container_type"}
        parser["metadata"] = {}
        for key, value in self._metadata.items():
            # only serialize the data that's not in the ignore list
            if key not in ignored_metadata_keys:
                parser["metadata"][key] = str(value)

        parser.add_section("containers")
        for index in range(len(self._containers)):
            parser["containers"][str(index)] = str(
                self._containers[index].getId())

        stream = io.StringIO()
        parser.write(stream)
        return stream.getvalue()

    @classmethod
    def _readAndValidateSerialized(
            cls, serialized: str) -> configparser.ConfigParser:
        """Deserializes the given data and checks if the required fields are present.

        The profile upgrading code depends on information such as "configuration_type" and "version", which come from
        the serialized data. Due to legacy problem, those data may not be available if it comes from an ancient Cura.
        """

        parser = configparser.ConfigParser(interpolation=None,
                                           empty_lines_in_values=False)
        parser.read_string(serialized)

        if "general" not in parser or any(pn not in parser["general"]
                                          for pn in ("version", "name", "id")):
            raise InvalidContainerStackError(
                "Missing required section 'general' or 'version' property")

        return parser

    @classmethod
    def getConfigurationTypeFromSerialized(cls,
                                           serialized: str) -> Optional[str]:
        configuration_type = None
        try:
            parser = cls._readAndValidateSerialized(serialized)
            configuration_type = parser["metadata"]["type"]
        except InvalidContainerStackError as icse:
            raise icse
        except Exception as e:
            Logger.log("e", "Could not get configuration type: %s", e)
        return configuration_type

    @classmethod
    def getVersionFromSerialized(cls, serialized: str) -> Optional[int]:
        configuration_type = cls.getConfigurationTypeFromSerialized(serialized)
        if not configuration_type:
            Logger.log("d", "Could not get type from serialized.")
            return None

        # Get version
        version = None
        try:
            from UM.VersionUpgradeManager import VersionUpgradeManager
            version = VersionUpgradeManager.getInstance().getFileVersion(
                configuration_type, serialized)
        except Exception as e:
            Logger.log("d", "Could not get version from serialized: %s", e)
        return version

    def deserialize(self,
                    serialized: str,
                    file_name: Optional[str] = None) -> str:
        """:copydoc ContainerInterface::deserialize

        Reimplemented from ContainerInterface

        TODO: Expand documentation here, include the fact that this should _not_ include all containers
        """

        # Update the serialized data first
        serialized = super().deserialize(serialized, file_name)
        parser = self._readAndValidateSerialized(serialized)

        if parser.getint("general", "version") != self.Version:
            raise IncorrectVersionError()

        # Clear all data before starting.
        for container in self._containers:
            container.propertyChanged.disconnect(self._collectPropertyChanges)

        self._containers = []
        self._metadata = {}

        if "metadata" in parser:
            self._metadata = dict(parser["metadata"])
        self._metadata["id"] = parser["general"]["id"]
        self._metadata["name"] = parser["general"].get("name", self.getId())
        self._metadata[
            "version"] = self.Version  # Guaranteed to be equal to what's in the container. See above.
        self._metadata["container_type"] = ContainerStack

        if "containers" in parser:
            for index, container_id in parser.items("containers"):
                containers = _containerRegistry.findContainers(id=container_id)
                if containers:
                    containers[0].propertyChanged.connect(
                        self._collectPropertyChanges)
                    self._containers.append(containers[0])
                else:
                    self._containers.append(
                        _containerRegistry.getEmptyInstanceContainer())
                    ConfigurationErrorMessage.getInstance(
                    ).addFaultyContainers(container_id, self.getId())
                    Logger.log(
                        "e",
                        "When trying to deserialize %s, we received an unknown container ID (%s)"
                        % (self.getId(), container_id))
                    raise ContainerFormatError(
                        "When trying to deserialize %s, we received an unknown container ID (%s)"
                        % (self.getId(), container_id))

        elif parser.has_option("general", "containers"):
            # Backward compatibility with 2.3.1: The containers used to be saved in a single comma-separated list.
            container_string = parser["general"].get("containers", "")
            Logger.log(
                "d",
                "While deserializing, we got the following container string: %s",
                container_string)
            container_id_list = container_string.split(",")
            for container_id in container_id_list:
                if container_id != "":
                    containers = _containerRegistry.findContainers(
                        id=container_id)
                    if containers:
                        containers[0].propertyChanged.connect(
                            self._collectPropertyChanges)
                        self._containers.append(containers[0])
                    else:
                        self._containers.append(
                            _containerRegistry.getEmptyInstanceContainer())
                        ConfigurationErrorMessage.getInstance(
                        ).addFaultyContainers(container_id, self.getId())
                        Logger.log(
                            "e",
                            "When trying to deserialize %s, we received an unknown container ID (%s)"
                            % (self.getId(), container_id))
                        raise ContainerFormatError(
                            "When trying to deserialize %s, we received an unknown container ID (%s)"
                            % (self.getId(), container_id))

        ## TODO; Deserialize the containers.

        return serialized

    @classmethod
    def deserializeMetadata(cls, serialized: str,
                            container_id: str) -> List[Dict[str, Any]]:
        """Gets the metadata of a container stack from a serialised format.

        This parses the entire CFG document and only extracts the metadata from
        it.

        :param serialized: A CFG document, serialised as a string.
        :param container_id: The ID of the container that we're getting the
        metadata of, as obtained from the file name.
        :return: A dictionary of metadata that was in the CFG document as a
        singleton list. If anything went wrong, this returns an empty list
        instead.
        """

        serialized = cls._updateSerialized(
            serialized)  # Update to most recent version.
        parser = configparser.ConfigParser(interpolation=None)
        parser.read_string(serialized)

        metadata = {"id": container_id, "container_type": ContainerStack}
        try:
            metadata["name"] = parser["general"]["name"]
            metadata["version"] = parser["general"]["version"]
        except KeyError as e:  # One of the keys or the General section itself is missing.
            raise InvalidContainerStackError(
                "Missing required fields: {error_msg}".format(
                    error_msg=str(e)))

        if "metadata" in parser:
            metadata.update(parser["metadata"])

        return [metadata]

    def getAllKeys(self) -> Set[str]:
        """Get all keys known to this container stack.

        In combination with getProperty(), you can obtain the current property
        values of all settings.

        :return: A set of all setting keys in this container stack.
        """

        keys = set()  # type: Set[str]
        definition_containers = [
            container for container in self.getContainers()
            if container.__class__ == DefinitionContainer
        ]  #To get all keys, get all definitions from all definition containers.
        for definition_container in cast(List[DefinitionContainer],
                                         definition_containers):
            keys |= definition_container.getAllKeys()
        if self._next_stack:
            keys |= self._next_stack.getAllKeys()
        return keys

    def getContainers(self) -> List[ContainerInterface]:
        """Get a list of all containers in this stack.

        Note that it returns a shallow copy of the container list, as it's only allowed to change the order or entries
        in this list by the proper functions.
        :return: A list of all containers in this stack.
        """

        return self._containers[:]

    def getContainerIndex(self, container: ContainerInterface) -> int:
        return self._containers.index(container)

    def getContainer(self, index: int) -> ContainerInterface:
        """Get a container by index.

        :param index: The index of the container to get.

        :return: The container at the specified index.

        :exception IndexError: Raised when the specified index is out of bounds.
        """

        if index < 0:
            raise IndexError
        return self._containers[index]

    def getTop(self) -> Optional[ContainerInterface]:
        """Get the container at the top of the stack.

        This is a convenience method that will always return the top of the stack.

        :return: The container at the top of the stack, or None if no containers have been added.
        """

        if self._containers:
            return self._containers[0]

        return None

    def getBottom(self) -> Optional[ContainerInterface]:
        """Get the container at the bottom of the stack.

        This is a convenience method that will always return the bottom of the stack.

        :return: The container at the bottom of the stack, or None if no containers have been added.
        """

        if self._containers:
            return self._containers[-1]

        return None

    def getPath(self) -> str:
        """:copydoc ContainerInterface::getPath.

        Reimplemented from ContainerInterface
        """

        return self._path

    def setPath(self, path: str) -> None:
        """:copydoc ContainerInterface::setPath

        Reimplemented from ContainerInterface
        """

        self._path = path

    def getSettingDefinition(self, key: str) -> Optional[SettingDefinition]:
        """Get the SettingDefinition object for a specified key"""

        for container in self._containers:
            if not isinstance(container, DefinitionContainer):
                continue

            settings = container.findDefinitions(key=key)
            if settings:
                return settings[0]

        if self._next_stack:
            return self._next_stack.getSettingDefinition(key)
        else:
            return None

    @UM.FlameProfiler.profile
    def findContainer(self,
                      criteria: Dict[str, Any] = None,
                      container_type: type = None,
                      **kwargs: Any) -> Optional[ContainerInterface]:
        """Find a container matching certain criteria.

        :param criteria: A dictionary containing key and value pairs that need to
        match the container. Note that the value of "*" can be used as a wild
        card. This will ensure that any container that has the specified key in
        the meta data is found.
        :param container_type: An optional type of container to filter on.
        :return: The first container that matches the filter criteria or None if not found.
        """

        if not criteria and kwargs:
            criteria = kwargs
        elif criteria is None:
            criteria = {}

        for container in self._containers:
            meta_data = container.getMetaData()
            match = container.__class__ == container_type or container_type is None
            for key in criteria:
                if not match:
                    break
                try:
                    if meta_data[key] == criteria[key] or criteria[key] == "*":
                        continue
                    else:
                        match = False
                        break
                except KeyError:
                    match = False
                    break

            if match:
                return container

        return None

    def addContainer(self, container: ContainerInterface) -> None:
        """Add a container to the top of the stack.

        :param container: The container to add to the stack.
        """

        self.insertContainer(0, container)

    def insertContainer(self, index: int,
                        container: ContainerInterface) -> None:
        """Insert a container into the stack.

        :param index: The index of to insert the container at.
        A negative index counts from the bottom
        :param container: The container to add to the stack.
        """

        if container is self:
            raise Exception("Unable to add stack to itself.")

        container.propertyChanged.connect(self._collectPropertyChanges)
        self._containers.insert(index, container)
        self.containersChanged.emit(container)
        self._dirty = True

    def replaceContainer(self,
                         index: int,
                         container: ContainerInterface,
                         postpone_emit: bool = False) -> None:
        """Replace a container in the stack.

        :param index: :type{int} The index of the container to replace.
        :param container: The container to replace the existing entry with.
        :param postpone_emit:  During stack manipulation you may want to emit later.

        :exception IndexError: Raised when the specified index is out of bounds.
        :exception Exception: when trying to replace container ContainerStack.
        """

        if index < 0:
            raise IndexError
        if container is self:
            raise Exception(
                "Unable to replace container with ContainerStack (self) ")

        self._containers[index].propertyChanged.disconnect(
            self._collectPropertyChanges)
        container.propertyChanged.connect(self._collectPropertyChanges)
        self._containers[index] = container
        self._dirty = True
        if postpone_emit:
            # send it using sendPostponedEmits
            self._postponed_emits.append((self.containersChanged, container))
        else:
            self.containersChanged.emit(container)

    def removeContainer(self, index: int = 0) -> None:
        """Remove a container from the stack.

        :param index: :type{int} The index of the container to remove.

        :exception IndexError: Raised when the specified index is out of bounds.
        """

        if index < 0:
            raise IndexError
        try:
            container = self._containers[index]
            self._dirty = True
            container.propertyChanged.disconnect(self._collectPropertyChanges)
            del self._containers[index]
            self.containersChanged.emit(container)
        except TypeError:
            raise IndexError("Can't delete container with index %s" % index)

    def getNextStack(self) -> Optional["ContainerStack"]:
        """Get the next stack

        The next stack is the stack that is searched for a setting value if the
        bottom of the stack is reached when searching for a value.

        :return: :type{ContainerStack} The next stack or None if not set.
        """

        return self._next_stack

    def setNextStack(self,
                     stack: "ContainerStack",
                     connect_signals: bool = True) -> None:
        """Set the next stack

        :param stack: :type{ContainerStack} The next stack to set. Can be None.
        Raises Exception when trying to set itself as next stack (to prevent infinite loops)
        :sa getNextStack
        """

        if self is stack:
            raise Exception("Next stack can not be itself")
        if self._next_stack == stack:
            return

        if self._next_stack:
            self._next_stack.propertyChanged.disconnect(
                self._collectPropertyChanges)
            self.containersChanged.disconnect(
                self._next_stack.containersChanged)
        self._next_stack = stack
        if self._next_stack and connect_signals:
            self._next_stack.propertyChanged.connect(
                self._collectPropertyChanges)
            self.containersChanged.connect(self._next_stack.containersChanged)

    def sendPostponedEmits(self) -> None:
        """Send postponed emits
        These emits are collected from the option postpone_emit.
        Note: the option can be implemented for all functions modifying the stack.
        """

        while self._postponed_emits:
            signal, signal_arg = self._postponed_emits.pop(0)
            signal.emit(signal_arg)

    @UM.FlameProfiler.profile
    def hasErrors(self) -> bool:
        """Check if the container stack has errors"""

        for key in self.getAllKeys():
            enabled = self.getProperty(key, "enabled")
            if not enabled:
                continue
            validation_state = self.getProperty(key, "validationState")
            if validation_state is None:
                # Setting is not validated. This can happen if there is only a setting definition.
                # We do need to validate it, because a setting defintions value can be set by a function, which could
                # be an invalid setting.
                definition = cast(SettingDefinition,
                                  self.getSettingDefinition(key))
                validator_type = SettingDefinition.getValidatorForType(
                    definition.type)
                if validator_type:
                    validator = validator_type(key)
                    validation_state = validator(self)
            if validation_state in (ValidatorState.Exception,
                                    ValidatorState.MaximumError,
                                    ValidatorState.MinimumError,
                                    ValidatorState.Invalid):
                return True
        return False

    @UM.FlameProfiler.profile
    def getErrorKeys(self) -> List[str]:
        """Get all the keys that are in an error state in this stack"""

        error_keys = []
        for key in self.getAllKeys():
            validation_state = self.getProperty(key, "validationState")
            if validation_state is None:
                # Setting is not validated. This can happen if there is only a setting definition.
                # We do need to validate it, because a setting defintions value can be set by a function, which could
                # be an invalid setting.
                definition = cast(SettingDefinition,
                                  self.getSettingDefinition(key))
                validator_type = SettingDefinition.getValidatorForType(
                    definition.type)
                if validator_type:
                    validator = validator_type(key)
                    validation_state = validator(self)
            if validation_state in (ValidatorState.Exception,
                                    ValidatorState.MaximumError,
                                    ValidatorState.MinimumError,
                                    ValidatorState.Invalid):
                error_keys.append(key)
        return error_keys

    # protected:

    # Gather up all signal emissions and delay their emit until the next time the event
    # loop can run. This prevents us from sending the same change signal multiple times.
    # In addition, it allows us to emit a single signal that reports all properties that
    # have changed.
    def _collectPropertyChanges(self, key: str, property_name: str) -> None:
        if key not in self._property_changes:
            self._property_changes[key] = set()

        self._property_changes[key].add(property_name)

        if not self._emit_property_changed_queued:
            from UM.Application import Application
            Application.getInstance().callLater(
                self._emitCollectedPropertyChanges)
            self._emit_property_changed_queued = True

    # Perform the emission of the change signals that were collected in a previous step.
    def _emitCollectedPropertyChanges(self) -> None:
        for key, property_names in self._property_changes.items():
            self.propertiesChanged.emit(key, property_names)

            for property_name in property_names:
                self.propertyChanged.emit(key, property_name)

        self._property_changes = {}
        self._emit_property_changed_queued = False

    def __str__(self) -> str:
        return "<{class_name} '{id}' containers={containers}>".format(
            class_name=type(self).__name__,
            id=self.getId(),
            containers=self._containers)

    def __repr__(self) -> str:
        return str(self)
Ejemplo n.º 17
0
class PyDateEdit(QDateEdit):
    #
    # Initialize base class
    # Force use of the calendar popup
    # Set default values for calendar properties
    #
    def __init__(self, *args):
        super(PyDateEdit, self).__init__(*args)

        self.setCalendarPopup(True)
        self.__cw = None
        self.__firstDayOfWeek = Qt.Monday
        self.__gridVisible = False
        self.__horizontalHeaderFormat = QCalendarWidget.ShortDayNames
        self.__verticalHeaderFormat = QCalendarWidget.ISOWeekNumbers
        self.__navigationBarVisible = True

    #
    # Call event handler of base class
    # Get the calendar widget, if not already done
    # Set the calendar properties
    #
    def mousePressEvent(self, event):
        super(PyDateEdit, self).mousePressEvent(event)

        if not self.__cw:
            self.__cw = self.findChild(QCalendarWidget)
            if self.__cw:
                self.__cw.setFirstDayOfWeek(self.__firstDayOfWeek)
                self.__cw.setGridVisible(self.__gridVisible)
                self.__cw.setHorizontalHeaderFormat(
                    self.__horizontalHeaderFormat)
                self.__cw.setVerticalHeaderFormat(self.__verticalHeaderFormat)
                self.__cw.setNavigationBarVisible(self.__navigationBarVisible)

    #
    # Make sure, the calendarPopup property is invisible in Designer
    #
    def getCalendarPopup(self):
        return True

    calendarPopup = pyqtProperty(bool, fget=getCalendarPopup)

    #
    # Property firstDayOfWeek: Qt::DayOfWeek
    # Get: getFirstDayOfWeek()
    # Set: setFirstDayOfWeek()
    # Reset: resetFirstDayOfWeek()
    #
    def getFirstDayOfWeek(self):
        return self.__firstDayOfWeek

    def setFirstDayOfWeek(self, dayOfWeek):
        if dayOfWeek != self.__firstDayOfWeek:
            self.__firstDayOfWeek = dayOfWeek
            if self.__cw:
                self.__cw.setFirstDayOfWeek(dayOfWeek)

    def resetFirstDayOfWeek(self):
        if self.__firstDayOfWeek != Qt.Monday:
            self.__firstDayOfWeek = Qt.Monday
            if self.__cw:
                self.__cw.setFirstDayOfWeek(Qt.Monday)

    firstDayOfWeek = pyqtProperty(
        Qt.DayOfWeek,
        fget=getFirstDayOfWeek,
        fset=setFirstDayOfWeek,
        freset=resetFirstDayOfWeek,
    )

    #
    # Property gridVisible: bool
    # Get: isGridVisible()
    # Set: setGridVisible()
    # Reset: resetGridVisible()
    #
    def isGridVisible(self):
        return self.__gridVisible

    def setGridVisible(self, show):
        if show != self.__gridVisible:
            self.__gridVisible = show
            if self.__cw:
                self.__cw.setGridVisible(show)

    def resetGridVisible(self):
        if self.__gridVisible != False:
            self.__gridVisible = False
            if self.__cw:
                self.__cw.setGridVisible(False)

    gridVisible = pyqtProperty(bool,
                               fget=isGridVisible,
                               fset=setGridVisible,
                               freset=resetGridVisible)

    #
    # Property horizontalHeaderFormat: QCalendarWidget::HorizontalHeaderFormat
    # Get: getHorizontalHeaderFormat()
    # Set: setHorizontalHeaderFormat()
    # Reset: resetHorizontalHeaderFormat()
    #
    def getHorizontalHeaderFormat(self):
        return self.__horizontalHeaderFormat

    def setHorizontalHeaderFormat(self, format):
        if format != self.__horizontalHeaderFormat:
            self.__horizontalHeaderFormat = format
            if self.__cw:
                self.__cw.setHorizontalHeaderFormat(format)

    def resetHorizontalHeaderFormat(self):
        if self.__horizontalHeaderFormat != QCalendarWidget.ShortDayNames:
            self.__horizontalHeaderFormat = QCalendarWidget.ShortDayNames
            if self.__cw:
                self.__cw.setHorizontalHeaderFormat(
                    QCalendarWidget.ShortDayNames)

    horizontalHeaderFormat = pyqtProperty(
        QCalendarWidget.HorizontalHeaderFormat,
        fget=getHorizontalHeaderFormat,
        fset=setHorizontalHeaderFormat,
        freset=resetHorizontalHeaderFormat,
    )

    #
    # Property verticalHeaderFormat: QCalendarWidget::VerticalHeaderFormat
    # Get: getVerticalHeaderFormat()
    # Set: setVerticalHeaderFormat()
    # Reset: resetVerticalHeaderFormat()
    #
    def getVerticalHeaderFormat(self):
        return self.__verticalHeaderFormat

    def setVerticalHeaderFormat(self, format):
        if format != self.__verticalHeaderFormat:
            self.__verticalHeaderFormat = format
            if self.__cw:
                self.__cw.setVerticalHeaderFormat(format)

    def resetVerticalHeaderFormat(self):
        if self.__verticalHeaderFormat != QCalendarWidget.ISOWeekNumbers:
            self.__verticalHeaderFormat = QCalendarWidget.ISOWeekNumbers
            if self.__cw:
                self.__cw.setVerticalHeaderFormat(
                    QCalendarWidget.ISOWeekNumbers)

    verticalHeaderFormat = pyqtProperty(
        QCalendarWidget.VerticalHeaderFormat,
        fget=getVerticalHeaderFormat,
        fset=setVerticalHeaderFormat,
        freset=resetVerticalHeaderFormat,
    )

    #
    # Property navigationBarVisible: bool
    # Get: isNavigationBarVisible()
    # Set: setNavigationBarVisible()
    # Reset: resetNavigationBarVisible()
    #
    def isNavigationBarVisible(self):
        return self.__navigationBarVisible

    def setNavigationBarVisible(self, visible):
        if visible != self.__navigationBarVisible:
            self.__navigationBarVisible = visible
            if self.__cw:
                self.__cw.setNavigationBarVisible(visible)

    def resetNavigationBarVisible(self):
        if self.__navigationBarVisible != True:
            self.__navigationBarVisible = True
            if self.__cw:
                self.__cw.setNavigationBarVisible(True)

    navigationBarVisible = pyqtProperty(
        bool,
        fget=isNavigationBarVisible,
        fset=setNavigationBarVisible,
        freset=resetNavigationBarVisible,
    )
Ejemplo n.º 18
0
class MachineLog(QTextEdit, _HalWidgetBase):
    def __init__(self, parent=None):
        super(MachineLog, self).__init__(parent)
        self._delay = 0
        self._hash_code = None
        self._machine_log = True
        self._integrator_log = False
        self.integratorPath = os.path.expanduser('~/qtvcp.log')
        self.machineLogPath = os.path.expanduser(INFO.MACHINE_LOG_HISTORY_PATH)

    def _hal_init(self):
        if self._machine_log:
            STATUS.connect('machine-log-changed',lambda w: self.loadLog())
        elif self._integrator_log:
            STATUS.connect('periodic', self._periodicCheck)

    def _periodicCheck(self, w):
        if self._delay < 9:
            self._delay += 1
            return
        if STATUS.is_status_valid() == False:
            return
        self._delay = 0
        m1 = self.md5sum(self.integratorPath)
        if m1 and self._hash_code != m1:
            self._hash_code = m1
            self.loadIntegratorLog()

       # create a hash code
    def md5sum(self,filename):
        try:
            f = open(filename, "rb")
        except:
            return None
        else:
            return hashlib.md5(f.read()).hexdigest()

    def loadLog(self):
        file = QFile(self.machineLogPath)
        file.open(QFile.ReadOnly)
        logText = file.readAll()
        try:
            # Python v2.
            logText = unicode(logText, encoding='utf8')
        except NameError:
            # Python v3.
            logText = str(logText, encoding='utf8')
        self.setPlainText(logText)

    def loadIntegratorLog(self):
        file = QFile(self.integratorPath)
        file.open(QFile.ReadOnly)
        logText = file.readAll()
        try:
            # Python v2.
            logText = unicode(logText, encoding='utf8')
        except NameError:
            # Python v3.
            logText = str(logText, encoding='utf8')
        self.setPlainText(logText)


################## properties ###################

    def _toggle_properties(self, picked):
        data = ('machine_log', 'integrator_log')

        for i in data:
            if not i == picked:
                self[i+'_option'] = False

    def set_machine_log(self, value):
        self._machine_log = value
        if value:
            self._toggle_properties('machine_log')
    def get_machine_log(self):
        return self._machine_log
    def reset_machine_log(self):
        self._machine_log = True
    machine_log_option = pyqtProperty(bool, get_machine_log, set_machine_log, reset_machine_log)

    def set_integrator_log(self, value):
        self._integrator_log = value
        if value:
            self._toggle_properties('integrator_log')
    def get_integrator_log(self):
        return self._integrator_log
    def reset_integrator_log(self):
        self._integrator_log = False
    integrator_log_option = pyqtProperty(bool, get_integrator_log, set_integrator_log, reset_integrator_log)

    ##############################
    # required class boiler code #
    ##############################

    def __getitem__(self, item):
        return getattr(self, item)
    def __setitem__(self, item, value):
        return setattr(self, item, value)
Ejemplo n.º 19
0
class NetworkMJPGImage(QQuickPaintedItem):
    def __init__(self, *args, **kwargs) -> None:
        super().__init__(*args, **kwargs)

        self._stream_buffer = QByteArray()
        self._stream_buffer_start_index = -1
        self._network_manager = None  # type: QNetworkAccessManager
        self._image_request = None  # type: QNetworkRequest
        self._image_reply = None  # type: QNetworkReply
        self._image = QImage()
        self._image_rect = QRect()

        self._source_url = QUrl()
        self._started = False

        self._mirror = False

        self.setAntialiasing(True)

    ##  Ensure that close gets called when object is destroyed
    def __del__(self) -> None:
        self.stop()

    def paint(self, painter: "QPainter") -> None:
        if self._mirror:
            painter.drawImage(self.contentsBoundingRect(),
                              self._image.mirrored())
            return

        painter.drawImage(self.contentsBoundingRect(), self._image)

    def setSourceURL(self, source_url: "QUrl") -> None:
        self._source_url = source_url
        self.sourceURLChanged.emit()
        if self._started:
            self.start()

    def getSourceURL(self) -> "QUrl":
        return self._source_url

    sourceURLChanged = pyqtSignal()
    source = pyqtProperty(QUrl,
                          fget=getSourceURL,
                          fset=setSourceURL,
                          notify=sourceURLChanged)

    def setMirror(self, mirror: bool) -> None:
        if mirror == self._mirror:
            return
        self._mirror = mirror
        self.mirrorChanged.emit()
        self.update()

    def getMirror(self) -> bool:
        return self._mirror

    mirrorChanged = pyqtSignal()
    mirror = pyqtProperty(bool,
                          fget=getMirror,
                          fset=setMirror,
                          notify=mirrorChanged)

    imageSizeChanged = pyqtSignal()

    @pyqtProperty(int, notify=imageSizeChanged)
    def imageWidth(self) -> int:
        return self._image.width()

    @pyqtProperty(int, notify=imageSizeChanged)
    def imageHeight(self) -> int:
        return self._image.height()

    @pyqtSlot()
    def start(self) -> None:
        self.stop()  # Ensure that previous requests (if any) are stopped.

        if not self._source_url:
            Logger.log("w", "Unable to start camera stream without target!")
            return

        auth_data = ""
        if self._source_url.userInfo():
            # move auth data to basic authorization header
            auth_data = base64.b64encode(
                self._source_url.userInfo().encode()).decode("utf-8")
            authority = self._source_url.authority()
            self._source_url.setAuthority(authority.rsplit("@", 1)[1])

        self._image_request = QNetworkRequest(self._source_url)
        self._image_request.setAttribute(
            QNetworkRequest.FollowRedirectsAttribute, True)

        if auth_data:
            self._image_request.setRawHeader(b"Authorization",
                                             ("basic %s" % auth_data).encode())

        if self._source_url.scheme().lower() == "https":
            # ignore SSL errors (eg for self-signed certificates)
            ssl_configuration = QSslConfiguration.defaultConfiguration()
            ssl_configuration.setPeerVerifyMode(QSslSocket.VerifyNone)
            self._image_request.setSslConfiguration(ssl_configuration)

        if self._network_manager is None:
            self._network_manager = QNetworkAccessManager()

        self._image_reply = self._network_manager.get(self._image_request)
        self._image_reply.downloadProgress.connect(
            self._onStreamDownloadProgress)

        self._started = True

    @pyqtSlot()
    def stop(self) -> None:
        self._stream_buffer = QByteArray()
        self._stream_buffer_start_index = -1

        if self._image_reply:
            try:
                try:
                    self._image_reply.downloadProgress.disconnect(
                        self._onStreamDownloadProgress)
                except Exception:
                    pass

                if not self._image_reply.isFinished():
                    self._image_reply.close()
            except Exception as e:  # RuntimeError
                pass  # It can happen that the wrapped c++ object is already deleted.

            self._image_reply = None
            self._image_request = None

        self._network_manager = None

        self._started = False

    def _onStreamDownloadProgress(self, bytes_received: int,
                                  bytes_total: int) -> None:
        # An MJPG stream is (for our purpose) a stream of concatenated JPG images.
        # JPG images start with the marker 0xFFD8, and end with 0xFFD9
        if self._image_reply is None:
            return
        self._stream_buffer += self._image_reply.readAll()

        if len(self._stream_buffer
               ) > 2000000:  # No single camera frame should be 2 Mb or larger
            Logger.log(
                "w",
                "MJPEG buffer exceeds reasonable size. Restarting stream...")
            self.stop()  # resets stream buffer and start index
            self.start()
            return

        if self._stream_buffer_start_index == -1:
            self._stream_buffer_start_index = self._stream_buffer.indexOf(
                b'\xff\xd8')
        stream_buffer_end_index = self._stream_buffer.lastIndexOf(b'\xff\xd9')
        # If this happens to be more than a single frame, then so be it; the JPG decoder will
        # ignore the extra data. We do it like this in order not to get a buildup of frames

        if self._stream_buffer_start_index != -1 and stream_buffer_end_index != -1:
            jpg_data = self._stream_buffer[
                self._stream_buffer_start_index:stream_buffer_end_index + 2]
            self._stream_buffer = self._stream_buffer[stream_buffer_end_index +
                                                      2:]
            self._stream_buffer_start_index = -1
            self._image.loadFromData(jpg_data)

            if self._image.rect() != self._image_rect:
                self.imageSizeChanged.emit()

            self.update()
Ejemplo n.º 20
0
class EntryDialog(QDialog, _HalWidgetBase):
    def __init__(self, parent=None):
        super(EntryDialog, self).__init__(parent)
        self._color = QColor(0, 0, 0, 150)
        self.play_sound = False
        self._request_name = 'ENTRY'
        self.title = 'Numerical Entry'
        self.setWindowFlags(self.windowFlags() | Qt.Tool | Qt.Dialog
                            | Qt.WindowStaysOnTopHint
                            | Qt.WindowSystemMenuHint)

        l = QVBoxLayout()
        self.setLayout(l)

        o = TouchInputWidget()
        l.addWidget(o)

        self.Num = QLineEdit()
        # actiate touch input
        self.Num.keyboard_type = 'numeric'
        self.Num.returnPressed.connect(lambda: self.accept())

        gl = QVBoxLayout()
        gl.addWidget(self.Num)

        self.bBox = QDialogButtonBox()
        self.bBox.addButton('Apply', QDialogButtonBox.AcceptRole)
        self.bBox.addButton('Cancel', QDialogButtonBox.RejectRole)
        self.bBox.rejected.connect(self.reject)
        self.bBox.accepted.connect(self.accept)

        gl.addWidget(self.bBox)
        o.setLayout(gl)

    def _hal_init(self):
        x = self.geometry().x()
        y = self.geometry().y()
        w = self.geometry().width()
        h = self.geometry().height()
        geo = '%s %s %s %s' % (x, y, w, h)
        self._default_geometry = [x, y, w, h]
        if self.PREFS_:
            self._geometry_string = self.PREFS_.getpref(
                'EntryDialog-geometry', geo, str, 'DIALOG_OPTIONS')
        else:
            self._geometry_string = 'default'
        if self.PREFS_:
            self.play_sound = self.PREFS_.getpref('toolDialog_play_sound',
                                                  True, bool, 'DIALOG_OPTIONS')
            self.sound_type = self.PREFS_.getpref('toolDialog_sound_type',
                                                  'RING', str,
                                                  'DIALOG_OPTIONS')
        else:
            self.play_sound = False
        STATUS.connect('dialog-request', self._external_request)

    # this processes STATUS called dialog requests
    # We check the cmd to see if it was for us
    # then we check for a id string
    # if all good show the dialog
    # and then send back the dialog response via a general message
    def _external_request(self, w, message):
        if message.get('NAME') == self._request_name:
            t = message.get('TITLE')
            if t:
                self.title = t
            else:
                self.title = 'Entry'
            num = self.showdialog()
            message['RETURN'] = num
            STATUS.emit('general', message)

    def showdialog(self):
        STATUS.emit('focus-overlay-changed', True, 'Origin Setting',
                    self._color)
        self.setWindowTitle(self.title)
        if self.play_sound:
            STATUS.emit('play-sound', self.sound_type)
        self.calculate_placement()
        retval = self.exec_()
        STATUS.emit('focus-overlay-changed', False, None, None)
        record_geometry(self, 'EntryDialog-geometry')
        LOG.debug("Value of pressed button: {}".format(retval))
        if retval:
            try:
                return float(self.Num.text())
            except Exception as e:
                print e
        return None

    def calculate_placement(self):
        geometry_parsing(self, 'EntryDialog-geometry')

    def getColor(self):
        return self._color

    def setColor(self, value):
        self._color = value

    def resetState(self):
        self._color = QColor(0, 0, 0, 150)

    overlay_color = pyqtProperty(QColor, getColor, setColor)
Ejemplo n.º 21
0
class surfaceCheck(FoamApp):
    """
    surfaceCheck - Utility
    ======================
    Checks geometric and topological quality of a surface.
    (src: http://www.openfoam.org/docs/user/standard-utilities.php)
    """

    app_name = "surfaceCheck"
    input_types = ["stl_files"]
    output_types = ["stl_files"]

    def __init__(self, parent, instance_name, status):
        FoamApp.__init__(self, parent, instance_name, status)

        # Input/Output objects
        # ====================
        self.__stl_input_dict = {}
        self.__input_files = []

        # TreeView model for GUI
        # ======================
        self.__tree_view_model = []

        # Visualization objects
        # =====================
        self.__input_vis = []  # the visualization of the input file

    def load(self):
        self.update_tree_view()

    def run(self):
        for app in self.__stl_input_dict:
            for stl_file in self.__stl_input_dict[app]:
                if self.foam_exec(["surfaceCheck", stl_file._filename]) != 0:
                    return False
        return True

    def stl_files_in(self, input_dict):
        self.__input_files = []
        self.__stl_input_dict = input_dict
        for files in input_dict.values():
            self.__input_files.extend(files)
        self.update_tree_view()
        self.stl_files_out_changed.emit()

        for iv in self.__input_vis:
            self.remove_vis_object(iv)
        self.__input_vis = []
        for input_file in self.__input_files:
            input_vis = STLLoader(input_file)
            self.__input_vis.append(input_vis)
            self.add_vis_object(input_vis)

    '''
    TreeView for app
    ================
    '''
    tree_view_model_changed = pyqtSignal(name="treeViewModelChanged")

    @property
    def tree_view_model(self):
        return self.__tree_view_model

    @tree_view_model.setter
    def tree_view_model(self, tree_view_model):
        if self.__tree_view_model != tree_view_model:
            self.__tree_view_model = tree_view_model
            self.tree_view_model_changed.emit()

    treeViewModel = pyqtProperty("QVariantList",
                                 fget=tree_view_model.fget,
                                 fset=tree_view_model.fset,
                                 notify=tree_view_model_changed)

    def __create_tree_view_model(self):
        stl_model = [{
            'text':
            stl.filename(),
            'isRegion':
            False,
            'isRefinementObject':
            False,
            'type':
            'stl_file',
            'elements': [{
                'text': region['name'],
                'isRegion': True,
                'isRefinementObject': False,
                'type': 'stl_region'
            } for region in stl.patchInfo()]
        } for stl in self.__input_files]
        return stl_model

    def update_tree_view(self):
        self.tree_view_model = self.__create_tree_view_model()

    '''
    Output for other Apps
    =====================
    '''

    def stl_files_out(self):
        return copy.deepcopy(self.__input_files)

    stl_files_out_changed = pyqtSignal()
Ejemplo n.º 22
0
class FileDialog(QFileDialog, _HalWidgetBase):
    def __init__(self, parent=None):
        super(FileDialog, self).__init__(parent)
        self._state = False
        self._load_request_name = 'LOAD'
        self._save_request_name = 'SAVE'
        self._color = QColor(0, 0, 0, 150)
        options = QFileDialog.Options()
        options |= QFileDialog.DontUseNativeDialog
        self.setOptions(options)
        self.setWindowModality(Qt.ApplicationModal)
        exts = INFO.get_qt_filter_extensions()
        self.setNameFilter(exts)
        self.default_path = (os.path.join(os.path.expanduser('~'),
                                          'linuxcnc/nc_files/examples'))

    def _hal_init(self):
        x = self.geometry().x()
        y = self.geometry().y()
        w = self.geometry().width()
        h = self.geometry().height()
        geo = '%s %s %s %s' % (x, y, w, h)
        self._default_geometry = [x, y, w, h]
        if self.PREFS_:
            self._geometry_string = self.PREFS_.getpref(
                'FileDialog-geometry', geo, str, 'DIALOG_OPTIONS')
        else:
            self._geometry_string = 'default'
        STATUS.connect('dialog-request', self._external_request)
        if self.PREFS_:
            self.play_sound = self.PREFS_.getpref('fileDialog_play_sound',
                                                  True, bool, 'DIALOG_OPTIONS')
            self.sound_type = self.PREFS_.getpref('fileDialog_sound_type',
                                                  'RING', str,
                                                  'DIALOG_OPTIONS')
            last_path = self.PREFS_.getpref('last_file_path',
                                            self.default_path, str,
                                            'BOOK_KEEPING')
            self.setDirectory(last_path)
        else:
            self.play_sound = False

    def _external_request(self, w, message):
        ext = message.get('EXTENTIONS')
        pre = message.get('FILENAME')
        dir = message.get('DIRECTORY')
        if message.get('NAME') == self._load_request_name:
            # if there is an ID then a file name response is expected
            if message.get('ID'):
                print message.get('ID')
                message['RETURN'] = self.load_dialog(ext, pre, dir, True)
                STATUS.emit('general', message)
            else:
                self.load_dialog(extentions=ext)
        elif message.get('NAME') == self._save_request_name:
            if message.get('ID'):
                message['RETURN'] = self.save_dialog(ext, pre, dir)
                STATUS.emit('general', message)

    def load_dialog(self,
                    extentions=None,
                    preselect=None,
                    directory=None,
                    return_path=False):
        self.setFileMode(QFileDialog.ExistingFile)
        self.setAcceptMode(QFileDialog.AcceptOpen)
        if extentions:
            self.setNameFilter(extentions)
        if preselect:
            self.selectFile(preselect)
        else:
            self.selectFile('')
        if directory:
            self.setDirectory(directory)
        self.setWindowTitle('Open')
        STATUS.emit('focus-overlay-changed', True, 'Open Gcode', self._color)
        if self.play_sound:
            STATUS.emit('play-sound', self.sound_type)
        self.calculate_placement()
        fname = None
        if (self.exec_()):
            fname = self.selectedFiles()[0]
            path = self.directory().absolutePath()
            self.setDirectory(path)
        STATUS.emit('focus-overlay-changed', False, None, None)
        record_geometry(self, 'FileDialog-geometry')
        if fname and not return_path:
            if self.PREFS_:
                self.PREFS_.putpref('last_file_path', path, str,
                                    'BOOK_KEEPING')
            ACTION.OPEN_PROGRAM(fname)
            STATUS.emit('update-machine-log', 'Loaded: ' + fname, 'TIME')
        return fname

    def save_dialog(self, extentions=None, preselect=None, directory=None):
        self.setFileMode(QFileDialog.AnyFile)
        self.setAcceptMode(QFileDialog.AcceptSave)
        if extentions:
            self.setNameFilter(extensions)
        if preselect:
            self.selectFile(preselect)
        else:
            self.selectFile('')
        if directory:
            self.setDirectory(directory)
        self.setWindowTitle('Save')
        STATUS.emit('focus-overlay-changed', True, 'Save Gcode', self._color)
        if self.play_sound:
            STATUS.emit('play-sound', self.sound_type)
        self.calculate_placement()
        fname = None
        if (self.exec_()):
            fname = self.selectedFiles()[0]
            path = self.directory().absolutePath()
            self.setDirectory(path)
        else:
            fname = None
        STATUS.emit('focus-overlay-changed', False, None, None)
        record_geometry(self, 'FileDialog-geometry')
        if fname:
            if self.PREFS_:
                self.PREFS_.putpref('last_file_path', path, str,
                                    'BOOK_KEEPING')
        return fname

    def calculate_placement(self):
        geometry_parsing(self, 'FileDialog-geometry')

    #**********************
    # Designer properties
    #**********************

    @pyqtSlot(bool)
    def setState(self, value):
        self._state = value
        if value:
            self.show()
        else:
            self.hide()

    def getState(self):
        return self._state

    def resetState(self):
        self._state = False

    def getColor(self):
        return self._color

    def setColor(self, value):
        self._color = value

    def resetState(self):
        self._color = QColor(0, 0, 0, 150)

    state = pyqtProperty(bool, getState, setState, resetState)
    overlay_color = pyqtProperty(QColor, getColor, setColor)
Ejemplo n.º 23
0
class CPUInfo(QObject):
    def __init__(self):
        QObject.__init__(self)
        
        self.ctemp, self.gtemp, self.time = [], [], []
        self.cpuAvg, self.gpuAvg = 0.0, 0.0
        
        self.carm, self.ccore, self.ch264 = 0, 0, 0
        self.cuart, self.cpwm, self.chdmi = 0, 0, 0
        self.vcore, self.sdram_c, self.sdram_i, self.sdram_p = 0.0, 0.0, 0.0, 0.0
        self.coh264, self.compg2, self.compg4 = False, False, False
        self.cowvc1, self.comjpg = False, False        
        self.mcpu, self.mgpu = 0, 0
        
        
        
        
        
    cpuSig  = pyqtSignal(list)
    gpuSig  = pyqtSignal(list)
    timeSig = pyqtSignal(list)
    
    cpuAvgSig = pyqtSignal(float)
    gpuAvgSig = pyqtSignal(float)
    
    
    #************  1  **************#
    
    carmSig  = pyqtSignal(int)
    ccoreSig  = pyqtSignal(int)
    ch264Sig  = pyqtSignal(int)
    
    cuartSig  = pyqtSignal(int)
    cpwmSig   = pyqtSignal(int)
    chdmiSig  = pyqtSignal(int)
     
    
    #************  2  ****************#
    
    vcoreSig    = pyqtSignal(float)
    sdram_cSig  = pyqtSignal(float)
    sdram_iSig  = pyqtSignal(float)
    sdram_pSig  = pyqtSignal(float)
    
    #*********** 3 ******************#
    
    coh264Sig    = pyqtSignal(bool)
    compg2Sig    = pyqtSignal(bool)
    compg4Sig    = pyqtSignal(bool)
    cowvc1Sig    = pyqtSignal(bool)
    comjpgSig    = pyqtSignal(bool)
    
    #********** 4 *******************#
    
    mcpuSig = pyqtSignal(int)
    mgpuSig = pyqtSignal(int)
        
    def setCputemp(self, temp):
        if self.ctemp != temp:
            self.ctemp = temp
        self.cpuSig.emit(self.ctemp)
            
    def getCputemp(self):
        return self.ctemp
        
    def setGputemp(self, temp):
        if self.gtemp != temp:
            self.gtemp = temp
        self.gpuSig.emit(self.gtemp)
            
    def getGputemp(self):
        return self.gtemp
        
    def setTime(self, t):
        if self.time != t:
            self.time = t
        self.timeSig.emit(self.time)
            
    def getTime(self):
        return self.time
    
    def setCpuAvg(self, temp):
        if self.cpuAvg != temp:
            self.cpuAvg = temp
        self.cpuAvgSig.emit(self.cpuAvg)
            
    def getCpuAvg(self):
        return self.cpuAvg
    
    def setGpuAvg(self, temp):
        if self.gpuAvg != temp:
            self.gpuAvg = temp
        self.gpuAvgSig.emit(self.gpuAvg)
            
    def getGpuAvg(self):
        return self.gpuAvg
        
    
    def setCarm(self, arm):
        if self.carm != arm:
            self.carm = arm
        self.carmSig.emit(self.carm)
    
    def getCarm(self):
        return self.carm
    
    def setCcore(self, core):
        if self.ccore != core:
            self.ccore = core
        self.ccoreSig.emit(self.ccore)
    
    def getCcore(self):
        return self.ccore
    
    def setCh264(self, h264):
        if self.ch264 != h264:
            self.ch264 = h264
        self.ch264Sig.emit(self.ch264)
    
    def getCh264(self):
        return self.ch264
    
    def setCuart(self, uart):
        if self.cuart != uart:
            self.cuart = uart
        self.cuartSig.emit(self.cuart)
    
    def getCuart(self):
        return self.cuart
    
    def setCpwm(self, pwm):
        if self.cpwm != pwm:
            self.cpwm = pwm
        self.cpwmSig.emit(self.cpwm)
    
    def getCpwm(self):
        return self.cpwm
    
    
    def setChdmi(self, hdmi):
        if self.chdmi != hdmi:
            self.chdmi = hdmi
        self.chdmiSig.emit(self.chdmi)
    
    def getChdmi(self):
        return self.chdmi
    
    
    def setVcore(self, core):
        if self.vcore != core:
            self.vcore = core
        self.vcoreSig.emit(self.vcore)
    
    def getVcore(self):
        return self.vcore
    
    
    def setSdram_c(self, ram):
        if self.sdram_c != ram:
            self.sdram_c = ram
        self.sdram_cSig.emit(self.sdram_c)
    
    def getSdram_c(self):
        return self.sdram_c
    
    
    def setSdram_i(self, ram):
        if self.sdram_i != ram:
            self.sdram_i = ram
        self.sdram_iSig.emit(self.sdram_i)
    
    def getSdram_i(self):
        return self.sdram_i
    
    
    def setSdram_p(self, ram):
        if self.sdram_p != ram:
            self.sdram_p = ram
        self.sdram_pSig.emit(self.sdram_p)
    
    def getSdram_p(self):
        return self.sdram_p
    
    
    def setCoh264(self, h):
        if self.coh264 != h:
            self.coh264 = h
        self.coh264Sig.emit(self.coh264)
    
    def getCoh264(self):
        return self.coh264
    
    def setCompg2(self, h):
        if self.compg2 != h:
            self.compg2 = h
        self.compg2Sig.emit(self.compg2)
    
    def getCompg2(self):
        return self.compg2
    
    def setCompg4(self, h):
        if self.compg4 != h:
            self.compg4 = h
        self.compg4Sig.emit(self.compg4)
    
    def getCompg4(self):
        return self.compg4
    
    
    def setCowvc1(self, h):
        if self.cowvc1 != h:
            self.cowvc1 = h
        self.cowvc1Sig.emit(self.cowvc1)
    
    def getCowvc1(self):
        return self.cowvc1
    
        
    def setComjpg(self, h):
        if self.comjpg != h:
            self.comjpg = h
        self.comjpgSig.emit(self.comjpg)
    
    def getComjpg(self):
        return self.comjpg
 
 
         
    def setMcpu(self, u):
        if self.mcpu != u:
            self.mcpu = u
        self.mcpuSig.emit(self.mcpu)
    
    def getMcpu(self):
        return self.mcpu   
    
             
    def setMgpu(self, u):
        if self.mgpu != u:
            self.mgpu = u
        self.mgpuSig.emit(self.mgpu)
    
    def getMgpu(self):
        return self.mgpu
    
       
    @pyqtSlot()
    def sig(self):
        try:
            data = pd.read_csv("data.csv")
        except:
            pass
        
        data = pd.read_csv("data.csv")
        time = np.arange(len(data['cpu'].tolist()))
        cavg = format(np.mean(data['cpu'].to_numpy()), '.1f')
        gavg = format(np.mean(data['gpu'].to_numpy()), '.1f')
         
                     
        self.setCputemp(data['cpu'].tolist())
        self.setGputemp(data['gpu'].tolist())               
        self.setTime(time.tolist())
        self.setCpuAvg(float(cavg))     
        self.setGpuAvg(float(gavg))       
        
        #*********************************#
        self.setCarm(data['carm'].to_numpy()[-1])
        self.setCcore(data['ccore'].to_numpy()[-1])
        self.setCh264(data['ch264'].to_numpy()[-1])
        self.setCuart(data['cuart'].to_numpy()[-1])
        self.setCpwm(data['cpwm'].to_numpy()[-1])
        self.setChdmi(data['chdmi'].to_numpy()[-1])
        
        
        vc = float(format(data['vcore'].to_numpy()[-1],'.3f'))
        vs = float(format(data['sdram_c'].to_numpy()[-1],'.3f'))
        vi = float(format(data['sdram_i'].to_numpy()[-1],'.3f'))
        vp = float(format(data['sdram_p'].to_numpy()[-1],'.3f'))

        self.setVcore(vc)
        self.setSdram_c(vs)
        self.setSdram_i(vi)
        self.setSdram_p(vp)
        
        
        self.setCoh264(data['coh264'].tolist()[-1])
        self.setCompg2(data['compg2'].tolist()[-1])
        self.setCompg4(data['compg4'].tolist()[-1])
        self.setCowvc1(data['cowvc1'].tolist()[-1])
        self.setComjpg(data['comjpg'].tolist()[-1])
        
        self.setMcpu(data['mcpu'].to_numpy()[-1])
        self.setMgpu(data['mgpu'].to_numpy()[-1])
        
                     
    sig_cpu  = pyqtProperty(list, getCputemp, notify=cpuSig)
    sig_gpu  = pyqtProperty(list, getGputemp, notify=gpuSig)
    sig_time = pyqtProperty(list, getTime, notify=timeSig)
    sig_cavg = pyqtProperty(float, getCpuAvg, notify=cpuAvgSig)
    sig_gavg = pyqtProperty(float, getGpuAvg, notify=gpuAvgSig)
    
    sig_carm    = pyqtProperty(int, getCarm, notify=carmSig)
    sig_ccore   = pyqtProperty(int, getCcore, notify=ccoreSig)
    sig_ch264   = pyqtProperty(int, getCh264, notify=ch264Sig)
    sig_cuart   = pyqtProperty(int, getCuart, notify=cuartSig)
    sig_cpwm    = pyqtProperty(int, getCpwm, notify=cpwmSig)
    sig_chdmi   = pyqtProperty(int, getChdmi, notify=chdmiSig)
    
    
    sig_vcore   = pyqtProperty(float, getVcore, notify=vcoreSig)
    sig_sdram_c = pyqtProperty(float, getSdram_c, notify=sdram_cSig)
    sig_sdram_i = pyqtProperty(float, getSdram_i, notify=sdram_iSig)
    sig_sdram_p = pyqtProperty(float, getSdram_p, notify=sdram_pSig)
    
     
    sig_coh264  = pyqtProperty(bool, getCoh264, notify=coh264Sig)
    sig_compg2  = pyqtProperty(bool, getCompg2, notify=compg2Sig)
    sig_compg4  = pyqtProperty(bool, getCompg4, notify=compg4Sig)
    sig_comjpg  = pyqtProperty(bool, getComjpg, notify=comjpgSig)
    sig_cowvc1  = pyqtProperty(bool, getCowvc1, notify=cowvc1Sig)
    
    
    sig_mcpu  = pyqtProperty(int, getMcpu, notify=mcpuSig)
    sig_mgpu  = pyqtProperty(int, getMgpu, notify=mgpuSig)
        
        
        
    """ 
Ejemplo n.º 24
0
class ToolOffsetDialog(QDialog, _HalWidgetBase):
    def __init__(self, parent=None):
        super(ToolOffsetDialog, self).__init__(parent)
        self._color = QColor(0, 0, 0, 150)
        self._state = False
        self._request_name = 'TOOLOFFSET'

        self.setWindowModality(Qt.ApplicationModal)
        self.setWindowFlags(self.windowFlags() | Qt.Tool | Qt.Dialog
                            | Qt.WindowStaysOnTopHint
                            | Qt.WindowSystemMenuHint)
        self.setMinimumSize(200, 200)
        buttonBox = QDialogButtonBox()
        buttonBox.setEnabled(False)
        STATUS.connect('not-all-homed',
                       lambda w, axis: buttonBox.setEnabled(False))
        STATUS.connect('all-homed', lambda w: buttonBox.setEnabled(True))
        STATUS.connect('state-estop', lambda w: buttonBox.setEnabled(False))
        STATUS.connect(
            'state-estop-reset', lambda w: buttonBox.setEnabled(
                STATUS.machine_is_on() and STATUS.is_all_homed()))
        for i in ('X', 'Y', 'Z'):
            b = 'button_%s' % i
            self[b] = QPushButton('Zero %s' % i)
            self[b].clicked.connect(self.zeroPress('%s' % i))
            buttonBox.addButton(self[b], 3)

        v = QVBoxLayout()
        h = QHBoxLayout()
        self._o = TOOLVIEW_WIDGET()
        self._o._hal_init()
        self.setLayout(v)
        v.addWidget(self._o)
        b = QPushButton('OK')
        b.clicked.connect(lambda: self.close())
        h.addWidget(b)
        h.addWidget(buttonBox)
        v.addLayout(h)
        self.setModal(True)

    def _hal_init(self):
        x = self.geometry().x()
        y = self.geometry().y()
        w = self.geometry().width()
        h = self.geometry().height()
        geo = '%s %s %s %s' % (x, y, w, h)
        self._default_geometry = [x, y, w, h]
        if self.PREFS_:
            self._geometry_string = self.PREFS_.getpref(
                'ToolOffsetDialog-geometry', geo, str, 'DIALOG_OPTIONS')
        else:
            self._geometry_string = 'default'
        self.topParent = self.QTVCP_INSTANCE_
        STATUS.connect('dialog-request', self._external_request)

    def _external_request(self, w, message):
        if message['NAME'] == self._request_name:
            self.load_dialog()

    # This weird code is just so we can get the axis
    # letter
    # using clicked.connect() apparently can't easily
    # add user data
    def zeroPress(self, data):
        def calluser():
            self.zeroAxis(data)

        return calluser

    def zeroAxis(self, index):
        ACTION.SET_AXIS_ORIGIN(index, 0)

    def load_dialog(self):
        STATUS.emit('focus-overlay-changed', True, 'Set Origin Offsets',
                    self._color)
        self.calculate_placement()
        self.show()
        self.exec_()
        STATUS.emit('focus-overlay-changed', False, None, None)
        record_geometry(self, 'ToolOffsetDialog-geometry')

    def calculate_placement(self):
        geometry_parsing(self, 'ToolOffsetDialog-geometry')

    # usual boiler code
    # (used so we can use code such as self[SomeDataName]
    def __getitem__(self, item):
        return getattr(self, item)

    def __setitem__(self, item, value):
        return setattr(self, item, value)

    # **********************
    # Designer properties
    # **********************

    @pyqtSlot(bool)
    def setState(self, value):
        self._state = value
        if value:
            self.show()
        else:
            self.hide()

    def getState(self):
        return self._state

    def resetState(self):
        self._state = False

    def getColor(self):
        return self._color

    def setColor(self, value):
        self._color = value

    def resetState(self):
        self._color = QColor(0, 0, 0, 150)

    state = pyqtProperty(bool, getState, setState, resetState)
    overlay_color = pyqtProperty(QColor, getColor, setColor)
Ejemplo n.º 25
0
class MainWindow(QQuickWindow):
    def __init__(self, parent = None):
        super(MainWindow, self).__init__(parent)

        self._background_color = QColor(204, 204, 204, 255)

        self.setClearBeforeRendering(False)
        self.beforeRendering.connect(self._render, type=Qt.DirectConnection)

        self._mouse_device = QtMouseDevice(self)
        self._mouse_device.setPluginId("qt_mouse")
        self._key_device = QtKeyDevice()
        self._key_device.setPluginId("qt_key")
        self._previous_focus = None

        self._app = QCoreApplication.instance()
        self._app.getController().addInputDevice(self._mouse_device)
        self._app.getController().addInputDevice(self._key_device)
        self._app.getController().getScene().sceneChanged.connect(self._onSceneChanged)
        self._preferences = Preferences.getInstance()

        self._preferences.addPreference("general/window_width", 1280)
        self._preferences.addPreference("general/window_height", 720)
        self._preferences.addPreference("general/window_left", 50)
        self._preferences.addPreference("general/window_top", 50)
        self._preferences.addPreference("general/window_state", Qt.WindowNoState)

        # Restore window geometry
        self.setWidth(int(self._preferences.getValue("general/window_width")))
        self.setHeight(int(self._preferences.getValue("general/window_height")))
        self.setPosition(int(self._preferences.getValue("general/window_left")), int(self._preferences.getValue("general/window_top")))
        # Make sure restored geometry is not outside the currently available screens
        screen_found = False
        for s in range(0, self._app.desktop().screenCount()):
            if self.geometry().intersects(self._app.desktop().availableGeometry(s)):
                screen_found = True
                break
        if not screen_found:
            self.setPosition(50,50)

        self.setWindowState(int(self._preferences.getValue("general/window_state")))
        self._mouse_x = 0
        self._mouse_y = 0

        self._viewport_rect = QRectF(0, 0, 1.0, 1.0)

        Application.getInstance().setMainWindow(self)
        self._fullscreen = False

    @pyqtSlot()
    def toggleFullscreen(self):
        if self._fullscreen:
            self.setVisibility(QQuickWindow.Windowed) # Switch back to windowed
        else:
            self.setVisibility(QQuickWindow.FullScreen) # Go to fullscreen
        self._fullscreen = not self._fullscreen

    def getBackgroundColor(self):
        return self._background_color

    def setBackgroundColor(self, color):
        self._background_color = color
        self._app.getRenderer().setBackgroundColor(color)

    backgroundColor = pyqtProperty(QColor, fget=getBackgroundColor, fset=setBackgroundColor)

    mousePositionChanged = pyqtSignal()

    @pyqtProperty(int, notify = mousePositionChanged)
    def mouseX(self):
        return self._mouse_x

    @pyqtProperty(int, notify = mousePositionChanged)
    def mouseY(self):
        return self._mouse_y

    def setViewportRect(self, rect):
        if rect != self._viewport_rect:
            self._viewport_rect = rect
            self._updateViewportGeometry(self.width() * self.devicePixelRatio(), self.height() * self.devicePixelRatio())
            self.viewportRectChanged.emit()

    viewportRectChanged = pyqtSignal()

    @pyqtProperty(QRectF, fset = setViewportRect, notify = viewportRectChanged)
    def viewportRect(self):
        return self._viewport_rect

#   Warning! Never reimplemented this as a QExposeEvent can cause a deadlock with QSGThreadedRender due to both trying
#   to claim the Python GIL.
#   def event(self, event):

    def mousePressEvent(self, event):
        super().mousePressEvent(event)
        if event.isAccepted():
            return

        if self.activeFocusItem() != None and self.activeFocusItem() != self._previous_focus:
            self.activeFocusItem().setFocus(False)

        self._previous_focus = self.activeFocusItem()
        self._mouse_device.handleEvent(event)

    def mouseMoveEvent(self, event):
        self._mouse_x = event.x()
        self._mouse_y = event.y()
        self.mousePositionChanged.emit()

        super().mouseMoveEvent(event)
        if event.isAccepted():
            return

        self._mouse_device.handleEvent(event)

    def mouseReleaseEvent(self, event):
        super().mouseReleaseEvent(event)
        if event.isAccepted():
            return
        self._mouse_device.handleEvent(event)

    def keyPressEvent(self, event):
        super().keyPressEvent(event)
        if event.isAccepted():
            return

        self._key_device.handleEvent(event)

    def keyReleaseEvent(self, event):
        super().keyReleaseEvent(event)
        if event.isAccepted():
            return

        self._key_device.handleEvent(event)

    def wheelEvent(self, event):
        super().wheelEvent(event)
        if event.isAccepted():
            return

        self._mouse_device.handleEvent(event)

    def moveEvent(self, event):
        QMetaObject.invokeMethod(self, "_onWindowGeometryChanged", Qt.QueuedConnection);

    def resizeEvent(self, event):
        super().resizeEvent(event)

        win_w = event.size().width() * self.devicePixelRatio()
        win_h = event.size().height() * self.devicePixelRatio()

        self._updateViewportGeometry(win_w, win_h)

        QMetaObject.invokeMethod(self, "_onWindowGeometryChanged", Qt.QueuedConnection);

    def hideEvent(self, event):
        Application.getInstance().windowClosed()

    def _render(self):
        renderer = self._app.getRenderer()
        view = self._app.getController().getActiveView()

        renderer.beginRendering()
        view.beginRendering()
        renderer.render()
        view.endRendering()
        renderer.endRendering()

    def _onSceneChanged(self, object):
        self.update()

    @pyqtSlot()
    def _onWindowGeometryChanged(self):
        if self.windowState() == Qt.WindowNoState:
            self._preferences.setValue("general/window_width", self.width())
            self._preferences.setValue("general/window_height", self.height())
            self._preferences.setValue("general/window_left", self.x())
            self._preferences.setValue("general/window_top", self.y())
            self._preferences.setValue("general/window_state", Qt.WindowNoState)
        elif self.windowState() == Qt.WindowMaximized:
            self._preferences.setValue("general/window_state", Qt.WindowMaximized)

    def _updateViewportGeometry(self, width, height):
        view_w = width * self._viewport_rect.width()
        view_h = height * self._viewport_rect.height()

        for camera in self._app.getController().getScene().getAllCameras():
            camera.setViewportSize(view_w, view_h)
            camera.setWindowSize(width, height)
            proj = Matrix()
            if camera.isPerspective():
                proj.setPerspective(30, view_w / view_h, 1, 500)
            else:
                proj.setOrtho(-view_w / 2, view_w / 2, -view_h / 2, view_h / 2, -500, 500)
            camera.setProjectionMatrix(proj)

        self._app.getRenderer().setViewportSize(view_w, view_h)
        self._app.getRenderer().setWindowSize(width, height)
Ejemplo n.º 26
0
class VersaProbeDialog(QDialog, _HalWidgetBase):
    def __init__(self, parent=None):
        super(VersaProbeDialog, self).__init__(parent)
        self._color = QColor(0, 0, 0, 150)
        self._state = False
        self._request_name = 'VERSAPROBE'
        self.setWindowModality(Qt.ApplicationModal)
        self.setWindowFlags(self.windowFlags() | Qt.Tool | Qt.Dialog
                            | Qt.WindowStaysOnTopHint
                            | Qt.WindowSystemMenuHint)
        self.setMinimumSize(200, 200)
        buttonBox = QDialogButtonBox(QDialogButtonBox.Ok)
        b = buttonBox.button(QDialogButtonBox.Ok)
        b.clicked.connect(lambda: self.close())
        l = QVBoxLayout()
        self._o = VersaProbe()
        self.setLayout(l)
        l.addWidget(self._o)
        l.addWidget(buttonBox)

    def _hal_init(self):
        self._o.hal_init(self.HAL_GCOMP_, self.HAL_NAME_, self.QT_OBJECT_,
                         self.QTVCP_INSTANCE_, self.PATHS_, self.PREFS_)
        x = self.geometry().x()
        y = self.geometry().y()
        w = self.geometry().width()
        h = self.geometry().height()
        geo = '%s %s %s %s' % (x, y, w, h)
        self._default_geometry = [x, y, w, h]
        if self.PREFS_:
            self._geometry_string = self.PREFS_.getpref(
                'VersaProbeDialog-geometry', geo, str, 'DIALOG_OPTIONS')
        else:
            self._geometry_string = 'default'
        self.topParent = self.QTVCP_INSTANCE_
        STATUS.connect('dialog-request', self._external_request)

    def _external_request(self, w, message):
        if message['NAME'] == self._request_name:
            self.load_dialog()

    def load_dialog(self):
        STATUS.emit('focus-overlay-changed', True, 'VersaProbe Dialog',
                    self._color)
        self.calculate_placement()
        self.show()
        self.exec_()
        STATUS.emit('focus-overlay-changed', False, None, None)
        record_geometry(self, 'VersaProbeDialog-geometry')

    def calculate_placement(self):
        geometry_parsing(self, 'VersaProbeDialog-geometry')

    # **********************
    # Designer properties
    # **********************

    @pyqtSlot(bool)
    def setState(self, value):
        self._state = value
        if value:
            self.show()
        else:
            self.hide()

    def getState(self):
        return self._state

    def resetState(self):
        self._state = False

    def getColor(self):
        return self._color

    def setColor(self, value):
        self._color = value

    def resetState(self):
        self._color = QColor(0, 0, 0, 150)

    state = pyqtProperty(bool, getState, setState, resetState)
    overlay_color = pyqtProperty(QColor, getColor, setColor)
Ejemplo n.º 27
0
class PolygonWidget(QWidget):
    """PolygonWidget(QWidget)
    
    Provides a custom widget to display a polygon with properties and slots
    that can be used to customize its appearance.
    """
    def __init__(self, parent=None):

        super(PolygonWidget, self).__init__(parent)

        self._sides = 5
        self._innerRadius = 20
        self._outerRadius = 50
        self._angle = 0

        self.createPath()

        self._innerColor = QColor(255, 255, 128)
        self._outerColor = QColor(255, 0, 128)

        self.createGradient()

    def paintEvent(self, event):

        painter = QPainter()
        painter.begin(self)
        painter.setRenderHint(QPainter.Antialiasing)
        painter.setBrush(QBrush(QColor(192, 192, 255)))
        painter.draw_rect(event.rect())

        painter.translate(self.width() / 2.0, self.height() / 2.0)
        painter.rotate(self._angle)
        painter.setBrush(QBrush(self.gradient))
        painter.draw_path(self.path)
        painter.end()

    def sizeHint(self):

        return QSize(2 * self._outerRadius + 20, 2 * self._outerRadius + 20)

    def createPath(self):

        self.path = QPainterPath()
        angle = 2 * math.pi / self._sides
        self.path.moveTo(self._outerRadius, 0)
        for step in range(1, self._sides + 1):
            self.path.lineTo(
                self._innerRadius * math.cos((step - 0.5) * angle),
                self._innerRadius * math.sin((step - 0.5) * angle))
            self.path.lineTo(self._outerRadius * math.cos(step * angle),
                             self._outerRadius * math.sin(step * angle))
        self.path.closeSubpath()

    def createGradient(self):

        center = QPointF(0, 0)
        self.gradient = QRadialGradient(center, self._outerRadius, center)
        self.gradient.setColorAt(0.5, QColor(self._innerColor))
        self.gradient.setColorAt(1.0, QColor(self._outerColor))

    # The angle property is implemented using the getAngle() and setAngle()
    # methods.

    def getAngle(self):
        return self._angle

    # The setAngle() setter method is also a slot.
    @pyqtSlot(int)
    def setAngle(self, angle):
        self._angle = min(max(0, angle), 360)
        self.update()

    angle = pyqtProperty(int, getAngle, setAngle)

    # The innerRadius property is implemented using the getInnerRadius() and
    # setInnerRadius() methods.

    def getInnerRadius(self):
        return self._innerRadius

    # The setInnerRadius() setter method is also a slot.
    @pyqtSlot(int)
    def setInnerRadius(self, radius):
        self._innerRadius = radius
        self.createPath()
        self.createGradient()
        self.update()

    innerRadius = pyqtProperty(int, getInnerRadius, setInnerRadius)

    # The outerRadius property is implemented using the getOuterRadius() and
    # setOuterRadius() methods.

    def getOuterRadius(self):
        return self._outerRadius

    # The setOuterRadius() setter method is also a slot.
    @pyqtSlot(int)
    def setOuterRadius(self, radius):
        self._outerRadius = radius
        self.createPath()
        self.createGradient()
        self.update()

    outerRadius = pyqtProperty(int, getOuterRadius, setOuterRadius)

    # The numberOfSides property is implemented using the getNumberOfSides()
    # and setNumberOfSides() methods.

    def getNumberOfSides(self):
        return self._sides

    # The setNumberOfSides() setter method is also a slot.
    @pyqtSlot(int)
    def setNumberOfSides(self, sides):
        self._sides = max(3, sides)
        self.createPath()
        self.update()

    numberOfSides = pyqtProperty(int, getNumberOfSides, setNumberOfSides)

    # The innerColor property is implemented using the getInnerColor() and
    # setInnerColor() methods.

    def getInnerColor(self):
        return self._innerColor

    def setInnerColor(self, color):
        self._innerColor = max(3, color)
        self.createGradient()
        self.update()

    innerColor = pyqtProperty(QColor, getInnerColor, setInnerColor)

    # The outerColor property is implemented using the getOuterColor() and
    # setOuterColor() methods.

    def getOuterColor(self):
        return self._outerColor

    def setOuterColor(self, color):
        self._outerColor = color
        self.createGradient()
        self.update()

    outerColor = pyqtProperty(QColor, getOuterColor, setOuterColor)
Ejemplo n.º 28
0
class StateLED(LED):
    def __init__(self, parent=None):
        super(StateLED, self).__init__(parent)
        self.has_hal_pins = False
        self.setState(False)

        self.is_estopped = False
        self.is_on = False
        self.is_homed = False
        self.is_idle = False
        self.is_paused = False
        self.invert_state = False
        self.is_flood = False
        self.is_mist = False
        self.is_block_delete = False
        self.is_optional_stop = False
        self.is_joint_homed = False
        self.is_limits_overridden = False

        self.joint_number = 0

    def _hal_init(self):
        def only_false(data):
            if data:
                return
            self._flip_state(False)

        if self.is_estopped:
            STATUS.connect('state-estop', lambda w: self._flip_state(True))
            STATUS.connect('state-estop-reset',
                           lambda w: self._flip_state(False))
        elif self.is_on:
            STATUS.connect('state-on', lambda w: self._flip_state(True))
            STATUS.connect('state-off', lambda w: self._flip_state(False))
        elif self.is_homed:
            STATUS.connect('all-homed', lambda w: self._flip_state(True))
            STATUS.connect('not-all-homed',
                           lambda w, axis: self._flip_state(False))
        elif self.is_idle:
            STATUS.connect('interp-idle', lambda w: self._flip_state(True))
            STATUS.connect('interp-run', lambda w: self._flip_state(False))
        elif self.is_paused:
            STATUS.connect('program-pause-changed',
                           lambda w, data: self._flip_state(data))
        elif self.is_flood:
            STATUS.connect('flood-changed',
                           lambda w, data: self._flip_state(data))
        elif self.is_mist:
            STATUS.connect('mist-changed',
                           lambda w, data: self._flip_state(data))
        elif self.is_block_delete:
            STATUS.connect('block-delete-changed',
                           lambda w, data: self._flip_state(data))
        elif self.is_optional_stop:
            STATUS.connect('optional-stop-changed',
                           lambda w, data: self._flip_state(data))
        elif self.is_joint_homed:
            STATUS.connect('homed', lambda w, data: self.joint_homed(data))
            STATUS.connect('not-all-homed',
                           lambda w, data: self.joints_unhomed(data))
        elif self.is_limits_overridden:
            STATUS.connect('override-limits-changed',
                           self.check_override_limits)
            STATUS.connect('hard-limits-tripped',
                           lambda w, data: only_false(data))

    def _flip_state(self, data):
        if self.invert_state:
            data = not data
        self.change_state(data)

    def joint_homed(self, joint):
        if int(joint) == self.joint_number:
            self._flip_state(True)

    def joints_unhomed(self, jlist):
        if str(self.joint_number) in jlist:
            self._flip_state(False)

    def check_override_limits(self, w, state, data):
        for i in data:
            if i == 1:
                self._flip_state(True)
                return
        self._flip_state(False)

    #########################################################################
    # This is how designer can interact with our widget properties.
    # designer will show the pyqtProperty properties in the editor
    # it will use the get set and reset calls to do those actions
    #
    # _toggle_properties makes it so we can only select one option
    ########################################################################

    def _toggle_properties(self, picked):
        data = ('is_paused', 'is_estopped', 'is_on', 'is_idle', 'is_homed',
                'is_flood', 'is_mist', 'is_block_delete', 'is_optional_stop',
                'is_joint_homed', 'is_limits_overridden')

        for i in data:
            if not i == picked:
                self[i + '_status'] = False


# property getter/setters

# invert status

    def set_invert_state(self, data):
        self.invert_state = data

    def get_invert_state(self):
        return self.invert_state

    def reset_invert_state(self):
        self.invert_state = False

    # machine is paused status
    def set_is_paused(self, data):
        self.is_paused = data
        if data:
            self._toggle_properties('is_paused')

    def get_is_paused(self):
        return self.is_paused

    def reset_is_paused(self):
        self.is_paused = False

    # machine is estopped status
    def set_is_estopped(self, data):
        self.is_estopped = data
        if data:
            self._toggle_properties('is_estopped')

    def get_is_estopped(self):
        return self.is_estopped

    def reset_is_estopped(self):
        self.is_estopped = False

    # machine is on status
    def set_is_on(self, data):
        self.is_on = data
        if data:
            self._toggle_properties('is_on')

    def get_is_on(self):
        return self.is_on

    def reset_is_on(self):
        self.is_on = False

    # machine is idle status
    def set_is_idle(self, data):
        self.is_idle = data
        if data:
            self._toggle_properties('is_idle')

    def get_is_idle(self):
        return self.is_idle

    def reset_is_idle(self):
        self.is_idle = False

    # machine_is_homed status
    def set_is_homed(self, data):
        self.is_homed = data
        if data:
            self._toggle_properties('is_homed')

    def get_is_homed(self):
        return self.is_homed

    def reset_is_homed(self):
        self.is_homed = False

    # machine is_flood status
    def set_is_flood(self, data):
        self.is_flood = data
        if data:
            self._toggle_properties('is_flood')

    def get_is_flood(self):
        return self.is_flood

    def reset_is_flood(self):
        self.is_flood = False

    # machine is_mist status
    def set_is_mist(self, data):
        self.is_mist = data
        if data:
            self._toggle_properties('is_mist')

    def get_is_mist(self):
        return self.is_mist

    def reset_is_mist(self):
        self.is_mist = False

    # machine_is_block_delete status
    def set_is_block_delete(self, data):
        self.is_block_delete = data
        if data:
            self._toggle_properties('is_block_delete')

    def get_is_block_delete(self):
        return self.is_block_delete

    def reset_is_block_delete(self):
        self.is_block_delete = False

    # machine_is_optional_stop status
    def set_is_optional_stop(self, data):
        self.is_optional_stop = data
        if data:
            self._toggle_properties('is_optional_stop')

    def get_is_optional_stop(self):
        return self.is_optional_stop

    def reset_is_optional_stop(self):
        self.is_optional_stop = False

    # machine_is_joint_homed status
    def set_is_joint_homed(self, data):
        self.is_joint_homed = data
        if data:
            self._toggle_properties('is_joint_homed')

    def get_is_joint_homed(self):
        return self.is_joint_homed

    def reset_is_joint_homed(self):
        self.is_joint_homed = False

    # machine_is_limits_overridden status
    def set_is_limits_overridden(self, data):
        self.is_limits_overridden = data
        if data:
            self._toggle_properties('is_limits_overridden')

    def get_is_limits_overridden(self):
        return self.is_limits_overridden

    def reset_is_limits_overridden(self):
        self.is_limits_overridden = False

    # Non bool

    # machine_joint_number status
    def set_joint_number(self, data):
        self.joint_number = data

    def get_joint_number(self):
        return self.joint_number

    def reset_joint_number(self):
        self.joint_number = 0

    # designer will show these properties in this order:
    # BOOL
    invert_state_status = pyqtProperty(bool, get_invert_state,
                                       set_invert_state, reset_invert_state)
    is_paused_status = pyqtProperty(bool, get_is_paused, set_is_paused,
                                    reset_is_paused)
    is_estopped_status = pyqtProperty(bool, get_is_estopped, set_is_estopped,
                                      reset_is_estopped)
    is_on_status = pyqtProperty(bool, get_is_on, set_is_on, reset_is_on)
    is_idle_status = pyqtProperty(bool, get_is_idle, set_is_idle,
                                  reset_is_idle)
    is_homed_status = pyqtProperty(bool, get_is_homed, set_is_homed,
                                   reset_is_homed)
    is_flood_status = pyqtProperty(bool, get_is_flood, set_is_flood,
                                   reset_is_flood)
    is_mist_status = pyqtProperty(bool, get_is_mist, set_is_mist,
                                  reset_is_mist)
    is_block_delete_status = pyqtProperty(bool, get_is_block_delete,
                                          set_is_block_delete,
                                          reset_is_block_delete)
    is_optional_stop_status = pyqtProperty(bool, get_is_optional_stop,
                                           set_is_optional_stop,
                                           reset_is_optional_stop)
    is_joint_homed_status = pyqtProperty(bool, get_is_joint_homed,
                                         set_is_joint_homed,
                                         reset_is_joint_homed)
    is_limits_overridden_status = pyqtProperty(bool, get_is_limits_overridden,
                                               set_is_limits_overridden,
                                               reset_is_limits_overridden)

    # NON BOOL
    joint_number_status = pyqtProperty(int, get_joint_number, set_joint_number,
                                       reset_joint_number)

    # boilder code
    def __getitem__(self, item):
        return getattr(self, item)

    def __setitem__(self, item, value):
        return setattr(self, item, value)
Ejemplo n.º 29
0
class BubbleLabel(QWidget):

    BackgroundColor = QColor(195, 195, 195)
    BorderColor = QColor(150, 150, 150)

    def __init__(self, *args, **kwargs):
        text = kwargs.pop("text", "")
        super(BubbleLabel, self).__init__(*args, **kwargs)
        # 设置无边框置顶
        self.setWindowFlags(Qt.Window | Qt.Tool | Qt.FramelessWindowHint
                            | Qt.WindowStaysOnTopHint
                            | Qt.X11BypassWindowManagerHint)
        # 设置最小宽度和高度
        self.setMinimumWidth(200)
        self.setMinimumHeight(48)
        self.setAttribute(Qt.WA_TranslucentBackground, True)
        layout = QVBoxLayout(self)
        # 左上右下的边距(下方16是因为包括了三角形)
        layout.setContentsMargins(8, 8, 8, 16)
        self.label = QLabel(self)
        layout.addWidget(self.label)
        self.setText(text)
        # 获取屏幕高宽
        self._desktop = QApplication.instance().desktop()

    def setText(self, text):
        self.label.setText(text)

    def text(self):
        return self.label.text()

    def stop(self):
        self.hide()
        self.animationGroup.stop()
        self.close()

    def show(self):
        super(BubbleLabel, self).show()
        # 窗口开始位置
        startPos = QPoint(
            self._desktop.screenGeometry().width() - self.width() - 100,
            self._desktop.availableGeometry().height() - self.height())
        endPos = QPoint(
            self._desktop.screenGeometry().width() - self.width() - 100,
            self._desktop.availableGeometry().height() - self.height() * 3 - 5)
        print(startPos, endPos)
        self.move(startPos)
        # 初始化动画
        self.initAnimation(startPos, endPos)

    def initAnimation(self, startPos, endPos):
        # 透明度动画
        opacityAnimation = QPropertyAnimation(self, b"opacity")
        opacityAnimation.setStartValue(1.0)
        opacityAnimation.setEndValue(0.0)
        # 设置动画曲线
        opacityAnimation.setEasingCurve(QEasingCurve.InQuad)
        opacityAnimation.setDuration(4000)  # 在4秒的时间内完成
        # 往上移动动画
        moveAnimation = QPropertyAnimation(self, b"pos")
        moveAnimation.setStartValue(startPos)
        moveAnimation.setEndValue(endPos)
        moveAnimation.setEasingCurve(QEasingCurve.InQuad)
        moveAnimation.setDuration(5000)  # 在5秒的时间内完成
        # 并行动画组(目的是让上面的两个动画同时进行)
        self.animationGroup = QParallelAnimationGroup(self)
        self.animationGroup.addAnimation(opacityAnimation)
        self.animationGroup.addAnimation(moveAnimation)
        self.animationGroup.finished.connect(self.close)  # 动画结束时关闭窗口
        self.animationGroup.start()

    def paintEvent(self, event):
        super(BubbleLabel, self).paintEvent(event)
        painter = QPainter(self)
        painter.setRenderHint(QPainter.Antialiasing)  # 抗锯齿

        rectPath = QPainterPath()  # 圆角矩形
        triPath = QPainterPath()  # 底部三角形

        height = self.height() - 8  # 往上偏移8
        rectPath.addRoundedRect(QRectF(0, 0, self.width(), height), 5, 5)
        x = self.width() / 5 * 4
        triPath.moveTo(x, height)  # 移动到底部横线4/5处
        # 画三角形
        triPath.lineTo(x + 6, height + 8)
        triPath.lineTo(x + 12, height)

        rectPath.addPath(triPath)  # 添加三角形到之前的矩形上

        # 边框画笔
        painter.setPen(
            QPen(self.BorderColor, 1, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin))
        # 背景画刷
        painter.setBrush(self.BackgroundColor)
        # 绘制形状
        painter.drawPath(rectPath)
        # 三角形底边绘制一条线保证颜色与背景一样
        painter.setPen(
            QPen(self.BackgroundColor, 1, Qt.SolidLine, Qt.RoundCap,
                 Qt.RoundJoin))
        painter.drawLine(x, height, x + 12, height)

    def windowOpacity(self):
        return super(BubbleLabel, self).windowOpacity()

    def setWindowOpacity(self, opacity):
        super(BubbleLabel, self).setWindowOpacity(opacity)

    # 由于opacity属性不在QWidget中需要重新定义一个
    opacity = pyqtProperty(float, windowOpacity, setWindowOpacity)
Ejemplo n.º 30
0
class Command(QQuickItem):
    def __init__(self, parent=None):
        super(QQuickItem, self).__init__(parent)

        self._serial = 0

        self.c = linuxcnc.command()
        self.setSerial(self.c.serial)

    @pyqtSlot()
    def abort(self):
        self.c.abort()

    @pyqtSlot(int, name="auto")
    def auto1(self, param1):
        self.c.auto(param1)

    @pyqtSlot(int, int, name="auto")
    def auto2(self, param1, param2):
        self.c.auto(param1, param2)

    @pyqtSlot(int)
    def brake(self, param1):
        self.c.brake(param1)

    @pyqtSlot(int)
    def debug(self, param1):
        self.c.debug(param1)

    @pyqtSlot(float)
    def feedrate(self, param1):
        self.c.feedrate(param1)

    @pyqtSlot(int)
    def flood(self, param1):
        self.c.flood(param1)

    @pyqtSlot(int)
    def home(self, param1):
        self.c.home(param1)

    @pyqtSlot(int, int, name="jog")
    def jog1(self, param1, param2):
        self.c.jog(param1, param2)

    @pyqtSlot(int, int, int, name="jog")
    def jog2(self, param1, param2, param3):
        self.c.jog(param1, param2, param3)

    @pyqtSlot(int, int, int, int, name="jog")
    def jog3(self, param1, param2, param3, param4):
        self.c.jog(param1, param2, param3, param4)

    @pyqtSlot()
    def loadToolTable(self):
        self.c.load_tool_table()

    @pyqtSlot(float)
    def maxvel(self, param1):
        self.c.maxvel(param1)

    @pyqtSlot('QString')
    def mdi(self, param1):
        self.c.mdi(param1)

    @pyqtSlot(int)
    def mist(self, param1):
        self.c.mist(param1)

    @pyqtSlot()
    def overrideLimits(self):
        self.c.override_limits()

    @pyqtSlot('QString')
    def programOpen(self, param1):
        self.c.program_open(param1)

    @pyqtSlot()
    def resetInterpreter(self):
        self.c.reset_interpreter()

    @pyqtSlot(int)
    def setAdaptiveFeed(self, param1):
        self.c.set_adaptive_feed(param1)

    @pyqtSlot(int, float)
    def setAnalogOutput(self, param1, param2):
        self.c.set_analog_output(param1, param2)

    @pyqtSlot(int)
    def setBlockDelete(self, param1):
        self.c.set_block_delete(param1)

    @pyqtSlot(int, int)
    def setDigitalOutput(self, param1, param2):
        self.c.set_digital_output(param1, param2)

    @pyqtSlot(int)
    def setFeedHold(self, param1):
        self.c.set_feed_hold(param1)

    @pyqtSlot(int)
    def setFeedOverride(self, param1):
        self.c.set_feed_override(param1)

    @pyqtSlot(int, float)
    def setMaxLimit(self, param1, param2):
        self.c.set_max_limit(param1, param2)

    @pyqtSlot(int, float)
    def setMinLimit(self, param1, param2):
        self.c.set_min_limit(param1, param2)

    @pyqtSlot(int)
    def setOptionalStop(self, param1):
        self.c.set_option_stop(param1)

    @pyqtSlot(int)
    def setSpindleOverride(self, param1):
        self.c.set_spindle_override(param1)

    @pyqtSlot(int)
    def spindle(self, param1):
        self.c.spindle(param1)

    @pyqtSlot(float)
    def spindleoverride(self, param1):
        self.c.spindleoverride(param1)

    @pyqtSlot(int)
    def state(self, param1):
        self.c.state(param1)

    @pyqtSlot(int)
    def teleopEnabled(self, param1):
        self.c.teleop_enable(param1)

    @pyqtSlot(float, float, float, name="teleopVector")
    def teleopVector1(self, param1, param2, param3):
        self.c.teleop_vector(param1, param2, param3)

    @pyqtSlot(float, float, float, float, name="teleopVector")
    def teleopVector2(self, param1, param2, param3, param4):
        self.c.teleop_vector(param1, param2, param3, param4)

    @pyqtSlot(float, float, float, float, float, name="teleopVector")
    def teleopVector3(self, param1, param2, param3, param4, param5):
        self.c.teleop_vector(param1, param2, param3, param4, param5)

    @pyqtSlot(float, float, float, float, float, float, name="teleopVector")
    def teleopVector4(self, param1, param2, param3, param4, param5, param6):
        self.c.teleop_vector(param1, param2, param3, param4, param5, param6)

    @pyqtSlot(int, float, float, float, float, float, int)
    def toolOffset(self, param1, param2, param3, param4, param5, param6,
                   param7):
        self.c.tool_offset(param1, param2, param3, param4, param5, param6)

    @pyqtSlot(int)
    def trajMode(self, param1):
        self.c.traj_mode(param1)

    @pyqtSlot(int)
    def unhome(self, param1):
        self.c.unhome(param1)

    @pyqtSlot(name="waitComplete")
    def waitComplete1(self):
        self.c.wait_complete()

    @pyqtSlot(float, name="waitComplete")
    def waitComplete2(self, param1):
        self.c.wait_complete(param1)

    # serial Property
    serialChanged = pyqtSignal(int)

    def getSerial(self):
        return self._serial

    def setSerial(self, serial):
        if (self._serial != serial):
            self._serial = serial
            self.serialChanged.emit(self._serial)

    serial = pyqtProperty(int, getSerial, notify=serialChanged)
Ejemplo n.º 31
0
class PandaPrimitiveWidget(QFrame):
    # static variables
    status_label_dict = {
        pp.PandaPrimitiveStatus.NEUTRAL: '',
        pp.PandaPrimitiveStatus.READY: pp.PandaPrimitiveStatus.READY.name,
        pp.PandaPrimitiveStatus.ERROR: pp.PandaPrimitiveStatus.ERROR.name,
        pp.PandaPrimitiveStatus.EXECUTING:
        pp.PandaPrimitiveStatus.EXECUTING.name,
        pp.PandaPrimitiveStatus.REVERTING:
        pp.PandaPrimitiveStatus.REVERTING.name,
        pp.PandaPrimitiveStatus.EXECUTED: ''
    }
    font = QFont()
    font.setBold(True)
    font.setPointSize(10)

    sizePolicy = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)

    def __init__(self, parent, panda_primitive):
        super(PandaPrimitiveWidget, self).__init__(parent)
        self.initUI(panda_primitive)

    def initUI(self, panda_primitive):
        # Create widget subcomponents
        self.primitive = panda_primitive
        self.primitive_label = QLabel()

        self.status_label = QLabel(
            str(PandaPrimitiveWidget.status_label_dict[
                panda_primitive.status]))
        self.status_label.setAlignment(Qt.AlignCenter)

        # Fetch fitting icon for primitive
        primitive_icon_path = os.path.join(
            rospkg.RosPack().get_path('panda_pbd'), 'resources',
            panda_primitive.__class__.__name__ + '.png')

        primitive_image = QPixmap(primitive_icon_path)
        self.primitive_label.setPixmap(primitive_image)

        # Add vertical layout
        layout = QVBoxLayout(self)
        layout.addWidget(self.primitive_label)
        layout.addWidget(self.status_label)

        self.setSizePolicy(PandaPrimitiveWidget.sizePolicy)

        # Beautify QFrame and Color
        self.setFrameShape(QFrame.Panel)
        self.setFrameShadow(QFrame.Raised)
        self.setLineWidth(2)

        self.status_label.setFont(PandaPrimitiveWidget.font)

        # Animation
        self.animation = QPropertyAnimation(self, 'background_color')
        self.animation.setDuration(2000)  # in ms
        self.animation.setLoopCount(-1)
        self.animation.setStartValue(QColor('ghostwhite'))
        self.animation.setEndValue(QColor('ghostwhite'))
        self.animation.setKeyValueAt(0.5, QColor('cornflowerblue'))

        self.setAutoFillBackground(True)
        self.setPalette(gray_palette)

    def get_background_color(self):
        return self.palette().color(QPalette.Background)

    def set_background_color(self, color):
        palette = QPalette()
        palette.setColor(QPalette.Background, color)
        self.setPalette(palette)

    background_color = pyqtProperty(QColor, get_background_color,
                                    set_background_color)

    def sizeHint(self):
        return QSize(PRIMITIVE_WIDTH, PRIMITIVE_HEIGHT)

    def updateWidget(self):
        if self.primitive.status == pp.PandaPrimitiveStatus.EXECUTING or \
                self.primitive.status == pp.PandaPrimitiveStatus.REVERTING:
            self.animation.start()
        elif self.primitive.status == pp.PandaPrimitiveStatus.ERROR:
            self.animation.stop()
            self.setPalette(error_palette)
        else:
            self.animation.stop()
            if self.primitive.status == pp.PandaPrimitiveStatus.EXECUTED:
                self.setPalette(executed_primitive_palette)
            elif self.primitive.status == pp.PandaPrimitiveStatus.READY:
                self.setPalette(white_palette)
            else:
                self.setPalette(gray_palette)

        self.status_label.setText(
            str(PandaPrimitiveWidget.status_label_dict[self.primitive.status]))
        self.update()
Ejemplo n.º 32
0
class StatusImageSwitcher(ImageSwitcher):

    def __init__(self, parent=None):
        super(StatusImageSwitcher, self).__init__(parent)
        self._imagePath = [os.path.join(self.IMAGEDIR,'applet-critical.png'),
                    os.path.join(self.IMAGEDIR,'spindle_ccw.gif'),
                    os.path.join(self.IMAGEDIR,'spindle_cw.gif')]
        self.spindle = True
        self.all_homed = False
        self.hard_limits = False
        self._last_limit = []
        for i in range(0,len(INFO.AVAILABLE_JOINTS)):
            self._last_limit.append([0,0])

    def _hal_init(self):
        if self.spindle:
            STATUS.connect('spindle-control-changed', lambda w, b, d: self.switch_on_spindle(b,d))
        elif self.all_homed:
            STATUS.connect('not-all-homed', lambda w, data: self.switch_on_homed(0))
            STATUS.connect('all-homed', lambda w: self.switch_on_homed(1))
        elif self.hard_limits:
            STATUS.connect('hard-limits-tripped', lambda w, data, group: self.switch_on_hard_limits(data, group))

    def switch_on_spindle(self, b, data):
        if data <0: data= 2
        self.set_image_number(data)

    def switch_on_homed(self, data):
        if not data <0:
            self.set_image_number(data)

    def switch_on_hard_limits(self, data, group):
        if not data:
            # tripped
            self.set_image_number(0)
        elif (len(self._imagePath)) == 2:
            #print 'bool images'
            self.set_image_number(1)
        elif (len(self._imagePath)-1) == (len(INFO.AVAILABLE_JOINTS)):
            #print 'per joint limts images', self._last_limit, group
            for i in range(0,len(INFO.AVAILABLE_JOINTS)):
                if group[i] == self._last_limit[i]:
                    pass
                elif group[i] == [0,0]:
                    pass
                else:
                    self.set_image_number(i+1)
                    break
        elif (len(self._imagePath)-1) == (len(INFO.AVAILABLE_JOINTS) * 2):
            pass
            #print 'per joint and per end limts images'
        self._last_limit = group

    #########################################################################
    # This is how designer can interact with our widget properties.
    # designer will show the pyqtProperty properties in the editor
    # it will use the get set and reset calls to do those actions
    #
    # _toggle_properties makes it so we can only select one option
    ########################################################################

    def _toggle_properties(self, picked):
        data = ('spindle','all_homed', 'hard_limits' )

        for i in data:
            if not i == picked:
                self['watch_'+i] = False

# property getter/setters

    # machine_spindle status
    def set_spindle(self, data):
        self.spindle = data
        if data:
            self._toggle_properties('spindle')
    def get_spindle(self):
        return self.spindle
    def reset_spindle(self):
        self.spindle = False
    watch_spindle = pyqtProperty(bool, get_spindle, set_spindle, reset_spindle)

    # machine_homed status
    def set_homed(self, data):
        self.all_homed = data
        if data:
            self._toggle_properties('all_homed')
    def get_homed(self):
        return self.all_homed
    def reset_homed(self):
        self.all_homed = False
    watch_all_homed = pyqtProperty(bool, get_homed, set_homed, reset_homed)

    # machine_limits status
    def set_limits(self, data):
        self.hard_limits = data
        if data:
            self._toggle_properties('hard_limits')
    def get_limits(self):
        return self.hard_limits
    def reset_limits(self):
        self.hard_limits = False
    watch_hard_limits = pyqtProperty(bool, get_limits, set_limits, reset_limits)

    ##############################
    # required class boiler code #
    ##############################

    def __getitem__(self, item):
        return getattr(self, item)
    def __setitem__(self, item, value):
        return setattr(self, item, value)
Ejemplo n.º 33
0
class StatusAdjustmentBar(HAdjustmentBar, _HalWidgetBase):
    def __init__(self, parent=None):
        super(StatusAdjustmentBar, self).__init__(parent)
        self._block_signal = False
        self.rapid = True
        self.feed = False
        self.spindle = False
        self.jograte = False
        self.jograte_angular = False
        self.maxv = False
        self.texttemplate = 'Value =  %s'

    # self.PREFS_
    # self.HAL_NAME_
    # comes from base class
    def _hal_init(self):

        # if we build the widget before now, designer options are not
        # taken account
        self.buildWidget()

        # when estopped disable buttons
        STATUS.connect('state-estop', lambda w: self.setEnabled(False))
        STATUS.connect('state-estop-reset', lambda w: self.setEnabled(True))

        # set options
        if self.rapid:
            STATUS.connect('rapid-override-changed',
                           lambda w, data: self.setValue(data))
        elif self.feed:
            STATUS.connect('feed-override-changed',
                           lambda w, data: self.setValue(data))
            self.setMaximum(int(INFO.MAX_FEED_OVERRIDE))
        elif self.spindle:
            STATUS.connect('spindle-override-changed',
                           lambda w, data: self.setValue(data))
            self.setMaximum(int(INFO.MAX_SPINDLE_OVERRIDE))
            self.setMinimum(int(INFO.MIN_SPINDLE_OVERRIDE))
        elif self.jograte:
            STATUS.connect('jograte-changed',
                           lambda w, data: self.setValue(data))
            self.setMaximum(int(INFO.MAX_LINEAR_JOG_VEL))
        elif self.jograte_angular:
            STATUS.connect('jograte-angular-changed',
                           lambda w, data: self.setValue(data))
            print int(INFO.MAX_ANGULAR_JOG_VEL)
            self.setMaximum(int(INFO.MAX_ANGULAR_JOG_VEL))
        elif self.maxv:
            STATUS.connect('max-velocity-override-changed',
                           lambda w, data: self.setValue(data))
            self.setMaximum(int(INFO.MAX_TRAJ_VELOCITY))
        else:
            LOG.error('{} : no option recognised'.format(self.HAL_NAME_))

        # If there is a preference file object use it to load the hi/low toggle points
        if self.PREFS_:
            self.hi_value = self.PREFS_.getpref(self.HAL_NAME_ + '-hi-value',
                                                75, int,
                                                'SCREEN_CONTROL_LAST_SETTING')
            self.low_value = self.PREFS_.getpref(
                self.HAL_NAME_ + '-low-value', 25, int,
                'SCREEN_CONTROL_LAST_SETTING')

        # connect a signal and callback function to the button
        self.valueChanged.connect(self._action)

    # when qtvcp closes this gets called
    def closing_cleanup__(self):
        if self.PREFS_:
            LOG.debug('Saving {} data to file.'.format(self.HAL_NAME_))
            self.PREFS_.putpref(self.HAL_NAME_ + '-hi-value', self.hi_value,
                                int, 'SCREEN_CONTROL_LAST_SETTING')
            self.PREFS_.putpref(self.HAL_NAME_ + '-low-value', self.low_value,
                                int, 'SCREEN_CONTROL_LAST_SETTING')

    def _action(self, value):
        if self.rapid:
            ACTION.SET_RAPID_RATE(value)
        elif self.feed:
            ACTION.SET_FEED_RATE(value)
        elif self.spindle:
            ACTION.SET_SPINDLE_RATE(value)
        elif self.jograte:
            ACTION.SET_JOG_RATE(value)
        elif self.jograte_angular:
            ACTION.SET_JOG_RATE_ANGULAR(value)
        elif self.maxv:
            ACTION.SET_MAX_VELOCITY_RATE(value)
        else:
            LOG.error('{} no action recognised'.format(self.HAL_NAME_))

    #########################################################################
    # This is how designer can interact with our widget properties.
    # designer will show the pyqtProperty properties in the editor
    # it will use the get set and reset calls to do those actions
    #
    # _toggle_properties makes it so we can only select one option
    ########################################################################

    def _toggle_properties(self, picked):
        data = ('rapid', 'feed', 'spindle', 'jograte', 'jograte_angular',
                'maxv')

        for i in data:
            if not i == picked:
                self[i + '_rate'] = False

    def setrapid(self, data):
        self.rapid = data
        if data:
            self._toggle_properties('rapid')

    def getrapid(self):
        return self.rapid

    def resetrapid(self):
        self.rapid = False

    def setfeed(self, data):
        self.feed = data
        if data:
            self._toggle_properties('feed')

    def getfeed(self):
        return self.feed

    def resetfeed(self):
        self.feed = False

    def setspindle(self, data):
        self.spindle = data
        if data:
            self._toggle_properties('spindle')

    def getspindle(self):
        return self.spindle

    def resetspindle(self):
        self.spindle = False

    def setjograte(self, data):
        self.jograte = data
        if data:
            self._toggle_properties('jograte')

    def getjograte(self):
        return self.jograte

    def resetjograte(self):
        self.jograte = False

    def setjograteangular(self, data):
        self.jograte_angular = data
        if data:
            self._toggle_properties('jograte_angular')

    def getjograteangular(self):
        return self.jograte_angular

    def resetjograteangular(self):
        self.jograte_angular = False

    def setmaxv(self, data):
        self.maxv = data
        if data:
            self._toggle_properties('maxv')

    def getmaxv(self):
        return self.maxv

    def resetmaxv(self):
        self.maxv = False

    def setshowtoggle(self, data):
        self.showToggleButton = data

    def getshowtoggle(self):
        return self.showToggleButton

    def resetshowtoggle(self):
        self.showToggleButton = True

    def setsettingmenu(self, data):
        self.showSettingMenu = data

    def getsettingmenu(self):
        return self.showSettingMenu

    def resetsettingmenu(self):
        self.showSettingMenu = True

    # Designer makes '\n' into '\\n' if added
    # we put fix it here so we actually get the newline
    def settexttemplate(self, data):
        data = data.replace('\\n', '\n')
        self.texttemplate = data

    def gettexttemplate(self):
        return self.texttemplate

    def resettexttemplate(self):
        self.texttemplate = 'Value = %s'

    def setsteprate(self, data):
        self.step = data

    def getsteprate(self):
        return self.step

    def resetsteprate(self):
        self.steprate = 1

    rapid_rate = pyqtProperty(bool, getrapid, setrapid, resetrapid)
    feed_rate = pyqtProperty(bool, getfeed, setfeed, resetfeed)
    spindle_rate = pyqtProperty(bool, getspindle, setspindle, resetspindle)
    jograte_rate = pyqtProperty(bool, getjograte, setjograte, resetjograte)
    jograte_angular_rate = pyqtProperty(bool, getjograteangular,
                                        setjograteangular, resetjograteangular)
    max_velocity_rate = pyqtProperty(bool, getmaxv, setmaxv, resetmaxv)
    show_toggle_button = pyqtProperty(bool, getshowtoggle, setshowtoggle,
                                      resetshowtoggle)
    show_setting_menu = pyqtProperty(bool, getsettingmenu, setsettingmenu,
                                     resetsettingmenu)

    text_template = pyqtProperty(str, gettexttemplate, settexttemplate,
                                 resettexttemplate)
    step_rate = pyqtProperty(int, getsteprate, setsteprate, resetsteprate)

    ##############################
    # required class boiler code #
    ##############################

    def __getitem__(self, item):
        return getattr(self, item)

    def __setitem__(self, item, value):
        return setattr(self, item, value)
Ejemplo n.º 34
0
class GEditor(QMainWindow, _HalWidgetBase):
    percentDone = pyqtSignal(int)

    def __init__(self, parent=None, designer=False):
        if not designer:
            parent = None
        super().__init__(parent)
        self._show_editor = True
        self.load_dialog_code = 'LOAD'
        self.save_dialog_code = 'SAVE'
        self.dialog_code = 'KEYBOARD'

        STATUS.connect('general',self.returnFromDialog)

        self.isCaseSensitive = 0

        self.setMinimumSize(QSize(300, 200))    
        self.setWindowTitle("PyQt5 editor test example") 

        # make editor
        self.editor = GcodeDisplay(self)
        self.setCentralWidget(self.editor)

        # class patch editor's function to ours
        # so we get the lines percent update
        self.editor.emit_percent = self.emit_percent

        self.editor.setReadOnly(True)
        self.editor.setModified(False)

        self.createActions()

        # Create toolbar and add action
        self.toolBar = QToolBar('File')
        self.toolBar.setObjectName('{}_toolbarfile'.format( self.objectName()))
        self.toolBar.addAction(self.newAction)
        self.toolBar.addAction(self.openAction)
        self.toolBar.addAction(self.saveAction)
        self.toolBar.addAction(self.exitAction)
        self.addToolBar(Qt.TopToolBarArea, self.toolBar)

        #self.toolBar.addSeparator()
        self.toolBarLexer = QToolBar('Lexer')
        self.toolBarLexer.setObjectName('{}_toolbarlexer'.format( self.objectName()))

        # add lexer actions
        self.toolBarLexer.addAction(self.gCodeLexerAction)
        self.toolBarLexer.addAction(self.pythonLexerAction)

        self.toolBarLexer.addSeparator()
        self.label = QLabel('''<html><head/><body><p><span style=" font-size:20pt;
                         font-weight:600;">Edit Mode</span></p></body></html>''')
        self.toolBarLexer.addWidget(self.label)

        self.addToolBar(Qt.TopToolBarArea, self.toolBarLexer)

        # Create toolbar and add action
        self.toolBarEdit = QToolBar('Edit')
        self.toolBarEdit.setObjectName('{}_toolbaredit'.format( self.objectName()))

        self.toolBarEdit.addAction(self.undoAction)
        self.toolBarEdit.addAction(self.redoAction)
        self.toolBarEdit.addSeparator()
        self.toolBarEdit.addAction(self.replaceAction)
        self.toolBarEdit.addAction(self.findAction)
        self.toolBarEdit.addAction(self.previousAction)
        self.toolBarEdit.addSeparator()
        self.toolBarEdit.addAction(self.caseAction)
        self.addToolBar(Qt.BottomToolBarArea, self.toolBarEdit)

        self.toolBarEntry = QToolBar('entry')
        self.toolBarEntry.setObjectName('{}_toolbarentry'.format( self.objectName()))

        frame = QFrame()
        box = QHBoxLayout()
        box.addWidget(self.searchText)
        box.addWidget(self.replaceText)
        box.addStretch(1)
        frame.setLayout(box)
        self.toolBarEntry.addWidget(frame)
        self.addToolBar(Qt.BottomToolBarArea, self.toolBarEntry)
        self.readOnlyMode(save = False)

    def createActions(self):
        # Create new action
        self.newAction = QAction(QIcon.fromTheme('document-new'), 'New', self)       
        self.newAction.setShortcut('Ctrl+N')
        self.newAction.setStatusTip('New document')
        self.newAction.triggered.connect(self.newCall)

        # Create open action
        self.openAction = QAction(QIcon.fromTheme('document-open'), '&Open', self)
        self.openAction.setShortcut('Ctrl+O')
        self.openAction.setStatusTip('Open document')
        self.openAction.triggered.connect(self.openCall)

        # Create save action
        self.saveAction = QAction(QIcon.fromTheme('document-save'), '&Save', self)
        self.saveAction.setShortcut('Ctrl+S')
        self.saveAction.setStatusTip('Save document')
        self.saveAction.triggered.connect(self.saveCall)

        # Create exit action
        self.exitAction = QAction(QIcon.fromTheme('application-exit'), '&Exit', self)
        self.exitAction.setShortcut('Ctrl+Q')
        self.exitAction.setToolTip('Exit Edit Mode')
        self.exitAction.setStatusTip('Exit Edit Mode')
        self.exitAction.triggered.connect(self.exitCall)

        # Create gcode lexer action
        self.gCodeLexerAction = QAction(QIcon.fromTheme('lexer.png'), '&Gcode\nLexer', self)
        self.gCodeLexerAction.setCheckable(1)
        self.gCodeLexerAction.setShortcut('Ctrl+G')
        self.gCodeLexerAction.setStatusTip('Set Gcode highlighting')
        self.gCodeLexerAction.triggered.connect(self.gcodeLexerCall)

        # Create gcode lexer action
        self.pythonLexerAction = QAction(QIcon.fromTheme('lexer.png'), '&Python\nLexer', self)
        self.pythonLexerAction.setShortcut('Ctrl+P')
        self.pythonLexerAction.setStatusTip('Set Python highlighting')
        self.pythonLexerAction.triggered.connect(self.pythonLexerCall)

        self.searchText = QLineEdit(self)
        self.searchText.setToolTip('Search Text')
        self.searchText.setStatusTip('Text to search for')
        self.searchText.installEventFilter(self)

        self.replaceText = QLineEdit(self)
        self.replaceText.setToolTip('Replace Text')
        self.replaceText.setStatusTip('Replace search text with this text')
        self.replaceText.installEventFilter(self)

        # Create new action
        self.undoAction = QAction(QIcon.fromTheme('edit-undo'), 'Undo', self)
        self.undoAction.setStatusTip('Undo')
        self.undoAction.triggered.connect(self.undoCall)

        # create redo action
        self.redoAction = QAction(QIcon.fromTheme('edit-redo'), 'Redo', self)
        self.redoAction.setStatusTip('Redo')
        self.redoAction.triggered.connect(self.redoCall)

        # create replace action
        self.replaceAction = QAction(QIcon.fromTheme('edit-find-replace'), 'Replace', self)
        self.replaceAction.setStatusTip('Replace text')
        self.replaceAction.triggered.connect(self.replaceCall)

        # create find action
        self.findAction = QAction(QIcon.fromTheme('edit-find'), 'Find', self)
        self.findAction.setStatusTip('Find next occurrence of text')
        self.findAction.triggered.connect(self.findCall)

        # create next action
        self.previousAction = QAction(QIcon.fromTheme('go-previous'), 'Find Previous', self)
        self.previousAction.setStatusTip('Find previous occurrence of text')
        self.previousAction.triggered.connect(self.previousCall)

        # create case action
        self.caseAction = QAction(QIcon.fromTheme('edit-case'), 'Aa', self)
        self.caseAction.setToolTip('Toggle Match Case')
        self.caseAction.setStatusTip('Toggle between any case and match case')
        self.caseAction.setCheckable(1)      
        self.caseAction.triggered.connect(self.caseCall)

    # catch focusIn event to pop keyboard dialog
    def eventFilter(self, obj, event):
        if event.type() == QEvent.FocusIn:
            if isinstance(obj, QLineEdit):
                # only if mouse selected
                if event.reason () == 0:
                    self.popEntry(obj)
        return super().eventFilter(obj, event)

    # callback functions built for easy class patching ##########
    # want to refrain from renaming these functions as it will break
    # any class patch user's use
    # we split them like this so a user can intercept the callback
    # but still call the original functionality

    def caseCall(self):
        self.case()
    def case(self):
        self.isCaseSensitive -=1
        self.isCaseSensitive *=-1

    def exitCall(self):
        self.exit()
    def exit(self):
        if self.editor.isModified():
            result = self.killCheck()
            if result:
                try:
                    self.editor.reload_last(None)
                except Exception as e:
                    print (e)
                self.readOnlyMode()
            return result
        return True

    def findCall(self):
        self.find()
    def find(self):
        self.editor.search(str(self.searchText.text()),
                             re=False, case=self.isCaseSensitive,
                             word=False, wrap= True, fwd=True)

    def previousCall(self):
        self.previous()
    def previous(self):
        self.editor.setCursorPosition(self.editor.getSelection()[0],
                                      self.editor.getSelection()[1])
        self.editor.search(str(self.searchText.text()),
                           re=False, case=self.isCaseSensitive,
                           word=False, wrap=True, fwd=False)

    def gcodeLexerCall(self):
        self.gcodeLexer()
    def gcodeLexer(self):
        self.editor.set_gcode_lexer()

    def nextCall(self):
        self.next()
    def next(self):
        self.editor.search(str(self.searchText.text()),
                             re=False, case=self.isCaseSensitive,
                             word=False, wrap=True, fwd=False)
        self.editor.search_Next()

    def newCall(self):
        self.new()
    def new(self):
        if self.editor.isModified():
            result = self.killCheck()
            if result:
                self.editor.new_text()
        else:
            self.editor.new_text()

    def openCall(self):
        self.open()
    def open(self):
        self.getFileName()
    def openReturn(self,f):
        ACTION.OPEN_PROGRAM(f)
        self.editor.setModified(False)

    def pythonLexerCall(self):
        self.pythonLexer()
    def pythonLexer(self):
        self.editor.set_python_lexer()

    def redoCall(self):
        self.redo()
    def redo(self):
        self.editor.redo()

    def replaceCall(self):
        self.replace()
    def replace(self):
        self.editor.replace_text(str(self.replaceText.text()))
        self.editor.search(str(self.searchText.text()),
                             re=False, case=self.isCaseSensitive,
                             word=False, wrap=True, fwd=True)

    def saveCall(self):
        self.save()
    def save(self):
        self.getSaveFileName()
    def saveReturn(self, fname):
        saved = ACTION.SAVE_PROGRAM(self.editor.text(), fname)
        if saved is not None:
            self.editor.setModified(False)
            ACTION.OPEN_PROGRAM(saved)

    def undoCall(self):
        self.undo()
    def undo(self):
        self.editor.undo()

    # helper functions ############################################

    def saveSettings(self):
        self.SETTINGS_.beginGroup("geditor-{}".format(self.objectName()))
        self.SETTINGS_.setValue('state', QVariant(self.saveState().data()))
        self.SETTINGS_.endGroup()

    def restoreSettings(self):
        # set recorded toolbar settings
        self.SETTINGS_.beginGroup("geditor-{}".format(self.objectName()))
        state = self.SETTINGS_.value('state')
        self.SETTINGS_.endGroup()
        if not state is None:
            try:
                self.restoreState(QByteArray(state))
            except Exception as e:
                print(e)
            else:
                return True
        return False

    def editMode(self):
        self.editor.setReadOnly(False)
        result = self.restoreSettings()
        check = (self.toolBar.toggleViewAction().isChecked() and
                    self.toolBarEdit.toggleViewAction().isChecked() and
                    self.toolBarEntry.toggleViewAction().isChecked() and
                    self.toolBarLexer.toggleViewAction().isChecked())
        if not check:
            self.toolBar.toggleViewAction().setChecked(False)
            self.toolBar.toggleViewAction().trigger()
        if not result:
            self.toolBarEdit.toggleViewAction().setChecked(False)
            self.toolBarEdit.toggleViewAction().trigger()
            self.toolBarEntry.toggleViewAction().setChecked(False)
            self.toolBarEntry.toggleViewAction().trigger()
            self.toolBarLexer.toggleViewAction().setChecked(False)
            self.toolBarLexer.toggleViewAction().trigger()

    def readOnlyMode(self, save = True):
        if save:
            self.saveSettings()
        self.editor.setReadOnly(True)
        self.toolBar.toggleViewAction().setChecked(True)
        self.toolBar.toggleViewAction().trigger()
        self.toolBarEdit.toggleViewAction().setChecked(True)
        self.toolBarEdit.toggleViewAction().trigger()
        self.toolBarEntry.toggleViewAction().setChecked(True)
        self.toolBarEntry.toggleViewAction().trigger()
        self.toolBarLexer.toggleViewAction().setChecked(True)
        self.toolBarLexer.toggleViewAction().trigger()


    def getFileName(self):
        mess = {'NAME':self.load_dialog_code,'ID':'%s__' % self.objectName(),
            'TITLE':'Load Editor'}
        STATUS.emit('dialog-request', mess)

    def getSaveFileName(self):
        mess = {'NAME':self.save_dialog_code,'ID':'%s__' % self.objectName(),
            'TITLE':'Save Editor'}
        STATUS.emit('dialog-request', mess)


    def popEntry(self, obj):
            mess = {'NAME':self.dialog_code,
                    'ID':'%s__' % self.objectName(),
                    'OVERLAY':False,
                    'OBJECT':obj,
                    'TITLE':'Set Entry for {}'.format(obj.objectName().upper()),
                    'GEONAME':'_{}'.format(self.dialog_code)
            }
            STATUS.emit('dialog-request', mess)
            LOG.debug('message sent:{}'.format (mess))

    # process the STATUS return message
    def returnFromDialog(self, w, message):
        if message.get('NAME') == self.load_dialog_code:
            path = message.get('RETURN')
            code = bool(message.get('ID') == '%s__'% self.objectName())
            if path and code:
                self.openReturn(path)
        elif message.get('NAME') == self.save_dialog_code:
            path = message.get('RETURN')
            code = bool(message.get('ID') == '%s__'% self.objectName())
            if path and code:
                self.saveReturn(path)
        elif message.get('NAME') == self.dialog_code:
            txt = message['RETURN']
            code = bool(message.get('ID') == '%s__'% self.objectName())
            if code and txt is not None:
                LOG.debug('message return:{}'.format (message))
                obj = message.get('OBJECT')
                obj.setText(str(txt))

    def killCheck(self):
        choice = QMessageBox.question(self, 'Warning!',
             """This file has changed since loading and
has not been saved. You will lose your changes.
Still want to proceed?""",
                                            QMessageBox.Yes | QMessageBox.No)
        if choice == QMessageBox.Yes:
            return True
        else:
            return False

    def emit_percent(self, percent):
        self.percentDone.emit(percent)

    def select_lineup(self):
        self.editor.select_lineup(None)

    def select_linedown(self):
        self.editor.select_linedown(None)

    def select_line(self, line):
        self.editor.highlight_line(None, line)

    def jump_line(self, jump):
        self.editor.jump_line(jump)

    def get_line(self):
        return self.editor.getCursorPosition()[0] +1

    def set_margin_width(self,width):
        self.editor.set_margin_width(width)

    def set_font(self, font):
        self.editor.font = font
        for i in range(0,8):
            self.editor.lexer.setFont(font,i)

    def set_background_color(self, color):
        self.editor.set_background_color(color)

    def isReadOnly(self):
        return self.editor.isReadOnly()

    # designer recognized getter/setters

    # auto_show_mdi status
    # These adjust the self.editor instance
    def set_auto_show_mdi(self, data):
        self.editor.auto_show_mdi = data
    def get_auto_show_mdi(self):
        return self.editor.auto_show_mdi
    def reset_auto_show_mdi(self):
        self.editor.auto_show_mdi = True
    auto_show_mdi_status = pyqtProperty(bool, get_auto_show_mdi, set_auto_show_mdi, reset_auto_show_mdi)

    # designer recognized getter/setters
    # auto_show_manual status
    def set_auto_show_manual(self, data):
        self.editor.auto_show_manual = data
    def get_auto_show_manual(self):
        return self.editor.auto_show_manual
    def reset_auto_show_manual(self):
        self.editor.auto_show_manual = True
    auto_show_manual_status = pyqtProperty(bool, get_auto_show_manual, set_auto_show_manual, reset_auto_show_manual)

    # designer recognized getter/setters
    # show_editor status
    def set_show_editor(self, data):
        self._show_editor = data
        if data:
            self.toolBar.show()
            self.toolBarLexer.show()
            self.toolBarEntry.show()
            self.toolBarEdit.show()
        else:
            self.toolBar.hide()
            self.toolBarLexer.hide()
            self.toolBarEntry.hide()
            self.toolBarEdit.hide()
    def get_show_editor(self):
        return self._show_editor
    def reset_show_editor(self):
        self._show_editor = True
        self.toolBar.show()
        self.toolBarLexer.show()
        self.toolBarEntry.show()
        self.toolBarEdit.show()
    show_editor = pyqtProperty(bool, get_show_editor, set_show_editor, reset_show_editor)
Ejemplo n.º 35
0
class CuraContainerStack(ContainerStack):
    def __init__(self, container_id: str) -> None:
        super().__init__(container_id)

        self._empty_instance_container = cura_empty_instance_containers.empty_container  #type: InstanceContainer

        self._empty_quality_changes = cura_empty_instance_containers.empty_quality_changes_container  #type: InstanceContainer
        self._empty_quality = cura_empty_instance_containers.empty_quality_container  #type: InstanceContainer
        self._empty_material = cura_empty_instance_containers.empty_material_container  #type: InstanceContainer
        self._empty_variant = cura_empty_instance_containers.empty_variant_container  #type: InstanceContainer

        self._containers = [
            self._empty_instance_container
            for i in range(len(_ContainerIndexes.IndexTypeMap))
        ]  #type: List[ContainerInterface]
        self._containers[
            _ContainerIndexes.QualityChanges] = self._empty_quality_changes
        self._containers[_ContainerIndexes.Quality] = self._empty_quality
        self._containers[_ContainerIndexes.Material] = self._empty_material
        self._containers[_ContainerIndexes.Variant] = self._empty_variant

        self.containersChanged.connect(self._onContainersChanged)

        import cura.CuraApplication  #Here to prevent circular imports.
        self.setMetaDataEntry(
            "setting_version",
            cura.CuraApplication.CuraApplication.SettingVersion)

    # This is emitted whenever the containersChanged signal from the ContainerStack base class is emitted.
    pyqtContainersChanged = pyqtSignal()

    ##  Set the user changes container.
    #
    #   \param new_user_changes The new user changes container. It is expected to have a "type" metadata entry with the value "user".
    def setUserChanges(self, new_user_changes: InstanceContainer) -> None:
        self.replaceContainer(_ContainerIndexes.UserChanges, new_user_changes)

    ##  Get the user changes container.
    #
    #   \return The user changes container. Should always be a valid container, but can be equal to the empty InstanceContainer.
    @pyqtProperty(InstanceContainer,
                  fset=setUserChanges,
                  notify=pyqtContainersChanged)
    def userChanges(self) -> InstanceContainer:
        return cast(InstanceContainer,
                    self._containers[_ContainerIndexes.UserChanges])

    ##  Set the quality changes container.
    #
    #   \param new_quality_changes The new quality changes container. It is expected to have a "type" metadata entry with the value "quality_changes".
    def setQualityChanges(self,
                          new_quality_changes: InstanceContainer,
                          postpone_emit=False) -> None:
        self.replaceContainer(_ContainerIndexes.QualityChanges,
                              new_quality_changes,
                              postpone_emit=postpone_emit)

    ##  Get the quality changes container.
    #
    #   \return The quality changes container. Should always be a valid container, but can be equal to the empty InstanceContainer.
    @pyqtProperty(InstanceContainer,
                  fset=setQualityChanges,
                  notify=pyqtContainersChanged)
    def qualityChanges(self) -> InstanceContainer:
        return cast(InstanceContainer,
                    self._containers[_ContainerIndexes.QualityChanges])

    ##  Set the intent container.
    #
    #   \param new_intent The new intent container. It is expected to have a "type" metadata entry with the value "intent".
    def setIntent(self,
                  new_intent: InstanceContainer,
                  postpone_emit: bool = False) -> None:
        self.replaceContainer(_ContainerIndexes.Intent,
                              new_intent,
                              postpone_emit=postpone_emit)

    ##  Get the quality container.
    #
    #   \return The intent container. Should always be a valid container, but can be equal to the empty InstanceContainer.
    @pyqtProperty(InstanceContainer,
                  fset=setIntent,
                  notify=pyqtContainersChanged)
    def intent(self) -> InstanceContainer:
        return cast(InstanceContainer,
                    self._containers[_ContainerIndexes.Intent])

    ##  Set the quality container.
    #
    #   \param new_quality The new quality container. It is expected to have a "type" metadata entry with the value "quality".
    def setQuality(self,
                   new_quality: InstanceContainer,
                   postpone_emit: bool = False) -> None:
        self.replaceContainer(_ContainerIndexes.Quality,
                              new_quality,
                              postpone_emit=postpone_emit)

    ##  Get the quality container.
    #
    #   \return The quality container. Should always be a valid container, but can be equal to the empty InstanceContainer.
    @pyqtProperty(InstanceContainer,
                  fset=setQuality,
                  notify=pyqtContainersChanged)
    def quality(self) -> InstanceContainer:
        return cast(InstanceContainer,
                    self._containers[_ContainerIndexes.Quality])

    ##  Set the material container.
    #
    #   \param new_material The new material container. It is expected to have a "type" metadata entry with the value "material".
    def setMaterial(self,
                    new_material: InstanceContainer,
                    postpone_emit: bool = False) -> None:
        self.replaceContainer(_ContainerIndexes.Material,
                              new_material,
                              postpone_emit=postpone_emit)

    ##  Get the material container.
    #
    #   \return The material container. Should always be a valid container, but can be equal to the empty InstanceContainer.
    @pyqtProperty(InstanceContainer,
                  fset=setMaterial,
                  notify=pyqtContainersChanged)
    def material(self) -> InstanceContainer:
        return cast(InstanceContainer,
                    self._containers[_ContainerIndexes.Material])

    ##  Set the variant container.
    #
    #   \param new_variant The new variant container. It is expected to have a "type" metadata entry with the value "variant".
    def setVariant(self, new_variant: InstanceContainer) -> None:
        self.replaceContainer(_ContainerIndexes.Variant, new_variant)

    ##  Get the variant container.
    #
    #   \return The variant container. Should always be a valid container, but can be equal to the empty InstanceContainer.
    @pyqtProperty(InstanceContainer,
                  fset=setVariant,
                  notify=pyqtContainersChanged)
    def variant(self) -> InstanceContainer:
        return cast(InstanceContainer,
                    self._containers[_ContainerIndexes.Variant])

    ##  Set the definition changes container.
    #
    #   \param new_definition_changes The new definition changes container. It is expected to have a "type" metadata entry with the value "definition_changes".
    def setDefinitionChanges(
            self, new_definition_changes: InstanceContainer) -> None:
        self.replaceContainer(_ContainerIndexes.DefinitionChanges,
                              new_definition_changes)

    ##  Get the definition changes container.
    #
    #   \return The definition changes container. Should always be a valid container, but can be equal to the empty InstanceContainer.
    @pyqtProperty(InstanceContainer,
                  fset=setDefinitionChanges,
                  notify=pyqtContainersChanged)
    def definitionChanges(self) -> InstanceContainer:
        return cast(InstanceContainer,
                    self._containers[_ContainerIndexes.DefinitionChanges])

    ##  Set the definition container.
    #
    #   \param new_definition The new definition container. It is expected to have a "type" metadata entry with the value "definition".
    def setDefinition(self,
                      new_definition: DefinitionContainerInterface) -> None:
        self.replaceContainer(_ContainerIndexes.Definition, new_definition)

    def getDefinition(self) -> "DefinitionContainer":
        return cast(DefinitionContainer,
                    self._containers[_ContainerIndexes.Definition])

    definition = pyqtProperty(QObject,
                              fget=getDefinition,
                              fset=setDefinition,
                              notify=pyqtContainersChanged)

    @override(ContainerStack)
    def getBottom(self) -> "DefinitionContainer":
        return self.definition

    @override(ContainerStack)
    def getTop(self) -> "InstanceContainer":
        return self.userChanges

    ##  Check whether the specified setting has a 'user' value.
    #
    #   A user value here is defined as the setting having a value in either
    #   the UserChanges or QualityChanges container.
    #
    #   \return True if the setting has a user value, False if not.
    @pyqtSlot(str, result=bool)
    def hasUserValue(self, key: str) -> bool:
        if self._containers[_ContainerIndexes.UserChanges].hasProperty(
                key, "value"):
            return True

        if self._containers[_ContainerIndexes.QualityChanges].hasProperty(
                key, "value"):
            return True

        return False

    ##  Set a property of a setting.
    #
    #   This will set a property of a specified setting. Since the container stack does not contain
    #   any settings itself, it is required to specify a container to set the property on. The target
    #   container is matched by container type.
    #
    #   \param key The key of the setting to set.
    #   \param property_name The name of the property to set.
    #   \param new_value The new value to set the property to.
    def setProperty(self,
                    key: str,
                    property_name: str,
                    property_value: Any,
                    container: "ContainerInterface" = None,
                    set_from_cache: bool = False) -> None:
        container_index = _ContainerIndexes.UserChanges
        self._containers[container_index].setProperty(key, property_name,
                                                      property_value,
                                                      container,
                                                      set_from_cache)

    ##  Overridden from ContainerStack
    #
    #   Since we have a fixed order of containers in the stack and this method would modify the container
    #   ordering, we disallow this operation.
    @override(ContainerStack)
    def addContainer(self, container: ContainerInterface) -> None:
        raise Exceptions.InvalidOperationError(
            "Cannot add a container to Global stack")

    ##  Overridden from ContainerStack
    #
    #   Since we have a fixed order of containers in the stack and this method would modify the container
    #   ordering, we disallow this operation.
    @override(ContainerStack)
    def insertContainer(self, index: int,
                        container: ContainerInterface) -> None:
        raise Exceptions.InvalidOperationError(
            "Cannot insert a container into Global stack")

    ##  Overridden from ContainerStack
    #
    #   Since we have a fixed order of containers in the stack and this method would modify the container
    #   ordering, we disallow this operation.
    @override(ContainerStack)
    def removeContainer(self, index: int = 0) -> None:
        raise Exceptions.InvalidOperationError(
            "Cannot remove a container from Global stack")

    ##  Overridden from ContainerStack
    #
    #   Replaces the container at the specified index with another container.
    #   This version performs checks to make sure the new container has the expected metadata and type.
    #
    #   \throws Exception.InvalidContainerError Raised when trying to replace a container with a container that has an incorrect type.
    @override(ContainerStack)
    def replaceContainer(self,
                         index: int,
                         container: ContainerInterface,
                         postpone_emit: bool = False) -> None:
        expected_type = _ContainerIndexes.IndexTypeMap[index]
        if expected_type == "definition":
            if not isinstance(container, DefinitionContainer):
                raise Exceptions.InvalidContainerError(
                    "Cannot replace container at index {index} with a container that is not a DefinitionContainer"
                    .format(index=index))
        elif container != self._empty_instance_container and container.getMetaDataEntry(
                "type") != expected_type:
            raise Exceptions.InvalidContainerError(
                "Cannot replace container at index {index} with a container that is not of {type} type, but {actual_type} type."
                .format(index=index,
                        type=expected_type,
                        actual_type=container.getMetaDataEntry("type")))

        current_container = self._containers[index]
        if current_container.getId() == container.getId():
            return

        super().replaceContainer(index, container, postpone_emit)

    ##  Overridden from ContainerStack
    #
    #   This deserialize will make sure the internal list of containers matches with what we expect.
    #   It will first check to see if the container at a certain index already matches with what we
    #   expect. If it does not, it will search for a matching container with the correct type. Should
    #   no container with the correct type be found, it will use the empty container.
    #
    #   \throws InvalidContainerStackError Raised when no definition can be found for the stack.
    @override(ContainerStack)
    def deserialize(self,
                    serialized: str,
                    file_name: Optional[str] = None) -> str:
        # update the serialized data first
        serialized = super().deserialize(serialized, file_name)

        new_containers = self._containers.copy()
        while len(new_containers) < len(_ContainerIndexes.IndexTypeMap):
            new_containers.append(self._empty_instance_container)

        # Validate and ensure the list of containers matches with what we expect
        for index, type_name in _ContainerIndexes.IndexTypeMap.items():
            container = None
            try:
                container = new_containers[index]
            except IndexError:
                pass

            if type_name == "definition":
                if not container or not isinstance(container,
                                                   DefinitionContainer):
                    definition = self.findContainer(
                        container_type=DefinitionContainer)
                    if not definition:
                        raise InvalidContainerStackError(
                            "Stack {id} does not have a definition!".format(
                                id=self.getId()))

                    new_containers[index] = definition
                continue

            if not container or container.getMetaDataEntry(
                    "type") != type_name:
                actual_container = self.findContainer(type=type_name)
                if actual_container:
                    new_containers[index] = actual_container
                else:
                    new_containers[index] = self._empty_instance_container

        self._containers = new_containers

        # CURA-5281
        # Some stacks can have empty definition_changes containers which will cause problems.
        # Make sure that all stacks here have non-empty definition_changes containers.
        if isinstance(new_containers[_ContainerIndexes.DefinitionChanges],
                      type(self._empty_instance_container)):
            from cura.Settings.CuraStackBuilder import CuraStackBuilder
            CuraStackBuilder.createDefinitionChangesContainer(
                self,
                self.getId() + "_settings")

        ## TODO; Deserialize the containers.
        return serialized

    ## protected:

    # Helper to make sure we emit a PyQt signal on container changes.
    def _onContainersChanged(self, container: Any) -> None:
        Application.getInstance().callLater(self.pyqtContainersChanged.emit)

    # Helper that can be overridden to get the "machine" definition, that is, the definition that defines the machine
    # and its properties rather than, for example, the extruder. Defaults to simply returning the definition property.
    def _getMachineDefinition(self) -> DefinitionContainer:
        return self.definition

    ##  Find the ID that should be used when searching for instance containers for a specified definition.
    #
    #   This handles the situation where the definition specifies we should use a different definition when
    #   searching for instance containers.
    #
    #   \param machine_definition The definition to find the "quality definition" for.
    #
    #   \return The ID of the definition container to use when searching for instance containers.
    @classmethod
    def _findInstanceContainerDefinitionId(
            cls, machine_definition: DefinitionContainerInterface) -> str:
        quality_definition = machine_definition.getMetaDataEntry(
            "quality_definition")
        if not quality_definition:
            return machine_definition.id  #type: ignore

        definitions = ContainerRegistry.getInstance().findDefinitionContainers(
            id=quality_definition)
        if not definitions:
            Logger.log(
                "w",
                "Unable to find parent definition {parent} for machine {machine}",
                parent=quality_definition,
                machine=machine_definition.id)  #type: ignore
            return machine_definition.id  #type: ignore

        return cls._findInstanceContainerDefinitionId(definitions[0])

    ##  getProperty for extruder positions, with translation from -1 to default extruder number
    def getExtruderPositionValueWithDefault(self, key):
        value = self.getProperty(key, "value")
        if value == -1:
            value = int(Application.getInstance().getMachineManager().
                        defaultExtruderPosition)
        return value