I'm trying to develop a simple Wallpaper manager, but I am not able to find any method to place my PyQt Window between the current wallpaper and the desktop icons using XLib (on windows it's way easier and works perfectly using pywin32).
I'm trying to develop a simple Wallpaper manager, but I am not able to find any method to place my PyQt Window between the current wallpaper and the desktop icons using XLib (on windows it's way easier and works perfectly using pywin32).
I tried to reparent the window, but it doesn't work (possibly it's not the right strategy). I have tried this on Ubuntu/Unity, Mint/Cinnamon and Raspbian, with no success.
EDIT: I tried changing the window properties (commented in my sample code), as well as creating a new window, change its properties as DESKTOP, and set that new window as the target window parent... with no success. In fact, the newly created window does not show behind desktop icons (see modified code below)
Any help or clue is welcome!!!
#!/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
CodePudding user response:
I eventually (and by chance) found the solution. No need to use Xlib. This PyQt piece of code did the trick. I have to find a smarter way to bring the Desktop (desktop icons) on top of the wallpaper after showing it. Simulating a mouse click on the desktop is working by the moment.
#!/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