Skip to content

Commit ce17091

Browse files
committedDec 1, 2017
Start moving processing algorithm dialog to c++
In an attempt to avoid Python global interpreter locks which block the UI thread.
1 parent 3c238a2 commit ce17091

11 files changed

+1011
-68
lines changed
 

‎python/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ IF (WITH_GUI)
148148
${CMAKE_SOURCE_DIR}/src/gui/layertree
149149
${CMAKE_SOURCE_DIR}/src/gui/layout
150150
${CMAKE_SOURCE_DIR}/src/gui/locator
151+
${CMAKE_SOURCE_DIR}/src/gui/processing
151152

152153
${CMAKE_BINARY_DIR}/src/gui
153154
)

‎python/gui/gui_auto.sip

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,4 +310,5 @@
310310
%Include layout/qgslayoutviewtooltemporarymousepan.sip
311311
%Include layout/qgslayoutviewtoolzoom.sip
312312
%Include locator/qgslocatorwidget.sip
313+
%Include processing/qgsprocessingalgorithmdialogbase.sip
313314
%Include qgsadvanceddigitizingcanvasitem.sip
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
/************************************************************************
2+
* This file has been generated automatically from *
3+
* *
4+
* src/gui/processing/qgsprocessingalgorithmdialogbase.h *
5+
* *
6+
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
7+
************************************************************************/
8+
9+
10+
11+
12+
13+
14+
class QgsProcessingAlgorithmDialogBase : QDialog
15+
{
16+
%Docstring
17+
Base class for processing algorithm dialogs.
18+
.. note::
19+
20+
This is not considered stable API and may change in future QGIS versions.
21+
.. versionadded:: 3.0
22+
%End
23+
24+
%TypeHeaderCode
25+
#include "qgsprocessingalgorithmdialogbase.h"
26+
%End
27+
public:
28+
29+
QgsProcessingAlgorithmDialogBase( QWidget *parent = 0, Qt::WindowFlags flags = 0 );
30+
%Docstring
31+
Constructor for QgsProcessingAlgorithmDialogBase.
32+
%End
33+
34+
void setAlgorithm( QgsProcessingAlgorithm *algorithm );
35+
%Docstring
36+
Sets the ``algorithm`` to run in the dialog.
37+
.. seealso:: algorithm()
38+
%End
39+
40+
QgsProcessingAlgorithm *algorithm();
41+
%Docstring
42+
Returns the algorithm running in the dialog.
43+
.. seealso:: setAlgorithm()
44+
:rtype: QgsProcessingAlgorithm
45+
%End
46+
47+
void setMainWidget( QWidget *widget /Transfer/ );
48+
%Docstring
49+
Sets the main ``widget`` for the dialog, usually a panel for configuring algorithm parameters.
50+
.. seealso:: mainWidget()
51+
%End
52+
53+
QWidget *mainWidget();
54+
%Docstring
55+
Returns the main widget for the dialog, usually a panel for configuring algorithm parameters.
56+
.. seealso:: setMainWidget()
57+
:rtype: QWidget
58+
%End
59+
60+
void showLog();
61+
%Docstring
62+
Switches the dialog to the log page.
63+
%End
64+
65+
bool wasExecuted() const;
66+
%Docstring
67+
Returns true if an algorithm was executed in the dialog.
68+
:rtype: bool
69+
%End
70+
71+
QgsProcessingFeedback *createFeedback() /Factory/;
72+
%Docstring
73+
Creates a new processing feedback object, automatically connected to the appropriate
74+
slots in this dialog.
75+
:rtype: QgsProcessingFeedback
76+
%End
77+
78+
virtual QVariantMap getParameterValues() const;
79+
%Docstring
80+
Returns the parameter values for the algorithm to run in the dialog.
81+
:rtype: QVariantMap
82+
%End
83+
84+
public slots:
85+
86+
virtual void accept();
87+
88+
virtual void reject();
89+
90+
91+
void reportError( const QString &error );
92+
%Docstring
93+
Reports an ``error`` string to the dialog's log.
94+
%End
95+
96+
void pushInfo( const QString &info );
97+
%Docstring
98+
Pushes an information string to the dialog's log.
99+
%End
100+
101+
void pushDebugInfo( const QString &message );
102+
%Docstring
103+
Pushes a debug info string to the dialog's log.
104+
%End
105+
106+
void pushCommandInfo( const QString &info );
107+
%Docstring
108+
Pushes command info to the dialog's log.
109+
%End
110+
111+
void setPercentage( double percent );
112+
%Docstring
113+
Sets the percentage progress for the dialog, between 0 and 100.
114+
%End
115+
116+
void setProgressText( const QString &text );
117+
%Docstring
118+
Sets a progress text message.
119+
%End
120+
121+
void pushConsoleInfo( const QString &info );
122+
%Docstring
123+
Pushes a console info string to the dialog's log.
124+
%End
125+
126+
protected:
127+
128+
virtual void closeEvent( QCloseEvent *e );
129+
130+
131+
QPushButton *runButton();
132+
%Docstring
133+
Returns the dialog's run button.
134+
:rtype: QPushButton
135+
%End
136+
137+
QPushButton *cancelButton();
138+
%Docstring
139+
Returns the dialog's cancel button.
140+
:rtype: QPushButton
141+
%End
142+
143+
QDialogButtonBox *buttonBox();
144+
%Docstring
145+
Returns the dialog's button box.
146+
:rtype: QDialogButtonBox
147+
%End
148+
149+
void clearProgress();
150+
%Docstring
151+
Clears any current progress from the dialog.
152+
%End
153+
154+
void setExecuted( bool executed );
155+
%Docstring
156+
Sets whether the algorithm was executed through the dialog.
157+
%End
158+
159+
void setInfo( const QString &message, bool isError = false, bool escapeHtml = true );
160+
%Docstring
161+
Displays an info ``message`` in the dialog's log.
162+
%End
163+
164+
void resetGui();
165+
%Docstring
166+
Resets the dialog's gui, ready for another algorithm execution.
167+
%End
168+
169+
QgsMessageBar *messageBar();
170+
%Docstring
171+
Returns the dialog's message bar.
172+
:rtype: QgsMessageBar
173+
%End
174+
175+
protected slots:
176+
177+
virtual void finished( bool successful, const QVariantMap &result, QgsProcessingContext &context, QgsProcessingFeedback *feedback );
178+
%Docstring
179+
Called when the algorithm has finished executing.
180+
%End
181+
182+
};
183+
184+
185+
/************************************************************************
186+
* This file has been generated automatically from *
187+
* *
188+
* src/gui/processing/qgsprocessingalgorithmdialogbase.h *
189+
* *
190+
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
191+
************************************************************************/

