Шаг 1: Введение

Недавно столкнулся с проблемой рисования поверх всех приложений чтобы при этом я мог продолжать в них работать, перерыв тучу сайтов нормального ответа толком и не нашел.

В данной статье мы рассмотрим, как создать прозрачное окно для рисования на языке Python, используя библиотеку PyQt5. Мы научимся создавать окно, настраивать его прозрачность и отображать его поверх всех окон на рабочем столе, сохраняя при этом функциональность и удобство использования для пользователей.

Для создания прозрачного окна мы будем использовать мощные инструменты, предоставляемые библиотекой PyQt5.

Шаг 2: Установка необходимых библиотек

Для создания прозрачного окна мы будем использовать библиотеку PyQt5, которая предоставляет мощные инструменты для создания графического интерфейса на основе Qt.

Для установки библиотек можно воспользоваться командой pip:

pip install PyQt5

или

pip install pyqt5-tools

Шаг 3: Импорт необходимых модулей

После установки библиотек мы можем импортировать их в наш код:

import sys
from PyQt5.QtWidgets import QApplication, QMainWindow
from PyQt5.QtGui import QPainter, QBrush, QColor, QPen
from PyQt5.QtCore import Qt, QTimer, QRect
import random

Шаг 4: Определение класса DrawingWindow

Для создания прозрачного окна мы определяем класс DrawingWindow, который наследуется от класса QMainWindow. В конструкторе класса устанавливаются необходимые параметры окна, такие как заголовок, геометрия и флаги. Также инициализируется объект QPainter для рисования на окне.

class DrawingWindow(QMainWindow):
    def __init__(self, coordinates):
        super().__init__()

        # Устанавливаем заголовок окна
        self.setWindowTitle("Прозрачное окно для рисования")

        # Устанавливаем геометрию окна, чтобы оно занимало весь экран
        self.setGeometry(0, 0, QApplication.desktop().screenGeometry().width(),
                         QApplication.desktop().screenGeometry().height())

        # Устанавливаем флаги, чтобы окно было без рамки и оставалось поверх других окон
        self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowSt

aysOnTopHint)

        # Создаем объект QPainter для рисования на окне
        self.painter = QPainter()
        self.painter.setRenderHint(QPainter.Antialiasing)

        # Устанавливаем начальный цвет пера (красный) и ширину пера (4 пикселя)
        self.pen_color = QColor(255, 0, 0)
        self.pen_width = 4

        # Сохраняем переданные координаты прямоугольников для рисования
        self.coordinates = coordinates

        # Создаем таймер для обновления окна
        self.draw_timer = QTimer()

        # Запускаем таймер и устанавливаем интервал обновления окна (10 миллисекунд)
        self.draw_timer.start(10)

Шаг 5: Обновление и отображение окна

В функции paintEvent происходит обновление окна при каждом событии paintEvent. Мы используем объект QPainter для рисования на окне. Сначала рисуется прозрачный фон, затем рисуются прямоугольники с использованием координат из переменной self.coordinates.

def paintEvent(self, event):
    self.painter.begin(self)

    # Устанавливаем прозрачный фон
    self.painter.setPen(Qt.NoPen)
    self.painter.setBrush(QBrush(Qt.transparent))
    self.painter.drawRect(QRect(0, 0, self.width(), self.height()))

    # Устанавливаем цвет пера и его ширину
    self.painter.setPen(QPen(QColor(self.pen_color), self.pen_width))
    self.painter.setBrush(QBrush(Qt.transparent))

    # Рисуем прямоугольники, используя переданные координаты
    for coord in self.coordinates:
        x, y, width, height = coord
        self.painter.drawRect(x, y, width, height)

    self.painter.end()

    # Обновляем координаты
    self.update_coord()

    # Планируем перерисовку через 1 секунду
    QTimer.singleShot(1000, self.update)

Шаг 6: Обновление координат

Координаты прямоугольников обновляются с помощью вызова метода update_coord(), который получает новые координаты с помощью функции R.run() из модуля ScreenCapture. Если новые координаты представлены в виде списка, то они сохраняются в переменную self.coordinates. В противном случае, новые координаты преобразуются в список и сохраняются.

def update_coord(self):
    self.coordinates = [(random.randrange(0, 1000), random.randrange(0, 1000), random.randrange(0, 1000)), (random.randrange(0, 1000), random.randrange(0, 1000), random.randrange(0, 1000))]

Шаг 7: Запуск приложения

При запуске определяются начальные координаты прямоугольников s_coordinates. Затем создается экземпляр класса Recognizer и окно DrawingWindow с передачей координат s_coordinates. Затем окно отображается с помощью метода show().

if __name__ == "__main__":
    # Начальные координаты прямоугольников
    s_coordinates = [(524, 474, 84, 64), (524, 367, 84, 47)]

    app = QApplication(sys.argv)

    # Создаем экземпляр класса Recognizer
    

    # Создаем экземпляр класса DrawingWindow с передачей координат
    window = DrawingWindow(s_coordinates)

    # Отображаем окно
    window.show()

    # Запускаем цикл обработки событий приложения и выходим, когда цикл завершится
    sys.exit(app.exec_())

