diff --git a/src/Py2exe-GUI.py b/src/Py2exe-GUI.py index e2c6bd1..e8bb244 100644 --- a/src/Py2exe-GUI.py +++ b/src/Py2exe-GUI.py @@ -11,13 +11,6 @@ Py2exe-GUI 启动方式: python -m py2exe_gui """ -import sys +from py2exe_gui.__main__ import main -from PySide6.QtWidgets import QApplication - -from py2exe_gui.__main__ import MainApp - -app = QApplication(sys.argv) -window = MainApp() -window.show() -sys.exit(app.exec()) +main() diff --git a/src/py2exe_gui/Resources/COMPILED_RESOURCES.py b/src/py2exe_gui/Resources/COMPILED_RESOURCES.py index c031515..fec0379 100644 --- a/src/py2exe_gui/Resources/COMPILED_RESOURCES.py +++ b/src/py2exe_gui/Resources/COMPILED_RESOURCES.py @@ -6,6 +6,478 @@ from PySide6 import QtCore qt_resource_data = b"\ +\x00\x00\x1d^\ +<\ +\xb8d\x18\xca\xef\x9c\x95\xcd!\x1c\xbf`\xa1\xbd\xdd\xa7\ +\x00\x00\x00\x05zh_CNB\x00\x00\x03\x00\x00\x00\ ++;\x00\x00\x01\x82\x00\x00+;\x00\x00\x11\xbd\x00\x02\ +\xb4\xc7\x00\x00\x01P\x00\x02\xb4\xc7\x00\x00\x11\x85\x00\x04\ +\xa6y\x00\x00\x02&\x00\x05H5\x00\x00\x12w\x00\x05\ +f\xbe\x00\x00\x0b\xc1\x00\x05f\xbe\x00\x00\x13\xa8\x00\x10\ +\xcan\x00\x00\x04h\x00\x13U\xee\x00\x00\x0a;\x00*\ +\xcf\x04\x00\x00\x0eN\x00*\xd0%\x00\x00\x0e~\x00*\ +\xec0\x00\x00\x0e\xae\x00G\x96\xc4\x00\x00\x00\x00\x00I\ +l\x0e\x00\x00\x03L\x00J+~\x00\x00\x05\x02\x00J\ +6\x95\x00\x00\x14\xf0\x00Kdq\x00\x00\x15\x1b\x00L\ +\x99b\x00\x00\x05\xa0\x00L\x99b\x00\x00\x17\xc3\x01*\ +\xbc^\x00\x00\x04\xb4\x01\x8a\xa33\x00\x00\x11\xf4\x01\xdb\ +\xb2\x8f\x00\x00\x07B\x01\xf3(\xfe\x00\x00\x15\xfd\x02$\ +\x06\xee\x00\x00\x19E\x02TK\xfa\x00\x00\x03\x98\x02o\ +\xeej\x00\x00\x17X\x02r\xf7\xfe\x00\x00\x05,\x02\xa7\ +\x96\xc4\x00\x00\x0e\x1d\x02\xa7\xdeU\x00\x00\x0f\x81\x02\xb6\ +\xee\xd3\x00\x00\x10\x7f\x03\x8aRY\x00\x00\x0bb\x03\xbc\ +\xb9r\x00\x00\x01\xe6\x04\x13\xa2*\x00\x00\x08\x93\x04a\ +\xc9p\x00\x00\x18T\x04t\xcbE\x00\x00\x06G\x04\x98\ +I\xbc\x00\x00\x0b\x07\x04\x98I\xbc\x00\x00\x13|\x04\x98\ +I\xbc\x00\x00\x14\xc4\x04\x99n\x95\x00\x00\x03\xcc\x04\x99\ +n\x95\x00\x00\x17\x92\x04\xcfv\x94\x00\x00\x02S\x04\xd1\ +N\xb2\x00\x00\x06~\x05g\xb0^\x00\x00\x13\x08\x05\x8b\ +{\xbe\x00\x00\x0d\xf5\x06\x04\x81\x85\x00\x00\x0b1\x06L\ +\xc5c\x00\x00\x01\xb3\x06\x5c\xc6\xd5\x00\x00\x14\x19\x06m\ +\x81M\x00\x00\x0fF\x06\xc6l\x14\x00\x00\x0f\x12\x07$\ +\xfe\xde\x00\x00\x0a\xdb\x07?\xfd\xfe\x00\x00\x18\xdf\x07\xca\ +r\x11\x00\x00\x09x\x08\x05\x1f\xb0\x00\x00\x06\xfd\x08\xa1\ +\x96\xd9\x00\x00\x0cm\x09\x07[\x85\x00\x00\x0f\xd4\x09\x08\ +b\x8a\x00\x00\x08X\x09 \x93N\x00\x00\x10+\x09(\ +n\x13\x00\x00\x02\xc1\x09<\x5c\x83\x00\x00\x10\xd9\x09M\ +g\xfe\x00\x00\x12\xce\x09pU\x99\x00\x00\x14X\x09\xc4\ +\xb21\x00\x00\x03\xf7\x09\xd8%U\x00\x00\x0c-\x09\xf2\ +5X\x00\x00\x08\x05\x0a\x0f\xec\x8e\x00\x00\x00&\x0a\x1e\ +\xda\xd4\x00\x00\x05\xca\x0ae\xa8\xe3\x00\x00\x15L\x0a\x98\ +I\x9c\x00\x00\x00\xe6\x0a\x98I\x9c\x00\x00\x11\x0f\x0a\xac\ +,\x85\x00\x00\x01\x1b\x0a\xac,\x85\x00\x00\x11J\x0a\xb8\ +\xc2\xa1\x00\x00\x08\xe3\x0a\xebf\x09\x00\x00\x0a\x9e\x0a\xeb\ +f\x09\x00\x00\x0b\xe9\x0a\xebf\x09\x00\x00\x13=\x0b\x05\ +MN\x00\x00\x06\x00\x0b\xab\x22\xae\x00\x00\x02\xfc\x0b\xd4\ +\xbf\xa1\x00\x00\x16\xa2\x0b\xddZ\xc5\x00\x00\x00\xa8\x0b\xe1\ +gZ\x00\x00\x16\xf4\x0c\x05\x06\xc4\x00\x00\x15\xc6\x0c\x0a\ +\xbe\xf5\x00\x00\x04%\x0c\x1b\xd1\xa1\x00\x00\x19\xb1\x0cP\ +\xa0*\x00\x00\x06\xb7\x0c~`9\x00\x00\x0d\x87\x0c\xa8\ +?b\x00\x00\x0c\xc6\x0c\xba\xefs\x00\x00\x0e\xde\x0c\xc9\ +\xa0\x0e\x00\x00\x12\xa1\x0d\x89P\x17\x00\x00\x0a\x0f\x0e\x01\ ++e\x00\x00\x18\x9f\x0ef\xfa\x7f\x00\x00\x17\xf3\x0e\x81\ +2\x09\x00\x00\x0d\x13\x0f\xc0\xd03\x00\x00\x02\x82\x0f\xcf\ +\xb2#\x00\x00\x129\x0f\xe8\x01e\x00\x00\x13\xd2i\x00\ +\x00\x1a:\x03\x00\x00\x00\x04QsN\x8e\x08\x00\x00\x00\ +\x00\x06\x00\x00\x00\x05About\x07\x00\x00\x00\x08\ +AboutDlg\x01\x03\x00\x00\x00(e\xe0\ +l\xd5bS_\x00QsN\x8ee\x87hc\xff\x0c\ +\x8b\xf7\x5c\x1d\x8b\xd5\x91\xcde\xb0\x83\xb7S\xd6g,\ +z\x0b^\x8f0\x02\x08\x00\x00\x00\x00\x06\x00\x00\x00=\ +Can't open the A\ +bout document, t\ +ry to reinstall \ +this program.\x07\x00\x00\ +\x00\x08AboutDlg\x01\x03\x00\x00\x00\x10\ +mO\x89\xc8e\x87N\xf6\x00(\x00&\x00B\x00)\ +\x08\x00\x00\x00\x00\x06\x00\x00\x00\x0c&Brows\ +e File\x07\x00\x00\x00\x0dAddDa\ +taWindow\x01\x03\x00\x00\x00\x0cS\xd6\ +m\x88\x00(\x00&\x00C\x00)\x08\x00\x00\x00\x00\x06\ +\x00\x00\x00\x07&Cancel\x07\x00\x00\x00\x0d\ +AddDataWindow\x01\x03\x00\ +\x00\x00\x0cR \x96d\x00(\x00&\x00D\x00)\x08\ +\x00\x00\x00\x00\x06\x00\x00\x00\x07&Delete\ +\x07\x00\x00\x00\x0dAddDataWind\ +ow\x01\x03\x00\x00\x00\x0ce\xb0^\xfa\x00(\x00&\ +\x00N\x00)\x08\x00\x00\x00\x00\x06\x00\x00\x00\x04&N\ +ew\x07\x00\x00\x00\x0dAddDataWi\ +ndow\x01\x03\x00\x00\x00\x0cxn\x8b\xa4\x00(\ +\x00&\x00O\x00)\x08\x00\x00\x00\x00\x06\x00\x00\x00\x03\ +&OK\x07\x00\x00\x00\x0dAddDataW\ +indow\x01\x03\x00\x00\x00\x08m\xfbR\xa0e\ +\x87N\xf6\x08\x00\x00\x00\x00\x06\x00\x00\x00\x09Add\ + Files\x07\x00\x00\x00\x0dAddDa\ +taWindow\x01\x03\x00\x00\x00\x10mO\ +\x89\xc8v\xee_U\x00(\x00&\x00F\x00)\x08\x00\ +\x00\x00\x00\x06\x00\x00\x00\x0eBrowse &\ +Folder\x07\x00\x00\x00\x0dAddDa\ +taWindow\x01\x03\x00\x00\x00\x04Y\x0d\ +R6\x08\x00\x00\x00\x00\x06\x00\x00\x00\x04Copy\ +\x07\x00\x00\x00\x10ArgumentsBr\ +owser\x01\x03\x00\x00\x00\x04[\xfcQ\xfa\x08\ +\x00\x00\x00\x00\x06\x00\x00\x00\x06Export\x07\ +\x00\x00\x00\x10ArgumentsBro\ +wser\x01\x03\x00\x00\x00\x0em\xfbR\xa0N\x8c\ +\x8f\xdbR6e\x87N\xf6\x08\x00\x00\x00\x00\x06\x00\x00\ +\x00\x10Add Binary Fil\ +es\x07\x00\x00\x00\x0cCenterWid\ +get\x01\x03\x00\x00\x00\x0cm\xfbR\xa0epc\ +ne\x87N\xf6\x08\x00\x00\x00\x00\x06\x00\x00\x00\x0eA\ +dd Data Files\x07\x00\x00\ +\x00\x0cCenterWidget\x01\x03\ +\x00\x00\x00\x16m\xfbR\xa0N\x8c\x8f\xdbR6e\x87\ +N\xf6]\xf2f\xf4e\xb00\x02\x08\x00\x00\x00\x00\x06\ +\x00\x00\x00\x19Add binary f\ +iles updated.\x07\x00\x00\ +\x00\x0cCenterWidget\x01\x03\ +\x00\x00\x00\x14m\xfbR\xa0epcne\x87N\xf6\ +]\xf2f\xf4e\xb00\x02\x08\x00\x00\x00\x00\x06\x00\x00\ +\x00\x17Add data files\ + updated.\x07\x00\x00\x00\x0cCe\ +nterWidget\x01\x03\x00\x00\x00\x0a\ +\x98yv\xeeT\x0dy\xf0\xff\x1a\x08\x00\x00\x00\x00\x06\ +\x00\x00\x00\x09App Name:\x07\x00\x00\ +\x00\x0cCenterWidget\x01\x03\ +\x00\x00\x00\x04mO\x89\xc8\x08\x00\x00\x00\x00\x06\x00\x00\ +\x00\x06Browse\x07\x00\x00\x00\x0cCen\ +terWidget\x01\x03\x00\x00\x00\x06b\ +SS\x05\xff\x01\x08\x00\x00\x00\x00\x06\x00\x00\x00\x07B\ +undle!\x07\x00\x00\x00\x0cCente\ +rWidget\x01\x03\x00\x00\x00\x12bSS\ +\x05v\x84^\x94u(z\x0b^\x8fT\x0dy\xf0\x08\ +\x00\x00\x00\x00\x06\x00\x00\x00\x10Bundled\ + app name\x07\x00\x00\x00\x0cCe\ +nterWidget\x01\x03\x00\x00\x00\x14\ +\x5c\x06bSS\x05\x81\xf3SUN*e\x87N\xf6\ +N-0\x02\x08\x00\x00\x00\x00\x06\x00\x00\x00\x17Bu\ +ndling into one \ +file.\x07\x00\x00\x00\x0cCenter\ +Widget\x01\x03\x00\x00\x00\x14\x5c\x06bS\ +S\x05\x81\xf3SUN*v\xee_UN-0\x02\ +\x08\x00\x00\x00\x00\x06\x00\x00\x00\x19Bundli\ +ng into one fold\ +er.\x07\x00\x00\x00\x0cCenterWi\ +dget\x01\x03\x00\x00\x00\x04n\x05t\x06\x08\x00\ +\x00\x00\x00\x06\x00\x00\x00\x05Clean\x07\x00\x00\ +\x00\x0cCenterWidget\x01\x03\ +\x00\x00\x00\x1cg\x84^\xfaRM\x5c\x06n\x05\x96d\ +\x7f\x13[XN\x0eN4e\xf6v\xee_U0\x02\ +\x08\x00\x00\x00\x00\x06\x00\x00\x007Clean \ +cache and remove\ + temporary files\ + before building\ +.\x07\x00\x00\x00\x0cCenterWidg\ +et\x01\x03\x00\x00\x00\x04\x95\x19\x8b\xef\x08\x00\x00\x00\ +\x00\x06\x00\x00\x00\x05Error\x07\x00\x00\x00\x0c\ +CenterWidget\x01\x03\x00\x00\ +\x00\x08\x96\x90_\x0f[\xfcQe\x08\x00\x00\x00\x00\x06\ +\x00\x00\x00\x0dHidden Impor\ +t\x07\x00\x00\x00\x0cCenterWidg\ +et\x01\x03\x00\x00\x00\x10\x96\x90_\x0f[\xfcQe\ +]\xf2f\xf4e\xb00\x02\x08\x00\x00\x00\x00\x06\x00\x00\ +\x00\x16Hidden import \ +updated.\x07\x00\x00\x00\x0cCen\ +terWidget\x01\x03\x00\x00\x00\x0eb\ +SS\x05\x81\xf3SUN*e\x87N\xf6\x08\x00\x00\ +\x00\x00\x06\x00\x00\x00\x08One File\x07\ +\x00\x00\x00\x0cCenterWidget\ +\x01\x03\x00\x00\x00\x0ebSS\x05\x81\xf3SUN*\ +v\xee_U\x08\x00\x00\x00\x00\x06\x00\x00\x00\x0aOn\ +e Folder\x07\x00\x00\x00\x0cCen\ +terWidget\x01\x03\x00\x00\x00\x10S\ +Uv\xee_U\x00/SUe\x87N\xf6\xff\x1a\x08\ +\x00\x00\x00\x00\x06\x00\x00\x00\x15One Fol\ +der/ One File:\x07\x00\ +\x00\x00\x0cCenterWidget\x01\ +\x03\x00\x00\x00\x10bS_\x00\x81\x1ag,\x8d\xef_\ +\x84\xff\x1a\x00 \x08\x00\x00\x00\x00\x06\x00\x00\x00\x14O\ +pened script pat\ +h: \x07\x00\x00\x00\x0cCenterWi\ +dget\x01\x03\x00\x00\x00NW(\x8b\xe5\x00 \ +\x00P\x00y\x00t\x00h\x00o\x00n\x00 s\xaf\ +X\x83N-O](https://muzi\ @@ -3726,7 +4198,7 @@ e) \xe7\x9a\x84\xe7\xba\xa6\xe6\x9d\x9f\xe4\xb8\x8b\xef\ \xb6\xe8\xbf\x9b\xe8\xa1\x8c\xe5\x86\x8d\xe5\x8f\x91\xe5\xb8\x83\ \xe5\x92\x8c/\xe6\x88\x96\xe4\xbf\xae\xe6\x94\xb9\xe3\x80\x82\ \x0a\x0a\xe7\x89\x88\xe6\x9d\x83\xe6\x89\x80\xe6\x9c\x89 \xc2\ -\xa9 2022-2023 [Mu\ +\xa9 2022-2024 [Mu\ zing \x5c](h\ ttps://muzing.to\ @@ -3742,6 +4214,14 @@ qt_resource_name = b"\ \x00O\xa6S\ \x00I\ \x00c\x00o\x00n\x00s\ +\x00\x04\ +\x00\x06\xc4\xee\ +\x00i\ +\x001\x008\x00n\ +\x00\x08\ +\x0e8\x05}\ +\x00z\ +\x00h\x00_\x00C\x00N\x00.\x00q\x00m\ \x00\x14\ \x01O\xad\x98\ \x00P\ @@ -3779,32 +4259,36 @@ qt_resource_name = b"\ " qt_resource_struct = b"\ -\x00\x00\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x01\ +\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x01\ \x00\x00\x00\x00\x00\x00\x00\x00\ -\x00\x00\x00\x10\x00\x02\x00\x00\x00\x06\x00\x00\x00\x07\ +\x00\x00\x00 \x00\x02\x00\x00\x00\x01\x00\x00\x00\x0e\ \x00\x00\x00\x00\x00\x00\x00\x00\ -\x00\x00\x00\x00\x00\x02\x00\x00\x00\x04\x00\x00\x00\x03\ +\x00\x00\x00\x10\x00\x02\x00\x00\x00\x06\x00\x00\x00\x08\ \x00\x00\x00\x00\x00\x00\x00\x00\ -\x00\x00\x00\xc6\x00\x00\x00\x00\x00K\x00\x00\x80\xaa\ -\x00\x00\x01\x8c\x9a\xb7\xd3\xac\ -\x00\x00\x00\xc6\x00\x00\x00\x00\x00:\x00\x00\xb2A\ -\x00\x00\x01\x8c\x9a\xb7\xd3\xac\ -\x00\x00\x00\xf2\x00\x00\x00\x00\x00K\x00\x00\xe1\x9d\ -\x00\x00\x01\x8c\x9a\xb7\xd3\xac\ -\x00\x00\x00\xf2\x00\x00\x00\x00\x00:\x00\x00\xe4\xb0\ -\x00\x00\x01\x8c\x9a\xb7\xd3\xac\ -\x00\x00\x00\x9a\x00\x00\x00\x00\x00\x01\x00\x00f\x93\ -\x00\x00\x01\x8c\xbd[\xc8Z\ -\x00\x00\x00 \x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\ -\x00\x00\x01\x8c\x9a\xb7\xd3\xaa\ -\x00\x00\x00l\x00\x00\x00\x00\x00\x01\x00\x00\x19\xb6\ -\x00\x00\x01\x8c\x9a\xb7\xd3\xab\ -\x00\x00\x00\xaa\x00\x00\x00\x00\x00\x01\x00\x00w_\ -\x00\x00\x01\x8c\xb3\xa0\x98\xeb\ -\x00\x00\x00~\x00\x00\x00\x00\x00\x01\x00\x00<\x13\ -\x00\x00\x01\x8c\x9a\xb7\xd3\xab\ -\x00\x00\x00N\x00\x00\x00\x00\x00\x01\x00\x00\x0c\xfd\ -\x00\x00\x01\x8c\x9a\xb7\xd3\xaa\ +\x00\x00\x00\x00\x00\x02\x00\x00\x00\x04\x00\x00\x00\x04\ +\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\xea\x00\x00\x00\x00\x00K\x00\x00\x9e\x0c\ +\x00\x00\x01\x8c\x9fP\xce\x07\ +\x00\x00\x00\xea\x00\x00\x002\x00:\x00\x00\xcf\xa3\ +\x00\x00\x01\x8c\x9fP\xce\x07\ +\x00\x00\x01\x16\x00\x00\x00\x00\x00K\x00\x00\xfe\xff\ +\x00\x00\x01\x8c\xd4u\x1b\xc8\ +\x00\x00\x01\x16\x00\x00\x002\x00:\x00\x01\x02\x12\ +\x00\x00\x01\x8c\xd4u\x1b\xcf\ +\x00\x00\x00\xbe\x00\x00\x00\x00\x00\x01\x00\x00\x83\xf5\ +\x00\x00\x01\x8c\xbd\xfa\xbd\x88\ +\x00\x00\x00D\x00\x00\x00\x00\x00\x01\x00\x00\x1db\ +\x00\x00\x01\x8b\xbd\x8a$\x9a\ +\x00\x00\x00\x90\x00\x00\x00\x00\x00\x01\x00\x007\x18\ +\x00\x00\x01\x8cz\xef\xd4\x05\ +\x00\x00\x00\xce\x00\x00\x00\x00\x00\x01\x00\x00\x94\xc1\ +\x00\x00\x01\x8c\xb3\x93\xcb\xe3\ +\x00\x00\x00\xa2\x00\x00\x00\x00\x00\x01\x00\x00Yu\ +\x00\x00\x01\x8c\xbd\xfa\xbd\x88\ +\x00\x00\x00r\x00\x00\x00\x00\x00\x01\x00\x00*_\ +\x00\x00\x01\x8b\xbd\x8a$\x9a\ +\x00\x00\x00.\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\ +\x00\x00\x01\x8c\xd3\xb8s\xe7\ " diff --git a/src/py2exe_gui/Resources/i18n/zh_CN.qm b/src/py2exe_gui/Resources/i18n/zh_CN.qm new file mode 100644 index 0000000..97675eb Binary files /dev/null and b/src/py2exe_gui/Resources/i18n/zh_CN.qm differ diff --git a/src/py2exe_gui/Resources/i18n/zh_CN.ts b/src/py2exe_gui/Resources/i18n/zh_CN.ts new file mode 100644 index 0000000..1dfa8c8 --- /dev/null +++ b/src/py2exe_gui/Resources/i18n/zh_CN.ts @@ -0,0 +1,540 @@ + + + + + AboutDlg + + + About + 关于 + + + + Can't open the About document, try to reinstall this program. + 无法打开关于文档,请尝试重新获取本程序。 + + + + AddDataWindow + + + Add Files + 添加文件 + + + + &New + 新建(&N) + + + + &Browse File + 浏览文件(&B) + + + + Browse &Folder + 浏览目录(&F) + + + + &Delete + 删除(&D) + + + + &OK + 确认(&O) + + + + &Cancel + 取消(&C) + + + + ArgumentsBrowser + + + Copy + 复制 + + + + Export + 导出 + + + + CenterWidget + + + Python entry script path + Python 入口脚本路径 + + + + + Browse + 浏览 + + + + Python script: + 待打包脚本: + + + + App Name: + 项目名称: + + + + Bundled app name + 打包的应用程序名称 + + + + One Folder/ One File: + 单目录/单文件: + + + + One Folder + 打包至单个目录 + + + + One File + 打包至单个文件 + + + + + Add Data Files + 添加数据文件 + + + + + Add Binary Files + 添加二进制文件 + + + + Hidden Import + 隐式导入 + + + + Clean + 清理 + + + + Bundle! + 打包! + + + + Bundling into one folder. + 将打包至单个目录中。 + + + + Bundling into one file. + 将打包至单个文件中。 + + + + Add data files updated. + 添加数据文件已更新。 + + + + Add binary files updated. + 添加二进制文件已更新。 + + + + Hidden import updated. + 隐式导入已更新。 + + + + Clean cache and remove temporary files before building. + 构建前将清除缓存与临时目录。 + + + + Will not delete cache and temporary files. + 不会删除缓存与临时文件。 + + + + Opened script path: + 打开脚本路径: + + + + The app name has been set to: + 已将项目名设置为: + + + + + Error + 错误 + + + + The selection is not a valid Python script file, please reselect it! + 选择的不是有效的Python脚本文件,请重新选择! + + + + Warning + 警告 + + + + Pyinstaller doesn't seem to be installed in this Python environment, still continue? + 在该 Python 环境中似乎没有安装 Pyinstaller,是否仍要继续? + + + + The selection is not a valid Python interpreter, please reselect it! + 选择的不是有效的Python解释器,请重新选择! + + + + IconFileDlg + + + Icon Files (*.ico *.icns) + 图标文件 (*.ico *.icns) + + + + All Files (*) + 所有文件 (*) + + + + App Icon + 图标 + + + + Icon File + 图标文件 + + + + Open + 打开 + + + + Cancel + 取消 + + + + InterpreterFileDlg + + + Python Interpreter + Python 解释器 + + + + Executable File + 可执行文件 + + + + Python Interpreter (python.exe) + Python 解释器 (python.exe) + + + + Executable Files (*.exe) + 可执行文件 (*.exe) + + + + + All Files (*) + 所有文件 (*) + + + + Python Interpreter (python3*) + Python 解释器 (python3*) + + + + MainApp + + + Ready. + 就绪。 + + + + MainWindow + + + &File + 文件(&F) + + + + Import Config From JSON File + 从 JSON 文件中导入配置 + + + + Export Config To JSON File + 导出配置至 JSON 文件 + + + + &Settings + 设置(&S) + + + + &Exit + 退出(&E) + + + + &Help + 帮助(&H) + + + + PyInstaller Documentation + PyInstaller 文档 + + + + PyInstaller Options Details + PyInstaller 选项详情 + + + + Report Bugs + 报告 Bug + + + + &About + 关于(&A) + + + + About This Program + 关于此程序 + + + + About &Qt + 关于 &Qt + + + + MultiItemEditWindow + + + &New + 新建(&N) + + + + &Delete + 删除(&D) + + + + &OK + 确认(&O) + + + + &Cancel + 取消(&C) + + + + MultiPkgEditWindow + + + &Browse packages + 浏览包(&B) + + + + PkgBrowserDlg + + + Installed Packages + 已安装的包 + + + + Name + 这里专指软件包的名称 + 包名 + + + + Version + 版本 + + + + PyinstallerOptionTable + + + Option + 选项 + + + + Description + 描述 + + + + ScriptFileDlg + + + Python Script (*.py *.pyw) + Python 脚本文件 (*.py *.pyw) + + + + All Files (*) + 所有文件 (*) + + + + Python Entry File + Python入口文件 + + + + Python File + Python 文件 + + + + Open + 打开 + + + + Cancel + 取消 + + + + SubProcessDlg + + + Done! + 此处专指PyInstaller运行完成。 + 打包完成! + + + + + Open Dist + 打开输出位置 + + + + Execution ends, but an error occurs and the exit code is + 运行结束,但有错误发生,退出码为 + + + + + + Cancel + 取消 + + + + PyInstaller Error! + PyInstaller 错误! + + + + PyInstaller subprocess output: + PyInstaller 子进程输出: + + + + Please check if you have installed the correct version of PyInstaller or not. + 请检查是否已经安装正确版本的 PyInstaller。 + + + + + Close + 关闭 + + + + WinMacCenterWidget + + + App icon: + 应用图标: + + + + Path to icon file + 图标路径 + + + + Browse + 浏览 + + + + Open a console window for standard I/O + 为标准I/O启用终端 + + + + Terminal will be enabled. + 将为打包程序的 stdio 启用终端。 + + + + Terminal will not be enabled. + 不会为打包程序的 stdio 启用终端。 + + + + Opened icon path: + 打开的图标路径: + + + + Error + 错误 + + + + The selection is not a valid icon file, please re-select it! + 选择的不是有效的图标文件,请重新选择! + + + diff --git a/src/py2exe_gui/Widgets/add_data_widget.py b/src/py2exe_gui/Widgets/add_data_widget.py index 4d9f83c..9d1c7d9 100644 --- a/src/py2exe_gui/Widgets/add_data_widget.py +++ b/src/py2exe_gui/Widgets/add_data_widget.py @@ -6,14 +6,12 @@ __all__ = ["AddDataWindow"] -import sys from pathlib import Path from typing import Optional from PySide6.QtCore import QItemSelectionModel, Qt, Signal, Slot from PySide6.QtGui import QIcon, QPixmap from PySide6.QtWidgets import ( - QApplication, QFileDialog, QHBoxLayout, QHeaderView, @@ -24,8 +22,10 @@ from PySide6.QtWidgets import ( QWidget, ) +from ..Utilities import QObjTr -class AddDataWindow(QWidget): + +class AddDataWindow(QObjTr, QWidget): """用于提供 PyInstaller --add-data 和 --add-binary 功能的窗口""" # 类型别名 @@ -68,7 +68,7 @@ class AddDataWindow(QWidget): def _setup_ui(self) -> None: """处理 UI 内容""" - self.setWindowTitle("添加文件") + self.setWindowTitle(AddDataWindow.tr("Add Files")) self.setMinimumWidth(550) self.setWindowIcon(QIcon(QPixmap(":/Icons/Py2exe-GUI_icon_72px"))) @@ -85,13 +85,13 @@ class AddDataWindow(QWidget): 1, QHeaderView.ResizeMode.ResizeToContents ) - self.new_btn.setText("新建(&N)") - self.browse_btn.setText("浏览文件(&B)") - self.browse_dir_btn.setText("浏览目录(&F)") - self.delete_btn.setText("删除(&D)") + self.new_btn.setText(AddDataWindow.tr("&New")) + self.browse_btn.setText(AddDataWindow.tr("&Browse File")) + self.browse_dir_btn.setText(AddDataWindow.tr("Browse &Folder")) + self.delete_btn.setText(AddDataWindow.tr("&Delete")) - self.ok_btn.setText("确定") - self.cancel_btn.setText("取消") + self.ok_btn.setText(AddDataWindow.tr("&OK")) + self.cancel_btn.setText(AddDataWindow.tr("&Cancel")) # noinspection DuplicatedCode def _setup_layout(self) -> None: @@ -119,6 +119,7 @@ class AddDataWindow(QWidget): self.setLayout(main_layout) + # noinspection DuplicatedCode def _connect_slots(self) -> None: """构建各槽函数、连接信号""" @@ -212,7 +213,6 @@ class AddDataWindow(QWidget): self.data_selected.emit(self._submit()) self.close() - # noinspection DuplicatedCode self.new_btn.clicked.connect(handle_new_btn) self.delete_btn.clicked.connect(handle_delete_btn) self.browse_btn.clicked.connect(handel_browse_btn) @@ -261,10 +261,3 @@ class AddDataWindow(QWidget): ) self.item_table.setItem(row, 0, source_item) self.item_table.setItem(row, 1, QTableWidgetItem(data_item[1])) - - -if __name__ == "__main__": - app = QApplication(sys.argv) - window = AddDataWindow() - window.show() - sys.exit(app.exec()) diff --git a/src/py2exe_gui/Widgets/arguments_browser.py b/src/py2exe_gui/Widgets/arguments_browser.py index 12ac2f8..c55ddba 100644 --- a/src/py2exe_gui/Widgets/arguments_browser.py +++ b/src/py2exe_gui/Widgets/arguments_browser.py @@ -4,7 +4,10 @@ """此模块主要包含用于在界面上预览显示 PyInstaller 命令选项的 `ArgumentsBrowser` 类 """ -__all__ = ["get_line_continuation", "ArgumentsBrowser"] +__all__ = [ + "get_line_continuation", + "ArgumentsBrowser", +] from typing import Optional @@ -12,6 +15,7 @@ from PySide6.QtGui import QContextMenuEvent from PySide6.QtWidgets import QMenu, QTextBrowser, QWidget from ..Constants import RUNTIME_INFO, Platform +from ..Utilities import QObjTr # 一组适合浅色背景的颜色 colors = ["#FD6D5A", "#FEB40B", "#6DC354", "#994487", "#518CD8", "#443295"] @@ -46,7 +50,7 @@ def wrap_font_tag(raw_text: str, color: str, **kwargs): return f"{raw_text}" -class ArgumentsBrowser(QTextBrowser): +class ArgumentsBrowser(QObjTr, QTextBrowser): """针对命令行参数列表特别优化的文本浏览器""" def __init__(self, parent: Optional[QWidget] = None) -> None: @@ -58,9 +62,9 @@ class ArgumentsBrowser(QTextBrowser): # 右键菜单 self.context_menu = QMenu(self) - copy_action = self.context_menu.addAction("复制") + copy_action = self.context_menu.addAction(ArgumentsBrowser.tr("Copy")) copy_action.triggered.connect(self._handle_copy_action) - export_action = self.context_menu.addAction("导出") + export_action = self.context_menu.addAction(ArgumentsBrowser.tr("Export")) export_action.triggered.connect(self._handle_export_action) def contextMenuEvent(self, event: QContextMenuEvent) -> None: diff --git a/src/py2exe_gui/Widgets/center_widget.py b/src/py2exe_gui/Widgets/center_widget.py index 0f81681..057dda4 100644 --- a/src/py2exe_gui/Widgets/center_widget.py +++ b/src/py2exe_gui/Widgets/center_widget.py @@ -31,7 +31,7 @@ from PySide6.QtWidgets import ( from ..Constants import PyInstOpt from ..Core import InterpreterValidator -from ..Utilities import ALL_PY_ENVs, PyEnv +from ..Utilities import ALL_PY_ENVs, PyEnv, QObjTr from .add_data_widget import AddDataWindow from .arguments_browser import ArgumentsBrowser from .dialog_widgets import ( @@ -45,7 +45,7 @@ from .pyenv_combobox import PyEnvComboBox from .pyinstaller_option_widget import PyinstallerOptionTable -class CenterWidget(QWidget): +class CenterWidget(QObjTr, QWidget): """主界面的中央控件 此类为可以实例化的基类,适用于所有平台。 @@ -54,7 +54,6 @@ class CenterWidget(QWidget): # 自定义信号 option_selected = QtCore.Signal(tuple) # 用户通过界面控件选择选项后发射此信号 - # option_selected 实际类型为 tuple[PyinstallerArgs, str] def __init__(self, parent: QMainWindow) -> None: @@ -122,38 +121,40 @@ class CenterWidget(QWidget): def _setup_ui(self) -> None: """设置各种控件的属性""" - self.script_path_label.setText("待打包脚本:") + self.script_path_label.setText(CenterWidget.tr("Python script:")) self.script_path_le.setReadOnly(True) - self.script_path_le.setPlaceholderText("Python入口文件路径") - self.script_browse_btn.setText("浏览") + self.script_path_le.setPlaceholderText( + CenterWidget.tr("Python entry script path") + ) + self.script_browse_btn.setText(CenterWidget.tr("Browse")) self.script_browse_btn.setFixedWidth(80) - self.pyenv_browse_btn.setText("浏览") + self.pyenv_browse_btn.setText(CenterWidget.tr("Browse")) self.pyenv_browse_btn.setFixedWidth(80) - self.project_name_label.setText("项目名称:") - self.project_name_le.setPlaceholderText("打包的应用程序名称") + self.project_name_label.setText(CenterWidget.tr("App Name:")) + self.project_name_le.setPlaceholderText(CenterWidget.tr("Bundled app name")) - self.fd_label.setText("单文件/单目录:") - self.one_dir_btn.setText("打包至单个目录") + self.fd_label.setText(CenterWidget.tr("One Folder/ One File:")) + self.one_dir_btn.setText(CenterWidget.tr("One Folder")) self.one_dir_btn.setChecked(True) # 默认值 - self.one_file_btn.setText("打包至单个文件") + self.one_file_btn.setText(CenterWidget.tr("One File")) self.fd_group.addButton(self.one_dir_btn, 0) self.fd_group.addButton(self.one_file_btn, 1) - self.add_data_btn.setText("添加数据文件") - self.add_binary_btn.setText("添加二进制文件") - self.add_data_dlg.setWindowTitle("添加数据文件") - self.add_binary_dlg.setWindowTitle("添加二进制文件") + self.add_data_btn.setText(CenterWidget.tr("Add Data Files")) + self.add_binary_btn.setText(CenterWidget.tr("Add Binary Files")) + self.add_data_dlg.setWindowTitle(CenterWidget.tr("Add Data Files")) + self.add_binary_dlg.setWindowTitle(CenterWidget.tr("Add Binary Files")) - self.hidden_import_btn.setText("隐式导入") - self.hidden_import_dlg.setWindowTitle("Hidden import") + self.hidden_import_btn.setText(CenterWidget.tr("Hidden Import")) + self.hidden_import_dlg.setWindowTitle("Hidden Import") - self.clean_checkbox.setText("清理") + self.clean_checkbox.setText(CenterWidget.tr("Clean")) self.clean_checkbox.setChecked(False) self.pyinstaller_args_browser.setMaximumHeight(80) - self.run_packaging_btn.setText("打包!") + self.run_packaging_btn.setText(CenterWidget.tr("Bundle!")) self.run_packaging_btn.setEnabled(False) # 将 PyInstaller 选项详情设置成各控件的 ToolTip @@ -183,16 +184,13 @@ class CenterWidget(QWidget): self.option_selected.emit((PyInstOpt.script_path, file_path)) - @QtCore.Slot(int) - def handle_pyenv_change(new_index: int) -> None: - """处理用户通过选择不同的 Python 解释器时的响应 - - :param new_index: 选择的 Python 解释器在 `pyenv_combobox` 中的索引 - """ + @QtCore.Slot() + def handle_pyenv_change() -> None: + """处理用户通过选择不同的 Python 解释器时的响应""" current_pyenv = self.pyenv_combobox.get_current_pyenv() - # TODO 设置为懒加载,用户打开“已安装的包”对话框时才运行 + # 首次调用 current_pyenv.installed_packages 为重大性能热点,优化时应首先考虑 self.pkg_browser_dlg.load_pkg_list(current_pyenv.installed_packages) @QtCore.Slot() @@ -211,10 +209,10 @@ class CenterWidget(QWidget): if btn_id == 0: self.option_selected.emit((PyInstOpt.FD, "--onedir")) - self.parent_widget.statusBar().showMessage("将打包至单个目录中") + self.show_status_msg(CenterWidget.tr("Bundling into one folder.")) elif btn_id == 1: self.option_selected.emit((PyInstOpt.FD, "--onefile")) - self.parent_widget.statusBar().showMessage("将打包至单个文件中") + self.show_status_msg(CenterWidget.tr("Bundling into one file.")) @QtCore.Slot() def handle_add_data_btn_clicked() -> None: @@ -228,7 +226,7 @@ class CenterWidget(QWidget): """用户完成了添加数据操作的槽函数""" self.data_item_list = data_item_list - self.parent_widget.statusBar().showMessage("添加数据文件已更新") + self.show_status_msg(CenterWidget.tr("Add data files updated.")) self.option_selected.emit((PyInstOpt.add_data, data_item_list)) @QtCore.Slot() @@ -243,7 +241,7 @@ class CenterWidget(QWidget): """用户完成了添加二进制文件操作的槽函数""" self.binary_item_list = binary_item_list - self.parent_widget.statusBar().showMessage("添加二进制文件已更新") + self.show_status_msg(CenterWidget.tr("Add binary files updated.")) self.option_selected.emit((PyInstOpt.add_binary, binary_item_list)) @QtCore.Slot() @@ -260,7 +258,7 @@ class CenterWidget(QWidget): """ self.hidden_import_list = hidden_import_list - self.parent_widget.statusBar().showMessage("隐式导入已更新") + self.show_status_msg(CenterWidget.tr("Hidden import updated.")) self.option_selected.emit((PyInstOpt.hidden_import, hidden_import_list)) @QtCore.Slot(bool) @@ -272,10 +270,16 @@ class CenterWidget(QWidget): if selected: self.option_selected.emit((PyInstOpt.clean, "--clean")) - self.parent_widget.statusBar().showMessage("构建前将清理缓存与临时文件") + self.show_status_msg( + CenterWidget.tr( + "Clean cache and remove temporary files before building." + ) + ) else: self.option_selected.emit((PyInstOpt.clean, "")) - self.parent_widget.statusBar().showMessage("不会删除缓存与临时文件") + self.show_status_msg( + CenterWidget.tr("Will not delete cache and temporary files.") + ) # 连接信号与槽 # 入口脚本文件 @@ -285,7 +289,7 @@ class CenterWidget(QWidget): # 添加与选择 Python 解释器 self.pyenv_browse_btn.clicked.connect(self.itp_dlg.open) self.itp_dlg.fileSelected.connect(self._handle_itp_file_selected) - handle_pyenv_change(0) # 显式调用一次,为默认项设置相关内容 + handle_pyenv_change() # 显式调用一次,为默认项设置相关内容 self.pyenv_combobox.currentIndexChanged.connect(handle_pyenv_change) # 项目名称 @@ -366,15 +370,18 @@ class CenterWidget(QWidget): if option_key == PyInstOpt.script_path: script_path = Path(option_value) self.script_path_le.setText(script_path.name) - self.parent_widget.statusBar().showMessage( - f"打开脚本路径:{str(script_path.absolute())}" + self.show_status_msg( + CenterWidget.tr("Opened script path: ") + + f"{str(script_path.absolute())}" ) self.add_data_dlg.set_work_dir(script_path.parent) self.add_binary_dlg.set_work_dir(script_path.parent) elif option_key == PyInstOpt.out_name: self.project_name_le.setText(option_value) - self.parent_widget.statusBar().showMessage(f"已将项目名设置为:{option_value}") + self.show_status_msg( + CenterWidget.tr("The app name has been set to:") + f"{option_value}" + ) @QtCore.Slot(PyInstOpt) def handle_option_error(self, option: PyInstOpt) -> None: @@ -389,8 +396,11 @@ class CenterWidget(QWidget): # 警告对话框 result = QMessageBox.critical( self, - "错误", - "选择的不是有效的Python脚本文件,请重新选择!", + CenterWidget.tr("Error"), + CenterWidget.tr( + "The selection is not a valid Python script file, " + "please reselect it!" + ), QMessageBox.StandardButton.Cancel, QMessageBox.StandardButton.Ok, ) @@ -437,8 +447,11 @@ class CenterWidget(QWidget): # TODO 自实现 MessageBox,包含"仍要使用"、"取消"、"尝试安装PyInstaller"三个按钮 result = QMessageBox.warning( self, - "警告", - "在该 Python 环境中似乎没有安装 Pyinstaller," "是否仍要继续?", + CenterWidget.tr("Warning"), + CenterWidget.tr( + "Pyinstaller doesn't seem to be installed in this Python " + "environment, still continue?" + ), QMessageBox.StandardButton.Ok, QMessageBox.StandardButton.Cancel, ) @@ -456,14 +469,25 @@ class CenterWidget(QWidget): self.itp_dlg.close() result = QMessageBox.critical( self, - "错误", - "选择的不是有效的Python解释器,请重新选择!", + CenterWidget.tr("Error"), + CenterWidget.tr( + "The selection is not a valid Python interpreter, " + "please reselect it!" + ), QMessageBox.StandardButton.Cancel, QMessageBox.StandardButton.Ok, ) if result == QMessageBox.StandardButton.Ok: self.itp_dlg.exec() + def show_status_msg(self, msg: str) -> None: + """向父控件(QMainWindow)的状态栏显示信息 + + :param msg: 要在状态栏显示的信息 + """ + + self.parent_widget.statusBar().showMessage(msg) + class WinMacCenterWidget(CenterWidget): """包含 Windows 与 MacOS 特有功能的主界面中央控件 @@ -492,13 +516,15 @@ class WinMacCenterWidget(CenterWidget): super()._setup_ui() - self.icon_path_label.setText("应用图标:") + self.icon_path_label.setText(WinMacCenterWidget.tr("App icon:")) self.icon_path_le.setReadOnly(True) - self.icon_path_le.setPlaceholderText("图标文件路径") - self.icon_browse_btn.setText("浏览") + self.icon_path_le.setPlaceholderText(WinMacCenterWidget.tr("Path to icon file")) + self.icon_browse_btn.setText(WinMacCenterWidget.tr("Browse")) self.icon_browse_btn.setFixedWidth(80) - self.console_checkbox.setText("为标准I/O启用终端") + self.console_checkbox.setText( + WinMacCenterWidget.tr("Open a console window for standard I/O") + ) self.console_checkbox.setChecked(True) # 默认值 # 将 PyInstaller 选项详情设置成各控件的 ToolTip @@ -520,8 +546,8 @@ class WinMacCenterWidget(CenterWidget): @QtCore.Slot(str) def handle_icon_file_selected(file_path: str) -> None: - """ - 图标文件完成选择的槽函数 \n + """图标文件完成选择的槽函数 + :param file_path: 图标路径 """ @@ -536,10 +562,12 @@ class WinMacCenterWidget(CenterWidget): if console: self.option_selected.emit((PyInstOpt.console, "--console")) - self.parent_widget.statusBar().showMessage("将为打包程序的 stdio 启用终端") + self.show_status_msg(WinMacCenterWidget.tr("Terminal will be enabled.")) else: self.option_selected.emit((PyInstOpt.console, "--windowed")) - self.parent_widget.statusBar().showMessage("不会为打包程序的 stdio 启用终端") + self.show_status_msg( + WinMacCenterWidget.tr("Terminal will not be enabled.") + ) # 图标文件 self.icon_browse_btn.clicked.connect(self.icon_file_dlg.open) @@ -582,9 +610,11 @@ class WinMacCenterWidget(CenterWidget): elif option_key == PyInstOpt.icon_path: icon_path = Path(option_value) self.icon_path_le.setText(icon_path.name) - self.parent_widget.statusBar().showMessage( - f"打开图标路径:{str(icon_path.absolute())}" + msg = ( + WinMacCenterWidget.tr("Opened icon path: ") + + f"{str(icon_path.absolute())}" ) + self.show_status_msg(msg) @QtCore.Slot(PyInstOpt) def handle_option_error(self, option: PyInstOpt) -> None: @@ -599,8 +629,10 @@ class WinMacCenterWidget(CenterWidget): self.icon_file_dlg.close() result = QMessageBox.critical( self.parent_widget, - "错误", - "选择的不是有效的图标文件,请重新选择!", + WinMacCenterWidget.tr("Error"), + WinMacCenterWidget.tr( + "The selection is not a valid icon file, please re-select it!" + ), QMessageBox.StandardButton.Cancel, QMessageBox.StandardButton.Ok, ) diff --git a/src/py2exe_gui/Widgets/dialog_widgets.py b/src/py2exe_gui/Widgets/dialog_widgets.py index 29329c1..27bd24a 100644 --- a/src/py2exe_gui/Widgets/dialog_widgets.py +++ b/src/py2exe_gui/Widgets/dialog_widgets.py @@ -29,10 +29,10 @@ from PySide6.QtWidgets import ( ) from ..Constants import RUNTIME_INFO, Platform -from ..Utilities import QtFileOpen +from ..Utilities import QObjTr, QtFileOpen -class ScriptFileDlg(QFileDialog): +class ScriptFileDlg(QObjTr, QFileDialog): """用于获取入口脚本文件的对话框""" def __init__(self, parent: Optional[QWidget] = None) -> None: @@ -44,15 +44,24 @@ class ScriptFileDlg(QFileDialog): self.setAcceptMode(QFileDialog.AcceptMode.AcceptOpen) self.setViewMode(QFileDialog.ViewMode.Detail) - self.setNameFilters(("Python脚本文件 (*.py *.pyw)", "所有文件 (*)")) + self.setNameFilters( + ( + ScriptFileDlg.tr("Python Script (*.py *.pyw)"), + ScriptFileDlg.tr("All Files (*)"), + ) + ) self.setFileMode(QFileDialog.FileMode.ExistingFiles) - self.setLabelText(QFileDialog.DialogLabel.FileName, "Python入口文件") - self.setLabelText(QFileDialog.DialogLabel.FileType, "Python文件") - self.setLabelText(QFileDialog.DialogLabel.Accept, "打开") - self.setLabelText(QFileDialog.DialogLabel.Reject, "取消") + self.setLabelText( + QFileDialog.DialogLabel.FileName, ScriptFileDlg.tr("Python Entry File") + ) + self.setLabelText( + QFileDialog.DialogLabel.FileType, ScriptFileDlg.tr("Python File") + ) + self.setLabelText(QFileDialog.DialogLabel.Accept, ScriptFileDlg.tr("Open")) + self.setLabelText(QFileDialog.DialogLabel.Reject, ScriptFileDlg.tr("Cancel")) -class IconFileDlg(QFileDialog): +class IconFileDlg(QObjTr, QFileDialog): """用于获取应用图标文件的对话框""" def __init__(self, parent: Optional[QWidget] = None) -> None: @@ -64,15 +73,20 @@ class IconFileDlg(QFileDialog): self.setAcceptMode(QFileDialog.AcceptMode.AcceptOpen) self.setViewMode(QFileDialog.ViewMode.Detail) - self.setNameFilters(("图标文件 (*.ico *.icns)", "所有文件 (*)")) + self.setNameFilters( + ( + IconFileDlg.tr("Icon Files (*.ico *.icns)"), + IconFileDlg.tr("All Files (*)"), + ) + ) self.setFileMode(QFileDialog.FileMode.ExistingFile) - self.setLabelText(QFileDialog.DialogLabel.FileName, "图标") - self.setLabelText(QFileDialog.DialogLabel.FileType, "图标文件") - self.setLabelText(QFileDialog.DialogLabel.Accept, "打开") - self.setLabelText(QFileDialog.DialogLabel.Reject, "取消") + self.setLabelText(QFileDialog.DialogLabel.FileName, IconFileDlg.tr("App Icon")) + self.setLabelText(QFileDialog.DialogLabel.FileType, IconFileDlg.tr("Icon File")) + self.setLabelText(QFileDialog.DialogLabel.Accept, IconFileDlg.tr("Open")) + self.setLabelText(QFileDialog.DialogLabel.Reject, IconFileDlg.tr("Cancel")) -class InterpreterFileDlg(QFileDialog): +class InterpreterFileDlg(QObjTr, QFileDialog): """用于获取 Python 解释器可执行文件的对话框""" def __init__(self, parent: Optional[QWidget] = None) -> None: @@ -85,17 +99,32 @@ class InterpreterFileDlg(QFileDialog): self.setAcceptMode(QFileDialog.AcceptMode.AcceptOpen) self.setViewMode(QFileDialog.ViewMode.Detail) self.setFileMode(QFileDialog.FileMode.ExistingFile) - self.setLabelText(QFileDialog.DialogLabel.FileName, "Python解释器") - self.setLabelText(QFileDialog.DialogLabel.FileType, "可执行文件") + self.setLabelText( + QFileDialog.DialogLabel.FileName, + InterpreterFileDlg.tr("Python Interpreter"), + ) + self.setLabelText( + QFileDialog.DialogLabel.FileType, InterpreterFileDlg.tr("Executable File") + ) - # 进行一定的文件过滤 - # 目前已知的可行方法只有 Windows 下按文件后缀,Linux 下似乎不可行 - # https://stackoverflow.com/questions/50213049/ if RUNTIME_INFO.platform == Platform.windows: - self.setNameFilters(("可执行文件 (*.exe)", "所有文件 (*)")) + self.setNameFilters( + ( + InterpreterFileDlg.tr("Python Interpreter (python.exe)"), + InterpreterFileDlg.tr("Executable Files (*.exe)"), + InterpreterFileDlg.tr("All Files (*)"), + ) + ) + else: + self.setNameFilters( + ( + InterpreterFileDlg.tr("Python Interpreter (python3*)"), + InterpreterFileDlg.tr("All Files (*)"), + ) + ) -class AboutDlg(QMessageBox): +class AboutDlg(QObjTr, QMessageBox): """用于显示关于信息的对话框""" def __init__(self, parent: Optional[QWidget] = None) -> None: @@ -106,7 +135,7 @@ class AboutDlg(QMessageBox): super().__init__(parent) self._about_text: str = "" - self.setWindowTitle("关于") + self.setWindowTitle(AboutDlg.tr("About")) self.setStandardButtons(QMessageBox.StandardButton.Ok) self.setTextFormat(Qt.TextFormat.MarkdownText) self.setText(self.about_text) @@ -124,13 +153,17 @@ class AboutDlg(QMessageBox): with QtFileOpen(":/Texts/About_Text", encoding="utf-8") as about_file: self._about_text = about_file.read() except OSError as e: - warnings.warn(f"无法打开关于文档,错误信息:{e}", RuntimeWarning, stacklevel=1) - self._about_text = "无法打开关于文档,请尝试重新获取本程序。" + warnings.warn( + f"Cannot open About document: {e}", RuntimeWarning, stacklevel=1 + ) + self._about_text = AboutDlg.tr( + "Can't open the About document, try to reinstall this program." + ) return self._about_text -class PkgBrowserDlg(QDialog): +class PkgBrowserDlg(QObjTr, QDialog): """浏览已安装的所有包的对话框""" def __init__(self, parent: Optional[QWidget] = None) -> None: @@ -148,11 +181,13 @@ class PkgBrowserDlg(QDialog): def _setup_ui(self) -> None: """处理 UI""" - self.setWindowTitle("已安装的包") + self.setWindowTitle(PkgBrowserDlg.tr("Installed Packages")) self.setWindowIcon(QIcon(QPixmap(":/Icons/Python_128px"))) self.pkg_table.setColumnCount(2) - self.pkg_table.setHorizontalHeaderLabels(["包名", "版本"]) + self.pkg_table.setHorizontalHeaderLabels( + [PkgBrowserDlg.tr("Name"), PkgBrowserDlg.tr("Version")] + ) self.pkg_table.horizontalHeader().setSectionResizeMode( 0, QHeaderView.ResizeMode.Stretch ) diff --git a/src/py2exe_gui/Widgets/main_window.py b/src/py2exe_gui/Widgets/main_window.py index c06e9c1..34e543b 100644 --- a/src/py2exe_gui/Widgets/main_window.py +++ b/src/py2exe_gui/Widgets/main_window.py @@ -13,9 +13,9 @@ from PySide6.QtGui import QDesktopServices, QIcon, QPixmap from PySide6.QtWidgets import QApplication, QLabel, QMainWindow, QMenuBar, QStatusBar from ..Constants import RUNTIME_INFO, APP_URLs, AppConstant, Platform +from ..Utilities.qobject_tr import QObjTr from .center_widget import CenterWidget, WinMacCenterWidget from .dialog_widgets import AboutDlg -from .pyinstaller_option_widget import PyinstallerOptionTable def open_url(url: str) -> None: @@ -27,7 +27,7 @@ def open_url(url: str) -> None: QDesktopServices.openUrl(QUrl(url)) -class MainWindow(QMainWindow): +class MainWindow(QObjTr, QMainWindow): """ 主界面主窗口 """ @@ -64,29 +64,32 @@ class MainWindow(QMainWindow): def _setup_menu_bar(self) -> None: """配置主窗口菜单栏""" - file_menu = self.menu_bar.addMenu("文件(&F)") - file_menu.addAction("打开打包任务") # 暂时只为占位 - file_menu.addAction("保存当前打包任务") # 暂时只为占位 + file_menu = self.menu_bar.addMenu(MainWindow.tr("&File")) + file_menu.addAction(MainWindow.tr("Import Config From JSON File")) # 暂时只为占位 + file_menu.addAction(MainWindow.tr("Export Config To JSON File")) # 暂时只为占位 file_menu.addSeparator() - file_menu.addAction("设置") # 暂时只为占位 + file_menu.addAction(MainWindow.tr("&Settings")) # 暂时只为占位 file_menu.addSeparator() - file_menu.addAction("退出(&X)", self.close) + file_menu.addAction(MainWindow.tr("&Exit"), self.close) - help_menu = self.menu_bar.addMenu("帮助(&H)") + help_menu = self.menu_bar.addMenu(MainWindow.tr("&Help")) help_menu.addAction( - "PyInstaller官方文档", + MainWindow.tr("PyInstaller Documentation"), lambda: open_url(APP_URLs["Pyinstaller_doc"]), ) help_menu.addAction( - "PyInstaller选项详情", self.center_widget.pyinstaller_option_table.show + MainWindow.tr("PyInstaller Options Details"), + self.center_widget.pyinstaller_option_table.show, ) help_menu.addSeparator() - help_menu.addAction("报告Bug", lambda: open_url(APP_URLs["BugTracker"])) + help_menu.addAction( + MainWindow.tr("Report Bugs"), lambda: open_url(APP_URLs["BugTracker"]) + ) - about_menu = self.menu_bar.addMenu("关于(&A)") - about_menu.addAction("关于本程序", AboutDlg(self).exec) - about_menu.addAction("关于 &Qt", QApplication.aboutQt) + about_menu = self.menu_bar.addMenu(MainWindow.tr("&About")) + about_menu.addAction(MainWindow.tr("About This Program"), AboutDlg(self).exec) + about_menu.addAction(MainWindow.tr("About &Qt"), QApplication.aboutQt) def _setup_status_bar(self) -> None: """配置主窗口状态栏""" diff --git a/src/py2exe_gui/Widgets/multi_item_edit_widget.py b/src/py2exe_gui/Widgets/multi_item_edit_widget.py index e94e090..3e7b33c 100644 --- a/src/py2exe_gui/Widgets/multi_item_edit_widget.py +++ b/src/py2exe_gui/Widgets/multi_item_edit_widget.py @@ -8,7 +8,10 @@ `MultiPkgEditWindow` 继承自 `MultiItemEditWindow`,多了一个浏览当前 Python 环境中已安装 Python 包的功能 """ -__all__ = ["MultiItemEditWindow", "MultiPkgEditWindow"] +__all__ = [ + "MultiItemEditWindow", + "MultiPkgEditWindow", +] from typing import Optional @@ -23,10 +26,11 @@ from PySide6.QtWidgets import ( QWidget, ) +from ..Utilities import QObjTr from .dialog_widgets import PkgBrowserDlg -class MultiItemEditWindow(QWidget): +class MultiItemEditWindow(QObjTr, QWidget): """用于添加多个条目的窗口控件,实现如 --hidden-import、--collect-submodules 等功能""" items_selected = Signal(list) # 用户在添加条目窗口完成所有编辑后,提交的信号.完整数据类型为 list[str] @@ -65,11 +69,11 @@ class MultiItemEditWindow(QWidget): self.setWindowIcon(QIcon(QPixmap(":/Icons/Py2exe-GUI_icon_72px"))) - self.new_btn.setText("新建(&N)") - self.delete_btn.setText("删除(&D)") + self.new_btn.setText(MultiItemEditWindow.tr("&New")) + self.delete_btn.setText(MultiItemEditWindow.tr("&Delete")) - self.ok_btn.setText("确定") - self.cancel_btn.setText("取消") + self.ok_btn.setText(MultiItemEditWindow.tr("&OK")) + self.cancel_btn.setText(MultiItemEditWindow.tr("&Cancel")) # noinspection DuplicatedCode def _setup_layout(self) -> None: @@ -185,7 +189,7 @@ class MultiPkgEditWindow(MultiItemEditWindow): """处理 UI""" super()._setup_ui() - self.browse_pkg_button.setText("浏览包(&B)") + self.browse_pkg_button.setText(MultiPkgEditWindow.tr("&Browse packages")) def _setup_layout(self) -> None: """构建与设置布局管理器""" diff --git a/src/py2exe_gui/Widgets/pyinstaller_option_widget.py b/src/py2exe_gui/Widgets/pyinstaller_option_widget.py index e012cca..f7c2c08 100644 --- a/src/py2exe_gui/Widgets/pyinstaller_option_widget.py +++ b/src/py2exe_gui/Widgets/pyinstaller_option_widget.py @@ -7,7 +7,10 @@ `PyinstallerOptionTable` 类是用于显示 PyInstaller 命令行选项的表格控件窗口,界面有待进一步优化 """ -__all__ = ["load_pyinst_options", "PyinstallerOptionTable"] +__all__ = [ + "load_pyinst_options", + "PyinstallerOptionTable", +] import warnings @@ -16,7 +19,7 @@ from PySide6.QtGui import QPixmap from PySide6.QtWidgets import QHeaderView, QTableWidget, QTableWidgetItem from ..Constants import RUNTIME_INFO -from ..Utilities import QtFileOpen +from ..Utilities import QObjTr, QtFileOpen def load_pyinst_options() -> dict[str, str]: @@ -33,7 +36,7 @@ def load_pyinst_options() -> dict[str, str]: option_file_text = option_file.read() except OSError as e: warnings.warn( - f"PyInstaller Options 加载失败,错误信息:{e}", RuntimeWarning, stacklevel=1 + f"Failed to load PyInstaller Options: {e}", RuntimeWarning, stacklevel=1 ) return dict() @@ -57,7 +60,7 @@ def load_pyinst_options() -> dict[str, str]: return option_dict -class PyinstallerOptionTable(QTableWidget): +class PyinstallerOptionTable(QObjTr, QTableWidget): """用于显示 PyInstaller 命令行选项的表格控件""" def __init__(self) -> None: @@ -68,7 +71,12 @@ class PyinstallerOptionTable(QTableWidget): self.setMinimumSize(700, 430) self.setWindowIcon(QPixmap(":/Icons/PyInstaller")) self.setColumnCount(2) - self.setHorizontalHeaderLabels(["选项", "描述"]) + self.setHorizontalHeaderLabels( + [ + PyinstallerOptionTable.tr("Option"), + PyinstallerOptionTable.tr("Description"), + ] + ) self.horizontalHeader().setSectionResizeMode(1, QHeaderView.ResizeMode.Stretch) # 存储选项信息的字典 diff --git a/src/py2exe_gui/Widgets/subprocess_widget.py b/src/py2exe_gui/Widgets/subprocess_widget.py index b74006e..56ef438 100644 --- a/src/py2exe_gui/Widgets/subprocess_widget.py +++ b/src/py2exe_gui/Widgets/subprocess_widget.py @@ -18,9 +18,10 @@ from PySide6.QtWidgets import ( ) from ..Core.subprocess_tool import SubProcessTool +from ..Utilities import QObjTr -class SubProcessDlg(QDialog): +class SubProcessDlg(QObjTr, QDialog): """用于显示子进程信息的对话框""" def __init__(self, parent: QWidget) -> None: @@ -64,7 +65,7 @@ class SubProcessDlg(QDialog): if output_type == SubProcessTool.OutputType.STATE: self.info_label.setText(output_text) if output_text == "The process is running...": - self.multifunction_btn.setText("取消") + self.multifunction_btn.setText(SubProcessDlg.tr("Cancel")) elif ( output_type == SubProcessTool.OutputType.STDOUT @@ -74,20 +75,32 @@ class SubProcessDlg(QDialog): elif output_type == SubProcessTool.OutputType.FINISHED: if output_text == "0": - self.info_label.setText("打包完成!") - self.multifunction_btn.setText("打开输出位置") + self.info_label.setText(SubProcessDlg.tr("Done!")) + self.multifunction_btn.setText(SubProcessDlg.tr("Open Dist")) else: - self.info_label.setText(f"运行结束,但有错误发生,退出码为 {output_text}") - self.multifunction_btn.setText("取消") + self.info_label.setText( + SubProcessDlg.tr( + "Execution ends, but an error occurs and the exit code is" + ) + + f"{output_text}" + ) + self.multifunction_btn.setText(SubProcessDlg.tr("Cancel")) elif output_type == SubProcessTool.OutputType.ERROR: - self.info_label.setText("PyInstaller错误!") - self.text_browser.append(f"PyInstaller 子进程输出信息:{output_text}") - self.text_browser.append("请检查是否已经安装正确版本的 PyInstaller") - self.multifunction_btn.setText("关闭") + self.info_label.setText(SubProcessDlg.tr("PyInstaller Error!")) + self.text_browser.append( + SubProcessDlg.tr("PyInstaller subprocess output:") + f"{output_text}" + ) + self.text_browser.append( + SubProcessDlg.tr( + "Please check if you have installed " + "the correct version of PyInstaller or not." + ) + ) + self.multifunction_btn.setText(SubProcessDlg.tr("Close")) elif not isinstance(output_type, SubProcessTool.OutputType): - raise ValueError(f"不支持的输出类型:{output_type}") + raise ValueError(f"Unsupported output type: {output_type}") def closeEvent(self, event: QCloseEvent) -> None: """重写关闭事件,进行收尾清理 diff --git a/src/py2exe_gui/__main__.py b/src/py2exe_gui/__main__.py index d80a38b..24f2c58 100644 --- a/src/py2exe_gui/__main__.py +++ b/src/py2exe_gui/__main__.py @@ -10,14 +10,14 @@ import sys from pathlib import Path -from PySide6.QtCore import Slot +from PySide6.QtCore import QTranslator, Slot from PySide6.QtGui import QCloseEvent from PySide6.QtWidgets import QApplication -from .Constants import PyInstOpt +from .Constants import RUNTIME_INFO, PyInstOpt from .Core import Packaging, PackagingTask from .Resources import COMPILED_RESOURCES # noqa -from .Utilities import PyEnv, open_dir_in_explorer +from .Utilities import open_dir_in_explorer from .Widgets import MainWindow, SubProcessDlg @@ -27,17 +27,16 @@ class MainApp(MainWindow): def __init__(self, *args, **kwargs) -> None: super().__init__(*args, **kwargs) - self.current_pyenv: PyEnv self.packaging_task = PackagingTask(self) self.packager = Packaging(self) self.subprocess_dlg = SubProcessDlg(self) self._connect_slots() - self.status_bar.showMessage("就绪") + self.status_bar.showMessage(MainApp.tr("Ready.")) # def show(self): - # """仅供性能分析使用,切勿取消注释!!! + # """仅供分析启动性能使用,切勿取消注释!!! # """ # # super().show() @@ -88,16 +87,16 @@ class MainApp(MainWindow): """处理子进程窗口多功能按钮点击信号的槽""" btn_text = self.subprocess_dlg.multifunction_btn.text() - if btn_text == "取消": + if btn_text == SubProcessDlg.tr("Cancel"): self.packager.subprocess.abort_process() self.subprocess_dlg.close() - elif btn_text == "打开输出位置": + elif btn_text == SubProcessDlg.tr("Open Dist"): script_path: Path = self.packaging_task.using_option[ PyInstOpt.script_path ] dist_path = script_path.parent / "dist" open_dir_in_explorer(dist_path) - elif btn_text == "关闭": + elif btn_text == SubProcessDlg.tr("Close"): self.subprocess_dlg.close() subprocess_dlg.multifunction_btn.clicked.connect(handle_mul_btn_clicked) @@ -116,6 +115,13 @@ def main() -> None: """应用程序主入口函数,便于 Poetry 由此函数级入口构建启动脚本""" app = QApplication(sys.argv) + + # TODO 翻译机制待优化 + translator = QTranslator() + if RUNTIME_INFO.language_code == "zh_CN": + translator.load(":/i18n/zh_CN.qm") + app.installTranslator(translator) + window = MainApp() window.show() sys.exit(app.exec())