Ejemplo n.º 1
0
class AutoSaver(QObject):
    """
    Class implementing the auto saver.
    """
    AUTOSAVE_IN = 1000 * 3
    MAXWAIT     = 1000 * 15
    
    def __init__(self, parent, save):
        """
        Constructor
        
        @param parent reference to the parent object (QObject)
        @param save slot to be called to perform the save operation
        """
        QObject.__init__(self, parent)
        
        if parent is None:
            raise RuntimeError("AutoSaver: parent must not be None.")
        
        self.__save = save
        
        self.__timer = QBasicTimer()
        self.__firstChange = QTime()
    
    def changeOccurred(self):
        """
        Public slot handling a change.
        """
        if self.__firstChange.isNull():
            self.__firstChange.start()
        
        if self.__firstChange.elapsed() > self.MAXWAIT:
            self.saveIfNeccessary()
        else:
            self.__timer.start(self.AUTOSAVE_IN, self)
    
    def timerEvent(self, evt):
        """
        Protected method handling timer events.
        
        @param evt reference to the timer event (QTimerEvent)
        """
        if evt.timerId() == self.__timer.timerId():
            self.saveIfNeccessary()
        else:
            QObject.timerEvent(self, evt)
    
    def saveIfNeccessary(self):
        """
        Public method to activate the save operation.
        """
        if not self.__timer.isActive():
            return
        
        self.__timer.stop()
        self.__firstChange = QTime()
        self.__save()
Ejemplo n.º 2
0
class FlickableTicker(QObject):
    def __init__(self, scroller, parent=None):

        QObject.__init__(self, parent)
        self.m_scroller = scroller
        self.m_timer = QBasicTimer()

    def start(self, interval):
        if not self.m_timer.isActive():
            self.m_timer.start(interval, self)

    def stop(self):
        self.m_timer.stop()

    def timerEvent(self, event):
        self.m_scroller.tick()
Ejemplo n.º 3
0
class FlickableTicker(QObject):

    def __init__(self, scroller, parent = None):
    
        QObject.__init__(self, parent)
        self.m_scroller = scroller
        self.m_timer = QBasicTimer()

    def start(self, interval):
        if not self.m_timer.isActive():
            self.m_timer.start(interval, self)

    def stop(self):
        self.m_timer.stop()
    
    def timerEvent(self, event):
        self.m_scroller.tick()