‎python/plugins/processing/gui/AlgorithmDialog.py

Lines changed: 47 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@
4343
QgsProcessingParameterFeatureSink,
4444
QgsProcessingParameterRasterDestination,
4545
QgsProcessingAlgorithm)
46-
from qgis.gui import QgsMessageBar
46+
from qgis.gui import (QgsMessageBar,
47+
QgsProcessingAlgorithmDialogBase)
4748
from qgis.utils import iface
4849

4950
from processing.core.ProcessingLog import ProcessingLog
@@ -58,43 +59,38 @@
5859
from processing.tools import dataobjects
5960

6061

61-
class AlgorithmDialog(AlgorithmDialogBase):
62+
class AlgorithmDialog(QgsProcessingAlgorithmDialogBase):
6263

6364
def __init__(self, alg):
64-
AlgorithmDialogBase.__init__(self, alg)
65-
66-
self.alg = alg
65+
super().__init__()
6766

67+
self.setAlgorithm(alg)
6868
self.setMainWidget(self.getParametersPanel(alg, self))
6969

70-
self.bar = QgsMessageBar()
71-
self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
72-
self.layout().insertWidget(0, self.bar)
73-
7470
self.runAsBatchButton = QPushButton(QCoreApplication.translate("AlgorithmDialog", "Run as Batch Process…"))
7571
self.runAsBatchButton.clicked.connect(self.runAsBatch)
76-
self.buttonBox.addButton(self.runAsBatchButton, QDialogButtonBox.ResetRole) # reset role to ensure left alignment
72+
self.buttonBox().addButton(self.runAsBatchButton, QDialogButtonBox.ResetRole) # reset role to ensure left alignment
7773

7874
def getParametersPanel(self, alg, parent):
7975
return ParametersPanel(parent, alg)
8076

8177
def runAsBatch(self):
8278
self.close()
83-
dlg = BatchAlgorithmDialog(self.alg)
79+
dlg = BatchAlgorithmDialog(self.algorithm())
8480
dlg.show()
8581
dlg.exec_()
8682

8783
def getParamValues(self):
8884
parameters = {}
8985

90-
if not hasattr(self, 'mainWidget') or self.mainWidget is None:
86+
if self.mainWidget() is None:
9187
return parameters
9288

93-
for param in self.alg.parameterDefinitions():
89+
for param in self.algorithm().parameterDefinitions():
9490
if param.flags() & QgsProcessingParameterDefinition.FlagHidden:
9591
continue
9692
if not param.isDestination():
97-
wrapper = self.mainWidget.wrappers[param.name()]
93+
wrapper = self.mainWidget().wrappers[param.name()]
9894
value = None
9995
if wrapper.widget:
10096
value = wrapper.value()
@@ -106,10 +102,10 @@ def getParamValues(self):
106102
dest_project = None
107103
if not param.flags() & QgsProcessingParameterDefinition.FlagHidden and \
108104
isinstance(param, (QgsProcessingParameterRasterDestination, QgsProcessingParameterFeatureSink, QgsProcessingParameterVectorDestination)):
109-
if self.mainWidget.checkBoxes[param.name()].isChecked():
105+
if self.mainWidget().checkBoxes[param.name()].isChecked():
110106
dest_project = QgsProject.instance()
111107

112-
value = self.mainWidget.outputWidgets[param.name()].getValue()
108+
value = self.mainWidget().outputWidgets[param.name()].getValue()
113109
if value and isinstance(value, QgsProcessingOutputLayerDefinition):
114110
value.destinationProject = dest_project
115111
if value:
@@ -123,7 +119,7 @@ def checkExtentCRS(self):
123119
context = dataobjects.createContext()
124120
projectCRS = iface.mapCanvas().mapSettings().destinationCrs()
125121
layers = QgsProcessingUtils.compatibleLayers(QgsProject.instance())
126-
for param in self.alg.parameterDefinitions():
122+
for param in self.algorithm().parameterDefinitions():
127123
if isinstance(param, (ParameterRaster, ParameterVector, ParameterMultipleInput)):
128124
if param.value:
129125
if isinstance(param, ParameterMultipleInput):
@@ -144,23 +140,21 @@ def checkExtentCRS(self):
144140
if param.skip_crs_check:
145141
continue
146142

147-
value = self.mainWidget.wrappers[param.name()].widget.leText.text().strip()
143+
value = self.mainWidget().wrappers[param.name()].widget.leText.text().strip()
148144
if value:
149145
hasExtent = True
150146

151147
return hasExtent and unmatchingCRS
152148

153149
def accept(self):
154-
super(AlgorithmDialog, self)._saveGeometry()
155-
156150
feedback = self.createFeedback()
157151
context = dataobjects.createContext(feedback)
158152

159153
checkCRS = ProcessingConfig.getSetting(ProcessingConfig.WARN_UNMATCHING_CRS)
160154
try:
161155
parameters = self.getParamValues()
162156

163-
if checkCRS and not self.alg.validateInputCrs(parameters, context):
157+
if checkCRS and not self.algorithm().validateInputCrs(parameters, context):
164158
reply = QMessageBox.question(self, self.tr("Unmatching CRS's"),
165159
self.tr('Layers do not all use the same CRS. This can '
166160
'cause unexpected results.\nDo you want to '
@@ -181,14 +175,14 @@ def accept(self):
181175
QMessageBox.No)
182176
if reply == QMessageBox.No:
183177
return
184-
ok, msg = self.alg.checkParameterValues(parameters, context)
178+
ok, msg = self.algorithm().checkParameterValues(parameters, context)
185179
if msg:
186180
QMessageBox.warning(
187181
self, self.tr('Unable to execute algorithm'), msg)
188182
return
189-
self.btnRun.setEnabled(False)
190-
self.btnClose.setEnabled(False)
191-
buttons = self.mainWidget.iterateButtons
183+
self.runButton().setEnabled(False)
184+
self.cancelButton().setEnabled(False)
185+
buttons = self.mainWidget().iterateButtons
192186
self.iterateParam = None
193187

194188
for i in range(len(list(buttons.values()))):
@@ -197,41 +191,41 @@ def accept(self):
197191
self.iterateParam = list(buttons.keys())[i]
198192
break
199193

200-
self.progressBar.setMaximum(0)
201-
self.lblProgress.setText(self.tr('Processing algorithm...'))
194+
self.clearProgress()
195+
self.setProgressText(self.tr('Processing algorithm...'))
202196
# Make sure the Log tab is visible before executing the algorithm
203197
try:
204-
self.tabWidget.setCurrentIndex(1)
198+
self.showLog()
205199
self.repaint()
206200
except:
207201
pass
208202

209203
self.setInfo(
210-
self.tr('<b>Algorithm \'{0}\' starting...</b>').format(self.alg.displayName()), escape_html=False)
204+
self.tr('<b>Algorithm \'{0}\' starting...</b>').format(self.algorithm().displayName()), escapeHtml=False)
211205

212206
feedback.pushInfo(self.tr('Input parameters:'))
213207
display_params = []
214208
for k, v in parameters.items():
215-
display_params.append("'" + k + "' : " + self.alg.parameterDefinition(k).valueAsPythonString(v, context))
209+
display_params.append("'" + k + "' : " + self.algorithm().parameterDefinition(k).valueAsPythonString(v, context))
216210
feedback.pushCommandInfo('{ ' + ', '.join(display_params) + ' }')
217211
feedback.pushInfo('')
218212
start_time = time.time()
219213

220214
if self.iterateParam:
221-
self.buttonCancel.setEnabled(self.alg.flags() & QgsProcessingAlgorithm.FlagCanCancel)
222-
if executeIterating(self.alg, parameters, self.iterateParam, context, feedback):
215+
self.cancelButton().setEnabled(self.algorithm().flags() & QgsProcessingAlgorithm.FlagCanCancel)
216+
if executeIterating(self.algorithm(), parameters, self.iterateParam, context, feedback):
223217
feedback.pushInfo(
224218
self.tr('Execution completed in {0:0.2f} seconds'.format(time.time() - start_time)))
225-
self.buttonCancel.setEnabled(False)
219+
self.cancelButton().setEnabled(False)
226220
self.finish(True, parameters, context, feedback)
227221
else:
228-
self.buttonCancel.setEnabled(False)
229-
self.resetGUI()
222+
self.cancelButton().setEnabled(False)
223+
self.resetGui()
230224
else:
231-
command = self.alg.asPythonCommand(parameters, context)
225+
command = self.algorithm().asPythonCommand(parameters, context)
232226
if command:
233227
ProcessingLog.addToLog(command)
234-
self.buttonCancel.setEnabled(self.alg.flags() & QgsProcessingAlgorithm.FlagCanCancel)
228+
self.cancelButton().setEnabled(self.algorithm().flags() & QgsProcessingAlgorithm.FlagCanCancel)
235229

236230
def on_complete(ok, results):
237231
if ok:
@@ -243,49 +237,49 @@ def on_complete(ok, results):
243237
self.tr('Execution failed after {0:0.2f} seconds'.format(time.time() - start_time)))
244238
feedback.pushInfo('')
245239

246-
self.buttonCancel.setEnabled(False)
240+
self.cancelButton().setEnabled(False)
247241
self.finish(ok, results, context, feedback)
248242

249-
task = QgsProcessingAlgRunnerTask(self.alg, parameters, context, feedback)
243+
task = QgsProcessingAlgRunnerTask(self.algorithm(), parameters, context, feedback)
250244
task.executed.connect(on_complete)
251245
QgsApplication.taskManager().addTask(task)
252246

253247
except AlgorithmDialogBase.InvalidParameterValue as e:
254248
try:
255-
self.buttonBox.accepted.connect(lambda e=e:
256-
e.widget.setPalette(QPalette()))
249+
self.buttonBox().accepted.connect(lambda e=e:
250+
e.widget.setPalette(QPalette()))
257251
palette = e.widget.palette()
258252
palette.setColor(QPalette.Base, QColor(255, 255, 0))
259253
e.widget.setPalette(palette)
260254
except:
261255
pass
262-
self.bar.clearWidgets()
263-
self.bar.pushMessage("", self.tr("Wrong or missing parameter value: {0}").format(e.parameter.description()),
264-
level=QgsMessageBar.WARNING, duration=5)
256+
self.messageBar().clearWidgets()
257+
self.messageBar().pushMessage("", self.tr("Wrong or missing parameter value: {0}").format(e.parameter.description()),
258+
level=QgsMessageBar.WARNING, duration=5)
265259

266260
def finish(self, successful, result, context, feedback):
267261
keepOpen = not successful or ProcessingConfig.getSetting(ProcessingConfig.KEEP_DIALOG_OPEN)
268262

269263
if self.iterateParam is None:
270264

271265
# add html results to results dock
272-
for out in self.alg.outputDefinitions():
266+
for out in self.algorithm().outputDefinitions():
273267
if isinstance(out, QgsProcessingOutputHtml) and out.name() in result and result[out.name()]:
274-
resultsList.addResult(icon=self.alg.icon(), name=out.description(),
268+
resultsList.addResult(icon=self.algorithm().icon(), name=out.description(),
275269
result=result[out.name()])
276270

277-
if not handleAlgorithmResults(self.alg, context, feedback, not keepOpen):
278-
self.resetGUI()
271+
if not handleAlgorithmResults(self.algorithm(), context, feedback, not keepOpen):
272+
self.resetGui()
279273
return
280274

281-
self.executed = True
282-
self.setInfo(self.tr('Algorithm \'{0}\' finished').format(self.alg.displayName()), escape_html=False)
275+
self.setExecuted(True)
276+
self.setInfo(self.tr('Algorithm \'{0}\' finished').format(self.algorithm().displayName()), escapeHtml=False)
283277

284278
if not keepOpen:
285279
self.close()
286280
else:
287-
self.resetGUI()
288-
if self.alg.hasHtmlOutputs():
281+
self.resetGui()
282+
if self.algorithm().hasHtmlOutputs():
289283
self.setInfo(
290284
self.tr('HTML output has been generated by this algorithm.'
291-
'\nOpen the results dialog to check it.'), escape_html=False)
285+
'\nOpen the results dialog to check it.'), escapeHtml=False)

‎python/plugins/processing/gui/BatchAlgorithmDialog.py

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@
4141
QgsProcessingOutputString,
4242
QgsProject)
4343

44-
from qgis.gui import QgsMessageBar
44+
from qgis.gui import (QgsMessageBar,
45+
QgsProcessingAlgorithmDialogBase)
4546
from qgis.utils import OverrideCursor
4647

