def __init__(self, iface, project, offline_editing, parent=None):
        """Constructor."""
        super(PackageDialog, self).__init__(parent=parent)
        self.setupUi(self)

        self.iface = iface
        self.offline_editing = offline_editing
        self.project = project
        self.qfield_preferences = Preferences()
        self.project_lbl.setText(get_project_title(self.project))
        self.button_box.button(QDialogButtonBox.Save).setText(
            self.tr('Create'))
        self.button_box.button(QDialogButtonBox.Save).clicked.connect(
            self.package_project)
        self.button_box.button(QDialogButtonBox.Reset).setText(
            self.tr('Configure current project...'))
        self.button_box.button(QDialogButtonBox.Reset).setIcon(
            QIcon(
                os.path.join(os.path.dirname(__file__),
                             '../resources/project_properties.svg')))
        self.button_box.button(QDialogButtonBox.Reset).clicked.connect(
            self.show_settings)
        self.iface.mapCanvas().extentsChanged.connect(self.extent_changed)
        self.extent_changed()

        self.devices = None
        # self.refresh_devices()
        self.setup_gui()

        self.offline_editing.warning.connect(self.show_warning)
    def __init__(self, iface, offline_editing, parent=None):
        """Constructor.
        """
        super(SynchronizeDialog, self).__init__(parent=parent)
        self.setupUi(self)
        self.iface = iface
        self.preferences = Preferences()
        self.offline_editing = offline_editing
        self.button_box.button(QDialogButtonBox.Save).setText(
            self.tr('Synchronize'))
        self.button_box.button(QDialogButtonBox.Save).clicked.connect(
            self.start_synchronization)
        self.qfieldDir.setText(
            self.preferences.value('importDirectoryProject')
            or self.preferences.value('importDirectory'))
        self.qfieldDir_button.clicked.connect(
            make_folder_selector(self.qfieldDir))

        self.offline_editing_done = False
    def __init__(self, parent=None):
        preferences = Preferences()
        SettingDialog.__init__(self, setting_manager=preferences)
        super().__init__(parent, setting_manager=preferences)
        self.setupUi(self)
        self.init_widgets()

        self.setting_widget('importDirectory').widget.setStorageMode(
            QgsFileWidget.GetDirectory)
        self.setting_widget('exportDirectory').widget.setStorageMode(
            QgsFileWidget.GetDirectory)
    def __init__(self, iface, project, offline_editing, parent=None):
        """Constructor."""
        super(PackageDialog, self).__init__(parent=parent)
        self.setupUi(self)

        self.iface = iface
        self.offline_editing = offline_editing
        self.project = project
        self.qfield_preferences = Preferences()
        self.project_lbl.setText(get_project_title(self.project))
        self.push_btn = QPushButton(self.tr('Create'))
        self.push_btn.clicked.connect(self.package_project)
        self.button_box.addButton(self.push_btn, QDialogButtonBox.ActionRole)
        self.iface.mapCanvas().extentsChanged.connect(self.extent_changed)
        self.extent_changed()

        self.devices = None
        # self.refresh_devices()
        self.setup_gui()

        self.offline_editing.warning.connect(self.show_warning)
Exemple #5
0
    def __init__(self, parent=None):
        preferences = Preferences()
        super(PreferencesDialog, self).__init__(parent=parent)
        SettingDialog.__init__(self,
                               setting_manager=preferences,
                               mode=UpdateMode.DialogAccept)
        self.setupUi(self)
        self.init_widgets()

        self.setting_widget('importDirectory').widget.setStorageMode(
            QgsFileWidget.GetDirectory)
        self.setting_widget('exportDirectory').widget.setStorageMode(
            QgsFileWidget.GetDirectory)
