Skip to content

Commit bfe7166

Browse files
author
Christopher Doris
committed
ipython compat
1 parent f2827fd commit bfe7166

File tree

6 files changed

+135
-134
lines changed

6 files changed

+135
-134
lines changed

src/PythonCall.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,12 +77,14 @@ include("compat/multimedia.jl")
7777
include("compat/serialization.jl")
7878
include("compat/gui.jl")
7979
include("compat/matplotlib.jl")
80+
include("compat/ipython.jl")
8081

8182
function __init__()
8283
C.with_gil() do
8384
init_consts()
8485
init_pyconvert()
8586
init_datetime()
87+
# juliacall/jlwrap
8688
init_juliacall()
8789
init_jlwrap_base()
8890
init_jlwrap_raw()
@@ -98,9 +100,11 @@ function __init__()
98100
init_jlwrap_number()
99101
init_jlwrap_io()
100102
init_juliacall_2()
103+
# compat
101104
init_stdlib()
102105
init_gui()
103106
init_matplotlib()
107+
init_ipython()
104108
end
105109
end
106110

src/compat/gui.jl

Lines changed: 77 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -59,81 +59,83 @@ const EVENT_LOOPS = Dict{Symbol,Base.Timer}()
5959
const new_event_loop_callback = pynew()
6060

6161
function init_gui()
62-
# define callbacks
63-
@py g = {}
64-
@py @exec """
65-
def new_event_loop_callback(g, interval=0.04):
66-
if g in ("pyqt4","pyqt5","pyside","pyside2"):
67-
if g == "pyqt4":
68-
import PyQt4.QtCore as QtCore
69-
elif g == "pyqt5":
70-
import PyQt5.QtCore as QtCore
71-
elif g == "pyside":
72-
import PySide.QtCore as QtCore
73-
elif g == "pyside2":
74-
import PySide2.QtCore as QtCore
75-
instance = QtCore.QCoreApplication.instance
76-
AllEvents = QtCore.QEventLoop.AllEvents
77-
processEvents = QtCore.QCoreApplication.processEvents
78-
maxtime = interval * 1000
79-
def callback():
80-
app = instance()
81-
if app is not None:
82-
app._in_event_loop = True
83-
processEvents(AllEvents, maxtime)
84-
elif g in ("gtk","gtk3"):
85-
if g == "gtk3":
86-
import gi
87-
if gi.get_required_version("Gtk") is None:
88-
gi.require_version("Gtk", "3.0")
89-
import gi.repository.Gtk as gtk
90-
elif g == "gtk":
91-
import gtk
92-
events_pending = gtk.events_pending
93-
main_iteration = gtk.main_iteration
94-
def callback():
95-
while events_pending():
96-
main_iteration()
97-
elif g == "wx":
98-
import wx
99-
GetApp = wx.GetApp
100-
EventLoop = wx.EventLoop
101-
EventLoopActivator = wx.EventLoopActivator
102-
def callback():
103-
app = GetApp()
104-
if app is not None:
105-
app._in_event_loop = True
106-
evtloop = EventLoop()
107-
ea = EventLoopActivator(evtloop)
108-
Pending = evtloop.Pending
109-
Dispatch = evtloop.Dispatch
110-
while Pending():
111-
Dispatch()
112-
app.ProcessIdle()
113-
elif g == "tkinter":
114-
import tkinter, _tkinter
115-
flag = _tkinter.ALL_EVENTS | _tkinter.DONT_WAIT
116-
root = None
117-
def callback():
118-
global root
119-
new_root = tkinter._default_root
120-
if new_root is not None:
121-
root = new_root
122-
if root is not None:
123-
while root.dooneevent(flag):
124-
pass
125-
else:
126-
raise ValueError("invalid event loop name: {}".format(g))
127-
return callback
128-
""" g
129-
pycopy!(new_event_loop_callback, g["new_event_loop_callback"])
130-
131-
# add a hook to automatically call fix_qt_plugin_path()
132-
fixqthook = Py(() -> (CONFIG.auto_fix_qt_plugin_path && fix_qt_plugin_path(); nothing))
133-
pymodulehooks.add_hook("PyQt4", fixqthook)
134-
pymodulehooks.add_hook("PyQt5", fixqthook)
135-
pymodulehooks.add_hook("PySide", fixqthook)
136-
pymodulehooks.add_hook("PySide2", fixqthook)
62+
if !C.CTX.is_embedded
63+
# define callbacks
64+
@py g = {}
65+
@py @exec """
66+
def new_event_loop_callback(g, interval=0.04):
67+
if g in ("pyqt4","pyqt5","pyside","pyside2"):
68+
if g == "pyqt4":
69+
import PyQt4.QtCore as QtCore
70+
elif g == "pyqt5":
71+
import PyQt5.QtCore as QtCore
72+
elif g == "pyside":
73+
import PySide.QtCore as QtCore
74+
elif g == "pyside2":
75+
import PySide2.QtCore as QtCore
76+
instance = QtCore.QCoreApplication.instance
77+
AllEvents = QtCore.QEventLoop.AllEvents
78+
processEvents = QtCore.QCoreApplication.processEvents
79+
maxtime = interval * 1000
80+
def callback():
81+
app = instance()
82+
if app is not None:
83+
app._in_event_loop = True
84+
processEvents(AllEvents, maxtime)
85+
elif g in ("gtk","gtk3"):
86+
if g == "gtk3":
87+
import gi
88+
if gi.get_required_version("Gtk") is None:
89+
gi.require_version("Gtk", "3.0")
90+
import gi.repository.Gtk as gtk
91+
elif g == "gtk":
92+
import gtk
93+
events_pending = gtk.events_pending
94+
main_iteration = gtk.main_iteration
95+
def callback():
96+
while events_pending():
97+
main_iteration()
98+
elif g == "wx":
99+
import wx
100+
GetApp = wx.GetApp
101+
EventLoop = wx.EventLoop
102+
EventLoopActivator = wx.EventLoopActivator
103+
def callback():
104+
app = GetApp()
105+
if app is not None:
106+
app._in_event_loop = True
107+
evtloop = EventLoop()
108+
ea = EventLoopActivator(evtloop)
109+
Pending = evtloop.Pending
110+
Dispatch = evtloop.Dispatch
111+
while Pending():
112+
Dispatch()
113+
app.ProcessIdle()
114+
elif g == "tkinter":
115+
import tkinter, _tkinter
116+
flag = _tkinter.ALL_EVENTS | _tkinter.DONT_WAIT
117+
root = None
118+
def callback():
119+
global root
120+
new_root = tkinter._default_root
121+
if new_root is not None:
122+
root = new_root
123+
if root is not None:
124+
while root.dooneevent(flag):
125+
pass
126+
else:
127+
raise ValueError("invalid event loop name: {}".format(g))
128+
return callback
129+
""" g
130+
pycopy!(new_event_loop_callback, g["new_event_loop_callback"])
131+
132+
# add a hook to automatically call fix_qt_plugin_path()
133+
fixqthook = Py(() -> (CONFIG.auto_fix_qt_plugin_path && fix_qt_plugin_path(); nothing))
134+
pymodulehooks.add_hook("PyQt4", fixqthook)
135+
pymodulehooks.add_hook("PyQt5", fixqthook)
136+
pymodulehooks.add_hook("PySide", fixqthook)
137+
pymodulehooks.add_hook("PySide2", fixqthook)
138+
end
137139
end
138140

