[Python • Project Combat] pytesseract+pyqt realiza um pequeno projeto de software de reconhecimento de imagem - (2) realiza a função de captura de tela QQ

Este artigo pressupõe que você já estudou o artigo anterior.Se você ainda não estudou, vá aprender rapidamente. Siga os blogueiros para saber mais.


prefácio

Após o último estudo, instalamos o mecanismo de reconhecimento do tesseract e reconhecemos rapidamente o conteúdo da imagem por meio do pytesseract. Em seguida, PyQt5DesignModecriamos nosso projeto por meio do modelo de projeto, e a interface do software foi desenhada e um evento de clique simples foi adicionado para o botão de captura de tela.

Neste artigo, continuaremos a melhorar este projeto e realizar a função de captura de tela do QQ.


1. Finalidade da tarefa

Com base na seção anterior, realize a função de captura de tela do QQ, salve a imagem e exiba-a na interface do software.

Exigir

  • Você pode usar as teclas de atalho para tirar screenshots
  • Você pode clicar no botão de captura de tela para tirar uma captura de tela
  • Capturas de tela são exibidas no software

2. Implemente a função de captura de tela

1. Análise da função de captura de tela

Base teórica

1. Função de captura de tela

O Qt fornece uma maneira de tirar screenshots - grabWindow, o protótipo da função

def grabWindow(WID window, x=0, y=0, width=-1, height=-1)

Os argumentos são 窗口IDe 要截取的区域(uma área retangular de x, y, largura e altura).
窗口IDPode ser obtido através do winId() do QWidget. Se o ID da janela da tela inteira for interceptado, passe 0.

2. Suporta evento de movimento do mouse

Os QWidgets do Qt fornecem um método que permite que a janela suporte eventos de movimento do mouse - mouseTracking, indicando se a propriedade de rastreamento do mouse da janela está em vigor. protótipo de função

def setMouseTracking(self, bool)

O parâmetro possui apenas um booltipo de parâmetro, indicando se deseja habilitar eventos de rastreamento do mouse.

3. Janela sem bordas

Os QWidgets do Qt fornecem um método que pode tornar a janela sem bordas - setWindowFlago sinalizador da janela precisa ser passado, ou seja, Qt_WindowTypepara habilitar janelas de estilos diferentes. protótipo de função

def setWindowFlag(self, Qt_WindowType, on=True)

O principal parâmetro que usamos aqui é Qt_WindowTypepara indicar o estilo da janela, e suas opções são as seguintes

  • Qt::Widget: O valor padrão do construtor QWidget. Se o novo widget não tiver um widget pai, é uma janela independente, caso contrário, é um widget filho.
  • Qt::Window: Quer haja ou não um widget pai, o novo widget é uma janela, geralmente com uma borda de janela e uma barra de título.
  • Qt::Dialog : O novo widget é uma caixa de diálogo
  • Qt::Sheet: O novo widget é um formulário do Macintosh.
  • Qt::Drawer: O novo widget é uma gaveta do Macintosh.
  • Qt::Popup: o novo widget é uma janela pop-up de nível superior.
  • Qt::Tool: O novo widget é uma janela de ferramentas, que geralmente é uma pequena janela usada para exibir botões de ferramentas.
    Se uma janela de ferramentas tiver um widget pai, ele será exibido no topo do widget pai, caso contrário, será equivalente a usar
  • Qt::WindowStaysOnTopHintExibir.
  • Qt::Tooltip: O novo widget é uma janela de prompt sem barra de título e borda da janela.
  • Qt::SplashScreen: O novo widget é uma janela de boas-vindas, que é o valor padrão do construtor QSplashScreen.
  • Qt::Desktop: O novo widget é a área de trabalho, que é o padrão para o construtor QDesktopWidget.
  • Qt::SubWindow: o novo widget é uma janela filho, independentemente de o widget ter ou não um widget pai.
  • Qt::X11BypassWindowManagerHint: Ignora completamente o gerenciador de janelas. Sua função é gerar uma janela sem moldura de janela que não é gerenciada. Neste momento, o usuário não pode usar o teclado para inserir a menos que a função QWidget::ActivateWindow() seja chamada manualmente.
  • Qt::FramelessWindowHint: Produz uma janela sem bordas de janela, neste momento o usuário não pode mover a janela e alterar seu tamanho.
  • Qt::CustomizeWindowHint: desative a dica de título da janela padrão.

4. Tela cheia da janela

Os QWidgets do Qt fornecem um método que pode tornar a exibição da janela em tela cheia - setWindowState, o que significa definir o estado da janela. protótipo de função

def setWindowState(self, Union, Qt_WindowStates=None, Qt_WindowState=None):

Quando o usamos, usamos principalmente Qt_WindowState, e suas opções são as seguintes

  • Qt: :WindowNoStateestado normal
  • Qt: :WindowMinimizedjanela minimizada
  • Qt:: WindowMaximizedmaximizar janela
  • Qt::WindowFullScreenA janela preenche toda a tela e não tem bordas
  • Qt:: Window ActiveUma janela que se torna ativa, por exemplo, pode receber entrada do teclado

Ideias de implementação

Herde a classe QWidgets para implementar uma janela de tela inteira sem bordas, cobrindo a tela inteira e fornecendo um plano de fundo preto transparente.

Manipule os seguintes eventos conforme mostrado na figura abaixo

  • Pressione o botão esquerdo do mouse para gravar as coordenadas iniciais
  • Pressione o botão direito do mouse para cancelar a operação
  • Movimento do mouse, gravar coordenadas do mouse
  • Solte o botão esquerdo do mouse e registre as coordenadas finais do movimento do mouse
  • Clique duas vezes com o mouse para salvar o conteúdo da captura de tela
  • Evento de desenho, desenha a caixa arrastada pelo mouse em tempo real

A lógica aqui deve ser algo assim. Quando o botão esquerdo do mouse é pressionado, as coordenadas da posição clicada do mouse começam a ser registradas. Quando o mouse se move, uma nova coordenada é atualizada. A nova coordenada e a coordenada anterior podem calcular x, y, largura e altura da área retangular em tempo real. Quando o botão esquerdo do mouse for liberado, as coordenadas deste ponto serão gravadas e calculadas com as primeiras coordenadas iniciais para obter as coordenadas da área da captura de tela.

O evento de movimento do mouse está sempre em execução, porque queremos visualizar o efeito de nossas capturas de tela em tempo real, portanto, precisamos sempre obter a posição do mouse. Ao mesmo tempo, o evento de desenho também precisa rodar o tempo todo, precisa calcular a área retangular que visualizamos em tempo real e desenhá-la na interface.

O evento do botão direito do mouse é muito simples, que é cancelar a operação atual, sua lógica é que se uma área estiver selecionada no momento, cancele essa área, e se nenhuma área estiver selecionada, feche a janela. O efeito final é o seguinte.

2. Realização da função de captura de tela

As ideias de código a seguir vêm de usuários do CSDN @Karbob, obrigado por suas ideias.

1. Crie uma janela

Crie uma janela que herde o QWidget, e definimos a janela basicamente. init_windowO método é o que usamos para inicializar a janela, habilitamos a função de rastreamento do mouse, configuramos o cursor do mouse, configuramos a janela sem bordas e a janela em tela cheia.

class CaptureScreen(QWidget):

    def __init__(self):
        super(QWidget, self).__init__()
        self.init_window()  # 初始化窗口
        self.capture_full_screen()  # 获取全屏

    def init_window(self):
        self.setMouseTracking(True)  # 鼠标追踪
        self.setCursor(Qt.CrossCursor)  # 设置光标
        self.setWindowFlag(Qt.FramelessWindowHint)  # 窗口无边框
        self.setWindowState(Qt.WindowFullScreen)  # 窗口全屏


Vale ressaltar que tiramos uma captura de tela da tela inteira da área de trabalho quando a janela foi inicializada e salvamos no campo da janela, que será usado quando fizermos a captura de tela posteriormente.

self.capture_full_screen()  # 获取全屏

Sua implementação é chamar grabWindowo método para tirar uma captura de tela da área de trabalho. O código de implementação é o seguinte,

    def capture_full_screen(self):
        self.full_screen_image = QGuiApplication.primaryScreen().grabWindow(QApplication.desktop().winId())

Existem também algumas variáveis ​​que precisam ser inicializadas

    begin_position = None
    end_position = None
    full_screen_image = None
    capture_image = None
    is_mouse_pressLeft = None
    painter = QPainter()
  • begin_positionRepresenta as coordenadas iniciais do mouse
  • end_positionRepresenta as coordenadas finais do mouse
  • full_screen_imageImagem para armazenar capturas de tela em tela cheia
  • capture_imageimagem capturada
  • is_mouse_pressLeftSe o botão esquerdo do mouse é pressionado
  • paintero objeto para desenhar

2. Implemente o evento de pressionamento do mouse