4748
from processing.gui.BatchPanel import BatchPanel
@@ -57,23 +58,17 @@
5758
import codecs
5859

5960

60-
class BatchAlgorithmDialog(AlgorithmDialogBase):
61+
class BatchAlgorithmDialog(QgsProcessingAlgorithmDialogBase):
6162

6263
def __init__(self, alg):
6364
AlgorithmDialogBase.__init__(self, alg)
6465

6566
self.alg = alg
6667

6768
self.setWindowTitle(self.tr('Batch Processing - {0}').format(self.alg.displayName()))
68-
6969
self.setMainWidget(BatchPanel(self, self.alg))
70-
7170
self.textShortHelp.setVisible(False)
7271

73-
self.bar = QgsMessageBar()
74-
self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
75-
self.layout().insertWidget(0, self.bar)
76-
7772
def accept(self):
7873
alg_parameters = []
7974
load = []
@@ -92,9 +87,9 @@ def accept(self):
9287
wrapper = self.mainWidget.wrappers[row][col]
9388
parameters[param.name()] = wrapper.value()
9489
if not param.checkValueIsAcceptable(wrapper.value()):
95-
self.bar.pushMessage("", self.tr('Wrong or missing parameter value: {0} (row {1})').format(
96-
param.description(), row + 1),
97-
level=QgsMessageBar.WARNING, duration=5)
90+
self.messageBar().pushMessage("", self.tr('Wrong or missing parameter value: {0} (row {1})').format(
91+
param.description(), row + 1),
92+
level=QgsMessageBar.WARNING, duration=5)
9893
return
9994
col += 1
10095
count_visible_outputs = 0
@@ -114,9 +109,9 @@ def accept(self):
114109
parameters[out.name()] = text
115110
col += 1
116111
else:
117-
self.bar.pushMessage("", self.tr('Wrong or missing output value: {0} (row {1})').format(
118-
out.description(), row + 1),
119-
level=QgsMessageBar.WARNING, duration=5)
112+
self.messageBar().pushMessage("", self.tr('Wrong or missing output value: {0} (row {1})').format(
113+
out.description(), row + 1),
114+
level=QgsMessageBar.WARNING, duration=5)
120115
return
121116

122117
alg_parameters.append(parameters)

‎python/plugins/processing/gui/ProcessingToolbox.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ def executeAlgorithm(self):
293293
except:
294294
pass
295295
canvas.setMapTool(prevMapTool)
296-
if dlg.executed:
296+
if dlg.wasExecuted():
297297
showRecent = ProcessingConfig.getSetting(
298298
ProcessingConfig.SHOW_RECENT_ALGORITHMS)
299299
if showRecent:

‎src/gui/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,8 @@ SET(QGIS_GUI_SRCS
188188
ogr/qgsnewogrconnection.cpp
189189
ogr/qgsvectorlayersaveasdialog.cpp
190190

191+
processing/qgsprocessingalgorithmdialogbase.cpp
192+
191193
qgisinterface.cpp
192194
qgsactionmenu.cpp
193195
qgsadvanceddigitizingcanvasitem.cpp
@@ -697,6 +699,8 @@ SET(QGIS_GUI_MOC_HDRS
697699
layout/qgslayoutviewtoolzoom.h
698700

699701
locator/qgslocatorwidget.h
702+
703+
processing/qgsprocessingalgorithmdialogbase.h
700704
)
701705
SET_PROPERTY(GLOBAL PROPERTY QGIS_GUI_MOC_HDRS ${QGIS_GUI_MOC_HDRS})
702706

Lines changed: 356 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,356 @@
1+
/***************************************************************************
2+
qgsprocessingalgorithmdialogbase.cpp
3+
------------------------------------
4+
Date : November 2017
5+
Copyright : (C) 2017 Nyall Dawson
6+
Email : nyall dot dawson at gmail dot com
7+
***************************************************************************
8+
* *
9+
* This program is free software; you can redistribute it and/or modify *
10+
* it under the terms of the GNU General Public License as published by *
11+
* the Free Software Foundation; either version 2 of the License, or *
12+
* (at your option) any later version. *
13+
* *
14+
***************************************************************************/
15+
16+
#include "qgsprocessingalgorithmdialogbase.h"
17+
#include "qgssettings.h"
18+
#include "qgshelp.h"
19+
#include "qgsmessagebar.h"
20+
#include "processing/qgsprocessingalgorithm.h"
21+
#include "processing/qgsprocessingprovider.h"
22+
#include <QToolButton>
23+
#include <QDesktopServices>
24+
25+
///@cond NOT_STABLE
26+
27+
QgsProcessingAlgorithmDialogFeedback::QgsProcessingAlgorithmDialogFeedback( QgsProcessingAlgorithmDialogBase *dialog )
28+
: QgsProcessingFeedback()
29+
, mDialog( dialog )
30+
{
31+
}
32+
33+
void QgsProcessingAlgorithmDialogFeedback::setProgressText( const QString &text )
34+
{
35+
mDialog->setProgressText( text );
36+
}
37+
38+
void QgsProcessingAlgorithmDialogFeedback::reportError( const QString &error )
39+
{
40+
mDialog->reportError( error );
41+
}
42+
43+
void QgsProcessingAlgorithmDialogFeedback::pushInfo( const QString &info )
44+
{
45+
mDialog->pushInfo( info );
46+
}
47+
48+
void QgsProcessingAlgorithmDialogFeedback::pushCommandInfo( const QString &info )
49+
{
50+
mDialog->pushCommandInfo( info );
51+
}
52+
53+
void QgsProcessingAlgorithmDialogFeedback::pushDebugInfo( const QString &info )
54+
{
55+
mDialog->pushDebugInfo( info );
56+
}
57+
58+
void QgsProcessingAlgorithmDialogFeedback::pushConsoleInfo( const QString &info )
59+
{
60+
mDialog->pushConsoleInfo( info );
61+
}
62+
63+
//
64+
// QgsProcessingAlgorithmDialogBase
65+
//
66+
67+
QgsProcessingAlgorithmDialogBase::QgsProcessingAlgorithmDialogBase( QWidget *parent, Qt::WindowFlags flags )
68+
: QDialog( parent, flags )
69+
{
70+
setupUi( this );
71+
72+
//don't collapse parameters panel
73+
splitter->setCollapsible( 0, false );
74+
75+
// add collapse button to splitter
76+
QSplitterHandle *splitterHandle = splitter->handle( 1 );
77+
QVBoxLayout *handleLayout = new QVBoxLayout();
78+
handleLayout->setContentsMargins( 0, 0, 0, 0 );
79+
mButtonCollapse = new QToolButton( splitterHandle );
80+
mButtonCollapse->setAutoRaise( true );
81+
mButtonCollapse->setFixedSize( 12, 12 );
82+
mButtonCollapse->setCursor( Qt::ArrowCursor );
83+
handleLayout->addWidget( mButtonCollapse );
84+
handleLayout->addStretch();
85+
splitterHandle->setLayout( handleLayout );
86+
87+
QgsSettings settings;
88+
restoreGeometry( settings.value( QStringLiteral( "/Processing/dialogBase" ) ).toByteArray() );
89+
splitter->restoreState( settings.value( QStringLiteral( "/Processing/dialogBaseSplitter" ), QByteArray() ).toByteArray() );
90+
mSplitterState = splitter->saveState();
91+
splitterChanged( 0, 0 );
92+
93+
connect( mButtonBox, &QDialogButtonBox::rejected, this, &QgsProcessingAlgorithmDialogBase::reject );
94+
connect( mButtonBox, &QDialogButtonBox::accepted, this, &QgsProcessingAlgorithmDialogBase::accept );
95+
96+
// Rename OK button to Run
97+
mButtonRun = mButtonBox->button( QDialogButtonBox::Ok );
98+
mButtonRun->setText( tr( "Run" ) );
99+
100+
buttonCancel->setEnabled( false );
101+
mButtonClose = mButtonBox->button( QDialogButtonBox::Close );
102+
103+
connect( mButtonBox, &QDialogButtonBox::helpRequested, this, &QgsProcessingAlgorithmDialogBase::openHelp );
104+
connect( mButtonCollapse, &QToolButton::clicked, this, &QgsProcessingAlgorithmDialogBase::toggleCollapsed );
105+
connect( splitter, &QSplitter::splitterMoved, this, &QgsProcessingAlgorithmDialogBase::splitterChanged );
106+
107+
mMessageBar = new QgsMessageBar();
108+
mMessageBar->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Fixed );
109+
verticalLayout->insertWidget( 0, mMessageBar );
110+
}
111+
112+
void QgsProcessingAlgorithmDialogBase::setAlgorithm( QgsProcessingAlgorithm *algorithm )
113+
{
114+
mAlgorithm = algorithm;
115+
setWindowTitle( mAlgorithm->displayName() );
116+
117+
QString algHelp = formatHelp( algorithm );
118+
if ( algHelp.isEmpty() )
119+
textShortHelp->hide();
120+
else
121+
{
122+
textShortHelp->document()->setDefaultStyleSheet( QStringLiteral( ".summary { margin-left: 10px; margin-right: 10px; }\n"
123+
"h2 { color: #555555; padding-bottom: 15px; }\n"
124+
"a { text - decoration: none; color: #3498db; font-weight: bold; }\n"
125+
"p { color: #666666; }\n"
126+
"b { color: #333333; }\n"
127+
"dl dd { margin - bottom: 5px; }" ) );
128+
textShortHelp->setHtml( algHelp );
129+
connect( textShortHelp, &QTextBrowser::anchorClicked, this, &QgsProcessingAlgorithmDialogBase::linkClicked );
130+
}
131+
}
132+
133+
QgsProcessingAlgorithm *QgsProcessingAlgorithmDialogBase::algorithm()
134+
{
135+
return mAlgorithm;
136+
}
137+
138+
void QgsProcessingAlgorithmDialogBase::setMainWidget( QWidget *widget )
139+
{
140+
if ( mMainWidget )
141+
{
142+
//QgsProject.instance().layerWasAdded.disconnect( self.mainWidget.layerRegistryChanged )
143+
//QgsProject.instance().layersWillBeRemoved.disconnect( self.mainWidget.layerRegistryChanged )
144+
mMainWidget->deleteLater();
145+
}
146+
147+
mMainWidget = widget;
148+
tabWidget->widget( 0 )->layout()->addWidget( mMainWidget );
149+
//QgsProject.instance().layerWasAdded.connect( self.mainWidget.layerRegistryChanged )
150+
//QgsProject.instance().layersWillBeRemoved.connect( self.mainWidget.layerRegistryChanged )
151+
}
152+
153+
QWidget *QgsProcessingAlgorithmDialogBase::mainWidget()
154+
{
155+
return mMainWidget;
156+
}
157+
158+
QVariantMap QgsProcessingAlgorithmDialogBase::getParameterValues() const
159+
{
160+
return QVariantMap();
161+
}
162+
163+
QgsProcessingFeedback *QgsProcessingAlgorithmDialogBase::createFeedback()
164+
{
165+
auto feedback = qgis::make_unique< QgsProcessingAlgorithmDialogFeedback >( this );
166+
connect( feedback.get(), &QgsProcessingFeedback::progressChanged, this, &QgsProcessingAlgorithmDialogBase::setPercentage );
167+
connect( buttonCancel, &QPushButton::clicked, feedback.get(), &QgsProcessingFeedback::cancel );
168+
return feedback.release();
169+
}
170+
171+
QDialogButtonBox *QgsProcessingAlgorithmDialogBase::buttonBox()
172+
{
173+
return mButtonBox;
174+
}
175+
176+
void QgsProcessingAlgorithmDialogBase::showLog()
177+
{
178+
tabWidget->setCurrentIndex( 1 );
179+
}
180+
181+
void QgsProcessingAlgorithmDialogBase::closeEvent( QCloseEvent *e )
182+
{
183+
saveWindowGeometry();
184+
QDialog::closeEvent( e );
185+
}
186+
187+
QPushButton *QgsProcessingAlgorithmDialogBase::runButton()
188+
{
189+
return mButtonRun;
190+
}
191+
192+
QPushButton *QgsProcessingAlgorithmDialogBase::cancelButton()
193+
{
194+
return buttonCancel;
195+
}
196+
197+
void QgsProcessingAlgorithmDialogBase::clearProgress()
198+
{
199+
progressBar->setMaximum( 0 );
200+
}
201+
202+
void QgsProcessingAlgorithmDialogBase::setExecuted( bool executed )
203+
{
204+
mExecuted = executed;
205+
}
206+
207+
void QgsProcessingAlgorithmDialogBase::finished( bool, const QVariantMap &, QgsProcessingContext &, QgsProcessingFeedback * )
208+
{
209+
210+
}
211+
212+
void QgsProcessingAlgorithmDialogBase::accept()
213+
{
214+
}
215+
216+
void QgsProcessingAlgorithmDialogBase::reject()
217+
{
218+
saveWindowGeometry();
219+
QDialog::reject();
220+
}
221+
222+
void QgsProcessingAlgorithmDialogBase::openHelp()
223+
{
224+
QUrl algHelp = mAlgorithm->helpUrl();
225+
if ( algHelp.isEmpty() )
226+
{
227+
algHelp = QgsHelp::helpUrl( QStringLiteral( "processing_algs/%1/%2" ).arg( mAlgorithm->provider()->id(), mAlgorithm->id() ) );
228+
}
229+
230+
if ( !algHelp.isEmpty() )
231+
QDesktopServices::openUrl( algHelp );
232+
}
233+
234+
void QgsProcessingAlgorithmDialogBase::toggleCollapsed()
235+
{
236+
if ( mHelpCollapsed )
237+
{
238+
splitter->restoreState( mSplitterState );
239+
mButtonCollapse->setArrowType( Qt::RightArrow );
240+
}
241+
else
242+
{
243+
mSplitterState = splitter->saveState();
244+
splitter->setSizes( QList<int>() << 1 << 0 );
245+
mButtonCollapse->setArrowType( Qt::LeftArrow );
246+
}
247+
mHelpCollapsed = !mHelpCollapsed;
248+
}
249+
250+
void QgsProcessingAlgorithmDialogBase::splitterChanged( int, int )
251+
{
252+
if ( splitter->sizes().at( 1 ) == 0 )
253+
{
254+
mHelpCollapsed = true;
255+
mButtonCollapse->setArrowType( Qt::LeftArrow );
256+
}
257+
else
258+
{
259+
mHelpCollapsed = false;
260+
mButtonCollapse->setArrowType( Qt::RightArrow );
261+
}
262+
}
263+
264+
void QgsProcessingAlgorithmDialogBase::linkClicked( const QUrl &url )
265+
{
266+
QDesktopServices::openUrl( url.toString() );
267+
}
268+
269+
void QgsProcessingAlgorithmDialogBase::reportError( const QString &error )
270+
{
271+
setInfo( error, true );
272+
resetGui();
273+
tabWidget->setCurrentIndex( 1 );
274+
}
275+
276+
void QgsProcessingAlgorithmDialogBase::pushInfo( const QString &info )
277+
{
278+
setInfo( info );
279+
}
280+
281+
void QgsProcessingAlgorithmDialogBase::pushCommandInfo( const QString &command )
282+
{
283+
txtLog->append( QStringLiteral( "<code>%1<code>" ).arg( command.toHtmlEscaped() ) );
284+
}
285+
286+
void QgsProcessingAlgorithmDialogBase::pushDebugInfo( const QString &message )
287+
{
288+
txtLog->append( QStringLiteral( "<span style=\"color:blue\">%1</span>" ).arg( message.toHtmlEscaped() ) );
289+
}
290+
291+
void QgsProcessingAlgorithmDialogBase::pushConsoleInfo( const QString &info )
292+
{
293+
txtLog->append( QStringLiteral( "<code><span style=\"color:blue\">%1</darkgray></code>" ).arg( info.toHtmlEscaped() ) );
294+
}
295+
296+
void QgsProcessingAlgorithmDialogBase::setPercentage( double percent )
297+
{
298+
// delay setting maximum progress value until we know algorithm reports progress
299+
if ( progressBar->maximum() == 0 )
300+
progressBar->setMaximum( 100 );
301+
progressBar->setValue( percent );
302+
}
303+
304+
void QgsProcessingAlgorithmDialogBase::setProgressText( const QString &text )
305+
{
306+
lblProgress->setText( text );
307+
setInfo( text, false );
308+
}
309+
310+
QString QgsProcessingAlgorithmDialogBase::formatHelp( QgsProcessingAlgorithm *algorithm )
311+
{
312+
QString text = algorithm->shortHelpString();
313+
if ( !text.isEmpty() )
314+
{
315+
QStringList paragraphs = text.split( '\n' );
316+
QString help;
317+
for ( const QString &paragraph : paragraphs )
318+
{
319+
help += QStringLiteral( "<p>%1</p>" ).arg( paragraph );
320+
}
321+
return QStringLiteral( "<h2>%1</h2>%2" ).arg( algorithm->displayName(), help );
322+
}
323+
else
324+
return QString();
325+
}
326+
327+
void QgsProcessingAlgorithmDialogBase::saveWindowGeometry()
328+
{
329+
330+
}
331+
332+
void QgsProcessingAlgorithmDialogBase::resetGui()
333+
{
334+
lblProgress->clear();
335+
progressBar->setMaximum( 100 );
336+
progressBar->setValue( 0 );
337+
mButtonRun->setEnabled( true );
338+
mButtonClose->setEnabled( true );
339+
}
340+
341+
QgsMessageBar *QgsProcessingAlgorithmDialogBase::messageBar()
342+
{
343+
return mMessageBar;
344+
}
345+
346+
void QgsProcessingAlgorithmDialogBase::setInfo( const QString &message, bool isError, bool escapeHtml )
347+
{
348+
if ( isError )
349+
txtLog->append( QStringLiteral( "<span style=\"color:red\">%1</span><br />" ).arg( message ) );
350+
else if ( escapeHtml )
351+
txtLog->append( message.toHtmlEscaped() );
352+
else
353+
txtLog->append( message );
354+
}
355+
356+
///@endcond
Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
/***************************************************************************
2+
qgsprocessingalgorithmdialogbase.h
3+
----------------------------------
4+
Date : November 2017
5+
Copyright : (C) 2017 Nyall Dawson
6+
Email : nyall dot dawson at gmail dot com
7+
***************************************************************************
8+
* *
9+
* This program is free software; you can redistribute it and/or modify *
10+
* it under the terms of the GNU General Public License as published by *
11+
* the Free Software Foundation; either version 2 of the License, or *
12+
* (at your option) any later version. *
13+
* *
14+
***************************************************************************/
15+
16+
#ifndef QGSPROCESSINGALGORITHMDIALOGBASE_H
17+
#define QGSPROCESSINGALGORITHMDIALOGBASE_H
18+
19+
#include "qgis.h"
20+
#include "qgis_gui.h"
21+
#include "ui_qgsprocessingalgorithmdialogbase.h"
22+
#include "processing/qgsprocessingcontext.h"
23+
#include "processing/qgsprocessingfeedback.h"
24+
25+
///@cond NOT_STABLE
26+
27+
class QgsProcessingAlgorithm;
28+
class QToolButton;
29+
class QgsProcessingAlgorithmDialogBase;
30+
class QgsMessageBar;
31+
32+
#ifndef SIP_RUN
33+
34+
/**
35+
* \ingroup gui
36+
* \brief QgsProcessingFeedback subclass linked to a QgsProcessingAlgorithmDialogBase
37+
* \note Not stable API
38+
* \since QGIS 3.0
39+
*/
40+
class QgsProcessingAlgorithmDialogFeedback : public QgsProcessingFeedback
41+
{
42+
Q_OBJECT
43+
44+
public:
45+
46+
/**
47+
* Constructor for QgsProcessingAlgorithmDialogFeedback.
48+
*/
49+
QgsProcessingAlgorithmDialogFeedback( QgsProcessingAlgorithmDialogBase *dialog );
50+
51+
public slots:
52+
53+
void setProgressText( const QString &text ) override;
54+
void reportError( const QString &error ) override;
55+
void pushInfo( const QString &info ) override;
56+
void pushCommandInfo( const QString &info ) override;
57+
void pushDebugInfo( const QString &info ) override;
58+
void pushConsoleInfo( const QString &info ) override;
59+
60+
private:
61+
62+
QgsProcessingAlgorithmDialogBase *mDialog = nullptr;
63+
64+
};
65+
#endif
66+
67+
/**
68+
* \ingroup gui
69+
* \brief Base class for processing algorithm dialogs.
70+
* \note This is not considered stable API and may change in future QGIS versions.
71+
* \since QGIS 3.0
72+
*/
73+
class GUI_EXPORT QgsProcessingAlgorithmDialogBase : public QDialog, private Ui::QgsProcessingDialogBase
74+
{
75+
Q_OBJECT
76+
77+
public:
78+
79+
/**
80+
* Constructor for QgsProcessingAlgorithmDialogBase.
81+
*/
82+
QgsProcessingAlgorithmDialogBase( QWidget *parent = nullptr, Qt::WindowFlags flags = 0 );
83+
84+
/**
85+
* Sets the \a algorithm to run in the dialog.
86+
* \see algorithm()
87+
*/
88+
void setAlgorithm( QgsProcessingAlgorithm *algorithm );
89+
90+
/**
91+
* Returns the algorithm running in the dialog.
92+
* \see setAlgorithm()
93+
*/
94+
QgsProcessingAlgorithm *algorithm();
95+
96+
/**
97+
* Sets the main \a widget for the dialog, usually a panel for configuring algorithm parameters.
98+
* \see mainWidget()
99+
*/
100+
void setMainWidget( QWidget *widget SIP_TRANSFER );
101+
102+
/**
103+
* Returns the main widget for the dialog, usually a panel for configuring algorithm parameters.
104+
* \see setMainWidget()
105+
*/
106+
QWidget *mainWidget();
107+
108+
/**
109+
* Switches the dialog to the log page.
110+
*/
111+
void showLog();
112+
113+
/**
114+
* Returns true if an algorithm was executed in the dialog.
115+
*/
116+
bool wasExecuted() const { return mExecuted; }
117+
118+
/**
119+
* Creates a new processing feedback object, automatically connected to the appropriate
120+
* slots in this dialog.
121+
*/
122+
QgsProcessingFeedback *createFeedback() SIP_FACTORY;
123+
124+
/**
125+
* Returns the parameter values for the algorithm to run in the dialog.
126+
*/
127+
virtual QVariantMap getParameterValues() const;
128+
129+
public slots:
130+
131+
void accept() override;
132+
void reject() override;
133+
134+
/**
135+
* Reports an \a error string to the dialog's log.
136+
*/
137+
void reportError( const QString &error );
138+
139+
/**
140+
* Pushes an information string to the dialog's log.
141+
*/
142+
void pushInfo( const QString &info );
143+
144+
/**
145+
* Pushes a debug info string to the dialog's log.
146+
*/
147+
void pushDebugInfo( const QString &message );
148+
149+
/**
150+
* Pushes command info to the dialog's log.
151+
*/
152+
void pushCommandInfo( const QString &info );
153+
154+
/**
155+
* Sets the percentage progress for the dialog, between 0 and 100.
156+
*/
157+
void setPercentage( double percent );
158+
159+
/**
160+
* Sets a progress text message.
161+
*/
162+
void setProgressText( const QString &text );
163+
164+
/**
165+
* Pushes a console info string to the dialog's log.
166+
*/
167+
void pushConsoleInfo( const QString &info );
168+
169+
protected:
170+
171+
void closeEvent( QCloseEvent *e ) override;
172+
173+
/**
174+
* Returns the dialog's run button.
175+
*/
176+
QPushButton *runButton();
177+
178+
/**
179+
* Returns the dialog's cancel button.
180+
*/
181+
QPushButton *cancelButton();
182+
183+
/**
184+
* Returns the dialog's button box.
185+
*/
186+
QDialogButtonBox *buttonBox();
187+
188+
/**
189+
* Clears any current progress from the dialog.
190+
*/
191+
void clearProgress();
192+
193+
/**
194+
* Sets whether the algorithm was executed through the dialog.
195+
*/
196+
void setExecuted( bool executed );
197+
198+
/**
199+
* Displays an info \a message in the dialog's log.
200+
*/
201+
void setInfo( const QString &message, bool isError = false, bool escapeHtml = true );
202+
203+
/**
204+
* Resets the dialog's gui, ready for another algorithm execution.
205+
*/
206+
void resetGui();
207+
208+
/**
209+
* Returns the dialog's message bar.
210+
*/
211+
QgsMessageBar *messageBar();
212+
213+
protected slots:
214+
215+
/**
216+
* Called when the algorithm has finished executing.
217+
*/
218+
virtual void finished( bool successful, const QVariantMap &result, QgsProcessingContext &context, QgsProcessingFeedback *feedback );
219+
220+
private slots:
221+
222+
void openHelp();
223+
void toggleCollapsed();
224+
225+
void splitterChanged( int pos, int index );
226+
void linkClicked( const QUrl &url );
227+
228+
private:
229+
230+
QPushButton *mButtonRun = nullptr;
231+
QPushButton *mButtonClose = nullptr;
232+
QByteArray mSplitterState;
233+
QToolButton *mButtonCollapse = nullptr;
234+
QgsMessageBar *mMessageBar = nullptr;
235+
236+
bool mExecuted = false;
237+
QWidget *mMainWidget = nullptr;
238+
QgsProcessingAlgorithm *mAlgorithm = nullptr;
239+
bool mHelpCollapsed = false;
240+
241+
QString formatHelp( QgsProcessingAlgorithm *algorithm );
242+
void saveWindowGeometry();
243+
244+
};
245+
246+
///@endcond
247+
248+
#endif // QGSPROCESSINGALGORITHMDIALOGBASE_H

