pythonpython-3.xpyqt6

PyQt6 issue in fetching geometry of the window


I am currently learning the PyQt6 library and I want to get the geometry of the window. The issue is that the x and y positions are always 0 even after I changed the position of the window on the screen.

import sys
from PyQt6.QtWidgets import QApplication, QVBoxLayout, QPushButton, QWidget
from PyQt6.QtCore import QTimer

class MainWindow(QWidget):
    def __init__(self):
        super().__init__()
        self.resize(200, 300)
        self.setWindowTitle("Main Window")
        layout = QVBoxLayout()
        btn = QPushButton("Click", self)
        btn.clicked.connect(self.print_geometry)
        layout.addWidget(btn)
        self.setLayout(layout)

        self.show()

    def print_geometry(self):
        print(self.geometry())  # This will now print the correct geometry

if __name__ == '__main__':
    app = QApplication(sys.argv)
    win = MainWindow()
    sys.exit(app.exec())

Output:

PyQt6.QtCore.QRect(0, 0, 200, 300)
PyQt6.QtCore.QRect(0, 0, 200, 300)
PyQt6.QtCore.QRect(0, 0, 200, 300)
PyQt6.QtCore.QRect(0, 0, 200, 300)

I don't know why it is returning 0 for x and y positions. It should have returned the exact position.


Solution

  • why it doesn't work

    On Ubuntu, Wayland is used by default for regular user sessions. Wayland has certain limitations, including restricting access to low-level details like window position, which caused this issue. However, sudo runs the program with root privileges, which forces the system to use X11 (instead of Wayland), where such restrictions don't apply. This is why running the program with sudo sometimes works, but it isn't the best solution.

    Solution Methods:

    The issue was resolved by setting the QT_QPA_PLATFORM environment variable to xcb in the script, which forces PyQt6 to use X11 instead of Wayland. This allowed the window geometry to be fetched correctly without requiring root privileges. Additionally, installing the X11 plugins (libxcb-cursor0, libxcb1, and libxcb-xinerama0) ensures that X11 works properly

    Method 1: Running the app as sudo:

    If you run the application with sudo, it will use X11 instead of Wayland. This bypasses the restrictions imposed by Wayland and allows window geometry to be fetched correctly. However, running GUI applications as root is generally not recommended due to potential security and permission issues.

    Method 2: Set the environment variable to force Qt to use xcb:

    A cleaner solution is to explicitly set the QT_QPA_PLATFORM environment variable to xcb in the Python script, forcing Qt to use the X11 plugin. This avoids the need to run the application as root and resolves the geometry issue without requiring elevated privileges:

    import os
    os.environ['QT_QPA_PLATFORM'] = 'xcb'
    

    The error you might encounter

    qt.qpa.plugin: From 6.5.0, xcb-cursor0 or libxcb-cursor0 is needed to load the Qt xcb platform plugin.
    qt.qpa.plugin: Could not load the Qt platform plugin "xcb" in "" even though it was found.
    This application failed to start because no Qt platform plugin could be initialized. Reinstalling the application may fix this problem.
        
    Available platform plugins are: wayland, vnc, linuxfb, xcb, eglfs, vkkhrdisplay, minimalegl, minimal, offscreen, wayland-egl.
        
    Aborted (core dumped)
    

    Solution: If you wish to use X11 through sudo, you must install the necessary plugins to support it. You can install the required X11 plugins with the following commands:

    sudo apt-get install libxcb-cursor0 libxcb1 libxcb-xinerama0
    

    These libraries are needed for the X11 plugin to function properly and allow you to retrieve window geometry correctly

    final code

    import sys
    import os
    from PyQt6.QtWidgets import QApplication, QVBoxLayout, QPushButton, QWidget
    
    os.environ['QT_QPA_PLATFORM'] = 'xcb'
    
    class MainWindow(QWidget):
        def __init__(self):
            super().__init__()
            self.resize(200, 300)
            self.setWindowTitle("Main Window")
            layout = QVBoxLayout()
            btn = QPushButton("Click", self)
            btn.clicked.connect(self.print_geometry)
            layout.addWidget(btn)
            self.setLayout(layout)
    
            self.show()
    
        def print_geometry(self):
            print("Widget Geometry:", self.geometry())  
            
    if __name__ == '__main__':
        app = QApplication(sys.argv)
        win = MainWindow()
        sys.exit(app.exec())