Skip to content
Permalink
Browse files

Make Model Designer window title follow app conventions

Show unsaved changes via * prefix, show model name in title

Also less Python, more c++
  • Loading branch information
nyalldawson committed Mar 9, 2020
1 parent c4df4dc commit e9c594d3886457a6fd0359c5e0e37e3541c273d8
@@ -43,12 +43,28 @@ Starts an undo command. This should be called before any changes are made to the
void endUndoCommand();
%Docstring
Ends the current undo command. This should be called after changes are made to the model.
%End

QgsProcessingModelAlgorithm *model();
%Docstring
Returns the model shown in the dialog.
%End

void setModel( QgsProcessingModelAlgorithm *model /Transfer/ );
%Docstring
Sets the ``model`` shown in the dialog.

Ownership of ``model`` is transferred to the dialog.
%End

void loadModel( const QString &path );
%Docstring
Loads a model into the designer from the specified file ``path``.
%End

protected:

virtual void repaintModel( bool showControls = true ) = 0;
virtual QgsProcessingModelAlgorithm *model() = 0;
virtual void addAlgorithm( const QString &algorithmId, const QPointF &pos ) = 0;
virtual void addInput( const QString &inputId, const QPointF &pos ) = 0;
virtual void exportAsScriptAlgorithm() = 0;
@@ -59,15 +75,16 @@ Ends the current undo command. This should be called after changes are made to t
QAction *actionSaveInProject();
QAction *actionEditHelp();
QAction *actionRun();
QLineEdit *textName();
QLineEdit *textGroup();
QgsMessageBar *messageBar();
QGraphicsView *view();

void updateVariablesGui();

void setDirty( bool dirty );

bool validateSave();
%Docstring
Checks if the model can current be saved, and returns ``True`` if it can.
%End

};


@@ -82,7 +82,6 @@ def create(model=None):

def __init__(self, model=None, parent=None):
super().__init__(parent)
self._model = None

if iface is not None:
self.toolbar().setIconSize(iface.iconSize())
@@ -104,22 +103,11 @@ def __init__(self, model=None, parent=None):
self.actionRun().triggered.connect(self.runModel)

if model is not None:
self._model = model.create()
self._model.setSourceFilePath(model.sourceFilePath())
self.textGroup().setText(self._model.group())
self.textName().setText(self._model.displayName())
self.repaintModel()

else:
self._model = QgsProcessingModelAlgorithm()
self._model.setProvider(QgsApplication.processingRegistry().providerById('model'))
self.updateVariablesGui()
_model = model.create()
_model.setSourceFilePath(model.sourceFilePath())
self.setModel(_model)

self.view().centerOn(0, 0)
self.help = None

def model(self):
return self._model

def editHelp(self):
alg = self.model()
@@ -145,11 +133,9 @@ def runModel(self):
self.model().setDesignerParameterValues(dlg.getParameterValues())

def saveInProject(self):
if not self.can_save():
if not self.validateSave():
return

self.model().setName(str(self.textName().text()))
self.model().setGroup(str(self.textGroup().text()))
self.model().setSourceFilePath(None)

project_provider = QgsApplication.processingRegistry().providerById(PROJECT_PROVIDER_ID)
@@ -162,24 +148,9 @@ def saveInProject(self):
self.setDirty(False)
QgsProject.instance().setDirty(True)

def can_save(self):
"""
Tests whether a model can be saved, or if it is not yet valid
:return: bool
"""
if str(self.textName().text()).strip() == '':
self.messageBar().pushWarning(
"", self.tr('Please a enter model name before saving')
)
return False

return True

def saveModel(self, saveAs):
if not self.can_save():
if not self.validateSave():
return
self.model().setName(str(self.textName().text()))
self.model().setGroup(str(self.textGroup().text()))
if self.model().sourceFilePath() and not saveAs:
filename = self.model().sourceFilePath()
else:
@@ -221,27 +192,6 @@ def openModel(self):
if filename:
self.loadModel(filename)