139141
"""

src/compat/ipython.jl

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
struct IPythonDisplay <: AbstractDisplay end
2+
3+
function Base.display(d::IPythonDisplay, m::MIME, @nospecialize(x))
4+
ipy = pyimport("IPython")
5+
buf = IOBuffer()
6+
dict = pydict()
7+
try
8+
show(buf, m, x)
9+
catch
10+
throw(MethodError(display, (d, m, x)))
11+
end
12+
data = take!(buf)
13+
dict[string(m)] = istextmime(m) ? pystr_fromUTF8(data) : pybytes(data)
14+
ipy.display.display(dict, raw=true)
15+
return
16+
end
17+
18+
function Base.display(d::IPythonDisplay, @nospecialize(x))
19+
ipy = pyimport("IPython")
20+
if ispy(x)
21+
ipy.display.display(x)
22+
return
23+
end
24+
buf = IOBuffer()
25+
dict = pydict()
26+
for m in Utils.mimes_for(x)
27+
try
28+
show(buf, MIME(m), x)
29+
catch
30+
continue
31+
end
32+
data = take!(buf)
33+
dict[m] = istextmime(m) ? pystr_fromUTF8(data) : pybytes(data)
34+
end
35+
length(dict) == 0 && throw(MethodError(display, (d, x)))
36+
ipy.display.display(dict, raw=true)
37+
return
38+
end
39+
40+
function init_ipython()
41+
# EXPERIMENTAL: IPython integration
42+
if C.CTX.is_embedded && CONFIG.auto_ipython_display
43+
is_ipython = ("IPython" in pysysmodule.modules) && !pyisnone(pysysmodule.modules["IPython"].get_ipython())
44+
if is_ipython
45+
# Set `Base.stdout` to `sys.stdout` and ensure it is flushed after each execution
46+
@eval Base stdout = $(PyIO(pysysmodule.stdout))
47+
pysysmodule.modules["IPython"].get_ipython().events.register("post_execute", pycallback(() -> (flush(Base.stdout); nothing)))
48+
# set displays so that Base.display() renders in ipython
49+
pushdisplay(TextDisplay(Base.stdout))
50+
pushdisplay(IPythonDisplay())
51+
end
52+
end
53+
end

src/config.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ Base.@kwdef mutable struct Config
22
auto_sys_last_traceback :: Bool = true
33
auto_fix_qt_plugin_path :: Bool = true
44
auto_pyplot_show :: Bool = true
5+
auto_ipython_display :: Bool = true
56
end
67

78
const CONFIG = Config()

src/cpython/context.jl

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -221,28 +221,6 @@ function init_context()
221221
"Only Python 3 is supported, this is Python $(CTX.version) at $(CTX.exe_path===missing ? "unknown location" : CTX.exe_path).",
222222
)
223223

224-
# # EXPERIMENTAL: IPython integration
225-
# if CONFIG.isembedded && CONFIG.ipythonintegration
226-
# if !CONFIG.isipython
227-
# @py ```
228-
# try:
229-
# ok = "IPython" in sys.modules and sys.modules["IPython"].get_ipython() is not None
230-
# except:
231-
# ok = False
232-
# $(CONFIG.isipython::Bool) = ok
233-
# ```
234-
# end
235-
# if CONFIG.isipython
236-
# # Set `Base.stdout` to `sys.stdout` and ensure it is flushed after each execution
237-
# @eval Base stdout = $(@pyv `sys.stdout`::PyIO)
238-
# pushdisplay(TextDisplay(Base.stdout))
239-
# pushdisplay(IPythonDisplay())
240-
# @py ```
241-
# mkcb = lambda cb: lambda: cb()
242-
# sys.modules["IPython"].get_ipython().events.register("post_execute", mkcb($(() -> flush(Base.stdout))))
243-
# ```
244-
# end
245-
# end
246224
end
247225

248226
@debug "Initialized PythonCall.jl" CTX.is_embedded CTX.is_initialized CTX.exe_path CTX.lib_path CTX.lib_ptr CTX.pyprogname CTX.pyhome CTX.version, CTX.is_conda CTX.conda_env

src/old/ipython.jl

Lines changed: 0 additions & 37 deletions
This file was deleted.

0 commit comments

Comments
 (0)