‎src/ui/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ FILE(GLOB EDITORWIDGET_UIS "${CMAKE_CURRENT_SOURCE_DIR}/editorwidgets/*.ui")
55
FILE(GLOB PAINTEFFECT_UIS "${CMAKE_CURRENT_SOURCE_DIR}/effects/*.ui")
66
FILE(GLOB COMPOSER_UIS "${CMAKE_CURRENT_SOURCE_DIR}/composer/*.ui")
77
FILE(GLOB LAYOUT_UIS "${CMAKE_CURRENT_SOURCE_DIR}/layout/*.ui")
8+
FILE(GLOB PROCESSING_UIS "${CMAKE_CURRENT_SOURCE_DIR}/processing/*.ui")
89
FILE(GLOB AUTH_UIS "${CMAKE_CURRENT_SOURCE_DIR}/auth/*.ui")
910
FILE(GLOB RASTER_UIS "${CMAKE_CURRENT_SOURCE_DIR}/raster/*.ui")
1011
FILE(GLOB STYLEDOCK_UIS "${CMAKE_CURRENT_SOURCE_DIR}/styledock/*.ui")
@@ -16,6 +17,7 @@ QT5_WRAP_UI(QGIS_UIS_H
1617
${EDITORWIDGET_UIS}
1718
${PAINTEFFECT_UIS}
1819
${COMPOSER_UIS}
20+
${PROCESSING_UIS}
1921
${AUTH_UIS}
2022
${RASTER_UIS}
2123
${STYLEDOCK_UIS}
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<ui version="4.0">
3+
<class>QgsProcessingDialogBase</class>
4+
<widget class="QDialog" name="QgsProcessingDialogBase">
5+
<property name="geometry">
6+
<rect>
7+
<x>0</x>
8+
<y>0</y>
9+
<width>688</width>
10+
<height>523</height>
11+
</rect>
12+
</property>
13+
<property name="windowTitle">
14+
<string>Dialog</string>
15+
</property>
16+
<layout class="QVBoxLayout" name="verticalLayout">
17+
<item>
18+
<widget class="QSplitter" name="splitter">
19+
<property name="sizePolicy">
20+
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
21+
<horstretch>0</horstretch>
22+
<verstretch>0</verstretch>
23+
</sizepolicy>
24+
</property>
25+
<property name="orientation">
26+
<enum>Qt::Horizontal</enum>
27+
</property>
28+
<property name="handleWidth">
29+
<number>16</number>
30+
</property>
31+
<widget class="QTabWidget" name="tabWidget">
32+
<property name="sizePolicy">
33+
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
34+
<horstretch>2</horstretch>
35+
<verstretch>0</verstretch>
36+
</sizepolicy>
37+
</property>
38+
<property name="currentIndex">
39+
<number>0</number>
40+
</property>
41+
<widget class="QWidget" name="tab">
42+
<attribute name="title">
43+
<string>Parameters</string>
44+
</attribute>
45+
<layout class="QVBoxLayout" name="verticalLayout_4">
46+
<property name="spacing">
47+
<number>2</number>
48+
</property>
49+
<property name="leftMargin">
50+
<number>0</number>
51+
</property>
52+
<property name="topMargin">
53+
<number>0</number>
54+
</property>
55+
<property name="rightMargin">
56+
<number>0</number>
57+
</property>
58+
<property name="bottomMargin">
59+
<number>0</number>
60+
</property>
61+
</layout>
62+
</widget>
63+
<widget class="QWidget" name="tab_2">
64+
<attribute name="title">
65+
<string>Log</string>
66+
</attribute>
67+
<layout class="QVBoxLayout" name="verticalLayout_2">
68+
<property name="spacing">
69+
<number>2</number>
70+
</property>
71+
<property name="leftMargin">
72+
<number>0</number>
73+
</property>
74+
<property name="topMargin">
75+
<number>0</number>
76+
</property>
77+
<property name="rightMargin">
78+
<number>0</number>
79+
</property>
80+
<property name="bottomMargin">
81+
<number>0</number>
82+
</property>
83+
<item>
84+
<widget class="QTextEdit" name="txtLog">
85+
<property name="readOnly">
86+
<bool>true</bool>
87+
</property>
88+
</widget>
89+
</item>
90+
</layout>
91+
</widget>
92+
</widget>
93+
<widget class="QTextBrowser" name="textShortHelp">
94+
<property name="sizePolicy">
95+
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
96+
<horstretch>0</horstretch>
97+
<verstretch>0</verstretch>
98+
</sizepolicy>
99+
</property>
100+
<property name="accessibleName">
101+
<string/>
102+
</property>
103+
<property name="openLinks">
104+
<bool>false</bool>
105+
</property>
106+
</widget>
107+
</widget>
108+
</item>
109+
<item>
110+
<widget class="QLabel" name="lblProgress">
111+
<property name="text">
112+
<string/>
113+
</property>
114+
</widget>
115+
</item>
116+
<item>
117+
<layout class="QHBoxLayout" name="horizontalLayout_3">
118+
<property name="topMargin">
119+
<number>0</number>
120+
</property>
121+
<item>
122+
<widget class="QProgressBar" name="progressBar">
123+
<property name="value">
124+
<number>0</number>
125+
</property>
126+
</widget>
127+
</item>
128+
<item>
129+
<widget class="QPushButton" name="buttonCancel">
130+
<property name="text">
131+
<string>Cancel</string>
132+
</property>
133+
</widget>
134+
</item>
135+
</layout>
136+
</item>
137+
<item>
138+
<widget class="QDialogButtonBox" name="mButtonBox">
139+
<property name="orientation">
140+
<enum>Qt::Horizontal</enum>
141+
</property>
142+
<property name="standardButtons">
143+
<set>QDialogButtonBox::Close|QDialogButtonBox::Help|QDialogButtonBox::Ok</set>
144+
</property>
145+
</widget>
146+
</item>
147+
</layout>
148+
</widget>
149+
<resources/>
150+
<connections/>
151+
</ui>

0 commit comments

Comments
 (0)
Please sign in to comment.