def loadModel(self, filename):
alg = QgsProcessingModelAlgorithm()
if alg.fromFile(filename):
self._model = alg
self._model.setProvider(QgsApplication.processingRegistry().providerById('model'))
self.textGroup().setText(alg.group())
self.textName().setText(alg.name())
self.repaintModel()

self.updateVariablesGui()

self.view().centerOn(0, 0)
self.setDirty(False)
else:
QgsMessageLog.logMessage(self.tr('Could not load model {0}').format(filename),
self.tr('Processing'),
Qgis.Critical)
QMessageBox.critical(self, self.tr('Open Model'),
self.tr('The selected model could not be loaded.\n'
'See the log for more information.'))

def repaintModel(self, showControls=True):
self.scene = ModelerScene(self)
self.scene.setSceneRect(QRectF(0, 0, self.CANVAS_SIZE,
@@ -75,13 +75,20 @@ QgsModelDesignerDialog::QgsModelDesignerDialog( QWidget *parent, Qt::WindowFlags
Qt::WindowMaximizeButtonHint |
Qt::WindowCloseButtonHint );

mModel = qgis::make_unique< QgsProcessingModelAlgorithm >();
mModel->setProvider( QgsApplication::processingRegistry()->providerById( QStringLiteral( "model" ) ) );

mUndoStack = new QUndoStack( this );
connect( mUndoStack, &QUndoStack::indexChanged, this, [ = ]
{
if ( mIgnoreUndoStackChanges )
return;

mBlockUndoCommands++;
updateVariablesGui();
mGroupEdit->setText( mModel->group() );
mNameEdit->setText( mModel->displayName() );
mBlockUndoCommands--;
repaintModel();
} );

@@ -183,10 +190,29 @@ QgsModelDesignerDialog::QgsModelDesignerDialog( QWidget *parent, Qt::WindowFlags

connect( mVariablesEditor, &QgsVariableEditorWidget::scopeChanged, this, [ = ]
{
if ( model() )
if ( mModel )
{
beginUndoCommand( tr( "Change Model Variables" ) );
model()->setVariables( mVariablesEditor->variablesInActiveScope() );
mModel->setVariables( mVariablesEditor->variablesInActiveScope() );
endUndoCommand();
}
} );
connect( mNameEdit, &QLineEdit::textChanged, this, [ = ]( const QString & name )
{
if ( mModel )
{
beginUndoCommand( tr( "Change Model Name" ), NameChanged );
mModel->setName( name );
endUndoCommand();
updateWindowTitle();
}
} );
connect( mGroupEdit, &QLineEdit::textChanged, this, [ = ]( const QString & group )
{
if ( mModel )
{
beginUndoCommand( tr( "Change Model Group" ), GroupChanged );
mModel->setGroup( group );
endUndoCommand();
}
} );
@@ -202,6 +228,8 @@ QgsModelDesignerDialog::QgsModelDesignerDialog( QWidget *parent, Qt::WindowFlags

mActionShowComments->setChecked( settings.value( QStringLiteral( "/Processing/Modeler/ShowComments" ), true ).toBool() );
connect( mActionShowComments, &QAction::toggled, this, &QgsModelDesignerDialog::toggleComments );

updateWindowTitle();
}

QgsModelDesignerDialog::~QgsModelDesignerDialog()
@@ -211,7 +239,7 @@ QgsModelDesignerDialog::~QgsModelDesignerDialog()

void QgsModelDesignerDialog::closeEvent( QCloseEvent *event )
{
if ( mHasChanged )
if ( isDirty() )
{
QMessageBox::StandardButton ret = QMessageBox::question( this, tr( "Save Model?" ),
tr( "There are unsaved changes in this model. Do you want to keep those?" ),
@@ -246,7 +274,7 @@ void QgsModelDesignerDialog::beginUndoCommand( const QString &text, int id )
if ( mActiveCommand )
endUndoCommand();

mActiveCommand = qgis::make_unique< QgsModelUndoCommand >( model(), text, id );
mActiveCommand = qgis::make_unique< QgsModelUndoCommand >( mModel.get(), text, id );
}

void QgsModelDesignerDialog::endUndoCommand()
@@ -259,12 +287,49 @@ void QgsModelDesignerDialog::endUndoCommand()
setDirty( true );
}

QgsProcessingModelAlgorithm *QgsModelDesignerDialog::model()
{
return mModel.get();
}

void QgsModelDesignerDialog::setModel( QgsProcessingModelAlgorithm *model )
{
mModel.reset( model );

mGroupEdit->setText( mModel->group() );
mNameEdit->setText( mModel->displayName() );
repaintModel();
updateVariablesGui();

mView->centerOn( 0, 0 );
setDirty( false );
mUndoStack->clear();

updateWindowTitle();
}

void QgsModelDesignerDialog::loadModel( const QString &path )
{
std::unique_ptr< QgsProcessingModelAlgorithm > alg = qgis::make_unique< QgsProcessingModelAlgorithm >();
if ( alg->fromFile( path ) )
{
alg->setProvider( QgsApplication::processingRegistry()->providerById( QStringLiteral( "model" ) ) );
setModel( alg.release() );
}
else
{
QgsMessageLog::logMessage( tr( "Could not load model %1" ).arg( path ), tr( "Processing" ), Qgis::Critical );
QMessageBox::critical( this, tr( "Open Model" ), tr( "The selected model could not be loaded.\n"
"See the log for more information." ) );
}
}

void QgsModelDesignerDialog::updateVariablesGui()
{
mBlockUndoCommands++;

std::unique_ptr< QgsExpressionContextScope > variablesScope = qgis::make_unique< QgsExpressionContextScope >( tr( "Model Variables" ) );
const QVariantMap modelVars = model()->variables();
const QVariantMap modelVars = mModel->variables();
for ( auto it = modelVars.constBegin(); it != modelVars.constEnd(); ++it )
{
variablesScope->setVariable( it.key(), it.value() );
@@ -280,6 +345,18 @@ void QgsModelDesignerDialog::updateVariablesGui()
void QgsModelDesignerDialog::setDirty( bool dirty )
{
mHasChanged = dirty;
updateWindowTitle();
}

bool QgsModelDesignerDialog::validateSave()
{
if ( mNameEdit->text().trimmed().isEmpty() )
{
mMessageBar->pushWarning( QString(), tr( "Please a enter model name before saving" ) );
return false;
}

return true;
}

void QgsModelDesignerDialog::zoomIn()
@@ -392,7 +469,7 @@ void QgsModelDesignerDialog::exportToSvg()
svg.setFileName( filename );
svg.setSize( QSize( totalRect.width(), totalRect.height() ) );
svg.setViewBox( svgRect );
svg.setTitle( model()->displayName() );
svg.setTitle( mModel->displayName() );

QPainter painter( &svg );
mView->scene()->render( &painter, svgRect, totalRect );
@@ -410,7 +487,7 @@ void QgsModelDesignerDialog::exportAsPython()

filename = QgsFileUtils::ensureFileNameHasExtension( filename, QStringList() << QStringLiteral( "py" ) );

const QString text = model()->asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, 4 ).join( '\n' );
const QString text = mModel->asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, 4 ).join( '\n' );

QFile outFile( filename );
if ( !outFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
@@ -431,6 +508,23 @@ void QgsModelDesignerDialog::toggleComments( bool show )
repaintModel( true );
}

void QgsModelDesignerDialog::updateWindowTitle()
{
QString title = tr( "Model Designer" );
if ( !mModel->name().isEmpty() )
title = QStringLiteral( "%1 - %2" ).arg( title, mModel->name() );

if ( isDirty() )
title.prepend( '*' );

setWindowTitle( title );
}

bool QgsModelDesignerDialog::isDirty() const
{
return mHasChanged && mUndoStack->index() == -1;
}

void QgsModelDesignerDialog::fillInputsTree()
{
const QIcon icon = QgsApplication::getThemeIcon( QStringLiteral( "mIconModelInput.svg" ) );

0 comments on commit e9c594d

Please sign in to comment.
You can’t perform that action at this time.