-
Notifications
You must be signed in to change notification settings - Fork 4
/
cmtkgui.py
382 lines (344 loc) · 10.3 KB
/
cmtkgui.py
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
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
'''
cmtkgui module containing helper functions for Fiji CMTK GUI
'''
import os, sys, time, csv
import subprocess, re, urllib2, tempfile
import platform
is_jython = platform.system()=="Java"
if(is_jython):
import java.io.File
gui_tarball_url='https://github.com/jefferis/fiji-cmtk-gui/tarball/master'
def myExit(err):
'''Placeholder until I figure out suitable way to break out of jython script'''
if err==None:
err=''
sys.exit(err)
def myErr(err):
from ij import IJ
IJ.error(err)
myExit(err)
def log(msg):
'''
logger that chooses simple prints or ImageJ log as appropriate
'''
if is_jython:
from ij import IJ
if IJ.debugMode:
print(msg)
else:
print(msg)
def which(execname):
'''
run system which command to locate an executable
'''
return subprocess.Popen(["which", execname], stdout=subprocess.PIPE).communicate()[0].rstrip()
def findExecutable(execname,msg=''):
'''
@return full path to a command using the shell's which function
'''
execpath=which(execname)
if execpath == '' :
import fiji.util.gui.GenericDialogPlus
gd = fiji.util.gui.GenericDialogPlus(msg)
if msg == '' :
msg="Locate the directory containing program "+execname
gd.addDirectoryField(msg, "")
gd.showDialog()
execdir = gd.getNextString()
execpath = os.path.join(execdir,execname)
if os.path.exists(execpath) :
return execpath
else :
sys.exit('Unable to locate '+execname+' in directory '+execdir)
else : return execpath
def relpath(path, basedir = os.curdir):
''' @return a relative path from basedir to (sub) path.
basedir defaults to curdir
Does not work with paths outside basedir
This is superseded by a builtin in python >=2.6
'''
basedir = os.path.abspath(basedir)
path = os.path.abspath(path)
if basedir == path :
return '.'
if basedir in path :
# nb joining empty string appends terminal pathsep in platform independent way
return path.replace(os.path.join(basedir,''),'')
return (path)
def makescript(cmd,rootdir,outdir):
'''
Make a shell script file that can be used to rerun the warp action, using
chmod to ensure that it is executable.
On MacOS X the file suffix is command so that it can be double clicked in the Finder.
'''
if not os.path.exists(outdir): os.mkdir(outdir)
mtime = time.strftime('%Y-%m-%d_%H.%M.%S')
suffix='sh'
if "MacOSX" in os_string():
suffix='command'
filename= "munger_%s.%s" %(mtime,suffix)
filepath=os.path.join(outdir,filename)
f = open(filepath, 'w')
f.write('#!/bin/sh\n# %s\ncd \"%s"\n%s' % (mtime, rootdir, cmd))
f.close()
os.chmod(filepath,0755)
return filepath
def downloads():
'''
Fetch a list of available tar.gz downloads
Exclude macports versions or
'''
nitrc_url='http://www.nitrc.org'
cmtk_dwld_url=nitrc_url+'/frs/?group_id=212'
response = urllib2.urlopen(cmtk_dwld_url)
html = response.readlines()
urls=list()
for line in html:
match=re.search('href="(.*tar.gz)".*',line)
if match is not None:
badmatch=re.search('(-mp-|-MacPorts-|Source)',line)
if badmatch == None:
relpath=match.group(1)
url=nitrc_url+relpath
urls.append(url)
if len(urls)==0:return None
else: return urls
def os_string():
'''
Return a string indicating the current OS formatted according to CMTK filename conventions
Linux
MacOSX-10.4
MacOSX-10.5
MacOSX-10.6
CYGWIN
'''
from java.lang import System
osname=System.getProperty("os.name")
if osname.startswith('Windows'):
return 'CYGWIN'
elif osname.startswith('Linux'):
return 'Linux'
elif osname.startswith('Mac'):
osversion=System.getProperty("os.version")
match=re.match(r'10\.([0-9]+)(\.[0-9])*',osversion)
if match==None:
sys.exit('Unable to identify OS version: '+osversion)
osmajor=match.group(1)
iosmajor=int(osmajor)
if iosmajor<4:
myErr('Sorry CMTK requires MacOSX >=10.4')
elif iosmajor>6:
osmajor='6'
return 'MacOSX-10.'+osmajor
else:
return None
def recommended_file(files):
'''
Pick the recommended download file based on current OS
'''
osname=os_string()
if osname==None:
return None
for f in files:
match=re.search(osname,f)
if match:
return f
return None
def install_dir():
'''return sensible location to install cmtk'''
from ij import IJ
ijdir=IJ.getDirectory('imagej')
return os.path.join(ijdir,'bin','cmtk')
def gui_install_dir():
'''return sensible location to install CMTK GUI'''
from ij import IJ
pluginsdir=IJ.getDirectory('plugins')
return os.path.join(pluginsdir,'CMTK_Registration')
def bin_dir():
'''
Return path to cmtk binaries
Currently the same as install dir
'''
return install_dir()
def system_cmtk_bin_dir(cmtktool='streamxform'):
'''
return path to the system folder containing CMTK binaries
'''
extradirs=('/usr/local/bin', '/opt/local/bin', '/usr/local/lib/cmtk/bin', '/opt/local/lib/cmtk/bin')
cmtktoolpath=which(cmtktool)
if(cmtktoolpath!=''):
return os.path.dirname(cmtktoolpath)
cmtkwrapperpath=which("cmtkk")
if(cmtkwrapperpath!=''):
return os.path.abspath(os.path.join(os.path.dirname(cmtkwrapperpath),"..","lib", "cmtk","bin"))
return dir_containing_file(extradirs, cmtktool)
def dir_containing_file(dirs, filename):
'''
return absolute path to directory containing a file
'''
for d in dirs:
filepath=os.path.join(d, filename)
if os.path.exists(filepath):
return os.path.abspath(d)
return None
def tool_path(tool):
'''path to a CMTK tool'''
return os.path.join(bin_dir(),tool)
def installed_version():
'''Return current version of CMTK installed in Fiji bin dir'''
ver=run_tool_stdout('warpx','--version')
if ver:
return ver
else: return ""
def run_tool_stdout(tool,args):
'''Return stdout after running CMTK tool with args'''
toolp=tool_path(tool)
if not os.path.exists(toolp):
return None
arglist=[toolp]
if isinstance(args,list):
arglist+=args
elif args is None:
arglist+=['']
else:
arglist+=[args]
return subprocess.Popen(arglist, stdout=subprocess.PIPE).communicate()[0].rstrip()
def nitrc_version():
'''Check NITRC website to see what version is available'''
match=re.search(r"CMTK-([0-9]+\.[0-9]+\.[0-9]+)",os.path.basename(downloads()[0]))
if match:
return match.group(1)
else:
return None
def parse_version_string(ver_string):
'''
Parse a CMTK version string into major,minor and patch numbers (integers)
'''
if ver_string is None:
return None
v=re.match(r"(?P<major>[0-9]+)\.(?P<minor>[0-9]+)\.(?P<patch>[0-9]+)",ver_string)
if v:
ver_list=(v.group('major'),v.group('minor'),v.group('patch'))
return map(int,ver_list)
else:
return None
def update_available():
'''
Check if NITRC version is newer than current version.
Assumes that NITRC version can never be older than current version.
'''
r=parse_version_string(nitrc_version())
l=parse_version_string(installed_version())
if r is None: return None
if l is None: return True
if l[0]<r[0]: return True
elif l[1]<r[1]: return True
elif l[2]<r[2]: return True
else: return False
def gui_github_versioninfo():
'''
Create a dictionary containing version information for current
github code
'''
try:
u = urllib2.urlopen(gui_tarball_url)
headers = u.info()
date = headers['date']
size = int(headers['Content-Length'])
filename = re.sub(".*filename=","",headers['Content-Disposition'])
u.close()
abbrev_sha1 = re.sub(".*-([a-f0-9]+).tar.gz","\\1",filename)
return {'abbrev_sha1':abbrev_sha1, 'filename':filename, 'date':date, 'size':size}
except IOError, e:
myErr("Unable to read github repository")
except :
myErr("Trouble parsing github return information")
def gui_local_versioninfo():
'''
Read a csv file containing the version information for current
CMTK GUI code
'''
version_file = os.path.join(gui_install_dir(),"CMTKGUIVersion.csv")
if not os.path.exists(version_file):
return {}
versioninfo = {}
for key, val in csv.reader(open(version_file)):
versioninfo[key] = val
return(versioninfo)
def gui_write_local_versioninfo(versioninfo):
'''
Write a csv file containing the version information for current
CMTK GUI code
'''
version_file = os.path.join(gui_install_dir(),"CMTKGUIVersion.csv")
myfile = open(version_file, "w")
w = csv.writer(myfile)
for key, val in versioninfo.items():
w.writerow([key, val])
myfile.close()
def gui_update_available(github_versioninfo=None):
'''
Check if an update is available on github
optionally using a cached copy of the versioninfo dictionary
'''
lvi=gui_local_versioninfo()
if not lvi.has_key('abbrev_sha1'):
return True
if github_versioninfo is None:
github_versioninfo=gui_github_versioninfo()
if lvi['abbrev_sha1']==github_versioninfo['abbrev_sha1']:
return(False)
else:
return(True)
def download_and_untar_url(download_url,target_dir,untarfun,download_file=None,
download_msg=None):
'''
download file in temporary location
untar to target_dir using untarfun
clean up download
'''
# open url and set up using header information
u = urllib2.urlopen(download_url)
headers = u.info()
download_size = int(headers['Content-Length'])
if download_file == None:
if headers.has_key('Content-Disposition'):
download_file = re.sub(".*filename=","",headers['Content-Disposition'])
else:
myErr("No filename specified for download and none in http header!")
if download_msg==None:
download_msg='Downloading: %s' % (download_file)
tf=tempfile.NamedTemporaryFile(suffix=download_file)
print 'Downloading '+download_url+' to '+ tf.name
print "Download size should be %d" % (download_size)
from ij import IJ
# Now for the download
block_size=100000
if download_size>block_size:
bytes_read=0
while bytes_read<download_size:
IJ.showStatus("%s (%.1f/%.1f Mb)" %
(download_msg,(bytes_read/1000000.0),(download_size/1000000.0)))
IJ.showProgress(bytes_read,download_size)
tf.file.write(u.read(block_size))
bytes_read+=block_size
IJ.showProgress(1.0)
else:
tf.file.write(u.read())
u.close()
tf.file.close()
print ('Downloaded file has size %d')%(os.path.getsize(tf.name))
untarfun(tf.name,target_dir)
IJ.showStatus('Cleaning up!')
tf.close()
def movefile(frompath, todir):
'''
move a file to a new location (renaming) using Java calls
needed because jython shutil.move has bugs
'''
if is_jython:
fromf=java.io.File(frompath)
tof=java.io.File(os.path.join(todir,os.path.basename(frompath)))
fromf.renameTo(tof)
else:
shutil.move(frompath, to)