简体   繁体   中英

pyqt5 : Build an overlay that updates under specific conditions on top of an existing program

(pyqt5 noob here.)

I want to create an overlay that change text location when some conditions are met. I run it under a thread, that I can control from main, as this would be used as an extension of an already existing program. This one would provide coordinates, and the overlay should update the text location based on those.

However, this error is triggered: A QApplication::exec: Must be called from the main thread is called

I don't get what I'm missing here.

(I've noticed that using X11BypassWindowManagerHint causes some issues, especially with event triggers, but I want the overlay to be transparent, always displayed above the window I'm using, and clickable-through).

Any help, advice or suggestions would be much appreciated !

import sys
from PyQt5.QtWidgets import QApplication, QLabel, QMainWindow, QVBoxLayout, QWidget, QDesktopWidget
from PyQt5.QtGui import QFont
from PyQt5.QtCore import Qt, QCoreApplication, QThread, pyqtSignal, pyqtSlot
from time import sleep

class text_thread(QThread):
    position_changed = pyqtSignal(int, int)

    def __init__(self, app):
        self.app = app
        QThread.__init__(self)

    def run(self):
        self.window = QMainWindow()
        self.window.setWindowFlag(Qt.X11BypassWindowManagerHint)
        self.window.setWindowFlag(Qt.FramelessWindowHint)
        self.window.setWindowFlag(Qt.WindowStaysOnTopHint)
        self.window.setAttribute(Qt.WA_TranslucentBackground)
        self.window.resize(500, 500)

        self.f = QFont("Arial", 30, QFont.Bold)
        self.label = QLabel("Text")
        self.label.setFont(self.f)

        self.label.setGeometry(100, 100, 50, 50)

        self.layout = QVBoxLayout()
        self.layout.addWidget(self.label)

        self.central_widget = QWidget()
        self.central_widget.setLayout(self.layout)

        self.window.setCentralWidget(self.central_widget)

        self.position_changed.connect(self.update_position)

        self.window.show()

        self.app.exec()

    @pyqtSlot(int, int)
    def update_position(self, x, y):
        self.label.setGeometry(x, y, 50, 50)

app = QApplication(sys.argv)

thread = text_thread(app)
thread.start()

while True:
    for i in range(10):
        thread.position_changed.emit(100+i*10, 100+i*10)
        sleep(1)

I believe that this way you can get a result similar to what you want.

I recommend isolating your test tool.

import sys
from PyQt5.QtWidgets import QApplication, QLabel, QMainWindow, QVBoxLayout, QWidget, QDesktopWidget
from PyQt5.QtGui import QFont
from PyQt5.QtCore import pyqtSignal as Signal, QObject, Qt, QCoreApplication, QThread, pyqtSignal, pyqtSlot
import time


class Tester(QObject):
    sig_positions = Signal(int, int)

    @pyqtSlot(int)
    def action(self, n):
        for i in range(1, n+1):
            time.sleep(1)
            self.sig_positions.emit(100+i*10, 100+i*10)


class Window(QMainWindow):
    sig_requested = Signal(int)

    def __init__(self):
        super().__init__()
        self.setWindowFlag(Qt.X11BypassWindowManagerHint)
        self.setWindowFlag(Qt.FramelessWindowHint)
        self.setWindowFlag(Qt.WindowStaysOnTopHint)
        self.setAttribute(Qt.WA_TranslucentBackground)
        self.resize(500, 500)
        self.f = QFont("Arial", 30, QFont.Bold)
        self.label = QLabel("Text")
        self.label.setFont(self.f)
        self.label.setGeometry(100, 100, 50, 50)
        self.layout = QVBoxLayout()
        self.layout.addWidget(self.label)
        self.central_widget = QWidget()
        self.central_widget.setLayout(self.layout)
        self.setCentralWidget(self.central_widget)

        self.t = Tester()
        self.q = QThread()
        self.t.sig_positions.connect(self.update_position)
        self.sig_requested.connect(self.t.action)
        self.t.moveToThread(self.q)
        self.q.start()

        self.show()


    def start_test(self):
        print('start')
        self.sig_requested.emit(3)

    def update_position(self, x, y):
        print('update_position')
        self.label.setGeometry(x, y, 50, 50)


if __name__ == "__main__":
    App = QApplication(sys.argv)
    window = Window()
    window.start_test()
    sys.exit(App.exec())

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM