Python 实现「PC端一键框选识别二维码」工具

Python 实现「PC端一键框选识别二维码」工具(附完整源码)

Python 实现「一键框选识别二维码」工具(附完整源码)🔍 一、前言:为什么要做这个工具?💡 二、使用方法🧰 三、技术栈与依赖✅ 安装依赖
📦 四、完整源码实现🎉 五、结语

Python 实现「一键框选识别二维码」工具(附完整源码)

发布日期:2025年8月25日
标签:Python, PyQt5, 二维码识别, pyzbar, pyautogui, 实用工具


🔍 一、前言:为什么要做这个工具?

在日常工作中,我们经常需要扫描网页、PDF 或截图中的二维码,但大多数情况只能借助手机扫码,非常不方便。

有没有一种方式,能直接用鼠标框选屏幕上的二维码,自动识别并复制内容

答案是:有!而且用 Python 就能轻松实现!

今天,我将带你一步步实现一个 「二维码框选识别工具」,支持:

✅ 全局快捷键启动(
Ctrl+Alt+Q

✅ 鼠标拖拽框选任意区域
✅ 自动识别二维码内容
✅ 复制结果到剪贴板
✅ 弹窗提示识别结果
✅ 无需打开任何软件,秒级操作!


💡 二、使用方法

按下
Ctrl + Alt + Q
鼠标拖拽选择二维码区域松开鼠标,自动识别内容复制到剪贴板,弹出提示

👉 效果:像截图一样简单,但输出的是二维码的文本内容!


🧰 三、技术栈与依赖

本工具基于以下 Python 库构建:

用途

PyQt5
创建全屏透明窗口,实现框选界面

pyautogui
截图、模拟操作

opencv-python
图像处理

pyzbar
二维码识别引擎

keyboard
监听全局快捷键

numpy
图像数据转换

ctypes
提升管理员权限(避免截图失败)

✅ 安装依赖

创建
requirements.txt
文件,内容如下:


keyboard==0.13.5
numpy==1.24.4
opencv-python==4.11.0.86
pyautogui==0.9.54
PyQt5==5.15.11
pyzbar==0.1.9

安装命令:


pip install -r requirements.txt

📦 四、完整源码实现


import sys
import keyboard
import pyautogui
from PyQt5.QtWidgets import QApplication, QWidget, QMessageBox
from PyQt5.QtCore import Qt, QObject, QEvent, QCoreApplication, QRect
from PyQt5.QtGui import QPainter, QPen, QBrush, QColor, QPainterPath
import threading
import cv2
from pyzbar import pyzbar
import numpy as np
import ctypes
import os


# ==================== 检查管理员权限 ====================
def is_admin():
    try:
        return ctypes.windll.shell32.IsUserAnAdmin()
    except:
        return False

def run_as_admin():
    if is_admin():
        return True
    script = os.path.abspath(sys.argv[0])
    params = ' '.join([script] + sys.argv[1:])
    try:
        ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, params, None, 1)
    except Exception as e:
        QMessageBox.critical(None, "错误", f"无法获取管理员权限,请右键选择“以管理员身份运行”。

{e}")
    return False


# ==================== 自定义事件 ====================
class CaptureEvent(QEvent):
    EVENT_TYPE = QEvent.Type(QEvent.registerEventType())


# ==================== 框选截图窗口 ====================
class CaptureWindow(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowState(Qt.WindowFullScreen)
        self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)
        self.setCursor(Qt.CrossCursor)
        self.setFocusPolicy(Qt.StrongFocus)

        # 📸 截图作为背景
        screen = QApplication.primaryScreen()
        self.screenshot_pixmap = screen.grabWindow(0)

        self.start_pos = None
        self.end_pos = None
        self.is_drawing = False

    def paintEvent(self, event):
        painter = QPainter(self)
        
        # 🖼️ 绘制截图
        if self.screenshot_pixmap:
            painter.drawPixmap(self.rect(), self.screenshot_pixmap)
        
        # 🌑 变暗
        painter.setCompositionMode(QPainter.CompositionMode_DestinationIn)
        painter.fillRect(self.rect(), QColor(0, 0, 0, 120))
        painter.setCompositionMode(QPainter.CompositionMode_SourceOver)

        # 🟩 绿色框
        if self.is_drawing and self.start_pos and self.end_pos:
            rect = QRect(self.start_pos, self.end_pos).normalized()
            pen = QPen(QColor(0, 255, 0), 2, Qt.SolidLine)
            brush = QBrush(QColor(0, 255, 0, 30))
            painter.setPen(pen)
            painter.setBrush(brush)
            painter.drawRect(rect)

    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton:
            self.start_pos = event.pos()
            self.end_pos = event.pos()
            self.is_drawing = True
            self.update()

    def mouseMoveEvent(self, event):
        if self.is_drawing:
            self.end_pos = event.pos()
            self.update()

    def mouseReleaseEvent(self, event):
        if event.button() == Qt.LeftButton and self.is_drawing:
            self.end_pos = event.pos()
            self.is_drawing = False
            self.close()  # ✅ 先关闭窗口
            print("a")
            self.capture_selected_area()  # 再识别(此时窗口已关闭)

    def keyPressEvent(self, event):
        if event.key() == Qt.Key_Escape:
            self.close()

    def capture_selected_area(self):
        x1 = min(self.start_pos.x(), self.end_pos.x())
        y1 = min(self.start_pos.y(), self.end_pos.y())
        x2 = max(self.start_pos.x(), self.end_pos.x())
        y2 = max(self.start_pos.y(), self.end_pos.y())
        width = x2 - x1
        height = y2 - y1

        if width < 10 or height < 10:
            print("❌ 区域太小,取消识别")
            return

        screen = QApplication.primaryScreen()
        screen_x = screen.geometry().x()
        screen_y = screen.geometry().y()
        real_x = x1 + screen_x
        real_y = y1 + screen_y

        print(f"📸 截图区域: x={real_x}, y={real_y}, w={width}, h={height}")
        screenshot = pyautogui.screenshot(region=(real_x, real_y, width, height))
        self.recognize_qr_from_image(screenshot)

    def recognize_qr_from_image(self, image):
        """识别PIL.Image中的二维码"""
        try:
            # 将PIL图像转为OpenCV格式 (RGB -> BGR)
            cv_image = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2BGR)

            # 使用pyzbar识别二维码
            decoded_objects = pyzbar.decode(cv_image)

            if not decoded_objects:
                print("❌ 未识别到二维码")
                QMessageBox.information(None, "识别结果", "未识别到二维码,请重试。")
                return

            # 取第一个二维码
            obj = decoded_objects[0]
            data = obj.data.decode('utf-8')

            print(f"✅ 识别成功: {data}")

            QApplication.clipboard().setText(data)
            QMessageBox.information(None, "识别成功", f"已识别并复制到剪贴板:
{data}")
            '''
            # 弹出消息框显示结果
            msg_box = QMessageBox()
            msg_box.setWindowTitle("识别结果")
            msg_box.setText(f"识别内容:
{data}")
            msg_box.setTextInteractionFlags(Qt.TextSelectableByMouse)  # 可复制
            msg_box.exec_()
            '''

        except Exception as e:
            print(f"❌ 识别失败: {e}")
            QMessageBox.critical(None, "识别失败", f"解码出错:
{e}")

# ==================== 事件接收器 ====================
class EventReceiver(QObject):
    def __init__(self):
        super().__init__()
        self.current_window = None  # ✅ 持有窗口引用

    def customEvent(self, event):
        print("🎯 customEvent 被触发!")
        if isinstance(event, CaptureEvent):
            print("🎯 检测到 CaptureEvent")
            self.show_capture_window()

    def show_capture_window(self):
        global is_active
        print("🖼️ show_capture_window 被调用")
        if not is_active:
            print("⚠️ 当前处于非活跃状态,忽略")
            return
        is_active = False

        # 创建窗口
        self.current_window = CaptureWindow()

        # 窗口关闭后重置状态
        def on_window_closed():
            global is_active
            is_active = True
            self.current_window = None
            print("✅ 窗口已关闭,状态重置")

        # 使用 destroyed 信号
        self.current_window.destroyed.connect(lambda: on_window_closed())

        # 显示窗口
        self.current_window.show()
        print("✅ 全屏窗口已显示")


# ==================== 快捷键监听 ====================
def start_listener():
    print("👂 监听快捷键: Ctrl+Alt+Q")

    def on_hotkey():
        print("✅ 检测到快捷键: Ctrl+Alt+Q")
        try:
            if event_receiver is None:
                print("❌ event_receiver 未初始化")
                return
            # 注意:QCoreApplication.postEvent 返回 None 是正常的!
            QCoreApplication.postEvent(event_receiver, CaptureEvent(CaptureEvent.EVENT_TYPE))
            print("📤 事件已发送")
        except Exception as e:
            print(f"❌ 事件发送失败: {e}")

    keyboard.add_hotkey('ctrl+alt+q', on_hotkey, suppress=False)
    print("✅ 热键监听启动,程序将持续运行...")

    try:
        while True:
            keyboard.wait()  # 等待任意键盘事件,避免忙等待
    except KeyboardInterrupt:
        print("⏹️ 监听线程退出")


# ==================== 主程序 ====================
if __name__ == '__main__':
    global is_active, event_receiver

    if not run_as_admin():
        sys.exit()

    app = QApplication(sys.argv)
    app.setQuitOnLastWindowClosed(True)

    dummy = QWidget()
    dummy.hide()

    is_active = True
    event_receiver = EventReceiver()  # ✅ 现在支持 self.current_window
    event_receiver.setParent(dummy)

    listener_thread = threading.Thread(target=start_listener, daemon=True)
    listener_thread.start()

    print("=" * 50)
    print("🚀 二维码框选识别工具已启动")
    print("📌 按下 Ctrl+Alt+q 开始框选")
    print("✅ 支持全屏置顶、手动框选、自动识别")
    print("❌ 关闭窗口退出程序")
    print("=" * 50)

    sys.exit(app.exec_())

🎉 五、结语

放于桌面随取随用。

版权声明:本文为 CSDN 博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文作者、出处链接及本声明。

© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
别忘初心的头像 - 鹿快
评论 抢沙发

请登录后发表评论

    暂无评论内容