gnuplotrb 0.3.1 → 0.3.2

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.
@@ -1,80 +1,89 @@
1
- module GnuplotRB
2
- ##
3
- # This module takes care of path to gnuplot executable and checking its version.
4
- module Settings
5
- ##
6
- # GnuplotRB can work with Gnuplot 5.0+
7
- MIN_GNUPLOT_VERSION = 5.0
8
-
9
- class << self
10
- ##
11
- # For heavy calculations max_fit_delay may be increased.
12
- attr_writer :max_fit_delay
13
- ##
14
- # Get max fit delay.
15
- #
16
- # Max fit delay (5s by default) is used inside Fit::fit function.
17
- # If it waits for output more than max_fit_delay seconds
18
- # this behaviour is considered as errorneus.
19
- # @return [Integer] seconds to wait for output
20
- def max_fit_delay
21
- @max_fit_delay ||= 5
22
- end
23
-
24
- ##
25
- # Get path that should be used to run gnuplot executable.
26
- # Default value: 'gnuplot'.
27
- # @return [String] path to gnuplot executable
28
- def gnuplot_path
29
- self.gnuplot_path = 'gnuplot' unless defined?(@gnuplot_path)
30
- @gnuplot_path
31
- end
32
-
33
- ##
34
- # Set path to gnuplot executable.
35
- # @param path [String] path to gnuplot executable
36
- # @return given path
37
- def gnuplot_path=(path)
38
- validate_version(path)
39
- opts = { stdin_data: "set term\n" }
40
- @available_terminals = Open3.capture2e(path, **opts)
41
- .first
42
- .scan(/[:\n] +([a-z][^ ]+)/)
43
- .map(&:first)
44
- @gnuplot_path = path
45
- end
46
-
47
- ##
48
- # Get list of terminals (png, html, qt, jpeg etc) available for this gnuplot.
49
- # @return [Array of String] array of terminals available for this gnuplot
50
- def available_terminals
51
- gnuplot_path
52
- @available_terminals
53
- end
54
-
55
- ##
56
- # Get gnuplot version. Uses gnuplot_path to find gnuplot executable.
57
- # @return [Numeric] gnuplot version
58
- def version
59
- gnuplot_path
60
- @version
61
- end
62
-
63
- ##
64
- # @private
65
- # Validate gnuplot version. Compares current gnuplot's
66
- # version with ::MIN_GNUPLOT_VERSION. Throws exception if version is
67
- # less than min.
68
- #
69
- # @param path [String] path to gnuplot executable
70
- def validate_version(path)
71
- @version = IO.popen("#{path} --version")
72
- .read
73
- .match(/gnuplot ([^ ]+)/)[1]
74
- .to_f
75
- message = "Your Gnuplot version is #{@version}, please update it to at least 5.0"
76
- fail(ArgumentError, message) if @version < MIN_GNUPLOT_VERSION
77
- end
78
- end
79
- end
80
- end
1
+ module GnuplotRB
2
+ ##
3
+ # This module takes care of path to gnuplot executable and checking its version.
4
+ module Settings
5
+ ##
6
+ # GnuplotRB can work with Gnuplot 5.0+
7
+ MIN_GNUPLOT_VERSION = 5.0
8
+
9
+ class << self
10
+ DEFAULT_MAX_FIT_DELAY = 5
11
+ DEFAULT_GNUPLOT_PATH = 'gnuplot'
12
+ ##
13
+ # For heavy calculations max_fit_delay may be increased.
14
+ attr_writer :max_fit_delay
15
+ ##
16
+ # Get max fit delay.
17
+ #
18
+ # Max fit delay (5s by default) is used inside Fit::fit function.
19
+ # If it waits for output more than max_fit_delay seconds
20
+ # this behaviour is considered as errorneus.
21
+ # @return [Integer] seconds to wait for output
22
+ def max_fit_delay
23
+ @max_fit_delay ||= DEFAULT_MAX_FIT_DELAY
24
+ end
25
+
26
+ ##
27
+ # Get path that should be used to run gnuplot executable.
28
+ # Default value: 'gnuplot'.
29
+ # @return [String] path to gnuplot executable
30
+ def gnuplot_path
31
+ self.gnuplot_path = DEFAULT_GNUPLOT_PATH unless defined?(@gnuplot_path)
32
+ @gnuplot_path
33
+ end
34
+
35
+ ##
36
+ # Set path to gnuplot executable.
37
+ # @param path [String] path to gnuplot executable
38
+ # @return given path
39
+ def gnuplot_path=(path)
40
+ validate_version(path)
41
+ opts = { stdin_data: "set term\n" }
42
+ @available_terminals = Open3.capture2e(path, **opts)
43
+ .first
44
+ .scan(/[:\n] +([a-z][^ ]+)/)
45
+ .map(&:first)
46
+ @gnuplot_path = path
47
+ end
48
+
49
+ ##
50
+ # Get list of terminals (png, html, qt, jpeg etc) available for this gnuplot.
51
+ # @return [Array of String] array of terminals available for this gnuplot
52
+ def available_terminals
53
+ gnuplot_path
54
+ @available_terminals
55
+ end
56
+
57
+ ##
58
+ # Get gnuplot version. Uses gnuplot_path to find gnuplot executable.
59
+ # @return [Numeric] gnuplot version
60
+ def version
61
+ gnuplot_path
62
+ @version
63
+ end
64
+
65
+ ##
66
+ # @private
67
+ # Validate gnuplot version. Compares current gnuplot's
68
+ # version with ::MIN_GNUPLOT_VERSION. Throws exception if version is
69
+ # less than min.
70
+ #
71
+ # @param path [String] path to gnuplot executable
72
+ def validate_version(path)
73
+ @version = IO.popen("#{path} --version")
74
+ .read
75
+ .match(/gnuplot ([^ ]+)/)[1]
76
+ .to_f
77
+ raise(
78
+ ArgumentError,
79
+ "Your Gnuplot version is #{@version}, please update it to at least 5.0"
80
+ ) if @version < MIN_GNUPLOT_VERSION
81
+ rescue Errno::ENOENT
82
+ raise(
83
+ ArgumentError,
84
+ "Can't find Gnuplot executable. Please make sure it's installed and added to PATH."
85
+ )
86
+ end
87
+ end
88
+ end
89
+ end
@@ -1,202 +1,202 @@
1
- module GnuplotRB
2
- ##
3
- # Terminal keeps open pipe to gnuplot process, cares about naming in-memory
4
- # datablocks (just indexing with sequential integers). All the output
5
- # to gnuplot handled by this class. Terminal also handles options passed
6
- # to gnuplot as 'set key value'.
7
- class Terminal
8
- include ErrorHandling
9
-
10
- # order is important for some options
11
- OPTION_ORDER = [:term, :output, :multiplot, :timefmt, :xrange]
12
-
13
- private_constant :OPTION_ORDER
14
-
15
- class << self
16
- ##
17
- # Close given gnuplot pipe
18
- # @param stream [IO] pipe to close
19
- def close_arg(stream)
20
- stream.puts
21
- stream.puts 'exit'
22
- Process.waitpid(stream.pid)
23
- end
24
-
25
- ##
26
- # Plot test page for given term_name into file
27
- # with file_name (optional).
28
- #
29
- # Test page contains possibilities of the term.
30
- # @param term_name [String] terminal name ('png', 'gif', 'svg' etc)
31
- # @param file_name [String] filename to output image if needed
32
- # and chosen terminal supports image output
33
- # @return nil
34
- def test(term_name, file_name = nil)
35
- Terminal.new.set(term: term_name).test(file_name)
36
- end
37
- end
38
-
39
- ##
40
- # Create new Terminal connected with gnuplot.
41
- # Uses Settings::gnuplot_path to find gnuplot
42
- # executable. Each time you create Terminal it starts new
43
- # gnuplot subprocess which is closed after GC deletes
44
- # linked Terminal object.
45
- #
46
- # @param :persist [Boolean] gnuplot's "-persist" option
47
- def initialize(persist: false)
48
- @cmd = Settings.gnuplot_path
49
- @current_datablock = 0
50
- @cmd += ' -persist' if persist
51
- @cmd += ' 2>&1'
52
- stream = IO.popen(@cmd, 'w+')
53
- handle_stderr(stream)
54
- ObjectSpace.define_finalizer(self, proc { Terminal.close_arg(stream) })
55
- @in = stream
56
- yield(self) if block_given?
57
- end
58
-
59
- ##
60
- # Output datablock to this gnuplot terminal.
61
- #
62
- # @param data [String] data stored in datablock
63
- # @example
64
- # data = "1 1\n2 4\n3 9"
65
- # Terminal.new.store_datablock(data)
66
- # #=> returns '$DATA1'
67
- # #=> outputs to gnuplot:
68
- # #=> $DATA1 << EOD
69
- # #=> 1 1
70
- # #=> 2 4
71
- # #=> 3 9
72
- # #=> EOD
73
- def store_datablock(data)
74
- name = "$DATA#{@current_datablock += 1}"
75
- stream_puts "#{name} << EOD"
76
- stream_puts data
77
- stream_puts 'EOD'
78
- name
79
- end
80
-
81
- ##
82
- # Convert given options to gnuplot format.
83
- #
84
- # For "{ opt1: val1, .. , optN: valN }" it returns
85
- # set opt1 val1
86
- # ..
87
- # set optN valN
88
- #
89
- # @param ptions [Hash] options to convert
90
- # @return [String] options in Gnuplot format
91
- def options_hash_to_string(options)
92
- result = ''
93
- options.sort_by { |key, _| OPTION_ORDER.find_index(key) || -1 }.each do |key, value|
94
- if value
95
- result += "set #{OptionHandling.option_to_string(key, value)}\n"
96
- else
97
- result += "unset #{key}\n"
98
- end
99
- end
100
- result
101
- end
102
-
103
- ##
104
- # Applie given options to current gnuplot instance.
105
- #
106
- # For "{ opt1: val1, .. , optN: valN }" it will output to gnuplot
107
- # set opt1 val1
108
- # ..
109
- # set optN valN
110
- #
111
- # @param options [Hash] options to set
112
- # @return [Terminal] self
113
- # @example
114
- # set({term: ['qt', size: [100, 100]]})
115
- # #=> outputs to gnuplot: "set term qt size 100,100\n"
116
- def set(options)
117
- OptionHandling.validate_terminal_options(options)
118
- stream_puts(options_hash_to_string(options))
119
- end
120
-
121
- ##
122
- # Unset options
123
- #
124
- # @param *options [Sequence of Symbol] each symbol considered as option key
125
- # @return [Terminal] self
126
- def unset(*options)
127
- options.flatten
128
- .sort_by { |key| OPTION_ORDER.find_index(key) || -1 }
129
- .each { |key| stream_puts "unset #{OptionHandling.string_key(key)}" }
130
- self
131
- end
132
-
133
- ##
134
- # Short way to plot Datablock, Plot or Splot object.
135
- # Other items will be just piped out to gnuplot.
136
- # @param item Object that should be outputted to Gnuplot
137
- # @return [Terminal] self
138
- def <<(item)
139
- if item.is_a? Plottable
140
- item.plot(self)
141
- else
142
- stream_print(item.to_s)
143
- end
144
- self
145
- end
146
-
147
- ##
148
- # Just put *command* + "\n" to gnuplot pipe.
149
- # @param command [String] command to send
150
- # @return [Terminal] self
151
- def stream_puts(command)
152
- stream_print("#{command}\n")
153
- end
154
-
155
- ##
156
- # Just print *command* to gnuplot pipe.
157
- # @param command [String] command to send
158
- # @return [Terminal] self
159
- def stream_print(command)
160
- check_errors
161
- @in.print(command)
162
- self
163
- end
164
-
165
- ##
166
- # @deprecated
167
- # Call replot on gnuplot. This will execute last plot once again
168
- # with rereading data.
169
- # @param options [Hash] options will be set before replotting
170
- # @return [Terminal] self
171
- def replot(**options)
172
- set(options)
173
- stream_puts('replot')
174
- unset(options.keys)
175
- sleep 0.01 until File.size?(options[:output]) if options[:output]
176
- self
177
- end
178
-
179
- ##
180
- # Send gnuplot command to turn it off and for its Process to quit.
181
- # Closes pipe so Terminal object should not be used after #close call.
182
- def close
183
- check_errors
184
- Terminal.close_arg(@in)
185
- end
186
-
187
-
188
- ##
189
- # Plot test page into file with file_name (optional).
190
- #
191
- # Test page contains possibilities of the term.
192
- # @param file_name [String] filename to output image if needed
193
- # and chosen terminal supports image output
194
- # @return nil
195
- def test(file_name = nil)
196
- set(output: file_name) if file_name
197
- stream_puts('test')
198
- unset(:output)
199
- nil
200
- end
201
- end
202
- end
1
+ module GnuplotRB
2
+ ##
3
+ # Terminal keeps open pipe to gnuplot process, cares about naming in-memory
4
+ # datablocks (just indexing with sequential integers). All the output
5
+ # to gnuplot handled by this class. Terminal also handles options passed
6
+ # to gnuplot as 'set key value'.
7
+ class Terminal
8
+ include ErrorHandling
9
+
10
+ # order is important for some options
11
+ OPTION_ORDER = [:term, :output, :multiplot, :timefmt, :xrange]
12
+
13
+ private_constant :OPTION_ORDER
14
+
15
+ class << self
16
+ ##
17
+ # Close given gnuplot pipe
18
+ # @param stream [IO] pipe to close
19
+ def close_arg(stream)
20
+ stream.puts
21
+ stream.puts 'exit'
22
+ Process.waitpid(stream.pid)
23
+ end
24
+
25
+ ##
26
+ # Plot test page for given term_name into file
27
+ # with file_name (optional).
28
+ #
29
+ # Test page contains possibilities of the term.
30
+ # @param term_name [String] terminal name ('png', 'gif', 'svg' etc)
31
+ # @param file_name [String] filename to output image if needed
32
+ # and chosen terminal supports image output
33
+ # @return nil
34
+ def test(term_name, file_name = nil)
35
+ Terminal.new.set(term: term_name).test(file_name)
36
+ end
37
+ end
38
+
39
+ ##
40
+ # Create new Terminal connected with gnuplot.
41
+ # Uses Settings::gnuplot_path to find gnuplot
42
+ # executable. Each time you create Terminal it starts new
43
+ # gnuplot subprocess which is closed after GC deletes
44
+ # linked Terminal object.
45
+ #
46
+ # @param :persist [Boolean] gnuplot's "-persist" option
47
+ def initialize(persist: false)
48
+ @cmd = Settings.gnuplot_path
49
+ @current_datablock = 0
50
+ @cmd += ' -persist' if persist
51
+ @cmd += ' 2>&1'
52
+ stream = IO.popen(@cmd, 'w+')
53
+ handle_stderr(stream)
54
+ ObjectSpace.define_finalizer(self, proc { Terminal.close_arg(stream) })
55
+ @in = stream
56
+ yield(self) if block_given?
57
+ end
58
+
59
+ ##
60
+ # Output datablock to this gnuplot terminal.
61
+ #
62
+ # @param data [String] data stored in datablock
63
+ # @example
64
+ # data = "1 1\n2 4\n3 9"
65
+ # Terminal.new.store_datablock(data)
66
+ # #=> returns '$DATA1'
67
+ # #=> outputs to gnuplot:
68
+ # #=> $DATA1 << EOD
69
+ # #=> 1 1
70
+ # #=> 2 4
71
+ # #=> 3 9
72
+ # #=> EOD
73
+ def store_datablock(data)
74
+ name = "$DATA#{@current_datablock += 1}"
75
+ stream_puts "#{name} << EOD"
76
+ stream_puts data
77
+ stream_puts 'EOD'
78
+ name
79
+ end
80
+
81
+ ##
82
+ # Convert given options to gnuplot format.
83
+ #
84
+ # For "{ opt1: val1, .. , optN: valN }" it returns
85
+ # set opt1 val1
86
+ # ..
87
+ # set optN valN
88
+ #
89
+ # @param ptions [Hash] options to convert
90
+ # @return [String] options in Gnuplot format
91
+ def options_hash_to_string(options)
92
+ result = ''
93
+ options.sort_by { |key, _| OPTION_ORDER.find_index(key) || -1 }.each do |key, value|
94
+ if value
95
+ result += "set #{OptionHandling.option_to_string(key, value)}\n"
96
+ else
97
+ result += "unset #{key}\n"
98
+ end
99
+ end
100
+ result
101
+ end
102
+
103
+ ##
104
+ # Applie given options to current gnuplot instance.
105
+ #
106
+ # For "{ opt1: val1, .. , optN: valN }" it will output to gnuplot
107
+ # set opt1 val1
108
+ # ..
109
+ # set optN valN
110
+ #
111
+ # @param options [Hash] options to set
112
+ # @return [Terminal] self
113
+ # @example
114
+ # set({term: ['qt', size: [100, 100]]})
115
+ # #=> outputs to gnuplot: "set term qt size 100,100\n"
116
+ def set(options)
117
+ OptionHandling.validate_terminal_options(options)
118
+ stream_puts(options_hash_to_string(options))
119
+ end
120
+
121
+ ##
122
+ # Unset options
123
+ #
124
+ # @param *options [Sequence of Symbol] each symbol considered as option key
125
+ # @return [Terminal] self
126
+ def unset(*options)
127
+ options.flatten
128
+ .sort_by { |key| OPTION_ORDER.find_index(key) || -1 }
129
+ .each { |key| stream_puts "unset #{OptionHandling.string_key(key)}" }
130
+ self
131
+ end
132
+
133
+ ##
134
+ # Short way to plot Datablock, Plot or Splot object.
135
+ # Other items will be just piped out to gnuplot.
136
+ # @param item Object that should be outputted to Gnuplot
137
+ # @return [Terminal] self
138
+ def <<(item)
139
+ if item.is_a? Plottable
140
+ item.plot(self)
141
+ else
142
+ stream_print(item.to_s)
143
+ end
144
+ self
145
+ end
146
+
147
+ ##
148
+ # Just put *command* + "\n" to gnuplot pipe.
149
+ # @param command [String] command to send
150
+ # @return [Terminal] self
151
+ def stream_puts(command)
152
+ stream_print("#{command}\n")
153
+ end
154
+
155
+ ##
156
+ # Just print *command* to gnuplot pipe.
157
+ # @param command [String] command to send
158
+ # @return [Terminal] self
159
+ def stream_print(command)
160
+ check_errors
161
+ @in.print(command)
162
+ self
163
+ end
164
+
165
+ ##
166
+ # @deprecated
167
+ # Call replot on gnuplot. This will execute last plot once again
168
+ # with rereading data.
169
+ # @param options [Hash] options will be set before replotting
170
+ # @return [Terminal] self
171
+ def replot(**options)
172
+ set(options)
173
+ stream_puts('replot')
174
+ unset(options.keys)
175
+ sleep 0.01 until File.size?(options[:output]) if options[:output]
176
+ self
177
+ end
178
+
179
+ ##
180
+ # Send gnuplot command to turn it off and for its Process to quit.
181
+ # Closes pipe so Terminal object should not be used after #close call.
182
+ def close
183
+ check_errors
184
+ Terminal.close_arg(@in)
185
+ end
186
+
187
+
188
+ ##
189
+ # Plot test page into file with file_name (optional).
190
+ #
191
+ # Test page contains possibilities of the term.
192
+ # @param file_name [String] filename to output image if needed
193
+ # and chosen terminal supports image output
194
+ # @return nil
195
+ def test(file_name = nil)
196
+ set(output: file_name) if file_name
197
+ stream_puts('test')
198
+ unset(:output)
199
+ nil
200
+ end
201
+ end
202
+ end