|
| 1 | +import sys |
| 2 | + |
| 3 | +# Tkinter Mac Setting |
| 4 | +if sys.platform == 'darwin': |
| 5 | + import matplotlib |
| 6 | + matplotlib.use('TkAgg') |
| 7 | + |
| 8 | +import memory |
| 9 | + |
| 10 | +# This implementation is a modified version of the example of |
| 11 | +# embedding CEF Python browser using Tkinter toolkit. |
| 12 | +# Reference https://github.com/cztomczak/cefpython/blob/master/examples/tkinter_.py |
| 13 | +# |
| 14 | +# NOTE: This example often crashes on Mac (Python 2.7, Tk 8.5/8.6) |
| 15 | +# during initial app loading with such message: |
| 16 | +# "Segmentation fault: 11". Reported as Issue #309. |
| 17 | +# |
| 18 | +# Tested configurations: |
| 19 | +# - Tk 8.5 on Windows/Mac |
| 20 | +# - Tk 8.6 on Linux |
| 21 | +# - CEF Python v55.3+ |
| 22 | +# |
| 23 | +# Known issue on Linux: When typing url, mouse must be over url |
| 24 | +# entry widget otherwise keyboard focus is lost (Issue #255 |
| 25 | +# and Issue #284). |
| 26 | + |
| 27 | +from cefpython3 import cefpython as cef |
| 28 | +import ctypes |
| 29 | +try: |
| 30 | + # for Python2 |
| 31 | + from Tkinter import * |
| 32 | + #from Tkinter import Tkversion |
| 33 | + import Tkinter as tk |
| 34 | + import ttk |
| 35 | + import Tkconstants |
| 36 | +except ImportError: |
| 37 | + # for Python3 |
| 38 | + from tkinter import * |
| 39 | + import tkinter as tk |
| 40 | + from tkinter import ttk |
| 41 | +import sys |
| 42 | +import os |
| 43 | +import platform |
| 44 | +import logging as _logging |
| 45 | + |
| 46 | +# Platforms |
| 47 | +WINDOWS = (platform.system() == "Windows") |
| 48 | +LINUX = (platform.system() == "Linux") |
| 49 | +MAC = (platform.system() == "Darwin") |
| 50 | + |
| 51 | +# Globals |
| 52 | +logger = _logging.getLogger("tkinter_.py") |
| 53 | + |
| 54 | +# Constants |
| 55 | +# Tk 8.5 doesn't support png images |
| 56 | +#IMAGE_EXT = ".png" if TkVersion > 8.5 else ".gif" |
| 57 | + |
| 58 | +interactive_map = "" |
| 59 | +browser_frame = "" |
| 60 | +FourthFrame = "" |
| 61 | +def gimmick_initialize(window, map): |
| 62 | + global browser_frame, FourthFrame |
| 63 | + if not browser_frame and not FourthFrame: |
| 64 | + global interactive_map |
| 65 | + interactive_map = map |
| 66 | + logger.setLevel(_logging.INFO) |
| 67 | + stream_handler = _logging.StreamHandler() |
| 68 | + formatter = _logging.Formatter("[%(filename)s] %(message)s") |
| 69 | + stream_handler.setFormatter(formatter) |
| 70 | + logger.addHandler(stream_handler) |
| 71 | + logger.info("CEF Python {ver}".format(ver=cef.__version__)) |
| 72 | + logger.info("Python {ver} {arch}".format( |
| 73 | + ver=platform.python_version(), arch=platform.architecture()[0])) |
| 74 | + logger.info("Tk {ver}".format(ver=tk.Tcl().eval('info patchlevel'))) |
| 75 | + assert cef.__version__ >= "55.3", "CEF Python v55.3+ required to run this" |
| 76 | + sys.excepthook = cef.ExceptHook # To shutdown all CEF processes on error |
| 77 | + |
| 78 | + FourthFrame = ttk.Frame(window, width=500, height=500, padding="10 10 10 10",relief= GROOVE) |
| 79 | + FourthFrame.grid(column=50, row=10, sticky=(N, W, E, S), columnspan=200, rowspan=200, padx=5, pady=5) |
| 80 | + |
| 81 | + browser_frame = BrowserFrame(FourthFrame) |
| 82 | + browser_frame.grid(row=0, column=0,sticky=(N, W, E, S),columnspan=100, rowspan=100, padx=5, pady=5) |
| 83 | + |
| 84 | + FourthFrame.columnconfigure(50, weight=1) |
| 85 | + FourthFrame.rowconfigure(10, weight=1) |
| 86 | + browser_frame.columnconfigure(0, weight=1) |
| 87 | + browser_frame.rowconfigure(0, weight=1) |
| 88 | + |
| 89 | + window.update() |
| 90 | + else: |
| 91 | + if FourthFrame: |
| 92 | + FourthFrame.grid_forget() |
| 93 | + FourthFrame, browser_frame = "", "" |
| 94 | + window.update() |
| 95 | + |
| 96 | +def show_frame(self, cont): |
| 97 | + frame = self.frames[cont] |
| 98 | + frame.tkraise() |
| 99 | + |
| 100 | +class BrowserFrame(tk.Frame): |
| 101 | + |
| 102 | + def __init__(self, master): |
| 103 | + self.closing = False |
| 104 | + self.browser = None |
| 105 | + # Python2 has a ttk frame error of using self as argument so use tk |
| 106 | + #ttk.Frame.__init__(self, master, width=500, height=400, padding="10 10 10 10", relief=GROOVE) |
| 107 | + tk.Frame.__init__(self, master, width=500, height=400) |
| 108 | + self.bind("<FocusIn>", self.on_focus_in) |
| 109 | + self.bind("<FocusOut>", self.on_focus_out) |
| 110 | + self.bind("<Configure>", self.on_configure) |
| 111 | + self.focus_set() |
| 112 | + |
| 113 | + def embed_browser(self): |
| 114 | + window_info = cef.WindowInfo() |
| 115 | + rect = [0, 0, self.winfo_width(), self.winfo_height()] |
| 116 | + window_info.SetAsChild(self.get_window_handle(), rect) |
| 117 | + self.browser = cef.CreateBrowserSync(window_info, url=interactive_map) |
| 118 | + assert self.browser |
| 119 | + self.browser.SetClientHandler(LoadHandler(self)) |
| 120 | + self.browser.SetClientHandler(FocusHandler(self)) |
| 121 | + self.message_loop_work() |
| 122 | + |
| 123 | + def get_window_handle(self): |
| 124 | + if self.winfo_id() > 0 and not MAC: |
| 125 | + return self.winfo_id() |
| 126 | + elif MAC: |
| 127 | + raise Exception("Couldn't obtain window handle") |
| 128 | + # CEF crashes in mac so temp disable |
| 129 | + # * CreateBrowserSync calling window handle crashes with segmentation fault 11 |
| 130 | + # * https://github.com/cztomczak/cefpython/issues/309 |
| 131 | + # On Mac window id is an invalid negative value (Issue #308). |
| 132 | + # This is kind of a dirty hack to get window handle using |
| 133 | + # PyObjC package. If you change structure of windows then you |
| 134 | + # need to do modifications here as well. |
| 135 | + # noinspection PyUnresolvedReferences |
| 136 | + """ |
| 137 | + try: |
| 138 | + from AppKit import NSApp |
| 139 | + # noinspection PyUnresolvedReferences |
| 140 | + import objc |
| 141 | + # Sometimes there is more than one window, when application |
| 142 | + # didn't close cleanly last time Python displays an NSAlert |
| 143 | + # window asking whether to Reopen that window. |
| 144 | + # noinspection PyUnresolvedReferences |
| 145 | + return objc.pyobjc_id(NSApp.windows()[-1].contentView()) |
| 146 | + except: |
| 147 | + raise Exception("Couldn't obtain window handle") |
| 148 | + """ |
| 149 | + else: |
| 150 | + raise Exception("Couldn't obtain window handle") |
| 151 | + |
| 152 | + def message_loop_work(self): |
| 153 | + cef.MessageLoopWork() |
| 154 | + self.after(10, self.message_loop_work) |
| 155 | + |
| 156 | + def on_configure(self, _): |
| 157 | + if not self.browser: |
| 158 | + self.embed_browser() |
| 159 | + |
| 160 | + def on_root_configure(self): |
| 161 | + # Root <Configure> event will be called when top window is moved |
| 162 | + if self.browser: |
| 163 | + self.browser.NotifyMoveOrResizeStarted() |
| 164 | + |
| 165 | + def on_mainframe_configure(self, width, height): |
| 166 | + if self.browser: |
| 167 | + if WINDOWS: |
| 168 | + ctypes.windll.user32.SetWindowPos( |
| 169 | + self.browser.GetWindowHandle(), 0, |
| 170 | + 0, 0, width, height, 0x0002) |
| 171 | + elif LINUX: |
| 172 | + self.browser.SetBounds(0, 0, width, height) |
| 173 | + self.browser.NotifyMoveOrResizeStarted() |
| 174 | + |
| 175 | + def on_focus_in(self, _): |
| 176 | + logger.debug("BrowserFrame.on_focus_in") |
| 177 | + if self.browser: |
| 178 | + self.browser.SetFocus(True) |
| 179 | + |
| 180 | + def on_focus_out(self, _): |
| 181 | + logger.debug("BrowserFrame.on_focus_out") |
| 182 | + if self.browser: |
| 183 | + self.browser.SetFocus(False) |
| 184 | + |
| 185 | + def on_root_close(self): |
| 186 | + if self.browser: |
| 187 | + self.browser.CloseBrowser(True) |
| 188 | + self.clear_browser_references() |
| 189 | + self.destroy() |
| 190 | + |
| 191 | + def clear_browser_references(self): |
| 192 | + # Clear browser references that you keep anywhere in your |
| 193 | + # code. All references must be cleared for CEF to shutdown cleanly. |
| 194 | + self.browser = None |
| 195 | + |
| 196 | + |
| 197 | +class LoadHandler(object): |
| 198 | + |
| 199 | + def __init__(self, browser_frame): |
| 200 | + self.browser_frame = browser_frame |
| 201 | + |
| 202 | +class FocusHandler(object): |
| 203 | + |
| 204 | + def __init__(self, browser_frame): |
| 205 | + self.browser_frame = browser_frame |
| 206 | + |
| 207 | + def OnTakeFocus(self, next_component, **_): |
| 208 | + logger.debug("FocusHandler.OnTakeFocus, next={next}" |
| 209 | + .format(next=next_component)) |
| 210 | + |
| 211 | + def OnSetFocus(self, source, **_): |
| 212 | + logger.debug("FocusHandler.OnSetFocus, source={source}" |
| 213 | + .format(source=source)) |
| 214 | + return False |
| 215 | + |
| 216 | + def OnGotFocus(self, **_): |
| 217 | + """Fix CEF focus issues (#255). Call browser frame's focus_set |
| 218 | + to get rid of type cursor in url entry widget.""" |
| 219 | + logger.debug("FocusHandler.OnGotFocus") |
| 220 | + self.browser_frame.focus_set() |
| 221 | + |
| 222 | + |
| 223 | + |
0 commit comments