Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
F
fslpy
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Deploy
Releases
Container Registry
Model registry
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
Michiel Cottaar
fslpy
Commits
8f56ea79
Commit
8f56ea79
authored
11 years ago
by
Paul McCarthy
Browse files
Options
Downloads
Patches
Plain Diff
First steps towards de-Tkinterising the properties code. Things are broken.
parent
6aa199df
No related branches found
No related tags found
No related merge requests found
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
fsl/__init__.py
+0
-0
0 additions, 0 deletions
fsl/__init__.py
fsl/props/__init__.py
+5
-6
5 additions, 6 deletions
fsl/props/__init__.py
fsl/props/properties.py
+99
-158
99 additions, 158 deletions
fsl/props/properties.py
with
104 additions
and
164 deletions
fsl/__init__.py
0 → 100644
+
0
−
0
View file @
8f56ea79
This diff is collapsed.
Click to expand it.
fsl/props/__init__.py
+
5
−
6
View file @
8f56ea79
#!/usr/bin/env python
#!/usr/bin/env python
#
#
# __init__.py - Sets up the
tk
prop package namespace.
# __init__.py - Sets up the
fsl.
prop
s
package namespace.
#
#
# Author: Paul McCarthy <pauldmccarthy@gmail.com>
# Author: Paul McCarthy <pauldmccarthy@gmail.com>
#
#
from
tkprop.properties
import
\
from
fsl.props.properties
import
\
TkVarProxy
,
\
PropertyBase
,
\
PropertyBase
,
\
HasProperties
HasProperties
from
tk
prop.properties_types
import
\
from
fsl.
prop
s
.properties_types
import
\
Boolean
,
\
Boolean
,
\
Int
,
\
Int
,
\
Double
,
\
Double
,
\
...
@@ -20,10 +19,10 @@ from tkprop.properties_types import \
...
@@ -20,10 +19,10 @@ from tkprop.properties_types import \
Choice
,
\
Choice
,
\
List
List
from
tk
prop.widgets
import
\
from
fsl.
prop
s
.widgets
import
\
makeWidget
makeWidget
from
tk
prop.build
import
\
from
fsl.
prop
s
.build
import
\
buildGUI
,
\
buildGUI
,
\
ViewItem
,
\
ViewItem
,
\
Button
,
\
Button
,
\
...
...
This diff is collapsed.
Click to expand it.
fsl/props/properties.py
+
99
−
158
View file @
8f56ea79
#!/usr/bin/env python
#!/usr/bin/env python
#
#
# properties.py - Tkinter control variables encapsulated inside Python
# properties.py - Python descriptors of various types.
# descriptors.
#
#
# This module should not be imported directly - import the
tk
props
# This module should not be imported directly - import the
fsl.
props
# package instead. Property type definitions are in properties_types.py.
# package instead. Property type definitions are in properties_types.py.
#
#
# Usage:
# Usage:
...
@@ -126,64 +125,56 @@
...
@@ -126,64 +125,56 @@
# author: Paul McCarthy <pauldmccarthy@gmail.com>
# author: Paul McCarthy <pauldmccarthy@gmail.com>
#
#
import
types
import
types
import
logging
as
log
import
logging
as
log
import
Tkinter
as
tk
class
TkVarProxy
(
object
):
class
PropertyValue
(
object
):
"""
"""
Proxy object which encapsulates a
Tkinter control variable. One or
Proxy object which encapsulates a
single value for a property.
more TkVarProxy
objects is created for every property
of a
One or more PropertyValue
objects is created for every property
HasProperties instance.
of a
HasProperties instance.
"""
"""
def
__init__
(
self
,
tkP
rop
,
owner
,
tkVarType
,
value
,
name
=
None
):
def
__init__
(
self
,
p
rop
,
owner
,
value
,
name
=
None
):
"""
"""
Creates an instance of the specified tkVarType, and sets a
trace on it.
Parameters:
Parameters:
- tkProp: The PropertyBase object which manages this
- prop: The PropertyBase object which manages this
TkVarProxy.
PropertyValue.
- owner: The HasProperties object, the owner of the
- owner: The HasProperties object, the owner of the
tkProp property.
prop property.
- tkVarType: The type of Tkinter control variable that
- value: Initial value.
this TkVarProxy encapsulates.
- name: Variable name - if not provided, a default,
- value: Initial value.
unique name is created.
- name: Variable name - if not provided, a default,
unique name is created.
"""
"""
if
name
is
None
:
name
=
'
{}_{}
'
.
format
(
tkP
rop
.
label
,
id
(
self
))
if
name
is
None
:
name
=
'
{}_{}
'
.
format
(
p
rop
.
label
,
id
(
self
))
self
.
tkVarType
=
tkVarType
self
.
prop
=
prop
self
.
tkProp
=
tkProp
self
.
owner
=
owner
self
.
owner
=
owner
self
.
changeListeners
=
{}
self
.
name
=
name
self
.
name
=
name
self
.
changeListeners
=
{}
self
.
_value
=
value
self
.
_valid
=
None
self
.
_lastValue
=
value
self
.
_lastValue
=
value
self
.
_lastValid
=
None
self
.
_lastValid
=
None
self
.
tkVar
=
tkVarType
(
value
=
value
,
name
=
name
)
self
.
traceName
=
self
.
tkVar
.
trace
(
'
w
'
,
self
.
_traceCb
)
def
addListener
(
self
,
name
,
callback
):
def
addListener
(
self
,
name
,
callback
):
"""
"""
Adds a listener for this va
riab
le. When the va
riable valu
e
Adds a listener for this val
u
e. When the va
lue changes, th
e
changes, the
listener callback function is called. Listener
listener callback function is called. Listener
notification
notification
may also be programmatically triggered via the
may also be programmatically triggered via the
PropertyBase.forceValidation method. The callback function
PropertyBase.forceValidation method. The callback function
must accept these arguments:
must accept these arguments:
value - The property value
value - The property value
valid - Whether the value is valid or invalid
valid - Whether the value is valid or invalid
instance - The HasProperties instance
instance - The HasProperties instance
tkP
rop - The PropertyBase instance
p
rop
- The PropertyBase instance
name - The name of this
TkVarProxy
name - The name of this
PropertyValue
If you are only interested in the value, you can define your
If you are only interested in the value, you can define your
callback function like
'
def callback(value, *a): ...
'
callback function like
'
def callback(value, *a): ...
'
...
@@ -205,56 +196,45 @@ class TkVarProxy(object):
...
@@ -205,56 +196,45 @@ class TkVarProxy(object):
self
.
changeListeners
[
instance
].
pop
(
name
,
None
)
self
.
changeListeners
[
instance
].
pop
(
name
,
None
)
def
get
(
self
):
"""
Returns the current property value.
"""
return
self
.
_value
def
_getVarValue
(
self
):
def
set
(
self
,
newValue
):
"""
"""
R
et
urn
s the
current value of the Tkinter control
S
ets the
property value. The property is validated, and any
variable being managed by this TkVarProxy object
.
registered listeners are notified
.
"""
"""
# This is silly. Tkinter allows Boolean/Int/Double
self
.
_value
=
newValue
# variables to be set to invalid values (e.g. it
# allows DoubleVars to be set to strings containing
# non numeric characters). But then, later calls to
# get() will fail, as they will attempt to convert
# the invalid value to a boolean/int/double. So here
# we attempt to get the current value in the normal
# way ...
try
:
value
=
self
.
tkVar
.
get
()
# and if that fails, we manually look up the value
log
.
debug
(
'
Variable {} changed: {}
'
.
format
(
self
.
name
,
newValue
))
# via the current tk context, thus avoiding the
# failing type cast. Ugly.
except
:
value
=
self
.
tkVar
.
_tk
.
globalgetvar
(
self
.
name
)
# More silliness related to above silliness. All
# Validate the new value and notify any registered listeners
# variables in Tk, be they IntVars, BooleanVars, or
self
.
validateAndNotify
()
# whatever, are stored as strings, and cannot have no
# value. If you try to set a Tk variable to None, it
# Notify the property owner that this property has changed
# will be converted to a string, and stored as 'None',
self
.
owner
.
_propChanged
(
self
.
prop
)
# which is quite different. So I'm following the
# convention that an empty string, for any of the
# variable types, is equivalent to None.
if
value
==
''
:
value
=
None
return
value
def
validateAndNotify
(
self
):
def
validateAndNotify
(
self
):
"""
"""
Passes the current variable value to the validate()
Passes the current value to the validate() method
method of the PropertyBase object which owns this
of the PropertyBase object which owns this PropertyValue.
TkVarProxy. If the value, or the validity of that
If the value, or the validity of that value, has changed
value, has changed since the last validation, any
since the last validation, any listeners which have been
listeners which have been registered with this
registered with this PropertyValue object are notified.
TkVarProxy object are notified..
"""
"""
value
=
self
.
_
get
VarValue
()
value
=
self
.
get
()
valid
=
True
valid
=
True
listeners
=
self
.
changeListeners
.
items
()
listeners
=
self
.
changeListeners
.
items
()
try
:
self
.
tkP
rop
.
validate
(
self
.
owner
,
value
)
try
:
self
.
p
rop
.
validate
(
self
.
owner
,
value
)
except
ValueError
:
valid
=
False
except
ValueError
:
valid
=
False
# Listeners are only notified if the value or its
# Listeners are only notified if the value or its
...
@@ -272,74 +252,46 @@ class TkVarProxy(object):
...
@@ -272,74 +252,46 @@ class TkVarProxy(object):
log
.
debug
(
'
Notifying listener on {}: {}
'
.
format
(
self
.
name
,
name
))
log
.
debug
(
'
Notifying listener on {}: {}
'
.
format
(
self
.
name
,
name
))
try
:
func
(
value
,
valid
,
self
.
owner
,
self
.
tkP
rop
,
self
.
name
)
try
:
func
(
value
,
valid
,
self
.
owner
,
self
.
p
rop
,
self
.
name
)
except
Exception
as
e
:
except
Exception
as
e
:
log
.
debug
(
'
Listener on {} ({}) raised exception: {}
'
.
format
(
log
.
debug
(
'
Listener on {} ({}) raised exception: {}
'
.
format
(
self
.
name
,
name
,
e
))
self
.
name
,
name
,
e
))
def
_traceCb
(
self
,
*
args
):
"""
Called whenever the Tkinter control variable value is changed.
Notifies any registered listeners, and the HasProperties
property owner, of the change.
"""
newValue
=
self
.
_getVarValue
()
log
.
debug
(
'
Variable {} changed: {}
'
.
format
(
self
.
name
,
newValue
))
# Validate the new value and notify any registered listeners
self
.
validateAndNotify
()
# Notify the property owner that this property has changed
self
.
owner
.
_propChanged
(
self
.
tkProp
)
def
__del__
(
self
):
"""
Remove the trace on the Tkinter variable.
"""
self
.
tkVar
.
trace_vdelete
(
'
w
'
,
self
.
traceName
)
class
PropertyBase
(
object
):
class
PropertyBase
(
object
):
"""
"""
The base class for properties. Subclasses should:
The base class for properties. For every object which has this
PropertyBase object as a property, one or more PropertyValue
instances are created and attached as an attribute of the parent.
Subclasses should:
- Ensure that PropertyBase.__init__ is called.
- Ensure that PropertyBase.__init__ is called.
- Override the validate method to implement any built in
- Override the validate method to implement any built in
validation rules, ensuring that the PropertyBase.validate
validation rules, ensuring that the PropertyBase.validate
method is called.
method is called
first
.
- Override __get__ and __set__ for any required implicit
- Override __get__ and __set__ for any required implicit
casting/data transformation rules (see
casting/data transformation rules (see
properties_types.String for an example).
properties_types.String for an example).
- Override _make
Tk
Va
r
if creation of the
TkVarProxy needs
- Override _make
Prop
Va
l
if creation of the
PropertyValue
to be controlled (see properties_types.Choice for
an
needs
to be controlled (see properties_types.Choice for
example).
an
example).
- Override get
Tk
Va
r
for properties which consist of
- Override get
Prop
Va
l
for properties which consist of
more than one
TkVarProxy
object
more than one
PropertyValue
object
(see properties_types.List for an example).
(see properties_types.List for an example).
- Override whatever you want for advanced usage (see
- Override whatever you want for advanced usage (see
properties_types.List for an example).
properties_types.List for an example).
"""
"""
def
__init__
(
self
,
tkVarType
,
default
,
required
=
False
,
validateFunc
=
None
):
def
__init__
(
self
,
default
,
required
=
False
,
validateFunc
=
None
):
"""
"""
The tkvartype parameter should be one of the Tkinter.*Var
Parameters:
classes. For every object (the parent) which has this
PropertyBase object as a property, one or more TkVarProxy
instances are created and attached as an attribute of the
parent. Parameters:
- tkVarType: Tkinter control variable class. May be
None for properties which manage multiple
TkVarProxy objects.
- default: Default/initial value.
- default: Default/initial value.
...
@@ -357,7 +309,6 @@ class PropertyBase(object):
...
@@ -357,7 +309,6 @@ class PropertyBase(object):
new value is invalid.
new value is invalid.
"""
"""
self
.
label
=
None
self
.
label
=
None
self
.
tkVarType
=
tkVarType
self
.
default
=
default
self
.
default
=
default
self
.
required
=
required
self
.
required
=
required
self
.
validateFunc
=
validateFunc
self
.
validateFunc
=
validateFunc
...
@@ -367,7 +318,7 @@ class PropertyBase(object):
...
@@ -367,7 +318,7 @@ class PropertyBase(object):
def
addListener
(
self
,
instance
,
name
,
callback
):
def
addListener
(
self
,
instance
,
name
,
callback
):
"""
"""
Register a listener with this property. When the property value
Register a listener with this property. When the property value
changes, the listener will be notified. See
TkVarProxy
.addListener
changes, the listener will be notified. See
PropertyValue
.addListener
for required callback function signature.
for required callback function signature.
"""
"""
...
@@ -398,20 +349,20 @@ class PropertyBase(object):
...
@@ -398,20 +349,20 @@ class PropertyBase(object):
This will result in any registered listeners being notified.
This will result in any registered listeners being notified.
"""
"""
varProxie
s
=
instance
.
get
Tk
Va
r
(
self
.
label
)
propVal
s
=
instance
.
get
Prop
Va
l
(
self
.
label
)
# get
Tk
Va
r
returns either a
TkVarProxy
object,
or a
# get
Prop
Va
l
returns either a
PropertyValue
object,
# list of
TkVarProxy objects
(it should do, anyway).
#
or a
list of
them
(it should do, anyway).
if
isinstance
(
varProxies
,
TkVarProxy
):
if
isinstance
(
propVals
,
PropertyValue
):
varProxies
=
[
varProxie
s
]
propVals
=
[
propVal
s
]
for
va
r
in
varProxie
s
:
for
va
l
in
propVal
s
:
va
r
.
validateAndNotify
()
va
l
.
validateAndNotify
()
def
_varChanged
(
self
,
value
,
valid
,
instance
,
tkP
rop
,
name
):
def
_varChanged
(
self
,
value
,
valid
,
instance
,
p
rop
,
name
):
"""
"""
This function is registered with the
TkVarProxy
object (or
This function is registered with the
PropertyValue
object (or
objects) which are managed by this PropertyBase instance.
objects) which are managed by this PropertyBase instance.
It notifies any listeners which have been registered to
It notifies any listeners which have been registered to
this property, (and to the associated HasProperties instance).
this property, (and to the associated HasProperties instance).
...
@@ -425,18 +376,17 @@ class PropertyBase(object):
...
@@ -425,18 +376,17 @@ class PropertyBase(object):
log
.
debug
(
'
Notifying listener on {}: {}
'
.
format
(
self
.
label
,
lName
))
log
.
debug
(
'
Notifying listener on {}: {}
'
.
format
(
self
.
label
,
lName
))
func
(
value
,
valid
,
instance
,
tkP
rop
,
name
)
func
(
value
,
valid
,
instance
,
p
rop
,
name
)
def
_make
Tk
Va
r
(
self
,
instance
):
def
_make
Prop
Va
l
(
self
,
instance
):
"""
"""
Creates a
TkVarProxy
object, and attaches it to the given
Creates a
PropertyValue
object, and attaches it to the given
instance. Also registers this PropertyBase instance as a
instance. Also registers this PropertyBase instance as a
listener on the
TkVarProxy
object.
listener on the
PropertyValue
object.
"""
"""
instval
=
TkVarProxy
(
instval
=
PropertyValue
(
self
,
instance
,
self
.
default
,
self
.
label
)
self
,
instance
,
self
.
tkVarType
,
self
.
default
,
self
.
label
)
instance
.
__dict__
[
self
.
label
]
=
instval
instance
.
__dict__
[
self
.
label
]
=
instval
listenerName
=
'
PropertyBase_{}_{}
'
.
format
(
self
.
label
,
id
(
instval
))
listenerName
=
'
PropertyBase_{}_{}
'
.
format
(
self
.
label
,
id
(
instval
))
...
@@ -446,11 +396,11 @@ class PropertyBase(object):
...
@@ -446,11 +396,11 @@ class PropertyBase(object):
return
instval
return
instval
def
get
Tk
Va
r
(
self
,
instance
):
def
get
Prop
Va
l
(
self
,
instance
):
"""
"""
Return the
TkVarProxy
object (or objects) for this property,
Return the
PropertyValue
object (or objects) for this property,
associated with the given HasProperties instance. Properties
associated with the given HasProperties instance. Properties
which contain multiple
TkVarProxy
objects should override
which contain multiple
PropertyValue
objects should override
this method to return a list of said objects.
this method to return a list of said objects.
"""
"""
return
instance
.
__dict__
[
self
.
label
]
return
instance
.
__dict__
[
self
.
label
]
...
@@ -489,38 +439,29 @@ class PropertyBase(object):
...
@@ -489,38 +439,29 @@ class PropertyBase(object):
"""
"""
If called on the HasProperties class, and not on an instance,
If called on the HasProperties class, and not on an instance,
returns this PropertyBase object. Otherwise, returns the value
returns this PropertyBase object. Otherwise, returns the value
contained in the
TkVarProxy
variable which is attached to the
contained in the
PropertyValue
variable which is attached to the
instance.
instance.
"""
"""
if
instance
is
None
:
if
instance
is
None
:
return
self
return
self
instval
=
instance
.
__dict__
.
get
(
self
.
label
,
None
)
instval
=
self
.
getPropVal
(
instance
)
if
instval
is
None
:
instval
=
self
.
_makeTkVar
(
instance
)
if
instval
is
None
:
instval
=
self
.
_makePropVal
(
instance
)
# See comments in TkVarProxy._getVarValue
# for a brief overview of this silliness.
try
:
val
=
instval
.
tkVar
.
get
()
except
:
val
=
instval
.
tkVar
.
_tk
.
globalgetvar
(
instval
.
tkVar
.
_name
)
if
val
==
''
:
val
=
None
return
val
return
instval
.
get
()
def
__set__
(
self
,
instance
,
value
):
def
__set__
(
self
,
instance
,
value
):
"""
"""
Set the
Tkinter variable,
attached to the given
instance, to
Set the
value of this property, as
attached to the given
the given value.
instance, to
the given value.
"""
"""
# See comments in TkVarProxy._getVarValue
# for a brief overview of this silliness.
if
value
is
None
:
value
=
''
instval
=
self
.
get
Tk
Va
r
(
instance
)
instval
=
self
.
get
Prop
Va
l
(
instance
)
instval
.
tkVar
.
set
(
value
)
instval
.
set
(
value
)
class
PropertyOwner
(
type
):
class
PropertyOwner
(
type
):
...
@@ -562,18 +503,18 @@ class HasProperties(object):
...
@@ -562,18 +503,18 @@ class HasProperties(object):
return
inst
return
inst
def
get
Tk
Prop
(
self
,
propName
):
def
getProp
(
self
,
propName
):
"""
"""
Return the
tkprop
PropertyBase object for the given property.
Return the PropertyBase object for the given property.
"""
"""
return
getattr
(
self
.
__class__
,
propName
)
return
getattr
(
self
.
__class__
,
propName
)
def
get
Tk
Va
r
(
self
,
propName
):
def
get
Prop
Va
l
(
self
,
propName
):
"""
"""
Return the
TkVarProxy
object(s) for the given property.
Return the
PropertyValue
object(s) for the given property.
"""
"""
return
self
.
get
Tk
Prop
(
propName
).
get
Tk
Va
r
(
self
)
return
self
.
getProp
(
propName
).
get
Prop
Va
l
(
self
)
def
getAllProperties
(
self
):
def
getAllProperties
(
self
):
...
@@ -618,7 +559,7 @@ class HasProperties(object):
...
@@ -618,7 +559,7 @@ class HasProperties(object):
return
errors
return
errors
def
_propChanged
(
self
,
cProp
):
def
_propChanged
(
self
,
c
hanged
Prop
):
"""
"""
Called whenever any property value changes. Forces validation
Called whenever any property value changes. Forces validation
for all other properties, and notification of their registered
for all other properties, and notification of their registered
...
@@ -632,7 +573,7 @@ class HasProperties(object):
...
@@ -632,7 +573,7 @@ class HasProperties(object):
propNames
,
props
=
self
.
getAllProperties
()
propNames
,
props
=
self
.
getAllProperties
()
for
prop
in
props
:
for
prop
in
props
:
if
prop
==
cProp
:
continue
if
prop
==
c
hanged
Prop
:
continue
prop
.
forceValidation
(
self
)
prop
.
forceValidation
(
self
)
...
...
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment