Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
FSL
fslgui
Commits
64cd57c6
Commit
64cd57c6
authored
Jun 02, 2020
by
Taylor Hanayik
Browse files
Merge branch 'props' into 'master'
Props See merge request
!2
parents
edb0d0f1
acee8809
Changes
12
Hide whitespace changes
Inline
Side-by-side
.gitignore
View file @
64cd57c6
...
...
@@ -5,4 +5,6 @@ __pycache__/
fslgui.egg-info/
run.sh
/mockups
/build
/dist
.vscode/
fsl/gui/core.py
0 → 100644
View file @
64cd57c6
"""
core functions for setting up and controlling GUIs
"""
import
os
import
sys
import
json
import
wx
import
yaml
import
fsleyes_props
as
props
import
fsl.gui.exceptions
as
fslerrs
import
fsl.gui.views
as
fslviews
import
fsl.gui.widgets
as
fslwidgets
allowedContainerWidgets
=
(
'column'
,
'row'
,
'group'
,
'page'
,
'notebook'
)
allowedWidgets
=
(
*
allowedContainerWidgets
,
'filepath'
,
'checkbox'
,
'choice'
,
'number'
,
'button'
,
'FsleyesImage'
)
allowedConfigKeys
=
(
'appName'
,
'windowSize'
,
'width'
,
'height'
,
'layout'
,
)
allowedKeys
=
(
*
allowedConfigKeys
,
*
allowedWidgets
,
)
def
isGroupKey
(
key
):
"""
returns True is key == "group"
returns False otherwise
"""
if
key
==
"group"
:
return
True
else
:
return
False
def
isPageKey
(
key
):
"""
returns True is key == "group"
returns False otherwise
"""
if
key
==
"page"
:
return
True
else
:
return
False
def
isContainerKey
(
key
):
"""
returns True if this key is associated with a container widget
from allowedContainerWidgets.
returns False otherwise
a container widget is not really intereactive for the user.
it holds other widgets as children
"""
if
key
in
allowedContainerWidgets
:
return
True
else
:
return
False
def
loadSpec
(
specFile
):
"""
return the loaded yaml data as a dict
"""
with
open
(
specFile
)
as
sf
:
spec
=
yaml
.
load
(
sf
,
Loader
=
yaml
.
FullLoader
)
return
spec
def
checkSpec
(
buildSpec
):
"""
make sure the build spec contains only expected fields
"""
def
dictCheck
(
d
):
if
type
(
d
)
is
list
:
for
entry
in
d
:
print
(
entry
)
for
k
,
v
in
entry
.
items
():
k
,
_
=
parseWidgetKey
(
k
)
if
k
not
in
allowedKeys
:
raise
fslerrs
.
NotAValidKey
(
"{} is not an allowed buildSpec key"
.
format
(
k
))
if
type
(
v
)
is
dict
:
dictCheck
(
v
)
dictCheck
(
buildSpec
)
def
layoutFrom
(
widget
):
"""
redo layout of all widgets up the parent tree from this widget.
Stop when we get to a frame. This was taken from a wx wiki post
"""
while
widget
.
GetParent
():
widget
.
Layout
()
widget
=
widget
.
GetParent
()
widget
.
Layout
()
if
widget
.
IsTopLevel
():
break
def
parseWidgetKey
(
key
):
"""
returns string tuple (key, tag)
the string "key" must not contain more than one underscore
"""
parts
=
key
.
split
(
sep
=
"_"
)
t
=
""
if
len
(
parts
)
==
1
:
n
=
parts
[
0
]
elif
len
(
parts
)
==
2
:
n
=
parts
[
0
]
t
=
parts
[
-
1
]
elif
len
(
parts
)
>
2
:
raise
Exception
(
"keys must be in the forms 'key' or 'key_name'"
)
else
:
n
=
""
return
n
,
t
def
isGroup
(
widget
):
if
isinstance
(
widget
.
GetSizer
(),
wx
.
StaticBoxSizer
):
return
True
else
:
return
False
def
isNotebook
(
widget
):
if
isinstance
(
widget
,
wx
.
Notebook
):
return
True
else
:
return
False
def
isPage
(
widget
):
if
isinstance
(
widget
.
GetParent
(),
wx
.
Notebook
):
return
True
else
:
return
False
def
addWidgetToGroup
(
parent
,
widget
):
w
=
widget
(
parent
.
GetSizer
().
GetStaticBox
())
return
w
def
addPageToNotebook
(
parent
,
widget
,
name
):
parent
.
AddPage
(
widget
,
name
)
return
widget
def
widgetFromKey
(
key
):
"""
returns the appropriate widget creation function,
but does not return the actual widget instance.
That comes later.
"""
widget
=
getattr
(
fslwidgets
,
key
)
return
widget
def
makeWidget
(
parent
,
propObj
,
key
,
tag
,
value
):
"""
returns the appropriate widget from a key string
key: str parsed from the form "key_tag" or "key"
tag: str parsed from the form "key_tag"
value: the dict value for this key. Only value==dict is used here
"""
if
key
in
allowedWidgets
:
wid
=
widgetFromKey
(
key
)
print
(
'making widget: '
,
key
)
if
isinstance
(
value
,
dict
):
w
=
wid
(
parent
,
propObj
,
**
value
)
else
:
if
isGroupKey
(
key
):
w
=
wid
(
parent
,
tag
)
else
:
if
isGroup
(
parent
):
w
=
addWidgetToGroup
(
parent
,
wid
)
elif
isNotebook
(
parent
):
w
=
wid
(
parent
)
w
=
addPageToNotebook
(
parent
,
w
,
tag
)
else
:
w
=
wid
(
parent
)
return
w
def
layout
(
parent
,
buildSpec
,
propObj
):
"""
layout all allowed widgets in a buildSpec
"""
for
entry
in
buildSpec
:
for
k
,
v
in
entry
.
items
():
k
,
t
=
parseWidgetKey
(
k
)
w
=
makeWidget
(
parent
,
propObj
,
k
,
t
,
v
,)
if
not
isPage
(
w
):
parent
.
GetSizer
().
Add
(
w
,
proportion
=
0
,
flag
=
wx
.
ALL
|
wx
.
EXPAND
,
border
=
5
)
layoutFrom
(
w
)
if
isContainerKey
(
k
):
if
type
(
v
)
is
list
:
layout
(
w
,
v
,
propObj
)
parent
.
Layout
()
return
parent
def
buildGUI
(
buildSpec
,
propObj
):
"""
build a GUI from a build spec dictionary (or JSON)
"""
checkSpec
(
buildSpec
)
mainWin
=
wx
.
Frame
(
None
)
mainWin
.
SetTitle
(
buildSpec
[
'appName'
])
mainWin
.
SetSize
((
buildSpec
[
'windowSize'
][
'width'
],
buildSpec
[
'windowSize'
][
'height'
]))
mainSizer
=
wx
.
BoxSizer
(
wx
.
VERTICAL
)
mainWin
.
SetSizer
(
mainSizer
)
mainWin
=
layout
(
mainWin
,
buildSpec
[
'layout'
],
propObj
)
mainWin
.
Centre
()
mainWin
.
Show
()
return
mainWin
fsl/gui/exceptions.py
0 → 100644
View file @
64cd57c6
"""
custom exceptions to be used in fslgui
"""
class
NotAValidKey
(
Exception
):
pass
class
MissingRequiredKey
(
Exception
):
pass
fsl/gui/guis.py
View file @
64cd57c6
...
...
@@ -9,6 +9,9 @@ import random
import
wx
from
fsl.data
import
image
import
fsleyes_props
as
props
#from fsleyes_props.build import buildGUI
props
.
initGUI
()
import
fsl.gui.views
as
fslviews
import
fsl.gui.tools
as
fsltools
...
...
@@ -210,4 +213,7 @@ class FlirtGui(BaseGui):
else
:
self
.
view
.
input
.
set_label
(
"Input image*"
)
self
.
view
.
input_lowres
.
Hide
()
self
.
_layout_from
(
self
.
view
.
input_lowres
)
\ No newline at end of file
self
.
_layout_from
(
self
.
view
.
input_lowres
)
fsl/gui/scripts/bet_gui.py
View file @
64cd57c6
#!/usr/bin/env python
import
wx
import
sys
from
fsl.gui.guis
import
BetGui
import
argparse
from
collections
import
OrderedDict
import
wx
import
yaml
from
fsl.utils
import
idle
import
fsleyes_props
as
props
import
fsl.gui.core
as
core
import
fsl.gui.views
as
fslViews
import
fsl.gui.tools
as
fslTools
class
Bet
(
props
.
HasProperties
):
"""
Bet is the data model for Bet Gui
"""
betRunChoices
=
OrderedDict
((
(
'default'
,
'Default Brain extraction'
),
(
'robust'
,
'Robust'
),
(
'eyeClean'
,
'Eye and optic nerve cleanup'
),
(
'biasAndNeckClean'
,
'Bias field and neck Clean'
),
(
'smallZ'
,
'Brain has small Z FOV'
),
(
'4d'
,
'Apply to 4D fMRI'
),
(
'withBetSurf'
,
'Also run betsurf'
),
(
'withBetSurfT2'
,
'Run betsurf with additional T2 image'
)
))
inputFile
=
props
.
FilePath
(
required
=
True
,
exists
=
True
)
outputFile
=
props
.
FilePath
(
required
=
True
,
exists
=
False
)
fval
=
props
.
Real
(
precision
=
0.001
,
minval
=
0
,
maxval
=
1
,
clamped
=
True
)
betRunType
=
props
.
Choice
(
choices
=
list
(
betRunChoices
.
values
()))
parser
=
argparse
.
ArgumentParser
(
description
=
"FSL's brain extraction tool"
)
def
__init__
(
self
,
**
kwargs
):
super
().
__init__
(
**
kwargs
)
def
main
():
args
=
parser
.
parse_args
()
# get an app instance
app
=
wx
.
App
()
frame
=
wx
.
Frame
(
None
,
size
=
(
800
,
600
))
sizer
=
wx
.
BoxSizer
(
wx
.
VERTICAL
)
betgui
=
BetGui
(
frame
,
"BET"
)
sizer
.
Add
(
betgui
.
view
,
proportion
=
1
,
flag
=
wx
.
EXPAND
|
wx
.
ALL
,
border
=
5
)
frame
.
SetSizer
(
sizer
)
frame
.
Centre
()
frame
.
Show
()
bet
=
Bet
()
betView
=
core
.
loadSpec
(
fslViews
.
bet
)
gui
=
core
.
buildGUI
(
betView
,
bet
)
app
.
MainLoop
()
if
__name__
==
"__main__"
:
sys
.
exit
(
main
())
fsl/gui/scripts/pnm_gui.py
0 → 100644
View file @
64cd57c6
#!/usr/bin/env python
import
sys
import
argparse
from
collections
import
OrderedDict
import
wx
import
yaml
from
fsl.utils
import
idle
import
fsleyes_props
as
props
import
fsl.gui.core
as
core
import
fsl.gui.views
as
fslViews
import
fsl.gui.tools
as
fslTools
def
run
(
p
,
button
):
print
(
'RUN COMMAND: '
)
print
(
p
.
command
())
class
Pnm
(
props
.
HasProperties
):
"""
Pnm is the data model for PnmGui
"""
physioFile
=
props
.
FilePath
(
required
=
True
,
exists
=
True
)
imageFile
=
props
.
FilePath
(
required
=
True
,
exists
=
True
)
sliceOrderChoices
=
OrderedDict
((
(
'up'
,
'Up'
),
(
'down'
,
'Down'
),
(
'iup'
,
'Interleaved Up'
),
(
'idown'
,
'Interleaved Down'
),
(
'file'
,
'User Specified File'
)
))
scannerSliceDirChoices
=
OrderedDict
((
(
'x'
,
'X'
),
(
'y'
,
'Y'
),
(
'z'
,
'Z'
)
))
idxCardiac
=
props
.
Int
(
minval
=
0
,
slider
=
False
)
idxResp
=
props
.
Int
(
minval
=
0
,
slider
=
False
)
idxTrig
=
props
.
Int
(
minval
=
0
,
slider
=
False
)
pulseOxTrig
=
props
.
Boolean
()
sampleRate
=
props
.
Int
(
minVal
=
0
,
slider
=
False
)
tr
=
props
.
Real
(
precision
=
0.01
,
slider
=
False
)
sliceOrder
=
props
.
Choice
(
choices
=
list
(
sliceOrderChoices
.
values
()))
sliceDir
=
props
.
Choice
(
choices
=
list
(
scannerSliceDirChoices
.
values
()))
sliceTimeFile
=
props
.
FilePath
(
required
=
False
,
exists
=
True
)
outputFile
=
props
.
FilePath
(
required
=
True
,
exists
=
False
)
orderCardiacEV
=
props
.
Int
(
minVal
=
0
,
slider
=
False
)
orderRespEV
=
props
.
Int
(
minVal
=
0
,
slider
=
False
)
orderCardiacIntEV
=
props
.
Int
(
minVal
=
0
,
slider
=
False
)
orderRespIntEV
=
props
.
Int
(
minVal
=
0
,
slider
=
False
)
rvt
=
props
.
Boolean
()
heartRate
=
props
.
Boolean
()
csf
=
props
.
Boolean
()
csfFile
=
props
.
FilePath
(
required
=
False
,
exists
=
True
)
cardiacSmoothSec
=
props
.
Real
(
precision
=
0.001
,
slider
=
False
)
respSmoothSec
=
props
.
Real
(
precision
=
0.001
,
slider
=
False
)
hrSmoothSec
=
props
.
Real
(
precision
=
0.001
,
slider
=
False
)
rvtSmoothSec
=
props
.
Real
(
precision
=
0.001
,
slider
=
False
)
applyCleanup
=
props
.
Boolean
()
invertRespTrace
=
props
.
Boolean
()
invertCardiacTrace
=
props
.
Boolean
()
runButton
=
props
.
Button
(
text
=
'Run'
,
callback
=
run
)
def
command
(
self
):
return
"pnm_stage1 blah blah blah"
def
__init__
(
self
,
**
kwargs
):
super
().
__init__
(
**
kwargs
)
def
main
():
# get an app instance
app
=
wx
.
App
()
pnm
=
Pnm
()
pnmView
=
core
.
loadSpec
(
fslViews
.
pnm
)
gui
=
core
.
buildGUI
(
pnmView
,
pnm
)
app
.
MainLoop
()
if
__name__
==
"__main__"
:
sys
.
exit
(
main
())
fsl/gui/tools.py
View file @
64cd57c6
...
...
@@ -8,9 +8,11 @@ tools are models that store data and allow FSL tools to be called from other pyt
# Author: Taylor Hanayik <hanayik@gmail.com>
import
os
import
subprocess
from
collections
import
OrderedDict
from
fsl.utils
import
idle
from
fsl.utils
import
idle
import
fsleyes_props
as
props
class
Bet
(
object
):
...
...
@@ -318,3 +320,5 @@ class Flirt(object):
search_zmax
=
90
):
pass
fsl/gui/view_specs/bet_spec.yaml
0 → 100644
View file @
64cd57c6
---
# the pnm view layout specification
appName
:
BET
windowSize
:
width
:
800
height
:
500
layout
:
-
notebook
:
-
page_Inputs
:
-
group_Files
:
-
column
:
-
filepath
:
{
label
:
Input file
,
propName
:
inputFile
}
-
filepath
:
{
label
:
Output file
,
propName
:
outputFile
}
-
row
:
-
number
:
{
label
:
F value
,
propName
:
fval
,
showLimits
:
0
}
-
column
:
-
FsleyesImage
:
{
name
:
FsleyesImage
}
-
page_Options
:
-
column
:
-
choice
:
{
label
:
BET run type
,
propName
:
betRunType
}
\ No newline at end of file
fsl/gui/view_specs/pnm_spec.yaml
0 → 100644
View file @
64cd57c6
---
# the pnm view layout specification
appName
:
PNM
windowSize
:
width
:
800
height
:
840
layout
:
-
notebook
:
-
page_Setup
:
-
group_Input
:
-
column
:
-
filepath
:
{
label
:
Physio file
,
propName
:
physioFile
}
-
filepath
:
{
label
:
Image file
,
propName
:
imageFile
}
-
group_Format
:
-
row
:
-
number
:
{
label
:
Caridac column
,
propName
:
idxCardiac
}
-
number
:
{
label
:
Respitory column
,
propName
:
idxResp
}
-
number
:
{
label
:
Scan Trig column
,
propName
:
idxTrig
}
-
row
:
-
checkbox
:
{
label
:
Pulse Ox Trig
,
propName
:
pulseOxTrig
}
-
number
:
{
label
:
Sample Rate (Hz)
,
propName
:
sampleRate
}
-
number
:
{
label
:
TR (sec)
,
propName
:
tr
}
-
row
:
-
choice
:
{
label
:
Slice Order
,
propName
:
sliceOrder
}
-
filepath
:
{
label
:
Slice time file
,
propName
:
sliceTimeFile
}
-
choice
:
{
label
:
Slice Direction
,
propName
:
sliceDir
}
-
group_Output
:
-
column
:
-
filepath
:
{
label
:
Output file
,
propName
:
outputFile
}
-
group_EVs
:
-
row
:
-
number
:
{
label
:
Cardiac EV order
,
propName
:
orderCardiacEV
}
-
number
:
{
label
:
Resp EV order
,
propName
:
orderRespEV
}
-
number
:
{
label
:
Cariac interaction EV order
,
propName
:
orderCardiacIntEV
}
-
number
:
{
label
:
Resp interaction EV order
,
propName
:
orderRespIntEV
}
-
row
:
-
checkbox
:
{
label
:
RVT
,
propName
:
rvt
}
-
checkbox
:
{
label
:
Heart Rate
,
propName
:
heartRate
}
-
checkbox
:
{
label
:
CSF
,
propName
:
csf
}
-
column
:
-
filepath
:
{
label
:
CSF mask
,
propName
:
csfFile
}
-
page_Advanced
:
-
row
:
-
number
:
{
label
:
Cardiac smooth (sec)
,
propName
:
cardiacSmoothSec
}
-
number
:
{
label
:
Resp smooth (sec)
,
propName
:
respSmoothSec
}
-
number
:
{
label
:
HR smooth (sec)
,
propName
:
hrSmoothSec
}
-
number
:
{
label
:
RVT smooth (sec)
,
propName
:
rvtSmoothSec
}
-
row
:
-
checkbox
:
{
label
:
Clean up
,
propName
:
applyCleanup
}
-
checkbox
:
{
label
:
Invert resp trace
,
propName
:
invertRespTrace
}
-
checkbox
:
{
label
:
Invert cardiac trace
,
propName
:
invertCardiacTrace
}
-
page_Plots
:
-
row
:
-
button
:
{
label
:
Run
,
propName
:
runButton
}
\ No newline at end of file
fsl/gui/views.py
View file @
64cd57c6
#!/usr/bin/env python
#
# bet_view.py
#
# Author: Taylor Hanayik <hanayik@gmail.com>
"""
views are the graphical layouts of windows.
User interaction is controlled by guis.py.
Data is stored and updated using tools.py
Author: Taylor Hanayik, hanayik@gmail.com
"""
from
os.path
import
dirname
,
join
,
abspath
import
wx
import
wx.lib.scrolledpanel
as
scrolled
...
...
@@ -201,7 +210,7 @@ class BetView(wx.Panel):
self
.
SetSizer
(
sizer
)
class
PnmView
(
wx
.
Panel
):
class
NotUsed
PnmView
(
wx
.
Panel
):
"""
PnmView defines the graphical layout of widgets for the PNM gui
"""
...
...
@@ -209,6 +218,15 @@ class PnmView(wx.Panel):
super
().
__init__
(
parent
)
sizer
=
wx
.
BoxSizer
(
wx
.
VERTICAL
)
# init title panel
self
.
title_panel
=
fslwidgets
.
Title
(
self
,
title
=
title
)
# add title panel
sizer
.
Add
(
self
.
title_panel
,
proportion
=
0
,
flag
=
wx
.
ALIGN_CENTER
|
wx
.
ALL
,
border
=
5
)
#
# lastly set the sizer with all widgets added
self
.
SetSizer
(
sizer
)
class
FlirtView
(
wx
.
Panel
):
"""
...
...
@@ -240,7 +258,7 @@ class FlirtView(wx.Panel):
self
.
input_lowres
=
fslwidgets
.
Input
(
self
).
set_label
(
"Low res image*"
)
sizer
.
Add
(
self
.
input_lowres
,
proportion
=
0
,
flag
=
wx
.
EXPAND
|
wx
.
ALL
,
border
=
5
)
self
.
input_lowres
.
Hide
()
# high res input panel
self
.
input
=
fslwidgets
.
Input
(
self
).
set_label
(
"Input image*"
)
sizer
.
Add
(
self
.
input
,
proportion
=
0
,
flag
=
wx
.
EXPAND
|
wx
.
ALL
,
border
=
5
)
...
...
@@ -259,12 +277,18 @@ class FlirtView(wx.Panel):
# lastly set the sizer with all widgets added
self
.
SetSizer
(
sizer
)
VIEW_PATH
=
join
(
abspath
(
dirname
(
__file__
)),
'view_specs'
)
pnm
=
join
(
VIEW_PATH
,
'pnm_spec.yaml'
)
bet
=
join
(
VIEW_PATH
,
'bet_spec.yaml'
)
class
FslView
():
"""
FslView is the main FSL start window. not to be
confused with the deprecated image viewer fslview (RIP)
"""
fsl/gui/widgets.py
View file @
64cd57c6
...
...
@@ -13,12 +13,120 @@ import fsleyes.views.orthopanel as orthopanel
import
fsleyes.profiles
as
profiles
import
fsleyes.profiles.profilemap
as
profilemap
import
fsleyes.colourmaps
as
colourmaps
import
fsleyes_props
as
props
from
fsl.utils.platform
import
platform
as
fslplatform
from
fsl.utils
import
idle
import
fsl.data.image
as
fslimage
import
fsl.gui.icons
as
fslicons