Разместите окно за значками рабочего стола с помощью PyQt в Linux

#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