-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathQtDesignerForPython.html
271 lines (221 loc) · 18.1 KB
/
QtDesignerForPython.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
<!DOCTYPE html>
<html lang="en" data-content_root="./">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Qt Designer for Python</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=03e43079" />
<link rel="stylesheet" type="text/css" href="_static/bootstrap-sphinx.css?v=fadd4351" />
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=77160d70" />
<script src="_static/documentation_options.js?v=a8da1a53"></script>
<script src="_static/doctools.js?v=9bcbadda"></script>
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
<link rel="index" title="Index" href="genindex.html" />
<link rel="search" title="Search" href="search.html" />
<link rel="next" title="Mockups with Balsamiq Wireframes" href="BalsamiqWireframes.html" />
<link rel="prev" title="MVP Calculator GUI Example" href="MVPTutorial/CalculatorExample/index.html" />
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-59110517-1', 'auto');
ga('send', 'pageview');
</script>
</head><body>
<div id="navbar" class="navbar navbar-default ">
<div class="container">
<div class="navbar-header">
<!-- .btn-navbar is used as the toggle for collapsed navbar content -->
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".nav-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="http://www.mantidproject.org">
</a>
<span class="navbar-text navbar-version pull-left"><b>main</b></span>
</div>
<div class="collapse navbar-collapse nav-collapse">
<ul class="nav navbar-nav">
<li class="divider-vertical"></li>
<li><a href="index.html">Home</a></li>
<li><a href="https://download.mantidproject.org">Download</a></li>
<li><a href="https://docs.mantidproject.org">User Documentation</a></li>
<li><a href="http://www.mantidproject.org/contact">Contact Us</a></li>
</ul>
<form class="navbar-form navbar-right" action="search.html" method="get">
<div class="form-group">
<input type="text" name="q" class="form-control" placeholder="Search" />
</div>
<input type="hidden" name="check_keywords" value="yes" />
<input type="hidden" name="area" value="default" />
</form>
</div>
</div>
<p>
<div class="related" role="navigation" aria-label="related navigation">
<h3>Navigation</h3>
<ul>
<li class="nav-item nav-item-0"><a href="index.html">Documentation</a> »</li>
<li class="nav-item nav-item-this"><a href="">Qt Designer for Python</a></li>
</ul>
</div> </p>
</div>
<div class="container">
<div class="row">
<div class="body col-md-12 content" role="main">
<section id="qt-designer-for-python">
<span id="qtdesignerforpython"></span><h1>Qt Designer for Python<a class="headerlink" href="#qt-designer-for-python" title="Link to this heading">¶</a></h1>
<section id="motivation">
<h2>Motivation<a class="headerlink" href="#motivation" title="Link to this heading">¶</a></h2>
<p>Code for setting up individual widgets and the layout of a view can
become large and difficult to maintain by hand. It usually easier to
edit such code using a drag and drop WYSIWYG (What You See Is What You
Get) editor such as Qt Creator/Designer. However, doing so requires some
additional actions to implement your layout into a view script.</p>
</section>
<section id="implementation">
<h2>Implementation<a class="headerlink" href="#implementation" title="Link to this heading">¶</a></h2>
<p>Qt Creator was not originally designed to work with Python, and it is
therefore not possible to directly save or export the layout as a
Python script. Instead you must first save the layout in a <code class="docutils literal notranslate"><span class="pre">.ui</span></code>
file and use a separate package to to convert it to a python class.
Including this at the start of your view script will import a class
<code class="docutils literal notranslate"><span class="pre">Ui_MyWidget</span></code> that you can use with your view.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span><span class="w"> </span><span class="nn">mantidqt.utils.qt</span><span class="w"> </span><span class="kn">import</span> <span class="n">load_ui</span>
<span class="n">Ui_MyWidget</span><span class="p">,</span> <span class="n">_</span> <span class="o">=</span> <span class="n">load_ui</span><span class="p">(</span><span class="vm">__file__</span><span class="p">,</span> <span class="s2">"my_widget.ui"</span><span class="p">)</span>
</pre></div>
</div>
</section>
<section id="using-the-generated-class">
<h2>Using the Generated Class<a class="headerlink" href="#using-the-generated-class" title="Link to this heading">¶</a></h2>
<p>When following the MVP design pattern as described at
<a class="reference internal" href="MVPDesign.html#mvpdesignintro"><span class="std std-ref">MVP (Model View Presenter)</span></a>, the generated class alone is not
sufficient as a <a class="reference internal" href="MVPDesign.html#mvpdesignview"><span class="std std-ref">View</span></a>. The generated class
lacks any method for sending signals or changing data in its own widgets.
Directly accessing the widgets and the signals defined on the view from
the presenter moves the view implementation details into the presenter,
which makes it harder to change the names and types of widgets used to
display the information. Instead it is best to create a separate new class
which inherits from the imported one.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span><span class="w"> </span><span class="nn">qtpy</span><span class="w"> </span><span class="kn">import</span> <span class="n">QtCore</span><span class="p">,</span> <span class="n">QtGui</span><span class="p">,</span> <span class="n">QtWidgets</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">matplotlib.pyplot</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="nn">plt</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">mantidqt.utils.qt</span><span class="w"> </span><span class="kn">import</span> <span class="n">load_ui</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">matplotlib.backends.backend_qt5agg</span><span class="w"> </span><span class="kn">import</span> <span class="n">FigureCanvasQTAgg</span> <span class="k">as</span> <span class="n">FigureCanvas</span>
<span class="n">Ui_MyWidget</span><span class="p">,</span> <span class="n">_</span> <span class="o">=</span> <span class="n">load_ui</span><span class="p">(</span><span class="vm">__file__</span><span class="p">,</span> <span class="s2">"my_widget.ui"</span><span class="p">)</span>
<span class="k">class</span><span class="w"> </span><span class="nc">MyWidget</span><span class="p">(</span><span class="n">QtWidgets</span><span class="o">.</span><span class="n">QWidget</span><span class="p">,</span> <span class="n">Ui_MyWidget</span><span class="p">):</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">parent</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
<span class="nb">super</span><span class="p">(</span><span class="n">MyWidget</span><span class="p">,</span> <span class="bp">self</span><span class="p">)</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="n">parent</span><span class="p">)</span>
</pre></div>
</div>
<p>You can then add separate methods to the view for accessing and mutating
the content of the widgets as well as add any necessary signals which
form the interface to the view.</p>
</section>
<section id="integrating-into-workbench">
<h2>Integrating into Workbench<a class="headerlink" href="#integrating-into-workbench" title="Link to this heading">¶</a></h2>
<p>In order to implement a new interface into Mantid Workbench it is best to
start with a up to date build from <code class="docutils literal notranslate"><span class="pre">main</span></code>. For this demo our interface does
not need any presenter or model, those can be added later, all that is needed
is a <code class="docutils literal notranslate"><span class="pre">.ui</span></code> file and a script that will open it as an application. Take view
from above and use the following as your main:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span><span class="w"> </span><span class="nn">qtpy</span><span class="w"> </span><span class="kn">import</span> <span class="n">QtWidgets</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">sys</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">my_widget.view</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="nn">view</span>
<span class="k">class</span><span class="w"> </span><span class="nc">demo</span><span class="p">(</span><span class="n">QtWidgets</span><span class="o">.</span><span class="n">QMainWindow</span><span class="p">):</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">parent</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
<span class="nb">super</span><span class="p">(</span><span class="n">demo</span><span class="p">,</span> <span class="bp">self</span><span class="p">)</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="n">parent</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">window</span> <span class="o">=</span> <span class="n">QtWidgets</span><span class="o">.</span><span class="n">QMainWindow</span><span class="p">()</span>
<span class="n">my_view</span> <span class="o">=</span> <span class="n">view</span><span class="o">.</span><span class="n">MyWidget</span><span class="p">(</span><span class="n">parent</span><span class="o">=</span><span class="bp">self</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">setCentralWidget</span><span class="p">(</span><span class="n">my_view</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">setWindowTitle</span><span class="p">(</span><span class="s2">"Demo Interface"</span><span class="p">)</span>
</pre></div>
</div>
<p>and put them in folder called <code class="docutils literal notranslate"><span class="pre">my_widget.py</span></code> in the <code class="docutils literal notranslate"><span class="pre">scripts</span></code> folder of your
source directory. You will note that when we are importing view into the main
script we are importing <code class="docutils literal notranslate"><span class="pre">my_widget.view</span></code>, this is because our interface is in
the form of a package that will be called by another script.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span><span class="w"> </span><span class="nn">my_widget.main</span><span class="w"> </span><span class="kn">import</span> <span class="n">demo</span>
<span class="n">widget</span> <span class="o">=</span> <span class="n">demo</span><span class="p">()</span>
<span class="n">widget</span><span class="o">.</span><span class="n">show</span><span class="p">()</span>
</pre></div>
</div>
<p>This script call this script <code class="docutils literal notranslate"><span class="pre">my_widget.py</span></code> saved in the <code class="docutils literal notranslate"><span class="pre">scripts</span></code> folder of
your source directory.</p>
<p>Finally we have to add <code class="docutils literal notranslate"><span class="pre">my_widget</span></code> to the list of interfaces. to do this open
<code class="docutils literal notranslate"><span class="pre">[Mantid</span> <span class="pre">Source</span> <span class="pre">Directory]/Framework/Properties/Mandtid.properties.template</span></code>
and find the line starting <code class="docutils literal notranslate"><span class="pre">mantidqt.python_interfaces</span> <span class="pre">=</span></code>. At the end of this
line, add <code class="docutils literal notranslate"><span class="pre">My_widgets/my_widget.py</span></code>.</p>
<p>Rebuild Mantid and open workbench. Your interface should be located under
Interfaces –> My Widgets –> my widget.</p>
<section id="keeping-guis-modular-using-widgets">
<h3>Keeping GUIs modular using Widgets<a class="headerlink" href="#keeping-guis-modular-using-widgets" title="Link to this heading">¶</a></h3>
</section>
</section>
<section id="motivation-1">
<span id="id1"></span><h2>Motivation<a class="headerlink" href="#motivation-1" title="Link to this heading">¶</a></h2>
<p>When designing a GUI in QtCreator it is often too easy to end up with
the entire interface in a single UI file. This can then lead to having a
single presenter for the entire GUI and sometimes even a single model.
This makes the UI harder to maintain as a whole and difficult to re-use,
move and separate out individual sections.</p>
<p>Instead when building a GUI it is sometimes better to separate parts of
the interface into smaller views and presenters which form a hierarchy
of widgets. For example the new SANS Run Summation page is in it’s own
UI file and uses two separate widgets - a <code class="docutils literal notranslate"><span class="pre">RunSelectorWidget</span></code> and a
<code class="docutils literal notranslate"><span class="pre">SummationSettingsWidget</span></code>. Although these widgets are not currently
used in any other interface, they are still isolated from the Run
Summation tab and could easily be used in another interface should the
need arise. The code is also better organised and more modular as a
result of this clean separation.</p>
</section>
<section id="implementation-1">
<span id="id2"></span><h2>Implementation<a class="headerlink" href="#implementation-1" title="Link to this heading">¶</a></h2>
<p>Qt Creator allows us to promote widgets in a <code class="docutils literal notranslate"><span class="pre">.ui</span></code> file into more complex
widgets from a python script. This allows for the easy reuse of widgets
in other interfaces while keeping the same methods.</p>
<p>Begin in Qt Creator by making a blank QWidget then right click on it and
go to <code class="docutils literal notranslate"><span class="pre">Promote</span> <span class="pre">to..</span></code></p>
<img alt="_images/qtcreator_promoted.png" src="_images/qtcreator_promoted.png" />
<p>The header will need to point to the view file that you made previously.
Make sure this widgets name is left as the default <code class="docutils literal notranslate"><span class="pre">widget</span></code> and save.
This promoted widget will contain all the same properties as the optional
including any methods you added to <code class="docutils literal notranslate"><span class="pre">view.py</span></code>. Replace the view used in
the previous example with a script that generates your promoted widget
called <code class="docutils literal notranslate"><span class="pre">view_promoted.py</span></code>. Restart Workbench and check your widget, it
should look identical. You can experiment with adding more than one
promoted widget.</p>
<p>Your original view is embedded in the new widget as an object, as such
any methods are also within that object. As such where you would have called
a method in the original with <code class="docutils literal notranslate"><span class="pre">self.view.getColours()</span></code>, when using a
promoted widget you call it using <code class="docutils literal notranslate"><span class="pre">self.view.widget.getColours()</span></code>. Good
practice would be to write a method in the master view script that returns the
sub view object like in the script for <a class="reference internal" href="MVPTutorial/CompleteGUI.html#completegui"><span class="std std-ref">the plotting widget</span></a>.</p>
<p>It may help to get some practice with Qt Creator by recreating
<a class="reference internal" href="MVPTutorial/CompleteGUI.html#completegui"><span class="std std-ref">the plotting widget</span></a>. you should be able to use the same
model and presenter scripts with the <code class="docutils literal notranslate"><span class="pre">.ui</span></code> based view scripts.</p>
</section>
</section>
</div>
</div>
</div>
<footer class="footer">
<div class="container">
<ul class="nav navbar-nav" style=" float: right;">
<li>
<a href="MVPTutorial/CalculatorExample/index.html" title="Previous Chapter: MVP Calculator GUI Example"><span class="glyphicon glyphicon-chevron-left visible-sm"></span><span class="hidden-sm hidden-tablet">« MVP Calculato...</span>
</a>
</li>
<li>
<a href="BalsamiqWireframes.html" title="Next Chapter: Mockups with Balsamiq Wireframes"><span class="glyphicon glyphicon-chevron-right visible-sm"></span><span class="hidden-sm hidden-tablet">Mockups with ... »</span>
</a>
</li>
<li><a href="#">Back to top</a></li>
</ul>
<p>
</p>
</div>
</footer>
</body>
</html>