#python #unix #pyqt5 #xlib
Вопрос:
Я пытаюсь разработать простой менеджер обоев, но я не могу найти способ разместить окно PyQt между текущими обоями и значками рабочего стола с помощью XLib (в Windows это намного проще и отлично работает с использованием pywin32).
Я пытаюсь разработать простой менеджер обоев, но я не могу найти способ разместить окно PyQt между текущими обоями и значками рабочего стола с помощью XLib (в Windows это намного проще и отлично работает с использованием pywin32).
Я попытался перекрасить окно, но это не работает (возможно, это не правильная стратегия). Я пробовал это на Ubuntu/Unity, Мяте/Корице и Малине, но безуспешно.
ИЗМЕНИТЬ: Я попытался изменить свойства окна (прокомментировано в моем примере кода), а также создать новое окно, изменить его свойства в качестве рабочего стола и установить это новое окно в качестве родительского целевого окна… безрезультатно. Фактически, вновь созданное окно не отображается за значками рабочего стола (см. Измененный код ниже).
Любая помощь или подсказка приветствуется!!!
#!/usr/bin/python # -*- coding: utf-8 -*- import signal import sys from PyQt5 import QtWidgets, QtCore, QtGui import Xlib import Xlib.X import Xlib.display import ewmh import traceback DISP = Xlib.display.Display() SCREEN = DISP.screen() ROOT = SCREEN.root EWMH = ewmh.EWMH(_display=DISP, root=ROOT) class Window(QtWidgets.QMainWindow): def __init__(self, *args, **kwargs): QtWidgets.QMainWindow.__init__(self, *args, **kwargs) caption = "TestIt" self.setWindowTitle(caption) self.setWindowFlags(QtCore.Qt.WindowStaysOnBottomHint | QtCore.Qt.FramelessWindowHint) screenSize = QtWidgets.QApplication.primaryScreen().size() x = screenSize.width() y = screenSize.height() self.setGeometry(0, 0, x, y) self.bkg_label = QtWidgets.QLabel() self.bkg_label.setGeometry(0, 0, x, y) pixmap = resizeImageWithQT("resourcesB/Doom.jpg", x, y) self.bkg_label.setPixmap(pixmap) self.layout().addWidget(self.bkg_label) self.bkg_label.show() sendBehind(caption) def resizeImageWithQT(src, width, height, keepAspect=True): try: pixmap = QtGui.QPixmap(src) if keepAspect: pixmap_resized = pixmap.scaled(int(width), int(height), QtCore.Qt.KeepAspectRatioByExpanding, QtCore.Qt.SmoothTransformation) else: pixmap_resized = pixmap.scaled(int(width), int(height), QtCore.Qt.IgnoreAspectRatio, QtCore.Qt.SmoothTransformation) except: pixmap_resized = None return pixmap_resized def sendBehind(name): def getAllWindows(): # windows = EWMH.getClientList() # This will return the "user" apps only windows = ROOT.query_tree().children return windows def getWindowsWithTitle(title): matches = [] for win in getAllWindows(): if title == EWMH.getWmName(win): matches.append(win) return matches def getActiveWindow(): win_id = EWMH.getActiveWindow() if win_id: return win_id return None win = getWindowsWithTitle(name) if win: win = win[0] w = DISP.create_resource_object('window', win) # https://stackoverflow.com/questions/58885803/can-i-use-net-wm-window-type-dock-ewhm-extension-in-openbox # Does not sends current window below. It does with the new window, but not behind the desktop icons # w.change_property(DISP.intern_atom('_NET_WM_WINDOW_TYPE'), Xlib.Xatom.ATOM, # 32, [DISP.intern_atom("_NET_WM_WINDOW_TYPE_DESKTOP"), ], # Xlib.X.PropModeReplace) # w.map() # DISP.next_event() # DISP.next_event() newWin = ROOT.create_window(0, 0, 500, 500, 1, SCREEN.root_depth, background_pixel=SCREEN.black_pixel, event_mask=Xlib.X.ExposureMask | Xlib.X.KeyPressMask) newWin.change_property(DISP.intern_atom('_NET_WM_WINDOW_TYPE'), Xlib.Xatom.ATOM, 32, [DISP.intern_atom("_NET_WM_WINDOW_TYPE_DESKTOP"), ], Xlib.X.PropModeReplace) newWin.map() DISP.next_event() DISP.next_event() w.reparent(newWin, 0, 0) def sigint_handler(*args): # https://stackoverflow.com/questions/4938723/what-is-the-correct-way-to-make-my-pyqt-application-quit-when-killed-from-the-co app.closeAllWindows() def exception_hook(exctype, value, tb): # https://stackoverflow.com/questions/56991627/how-does-the-sys-excepthook-function-work-with-pyqt5 traceback_formated = traceback.format_exception(exctype, value, tb) traceback_string = "".join(traceback_formated) print(traceback_string, file=sys.stderr) sys.exit(1) if __name__ == "__main__": app = QtWidgets.QApplication(sys.argv) if "python" in sys.executable.lower(): # This will allow to manage Ctl-C interruption (e.g. when running from IDE) signal.signal(signal.SIGINT, sigint_handler) timer = QtCore.QTimer() timer.start(500) timer.timeout.connect(lambda: None) # This will allow to show some tracebacks (not all, anyway) sys._excepthook = sys.excepthook sys.excepthook = exception_hook win = Window() win.show() try: app.exec_() except: pass
Ответ №1:
В конце концов (и случайно) я нашел решение. Нет необходимости использовать Xlib. Этот фрагмент кода PyQt сделал ВАЖНЫЙ трюк: на мяте/Корице (все еще борется с Ubuntu/Unity). Я должен найти более разумный способ поместить рабочий стол (значки рабочего стола) поверх обоев после его показа. Имитация щелчка мыши на рабочем столе в данный момент работает.
#!/usr/bin/python # -*- coding: utf-8 -*- import signal import sys from PyQt5 import QtWidgets, QtCore, QtGui import Xlib import Xlib.X import Xlib.display import ewmh import traceback DISP = Xlib.display.Display() SCREEN = DISP.screen() ROOT = SCREEN.root EWMH = ewmh.EWMH(_display=DISP, root=ROOT) class Window(QtWidgets.QMainWindow): def __init__(self, *args, **kwargs): QtWidgets.QMainWindow.__init__(self, *args, **kwargs) caption = "TestIt" self.setWindowTitle(caption) if "Linux" in platform.platform(): parent.setAttribute(QtCore.Qt.WA_X11NetWmWindowTypeDesktop) parent.setFocusPolicy(QtCore.Qt.NoFocus) parent.setAttribute(QtCore.Qt.WA_TransparentForMouseEvents) parent.setAttribute(QtCore.Qt.WA_InputMethodTransparent) parent.setWindowFlags(QtCore.Qt.Window | QtCore.Qt.CustomizeWindowHint | QtCore.Qt.WindowStaysOnBottomHint | QtCore.Qt.FramelessWindowHint) screenSize = QtWidgets.QApplication.primaryScreen().size() x = screenSize.width() y = screenSize.height() self.setGeometry(0, 0, x, y) self.bkg_label = QtWidgets.QLabel() self.bkg_label.setGeometry(0, 0, x, y) pixmap = resizeImageWithQT("resourcesB/Doom.jpg", x, y) self.bkg_label.setPixmap(pixmap) self.layout().addWidget(self.bkg_label) self.bkg_label.show() sendBehind() def resizeImageWithQT(src, width, height, keepAspect=True): try: pixmap = QtGui.QPixmap(src) if keepAspect: pixmap_resized = pixmap.scaled(int(width), int(height), QtCore.Qt.KeepAspectRatioByExpanding, QtCore.Qt.SmoothTransformation) else: pixmap_resized = pixmap.scaled(int(width), int(height), QtCore.Qt.IgnoreAspectRatio, QtCore.Qt.SmoothTransformation) except: pixmap_resized = None return pixmap_resized def sendBehind(): pyautogui.leftclick(x=1, y=1) def sigint_handler(*args): # https://stackoverflow.com/questions/4938723/what-is-the-correct-way-to-make-my-pyqt-application-quit-when-killed-from-the-co app.closeAllWindows() def exception_hook(exctype, value, tb): # https://stackoverflow.com/questions/56991627/how-does-the-sys-excepthook-function-work-with-pyqt5 traceback_formated = traceback.format_exception(exctype, value, tb) traceback_string = "".join(traceback_formated) print(traceback_string, file=sys.stderr) sys.exit(1) if __name__ == "__main__": app = QtWidgets.QApplication(sys.argv) if "python" in sys.executable.lower(): # This will allow to manage Ctl-C interruption (e.g. when running from IDE) signal.signal(signal.SIGINT, sigint_handler) timer = QtCore.QTimer() timer.start(500) timer.timeout.connect(lambda: None) # This will allow to show some tracebacks (not all, anyway) sys._excepthook = sys.excepthook sys.excepthook = exception_hook win = Window() win.show() try: app.exec_() except: pass