1+ # Additional and wx specific layer of abstraction for the cefpython
2+ # __author__ = "Greg Kacy <grkacy@gmail.com>"
3+
4+ #--------------------------------------------------------------------------------
5+
6+ import platform
7+ if platform .architecture ()[0 ] != "32bit" :
8+ raise Exception ("Only 32bit architecture is supported" )
9+
10+ import os
11+ import sys
12+ try :
13+ # Import local PYD file (portable zip).
14+ if sys .hexversion >= 0x02070000 and sys .hexversion < 0x03000000 :
15+ import cefpython_py27 as cefpython
16+ elif sys .hexversion >= 0x03000000 and sys .hexversion < 0x04000000 :
17+ import cefpython_py32 as cefpython
18+ else :
19+ raise Exception ("Unsupported python version: %s" % sys .version )
20+ except ImportError :
21+ # Import from package (installer).
22+ from cefpython1 import cefpython
23+
24+ import wx
25+ import wx .lib .buttons as buttons
26+
27+ from cefpython1 .wx .utils import GetApplicationPath
28+
29+ #-------------------------------------------------------------------------------
30+
31+ # Default timer interval when timer used to service CEF message loop
32+ DEFAULT_TIMER_MILLIS = 10
33+
34+ #-------------------------------------------------------------------------------
35+
36+ class NavigationBar (wx .Panel ):
37+ def __init__ (self , parent , * args , ** kwargs ):
38+ wx .Panel .__init__ (self , parent , * args , ** kwargs )
39+
40+ self .bitmapDir = os .path .join (os .path .dirname (
41+ os .path .abspath (__file__ )), "images" )
42+
43+ self ._InitComponents ()
44+ self ._LayoutComponents ()
45+ self ._InitEventHandlers ()
46+
47+ def _InitComponents (self ):
48+ self .backBtn = buttons .GenBitmapButton (self , - 1 ,
49+ wx .Bitmap (os .path .join (self .bitmapDir , "Arrow Left.png" ),
50+ wx .BITMAP_TYPE_PNG ), style = wx .BORDER_NONE )
51+ self .forwardBtn = buttons .GenBitmapButton (self , - 1 ,
52+ wx .Bitmap (os .path .join (self .bitmapDir , "Arrow Right.png" ),
53+ wx .BITMAP_TYPE_PNG ), style = wx .BORDER_NONE )
54+ self .reloadBtn = buttons .GenBitmapButton (self , - 1 ,
55+ wx .Bitmap (os .path .join (self .bitmapDir , "Button Load.png" ),
56+ wx .BITMAP_TYPE_PNG ), style = wx .BORDER_NONE )
57+
58+ self .url = wx .TextCtrl (self , id = - 1 , style = 0 )
59+
60+ self .historyPopup = wx .Menu ()
61+
62+ def _LayoutComponents (self ):
63+ sizer = wx .BoxSizer (wx .HORIZONTAL )
64+ sizer .Add (self .backBtn , 0 , wx .ALIGN_LEFT | wx .ALIGN_CENTER_VERTICAL |
65+ wx .ALL , 0 )
66+ sizer .Add (self .forwardBtn , 0 , wx .ALIGN_LEFT | wx .ALIGN_CENTER_VERTICAL |
67+ wx .ALL , 0 )
68+ sizer .Add (self .reloadBtn , 0 , wx .ALIGN_LEFT | wx .ALIGN_CENTER_VERTICAL |
69+ wx .ALL , 0 )
70+
71+ sizer .Add (self .url , 1 , wx .EXPAND | wx .ALIGN_CENTER_VERTICAL | wx .ALL , 12 )
72+
73+ self .SetSizer (sizer )
74+ self .Fit ()
75+
76+ def _InitEventHandlers (self ):
77+ self .backBtn .Bind (wx .EVT_CONTEXT_MENU , self .OnButtonContext )
78+
79+ def __del__ (self ):
80+ self .historyPopup .Destroy ()
81+
82+ def GetBackButton (self ):
83+ return self .backBtn
84+
85+ def GetForwardButton (self ):
86+ return self .forwardBtn
87+
88+ def GetReloadButton (self ):
89+ return self .reloadBtn
90+
91+ def GetUrlCtrl (self ):
92+ return self .url
93+
94+ def InitHistoryPopup (self ):
95+ self .historyPopup = wx .Menu ()
96+
97+ def AddToHistory (self , url ):
98+ self .historyPopup .Append (- 1 , url )
99+
100+ def OnButtonContext (self , event ):
101+ self .PopupMenu (self .historyPopup )
102+
103+
104+ class ChromeWindow (wx .Window ):
105+ """
106+ Standalone CEF component. The class provides facilites for interacting
107+ with wx message loop
108+ """
109+ def __init__ (self , parent , url = "" , useTimer = False ,
110+ timerMillis = DEFAULT_TIMER_MILLIS , size = (- 1 , - 1 ),
111+ * args , ** kwargs ):
112+ wx .Window .__init__ (self , parent , id = wx .ID_ANY , size = size ,
113+ * args , ** kwargs )
114+ self .url = url
115+ windowInfo = cefpython .WindowInfo ()
116+ windowInfo .SetAsChild (self .GetHandle ())
117+ self .browser = cefpython .CreateBrowserSync (windowInfo ,
118+ browserSettings = {}, navigateUrl = url )
119+
120+ self .Bind (wx .EVT_SET_FOCUS , self .OnSetFocus )
121+ self .Bind (wx .EVT_SIZE , self .OnSize )
122+ if useTimer :
123+ self .timerID = 1
124+ self ._CreateTimer (timerMillis )
125+ else :
126+ self .Bind (wx .EVT_IDLE , self .OnIdle )
127+ self ._useTimer = useTimer
128+
129+ def __del__ (self ):
130+ '''cleanup stuff'''
131+ if self ._useTimer :
132+ self .timer .Stop ()
133+ else :
134+ self .Unbind (wx .EVT_IDLE )
135+ self .browser .CloseBrowser ()
136+
137+ def _CreateTimer (self , millis ):
138+ self .timer = wx .Timer (self , self .timerID )
139+ self .timer .Start (millis ) #
140+ wx .EVT_TIMER (self , self .timerID , self .OnTimer )
141+
142+ def OnTimer (self , event ):
143+ """Service CEF message loop when useTimer is True"""
144+ cefpython .MessageLoopWork ()
145+
146+ def OnIdle (self , event ):
147+ """Service CEF message loop when useTimer is False"""
148+ cefpython .MessageLoopWork ()
149+ event .Skip ()
150+
151+ def OnSetFocus (self , event ):
152+ cefpython .WindowUtils .OnSetFocus (self .GetHandle (), 0 , 0 , 0 )
153+ event .Skip ()
154+
155+ def OnSize (self , event ):
156+ """Handle the the size event"""
157+ cefpython .WindowUtils .OnSize (self .GetHandle (), 0 , 0 , 0 )
158+ event .Skip ()
159+
160+ def GetBrowser (self ):
161+ """Returns the CEF's browser object"""
162+ return self .browser
163+
164+ def LoadUrl (self , url , onLoadStart = None , onLoadEnd = None ):
165+ if onLoadStart or onLoadEnd :
166+ self .GetBrowser ().SetClientHandler (
167+ CallbackClientHandler (onLoadStart , onLoadEnd ))
168+ self .GetBrowser ().GetMainFrame ().LoadUrl (url )
169+
170+
171+ class ChromeCtrl (wx .Panel ):
172+ def __init__ (self , parent , url = "" , useTimer = False ,
173+ timerMillis = DEFAULT_TIMER_MILLIS , hasNavBar = True ,
174+ * args , ** kwargs ):
175+ wx .Panel .__init__ (self , parent , * args , ** kwargs )
176+
177+ self .chromeWindow = ChromeWindow (self , url = str (url ), useTimer = useTimer )
178+ sizer = wx .BoxSizer (wx .VERTICAL )
179+ self .navigationBar = None
180+ if hasNavBar :
181+ self .navigationBar = self .CreateNavigationBar ()
182+ sizer .Add (self .navigationBar , 0 , wx .EXPAND | wx .ALL , 0 )
183+ self ._InitEventHandlers ()
184+
185+ sizer .Add (self .chromeWindow , 1 , wx .EXPAND , 0 )
186+
187+ self .SetSizer (sizer )
188+ self .Fit ()
189+
190+ ch = DefaultClientHandler (self )
191+ self .SetClientHandler (ch )
192+ if self .navigationBar :
193+ self .UpdateButtonsState ()
194+
195+ def _InitEventHandlers (self ):
196+ self .navigationBar .backBtn .Bind (wx .EVT_BUTTON , self .OnLeft )
197+ self .navigationBar .forwardBtn .Bind (wx .EVT_BUTTON , self .OnRight )
198+ self .navigationBar .reloadBtn .Bind (wx .EVT_BUTTON , self .OnReload )
199+
200+ def GetNavigationBar (self ):
201+ return self .navigationBar
202+
203+ def SetNavigationBar (self , navigationBar ):
204+ sizer = self .GetSizer ()
205+ if self .navigationBar :
206+ # remove previous one
207+ sizer .Replace (self .navigationBar , navigationBar )
208+ self .navigationBar .Hide ()
209+ del self .navigationBar
210+ else :
211+ sizer .Insert (0 , navigationBar , 0 , wx .EXPAND )
212+ self .navigationBar = navigationBar
213+ sizer .Fit (self )
214+
215+ def CreateNavigationBar (self ):
216+ np = NavigationBar (self )
217+ return np
218+
219+ def SetClientHandler (self , handler ):
220+ self .chromeWindow .GetBrowser ().SetClientHandler (handler )
221+
222+ def OnLeft (self , event ):
223+ if self .chromeWindow .GetBrowser ().CanGoBack ():
224+ self .chromeWindow .GetBrowser ().GoBack ()
225+ self .UpdateButtonsState ()
226+
227+ def OnRight (self , event ):
228+ if self .chromeWindow .GetBrowser ().CanGoForward ():
229+ self .chromeWindow .GetBrowser ().GoForward ()
230+ self .UpdateButtonsState ()
231+
232+ def OnReload (self , event ):
233+ self .chromeWindow .GetBrowser ().Reload ()
234+ self .UpdateButtonsState ()
235+
236+ def UpdateButtonsState (self ):
237+ self .navigationBar .backBtn .Enable (
238+ self .chromeWindow .GetBrowser ().CanGoBack ())
239+ self .navigationBar .forwardBtn .Enable (
240+ self .chromeWindow .GetBrowser ().CanGoForward ())
241+
242+ def OnLoadStart (self , browser , frame ):
243+ if self .navigationBar :
244+ self .UpdateButtonsState ()
245+ self .navigationBar .GetUrlCtrl ().SetValue (
246+ browser .GetMainFrame ().GetUrl ())
247+ self .navigationBar .AddToHistory (browser .GetMainFrame ().GetUrl ())
248+
249+
250+ class DefaultClientHandler (object ):
251+ def __init__ (self , parentCtrl ):
252+ self .parentCtrl = parentCtrl
253+
254+ def OnLoadStart (self , browser , frame ):
255+ self .parentCtrl .OnLoadStart (browser , frame )
256+
257+ def OnLoadEnd (self , browser , frame , httpStatusCode ):
258+ self .parentCtrl .OnLoadEnd (browser , frame , httpStatusCode )
259+
260+ def OnLoadError (self , browser , frame , errorCode , failedUrl , errorText ):
261+ # TODO
262+ print "ERROR LOADING URL : %" % failedUrl
263+
264+ class CallbackClientHandler (object ):
265+ def __init__ (self , onLoadStart = None , onLoadEnd = None ):
266+ self .onLoadStart = onLoadStart
267+ self .onLoadEnd = onLoadEnd
268+
269+ def OnLoadStart (self , browser , frame ):
270+ if self .onLoadStart and frame .GetUrl () != "about:blank" :
271+ self .onLoadStart (browser , frame )
272+
273+ def OnLoadEnd (self , browser , frame , httpStatusCode ):
274+ if self .onLoadEnd and frame .GetUrl () != "about:blank" :
275+ self .onLoadEnd (browser , frame , httpStatusCode )
276+
277+ def OnLoadError (self , browser , frame , errorCode , failedUrl , errorText ):
278+ # TODO
279+ print "ERROR LOADING URL : %" % failedUrl
280+
281+ #-------------------------------------------------------------------------------
282+
283+ def Initialize (settings = None ):
284+ """Initializes CEF, We should do it before initializing wx
285+ If no settings passed a default is used
286+ """
287+ sys .excepthook = ExceptHook
288+ if not settings :
289+ settings = {
290+ "log_severity" : cefpython .LOGSEVERITY_INFO ,
291+ "log_file" : GetApplicationPath ("debug.log" ),
292+ "release_dcheck_enabled" : True # Enable only when debugging.
293+ }
294+ cefpython .Initialize (settings )
295+
296+ def Shutdown ():
297+ """Shuts down CEF, should be called by app exiting code"""
298+ cefpython .Shutdown ()
299+
300+ def ExceptHook (t , value , traceObject ):
301+ import traceback , os , time
302+ # This hook does the following: in case of exception display it,
303+ # write to error.log, shutdown CEF and exit application.
304+ error = "\n " .join (traceback .format_exception (t , value , traceObject ))
305+ with open (GetApplicationPath ("error.log" ), "a" ) as f :
306+ f .write ("\n [%s] %s\n " % (time .strftime ("%Y-%m-%d %H:%M:%S" ), error ))
307+ print ("\n " + error + "\n " )
308+ ##cefpython.QuitMessageLoop()
309+ ##cefpython.Shutdown()
310+ # So that "finally" does not execute.
311+ ##os._exit(1)
0 commit comments