Giter Site home page Giter Site logo

Comments (5)

th3w1zard1 avatar th3w1zard1 commented on July 17, 2024 1

Is there perhaps anything else my test needs that I'm not thinking about, before I send away to production? Thanks!

You're asking us to review a lot of non-trivial code that's not going to be part of this project. And, sorry to say it, we don't have time for that.

You are absolutely right and I apologize, at the time it seemed relevant.

from qtpy.

dalthviz avatar dalthviz commented on July 17, 2024 1

Hey @th3w1zard1, thanks for reporting. Your suggestion sounds reasonable to me.

@dalthviz, what do you think?

The initial suggestion makes sense to me 👍

Also, just in case, I think this behavior with PyQt6 is related with the deprecations done to QKeyCombination (int is deprecated and toCombined should be used and the & operator is deprecated and the | operator is the one recommended): https://doc.qt.io/qt-6/qkeycombination-obsolete.html

So, for example, with PyQt6/PySide6 you can do something like:

(Qt.KeyboardModifier.ControlModifier | Qt.Key.Key_A).toCombined()

But not something like:

int(Qt.KeyboardModifier.ControlModifier & Qt.Key.Key_A)

from qtpy.

ccordoba12 avatar ccordoba12 commented on July 17, 2024

Hey @th3w1zard1, thanks for reporting. Your suggestion sounds reasonable to me.

@dalthviz, what do you think?

from qtpy.

th3w1zard1 avatar th3w1zard1 commented on July 17, 2024

I got fed up with the nonsense differences between the qt versions, ended up writing this monstrosity which has been tested for the last 3 hours:

from __future__ import annotations

from typing import TYPE_CHECKING, TypeVar

from qtpy import API_NAME, QtCore
from qtpy.QtCore import QUrl
from qtpy.QtGui import QDesktopServices, QKeySequence

from utility.system.path import Path

if TYPE_CHECKING:

    import os

QtKey = QtCore.Qt.Key
QtMouse = QtCore.Qt.MouseButton
T = TypeVar("T")

MODIFIER_KEY_NAMES = {
    QtKey.Key_Control: "CTRL",
    QtKey.Key_Shift: "SHIFT",
    QtKey.Key_Alt: "ALT",
    QtKey.Key_Meta: "META",  # Often corresponds to the Windows or Command key
    QtKey.Key_AltGr: "ALTGR",  # Alt Graph key
    QtKey.Key_CapsLock: "CAPSLOCK",
    QtKey.Key_NumLock: "NUMLOCK",
    QtKey.Key_ScrollLock: "SCROLLLOCK",
}
MODIFIER_KEYNAME_TO_KEY: dict[str, QtKey] = {v: k for k, v in MODIFIER_KEY_NAMES.items()}



MOUSE_BUTTON_NAMES: dict[QtMouse, str] = {
    QtMouse.LeftButton: "LeftButton",
    QtMouse.RightButton: "RightButton",
    QtMouse.MiddleButton: "MiddleButton",
    QtMouse.BackButton: "BackButton",
    QtMouse.ForwardButton: "ForwardButton",
    QtMouse.TaskButton: "TaskButton",
    QtMouse.ExtraButton1: "ExtraButton1",
    QtMouse.ExtraButton2: "ExtraButton2",
    QtMouse.ExtraButton3: "ExtraButton3",
    QtMouse.ExtraButton4: "ExtraButton4",
    QtMouse.ExtraButton5: "ExtraButton5",
    QtMouse.ExtraButton6: "ExtraButton6",
    QtMouse.ExtraButton7: "ExtraButton7",
    QtMouse.ExtraButton8: "ExtraButton8",
    QtMouse.ExtraButton9: "ExtraButton9",
    QtMouse.ExtraButton10: "ExtraButton10",
    QtMouse.ExtraButton11: "ExtraButton11",
    QtMouse.ExtraButton12: "ExtraButton12",
    QtMouse.ExtraButton13: "ExtraButton13",
    QtMouse.ExtraButton14: "ExtraButton14",
    QtMouse.ExtraButton15: "ExtraButton15",
    QtMouse.ExtraButton16: "ExtraButton16",
    QtMouse.ExtraButton17: "ExtraButton17",
    QtMouse.ExtraButton18: "ExtraButton18",
    QtMouse.ExtraButton19: "ExtraButton19",
    QtMouse.ExtraButton20: "ExtraButton20",
    QtMouse.ExtraButton21: "ExtraButton21",
    QtMouse.ExtraButton22: "ExtraButton22",
    QtMouse.ExtraButton23: "ExtraButton23",
    QtMouse.ExtraButton24: "ExtraButton24",
    #not actual buttons:
    #QtMouse.NoButton: "NoButton"
    #QtMouse.AllButtons: "AllButtons"
}
STRING_TO_MOUSE: dict[str, QtMouse] = {k: v for k, v in QtMouse.__dict__.items() if "Button" in k}
STRING_TO_MOUSE.update({v: k for k, v in MOUSE_BUTTON_NAMES.items()})

BUTTON_TO_INT: dict[QtMouse, int] = {
    QtMouse.LeftButton: int(QtMouse.LeftButton) if API_NAME in ("PyQt5", "PySide2") else QtMouse.LeftButton.value,
    QtMouse.RightButton: int(QtMouse.RightButton) if API_NAME in ("PyQt5", "PySide2") else QtMouse.RightButton.value,
    QtMouse.MiddleButton: int(QtMouse.MiddleButton) if API_NAME in ("PyQt5", "PySide2") else QtMouse.MiddleButton.value,
    QtMouse.BackButton: int(QtMouse.BackButton) if API_NAME in ("PyQt5", "PySide2") else QtMouse.BackButton.value,
    QtMouse.ForwardButton: int(QtMouse.ForwardButton) if API_NAME in ("PyQt5", "PySide2") else QtMouse.ForwardButton.value,
    QtMouse.TaskButton: int(QtMouse.TaskButton) if API_NAME in ("PyQt5", "PySide2") else QtMouse.TaskButton.value,
    QtMouse.ExtraButton1: int(QtMouse.ExtraButton1) if API_NAME in ("PyQt5", "PySide2") else QtMouse.ExtraButton1.value,
    QtMouse.ExtraButton2: int(QtMouse.ExtraButton2) if API_NAME in ("PyQt5", "PySide2") else QtMouse.ExtraButton2.value,
    QtMouse.ExtraButton3: int(QtMouse.ExtraButton3) if API_NAME in ("PyQt5", "PySide2") else QtMouse.ExtraButton3.value,
    QtMouse.ExtraButton4: int(QtMouse.ExtraButton4) if API_NAME in ("PyQt5", "PySide2") else QtMouse.ExtraButton4.value,
    QtMouse.ExtraButton5: int(QtMouse.ExtraButton5) if API_NAME in ("PyQt5", "PySide2") else QtMouse.ExtraButton5.value,
    QtMouse.ExtraButton6: int(QtMouse.ExtraButton6) if API_NAME in ("PyQt5", "PySide2") else QtMouse.ExtraButton6.value,
    QtMouse.ExtraButton7: int(QtMouse.ExtraButton7) if API_NAME in ("PyQt5", "PySide2") else QtMouse.ExtraButton7.value,
    QtMouse.ExtraButton8: int(QtMouse.ExtraButton8) if API_NAME in ("PyQt5", "PySide2") else QtMouse.ExtraButton8.value,
    QtMouse.ExtraButton9: int(QtMouse.ExtraButton9) if API_NAME in ("PyQt5", "PySide2") else QtMouse.ExtraButton9.value,
    QtMouse.ExtraButton10: int(QtMouse.ExtraButton10) if API_NAME in ("PyQt5", "PySide2") else QtMouse.ExtraButton10.value,
    QtMouse.ExtraButton11: int(QtMouse.ExtraButton11) if API_NAME in ("PyQt5", "PySide2") else QtMouse.ExtraButton11.value,
    QtMouse.ExtraButton12: int(QtMouse.ExtraButton12) if API_NAME in ("PyQt5", "PySide2") else QtMouse.ExtraButton12.value,
    QtMouse.ExtraButton13: int(QtMouse.ExtraButton13) if API_NAME in ("PyQt5", "PySide2") else QtMouse.ExtraButton13.value,
    QtMouse.ExtraButton14: int(QtMouse.ExtraButton14) if API_NAME in ("PyQt5", "PySide2") else QtMouse.ExtraButton14.value,
    QtMouse.ExtraButton15: int(QtMouse.ExtraButton15) if API_NAME in ("PyQt5", "PySide2") else QtMouse.ExtraButton15.value,
    QtMouse.ExtraButton16: int(QtMouse.ExtraButton16) if API_NAME in ("PyQt5", "PySide2") else QtMouse.ExtraButton16.value,
    QtMouse.ExtraButton17: int(QtMouse.ExtraButton17) if API_NAME in ("PyQt5", "PySide2") else QtMouse.ExtraButton17.value,
    QtMouse.ExtraButton18: int(QtMouse.ExtraButton18) if API_NAME in ("PyQt5", "PySide2") else QtMouse.ExtraButton18.value,
    QtMouse.ExtraButton19: int(QtMouse.ExtraButton19) if API_NAME in ("PyQt5", "PySide2") else QtMouse.ExtraButton19.value,
    QtMouse.ExtraButton20: int(QtMouse.ExtraButton20) if API_NAME in ("PyQt5", "PySide2") else QtMouse.ExtraButton20.value,
    QtMouse.ExtraButton21: int(QtMouse.ExtraButton21) if API_NAME in ("PyQt5", "PySide2") else QtMouse.ExtraButton21.value,
    QtMouse.ExtraButton22: int(QtMouse.ExtraButton22) if API_NAME in ("PyQt5", "PySide2") else QtMouse.ExtraButton22.value,
    QtMouse.ExtraButton23: int(QtMouse.ExtraButton23) if API_NAME in ("PyQt5", "PySide2") else QtMouse.ExtraButton23.value,
    QtMouse.ExtraButton24: int(QtMouse.ExtraButton24) if API_NAME in ("PyQt5", "PySide2") else QtMouse.ExtraButton24.value,
    QtMouse.NoButton: int(QtMouse.NoButton) if API_NAME in ("PyQt5", "PySide2") else QtMouse.NoButton.value,
}
if API_NAME in ("PySide2", "PySide6"):
    BUTTON_TO_INT[QtMouse.MouseButtonMask] = int(QtMouse.MouseButtonMask) if API_NAME == "PySide2" else QtMouse.MouseButtonMask.value
INT_TO_BUTTON: dict[int, QtMouse] = {v: k for k, v in BUTTON_TO_INT.items()}


STRING_KEY_TO_INT: dict[str, int] = {k: v.value if API_NAME in ("PyQt6", "PySide6") else v for k, v in QtKey.__dict__.items() if k.startswith("Key_")}

def getQtKey(obj: QtKey | T) -> QtKey | T:
    if isinstance(obj, bytes):
        obj = obj.decode(errors="replace")

    key = MODIFIER_KEYNAME_TO_KEY.get(obj)
    if key is not None:
        return key
    if isinstance(obj, int) and API_NAME in ("PyQt6", "PySide6"):
        return QtKey(obj)
    if obj in dir(QtKey):
        return QtKey.__dict__[obj]

    # Convert the string to QKeySequence and extract the key code
    try:
        key_sequence = QKeySequence.fromString(obj)
    except TypeError:
        return obj
    return key_sequence[0] if key_sequence.count() > 0 else obj

def getQtKeyString(key: QtKey | T) -> str:
    result = getattr(key, "name", MODIFIER_KEY_NAMES.get(key, QKeySequence(key).toString()))  # type: ignore[arg-type]
    return result.decode(errors="replace") if isinstance(result, bytes) else result

def getQtKeyStringLocalized(key: QtKey | str | int | bytes):
    return MODIFIER_KEY_NAMES.get(key, getattr(key, "name", QKeySequence(key).toString())).upper().strip().replace("KEY_", "").replace("CONTROL", "CTRL")  # type: ignore[arg-type]


def getQtButtonString(button: QtMouse | int) -> str:
    # sourcery skip: assign-if-exp, reintroduce-else
    if isinstance(button, bytes):
        button = button.decode(errors="replace")
    attrButtonName = getattr(button, "name", None)
    if isinstance(attrButtonName, bytes):
        return attrButtonName.decode(errors="replace")
    if attrButtonName is None:
        return MOUSE_BUTTON_NAMES.get(button)
    return attrButtonName  # type: ignore[arg-type]


def getQtMouseButton(obj: QtMouse | T) -> QtMouse | T:
    # sourcery skip: assign-if-exp, reintroduce-else
    buttonFromString = STRING_TO_MOUSE.get(str(obj))
    if buttonFromString is not None:
        return buttonFromString
    buttonFromInt = INT_TO_BUTTON.get(obj)
    if buttonFromInt is not None:
        return buttonFromInt
    buttonFromDict = QtMouse.__dict__.get(obj)
    if buttonFromDict is not None:
        return buttonFromDict
    return None  # type: ignore[arg-type]


if __name__ == "__main__":  # quick test
    all_keys = [getattr(QtKey, key) for key in dir(QtKey) if key.startswith("Key_")]
    all_buttons = [getattr(QtMouse, button) for button in dir(QtMouse) if "Button" in button and button not in ("AllButtons", "NoButton")]

    for key in all_keys:
        key_string = getQtKeyString(key)
        key_from_string = getQtKey(key_string)
        assert key is key_from_string, f"Key str mismatch: {key} != {key_from_string}"
        key_int = STRING_KEY_TO_INT[key_string]
        key_from_int = QtKey(key_int)
        assert key_from_string == key_from_int, f"Key int mismatch: {key_from_string} ({id(key_from_string)}) != {key_from_int} ({id(key_from_int)})"

    for button in all_buttons:
        button_string = getQtButtonString(button)
        button_from_string = getQtMouseButton(button_string)
        assert button == button_from_string, f"Button str mismatch: {button} != {button_from_string}"
        button_int = BUTTON_TO_INT[button_from_string]
        button_from_int = INT_TO_BUTTON[button_int]
        assert button_from_string == button_from_int, f"Button int mismatch: {button_from_string} != {button_from_int}"

    print("All keys/buttons matched successfully!")

the asserts were originally using is. It seems ONLY pyside2 requires == for some reason.

No idea how this will work with different versions, these were tested on all 4 qt libs with the most up-to-date versions available on pip.

Is there perhaps anything else my test needs that I'm not thinking about, before I send away to production? Thanks!

from qtpy.

ccordoba12 avatar ccordoba12 commented on July 17, 2024

Is there perhaps anything else my test needs that I'm not thinking about, before I send away to production? Thanks!

You're asking us to review a lot of non-trivial code that's not going to be part of this project. And, sorry to say it, we don't have time for that.

from qtpy.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.