cmbant/getdist

`getdist-gui` fails with secondary monitor

lukashergt opened this issue · 8 comments

Description

When I run getdist-gui it works with a single monitor setup, but fails with dual monitor setup.

Did anybody else encounter this before?
Any ideas how I could fix this (without the need of disconnecting the secondary screen)?

(It might be that this has to do with my i3 tiling window manager setup, though...)

Setup

GetDist                  1.3.3
PySide2                  5.15.2.1
shiboken2                5.15.2.1

Error message

(py310env) [~]$ getdist-gui
Traceback (most recent call last):
  File "/home/lukas/.virtualenv/py310env/bin/getdist-gui", line 8, in <module>
    sys.exit(getdist_gui())
  File "/home/lukas/.virtualenv/py310env/lib/python3.10/site-packages/getdist/command_line.py", line 355, in getdist_gui
    run_gui()
  File "/home/lukas/.virtualenv/py310env/lib/python3.10/site-packages/getdist/gui/mainwindow.py", line 2333, in run_gui
    mainWin = MainWindow(app, ini=args.ini, plot_scale=args.plot_scale)
  File "/home/lukas/.virtualenv/py310env/lib/python3.10/site-packages/getdist/gui/mainwindow.py", line 139, in __init__
    self._createWidgets()
  File "/home/lukas/.virtualenv/py310env/lib/python3.10/site-packages/getdist/gui/mainwindow.py", line 604, in _createWidgets
    self.readSettings()
  File "/home/lukas/.virtualenv/py310env/lib/python3.10/site-packages/getdist/gui/mainwindow.py", line 618, in readSettings
    screen = self.getScreen()
  File "/home/lukas/.virtualenv/py310env/lib/python3.10/site-packages/getdist/gui/mainwindow.py", line 614, in getScreen
    return QApplication.screenAt(self.mapToGlobal(QPoint(self.width() / 2, 0))).availableGeometry()
AttributeError: 'NoneType' object has no attribute 'availableGeometry'

Problem

screenAt(point) is supposed to return the screen at point, or None if outside of any screen, so here it clearly is pointing outside a screen.

ugly fix

Disconnect the secondary screen, then launch getdist-gui. You can reconnect the secondary screen afterwards.

Not sure, was your window very wide or something? Does it work with QApplication.screenAt(self.mapToGlobal(QPoint(0,0)))?

Does it work with QApplication.screenAt(self.mapToGlobal(QPoint(0,0)))?

No, (0, 0) didn't work either, but I tried a bit more and I think I know now what happens. My secondary screen has a better resolution than my primary screen and I aligned them at the bottom edge, while the (0, 0) point is in the top left corner and points to empty space:

(0,0)
  · · · · · · · · · · · · · · · |--------------------------------------|
  ·                             |                                      |
  ·          (empty)            |                                      |
  ·                             |                                      |
  ------------------------------|                                      | 1
  |                             |              (secondary)             | 4
1 |                             |                                      | 4
0 |         (primary)           |                                      | 0
8 |                             |                                      |
0 |                             |                                      |
  |                             |                                      |
  ----------------------------------------------------------------------
             1920                               2560

I have to increase the y value to hit the primary screen, e.g.:

QApplication.screenAt(self.mapToGlobal(QPoint(0, 1440)))

where I used the y-resolution of my secondary screen as y-input.

Not sure whether there might be a good generalised implementation to make this run robustly on all sorts of setups (happy to test things out), or whether this is too specific to my setup. Anyhow, this is a much nicer solution than constantly disconnecting my secondary screen, so I'm happy to implement this locally (i.e. happy for this issue to be closed). Maybe this proves useful to someone else, too.

Thanks, Antony!

Looks like a bug in qt/pyside to me, maptoglobal is supposed to map from the current widget so 0,0 should always work

https://doc.qt.io/qtforpython-5/PySide2/QtWidgets/QWidget.html#PySide2.QtWidgets.PySide2.QtWidgets.QWidget.mapToGlobal

Hmm, but mapToGlobal works, it is screenAt((0, 0)) that returns None when (0, 0) points off-screen, which can happen with dual monitor setup.

Why not change GetDist's getScreen method from:

    def getScreen(self):
        return self.screenAt(self.mapToGlobal(QPoint(self.width() / 2, 0))).availableGeometry()

to:

    def getScreen(self):
        return self.screen().availableGeometry()

That works for me out of the box and seems a lot less complicated than using screenAt.

On my system self.screen() give
AttributeError: 'MainWindow' object has no attribute 'screen'

Shouldn't matToGlobal not return (0,0) but the actual valid global coordinate of the window position?

MainWindow is inheriting from QMainWindow which in turn inherits from QWidget which should have a method screen().

This should at least be the case for newer versions of PySide2. I've used version 5.15.2. QWidget in version 5.12.3 indeed does not have the method screen.

I tried putting in an except block with fallback to original code:
8d16213

Great, thanks Antony!