class PackageDialog(QDialog, DialogUi):
    def __init__(self, iface, project, offline_editing, parent=None):
        """Constructor."""
        super(PackageDialog, self).__init__(parent=parent)
        self.setupUi(self)

        self.iface = iface
        self.offline_editing = offline_editing
        self.project = project
        self.qfield_preferences = Preferences()
        self.project_lbl.setText(get_project_title(self.project))
        self.push_btn = QPushButton(self.tr('Create'))
        self.push_btn.clicked.connect(self.package_project)
        self.button_box.addButton(self.push_btn, QDialogButtonBox.ActionRole)
        self.iface.mapCanvas().extentsChanged.connect(self.extent_changed)
        self.extent_changed()

        self.devices = None
        # self.refresh_devices()
        self.setup_gui()

        self.offline_editing.warning.connect(self.show_warning)

    def update_progress(self, sent, total):
        progress = float(sent) / total * 100
        self.progress_bar.setValue(progress)

    def setup_gui(self):
        """Populate gui and connect signals of the push dialog"""
        export_folder_path = self.qfield_preferences.value(
            'exportDirectoryProject')
        if not export_folder_path:
            project_fn = QgsProject.instance().fileName()
            export_folder_name = fileparts(project_fn)[1]
            export_folder_path = os.path.join(
                self.qfield_preferences.value('exportDirectory'),
                export_folder_name)

        self.manualDir.setText(export_folder_path)
        self.manualDir_btn.clicked.connect(make_folder_selector(
            self.manualDir))
        self.update_info_visibility()
        self.infoLabel.setTextInteractionFlags(Qt.TextBrowserInteraction)
        self.infoLabel.linkActivated.connect(lambda: self.show_settings())

    def get_export_folder_from_dialog(self):
        """Get the export folder according to the inputs in the selected"""
        # manual
        return self.manualDir.text()

    def package_project(self):
        self.push_btn.setEnabled(False)
        self.informationStack.setCurrentWidget(self.progressPage)

        export_folder = self.get_export_folder_from_dialog()

        self.qfield_preferences.set_value('exportDirectoryProject',
                                          export_folder)

        offline_convertor = OfflineConverter(self.project, export_folder,
                                             self.iface.mapCanvas().extent(),
                                             self.offline_editing)

        # progress connections
        offline_convertor.total_progress_updated.connect(self.update_total)
        offline_convertor.task_progress_updated.connect(self.update_task)

        offline_convertor.convert()
        self.do_post_offline_convert_action()
        self.close()

        self.progress_group.setEnabled(False)

    def do_post_offline_convert_action(self):
        """
        Show an information label that the project has been copied
        with a nice link to open the result folder.
        """
        export_folder = self.get_export_folder_from_dialog()

        result_label = QLabel(
            self.
            tr('Finished creating the project at {result_folder}. Please copy this folder to '
               'your QField device.').format(
                   result_folder='<a href="{folder}">{folder}</a>'.format(
                       folder=export_folder)))
        result_label.setTextFormat(Qt.RichText)
        result_label.setTextInteractionFlags(Qt.TextBrowserInteraction)
        result_label.linkActivated.connect(lambda: open_folder(export_folder))
        result_label.setSizePolicy(QSizePolicy.MinimumExpanding,
                                   QSizePolicy.Preferred)

        self.iface.messageBar().pushWidget(result_label, Qgis.Info, 0)

    def update_info_visibility(self):
        """
        Show the info label if there are unconfigured layers
        """
        self.infoGroupBox.hide()
        for layer in list(self.project.mapLayers().values()):
            if not LayerSource(layer).is_configured:
                self.infoGroupBox.show()

        project_configuration = ProjectConfiguration(self.project)

        if project_configuration.offline_copy_only_aoi or project_configuration.create_base_map:
            self.informationStack.setCurrentWidget(self.selectExtentPage)
        else:
            self.informationStack.setCurrentWidget(self.progressPage)

    def show_settings(self):
        dlg = ProjectConfigurationDialog(self.iface, self.iface.mainWindow())
        dlg.exec_()
        self.update_info_visibility()

    @pyqtSlot(int, int, str)
    def update_total(self, current, layer_count, message):
        self.totalProgressBar.setMaximum(layer_count)
        self.totalProgressBar.setValue(current)
        self.statusLabel.setText(message)

    @pyqtSlot(int, int)
    def update_task(self, progress, max_progress):
        self.layerProgressBar.setMaximum(max_progress)
        self.layerProgressBar.setValue(progress)

    @pyqtSlot()
    def extent_changed(self):
        extent = self.iface.mapCanvas().extent()
        self.xMinLabel.setText(str(extent.xMinimum()))
        self.xMaxLabel.setText(str(extent.xMaximum()))
        self.yMinLabel.setText(str(extent.yMinimum()))
        self.yMaxLabel.setText(str(extent.yMaximum()))

    @pyqtSlot(str, str)
    def show_warning(self, _, message):
        # Most messages from the offline editing plugin are not important enough to show in the message bar.
        # In case we find important ones in the future, we need to filter them.
        QgsApplication.instance().messageLog().logMessage(
            message, 'QFieldSync')
