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

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,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