【Python】ScrShotShow-生成可直接拖动与缩放的置顶截屏图片块

动机

平时在电脑上阅读文献或者文章的时候,有一些知识点需要前后参照,比如公式。
这个时候你就需要来回翻,那有没有一种方法能直接把需要的部分截下来做成悬浮的图片块呢?
就是这款ScrShotShow小工具。
此小工具还有其他功能,比如拖动,缩放,默认置顶,比较方便。
至于内存占用,一个图片块需要占用25M左右内存,相对轻量,一般同时运行的图片块也就1-3个,不会占用很多内存。

设计思路

首先设计一个类,从剪切板里获取图片到程序中。这一步需要用到pillow库。
然后设计一个显示程序,使用wx库,使用StaticBitmap组件来显示Bitmap图片,同时绑定各种功能,比如直接拖动,缩放。
最后设计一个激活程序,直接调用系统截图,然后通过一个监听键盘任意键按下的阻塞进程等待截图完毕,然后显示截屏块。这一步要用pynput库,之前写过相关操作

具体的代码和注释放在程序中,以上三个模块变成了三个类

"""图片处理用"""
from PIL import ImageGrab
import os

"""显示图片用"""
import wx

"""程序被激活的入口以及监控调用下一步显示"""
from pynput.keyboard import Key, Controller, Listener


class Util:
    """图片处理类"""

    @staticmethod
    def get_pic():
        img = ImageGrab.grabclipboard()
        img.save('picture.png')

    @staticmethod
    def del_pic():
        os.remove('picture.png')  # 及时清理文件


class PicFrame(wx.Frame):
    """窗体类"""

    def __init__(self):
        """生成窗体"""
        # 参数设置
        self.shrink_rate = 1.08
        self.now_rate = 1
        self.shrink_quality = wx.IMAGE_QUALITY_NORMAL
        # 从剪切板生成图片
        Util.get_pic()
        # 读取本地图片为wxImage,之后转换生成bitmap对象
        self.img = wx.Image('picture.png', wx.BITMAP_TYPE_PNG)
        self.size = self.img.GetSize()
        # 清理图片
        Util.del_pic()
        # 初始化展示图片,None表示没有父节点,style通过参数调节没有边框
        # 框架的size通过图片的尺寸来自适应,位置用center函数取屏幕中央
        super().__init__(None, style=wx.SIMPLE_BORDER |
                                     wx.STAY_ON_TOP |
                                     wx.TRANSPARENT_WINDOW, size=self.size)
        self.Center(dir=wx.BOTH)
        # 创建一个Bitmap显示组件
        self.bitmap = wx.StaticBitmap(self, bitmap=wx.Bitmap(self.img))
        # 关联事件,移动窗口
        self.bitmap.Bind(wx.EVT_LEFT_DOWN, self.on_left_down)
        self.bitmap.Bind(wx.EVT_MOTION, self.on_drag)
        self.bitmap.Bind(wx.EVT_LEFT_UP, self.on_left_up)
        self.bitmap.Bind(wx.EVT_MOUSEWHEEL, self.on_scroll)

    def on_left_down(self, event):
        """左键按下"""
        """位置解析:
        默认左上角是(0,0)
        self.GetPosition() self代表的Client相对于Screen的位置
        event.GetPosition() event相对于Client的位置
        wx.GetMousePosition() 获取鼠标位置
        self.ClientToScreen()=前面的self+event位置
        以下这一句可以验证
        print(self.GetPosition()+event.GetPosition(),self.ClientToScreen(self.delta))
        """
        self.delta = event.GetPosition()

    def on_drag(self, event):
        """鼠标拖动"""
        # 如果不加LeftIsDown,就会导致递归移动,移动事件触发移动,鼠标放下也没用
        if event.Dragging() and event.LeftIsDown():
            mouse = wx.GetMousePosition()  # 用记录的delta抵消位置偏差
            self.Move((mouse.x - self.delta[0], mouse.y - self.delta[1]))

    def on_left_up(self, event):
        """左键放开"""
        if self.HasCapture():
            self.ReleaseMouse()

    def on_scroll(self, event):
        """
        滚轮控制缩放
        图像因为连续缩放会导致模糊,所以保留最初图像,每次从最初图像计算,保真
        """
        # 更新缩放倍数
        if event.GetWheelRotation() > 0:
            self.now_rate = self.now_rate * self.shrink_rate
        else:
            self.now_rate = self.now_rate / self.shrink_rate
        # 计算临时尺寸
        temp_size = (self.size[0] * self.now_rate, self.size[1] * self.now_rate)
        # 更改窗口大小
        self.SetSize(temp_size)
        # 更改图片大小
        temp_img = self.img.Scale(*temp_size, self.shrink_quality)
        self.bitmap.SetBitmap(wx.Bitmap(temp_img))


class ScrShotShow:
    """主类"""

    def __init__(self):
        """主函数"""
        # 激活截图,使用无敌简洁的写法,完美!!!
        controller = Controller()
        with controller.pressed(Key.cmd):
            with controller.pressed(Key.shift):
                controller.press('s')
                controller.release('s')
        # 监控enter键,检查到就激活图片处理
        # 设定join类监听,阻塞程序,如果出现enter,就将监听线程停掉继续后面的操作
        with Listener(
                on_press=self.on_press, on_release=None
        ) as self.listener:
            self.listener.join()
        # 激活图片显示
        app = wx.App()  # 创建app
        PicFrame().Show()  # 生成我的窗体类
        app.MainLoop()  # 循环

    def on_press(self, key):
        """按键按下响应"""
        # 停掉监听线程,进行后面的操作,也可以通过 return False实现
        self.listener.stop()
        # return False


if __name__ == '__main__':
    ScrShotShow()

打包的问题

pyinstaller打包以后竟然有100M多,这令我难以置信。
此后我查阅了很多资料,尝试了conda虚拟环境和pipenv虚拟环境,结果conda那个是小了,但是无法运行,pipenv是主流方法,但是出来的结果和直接打包一样大,最后努力了一上午,还是败下阵来。
那就100多M打包就算了。
最后还是再挣扎一下:我通过pyinstaller命令行的INFO,感觉引入了很多hook,还有matplotlib相关的东西,我怀疑这些和pillow库有关系,因为只用wx和pynput只会有最多40M,所以这一个pillow库就有60M?我不理解。

后记

刚写完,就悲催的发现已经有哥们用三年开发出一款强大的截屏软件了:Snipaste

Snipaste

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

亦梦亦醒乐逍遥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值