matplotlib 0.1.0.alpha.20170302 → 0.1.0.alpha.20170307

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,28 +1,6 @@
1
1
  module Matplotlib
2
- class Axes3D
2
+ class Axes
3
3
  include PyCall::PyObjectWrapper
4
-
5
- @__pyobj__ = PyCall.import_module('matplotlib.axes').Axes
6
-
7
- PyCall.dir(@__pyobj__).each do |name|
8
- obj = PyCall.getattr(@__pyobj__, name)
9
- next unless obj.kind_of?(PyCall::PyObject) || obj.kind_of?(PyCall::PyObjectWrapper)
10
- next unless PyCall.callable?(obj)
11
-
12
- define_method(name) do |*args, **kwargs|
13
- PyCall.getattr(__pyobj__, name).(*args, **kwargs)
14
- end
15
- end
16
-
17
- class << self
18
- attr_reader :__pyobj__
19
-
20
- def method_missing(name, *args, **kwargs)
21
- return super unless PyCall.hasattr?(__pyobj__, name)
22
- PyCall.getattr(__pyobj__, name)
23
- end
24
- end
25
-
26
- PyCall::Conversions.python_type_mapping(__pyobj__, self)
4
+ wrap_class PyCall.import_module('matplotlib.axes').Axes
27
5
  end
28
6
  end
@@ -1,28 +1,6 @@
1
1
  module Matplotlib
2
2
  class Axes3D
3
3
  include PyCall::PyObjectWrapper
4
-
5
- @__pyobj__ = PyCall.import_module('mpl_toolkits.mplot3d').Axes3D
6
-
7
- PyCall.dir(@__pyobj__).each do |name|
8
- obj = PyCall.getattr(@__pyobj__, name)
9
- next unless obj.kind_of?(PyCall::PyObject) || obj.kind_of?(PyCall::PyObjectWrapper)
10
- next unless PyCall.callable?(obj)
11
-
12
- define_method(name) do |*args, **kwargs|
13
- PyCall.getattr(__pyobj__, name).(*args, **kwargs)
14
- end
15
- end
16
-
17
- class << self
18
- attr_reader :__pyobj__
19
-
20
- def method_missing(name, *args, **kwargs)
21
- return super unless PyCall.hasattr?(__pyobj__, name)
22
- PyCall.getattr(__pyobj__, name)
23
- end
24
- end
25
-
26
- PyCall::Conversions.python_type_mapping(__pyobj__, self)
4
+ wrap_class PyCall.import_module('mpl_toolkits.mplot3d').Axes3D
27
5
  end
28
6
  end
@@ -2,6 +2,79 @@ require 'pycall'
2
2
 
3
3
  module Matplotlib
4
4
  module IRuby
5
+ module HookExtension
6
+ def self.extended(obj)
7
+ @event_registry ||= {}
8
+ @event_registry[obj] = {}
9
+ end
10
+
11
+ def self.register_event(target, event, hook)
12
+ @event_registry[target][event] ||= []
13
+ @event_registry[target][event] << hook
14
+ end
15
+
16
+ def register_event(event, hook=nil, &block)
17
+ HookExtension.register_event(self, event, [hook, block].compact)
18
+ end
19
+
20
+ def self.unregister_event(target, event, hook)
21
+ return unless @event_registry[target]
22
+ return unless @event_registry[target][event]
23
+ @event_registry[target][event].delete(hook)
24
+ end
25
+
26
+ def unregister_event(event, hook)
27
+ HookExtension.unregister_event(self, event, hook)
28
+ end
29
+
30
+ def self.trigger_event(target, event)
31
+ return unless @event_registry[target][event]
32
+ @event_registry[target][event].each do |hooks|
33
+ hooks.to_a.each do |hook|
34
+ hook.call if hook
35
+ end
36
+ end
37
+ rescue Exception
38
+ $stderr.puts "Error occurred in triggerred event: target=#{target} event=#{event}", $!.to_s, *$!.backtrace
39
+ end
40
+
41
+ def trigger_event(event)
42
+ HookExtension.trigger_event(self, event)
43
+ end
44
+
45
+ def execute_request(msg)
46
+ code = msg[:content]['code']
47
+ @execution_count += 1 if msg[:content]['store_history']
48
+ @session.send(:publish, :execute_input, code: code, execution_count: @execution_count)
49
+
50
+ trigger_event(:pre_execute)
51
+
52
+ content = {
53
+ status: :ok,
54
+ payload: [],
55
+ user_expressions: {},
56
+ execution_count: @execution_count
57
+ }
58
+ result = nil
59
+ begin
60
+ result = @backend.eval(code, msg[:content]['store_history'])
61
+ rescue SystemExit
62
+ content[:payload] << { source: :ask_exit }
63
+ rescue Exception => e
64
+ content = error_message(e)
65
+ @session.send(:publish, :error, content)
66
+ end
67
+
68
+ trigger_event(:post_execute)
69
+
70
+ @session.send(:reply, :execute_reply, content)
71
+ @session.send(:publish, :execute_result,
72
+ data: ::IRuby::Display.display(result),
73
+ metadata: {},
74
+ execution_count: @execution_count) unless result.nil? || msg[:content]['silent']
75
+ end
76
+ end
77
+
5
78
  AGG_FORMATS = {
6
79
  "image/png" => "png",
7
80
  "application/pdf" => "pdf",
@@ -9,7 +82,7 @@ module Matplotlib
9
82
  "image/eps" => "eps",
10
83
  "application/postscript" => "ps",
11
84
  "image/svg+xml" => "svg"
12
- }
85
+ }.freeze
13
86
 
