完成区域截图 高亮功能测试
parent
2ba819474b
commit
59e6e16a94
|
@ -6,6 +6,9 @@ import gi, cairo
|
|||
gi.require_version('Gtk', '3.0')
|
||||
from gi.repository import Gtk, Gdk, GdkPixbuf
|
||||
|
||||
|
||||
from utils import blur_image
|
||||
|
||||
# 获取默认显示器
|
||||
dp = Gdk.Display.get_default()
|
||||
|
||||
|
@ -43,6 +46,8 @@ def on_mouse_move(widget, event):
|
|||
|
||||
class App(Gtk.Window):
|
||||
|
||||
saved = False
|
||||
|
||||
def __init__(self):
|
||||
Gtk.Window.__init__(self, type=Gtk.WindowType.POPUP)
|
||||
|
||||
|
@ -54,26 +59,25 @@ class App(Gtk.Window):
|
|||
# 设置透明背景
|
||||
screen = self.get_screen()
|
||||
visual = screen.get_rgba_visual()
|
||||
# if visual and screen.is_composited():
|
||||
# self.set_visual(visual)
|
||||
if visual and screen.is_composited():
|
||||
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-release-event", on_mouse_move)
|
||||
# window.connect("motion-notify-event", on_mouse_move)
|
||||
# window.connect("motion-notify-event", on_mouse_move)
|
||||
|
||||
|
||||
# layout = Gtk.Fixed()
|
||||
# btn = Gtk.Button(label = 'hello world')
|
||||
# btn.set_size_request(200, 32)
|
||||
# layout.put(btn, 0, 0)
|
||||
layout = Gtk.Fixed()
|
||||
|
||||
# self.add(layout)
|
||||
|
||||
layout = Gtk.Fixed()
|
||||
# layout = Gtk.Fixed()
|
||||
self.drawing_area = Gtk.DrawingArea()
|
||||
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)
|
||||
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)
|
||||
# Initialize rectangle and control point size
|
||||
self.start_x = None
|
||||
|
@ -96,7 +104,26 @@ class App(Gtk.Window):
|
|||
self.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:
|
||||
width = self.current_x - self.start_x
|
||||
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.rectangle(self.start_x, self.start_y, width, height)
|
||||
cr.stroke()
|
||||
center = (self.start_x + width / 2, self.start_y + height / 2)
|
||||
# draw resizing dots
|
||||
for x in [self.start_x, self.start_x + width / 2, self.current_x]:
|
||||
for y in [self.start_y, self.start_y + height / 2, self.current_y]:
|
||||
for x in [self.start_x, center[0], self.current_x]:
|
||||
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.fill()
|
||||
|
||||
|
@ -126,6 +168,7 @@ class App(Gtk.Window):
|
|||
def on_button_release(self, widget, event):
|
||||
if event.button == 1: # left mouse button
|
||||
self.dragging = False
|
||||
self.screenshot()
|
||||
self.queue_draw()
|
||||
|
||||
|
||||
|
@ -136,6 +179,26 @@ class App(Gtk.Window):
|
|||
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):
|
||||
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