Solved Additional windows not opening on correct monitor
-
Hi,
We are making an internal configuration tool using PyQt5 5.15.4.
Development is done on macOS Catalina 10.15.7
The final version will run on both OSX and Windows 10We are having an issue where our additional windows are not opening where we would want them to.
The desired behaviour is for the new windows to open on top of the MainWindow.Our additional window widgets are based on QMainWindow since we want multiple features present in QMainWindow that aren't present in QWindow (setWindowTitle for example).
This doesn't seem to cause any issues.Currently when we open one of our additional windows, it's initial position is based on our IDE (PyCharm)
I am not sure why PyCharm is the point of reference and not the actual application.Our MainWindow position is stored in our settings using self.pos()
self.settings.setValue('windowPosition', self.pos())
It is then restored with self.move()
self.move(self.settings.value('windowPosition'))
This means it does not open on the same display as the IDE since the position is saved in the settings.
This logic also works for our additional windows, the issue is present only the first time we run the application.
Hence our attempts to force an initial position.When our additional window is opened, it tries to set it's position based on the MainWindow's position.
We tried multiple different methods.def initialPosition(self): geometry = self.frameGeometry() centerPoint = QDesktopWidget().availableGeometry().center() centerPoint.setY(self.mainWindow.pos().y()) geometry.moveCenter(centerPoint) self.move(geometry.topLeft())
def initialPosition(self): self.move(0, self.mainWindow.pos().y())
def initialPosition(self): self.move(self.mainWindow.pos())
def initialPosition(self): self.mapToGlobal(QtCore.QPoint(0, 0))
def initialPosition(self): geo = self.geometry() geo.setX(self.mainWindow.geometry().center().x()) geo.setY(self.mainWindow.geometry().top()) geo.setY(self.mainWindow.geometry().center().y()) geo.moveCenter(QPoint(self.mainWindow.geometry().center().x(), self.mainWindow.geometry().top()))
def initialPosition(self): gp = self.mainWindow.mapToGlobal(QtCore.QPoint(0, 0)) self.mapFromGlobal(gp)
def initialPosition(self): geo = self.geometry() geo.moveCenter(QPoint(self.mainWindow.geometry().center().x(), self.mainWindow.geometry().top())) self.setGeometry(geo)
def initialPosition(self): geo = self.geometry() geo.setX(self.mainWindow.geometry().center().x()) geo.setY(self.mainWindow.geometry().center().y()) self.setGeometry(geo)
These all seem to work in the sense that the position is correct BUT the window opens on the wrong display!
I can see that it is centered as I want on the wrong monitor... It will only open on the same monitor as PyCharm.
I want it's reference position to be the MainWindow, not the IDE.I am not sure what to do, I can't find anyone with the same issues a I am experiencing.
Any help is appreciated!
Thanks in advance.
FVez -
For those interested, I got it to work.
The main issue was that I was trying to set the position in the __init__ method of my widget.
I changed my approach and decided to override the show() method of the widget.I think the initial position of my mainWindow wasn't correct when the __init__ method was called and thus my widget couldn't center properly.
Here is the code for anyone interested in my solution.def show(self): geo = self.frameGeometry() geo.moveCenter(self.mainWindow.geometry().center()) geo.moveTop(self.mainWindow.geometry().top()) self.move(geo.topLeft()) super().show()
Had to take a break to figure out what I was doing wrong.
Thanks for the help!FVez
-
Hi,
The way you use QDesktopWidget is wrong. You should be using the QApplication instance to retrieve it.
-
Hi SGaist,
Thanks for the precision and the quick reply. Mine will be a bit slower since I seem to have a 600 second limitation on my posts.
I changed the function to :def initialPosition(self): geometry = self.frameGeometry() centerPoint = QApplication.desktop().availableGeometry().center() centerPoint.setY(self.mainWindow.pos().y()) geometry.moveCenter(centerPoint) self.move(geometry.topLeft())
This did not solve my issue, the additional windows are still opening on the wrong monitor.
They are opening on the same monitor as PyCharm. It is as if the position obtained from the multiple different methods I posted all base themselves on the IDE rather than the actual MainWindow. Even when I get the actual MainWindow position.FVez
-
Something is off. You should pass your widget to the availableGeometry call so that you get the geometry of the screen where the widget it located.
-
Hi SGaist,
We're getting there!
With this function :def initialPosition(self): geometry = self.frameGeometry() centerPoint = QApplication.desktop().availableGeometry(self.mainWindow).center() centerPoint.setY(self.mainWindow.pos().y()) geometry.moveCenter(centerPoint) self.move(geometry.topRight())
The window now opens on the correct monitor!
However, it doesn't if I don't use .topRight. I would prefer it to open top center.
if I use the following line instead of the .topRight call shown above, it opens on the wrong monitor again.self.move(geometry.center().x(), geometry.top())
Any idea why trying to center it changes the screen?
Thanks!
FVez -
I would check the values you get and the one you create to ensure you stay within the correct screen.
-
So for the function :
def initialPosition(self): geometry = self.frameGeometry() centerPoint = QApplication.desktop().availableGeometry(self.mainWindow).center() centerPoint.setY(self.mainWindow.pos().y()) geometry.moveCenter(centerPoint) self.move(...)
On geometry = self.frameGeometry(), geometry is QRect(0, 0, 900, 700)
On centerPoint = QApplication.desktop().availableGeometry(self.mainWindow).center(), centerpoint is QPoint(408, -529)
On centerPoint.setY(self.mainWindow.pos().y()), centerpoint is now QPoint(408, 0)
On geometry.moveCenter(centerPoint), geometry is now QRect(-41, -349, 900, 700)With these two different approaches for the move action :
self.move(geometry.center().x(), geometry.top())
After this line, geometry is still QRect(-41, -349, 900, 700)
geometry.center().x() returns 408
geometry.top() returns -349self.move(geometry.topRight())
After this line geometry is still QRect(-41, -349, 900, 700)
geometry.topRight() returns QPoint(858, -349)From what I understand, in both lines the geometry object is the same, no? Why would the behaviour change?
The Y value is the same in both instances, wouldn't this mean they should pop in the same screen?I am having a hard time understanding this issue, it really isn't behaving the way I am expecting.
I would think they would appear on the same screen in both cases since the Y value is the same.Is there some hidden property with QRect that dictates on which screen the window is shown?
Thanks,
FVez -
Overall it seems really inconsistent.
I've been playing around changing screens and reopening the windows, the position is always a bit different.All I want is for the additional window to always open on top on my MainWindow.
I want it to be centered on the X axis with the MainWindow and on the Y axis I want it to be at the same height as the MainWindow.
This is desired for any type of screen, OS, etc. In all cases I always want them at the same relative position.Any idea how this can be achieved? I feel like the current solution is not quite what I'm looking for.
Thanks,
FVez -
Please use QWidget.saveGeometry/restoreGeometry() to save geometries to QSettings. This will store the screen correctly.
Also, please use the QScreen class added in Qt 5 to retrieve the geometries. QWidget.screen() gives you the screen the widget is on. The windows should then be parented on the main window using the Qt.Window flag.
-
Hi Friedemannkleint,
I find using saveGeometry and restoreGeometry to cause some weird visual glitches on startup.
When the settings are restored the screen visibly shrinks to the right size and moves to the correct position.Using move does not do this, the transition is instant, as I think it should be.
As for QWidget.screen(), I saw that this exists, however, there seems to be no way for me to set the screen once I have it.
I did not find any setScreen function.Thanks,
FVez -
Seems I have spoken too fast.
QWidget does have a setScreen function.However this function is not present for QMainWindow.
-
@fvez_demtroys
QMainWindow
is derived fromQWidget
, so it must have all its methods.... -
Hi JonB,
Here is my additional window.
class HelperWindow(QMainWindow): def __init__(self, mainWindow): super().__init__() self.mainWindow = mainWindow self.setMinimumSize(900, 700) self.setWindowTitle('Helper') self.initialPosition()
def initialPosition(self): geometry = self.frameGeometry() centerPoint = QApplication.desktop().availableGeometry(self.mainPage.mainWindow).center() centerPoint.setY(self.mainPage.tabWidget.y()) geometry.moveCenter(centerPoint) self.move(geometry.topRight()) self.setScreen(self.mainPage.mainWindow.screen())
I get :
AttributeError: 'HelperWindow' object has no attribute 'setScreen'Any idea if I have an error in my code?
It should inherit all methods from QMainWindow...FVez
-
@JonB I think setScreen is not present in Qt5 but in in Qt6
-
Is there no simple way to align both windows?
I want the top of the second window to be aligned with the top of the main window. Nothing more.Thanks!
FVez -
When I try to simply self.move(self.mainWindow.pos())
I get the following error.
qt.qpa.window: Window position QRect(0,-22 900x700) outside any known screen, using primary screenNot sure why self.mainWindow.pos() returns a position outside the screen, I've been using only one monitor today to simplify the issue.
-
@jsulm said in Additional windows not opening on correct monitor:
@JonB I think setScreen is not present in Qt5 but in in Qt6
All I know is @fvez_demtroys wrote
QWidget does have a setScreen function.
However this function is not present for QMainWindow.
I don't know whether Qt 5 or 6, I just don't see how
QWidget
can have a method whichQMainWindow
does not. -
Hi JonB,
As I mentioned in my original post, I am using PyQt5 5.15.4.
PyQt5 documentation does not mention a setScreen function for the QWidget class.
https://doc.qt.io/qt-5/qwidget.htmlFVez
-
@fvez_demtroys
Earlier you wroteQWidget does have a setScreen function.
but now you agree it does not. I was confused. Anyway @jsulm has explained it's only Qt6.
-
Hi JonB,
Yes my error, I was looking at the PyQt6 Doc!
Sorry for the confusion.I'm still having no luck in achieving what I want though.
Is there no way to tell the new window to open at the same Y position as the main window and be centered on the main window?
Could this issue be caused by the OSX environment?I don't understand why I can't use mainWindow.pos().y() and mainWindow.geometry().center().x() to properly display my new window where I want it to be...
Thanks,
FVez