#!/usr/bin/python3 import gi, json, os gi.require_version("Gtk", "3.0") gi.require_version("WebKit2", "4.1") gi.require_version("Keybinder", "3.0") from gi.repository import Gtk, Gdk, WebKit2, GLib, Keybinder from gi.repository.GdkPixbuf import Pixbuf from notes.utils import * # 优先尝试使用指示器, 没有再使用 Gtk.StatusIcon try: gi.require_version('AyatanaAppIndicator3', '0.1') # 需要安装这个包 gir1.2-ayatanaappindicator3-0.1 from gi.repository import AyatanaAppIndicator3 as AppIndicator3 except (ValueError, ImportError): AppIndicator3 = None # 初始化 Keybinder Keybinder.init() class WebKitWindow(Gtk.Window): def __init__(self): Gtk.Window.__init__(self, title="WebKit Example") self.set_default_size(800, 600) settings = WebKit2.Settings() settings.set_enable_page_cache(True) settings.set_enable_offline_web_application_cache(True) settings.set_enable_developer_extras(True) settings.set_enable_html5_database(True) settings.set_enable_html5_local_storage(True) settings.set_javascript_can_access_clipboard(True) settings.set_javascript_can_open_windows_automatically(True) settings.set_user_agent("Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/605.1.15 (KHTML, like Gecko) weapp/1.0.0 Version/16.4 Safari/605.1.15") manager = WebKit2.UserContentManager() script = open(self.file_path('./inject.js'), 'r').read() frame = WebKit2.UserContentInjectedFrames.ALL_FRAMES time = WebKit2.UserScriptInjectionTime.END script = WebKit2.UserScript(script, frame, time, None, None) manager.add_script(script) manager.connect('script-message-received::app', self.on_script_message) manager.register_script_message_handler('app') self.webview = WebKit2.WebView.new_with_user_content_manager(manager) self.webview.set_settings(settings) self.webview.load_uri("http://127.0.0.1:10086/index.html") # self.webview.load_uri("https://benchmark.wkit.fun") self.clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD) self.add(self.webview) def create_tray(self): if AppIndicator3 : indicator = AppIndicator3.Indicator.new( "youtube", "youtube", AppIndicator3.IndicatorCategory.APPLICATION_STATUS ) # indicator.set_title('alacritty 6666') # indicator.set_label('alacritty 8888', '') # indicator.set_icon_full('alacritty','alacritty') # indicator.set_attention_icon_full('alacritty', 'alacritty') # indicator.set_ordering_index(99) indicator.set_status(AppIndicator3.IndicatorStatus.ACTIVE) # indicator.set_status(AppIndicator3.IndicatorStatus.ATTENTION) else: # windows 和 macos 必须传二进制图标, linux可传图标名称(自会去主题中找) indicator = Gtk.StatusIcon.new_from_pixbuf(get_logo(32)) # linux indicator = Gtk.StatusIcon.new_from_icon_name('youtube') return indicator # indicator = Gtk.StatusIcon.new_from_icon_name('youtube') # indicator.connect('activate', self.toggle_visible) # return indicator def toggle_visible(self, icon): # trayIcon print(icon) if self.is_active(): self.hide() else: self.present() def call_js(self, method, data = None): scripts = 'native.$emit("' + method + '",' + json.dumps(data) + ')' self.webview.evaluate_javascript(scripts, -1) def file_path(self, filepath): root = os.path.dirname(os.path.realpath(__file__)) return os.path.join(root, filepath) def on_script_message(self, webview, message): data = json.loads(message.get_js_value().to_json(0)) event = data.get('event') callback = data.get('callback') params = data.get('data') match event: case 'fs': pass case 'clipboard': output = None # 读文本 if params['action'] == 'wait_for_text': output = self.clipboard.wait_for_text() # 写文本 elif params['action'] == 'set_text': self.clipboard.set_text(params['value'], -1) # 写图片 elif params['action'] == 'set_image': image = params['value'] # 前端传进来的值, 如果是路径的话, 直接读取 if type(image) == str: image = Pixbuf.new_from_file(image) else: image = dict_to_pixbuf(image) self.clipboard.set_image(image) self.clipboard.store() # 读图片 elif params['action'] == 'wait_for_image': output = self.clipboard.wait_for_image() output = pixbuf_to_dict(output, 'noname.png') # 清除剪切板 elif params['action'] == 'clear': self.clipboard.clear() # 回调给前端 if callback: self.call_js(callback, output) # 退出app case 'quit': all_quit(self) # 读取图片, 返回图片像素数据 case 'image': filename = params['value'] pixbuf = Pixbuf.new_from_file(filename) image = pixbuf_to_dict(pixbuf, filename) self.call_js(callback, image) case 'monitor': if params['action'] == 'get-all': display = Gdk.Display.get_default() monitor_num = display.get_n_monitors() monitors = [display.get_monitor(i) for i in range(monitor_num)] monitors = [get_monitor_info(m) for m in monitors] self.call_js(callback, monitors) elif params['action'] == 'get-primary': display = Gdk.Display.get_default() monitor = display.get_primary_monitor() info = get_monitor_info(monitor) self.call_js(callback, info) case 'keybinder': output = None keymap = params.get('value') shortcut_callback = params.get('shortcut_callback') or '' if params['action'] == 'register': # 绑定之前, 先解绑, 避免被重复绑定 Keybinder.unbind(keymap) output = Keybinder.bind( keymap, lambda km : self.call_js(shortcut_callback) ) elif params['action'] == 'unregister': Keybinder.unbind(keymap) output = True elif params['action'] == 'supported': output = Keybinder.supported() # 有回调则返回结果 if callback: self.call_js(callback, output) case 'tray': if params['action'] == 'create': pass elif params['action'] == 'remove': pass if callback : self.call_js(callback, True) case _: if callback : res = {"foo": 123, "bar": (11,22,33)} self.call_js(callback, res) def all_quit(win): print('朕要休息了~~~') Gtk.main_quit() win = WebKitWindow() win.connect("destroy", all_quit) win.show_all() tray = win.create_tray() Gtk.main()