使用 PySide2 开发 Maya 插件系列三:qt语言国际化(internationalization)
使用 PySide2 开发 Maya 插件系列三:qt语言国际化(internationalization)
前言:
这是 qt for python 的语言国际化,基于 UI 的,python 也有自身的语言国际化,两者是不同的。
先来看最终效果:
前期准备:
这次创建一个 main window 在 menu bar 加一个 language 的 menu:
我们还要对 action 进行一些设置,如下:
生成 .py 文件:
生成代码:
1 # -*- coding: utf-8 -*- 2 3 # Form implementation generated from reading ui file '.\internationalizationTest.ui' 4 # 5 # Created: Sun Nov 18 02:16:18 2018 6 # by: pyside-uic 0.2.15 running on PySide 1.2.4 7 # 8 # WARNING! All changes made in this file will be lost! 9 10 from PySide import QtCore, QtGui 11 12 class Ui_MainWindow(object): 13 def setupUi(self, MainWindow): 14 MainWindow.setObjectName("MainWindow") 15 MainWindow.resize(320, 248) 16 self.centralwidget = QtGui.QWidget(MainWindow) 17 self.centralwidget.setObjectName("centralwidget") 18 MainWindow.setCentralWidget(self.centralwidget) 19 self.menubar = QtGui.QMenuBar(MainWindow) 20 self.menubar.setGeometry(QtCore.QRect(0, 0, 320, 26)) 21 self.menubar.setObjectName("menubar") 22 self.menuLanguage = QtGui.QMenu(self.menubar) 23 self.menuLanguage.setObjectName("menuLanguage") 24 MainWindow.setMenuBar(self.menubar) 25 self.actionEnglish = QtGui.QAction(MainWindow) 26 self.actionEnglish.setObjectName("actionEnglish") 27 self.actionChinese = QtGui.QAction(MainWindow) 28 self.actionChinese.setObjectName("actionChinese") 29 self.menuLanguage.addAction(self.actionEnglish) 30 self.menuLanguage.addAction(self.actionChinese) 31 self.menubar.addAction(self.menuLanguage.menuAction()) 32 33 self.retranslateUi(MainWindow) 34 QtCore.QMetaObject.connectSlotsByName(MainWindow) 35 36 def retranslateUi(self, MainWindow): 37 MainWindow.setWindowTitle(QtGui.QApplication.translate("MainWindow", "MainWindow", None, QtGui.QApplication.UnicodeUTF8)) 38 self.menuLanguage.setTitle(QtGui.QApplication.translate("MainWindow", "Language", None, QtGui.QApplication.UnicodeUTF8)) 39 self.actionEnglish.setText(QtGui.QApplication.translate("MainWindow", "English", None, QtGui.QApplication.UnicodeUTF8)) 40 self.actionChinese.setText(QtGui.QApplication.translate("MainWindow", "Chinese", None, QtGui.QApplication.UnicodeUTF8))
其实 pyside 有 uic 模块可以直接load .ui 文件获得 ui 类,但是没办法实现语言国际化,或许通过另外的方法可以。
1. 使用 lupdate 从 .py 生成 .ts
安装了 PySide 或者 PySide2 后,就会有 lupdate 工具,如果不知道在哪,就在 python 的安装目录下搜 lupdate ,PySide 和 PyQt 的前缀都不一样:
这里我们使用第一个 pyside-lupdate.exe,这个参数会简单,lupdate 的参数相对复杂,在 cmd 中查看 pyside-lupdate.exe 的帮助:
可以看到 Usage,有两种用法,这里我们使用第一种project-file,方便以后反复修改,我们新建一个文件 translation_en_to_zh_CN.pro, 内容语法如下:
SOURCES = internationalizationTest_ui_pyside.py
TRANSLATIONS = translation_en_to_zh_CN.ts
CODECFORTR = UTF-8
CODECFORSRC = UTF-8
SOURCES:从一个或者多个 .py 文件中提取需要翻译的文本,不同 .py 用空格隔开,例如:
SOURCES = internationalizationTest_ui_pyside.py otherUI.py
TRANSLATIONS:指定生成的 ts 文件名,注意文件名,因为我们不单单会有一种语言的翻译,例如如果我们还需要中文繁体,那么就可以新建另外的一个 project 文件:translation_en_to_zh_TW.pro,关于各国语言的简写,请参考:https://www.cnblogs.com/ibingshan/p/9871211.html
CODECFORTR 和 CODECFORSRC:指定一些编码,可以有可以没有。
下面我们来生成 .ts 文件:
这样就生成了 translation_en_to_zh_CN.ts:
1 <?xml version="1.0" encoding="utf-8"?> 2 <!DOCTYPE TS><TS version="1.1"> 3 <context> 4 <name>MainWindow</name> 5 <message> 6 <location filename="internationalizationTest_ui_pyside.py" line="37"/> 7 <source>MainWindow</source> 8 <translation type="unfinished"></translation> 9 </message> 10 <message> 11 <location filename="internationalizationTest_ui_pyside.py" line="38"/> 12 <source>Language</source> 13 <translation type="unfinished"></translation> 14 </message> 15 <message> 16 <location filename="internationalizationTest_ui_pyside.py" line="39"/> 17 <source>English</source> 18 <translation type="unfinished"></translation> 19 </message> 20 <message> 21 <location filename="internationalizationTest_ui_pyside.py" line="40"/> 22 <source>Chinese</source> 23 <translation type="unfinished"></translation> 24 </message> 25 </context> 26 </TS>
那么 lupdate 是根据什么来决定那些文本需要被翻译的呢,看 internationalizationTest_ui_pyside.py 中的
def retranslateUi(self, MainWindow):
MainWindow.setWindowTitle(QtGui.QApplication.translate("MainWindow", "MainWindow", None, QtGui.QApplication.UnicodeUTF8))
self.menuLanguage.setTitle(QtGui.QApplication.translate("MainWindow", "Language", None, QtGui.QApplication.UnicodeUTF8))
self.actionEnglish.setText(QtGui.QApplication.translate("MainWindow", "English", None, QtGui.QApplication.UnicodeUTF8))
self.actionChinese.setText(QtGui.QApplication.translate("MainWindow", "Chinese", None, QtGui.QApplication.UnicodeUTF8))
可以看到并不是直接的 setText('text') ,而是有 QtGui.QApplication.translate ,lupdate 就是根据这个来决定的,注意 PySide 和 PySide2 的 QtGui.QApplication.translate 后面的参数有所不一样。
注意:QtGui.QApplication.translate 的前面连个参数是 str 类型,如果需要用参数来传递,最好转换成 str 例如 str(myStr)。
2. 使用 qt linguist 打开 .ts 文件来翻译成各语言版本
打开 qt linguist 了
file -> open,找到 translation_en_to_zh_CN.ts 并且打开,这是会弹出一个指定源语言和目标语言的选择窗口,我们可以在这里设置,也可以以后发布的时候通过 Edit -> Translation File Setting 打开这个窗口:
Source language 我们选择英语,其实源语言决定你使用 qt designer 的时候 widgets 的语言:
打开后如下:
这个使用是非常容易上手的,而且不用担心 lupdate 重新生成 .ts 后会把之前的翻译覆盖掉,事实上 lupdate 会保留已经翻译好的信息,而且会增加你添加的文本,或者你的 UI 中删除了,lupdate都能识别。
点击前面的问号,标记一下你已经翻译好了:
保存后的 .ts 文件:
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE TS> <TS version="2.0" language="zh_CN" sourcelanguage="en"> <context> <name>MainWindow</name> <message> <location filename="internationalizationTest_ui_pyside.py" line="37"/> <source>MainWindow</source> <translation type="unfinished">主窗口</translation> </message> <message> <location filename="internationalizationTest_ui_pyside.py" line="38"/> <source>Language</source> <translation>语言</translation> </message> <message> <location filename="internationalizationTest_ui_pyside.py" line="39"/> <source>English</source> <translation>英文</translation> </message> <message> <location filename="internationalizationTest_ui_pyside.py" line="40"/> <source>Chinese</source> <translation>中文</translation> </message> </context> </TS>
3. ts 发布 .pm
PySide 翻译的时候并不是用 .ts 文件的,需要把 .ts 文件发布为 .pm 文件。
我们可以使用 lrelease.exe 来发布:
在 linguist 中保存好 .ts 文件后:
也可以在 linguist 中直接发布:
这时候就会生成 translation_en_to_zh_CN.qm,如果我们用文本编辑器打开,里面都是乱码。
4.继承 internationalizationTest_ui_pyside.py 中的类
新建一个 internationalizationTest.py:
1 # -*- coding: utf-8 -*- 2 3 import os 4 import sys 5 6 import internationalizationTest_ui_pyside as ui 7 8 from PySide import QtGui 9 from PySide import QtGui as QtWidgets 10 from PySide import QtCore 11 12 TRANSLATOR = QtCore.QTranslator() 13 14 #TRANSLATION_DIR = os.path.join(os.path.dirname(__file__), 'translations') 15 16 class MainWindow(QtWidgets.QMainWindow, ui.Ui_MainWindow): 17 def __init__(self, parent = None): 18 super(MainWindow, self).__init__(parent) 19 self.setupUi(self) 20 21 self.languageActionGroup = QtWidgets.QActionGroup(self.menuLanguage) #这里把语言actions放到一个组里面,使得每次只能选择一个 22 self.languageActionGroup.addAction(self.actionEnglish) 23 self.languageActionGroup.addAction(self.actionChinese) 24 self.languageActionGroup.triggered[QtWidgets.QAction].connect(self.on_language_changed) #连接 trigger 槽,on_language_changed(self, action)中的action是自动传递的别点击的action对象 25 26 def on_language_changed(self, action): 27 result = False #为了debug TRANSLATOR 加载 pm 文件是否成功 28 if action == self.actionChinese: #通过action来判断哪个语言action被点击来选择不同的 pm 加载 29 result = TRANSLATOR.load('translation_en_to_zh_CN') #注意,可以不需要 .pm 后缀 30 #TRANSLATOR.load('translation_en_to_zh_CN', directory = TRANSLATION_DIR)#这里是指定某个路径下的 pm 31 else: 32 TRANSLATOR.load('') #如果加载失败,则会重置会第一个语言 33 34 print(result) #打印加载结果 35 self.retranslateUi(self) #在TRANSLATOR加载后,记得一定要执行 retranslateUi 或者其他自己定义的重新设置文本的方法。 36 37 def main(): 38 app = QtWidgets.QApplication(sys.argv) 39 40 app.installTranslator(TRANSLATOR) #非常重要的一步,为 app 安装 TRANSLATOR,如果不安装,是没有效果的 41 42 win = MainWindow() 43 win.show() 44 sys.exit(app.exec_()) 45 46 if __name__ == "__main__": 47 main()
下面来看看效果:
5.在 maya 中实现
这次是在 maya2015 中实现,如果是 maya2017 以上,请自己修改一下代码,使得 pyside 和 pyside2 兼容
新建一个 internationalizationTest_maya.py:
1 # -*- coding: utf-8 -*- 2 3 #from PySide import QtGui 4 from PySide import QtGui as QtWidgets #这样可以使得 pyside 和 pyside2 基本兼容,而且可以不用额外的补丁,也就是说是以 pyside2 为基础的 5 #import shiboken2 as shiboken #如果是 maya2017 以上,可以这样。 6 import shiboken 7 8 import internationalizationTest as ui 9 10 import maya.OpenMayaUI as omui 11 def maya_main_window(): 12 main_window_ptr = omui.MQtUtil.mainWindow() #获得maya主窗口的指针,主要是为了让插件界面设置它为父窗口 13 return shiboken.wrapInstance(long(main_window_ptr), QtWidgets.QWidget) #把maya主窗口封装从QtGui对象 14 15 class MainWindow(ui.MainWindow): 16 def __init__(self, parent = None): 17 super(MainWindow, self).__init__(parent) 18 19 win = MainWindow(maya_main_window()) 20 def main(): 21 app = QtWidgets.QApplication.instance() #因为 maya 已经是启动的 app,所以这里是获得 app 的实例 22 23 app.installTranslator(ui.TRANSLATOR) #安装 translator 24 25 global win 26 try: 27 win.close() 28 except: 29 pass 30 win.show()
因为是在maya中运行,所以 internationalizationTest.py 要改为如下,也就是 TRANSLATOR.load('translation_en_to_zh_CN', directory = TRANSLATION_DIR) ,要为 translation_en_to_zh_CN 指定一个路径,如果不指定,那么默认路径是 app 的路径下查找,但是maya的app路径并不在 internationalizationTest_maya.py 所在的路径:
1 # -*- coding: utf-8 -*- 2 3 import os 4 import sys 5 6 import internationalizationTest_ui_pyside as ui 7 8 from PySide import QtGui 9 from PySide import QtGui as QtWidgets 10 from PySide import QtCore 11 12 TRANSLATOR = QtCore.QTranslator() 13 14 TRANSLATION_DIR = os.path.dirname(__file__) 15 16 class MainWindow(QtWidgets.QMainWindow, ui.Ui_MainWindow): 17 def __init__(self, parent = None): 18 super(MainWindow, self).__init__(parent) 19 self.setupUi(self) 20 21 self.languageActionGroup = QtWidgets.QActionGroup(self.menuLanguage) #这里把语言actions放到一个组里面,使得每次只能选择一个 22 self.languageActionGroup.addAction(self.actionEnglish) 23 self.languageActionGroup.addAction(self.actionChinese) 24 self.languageActionGroup.triggered[QtWidgets.QAction].connect(self.on_language_changed) #连接 trigger 槽,on_language_changed(self, action)中的action是自动传递的别点击的action对象 25 26 def on_language_changed(self, action): 27 result = False #为了debug TRANSLATOR 加载 qm 文件是否成功 28 if action == self.actionChinese: #通过action来判断哪个语言action被点击来选择不同的 qm 加载 29 #result = TRANSLATOR.load('translation_en_to_zh_CN') #注意,可以不需要 .qm 后缀 30 result = TRANSLATOR.load('translation_en_to_zh_CN', directory = TRANSLATION_DIR)#这里是指定某个路径下的 qm 31 32 else: 33 TRANSLATOR.load('') #如果加载失败,则会重置会第一个语言 34 35 print(result) #打印加载结果 36 self.retranslateUi(self) #在TRANSLATOR加载后,记得一定要执行 retranslateUi 或者其他自己定义的重新设置文本的方法。 37 38 def main(): 39 app = QtWidgets.QApplication(sys.argv) 40 print(app) 41 app.installTranslator(TRANSLATOR) #非常重要的一步,为 app 安装 TRANSLATOR,如果不安装,是没有效果的 42 43 win = MainWindow() 44 win.show() 45 sys.exit(app.exec_()) 46 47 if __name__ == "__main__": 48 main()
启动 maya,打开 Script Editor,在 python 栏输入:
import sys
sys.path.append(r'E:\Works\Maya\Scripts\PySideTest') #把代码所在的路径添加到环境变量PATH中,这样可以import它们
import internationalizationTest_maya
reload(internationalizationTest_maya)
internationalizationTest_maya.main()
注意:sys.path.append 的路径改为自己的路径
全选代码,ctrl+shift+enter 运行,效果如下:
总结:
qt 语言国际化实现基本思路:
- 创建一个 translator 对象:TRANSLATOR = QtCore.QTranslator()
- app对象安装 translator:app.installTranslator(TRANSLATOR)
- translator 加载 qm 文件:TRANSLATOR.load('translation_en_to_zh_CN', directory = TRANSLATION_DIR)
- UI 再次设置 widget 文本:self.retranslateUi(self)
至此,使用 PySide2 开发 Maya 插件系列已经完毕,希望对大家有所帮助。
回到总览:使用 PySide2 开发 Maya 插件系列 总览