3737pygtk .require ('2.0' )
3838import gtk
3939
40+ import kivy
4041from kivy .app import App
42+ from kivy .uix .button import Button
43+ from kivy .uix .popup import Popup
44+ from kivy .uix .label import Label
4145from kivy .uix .widget import Widget
4246from kivy .graphics import Color , Rectangle , GraphicException
4347from kivy .clock import Clock
4751from kivy .uix .boxlayout import BoxLayout
4852from kivy .base import EventLoop
4953
50- ####Kivy APP ####
51- Builder .load_string ("""
52-
53- <BrowserLayout>:
54- orientation: 'vertical'
55- BoxLayout:
56- size_hint_y: None
57- height: 40
58- Button:
59- text: "Back"
60- on_press: browser.go_back()
61- Button:
62- text: "Forward"
63- on_press: browser.go_forward()
64- Button:
65- text: "Reload"
66- on_press: browser.reload()
67- Button:
68- text: "Print"
69- on_press: browser.print_page()
70- Button:
71- text: "DevTools"
72- on_press: browser.devtools()
73- CefBrowser:
74- id: browser
75-
76- """ )
77-
54+ # Global variables
55+ g_switches = None
7856
7957
8058class BrowserLayout (BoxLayout ):
59+ browser = None
8160
8261 def __init__ (self , ** kwargs ):
8362 super (BrowserLayout , self ).__init__ (** kwargs )
63+ self .orientation = "vertical"
64+
65+ browser = CefBrowser (id = "browser" )
66+
67+ layout = BoxLayout ()
68+ layout .size_hint_y = None
69+ layout .height = 40
70+ layout .add_widget (Button (text = "Back" , on_press = browser .go_back ))
71+ layout .add_widget (Button (text = "Forward" , on_press = browser .go_forward ))
72+ layout .add_widget (Button (text = "Reload" , on_press = browser .reload ))
73+ layout .add_widget (Button (text = "Print" , on_press = browser .print_page ))
74+ layout .add_widget (Button (text = "DevTools" , on_press = browser .devtools ))
75+
76+ self .add_widget (layout )
77+ self .add_widget (browser )
8478
8579
8680
@@ -97,6 +91,9 @@ class CefBrowser(Widget):
9791 def __init__ (self , start_url = 'https://www.google.com/' , ** kwargs ):
9892 super (CefBrowser , self ).__init__ (** kwargs )
9993
94+ for arg in sys .argv :
95+ if arg .startswith ("http://" ) or arg .startswith ("https://" ):
96+ start_url = arg
10097 self .start_url = start_url
10198
10299 #Workaround for flexible size:
@@ -128,12 +125,29 @@ def size_changed(self, *kwargs):
128125 self .browser .WasResized ()
129126
130127
131- def _cef_mes (self , * kwargs ):
128+ count = 0
129+ def _message_loop_work (self , * kwargs ):
132130 '''Get called every frame.
133131 '''
132+ self .count += 1
133+ # print(self.count)
134134 cefpython .MessageLoopWork ()
135135 self .on_mouse_move_emulate ()
136136
137+ # From Kivy docs:
138+ # Clock.schedule_once(my_callback, 0) # call after the next frame
139+ # Clock.schedule_once(my_callback, -1) # call before the next frame
140+
141+ # When scheduling "after the next frame" Kivy calls _message_loop_work
142+ # in about 13ms intervals. We use a small trick to make this 6ms
143+ # interval by scheduling it alternately before and after the next
144+ # frame. This gives better User Experience when scrolling.
145+ # See Issue #240 for more details on OSR performance.
146+ if self .count % 2 == 0 :
147+ Clock .schedule_once (self ._message_loop_work , 0 )
148+ else :
149+ Clock .schedule_once (self ._message_loop_work , - 1 )
150+
137151
138152 def _update_rect (self , * kwargs ):
139153 '''Get called whenever the texture got updated.
@@ -153,7 +167,7 @@ def start_cef(self):
153167 Color (1 , 1 , 1 )
154168 self .rect = Rectangle (size = self .size , texture = self .texture )
155169
156- #configure cef
170+ # Configure CEF
157171 settings = {
158172 "debug" : True , # cefpython debug messages in console and in log_file
159173 "log_severity" : cefpython .LOGSEVERITY_INFO ,
@@ -169,26 +183,41 @@ def start_cef(self):
169183 "enabled" : False ,
170184 },
171185 }
186+ switches = {
187+ # Tweaking OSR performance by setting the same Chromium flags
188+ # as in upstream cefclient (# Issue #240).
189+ "disable-surfaces" : "" ,
190+ "disable-gpu" : "" ,
191+ "disable-gpu-compositing" : "" ,
192+ "enable-begin-frame-scheduling" : "" ,
193+ }
194+ browserSettings = {
195+ # Tweaking OSR performance (Issue #240)
196+ "windowless_frame_rate" : 60
197+ }
172198
173- #start idle
174- Clock .schedule_interval (self ._cef_mes , 0 )
175-
176- #init CEF
199+ # Initialize CEF
177200 cefpython .WindowUtils .InstallX11ErrorHandlers ()
178- cefpython .Initialize (settings )
201+
202+ global g_switches
203+ g_switches = switches
204+ cefpython .Initialize (settings , switches )
205+
206+ # Start idle - CEF message loop work.
207+ Clock .schedule_once (self ._message_loop_work , 0 )
179208
180209 # CEF needs a valid window handle passed to SetAsOffscreen()
181210 # for Printing to work. There is no API to get Kivy's window
182211 # handle so creating a dummy hidden Window using GTK.
183212 gtkwin = gtk .Window ()
184213 gtkwin .realize ()
185214
186- #WindowInfo offscreen flag
215+ # WindowInfo offscreen flag
187216 windowInfo = cefpython .WindowInfo ()
188217 windowInfo .SetAsOffscreen (gtkwin .window .xid )
189218
190- #Create Broswer and naviagte to empty page <= OnPaint won't get called yet
191- browserSettings = {}
219+ # Create Broswer and naviagte to empty page <= OnPaint won't get
220+ # called yet
192221 # The render handler callbacks are not yet set, thus an
193222 # error report will be thrown in the console (when release
194223 # DCHECKS are enabled), however don't worry, it is harmless.
@@ -201,17 +230,19 @@ def start_cef(self):
201230 # --
202231 # Do not use "about:blank" as navigateUrl - this will cause
203232 # the GoBack() and GoForward() methods to not work.
233+ #
204234 self .browser = cefpython .CreateBrowserSync (windowInfo , browserSettings ,
205235 navigateUrl = self .start_url )
206236
207- #set focus
237+ # Set focus
208238 self .browser .SendFocusEvent (True )
209239
210240 self ._client_handler = ClientHandler (self )
211241 self .browser .SetClientHandler (self ._client_handler )
212242 self .set_js_bindings ()
213243
214- #Call WasResized() => force cef to call GetViewRect() and OnPaint afterwards
244+ # Call WasResized() => force cef to call GetViewRect() and OnPaint
245+ # afterwards
215246 self .browser .WasResized ()
216247
217248 # The browserWidget instance is required in OnLoadingStateChange().
@@ -493,33 +524,53 @@ def get_windows_key_code(self, kivycode):
493524 return cefcode
494525
495526
496- def go_forward (self ):
527+ def go_forward (self , * kwargs ):
497528 '''Going to forward in browser history
498529 '''
499530 print "go forward"
500531 self .browser .GoForward ()
501532
502533
503- def go_back (self ):
534+ def go_back (self , * kwargs ):
504535 '''Going back in browser history
505536 '''
506537 print "go back"
507538 self .browser .GoBack ()
508539
509540
510- def reload (self ):
541+ def reload (self , * kwargs ):
511542 self .browser .Reload ()
512543
513544
514- def print_page (self ):
545+ def print_page (self , * kwargs ):
515546 self .browser .Print ()
516547
517548
518- def devtools (self ):
519- self .browser .ShowDevTools ()
549+ def devtools (self , * kwargs ):
550+ # ShowDevTools doesn't work with the 'enable-begin-frame-scheduling'
551+ # switch. This might be fixed by implementing native popup widgets,
552+ # see Issue #69. The solution for now is to remove that switch,
553+ # but this will impact performance.
554+ if "enable-begin-frame-scheduling" in g_switches :
555+ text = ("To enable DevTools you need to remove the\n "
556+ "'enable-begin-frame-scheduling' switch that\n "
557+ "is passed to cefpython.Initialize(). See also\n "
558+ "comment in CefBrowser.devtools()." )
559+ popup = Popup (title = 'DevTools INFO' , content = Label (text = text ),
560+ size_hint = (None , None ), size = (400 , 400 ))
561+ popup .open ()
562+
563+ else :
564+ self .browser .ShowDevTools ()
520565
521566
522567 def on_touch_down (self , touch , * kwargs ):
568+ # Mouse scrolling
569+ if "button" in touch .profile :
570+ if touch .button in ["scrollup" , "scrolldown" ]:
571+ # Handled in on_touch_up()
572+ return
573+
523574 if not self .collide_point (* touch .pos ):
524575 return
525576 touch .grab (self )
@@ -542,8 +593,32 @@ def on_touch_down(self, touch, *kwargs):
542593 cefpython .MOUSEBUTTON_LEFT ,
543594 mouseUp = False , clickCount = 1 )
544595
596+
597+ def on_touch_up (self , touch , * kwargs ):
598+ # Mouse scrolling
599+ if "button" in touch .profile :
600+ if touch .button in ["scrollup" , "scrolldown" ]:
601+ x = touch .x
602+ y = self .height - touch .pos [1 ]
603+ deltaY = - 40 if "scrollup" == touch .button else 40
604+ self .browser .SendMouseWheelEvent (x , y , deltaX = 0 , deltaY = deltaY )
605+ return
606+
607+ if touch .grab_current is not self :
608+ return
609+
610+ y = self .height - touch .pos [1 ]
611+ self .browser .SendMouseClickEvent (touch .x , y , cefpython .MOUSEBUTTON_LEFT ,
612+ mouseUp = True , clickCount = 1 )
613+ touch .ungrab (self )
614+
615+
545616 last_mouse_pos = None
546617 def on_mouse_move_emulate (self ):
618+ if not hasattr (self .get_root_window (), "mouse_pos" ):
619+ # Not all Kivy window providers have the "mouse_pos" attribute.
620+ # WindowPygame does have (kivy/kivy#325).
621+ return
547622 mouse_pos = self .get_root_window ().mouse_pos
548623 if self .last_mouse_pos == mouse_pos :
549624 return
@@ -566,15 +641,6 @@ def on_touch_move(self, touch, *kwargs):
566641 modifiers = modifiers )
567642
568643
569- def on_touch_up (self , touch , * kwargs ):
570- if touch .grab_current is not self :
571- return
572-
573- y = self .height - touch .pos [1 ]
574- self .browser .SendMouseClickEvent (touch .x , y , cefpython .MOUSEBUTTON_LEFT ,
575- mouseUp = True , clickCount = 1 )
576- touch .ungrab (self )
577-
578644
579645class ClientHandler :
580646
@@ -736,10 +802,11 @@ def OnBeforeUnloadJavascriptDialog(self, browser, messageText, isReload,
736802 return True
737803
738804
805+
739806if __name__ == '__main__' :
807+
740808 class CefBrowserApp (App ):
741809 def build (self ):
742810 return BrowserLayout ()
743811 CefBrowserApp ().run ()
744-
745812 cefpython .Shutdown ()
0 commit comments