Merge lp:mapclient/bugfixes into lp:mapclient/testing
- bugfixes
- Merge into testing
Proposed by
Hugh Sorby
Status: | Merged | ||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Approved by: | Hugh Sorby | ||||||||||||||||||||||||||||||||||||
Approved revision: | 32 | ||||||||||||||||||||||||||||||||||||
Merged at revision: | 48 | ||||||||||||||||||||||||||||||||||||
Proposed branch: | lp:mapclient/bugfixes | ||||||||||||||||||||||||||||||||||||
Merge into: | lp:mapclient/testing | ||||||||||||||||||||||||||||||||||||
Diff against target: |
549 lines (+169/-67) 12 files modified
plugins/skeletonstep/__init__.py (+3/-3) plugins/skeletonstep/skeletonstep/step.py (+8/-8) src/core/threadcommandmanager.py (+36/-37) src/widgets/aboutdialog.py (+7/-2) src/widgets/mainwindow.py (+15/-1) src/widgets/qt/aboutdialog.ui (+12/-3) src/widgets/ui_aboutdialog.py (+7/-7) src/widgets/workflowgraphicsitems.py (+6/-2) src/widgets/workflowgraphicsscene.py (+1/-1) src/widgets/workflowwidget.py (+22/-2) tests/widgets_tests/utils.py (+46/-0) tests/widgets_tests/widgetstests.py (+6/-1) |
||||||||||||||||||||||||||||||||||||
To merge this branch: | bzr merge lp:mapclient/bugfixes | ||||||||||||||||||||||||||||||||||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Hugh Sorby | Approve | ||
Review via email: mp+194279@code.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
Revision history for this message
Hugh Sorby (h-sorby) : | # |
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'plugins/skeletonstep/__init__.py' | |||
2 | --- plugins/skeletonstep/__init__.py 2013-07-02 02:33:08 +0000 | |||
3 | +++ plugins/skeletonstep/__init__.py 2013-11-07 04:12:40 +0000 | |||
4 | @@ -29,7 +29,7 @@ | |||
5 | 29 | sys.path.insert(0, current_dir) | 29 | sys.path.insert(0, current_dir) |
6 | 30 | 30 | ||
7 | 31 | # import class that derives itself from the step mountpoint. | 31 | # import class that derives itself from the step mountpoint. |
9 | 32 | #from skeletonstep import step | 32 | # from skeletonstep import step |
10 | 33 | 33 | ||
13 | 34 | ( _, tail ) = os.path.split(current_dir) | 34 | (_, tail) = os.path.split(current_dir) |
14 | 35 | #print("Plugin '{0}' version {1} by {2} loaded".format(tail, __version__, __author__)) | 35 | # print("Plugin '{0}' version {1} by {2} loaded".format(tail, __version__, __author__)) |
15 | 36 | 36 | ||
16 | === modified file 'plugins/skeletonstep/skeletonstep/step.py' | |||
17 | --- plugins/skeletonstep/skeletonstep/step.py 2013-07-02 02:33:08 +0000 | |||
18 | +++ plugins/skeletonstep/skeletonstep/step.py 2013-11-07 04:12:40 +0000 | |||
19 | @@ -25,22 +25,22 @@ | |||
20 | 25 | Skeleton step which is intended to be used as a starting point | 25 | Skeleton step which is intended to be used as a starting point |
21 | 26 | for new steps. | 26 | for new steps. |
22 | 27 | ''' | 27 | ''' |
24 | 28 | 28 | ||
25 | 29 | def __init__(self, location): | 29 | def __init__(self, location): |
26 | 30 | super(SkeletonStep, self).__init__('Skeleton', location) | 30 | super(SkeletonStep, self).__init__('Skeleton', location) |
28 | 31 | 31 | ||
29 | 32 | def configure(self): | 32 | def configure(self): |
30 | 33 | pass | 33 | pass |
32 | 34 | 34 | ||
33 | 35 | def getIdentifier(self): | 35 | def getIdentifier(self): |
36 | 36 | pass | 36 | return 'skeleton' |
37 | 37 | 37 | ||
38 | 38 | def setIdentifier(self, identifier): | 38 | def setIdentifier(self, identifier): |
39 | 39 | pass | 39 | pass |
41 | 40 | 40 | ||
42 | 41 | def serialize(self, location): | 41 | def serialize(self, location): |
43 | 42 | pass | 42 | pass |
45 | 43 | 43 | ||
46 | 44 | def deserialize(self, location): | 44 | def deserialize(self, location): |
47 | 45 | pass | 45 | pass |
49 | 46 | 46 | ||
50 | 47 | 47 | ||
51 | === modified file 'src/core/threadcommandmanager.py' | |||
52 | --- src/core/threadcommandmanager.py 2013-06-27 04:11:01 +0000 | |||
53 | +++ src/core/threadcommandmanager.py 2013-11-07 04:12:40 +0000 | |||
54 | @@ -39,35 +39,35 @@ | |||
55 | 39 | It has no functional purpose. | 39 | It has no functional purpose. |
56 | 40 | ''' | 40 | ''' |
57 | 41 | Thread.__init__(self, name=name) | 41 | Thread.__init__(self, name=name) |
59 | 42 | 42 | ||
60 | 43 | def setCaller(self, caller): | 43 | def setCaller(self, caller): |
61 | 44 | self._caller = caller | 44 | self._caller = caller |
63 | 45 | 45 | ||
64 | 46 | def runFinished(self): | 46 | def runFinished(self): |
65 | 47 | self._caller and self._caller._commandFinished(self.name) | 47 | self._caller and self._caller._commandFinished(self.name) |
68 | 48 | 48 | ||
69 | 49 | 49 | ||
70 | 50 | class CommandCopyDirectory(ThreadCommand): | 50 | class CommandCopyDirectory(ThreadCommand): |
71 | 51 | ''' Threadable command to copy the contents of one directory to another. | 51 | ''' Threadable command to copy the contents of one directory to another. |
72 | 52 | This copy is not recursive. | 52 | This copy is not recursive. |
73 | 53 | ''' | 53 | ''' |
75 | 54 | 54 | ||
76 | 55 | def __init__(self, from_dir, to_dir): | 55 | def __init__(self, from_dir, to_dir): |
77 | 56 | ThreadCommand.__init__(self, 'CommandCopyDirectory') | 56 | ThreadCommand.__init__(self, 'CommandCopyDirectory') |
78 | 57 | self._from_dir = from_dir | 57 | self._from_dir = from_dir |
79 | 58 | self._to_dir = to_dir | 58 | self._to_dir = to_dir |
81 | 59 | 59 | ||
82 | 60 | def run(self): | 60 | def run(self): |
83 | 61 | if not os.path.exists(self._to_dir): | 61 | if not os.path.exists(self._to_dir): |
84 | 62 | os.mkdir(self._to_dir) | 62 | os.mkdir(self._to_dir) |
86 | 63 | 63 | ||
87 | 64 | onlyfiles = [ join(self._from_dir, f) for f in listdir(self._from_dir) if isfile(join(self._from_dir, f)) ] | 64 | onlyfiles = [ join(self._from_dir, f) for f in listdir(self._from_dir) if isfile(join(self._from_dir, f)) ] |
88 | 65 | for f in onlyfiles: | 65 | for f in onlyfiles: |
89 | 66 | copy(f, self._to_dir) | 66 | copy(f, self._to_dir) |
91 | 67 | 67 | ||
92 | 68 | self.runFinished() | 68 | self.runFinished() |
95 | 69 | 69 | ||
96 | 70 | 70 | ||
97 | 71 | class CommandCreateWorkspace(ThreadCommand): | 71 | class CommandCreateWorkspace(ThreadCommand): |
98 | 72 | '''Threadable command to create a workspace on PMR. | 72 | '''Threadable command to create a workspace on PMR. |
99 | 73 | ''' | 73 | ''' |
100 | @@ -75,12 +75,12 @@ | |||
101 | 75 | ThreadCommand.__init__(self, 'CommandCreateWorkspace') | 75 | ThreadCommand.__init__(self, 'CommandCreateWorkspace') |
102 | 76 | self._title = title | 76 | self._title = title |
103 | 77 | self._description = description | 77 | self._description = description |
105 | 78 | 78 | ||
106 | 79 | def run(self): | 79 | def run(self): |
107 | 80 | print('Warning: Not fully implemented') | 80 | print('Warning: Not fully implemented') |
108 | 81 | self.runFinished() | 81 | self.runFinished() |
111 | 82 | 82 | ||
112 | 83 | 83 | ||
113 | 84 | class CommandIgnoreDirectoriesHg(ThreadCommand): | 84 | class CommandIgnoreDirectoriesHg(ThreadCommand): |
114 | 85 | ''' Threadable command to add ignore directives to | 85 | ''' Threadable command to add ignore directives to |
115 | 86 | all directories in given location. Requires a Mercurial | 86 | all directories in given location. Requires a Mercurial |
116 | @@ -92,8 +92,8 @@ | |||
117 | 92 | self._hg = None | 92 | self._hg = None |
118 | 93 | hg = which('hg') | 93 | hg = which('hg') |
119 | 94 | if len(hg) > 0: | 94 | if len(hg) > 0: |
122 | 95 | self._hg = hg[0] | 95 | self._hg = hg[0] |
123 | 96 | 96 | ||
124 | 97 | def run(self): | 97 | def run(self): |
125 | 98 | if self._hg and os.path.exists(join(self._location, '.hg')): | 98 | if self._hg and os.path.exists(join(self._location, '.hg')): |
126 | 99 | onlydirs = [x for x in listdir(self._location) if isdir(join(self._location, x)) ] | 99 | onlydirs = [x for x in listdir(self._location) if isdir(join(self._location, x)) ] |
127 | @@ -101,11 +101,11 @@ | |||
128 | 101 | f = open(join(self._location, '.hgignore'), 'w') | 101 | f = open(join(self._location, '.hgignore'), 'w') |
129 | 102 | f.writelines(ignoredirs) | 102 | f.writelines(ignoredirs) |
130 | 103 | f.close() | 103 | f.close() |
132 | 104 | 104 | ||
133 | 105 | class CommandCloneWorkspace(ThreadCommand): | 105 | class CommandCloneWorkspace(ThreadCommand): |
134 | 106 | ''' Threadable command to clone a PMR workspace. | 106 | ''' Threadable command to clone a PMR workspace. |
135 | 107 | ''' | 107 | ''' |
137 | 108 | 108 | ||
138 | 109 | def __init__(self, repourl, location, username, password): | 109 | def __init__(self, repourl, location, username, password): |
139 | 110 | ThreadCommand.__init__(self, 'CommandCloneWorkspace') | 110 | ThreadCommand.__init__(self, 'CommandCloneWorkspace') |
140 | 111 | self._repourl = repourl | 111 | self._repourl = repourl |
141 | @@ -115,8 +115,8 @@ | |||
142 | 115 | self._hg = None | 115 | self._hg = None |
143 | 116 | hg = which('hg') | 116 | hg = which('hg') |
144 | 117 | if len(hg) > 0: | 117 | if len(hg) > 0: |
147 | 118 | self._hg = hg[0] | 118 | self._hg = hg[0] |
148 | 119 | 119 | ||
149 | 120 | def run(self): | 120 | def run(self): |
150 | 121 | '''Mercurial will not clone into a directory that is not empty. To work | 121 | '''Mercurial will not clone into a directory that is not empty. To work |
151 | 122 | around this we clone into a temporary directory and then move the '.hg' | 122 | around this we clone into a temporary directory and then move the '.hg' |
152 | @@ -124,20 +124,20 @@ | |||
153 | 124 | ''' | 124 | ''' |
154 | 125 | if self._hg and not os.path.exists(join(self._location, '.hg')): | 125 | if self._hg and not os.path.exists(join(self._location, '.hg')): |
155 | 126 | d = tempfile.mkdtemp(dir=self._location) | 126 | d = tempfile.mkdtemp(dir=self._location) |
157 | 127 | 127 | ||
158 | 128 | repourl = self._repourl[:7] + self._username + ':' + self._password + '@' + self._repourl[7:] | 128 | repourl = self._repourl[:7] + self._username + ':' + self._password + '@' + self._repourl[7:] |
159 | 129 | call([self._hg, 'clone', repourl, d]) | 129 | call([self._hg, 'clone', repourl, d]) |
160 | 130 | mvdir(d, self._location) | 130 | mvdir(d, self._location) |
161 | 131 | # move(join(d, '.hg'), self._location) | 131 | # move(join(d, '.hg'), self._location) |
162 | 132 | rmtree(d) | 132 | rmtree(d) |
164 | 133 | 133 | ||
165 | 134 | self.runFinished() | 134 | self.runFinished() |
167 | 135 | 135 | ||
168 | 136 | 136 | ||
169 | 137 | class CommandCommit(ThreadCommand): | 137 | class CommandCommit(ThreadCommand): |
170 | 138 | '''Threadable command to commit all changes at location to PMR | 138 | '''Threadable command to commit all changes at location to PMR |
171 | 139 | ''' | 139 | ''' |
173 | 140 | 140 | ||
174 | 141 | def __init__(self, location, username, password, comment): | 141 | def __init__(self, location, username, password, comment): |
175 | 142 | ThreadCommand.__init__(self, 'CommandCommit') | 142 | ThreadCommand.__init__(self, 'CommandCommit') |
176 | 143 | self._location = location | 143 | self._location = location |
177 | @@ -147,8 +147,8 @@ | |||
178 | 147 | self._hg = None | 147 | self._hg = None |
179 | 148 | hg = which('hg') | 148 | hg = which('hg') |
180 | 149 | if len(hg) > 0: | 149 | if len(hg) > 0: |
183 | 150 | self._hg = hg[0] | 150 | self._hg = hg[0] |
184 | 151 | 151 | ||
185 | 152 | def run(self): | 152 | def run(self): |
186 | 153 | if self._hg and os.path.exists(join(self._location, '.hg')): | 153 | if self._hg and os.path.exists(join(self._location, '.hg')): |
187 | 154 | # This is for the commit command | 154 | # This is for the commit command |
188 | @@ -163,25 +163,25 @@ | |||
189 | 163 | repourl = repourl[:insert] + ':' + self._password + repourl[insert:] | 163 | repourl = repourl[:insert] + ':' + self._password + repourl[insert:] |
190 | 164 | process = Popen([self._hg, 'push', repourl], cwd=self._location) | 164 | process = Popen([self._hg, 'push', repourl], cwd=self._location) |
191 | 165 | process.communicate() | 165 | process.communicate() |
193 | 166 | 166 | ||
194 | 167 | self.runFinished() | 167 | self.runFinished() |
196 | 168 | 168 | ||
197 | 169 | 169 | ||
198 | 170 | class ThreadCommandManager(object): | 170 | class ThreadCommandManager(object): |
199 | 171 | '''This class managers thread commands in a queue. The queue will | 171 | '''This class managers thread commands in a queue. The queue will |
200 | 172 | be executed in order serially. | 172 | be executed in order serially. |
201 | 173 | ''' | 173 | ''' |
203 | 174 | 174 | ||
204 | 175 | def __init__(self): | 175 | def __init__(self): |
205 | 176 | self._queue = [] | 176 | self._queue = [] |
208 | 177 | self._finished = None # Callback for informing when the queue is empty | 177 | self._finished = None # Callback for informing when the queue is empty |
209 | 178 | 178 | ||
210 | 179 | def registerFinishedCallback(self, callback): | 179 | def registerFinishedCallback(self, callback): |
211 | 180 | self._finished = callback | 180 | self._finished = callback |
213 | 181 | 181 | ||
214 | 182 | def addCommand(self, c): | 182 | def addCommand(self, c): |
215 | 183 | self._queue.append(c) | 183 | self._queue.append(c) |
217 | 184 | 184 | ||
218 | 185 | def execute(self): | 185 | def execute(self): |
219 | 186 | if len(self._queue) > 0: | 186 | if len(self._queue) > 0: |
220 | 187 | c = self._queue.pop(0) | 187 | c = self._queue.pop(0) |
221 | @@ -189,10 +189,10 @@ | |||
222 | 189 | c.start() | 189 | c.start() |
223 | 190 | elif self._finished: | 190 | elif self._finished: |
224 | 191 | self._finished() | 191 | self._finished() |
226 | 192 | 192 | ||
227 | 193 | def _commandFinished(self, thread_name): | 193 | def _commandFinished(self, thread_name): |
228 | 194 | self.execute() | 194 | self.execute() |
230 | 195 | 195 | ||
231 | 196 | 196 | ||
232 | 197 | def which(name, flags=os.X_OK): | 197 | def which(name, flags=os.X_OK): |
233 | 198 | result = [] | 198 | result = [] |
234 | @@ -222,6 +222,5 @@ | |||
235 | 222 | if os.path.exists(dst_file): | 222 | if os.path.exists(dst_file): |
236 | 223 | os.remove(dst_file) | 223 | os.remove(dst_file) |
237 | 224 | move(src_file, dst_dir) | 224 | move(src_file, dst_dir) |
238 | 225 | |||
239 | 226 | |||
240 | 227 | |||
241 | 228 | \ No newline at end of file | 225 | \ No newline at end of file |
242 | 226 | |||
243 | 227 | |||
244 | 229 | 228 | ||
245 | === modified file 'src/widgets/aboutdialog.py' | |||
246 | --- src/widgets/aboutdialog.py 2013-06-14 01:51:27 +0000 | |||
247 | +++ src/widgets/aboutdialog.py 2013-11-07 04:12:40 +0000 | |||
248 | @@ -18,7 +18,10 @@ | |||
249 | 18 | along with MAP Client. If not, see <http://www.gnu.org/licenses/>.. | 18 | along with MAP Client. If not, see <http://www.gnu.org/licenses/>.. |
250 | 19 | ''' | 19 | ''' |
251 | 20 | from PySide.QtGui import QDialog | 20 | from PySide.QtGui import QDialog |
252 | 21 | |||
253 | 22 | from settings import info | ||
254 | 21 | from widgets.ui_aboutdialog import Ui_AboutDialog | 23 | from widgets.ui_aboutdialog import Ui_AboutDialog |
255 | 24 | |||
256 | 22 | class AboutDialog(QDialog): | 25 | class AboutDialog(QDialog): |
257 | 23 | ''' | 26 | ''' |
258 | 24 | About dialog to display program about information. | 27 | About dialog to display program about information. |
259 | @@ -32,12 +35,14 @@ | |||
260 | 32 | QDialog.__init__(self, parent) | 35 | QDialog.__init__(self, parent) |
261 | 33 | self._ui = Ui_AboutDialog() | 36 | self._ui = Ui_AboutDialog() |
262 | 34 | self._ui.setupUi(self) | 37 | self._ui.setupUi(self) |
263 | 38 | text = self._ui.aboutTextLabel.text() | ||
264 | 39 | self._ui.aboutTextLabel.setText(text.replace('##version##', info.VERSION_STRING)) | ||
265 | 35 | self._makeConnections() | 40 | self._makeConnections() |
267 | 36 | 41 | ||
268 | 37 | def _makeConnections(self): | 42 | def _makeConnections(self): |
269 | 38 | self._ui.btn_Credits.clicked.connect(self.showCreditsDialog) | 43 | self._ui.btn_Credits.clicked.connect(self.showCreditsDialog) |
270 | 39 | self._ui.btn_License.clicked.connect(self.showLicenseDialog) | 44 | self._ui.btn_License.clicked.connect(self.showLicenseDialog) |
272 | 40 | 45 | ||
273 | 41 | def showCreditsDialog(self): | 46 | def showCreditsDialog(self): |
274 | 42 | from widgets.creditsdialog import CreditsDialog | 47 | from widgets.creditsdialog import CreditsDialog |
275 | 43 | dlg = CreditsDialog(self) | 48 | dlg = CreditsDialog(self) |
276 | 44 | 49 | ||
277 | === modified file 'src/widgets/mainwindow.py' | |||
278 | --- src/widgets/mainwindow.py 2013-10-26 09:31:00 +0000 | |||
279 | +++ src/widgets/mainwindow.py 2013-11-07 04:12:40 +0000 | |||
280 | @@ -55,6 +55,8 @@ | |||
281 | 55 | self._ui.stackedWidget.addWidget(self._workflowWidget) | 55 | self._ui.stackedWidget.addWidget(self._workflowWidget) |
282 | 56 | self.setCurrentUndoRedoStack(self._workflowWidget.undoRedoStack()) | 56 | self.setCurrentUndoRedoStack(self._workflowWidget.undoRedoStack()) |
283 | 57 | 57 | ||
284 | 58 | self._pluginManagerDlg = None | ||
285 | 59 | |||
286 | 58 | def _createUndoAction(self, parent): | 60 | def _createUndoAction(self, parent): |
287 | 59 | self.undoAction = QtGui.QAction('Undo', parent) | 61 | self.undoAction = QtGui.QAction('Undo', parent) |
288 | 60 | self.undoAction.setShortcut(QtGui.QKeySequence('Ctrl+Z')) | 62 | self.undoAction.setShortcut(QtGui.QKeySequence('Ctrl+Z')) |
289 | @@ -147,9 +149,10 @@ | |||
290 | 147 | def pluginManager(self): | 149 | def pluginManager(self): |
291 | 148 | from tools.pluginmanagerdialog import PluginManagerDialog | 150 | from tools.pluginmanagerdialog import PluginManagerDialog |
292 | 149 | dlg = PluginManagerDialog(self) | 151 | dlg = PluginManagerDialog(self) |
293 | 152 | self._pluginManagerDlg = dlg | ||
294 | 150 | dlg.setDirectories(self._model.pluginManager().directories()) | 153 | dlg.setDirectories(self._model.pluginManager().directories()) |
295 | 151 | dlg.setLoadDefaultPlugins(self._model.pluginManager().loadDefaultPlugins()) | 154 | dlg.setLoadDefaultPlugins(self._model.pluginManager().loadDefaultPlugins()) |
297 | 152 | dlg.reloadPlugins = self._model.pluginManager().load | 155 | dlg.reloadPlugins = self._pluginManagerReloadPlugins |
298 | 153 | 156 | ||
299 | 154 | dlg.setModal(True) | 157 | dlg.setModal(True) |
300 | 155 | if dlg.exec_(): | 158 | if dlg.exec_(): |
301 | @@ -159,6 +162,17 @@ | |||
302 | 159 | self._model.pluginManager().load() | 162 | self._model.pluginManager().load() |
303 | 160 | self._workflowWidget.updateStepTree() | 163 | self._workflowWidget.updateStepTree() |
304 | 161 | 164 | ||
305 | 165 | self._pluginManagerDlg = None | ||
306 | 166 | |||
307 | 167 | def _pluginManagerReloadPlugins(self): | ||
308 | 168 | ''' | ||
309 | 169 | Callback from the plugin manager to reload the current plugins. | ||
310 | 170 | ''' | ||
311 | 171 | self._model.pluginManager().setDirectories(self._pluginManagerDlg.directories()) | ||
312 | 172 | self._model.pluginManager().setLoadDefaultPlugins(self._pluginManagerDlg.loadDefaultPlugins()) | ||
313 | 173 | self._model.pluginManager().load() | ||
314 | 174 | self._workflowWidget.updateStepTree() | ||
315 | 175 | |||
316 | 162 | def pluginWizard(self): | 176 | def pluginWizard(self): |
317 | 163 | from tools.pluginwizard.wizarddialog import WizardDialog | 177 | from tools.pluginwizard.wizarddialog import WizardDialog |
318 | 164 | from tools.pluginwizard.skeleton import Skeleton | 178 | from tools.pluginwizard.skeleton import Skeleton |
319 | 165 | 179 | ||
320 | === modified file 'src/widgets/qt/aboutdialog.ui' | |||
321 | --- src/widgets/qt/aboutdialog.ui 2013-02-20 00:50:38 +0000 | |||
322 | +++ src/widgets/qt/aboutdialog.ui 2013-11-07 04:12:40 +0000 | |||
323 | @@ -23,7 +23,16 @@ | |||
324 | 23 | <string>About MAP Client</string> | 23 | <string>About MAP Client</string> |
325 | 24 | </property> | 24 | </property> |
326 | 25 | <layout class="QGridLayout" name="gridLayout"> | 25 | <layout class="QGridLayout" name="gridLayout"> |
328 | 26 | <property name="margin"> | 26 | <property name="leftMargin"> |
329 | 27 | <number>0</number> | ||
330 | 28 | </property> | ||
331 | 29 | <property name="topMargin"> | ||
332 | 30 | <number>0</number> | ||
333 | 31 | </property> | ||
334 | 32 | <property name="rightMargin"> | ||
335 | 33 | <number>0</number> | ||
336 | 34 | </property> | ||
337 | 35 | <property name="bottomMargin"> | ||
338 | 27 | <number>0</number> | 36 | <number>0</number> |
339 | 28 | </property> | 37 | </property> |
340 | 29 | <item row="0" column="0"> | 38 | <item row="0" column="0"> |
341 | @@ -49,12 +58,12 @@ | |||
342 | 49 | </widget> | 58 | </widget> |
343 | 50 | </item> | 59 | </item> |
344 | 51 | <item> | 60 | <item> |
346 | 52 | <widget class="QLabel" name="label"> | 61 | <widget class="QLabel" name="aboutTextLabel"> |
347 | 53 | <property name="styleSheet"> | 62 | <property name="styleSheet"> |
348 | 54 | <string notr="true">QLabel { background-color : white }</string> | 63 | <string notr="true">QLabel { background-color : white }</string> |
349 | 55 | </property> | 64 | </property> |
350 | 56 | <property name="text"> | 65 | <property name="text"> |
352 | 57 | <string><html><head/><body><p align="center"><span style=" font-size:24pt; font-weight:600;">MAP Client</span></p><p align="center">MAP Client, a program to generate detailed musculoskeletal models for OpenSim.</p><p align="center">Copyright (C) 2012 University of Auckland</p><p align="justify">MAP Client is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.</p><p align="justify">MAP Client is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.</p><p align="justify">You should have received a copy of the GNU General Public License along with MAP Client. If not, see &lt;http://www.gnu.org/licenses/&gt;.</p><p><br/></p></body></html></string> | 66 | <string><html><head/><body><p align="center"><span style=" font-size:24pt; font-weight:600;">MAP Client ##version##</span></p><p align="center">MAP Client, a program to generate detailed musculoskeletal models for OpenSim.</p><p align="center">Copyright (C) 2012 University of Auckland</p><p align="justify">MAP Client is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.</p><p align="justify">MAP Client is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.</p><p align="justify">You should have received a copy of the GNU General Public License along with MAP Client. If not, see &lt;http://www.gnu.org/licenses/&gt;.</p><p><br/></p></body></html></string> |
353 | 58 | </property> | 67 | </property> |
354 | 59 | <property name="wordWrap"> | 68 | <property name="wordWrap"> |
355 | 60 | <bool>true</bool> | 69 | <bool>true</bool> |
356 | 61 | 70 | ||
357 | === modified file 'src/widgets/ui_aboutdialog.py' | |||
358 | --- src/widgets/ui_aboutdialog.py 2013-06-14 01:51:27 +0000 | |||
359 | +++ src/widgets/ui_aboutdialog.py 2013-11-07 04:12:40 +0000 | |||
360 | @@ -2,7 +2,7 @@ | |||
361 | 2 | 2 | ||
362 | 3 | # Form implementation generated from reading ui file 'qt/aboutdialog.ui' | 3 | # Form implementation generated from reading ui file 'qt/aboutdialog.ui' |
363 | 4 | # | 4 | # |
365 | 5 | # Created: Fri Jun 14 11:25:36 2013 | 5 | # Created: Tue Nov 5 15:34:40 2013 |
366 | 6 | # by: pyside-uic 0.2.14 running on PySide 1.1.2 | 6 | # by: pyside-uic 0.2.14 running on PySide 1.1.2 |
367 | 7 | # | 7 | # |
368 | 8 | # WARNING! All changes made in this file will be lost! | 8 | # WARNING! All changes made in this file will be lost! |
369 | @@ -34,11 +34,11 @@ | |||
370 | 34 | self.label_2.setAlignment(QtCore.Qt.AlignCenter) | 34 | self.label_2.setAlignment(QtCore.Qt.AlignCenter) |
371 | 35 | self.label_2.setObjectName("label_2") | 35 | self.label_2.setObjectName("label_2") |
372 | 36 | self.verticalLayout.addWidget(self.label_2) | 36 | self.verticalLayout.addWidget(self.label_2) |
378 | 37 | self.label = QtGui.QLabel(self.frame) | 37 | self.aboutTextLabel = QtGui.QLabel(self.frame) |
379 | 38 | self.label.setStyleSheet("QLabel { background-color : white }") | 38 | self.aboutTextLabel.setStyleSheet("QLabel { background-color : white }") |
380 | 39 | self.label.setWordWrap(True) | 39 | self.aboutTextLabel.setWordWrap(True) |
381 | 40 | self.label.setObjectName("label") | 40 | self.aboutTextLabel.setObjectName("aboutTextLabel") |
382 | 41 | self.verticalLayout.addWidget(self.label) | 41 | self.verticalLayout.addWidget(self.aboutTextLabel) |
383 | 42 | self.gridLayout.addWidget(self.frame, 0, 0, 1, 1) | 42 | self.gridLayout.addWidget(self.frame, 0, 0, 1, 1) |
384 | 43 | self.frame_3 = QtGui.QFrame(AboutDialog) | 43 | self.frame_3 = QtGui.QFrame(AboutDialog) |
385 | 44 | self.frame_3.setFrameShape(QtGui.QFrame.StyledPanel) | 44 | self.frame_3.setFrameShape(QtGui.QFrame.StyledPanel) |
386 | @@ -70,7 +70,7 @@ | |||
387 | 70 | 70 | ||
388 | 71 | def retranslateUi(self, AboutDialog): | 71 | def retranslateUi(self, AboutDialog): |
389 | 72 | AboutDialog.setWindowTitle(QtGui.QApplication.translate("AboutDialog", "About MAP Client", None, QtGui.QApplication.UnicodeUTF8)) | 72 | AboutDialog.setWindowTitle(QtGui.QApplication.translate("AboutDialog", "About MAP Client", None, QtGui.QApplication.UnicodeUTF8)) |
391 | 73 | self.label.setText(QtGui.QApplication.translate("AboutDialog", "<html><head/><body><p align=\"center\"><span style=\" font-size:24pt; font-weight:600;\">MAP Client</span></p><p align=\"center\">MAP Client, a program to generate detailed musculoskeletal models for OpenSim.</p><p align=\"center\">Copyright (C) 2012 University of Auckland</p><p align=\"justify\">MAP Client is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.</p><p align=\"justify\">MAP Client is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.</p><p align=\"justify\">You should have received a copy of the GNU General Public License along with MAP Client. If not, see <http://www.gnu.org/licenses/>.</p><p><br/></p></body></html>", None, QtGui.QApplication.UnicodeUTF8)) | 73 | self.aboutTextLabel.setText(QtGui.QApplication.translate("AboutDialog", "<html><head/><body><p align=\"center\"><span style=\" font-size:24pt; font-weight:600;\">MAP Client ##version##</span></p><p align=\"center\">MAP Client, a program to generate detailed musculoskeletal models for OpenSim.</p><p align=\"center\">Copyright (C) 2012 University of Auckland</p><p align=\"justify\">MAP Client is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.</p><p align=\"justify\">MAP Client is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.</p><p align=\"justify\">You should have received a copy of the GNU General Public License along with MAP Client. If not, see <http://www.gnu.org/licenses/>.</p><p><br/></p></body></html>", None, QtGui.QApplication.UnicodeUTF8)) |
392 | 74 | self.btn_Credits.setText(QtGui.QApplication.translate("AboutDialog", "C&redits", None, QtGui.QApplication.UnicodeUTF8)) | 74 | self.btn_Credits.setText(QtGui.QApplication.translate("AboutDialog", "C&redits", None, QtGui.QApplication.UnicodeUTF8)) |
393 | 75 | self.btn_License.setText(QtGui.QApplication.translate("AboutDialog", "&License", None, QtGui.QApplication.UnicodeUTF8)) | 75 | self.btn_License.setText(QtGui.QApplication.translate("AboutDialog", "&License", None, QtGui.QApplication.UnicodeUTF8)) |
394 | 76 | self.btn_Close.setText(QtGui.QApplication.translate("AboutDialog", "&Close", None, QtGui.QApplication.UnicodeUTF8)) | 76 | self.btn_Close.setText(QtGui.QApplication.translate("AboutDialog", "&Close", None, QtGui.QApplication.UnicodeUTF8)) |
395 | 77 | 77 | ||
396 | === modified file 'src/widgets/workflowgraphicsitems.py' | |||
397 | --- src/widgets/workflowgraphicsitems.py 2013-10-25 07:33:34 +0000 | |||
398 | +++ src/widgets/workflowgraphicsitems.py 2013-11-07 04:12:40 +0000 | |||
399 | @@ -269,17 +269,21 @@ | |||
400 | 269 | x_pos = -3 * w / 4 | 269 | x_pos = -3 * w / 4 |
401 | 270 | uses_count += 1 | 270 | uses_count += 1 |
402 | 271 | pred = 'http://physiomeproject.org/workflow/1.0/rdf-schema#uses' | 271 | pred = 'http://physiomeproject.org/workflow/1.0/rdf-schema#uses' |
403 | 272 | tooltip_stub = 'uses: ' | ||
404 | 272 | else: # port in provides_ports: | 273 | else: # port in provides_ports: |
405 | 273 | port_total = provides_total | 274 | port_total = provides_total |
406 | 274 | index = provides_count | 275 | index = provides_count |
407 | 275 | x_pos = self.Size - w / 4 | 276 | x_pos = self.Size - w / 4 |
408 | 276 | provides_count += 1 | 277 | provides_count += 1 |
409 | 277 | pred = 'http://physiomeproject.org/workflow/1.0/rdf-schema#provides' | 278 | pred = 'http://physiomeproject.org/workflow/1.0/rdf-schema#provides' |
410 | 279 | tooltip_stub = 'provides: ' | ||
411 | 278 | 280 | ||
412 | 279 | triples = port.getTriplesForPred(pred) | 281 | triples = port.getTriplesForPred(pred) |
413 | 280 | triple_objects = [triple[2] for triple in triples] | 282 | triple_objects = [triple[2] for triple in triples] |
416 | 281 | port_item.moveBy(x_pos, self.Size / 2 + h / 3 * (4 * index - 2 * (port_total - 1) - 1)) | 283 | alpha = h / 4.0 # Controls the spacing between the ports |
417 | 282 | port_item.setToolTip('uses: ' + ', '.join(triple_objects)) | 284 | y_pos = self.Size / 2.0 - (port_total * h + (port_total - 1) * alpha) / 2.0 + (h + alpha) * index |
418 | 285 | port_item.moveBy(x_pos, y_pos) | ||
419 | 286 | port_item.setToolTip(tooltip_stub + ', '.join(triple_objects)) | ||
420 | 283 | self._step_port_items.append(port_item) | 287 | self._step_port_items.append(port_item) |
421 | 284 | 288 | ||
422 | 285 | self._configure_item = ConfigureIcon(self) | 289 | self._configure_item = ConfigureIcon(self) |
423 | 286 | 290 | ||
424 | === modified file 'src/widgets/workflowgraphicsscene.py' | |||
425 | --- src/widgets/workflowgraphicsscene.py 2013-10-25 07:33:34 +0000 | |||
426 | +++ src/widgets/workflowgraphicsscene.py 2013-11-07 04:12:40 +0000 | |||
427 | @@ -161,7 +161,7 @@ | |||
428 | 161 | self.parent().setWidgetUndoRedoStack(stack) | 161 | self.parent().setWidgetUndoRedoStack(stack) |
429 | 162 | 162 | ||
430 | 163 | def doneExecution(self): | 163 | def doneExecution(self): |
432 | 164 | self.parent().executeWorkflow() | 164 | self.parent().executeNext() |
433 | 165 | 165 | ||
434 | 166 | def identifierOccursCount(self, identifier): | 166 | def identifierOccursCount(self, identifier): |
435 | 167 | return self.parent().identifierOccursCount(identifier) | 167 | return self.parent().identifierOccursCount(identifier) |
436 | 168 | 168 | ||
437 | === modified file 'src/widgets/workflowwidget.py' | |||
438 | --- src/widgets/workflowwidget.py 2013-10-25 07:33:34 +0000 | |||
439 | +++ src/widgets/workflowwidget.py 2013-11-07 04:12:40 +0000 | |||
440 | @@ -73,7 +73,6 @@ | |||
441 | 73 | self.action_Close.setEnabled(workflowOpen) | 73 | self.action_Close.setEnabled(workflowOpen) |
442 | 74 | self.setEnabled(workflowOpen) | 74 | self.setEnabled(workflowOpen) |
443 | 75 | self.action_Save.setEnabled(wfm.isModified()) | 75 | self.action_Save.setEnabled(wfm.isModified()) |
444 | 76 | self._ui.executeButton.setEnabled(wfm.scene().canExecute() and not wfm.isModified()) | ||
445 | 77 | self._action_annotation.setEnabled(workflowOpen) | 76 | self._action_annotation.setEnabled(workflowOpen) |
446 | 78 | 77 | ||
447 | 79 | def updateStepTree(self): | 78 | def updateStepTree(self): |
448 | @@ -92,8 +91,29 @@ | |||
449 | 92 | print('setting active - workflow widget') | 91 | print('setting active - workflow widget') |
450 | 93 | self._mainWindow.setCurrentUndoRedoStack(self._undoStack) | 92 | self._mainWindow.setCurrentUndoRedoStack(self._undoStack) |
451 | 94 | 93 | ||
452 | 94 | def executeNext(self): | ||
453 | 95 | self._mainWindow.execute() | ||
454 | 96 | |||
455 | 95 | def executeWorkflow(self): | 97 | def executeWorkflow(self): |
457 | 96 | self._mainWindow.execute() # .model().workflowManager().execute() | 98 | wfm = self._mainWindow.model().workflowManager() |
458 | 99 | error_count = 0 | ||
459 | 100 | error_msg = '' | ||
460 | 101 | if wfm.isModified(): | ||
461 | 102 | error_count += 1 | ||
462 | 103 | error_msg += ' ' + str(error_count) + '. The workflow has not been saved.\n' | ||
463 | 104 | |||
464 | 105 | if not wfm.scene().canExecute(): | ||
465 | 106 | error_count += 1 | ||
466 | 107 | error_msg += ' ' + str(error_count) + '. Not all steps in the workflow have been successfully configured.\n' | ||
467 | 108 | |||
468 | 109 | if error_count == 0: | ||
469 | 110 | self._mainWindow.execute() # .model().workflowManager().execute() | ||
470 | 111 | else: | ||
471 | 112 | error_prefix = 'The workflow could not be executed for the following reason' | ||
472 | 113 | if error_count > 1: | ||
473 | 114 | error_prefix += 's' | ||
474 | 115 | error_prefix += ':\n\n' | ||
475 | 116 | QtGui.QMessageBox.critical(self, 'Workflow Execution', error_prefix + error_msg, QtGui.QMessageBox.Ok) | ||
476 | 97 | 117 | ||
477 | 98 | def identifierOccursCount(self, identifier): | 118 | def identifierOccursCount(self, identifier): |
478 | 99 | return self._mainWindow.model().workflowManager().identifierOccursCount(identifier) | 119 | return self._mainWindow.model().workflowManager().identifierOccursCount(identifier) |
479 | 100 | 120 | ||
480 | === added file 'tests/widgets_tests/utils.py' | |||
481 | --- tests/widgets_tests/utils.py 1970-01-01 00:00:00 +0000 | |||
482 | +++ tests/widgets_tests/utils.py 2013-11-07 04:12:40 +0000 | |||
483 | @@ -0,0 +1,46 @@ | |||
484 | 1 | ''' | ||
485 | 2 | MAP Client, a program to generate detailed musculoskeletal models for OpenSim. | ||
486 | 3 | Copyright (C) 2012 University of Auckland | ||
487 | 4 | |||
488 | 5 | This file is part of MAP Client. (http://launchpad.net/mapclient) | ||
489 | 6 | |||
490 | 7 | MAP Client is free software: you can redistribute it and/or modify | ||
491 | 8 | it under the terms of the GNU General Public License as published by | ||
492 | 9 | the Free Software Foundation, either version 3 of the License, or | ||
493 | 10 | (at your option) any later version. | ||
494 | 11 | |||
495 | 12 | MAP Client is distributed in the hope that it will be useful, | ||
496 | 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
497 | 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
498 | 15 | GNU General Public License for more details. | ||
499 | 16 | |||
500 | 17 | You should have received a copy of the GNU General Public License | ||
501 | 18 | along with MAP Client. If not, see <http://www.gnu.org/licenses/>.. | ||
502 | 19 | ''' | ||
503 | 20 | import unittest | ||
504 | 21 | |||
505 | 22 | class UtilsTestCase(unittest.TestCase): | ||
506 | 23 | |||
507 | 24 | size = 60 | ||
508 | 25 | h = 10 | ||
509 | 26 | alpha = 5 | ||
510 | 27 | |||
511 | 28 | |||
512 | 29 | def portPosition(self, n, i): | ||
513 | 30 | # print(self.size / 2.0 - self.h / 2.0, -(n - 1) * self.h - (n - 1) / 2 * self.alpha / 2.0, (self.h + self.alpha / 2.0) * i) | ||
514 | 31 | return self.size / 2.0 - (n * self.h + (n - 1) * self.alpha) / 2.0 + (self.h + self.alpha) * i | ||
515 | 32 | # return self.size / 2.0 - self.h / 2.0 - (n - 1) * self.h - (n - 1) / 2 * self.alpha / 2.0 + (self.h + self.alpha / 2.0) * i | ||
516 | 33 | |||
517 | 34 | def testPortLocation_neq1(self): | ||
518 | 35 | loc = self.portPosition(1, 0) | ||
519 | 36 | self.assertEqual(25, loc) | ||
520 | 37 | |||
521 | 38 | def testPortLocation_neq2(self): | ||
522 | 39 | loc = self.portPosition(2, 0) | ||
523 | 40 | self.assertEqual(17.5, loc) | ||
524 | 41 | loc = self.portPosition(2, 1) | ||
525 | 42 | self.assertEqual(32.5, loc) | ||
526 | 43 | |||
527 | 44 | if __name__ == "__main__": | ||
528 | 45 | # import sys;sys.argv = ['', 'Test.testName'] | ||
529 | 46 | unittest.main() | ||
530 | 0 | 47 | ||
531 | === modified file 'tests/widgets_tests/widgetstests.py' | |||
532 | --- tests/widgets_tests/widgetstests.py 2013-02-18 22:44:26 +0000 | |||
533 | +++ tests/widgets_tests/widgetstests.py 2013-11-07 04:12:40 +0000 | |||
534 | @@ -21,9 +21,14 @@ | |||
535 | 21 | import unittest | 21 | import unittest |
536 | 22 | 22 | ||
537 | 23 | def suite(): | 23 | def suite(): |
538 | 24 | tests = unittest.TestSuite() | ||
539 | 25 | |||
540 | 24 | from widgets_tests.mainwindow import MainWindowTestCase | 26 | from widgets_tests.mainwindow import MainWindowTestCase |
541 | 25 | tests = unittest.TestSuite() | ||
542 | 26 | tests.addTests(unittest.TestLoader().loadTestsFromTestCase(MainWindowTestCase)) | 27 | tests.addTests(unittest.TestLoader().loadTestsFromTestCase(MainWindowTestCase)) |
543 | 28 | |||
544 | 29 | from widgets_tests.utils import UtilsTestCase | ||
545 | 30 | tests.addTests(unittest.TestLoader().loadTestsFromTestCase(UtilsTestCase)) | ||
546 | 31 | |||
547 | 27 | return tests | 32 | return tests |
548 | 28 | 33 | ||
549 | 29 | def load_tests(loader, tests, pattern): | 34 | def load_tests(loader, tests, pattern): |