Repository: vispy/vispy

Issue:
Python DeprecationWarning on PyQt 5.12.3
I don't have a simple reproducer yet, as I encounter this on a rather complex napari workflow, but I wanted to write it down in case others have encountered it. Under certain circumstances and *only* with older PyQt versions (5.12.3 in this case), I get the following warning emitted from VisPy:

```pytb
/Users/jni/conda/envs/all/lib/python3.9/site-packages/vispy/app/backends/_qt.py:627: DeprecationWarning: an integer is required (got type KeyboardModifiers).  Implicit conversion to integers using __int__ is deprecated, and may be removed in a future version of Python.!
```

VisPy version is 0.8.1.

I was able to silence the warning by wrapping the relevant code block into a

```python
with warnings.catch_warnings():
    warnings.simplefilter('ignore')
    ...
```

context. But I don't know if that's the desired workaround. Suggestions are super welcome! Here is the offending code:

https://github.com/vispy/vispy/blob/5d58b832c853c45b44f5036b9e7c90d6e9f4f454/vispy/app/backends/_qt.py#L627-L628

Note: I can't update my PyQt version because that is the latest available on conda-forge and the pip version breaks a *different* part of my workflow. 😂 


Discussion:
Very strange. Looking at the code it seems like it should run into that code whenever you press a key. If not, I wonder if Qt or VisPy is usually sending an integer for the modifiers in the Event but then sometimes passes the Enum instead.
What version of Python are you using? Looks like this `__int__` deprecation was added in Python 3.8.

What version of `qt`? What version of `pyqt5-sip`?

Looking at the code more I see that `_modifiers` is called from any event in Qt so any mouse or keyboard event could cause this theoretically.
Ok, I've learned a lot in the last hour. Here is the related CPython change where the deprecation was discussed: https://bugs.python.org/issue36048

The summary is that this warning should show up when something from the C-level has to convert a Python object to an integer. If the object has a `__index__` method then it uses that which returns an integer and no warning is produced. If it doesn't have `__index__` then it calls `__int__` and raises the warning as this was an implicit conversion to integer.

On my Ubuntu based system on Python 3.9 with everything installed from conda (except vispy installed in editable mode from source) I am able to do this which means the warning should never appear:

```python
In [1]: from PyQt5.QtCore import Qt

In [2]: Qt.ShiftModifier.__index__()
Out[2]: 33554432
```

So...@jni what OS are you running? Can you run the above code?
Hi @djhoese and thanks for investigating! I agree it's weird. To answer your questions:

- your code runs fine, but I nevertheless see the warning in my use case.
- I'm on macOS, Python 3.9 from conda-forge
- Package versions:

```
 $ pip list | grep Qt
PyQt5                         5.12.3
PyQt5_sip                     4.19.18
PyQtChart                     5.12
PyQtWebEngine                 5.12.1
QtPy                          1.11.0

 $ pip list | grep qt
qtconsole                     5.1.1
sphinxcontrib-qthelp          1.0.3
superqt                       0.2.3
```

The following environment does it for me on macOS 11.4:

```yaml
name: napari-pyqt512
channels:
  - conda-forge
dependencies:
  - python=3.9
  - napari
  - pyqt=5.12.3
  - pip
  - pip:
    - platelet-unet-watershed==0.0.1
    - torch==1.9.0
    - torchvision==0.10.0
```

Then run:

```python
import napari
import numpy as np

viewer = napari.Viewer(ndisplay=3)
image_layer = viewer.add_image(np.random.random((2, 20, 512, 512)))
_, w = viewer.window.add_plugin_dock_widget('platelet-unet-watershed', 'U Net Predict Widget')
w.predict_widget()

napari.run()
```

And move the mouse on the canvas. Apologies again that I haven't had a chance to narrow it down further. Normal examples from the napari examples gallery don't show the warning... 🤔 

`pip install -U pyqt5` makes the problem go away...
So I installed napari from conda-forge, but wasn't familiar with napari plugins. It said that your plugin doesn't provide a dock widget so I was going to pip install from github source...and it wanted to download `torch` at 834MB :sob: so I stopped it.
Give me a few days to investigate further and boil it down. I raised this "early" in case it was something obvious/that you'd encountered before. Thanks for the time you've put into it already.
@jni I just ran into this in a realworld use case! My SIFT application is using some `.ui` files which it converts to Python using `pyuic5`. In these files I have sections like:

```python
        self.frameRangeGroupBox = QtWidgets.QGroupBox(ExportImageDialog)
        self.frameRangeGroupBox.setGeometry(QtCore.QRect(10, 30, 251, 111))
        self.frameRangeGroupBox.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignTop)
```

Turns out that in Python 3.9 you get the same deprecation warning you were seeing and in Python 3.10 it actually breaks! I see this error in Python 3.10:

```
TypeError: setAlignment(self, int): argument 1 has unexpected type 'Alignment'
```

The individual alignment flags have type `AlignmentFlag` which can be converted to integer just fine it seems, but when you do the OR'ing with `|` it becomes an `Alignment` which apparently can't be converted.

```
In [4]: type(QtCore.Qt.AlignLeading)
Out[4]: PyQt5.QtCore.Qt.AlignmentFlag

In [5]: type(QtCore.Qt.AlignLeading | QtCore.Qt.AlignLeft)
Out[5]: PyQt5.QtCore.Qt.Alignment
```
@jni I think theoretically if you wrap `q & qtmod` in an `int(q & qtmod)` it may fix your deprecation warning.

Resolving patch:
diff --git a/vispy/app/backends/_qt.py b/vispy/app/backends/_qt.py
--- a/vispy/app/backends/_qt.py
+++ b/vispy/app/backends/_qt.py
@@ -97,7 +97,7 @@ def _get_event_xy(ev):
         else:
             from PyQt5.QtOpenGL import QGLWidget, QGLFormat
     from PyQt5 import QtGui, QtCore, QtWidgets, QtTest
-    QWidget, QApplication = QtWidgets.QWidget, QtWidgets.QApplication  # 
+    QWidget, QApplication = QtWidgets.QWidget, QtWidgets.QApplication  # Compat
 elif qt_lib == 'pyqt6':
     _check_imports('PyQt6')
     if not USE_EGL:
@@ -630,7 +630,7 @@ def _modifiers(self, event):
                      [qt_keyboard_modifiers.ControlModifier, keys.CONTROL],
                      [qt_keyboard_modifiers.AltModifier, keys.ALT],
                      [qt_keyboard_modifiers.MetaModifier, keys.META]):
-            if q & qtmod:
+            if qtmod & q:
                 mod += (v,)
         return mod
 


Test patch: