I'm making an application with tkinter in python 3.7.8.
I have to show a map so I use Folium to generate it and cefpython3 to show it (as it generates as a html with javascript)
It works wonderful on Linux, but when I try to test it on Windows 10, the map is shown, i can click on buttons, but cant interact with any tkinter.Entry
unless i click on another window, and then come back to the window program.
I assumed it was a focus problem, but even trying to set the focus of the cefpython3 browser to False, the problem keep happening.
This is the MapVisualizer Code
class MapVisualizer(tk.Frame):
""" Generates the map with folium and then attach cefpython3 browser to the frame """
def __init__(self, root, size, mapPoints, zoom, pointDatas=None, **kargs):
self.browser_frame = None
self.navigation_bar = None
self.replaceFolium()
m = folium.Map(location=mapPoints, zoom_start=zoom, height=size[0], width=size[1], min_zoom=3, max_zoom=12)
for data_point in pointDatas:
folium.Marker(data_point[0], popup=data_point[1]).add_to(m)
m.save(resource_path("data/map_location.html"))
# Root
tk.Grid.rowconfigure(root, 0, weight=1)
tk.Grid.columnconfigure(root, 0, weight=1)
# MainFrame
tk.Frame.__init__(self, root)
# BrowserFrame
tk.Grid.rowconfigure(self, 1, weight=1)
tk.Grid.columnconfigure(self, 0, weight=1)
# Pack MainFrame
self.browser_frame = BrowserFrame(self, self.navigation_bar)
self.browser_frame.grid(row=1, column=0,
sticky=(tk.N + tk.S + tk.E + tk.W))
def on_root_configure(self, _):
if self.browser_frame:
self.browser_frame.on_root_configure()
def on_configure(self, event):
if self.browser_frame:
width = event.width
height = event.height
if self.navigation_bar:
height = height - self.navigation_bar.winfo_height()
self.browser_frame.on_mainframe_configure(width, height)
def on_focus_in(self, _):
self.browser.SetFocus(False)
def on_focus_out(self, _):
self.browser.SetFocus(False)
def on_close(self):
if self.browser_frame:
self.browser_frame.on_root_close()
self.master.destroy()
def get_browser(self):
if self.browser_frame:
return self.browser_frame.browser
return None
def get_browser_frame(self):
if self.browser_frame:
return self.browser_frame
return None
def onClose(self):
cef.Shutdown()
This is the Browser code
class BrowserFrame(tk.Frame):
def __init__(self, master, navigation_bar=None):
self.navigation_bar = navigation_bar
cef.Initialize()
self.closing = False
self.browser = None
tk.Frame.__init__(self, master)
self.bind("<FocusIn>", self.on_focus_in)
self.bind("<FocusOut>", self.on_focus_out)
self.bind("<Configure>", self.on_configure)
self.focus_set()
def embed_browser(self):
window_info = cef.WindowInfo()
rect = [0, 0, self.winfo_width(), self.winfo_height()]
window_info.SetAsChild(self.get_window_handle(), rect)
self.browser = cef.CreateBrowserSync(window_info,
url=f"file:///{os.getcwd()}/data/map_location.html") #todo
assert self.browser
self.browser.SetClientHandler(LoadHandler(self))
self.browser.SetClientHandler(FocusHandler(self))
self.message_loop_work()
def get_window_handle(self):
if self.winfo_id() > 0:
return self.winfo_id()
elif MAC:
from AppKit import NSApp
import objc
return objc.pyobjc_id(NSApp.windows()[-1].contentView())
else:
raise Exception("Couldn't obtain window handle")
def message_loop_work(self):
cef.MessageLoopWork()
self.after(10, self.message_loop_work)
def on_configure(self, _):
if not self.browser:
self.embed_browser()
def on_root_configure(self):
# Root <Configure> event will be called when top window is moved
if self.browser:
self.browser.NotifyMoveOrResizeStarted()
def on_mainframe_configure(self, width, height):
if self.browser:
if WINDOWS:
ctypes.windll.user32.SetWindowPos(
self.browser.GetWindowHandle(), 0,
0, 0, width, height, 0x0002)
elif LINUX:
self.browser.SetBounds(0, 0, width, height)
self.browser.NotifyMoveOrResizeStarted()
def on_focus_in(self, _):
if self.browser:
self.browser.SetFocus(True)
def on_focus_out(self, _):
if self.browser:
self.browser.SetFocus(False)
def on_root_close(self):
if self.browser:
self.browser.CloseBrowser(True)
self.clear_browser_references()
self.destroy()
def clear_browser_references(self):
self.browser = None
class LoadHandler(object):
def __init__(self, browser_frame):
self.browser_frame = browser_frame
def OnLoadStart(self, browser, **_):
if self.browser_frame.master.navigation_bar:
self.browser_frame.master.navigation_bar.set_url(browser.GetUrl())
class FocusHandler(object):
def __init__(self, browser_frame):
self.browser_frame = browser_frame
def OnTakeFocus(self, next_component, **_):
self.browser_frame.browser.SetFocus(False)
def OnSetFocus(self, source, **_):
self.browser_frame.browser.SetFocus(False)
return False
def OnGotFocus(self, **_):
self.browser_frame.browser.SetFocus(False)
There is no error or debug message, and as I said, this problem only happens on Windows (same env directory and everything).
I had to write a small crutch that opens a transparent window for a millisecond, programmatically the cefpython browser focus is reset and it becomes possible to install it on the tk.entry widget.