From 9f038b3c1ec75b3644563a14c4763d025eede64e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=81=8D=E5=85=AE=E6=83=9A=E5=85=AE?= <1173718158@qq.com> Date: Tue, 24 Dec 2024 15:22:04 +0800 Subject: [PATCH] . --- .gitignore | 1 + .../LunaHook/engines/ppsspp/specialgames.hpp | 3 + cpp/exec/PyStand.cpp | 63 +-- cpp/shareddllproxy/Atlas.cpp | 24 +- cpp/shareddllproxy/eztrans.cpp | 7 +- cpp/shareddllproxy/lec.cpp | 4 +- cpp/version.cmake | 4 +- py/LunaTranslator/LunaTranslator_main.py | 19 - py/LunaTranslator/gui/dialog_savedgame.py | 179 ++++-- .../gui/dialog_savedgame_common.py | 159 ++---- .../gui/dialog_savedgame_setting.py | 185 ++++--- py/LunaTranslator/gui/dialog_savedgame_v3.py | 57 +- py/LunaTranslator/gui/inputdialog.py | 13 - py/LunaTranslator/gui/setting_about.py | 96 ++-- py/LunaTranslator/gui/setting_cishu.py | 2 + .../gui/setting_display_scale.py | 2 + py/LunaTranslator/gui/setting_proxy.py | 2 +- .../gui/setting_textinput_ocr.py | 8 +- py/LunaTranslator/gui/setting_translate.py | 51 +- py/LunaTranslator/gui/setting_tts.py | 7 +- py/LunaTranslator/gui/setting_year.py | 223 ++++++++ py/LunaTranslator/gui/specialwidget.py | 159 ------ py/LunaTranslator/gui/usefulwidget.py | 207 ++++++- py/LunaTranslator/metadata/abstract.py | 3 + py/LunaTranslator/metadata/bangumi.py | 2 +- py/LunaTranslator/metadata/dlsite.py | 6 +- py/LunaTranslator/metadata/fanza.py | 4 + py/LunaTranslator/metadata/steam.py | 2 +- py/LunaTranslator/metadata/vndb.py | 4 +- py/LunaTranslator/myutils/config.py | 1 + py/LunaTranslator/myutils/localetools.py | 32 +- py/LunaTranslator/myutils/traceplaytime.py | 15 +- py/LunaTranslator/myutils/utils.py | 4 +- py/LunaTranslator/ocrengines/googlelens.py | 3 +- py/LunaTranslator/ocrengines/local.py | 9 +- py/LunaTranslator/ocrengines/windowsocr.py | 20 +- py/LunaTranslator/qtsymbols.py | 8 +- py/LunaTranslator/textsource/filetrans.py | 7 +- py/LunaTranslator/translator/atlas.py | 5 +- py/LunaTranslator/translator/dreye.py | 5 +- py/LunaTranslator/translator/eztrans.py | 19 +- py/LunaTranslator/translator/jb7.py | 39 +- py/LunaTranslator/translator/kingsoft.py | 5 +- py/LunaTranslator/translator/lec.py | 5 +- py/LunaTranslator/winrtutils.py | 34 +- py/files/defaultconfig/config.json | 12 +- py/files/defaultconfig/ocrsetting.json | 45 +- py/files/defaultconfig/static_data.json | 519 ++++++++++-------- py/files/defaultconfig/translatorsetting.json | 41 +- py/files/lang/ar.json | 9 +- py/files/lang/cht.json | 9 +- py/files/lang/cs.json | 9 +- py/files/lang/de.json | 9 +- py/files/lang/en.json | 9 +- py/files/lang/es.json | 9 +- py/files/lang/fr.json | 9 +- py/files/lang/it.json | 9 +- py/files/lang/ja.json | 9 +- py/files/lang/ko.json | 9 +- py/files/lang/nl.json | 9 +- py/files/lang/pl.json | 9 +- py/files/lang/pt.json | 9 +- py/files/lang/ru.json | 9 +- py/files/lang/sv.json | 9 +- py/files/lang/th.json | 9 +- py/files/lang/tr.json | 9 +- py/files/lang/uk.json | 9 +- py/files/lang/vi.json | 9 +- py/files/lang/zh.json | 9 +- py/files/yearsummary/yearsummary.html | 300 ++++++++++ 70 files changed, 1771 insertions(+), 1033 deletions(-) create mode 100644 py/LunaTranslator/gui/setting_year.py create mode 100644 py/files/yearsummary/yearsummary.html diff --git a/.gitignore b/.gitignore index 222dce2a..71f82d6f 100644 --- a/.gitignore +++ b/.gitignore @@ -49,3 +49,4 @@ cpp/LunaHook/.vscode/settings.json cpp/LunaHook/scripts/YY-Thunks-1.0.7-Binary.zip cpp/libs/YY-Thunks py/LunaTranslator/.vscode/settings.json +py/files/yearsummary/yearsummary.value.js diff --git a/cpp/LunaHook/LunaHook/engines/ppsspp/specialgames.hpp b/cpp/LunaHook/LunaHook/engines/ppsspp/specialgames.hpp index 672b6264..1f0f2e55 100644 --- a/cpp/LunaHook/LunaHook/engines/ppsspp/specialgames.hpp +++ b/cpp/LunaHook/LunaHook/engines/ppsspp/specialgames.hpp @@ -1517,6 +1517,8 @@ namespace ppsspp {0x88F09F4, {CODEC_UTF16, 0, 0, 0, ULJM05976, "ULJM05976"}}, // オメルタ~沈黙の掟~ THE LEGACY {0x88861C8, {0, 3, 0, 0, 0, "ULJM06393"}}, + {0x8885fd8, {0, 0, 0, 0, 0, "ULJM06393"}}, + {0x88ac3a8, {0, 1, 0, 0, 0, "ULJM06393"}}, // L.G.S~新説 封神演義~ {0x888A358, {0, 0, 0, 0, ULJM05943F, "ULJM06131"}}, // NAME+TEXT {0x88DB214, {0, 0, 0, 0, ULJM05943F, "ULJM06131"}}, // TEXT @@ -1534,6 +1536,7 @@ namespace ppsspp {0x886E094, {0, 0, 0, 0, ULJM06129, "ULJM06129"}}, // name+text // 十鬼の絆 花結綴り {0x886E354, {0, 0, 0, 0, ULJM06289, "ULJM06301"}}, // name+text + {0x88f878c, {0, 0, 0, 0, ULJM06289, "ULJM06301"}}, // ティンクル☆くるせいだーす STARLIT BRAVE!! {0x88A94BC, {0, 4, 0, 0, 0, "ULJS00315"}}, // text // ティンクル☆くるせいだーす GoGo! diff --git a/cpp/exec/PyStand.cpp b/cpp/exec/PyStand.cpp index df5e1d1e..ae56067d 100644 --- a/cpp/exec/PyStand.cpp +++ b/cpp/exec/PyStand.cpp @@ -236,8 +236,7 @@ int PyStand::DetectScript() //--------------------------------------------------------------------- const auto init_script = LR"( -import sys -import os +import os,functools, locale, sys PYSTAND = os.environ['PYSTAND'] PYSTAND_HOME = os.environ['PYSTAND_HOME'] PYSTAND_RUNTIME = os.environ['PYSTAND_RUNTIME'] @@ -253,26 +252,40 @@ def MessageBox(msg, info = 'Message'): os.MessageBox = MessageBox #sys.stdout=sys.stderr sys.path.insert(0, './LunaTranslator') -)" -#ifndef PYSTAND_CONSOLE - LR"( + + +def fuckwrite(origin, message): + try: + if isinstance(message, str): + code=locale.getpreferredencoding() + origin(message.encode(encoding=code, errors='replace').decode(encoding=code, errors='replace')) + else: + origin(message) + except: + return + import traceback, io + sio = io.StringIO() + traceback.print_exc(file = sio) + os.MessageBox(sio.getvalue(), message) + try: fd = os.open('CONOUT$', os.O_RDWR | os.O_BINARY) fp = os.fdopen(fd, 'w') sys.stdout = fp sys.stderr = fp attached = True + sys.stdout.write = functools.partial(fuckwrite,sys.stdout.write) + sys.stderr.write = functools.partial(fuckwrite,sys.stderr.write) + except Exception as e: try: - fp = open(os.devnull, 'w', errors='ignore') # sometimes FileNotFound Error: [Errno 2]No such file or directory: 'nul' + fp = open(os.devnull, 'w', errors='replace') # sometimes FileNotFound Error: [Errno 2]No such file or directory: 'nul' sys.stdout = fp sys.stderr = fp attached = False except: pass -)" -#endif - LR"( + sys.argv = [PYSTAND_SCRIPT] + sys.argv[1:] text = open(PYSTAND_SCRIPT, 'rb').read() environ = {'__file__': PYSTAND_SCRIPT, '__name__': '__main__'} @@ -324,41 +337,13 @@ int main() { return 3; } -#ifndef PYSTAND_CONSOLE - // winmain下的stderr没有任何卵用,对于崩溃时的stderr根本显示不出来,所以还是用控制台来保存log吧。 + // print cmd无法显示的字符时,如果使用cmd打开,不论debug还是普通,都会error31崩溃。如果双击打开debug,却不会崩溃 + // 但因为无法区分是使用cmd打开debug还是双击打开debug,所以干脆都这样吧。 if (AttachConsole(ATTACH_PARENT_PROCESS)) { freopen("CONOUT$", "w", stdout); freopen("CONOUT$", "w", stderr); - int fd = _fileno(stdout); - if (fd >= 0) - { - std::string fn = std::to_string(fd); - SetEnvironmentVariableA("PYSTAND_STDOUT", fn.c_str()); - } - fd = _fileno(stdin); - if (fd >= 0) - { - std::string fn = std::to_string(fd); - SetEnvironmentVariableA("PYSTAND_STDIN", fn.c_str()); - } } -#else - SetConsoleOutputCP(CP_UTF8); - /* - auto getCurrentTimestamp = [] - { - auto now = std::chrono::system_clock::now(); - std::time_t now_time_t = std::chrono::system_clock::to_time_t(now); - std::tm now_tm = *std::localtime(&now_time_t); - std::ostringstream oss; - oss << std::put_time(&now_tm, "log_%Y-%m-%d-%H-%M-%S.txt"); - return oss.str(); - }; - auto curr = getCurrentTimestamp(); - freopen(curr.c_str(), "a", stderr); - */ -#endif int hr = ps.RunString(init_script); return hr; } diff --git a/cpp/shareddllproxy/Atlas.cpp b/cpp/shareddllproxy/Atlas.cpp index 4e055aca..36a65652 100644 --- a/cpp/shareddllproxy/Atlas.cpp +++ b/cpp/shareddllproxy/Atlas.cpp @@ -531,28 +531,8 @@ wchar_t *TranslateFullLog(wchar_t *otext) struct AtlasConfig atlcfg; -static void writestring(wchar_t *text, HANDLE hPipe) -{ - DWORD _; - auto len = text ? (2 * wcslen(text)) : 0; - if (!WriteFile(hPipe, &len, 4, &_, NULL)) - return; - if (text) - if (!WriteFile(hPipe, text, len, &_, NULL)) - return; -} -static wchar_t *readstring(HANDLE hPipe) -{ - DWORD _; - int len; - if (!ReadFile(hPipe, &len, 4, &_, NULL)) - return nullptr; - wchar_t *otext = new wchar_t[len / 2 + 1]; - if (!ReadFile(hPipe, otext, len, &_, NULL)) - return nullptr; - otext[len / 2] = 0; - return otext; -} +void writestring(const wchar_t *text, HANDLE hPipe); +wchar_t *readstring(HANDLE hPipe); HANDLE mutex = NULL; int atlaswmain(int argc, wchar_t *argv[]) { diff --git a/cpp/shareddllproxy/eztrans.cpp b/cpp/shareddllproxy/eztrans.cpp index dbac9471..631ec35d 100644 --- a/cpp/shareddllproxy/eztrans.cpp +++ b/cpp/shareddllproxy/eztrans.cpp @@ -678,6 +678,7 @@ std::optional CTextProcess::eztrans_proc(const std::wstring &input output = HangulDecode(output); return output; } +void writestring(const wchar_t *text, HANDLE hPipe); int eztrans(int argc, wchar_t *argv[]) { @@ -700,12 +701,10 @@ int eztrans(int argc, wchar_t *argv[]) if (!ReadFile(hPipe, buff, 12000, &_, NULL)) break; auto trans = CTextProcess::eztrans_proc(buff); - std::wstring res; if (trans) - res = trans.value(); + writestring(trans.value().c_str(), hPipe); else - res = L"translate failed"; - WriteFile(hPipe, res.data(), 2 * res.size(), &_, NULL); + writestring(0, hPipe); } return 0; diff --git a/cpp/shareddllproxy/lec.cpp b/cpp/shareddllproxy/lec.cpp index 9da8c97a..8589b2da 100644 --- a/cpp/shareddllproxy/lec.cpp +++ b/cpp/shareddllproxy/lec.cpp @@ -149,7 +149,7 @@ void SetUpLEC() RegCloseKey(key); } } -static void writestring(wchar_t *text, HANDLE hPipe) +void writestring(const wchar_t *text, HANDLE hPipe) { DWORD _; auto len = text ? (2 * wcslen(text)) : 0; @@ -159,7 +159,7 @@ static void writestring(wchar_t *text, HANDLE hPipe) if (!WriteFile(hPipe, text, len, &_, NULL)) return; } -static wchar_t *readstring(HANDLE hPipe) +wchar_t *readstring(HANDLE hPipe) { DWORD _; int len; diff --git a/cpp/version.cmake b/cpp/version.cmake index 067b283e..2e34f373 100644 --- a/cpp/version.cmake +++ b/cpp/version.cmake @@ -1,7 +1,7 @@ set(VERSION_MAJOR 6) -set(VERSION_MINOR 14) -set(VERSION_PATCH 13) +set(VERSION_MINOR 15) +set(VERSION_PATCH 0) set(VERSION_REVISION 0) set(LUNA_VERSION "{${VERSION_MAJOR},${VERSION_MINOR},${VERSION_PATCH},${VERSION_REVISION}}") add_library(VERSION_DEF ${CMAKE_CURRENT_LIST_DIR}/version_def.cpp) diff --git a/py/LunaTranslator/LunaTranslator_main.py b/py/LunaTranslator/LunaTranslator_main.py index cf11ab47..4a53c300 100644 --- a/py/LunaTranslator/LunaTranslator_main.py +++ b/py/LunaTranslator/LunaTranslator_main.py @@ -223,26 +223,7 @@ def urlprotocol(): print_exc() -def is64_bit_os(): - import ctypes - - is64bit = ctypes.c_bool() - handle = ctypes.windll.kernel32.GetCurrentProcess() - success = ctypes.windll.kernel32.IsWow64Process(handle, ctypes.byref(is64bit)) - return (success and is64bit).value - - -def disablestdio(): - import platform - - if (int(platform.version().split(".")[0]) <= 6) and (not is64_bit_os()): - # win7 32位,有时候print会谜之报错PermissionError WinError 31 - sys.stdout = None - sys.stderr = None - - if __name__ == "__main__": - disablestdio() switchdir() prepareqtenv() from qtsymbols import QApplication diff --git a/py/LunaTranslator/gui/dialog_savedgame.py b/py/LunaTranslator/gui/dialog_savedgame.py index 6894c901..7bd21374 100644 --- a/py/LunaTranslator/gui/dialog_savedgame.py +++ b/py/LunaTranslator/gui/dialog_savedgame.py @@ -4,11 +4,11 @@ from qtsymbols import * import os, functools, uuid from traceback import print_exc import gobject, qtawesome -from gui.dynalang import LPushButton, LAction +from gui.dynalang import LAction from gui.dialog_savedgame_v3 import dialog_savedgame_v3 from gui.dialog_savedgame_legacy import dialog_savedgame_legacy from gui.dialog_savedgame_setting import dialog_setting_game, userlabelset -from myutils.wrapper import Singleton_close +from myutils.wrapper import Singleton_close, tryprint from gui.specialwidget import lazyscrollflow from myutils.utils import str2rgba from myutils.config import ( @@ -25,12 +25,13 @@ from gui.usefulwidget import ( Prompt_dialog, IconButton, getsimplecombobox, + FQLineEdit, + FocusCombo, ) from gui.dialog_savedgame_common import ( ItemWidget, dialog_syssetting, tagitem, - TagWidget, startgamecheck, loadvisinternal, getalistname, @@ -150,6 +151,108 @@ class dialog_savedgame_integrated(saveposwindow): ) +class TagWidget(QWidget): + tagschanged = pyqtSignal(tuple) # ((tag,type,refdata),) + linepressedenter = pyqtSignal(str) + tagclicked = pyqtSignal(tuple) # tag,type,refdata + + def __init__(self, parent=None, exfoucus=True): + super().__init__(parent) + tagitem.setstyles(self) + layout = QHBoxLayout() + layout.setContentsMargins(0, 0, 0, 0) + + self.setLayout(layout) + + self.lineEdit = FocusCombo() + if exfoucus: + self.lineEdit.setLineEdit(FQLineEdit()) + # FQLineEdit导致游戏管理页面里,点击编辑框后,下边界消失。 + # FQLineEdit仅用于和webview同一窗口内焦点缺失问题,所以既然用不到那就不要多此一举了 + else: + self.lineEdit.setEditable(True) + self.lineEdit.lineEdit().returnPressed.connect( + lambda: self.linepressedenter.emit(self.lineEdit.currentText()) + ) + + self.lineEdit.setSizePolicy( + QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Maximum + ) + self.tagtypes = ["usertags", "developers", "webtags", "usertags"] + self.tagtypes_zh = ["全部", "开发商", "标签", "自定义"] + self.tagtypes_1 = [ + tagitem.TYPE_SEARCH, + tagitem.TYPE_DEVELOPER, + tagitem.TYPE_TAG, + tagitem.TYPE_USERTAG, + ] + layout.addWidget(self.lineEdit) + + def __(idx): + t = self.lineEdit.currentText() + self.lineEdit.clear() + self.lineEdit.addItems(userlabelset(self.tagtypes[idx])) + self.lineEdit.setCurrentText(t) + + self.typecombo = getsimplecombobox(self.tagtypes_zh, callback=__) + layout.addWidget(self.typecombo) + self.setSizePolicy(QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Fixed) + + self.tag2widget = {} + + def callback(t): + if not t: + return + self.addTag(t, self.tagtypes_1[self.typecombo.currentIndex()]) + self.lineEdit.clearEditText() + + self.linepressedenter.connect(callback) + self.typecombo.currentIndexChanged.emit(0) + + def addTags(self, tags, signal=True): + for key in tags: + self.__addTag(key) + self.__calltagschanged(signal) + + @tryprint + def __addTag(self, key): + tag, _type, refdata = key + if not tag: + return + if key in self.tag2widget: + return + qw = tagitem(tag, _type=_type, refdata=refdata) + qw.removesignal.connect(self.removeTag) + qw.labelclicked.connect(self.tagclicked.emit) + layout = self.layout() + layout.insertWidget(layout.count() - 2, qw) + self.tag2widget[key] = qw + self.lineEdit.setFocus() + + def addTag(self, tag, _type, refdata=None, signal=True): + self.__addTag((tag, _type, refdata)) + self.__calltagschanged(signal) + + @tryprint + def __removeTag(self, key): + _w = self.tag2widget[key] + self.layout().removeWidget(_w) + self.tag2widget.pop(key) + + def removeTag(self, key, signal=True): + self.__removeTag(key) + self.__calltagschanged(signal) + + def __calltagschanged(self, signal): + if signal: + self.tagschanged.emit(tuple(self.tag2widget.keys())) + + def clearTag(self, signal=True): + for key in self.tag2widget.copy(): + self.__removeTag(key) + self.__calltagschanged(signal) + + class dialog_savedgame_new(QWidget): def dragEnterEvent(self, event: QDragEnterEvent): @@ -222,6 +325,10 @@ class dialog_savedgame_new(QWidget): if newtags != self.currtags: break notshow = False + webtags = [ + globalconfig["tagNameRemap"].get(tag, tag) + for tag in savehook_new_data[k]["webtags"] + ] for tag, _type, _ in tags: if _type == tagitem.TYPE_EXISTS: if os.path.exists(get_launchpath(k)) == False: @@ -232,16 +339,16 @@ class dialog_savedgame_new(QWidget): notshow = True break elif _type == tagitem.TYPE_TAG: - if tag not in savehook_new_data[k]["webtags"]: + if tag not in webtags: notshow = True break elif _type == tagitem.TYPE_USERTAG: if tag not in savehook_new_data[k]["usertags"]: notshow = True break - elif _type == tagitem.TYPE_RAND: + elif _type == tagitem.TYPE_SEARCH: if ( - tag not in savehook_new_data[k]["webtags"] + tag not in webtags and tag not in savehook_new_data[k]["usertags"] and tag not in savehook_new_data[k]["title"] and tag not in savehook_new_data[k]["developers"] @@ -431,24 +538,8 @@ class dialog_savedgame_new(QWidget): self.reflist = getreflist(globalconfig["currvislistuid"]) self.reftagid = globalconfig["currvislistuid"] - def callback(t): - if not t: - return - labelset = userlabelset() - if t in labelset: - tp = tagitem.TYPE_USERTAG - else: - tp = tagitem.TYPE_RAND - self.tagswidget.addTag(t, tp) - - self.tagswidget.lineEdit.clear() - self.tagswidget.lineEdit.addItems(labelset) - self.tagswidget.lineEdit.clearEditText() - self.tagswidget = TagWidget(self, exfoucus=False) - self.tagswidget.lineEdit.addItems(userlabelset()) - self.tagswidget.lineEdit.setCurrentText("") - self.tagswidget.linepressedenter.connect(callback) + self.currtags = tuple() self.tagswidget.tagschanged.connect(self.tagschanged) _ = QLabel() @@ -500,16 +591,40 @@ class dialog_savedgame_new(QWidget): getreflist(uid).insert(0, getreflist(uid).pop(idx)) def keyPressEvent(self, e: QKeyEvent): - if e.key() == Qt.Key.Key_Return: - startgamecheck(self, getreflist(self.reftagid), self.currentfocusuid) - elif e.key() == Qt.Key.Key_Delete: - self.clicked2() - elif e.key() == Qt.Key.Key_Left: - self.moverank(-1) - elif e.key() == Qt.Key.Key_Right: - self.moverank(1) + if self.currentfocusuid: + if e.key() == Qt.Key.Key_Return: + startgamecheck(self, getreflist(self.reftagid), self.currentfocusuid) + elif e.key() == Qt.Key.Key_Delete: + self.clicked2() + elif e.key() == Qt.Key.Key_Left: + if e.modifiers() == Qt.KeyboardModifier.ControlModifier: + self.moverank(-1) + else: + self.movefocus(-1) + elif e.key() == Qt.Key.Key_Right: + if e.modifiers() == Qt.KeyboardModifier.ControlModifier: + self.moverank(1) + else: + self.movefocus(1) super().keyPressEvent(e) + def movefocus(self, dx): + game = self.currentfocusuid + + idx1 = self.idxsave.index(game) + idx2 = (idx1 + dx) % len(self.idxsave) + + if idx1 == 0 and dx == -1: + self.flow.verticalScrollBar().setValue( + self.flow.verticalScrollBar().maximum() + ) + else: + self.flow.ensureWidgetVisible(self.flow.widget(idx2)) + try: + self.flow.widget(idx2).click() + except: + pass + def moverank(self, dx): game = self.currentfocusuid @@ -518,7 +633,7 @@ class dialog_savedgame_new(QWidget): game2 = self.idxsave[idx2] self.idxsave.insert(idx2, self.idxsave.pop(idx1)) self.flow.switchidx(idx1, idx2) - + # self.flow.ensureWidgetVisible(self.flow.widget(idx2)) idx1 = self.reflist.index(game) idx2 = self.reflist.index(game2) self.reflist.insert(idx2, self.reflist.pop(idx1)) diff --git a/py/LunaTranslator/gui/dialog_savedgame_common.py b/py/LunaTranslator/gui/dialog_savedgame_common.py index 17687a15..1a5b6c81 100644 --- a/py/LunaTranslator/gui/dialog_savedgame_common.py +++ b/py/LunaTranslator/gui/dialog_savedgame_common.py @@ -1,8 +1,8 @@ from qtsymbols import * import os, functools from traceback import print_exc -from myutils.wrapper import tryprint, threader, Singleton_close -from myutils.utils import str2rgba, find_or_create_uid, duplicateconfig +from myutils.wrapper import threader, Singleton_close +from myutils.utils import find_or_create_uid, duplicateconfig from myutils.hwnd import getExeIcon import gobject, hashlib from gui.inputdialog import autoinitdialog @@ -19,13 +19,11 @@ from myutils.config import ( ) from gui.usefulwidget import ( getIconButton, - FocusCombo, getsimplecombobox, getspinbox, getcolorbutton, getsimpleswitch, getsimplepatheditor, - FQLineEdit, getspinbox, selectcolor, SplitLine, @@ -211,12 +209,9 @@ class ClickableLabel(QLabel): clicked = pyqtSignal() -class tagitem(QWidget): - # website - TYPE_GLOABL_LIKE = 3 - TYPE_GAME_LIKE = 1 +class tagitem(QFrame): # search game - TYPE_RAND = 0 + TYPE_SEARCH = 0 TYPE_DEVELOPER = 1 TYPE_TAG = 2 TYPE_USERTAG = 3 @@ -224,132 +219,58 @@ class tagitem(QWidget): removesignal = pyqtSignal(tuple) labelclicked = pyqtSignal(tuple) - def remove(self): - self.hide() - _lay = self.layout() - _ws = [] - for i in range(_lay.count()): - witem = _lay.itemAt(i) - _ws.append(witem.widget()) - for w in _ws: - _lay.removeWidget(w) + @staticmethod + def setstyles(parent: QWidget): + parent.setStyleSheet( + """ + tagitem#red { + border: 1px solid red; + } + tagitem#black { + border: 1px solid black; + } + tagitem#green { + border: 1px solid green; + } + tagitem#blue { + border: 1px solid blue; + } + tagitem#yellow { + border: 1px solid yellow; + } + """ + ) - def paintEvent(self, event): - painter = QPainter(self) - painter.setRenderHint(QPainter.RenderHint.Antialiasing) - if self._type == tagitem.TYPE_RAND: - border_color = Qt.GlobalColor.black - elif self._type == tagitem.TYPE_DEVELOPER: - border_color = Qt.GlobalColor.red - elif self._type == tagitem.TYPE_TAG: - border_color = Qt.GlobalColor.green - elif self._type == tagitem.TYPE_USERTAG: - border_color = Qt.GlobalColor.blue - elif self._type == tagitem.TYPE_EXISTS: - border_color = Qt.GlobalColor.yellow - border_width = 1 - pen = QPen(border_color) - pen.setWidth(border_width) - painter.setPen(pen) - painter.drawRect(self.rect()) - - def __init__(self, tag, removeable=True, _type=TYPE_RAND, refdata=None) -> None: + def __init__(self, tag, removeable=True, _type=TYPE_SEARCH, refdata=None) -> None: super().__init__() + if _type == tagitem.TYPE_SEARCH: + border_color = "black" + elif _type == tagitem.TYPE_DEVELOPER: + border_color = "red" + elif _type == tagitem.TYPE_TAG: + border_color = "green" + elif _type == tagitem.TYPE_USERTAG: + border_color = "blue" + elif _type == tagitem.TYPE_EXISTS: + border_color = "yellow" + self.setObjectName(border_color) + tagLayout = QHBoxLayout() tagLayout.setContentsMargins(0, 0, 0, 0) tagLayout.setSpacing(0) - self._type = _type + key = (tag, _type, refdata) self.setLayout(tagLayout) lb = ClickableLabel() lb.setStyleSheet("background: transparent;") lb.setText(tag) lb.clicked.connect(functools.partial(self.labelclicked.emit, key)) - tagLayout.addWidget(lb) if removeable: button = getIconButton( functools.partial(self.removesignal.emit, key), icon="fa.times" ) tagLayout.addWidget(button) - - -class TagWidget(QWidget): - tagschanged = pyqtSignal(tuple) # ((tag,type,refdata),) - linepressedenter = pyqtSignal(str) - tagclicked = pyqtSignal(tuple) # tag,type,refdata - - def __init__(self, parent=None, exfoucus=True): - super().__init__(parent) - - layout = QHBoxLayout() - layout.setContentsMargins(0, 0, 0, 0) - - self.setLayout(layout) - - self.lineEdit = FocusCombo() - if exfoucus: - self.lineEdit.setLineEdit(FQLineEdit()) - # FQLineEdit导致游戏管理页面里,点击编辑框后,下边界消失。 - # FQLineEdit仅用于和webview同一窗口内焦点缺失问题,所以既然用不到那就不要多此一举了 - else: - self.lineEdit.setEditable(True) - self.lineEdit.lineEdit().returnPressed.connect( - lambda: self.linepressedenter.emit(self.lineEdit.currentText()) - ) - - self.lineEdit.setSizePolicy( - QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Maximum - ) - - layout.addWidget(self.lineEdit) - self.setSizePolicy(QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Fixed) - - self.tag2widget = {} - - def addTags(self, tags, signal=True): - for key in tags: - self.__addTag(key) - self.__calltagschanged(signal) - - @tryprint - def __addTag(self, key): - tag, _type, refdata = key - if not tag: - return - if key in self.tag2widget: - return - qw = tagitem(tag, _type=_type, refdata=refdata) - qw.removesignal.connect(self.removeTag) - qw.labelclicked.connect(self.tagclicked.emit) - layout = self.layout() - layout.insertWidget(layout.count() - 1, qw) - self.tag2widget[key] = qw - self.lineEdit.setFocus() - - def addTag(self, tag, _type, refdata=None, signal=True): - self.__addTag((tag, _type, refdata)) - self.__calltagschanged(signal) - - @tryprint - def __removeTag(self, key): - _w = self.tag2widget[key] - _w.remove() - - self.layout().removeWidget(_w) - self.tag2widget.pop(key) - - def removeTag(self, key, signal=True): - self.__removeTag(key) - self.__calltagschanged(signal) - - def __calltagschanged(self, signal): - if signal: - self.tagschanged.emit(tuple(self.tag2widget.keys())) - - def clearTag(self, signal=True): - for key in self.tag2widget.copy(): - self.__removeTag(key) - self.__calltagschanged(signal) + tagLayout.addWidget(lb) def opendirforgameuid(gameuid): diff --git a/py/LunaTranslator/gui/dialog_savedgame_setting.py b/py/LunaTranslator/gui/dialog_savedgame_setting.py index 9330abc2..e4ff754c 100644 --- a/py/LunaTranslator/gui/dialog_savedgame_setting.py +++ b/py/LunaTranslator/gui/dialog_savedgame_setting.py @@ -4,7 +4,7 @@ from qtsymbols import * import functools, uuid from datetime import datetime, timedelta from traceback import print_exc -import gobject +import gobject, winsharedutils from myutils.config import ( savehook_new_data, uid2gamepath, @@ -16,7 +16,7 @@ from myutils.config import ( ) from myutils.localetools import getgamecamptools, maycreatesettings from myutils.hwnd import getExeIcon -from myutils.wrapper import Singleton, Singleton_close, trypass +from myutils.wrapper import Singleton, Singleton_close from myutils.utils import ( gamdidchangedtask, checkpostlangmatch, @@ -34,9 +34,10 @@ from gui.inputdialog import ( autoinitdialog_items, postconfigdialog, ) -from gui.specialwidget import ScrollFlow, chartwidget +from gui.specialwidget import chartwidget from gui.usefulwidget import ( TableViewW, + FlowWidget, getsimpleswitch, getsimplepatheditor, getboxlayout, @@ -50,6 +51,8 @@ from gui.usefulwidget import ( getspinbox, getsmalllabel, listediterline, + editswitchTextBrowser, + FocusCombo, ) from gui.dynalang import ( LFormLayout, @@ -61,6 +64,7 @@ from gui.dynalang import ( LGroupBox, ) from gui.dialog_savedgame_common import tagitem +from gui.inputdialog import postconfigdialog_ @Singleton @@ -183,11 +187,11 @@ def maybehavebutton(self, gameuid, post): return None -def userlabelset(): +def userlabelset(key="usertags"): s = set() for gameuid in savehook_new_data: - s = s.union(savehook_new_data[gameuid]["usertags"]) - return list(s) + s = s.union(savehook_new_data[gameuid][key]) + return sorted(list(s)) class dialog_setting_game_internal(QWidget): @@ -199,8 +203,9 @@ class dialog_setting_game_internal(QWidget): if self.lauchpath: self.lauchpath.clear.clicked.emit() - def __init__(self, parent, gameuid) -> None: + def __init__(self, parent, gameuid, keepindexobject=None) -> None: super().__init__(parent) + self.keepindexobject = keepindexobject vbox = QVBoxLayout(self) self.lauchpath = None formLayout = LFormLayout() @@ -253,6 +258,11 @@ class dialog_setting_game_internal(QWidget): [_[0] for _ in functs], [functools.partial(self.doaddtab, _[1], gameuid) for _ in functs], delay=True, + initial=( + (self.keepindexobject, "p1") + if (self.keepindexobject is not None) + else None + ), ) vbox.addLayout(formLayout) vbox.addWidget(methodtab) @@ -285,12 +295,17 @@ class dialog_setting_game_internal(QWidget): functs = [ ("元数据", functools.partial(self.___tabf, self.metadataorigin)), ("统计", functools.partial(self.___tabf2, self.getstatistic)), - ("标签", functools.partial(self.___tabf2, self.getlabelsetting)), + ("信息", functools.partial(self.___tabf2, self.getlabelsetting)), ] methodtab, do = makesubtab_lazy( [_[0] for _ in functs], [functools.partial(self.doaddtab, _[1], gameuid) for _ in functs], delay=True, + initial=( + (self.keepindexobject, "gamedata") + if (self.keepindexobject is not None) + else None + ), ) vbox.addWidget(methodtab) do() @@ -310,6 +325,11 @@ class dialog_setting_game_internal(QWidget): [_[0] for _ in functs], [functools.partial(self.doaddtab, _[1], gameuid) for _ in functs], delay=True, + initial=( + (self.keepindexobject, "gamesetting") + if (self.keepindexobject is not None) + else None + ), ) self.methodtab = methodtab @@ -556,74 +576,106 @@ class dialog_setting_game_internal(QWidget): string = "0" return string + def tagenewitem( + self, + gameuid, + text, + refkey, + first=False, + _type=tagitem.TYPE_SEARCH, + ): + if _type == tagitem.TYPE_TAG: + text = globalconfig["tagNameRemap"].get(text, text) + qw = tagitem(text, True, _type) + + def __(gameuid, _qw, refkey, _): + t, _, _ = _ + try: + savehook_new_data[gameuid][refkey].remove(t) + self.flowwidget.removeWidget(_qw) + except: + print_exc() + + qw.removesignal.connect(functools.partial(__, gameuid, qw, refkey)) + + def safeaddtags(_): + try: + gobject.global_dialog_savedgame_new.tagswidget.addTag(*_) + except: + winsharedutils.clipboard_set(_[0]) + + qw.labelclicked.connect(safeaddtags) + if first: + self.flowwidget.insertWidget(self.labelflowmap[refkey], 1, qw) + else: + self.flowwidget.addWidget(self.labelflowmap[refkey], qw) + def getlabelsetting(self, formLayout: QVBoxLayout, gameuid): - self.labelflow = ScrollFlow() + self.labelflowmap = {} + flowwidget = FlowWidget(groups=4) + tagitem.setstyles(flowwidget) + self.flowwidget = flowwidget + scroll = QScrollArea() + scroll.setWidgetResizable(True) + scroll.setWidget(flowwidget) + formLayout.addWidget(scroll) + self.tagtypes = ["developers", "webtags", "usertags"] + self.tagtypes_zh = ["开发商", "标签", "自定义"] + self.tagtypes_1 = [ + tagitem.TYPE_DEVELOPER, + tagitem.TYPE_TAG, + tagitem.TYPE_USERTAG, + ] - def newitem(text, refkey, first=False, _type=tagitem.TYPE_RAND): - qw = tagitem(text, True, _type) + def createflows(label, key, _t, index): + self.labelflowmap[key] = index + flowwidget.addWidget(index, LLabel(label)) + for tag in savehook_new_data[gameuid][key]: + self.tagenewitem(gameuid, tag, key, _type=_t) - def __(gameuid, _qw, refkey, _): - t, _type, _ = _ - try: - _qw.remove() - savehook_new_data[gameuid][refkey].remove(t) - self.labelflow.removewidget(_qw) - except: - print_exc() + for i in range(len(self.tagtypes)): + createflows(self.tagtypes_zh[i], self.tagtypes[i], self.tagtypes_1[i], i) + flowwidget.addWidget(3, LLabel("简介")) + edit = editswitchTextBrowser() + edit.settext(savehook_new_data[gameuid].get("description", "")) - qw.removesignal.connect(functools.partial(__, gameuid, qw, refkey)) - - def safeaddtags(_): - try: - gobject.global_dialog_savedgame_new.tagswidget.addTag(*_) - except: - pass - - qw.labelclicked.connect(safeaddtags) - if first: - self.labelflow.insertwidget(0, qw) + def __(): + if not edit.text().strip(): + savehook_new_data[gameuid]["description"] = "" else: - self.labelflow.addwidget(qw) + savehook_new_data[gameuid]["description"] = edit.text().strip() - for tag in savehook_new_data[gameuid]["usertags"]: - newitem(tag, "usertags", _type=tagitem.TYPE_USERTAG) - for tag in savehook_new_data[gameuid]["developers"]: - newitem(tag, "developers", _type=tagitem.TYPE_DEVELOPER) - for tag in savehook_new_data[gameuid]["webtags"]: - newitem(tag, "webtags", _type=tagitem.TYPE_TAG) - formLayout.addWidget(self.labelflow) - _dict = {"new": 0} + edit.textChanged.connect(__) + flowwidget.addWidget(3, edit) - formLayout.addWidget(self.labelflow) button = LPushButton("添加") - - combo = getsimplecombobox(userlabelset(), _dict, "new", static=True) + typecombo = getsimplecombobox(self.tagtypes_zh, initial=2) + combo = FocusCombo() combo.setEditable(True) - combo.clearEditText() + + def __(idx): + t = combo.currentText() + combo.clear() + combo.addItems(userlabelset(self.tagtypes[idx])) + combo.setCurrentText(t) + + typecombo.currentIndexChanged.connect(__) + __(2) def _add(_): - labelset = userlabelset() tag = combo.currentText() - if (not tag) or (tag in savehook_new_data[gameuid]["usertags"]): + tp = self.tagtypes[typecombo.currentIndex()] + if (not tag) or (tag in savehook_new_data[gameuid][tp]): return - savehook_new_data[gameuid]["usertags"].insert(0, tag) - newitem(tag, "usertags", first=True, _type=tagitem.TYPE_USERTAG) + savehook_new_data[gameuid][tp].insert(0, tag) + self.tagenewitem( + gameuid, + tag, + tp, + first=True, + _type=self.tagtypes_1[typecombo.currentIndex()], + ) combo.clearEditText() - combo.clear() - combo.addItems(labelset) - try: - _ = ( - gobject.global_dialog_savedgame_new.tagswidget.lineEdit.currentText() - ) - gobject.global_dialog_savedgame_new.tagswidget.lineEdit.clear() - gobject.global_dialog_savedgame_new.tagswidget.lineEdit.addItems( - labelset - ) - gobject.global_dialog_savedgame_new.tagswidget.lineEdit.setCurrentText( - _ - ) - except: - pass button.clicked.connect(_add) @@ -631,11 +683,18 @@ class dialog_setting_game_internal(QWidget): getboxlayout( [ combo, + typecombo, button, + getIconButton(callback=self.edittagremap, icon="fa.gear"), ] ) ) + def edittagremap(self): + postconfigdialog_( + self, globalconfig["tagNameRemap"], "标签映射", ["From", "To"] + ) + def createfollowdefault( self, dic: dict, @@ -1111,7 +1170,7 @@ def calculate_centered_rect(original_rect: QRect, size: QSize) -> QRect: @Singleton_close -class dialog_setting_game(LDialog): +class dialog_setting_game(QDialog): def __init__(self, parent, gameuid, setindexhook=0) -> None: super().__init__(parent, Qt.WindowType.WindowCloseButtonHint) diff --git a/py/LunaTranslator/gui/dialog_savedgame_v3.py b/py/LunaTranslator/gui/dialog_savedgame_v3.py index 5e18610d..f8d56a75 100644 --- a/py/LunaTranslator/gui/dialog_savedgame_v3.py +++ b/py/LunaTranslator/gui/dialog_savedgame_v3.py @@ -669,7 +669,11 @@ class dialog_savedgame_v3(QWidget): tabadd_lazy( self.righttop, "设置", - lambda v: v.addWidget(dialog_setting_game_internal(self, k)), + lambda v: v.addWidget( + dialog_setting_game_internal( + self, k, keepindexobject=self.keepindexobject + ) + ), ) self.righttop.setCurrentIndex(currvis) except: @@ -835,6 +839,22 @@ class dialog_savedgame_v3(QWidget): ) self.setStyleSheet(style) + def movefocus(self, dx): + uid = self.currentfocusuid + idx1 = self.reallist[self.reftagid].index(uid) + idx2 = (idx1 + dx) % len(self.reallist[self.reftagid]) + group0 = self.stack.w(calculatetagidx(self.reftagid)) + if idx1 == 0 and dx == -1: + self.stack.verticalScrollBar().setValue( + self.stack.verticalScrollBar().maximum() + ) + else: + self.stack.ensureWidgetVisible(group0.w(idx2)) + try: + group0.w(idx2).click() + except: + pass + def __init__(self, parent) -> None: super().__init__(parent) parent.setWindowTitle("游戏管理") @@ -842,21 +862,29 @@ class dialog_savedgame_v3(QWidget): self.currentfocusuid = None self.reftagid = None self.reallist = {} + self.keepindexobject = {} class ___(stackedlist): def keyPressEvent(self, e: QKeyEvent): - if e.key() == Qt.Key.Key_Return: - startgamecheck( - self.ref, - getreflist(self.ref.reftagid), - self.ref.currentfocusuid, - ) - elif e.key() == Qt.Key.Key_Delete: - self.ref.shanchuyouxi() - elif e.key() == Qt.Key.Key_Left: - self.ref.moverank(-1) - elif e.key() == Qt.Key.Key_Right: - self.ref.moverank(1) + if self.ref.currentfocusuid: + if e.key() == Qt.Key.Key_Return: + startgamecheck( + self.ref, + getreflist(self.ref.reftagid), + self.ref.currentfocusuid, + ) + elif e.key() == Qt.Key.Key_Delete: + self.ref.shanchuyouxi() + elif e.key() == Qt.Key.Key_Left: + self.ref.moverank(-1) + elif e.key() == Qt.Key.Key_Right: + self.ref.moverank(1) + elif e.key() == Qt.Key.Key_Down: + self.ref.movefocus(1) + return e.ignore() + elif e.key() == Qt.Key.Key_Up: + self.ref.movefocus(-1) + return e.ignore() super().keyPressEvent(e) self.stack = ___() @@ -1047,6 +1075,9 @@ class dialog_savedgame_v3(QWidget): ) self.stack.w(calculatetagidx(self.reftagid)).switchidx(idx1, idx2) + self.stack.ensureWidgetVisible( + self.stack.w(calculatetagidx(self.reftagid)).w(idx2) + ) idx1 = getreflist(self.reftagid).index(uid) idx2 = getreflist(self.reftagid).index(uid2) getreflist(self.reftagid).insert(idx2, getreflist(self.reftagid).pop(idx1)) diff --git a/py/LunaTranslator/gui/inputdialog.py b/py/LunaTranslator/gui/inputdialog.py index 0930de70..9002c8cc 100644 --- a/py/LunaTranslator/gui/inputdialog.py +++ b/py/LunaTranslator/gui/inputdialog.py @@ -452,19 +452,6 @@ class autoinitdialog__(LDialog): super().__init__(parent, Qt.WindowType.WindowCloseButtonHint) self.setWindowTitle(title) self.resize(QSize(width, 10)) - for line in lines: - if line["type"] != "program": - continue - try: - func = getattr( - importlib.import_module(line["route"][0]), - line["route"][1], - ) - func(self) - except: - print_exc() - self.show() - return formLayout = VisLFormLayout() self.setLayout(formLayout) regist = {} diff --git a/py/LunaTranslator/gui/setting_about.py b/py/LunaTranslator/gui/setting_about.py index 319a158c..f0952e05 100644 --- a/py/LunaTranslator/gui/setting_about.py +++ b/py/LunaTranslator/gui/setting_about.py @@ -5,7 +5,7 @@ from myutils.config import globalconfig, static_data, _TR, get_platform from myutils.wrapper import threader, tryprint from myutils.hwnd import getcurrexe from myutils.utils import makehtml, getlanguse, dynamiclink -import requests, sys +import requests, importlib import shutil, gobject from myutils.proxy import getproxy import zipfile, os @@ -13,12 +13,15 @@ import subprocess from gui.usefulwidget import ( D_getsimpleswitch, makescrollgrid, + CollapsibleBox, makesubtab_lazy, D_getsimplecombobox, + makegrid, D_getIconButton, WebivewWidget, ) from gui.dynalang import LLabel +from gui.setting_year import yearsummary versionchecktask = queue.Queue() @@ -248,39 +251,6 @@ def versionlabelmaybesettext(self, x): self.versionlabel_cache = x -def solvelinkitems(grid, source): - name = source["name"] - link = source["link"] - grid.append([name, (makehtml(link), 2, "link")]) - - -def resourcegrid(self, l): - titles = [] - makewidgetsfunctions = [] - for sourcetype in static_data["aboutsource"]: - titles.append(sourcetype["name"]) - sources = sourcetype["sources"] - grid = [] - for source in sources: - - __grid = [] - for link in source["links"]: - __grid.append([link["name"], (makehtml(link["link"]), 2, "link")]) - grid.append( - [ - ( - dict(title=source.get("name", None), type="grid", grid=__grid), - 0, - "group", - ) - ] - ) - makewidgetsfunctions.append(functools.partial(makescrollgrid, grid)) - tab, dotab = makesubtab_lazy(titles, makewidgetsfunctions, delay=True) - l.addWidget(tab) - dotab() - - def createimageview(self): lb = QLabel() img = QPixmap.fromImage(QImage("./files/zan.jpg")) @@ -295,17 +265,60 @@ def createimageview(self): return lb -def setTab_aboutlazy(self, basel): - - resourcegrid(self, basel) - - def changelog(self, basel: QHBoxLayout): _ = WebivewWidget(self) _.navigate(dynamiclink("{main_server}/ChangeLog")) basel.addWidget(_) +def delayloadlinks(key, box): + sources = static_data["aboutsource"][key] + grid = [] + for source in sources: + __grid = [] + function = source.get("function") + if function: + func = getattr( + importlib.import_module(function[0]), + function[1], + ) + __grid.append([(func, 0)]) + else: + for link in source["links"]: + __grid.append( + [link["name"], (makehtml(link["link"]), 2, "link")] + + ([link.get("about")] if link.get("about") else []) + ) + grid.append( + [ + ( + dict(title=source.get("name", None), type="grid", grid=__grid), + 0, + "group", + ) + ] + ) + grid = [ + [ + ( + dict(type="grid", grid=grid), + 0, + "group", + ) + ] + ] + w, do = makegrid(grid, delay=True, w=False) + w.setContentsMargins(0, 0, 0, 0) + box.content_area.setLayout(w) + do() + + +def offlinelinks(key): + box = CollapsibleBox("下载") + box.setdelayload(functools.partial(delayloadlinks, key)) + return box + + def setTab_about1(self, basel): shuominggrid = [ @@ -363,12 +376,12 @@ def setTab_about1(self, basel): def setTab_about(self, basel): tab_widget, do = makesubtab_lazy( - ["关于软件", "其他设置", "资源下载", "更新记录"], + ["关于软件", "其他设置", "更新记录", "年度总结"], [ functools.partial(setTab_about1, self), functools.partial(setTab_update, self), - functools.partial(setTab_aboutlazy, self), functools.partial(changelog, self), + functools.partial(yearsummary, self), ], delay=True, ) @@ -384,6 +397,7 @@ def changeUIlanguage(_): except: pass + def setTab_update(self, basel): version = winsharedutils.queryversion(getcurrexe()) if version is None: diff --git a/py/LunaTranslator/gui/setting_cishu.py b/py/LunaTranslator/gui/setting_cishu.py index c35fe479..14ba365e 100644 --- a/py/LunaTranslator/gui/setting_cishu.py +++ b/py/LunaTranslator/gui/setting_cishu.py @@ -22,6 +22,7 @@ from gui.usefulwidget import ( D_getsimplecombobox, ) from gui.showword import showdiction +from gui.setting_about import offlinelinks def setTabcishu(self, basel): @@ -217,6 +218,7 @@ def setTabcishu_l(self): ) ] grids = [ + [(functools.partial(offlinelinks, "dict"), 0)], grids_1, grids2, [], diff --git a/py/LunaTranslator/gui/setting_display_scale.py b/py/LunaTranslator/gui/setting_display_scale.py index 961be56f..660cceb9 100644 --- a/py/LunaTranslator/gui/setting_display_scale.py +++ b/py/LunaTranslator/gui/setting_display_scale.py @@ -11,6 +11,7 @@ from gui.usefulwidget import ( makescrollgrid, D_getsimpleswitch, ) +from gui.setting_about import offlinelinks def makescalew(self, lay: QVBoxLayout): @@ -333,6 +334,7 @@ def makescalew(self, lay: QVBoxLayout): ), ("", 10), ], + [(functools.partial(offlinelinks, "magpie"),0)], ] commonfsgrid = [ diff --git a/py/LunaTranslator/gui/setting_proxy.py b/py/LunaTranslator/gui/setting_proxy.py index 11f0293d..20fdb360 100644 --- a/py/LunaTranslator/gui/setting_proxy.py +++ b/py/LunaTranslator/gui/setting_proxy.py @@ -98,7 +98,7 @@ def makegridW(grid, lay, save=False, savelist=None, savelay=None): def makeproxytab(): - lixians, pre, mianfei, develop, shoufei = splittranslatortypes() + lixians, pre, mianfei, shoufei = splittranslatortypes() mianfei = getall(l=mianfei, item="fanyi", name="./Lunatranslator/translator/%s.py") shoufei = getall(l=shoufei, item="fanyi", name=translate_exits) diff --git a/py/LunaTranslator/gui/setting_textinput_ocr.py b/py/LunaTranslator/gui/setting_textinput_ocr.py index 09776fe5..ff5a97c7 100644 --- a/py/LunaTranslator/gui/setting_textinput_ocr.py +++ b/py/LunaTranslator/gui/setting_textinput_ocr.py @@ -28,6 +28,7 @@ import gobject, qtawesome from gui.dynalang import LFormLayout, LDialog, LAction from myutils.ocrutil import ocr_end, ocr_init, ocr_run from myutils.wrapper import threader, Singleton_close +from gui.setting_about import offlinelinks def __label1(self): @@ -385,14 +386,17 @@ class showocrimage(saveposwindow): def internal(self): offline, online = splitocrtypes(globalconfig["ocr"]) - + offgrids = initgridsources(self, offline) + offgrids += [ + [(functools.partial(offlinelinks, "ocr"), 0)], + ] engines = [ [ ( dict( title="离线", type="grid", - grid=initgridsources(self, offline), + grid=offgrids, ), 0, "group", diff --git a/py/LunaTranslator/gui/setting_translate.py b/py/LunaTranslator/gui/setting_translate.py index 0b41f4b6..d5d5d356 100644 --- a/py/LunaTranslator/gui/setting_translate.py +++ b/py/LunaTranslator/gui/setting_translate.py @@ -1,7 +1,7 @@ from qtsymbols import * import functools, os import gobject, qtawesome, uuid, shutil -from myutils.config import globalconfig, translatorsetting +from myutils.config import globalconfig, translatorsetting, static_data from myutils.utils import ( selectdebugfile, splittranslatortypes, @@ -26,6 +26,7 @@ from gui.usefulwidget import ( makescrollgrid, ) from gui.dynalang import LPushButton, LLabel, LAction +from gui.setting_about import offlinelinks def deepcopydict(d): @@ -58,7 +59,7 @@ def splitapillm(l): def loadvisinternal(btnplus, copy): __vis = [] __uid = [] - lixians, pre, mianfei, develop, shoufei = splittranslatortypes() + lixians, pre, mianfei, shoufei = splittranslatortypes() if btnplus == "api": is_gpt_likes, not_is_gpt_like = splitapillm(shoufei) elif btnplus == "offline": @@ -161,6 +162,8 @@ def loadbutton(self, fanyi): aclass = "translator." + fanyi elif which == 1: aclass = "userconfig.copyed." + fanyi + else: + return return autoinitdialogx( self, translatorsetting[fanyi]["args"], @@ -222,18 +225,18 @@ def selectllmcallback(self, countnum, btnplus, fanyi, name): ), ) - if len(countnum) % 3 == 0: + offset = 5 * (len(countnum) % 3) + layout.addWidget(name, layout.rowCount() - 1, offset + 0) + layout.addWidget(swc, layout.rowCount() - 1, offset + 1) + layout.addWidget(color, layout.rowCount() - 1, offset + 2) + layout.addWidget(last, layout.rowCount() - 1, offset + 3) + if len(countnum) % 3 != 2: + layout.addWidget(QLabel(), layout.rowCount() - 1, offset + 4) + + else: layout.addWidget( getattr(self, "btnmany" + btnplus), layout.rowCount(), 5 * 2, 1, 4 ) - offset = 5 * (len(countnum) % 3) - layout.addWidget(name, layout.rowCount() - 2, offset + 0) - layout.addWidget(swc, layout.rowCount() - 2, offset + 1) - layout.addWidget(color, layout.rowCount() - 2, offset + 2) - layout.addWidget(last, layout.rowCount() - 2, offset + 3) - if len(countnum) % 3 != 2: - layout.addWidget(QLabel(), layout.rowCount() - 2, offset + 4) - countnum.append(uid) @@ -393,12 +396,12 @@ def initsome11(self, l, label=None, btnplus=False): if len(line): grids.append(line) if btnplus: - grids.append( - [ - ("", 10), - (functools.partial(createmanybtn, self, countnum, btnplus), 4), - ] - ) + + if i % 3 == 0: + grids.append([]) + if i % 3 != 2: + grids[-1].append(("", 5 * (2 - i % 3))) + grids[-1].append((functools.partial(createmanybtn, self, countnum, btnplus), 4)) return grids @@ -434,7 +437,6 @@ def initsome2(self, l, label=None, btnplus=None): return grids - def createbtnexport(self): bt = LPushButton("导出翻译记录为json文件") @@ -512,20 +514,11 @@ def setTabTwo_lazy(self, basel: QVBoxLayout): ], [], ] - _items = [ - { - "type": "file", - "dir": False, - "filter": "*.exe", - "name": "Chromium_路径", - "k": "chromepath", - }, - {"type": "okcancel"}, - ] - lixians, pre, mianfei, develop, shoufei = splittranslatortypes() + lixians, pre, mianfei, shoufei = splittranslatortypes() offlinegrid = initsome2(self, lixians, btnplus="offline") + offlinegrid += [[functools.partial(offlinelinks, "translate")]] onlinegrid = initsome11(self, mianfei) online_reg_grid += initsome2(self, shoufei, btnplus="api") pretransgrid += initsome11(self, pre) diff --git a/py/LunaTranslator/gui/setting_tts.py b/py/LunaTranslator/gui/setting_tts.py index e7229a14..37835786 100644 --- a/py/LunaTranslator/gui/setting_tts.py +++ b/py/LunaTranslator/gui/setting_tts.py @@ -9,6 +9,7 @@ from gui.inputdialog import ( autoinitdialog, yuyinzhidingsetting, ) +from gui.setting_about import offlinelinks from gui.setting_textinput import loadvalidtss from gui.usefulwidget import ( D_getsimplecombobox, @@ -131,6 +132,10 @@ def setTab5lz(self): grids = [] offline, online = splitocrtypes(globalconfig["reader"]) alltrans, alltransvis = loadvalidtss() + offilesgrid = getttsgrid(self, offline) + offilesgrid += [ + [(functools.partial(offlinelinks, "tts"), 0)], + ] grids += [ [ ( @@ -143,7 +148,7 @@ def setTab5lz(self): dict( title="离线", type="grid", - grid=getttsgrid(self, offline), + grid=offilesgrid, ), 0, "group", diff --git a/py/LunaTranslator/gui/setting_year.py b/py/LunaTranslator/gui/setting_year.py new file mode 100644 index 00000000..60061181 --- /dev/null +++ b/py/LunaTranslator/gui/setting_year.py @@ -0,0 +1,223 @@ +from qtsymbols import * +import queue +import gobject +import os +from gui.usefulwidget import WebivewWidget +from datetime import datetime, timedelta +from myutils.config import savehook_new_data, globalconfig + +timestamps = [1609459200, 1672531200, 1704067200, 1720032000] + + +def split_range_into_days(times): + everyday = {} + for start, end in times: + if start == 0: + everyday[0] = end + continue + + start_date = datetime.fromtimestamp(start) + end_date = datetime.fromtimestamp(end) + + current_date = start_date + while current_date <= end_date: + end_of_day = current_date.replace( + hour=23, minute=59, second=59, microsecond=0 + ) + end_of_day = end_of_day.timestamp() + 1 + + if end_of_day >= end_date.timestamp(): + useend = end_date.timestamp() + else: + useend = end_of_day + duration = useend - current_date.timestamp() + today = end_of_day - 1 + if today not in everyday: + everyday[today] = 0 + everyday[today] += duration + current_date += timedelta(days=1) + current_date = current_date.replace( + hour=0, minute=0, second=0, microsecond=0 + ) + return everyday + + +def calc_uid_times(inf): + everytimes = {} + for uid, ls in inf.items(): + x = 0 + for s, e in ls: + x += e - s + if int(x): + everytimes[uid] = x + + return everytimes + + +def spliteverygameinmothon(yearinfos): + allmonths = [] + for uid in yearinfos: + everydays = split_range_into_days(yearinfos, uidx=uid) + everymonth = group_by_month(everydays) + + +def getthisyearinfo(allinfos): + current_year = datetime.now().year + yearinfos = {} + for uid, ls in allinfos.items(): + yearinfos[uid] = [] + for s, e in ls: + if datetime.fromtimestamp(s).year == current_year: + yearinfos[uid].append((s, e)) + if len(yearinfos[uid]) == 0: + yearinfos.pop(uid) + return yearinfos + + +def geteverygameeveryday(yearinfos): + x = {} + for uid, ls in yearinfos.items(): + x[uid] = split_range_into_days(ls) + return x + + +def group_by_month(timerangeindays): + every = {} + for uid, ls in timerangeindays.items(): + + inmonths = {} + for day, time in ls.items(): + # 将时间戳转换为日期对象(时间戳为毫秒,需要除以1000) + date = datetime.fromtimestamp(day) + if date.month not in inmonths: + inmonths[date.month] = 0 + inmonths[date.month] += time + every[uid] = inmonths + return every + + +def have_game_days_count(everydays): + days = set() + for info in everydays.values(): + for d in info: + days.add(d) + return len(days) + + +def everymonths_game_images(everymonth): + months = {} + for i in range(1, 13): + months[i] = {} + for uid in everymonth: + for m, tm in everymonth[uid].items(): + months[m][uid] = tm + return months + + +def getuidimage(uid): + data = savehook_new_data.get(uid) + if not data: + return + main = savehook_new_data[uid].get("currentmainimage") + if (main in savehook_new_data[uid]["imagepath_all"]) and os.path.exists(main): + return os.path.abspath(main) + else: + for _ in savehook_new_data[uid]["imagepath_all"]: + if os.path.exists(_): + return os.path.abspath(_) + + +def guji_word(everytimes, alleverytimes): + cnt_zishu = 0 + for uid in everytimes: + data = savehook_new_data.get(uid) + if not data: + continue + if not everytimes[uid]: + return + cnt_zishu += ( + data.get("statistic_wordcount", 0) * everytimes[uid] / alleverytimes[uid] + ) + return cnt_zishu + + +def getimages(xx): + uids = list(xx.keys()) + uids.sort(key=lambda x: -xx[x]) + tu = [] + for uid in uids: + img = getuidimage(uid) + if img: + tu.append(img) + return tu + + +def getallgamelabels(yearinfos): + developers = {} + webtags = {} + # 可以考虑玩的最多的游戏的标签,和玩的时间最长的标签 + for uid in yearinfos: + data = savehook_new_data.get(uid) + if not data: + continue + img = getuidimage(uid) + if not img: + continue + for dev in data["developers"]: + if dev not in developers: + developers[dev] = [] + developers[dev].append(img) + for tag in data["webtags"]: + tag = globalconfig["tagNameRemap"].get(tag, tag) + if tag not in webtags: + webtags[tag] = [] + webtags[tag].append(img) + return developers, webtags + + +def yearsummary(self, basel: QHBoxLayout): + allinfos = gobject.baseobject.playtimemanager.all() # {uid:[[s,e]]} + yearinfos = getthisyearinfo(allinfos) # {uid:[[s,e]]} + alleverytimes = calc_uid_times(allinfos) # {uid:time} + everytimes = calc_uid_times(yearinfos) # {uid:time} + everydays = geteverygameeveryday(yearinfos) # {uid:{day_tms:time}} + everymonth = group_by_month(everydays) # {uid:{month:time}} + everymonth_uid_time = everymonths_game_images(everymonth) # {month:{uid:time}} + everymonth_time = { + k: sum(v.values()) for k, v in everymonth_uid_time.items() + } # {month:time} + uids = list(everytimes.keys()) + uids.sort(key=lambda x: -everytimes[x]) + tu = getimages(everytimes) + tu_m = {} + for m, info in everymonth_uid_time.items(): + tu_m[m] = getimages(info) + developer, webtags = getallgamelabels(yearinfos) + _ = WebivewWidget(self) + with open("files/yearsummary/yearsummary.value.js", "w", encoding="utf8") as ff2: + ff2.write( + r""" +GAMES_YEAR_PLAYED={GAMES_YEAR_PLAYED} +TIME_YEAR_PLAYED={TIME_YEAR_PLAYED} +COUNT_ZISHU_YEAR_PLAYED={COUNT_ZISHU_YEAR_PLAYED} +TIME_YEAR_PLAYED_DAY={TIME_YEAR_PLAYED_DAY} +TOP25_TIME_IMAGE={TOP25_TIME_IMAGE} +TOP25_TIME_IMAGE_M={TOP25_TIME_IMAGE_M} +everymonth_time={everymonth_time} +developer={developer} +webtags={webtags} +""".format( + developer=developer, + webtags=webtags, + GAMES_YEAR_PLAYED=len(everytimes), + TIME_YEAR_PLAYED=int(sum(everytimes.values()) / 3600), + COUNT_ZISHU_YEAR_PLAYED=int(guji_word(everytimes, alleverytimes)), + TIME_YEAR_PLAYED_DAY=have_game_days_count(everydays), + TOP25_TIME_IMAGE=tu, + TOP25_TIME_IMAGE_M=tu_m, + everymonth_time=everymonth_time, + ) + ) + + _.navigate(os.path.abspath("files/yearsummary/yearsummary.html")) + basel.addWidget(_) diff --git a/py/LunaTranslator/gui/specialwidget.py b/py/LunaTranslator/gui/specialwidget.py index abe9cbec..ba0b4f81 100644 --- a/py/LunaTranslator/gui/specialwidget.py +++ b/py/LunaTranslator/gui/specialwidget.py @@ -153,165 +153,6 @@ class ScrollArea(QScrollArea): super().keyPressEvent(e) -class ScrollFlow(QWidget): - bgclicked = pyqtSignal() - - def resizeEvent(self, a0) -> None: - self.qscrollarea.resize(self.size()) - return super().resizeEvent(a0) - - def __init__(self): - super(ScrollFlow, self).__init__() - - class qw(QWidget): - def mousePressEvent(_, _2) -> None: - self.bgclicked.emit() - - self.listWidget = qw(self) - # self.listWidget.setFixedWidth(600) - self.lazyitems = [] - self.lazydoneidx = [] - self.l = FlowLayout() - - self.listWidget.setLayout(self.l) - - self.qscrollarea = QScrollArea(self) - self.qscrollarea.setWidgetResizable(True) - self.qscrollarea.setWidget(self.listWidget) - - @trypass - def addwidget(self, wid): - self.l.addWidget(wid) - - @trypass - def insertwidget(self, idx, wid): - self.l.insertWidget(idx, wid) - - @trypass - def removewidget(self, wid): - self.l.removeWidget(wid) - - @trypass - def removeidx(self, index): - _ = self.l.takeAt(index) - _.widget().hide() - - @trypass - def setfocus(self, idx): - self.widget(idx).setFocus() - - @trypass - def widget(self, idx): - idx = min(idx, len(self.l._item_list) - 1) - idx = max(idx, 0) - return self.l._item_list[idx].widget() - - -class FlowLayout(QLayout): - heightChanged = pyqtSignal(int) - - def __init__(self, parent=None, margin=0, spacing=-1): - super().__init__(parent) - if parent is not None: - self.setContentsMargins(margin, margin, margin, margin) - self.setSpacing(spacing) - - self._item_list = [] - - def __del__(self): - while self.count(): - self.takeAt(0) - - def insertWidget(self, idx, widget): - item = QWidgetItem(widget) - widget.setParent(self.parentWidget()) - widget.adjustSize() - widget.setVisible(True) - self._item_list.insert(idx, item) - - self._do_layout(self.geometry(), False) - - def addItem(self, item): # pylint: disable=invalid-name - self._item_list.append(item) - - def addSpacing(self, size): # pylint: disable=invalid-name - self.addItem( - QSpacerItem(size, 0, QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Minimum) - ) - - def count(self): - return len(self._item_list) - - def itemAt(self, index): # pylint: disable=invalid-name - if 0 <= index < len(self._item_list): - return self._item_list[index] - return None - - def takeAt(self, index): # pylint: disable=invalid-name - if 0 <= index < len(self._item_list): - return self._item_list.pop(index) - return None - - def setGeometry(self, rect): # pylint: disable=invalid-name - super().setGeometry(rect) - self._do_layout(rect, False) - - def sizeHint(self): # pylint: disable=invalid-name - return self.minimumSize() - - def minimumSize(self): # pylint: disable=invalid-name - size = QSize() - - for item in self._item_list: - minsize = item.minimumSize() - extent = item.geometry().bottomRight() - size = size.expandedTo(QSize(minsize.width(), extent.y())) - - margin = self.contentsMargins().left() - size += QSize(2 * margin, 2 * margin) - return size - - def _do_layout(self, rect, test_only=False): - m = self.contentsMargins() - effective_rect = rect.adjusted(+m.left(), +m.top(), -m.right(), -m.bottom()) - x = effective_rect.x() - y = effective_rect.y() - line_height = 0 - for item in self._item_list: - wid = item.widget() - - space_x = self.spacing() - space_y = self.spacing() - if wid is not None: - space_x += wid.style().layoutSpacing( - QSizePolicy.ControlType.PushButton, - QSizePolicy.ControlType.PushButton, - Qt.Orientation.Horizontal, - ) - space_y += wid.style().layoutSpacing( - QSizePolicy.ControlType.PushButton, - QSizePolicy.ControlType.PushButton, - Qt.Orientation.Vertical, - ) - - next_x = x + item.sizeHint().width() + space_x - if next_x - space_x > effective_rect.right() and line_height > 0: - x = effective_rect.x() - y = y + line_height + space_y - next_x = x + item.sizeHint().width() + space_x - line_height = 0 - - if not test_only: - sz = item.sizeHint() - item.setGeometry(QRect(QPoint(x, y), sz)) - x = next_x - line_height = max(line_height, item.sizeHint().height()) - - new_height = y + line_height - rect.y() - self.heightChanged.emit(new_height) - return new_height - - class lazyscrollflow(ScrollArea): bgclicked = pyqtSignal() diff --git a/py/LunaTranslator/gui/usefulwidget.py b/py/LunaTranslator/gui/usefulwidget.py index 20db21b0..20673387 100644 --- a/py/LunaTranslator/gui/usefulwidget.py +++ b/py/LunaTranslator/gui/usefulwidget.py @@ -1,9 +1,9 @@ from qtsymbols import * -import os, platform, functools, uuid, json, math, csv, io, pickle +import os, re, functools, uuid, json, math, csv, io, pickle from traceback import print_exc import windows, qtawesome, winsharedutils, gobject from webviewpy import webview_native_handle_kind_t, Webview -from myutils.config import _TR, globalconfig, _TRL +from myutils.config import _TR, globalconfig from myutils.wrapper import Singleton_close, tryprint from myutils.utils import nowisdark, checkportavailable, checkisusingwine from gui.dynalang import ( @@ -913,8 +913,19 @@ def comboboxcallbackwrap(s: SuperCombo, d, k, call, _): def getsimplecombobox( - lst, d, k, callback=None, fixedsize=False, internal=None, static=False, emit=False + lst, + d=None, + k=None, + callback=None, + fixedsize=False, + internal=None, + static=False, + initial=None, ): + if d is None: + d = {} + if initial is not None: + d[k] = initial s = SuperCombo(static=static) s.addItems(lst, internal) @@ -1772,8 +1783,14 @@ def makegroupingrid(args): if title: group.setTitle(title) else: + _id = "luna" + str(uuid.uuid4()) + group.setObjectName(_id) group.setStyleSheet( - "QGroupBox{ margin-top:0px;} QGroupBox:title {margin-top: 0px;}" + "QGroupBox#" + + _id + + "{ margin-top:0px;} QGroupBox#" + + _id + + ":title {margin-top: 0px;}" ) if _type == "grid": @@ -1858,16 +1875,18 @@ def automakegrid(grid: QGridLayout, lis, save=False, savelist=None): grid.setRowMinimumHeight(nowr, 25) -def makegrid(grid=None, save=False, savelist=None, savelay=None, delay=False): +def makegrid(grid=None, save=False, savelist=None, savelay=None, delay=False, w=True): class gridwidget(QWidget): pass - gridlayoutwidget = gridwidget() + if w: + gridlayoutwidget = gridwidget() gridlay = QGridLayout() gridlay.setAlignment(Qt.AlignmentFlag.AlignTop) - gridlayoutwidget.setLayout(gridlay) - gridlayoutwidget.setStyleSheet("gridwidget{background-color:transparent;}") + if w: + gridlayoutwidget.setLayout(gridlay) + gridlayoutwidget.setStyleSheet("gridwidget{background-color:transparent;}") def do(gridlay, grid, save, savelist, savelay): automakegrid(gridlay, grid, save, savelist) @@ -1875,6 +1894,8 @@ def makegrid(grid=None, save=False, savelist=None, savelay=None, delay=False): savelay.append(gridlay) __do = functools.partial(do, gridlay, grid, save, savelist, savelay) + if not w: + gridlayoutwidget = gridlay if not delay: __do() return gridlayoutwidget @@ -1899,14 +1920,17 @@ def makescrollgrid(grid, lay, save=False, savelist=None, savelay=None): def makesubtab_lazy( - titles=None, functions=None, klass=None, callback=None, delay=False + titles=None, functions=None, klass=None, callback=None, delay=False, initial=None ): if klass: - tab = klass() + tab: LTabWidget = klass() else: tab = LTabWidget() - def __(t, i): + def __(t: LTabWidget, initial, i): + if initial: + object, key = initial + object[key] = i try: w = t.currentWidget() if "lazyfunction" in dir(w): @@ -1917,14 +1941,20 @@ def makesubtab_lazy( if callback: callback(i) - tab.currentChanged.connect(functools.partial(__, tab)) + can = initial and (initial[1] in initial[0]) + if not can: + tab.currentChanged.connect(functools.partial(__, tab, initial)) - def __do(tab, titles, functions): + def __do(tab: LTabWidget, titles, functions, initial): if titles and functions: for i, func in enumerate(functions): tabadd_lazy(tab, titles[i], func) + if can: + tab.setCurrentIndex(initial[0][initial[1]]) + tab.currentChanged.connect(functools.partial(__, tab, initial)) + tab.currentChanged.emit(initial[0][initial[1]]) - ___do = functools.partial(__do, tab, titles, functions) + ___do = functools.partial(__do, tab, titles, functions, initial) if not delay: ___do() return tab @@ -2561,3 +2591,152 @@ class VisLFormLayout(LFormLayout): label.widget().deleteLater() tres.fieldItem.widget().hide() self._row_vis[row_index] = visible + + +class CollapsibleBox(QWidget): + def setdelayload(self, func): + self.func = func + + def __init__(self, title="", parent=None): + super(CollapsibleBox, self).__init__(parent) + + self.toggle_button = QToolButton(text=title, checkable=True, checked=False) + self.toggle_button.setToolButtonStyle( + Qt.ToolButtonStyle.ToolButtonTextBesideIcon + ) + self.toggle_button.toggled.connect(self.on_toggled) + self.content_area = QWidget() + lay = QVBoxLayout(self) + lay.setSpacing(0) + lay.setContentsMargins(0, 0, 0, 0) + lay.addWidget(self.toggle_button) + lay.addWidget(self.content_area) + self.func = None + self.collapse() + + def on_toggled(self, checked): + if checked: + if self.func: + self.func(self) + self.func = None + self.toggle_button.setIcon(qtawesome.icon("fa.chevron-down")) + self.content_area.setVisible(True) + else: + self.toggle_button.setIcon(qtawesome.icon("fa.chevron-right")) + self.content_area.setVisible(False) + + def expand(self): + self.toggle_button.setChecked(True) + self.on_toggled(True) + + def collapse(self): + self.toggle_button.setChecked(False) + self.on_toggled(False) + + +class editswitchTextBrowser(QWidget): + textChanged = pyqtSignal(str) + + def heightForWidth(self, w): + return self.browser.heightForWidth(w) + + def resizeEvent(self, a0): + self.switch.move(self.width() - self.switch.width(), 0) + return super().resizeEvent(a0) + + def __init__(self, parent=None): + super().__init__(parent) + stack = QStackedWidget() + self.edit = QPlainTextEdit() + self.browser = QLabel() + self.browser.setAlignment(Qt.AlignmentFlag.AlignTop) + self.browser.setWordWrap(True) + self.browser.setTextInteractionFlags( + Qt.TextInteractionFlag.TextSelectableByMouse + ) + self.edit.textChanged.connect( + lambda: ( + self.browser.setText( + "" + + re.sub( + r'href="([^"]+)"', + "", + re.sub(r'src="([^"]+)"', "", self.edit.toPlainText()), + ) + + "" + ), + self.textChanged.emit(self.edit.toPlainText()), + ) + ) + stack.addWidget(self.browser) + stack.addWidget(self.edit) + l = QHBoxLayout() + self.setLayout(l) + l.setContentsMargins(0, 0, 0, 0) + l.addWidget(stack) + self.switch = MySwitch(self, icon="fa.edit") + self.switch.setFixedSize(QSize(25, 25)) + self.switch.raise_() + self.switch.clicked.connect(lambda c: stack.setCurrentIndex(not c)) + + def settext(self, text): + self.edit.setPlainText(text) + + def text(self): + return self.edit.toPlainText() + + +class FlowWidget(QWidget): + def __init__(self, parent=None, groups=3): + super().__init__(parent) + self.margin = QMargins(5, 5, 5, 5) + self.spacing = 5 + self._item_list = [[] for _ in range(groups)] + + def insertWidget(self, group, index, w: QWidget): + w.setParent(self) + w.show() + self._item_list[group].insert(index, w) + self.doresize() + + def addWidget(self, group, w: QWidget): + self.insertWidget(group, len(self._item_list[group]), w) + + def removeWidget(self, w: QWidget): + for _ in self._item_list: + if w in _: + _.remove(w) + w.deleteLater() + self.doresize() + break + + def doresize(self): + line_height = 0 + spacing = self.spacing + y = self.margin.left() + for listi in self._item_list: + x = self.margin.top() + for i, item in enumerate(listi): + + next_x = x + item.sizeHint().width() + spacing + if ( + next_x - spacing + self.margin.right() > self.width() + and line_height > 0 + ): + x = self.margin.top() + y = y + line_height + spacing + next_x = x + item.sizeHint().width() + spacing + + size = item.sizeHint() + if (i == len(listi) - 1) and isinstance(item, editswitchTextBrowser): + w = self.width() - self.margin.right() - x + size = QSize(w, max(size.height(), item.heightForWidth(w))) + + item.setGeometry(QRect(QPoint(x, y), size)) + line_height = max(line_height, size.height()) + x = next_x + y = y + line_height + spacing + self.setFixedHeight(y + self.margin.bottom() - spacing) + + def resizeEvent(self, a0): + self.doresize() diff --git a/py/LunaTranslator/metadata/abstract.py b/py/LunaTranslator/metadata/abstract.py index cb9b29a4..754e361d 100644 --- a/py/LunaTranslator/metadata/abstract.py +++ b/py/LunaTranslator/metadata/abstract.py @@ -157,6 +157,7 @@ class common: developers = data.get("developers", []) webtags = data.get("webtags", []) imagepath_all = data.get("imagepath_all", []) + description=data.get('description',None) normaled = [ os.path.abspath(_) for _ in savehook_new_data[gameuid]["imagepath_all"] ] @@ -175,6 +176,8 @@ class common: _urls = [_[1] for _ in savehook_new_data[gameuid]["relationlinks"]] if _url not in _urls: savehook_new_data[gameuid]["relationlinks"].append((_vis, _url)) + if description and not savehook_new_data[gameuid].get('description'): + savehook_new_data[gameuid]['description']=description if namemap: dedump = set() for _ in savehook_new_data[gameuid]["namemap2"]: diff --git a/py/LunaTranslator/metadata/bangumi.py b/py/LunaTranslator/metadata/bangumi.py index 47c20d9c..56f5acc3 100644 --- a/py/LunaTranslator/metadata/bangumi.py +++ b/py/LunaTranslator/metadata/bangumi.py @@ -346,7 +346,6 @@ class searcher(common): response = self.proxysession.get( "https://api.bgm.tv/v0/subjects/{}".format(sid), headers=headers ) - print(response.text) try: response = response.json() except: @@ -384,4 +383,5 @@ class searcher(common): "imagepath_all": [imagepath], "webtags": vndbtags, "developers": developers, + "description": response["summary"].replace("\n", "
"), } diff --git a/py/LunaTranslator/metadata/dlsite.py b/py/LunaTranslator/metadata/dlsite.py index 953e8ee3..fad932d9 100644 --- a/py/LunaTranslator/metadata/dlsite.py +++ b/py/LunaTranslator/metadata/dlsite.py @@ -113,10 +113,14 @@ class searcher(common): "https:" + _[0] for _ in re.findall('
', inner) ] print(imags1) - + description = simplehtmlparser( + response.text, "div", '
' + ) return { "title": title, "imagepath_all": [ @@ -177,4 +180,5 @@ class searcher(common): ], "webtags": tags, "developers": [devp], + 'description':description } diff --git a/py/LunaTranslator/metadata/steam.py b/py/LunaTranslator/metadata/steam.py index 27c6b520..b9ed5f02 100644 --- a/py/LunaTranslator/metadata/steam.py +++ b/py/LunaTranslator/metadata/steam.py @@ -172,7 +172,6 @@ class searcher(common): "https://store.steampowered.com/api/appdetails?appids={}".format(_id), proxies=self.proxy, ).json()[str(_id)]["data"] - devs = data.get("developers", []) + data.get("publishers", []) tagsofficial = [ _["description"] for _ in data.get("genres", []) @@ -191,4 +190,5 @@ class searcher(common): ], "webtags": tagsofficial, "developers": devs, + "description":data['detailed_description'] } diff --git a/py/LunaTranslator/metadata/vndb.py b/py/LunaTranslator/metadata/vndb.py index f09b3435..c9a1b6ee 100644 --- a/py/LunaTranslator/metadata/vndb.py +++ b/py/LunaTranslator/metadata/vndb.py @@ -126,7 +126,7 @@ def getinfosbyvid(proxy, vid): "vn", { "filters": ["id", "=", vid], - "fields": "tags.rating,tags.name,title,titles.title,titles.main,screenshots.url,image.url,developers.name,developers.original", + "fields": "tags.rating,tags.name,title,titles.title,titles.main,screenshots.url,image.url,developers.name,developers.original,description", }, ) if js: @@ -152,6 +152,7 @@ def getinfosbyvid(proxy, vid): sc=imgs, dev=dev, tags=sorted(tags, key=lambda x: -rates[tags.index(x)]), + description=js["results"][0]['description'] ) @@ -379,4 +380,5 @@ class searcher(common): "imagepath_all": img + sc, "webtags": infos["tags"], "developers": infos["dev"], + "description":infos['description'] } diff --git a/py/LunaTranslator/myutils/config.py b/py/LunaTranslator/myutils/config.py index aabf9119..a8e243ce 100644 --- a/py/LunaTranslator/myutils/config.py +++ b/py/LunaTranslator/myutils/config.py @@ -200,6 +200,7 @@ def getdefaultsavehook(title=None): "imagepath_all": [], "developers": [], "webtags": [], # 标签 + # "description": "", # 简介 # "infopath": None, # 离线存储的主页 } if title and len(title): diff --git a/py/LunaTranslator/myutils/localetools.py b/py/LunaTranslator/myutils/localetools.py index ae76663e..85712810 100644 --- a/py/LunaTranslator/myutils/localetools.py +++ b/py/LunaTranslator/myutils/localetools.py @@ -1,6 +1,6 @@ import windows, os, winsharedutils, re, functools from qtsymbols import * -from myutils.config import savehook_new_data, get_launchpath, globalconfig, get_platform +from myutils.config import savehook_new_data, get_launchpath, globalconfig, get_platform, _TR from gui.usefulwidget import getlineedit, getsimplecombobox, getsimplepatheditor from traceback import print_exc import xml.etree.ElementTree as ET @@ -50,9 +50,11 @@ class le_internal(LEbase): LCID=0x11, CodePage=932, RedirectRegistry=False, HookUILanguageAPI=False ) - def getlrpath(self): + def getlrpath(self, show=False): LEProc = globalconfig.get("le_extra_path", "") if not (LEProc and os.path.exists(LEProc)): + if show: + return _TR("内置") LEProc = os.path.abspath("files/plugins/Locale/Locale.Emulator/LEProc.exe") return LEProc @@ -129,14 +131,12 @@ class le_internal(LEbase): layout.addRow( "路径", getsimplepatheditor( - self.getlrpath(), + self.getlrpath(show=True), False, False, filter1="LEProc.exe", callback=functools.partial(self.reselect, config, Guids), - clearset=lambda: os.path.abspath( - "files/plugins/Locale/Locale.Emulator/LEProc.exe" - ), + clearset=lambda: _TR("内置"), icons=("fa.gear", "fa.refresh"), ), ) @@ -167,9 +167,11 @@ class NTLEAS64(LEbase): continue config[k] = v - def getlrpath(self): + def getlrpath(self, show=False): LEProc = globalconfig.get("ntleas_extra_path", "") if not (LEProc and os.path.exists(LEProc)): + if show: + return _TR("内置") LEProc = os.path.abspath("files/plugins/Locale/ntleas046_x64/Placeholder") return LEProc @@ -204,14 +206,12 @@ class NTLEAS64(LEbase): layout.addRow( "路径", getsimplepatheditor( - self.getlrpath(), + self.getlrpath(show=True), False, False, filter1="ntleasWin.exe", callback=self.reselect, - clearset=lambda: os.path.abspath( - "files/plugins/Locale/ntleas046_x64/Placeholder" - ), + clearset=lambda: _TR("内置"), icons=("fa.gear", "fa.refresh"), ), ) @@ -265,9 +265,11 @@ class lr_internal(LEbase): print_exc() return Names, Guids, run_as_admins - def getlrpath(self): + def getlrpath(self, show=False): LEProc = globalconfig.get("lr_extra_path", "") if not (LEProc and os.path.exists(LEProc)): + if show: + return _TR("内置") LEProc = os.path.abspath("files/plugins/Locale/Locale_Remulator/LRProc.exe") return LEProc @@ -303,14 +305,12 @@ class lr_internal(LEbase): layout.addRow( "路径", getsimplepatheditor( - self.getlrpath(), + self.getlrpath(show=True), False, False, filter1="LRProc.exe", callback=functools.partial(self.reselect, config, Guids), - clearset=lambda: os.path.abspath( - "files/plugins/Locale/Locale_Remulator/LRProc.exe" - ), + clearset=lambda: _TR("内置"), icons=("fa.gear", "fa.refresh"), ), ) diff --git a/py/LunaTranslator/myutils/traceplaytime.py b/py/LunaTranslator/myutils/traceplaytime.py index 3208838b..4944d5c1 100644 --- a/py/LunaTranslator/myutils/traceplaytime.py +++ b/py/LunaTranslator/myutils/traceplaytime.py @@ -10,6 +10,17 @@ import gobject class playtimemanager: + def all(self): + res = self.sqlsavegameinfo.execute( + "SELECT gameinternalid_v2.gameuid, trace_strict.timestart, trace_strict.timestop FROM gameinternalid_v2 JOIN trace_strict ON gameinternalid_v2.gameinternalid = trace_strict.gameinternalid " + ).fetchall() + mp = {} + for uid, s, e in res: + if uid not in mp: + mp[uid] = [] + mp[uid].append((s, e)) + return mp + def __init__(self): self.sqlsavegameinfo = sqlite3.connect( @@ -111,7 +122,9 @@ class playtimemanager: gameinternalid = self.get_gameinternalid(uid) if uid in dic: self.sqlsavegameinfo.execute( - "UPDATE {} SET timestop = ? WHERE (gameinternalid = ? and timestart = ?)".format(table), + "UPDATE {} SET timestop = ? WHERE (gameinternalid = ? and timestart = ?)".format( + table + ), (_t, gameinternalid, dic[uid]), ) diff --git a/py/LunaTranslator/myutils/utils.py b/py/LunaTranslator/myutils/utils.py index 31f41905..b2457750 100644 --- a/py/LunaTranslator/myutils/utils.py +++ b/py/LunaTranslator/myutils/utils.py @@ -356,7 +356,7 @@ def checkportavailable(port): def splittranslatortypes(): - pre, offline, free, dev, api = [], [], [], [], [] + pre, offline, free, api = [], [], [], [] for k in globalconfig["fanyi"]: try: {"pre": pre, "offline": offline, "free": free, "api": api}[ @@ -365,7 +365,7 @@ def splittranslatortypes(): except: pass - return offline, pre, free, dev, api + return offline, pre, free, api def splitocrtypes(dic): diff --git a/py/LunaTranslator/ocrengines/googlelens.py b/py/LunaTranslator/ocrengines/googlelens.py index 4e6b1958..022eeb08 100644 --- a/py/LunaTranslator/ocrengines/googlelens.py +++ b/py/LunaTranslator/ocrengines/googlelens.py @@ -13,8 +13,9 @@ class OCR(baseocr): headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36", } + cookies = {"SOCS": "CAESEwgDEgk0ODE3Nzk3MjQaAmVuIAEaBgiA_LyaBg"} files = {"encoded_image": ("screenshot.png", imagebinary, "image/png")} - res = self.proxysession.post(url, files=files, headers=headers) + res = self.proxysession.post(url, files=files, headers=headers, cookies=cookies) match = regex.search(res.text) if not match: return diff --git a/py/LunaTranslator/ocrengines/local.py b/py/LunaTranslator/ocrengines/local.py index 2bb7866c..448d9974 100644 --- a/py/LunaTranslator/ocrengines/local.py +++ b/py/LunaTranslator/ocrengines/local.py @@ -114,6 +114,8 @@ def getallsupports(): def dodownload(combo: QComboBox, allsupports: list): + if not allsupports: + return lang = allsupports[combo.currentIndex()] gobject.baseobject.openlink( dynamiclink("{main_server}/Resource/ocr_models/" + lang + ".zip") @@ -121,6 +123,8 @@ def dodownload(combo: QComboBox, allsupports: list): def doinstall(self, combo: QComboBox, allsupports: list, parent, callback): + if not allsupports: + return lang = allsupports[combo.currentIndex()] f = QFileDialog.getOpenFileName(parent, filter=lang + ".zip") fn = f[0] @@ -135,8 +139,10 @@ def doinstall(self, combo: QComboBox, allsupports: list, parent, callback): print_exc() -def question(dialog: QDialog): +def question(): + dialog = QWidget() formLayout = LFormLayout() + formLayout.setContentsMargins(0, 0, 0, 0) dialog.setLayout(formLayout) supportlang = LLabel() formLayout.addRow("当前支持的语言", supportlang) @@ -166,6 +172,7 @@ def question(dialog: QDialog): "添加语言包", getboxlayout([combo, btndownload, btninstall], makewidget=True), ) + return dialog class OCR(baseocr): diff --git a/py/LunaTranslator/ocrengines/windowsocr.py b/py/LunaTranslator/ocrengines/windowsocr.py index 4b570df8..16ed28f2 100644 --- a/py/LunaTranslator/ocrengines/windowsocr.py +++ b/py/LunaTranslator/ocrengines/windowsocr.py @@ -4,8 +4,7 @@ from myutils.config import _TR, static_data, getlang_inner2show from myutils.utils import dynamiclink from ocrengines.baseocrclass import baseocr from qtsymbols import * -from gui.usefulwidget import getboxlayout -from gui.dynalang import LPushButton, LFormLayout, LLabel +from gui.dynalang import LPushButton, LLabel from myutils.utils import getlanguagespace @@ -31,8 +30,10 @@ def initsupports(): return supportmap -def question(dialog: QDialog): - formLayout = LFormLayout() +def question(): + dialog = QWidget() + formLayout = QHBoxLayout() + formLayout.setContentsMargins(0, 0, 0, 0) dialog.setLayout(formLayout) _allsupport = initsupports() supportlang = LLabel() @@ -43,9 +44,10 @@ def question(dialog: QDialog): dynamiclink("{docs_server}/#/zh/windowsocr") ) ) - formLayout.addRow( - "当前支持的语言", getboxlayout([supportlang, btndownload], makewidget=True) - ) + formLayout.addWidget(LLabel("当前支持的语言")) + formLayout.addWidget(supportlang) + formLayout.addWidget(btndownload) + return dialog class OCR(baseocr): @@ -85,7 +87,9 @@ class OCR(baseocr): ) else: uselang = self.srclang - ret = winrtutils.OCR_f(imagebinary, self.supportmap[uselang], getlanguagespace(uselang)) + ret = winrtutils.OCR_f( + imagebinary, self.supportmap[uselang], getlanguagespace(uselang) + ) boxs = [_[1:] for _ in ret] texts = [_[0] for _ in ret] return {"box": boxs, "text": texts} diff --git a/py/LunaTranslator/qtsymbols.py b/py/LunaTranslator/qtsymbols.py index 43f198ac..28825606 100644 --- a/py/LunaTranslator/qtsymbols.py +++ b/py/LunaTranslator/qtsymbols.py @@ -1,8 +1,8 @@ try: from PyQt5 import QtSvg - from PyQt5.QtWidgets import QFrame,QListView,QCheckBox,QAbstractItemView,QTextEdit,QTableView,QHeaderView,QColorDialog,QSpinBox,QDoubleSpinBox,QComboBox,QDialogButtonBox,QMainWindow,QMessageBox,QDialog,QGridLayout,QTextBrowser,QGraphicsDropShadowEffect,QWidget,QSizePolicy,QScrollArea,QApplication,QPushButton,QSystemTrayIcon,QPlainTextEdit,QAction,QMenu,QFileDialog,QKeySequenceEdit,QLabel,QSpacerItem,QWidgetItem,QLayout,QTextBrowser,QLineEdit,QFormLayout,QSizePolicy,QTabWidget,QTabBar,QSplitter,QListWidget,QListWidgetItem,QHBoxLayout,QVBoxLayout,QSizeGrip,QFontComboBox,QProgressBar,QRadioButton,QButtonGroup,QSlider,QToolTip,QGroupBox,QGraphicsOpacityEffect,QStackedWidget,QStyledItemDelegate,QStyleOptionViewItem,QFontDialog,QTreeView + from PyQt5.QtWidgets import QFrame,QListView,QCheckBox,QAbstractItemView,QTextEdit,QTableView,QHeaderView,QColorDialog,QSpinBox,QDoubleSpinBox,QComboBox,QDialogButtonBox,QMainWindow,QMessageBox,QDialog,QGridLayout,QTextBrowser,QGraphicsDropShadowEffect,QWidget,QSizePolicy,QScrollArea,QApplication,QPushButton,QSystemTrayIcon,QPlainTextEdit,QAction,QMenu,QFileDialog,QKeySequenceEdit,QLabel,QSpacerItem,QWidgetItem,QLayout,QTextBrowser,QLineEdit,QFormLayout,QSizePolicy,QTabWidget,QTabBar,QSplitter,QListWidget,QListWidgetItem,QHBoxLayout,QVBoxLayout,QSizeGrip,QFontComboBox,QProgressBar,QRadioButton,QButtonGroup,QSlider,QToolTip,QGroupBox,QGraphicsOpacityEffect,QStackedWidget,QStyledItemDelegate,QStyleOptionViewItem,QFontDialog,QTreeView,QToolButton from PyQt5.QtGui import QIconEngine,QIntValidator,QStandardItem,QStandardItemModel,QImageWriter,QIcon,QTextCharFormat,QTextBlockFormat,QResizeEvent,QTextCursor,QFontMetricsF,QMouseEvent,QImage,QPainter,QRegion,QCloseEvent,QFontDatabase,QKeySequence,QPixmap,QCursor,QColor,QFont,QPen,QPainterPath,QBrush,QFontMetrics,QShowEvent,QWheelEvent,QPaintEvent,QTextLayout, QTextOption,QDragEnterEvent, QDropEvent,QTransform,QKeyEvent,QInputMethodEvent,QValidator - from PyQt5.QtCore import QObject,pyqtSignal,Qt,QSize,QByteArray,QBuffer,QPointF,QPoint,QRect,QEvent,QModelIndex,QTimer,QRectF,QVariantAnimation,QUrl,QPropertyAnimation,QLocale,QSignalBlocker + from PyQt5.QtCore import QObject,pyqtSignal,Qt,QSize,QByteArray,QBuffer,QPointF,QPoint,QRect,QEvent,QModelIndex,QTimer,QRectF,QVariantAnimation,QUrl,QPropertyAnimation,QLocale,QSignalBlocker,QMargins isqt5 = True class LineHeightTypes: LineDistanceHeight=QTextBlockFormat.LineHeightTypes.LineDistanceHeight @@ -11,9 +11,9 @@ except: #from traceback import print_exc #print_exc() from PyQt6 import QtSvg - from PyQt6.QtWidgets import QFrame,QListView,QCheckBox,QAbstractItemView,QTextEdit,QTableView,QHeaderView,QColorDialog,QSpinBox,QDoubleSpinBox,QComboBox,QDialogButtonBox,QMainWindow,QMessageBox,QDialog,QGridLayout,QTextBrowser,QGraphicsDropShadowEffect,QWidget,QSizePolicy,QScrollArea,QApplication,QPushButton,QSystemTrayIcon,QPlainTextEdit,QMenu,QFileDialog,QKeySequenceEdit,QLabel,QSpacerItem,QWidgetItem,QLayout,QTextBrowser,QLineEdit,QFormLayout,QSizePolicy,QTabWidget,QTabBar,QSplitter,QListWidget,QListWidgetItem,QHBoxLayout,QVBoxLayout,QSizeGrip,QFontComboBox,QProgressBar,QRadioButton,QButtonGroup,QSlider,QToolTip,QGroupBox,QGraphicsOpacityEffect,QStackedWidget,QTreeView + from PyQt6.QtWidgets import QFrame,QListView,QCheckBox,QAbstractItemView,QTextEdit,QTableView,QHeaderView,QColorDialog,QSpinBox,QDoubleSpinBox,QComboBox,QDialogButtonBox,QMainWindow,QMessageBox,QDialog,QGridLayout,QTextBrowser,QGraphicsDropShadowEffect,QWidget,QSizePolicy,QScrollArea,QApplication,QPushButton,QSystemTrayIcon,QPlainTextEdit,QMenu,QFileDialog,QKeySequenceEdit,QLabel,QSpacerItem,QWidgetItem,QLayout,QTextBrowser,QLineEdit,QFormLayout,QSizePolicy,QTabWidget,QTabBar,QSplitter,QListWidget,QListWidgetItem,QHBoxLayout,QVBoxLayout,QSizeGrip,QFontComboBox,QProgressBar,QRadioButton,QButtonGroup,QSlider,QToolTip,QGroupBox,QGraphicsOpacityEffect,QStackedWidget,QTreeView,QToolButton from PyQt6.QtGui import QIconEngine,QIntValidator,QAction,QStandardItem,QStandardItemModel,QImageWriter,QIcon,QTextCharFormat,QTextBlockFormat,QResizeEvent,QTextCursor,QFontMetricsF,QMouseEvent,QImage,QPainter,QRegion,QCloseEvent,QFontDatabase,QKeySequence,QPixmap,QCursor,QColor,QFont,QPen,QPainterPath,QBrush,QFontMetrics,QShowEvent,QWheelEvent,QPaintEvent,QTextLayout, QTextOption,QKeyEvent,QInputMethodEvent,QValidator - from PyQt6.QtCore import QObject,pyqtSignal,Qt,QSize,QByteArray,QBuffer,QPointF,QPoint,QRect,QEvent,QModelIndex,QTimer,QRectF,QVariantAnimation,QUrl,QPropertyAnimation,QLocale + from PyQt6.QtCore import QObject,pyqtSignal,Qt,QSize,QByteArray,QBuffer,QPointF,QPoint,QRect,QEvent,QModelIndex,QTimer,QRectF,QVariantAnimation,QUrl,QPropertyAnimation,QLocale,QMargins isqt5 = False class LineHeightTypes: diff --git a/py/LunaTranslator/textsource/filetrans.py b/py/LunaTranslator/textsource/filetrans.py index 7dd4d830..ec598f34 100644 --- a/py/LunaTranslator/textsource/filetrans.py +++ b/py/LunaTranslator/textsource/filetrans.py @@ -1,6 +1,6 @@ from textsource.textsourcebase import basetext from myutils.wrapper import threader -import json, time, os, gobject, re +import json, time, os, gobject, winsharedutils from myutils.config import globalconfig @@ -29,8 +29,9 @@ class parsejson: def load(self): for i, k in enumerate(self.data): - if self.data[k]: - yield i, None + if not isinstance(self.data[k], str): + continue + if winsharedutils.distance_ratio(self.data[k], k) < 0.2: continue yield i, k diff --git a/py/LunaTranslator/translator/atlas.py b/py/LunaTranslator/translator/atlas.py index 22e7fdca..f577968f 100644 --- a/py/LunaTranslator/translator/atlas.py +++ b/py/LunaTranslator/translator/atlas.py @@ -43,7 +43,7 @@ class TS(basetrans): ) return True - def x64(self, content): + def translate(self, content): self.checkpath() l = content.encode("utf-16-le") @@ -53,6 +53,3 @@ class TS(basetrans): if not size: raise Exception("not installed") return windows.ReadFile(self.hPipe, size).decode("utf-16-le") - - def translate(self, content): - return self.x64(content) diff --git a/py/LunaTranslator/translator/dreye.py b/py/LunaTranslator/translator/dreye.py index ee1f5250..12bf637e 100644 --- a/py/LunaTranslator/translator/dreye.py +++ b/py/LunaTranslator/translator/dreye.py @@ -62,7 +62,7 @@ class TS(basetrans): ) return True - def x64(self, content): + def translate(self, content): if self.checkpath() == False: raise Exception(_TR("翻译器加载失败")) @@ -74,6 +74,3 @@ class TS(basetrans): windows.WriteFile(self.hPipe, line.encode(codes[self.srclang])) ress.append(windows.ReadFile(self.hPipe, 4096).decode(codes[self.tgtlang])) return "\n".join(ress) - - def translate(self, content): - return self.x64(content) diff --git a/py/LunaTranslator/translator/eztrans.py b/py/LunaTranslator/translator/eztrans.py index 6432a198..d21ff609 100644 --- a/py/LunaTranslator/translator/eztrans.py +++ b/py/LunaTranslator/translator/eztrans.py @@ -1,7 +1,7 @@ from translator.basetranslator import basetrans from myutils.config import _TR import os, time -import windows +import windows, ctypes from myutils.subproc import subproc_w, autoproc @@ -54,7 +54,7 @@ class TS(basetrans): ) return True - def x64(self, content: str): + def translate(self, content): if self.checkpath() == False: raise Exception(_TR("翻译器加载失败")) @@ -62,14 +62,7 @@ class TS(basetrans): code1 = content.encode("utf-16-le") windows.WriteFile(self.hPipe, code1) - xx = windows.ReadFile(self.hPipe, 65535) - xx = xx.decode("utf-16-le", errors="ignore") - - return xx - - def translate(self, content): - - return self.x64(content) - - def langmap(self): - return {"zh": "936", "cht": "950"} + size = ctypes.c_int.from_buffer_copy(windows.ReadFile(self.hPipe, 4)).value + if not size: + raise Exception("not installed") + return windows.ReadFile(self.hPipe, size).decode("utf-16-le") diff --git a/py/LunaTranslator/translator/jb7.py b/py/LunaTranslator/translator/jb7.py index 2b1d189e..64cdb712 100644 --- a/py/LunaTranslator/translator/jb7.py +++ b/py/LunaTranslator/translator/jb7.py @@ -67,7 +67,7 @@ class TS(basetrans): ) return True - def x64(self, content: str): + def translate(self, content): if self.tgtlang not in ["936", "950"]: return "" if self.checkpath() == False: @@ -86,42 +86,5 @@ class TS(basetrans): ress.append(xx) return "\n".join(ress) - def x86(self, content): - CODEPAGE_JA = 932 - CODEPAGE_GB = 936 - CODEPAGE_BIG5 = 950 - BUFFER_SIZE = 3000 - # if globalconfig['fanjian'] in [0,1,4]: - # code=CODEPAGE_GB - # else: - # code=CODEPAGE_BIG5 - code = CODEPAGE_GB - - size = BUFFER_SIZE - out = ctypes.create_unicode_buffer(size) - buf = ctypes.create_unicode_buffer(size) - outsz = ctypes.c_int(size) - bufsz = ctypes.c_int(size) - try: - self.dll.JC_Transfer_Unicode( - 0, # int, unknown - CODEPAGE_JA, # uint from, supposed to be 0x3a4 (cp932) - code, # uint to, eighter cp950 or cp932 - 1, # int, unknown - 1, # int, unknown - content, # python 默认unicode 所有不用u' - out, # wchar_t* - ctypes.byref(outsz), # ∫ - buf, # wchar_t* - ctypes.byref(bufsz), - ) # ∫ - except: - pass - return out.value - - def translate(self, content): - - return self.x64(content) - def langmap(self): return {"zh": "936", "cht": "950"} diff --git a/py/LunaTranslator/translator/kingsoft.py b/py/LunaTranslator/translator/kingsoft.py index 66d242e4..5d21eed0 100644 --- a/py/LunaTranslator/translator/kingsoft.py +++ b/py/LunaTranslator/translator/kingsoft.py @@ -64,7 +64,7 @@ class TS(basetrans): ) return True - def x64(self, content): + def translate(self, content): if self.checkpath() == False: raise Exception(_TR("翻译器加载失败")) ress = [] @@ -77,8 +77,5 @@ class TS(basetrans): return "\n".join(ress) - def translate(self, content): - return self.x64(content) - def langmap(self): return {"zh": "SChinese", "cht": "TChinese", "en": "English", "ja": "Japanese", "auto": "Japanese"} diff --git a/py/LunaTranslator/translator/lec.py b/py/LunaTranslator/translator/lec.py index 474b8f13..429c4445 100644 --- a/py/LunaTranslator/translator/lec.py +++ b/py/LunaTranslator/translator/lec.py @@ -49,7 +49,7 @@ class TS(basetrans): ) ) - def x64(self, content: str): + def translate(self, content): self.checkpath() l = content.encode("utf-16-le") @@ -59,6 +59,3 @@ class TS(basetrans): if not size: raise Exception("not installed") return windows.ReadFile(self.hPipe, size).decode("utf-16-le") - - def translate(self, content): - return self.x64(content) diff --git a/py/LunaTranslator/winrtutils.py b/py/LunaTranslator/winrtutils.py index 620a6364..a81a97bd 100644 --- a/py/LunaTranslator/winrtutils.py +++ b/py/LunaTranslator/winrtutils.py @@ -10,13 +10,11 @@ from ctypes import ( POINTER, c_char, ) -from ctypes.wintypes import HANDLE import platform, gobject, threading try: if platform.system() != "Windows" or int(platform.version().split(".")[0]) <= 6: raise Exception() - winrtutilsdll = CDLL(gobject.GetDllpath(("winrtutils32.dll", "winrtutils64.dll"))) except: winrtutilsdll = 0 @@ -33,11 +31,6 @@ if winrtutilsdll: _getlanguagelist = winrtutilsdll.getlanguagelist _getlanguagelist.argtypes = (c_void_p,) - def getlanguagelist(): - ret = [] - _getlanguagelist(CFUNCTYPE(None, c_wchar_p)(ret.append)) - return ret - def OCR_f(data, lang, space): ret = [] @@ -62,13 +55,24 @@ if winrtutilsdll: _winrt_capture_window = winrtutilsdll.winrt_capture_window _winrt_capture_window.argtypes = c_void_p, c_void_p - def winrt_capture_window(hwnd): - ret = [] - def cb(ptr, size): - ret.append(cast(ptr, POINTER(c_char))[:size]) +def getlanguagelist(): + if not winrtutilsdll: + return [] + ret = [] + _getlanguagelist(CFUNCTYPE(None, c_wchar_p)(ret.append)) + return ret - _winrt_capture_window(hwnd, CFUNCTYPE(None, c_void_p, c_size_t)(cb)) - if len(ret): - return ret[0] - return None + +def winrt_capture_window(hwnd): + if not winrtutilsdll: + return + ret = [] + + def cb(ptr, size): + ret.append(cast(ptr, POINTER(c_char))[:size]) + + _winrt_capture_window(hwnd, CFUNCTYPE(None, c_void_p, c_size_t)(cb)) + if len(ret): + return ret[0] + return None diff --git a/py/files/defaultconfig/config.json b/py/files/defaultconfig/config.json index 4ab9dab4..1057815b 100644 --- a/py/files/defaultconfig/config.json +++ b/py/files/defaultconfig/config.json @@ -521,6 +521,7 @@ "useproxy": true, "usesysproxy": true, "fullscreenmethod_4": 0, + "tagNameRemap": {}, "dialog_savegame_layout": { "itemw": 250, "itemh": 350, @@ -532,6 +533,7 @@ "transparent": 25, "transparentselect": 25, "transparentnotexits": 25, + "listitemheight": 30, "listitemwidth_2": [ 300, 500 @@ -1181,19 +1183,13 @@ "use": true, "name": "MeCab", "args": { - "path": "", - "downloadlink": "{main_server}/Resource/dictionary/Mecab.zip" + "path": "" }, "argstype": { "path": { "type": "file", "name": "路径", "dir": true - }, - "downloadlink": { - "type": "label", - "islink": true, - "name": "下载" } }, "type": "offline" @@ -1670,7 +1666,7 @@ "type": "offline", "use": false, "color": "#1839f0", - "name": "Jbeijing7" + "name": "J北京7" }, "eztrans": { "type": "offline", diff --git a/py/files/defaultconfig/ocrsetting.json b/py/files/defaultconfig/ocrsetting.json index e885e2e4..0921344b 100644 --- a/py/files/defaultconfig/ocrsetting.json +++ b/py/files/defaultconfig/ocrsetting.json @@ -1,18 +1,4 @@ { - "local": { - "args": { - "": "" - }, - "argstype": { - "": { - "type": "program", - "route": [ - "ocrengines.local", - "question" - ] - } - } - }, "youdaodictocr": { "args": { "Translate": false @@ -24,20 +10,6 @@ } } }, - "windowsocr": { - "args": { - "": "" - }, - "argstype": { - "": { - "type": "program", - "route": [ - "ocrengines.windowsocr", - "question" - ] - } - } - }, "baiduocr_X": { "args": { "API Key": "", @@ -322,10 +294,7 @@ }, "mangaocr": { "args": { - "Port": 5665, - "项目仓库": "https://github.com/kha-white/manga-ocr", - "整合包_CPU": "{main_server}/Resource/IntegrationPack/manga_ocr/cpu", - "整合包_GPU": "{main_server}/Resource/IntegrationPack/manga_ocr/gpu" + "Port": 5665 }, "argstype": { "Port": { @@ -333,18 +302,6 @@ "min": 1, "max": 65535, "step": 1 - }, - "项目仓库": { - "type": "label", - "islink": true - }, - "整合包_CPU": { - "type": "label", - "islink": true - }, - "整合包_GPU": { - "type": "label", - "islink": true } } } diff --git a/py/files/defaultconfig/static_data.json b/py/files/defaultconfig/static_data.json index bdc23881..a24db89b 100644 --- a/py/files/defaultconfig/static_data.json +++ b/py/files/defaultconfig/static_data.json @@ -452,236 +452,295 @@ "北欧", "西里尔" ], - "aboutsource": [ - { - "name": "辞书", - "sources": [ - { - "name": "MeCab", - "links": [ - { - "name": "MeCab", - "link": "{main_server}/Resource/dictionary/Mecab.zip" - }, - { - "name": "Unidic", - "link": "https://clrd.ninjal.ac.jp/unidic/" - } - ] - }, - { - "name": "MDict", - "links": [ - { - "name": "论坛", - "link": "https://forum.freemdict.com/" - }, - { - "name": "freemdict", - "link": "https://search.freemdict.com/" - } - ] - } - ] - }, - { - "name": "语音合成", - "sources": [ - { - "name": "NeoSpeech", - "links": [ - { - "name": "Misaki", - "link": "{main_server}/Resource/voice/NeoSpeech.Japanese.Misaki.zip" - }, - { - "name": "Show", - "link": "{main_server}/Resource/voice/NeoSpeech.TTS.NeoSpeech.Japanese.Show_v3.10.0.0.zip" - } - ] - }, - { - "name": "VOICEVOX", - "links": [ - { - "name": "Github", - "link": "https://github.com/VOICEVOX/voicevox/releases" - } - ] - }, - { - "name": "vits-simple-api", - "links": [ - { - "name": "Github", - "link": "https://github.com/Artrajz/vits-simple-api/releases" - }, - { - "name": "整合包_CPU", - "link": "{main_server}/Resource/IntegrationPack/vits-simple-api/cpu" - }, - { - "name": "整合包_GPU", - "link": "{main_server}/Resource/IntegrationPack/vits-simple-api/gpu" - } - ] - }, - { - "name": "VoiceRoid+", - "links": [ - { - "name": "東北ずん子/东北俊子", - "link": "{main_server}/Resource/voice/VOICEROID+zunko.7z" - } - ] - }, - { - "name": "VoiceRoid2", - "links": [ - { - "name": "SFE_結月ゆかり/结月缘", - "link": "{main_server}/Resource/voice/Yukari2.zip" - }, - { - "name": "結月ゆかり/结月缘", - "link": "{main_server}/Resource/voice/VOICEROID2_Yuzuki_Yukari.zip" - }, - { - "name": "紲星あかり/绁星灯", - "link": "{main_server}/Resource/voice/VOICEROID2_Kizuna_Akari.zip" - }, - { - "name": "琴葉 茜・葵", - "link": "{main_server}/Resource/voice/VOICEROID2_Kotonoha_Akane_Aoi.zip" - }, - { - "name": "伊織弓鶴", - "link": "{main_server}/Resource/voice/VOICEROID2_Iori_Yuzuru.zip" - }, - { - "name": "ついなちゃん", - "link": "{main_server}/Resource/voice/VOICEROID2_tsuina-chan_dl_e.zip" - }, - { - "name": "東北イタコ/东北伊达子", - "link": "{main_server}/Resource/voice/VOICEROID2_Tohoku_Itako.zip" - }, - { - "name": "附加音源_結月ゆかり", - "link": "{main_server}/Resource/voice/yukari_emo_44.zip" - }, - { - "name": "附加音源_紲星あかり", - "link": "{main_server}/Resource/voice/akari_44.zip" - }, - { - "name": "附加音源_東北きりたん", - "link": "{main_server}/Resource/voice/kiritan_44.zip" - }, - { - "name": "附加音源_東北イタコ", - "link": "{main_server}/Resource/voice/itako_emo_44.zip" - }, - { - "name": "附加音源_東北ずん子", - "link": "{main_server}/Resource/voice/zunko_44.zip" - }, - { - "name": "附加音源_伊織弓鶴", - "link": "{main_server}/Resource/voice/yuzuru_emo_44.zip" - }, - { - "name": "附加音源_ついなちゃん", - "link": "{main_server}/Resource/voice/tsuina_44.zip" - }, - { - "name": "附加音源_ついなちゃん(関西弁)", - "link": "{main_server}/Resource/voice/tsuina_west_44.zip" - }, - { - "name": "附加音源_琴葉茜", - "link": "{main_server}/Resource/voice/akane_west_emo_44.zip" - }, - { - "name": "附加音源_琴葉葵", - "link": "{main_server}/Resource/voice/aoi_emo_44.zip" - }, - { - "name": "附加音源_水奈瀬コウ", - "link": "{main_server}/Resource/voice/kou_44.zip" - }, - { - "name": "附加音源_桜乃そら", - "link": "{main_server}/Resource/voice/sora_44.zip" - }, - { - "name": "附加音源_民安ともえ", - "link": "{main_server}/Resource/voice/tamiyasu_44.zip" - }, - { - "name": "附加音源_月読アイ", - "link": "{main_server}/Resource/voice/ai_44.zip" - }, - { - "name": "附加音源_月読ショウタ", - "link": "{main_server}/Resource/voice/shouta_44.zip" - }, - { - "name": "附加音源_京町セイカ", - "link": "{main_server}/Resource/voice/seika_44.zip" - }, - { - "name": "附加音源_音街ウナ", - "link": "{main_server}/Resource/voice/una_44.zip" - }, - { - "name": "附加音源_鷹の爪吉田", - "link": "{main_server}/Resource/voice/yoshidakun_44.zip" - }, - { - "name": "附加音源_ギャラ子", - "link": "{main_server}/Resource/voice/galaco_44.zip" - } - ] - } - ] - }, - { - "name": "其他工具", - "sources": [ - { - "name": "转区", - "links": [ - { - "name": "Locale-Emulator", - "link": "https://github.com/xupefei/Locale-Emulator/releases/download/v2.5.0.1/Locale.Emulator.2.5.0.1.zip" - }, - { - "name": "Locale_Remulator", - "link": "https://github.com/InWILL/Locale_Remulator/releases/download/v1.5.3-beta.1/Locale_Remulator.1.5.3-beta.1.zip" - }, - { - "name": "Ntleas", - "link": "https://github.com/zxyacb/ntlea/releases/download/0.46/ntleas046_x64.7z" - } - ] - }, - { - "name": "Magpie", - "links": [ - { - "name": "Github", - "link": "https://github.com/Blinue/Magpie/releases" - }, - { - "name": "win7适配版", - "link": "{main_server}/Resource/Magpie9_Win7" - } - ] - } - ] - } - ], + "aboutsource": { + "translate": [ + { + "name": "大模型", + "links": [ + { + "name": "Sakura大模型", + "link": "https://github.com/SakuraLLM/SakuraLLM", + "about": "日语_->_简体中文" + } + ] + }, + { + "name": "过时的翻译器", + "links": [ + { + "name": "J北京7", + "link": "{main_server}/Resource/translate/JBeijing7.zip", + "about": "日语_->_简体中文" + }, + { + "name": "金山快译", + "link": "{main_server}/Resource/translate/FastAIT09_Setup.25269.4101.zip", + "about": "日语_->_简体中文" + }, + { + "name": "快译通", + "link": "{main_server}/Resource/translate/DR.eye.zip", + "about": "日语_->_简体中文" + }, + { + "name": "Sugoi", + "link": "{main_server}/Resource/translate/Sugoi_Translator_V10.1.7z", + "about": "日语_->_英语" + }, + { + "name": "LEC", + "link": "{main_server}/Resource/translate/LEC", + "about": "日语_->_英语" + }, + { + "name": "Atlas", + "link": "{main_server}/Resource/translate/Atlas", + "about": "日语_->_英语" + }, + { + "name": "ezTrans", + "link": "{main_server}/Resource/translate/ezTrans", + "about": "日语_->_韩语" + } + ] + } + ], + "ocr": [ + { + "name": "本地OCR", + "function": [ + "ocrengines.local", + "question" + ] + }, + { + "name": "WindowsOCR", + "function": [ + "ocrengines.windowsocr", + "question" + ] + }, + { + "name": "manga-ocr", + "links": [ + { + "name": "项目仓库", + "link": "https://github.com/kha-white/manga-ocr" + }, + { + "name": "整合包_CPU", + "link": "{main_server}/Resource/IntegrationPack/manga_ocr/cpu" + }, + { + "name": "整合包_GPU", + "link": "{main_server}/Resource/IntegrationPack/manga_ocr/gpu" + } + ] + } + ], + "dict": [ + { + "name": "MeCab", + "links": [ + { + "name": "MeCab", + "link": "{main_server}/Resource/dictionary/Mecab.zip" + }, + { + "name": "Unidic", + "link": "https://clrd.ninjal.ac.jp/unidic/" + } + ] + }, + { + "name": "MDict", + "links": [ + { + "name": "论坛", + "link": "https://forum.freemdict.com/" + }, + { + "name": "freemdict", + "link": "https://search.freemdict.com/" + } + ] + } + ], + "tts": [ + { + "name": "NeoSpeech", + "links": [ + { + "name": "Misaki", + "link": "{main_server}/Resource/voice/NeoSpeech.Japanese.Misaki.zip" + }, + { + "name": "Show", + "link": "{main_server}/Resource/voice/NeoSpeech.TTS.NeoSpeech.Japanese.Show_v3.10.0.0.zip" + } + ] + }, + { + "name": "VOICEVOX", + "links": [ + { + "name": "Github", + "link": "https://github.com/VOICEVOX/voicevox/releases" + } + ] + }, + { + "name": "vits-simple-api", + "links": [ + { + "name": "Github", + "link": "https://github.com/Artrajz/vits-simple-api/releases" + }, + { + "name": "整合包_CPU", + "link": "{main_server}/Resource/IntegrationPack/vits-simple-api/cpu" + }, + { + "name": "整合包_GPU", + "link": "{main_server}/Resource/IntegrationPack/vits-simple-api/gpu" + } + ] + }, + { + "name": "VoiceRoid+", + "links": [ + { + "name": "東北ずん子/东北俊子", + "link": "{main_server}/Resource/voice/VOICEROID+zunko.7z" + } + ] + }, + { + "name": "VoiceRoid2", + "links": [ + { + "name": "SFE_結月ゆかり/结月缘", + "link": "{main_server}/Resource/voice/Yukari2.zip" + }, + { + "name": "結月ゆかり/结月缘", + "link": "{main_server}/Resource/voice/VOICEROID2_Yuzuki_Yukari.zip" + }, + { + "name": "紲星あかり/绁星灯", + "link": "{main_server}/Resource/voice/VOICEROID2_Kizuna_Akari.zip" + }, + { + "name": "琴葉 茜・葵", + "link": "{main_server}/Resource/voice/VOICEROID2_Kotonoha_Akane_Aoi.zip" + }, + { + "name": "伊織弓鶴", + "link": "{main_server}/Resource/voice/VOICEROID2_Iori_Yuzuru.zip" + }, + { + "name": "ついなちゃん", + "link": "{main_server}/Resource/voice/VOICEROID2_tsuina-chan_dl_e.zip" + }, + { + "name": "東北イタコ/东北伊达子", + "link": "{main_server}/Resource/voice/VOICEROID2_Tohoku_Itako.zip" + }, + { + "name": "附加音源_結月ゆかり", + "link": "{main_server}/Resource/voice/yukari_emo_44.zip" + }, + { + "name": "附加音源_紲星あかり", + "link": "{main_server}/Resource/voice/akari_44.zip" + }, + { + "name": "附加音源_東北きりたん", + "link": "{main_server}/Resource/voice/kiritan_44.zip" + }, + { + "name": "附加音源_東北イタコ", + "link": "{main_server}/Resource/voice/itako_emo_44.zip" + }, + { + "name": "附加音源_東北ずん子", + "link": "{main_server}/Resource/voice/zunko_44.zip" + }, + { + "name": "附加音源_伊織弓鶴", + "link": "{main_server}/Resource/voice/yuzuru_emo_44.zip" + }, + { + "name": "附加音源_ついなちゃん", + "link": "{main_server}/Resource/voice/tsuina_44.zip" + }, + { + "name": "附加音源_ついなちゃん(関西弁)", + "link": "{main_server}/Resource/voice/tsuina_west_44.zip" + }, + { + "name": "附加音源_琴葉茜", + "link": "{main_server}/Resource/voice/akane_west_emo_44.zip" + }, + { + "name": "附加音源_琴葉葵", + "link": "{main_server}/Resource/voice/aoi_emo_44.zip" + }, + { + "name": "附加音源_水奈瀬コウ", + "link": "{main_server}/Resource/voice/kou_44.zip" + }, + { + "name": "附加音源_桜乃そら", + "link": "{main_server}/Resource/voice/sora_44.zip" + }, + { + "name": "附加音源_民安ともえ", + "link": "{main_server}/Resource/voice/tamiyasu_44.zip" + }, + { + "name": "附加音源_月読アイ", + "link": "{main_server}/Resource/voice/ai_44.zip" + }, + { + "name": "附加音源_月読ショウタ", + "link": "{main_server}/Resource/voice/shouta_44.zip" + }, + { + "name": "附加音源_京町セイカ", + "link": "{main_server}/Resource/voice/seika_44.zip" + }, + { + "name": "附加音源_音街ウナ", + "link": "{main_server}/Resource/voice/una_44.zip" + }, + { + "name": "附加音源_鷹の爪吉田", + "link": "{main_server}/Resource/voice/yoshidakun_44.zip" + }, + { + "name": "附加音源_ギャラ子", + "link": "{main_server}/Resource/voice/galaco_44.zip" + } + ] + } + ], + "magpie": [ + { + "name": "Magpie", + "links": [ + { + "name": "Github", + "link": "https://github.com/Blinue/Magpie/releases" + }, + { + "name": "win7适配版", + "link": "{main_server}/Resource/Magpie9_Win7" + } + ] + } + ] + }, "mod_map": { "CTRL": 2, "SHIFT": 4, diff --git a/py/files/defaultconfig/translatorsetting.json b/py/files/defaultconfig/translatorsetting.json index 3ba4e541..6b8122b1 100644 --- a/py/files/defaultconfig/translatorsetting.json +++ b/py/files/defaultconfig/translatorsetting.json @@ -276,19 +276,7 @@ }, "sugoix": { "args": { - "api": "http://127.0.0.1:14366/", - "V8": "{main_server}/Resource/translate/Sugoi_Translator_V8.7z", - "V10.1": "{main_server}/Resource/translate/Sugoi_Translator_V10.1.7z" - }, - "argstype": { - "V8": { - "type": "label", - "islink": true - }, - "V10.1": { - "type": "label", - "islink": true - } + "api": "http://127.0.0.1:14366/" } }, "huoshanapi": { @@ -668,36 +656,26 @@ }, "jb7": { "args": { - "path": "", - "下载": "{main_server}/Resource/translate/JBeijing7.zip" + "path": "" }, "argstype": { "path": { "type": "file", "dir": true, "name": "路径" - }, - "下载": { - "type": "label", - "islink": true - } + } } }, "kingsoft": { "args": { - "path": "", - "下载": "{main_server}/Resource/translate/FastAIT09_Setup.25269.4101.zip" + "path": "" }, "argstype": { "path": { "name": "路径", "type": "file", "dir": true - }, - "下载": { - "type": "label", - "islink": true - } + } } }, "eztrans": { @@ -715,19 +693,14 @@ }, "dreye": { "args": { - "path": "", - "下载": "{main_server}/Resource/translate/DR.eye.zip" + "path": "" }, "argstype": { "path": { "name": "路径", "type": "file", "dir": true - }, - "下载": { - "type": "label", - "islink": true - } + } } }, "rengong": { diff --git a/py/files/lang/ar.json b/py/files/lang/ar.json index ddb25dd3..b93886c7 100644 --- a/py/files/lang/ar.json +++ b/py/files/lang/ar.json @@ -756,5 +756,12 @@ "成功": "النجاح .", "Win32通用钩子": "win32 هوك العالمي", "特殊码无效": "رمز خاص غير صالح", - "显示模式": "طريقة العرض" + "显示模式": "طريقة العرض", + "内置": "مدمجة", + "信息": "معلومات", + "开发商": "المطور", + "标签映射": "تسمية الخريطة", + "简介": "مقدمة", + "全部": "كامل", + "年度总结": "ملخص سنوي" } \ No newline at end of file diff --git a/py/files/lang/cht.json b/py/files/lang/cht.json index 05f1eb03..a822f7ba 100644 --- a/py/files/lang/cht.json +++ b/py/files/lang/cht.json @@ -756,5 +756,12 @@ "成功": "成功", "Win32通用钩子": "Win32通用鉤子", "特殊码无效": "特殊碼無效", - "显示模式": "顯示模式" + "显示模式": "顯示模式", + "内置": "內寘", + "信息": "訊息", + "开发商": "開發商", + "标签映射": "標籤映射", + "简介": "簡介", + "全部": "全部", + "年度总结": "年度總結" } \ No newline at end of file diff --git a/py/files/lang/cs.json b/py/files/lang/cs.json index 8074898a..9f365ce0 100644 --- a/py/files/lang/cs.json +++ b/py/files/lang/cs.json @@ -756,5 +756,12 @@ "成功": "úspěch", "Win32通用钩子": "Univerzální hák Win32", "特殊码无效": "Speciální kód je neplatný", - "显示模式": "režim zobrazení" + "显示模式": "režim zobrazení", + "内置": "vestavěné", + "信息": "informace", + "开发商": "Vývojáři", + "标签映射": "Mapování štítků", + "简介": "stručný úvod", + "全部": "celý", + "年度总结": "Roční shrnutí" } \ No newline at end of file diff --git a/py/files/lang/de.json b/py/files/lang/de.json index aa34e939..a5479a25 100644 --- a/py/files/lang/de.json +++ b/py/files/lang/de.json @@ -756,5 +756,12 @@ "成功": "Erfolg", "Win32通用钩子": "Win32 Universal Hook", "特殊码无效": "Der spezielle Code ist ungültig", - "显示模式": "Anzeigemodus" + "显示模式": "Anzeigemodus", + "内置": "eingebaut", + "信息": "Informationen", + "开发商": "Entwickler", + "标签映射": "Beschriftung", + "简介": "kurze Einführung", + "全部": "ganz", + "年度总结": "Jahreszusammenfassung" } \ No newline at end of file diff --git a/py/files/lang/en.json b/py/files/lang/en.json index 114d10a0..2c792d3f 100644 --- a/py/files/lang/en.json +++ b/py/files/lang/en.json @@ -756,5 +756,12 @@ "成功": "success", "Win32通用钩子": "Win32 Universal Hook", "特殊码无效": "The special code is invalid", - "显示模式": "display mode" + "显示模式": "display mode", + "内置": "built-in", + "信息": "information", + "开发商": "Developers", + "标签映射": "Label Mapping", + "简介": "brief introduction", + "全部": "all", + "年度总结": "Annual Summary" } \ No newline at end of file diff --git a/py/files/lang/es.json b/py/files/lang/es.json index c101e84a..4d6a444f 100644 --- a/py/files/lang/es.json +++ b/py/files/lang/es.json @@ -756,5 +756,12 @@ "成功": "éxito", "Win32通用钩子": "Gancho universal Win32", "特殊码无效": "El código especial no es válido", - "显示模式": "Modo de visualización" + "显示模式": "Modo de visualización", + "内置": "Incorporado", + "信息": "Información", + "开发商": "Desarrolladores", + "标签映射": "Mapeo de etiquetas", + "简介": "Introducción", + "全部": "Todo", + "年度总结": "Resumen Anual" } \ No newline at end of file diff --git a/py/files/lang/fr.json b/py/files/lang/fr.json index 3e66fdf8..7c387de1 100644 --- a/py/files/lang/fr.json +++ b/py/files/lang/fr.json @@ -756,5 +756,12 @@ "成功": "Succès", "Win32通用钩子": "Win32 crochet universel", "特殊码无效": "Code spécial invalide", - "显示模式": "Mode d'affichage" + "显示模式": "Mode d'affichage", + "内置": "Intégré", + "信息": "Informations", + "开发商": "Développeur", + "标签映射": "Mappage des étiquettes", + "简介": "Introduction", + "全部": "Tous", + "年度总结": "Résumé annuel" } \ No newline at end of file diff --git a/py/files/lang/it.json b/py/files/lang/it.json index 7ef05396..8e5a693a 100644 --- a/py/files/lang/it.json +++ b/py/files/lang/it.json @@ -756,5 +756,12 @@ "成功": "successo", "Win32通用钩子": "Win32 Universal Hook", "特殊码无效": "Il codice speciale non è valido", - "显示模式": "modalità di visualizzazione" + "显示模式": "modalità di visualizzazione", + "内置": "integrato", + "信息": "informazioni", + "开发商": "Sviluppatori", + "标签映射": "Mappatura etichette", + "简介": "breve introduzione", + "全部": "intero", + "年度总结": "Sintesi annuale" } \ No newline at end of file diff --git a/py/files/lang/ja.json b/py/files/lang/ja.json index 847cbce1..ef77c153 100644 --- a/py/files/lang/ja.json +++ b/py/files/lang/ja.json @@ -756,5 +756,12 @@ "成功": "成功", "Win32通用钩子": "Win 32汎用フック", "特殊码无效": "特殊コードが無効です", - "显示模式": "表示モード" + "显示模式": "表示モード", + "内置": "組み込み", + "信息": "情報#ジョウホウ#", + "开发商": "開発者", + "标签映射": "ラベルマッピング", + "简介": "概要", + "全部": "すべて", + "年度总结": "年度まとめ" } \ No newline at end of file diff --git a/py/files/lang/ko.json b/py/files/lang/ko.json index 8dc1910a..7bd72e96 100644 --- a/py/files/lang/ko.json +++ b/py/files/lang/ko.json @@ -756,5 +756,12 @@ "成功": "성공", "Win32通用钩子": "Win32 범용 갈고리", "特殊码无效": "잘못된 특수 코드", - "显示模式": "디스플레이 모드" + "显示模式": "디스플레이 모드", + "内置": "내장형", + "信息": "정보", + "开发商": "개발업자", + "标签映射": "태그 매핑", + "简介": "소개", + "全部": "모두", + "年度总结": "연간 요약" } \ No newline at end of file diff --git a/py/files/lang/nl.json b/py/files/lang/nl.json index 36b2a180..43fba4eb 100644 --- a/py/files/lang/nl.json +++ b/py/files/lang/nl.json @@ -756,5 +756,12 @@ "成功": "succes", "Win32通用钩子": "Win32 universele haak", "特殊码无效": "De speciale code is ongeldig", - "显示模式": "weergavemodus" + "显示模式": "weergavemodus", + "内置": "ingebouwd", + "信息": "informatie", + "开发商": "Ontwikkelaars", + "标签映射": "Label toewijzing", + "简介": "korte inleiding", + "全部": "geheel", + "年度总结": "Jaarlijkse samenvatting" } \ No newline at end of file diff --git a/py/files/lang/pl.json b/py/files/lang/pl.json index 223d2c5e..c98f2e63 100644 --- a/py/files/lang/pl.json +++ b/py/files/lang/pl.json @@ -756,5 +756,12 @@ "成功": "sukces", "Win32通用钩子": "Uniwersalny hak Win32", "特殊码无效": "Kod specjalny jest nieprawidłowy", - "显示模式": "tryb wyświetlania" + "显示模式": "tryb wyświetlania", + "内置": "wbudowany", + "信息": "informacje", + "开发商": "Deweloperzy", + "标签映射": "Mapowanie etykiet", + "简介": "krótkie wprowadzenie", + "全部": "całość", + "年度总结": "Roczne podsumowanie" } \ No newline at end of file diff --git a/py/files/lang/pt.json b/py/files/lang/pt.json index 4aac2eae..7efee3b0 100644 --- a/py/files/lang/pt.json +++ b/py/files/lang/pt.json @@ -756,5 +756,12 @@ "成功": "sucesso", "Win32通用钩子": "Gancho Universal Win32", "特殊码无效": "O código especial é inválido", - "显示模式": "modo de visualização" + "显示模式": "modo de visualização", + "内置": "embutido", + "信息": "informação", + "开发商": "Desenvolvedores", + "标签映射": "Mapeamento de Etiquetas", + "简介": "breve introdução", + "全部": "inteiro", + "年度总结": "Resumo anual" } \ No newline at end of file diff --git a/py/files/lang/ru.json b/py/files/lang/ru.json index a52e529d..2051989f 100644 --- a/py/files/lang/ru.json +++ b/py/files/lang/ru.json @@ -756,5 +756,12 @@ "成功": "Успех", "Win32通用钩子": "Win32 Универсальный крюк", "特殊码无效": "Специальный код не работает.", - "显示模式": "Показать режим" + "显示模式": "Показать режим", + "内置": "Встроенные", + "信息": "Информация", + "开发商": "Разработчики", + "标签映射": "Отображение метки", + "简介": "Введение", + "全部": "Все.", + "年度总结": "Ежегодное резюме" } \ No newline at end of file diff --git a/py/files/lang/sv.json b/py/files/lang/sv.json index 09c9d7f2..4ff27068 100644 --- a/py/files/lang/sv.json +++ b/py/files/lang/sv.json @@ -756,5 +756,12 @@ "成功": "framgång", "Win32通用钩子": "Win32 universalkrok", "特殊码无效": "Specialkoden är ogiltig", - "显示模式": "visningsläge" + "显示模式": "visningsläge", + "内置": "inbyggd", + "信息": "information", + "开发商": "Utvecklare", + "标签映射": "Etikettmappning", + "简介": "kort inledning", + "全部": "hela", + "年度总结": "Årlig sammanfattning" } \ No newline at end of file diff --git a/py/files/lang/th.json b/py/files/lang/th.json index 97b75b0d..831eba1d 100644 --- a/py/files/lang/th.json +++ b/py/files/lang/th.json @@ -756,5 +756,12 @@ "成功": "ความสำเร็จ", "Win32通用钩子": "ตะขอสากล Win32", "特殊码无效": "รหัสพิเศษไม่ถูกต้อง", - "显示模式": "โหมดการแสดงผล" + "显示模式": "โหมดการแสดงผล", + "内置": "สร้างขึ้นใน", + "信息": "ข้อมูล", + "开发商": "นักพัฒนา", + "标签映射": "แผนที่ป้ายกำกับ", + "简介": "บทนำ", + "全部": "ทั้งหมด", + "年度总结": "สรุปรายปี" } \ No newline at end of file diff --git a/py/files/lang/tr.json b/py/files/lang/tr.json index 5c242db2..7248aa2b 100644 --- a/py/files/lang/tr.json +++ b/py/files/lang/tr.json @@ -756,5 +756,12 @@ "成功": "başarılı", "Win32通用钩子": "Win32 Universal Hook", "特殊码无效": "Özel kodu geçersiz.", - "显示模式": "Görüntü modusu" + "显示模式": "Görüntü modusu", + "内置": "in şa edilmiş", + "信息": "bilgi", + "开发商": "Geliştiriciler", + "标签映射": "Etiket Haritası", + "简介": "kısa tanıtım", + "全部": "Tüm", + "年度总结": "Yıllık Toplantı" } \ No newline at end of file diff --git a/py/files/lang/uk.json b/py/files/lang/uk.json index 7e203af0..e217f4f5 100644 --- a/py/files/lang/uk.json +++ b/py/files/lang/uk.json @@ -756,5 +756,12 @@ "成功": "успіх", "Win32通用钩子": "Win32 Universal Hook", "特殊码无效": "Некоректний особливий код", - "显示模式": "режим показу" + "显示模式": "режим показу", + "内置": "вбудований", + "信息": "інформація", + "开发商": "Розробники", + "标签映射": "Мапування мітки", + "简介": "коротке введення", + "全部": "цілий", + "年度总结": "Річне резюме" } \ No newline at end of file diff --git a/py/files/lang/vi.json b/py/files/lang/vi.json index aa6dd03f..de2c4267 100644 --- a/py/files/lang/vi.json +++ b/py/files/lang/vi.json @@ -756,5 +756,12 @@ "成功": "Thành công", "Win32通用钩子": "Win32 phổ Hook", "特殊码无效": "Mã đặc biệt không hợp lệ", - "显示模式": "Chế độ hiển thị" + "显示模式": "Chế độ hiển thị", + "内置": "Được xây dựng trong", + "信息": "Thông tin", + "开发商": "Nhà phát triển", + "标签映射": "Bản đồ nhãn", + "简介": "Giới thiệu", + "全部": "Tất cả", + "年度总结": "Tóm tắt hàng năm" } \ No newline at end of file diff --git a/py/files/lang/zh.json b/py/files/lang/zh.json index 41d1fd4e..94caf85f 100644 --- a/py/files/lang/zh.json +++ b/py/files/lang/zh.json @@ -756,5 +756,12 @@ "成功": "", "Win32通用钩子": "", "特殊码无效": "", - "显示模式": "" + "显示模式": "", + "内置": "", + "信息": "", + "开发商": "", + "标签映射": "", + "简介": "", + "全部": "", + "年度总结": "" } \ No newline at end of file diff --git a/py/files/yearsummary/yearsummary.html b/py/files/yearsummary/yearsummary.html new file mode 100644 index 00000000..637efe77 --- /dev/null +++ b/py/files/yearsummary/yearsummary.html @@ -0,0 +1,300 @@ + + + + + ... + + + + + + + + + + + +
+
+ +
+
+

今年你一共总玩了
个游戏 +

+
+
+

今年你的累计游戏时长
小时,有
天玩了游戏,累计阅读字数达
+

+ +
+
+ + +
+ +
+
+
+
+
+
+
+
+
+

你所玩的游戏的标签

+
+
+
+

你所玩的游戏的开发商

+
+
+
Next
+ + + + + \ No newline at end of file