class SynchronizeDialog(QDialog, DialogUi):
    def __init__(self, iface, offline_editing, parent=None):
        """Constructor.
        """
        super(SynchronizeDialog, self).__init__(parent=parent)
        self.setupUi(self)
        self.iface = iface
        self.preferences = Preferences()
        self.offline_editing = offline_editing
        self.button_box.button(QDialogButtonBox.Save).setText(
            self.tr('Synchronize'))
        self.button_box.button(QDialogButtonBox.Save).clicked.connect(
            self.start_synchronization)
        self.qfieldDir.setText(
            self.preferences.value('importDirectoryProject')
            or self.preferences.value('importDirectory'))
        self.qfieldDir_button.clicked.connect(
            make_folder_selector(self.qfieldDir))

        self.offline_editing_done = False

    def start_synchronization(self):
        self.button_box.button(QDialogButtonBox.Save).setEnabled(False)
        qfield_folder = self.qfieldDir.text()
        self.preferences.set_value('importDirectoryProject', qfield_folder)
        try:
            current_import_file_checksum = import_file_checksum(qfield_folder)
            imported_files_checksums = import_checksums_of_project(
                qfield_folder)

            if imported_files_checksums and current_import_file_checksum and current_import_file_checksum in imported_files_checksums:
                message = self.tr(
                    "Data from this file are already synchronized with the original project."
                )
                raise NoProjectFoundError(message)
            qgs_file = get_project_in_folder(qfield_folder)
            open_project(qgs_file)
            self.offline_editing.progressStopped.connect(self.update_done)
            self.offline_editing.layerProgressUpdated.connect(
                self.update_total)
            self.offline_editing.progressModeSet.connect(self.update_mode)
            self.offline_editing.progressUpdated.connect(self.update_value)
            self.offline_editing.synchronize()
            if self.offline_editing_done:
                original_project_path = ProjectConfiguration(
                    QgsProject.instance()).original_project_path
                if original_project_path:
                    # import the DCIM folder
                    copy_images(
                        os.path.join(qfield_folder, "DCIM"),
                        os.path.join(os.path.dirname(original_project_path),
                                     "DCIM"))
                    if open_project(original_project_path):
                        # save the data_file_checksum to the project and save it
                        imported_files_checksums.append(
                            import_file_checksum(qfield_folder))
                        ProjectConfiguration(QgsProject.instance(
                        )).imported_files_checksums = imported_files_checksums
                        QgsProject.instance().write()
                        self.iface.messageBar().pushInfo(
                            'QFieldSync',
                            self.tr("Opened original project {}".format(
                                original_project_path)))
                    else:
                        self.iface.messageBar().pushInfo(
                            'QFieldSync',
                            self.
                            tr("The data has been synchronized successfully but the original project ({}) could not be opened"
                               .format(original_project_path)))
                else:
                    self.iface.messageBar().pushInfo(
                        'QFieldSync',
                        self.tr("No original project path found"))
                self.close()
            else:
                message = self.tr(
                    "The project you imported does not seem to be an offline project"
                )
                raise NoProjectFoundError(message)
        except NoProjectFoundError as e:
            self.iface.messageBar().pushWarning('QFieldSync', str(e))

    @pyqtSlot(int, int)
    def update_total(self, current, layer_count):
        self.totalProgressBar.setMaximum(layer_count)
        self.totalProgressBar.setValue(current)

    @pyqtSlot(int)
    def update_value(self, progress):
        self.layerProgressBar.setValue(progress)

    @pyqtSlot('QgsOfflineEditing::ProgressMode', int)
    def update_mode(self, _, mode_count):
        self.layerProgressBar.setMaximum(mode_count)
        self.layerProgressBar.setValue(0)

    @pyqtSlot()
    def update_done(self):
        self.offline_editing.progressStopped.disconnect(self.update_done)
        self.offline_editing.layerProgressUpdated.disconnect(self.update_total)
        self.offline_editing.progressModeSet.disconnect(self.update_mode)
        self.offline_editing.progressUpdated.disconnect(self.update_value)
        self.offline_editing_done = True