Ejemplo n.º 4
0
class AgknowDockWidgetTimeSlider(QtGui.QDockWidget, FORM_CLASS):
    """
     Class for the timeslider dockwidget of the agknow plugin.
    """
    closingPlugin = pyqtSignal()
    productChanged = pyqtSignal(str)

    def __init__(self, parent=None):
        """Constructor."""
        super(AgknowDockWidgetTimeSlider, self).__init__(parent)
        # Set up the user interface from Designer.
        # After setupUI you can access any designer object by doing
        # self.<objectname>, and you can use autoconnect slots - see
        # http://qt-project.org/doc/qt-4.8/designer-using-a-ui-file.html
        # #widgets-and-dialogs-with-auto-connect
        self.setupUi(self)

        # GUI Events
        # lambda keyword allows the button itself to be passed
        #https://www.tutorialspoint.com/pyqt/pyqt_qradiobutton_widget.htm
        self.rdBtnVisible.toggled.connect(lambda: self.rdBtnProductState_toggled(self.rdBtnVisible))
        self.rdBtnVitality.toggled.connect(lambda: self.rdBtnProductState_toggled(self.rdBtnVitality))
        self.rdBtnVariations.toggled.connect(lambda: self.rdBtnProductState_toggled(self.rdBtnVariations))
        self.rdBtnNDRE1.toggled.connect(lambda: self.rdBtnProductState_toggled(self.rdBtnNDRE1))
        self.rdBtnNDRE2.toggled.connect(lambda: self.rdBtnProductState_toggled(self.rdBtnNDRE2))
        self.rdBtnNDWI.toggled.connect(lambda: self.rdBtnProductState_toggled(self.rdBtnNDWI))
        self.rdBtnSAVI.toggled.connect(lambda: self.rdBtnProductState_toggled(self.rdBtnSAVI))
        self.rdBtnEVI2.toggled.connect(lambda: self.rdBtnProductState_toggled(self.rdBtnEVI2))
        self.rdBtnCIRE.toggled.connect(lambda: self.rdBtnProductState_toggled(self.rdBtnCIRE))
        self.rdBtnNDVI.toggled.connect(lambda: self.rdBtnProductState_toggled(self.rdBtnNDVI))
        self.rdBtnRefl.toggled.connect(lambda: self.rdBtnProductState_toggled(self.rdBtnRefl))

        self.sliderTime.valueChanged.connect(self.sliderValue_changed)
        self.btnTimePlay.clicked.connect(self.btnTimePlay_clicked)
        self.btnTimeBackward.clicked.connect(self.btnTimeBackward_clicked)
        self.btnTimeForward.clicked.connect(self.btnTimeForward_clicked)

        self.product = "vitality"
        self.rasters = [] # it's a list here - a dict in agknow_qgis_dockwidget.py
        self.data_source = "sentinel2"
        self.current_parcel_id = None

        # timeslider
        self.timer = QBasicTimer()
        self.step = 0

        self.iface = qgis.utils.iface

    def set_data_source(self, data_source):
        """
         Setter for the data_source (landsat-8 or sentinel-2).
        """
        #QgsMessageLog.logMessage("Selected Data source: {0}".format(data_source), 'agknow', QgsMessageLog.INFO)
        self.data_source = data_source

        self.toggle_products_data_source_compatibility()

    def set_product(self, product):
        """
         Setter for the product (vitality, variations, visible, etc).
        """
        #QgsMessageLog.logMessage("Selected Product: {0}".format(product), 'agknow', QgsMessageLog.INFO)
        self.product = product

        # notify slots of change
        self.productChanged.emit(product)

    def set_current_parcel_id(self, parcel_id):
        """
         Setter for the current parcel_id.
        """
        self.current_parcel_id = parcel_id

    def timerEvent(self, e):
        """
         Event handler for the timer.
        :param e:
        """
        print("timerEvent()")

        if self.step == self.sliderTime.maximum():
            self.timer.stop()
            self.step = 0  #reset step
            return
        else:
            self.step = self.step + 1

            self.sliderTime.setValue(self.sliderTime.value()+1)


    def rdBtnProductState_toggled(self, btn):
        """
         Handles the toggled event of the given radio button.

        :param btn: radio button (QRadioButton)
        """
        # trigger update
        # if radio button is checked at the end
        if btn.isChecked():

            if btn.text().lower() == "refl.":
                self.set_product("reflectances")
            else:
                self.set_product(btn.text().lower())

            parcel_id = self.current_parcel_id

            if len(self.rasters) > 0:

                # find subgroup
                root = QgsProject.instance().layerTreeRoot()
                parcel_group = root.findGroup("parcel id: {0}".format(parcel_id))

                # select active layer for identity e.g.
                self.set_layer_active_toc(parcel_id)

                if parcel_group is not None:
                    self.toggle_products(parcel_group)


    def set_layer_active_toc(self, parcel_id):
        """
         Sets the group layer of the given parcel ID in the TOC to active.

        :param parcel_id: parcel's ID (integer)
        """
        # take the first layer of the group "parcel id: - source - product"
        data_source_group = self.find_data_source_group(parcel_id, self.product, self.data_source)

        # layer name
        date = self.rasters[0]["date"]
        raster_id = self.rasters[0]["raster_id"]
        activeLyrName = date + " - " + str(raster_id)

        if data_source_group is not None:

            for lyr in data_source_group.findLayers():

                #print("searching for {0}..".format(activeLyrName))

                # QGIS 2.18.1 : name(), QGIS 2.18.0 : layerName()
                if lyr.layerName() == activeLyrName:

                    # QgsLayerTreeLayer to QgsMapLayer per .layer()
                    #print("setting lyr {0} active".format(lyr.layerName()))
                    self.iface.setActiveLayer(lyr.layer())

        else:
            pass
            #print("data_source_group not found: {0} - {1} - {2}".format(parcel_id, self.product, self.data_source))

    def sliderValue_changed(self):
        """
         Handles the value changed event of the timeslider.
        """

        if len(self.rasters) > 0:
            #print(self.sliderTime.value())

            idx = self.sliderTime.value()

            #set current raster to visible - the rest invisible

            # get data from position in self.rasters
            product_id = self.product + " " #whitespace!
            data_source = self.rasters[idx]["source"]
            parcel_id = self.rasters[idx]["parcel_id"]
            raster_id = self.rasters[idx]["raster_id"]
            date = self.rasters[idx]["date"]

            #print(product_id, data_source, parcel_id, raster_id, date)

            activeLyrName = "{0}|{1}|{2}|{3}".format(product_id.strip(), date, raster_id, data_source) #date + " - " + str(raster_id)

            #print(activeLyrName)

            self.toggle_image_layer(activeLyrName, data_source, parcel_id, product_id)

        else:
            QgsMessageLog.logMessage("AgknowDockWidgetTimeSlider - sliderValue_changed() - rasters are not set!",
                                     "agknow",
                                     QgsMessageLog.CRITICAL)


    def toggle_image_layer(self, activeLyrName, data_source, parcel_id, product_id):
        """
         Toggles all other image layers off except the given activeLayer.

        :param activeLyrName: active layer group (string)
        :param data_source: landsat-8 or sentinel-2 (string)
        :param parcel_id: parcel's ID (integer)
        :param product_id: product id: visible, vitality, variations, etc. (string)
        """
        # find subgroup
        data_source_group = self.find_data_source_group(parcel_id, product_id, data_source)

        if data_source_group is not None:

            for lyr in data_source_group.findLayers():
                # QGIS 2.18.1 : name(), QGIS 2.18.0 : layerName()
                if QGis.QGIS_VERSION_INT == 21800:
                    if lyr.layerName() == activeLyrName:
                        # set active layer visible
                        lyr.setVisible(Qt.Checked)
                        # select active layer for identity e.g.
                        # QgsLayerTreeLayer to QgsMapLayer per .layer()
                        self.iface.setActiveLayer(lyr.layer())
                    else:
                        lyr.setVisible(Qt.Unchecked)

                elif QGis.QGIS_VERSION_INT > 21800:
                    if lyr.name() == activeLyrName:
                        # set active layer visible
                        lyr.setVisible(Qt.Checked)
                        # select active layer for identity e.g.
                        # QgsLayerTreeLayer to QgsMapLayer per .layer()
                        self.iface.setActiveLayer(lyr.layer())
                    else:
                        lyr.setVisible(Qt.Unchecked)


    def find_data_source_group(self, parcel_id, product_id, data_source):
        """
         Returns the data source group layer of the TOC for the given parcel id, product and data source.

        :param parcel_id: parcel's ID (integer)
        :param product_id: product id: visible, vitality, variations, etc. (string)
        :param data_source: landsat-8 or sentinel-2 (string)

        :return: data_source_group (QgsLayerTreeGroup)
        """
        root = QgsProject.instance().layerTreeRoot()
        parcel_group = root.findGroup("parcel id: {0}".format(parcel_id))

        if parcel_group is not None:
            #print("parcel group found")
            product_group = parcel_group.findGroup(product_id)
            #print(product_group)

            if product_group is not None:
                #print("product group found")
                data_source_group = product_group.findGroup(data_source)

                return data_source_group


    def toggle_products(self, parcel_group):
        """
         Toggle all other product groups off except the given parcel_group.

        :param parcel_group: (QgsLayerTreeGroup)
        """
        data_source_list = ["landsat8", "sentinel2"]
        matrix = {"landsat8": ["visible", "vitality", "variations"],
                  "sentinel2": ["visible", "vitality", "variations", "reflectances", "ndvi", "ndre1", "ndre2", "ndre3",
                                "ndwi", "savi", "evi2", "cire"]
                  }

        # turn off other data_source groups
        for ds in data_source_list:
            #print("checking {0}..".format(ds))
            for p in matrix[ds]:
                #print("checking product {0}..".format(p))
                pg = parcel_group.findGroup(p + " ")

                if pg is not None:
                    #print("group {0} found!".format(p))
                    g = pg.findGroup(ds)

                    if g is not None:
                        if p == self.product and ds == self.data_source:
                            #print("group {0} visible".format(ds))
                            g.setVisible(Qt.Checked)
                        else:
                            #print("group {0} invisible!".format(ds))
                            g.setVisible(Qt.Unchecked)

    def toggle_products_data_source_compatibility(self):
        """
         Handles radio buttons for compatibility of products & data_source
        """
        print("toggle_products_data_source_compatibility()")
        matrix = {"landsat8": ["Visible", "Vitality", "Variations"],
                  "sentinel2": ["Visible", "Vitality", "Variations", "Refl.", "NDVI", "NDRE1", "NDRE2", "NDRE3",
                                    "NDWI", "SAVI", "EVI2", "CIRE"]
                   }
        radioBtns = [self.rdBtnVisible, self.rdBtnVitality, self.rdBtnVariations,
                        self.rdBtnNDRE1, self.rdBtnNDRE2, self.rdBtnNDWI, self.rdBtnSAVI, self.rdBtnEVI2,
                        self.rdBtnCIRE, self.rdBtnNDVI, self.rdBtnRefl]

        for rdBtn in radioBtns:
            # disable all other radio buttons
            if rdBtn.text() in matrix[self.data_source]:
                rdBtn.setEnabled(True)
            else:
                rdBtn.setEnabled(False)

        #TODO: if data source disables product - select another product
        # leads to segmentation faults at the moment because several threads are started then
        #for rdBtn in radioBtns:
        #    # check if selected radio button is disabled
        #    if rdBtn.isChecked() and not rdBtn.isEnabled():
        #        # if so take the first of possible radio buttons
        #        rdBtnToCheckName = matrix[self.data_source][0]
        #        print(rdBtnToCheckName)
        #        for r in radioBtns:
        #            if r.text() == rdBtnToCheckName:
        #                r.setChecked(Qt.Checked)

    def btnTimePlay_clicked(self):
        """
         Handles the click event of the Button btnTimePlay.
        """
        self.btnTimePlay.setText(" || ")
        interval = 1000 #ms

        # stop timer if it is already active
        if self.timer.isActive():
            self.btnTimePlay.setText(" > ")
            self.timer.stop()
            # reset timer
            self.step = 0
            self.sliderTime.setValue(0)

        # start timer
        else:
            self.timer.start(interval, self)


    def btnTimeForward_clicked(self):
        """
         Handles the click event of the Button btnTimeForward.
        """
        self.sliderTime.setValue(self.sliderTime.value()+1)


    def btnTimeBackward_clicked(self):
        """
         Handles the click event of the Button btnTimeBackward.
        """
        self.sliderTime.setValue(self.sliderTime.value()-1)


    def reload_images(self, rasters):
        """
         Reloads images for the given rasters.
         :param rasters: ()
        """
        #print("reload_images({0})".format(rasters))

        if rasters is not None:
            self.sliderTime.setRange(0, len(rasters)-1)
            self.sliderTime.setTickInterval = 1
            self.rasters = rasters

            if len(self.rasters) > 0:
                #print(self.rasters)
                parcel_id = self.rasters[0]["parcel_id"]

                QgsMessageLog.logMessage("AgknowDockWidgetTimeSlider - activating parcel {0}..".format(parcel_id),
                                         "agknow",
                                         QgsMessageLog.INFO)
                # select first image layer active in toc for identity e.g.
                self.set_layer_active_toc(parcel_id)

        else:
            QgsMessageLog.logMessage("AgknowDockWidgetTimeSlider - rasters is None!", "agknow",
                                     QgsMessageLog.WARNING)


    def closeEvent(self, event):
        """
         Handles the close event of the class.
        :param event:
        """
        self.closingPlugin.emit()
        event.accept()