Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 77 additions & 11 deletions qfieldsync/gui/checker_feedback_table.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,31 +20,65 @@
***************************************************************************/
"""

from libqfieldsync.project_checker import Feedback, ProjectCheckerFeedback
from qgis.core import QgsApplication
from qgis.PyQt.QtCore import Qt
from qgis.PyQt.QtWidgets import QLabel, QSizePolicy, QTableWidget, QTableWidgetItem
from typing import Optional

from libqfieldsync.project_checker import (
Feedback,
FeedbackTypeId,
ProjectCheckerFeedback,
)
from qgis.core import QgsApplication, QgsProject
from qgis.PyQt.QtCore import Qt, pyqtSignal
from qgis.PyQt.QtWidgets import (
QHBoxLayout,
QHeaderView,
QLabel,
QPushButton,
QSizePolicy,
QTableWidget,
QTableWidgetItem,
QWidget,
)

from qfieldsync.utils.qt_utils import make_icon


class CheckerFeedbackTable(QTableWidget):
feedback_fixed = pyqtSignal()

def __init__(self, checker_feedback: ProjectCheckerFeedback, *args, **kwargs):
super().__init__(*args, **kwargs)

# dictionary mapping FeedbackTypeId to fix action callbacks
self._fix_handlers = {
FeedbackTypeId.PROJECT_IS_DIRTY: self._fix_project_is_dirty,
}

self.setColumnCount(2)
self.setHorizontalHeaderLabels(["", self.tr("Message")])
self.horizontalHeader().setStretchLastSection(True)
self.horizontalHeader().setSectionResizeMode(
0, QHeaderView.ResizeMode.ResizeToContents
)
self.horizontalHeader().setSectionResizeMode(1, QHeaderView.ResizeMode.Stretch)

self.setRowCount(0)
self.setMinimumHeight(100)
self.setSizePolicy(
QSizePolicy.Policy.MinimumExpanding, QSizePolicy.Policy.MinimumExpanding
)

self.verticalHeader().hide()
self.horizontalHeader().sectionResized.connect(self.resizeRowsToContents)
self.setWordWrap(True)

self.set_feedback(checker_feedback)

def set_feedback(self, checker_feedback: ProjectCheckerFeedback) -> None:
self.setRowCount(0)

for layer_id in checker_feedback.feedbacks:
for feedback in checker_feedback.feedbacks[layer_id]:
row = self.rowCount()

self.insertRow(row)

# first column
Expand Down Expand Up @@ -72,6 +106,10 @@ def __init__(self, checker_feedback: ProjectCheckerFeedback, *args, **kwargs):
self.setItem(row, 1, item)

# we do not escape the values on purpose to support Markdown/HTML
cell_widget = QWidget()
cell_layout = QHBoxLayout(cell_widget)
cell_layout.setContentsMargins(0, 0, 8, 0)

label = QLabel("**{}**\n\n{}".format(source, feedback.message))
label.setWordWrap(True)
label.setMargin(5)
Expand All @@ -83,10 +121,38 @@ def __init__(self, checker_feedback: ProjectCheckerFeedback, *args, **kwargs):
| Qt.TextInteractionFlag.LinksAccessibleByKeyboard
)
label.setOpenExternalLinks(True)
self.setCellWidget(row, 1, label)
cell_layout.addWidget(label, stretch=1)

fix_button = self._get_feedback_fix(feedback)
if fix_button:
cell_layout.addWidget(
fix_button,
alignment=Qt.AlignmentFlag.AlignRight
| Qt.AlignmentFlag.AlignVCenter,
)

self.setCellWidget(row, 1, cell_widget)

self.verticalHeader().hide()
self.resizeColumnsToContents()
self.resizeRowsToContents()
self.horizontalHeader().sectionResized.connect(self.resizeRowsToContents)
self.setWordWrap(True)
Comment thread
SeqLaz marked this conversation as resolved.

def _get_feedback_fix(self, feedback: Feedback) -> Optional[QPushButton]:
"""Return a QPushButton configured with a fix callback."""
fix_action = self._fix_handlers.get(feedback.type_id)
if not fix_action:
return None

fix_button = QPushButton(self.tr("Fix!"))

def on_fix_clicked(
_checked: bool,
action=fix_action,
target_feedback=feedback,
) -> None:
action(target_feedback)
self.feedback_fixed.emit()

fix_button.clicked.connect(on_fix_clicked)
return fix_button

def _fix_project_is_dirty(self, _feedback: Feedback) -> None:
QgsProject.instance().write()
25 changes: 21 additions & 4 deletions qfieldsync/gui/cloud_transfer_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -265,14 +265,17 @@ def show_project_compatibility_page(self):
if not self.is_project_compatible_page_prepared:
feedback = None
if self.cloud_project and self.cloud_project.is_current_qgis_project:
checker = ProjectChecker(QgsProject.instance())
feedback = checker.check(ExportType.Cloud)
self.project_checker = ProjectChecker(QgsProject.instance())
feedback = self.project_checker.check(ExportType.Cloud)

if feedback and feedback.count > 0:
# check whether the widget has already been added the guard from adding twice due to repeated showEvent signal
has_errors = len(feedback.error_feedbacks) > 0
feedback_table = CheckerFeedbackTable(feedback)
self.feedbackTableWrapperLayout.addWidget(feedback_table)
self.feedback_table = CheckerFeedbackTable(feedback)
self.feedback_table.feedback_fixed.connect(
self.refresh_project_compatibility_page
)
self.feedbackTableWrapperLayout.addWidget(self.feedback_table)
self.stackedWidget.setCurrentWidget(self.projectCompatibilityPage)
self.buttonBox.button(QDialogButtonBox.StandardButton.Apply).setVisible(
True
Expand All @@ -288,6 +291,20 @@ def show_project_compatibility_page(self):

self.is_project_compatible_page_prepared = True

def refresh_project_compatibility_page(self):
if not self.cloud_project or not self.cloud_project.is_current_qgis_project:
return

feedback = self.project_checker.check(ExportType.Cloud)
if feedback.count == 0:
self.show_project_files_fetching_page()
else:
self.feedback_table.set_feedback(feedback)
has_errors = len(feedback.error_feedbacks) > 0
self.buttonBox.button(QDialogButtonBox.StandardButton.Apply).setEnabled(
not has_errors
)

def show_project_files_fetching_page(self):
self.stackedWidget.setCurrentWidget(self.getProjectFilesPage)
self.buttonBox.button(QDialogButtonBox.StandardButton.Apply).setVisible(False)
Expand Down
23 changes: 20 additions & 3 deletions qfieldsync/gui/package_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,20 +146,37 @@ def setup_gui(self):
self.dirsToCopyWidget.refresh_tree()

feedback = None
if os.path.exists(self.project.fileName()):
if Path(self.project.fileName()).exists():
feedback = self.project_checker.check(ExportType.Cable)

if feedback and feedback.count > 0:
has_errors = len(feedback.error_feedbacks) > 0

feedback_table = CheckerFeedbackTable(feedback)
self.feedbackTableWrapperLayout.addWidget(feedback_table)
self.feedback_table = CheckerFeedbackTable(feedback)
self.feedback_table.feedback_fixed.connect(
self.refresh_project_compatibility_page
)
self.feedbackTableWrapperLayout.addWidget(self.feedback_table)
self.stackedWidget.setCurrentWidget(self.projectCompatibilityPage)
self.nextButton.setVisible(True)
self.nextButton.setEnabled(not has_errors)
else:
self.show_package_page()

def refresh_project_compatibility_page(self):
# The project file might not exist on disk if the current session is an unsaved, newly created QGIS project
if not Path(self.project.fileName()).exists():
return

feedback = self.project_checker.check(ExportType.Cable)
if feedback.count == 0:
# All issues resolved -> advance directly to package page
self.show_package_page()
else:
self.feedback_table.set_feedback(feedback)
has_errors = len(feedback.error_feedbacks) > 0
self.nextButton.setEnabled(not has_errors)

def get_export_filename_suggestion(self) -> str:
"""Get the suggested export filename"""
export_dirname = self.qfield_preferences.value("exportDirectoryProject")
Expand Down
Loading