class PackageDialog(QDialog, DialogUi):
    def __init__(self, iface, project, offline_editing, parent=None):
        """Constructor."""
        super(PackageDialog, self).__init__(parent=parent)
        self.setupUi(self)

        self.iface = iface
        self.offline_editing = offline_editing
        self.project = project
        self.qfield_preferences = Preferences()
        self.project_lbl.setText(get_project_title(self.project))
        self.button_box.button(QDialogButtonBox.Save).setText(
            self.tr('Create'))
        self.button_box.button(QDialogButtonBox.Save).clicked.connect(
            self.package_project)
        self.button_box.button(QDialogButtonBox.Reset).setText(
            self.tr('Configure current project...'))
        self.button_box.button(QDialogButtonBox.Reset).setIcon(
            QIcon(
                os.path.join(os.path.dirname(__file__),
                             '../resources/project_properties.svg')))
        self.button_box.button(QDialogButtonBox.Reset).clicked.connect(
            self.show_settings)
        self.iface.mapCanvas().extentsChanged.connect(self.extent_changed)
        self.extent_changed()

        self.devices = None
        # self.refresh_devices()
        self.setup_gui()

        self.offline_editing.warning.connect(self.show_warning)

    def update_progress(self, sent, total):
        progress = float(sent) / total * 100
        self.progress_bar.setValue(progress)

    def setup_gui(self):
        """Populate gui and connect signals of the push dialog"""
        export_folder_path = self.qfield_preferences.value(
            'exportDirectoryProject')
        if not export_folder_path:
            project_fn = QgsProject.instance().fileName()
            export_folder_name = fileparts(project_fn)[1]
            export_folder_path = os.path.join(
                self.qfield_preferences.value('exportDirectory'),
                export_folder_name)

        self.manualDir.setText(export_folder_path)
        self.manualDir_btn.clicked.connect(make_folder_selector(
            self.manualDir))
        self.update_info_visibility()

    def get_export_folder_from_dialog(self):
        """Get the export folder according to the inputs in the selected"""
        # manual
        return self.manualDir.text()

    def package_project(self):
        self.button_box.button(QDialogButtonBox.Save).setEnabled(False)
        self.informationStack.setCurrentWidget(self.progressPage)

        export_folder = self.get_export_folder_from_dialog()

        self.qfield_preferences.set_value('exportDirectoryProject',
                                          export_folder)

        offline_convertor = OfflineConverter(self.project, export_folder,
                                             self.iface.mapCanvas().extent(),
                                             self.offline_editing)

        # progress connections
        offline_convertor.total_progress_updated.connect(self.update_total)
        offline_convertor.task_progress_updated.connect(self.update_task)

        offline_convertor.convert()
        self.do_post_offline_convert_action()
        self.close()

        self.progress_group.setEnabled(False)

    def do_post_offline_convert_action(self):
        """
        Show an information label that the project has been copied
        with a nice link to open the result folder.
        """
        export_folder = self.get_export_folder_from_dialog()

        result_message = self.tr(
            'Finished creating the project at {result_folder}. Please copy this folder to '
            'your QField device.').format(
                result_folder='<a href="{folder}">{folder}</a>'.format(
                    folder=export_folder))
        self.iface.messageBar().pushMessage(result_message, Qgis.Success, 0)

    def update_info_visibility(self):
        """
        Show the info label if there are unconfigured layers
        """
        pathResolver = QgsProject.instance().pathResolver()
        showInfoConfiguration = False
        localizedDataPathLayers = []
        for layer in list(self.project.mapLayers().values()):
            if not LayerSource(layer).is_configured:
                showInfoConfiguration = True
            if layer.dataProvider() is not None:
                metadata = QgsProviderRegistry.instance().providerMetadata(
                    layer.dataProvider().name())
                if metadata is not None:
                    decoded = metadata.decodeUri(layer.source())
                    if "path" in decoded:
                        path = pathResolver.writePath(decoded["path"])
                        if path.startswith("localized:"):
                            localizedDataPathLayers.append('- {} ({})'.format(
                                layer.name(), path[10:]))

        self.infoConfigurationLabel.setVisible(showInfoConfiguration)
        if localizedDataPathLayers:
            if len(localizedDataPathLayers) == 1:
                self.infoLocalizedLayersLabel.setText(
                    self.tr('The layer stored in a localized data path is:\n{}'
                            ).format("\n".join(localizedDataPathLayers)))
            else:
                self.infoLocalizedLayersLabel.setText(
                    self.tr(
                        'The layers stored in a localized data path are:\n{}').
                    format("\n".join(localizedDataPathLayers)))
            self.infoLocalizedLayersLabel.setVisible(True)
            self.infoLocalizedPresentLabel.setVisible(True)
        else:
            self.infoLocalizedLayersLabel.setVisible(False)
            self.infoLocalizedPresentLabel.setVisible(False)
        self.infoGroupBox.setVisible(showInfoConfiguration
                                     or len(localizedDataPathLayers) > 0)

        project_configuration = ProjectConfiguration(self.project)

        if project_configuration.offline_copy_only_aoi or project_configuration.create_base_map:
            self.informationStack.setCurrentWidget(self.selectExtentPage)
        else:
            self.informationStack.setCurrentWidget(self.progressPage)

    def show_settings(self):
        if Qgis.QGIS_VERSION_INT >= 31500:
            self.iface.showProjectPropertiesDialog('QField')
        else:
            dlg = ProjectConfigurationDialog(self.iface.mainWindow())
            dlg.exec_()
        self.update_info_visibility()

    @pyqtSlot(int, int, str)
    def update_total(self, current, layer_count, message):
        self.totalProgressBar.setMaximum(layer_count)
        self.totalProgressBar.setValue(current)
        self.statusLabel.setText(message)

    @pyqtSlot(int, int)
    def update_task(self, progress, max_progress):
        self.layerProgressBar.setMaximum(max_progress)
        self.layerProgressBar.setValue(progress)

    @pyqtSlot()
    def extent_changed(self):
        extent = self.iface.mapCanvas().extent()
        self.xMinLabel.setText(str(extent.xMinimum()))
        self.xMaxLabel.setText(str(extent.xMaximum()))
        self.yMinLabel.setText(str(extent.yMinimum()))
        self.yMaxLabel.setText(str(extent.yMaximum()))

    @pyqtSlot(str, str)
    def show_warning(self, _, message):
        # Most messages from the offline editing plugin are not important enough to show in the message bar.
        # In case we find important ones in the future, we need to filter them.
        QgsApplication.instance().messageLog().logMessage(
            message, 'QFieldSync')