0

我正在 python 3.7.8 中使用tkinter制作应用程序。

我必须显示一张地图,所以我使用Folium生成它并使用cefpython3显示它(因为它使用 javascript 生成为 html)

它在 Linux 上运行良好,但是当我尝试在 Windows 10 上对其进行测试时,显示地图,我可以单击按钮,但除非我单击另一个窗口,否则无法与任何按钮交互,然后返回窗口程序。tkinter.Entry

我认为这是一个焦点问题,但即使尝试将 cefpython3 浏览器的焦点设置为 False,问题仍然存在。

这是 MapVisualizer 代码

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()

这是浏览器代码

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)

没有错误或调试消息,正如我所说,这个问题只发生在 Windows 上(相同的env目录和所有内容)。

4

1 回答 1

0

我必须编写一个小拐杖,打开一个透明窗口一毫秒,以编程方式重置 cefpython 浏览器焦点,并且可以将其安装在 tk.entry 小部件上。

entrynum = ttk.Entry(self.f_top)
entrynum.pack(side=tk.LEFT)
entrynum.bind('<Button-1>', lambda e, f=entrynum: b1(e, f))
entrynum.focus_set()

def b1(e, f):
    toprs = tk.Toplevel()
    toprs.geometry(f'+{0}+{0}')
    toprs.attributes('-alpha', 0.0)
    tf = ttk.Frame(toprs)
    tf.pack()
    toprs.state('iconic')
    toprs.state('zoomed')
    toprs.attributes("-topmost", True)
    toprs.after(1, lambda: f.focus_set())
    toprs.after(2, lambda: toprs.destroy())
    toprs.mainloop()
于 2021-03-31T08:38:23.623 回答