14
87
  module Helper
15
88
  def register_formats
@@ -31,7 +104,152 @@ module Matplotlib
31
104
  class << self
32
105
  # NOTE: This method is translated from `IPython.core.activate_matplotlib` function.
33
106
  def activate(gui=:inline)
107
+ enable_matplotlib(gui)
108
+ end
109
+
110
+ GUI_BACKEND_MAP = {
111
+ tk: :TkAgg,
112
+ gtk: :GTKAgg,
113
+ gtk3: :GTK3Agg,
114
+ wx: :WXAgg,
115
+ qt: :Qt4Agg,
116
+ qt4: :Qt4Agg,
117
+ qt5: :Qt5Agg,
118
+ osx: :MacOSX,
119
+ nbagg: :nbAgg,
120
+ notebook: :nbAgg,
121
+ agg: :agg,
122
+ inline: 'module://ruby.matplotlib.backend_inline',
123
+ }.freeze
124
+
125
+ BACKEND_GUI_MAP = Hash[GUI_BACKEND_MAP.select {|k, v| v }].freeze
126
+
127
+ private_constant :GUI_BACKEND_MAP, :BACKEND_GUI_MAP
128
+
129
+ def available_gui_names
130
+ GUI_BACKEND_MAP.keys
131
+ end
132
+
133
+ private
134
+
135
+ # This method is based on IPython.core.interactiveshell.InteractiveShell.enable_matplotlib function.
136
+ def enable_matplotlib(gui=nil)
137
+ gui, backend = find_gui_and_backend(gui, @gui_select)
138
+
139
+ if gui != :inline
140
+ if @gui_select.nil?
141
+ @gui_select = gui
142
+ elsif gui != @gui_select
143
+ $stderr.puts "Warning: Cannot change to a different GUI toolkit: #{gui}. Using #{@gui_select} instead."
144
+ gui, backend = find_gui_and_backend(@gui_select)
145
+ end
146
+ end
147
+
148
+ activate_matplotlib(backend)
149
+ configure_inline_support(backend)
150
+ # self.enable_gui(gui)
151
+ # register matplotlib-aware execution runner for ExecutionMagics
152
+
153
+ [gui, backend]
154
+ end
155
+
156
+ # Given a gui string return the gui and matplotlib backend.
157
+ # This method is based on IPython.core.pylabtools.find_gui_and_backend function.
158
+ #
159
+ # @param [String, Symbol, nil] gui can be one of (:tk, :gtk, :wx, :qt, :qt4, :inline, :agg).
160
+ # @param [String, Symbol, nil] gui_select can be one of (:tk, :gtk, :wx, :qt, :qt4, :inline, :agg).
161
+ #
162
+ # @return A pair of (gui, backend) where backend is one of (:TkAgg, :GTKAgg, :WXAgg, :Qt4Agg, :agg).
163
+ def find_gui_and_backend(gui=nil, gui_select=nil)
164
+ gui = gui.to_sym if gui.kind_of? String
165
+
166
+ if gui && gui != :auto
167
+ # select backend based on requested gui
168
+ backend = GUI_BACKEND_MAP[gui]
169
+ gui = nil if gui == :agg
170
+ return [gui, backend]
171
+ end
172
+
173
+ backend = Matplotlib.rcParamsOrig['backend']&.to_sym
174
+ gui = BACKEND_GUI_MAP[backend]
175
+
176
+ # If we have already had a gui active, we need it and inline are the ones allowed.
177
+ if gui_select && gui != gui_select
178
+ gui = gui_select
179
+ backend = backend[gui]
180
+ end
181
+
182
+ [gui, backend]
183
+ end
184
+
185
+ # Activate the given backend and set interactive to true.
186
+ # This method is based on IPython.core.pylabtools.activate_matplotlib function.
187
+ #
188
+ # @param [String, Symbol] backend a name of matplotlib backend
189
+ def activate_matplotlib(backend)
190
+ require 'matplotlib'
191
+ Matplotlib.interactive(true)
192
+
193
+ backend = backend.to_s
194
+ Matplotlib.rcParams['backend'] = backend
195
+
34
196
  require 'matplotlib/pyplot'
197
+ Matplotlib::Pyplot.switch_backend(backend)
198
+
199
+ # TODO: should support wrapping python function
200
+ # plt = Matplotlib::Pyplot
201
+ # plt.__pyobj__.show._needmain = false
202
+ # plt.__pyobj__.draw_if_interactive = flag_calls(plt.__pyobj__.draw_if_interactive)
203
+ end
204
+
205
+ # This method is based on IPython.core.pylabtools.configure_inline_support function.
206
+ #
207
+ # @param shell an instance of IRuby shell
208
+ # @param backend a name of matplotlib backend
209
+ def configure_inline_support(backend)
210
+ # Temporally monky-patching IRuby kernel to enable flushing and closing figures.
211
+ # TODO: Make this feature a pull-request for sciruby/iruby.
212
+ kernel = ::IRuby::Kernel.instance
213
+ kernel.extend HookExtension
214
+ if backend == GUI_BACKEND_MAP[:inline]
215
+ kernel.register_event(:post_execute, method(:flush_figures))
216
+ # TODO: save original rcParams and overwrite rcParams with IRuby-specific configuration
217
+ new_backend_name = :inline
218
+ else
219
+ kernel.unregister_event(:post_execute, method(:flush_figures))
220
+ # TODO: restore saved original rcParams
221
+ new_backend_name = :not_inline
222
+ end
223
+ if new_backend_name != @current_backend
224
+ # TODO: select figure formats
225
+ @current_backend = new_backend_name
226
+ end
227
+ end
228
+
229
+ # This method is based on ipykernel.pylab.backend_inline.flush_figures function.
230
+ def flush_figures
231
+ # TODO: I want to allow users to turn on/off automatic figure closing.
232
+ show_figures(true)
233
+ end
234
+
235
+ # This method is based on ipykernel.pylab.backend_inline.show function.
236
+ #
237
+ # @param [true, false] close If true, a `plt.close('all')` call is automatically issued after sending all the figures.
238
+ def show_figures(close=false)
239
+ _pylab_helpers = PyCall.import_module('matplotlib._pylab_helpers')
240
+ gcf = PyCall.getattr(_pylab_helpers, :Gcf)
241
+ kernel = ::IRuby::Kernel.instance
242
+ gcf.get_all_fig_managers.().each do |fig_manager|
243
+ data = ::IRuby::Display.display(fig_manager.canvas.figure)
244
+ kernel.session.send(:publish, :execute_result,
245
+ data: data,
246
+ metadata: {},
247
+ execution_count: kernel.instance_variable_get(:@execution_count))
248
+ end
249
+ ensure
250
+ unless gcf.get_all_fig_managers.().none?
251
+ Matplotlib::Pyplot.close('all')
252
+ end
35
253
  end
36
254
  end
37
255
  end
@@ -0,0 +1,6 @@
1
+ module Matplotlib
2
+ class PolarAxes
3
+ include PyCall::PyObjectWrapper
4
+ wrap_class PyCall.import_module('matplotlib.projections.polar').PolarAxes
5
+ end
6
+ end
@@ -0,0 +1 @@
1
+ from matplotlib.backends.backend_agg import new_figure_manager, FigureCanvasAgg
@@ -1,3 +1,3 @@
1
1
  module Matplotlib
2
- VERSION = "0.1.0.alpha.20170302"
2
+ VERSION = "0.1.0.alpha.20170307"
3
3
  end
data/lib/matplotlib.rb CHANGED
@@ -35,4 +35,7 @@ module Matplotlib
35
35
  end
36
36
 
37
37
  require 'matplotlib/axes'
38
+ require 'matplotlib/polar_axes'
38
39
  require 'matplotlib/figure'
40
+
41
+ PyCall.append_sys_path(File.expand_path('../matplotlib/python', __FILE__))
data/matplotlib.gemspec CHANGED
@@ -21,7 +21,7 @@ Gem::Specification.new do |spec|
21
21
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
22
  spec.require_paths = ["lib"]
23
23
 
24
- spec.add_dependency "pycall", ">= 0.1.0.alpha.20170302"
24
+ spec.add_dependency "pycall", ">= 0.1.0.alpha.20170307"
25
25
 
26
26
  spec.add_development_dependency "bundler", "~> 1.13"
27
27
  spec.add_development_dependency "rake", "~> 10.0"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: matplotlib
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0.alpha.20170302
4
+ version: 0.1.0.alpha.20170307
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kenta Murata
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-03-02 00:00:00.000000000 Z
11
+ date: 2017-03-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pycall
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 0.1.0.alpha.20170302
19
+ version: 0.1.0.alpha.20170307
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: 0.1.0.alpha.20170302
26
+ version: 0.1.0.alpha.20170307
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: bundler
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -107,7 +107,9 @@ files:
107
107
  - lib/matplotlib/axes_3d.rb
108
108
  - lib/matplotlib/figure.rb
109
109
  - lib/matplotlib/iruby.rb
110
+ - lib/matplotlib/polar_axes.rb
110
111
  - lib/matplotlib/pyplot.rb
112
+ - lib/matplotlib/python/ruby/matplotlib/backend_inline.py
111
113
  - lib/matplotlib/version.rb
112
114
  - matplotlib.gemspec
113
115
  homepage: https://github.com/mrkn/matplotlib.rb