增加剪切板操作和图片操作的笔记
parent
af29a63061
commit
a57caef220
46
app.js
46
app.js
|
@ -7,22 +7,42 @@
|
|||
import 'es.shim'
|
||||
import { $, bind } from 'wkit'
|
||||
|
||||
function test1() {
|
||||
try {
|
||||
window.webkit.messageHandlers.app.postMessage({
|
||||
data: { foo: 'Test 1' }
|
||||
})
|
||||
$('#output').innerHTML = '这是没有回调的 '
|
||||
} catch (err) {
|
||||
alert(err)
|
||||
}
|
||||
async function test1() {
|
||||
let txt = await native.clipboard.readText()
|
||||
$('input').value = txt
|
||||
}
|
||||
|
||||
function test2() {
|
||||
native.handler('blabla', { foo: 'bar' }).then(r => {
|
||||
$('#output').innerHTML = JSON.stringify(r)
|
||||
})
|
||||
async function test2() {
|
||||
native.clipboard.writeText('这是一段写进剪切板的文本')
|
||||
// native.handler('blabla', { foo: 'bar' }).then(r => {
|
||||
// $('#output').innerHTML = JSON.stringify(r)
|
||||
// })
|
||||
}
|
||||
|
||||
bind($('.btn1'), 'click', test1)
|
||||
bind($('.btn2'), 'click', test2)
|
||||
bind($('.btn3'), 'click', async function () {
|
||||
// window.open('about:blank')
|
||||
// let img = await native.clipboard.writeImage('/code/gtk/webkit/debian.png')
|
||||
let img = await native.image('/code/gtk/webkit/debian.png')
|
||||
native.clipboard.writeImage(img)
|
||||
// native.clipboard.writeImage('/code/gtk/webkit/debian.png')
|
||||
try {
|
||||
$('img').src = URL.createObjectURL(await img.export())
|
||||
} catch (err) {
|
||||
alert(err)
|
||||
}
|
||||
})
|
||||
bind($('.btn4'), 'click', async function () {
|
||||
native.quit()
|
||||
// native.clipboard.clear()
|
||||
})
|
||||
|
||||
bind($('textarea'), 'paste', async function (ev) {
|
||||
let items = ev.clipboardData.items
|
||||
for (let it of items) {
|
||||
let file = it.getAsFile()
|
||||
$('img').src = URL.createObjectURL(file)
|
||||
break
|
||||
}
|
||||
})
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 3.2 KiB |
|
@ -0,0 +1,5 @@
|
|||
import fs from 'iofs'
|
||||
|
||||
let buf = fs.cat('./debian.png')
|
||||
console.log(buf)
|
||||
console.log([...buf], buf.byteLength)
|
11
index.html
11
index.html
|
@ -16,6 +16,9 @@
|
|||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
#output {max-width:100%;white-space: pre-wrap;word-break: break-all;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
@ -26,6 +29,14 @@
|
|||
|
||||
<div id="output">loading...</div>
|
||||
|
||||
<input type="text">
|
||||
|
||||
<hr>
|
||||
<button class="btn3">写图片到剪切板</button>
|
||||
<button class="btn4">退出</button>
|
||||
<img style="width:100px;border:1px solid #09f;" src="" alt="">
|
||||
<textarea></textarea>
|
||||
|
||||
<script type="module" src="/app.js"></script>
|
||||
|
||||
</body>
|
||||
|
|
107
inject.js
107
inject.js
|
@ -3,6 +3,40 @@
|
|||
* @author yutent<yutent.io@gmail.com>
|
||||
* @date 2023/07/21 17:38:11
|
||||
*/
|
||||
const MIME_TYPES = {
|
||||
html: 'text/html',
|
||||
json: 'application/json',
|
||||
js: 'application/javascript',
|
||||
htm: 'text/html',
|
||||
txt: 'text/plain',
|
||||
css: 'text/css',
|
||||
webp: 'image/webp',
|
||||
jpg: 'image/jpg',
|
||||
jpeg: 'image/jpeg',
|
||||
png: 'image/png',
|
||||
gif: 'image/gif',
|
||||
svg: 'image/svg+xml',
|
||||
ico: 'image/ico',
|
||||
mp3: 'audio/mpeg',
|
||||
ogg: 'audio/ogg',
|
||||
m4a: 'audio/m4a',
|
||||
amr: 'audio/amr',
|
||||
mp4: 'video/mp4',
|
||||
webm: 'video/webm',
|
||||
wasm: 'application/wasm',
|
||||
asm: 'application/asm',
|
||||
zip: 'application/zip',
|
||||
'7z': 'application/x-7z-compressed',
|
||||
eot: 'application/vnd.ms-fontobject',
|
||||
ttf: 'font/ttf',
|
||||
otf: 'font/otf',
|
||||
woff: 'font/woff',
|
||||
woff2: 'font/woff2',
|
||||
xls: 'application/vnd.ms-excel',
|
||||
doc: 'application/msword',
|
||||
xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||
docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
|
||||
}
|
||||
|
||||
function defer() {
|
||||
let obj = {}
|
||||
|
@ -19,8 +53,14 @@ function rand(prefix = 'cb_') {
|
|||
|
||||
function handler(event, data = {}, once = true) {
|
||||
let _ = defer()
|
||||
let callback = rand()
|
||||
native[once ? '$once' : '$on'](callback, _.resolve)
|
||||
let callback
|
||||
|
||||
if (typeof once === 'boolean') {
|
||||
callback = rand()
|
||||
native[once ? '$once' : '$on'](callback, _.resolve)
|
||||
} else {
|
||||
_.resolve(true)
|
||||
}
|
||||
window.webkit.messageHandlers.app.postMessage({
|
||||
event,
|
||||
data,
|
||||
|
@ -29,6 +69,43 @@ function handler(event, data = {}, once = true) {
|
|||
return _.promise
|
||||
}
|
||||
|
||||
class NativeImage {
|
||||
#origin
|
||||
|
||||
constructor(obj) {
|
||||
this.#origin = obj
|
||||
this.width = obj.width
|
||||
this.height = obj.height
|
||||
this.type = MIME_TYPES[obj.filepath.split('.').pop()]
|
||||
}
|
||||
|
||||
toPixbuf() {
|
||||
return this.#origin
|
||||
}
|
||||
|
||||
export(type) {
|
||||
let _ = defer()
|
||||
let canvas = document.createElement('canvas')
|
||||
canvas.width = this.width
|
||||
canvas.height = this.height
|
||||
let ctx = canvas.getContext('2d')
|
||||
let imgData = ctx.getImageData(0, 0, this.width, this.height)
|
||||
let data = imgData.data
|
||||
|
||||
for (let i = 0; i < this.#origin.bytes.length; i += 4) {
|
||||
imgData.data[i] = this.#origin.bytes[i]
|
||||
imgData.data[i + 1] = this.#origin.bytes[i + 1]
|
||||
imgData.data[i + 2] = this.#origin.bytes[i + 2]
|
||||
imgData.data[i + 3] = this.#origin.bytes[i + 3]
|
||||
}
|
||||
|
||||
ctx.putImageData(imgData, 0, 0)
|
||||
canvas.toBlob(_.resolve, type || this.type, 1)
|
||||
|
||||
return _.promise
|
||||
}
|
||||
}
|
||||
|
||||
class EventEmitter {
|
||||
//
|
||||
__events__ = Object.create(null)
|
||||
|
@ -79,8 +156,34 @@ class EventEmitter {
|
|||
window.native = new EventEmitter()
|
||||
|
||||
Object.assign(native, {
|
||||
quit() {
|
||||
return handler('quit')
|
||||
},
|
||||
fs: {
|
||||
read(filepath) {}
|
||||
},
|
||||
image(filepath) {
|
||||
return handler('image', { value: filepath }).then(r => new NativeImage(r))
|
||||
},
|
||||
clipboard: {
|
||||
readText() {
|
||||
return handler('clipboard', { action: 'wait_for_text' })
|
||||
},
|
||||
writeText(value) {
|
||||
return handler('clipboard', { action: 'set_text', value }, null)
|
||||
},
|
||||
readImage() {
|
||||
return handler('clipboard', { action: 'wait_for_image' })
|
||||
},
|
||||
writeImage(value) {
|
||||
if (typeof value === 'object') {
|
||||
value = value.toPixbuf()
|
||||
}
|
||||
return handler('clipboard', { action: 'set_image', value }, null)
|
||||
},
|
||||
clear() {
|
||||
return handler('clipboard', { action: 'clear' })
|
||||
}
|
||||
},
|
||||
handler
|
||||
})
|
||||
|
|
87
main.py
87
main.py
|
@ -2,10 +2,12 @@
|
|||
|
||||
import gi, json, os
|
||||
|
||||
|
||||
gi.require_version("Gtk", "3.0")
|
||||
gi.require_version("WebKit2", "4.1")
|
||||
|
||||
from gi.repository import Gtk, WebKit2
|
||||
from gi.repository import Gtk, Gdk, WebKit2, GLib
|
||||
from gi.repository.GdkPixbuf import Pixbuf
|
||||
|
||||
|
||||
class WebKitWindow(Gtk.Window):
|
||||
|
@ -46,6 +48,9 @@ class WebKitWindow(Gtk.Window):
|
|||
# self.webview.load_uri("https://benchmark.wkit.fun")
|
||||
|
||||
|
||||
self.clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
|
||||
|
||||
|
||||
|
||||
|
||||
self.add(self.webview)
|
||||
|
@ -54,7 +59,6 @@ class WebKitWindow(Gtk.Window):
|
|||
|
||||
def create_tray(self):
|
||||
indicator = Gtk.StatusIcon.new_from_icon_name('youtube')
|
||||
|
||||
indicator.connect('activate', self.toggle_visible)
|
||||
return indicator
|
||||
|
||||
|
@ -70,25 +74,94 @@ class WebKitWindow(Gtk.Window):
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
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 = message.get_js_value()
|
||||
data = json.loads(data.to_json(0))
|
||||
print('这是py收到的值: ',data)
|
||||
|
||||
data = json.loads(message.get_js_value().to_json(0))
|
||||
|
||||
event = data.get('event')
|
||||
callback = data.get('callback')
|
||||
res = {"foo": 123, "bar": (11,22,33)}
|
||||
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 = Pixbuf.new_from_data(
|
||||
data = bytes(image['bytes']),
|
||||
colorspace = image['colorspace'],
|
||||
has_alpha = image['has_alpha'],
|
||||
bits_per_sample = image['bits_per_sample'],
|
||||
width = image['width'],
|
||||
height = image['height'],
|
||||
rowstride = image['rowstride']
|
||||
)
|
||||
|
||||
self.clipboard.set_image(image)
|
||||
self.clipboard.store()
|
||||
|
||||
# 清除剪切板
|
||||
elif params['action'] == 'clear':
|
||||
self.clipboard.clear()
|
||||
|
||||
# 回调给前端
|
||||
if callback and output:
|
||||
scripts = 'native.$emit("' + callback + '",' + json.dumps(output) + ')'
|
||||
print(scripts)
|
||||
self.webview.evaluate_javascript(scripts, -1)
|
||||
|
||||
# 退出app
|
||||
case 'quit':
|
||||
Gtk.main_quit()
|
||||
|
||||
# 读取图片, 返回图片像素数据
|
||||
case 'image':
|
||||
filename = params['value']
|
||||
pixbuf = Pixbuf.new_from_file(filename)
|
||||
image = {
|
||||
"width": pixbuf.get_width(),
|
||||
"height": pixbuf.get_height(),
|
||||
"colorspace": pixbuf.get_colorspace(),
|
||||
"has_alpha": pixbuf. get_has_alpha(),
|
||||
"bits_per_sample": pixbuf.get_bits_per_sample(),
|
||||
"rowstride": pixbuf.get_rowstride(),
|
||||
"filepath": filename,
|
||||
"bytes": list(pixbuf.get_pixels())
|
||||
}
|
||||
|
||||
|
||||
scripts = 'native.$emit("' + callback + '",' + json.dumps(image) + ')'
|
||||
|
||||
self.webview.evaluate_javascript(scripts, -1)
|
||||
|
||||
case _:
|
||||
if callback :
|
||||
res = {"foo": 123, "bar": (11,22,33)}
|
||||
scripts = 'native.$emit("' + callback + '",' + json.dumps(res) + ')'
|
||||
print(scripts)
|
||||
self.webview.evaluate_javascript(scripts, -1)
|
||||
|
@ -115,6 +188,4 @@ win.show_all()
|
|||
|
||||
tray = win.create_tray()
|
||||
|
||||
print(tray)
|
||||
|
||||
Gtk.main()
|
|
@ -0,0 +1,41 @@
|
|||
|
||||
import gi
|
||||
|
||||
gi.require_version("Gtk", "3.0")
|
||||
|
||||
from gi.repository import Gtk, Gdk
|
||||
from gi.repository.GdkPixbuf import Pixbuf
|
||||
|
||||
clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
|
||||
|
||||
|
||||
# 读剪切板中的文本
|
||||
output = clipboard.wait_for_text()
|
||||
|
||||
# 写文本进剪切板
|
||||
clipboard.set_text('blabla', -1)
|
||||
|
||||
|
||||
|
||||
# 从文件中读取
|
||||
filepath = 'xxx.png'
|
||||
image = Pixbuf.new_from_file(filepath)
|
||||
|
||||
# 从其他地方拿到的图片像素对象
|
||||
image = Pixbuf.new_from_data(
|
||||
data = bytes(image['bytes']),
|
||||
colorspace = image['colorspace'],
|
||||
has_alpha = image['has_alpha'],
|
||||
bits_per_sample = image['bits_per_sample'],
|
||||
width = image['width'],
|
||||
height = image['height'],
|
||||
rowstride = image['rowstride']
|
||||
)
|
||||
|
||||
clipboard.set_image(image)
|
||||
clipboard.store()
|
||||
|
||||
|
||||
|
||||
# 清除
|
||||
clipboard.clear()
|
|
@ -0,0 +1,40 @@
|
|||
import gi
|
||||
|
||||
gi.require_version("Gtk", "3.0")
|
||||
|
||||
from gi.repository import Gtk, Gdk
|
||||
from gi.repository.GdkPixbuf import Pixbuf
|
||||
|
||||
# 读取图片, 返回图片像素数据
|
||||
filename = 'xxx.png'
|
||||
pixbuf = Pixbuf.new_from_file(filename)
|
||||
|
||||
# 得到这个对象, 就是可以传给前端的,
|
||||
|
||||
image = {
|
||||
"width": pixbuf.get_width(),
|
||||
"height": pixbuf.get_height(),
|
||||
"colorspace": pixbuf.get_colorspace(),
|
||||
"has_alpha": pixbuf. get_has_alpha(),
|
||||
"bits_per_sample": pixbuf.get_bits_per_sample(),
|
||||
"rowstride": pixbuf.get_rowstride(),
|
||||
"filepath": filename,
|
||||
"bytes": list(pixbuf.get_pixels())
|
||||
}
|
||||
|
||||
# scripts = 'native.$emit("' + callback + '",' + json.dumps(image) + ')'
|
||||
|
||||
# 倒扣到前端传回来的数据, 调用 Pixbuf.new_from_data 可转回 Pixbuf对象
|
||||
pixbuf = Pixbuf.new_from_data(
|
||||
data = bytes(image['bytes']),
|
||||
colorspace = image['colorspace'],
|
||||
has_alpha = image['has_alpha'],
|
||||
bits_per_sample = image['bits_per_sample'],
|
||||
width = image['width'],
|
||||
height = image['height'],
|
||||
rowstride = image['rowstride']
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
Reference in New Issue