Теперь вы знаете, как создать прозрачное окно для рисования поверх всех приложений с использованием библиотеки PyQt5 на языке Python. Это может быть полезно при создании приложений или скриптов, которые требуют взаимодействия с пользователем без прерывания их работы. Если у вас возникнут дополнительные вопросы, не стесняйтесь задавать их.

Результат работы

Как видно четырехугольники прекрасно отображаются поверх Pycharm и при этом не мешают работать с ним. А вообще этот кейс является частью проекта за которым можно следить здесь


PUMOVETZ/The-Fool-Game (github.com)

Вот полный код

import sys
from PyQt5.QtWidgets import QApplication, QMainWindow
from PyQt5.QtGui import QPainter, QBrush, QColor, QPen
from PyQt5.QtCore import Qt, QTimer, QRect
import random


class DrawingWindow(QMainWindow):
    def __init__(self, coordinates):
        super().__init__()
        self.setWindowTitle("Transparent Drawing Window")
        self.setGeometry(0, 0, QApplication.desktop().screenGeometry().width(),
                         QApplication.desktop().screenGeometry().height())
        self.setAttribute(Qt.WA_TranslucentBackground, True)
        self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)

        self.painter = QPainter()
        self.painter.setRenderHint(QPainter.Antialiasing)

        self.pen_color = QColor(255, 0, 0)  # Set the initial pen color to red
        self.pen_width = 4  # Set the initial pen width to 4

        self.coordinates = coordinates  # Store the coordinates for drawing rectangles

        self.draw_timer = QTimer()

        self.draw_timer.start(10)  # Update the window every 10 milliseconds

    def paintEvent(self, event):
        self.painter.begin(self)
        self.painter.setPen(Qt.NoPen)
        self.painter.setBrush(QBrush(Qt.transparent))
        self.painter.drawRect(QRect(0, 0, self.width(), self.height()))  # Draw a transparent background

        self.painter.setPen(QPen(QColor(self.pen_color), self.pen_width))
        self.painter.setBrush(QBrush(Qt.transparent))

        for coord in self.coordinates:
            x, y, width, height = coord
            self.painter.drawRect(x, y, width, height)  # Draw rectangles using the provided coordinates

        self.painter.end()

        self.update_coord()  # Update the coordinates
        QTimer.singleShot(1000, self.update)  # Schedule a repaint after 1 second

    def update_coord(self, coords=0):
        if coords != 0:
            pass
        else:
            self.coordinates = [
            (random.randrange(0, 500), random.randrange(0, 500), random.randrange(0, 500), random.randrange(0, 500))]


if __name__ == "__main__":
    coordinates = [(524, 474, 818-524, 689-474), (524, 367, 818-524, 473-367)]

    app = QApplication(sys.argv)

    window = DrawingWindow(coordinates)  # Create an instance of the DrawingWindow class with the given coordinates
    window.show()  # Display the window

    sys.exit(app.exec_())  # Start the application event loop and exit when it's finished

Комментарии (8)


  1. HemulGM
    21.06.2023 19:46
    +9

    Вот бы скучный был гайд на прозрачное окно в Delphi на FMX. А я его прям сейчас напишу:
    Шаг первый: Ставим галку Transparent у формы.

    А если хочешь таскать окно, то ещё строчку кода написать надо в событии OnMouseDown для контрола, за который хочешь таскать окно.

    StartWindowDrag;

    Готово.

    P.S. тег OpenCV тут зачем?


    1. WondeRu
      21.06.2023 19:46

      Я писал на делфи прогу, которая в 2003 году делала окошко Миранды сильно прозрачным) за спиной сидел начальник и поглядывал, чем я занимаюсь. Мессенджеры тогда были не в фаворе у компаний, воспринимались как развлекуха!


  1. dyadyaSerezha
    21.06.2023 19:46

    Ну понял почему перерисовка через секунду, а не по движению мышки. Ну и не оптимально конечно "обнулять" прозрачным весь экран, а потом заново перерисовать коллекцию прямоугольников.


    1. HemulGM
      21.06.2023 19:46

      Перерисовка вообще должна быть по событию от ОС (WM_PAINT).

      А по поводу второго замечания: вы вообще концепцию отрисовки понимаете?


      1. dyadyaSerezha
        21.06.2023 19:46

        WM_PAINT сам от движения мышки или обновления модели данных приложения не вызовется.

        По второму - а вы? Я занимался сложным GUI на виндах лет 15.


        1. HemulGM
          21.06.2023 19:46

          WM_PAINT вызовется когда нужно и столько, сколько нужно. Как вы 15 лет писали GUI - не понятно.


          1. dyadyaSerezha
            21.06.2023 19:46
            -1

            Когда нужно? Смешно читать)


  1. FGV
    21.06.2023 19:46
    -1

    Одним из интересных примеров является создание прозрачного окна для рисования, которое позволяет пользователям делать заметки, комментарии или рисовать поверх любого активного окна, не переключаясь на другие приложения

    Ага