O evento mouse down faz principalmente duas coisas,

  1. Determinar se o botão esquerdo do mouse ou o botão direito do mouse é pressionado
  2. Se for o botão esquerdo do mouse, registre a posição inicial do mouse, caso contrário, cancele a operação atual

O fluxograma do manipulador de eventos é mostrado abaixo,

Created with Raphaël 2.3.0 开始 是否左键? 记录当前坐标 设置属性is_mouse_pressLeft为True 结束 处理右键 是否选择截图区域? 取消截图区域 取消当前操作 yes no yes no

O código de implementação é o seguinte,

    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton:
            self.begin_position = event.pos()
            self.is_mouse_pressLeft = True
        if event.button() == Qt.RightButton:
            # 如果选取了图片,则按一次右键开始重新截图
            if self.capture_image is not None:
                self.capture_image = None
                self.paint_background_image()
                self.update()
            else:
                self.close()

3. Implemente o evento de movimento do mouse

O principal trabalho do evento de movimento do mouse é obter as coordenadas do mouse em tempo real e, em seguida, chamar o método de atualização dos widgets para atualizar a interface. O código aqui é o seguinte

    def mouseMoveEvent(self, event):
        if self.is_mouse_pressLeft is True:
            self.end_position = event.pos()
            self.update()

4. Implemente o evento de liberação do mouse

A principal tarefa do evento de liberação do mouse é registrar as coordenadas quando o mouse é liberado e, em seguida, is_mouse_pressLeftdefini-lo como falso para indicar que o mouse não está mais pressionado. O código é o seguinte

    def mouseReleaseEvent(self, event):
        self.end_position = event.pos()
        self.is_mouse_pressLeft = False

5. Implemente o evento de desenho

Na verdade, o evento de desenho executa duas coisas: uma é definir a imagem de fundo como uma captura de tela da área de trabalho e escurecer a cor; a outra é colocar a imagem retangular sem escurecer a cor na área selecionada pelo mouse.

    def paintEvent(self, event):
        self.painter.begin(self)  # 开始重绘
        self.paint_background_image()
        pen_color = QColor(30, 144, 245)  # 画笔颜色
        self.painter.setPen(QPen(pen_color, 1, Qt.SolidLine, Qt.RoundCap))  # 设置画笔,蓝色,1px大小,实线,圆形笔帽
        if self.is_mouse_pressLeft is True:
            pick_rect = self.get_rectangle(self.begin_position, self.end_position)  # 获得要截图的矩形框
            self.capture_image = self.full_screen_image.copy(pick_rect)  # 捕获截图矩形框内的图片
            self.painter.drawPixmap(pick_rect.topLeft(), self.capture_image)  # 填充截图的图片
            self.painter.drawRect(pick_rect)  # 画矩形边框
        self.painter.end()  # 结束重绘

No código, self.paint_background_image()é o método de desenhar um fundo cinza-preto. Seu código de implementação é o seguinte

    def paint_background_image(self):
        shadow_color = QColor(0, 0, 0, 100)  # 黑色半透明
        self.painter.drawPixmap(0, 0, self.full_screen_image)
        self.painter.fillRect(self.full_screen_image.rect(), shadow_color)  # 填充矩形阴影

self.get_rectangle(self.begin_position, self.end_position)Calcule o retângulo da área de seleção do quadro do mouse, o código de implementação é o seguinte,

    def get_rectangle(self, begin_point, end_point):
        pick_rect_width = int(qAbs(begin_point.x() - end_point.x()))
        pick_rect_height = int(qAbs(begin_point.y() - end_point.y()))
        pick_rect_top = begin_point.x() if begin_point.x() < end_point.x() else end_point.x()
        pick_rect_left = begin_point.y() if begin_point.y() < end_point.y() else end_point.y()
        pick_rect = QRect(pick_rect_top, pick_rect_left, pick_rect_width, pick_rect_height)
        # 避免高度宽度为0时候报错
        if pick_rect_width == 0:
            pick_rect.setWidth(2)
        if pick_rect_height == 0:
            pick_rect.setHeight(2)

        return pick_rect

Calcular o retângulo é realmente muito simples, sua ideia é registrar as coordenadas inicial e final, para que a largura e a altura do retângulo possam ser calculadas através das coordenadas finais, então as duas fórmulas a seguir são obtidas

H ( z ) = y 1 − y , W ( z ) = x 1 − x H(z) = y1-y, W(z) = x1-xH ( z )=a 1y ,W ( z )=x 1x


Existem mais dois pontos importantes aqui, que são interceptar os gráficos da área do retângulo do mouse, calcular a área de seleção do quadro do mouse através do método acima e, em seguida, copiar a imagem original da área de seleção do quadro do mouse, que usamos para full_screen_image.copyobter

pick_rect = self.get_rectangle(self.begin_position, self.end_position)  # 获得要截图的矩形框
self.capture_image = self.full_screen_image.copy(pick_rect)  # 捕获截图矩形框内的图片

O processo de implementação real aqui é o seguinte Através

das coordenadas iniciais e w e h do retângulo, podemos determinar qual área queremos e passá-la full_screen_image.copypara obter uma captura de tela da área correspondente. QRect é um tipo de dados, então só precisamos passar dados do tipo QRect.

6. Evento de clique duplo do mouse

No evento de clique duplo do mouse, chamamos a função de salvar a imagem e, em seguida, fechamos a janela.

Isso está relacionado à ligação entre os seguintes programas de janela, portanto, deve ser lido em conjunto com o conteúdo de acompanhamento

Seu código de implementação é o seguinte

    def mouseDoubleClickEvent(self, event):
        if self.capture_image is not None:
            self.save_image()
            self.close()

3. Passe a captura de tela para a interface principal

Crie um arquivo no pacote do controlador CaptureScreen.pye herde-o UI_CaptureScreen.py. Vamos escrever a lógica entre as janelas.

Chamamos save_image() no evento de clique duplo do mouse. Aqui a implementamos

    def save_image(self):
        self._signal[QPixmap].emit(self.capture_image)

Um sinal é acionado aqui, o seguinte é a definição do sinal

_signal = pyqtSignal(QPixmap)

O sinal de disparo pode transferir dados entre janelas, aqui definimos um sinal do tipo QPixmap, e então usamos um slot na MainWindow para receber esses dados

self.screenWindow._signal[QPixmap].connect(self.handle_capture_picture)

Depois existe o método de tratamento desses dados, ou seja, após receber os dados, exibi-los na Etiqueta

    @pyqtSlot(QPixmap)
    def handle_capture_picture(self, img):
        print("获取到图片", img)
        self.img_raw = img
        local_img = QPixmap(img).scaled(self.picture_label.width(), self.picture_label.height())
        # self.picture_label.setScaledContents(True)
        self.picture_label.setPixmap(local_img)

A lógica acima é que, quando o save_image acionar o sinal, o handle_capture_picture de MainWIndow processará o sinal e, em seguida, colocará a imagem na interface.

4. Realize a captura de tela da tecla de atalho

Observe a captura de tela da tecla de atalho A biblioteca system_hotkey é usada aqui, que também é a tecla de atalho global implementada pelo slot de sinal usado. Primeiro, defina um sinal

sig_keyhot = pyqtSignal(str)

e, em seguida, conecte-se à função do manipulador

self.sig_keyhot[str].connect(self.MKey_pressEvent)

Inicializar duas teclas de atalho e registrar teclas de atalho

self.hk_start, self.hk_stop = SystemHotkey(), SystemHotkey()
self.hk_start.register(('control', '1'), callback=lambda x: self.send_key_event("capture_start"))
self.hk_stop.register(('control', '2'), callback=lambda x: self.send_key_event("None"))

Depois, há a função de manipulador

    @pyqtSlot(str)
    def MKey_pressEvent(self, i_str):
        if i_str == 'capture_start':
            self.screenWindow.show()
        elif i_str == 'None':
            QMessageBox.information(self, '温馨提示', '其他功能请等待后续添加哦')

Resumir

O acima é todo o conteúdo deste artigo. Este artigo completa a função de captura de tela deste projeto, usa o método nativo do pyqt para realizar a captura de tela e responde à interface principal, que também é uma das funções poderosas do PyQt5DesignMode , que melhora a eficiência de desenvolvimento do programa.

Esta pequena ferramenta é apenas uma parte deste projeto. Na verdade, adicionaremos ferramentas como conversão de formato pdf posteriormente até que este projeto possa ser publicado. Aguarde os próximos artigos.

PyQt5DesignMode é um modelo de projeto implementado por mim combinando ideias MVC com pyqt5. Destina-se a realizar aplicativos multi-janela com pyqt5. Se você estiver interessado, por favor me dê um star.

Bem-vindo ao se inscrever nesta coluna para aprender mais sobre python.

Acho que você gosta

Origin blog.csdn.net/weixin_47754149/article/details/127470534
Recomendado
Clasificación