Merge lp:mapclient/bugfixes into lp:mapclient/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
Reviewer Review Type Date Requested Status
Hugh Sorby Approve
Review via email: mp+194279@code.launchpad.net
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
=== modified file 'plugins/skeletonstep/__init__.py'
--- plugins/skeletonstep/__init__.py 2013-07-02 02:33:08 +0000
+++ plugins/skeletonstep/__init__.py 2013-11-07 04:12:40 +0000
@@ -29,7 +29,7 @@
29 sys.path.insert(0, current_dir)29 sys.path.insert(0, current_dir)
3030
31# import class that derives itself from the step mountpoint.31# import class that derives itself from the step mountpoint.
32#from skeletonstep import step32# from skeletonstep import step
3333
34( _, tail ) = os.path.split(current_dir)34(_, tail) = os.path.split(current_dir)
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__))
3636
=== modified file 'plugins/skeletonstep/skeletonstep/step.py'
--- plugins/skeletonstep/skeletonstep/step.py 2013-07-02 02:33:08 +0000
+++ plugins/skeletonstep/skeletonstep/step.py 2013-11-07 04:12:40 +0000
@@ -25,22 +25,22 @@
25 Skeleton step which is intended to be used as a starting point25 Skeleton step which is intended to be used as a starting point
26 for new steps.26 for new steps.
27 '''27 '''
28 28
29 def __init__(self, location):29 def __init__(self, location):
30 super(SkeletonStep, self).__init__('Skeleton', location)30 super(SkeletonStep, self).__init__('Skeleton', location)
31 31
32 def configure(self):32 def configure(self):
33 pass33 pass
34 34
35 def getIdentifier(self):35 def getIdentifier(self):
36 pass36 return 'skeleton'
37 37
38 def setIdentifier(self, identifier):38 def setIdentifier(self, identifier):
39 pass39 pass
40 40
41 def serialize(self, location):41 def serialize(self, location):
42 pass42 pass
43 43
44 def deserialize(self, location):44 def deserialize(self, location):
45 pass45 pass
46 46
4747
=== modified file 'src/core/threadcommandmanager.py'
--- src/core/threadcommandmanager.py 2013-06-27 04:11:01 +0000
+++ src/core/threadcommandmanager.py 2013-11-07 04:12:40 +0000
@@ -39,35 +39,35 @@
39 It has no functional purpose.39 It has no functional purpose.
40 '''40 '''
41 Thread.__init__(self, name=name)41 Thread.__init__(self, name=name)
42 42
43 def setCaller(self, caller):43 def setCaller(self, caller):
44 self._caller = caller44 self._caller = caller
45 45
46 def runFinished(self):46 def runFinished(self):
47 self._caller and self._caller._commandFinished(self.name)47 self._caller and self._caller._commandFinished(self.name)
48 48
49 49
50class CommandCopyDirectory(ThreadCommand):50class CommandCopyDirectory(ThreadCommand):
51 ''' Threadable command to copy the contents of one directory to another.51 ''' Threadable command to copy the contents of one directory to another.
52 This copy is not recursive.52 This copy is not recursive.
53 '''53 '''
54 54
55 def __init__(self, from_dir, to_dir):55 def __init__(self, from_dir, to_dir):
56 ThreadCommand.__init__(self, 'CommandCopyDirectory')56 ThreadCommand.__init__(self, 'CommandCopyDirectory')
57 self._from_dir = from_dir57 self._from_dir = from_dir
58 self._to_dir = to_dir58 self._to_dir = to_dir
59 59
60 def run(self):60 def run(self):
61 if not os.path.exists(self._to_dir):61 if not os.path.exists(self._to_dir):
62 os.mkdir(self._to_dir)62 os.mkdir(self._to_dir)
63 63
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)) ]
65 for f in onlyfiles:65 for f in onlyfiles:
66 copy(f, self._to_dir)66 copy(f, self._to_dir)
67 67
68 self.runFinished()68 self.runFinished()
69 69
70 70
71class CommandCreateWorkspace(ThreadCommand):71class CommandCreateWorkspace(ThreadCommand):
72 '''Threadable command to create a workspace on PMR.72 '''Threadable command to create a workspace on PMR.
73 '''73 '''
@@ -75,12 +75,12 @@
75 ThreadCommand.__init__(self, 'CommandCreateWorkspace')75 ThreadCommand.__init__(self, 'CommandCreateWorkspace')
76 self._title = title76 self._title = title
77 self._description = description77 self._description = description
78 78
79 def run(self):79 def run(self):
80 print('Warning: Not fully implemented')80 print('Warning: Not fully implemented')
81 self.runFinished()81 self.runFinished()
82 82
83 83
84class CommandIgnoreDirectoriesHg(ThreadCommand):84class CommandIgnoreDirectoriesHg(ThreadCommand):
85 ''' Threadable command to add ignore directives to85 ''' Threadable command to add ignore directives to
86 all directories in given location. Requires a Mercurial86 all directories in given location. Requires a Mercurial
@@ -92,8 +92,8 @@
92 self._hg = None92 self._hg = None
93 hg = which('hg')93 hg = which('hg')
94 if len(hg) > 0:94 if len(hg) > 0:
95 self._hg = hg[0] 95 self._hg = hg[0]
96 96
97 def run(self):97 def run(self):
98 if self._hg and os.path.exists(join(self._location, '.hg')):98 if self._hg and os.path.exists(join(self._location, '.hg')):
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)) ]
@@ -101,11 +101,11 @@
101 f = open(join(self._location, '.hgignore'), 'w')101 f = open(join(self._location, '.hgignore'), 'w')
102 f.writelines(ignoredirs)102 f.writelines(ignoredirs)
103 f.close()103 f.close()
104 104
105class CommandCloneWorkspace(ThreadCommand):105class CommandCloneWorkspace(ThreadCommand):
106 ''' Threadable command to clone a PMR workspace.106 ''' Threadable command to clone a PMR workspace.
107 '''107 '''
108 108
109 def __init__(self, repourl, location, username, password):109 def __init__(self, repourl, location, username, password):
110 ThreadCommand.__init__(self, 'CommandCloneWorkspace')110 ThreadCommand.__init__(self, 'CommandCloneWorkspace')
111 self._repourl = repourl111 self._repourl = repourl
@@ -115,8 +115,8 @@
115 self._hg = None115 self._hg = None
116 hg = which('hg')116 hg = which('hg')
117 if len(hg) > 0:117 if len(hg) > 0:
118 self._hg = hg[0] 118 self._hg = hg[0]
119 119
120 def run(self):120 def run(self):
121 '''Mercurial will not clone into a directory that is not empty. To work121 '''Mercurial will not clone into a directory that is not empty. To work
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'
@@ -124,20 +124,20 @@
124 '''124 '''
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')):
126 d = tempfile.mkdtemp(dir=self._location)126 d = tempfile.mkdtemp(dir=self._location)
127 127
128 repourl = self._repourl[:7] + self._username + ':' + self._password + '@' + self._repourl[7:]128 repourl = self._repourl[:7] + self._username + ':' + self._password + '@' + self._repourl[7:]
129 call([self._hg, 'clone', repourl, d])129 call([self._hg, 'clone', repourl, d])
130 mvdir(d, self._location)130 mvdir(d, self._location)
131# move(join(d, '.hg'), self._location)131# move(join(d, '.hg'), self._location)
132 rmtree(d)132 rmtree(d)
133 133
134 self.runFinished()134 self.runFinished()
135 135
136136
137class CommandCommit(ThreadCommand):137class CommandCommit(ThreadCommand):
138 '''Threadable command to commit all changes at location to PMR138 '''Threadable command to commit all changes at location to PMR
139 '''139 '''
140 140
141 def __init__(self, location, username, password, comment):141 def __init__(self, location, username, password, comment):
142 ThreadCommand.__init__(self, 'CommandCommit')142 ThreadCommand.__init__(self, 'CommandCommit')
143 self._location = location143 self._location = location
@@ -147,8 +147,8 @@
147 self._hg = None147 self._hg = None
148 hg = which('hg')148 hg = which('hg')
149 if len(hg) > 0:149 if len(hg) > 0:
150 self._hg = hg[0] 150 self._hg = hg[0]
151 151
152 def run(self):152 def run(self):
153 if self._hg and os.path.exists(join(self._location, '.hg')):153 if self._hg and os.path.exists(join(self._location, '.hg')):
154 # This is for the commit command154 # This is for the commit command
@@ -163,25 +163,25 @@
163 repourl = repourl[:insert] + ':' + self._password + repourl[insert:]163 repourl = repourl[:insert] + ':' + self._password + repourl[insert:]
164 process = Popen([self._hg, 'push', repourl], cwd=self._location)164 process = Popen([self._hg, 'push', repourl], cwd=self._location)
165 process.communicate()165 process.communicate()
166 166
167 self.runFinished()167 self.runFinished()
168 168
169169
170class ThreadCommandManager(object):170class ThreadCommandManager(object):
171 '''This class managers thread commands in a queue. The queue will171 '''This class managers thread commands in a queue. The queue will
172 be executed in order serially.172 be executed in order serially.
173 '''173 '''
174 174
175 def __init__(self):175 def __init__(self):
176 self._queue = []176 self._queue = []
177 self._finished = None # Callback for informing when the queue is empty177 self._finished = None # Callback for informing when the queue is empty
178 178
179 def registerFinishedCallback(self, callback):179 def registerFinishedCallback(self, callback):
180 self._finished = callback180 self._finished = callback
181 181
182 def addCommand(self, c):182 def addCommand(self, c):
183 self._queue.append(c)183 self._queue.append(c)
184 184
185 def execute(self):185 def execute(self):
186 if len(self._queue) > 0:186 if len(self._queue) > 0:
187 c = self._queue.pop(0)187 c = self._queue.pop(0)
@@ -189,10 +189,10 @@
189 c.start()189 c.start()
190 elif self._finished:190 elif self._finished:
191 self._finished()191 self._finished()
192 192
193 def _commandFinished(self, thread_name):193 def _commandFinished(self, thread_name):
194 self.execute()194 self.execute()
195 195
196196
197def which(name, flags=os.X_OK):197def which(name, flags=os.X_OK):
198 result = []198 result = []
@@ -222,6 +222,5 @@
222 if os.path.exists(dst_file):222 if os.path.exists(dst_file):
223 os.remove(dst_file)223 os.remove(dst_file)
224 move(src_file, dst_dir)224 move(src_file, dst_dir)
225
226
227
228\ No newline at end of file225\ No newline at end of file
226
227
229228
=== modified file 'src/widgets/aboutdialog.py'
--- src/widgets/aboutdialog.py 2013-06-14 01:51:27 +0000
+++ src/widgets/aboutdialog.py 2013-11-07 04:12:40 +0000
@@ -18,7 +18,10 @@
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/>..
19'''19'''
20from PySide.QtGui import QDialog20from PySide.QtGui import QDialog
21
22from settings import info
21from widgets.ui_aboutdialog import Ui_AboutDialog23from widgets.ui_aboutdialog import Ui_AboutDialog
24
22class AboutDialog(QDialog):25class AboutDialog(QDialog):
23 '''26 '''
24 About dialog to display program about information.27 About dialog to display program about information.
@@ -32,12 +35,14 @@
32 QDialog.__init__(self, parent)35 QDialog.__init__(self, parent)
33 self._ui = Ui_AboutDialog()36 self._ui = Ui_AboutDialog()
34 self._ui.setupUi(self)37 self._ui.setupUi(self)
38 text = self._ui.aboutTextLabel.text()
39 self._ui.aboutTextLabel.setText(text.replace('##version##', info.VERSION_STRING))
35 self._makeConnections()40 self._makeConnections()
36 41
37 def _makeConnections(self):42 def _makeConnections(self):
38 self._ui.btn_Credits.clicked.connect(self.showCreditsDialog)43 self._ui.btn_Credits.clicked.connect(self.showCreditsDialog)
39 self._ui.btn_License.clicked.connect(self.showLicenseDialog)44 self._ui.btn_License.clicked.connect(self.showLicenseDialog)
40 45
41 def showCreditsDialog(self):46 def showCreditsDialog(self):
42 from widgets.creditsdialog import CreditsDialog47 from widgets.creditsdialog import CreditsDialog
43 dlg = CreditsDialog(self)48 dlg = CreditsDialog(self)
4449
=== modified file 'src/widgets/mainwindow.py'
--- src/widgets/mainwindow.py 2013-10-26 09:31:00 +0000
+++ src/widgets/mainwindow.py 2013-11-07 04:12:40 +0000
@@ -55,6 +55,8 @@
55 self._ui.stackedWidget.addWidget(self._workflowWidget)55 self._ui.stackedWidget.addWidget(self._workflowWidget)
56 self.setCurrentUndoRedoStack(self._workflowWidget.undoRedoStack())56 self.setCurrentUndoRedoStack(self._workflowWidget.undoRedoStack())
5757
58 self._pluginManagerDlg = None
59
58 def _createUndoAction(self, parent):60 def _createUndoAction(self, parent):
59 self.undoAction = QtGui.QAction('Undo', parent)61 self.undoAction = QtGui.QAction('Undo', parent)
60 self.undoAction.setShortcut(QtGui.QKeySequence('Ctrl+Z'))62 self.undoAction.setShortcut(QtGui.QKeySequence('Ctrl+Z'))
@@ -147,9 +149,10 @@
147 def pluginManager(self):149 def pluginManager(self):
148 from tools.pluginmanagerdialog import PluginManagerDialog150 from tools.pluginmanagerdialog import PluginManagerDialog
149 dlg = PluginManagerDialog(self)151 dlg = PluginManagerDialog(self)
152 self._pluginManagerDlg = dlg
150 dlg.setDirectories(self._model.pluginManager().directories())153 dlg.setDirectories(self._model.pluginManager().directories())
151 dlg.setLoadDefaultPlugins(self._model.pluginManager().loadDefaultPlugins())154 dlg.setLoadDefaultPlugins(self._model.pluginManager().loadDefaultPlugins())
152 dlg.reloadPlugins = self._model.pluginManager().load155 dlg.reloadPlugins = self._pluginManagerReloadPlugins
153156
154 dlg.setModal(True)157 dlg.setModal(True)
155 if dlg.exec_():158 if dlg.exec_():
@@ -159,6 +162,17 @@
159 self._model.pluginManager().load()162 self._model.pluginManager().load()
160 self._workflowWidget.updateStepTree()163 self._workflowWidget.updateStepTree()
161164
165 self._pluginManagerDlg = None
166
167 def _pluginManagerReloadPlugins(self):
168 '''
169 Callback from the plugin manager to reload the current plugins.
170 '''
171 self._model.pluginManager().setDirectories(self._pluginManagerDlg.directories())
172 self._model.pluginManager().setLoadDefaultPlugins(self._pluginManagerDlg.loadDefaultPlugins())
173 self._model.pluginManager().load()
174 self._workflowWidget.updateStepTree()
175
162 def pluginWizard(self):176 def pluginWizard(self):
163 from tools.pluginwizard.wizarddialog import WizardDialog177 from tools.pluginwizard.wizarddialog import WizardDialog
164 from tools.pluginwizard.skeleton import Skeleton178 from tools.pluginwizard.skeleton import Skeleton
165179
=== modified file 'src/widgets/qt/aboutdialog.ui'
--- src/widgets/qt/aboutdialog.ui 2013-02-20 00:50:38 +0000
+++ src/widgets/qt/aboutdialog.ui 2013-11-07 04:12:40 +0000
@@ -23,7 +23,16 @@
23 <string>About MAP Client</string>23 <string>About MAP Client</string>
24 </property>24 </property>
25 <layout class="QGridLayout" name="gridLayout">25 <layout class="QGridLayout" name="gridLayout">
26 <property name="margin">26 <property name="leftMargin">
27 <number>0</number>
28 </property>
29 <property name="topMargin">
30 <number>0</number>
31 </property>
32 <property name="rightMargin">
33 <number>0</number>
34 </property>
35 <property name="bottomMargin">
27 <number>0</number>36 <number>0</number>
28 </property>37 </property>
29 <item row="0" column="0">38 <item row="0" column="0">
@@ -49,12 +58,12 @@
49 </widget>58 </widget>
50 </item>59 </item>
51 <item>60 <item>
52 <widget class="QLabel" name="label">61 <widget class="QLabel" name="aboutTextLabel">
53 <property name="styleSheet">62 <property name="styleSheet">
54 <string notr="true">QLabel { background-color : white }</string>63 <string notr="true">QLabel { background-color : white }</string>
55 </property>64 </property>
56 <property name="text">65 <property name="text">
57 <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-size:24pt; font-weight:600;&quot;&gt;MAP Client&lt;/span&gt;&lt;/p&gt;&lt;p align=&quot;center&quot;&gt;MAP Client, a program to generate detailed musculoskeletal models for OpenSim.&lt;/p&gt;&lt;p align=&quot;center&quot;&gt;Copyright (C) 2012 University of Auckland&lt;/p&gt;&lt;p align=&quot;justify&quot;&gt;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.&lt;/p&gt;&lt;p align=&quot;justify&quot;&gt;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.&lt;/p&gt;&lt;p align=&quot;justify&quot;&gt;You should have received a copy of the GNU General Public License along with MAP Client. If not, see &amp;lt;http://www.gnu.org/licenses/&amp;gt;.&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>66 <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-size:24pt; font-weight:600;&quot;&gt;MAP Client ##version##&lt;/span&gt;&lt;/p&gt;&lt;p align=&quot;center&quot;&gt;MAP Client, a program to generate detailed musculoskeletal models for OpenSim.&lt;/p&gt;&lt;p align=&quot;center&quot;&gt;Copyright (C) 2012 University of Auckland&lt;/p&gt;&lt;p align=&quot;justify&quot;&gt;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.&lt;/p&gt;&lt;p align=&quot;justify&quot;&gt;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.&lt;/p&gt;&lt;p align=&quot;justify&quot;&gt;You should have received a copy of the GNU General Public License along with MAP Client. If not, see &amp;lt;http://www.gnu.org/licenses/&amp;gt;.&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
58 </property>67 </property>
59 <property name="wordWrap">68 <property name="wordWrap">
60 <bool>true</bool>69 <bool>true</bool>
6170
=== modified file 'src/widgets/ui_aboutdialog.py'
--- src/widgets/ui_aboutdialog.py 2013-06-14 01:51:27 +0000
+++ src/widgets/ui_aboutdialog.py 2013-11-07 04:12:40 +0000
@@ -2,7 +2,7 @@
22
3# Form implementation generated from reading ui file 'qt/aboutdialog.ui'3# Form implementation generated from reading ui file 'qt/aboutdialog.ui'
4#4#
5# Created: Fri Jun 14 11:25:36 20135# Created: Tue Nov 5 15:34:40 2013
6# by: pyside-uic 0.2.14 running on PySide 1.1.26# by: pyside-uic 0.2.14 running on PySide 1.1.2
7#7#
8# WARNING! All changes made in this file will be lost!8# WARNING! All changes made in this file will be lost!
@@ -34,11 +34,11 @@
34 self.label_2.setAlignment(QtCore.Qt.AlignCenter)34 self.label_2.setAlignment(QtCore.Qt.AlignCenter)
35 self.label_2.setObjectName("label_2")35 self.label_2.setObjectName("label_2")
36 self.verticalLayout.addWidget(self.label_2)36 self.verticalLayout.addWidget(self.label_2)
37 self.label = QtGui.QLabel(self.frame)37 self.aboutTextLabel = QtGui.QLabel(self.frame)
38 self.label.setStyleSheet("QLabel { background-color : white }")38 self.aboutTextLabel.setStyleSheet("QLabel { background-color : white }")
39 self.label.setWordWrap(True)39 self.aboutTextLabel.setWordWrap(True)
40 self.label.setObjectName("label")40 self.aboutTextLabel.setObjectName("aboutTextLabel")
41 self.verticalLayout.addWidget(self.label)41 self.verticalLayout.addWidget(self.aboutTextLabel)
42 self.gridLayout.addWidget(self.frame, 0, 0, 1, 1)42 self.gridLayout.addWidget(self.frame, 0, 0, 1, 1)
43 self.frame_3 = QtGui.QFrame(AboutDialog)43 self.frame_3 = QtGui.QFrame(AboutDialog)
44 self.frame_3.setFrameShape(QtGui.QFrame.StyledPanel)44 self.frame_3.setFrameShape(QtGui.QFrame.StyledPanel)
@@ -70,7 +70,7 @@
7070
71 def retranslateUi(self, AboutDialog):71 def retranslateUi(self, AboutDialog):
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))
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 &lt;http://www.gnu.org/licenses/&gt;.</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 &lt;http://www.gnu.org/licenses/&gt;.</p><p><br/></p></body></html>", None, QtGui.QApplication.UnicodeUTF8))
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))
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))
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))
7777
=== modified file 'src/widgets/workflowgraphicsitems.py'
--- src/widgets/workflowgraphicsitems.py 2013-10-25 07:33:34 +0000
+++ src/widgets/workflowgraphicsitems.py 2013-11-07 04:12:40 +0000
@@ -269,17 +269,21 @@
269 x_pos = -3 * w / 4269 x_pos = -3 * w / 4
270 uses_count += 1270 uses_count += 1
271 pred = 'http://physiomeproject.org/workflow/1.0/rdf-schema#uses'271 pred = 'http://physiomeproject.org/workflow/1.0/rdf-schema#uses'
272 tooltip_stub = 'uses: '
272 else: # port in provides_ports:273 else: # port in provides_ports:
273 port_total = provides_total274 port_total = provides_total
274 index = provides_count275 index = provides_count
275 x_pos = self.Size - w / 4276 x_pos = self.Size - w / 4
276 provides_count += 1277 provides_count += 1
277 pred = 'http://physiomeproject.org/workflow/1.0/rdf-schema#provides'278 pred = 'http://physiomeproject.org/workflow/1.0/rdf-schema#provides'
279 tooltip_stub = 'provides: '
278280
279 triples = port.getTriplesForPred(pred)281 triples = port.getTriplesForPred(pred)
280 triple_objects = [triple[2] for triple in triples]282 triple_objects = [triple[2] for triple in triples]
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
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
285 port_item.moveBy(x_pos, y_pos)
286 port_item.setToolTip(tooltip_stub + ', '.join(triple_objects))
283 self._step_port_items.append(port_item)287 self._step_port_items.append(port_item)
284288
285 self._configure_item = ConfigureIcon(self)289 self._configure_item = ConfigureIcon(self)
286290
=== modified file 'src/widgets/workflowgraphicsscene.py'
--- src/widgets/workflowgraphicsscene.py 2013-10-25 07:33:34 +0000
+++ src/widgets/workflowgraphicsscene.py 2013-11-07 04:12:40 +0000
@@ -161,7 +161,7 @@
161 self.parent().setWidgetUndoRedoStack(stack)161 self.parent().setWidgetUndoRedoStack(stack)
162162
163 def doneExecution(self):163 def doneExecution(self):
164 self.parent().executeWorkflow()164 self.parent().executeNext()
165165
166 def identifierOccursCount(self, identifier):166 def identifierOccursCount(self, identifier):
167 return self.parent().identifierOccursCount(identifier)167 return self.parent().identifierOccursCount(identifier)
168168
=== modified file 'src/widgets/workflowwidget.py'
--- src/widgets/workflowwidget.py 2013-10-25 07:33:34 +0000
+++ src/widgets/workflowwidget.py 2013-11-07 04:12:40 +0000
@@ -73,7 +73,6 @@
73 self.action_Close.setEnabled(workflowOpen)73 self.action_Close.setEnabled(workflowOpen)
74 self.setEnabled(workflowOpen)74 self.setEnabled(workflowOpen)
75 self.action_Save.setEnabled(wfm.isModified())75 self.action_Save.setEnabled(wfm.isModified())
76 self._ui.executeButton.setEnabled(wfm.scene().canExecute() and not wfm.isModified())
77 self._action_annotation.setEnabled(workflowOpen)76 self._action_annotation.setEnabled(workflowOpen)
7877
79 def updateStepTree(self):78 def updateStepTree(self):
@@ -92,8 +91,29 @@
92 print('setting active - workflow widget')91 print('setting active - workflow widget')
93 self._mainWindow.setCurrentUndoRedoStack(self._undoStack)92 self._mainWindow.setCurrentUndoRedoStack(self._undoStack)
9493
94 def executeNext(self):
95 self._mainWindow.execute()
96
95 def executeWorkflow(self):97 def executeWorkflow(self):
96 self._mainWindow.execute() # .model().workflowManager().execute()98 wfm = self._mainWindow.model().workflowManager()
99 error_count = 0
100 error_msg = ''
101 if wfm.isModified():
102 error_count += 1
103 error_msg += ' ' + str(error_count) + '. The workflow has not been saved.\n'
104
105 if not wfm.scene().canExecute():
106 error_count += 1
107 error_msg += ' ' + str(error_count) + '. Not all steps in the workflow have been successfully configured.\n'
108
109 if error_count == 0:
110 self._mainWindow.execute() # .model().workflowManager().execute()
111 else:
112 error_prefix = 'The workflow could not be executed for the following reason'
113 if error_count > 1:
114 error_prefix += 's'
115 error_prefix += ':\n\n'
116 QtGui.QMessageBox.critical(self, 'Workflow Execution', error_prefix + error_msg, QtGui.QMessageBox.Ok)
97117
98 def identifierOccursCount(self, identifier):118 def identifierOccursCount(self, identifier):
99 return self._mainWindow.model().workflowManager().identifierOccursCount(identifier)119 return self._mainWindow.model().workflowManager().identifierOccursCount(identifier)
100120
=== added file 'tests/widgets_tests/utils.py'
--- tests/widgets_tests/utils.py 1970-01-01 00:00:00 +0000
+++ tests/widgets_tests/utils.py 2013-11-07 04:12:40 +0000
@@ -0,0 +1,46 @@
1'''
2MAP Client, a program to generate detailed musculoskeletal models for OpenSim.
3 Copyright (C) 2012 University of Auckland
4
5This file is part of MAP Client. (http://launchpad.net/mapclient)
6
7 MAP Client is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11
12 MAP Client is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with MAP Client. If not, see <http://www.gnu.org/licenses/>..
19'''
20import unittest
21
22class UtilsTestCase(unittest.TestCase):
23
24 size = 60
25 h = 10
26 alpha = 5
27
28
29 def portPosition(self, n, i):
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)
31 return self.size / 2.0 - (n * self.h + (n - 1) * self.alpha) / 2.0 + (self.h + self.alpha) * i
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
33
34 def testPortLocation_neq1(self):
35 loc = self.portPosition(1, 0)
36 self.assertEqual(25, loc)
37
38 def testPortLocation_neq2(self):
39 loc = self.portPosition(2, 0)
40 self.assertEqual(17.5, loc)
41 loc = self.portPosition(2, 1)
42 self.assertEqual(32.5, loc)
43
44if __name__ == "__main__":
45 # import sys;sys.argv = ['', 'Test.testName']
46 unittest.main()
047
=== modified file 'tests/widgets_tests/widgetstests.py'
--- tests/widgets_tests/widgetstests.py 2013-02-18 22:44:26 +0000
+++ tests/widgets_tests/widgetstests.py 2013-11-07 04:12:40 +0000
@@ -21,9 +21,14 @@
21import unittest21import unittest
2222
23def suite():23def suite():
24 tests = unittest.TestSuite()
25
24 from widgets_tests.mainwindow import MainWindowTestCase26 from widgets_tests.mainwindow import MainWindowTestCase
25 tests = unittest.TestSuite()
26 tests.addTests(unittest.TestLoader().loadTestsFromTestCase(MainWindowTestCase))27 tests.addTests(unittest.TestLoader().loadTestsFromTestCase(MainWindowTestCase))
28
29 from widgets_tests.utils import UtilsTestCase
30 tests.addTests(unittest.TestLoader().loadTestsFromTestCase(UtilsTestCase))
31
27 return tests32 return tests
2833
29def load_tests(loader, tests, pattern):34def load_tests(loader, tests, pattern):

Subscribers

People subscribed via source and target branches

to all changes: