[PyKDE] Feedback Needed on Possible tr() Changes

Phil Thompson phil at riverbankcomputing.co.uk
Sat May 24 12:38:01 BST 2003


I think people have hinted to me before about this, but I've only just 
properly understood the problem. I suspect that it is impossible for PyQt to 
implement QObject.tr() properly - although I may get lucky with SIP v4's use 
of metaclasses. I've made a couple of attempts in the past, but it seems that 
the current implementation may not the best design decision I've ever made.

Translation in Qt is based on the string to translate and a context. The 
context is usually the name of the class from which tr() is called. Each C++ 
class that contains the Q_OBJECT macro has it's tr() method created 
automatically by moc. Each generated tr() has the proper context hardcoded. 
It is very difficult/impossible to emulate this behaviour in PyQt for Python 
classes that extend C++ classes.

Note that there is a workaround for all this. Just use...

qApp.translate("Context","String")

...instead of calls to tr().

At the moment I see 3 options...

1. Current Unpredictable Option
The current implementation uses the name of the class of the instance as the 
context. The problem is then that, if the class is further sub-classed, the 
context changes. An example (ignoring calls to __init__)...

class A(QObject):
	def hello(self):
		return self.tr("Hello")

class B(A):
	pass

class C(A):
	pass

a = A().hello()	# Context is "A"
b = B().hello()	# Context is "B"
c = C().hello()	# Context is "C"

...which requires 3 translations of the string. The problem is at its worse if 
A is a class generated by pyuic.

2. Old Predictable Option
The original implementation had an automatically generated tr() for each 
wrapped QObject sub-class. This meant that the context was correct for those, 
but incorrect for any Python sub-class where it was the most recent C++ in 
the hierarchy. For example...

class A(QObject):
	def hello(self):
		return self.tr("Hello")

class B(QObject):
	def welcome(self):
		return self.tr("Hello")

a = A().hello()		# Context is "QObject"
b = B().welcome()	# Context is "QObject"

...which (I think) has the consequence of creating one string for translation 
when, perhaps, there should be two. Also, I'm not sure if pylupdate will 
determine the correct context.

3. New Clumsy Option
Another option is to dump the problem onto the application developer. A 
Q_OBJECT() method could be added to QObject that does the same job as the 
Q_OBJECT macro and creates the tr() method with the correct context 
hardcoded. For example...

class A(QObject):
	def __init__(self):
		QObject.__init__(self)
		self.Q_OBJECT(A)

	def hello(self):
		return A.tr("Hello")

class B(A):
	def __init__(self):
		A.__init__(self)
		self.Q_OBJECT(B)

	def goodbye(self):
		return B.tr("Goodbye")

b = B()
b.hello()		# Context is "A"
b.goodbye()	# Context is "B"

...but note that you must explicitly call the class specific tr() and not 
self.tr() - which I feel means you might as well call qApp.translate() and 
forget the added complexity of Q_OBJECT().

Note that whatever option is used, I will fix pyuic so that generated forms 
get the context right.

Each option has its advantages and disadvantages - developers are currently 
dealing with Option 1, Option 2 is probably the least confusing.

Comments and other suggestions welcome.

Phil




More information about the PyQt mailing list