Создайте HUD, используя Python - PullRequest
0 голосов
/ 03 марта 2019

Я пытаюсь запрограммировать HUD для стороннего приложения, использующего Python.

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

Я пробовал некоторые функции оконного API ( SetWindowPos , BringWindowToTop и SetForegroundWindow ), но ни одна из них не дала мне ожидаемого результата:HUD всегда остается за сторонним приложением.

Вот простой пример кода, иллюстрирующий мою проблему:

import sys
from PyQt4.QtGui import *
from PyQt4.QtCore import *

import ctypes
import ctypes.wintypes

user32 =  ctypes.windll.user32
HUD_WIDTH = 100
HUD_HEIGHT = 50 
WINDOW_NAME = "Window name" #Replace this by the name of an opened window 

EVENT_OBJECT_LOCATIONCHANGE = 0x800B
EVENT_SYSTEM_FOREGROUND = 0x0003
WINEVENT_OUTOFCONTEXT = 0x0000
EVENT_SYSTEM_MINIMIZEEND = 0x0017

class Hud(QLabel):
    def __init__(self,window_handle):
        super().__init__()
        self.initUI()
        self.window_handle = window_handle
        rect = ctypes.wintypes.RECT()
        user32.GetWindowRect(window_handle,ctypes.byref(rect))
        self.window_x = rect.left
        self.window_y = rect.top
        self.setFollowWindow()

def initUI(self):
    self.setWindowFlags(Qt.Window | Qt.FramelessWindowHint)   
    self.resize(HUD_WIDTH,HUD_HEIGHT)   
    p = self.palette()
    p.setColor(self.backgroundRole(), Qt.black)
    p.setColor(self.foregroundRole(),Qt.white)
    self.setPalette(p)  
    self.setWindowOpacity(0.7)
    self.setText("Hello")
    self.setAlignment(Qt.AlignCenter)
    self.show()

def mousePressEvent(self,event):
    self.click_x_coordinate = event.x()
    self.click_y_coordinate = event.y()

def mouseMoveEvent(self,event):
    self.move(event.globalX()-self.click_x_coordinate,event.globalY()-self.click_y_coordinate)

def setFollowWindow(self):
    WinEventProcType = ctypes.WINFUNCTYPE(
        None, 
        ctypes.wintypes.HANDLE,
        ctypes.wintypes.DWORD,
        ctypes.wintypes.HWND,
        ctypes.wintypes.LONG,
        ctypes.wintypes.LONG,
        ctypes.wintypes.DWORD,
        ctypes.wintypes.DWORD
    )
    def callback(hWinEventHook, event, hwnd, idObject, idChild, dwEventThread, dwmsEventTime):
        if idObject != -9: #Avoids mouse events
            if event == EVENT_OBJECT_LOCATIONCHANGE and hwnd == self.window_handle:
                rect = ctypes.wintypes.RECT()
                user32.GetWindowRect(hwnd,ctypes.byref(rect))
                self.move(self.x() + rect.left - self.window_x,self.y() + rect.top - self.window_y)
                self.window_x = rect.left
                self.window_y = rect.top 
            elif event == EVENT_SYSTEM_FOREGROUND or event == EVENT_SYSTEM_MINIMIZEEND:
                if hwnd == self.window_handle:
                    user32.SetWindowPos(self.winId().__int__(),-1,self.x(),self.y(),self.width(),
                            self.height())

    self.WinEventProc = WinEventProcType(callback)        
    user32.SetWinEventHook.restype = ctypes.wintypes.HANDLE
    self.hook = user32.SetWinEventHook(
        EVENT_SYSTEM_FOREGROUND,
        EVENT_OBJECT_LOCATIONCHANGE,
        0,
        self.WinEventProc,
        0,
        0,
        WINEVENT_OUTOFCONTEXT
    )
class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow,self).__init__()
        self.initUI()
    def initUI(self):
        self.launch_hud_button = QPushButton("Launch HUD",self)
        self.launch_hud_button.clicked.connect(self.launchHud)
        self.show()
    def launchHud(self):
        window_handle = user32.FindWindowW(0,WINDOW_NAME)
        if window_handle != 0 :
            self.hud = Hud(window_handle)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    main_window = MainWindow()
    sys.exit(app.exec_())

Я также попытался установить стороннее приложение в качестве родительского для моего HUD, используя SetParent но при этом мой HUD вообще не отображается.

Есть ли у вас какие-либо предложения, чтобы заставить его работать, используя Window API или что-то еще?

Ответы [ 2 ]

0 голосов
/ 16 июля 2019

Мне, наконец, удалось убедиться, что мой HUD всегда остается на вершине данного стороннего приложения с SetParent, установив родительский класс HUD на QMainWindow вместо QLabel и удалив вызов на setWindowOpacity.Следующий код работает для меня:

import sys
from PyQt4.QtGui import *
from PyQt4.QtCore import *

import ctypes
import ctypes.wintypes

user32 =  ctypes.windll.user32
HUD_WIDTH = 100
HUD_HEIGHT = 50 
WINDOW_NAME = "Window name" #Replace this by the name of an opened window 

class Hud(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initUI()
        self.hwnd = self.winId().__int__()


    def initUI(self):
        self.setWindowFlags(Qt.FramelessWindowHint)   
        self.resize(HUD_WIDTH,HUD_HEIGHT)   
        self.textArea = QLabel()
        self.setCentralWidget(self.textArea);  
        self.setStyleSheet("background-color: rgb(0, 0, 0);color: rgb(255,255,255);")
        self.textArea.setText("Hello")
        self.textArea.setAlignment(Qt.AlignCenter)
        self.show()


class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow,self).__init__()
        self.initUI()
    def initUI(self):
        self.launch_hud_button = QPushButton("Launch HUD",self)
        self.launch_hud_button.clicked.connect(self.launchHud)
        self.show()
    def launchHud(self):
        window_handle = user32.FindWindowW(0,WINDOW_NAME)
        if window_handle != 0 :
            self.hud = Hud()
            user32.SetParent(self.hud.hwnd,window_handle)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    main_window = MainWindow()
    sys.exit(app.exec_())  

Мне было бы очень интересно, если бы кто-то знал, почему QMainWindow работает с SetParent, а не с другими классами Qt, такими как QLabel, или почему модифицируется окноНепрозрачность ведет к невидимому HUD (вызов SetParent не возвращает 0, поэтому я предполагаю, что HUD фактически задан как дочерний элемент моего стороннего приложения, но отображается как полностью прозрачный).

0 голосов
/ 04 марта 2019

Попробуйте:

# ... 
def launchHud(self):
    window_handle = user32.FindWindowW(0,WINDOW_NAME)
    if window_handle != 0 :
        self.hud = Hud(window_handle)

        self.hud.setWindowFlags(self.windowFlags()     |     # <---
                                Qt.FramelessWindowHint |     # <---
                                Qt.WindowStaysOnTopHint)     # <---
        self.hud.show()                                      # <--- !!!
# ...
...