diff --git a/python/PyQt6/gui/auto_generated/codeeditors/qgscodeeditorwidget.sip.in b/python/PyQt6/gui/auto_generated/codeeditors/qgscodeeditorwidget.sip.in
index 438655cb8b39..694c0c81e110 100644
--- a/python/PyQt6/gui/auto_generated/codeeditors/qgscodeeditorwidget.sip.in
+++ b/python/PyQt6/gui/auto_generated/codeeditors/qgscodeeditorwidget.sip.in
@@ -10,6 +10,7 @@
+
class QgsCodeEditorWidget : QgsPanelWidget
{
%Docstring(signature="appended")
@@ -50,6 +51,8 @@ feedback, otherwise an integrated message bar will be used.
virtual void showEvent( QShowEvent *event );
+ virtual bool eventFilter( QObject *obj, QEvent *event );
+
QgsCodeEditor *editor();
%Docstring
diff --git a/python/console/console_editor.py b/python/console/console_editor.py
index 15af87bec511..36110378dded 100644
--- a/python/console/console_editor.py
+++ b/python/console/console_editor.py
@@ -93,9 +93,6 @@ def __init__(self,
self.tab_widget: EditorTabWidget = tab_widget
self.code_editor_widget: Optional[QgsCodeEditorWidget] = None
- # recent modification time
- self.lastModified = 0
-
self.setMinimumHeight(120)
self.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
@@ -126,6 +123,12 @@ def __init__(self,
self.modificationChanged.connect(self.editor_tab.modified)
self.modificationAttempted.connect(self.fileReadOnly)
+ def set_code_editor_widget(self, widget: QgsCodeEditorWidget):
+ self.code_editor_widget = widget
+ self.code_editor_widget.loadedExternalChanges.connect(
+ self.loaded_external_changes
+ )
+
def settingsEditor(self):
# Set Python lexer
self.initializeLexer()
@@ -399,25 +402,8 @@ def syntaxCheck(self):
return True
- def focusInEvent(self, e):
- if self.code_editor_widget.filePath():
- if not QFileInfo(self.code_editor_widget.filePath()).exists():
- msgText = QCoreApplication.translate('PythonConsole',
- 'The file "{0}" has been deleted or is not accessible').format(self.code_editor_widget.filePath())
- self.showMessage(msgText,
- level=Qgis.MessageLevel.Critical)
- return
- if self.code_editor_widget.filePath() and self.lastModified != QFileInfo(self.code_editor_widget.filePath()).lastModified():
- self.beginUndoAction()
- self.selectAll()
- self.removeSelectedText()
- self.insert(Path(self.code_editor_widget.filePath()).read_text(encoding='utf-8'))
- self.setModified(False)
- self.endUndoAction()
-
- self.tab_widget.listObject(self.tab_widget.currentWidget())
- self.lastModified = QFileInfo(self.code_editor_widget.filePath()).lastModified()
- super().focusInEvent(e)
+ def loaded_external_changes(self):
+ self.tab_widget.listObject(self.tab_widget.currentWidget())
def fileReadOnly(self):
msgText = QCoreApplication.translate('PythonConsole',
@@ -536,7 +522,7 @@ def __init__(self,
self._editor_code_widget = QgsCodeEditorWidget(
self._editor
)
- self._editor.code_editor_widget = self._editor_code_widget
+ self._editor.set_code_editor_widget(self._editor_code_widget)
self._editor_code_widget.searchBarToggled.connect(
self.search_bar_toggled
)
diff --git a/python/gui/auto_generated/codeeditors/qgscodeeditorwidget.sip.in b/python/gui/auto_generated/codeeditors/qgscodeeditorwidget.sip.in
index 2ef672050241..694c0c81e110 100644
--- a/python/gui/auto_generated/codeeditors/qgscodeeditorwidget.sip.in
+++ b/python/gui/auto_generated/codeeditors/qgscodeeditorwidget.sip.in
@@ -51,6 +51,8 @@ feedback, otherwise an integrated message bar will be used.
virtual void showEvent( QShowEvent *event );
+ virtual bool eventFilter( QObject *obj, QEvent *event );
+
QgsCodeEditor *editor();
%Docstring
diff --git a/src/gui/codeeditors/qgscodeeditorwidget.cpp b/src/gui/codeeditors/qgscodeeditorwidget.cpp
index 49869cc4dc66..4ca1a5f14e34 100644
--- a/src/gui/codeeditors/qgscodeeditorwidget.cpp
+++ b/src/gui/codeeditors/qgscodeeditorwidget.cpp
@@ -42,6 +42,9 @@ QgsCodeEditorWidget::QgsCodeEditorWidget(
{
Q_ASSERT( mEditor );
+ mEditor->installEventFilter( this );
+ installEventFilter( this );
+
QVBoxLayout *vl = new QVBoxLayout();
vl->setContentsMargins( 0, 0, 0, 0 );
vl->setSpacing( 0 );
@@ -242,6 +245,51 @@ void QgsCodeEditorWidget::showEvent( QShowEvent *event )
updateHighlightController();
}
+bool QgsCodeEditorWidget::eventFilter( QObject *obj, QEvent *event )
+{
+ if ( event->type() == QEvent::FocusIn )
+ {
+ if ( !mFilePath.isEmpty() )
+ {
+ if ( !QFile::exists( mFilePath ) )
+ {
+ // file deleted externally
+ if ( mMessageBar )
+ {
+ mMessageBar->pushCritical( QString(), tr( "The file \"%1\" has been deleted or is not accessible" ).arg( QDir::toNativeSeparators( mFilePath ) ) );
+ }
+ }
+ else
+ {
+ const QFileInfo fi( mFilePath );
+ if ( mLastModified != fi.lastModified() )
+ {
+ // TODO - we should give users a choice of how to react to this, eg "ignore changes"
+ // note -- we intentionally don't call loadFile here -- we want this action to be undo-able
+ QFile file( mFilePath );
+ if ( file.open( QFile::ReadOnly ) )
+ {
+ const QString content = file.readAll();
+
+ // don't clear, instead perform undoable actions:
+ mEditor->beginUndoAction();
+ mEditor->selectAll();
+ mEditor->removeSelectedText();
+ mEditor->insert( content );
+ mEditor->setModified( false );
+ mEditor->recolor();
+ mEditor->endUndoAction();
+
+ mLastModified = fi.lastModified();
+ emit loadedExternalChanges();
+ }
+ }
+ }
+ }
+ }
+ return QgsPanelWidget::eventFilter( obj, event );
+}
+
QgsCodeEditorWidget::~QgsCodeEditorWidget() = default;
bool QgsCodeEditorWidget::isSearchBarVisible() const
diff --git a/src/gui/codeeditors/qgscodeeditorwidget.h b/src/gui/codeeditors/qgscodeeditorwidget.h
index 80b6f77f6741..5a61a4d569d4 100644
--- a/src/gui/codeeditors/qgscodeeditorwidget.h
+++ b/src/gui/codeeditors/qgscodeeditorwidget.h
@@ -66,6 +66,7 @@ class GUI_EXPORT QgsCodeEditorWidget : public QgsPanelWidget
void resizeEvent( QResizeEvent *event ) override;
void showEvent( QShowEvent *event ) override;
+ bool eventFilter( QObject *obj, QEvent *event ) override;
/**
* Returns the wrapped code editor.