完成区域截图 高亮功能测试
parent
2ba819474b
commit
59e6e16a94
|
@ -6,6 +6,9 @@ import gi, cairo
|
||||||
gi.require_version('Gtk', '3.0')
|
gi.require_version('Gtk', '3.0')
|
||||||
from gi.repository import Gtk, Gdk, GdkPixbuf
|
from gi.repository import Gtk, Gdk, GdkPixbuf
|
||||||
|
|
||||||
|
|
||||||
|
from utils import blur_image
|
||||||
|
|
||||||
# 获取默认显示器
|
# 获取默认显示器
|
||||||
dp = Gdk.Display.get_default()
|
dp = Gdk.Display.get_default()
|
||||||
|
|
||||||
|
@ -43,6 +46,8 @@ def on_mouse_move(widget, event):
|
||||||
|
|
||||||
class App(Gtk.Window):
|
class App(Gtk.Window):
|
||||||
|
|
||||||
|
saved = False
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
Gtk.Window.__init__(self, type=Gtk.WindowType.POPUP)
|
Gtk.Window.__init__(self, type=Gtk.WindowType.POPUP)
|
||||||
|
|
||||||
|
@ -54,26 +59,25 @@ class App(Gtk.Window):
|
||||||
# 设置透明背景
|
# 设置透明背景
|
||||||
screen = self.get_screen()
|
screen = self.get_screen()
|
||||||
visual = screen.get_rgba_visual()
|
visual = screen.get_rgba_visual()
|
||||||
# if visual and screen.is_composited():
|
if visual and screen.is_composited():
|
||||||
# self.set_visual(visual)
|
self.set_visual(visual)
|
||||||
|
|
||||||
# self.set_app_paintable(True)
|
self.set_app_paintable(True)
|
||||||
|
self.set_focus()
|
||||||
|
|
||||||
|
|
||||||
self.set_events(Gdk.EventMask.POINTER_MOTION_MASK)
|
# self.set_events(Gdk.EventMask.POINTER_MOTION_MASK)
|
||||||
# window.connect("button-press-event", on_mouse_move)
|
# window.connect("button-press-event", on_mouse_move)
|
||||||
# window.connect("button-release-event", on_mouse_move)
|
# window.connect("button-release-event", on_mouse_move)
|
||||||
# window.connect("motion-notify-event", on_mouse_move)
|
# window.connect("motion-notify-event", on_mouse_move)
|
||||||
# window.connect("motion-notify-event", on_mouse_move)
|
# window.connect("motion-notify-event", on_mouse_move)
|
||||||
|
|
||||||
|
|
||||||
# layout = Gtk.Fixed()
|
layout = Gtk.Fixed()
|
||||||
# btn = Gtk.Button(label = 'hello world')
|
|
||||||
# btn.set_size_request(200, 32)
|
|
||||||
# layout.put(btn, 0, 0)
|
|
||||||
# self.add(layout)
|
# self.add(layout)
|
||||||
|
|
||||||
layout = Gtk.Fixed()
|
# layout = Gtk.Fixed()
|
||||||
self.drawing_area = Gtk.DrawingArea()
|
self.drawing_area = Gtk.DrawingArea()
|
||||||
self.drawing_area.set_size_request(total_width, total_height)
|
self.drawing_area.set_size_request(total_width, total_height)
|
||||||
|
|
||||||
|
@ -85,7 +89,11 @@ class App(Gtk.Window):
|
||||||
self.drawing_area.connect("motion-notify-event", self.on_motion_notify)
|
self.drawing_area.connect("motion-notify-event", self.on_motion_notify)
|
||||||
layout.add(self.drawing_area)
|
layout.add(self.drawing_area)
|
||||||
|
|
||||||
|
btn = Gtk.Button(label = 'hello world')
|
||||||
|
btn.set_size_request(200, 60)
|
||||||
|
layout.put(btn, 200, 100)
|
||||||
|
|
||||||
|
self.layout = layout
|
||||||
self.add(layout)
|
self.add(layout)
|
||||||
# Initialize rectangle and control point size
|
# Initialize rectangle and control point size
|
||||||
self.start_x = None
|
self.start_x = None
|
||||||
|
@ -96,7 +104,26 @@ class App(Gtk.Window):
|
||||||
self.dragging = False
|
self.dragging = False
|
||||||
self.resize_dragging = False
|
self.resize_dragging = False
|
||||||
|
|
||||||
self.connect("draw", on_draw)
|
self.connect("draw", self.on_before_start)
|
||||||
|
self.connect("delete-event", Gtk.main_quit)
|
||||||
|
self.connect("key-press-event", self.on_key_down)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def on_before_start(self, win, cr):
|
||||||
|
|
||||||
|
if not self.saved:
|
||||||
|
# print('又进来了')
|
||||||
|
window = Gdk.get_default_root_window()
|
||||||
|
width, height = window.get_width(), window.get_height()
|
||||||
|
self.entire_desktop = Gdk.pixbuf_get_from_window(window, 0, 0, width, height)
|
||||||
|
# self.entire_desktop.savev("entire_desktop.png", "png", [], [])
|
||||||
|
self.saved = True
|
||||||
|
|
||||||
|
cr.set_source_rgba(0, 0, 0, 0.3) # 设置颜色和透明度
|
||||||
|
cr.set_operator(cairo.OPERATOR_SOURCE)
|
||||||
|
cr.paint()
|
||||||
|
cr.set_operator(cairo.OPERATOR_OVER)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -104,12 +131,27 @@ class App(Gtk.Window):
|
||||||
if self.start_x is not None and self.start_y is not None:
|
if self.start_x is not None and self.start_y is not None:
|
||||||
width = self.current_x - self.start_x
|
width = self.current_x - self.start_x
|
||||||
height = self.current_y - self.start_y
|
height = self.current_y - self.start_y
|
||||||
|
|
||||||
|
# print(abs(width), abs(height))
|
||||||
|
|
||||||
|
# 白色
|
||||||
|
# cr.set_source_rgb(1, 1, 1)
|
||||||
|
|
||||||
|
cr.set_source_rgb(208/255, 234/255, 1)
|
||||||
|
cr.set_line_width(2)
|
||||||
|
|
||||||
|
|
||||||
cr.set_dash([6.0, 4.0])
|
cr.set_dash([6.0, 4.0])
|
||||||
cr.rectangle(self.start_x, self.start_y, width, height)
|
cr.rectangle(self.start_x, self.start_y, width, height)
|
||||||
cr.stroke()
|
cr.stroke()
|
||||||
|
center = (self.start_x + width / 2, self.start_y + height / 2)
|
||||||
# draw resizing dots
|
# draw resizing dots
|
||||||
for x in [self.start_x, self.start_x + width / 2, self.current_x]:
|
for x in [self.start_x, center[0], self.current_x]:
|
||||||
for y in [self.start_y, self.start_y + height / 2, self.current_y]:
|
for y in [self.start_y, center[1], self.current_y]:
|
||||||
|
# 中心的圆点不画
|
||||||
|
if x == center[0] and y == center[1]:
|
||||||
|
continue
|
||||||
|
|
||||||
cr.arc(x, y, 5, 0, 2 * 3.14)
|
cr.arc(x, y, 5, 0, 2 * 3.14)
|
||||||
cr.fill()
|
cr.fill()
|
||||||
|
|
||||||
|
@ -126,6 +168,7 @@ class App(Gtk.Window):
|
||||||
def on_button_release(self, widget, event):
|
def on_button_release(self, widget, event):
|
||||||
if event.button == 1: # left mouse button
|
if event.button == 1: # left mouse button
|
||||||
self.dragging = False
|
self.dragging = False
|
||||||
|
self.screenshot()
|
||||||
self.queue_draw()
|
self.queue_draw()
|
||||||
|
|
||||||
|
|
||||||
|
@ -136,6 +179,26 @@ class App(Gtk.Window):
|
||||||
self.queue_draw()
|
self.queue_draw()
|
||||||
|
|
||||||
|
|
||||||
|
def screenshot(self):
|
||||||
|
x = self.start_x
|
||||||
|
y = self.start_y
|
||||||
|
w = self.current_x - x
|
||||||
|
h = self.current_y - y
|
||||||
|
pixbuf = GdkPixbuf.Pixbuf.new(GdkPixbuf.Colorspace.RGB, True, 8, abs(w), abs(h))
|
||||||
|
self.entire_desktop.copy_area(x, y, w, h, pixbuf, 0, 0)
|
||||||
|
|
||||||
|
# pixbuf = blur_image(pixbuf)
|
||||||
|
# pixbuf.savev("screenshot.png", "png", [], [])
|
||||||
|
img = Gtk.Image()
|
||||||
|
img.set_from_pixbuf(pixbuf)
|
||||||
|
self.layout.put(img, x + 1, y + 1)
|
||||||
|
self.layout.show_all()
|
||||||
|
|
||||||
|
|
||||||
|
def on_key_down(self, win, ev):
|
||||||
|
if ev.keyval == Gdk.KEY_Escape:
|
||||||
|
Gtk.main_quit()
|
||||||
|
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -0,0 +1,90 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
|
||||||
|
import gi, io, base64, threading
|
||||||
|
from gi.repository import Gdk, GLib, GdkPixbuf, Gio, GObject
|
||||||
|
from PIL import Image, ImageFilter
|
||||||
|
|
||||||
|
|
||||||
|
empty_pixbuf = GdkPixbuf.Pixbuf.new(GdkPixbuf.Colorspace.RGB, True, 8, 1, 1)
|
||||||
|
|
||||||
|
|
||||||
|
def pixbuf_to_pil(pixbuf):
|
||||||
|
data = pixbuf.get_pixels()
|
||||||
|
w = pixbuf.get_width()
|
||||||
|
h = pixbuf.get_height()
|
||||||
|
stride = pixbuf.get_rowstride()
|
||||||
|
mode = "RGBA" if pixbuf.get_has_alpha() else "RGB"
|
||||||
|
img = Image.frombytes(mode, (w, h), data, "raw", mode, stride)
|
||||||
|
return img
|
||||||
|
|
||||||
|
|
||||||
|
def pil_to_pixbuf(img):
|
||||||
|
data = []
|
||||||
|
for p in img.getdata():
|
||||||
|
data.extend(p)
|
||||||
|
data = bytes(data)
|
||||||
|
alpha = img.mode == 'RGBA'
|
||||||
|
w, h = img.size
|
||||||
|
rowstride = w * 4
|
||||||
|
try:
|
||||||
|
return GdkPixbuf.Pixbuf.new_from_bytes(GLib.Bytes.new(data), GdkPixbuf.Colorspace.RGB, alpha, 8, w, h, rowstride)
|
||||||
|
except:
|
||||||
|
return empty_pixbuf
|
||||||
|
|
||||||
|
|
||||||
|
def pic_to_pixbuf(pic):
|
||||||
|
data = pic.data
|
||||||
|
input_stream = Gio.MemoryInputStream.new_from_data(data, None)
|
||||||
|
try:
|
||||||
|
return GdkPixbuf.Pixbuf.new_from_stream(input_stream, None)
|
||||||
|
except:
|
||||||
|
return empty_pixbuf
|
||||||
|
|
||||||
|
def base64_to_pixbuf(base64_str):
|
||||||
|
data = base64.b64decode(base64_str)
|
||||||
|
input_stream = Gio.MemoryInputStream.new_from_data(data, None)
|
||||||
|
try:
|
||||||
|
return GdkPixbuf.Pixbuf.new_from_stream(input_stream, None)
|
||||||
|
except:
|
||||||
|
return empty_pixbuf
|
||||||
|
|
||||||
|
|
||||||
|
def blur_image(pixbuf):
|
||||||
|
# 加载图片并确认该图片为RGBA模式,保证透明度
|
||||||
|
img = pixbuf_to_pil(pixbuf).convert('RGBA')
|
||||||
|
mask = Image.new('RGBA', img.size, (64, 64, 64, 0))
|
||||||
|
img = img.filter(ImageFilter.GaussianBlur(radius = 6))
|
||||||
|
img.alpha_composite(mask)
|
||||||
|
|
||||||
|
return pil_to_pixbuf(img)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# 定义一个异步修饰器, 用于在子线程中运行一些会阻塞主线程的任务
|
||||||
|
def run_async(func):
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
thread = threading.Thread(target=func, args=args, kwargs=kwargs)
|
||||||
|
thread.daemon = True
|
||||||
|
thread.start()
|
||||||
|
return thread
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
# 类型js的settimeout的修饰器
|
||||||
|
def set_timeout(timeout = 0.5):
|
||||||
|
def decorator(callback):
|
||||||
|
def wrapper(*args):
|
||||||
|
t = threading.Timer(timeout, callback, args=args)
|
||||||
|
t.start()
|
||||||
|
return t
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
|
# 定义一个修饰器, 用于将当前方法转到主线程中运行 (子线程中调用方法时)
|
||||||
|
def idle(func):
|
||||||
|
def wrapper(*args):
|
||||||
|
GObject.idle_add(func, *args)
|
||||||
|
return wrapper
|
||||||
|
|
|
@ -0,0 +1,154 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
|
||||||
|
import gi, sys, os, cairo, math
|
||||||
|
import dbus
|
||||||
|
import dbus.service, dbus.mainloop.glib
|
||||||
|
|
||||||
|
gi.require_version('Gtk', '3.0')
|
||||||
|
|
||||||
|
from gi.repository import Gtk, Gdk, GLib, GdkPixbuf
|
||||||
|
from PIL import Image, ImageFilter, ImageChops
|
||||||
|
|
||||||
|
app_id = 'fun.wkit.xshot'
|
||||||
|
|
||||||
|
|
||||||
|
def pixbuf_to_pil(pixbuf):
|
||||||
|
data = pixbuf.get_pixels()
|
||||||
|
w = pixbuf.get_width()
|
||||||
|
h = pixbuf.get_height()
|
||||||
|
stride = pixbuf.get_rowstride()
|
||||||
|
mode = "RGBA" if pixbuf.get_has_alpha() else "RGB"
|
||||||
|
img = Image.frombytes(mode, (w, h), data, "raw", mode, stride)
|
||||||
|
return img
|
||||||
|
|
||||||
|
|
||||||
|
def pil_to_pixbuf(img):
|
||||||
|
data = []
|
||||||
|
for p in img.getdata():
|
||||||
|
data.extend(p)
|
||||||
|
data = bytes(data)
|
||||||
|
alpha = img.mode == 'RGBA'
|
||||||
|
w, h = img.size
|
||||||
|
rowstride = w * 4
|
||||||
|
return GdkPixbuf.Pixbuf.new_from_bytes(GLib.Bytes.new(data), GdkPixbuf.Colorspace.RGB, alpha, 8, w, h, rowstride)
|
||||||
|
|
||||||
|
|
||||||
|
def set_radius(pixbuf, radius = 4):
|
||||||
|
w = pixbuf.get_width()
|
||||||
|
h = pixbuf.get_height()
|
||||||
|
|
||||||
|
surface = cairo.ImageSurface(cairo.Format.ARGB32, w, h)
|
||||||
|
ctx = cairo.Context(surface)
|
||||||
|
Gdk.cairo_set_source_pixbuf(ctx, pixbuf, 0, 0)
|
||||||
|
|
||||||
|
# 左上角 圆角
|
||||||
|
ctx.arc(radius, radius, radius, -math.pi, -math.pi / 2)
|
||||||
|
ctx.line_to(w - radius, 0)
|
||||||
|
|
||||||
|
# 右上角 圆角
|
||||||
|
ctx.arc(w - radius, radius, radius, -math.pi / 2, 0)
|
||||||
|
ctx.line_to(w, -radius)
|
||||||
|
|
||||||
|
# 右下角 圆角
|
||||||
|
ctx.arc(w - radius, h - radius, radius, 0, math.pi / 2)
|
||||||
|
ctx.line_to(radius, h)
|
||||||
|
|
||||||
|
# 左下角 圆角
|
||||||
|
ctx.arc(radius, h - radius, radius, math.pi / 2, math.pi)
|
||||||
|
ctx.close_path()
|
||||||
|
|
||||||
|
ctx.clip()
|
||||||
|
ctx.paint()
|
||||||
|
|
||||||
|
return Gdk.pixbuf_get_from_surface(surface, 0, 0, w, h)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def add_shadow(img):
|
||||||
|
# 加载图片并确认该图片为RGBA模式,保证透明度
|
||||||
|
img = set_radius(img)
|
||||||
|
img = pixbuf_to_pil(img).convert('RGBA')
|
||||||
|
w,h = img.size
|
||||||
|
|
||||||
|
w += 64
|
||||||
|
h += 64
|
||||||
|
|
||||||
|
shadow = Image.new('RGBA', img.size, (80,80,80,223))
|
||||||
|
shadow = pixbuf_to_pil(set_radius(pil_to_pixbuf(shadow)))
|
||||||
|
|
||||||
|
#new_img = Image.new('RGBA', (w,h), (0,0,0,0))
|
||||||
|
new_img = Image.new('RGBA', (w,h), (255,255,255,255))
|
||||||
|
new_img.paste(shadow, (32, 32), shadow)
|
||||||
|
|
||||||
|
new_img = new_img.filter(ImageFilter.GaussianBlur(radius = 12))
|
||||||
|
new_img.paste(img, (32, 32), img)
|
||||||
|
|
||||||
|
return pil_to_pixbuf(new_img)
|
||||||
|
|
||||||
|
|
||||||
|
class Application(Gtk.Application):
|
||||||
|
def __init__(self):
|
||||||
|
Gtk.Application.__init__(self, application_id = app_id)
|
||||||
|
|
||||||
|
display = Gdk.Display.get_default()
|
||||||
|
self.clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
|
||||||
|
|
||||||
|
|
||||||
|
def do_activate(self):
|
||||||
|
self.shot_the_window()
|
||||||
|
|
||||||
|
|
||||||
|
def copy(self, image):
|
||||||
|
|
||||||
|
# 写图片进剪切板
|
||||||
|
self.clipboard.clear()
|
||||||
|
self.clipboard.set_image(image)
|
||||||
|
self.clipboard.store()
|
||||||
|
|
||||||
|
|
||||||
|
def shot_the_window(self):
|
||||||
|
|
||||||
|
screen = Gdk.Screen.get_default()
|
||||||
|
win = screen.get_active_window()
|
||||||
|
|
||||||
|
width = win.get_width()
|
||||||
|
height = win.get_height()
|
||||||
|
|
||||||
|
# 获取窗口图像
|
||||||
|
image = Gdk.pixbuf_get_from_window(win, 0, 0, width, height)
|
||||||
|
image = add_shadow(image)
|
||||||
|
|
||||||
|
self.copy(image)
|
||||||
|
|
||||||
|
class ApplicationService(dbus.service.Object):
|
||||||
|
def __init__(self, app):
|
||||||
|
self.app = app
|
||||||
|
bus_name = dbus.service.BusName(app_id, bus = dbus.SessionBus())
|
||||||
|
dbus.service.Object.__init__(self, bus_name, '/')
|
||||||
|
|
||||||
|
|
||||||
|
@dbus.service.method(app_id)
|
||||||
|
def call_app(self):
|
||||||
|
self.app.shot_the_window()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
dbus.mainloop.glib.DBusGMainLoop(set_as_default = True)
|
||||||
|
bus = dbus.SessionBus()
|
||||||
|
|
||||||
|
try:
|
||||||
|
obj = bus.get_object(app_id, '/')
|
||||||
|
obj.call_app()
|
||||||
|
sys.exit(0)
|
||||||
|
except dbus.DBusException:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
app = Application()
|
||||||
|
app.run(sys.argv)
|
||||||
|
|
||||||
|
ApplicationService(app)
|
||||||
|
|
||||||
|
Gtk.main()
|
||||||
|
|
Loading…
Reference in New Issue