eric6/Project/ProjectTranslationsBrowser.py

branch
maintenance
changeset 8043
0acf98cd089a
parent 7940
5c1057897164
parent 8001
3b33b7d493ff
child 8176
31965986ecd1
equal deleted inserted replaced
7991:866adc8c315b 8043:0acf98cd089a
24 ProjectBrowserDirectoryItem, ProjectBrowserTranslationType 24 ProjectBrowserDirectoryItem, ProjectBrowserTranslationType
25 ) 25 )
26 from .ProjectBaseBrowser import ProjectBaseBrowser 26 from .ProjectBaseBrowser import ProjectBaseBrowser
27 27
28 import UI.PixmapCache 28 import UI.PixmapCache
29 from UI.NotificationWidget import NotificationTypes
29 30
30 import Preferences 31 import Preferences
31 import Utilities 32 import Utilities
32 33
33 34
100 self.tsprocBackMenuActions = [] 101 self.tsprocBackMenuActions = []
101 self.qmprocBackMenuActions = [] 102 self.qmprocBackMenuActions = []
102 103
103 self.menu = QMenu(self) 104 self.menu = QMenu(self)
104 if self.project.getProjectType() in [ 105 if self.project.getProjectType() in [
105 "PyQt5", "PyQt5C", "E6Plugin", "PySide2", "PySide2C" 106 "PyQt5", "PyQt5C", "PyQt6", "PyQt6C", "E6Plugin",
107 "PySide2", "PySide2C", "PySide6", "PySide6C"
106 ]: 108 ]:
107 act = self.menu.addAction( 109 act = self.menu.addAction(
108 self.tr('Generate translation'), self.__generateSelected) 110 self.tr('Generate translation'), self.__generateSelected)
109 self.tsMenuActions.append(act) 111 self.tsMenuActions.append(act)
110 self.tsprocMenuActions.append(act) 112 self.tsprocMenuActions.append(act)
227 self.menu.addSeparator() 229 self.menu.addSeparator()
228 self.menu.addAction(self.tr('Configure...'), self._configure) 230 self.menu.addAction(self.tr('Configure...'), self._configure)
229 231
230 self.backMenu = QMenu(self) 232 self.backMenu = QMenu(self)
231 if self.project.getProjectType() in [ 233 if self.project.getProjectType() in [
232 "PyQt5", "PyQt5C", "E6Plugin", "PySide2", "PySide2C" 234 "PyQt5", "PyQt5C", "PyQt6", "PyQt6C", "E6Plugin",
235 "PySide2", "PySide2C", "PySide6", "PySide6C"
233 ]: 236 ]:
234 act = self.backMenu.addAction( 237 act = self.backMenu.addAction(
235 self.tr('Generate all translations'), 238 self.tr('Generate all translations'),
236 self.__generateAll) 239 self.__generateAll)
237 self.tsprocBackMenuActions.append(act) 240 self.tsprocBackMenuActions.append(act)
288 self.backMenu.setEnabled(False) 291 self.backMenu.setEnabled(False)
289 292
290 # create the menu for multiple selected files 293 # create the menu for multiple selected files
291 self.multiMenu = QMenu(self) 294 self.multiMenu = QMenu(self)
292 if self.project.getProjectType() in [ 295 if self.project.getProjectType() in [
293 "PyQt5", "PyQt5C", "E6Plugin", "PySide2", "PySide2C" 296 "PyQt5", "PyQt5C", "PyQt6", "PyQt6C", "E6Plugin",
297 "PySide2", "PySide2C", "PySide6", "PySide6C"
294 ]: 298 ]:
295 act = self.multiMenu.addAction( 299 act = self.multiMenu.addAction(
296 self.tr('Generate translations'), 300 self.tr('Generate translations'),
297 self.__generateSelected) 301 self.__generateSelected)
298 self.tsMultiMenuActions.append(act) 302 self.tsMultiMenuActions.append(act)
372 self.multiMenu.addSeparator() 376 self.multiMenu.addSeparator()
373 self.multiMenu.addAction(self.tr('Configure...'), self._configure) 377 self.multiMenu.addAction(self.tr('Configure...'), self._configure)
374 378
375 self.dirMenu = QMenu(self) 379 self.dirMenu = QMenu(self)
376 if self.project.getProjectType() in [ 380 if self.project.getProjectType() in [
377 "PyQt5", "PyQt5C", "E6Plugin", "PySide2", "PySide2C" 381 "PyQt5", "PyQt5C", "PyQt6", "PyQt6C", "E6Plugin",
382 "PySide2", "PySide2C", "PySide6", "PySide6C"
378 ]: 383 ]:
379 act = self.dirMenu.addAction( 384 act = self.dirMenu.addAction(
380 self.tr('Generate all translations'), 385 self.tr('Generate all translations'),
381 self.__generateAll) 386 self.__generateAll)
382 self.tsprocDirMenuActions.append(act) 387 self.tsprocDirMenuActions.append(act)
490 def __showContextMenu(self): 495 def __showContextMenu(self):
491 """ 496 """
492 Private slot called by the menu aboutToShow signal. 497 Private slot called by the menu aboutToShow signal.
493 """ 498 """
494 if self.project.getProjectType() in [ 499 if self.project.getProjectType() in [
495 "PyQt5", "PyQt5C", "E6Plugin", "PySide2", "PySide2C" 500 "PyQt5", "PyQt5C", "PyQt6", "PyQt6C", "E6Plugin",
501 "PySide2", "PySide2C", "PySide6", "PySide6C"
496 ]: 502 ]:
497 tsFiles = 0 503 tsFiles = 0
498 qmFiles = 0 504 qmFiles = 0
499 itmList = self.getSelectedItems() 505 itmList = self.getSelectedItems()
500 for itm in itmList[:]: 506 for itm in itmList[:]:
534 def __showContextMenuMulti(self): 540 def __showContextMenuMulti(self):
535 """ 541 """
536 Private slot called by the multiMenu aboutToShow signal. 542 Private slot called by the multiMenu aboutToShow signal.
537 """ 543 """
538 if self.project.getProjectType() in [ 544 if self.project.getProjectType() in [
539 "PyQt5", "PyQt5C", "E6Plugin", "PySide2", "PySide2C" 545 "PyQt5", "PyQt5C", "PyQt6", "PyQt6C", "E6Plugin",
546 "PySide2", "PySide2C", "PySide6", "PySide6C"
540 ]: 547 ]:
541 tsFiles = 0 548 tsFiles = 0
542 qmFiles = 0 549 qmFiles = 0
543 itmList = self.getSelectedItems() 550 itmList = self.getSelectedItems()
544 for itm in itmList[:]: 551 for itm in itmList[:]:
576 def __showContextMenuDir(self): 583 def __showContextMenuDir(self):
577 """ 584 """
578 Private slot called by the dirMenu aboutToShow signal. 585 Private slot called by the dirMenu aboutToShow signal.
579 """ 586 """
580 if self.project.getProjectType() in [ 587 if self.project.getProjectType() in [
581 "PyQt5", "PyQt5C", "E6Plugin", "PySide2", "PySide2C" 588 "PyQt5", "PyQt5C", "PyQt6", "PyQt6C", "E6Plugin",
589 "PySide2", "PySide2C", "PySide6", "PySide6C"
582 ]: 590 ]:
583 if self.pylupdateProcRunning: 591 if self.pylupdateProcRunning:
584 for act in self.tsprocDirMenuActions: 592 for act in self.tsprocDirMenuActions:
585 act.setEnabled(False) 593 act.setEnabled(False)
586 if self.lreleaseProcRunning: 594 if self.lreleaseProcRunning:
596 def __showContextMenuBack(self): 604 def __showContextMenuBack(self):
597 """ 605 """
598 Private slot called by the backMenu aboutToShow signal. 606 Private slot called by the backMenu aboutToShow signal.
599 """ 607 """
600 if self.project.getProjectType() in [ 608 if self.project.getProjectType() in [
601 "PyQt5", "PyQt5C", "E6Plugin", "PySide2", "PySide2C" 609 "PyQt5", "PyQt5C", "PyQt6", "PyQt6C", "E6Plugin",
610 "PySide2", "PySide2C", "PySide6", "PySide6C"
602 ]: 611 ]:
603 if self.pylupdateProcRunning: 612 if self.pylupdateProcRunning:
604 for act in self.tsprocBackMenuActions: 613 for act in self.tsprocBackMenuActions:
605 act.setEnabled(False) 614 act.setEnabled(False)
606 if self.lreleaseProcRunning: 615 if self.lreleaseProcRunning:
869 self.appendStdout.emit(s) 878 self.appendStdout.emit(s)
870 879
871 def __readStderrLupdate(self, proc): 880 def __readStderrLupdate(self, proc):
872 """ 881 """
873 Private slot to handle the readyReadStandardError signal of the 882 Private slot to handle the readyReadStandardError signal of the
874 pylupdate5/pyside2-lupdate process. 883 pylupdate5 / pylupdate6 / pyside2-lupdate / pyside6-lupdate process.
875 884
876 @param proc reference to the process 885 @param proc reference to the process
877 @type QProcess 886 @type QProcess
878 """ 887 """
879 self.__readStderr(proc, '{0}: '.format(self.pylupdate)) 888 self.__readStderr(proc, '{0}: '.format(self.pylupdate))
924 @param exitCode exit code of the process 933 @param exitCode exit code of the process
925 @type int 934 @type int
926 @param exitStatus exit status of the process 935 @param exitStatus exit status of the process
927 @type QProcess.ExitStatus 936 @type QProcess.ExitStatus
928 """ 937 """
938 ui = e5App().getObject("UserInterface")
929 if exitStatus == QProcess.NormalExit and exitCode == 0: 939 if exitStatus == QProcess.NormalExit and exitCode == 0:
930 ui = e5App().getObject("UserInterface") 940 ui.showNotification(
931 if ui.notificationsEnabled(): 941 UI.PixmapCache.getPixmap("linguist48"),
932 ui.showNotification( 942 self.tr("Translation file generation"),
933 UI.PixmapCache.getPixmap("linguist48"), 943 self.tr(
934 self.tr("Translation file generation"), 944 "The generation of the translation files (*.ts)"
935 self.tr( 945 " was successful."))
936 "The generation of the translation files (*.ts)"
937 " was successful."))
938 else:
939 E5MessageBox.information(
940 self,
941 self.tr("Translation file generation"),
942 self.tr(
943 "The generation of the translation files (*.ts)"
944 " was successful."))
945 else: 946 else:
946 if exitStatus == QProcess.CrashExit: 947 if exitStatus == QProcess.CrashExit:
947 info = self.tr(" The process has crashed.") 948 info = self.tr(" The process has crashed.")
948 else: 949 else:
949 info = "" 950 info = ""
950 E5MessageBox.critical( 951 ui.showNotification(
951 self, 952 UI.PixmapCache.getPixmap("linguist48"),
952 self.tr("Translation file generation"), 953 self.tr("Translation file generation"),
953 self.tr( 954 self.tr(
954 "The generation of the translation files (*.ts) has" 955 "The generation of the translation files (*.ts) has"
955 " failed.{0}").format(info)) 956 " failed.{0}").format(info),
957 kind=NotificationTypes.Critical,
958 timeout=0)
956 959
957 for index in range(len(self.__pylupdateProcesses)): 960 for index in range(len(self.__pylupdateProcesses)):
958 if proc == self.__pylupdateProcesses[index][0]: 961 if proc == self.__pylupdateProcesses[index][0]:
959 try: 962 tmpProjectFile = self.__pylupdateProcesses[index][1]
960 self.__tmpProjects.remove( 963 if tmpProjectFile:
961 self.__pylupdateProcesses[index][1]) 964 try:
962 os.remove(self.__pylupdateProcesses[index][1]) 965 self.__tmpProjects.remove(tmpProjectFile)
963 except OSError: 966 os.remove(tmpProjectFile)
964 pass 967 except OSError:
968 pass
965 del self.__pylupdateProcesses[index] 969 del self.__pylupdateProcesses[index]
966 break 970 break
971
967 if not self.__pylupdateProcesses: 972 if not self.__pylupdateProcesses:
968 # all done 973 # all done
969 self.pylupdateProcRunning = False 974 self.pylupdateProcRunning = False
970 975
971 def __generateTSFile(self, noobsolete=False, generateAll=True): 976 def __generateTSFile(self, noobsolete=False, generateAll=True):
972 """ 977 """
973 Private method used to run pylupdate5/pyside2-lupdate to 978 Private method used to run pylupdate5 / pylupdate6 / pyside2-lupdate /
974 generate the .ts files. 979 pyside6-lupdate to generate the .ts files.
975 980
976 @param noobsolete flag indicating whether obsolete entries should be 981 @param noobsolete flag indicating whether obsolete entries should be
977 kept (boolean) 982 kept (boolean)
978 @param generateAll flag indicating whether all translations should be 983 @param generateAll flag indicating whether all translations should be
979 generated (boolean) 984 generated (boolean)
1007 li = [self.project.getRelativePath(lang.fileName()) 1012 li = [self.project.getRelativePath(lang.fileName())
1008 for lang in langs] 1013 for lang in langs]
1009 self.hooks["generateSelectedWithObsolete"](li) 1014 self.hooks["generateSelectedWithObsolete"](li)
1010 return 1015 return
1011 1016
1012 # generate a minimal temporary projectfile suitable for pylupdate 1017 # generate a minimal temporary project file suitable for pylupdate
1013 self.__tmpProjects = [] 1018 self.__tmpProjects = []
1014 if self.project.getProjectLanguage() in [ 1019 if self.project.getProjectLanguage() in [
1015 "Python", "Python3" 1020 "Python", "Python3"
1016 ]: 1021 ]:
1017 ok = self.__writeTempProjectFile(langs, [".py"]) 1022 if self.project.getProjectType() not in ["PyQt6", "PyQt6C"]:
1018 else: 1023 ok = self.__writeTempProjectFile(langs, [".py"])
1019 ok = False 1024 if not ok:
1020 if not ok: 1025 return
1026 else:
1021 return 1027 return
1022 1028
1023 if self.project.getProjectType() in ["PyQt5", "PyQt5C"]: 1029 if self.project.getProjectType() in ["PyQt5", "PyQt5C"]:
1024 self.pylupdate = Utilities.generatePyQtToolPath('pylupdate5') 1030 self.pylupdate = Utilities.generatePyQtToolPath('pylupdate5')
1031 elif self.project.getProjectType() in ["PyQt6", "PyQt6C"]:
1032 self.pylupdate = Utilities.generatePyQtToolPath('pylupdate6')
1025 elif self.project.getProjectType() in ["E6Plugin"]: 1033 elif self.project.getProjectType() in ["E6Plugin"]:
1026 self.pylupdate = Utilities.generatePyQtToolPath('pylupdate5') 1034 self.pylupdate = Utilities.generatePyQtToolPath('pylupdate5')
1027 elif self.project.getProjectType() in ["PySide2", "PySide2C"]: 1035 elif self.project.getProjectType() in ["PySide2", "PySide2C"]:
1028 self.pylupdate = Utilities.generatePySideToolPath( 1036 self.pylupdate = Utilities.generatePySideToolPath(
1029 'pyside2-lupdate') 1037 'pyside2-lupdate', variant=2)
1038 elif self.project.getProjectType() in ["PySide6", "PySide6C"]:
1039 self.pylupdate = Utilities.generatePySideToolPath(
1040 'pyside6-lupdate', variant=6)
1030 else: 1041 else:
1031 return 1042 return
1032 1043
1033 self.__pylupdateProcesses = [] 1044 self.__pylupdateProcesses = []
1034 for tempProjectFile in self.__tmpProjects[:]: 1045 if self.project.getProjectType() in ["PyQt6", "PyQt6C"]:
1035 proc = QProcess() 1046 if langs:
1036 args = [] 1047 langs = [self.project.getRelativePath(lang.fileName())
1037 1048 for lang in langs if lang.fileName().endswith('.ts')]
1038 if noobsolete:
1039 args.append('-noobsolete')
1040
1041 args.append('-verbose')
1042 path, filename = os.path.split(tempProjectFile)
1043 args.append(filename)
1044 proc.setWorkingDirectory(os.path.join(self.project.ppath, path))
1045 proc.finished.connect(
1046 functools.partial(self.__generateTSFileDone, proc)
1047 )
1048 proc.readyReadStandardOutput.connect(
1049 functools.partial(self.__readStdoutLupdate, proc)
1050 )
1051 proc.readyReadStandardError.connect(
1052 functools.partial(self.__readStderrLupdate, proc)
1053 )
1054
1055 proc.start(self.pylupdate, args)
1056 procStarted = proc.waitForStarted()
1057 if procStarted:
1058 self.pylupdateProcRunning = True
1059 self.__pylupdateProcesses.append((proc, tempProjectFile))
1060 else: 1049 else:
1061 E5MessageBox.critical( 1050 try:
1051 pattern = self.project.pdata["TRANSLATIONPATTERN"].replace(
1052 "%language%", "*")
1053 langs = [
1054 lang for lang in self.project.pdata["TRANSLATIONS"]
1055 if fnmatch.fnmatch(lang, pattern)
1056 ]
1057 except IndexError:
1058 langs = []
1059 if not langs:
1060 E5MessageBox.warning(
1062 self, 1061 self,
1063 self.tr('Process Generation Error'), 1062 self.tr("Translation file generation"),
1064 self.tr( 1063 self.tr("""No translation files (*.ts) selected."""))
1065 'Could not start {0}.<br>' 1064 return
1066 'Ensure that it is in the search path.' 1065 for lang in langs:
1067 ).format(self.pylupdate)) 1066 proc = QProcess()
1068 # cleanup 1067 args = []
1069 try: 1068
1070 self.__tmpProjects.remove(tempProjectFile) 1069 if noobsolete:
1071 os.remove(tempProjectFile) 1070 args.append('--no-obsolete')
1072 except OSError: 1071
1073 pass 1072 args += ["--ts", lang]
1073 args.append(".")
1074
1075 proc.setWorkingDirectory(self.project.ppath)
1076 proc.finished.connect(
1077 functools.partial(self.__generateTSFileDone, proc)
1078 )
1079 proc.readyReadStandardOutput.connect(
1080 functools.partial(self.__readStdoutLupdate, proc)
1081 )
1082 proc.readyReadStandardError.connect(
1083 functools.partial(self.__readStderrLupdate, proc)
1084 )
1085
1086 proc.start(self.pylupdate, args)
1087 procStarted = proc.waitForStarted()
1088 if procStarted:
1089 self.pylupdateProcRunning = True
1090 self.__pylupdateProcesses.append((proc, ""))
1091 else:
1092 E5MessageBox.critical(
1093 self,
1094 self.tr('Process Generation Error'),
1095 self.tr(
1096 'Could not start {0}.<br>'
1097 'Ensure that it is in the search path.'
1098 ).format(self.pylupdate))
1099 else:
1100 for tempProjectFile in self.__tmpProjects[:]:
1101 proc = QProcess()
1102 args = []
1103
1104 if noobsolete:
1105 args.append('-noobsolete')
1106
1107 args.append('-verbose')
1108 path, filename = os.path.split(tempProjectFile)
1109 args.append(filename)
1110 proc.setWorkingDirectory(
1111 os.path.join(self.project.ppath, path))
1112 proc.finished.connect(
1113 functools.partial(self.__generateTSFileDone, proc)
1114 )
1115 proc.readyReadStandardOutput.connect(
1116 functools.partial(self.__readStdoutLupdate, proc)
1117 )
1118 proc.readyReadStandardError.connect(
1119 functools.partial(self.__readStderrLupdate, proc)
1120 )
1121
1122 proc.start(self.pylupdate, args)
1123 procStarted = proc.waitForStarted()
1124 if procStarted:
1125 self.pylupdateProcRunning = True
1126 self.__pylupdateProcesses.append((proc, tempProjectFile))
1127 else:
1128 E5MessageBox.critical(
1129 self,
1130 self.tr('Process Generation Error'),
1131 self.tr(
1132 'Could not start {0}.<br>'
1133 'Ensure that it is in the search path.'
1134 ).format(self.pylupdate))
1135 # cleanup
1136 try:
1137 self.__tmpProjects.remove(tempProjectFile)
1138 os.remove(tempProjectFile)
1139 except OSError:
1140 pass
1074 1141
1075 def __generateAll(self): 1142 def __generateAll(self):
1076 """ 1143 """
1077 Private method to generate all translation files (.ts) for Qt Linguist. 1144 Private method to generate all translation files (.ts) for Qt Linguist.
1078 1145
1119 @param exitCode exit code of the process 1186 @param exitCode exit code of the process
1120 @type int 1187 @type int
1121 @param exitStatus exit status of the process 1188 @param exitStatus exit status of the process
1122 @type QProcess.ExitStatus 1189 @type QProcess.ExitStatus
1123 """ 1190 """
1191 ui = e5App().getObject("UserInterface")
1124 if exitStatus == QProcess.NormalExit and exitCode == 0: 1192 if exitStatus == QProcess.NormalExit and exitCode == 0:
1125 ui = e5App().getObject("UserInterface") 1193 ui.showNotification(
1126 if ui.notificationsEnabled(): 1194 UI.PixmapCache.getPixmap("linguist48"),
1127 ui.showNotification( 1195 self.tr("Translation file release"),
1128 UI.PixmapCache.getPixmap("linguist48"), 1196 self.tr("The release of the translation files (*.qm)"
1129 self.tr("Translation file release"), 1197 " was successful."))
1130 self.tr("The release of the translation files (*.qm)"
1131 " was successful."))
1132 else:
1133 E5MessageBox.information(
1134 self,
1135 self.tr("Translation file release"),
1136 self.tr("The release of the translation files (*.qm)"
1137 " was successful."))
1138 if self.project.pdata["TRANSLATIONSBINPATH"]: 1198 if self.project.pdata["TRANSLATIONSBINPATH"]:
1139 target = os.path.join( 1199 target = os.path.join(
1140 self.project.ppath, 1200 self.project.ppath,
1141 self.project.pdata["TRANSLATIONSBINPATH"]) 1201 self.project.pdata["TRANSLATIONSBINPATH"])
1142 for langFile in self.project.pdata["TRANSLATIONS"][:]: 1202 for langFile in self.project.pdata["TRANSLATIONS"][:]:
1144 qmFile = os.path.join(self.project.ppath, 1204 qmFile = os.path.join(self.project.ppath,
1145 langFile.replace('.ts', '.qm')) 1205 langFile.replace('.ts', '.qm'))
1146 if os.path.exists(qmFile): 1206 if os.path.exists(qmFile):
1147 shutil.move(qmFile, target) 1207 shutil.move(qmFile, target)
1148 else: 1208 else:
1149 E5MessageBox.critical( 1209 ui.showNotification(
1150 self, 1210 UI.PixmapCache.getPixmap("linguist48"),
1151 self.tr("Translation file release"), 1211 self.tr("Translation file release"),
1152 self.tr( 1212 self.tr(
1153 "The release of the translation files (*.qm) has failed.")) 1213 "The release of the translation files (*.qm) has failed."),
1214 kind=NotificationTypes.Critical,
1215 timeout=0)
1154 1216
1155 for index in range(len(self.__lreleaseProcesses)): 1217 for index in range(len(self.__lreleaseProcesses)):
1156 if proc == self.__lreleaseProcesses[index]: 1218 if proc == self.__lreleaseProcesses[index]:
1157 del self.__lreleaseProcesses[index] 1219 del self.__lreleaseProcesses[index]
1158 break 1220 break
1184 for lang in langs] 1246 for lang in langs]
1185 self.hooks["releaseSelected"](li) 1247 self.hooks["releaseSelected"](li)
1186 return 1248 return
1187 1249
1188 if self.project.getProjectType() in [ 1250 if self.project.getProjectType() in [
1189 "PyQt5", "PyQt5C", "E6Plugin", "PySide2", "PySide2C" 1251 "PyQt5", "PyQt5C", "PyQt6", "PyQt6C", "E6Plugin",
1252 "PySide2", "PySide2C", "PySide6", "PySide6C"
1190 ]: 1253 ]:
1191 lrelease = os.path.join( 1254 lrelease = os.path.join(
1192 Utilities.getQtBinariesPath(), 1255 Utilities.getQtBinariesPath(),
1193 Utilities.generateQtToolName("lrelease")) 1256 Utilities.generateQtToolName("lrelease"))
1194 else: 1257 else:

eric ide

mercurial