ctioga 1.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/COPYING +340 -0
- data/ctioga/bin/ctable +28 -0
- data/ctioga/bin/ctioga +37 -0
- data/ctioga/doc/ctable.1 +156 -0
- data/ctioga/doc/ctioga.1 +2363 -0
- data/ctioga/examples/README +46 -0
- data/ctioga/examples/ctioga.gnuplot +4 -0
- data/ctioga/examples/ctioga_within_tioga.rb +53 -0
- data/ctioga/examples/ctiogarc.rb +24 -0
- data/ctioga/examples/include_1.rb +15 -0
- data/ctioga/examples/noise.dat +100 -0
- data/ctioga/examples/noise.rb +13 -0
- data/ctioga/examples/trig.csv +100 -0
- data/ctioga/examples/trig.dat +100 -0
- data/ctioga/examples/trig.rb +14 -0
- data/ctioga/examples/trigh.dat +100 -0
- data/ctioga/examples/trigh.rb +10 -0
- data/ctioga/examples/tutorial +763 -0
- data/ctioga/examples/tutorial.sh +269 -0
- data/ctioga/tests/README +14 -0
- data/ctioga/tests/axes.sh +40 -0
- data/ctioga/tests/basic.sh +11 -0
- data/ctioga/tests/draw.sh +24 -0
- data/ctioga/tests/histograms.sh +14 -0
- data/ctioga/tests/insets.sh +41 -0
- data/ctioga/tests/layouts.sh +29 -0
- data/ctioga/tests/legends.sh +113 -0
- data/ctioga/tests/styles.sh +43 -0
- data/ctioga/tests/test_style.sh +8 -0
- data/ctioga/tests/tests.sh +24 -0
- data/ctioga/tests/text_backend.sh +83 -0
- data/ctioga/tests/tioga_defaults.rb +18 -0
- data/lib/CTioga/axes.rb +904 -0
- data/lib/CTioga/backends.rb +88 -0
- data/lib/CTioga/boundaries.rb +224 -0
- data/lib/CTioga/ctable.rb +134 -0
- data/lib/CTioga/curve_style.rb +246 -0
- data/lib/CTioga/debug.rb +199 -0
- data/lib/CTioga/dimension.rb +133 -0
- data/lib/CTioga/elements.rb +17 -0
- data/lib/CTioga/elements/base.rb +84 -0
- data/lib/CTioga/elements/containers.rb +578 -0
- data/lib/CTioga/elements/curves.rb +368 -0
- data/lib/CTioga/elements/tioga_primitives.rb +440 -0
- data/lib/CTioga/layout.rb +595 -0
- data/lib/CTioga/legends.rb +29 -0
- data/lib/CTioga/legends/cmdline.rb +187 -0
- data/lib/CTioga/legends/item.rb +164 -0
- data/lib/CTioga/legends/style.rb +257 -0
- data/lib/CTioga/log.rb +73 -0
- data/lib/CTioga/movingarrays.rb +131 -0
- data/lib/CTioga/partition.rb +271 -0
- data/lib/CTioga/plot_style.rb +230 -0
- data/lib/CTioga/plotmaker.rb +1677 -0
- data/lib/CTioga/shortcuts.rb +69 -0
- data/lib/CTioga/structures.rb +82 -0
- data/lib/CTioga/styles.rb +140 -0
- data/lib/CTioga/themes.rb +581 -0
- data/lib/CTioga/themes/classical.rb +82 -0
- data/lib/CTioga/themes/demo.rb +63 -0
- data/lib/CTioga/themes/fits.rb +91 -0
- data/lib/CTioga/themes/mono.rb +33 -0
- data/lib/CTioga/tioga.rb +32 -0
- data/lib/CTioga/utils.rb +173 -0
- data/lib/MetaBuilder/Parameters/dates.rb +38 -0
- data/lib/MetaBuilder/Parameters/lists.rb +132 -0
- data/lib/MetaBuilder/Parameters/numbers.rb +69 -0
- data/lib/MetaBuilder/Parameters/strings.rb +86 -0
- data/lib/MetaBuilder/Parameters/styles.rb +75 -0
- data/lib/MetaBuilder/Qt4/Parameters/dates.rb +51 -0
- data/lib/MetaBuilder/Qt4/Parameters/numbers.rb +65 -0
- data/lib/MetaBuilder/Qt4/Parameters/strings.rb +106 -0
- data/lib/MetaBuilder/Qt4/parameter.rb +172 -0
- data/lib/MetaBuilder/Qt4/parameters.rb +9 -0
- data/lib/MetaBuilder/descriptions.rb +603 -0
- data/lib/MetaBuilder/factory.rb +101 -0
- data/lib/MetaBuilder/group.rb +57 -0
- data/lib/MetaBuilder/metabuilder.rb +10 -0
- data/lib/MetaBuilder/parameter.rb +374 -0
- data/lib/MetaBuilder/parameters.rb +11 -0
- data/lib/MetaBuilder/qt4.rb +8 -0
- data/lib/SciYAG/Backends/backend.rb +379 -0
- data/lib/SciYAG/Backends/binner.rb +168 -0
- data/lib/SciYAG/Backends/cache.rb +102 -0
- data/lib/SciYAG/Backends/dataset.rb +158 -0
- data/lib/SciYAG/Backends/descriptions.rb +469 -0
- data/lib/SciYAG/Backends/filters.rb +25 -0
- data/lib/SciYAG/Backends/filters/average.rb +134 -0
- data/lib/SciYAG/Backends/filters/cumulate.rb +37 -0
- data/lib/SciYAG/Backends/filters/filter.rb +70 -0
- data/lib/SciYAG/Backends/filters/norm.rb +39 -0
- data/lib/SciYAG/Backends/filters/smooth.rb +63 -0
- data/lib/SciYAG/Backends/filters/sort.rb +43 -0
- data/lib/SciYAG/Backends/filters/strip.rb +34 -0
- data/lib/SciYAG/Backends/filters/trim.rb +64 -0
- data/lib/SciYAG/Backends/gnuplot.rb +131 -0
- data/lib/SciYAG/Backends/math.rb +108 -0
- data/lib/SciYAG/Backends/mdb.rb +462 -0
- data/lib/SciYAG/Backends/multitext.rb +96 -0
- data/lib/SciYAG/Backends/source.rb +64 -0
- data/lib/SciYAG/Backends/text.rb +339 -0
- data/lib/SciYAG/backends.rb +16 -0
- metadata +191 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# cache.rb : A cache for various objects
|
|
2
|
+
# Copyright (C) 2007 Vincent Fourmond
|
|
3
|
+
|
|
4
|
+
# This program is free software; you can redistribute it and/or modify
|
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
|
6
|
+
# the Free Software Foundation; either version 2 of the License, or
|
|
7
|
+
# (at your option) any later version.
|
|
8
|
+
|
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
# GNU General Public License for more details.
|
|
13
|
+
|
|
14
|
+
# You should have received a copy of the GNU General Public License
|
|
15
|
+
# along with this program; if not, write to the Free Software
|
|
16
|
+
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
17
|
+
|
|
18
|
+
module SciYAG
|
|
19
|
+
|
|
20
|
+
module Backends
|
|
21
|
+
|
|
22
|
+
# The Cache class aims at providing a small and easy-to-use cache
|
|
23
|
+
# with validation checking, usage statistics and flushing (automatically
|
|
24
|
+
# removes some entries when it grows too big).
|
|
25
|
+
class Cache
|
|
26
|
+
|
|
27
|
+
# An element of the cache
|
|
28
|
+
class CacheEntry
|
|
29
|
+
# The name under which it is cached
|
|
30
|
+
attr_accessor :name
|
|
31
|
+
|
|
32
|
+
# The data cached
|
|
33
|
+
attr_accessor :data
|
|
34
|
+
|
|
35
|
+
# The meta-data that should be matched against when checking
|
|
36
|
+
# the cache's relevance
|
|
37
|
+
attr_accessor :meta_data
|
|
38
|
+
|
|
39
|
+
def initialize(name, data, meta_data)
|
|
40
|
+
@name = name
|
|
41
|
+
@data = data
|
|
42
|
+
# Maybe we should even .dup the contents ?
|
|
43
|
+
@meta_data = meta_data.dup # (just to make sure)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Checks that the meta_data corresponds to this cache entry
|
|
47
|
+
def relevant?(meta_data)
|
|
48
|
+
return @meta_data == meta_data
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Records all accessed items and the number of cache misses/succes
|
|
53
|
+
# and so on.
|
|
54
|
+
attr_accessor :statistics
|
|
55
|
+
|
|
56
|
+
# Creates a Cache
|
|
57
|
+
def initialize
|
|
58
|
+
# The cache itself
|
|
59
|
+
@cache = {}
|
|
60
|
+
|
|
61
|
+
@statistics = {}
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Look inside the cache for a cached element. If it is found and
|
|
65
|
+
# up-to-date, it is returned. If not, #get_cache runs _code_,
|
|
66
|
+
# stores its return value as a new cache for (_name_, _meta_data_)
|
|
67
|
+
# and returns it.
|
|
68
|
+
def get_cache(name, meta_data, &code)
|
|
69
|
+
if (@cache.key?(name)) and
|
|
70
|
+
((cached = @cache[name]).relevant?(meta_data))
|
|
71
|
+
stats(name)[:accesses] += 1
|
|
72
|
+
return cached.data
|
|
73
|
+
elsif @cache.key?(name)
|
|
74
|
+
stats(name)[:updates] += 1
|
|
75
|
+
end
|
|
76
|
+
stats(name)[:misses] += 1
|
|
77
|
+
|
|
78
|
+
# Now, we run the code to update the cache entry:
|
|
79
|
+
data = code.call
|
|
80
|
+
@cache[name] = CacheEntry.new(name, data, meta_data)
|
|
81
|
+
return data
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Returns the statistics for the given item, or create them
|
|
85
|
+
# on the fly if necessary:
|
|
86
|
+
def stats(name)
|
|
87
|
+
if @statistics[name]
|
|
88
|
+
return @statistics[name]
|
|
89
|
+
else
|
|
90
|
+
@statistics[name] = {
|
|
91
|
+
:accesses => 0,
|
|
92
|
+
:misses => 0,
|
|
93
|
+
:updates => 0
|
|
94
|
+
}
|
|
95
|
+
return @statistics[name]
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
end
|
|
102
|
+
end
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# dataset.rb : The implementation of data sets
|
|
2
|
+
# Copyright (C) 2006 Vincent Fourmond
|
|
3
|
+
|
|
4
|
+
# This program is free software; you can redistribute it and/or modify
|
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
|
6
|
+
# the Free Software Foundation; either version 2 of the License, or
|
|
7
|
+
# (at your option) any later version.
|
|
8
|
+
|
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
# GNU General Public License for more details.
|
|
13
|
+
|
|
14
|
+
# You should have received a copy of the GNU General Public License
|
|
15
|
+
# along with this program; if not, write to the Free Software
|
|
16
|
+
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
17
|
+
|
|
18
|
+
require 'MetaBuilder/metabuilder'
|
|
19
|
+
|
|
20
|
+
require 'forwardable'
|
|
21
|
+
|
|
22
|
+
module SciYAG
|
|
23
|
+
|
|
24
|
+
module Backends
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
# An abstract class representing a DataSet. You should consider using
|
|
28
|
+
# a subclass, DataSet2D or DataSet3D. A DataSet must be either 2D or 3D,
|
|
29
|
+
# and redefine #is_2D? and #is_3D? accordingly.
|
|
30
|
+
#
|
|
31
|
+
# As DataSets grow more complex, it is likely that it will be less and
|
|
32
|
+
# less easy to tweak directly the data inside. So, please, do use the
|
|
33
|
+
# accessors and tweakers provided in the interface, else you'll expose
|
|
34
|
+
# yourself to some intensive breakages when I get more clever.
|
|
35
|
+
# Meanwhile, the apply(meth,where,*rest) method should be used to keep
|
|
36
|
+
# everything in sync when you apply a method to a Dvector.
|
|
37
|
+
class DataSet
|
|
38
|
+
|
|
39
|
+
extend Forwardable
|
|
40
|
+
|
|
41
|
+
# The #creation_context attribute is a hash containing a :backend key
|
|
42
|
+
# indicating the name of the Backend created and filled with the
|
|
43
|
+
# contents of the backend's DescriptionInclude#save_state function.
|
|
44
|
+
attr_accessor :creation_context
|
|
45
|
+
|
|
46
|
+
# The data of the set.
|
|
47
|
+
attr_accessor :data
|
|
48
|
+
|
|
49
|
+
# Errors on data, if applicable.
|
|
50
|
+
# For 2D data, this will just be a hash with the following Dvectors:
|
|
51
|
+
# :xmin, :xmax for errors on the x values + :x, to make sure
|
|
52
|
+
# we keep it up-to-date.
|
|
53
|
+
# :ymin, :ymax for errors on y + :y.
|
|
54
|
+
#
|
|
55
|
+
# Both ?min and ?max have to be specified if any output should be
|
|
56
|
+
# created.
|
|
57
|
+
attr_accessor :errors
|
|
58
|
+
|
|
59
|
+
# The metadata given when the set was created.
|
|
60
|
+
attr_accessor :meta_data
|
|
61
|
+
|
|
62
|
+
def is_2D?
|
|
63
|
+
return false
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def is_3D?
|
|
67
|
+
return false
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# A few forwarded stuff to make the DataSet behave exactly like
|
|
71
|
+
# Function and it's future 3D counterpart
|
|
72
|
+
|
|
73
|
+
def_delegators :@data, :x, :y, :z
|
|
74
|
+
|
|
75
|
+
def initialize(context, data, errors = {}, meta_data = {})
|
|
76
|
+
if context.respond_to? :save_state
|
|
77
|
+
@creation_context = context.save_state
|
|
78
|
+
@creation_context[:backend] = context.description.name
|
|
79
|
+
else
|
|
80
|
+
@creation_context = context.dup
|
|
81
|
+
end
|
|
82
|
+
@data = data
|
|
83
|
+
@errors = errors
|
|
84
|
+
@meta_data = meta_data
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Apply a Dvector operation to the given dimension, including
|
|
88
|
+
# everything that could be left somewhere in the error bars.
|
|
89
|
+
# It should be very powerful in the end.
|
|
90
|
+
#
|
|
91
|
+
# Beautiful, isn't it ??
|
|
92
|
+
def apply(what, where, *rest)
|
|
93
|
+
self.send(where).send(what, *rest)
|
|
94
|
+
for key,values in @errors
|
|
95
|
+
if key.to_s =~ /^#{where}/ # Very much overkill, but, well...
|
|
96
|
+
values.send(what,*rest)
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Does a redirection to the underlying @data if that makes sense.
|
|
102
|
+
def method_missing(sym, *args, &b)
|
|
103
|
+
if @data.respond_to?(sym)
|
|
104
|
+
@data.send(sym, *args, &b)
|
|
105
|
+
elsif x.respond_to?(sym) # We are trying to apply something
|
|
106
|
+
# like data.mul!(:x, factor)
|
|
107
|
+
apply(sym,args.shift,*args,&b)
|
|
108
|
+
else
|
|
109
|
+
super
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Returns all the Dvectors held by this dataset. Can be used
|
|
114
|
+
# to Dvector#replace them
|
|
115
|
+
def all_vectors
|
|
116
|
+
list = [@data.x, @data.y, *@errors.values]
|
|
117
|
+
list << @data.z if @data.respond_to?(:z)
|
|
118
|
+
return list
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Sorts the data according to the X value. This also includes
|
|
122
|
+
# the error bars.
|
|
123
|
+
def sort!
|
|
124
|
+
new_error = {}
|
|
125
|
+
idx_vector = Dobjects::Dvector.new(@data.x.size) do |i|
|
|
126
|
+
i
|
|
127
|
+
end
|
|
128
|
+
f = Dobjects::Function.new(@data.x.dup, idx_vector)
|
|
129
|
+
f.sort
|
|
130
|
+
for vector in all_vectors
|
|
131
|
+
new_vector = Dobjects::Dvector.new(vector.size) do |i|
|
|
132
|
+
vector[f.y[i]]
|
|
133
|
+
end
|
|
134
|
+
vector.replace(new_vector)
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# A 2-dimensionnal DataSet
|
|
141
|
+
class DataSet2D < DataSet
|
|
142
|
+
|
|
143
|
+
def is_2D?
|
|
144
|
+
return true
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# A 3-dimensionnal DataSet
|
|
150
|
+
class DataSet3D < DataSet
|
|
151
|
+
def is_3D?
|
|
152
|
+
return true
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
end
|
|
158
|
+
end
|
|
@@ -0,0 +1,469 @@
|
|
|
1
|
+
# description.rb : How do classes describe themselves
|
|
2
|
+
# Copyright (C) 2006 Vincent Fourmond
|
|
3
|
+
|
|
4
|
+
# This program is free software; you can redistribute it and/or modify
|
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
|
6
|
+
# the Free Software Foundation; either version 2 of the License, or
|
|
7
|
+
# (at your option) any later version.
|
|
8
|
+
|
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
# GNU General Public License for more details.
|
|
13
|
+
|
|
14
|
+
# You should have received a copy of the GNU General Public License
|
|
15
|
+
# along with this program; if not, write to the Free Software
|
|
16
|
+
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
17
|
+
|
|
18
|
+
module SciYAG
|
|
19
|
+
|
|
20
|
+
module Backends
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
module Descriptions
|
|
24
|
+
|
|
25
|
+
# A module to convert text input into an object of the given type.
|
|
26
|
+
module Conversion
|
|
27
|
+
|
|
28
|
+
# The regular expression that matches 'true' for boolean values:
|
|
29
|
+
TRUE_RE = /^\s*(true|yes)\s*$/i
|
|
30
|
+
|
|
31
|
+
# The regular expression that matches 'false'
|
|
32
|
+
FALSE_RE = /^\s*(false|no(ne)?)\s*$/i
|
|
33
|
+
|
|
34
|
+
# Converts +text+ to the given type. It support different schemes:
|
|
35
|
+
# * if +type+ is String, Float, Array of Integer, the values are
|
|
36
|
+
# converted using the appropriate functions;
|
|
37
|
+
# * if +type+ is a string, the type is considered to be String;
|
|
38
|
+
# this leaves a possibility to implement a more precise
|
|
39
|
+
# mechanism for choosing; see Descriptions::Parameter#type
|
|
40
|
+
# * if +type+ is :bool, the text is interpreted as a true/false
|
|
41
|
+
# expression.
|
|
42
|
+
# * if +type+ is an array (of symbols/strings), then a valid input
|
|
43
|
+
# is one of the elements of the arrays (it is converted to symbols
|
|
44
|
+
# if necessary)
|
|
45
|
+
# * if +type+ is a hash, it behaves as if the keys had been
|
|
46
|
+
# specified as an array. The values can be used to provide
|
|
47
|
+
# a proper name;
|
|
48
|
+
# * for any other input, +type+ is assumed to be a class, and its
|
|
49
|
+
# constructor should support build from 1 argument, a String.
|
|
50
|
+
|
|
51
|
+
def text_to_type(text, type)
|
|
52
|
+
# warning: case cannot be used, as it is the comparison
|
|
53
|
+
# === which is used, that is is_a?.
|
|
54
|
+
if type == String
|
|
55
|
+
value = String(text)
|
|
56
|
+
elsif type.is_a?(String) # some special specification;
|
|
57
|
+
# always a string
|
|
58
|
+
value = String(text)
|
|
59
|
+
elsif type == Float
|
|
60
|
+
value = Float(text)
|
|
61
|
+
elsif type == Array
|
|
62
|
+
value = Array(text)
|
|
63
|
+
elsif type == Integer
|
|
64
|
+
value = Integer(text)
|
|
65
|
+
elsif type.is_a?(Array)
|
|
66
|
+
h = {}
|
|
67
|
+
type.each do |a|
|
|
68
|
+
h[a.to_s] = a
|
|
69
|
+
end
|
|
70
|
+
return h[text] # No checking done...
|
|
71
|
+
elsif type.is_a?(Hash)
|
|
72
|
+
return text_to_type(text, type.keys)
|
|
73
|
+
elsif type == :bool
|
|
74
|
+
if text =~ TRUE_RE
|
|
75
|
+
return true
|
|
76
|
+
else
|
|
77
|
+
return false
|
|
78
|
+
end
|
|
79
|
+
else
|
|
80
|
+
value = type.new(text)
|
|
81
|
+
end
|
|
82
|
+
return value
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
# A class that describes one parameter that will be fixed at
|
|
89
|
+
# run-time by the user.
|
|
90
|
+
class Parameter
|
|
91
|
+
include Conversion
|
|
92
|
+
# The short name of the parameter
|
|
93
|
+
attr_accessor :name
|
|
94
|
+
|
|
95
|
+
# The long name of the parameter, to be translated
|
|
96
|
+
attr_accessor :long_name
|
|
97
|
+
|
|
98
|
+
# The function names that should be used to set the symbol and
|
|
99
|
+
# retrieve it's current value. The corresponding functions should
|
|
100
|
+
# read or return a string, and writer(reader) should be a noop.
|
|
101
|
+
attr_accessor :reader_symbol, :writer_symbol
|
|
102
|
+
|
|
103
|
+
# The description
|
|
104
|
+
attr_accessor :description
|
|
105
|
+
|
|
106
|
+
# The type of the parameter. It can take several values;
|
|
107
|
+
# see Descriptions::Conversion.text_to_type. Moreover, several values
|
|
108
|
+
# for a string parameter can be interpreted by systems in charge
|
|
109
|
+
# of querying the parameter:
|
|
110
|
+
#
|
|
111
|
+
# * <tt>"File: ..."</tt> represents a file, and the rest is the
|
|
112
|
+
# filter, Qt style;
|
|
113
|
+
# * <tt>"Set"</tt> represents a data set, which can queried for by
|
|
114
|
+
# the sets_available function.
|
|
115
|
+
attr_accessor :type
|
|
116
|
+
|
|
117
|
+
def initialize(name, writer_symbol,
|
|
118
|
+
reader_symbol,
|
|
119
|
+
long_name,
|
|
120
|
+
type,
|
|
121
|
+
description)
|
|
122
|
+
@name = name
|
|
123
|
+
@writer_symbol = writer_symbol
|
|
124
|
+
@reader_symbol = reader_symbol
|
|
125
|
+
@description = description
|
|
126
|
+
@long_name = long_name
|
|
127
|
+
@type = type
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# Sets the value of the given parameter in the _target_. It tries
|
|
131
|
+
# to be clever somehow, using @type to know what should be
|
|
132
|
+
# expected. See the text_to_type function.
|
|
133
|
+
|
|
134
|
+
def set(target, value)
|
|
135
|
+
value = text_to_type(value, @type)
|
|
136
|
+
target.send(@writer_symbol, value)
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# Aquires the value from the backend, and returns it
|
|
140
|
+
def get(target)
|
|
141
|
+
target.send(@reader_symbol).to_s
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def value(v)
|
|
145
|
+
return text_to_type(v, @type)
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# The base class for all descriptions. A description describes
|
|
150
|
+
# a "plugin" class. It has the following attributes:
|
|
151
|
+
#
|
|
152
|
+
# * the basic name, code-like, which is used mainly for internal
|
|
153
|
+
# purposes;
|
|
154
|
+
# * the long name, which has to be translated
|
|
155
|
+
# * the description itself, some small text describing the nature
|
|
156
|
+
# of the plugin
|
|
157
|
+
# * a list of parameters the plugin can take, along with their
|
|
158
|
+
# description. These are Parameter .
|
|
159
|
+
#
|
|
160
|
+
class Description
|
|
161
|
+
# the class to instantiate.
|
|
162
|
+
attr_accessor :class
|
|
163
|
+
|
|
164
|
+
# The name of the backend (short, code-like)
|
|
165
|
+
attr_accessor :name
|
|
166
|
+
|
|
167
|
+
# (text) description !
|
|
168
|
+
attr_accessor :description
|
|
169
|
+
|
|
170
|
+
# Long name, the one for public display
|
|
171
|
+
attr_accessor :long_name
|
|
172
|
+
|
|
173
|
+
# The hash holding parameters. Useful mainly for subclasses
|
|
174
|
+
attr_reader :param_hash
|
|
175
|
+
|
|
176
|
+
# The parameter list
|
|
177
|
+
attr_reader :param_list
|
|
178
|
+
|
|
179
|
+
# The list of parameters that have to be fed into the initialize
|
|
180
|
+
# function.
|
|
181
|
+
attr_accessor :init_param_list
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
# Initializes a Description
|
|
185
|
+
def initialize(cls, name, long_name, description = "")
|
|
186
|
+
@class = cls
|
|
187
|
+
@name = name
|
|
188
|
+
@long_name = long_name
|
|
189
|
+
@description = description
|
|
190
|
+
@param_list = []
|
|
191
|
+
@param_hash = {}
|
|
192
|
+
@init_param_list = []
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
# Creates an instance of the class, forwards parameters to the
|
|
196
|
+
# initialize method.
|
|
197
|
+
def instantiate(*args)
|
|
198
|
+
return @class.new(*args)
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
# Prepares the argument list for instantiate based on
|
|
202
|
+
# init_param_list and the text arguments given:
|
|
203
|
+
def prepare_instantiate_arglist(*args)
|
|
204
|
+
target = []
|
|
205
|
+
for pars in @init_param_list
|
|
206
|
+
target << pars.value(args.shift)
|
|
207
|
+
end
|
|
208
|
+
return target
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def add_param(param)
|
|
212
|
+
@param_list << param
|
|
213
|
+
|
|
214
|
+
# Three different cross-linkings.
|
|
215
|
+
@param_hash[param.reader_symbol] = param
|
|
216
|
+
@param_hash[param.writer_symbol] = param
|
|
217
|
+
@param_hash[param.name] = param
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
def param_set(i, p)
|
|
221
|
+
param = p
|
|
222
|
+
instance = i
|
|
223
|
+
return proc { |x| param.set(instance,x) }
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
# Pushes the names of the params onto @init_list_param_list.
|
|
227
|
+
# Arguments have to be strings.
|
|
228
|
+
def init_params(*args)
|
|
229
|
+
@init_param_list += args
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
# Fills an OptionParser with all the parameters the
|
|
233
|
+
# Backend should be able to take.
|
|
234
|
+
|
|
235
|
+
def fill_parser(instance, parser, uniquify = true)
|
|
236
|
+
parser_banner(instance, parser)
|
|
237
|
+
parser_options(instance, parser, uniquify)
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
# Fills a parser with options (and only that)
|
|
241
|
+
def parser_options(instance, parser, uniquify = true)
|
|
242
|
+
raise "The instance is not of the right class" unless
|
|
243
|
+
instance.is_a? @class
|
|
244
|
+
for param in @param_list
|
|
245
|
+
if uniquify
|
|
246
|
+
param_name = "--#{@name}-#{param.name} #{param.name.upcase}"
|
|
247
|
+
else
|
|
248
|
+
param_name = "--#{param.name} #{param.name.upcase}"
|
|
249
|
+
end
|
|
250
|
+
parser.on(param_name, param.description,
|
|
251
|
+
param_set(instance, param))
|
|
252
|
+
end
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
# The parsers's banner.
|
|
256
|
+
def parser_banner(instance, parser)
|
|
257
|
+
# nothing by default
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
# Creates a parser entry in +parser+ that creates a new instance of the
|
|
261
|
+
# description's class and feed it to the +result+ method of
|
|
262
|
+
# the +receiver+. You can specify an optionnal +prefix+ for the
|
|
263
|
+
# option's name.
|
|
264
|
+
def parser_instantiate_option(parser, receiver, result, prefix = "")
|
|
265
|
+
op_name = "--#{prefix}#{@name}"
|
|
266
|
+
for arg in @init_param_list
|
|
267
|
+
op_name += " " + arg.name.upcase
|
|
268
|
+
end
|
|
269
|
+
# cool !
|
|
270
|
+
parser.on(op_name, @description) do |*a|
|
|
271
|
+
b = prepare_instantiate_arglist(*a)
|
|
272
|
+
instance = instantiate(*b)
|
|
273
|
+
receiver.send(result, instance)
|
|
274
|
+
end
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
# This module should be used with +include+ to provide the class with
|
|
280
|
+
# descriptions functionnalities. You also need to +extend+
|
|
281
|
+
# DescriptionExtend
|
|
282
|
+
module DescriptionInclude
|
|
283
|
+
# Returns the description associated with the backend object. Actually
|
|
284
|
+
# returns the one associated with the class.
|
|
285
|
+
def description
|
|
286
|
+
return self.class.description
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
# Fills an OptionParser with their parameters. Most probably, the
|
|
290
|
+
# default implementation should do for most cases. _uniquify_ asks
|
|
291
|
+
# if we should try to make the command-line options as unique as
|
|
292
|
+
# reasonable to do ?
|
|
293
|
+
def fill_parser(parser, uniquify = true)
|
|
294
|
+
description.fill_parser(self, parser, uniquify)
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
def parser_banner(parser)
|
|
298
|
+
description.parser_banner(self,parser)
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
def parser_options(parser, uniquify = true)
|
|
302
|
+
description.parser_options(self,parser, uniquify)
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
# Returns the long name of the backend:
|
|
306
|
+
def long_name
|
|
307
|
+
return description.long_name
|
|
308
|
+
end
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
# This module should be used with +extend+ to provide the class with
|
|
312
|
+
# descriptions functionnalities. You also need to +include+
|
|
313
|
+
# DescriptionInclude. Please not that all the *instance* methods
|
|
314
|
+
# defined here will become *class* methods there.
|
|
315
|
+
module DescriptionExtend
|
|
316
|
+
|
|
317
|
+
# Returns the description of the class.
|
|
318
|
+
def description
|
|
319
|
+
return @description
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
# Sets the description of the class. Classes should provide
|
|
323
|
+
# an easier interface for that.
|
|
324
|
+
def set_description(desc)
|
|
325
|
+
@description = desc
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
# Like param, but doesn't define an accessor.
|
|
329
|
+
def param_noaccess(writer, reader, name, long_name, type = String,
|
|
330
|
+
desc = "")
|
|
331
|
+
raise "Use describe first" if description.nil?
|
|
332
|
+
param = Descriptions::Parameter.new(name, writer, reader,
|
|
333
|
+
long_name,
|
|
334
|
+
type, desc)
|
|
335
|
+
description.add_param(param)
|
|
336
|
+
return param
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
# Tells if the current object is a base ancestor ?
|
|
340
|
+
def base_ancestor?
|
|
341
|
+
if superclass.respond_to?(:lookup_description_extend_ancestor)
|
|
342
|
+
return false
|
|
343
|
+
else
|
|
344
|
+
return true
|
|
345
|
+
end
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
# Retrieves the most ancient ancestor that has included
|
|
349
|
+
# DescriptionExtend.
|
|
350
|
+
def lookup_description_extend_ancestor
|
|
351
|
+
t = self
|
|
352
|
+
while not t.base_ancestor?
|
|
353
|
+
t = t.superclass
|
|
354
|
+
end
|
|
355
|
+
return t
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
# Valid only in the base class
|
|
359
|
+
def description_list_base
|
|
360
|
+
if base_ancestor?
|
|
361
|
+
return @description_list
|
|
362
|
+
else
|
|
363
|
+
raise "This function should only be called from the " +
|
|
364
|
+
"base ancestor"
|
|
365
|
+
end
|
|
366
|
+
end
|
|
367
|
+
|
|
368
|
+
# Valid only in the base class
|
|
369
|
+
def set_description_list_base()
|
|
370
|
+
@description_list = []
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
# Returns the list of descriptions associated with the base
|
|
375
|
+
# class
|
|
376
|
+
def description_list
|
|
377
|
+
return lookup_description_extend_ancestor.description_list_base
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
# Valid only in the base class
|
|
381
|
+
def description_hash_base
|
|
382
|
+
if base_ancestor?
|
|
383
|
+
return @description_hash
|
|
384
|
+
else
|
|
385
|
+
raise "This function should only be called from the " +
|
|
386
|
+
"base ancestor"
|
|
387
|
+
end
|
|
388
|
+
end
|
|
389
|
+
|
|
390
|
+
# Valid only in the base class
|
|
391
|
+
def set_description_hash_base()
|
|
392
|
+
@description_hash = {}
|
|
393
|
+
end
|
|
394
|
+
|
|
395
|
+
# Returns the hash of descriptions associated with the base
|
|
396
|
+
# class
|
|
397
|
+
def description_hash
|
|
398
|
+
return lookup_description_extend_ancestor.description_hash_base
|
|
399
|
+
end
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
# Registers a description in the base ancestor.
|
|
403
|
+
def register_description(desc)
|
|
404
|
+
description_list << desc
|
|
405
|
+
description_hash[desc.name] = desc
|
|
406
|
+
end
|
|
407
|
+
|
|
408
|
+
# Redefined so that we can add the description lists only in the
|
|
409
|
+
# base ancestor.
|
|
410
|
+
def DescriptionExtend.extend_object(t)
|
|
411
|
+
# First call super, to make sure the methods are here !
|
|
412
|
+
super
|
|
413
|
+
if t.base_ancestor?
|
|
414
|
+
t.set_description_list_base
|
|
415
|
+
t.set_description_hash_base
|
|
416
|
+
end
|
|
417
|
+
end
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
# This shortcut creates an accessor for the given symbol and registers
|
|
421
|
+
# it at the same time. Use if *after* describe. Something like
|
|
422
|
+
#
|
|
423
|
+
# param :size, "size", "Size", Integer , "The size of the backend"
|
|
424
|
+
#
|
|
425
|
+
# should be perfectly fine.
|
|
426
|
+
def param(symbol, name, long_name, type = String,
|
|
427
|
+
desc = "")
|
|
428
|
+
# We use the accessor = symbol.
|
|
429
|
+
param_noaccess((symbol.to_s + "=").to_sym,
|
|
430
|
+
symbol, name, long_name,
|
|
431
|
+
type, desc)
|
|
432
|
+
# Creates an accessor
|
|
433
|
+
attr_accessor symbol
|
|
434
|
+
end
|
|
435
|
+
|
|
436
|
+
# The parameters the class should inherit from its direct parents
|
|
437
|
+
# (which could have in turn inherited them...).
|
|
438
|
+
def inherit(*names)
|
|
439
|
+
if self.superclass.respond_to?(:description)
|
|
440
|
+
parents_params = self.superclass.description.param_hash
|
|
441
|
+
for n in names
|
|
442
|
+
if parents_params.key?(n)
|
|
443
|
+
description.add_param(parents_params[n])
|
|
444
|
+
else
|
|
445
|
+
warn "Param #{n} not found"
|
|
446
|
+
end
|
|
447
|
+
end
|
|
448
|
+
else
|
|
449
|
+
warn "The parent class has no description"
|
|
450
|
+
end
|
|
451
|
+
end
|
|
452
|
+
|
|
453
|
+
# Adds the params to the list of init params. Better use init_param
|
|
454
|
+
def init_params(*params)
|
|
455
|
+
description.init_params(*params)
|
|
456
|
+
end
|
|
457
|
+
|
|
458
|
+
# Creates a parameter which has to be used for instancitation
|
|
459
|
+
def init_param(name, long_name, type, desc)
|
|
460
|
+
p = param_noaccess(nil, nil, name, long_name, type, desc)
|
|
461
|
+
init_params(p)
|
|
462
|
+
end
|
|
463
|
+
|
|
464
|
+
end
|
|
465
|
+
|
|
466
|
+
end
|
|
467
|
+
end
|
|
468
|
+
|
|
469
|
+
end
|