Last active
April 21, 2023 10:40
-
-
Save ales-erjavec/9096e525c558cab9b77b235a0e4ba319 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
An experiment to fix PyQt QObject.tr context override | |
https://www.riverbankcomputing.com/static/Docs/PyQt5/i18n.html#differences-between-pyqt5-and-qt | |
.. note:: | |
Known deficiencies: | |
* No support for @property, @pyQtSlot, etc. decorators | |
.. warning:: | |
This is really an experiment not inteded to be used. | |
Example | |
------- | |
@fix_tr | |
class A(QObject): | |
def hello(self): | |
return self.tr("Hello") | |
class B(A): | |
pass | |
a = A() | |
a.hello() | |
b = B() | |
b.hello() # this translated in A context | |
""" | |
import sys | |
from types import FunctionType, CodeType | |
from PyQt5.QtCore import QObject, QCoreApplication, QTranslator | |
def CodeType_replace( | |
code: CodeType, **kwargs, | |
) -> CodeType: | |
""" | |
Replace the given members of `code` | |
This is the same as `CodeType.replace `on Python >= 3.8 | |
""" | |
if sys.version_info >= (3, 8): | |
return code.replace(**kwargs) | |
return CodeType( | |
kwargs.get("co_argcount", code.co_argcount), | |
kwargs.get("co_kwonlyargcount", code.co_kwonlyargcount), | |
kwargs.get("co_nlocals", code.co_nlocals), | |
kwargs.get("co_stacksize", code.co_stacksize), | |
kwargs.get("co_flags", code.co_flags), | |
kwargs.get("co_code", code.co_code), | |
kwargs.get("co_consts", code.co_consts), | |
kwargs.get("co_names", code.co_names), | |
kwargs.get("co_varnames", code.co_varnames), | |
kwargs.get("co_filename", code.co_filename), | |
kwargs.get("co_name", code.co_name), | |
kwargs.get("co_firstlineno", code.co_firstlineno), | |
kwargs.get("co_lnotab", code.co_lnotab), | |
kwargs.get("co_freevars", code.co_freevars), | |
kwargs.get("co_cellvars", code.co_cellvars), | |
) | |
def rebind( | |
method: FunctionType, | |
fname: str, | |
fmangle_name: str | |
): | |
""" | |
Rebind a named attribute lookup within method. | |
.. note:: This reassigns the method's code object. | |
""" | |
code = method.__code__ | |
names = code.co_names | |
if fname in names: | |
names_ = list(names) | |
idx = names_.index(fname) | |
names_[idx] = fmangle_name | |
code = CodeType_replace( | |
code, | |
co_names=tuple(names_) | |
) | |
method.__code__ = code | |
return method | |
def fix_tr(class_: type) -> type: | |
""" | |
Fix the `tr` method calls for methods defined in `class_` | |
Example | |
------- | |
>>> @fix_tr | |
... class A(QObject): | |
... def f(self): | |
... print(self.tr("Hello")) | |
... | |
>>> @fix_tr | |
... class B(A): | |
... def f(self): | |
... print(self.tr("Hello")) # This is in B context | |
... super().f() # This will translate in A context | |
""" | |
fname = "tr" | |
fmangled_name = f"_{class_.__name__}_{id(class_)}_tr" | |
methods = [ | |
(name, rebind(f, fname, fmangled_name)) | |
for name, f in class_.__dict__.items() if isinstance(f, FunctionType)] | |
namespace = dict(class_.__dict__) | |
namespace.update(methods) | |
def tr(self, *args, **kwargs): | |
return QCoreApplication.translate(class_.__name__, *args, **kwargs) | |
namespace[fmangled_name] = tr | |
for name, f in namespace.items(): | |
setattr(class_, name, f) | |
return class_ | |
@fix_tr | |
class A(QObject): | |
def f(self): | |
print(self.tr("Hello")) | |
@fix_tr | |
class B(A): | |
def f(self): | |
print(self.tr("Hello")) # This is in B context | |
super().f() # This will translate in A context | |
class Translator(QTranslator): | |
def translate( | |
self, context: str, sourceText: str, disambiguation="", n=-1 | |
) -> str: | |
return context + " " + sourceText | |
t = Translator() | |
a = QCoreApplication([]) | |
QCoreApplication.installTranslator(t) | |
A().f() | |
B().f() | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment