4444
4545
4646def main ():
47- logger .setLevel (_logging .INFO )
47+ logger .setLevel (_logging .DEBUG )
4848 stream_handler = _logging .StreamHandler ()
4949 formatter = _logging .Formatter ("[%(filename)s] %(message)s" )
5050 stream_handler .setFormatter (formatter )
@@ -55,19 +55,23 @@ def main():
5555 logger .info ("Tk {ver}" .format (ver = tk .Tcl ().eval ('info patchlevel' )))
5656 assert cef .__version__ >= "55.3" , "CEF Python v55.3+ required to run this"
5757 sys .excepthook = cef .ExceptHook # To shutdown all CEF processes on error
58+ # Tk must be initialized before CEF otherwise fatal error (Issue #306)
5859 root = tk .Tk ()
5960 app = MainFrame (root )
60- # Tk must be initialized before CEF otherwise fatal error (Issue #306)
61- cef .Initialize ()
61+ settings = {}
62+ if MAC :
63+ settings ["external_message_pump" ] = True
64+ cef .Initialize (settings = settings )
6265 app .mainloop ()
66+ logger .debug ("Main loop exited" )
6367 cef .Shutdown ()
6468
65-
6669class MainFrame (tk .Frame ):
6770
6871 def __init__ (self , root ):
6972 self .browser_frame = None
7073 self .navigation_bar = None
74+ self .root = root
7175
7276 # Root
7377 root .geometry ("900x640" )
@@ -124,7 +128,9 @@ def on_focus_out(self, _):
124128 def on_close (self ):
125129 if self .browser_frame :
126130 self .browser_frame .on_root_close ()
127- self .master .destroy ()
131+ self .browser_frame = None
132+ else :
133+ self .master .destroy ()
128134
129135 def get_browser (self ):
130136 if self .browser_frame :
@@ -147,11 +153,12 @@ def setup_icon(self):
147153
148154class BrowserFrame (tk .Frame ):
149155
150- def __init__ (self , master , navigation_bar = None ):
156+ def __init__ (self , mainframe , navigation_bar = None ):
151157 self .navigation_bar = navigation_bar
152158 self .closing = False
153159 self .browser = None
154- tk .Frame .__init__ (self , master )
160+ tk .Frame .__init__ (self , mainframe )
161+ self .mainframe = mainframe
155162 self .bind ("<FocusIn>" , self .on_focus_in )
156163 self .bind ("<FocusOut>" , self .on_focus_out )
157164 self .bind ("<Configure>" , self .on_configure )
@@ -165,27 +172,42 @@ def embed_browser(self):
165172 self .browser = cef .CreateBrowserSync (window_info ,
166173 url = "https://www.google.com/" )
167174 assert self .browser
175+ self .browser .SetClientHandler (LifespanHandler (self ))
168176 self .browser .SetClientHandler (LoadHandler (self ))
169177 self .browser .SetClientHandler (FocusHandler (self ))
170178 self .message_loop_work ()
171179
172180 def get_window_handle (self ):
173- if self .winfo_id () > 0 :
174- return self .winfo_id ()
175- elif MAC :
176- # On Mac window id is an invalid negative value (Issue #308).
177- # This is kind of a dirty hack to get window handle using
178- # PyObjC package. If you change structure of windows then you
181+ if MAC :
182+ # Do not use self.winfo_id() on Mac, because of these issues:
183+ # 1. Window id sometimes has an invalid negative value (Issue #308).
184+ # 2. Even with valid window id it crashes during the call to NSView.setAutoresizingMask:
185+ # https://github.com/cztomczak/cefpython/issues/309#issuecomment-661094466
186+ #
187+ # To fix it using PyObjC package to obtain window handle. If you change structure of windows then you
179188 # need to do modifications here as well.
189+ #
190+ # There is still one issue with this solution. Sometimes there is more than one window, for example when application
191+ # didn't close cleanly last time Python displays an NSAlert window asking whether to Reopen that window. In such
192+ # case app will crash and you will see in console:
193+ # > Fatal Python error: PyEval_RestoreThread: NULL tstate
194+ # > zsh: abort python tkinter_.py
195+ # Error messages related to this: https://github.com/cztomczak/cefpython/issues/441
196+ #
197+ # There is yet another issue that might be related as well:
198+ # https://github.com/cztomczak/cefpython/issues/583
199+
180200 # noinspection PyUnresolvedReferences
181201 from AppKit import NSApp
182202 # noinspection PyUnresolvedReferences
183203 import objc
184- # Sometimes there is more than one window, when application
185- # didn't close cleanly last time Python displays an NSAlert
186- # window asking whether to Reopen that window.
204+ logger .info ("winfo_id={}" .format (self .winfo_id ()))
187205 # noinspection PyUnresolvedReferences
188- return objc .pyobjc_id (NSApp .windows ()[- 1 ].contentView ())
206+ content_view = objc .pyobjc_id (NSApp .windows ()[- 1 ].contentView ())
207+ logger .info ("content_view={}" .format (content_view ))
208+ return content_view
209+ elif self .winfo_id () > 0 :
210+ return self .winfo_id ()
189211 else :
190212 raise Exception ("Couldn't obtain window handle" )
191213
@@ -224,17 +246,32 @@ def on_focus_out(self, _):
224246 self .browser .SetFocus (False )
225247
226248 def on_root_close (self ):
249+ logger .info ("BrowserFrame.on_root_close" )
227250 if self .browser :
251+ logger .debug ("CloseBrowser" )
228252 self .browser .CloseBrowser (True )
229253 self .clear_browser_references ()
230- self .destroy ()
254+ else :
255+ logger .debug ("tk.Frame.destroy" )
256+ self .destroy ()
257+
231258
232259 def clear_browser_references (self ):
233260 # Clear browser references that you keep anywhere in your
234261 # code. All references must be cleared for CEF to shutdown cleanly.
235262 self .browser = None
236263
237264
265+ class LifespanHandler (object ):
266+
267+ def __init__ (self , tkFrame ):
268+ self .tkFrame = tkFrame
269+
270+ def OnBeforeClose (self , browser , ** _ ):
271+ logger .debug ("LifespanHandler.OnBeforeClose" )
272+ self .tkFrame .quit ()
273+
274+
238275class LoadHandler (object ):
239276
240277 def __init__ (self , browser_frame ):
0 commit comments