在“很久”之前的一篇文章(tkinter使用WebView2网页组件_Smart-Space的博客-CSDN博客_tkinter webview)中,我介绍了如何在tkinter中通过pywebview创建一个与tkinter相契合的WebView2控件。但是,那一种控件,有如下缺点:
这些问题都是有读者反馈的,我也“深受其害”。
这次准备把它重改。因为思路基本变了,所以重新写一篇续篇。
最新的tkwebview2与tkinterie一样,需要在STA线程模式下创建。因为我们不再使用窗口,而网页渲染本身涉及到多线程,因此需要使用STA线程模式。
本次,我们不再直接使用pywebview提供的窗口,而是直接使用其提供的EdgeChrome
抽象类,创建基于WinFroms的WebView2组件。我已经写过了一些在tkinter中嵌入WinForms控件的文章,包括tkinterie
使用的WebBrowser。
from tkinter import Frame,Tk
import ctypes
from uuid import uuid4
import clr
from webview.window import Window
from webview.platforms.edgechromium import EdgeChrome
clr.AddReference('System.Windows.Forms')
clr.AddReference('System.Threading')
from System.Windows.Forms import Control
from System.Threading import Thread,ApartmentState,ThreadStart
user32=ctypes.windll.user32
这个基本没变:
class WebView2(Frame):
'''tkinter的WebView2绑定,基于pywebview & pythonnet'''
def __init__(self,parent,width:int,height:int,url:str='',**kw):
Frame.__init__(self,parent,width=width,height=height,**kw)
#...
首先我们需要创建一个WinFroms的Control
类,一是为了承载WebView2,二是提供一个可用的控件句柄。
其次,我们需要通过pywebview提供的Window
和EdgeChrome
类创建一个WebView2控件,并加入到Control中,再获取EdgeChrome所对应的WebView2控件。
这里的线程uid名称借用了pywebview的思路:使用一个列表
windows
,通过它判断Window
类所需要的uid名称。
control=Control()
uid = 'master' if len(windows) == 0 else 'child_' + uuid4().hex[:8]
window=Window(uid,str(id(self)), url=None, html=None, js_api=None, width=width, height=height, x=None, y=None,
resizable=True, fullscreen=False, min_size=(200, 100), hidden=False,
frameless=False, easy_drag=True,
minimized=False, on_top=False, confirm_close=False, background_color='#FFFFFF',
transparent=False, text_select=False, localization=None)
self.web_view=EdgeChrome(control,window)
self.control=control
self.web=self.web_view.web_view#EdgeChrome.web_view
windows.append(window)
接下来的操作就和以前一样了,待会直接给完整代码。
虽然是创建逻辑完全不一样,但是需要改的量很少。不过我倒是研究了pywebview的platforms.edgechromium很久,最后摸索出新的tkwebview2。同时,也希望喜欢玩tkinter的朋友也能静心研究。
class WebView2(Frame):
'''tkinter的WebView2绑定,基于pywebview & pythonnet'''
def __init__(self,parent,width:int,height:int,url:str='',**kw):
Frame.__init__(self,parent,width=width,height=height,**kw)
control=Control()
uid = 'master' if len(windows) == 0 else 'child_' + uuid4().hex[:8]
window=Window(uid,str(id(self)), url=None, html=None, js_api=None, width=width, height=height, x=None, y=None,
resizable=True, fullscreen=False, min_size=(200, 100), hidden=False,
frameless=False, easy_drag=True,
minimized=False, on_top=False, confirm_close=False, background_color='#FFFFFF',
transparent=False, text_select=False, localization=None)
self.web_view=EdgeChrome(control,window)
self.control=control
self.web=self.web_view.web_view
windows.append(window)
self.width=width
self.height=height
self.parent=parent
self.chwnd=int(str(self.control.Handle))
user32.SetParent(self.chwnd,self.winfo_id())
user32.MoveWindow(self.chwnd,0,0,width,height,True)
self.__go_bind()
def __go_bind(self):
self.bind('' ,lambda event:self.web.Dispose())
self.bind('' ,self.__resize_webview)
def __resize_webview(self,event):
user32.MoveWindow(self.chwnd,0,0,self.winfo_width(),self.winfo_height(),True)
def get_url(self):
#返回当前url,若果没有则为空
return self.web_view.get_current_url()
def evaluate_js(self,script):
#执行javascript代码,并返回最终结果
return self.web_view.evaluate_js(script)
def load_css(self,css):
#加载css
self.web_view.load_css(css)
def load_html(self,content,base_uri=None):
#加载HTML代码
#content=HTML内容
#base_uri=基本URL,默认为启动程序的目录
self.web_view.load_html(content,base_uri)
def load_url(self,url):
#加载全新的URL
self.web_view.load_url(url)
def none(self):
pass
关于WebView2的更多WinFroms操作,见微软提供的说明文档。
def main():
if not have_runtime():#没有webview2 runtime
install_runtime()
root=Tk()
root.title('pywebview for tkinter test')
root.geometry('1200x600+5+5')
frame=WebView2(root,500,500)
frame.pack(side='left')
frame.load_html('hi hi
')
frame2=WebView2(root,500,500)
frame2.pack(side='left',padx=20,fill='both',expand=True)
frame2.load_url('https://smart-space.com.cn/')
root.mainloop()
if __name__ == "__main__":
t = Thread(ThreadStart(main))
t.ApartmentState = ApartmentState.STA
t.Start()
t.Join()
self.core
,即WebView2.Core。毕竟玩tkinter只是业余爱好,就像玩游戏一样。所以还是让真正需要tkwebview2的人来继续开发和完善。
修复了一个导入错误,导致tkwebview2使用报错。
evaluate_js(js,uid='master',callback=None)
。使用callback绑定回调函数。忽略uid。
文字可选、菜单、开发者工具启用。
修复因为pywebview新版添加新参数导致的初始化错误。
由B16f00t反馈。
evaluate_js(js,callback=None)
。
GitHub - tkwebview2
静下心,放松~~。
一个新的tkwebview2不就出来了吗?(时间抚平了故事的曲折)
关于WebView2自身的问题,见这篇文章解释。
☀tkinter创新☀