This commit is contained in:
恍兮惚兮 2024-08-13 23:07:35 +08:00
parent 714c31846e
commit 833ca891d3
16 changed files with 3362 additions and 3136 deletions

View File

@ -14,7 +14,6 @@ from myutils.config import (
set_font_default,
)
from ctypes import c_int, CFUNCTYPE, c_void_p
import sqlite3
from myutils.utils import (
minmaxmoveobservefunc,
parsemayberegexreplace,
@ -33,7 +32,7 @@ from textsource.copyboard import copyboard
from textsource.texthook import texthook
from textsource.ocrtext import ocrtext
from gui.selecthook import hookselect
from gui.translatorUI import QUnFrameWindow
from gui.translatorUI import TranslatorWindow
import zhconv, functools
from gui.transhist import transhist
from gui.edittext import edittext
@ -48,6 +47,7 @@ import winsharedutils
from winsharedutils import collect_running_pids
from myutils.post import POSTSOLVE
from myutils.utils import nowisdark
from myutils.traceplaytime import playtimemanager
from myutils.audioplayer import series_audioplayer
from gui.dynalang import LAction, LMenu
@ -86,7 +86,6 @@ class MAINUI:
self.edittextui = None
self.edittextui_cached = None
self.edittextui_sync = True
self.sqlsavegameinfo = None
self.notifyonce = set()
self.audioplayer = series_audioplayer()
self._internal_reader = None
@ -463,9 +462,9 @@ class MAINUI:
if len(res) == 0:
return
if onlytrans == False:
if globalconfig["read_trans"] and (
list(globalconfig["fanyi"].keys())[globalconfig["read_translator"]]
== classname
if (
globalconfig["read_trans"]
and globalconfig["read_translator2"] == classname
):
self.currentread = res
self.readcurrent()
@ -955,121 +954,13 @@ class MAINUI:
time.sleep(0.5)
def setdarktheme(self, widget, dark):
if widget.testAttribute(Qt.WidgetAttribute.WA_TranslucentBackground):
def setdarkandbackdrop(self, widget, dark):
if self.__dontshowintaborsetbackdrop(widget):
return
winsharedutils.SetTheme(
int(widget.winId()), dark, globalconfig["WindowBackdrop"]
)
def createsavegamedb(self):
self.sqlsavegameinfo = sqlite3.connect(
gobject.getuserconfigdir("savegame.db"),
check_same_thread=False,
isolation_level=None,
)
try:
self.sqlsavegameinfo.execute(
"CREATE TABLE gameinternalid(gameinternalid INTEGER PRIMARY KEY AUTOINCREMENT,gamepath TEXT);"
)
self.sqlsavegameinfo.execute(
"CREATE TABLE traceplaytime_v4(id INTEGER PRIMARY KEY AUTOINCREMENT,gameinternalid INT,timestart BIGINT,timestop BIGINT);"
)
except:
pass
def querytraceplaytime_v4(self, gameuid):
gameinternalid = self.get_gameinternalid(uid2gamepath[gameuid])
return self.sqlsavegameinfo.execute(
"SELECT timestart,timestop FROM traceplaytime_v4 WHERE gameinternalid = ?",
(gameinternalid,),
).fetchall()
def get_gameinternalid(self, gamepath):
while True:
ret = self.sqlsavegameinfo.execute(
"SELECT gameinternalid FROM gameinternalid WHERE gamepath = ?",
(gamepath,),
).fetchone()
if ret is None:
self.sqlsavegameinfo.execute(
"INSERT INTO gameinternalid VALUES(NULL,?)", (gamepath,)
)
else:
return ret[0]
def resetgameinternal(self, fr, to):
_id = self.get_gameinternalid(fr)
self.sqlsavegameinfo.execute(
"UPDATE gameinternalid SET gamepath = ? WHERE (gameinternalid = ?)",
(to, _id),
)
def traceplaytime(self, gamepath, start, end, new):
gameinternalid = self.get_gameinternalid(gamepath)
if new:
self.sqlsavegameinfo.execute(
"INSERT INTO traceplaytime_v4 VALUES(NULL,?,?,?)",
(gameinternalid, start, end),
)
else:
self.sqlsavegameinfo.execute(
"UPDATE traceplaytime_v4 SET timestop = ? WHERE (gameinternalid = ? and timestart = ?)",
(end, gameinternalid, start),
)
def checkgameplayingthread(self):
self.__currentexe = None
self.__statistictime = time.time()
while True:
__t = time.time()
time.sleep(1)
_t = time.time()
def isok(gameuid):
# 可能开着程序进行虚拟机暂停导致一下子多了很多时间。不过测试vbox上应该没问题
maybevmpaused = (_t - __t) > 60
if not maybevmpaused:
savehook_new_data[gameuid]["statistic_playtime"] += _t - __t
if (not maybevmpaused) and (self.__currentexe == name_):
self.traceplaytime(
uid2gamepath[gameuid], self.__statistictime - 1, _t, False
)
else:
self.__statistictime = time.time()
self.__currentexe = name_
self.traceplaytime(
uid2gamepath[gameuid],
self.__statistictime - 1,
self.__statistictime,
True,
)
_hwnd = windows.GetForegroundWindow()
_pid = windows.GetWindowThreadProcessId(_hwnd)
try:
if len(self.textsource.pids) == 0:
raise Exception()
if _pid in self.textsource.pids or _pid == os.getpid():
isok(self.textsource.gameuid)
else:
self.__currentexe = None
except:
name_ = getpidexe(_pid)
if not name_:
return
uids = findgameuidofpath(name_, findall=True)
try:
if len(uids):
for uid in uids:
isok(uid)
else:
self.__currentexe = None
except:
print_exc()
@threader
def clickwordcallback(self, word, append):
if globalconfig["usewordorigin"] == False:
@ -1085,6 +976,15 @@ class MAINUI:
if globalconfig["usesearchword"]:
self.searchwordW.search_word.emit(word, append)
def __dontshowintaborsetbackdrop(self, widget):
window_flags = widget.windowFlags()
if (
Qt.WindowType.FramelessWindowHint & window_flags
== Qt.WindowType.FramelessWindowHint
):
return True
return False
def setshowintab_checked(self, widget):
try:
self.translation_ui
@ -1095,11 +995,7 @@ class MAINUI:
int(widget.winId()), globalconfig["showintab"], True
)
return
window_flags = widget.windowFlags()
if (
Qt.WindowType.FramelessWindowHint & window_flags
== Qt.WindowType.FramelessWindowHint
):
if self.__dontshowintaborsetbackdrop(widget):
return
if isinstance(widget, (QMenu, QFrame)):
return
@ -1160,7 +1056,7 @@ class MAINUI:
self.currentisdark = dark
for widget in QApplication.topLevelWidgets():
self.setdarktheme(widget, dark)
self.setdarkandbackdrop(widget, dark)
style = ""
for _ in (0,):
try:
@ -1209,7 +1105,7 @@ class MAINUI:
self.installeventfillter()
self.parsedefaultfont()
self.loadmetadatas()
self.translation_ui = QUnFrameWindow()
self.translation_ui = TranslatorWindow()
winsharedutils.showintab(
int(self.translation_ui.winId()), globalconfig["showintab"], True
)
@ -1236,11 +1132,10 @@ class MAINUI:
threading.Thread(
target=minmaxmoveobservefunc, args=(self.translation_ui,)
).start()
threading.Thread(target=self.checkgameplayingthread).start()
self.messagecallback__ = CFUNCTYPE(None, c_int, c_void_p)(self.messagecallback)
winsharedutils.globalmessagelistener(self.messagecallback__)
self.inittray()
self.createsavegamedb()
self.playtimemanager = playtimemanager()
self.__count = 0
def openlink(self, file):
@ -1264,6 +1159,20 @@ class MAINUI:
elif msg == 2:
self.translation_ui.closesignal.emit()
def _dowhenwndcreate(self, obj):
hwnd = obj.winId()
if not hwnd: # window create/destroy,when destroy winId is None
return
windows.SetProp(
int(obj.winId()),
"Magpie.ToolWindow",
windows.HANDLE(1),
)
winsharedutils.setdwmextendframe(int(hwnd))
if self.currentisdark is not None:
self.setdarkandbackdrop(obj, self.currentisdark)
self.setshowintab_checked(obj)
def installeventfillter(self):
class WindowEventFilter(QObject):
def eventFilter(_, obj: QObject, event: QEvent):
@ -1271,17 +1180,8 @@ class MAINUI:
if "updatelangtext" in dir(obj):
obj.updatelangtext()
elif event.type() == QEvent.Type.WinIdChange:
self._dowhenwndcreate(obj)
hwnd = obj.winId()
if hwnd: # window create/destroy,when destroy winId is None
if self.currentisdark is not None:
self.setdarktheme(obj, self.currentisdark)
windows.SetProp(
int(obj.winId()),
"Magpie.ToolWindow",
windows.HANDLE(1),
)
self.setshowintab_checked(obj)
return False
self.currentisdark = None

View File

@ -1,4 +1,6 @@
baseobject = None
global_dialog_savedgame_new = None
global_dialog_setting_game = None
import io, sys, platform, os
from ctypes import windll, wintypes

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,649 @@
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.hwnd import getExeIcon
import gobject, hashlib
from gui.inputdialog import autoinitdialog
from gui.dynalang import LFormLayout, LDialog
from myutils.localetools import localeswitchedrun
from myutils.config import (
savehook_new_data,
savegametaged,
uid2gamepath,
_TR,
savehook_new_list,
globalconfig,
)
from gui.usefulwidget import (
yuitsu_switch,
getIconButton,
FocusCombo,
getsimplecombobox,
getspinbox,
getcolorbutton,
getsimpleswitch,
FQLineEdit,
getspinbox,
selectcolor,
)
class ItemWidget(QWidget):
focuschanged = pyqtSignal(bool, str)
doubleclicked = pyqtSignal(str)
globallashfocus = None
@classmethod
def clearfocus(cls):
try: # 可能已被删除
if ItemWidget.globallashfocus:
ItemWidget.globallashfocus.focusOut()
except:
pass
ItemWidget.globallashfocus = None
def click(self):
try:
self.bottommask.setStyleSheet(
f'background-color: {str2rgba(globalconfig["dialog_savegame_layout"]["onselectcolor1"],globalconfig["dialog_savegame_layout"]["transparentselect"])};'
)
if self != ItemWidget.globallashfocus:
ItemWidget.clearfocus()
ItemWidget.globallashfocus = self
self.focuschanged.emit(True, self.gameuid)
except:
print_exc()
def mousePressEvent(self, ev) -> None:
self.click()
def focusOut(self):
self.bottommask.setStyleSheet("background-color: rgba(255,255,255, 0);")
self.focuschanged.emit(False, self.gameuid)
def mouseDoubleClickEvent(self, e):
self.doubleclicked.emit(self.gameuid)
def resizeEvent(self, a0: QResizeEvent) -> None:
self.bottommask.resize(a0.size())
self.maskshowfileexists.resize(a0.size())
def __init__(self, gameuid, pixmap, file) -> None:
super().__init__()
self.itemw = globalconfig["dialog_savegame_layout"]["itemw"]
self.itemh = globalconfig["dialog_savegame_layout"]["itemh"]
# self.imgw = globalconfig["dialog_savegame_layout"]["imgw"]
# self.imgh = globalconfig["dialog_savegame_layout"]["imgh"]
# margin = (
# self.itemw - self.imgw
# ) // 2 # globalconfig['dialog_savegame_layout']['margin']
margin = globalconfig["dialog_savegame_layout"]["margin"]
if globalconfig["showgametitle"]:
textH = globalconfig["dialog_savegame_layout"]["textH"]
else:
textH = 0
self.imgw = self.itemw - 2 * margin
self.imgh = self.itemh - textH - 2 * margin
#
self.setFixedSize(QSize(self.itemw, self.itemh))
# self.setFocusPolicy(Qt.StrongFocus)
self.maskshowfileexists = QLabel(self)
self.bottommask = QLabel(self)
self.bottommask.setStyleSheet("background-color: rgba(255,255,255, 0);")
layout = QVBoxLayout()
layout.setContentsMargins(0, 0, 0, 0)
self._img = IMGWidget(self.imgw, self.imgh, pixmap)
_w = QWidget()
_w.setStyleSheet("background-color: rgba(255,255,255, 0);")
wrap = QVBoxLayout()
_w.setLayout(wrap)
_w.setFixedHeight(self.imgh + 2 * margin)
wrap.setContentsMargins(margin, margin, margin, margin)
wrap.addWidget(self._img)
layout.addWidget(_w)
layout.setSpacing(0)
self._lb = QLabel()
if globalconfig["showgametitle"]:
self._lb.setText(file)
self._lb.setWordWrap(True)
self._lb.setStyleSheet("background-color: rgba(255,255,255, 0);")
self._lb.setAlignment(Qt.AlignmentFlag.AlignHCenter)
layout.addWidget(self._lb)
self.setLayout(layout)
self.gameuid = gameuid
c = globalconfig["dialog_savegame_layout"][
("onfilenoexistscolor1", "backcolor1")[
os.path.exists(uid2gamepath[gameuid])
]
]
c = str2rgba(
c,
globalconfig["dialog_savegame_layout"][
("transparentnotexits", "transparent")[
os.path.exists(uid2gamepath[gameuid])
]
],
)
self.maskshowfileexists.setStyleSheet(f"background-color:{c};")
class IMGWidget(QLabel):
def adaptsize(self, size: QSize):
if globalconfig["imagewrapmode"] == 0:
h, w = size.height(), size.width()
r = float(w) / h
max_r = float(self.width()) / self.height()
if r < max_r:
new_w = self.width()
new_h = int(new_w / r)
else:
new_h = self.height()
new_w = int(new_h * r)
return QSize(new_w, new_h)
elif globalconfig["imagewrapmode"] == 1:
h, w = size.height(), size.width()
r = float(w) / h
max_r = float(self.width()) / self.height()
if r > max_r:
new_w = self.width()
new_h = int(new_w / r)
else:
new_h = self.height()
new_w = int(new_h * r)
return QSize(new_w, new_h)
elif globalconfig["imagewrapmode"] == 2:
return self.size()
elif globalconfig["imagewrapmode"] == 3:
return size
def setimg(self, pixmap):
if type(pixmap) != QPixmap:
pixmap = pixmap()
rate = self.devicePixelRatioF()
newpixmap = QPixmap(self.size() * rate)
newpixmap.setDevicePixelRatio(rate)
newpixmap.fill(Qt.GlobalColor.transparent)
painter = QPainter(newpixmap)
painter.setRenderHint(QPainter.RenderHint.SmoothPixmapTransform)
painter.setRenderHint(QPainter.RenderHint.Antialiasing)
painter.drawPixmap(self.getrect(pixmap.size()), pixmap)
painter.end()
self.setPixmap(newpixmap)
def getrect(self, size):
size = self.adaptsize(size)
rect = QRect()
rect.setX(int((self.width() - size.width()) / 2))
rect.setY(int((self.height() - size.height()) / 2))
rect.setSize(size)
return rect
def __init__(self, w, h, pixmap) -> None:
super().__init__()
self.setFixedSize(QSize(w, h))
self.setScaledContents(True)
self.setimg(pixmap)
class ClickableLabel(QLabel):
def __init__(self):
super().__init__()
self.setClickable(True)
def setClickable(self, clickable):
self._clickable = clickable
def mousePressEvent(self, event):
if self._clickable and event.button() == Qt.MouseButton.LeftButton:
self.clicked.emit()
clicked = pyqtSignal()
class tagitem(QWidget):
# website
TYPE_GLOABL_LIKE = 3
TYPE_GAME_LIKE = 1
# search game
TYPE_RAND = 0
TYPE_DEVELOPER = 1
TYPE_TAG = 2
TYPE_USERTAG = 3
TYPE_EXISTS = 4
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)
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:
super().__init__()
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):
super().__init__(parent)
layout = QHBoxLayout()
layout.setContentsMargins(0, 0, 0, 0)
self.setLayout(layout)
self.lineEdit = FocusCombo()
self.lineEdit.setLineEdit(FQLineEdit())
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)
def opendirforgameuid(gameuid):
f = uid2gamepath[gameuid]
f = os.path.dirname(f)
if os.path.exists(f) and os.path.isdir(f):
os.startfile(f)
@threader
def startgame(gameuid):
try:
game = uid2gamepath[gameuid]
if os.path.exists(game):
mode = savehook_new_data[gameuid]["onloadautochangemode2"]
if mode > 0:
_ = {1: "texthook", 2: "copy", 3: "ocr"}
if globalconfig["sourcestatus2"][_[mode]]["use"] == False:
globalconfig["sourcestatus2"][_[mode]]["use"] = True
yuitsu_switch(
gobject.baseobject.settin_ui,
globalconfig["sourcestatus2"],
"sourceswitchs",
_[mode],
None,
True,
)
gobject.baseobject.starttextsource(use=_[mode], checked=True)
localeswitchedrun(gameuid)
except:
print_exc()
def __b64string(a: str):
return hashlib.md5(a.encode("utf8")).hexdigest()
def __scaletosize(_pix: QPixmap, tgt):
if min(_pix.width(), _pix.height()) > 400:
if _pix.height() < 400:
sz = QSize(_pix.width() * 400 // _pix.height(), 400)
else:
sz = QSize(400, _pix.height() * 400 // _pix.width())
_pix = _pix.scaled(
sz,
Qt.AspectRatioMode.KeepAspectRatio,
Qt.TransformationMode.SmoothTransformation,
)
_pix.save(tgt)
def getcachedimage(src, small):
if not small:
_pix = QPixmap(src)
if _pix.isNull():
return None
return _pix
if not os.path.exists(src):
return None
src2 = gobject.getcachedir(f"icon2/{__b64string(src)}.jpg")
_pix = QPixmap(src2)
if not _pix.isNull():
return _pix
_pix = QPixmap(src)
if _pix.isNull():
return None
__scaletosize(_pix, src2)
return _pix
def getpixfunction(kk, small=False):
if (
savehook_new_data[kk]["currentmainimage"]
in savehook_new_data[kk]["imagepath_all"]
):
src = savehook_new_data[kk]["currentmainimage"]
pix = getcachedimage(src, small)
if pix:
return pix
for _ in savehook_new_data[kk]["imagepath_all"]:
pix = getcachedimage(_, small)
if pix:
return pix
_pix = getExeIcon(uid2gamepath[kk], False, cache=True)
return _pix
def startgamecheck(self, gameuid):
if not gameuid:
return
if not os.path.exists(uid2gamepath[gameuid]):
return
if globalconfig["startgamenototop"] == False:
idx = savehook_new_list.index(gameuid)
savehook_new_list.insert(0, savehook_new_list.pop(idx))
self.parent().parent().close()
startgame(gameuid)
def addgamesingle(parent, callback, targetlist):
f = QFileDialog.getOpenFileName(options=QFileDialog.Option.DontResolveSymlinks)
res = f[0]
if res == "":
return
res = os.path.normpath(res)
uid = find_or_create_uid(targetlist, res)
if uid in targetlist:
idx = targetlist.index(uid)
response = QMessageBox.question(
parent,
"",
_TR("游戏已存在,是否重复添加?"),
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
QMessageBox.StandardButton.No,
)
if response == QMessageBox.StandardButton.No:
if idx == 0:
return
targetlist.pop(idx)
else:
uid = duplicateconfig(uid)
targetlist.insert(0, uid)
callback(uid)
def addgamebatch(callback, targetlist):
res = QFileDialog.getExistingDirectory(
options=QFileDialog.Option.DontResolveSymlinks
)
if res == "":
return
for _dir, _, _fs in os.walk(res):
for _f in _fs:
path = os.path.normpath(os.path.abspath(os.path.join(_dir, _f)))
if path.lower().endswith(".exe") == False:
continue
uid = find_or_create_uid(targetlist, path)
if uid in targetlist:
targetlist.pop(targetlist.index(uid))
targetlist.insert(0, uid)
callback(uid)
def loadvisinternal(skipid=False, skipidid=None):
__vis = []
__uid = []
for _ in savegametaged:
if _ is None:
__vis.append("GLOBAL")
__uid.append(None)
else:
__vis.append(_["title"])
__uid.append(_["uid"])
if skipid:
if skipidid == __uid[-1]:
__uid.pop(-1)
__vis.pop(-1)
return __vis, __uid
def getalistname(parent, callback, skipid=False, skipidid=None):
__d = {"k": 0}
__vis, __uid = loadvisinternal(skipid, skipidid)
def __wrap(callback, __d, __uid):
if len(__uid) == 0:
return
uid = __uid[__d["k"]]
callback(uid)
if len(__uid) > 1:
autoinitdialog(
parent,
"目标",
600,
[
{
"type": "combo",
"name": "目标",
"d": __d,
"k": "k",
"list": __vis,
},
{
"type": "okcancel",
"callback": functools.partial(__wrap, callback, __d, __uid),
},
],
)
elif len(__uid):
callback(__uid[0])
def calculatetagidx(tagid):
i = 0
for save in savegametaged:
if save is None and tagid is None:
return i
elif save and tagid and save["uid"] == tagid:
return i
i += 1
return None
def getreflist(reftagid):
_idx = calculatetagidx(reftagid)
if _idx is None:
return None
tag = savegametaged[_idx]
if tag is None:
return savehook_new_list
return tag["games"]
@Singleton_close
class dialog_syssetting(LDialog):
def __init__(self, parent, type_=1) -> None:
super().__init__(parent, Qt.WindowType.WindowCloseButtonHint)
self.setWindowTitle("其他设置")
formLayout = LFormLayout(self)
formLayout.addRow(
"隐藏不存在的游戏",
getsimpleswitch(globalconfig, "hide_not_exists"),
)
if type_ == 1:
for key, name in [
("itemw", "宽度"),
("itemh", "高度"),
# ("imgw", "图片宽度"),
# ("imgh", "图片高度"),
("margin", "边距"),
("textH", "文字区高度"),
]:
formLayout.addRow(
name,
getspinbox(0, 1000, globalconfig["dialog_savegame_layout"], key),
)
elif type_ == 2:
for key, name in [
("listitemheight", "文字区高度"),
("listitemwidth", "高度"),
]:
formLayout.addRow(
name,
getspinbox(0, 1000, globalconfig["dialog_savegame_layout"], key),
)
for key, key2, name in [
("backcolor1", "transparent", "颜色"),
("onselectcolor1", "transparentselect", "选中时颜色"),
("onfilenoexistscolor1", "transparentnotexits", "游戏不存在时颜色"),
]:
formLayout.addRow(
name,
getcolorbutton(
globalconfig["dialog_savegame_layout"],
key,
callback=functools.partial(
selectcolor,
self,
globalconfig["dialog_savegame_layout"],
key,
None,
self,
key,
),
name=key,
parent=self,
),
)
formLayout.addRow(
name + "_" + "不透明度",
getspinbox(0, 100, globalconfig["dialog_savegame_layout"], key2),
)
if type_ == 1:
formLayout.addRow(
"缩放",
getsimplecombobox(
["填充", "适应", "拉伸", "居中"],
globalconfig,
"imagewrapmode",
),
)
formLayout.addRow(
"启动游戏不修改顺序",
getsimpleswitch(globalconfig, "startgamenototop"),
)
if type_ == 1:
formLayout.addRow(
"显示标题",
getsimpleswitch(globalconfig, "showgametitle"),
)
self.show()

View File

@ -0,0 +1,219 @@
from PyQt5.QtWidgets import QWidget
from qtsymbols import *
import functools, threading
from myutils.config import savehook_new_list, savehook_new_data, uid2gamepath
from myutils.hwnd import getExeIcon
from gui.usefulwidget import (
TableViewW,
D_getsimpleswitch,
D_getIconButton,
getcolorbutton,
)
from gui.dialog_savedgame_setting import dialog_setting_game
from gui.dynalang import LPushButton, LStandardItemModel
from gui.dialog_savedgame_common import opendirforgameuid, startgamecheck, addgamesingle
class LazyLoadTableView(TableViewW):
def __init__(self, model: LStandardItemModel) -> None:
super().__init__()
self.widgetfunction = []
self.lock = threading.Lock()
self.setModel(model)
self.started = False
def starttraceir(self):
self.started = True
self.model().rowsRemoved.connect(functools.partial(self.insertremove))
self.model().rowsInserted.connect(functools.partial(self.insert))
def resizeEvent(self, e):
self.loadVisibleRows()
super().resizeEvent(e)
def insertremove(self, index, start, end):
off = end - start + 1
with self.lock:
collect = []
for i in range(len(self.widgetfunction)):
if self.widgetfunction[i][0] > end:
self.widgetfunction[i][0] -= off
elif (
self.widgetfunction[i][0] >= start
and self.widgetfunction[i][0] <= end
):
collect.append(i)
for i in collect:
self.widgetfunction.pop(i)
self.loadVisibleRows()
def insert(self, index, start, end):
off = end - start + 1
with self.lock:
for i in range(len(self.widgetfunction)):
if self.widgetfunction[i][0] >= start:
self.widgetfunction[i][0] += off
# print(self.widgetfunction[i])
self.loadVisibleRows()
def setIndexWidget(self, index: QModelIndex, widgetf):
if not self.started:
self.widgetfunction.append([index.row(), index.column(), widgetf])
return
if self.visualRect(index).intersects(self.viewport().rect()):
w = widgetf()
super().setIndexWidget(index, w)
else:
with self.lock:
self.widgetfunction.append([index.row(), index.column(), widgetf])
def scrollContentsBy(self, dx, dy):
super().scrollContentsBy(dx, dy)
self.loadVisibleRows()
def loadVisibleRows(self):
with self.lock:
collect = []
for i, index in enumerate(self.widgetfunction):
row, col, wf = index
if self.visualRect(self.model().index(row, col)).intersects(
self.viewport().rect()
):
collect.insert(0, i)
for i in collect:
row, col, wf = self.widgetfunction.pop(i)
w = wf()
super().setIndexWidget(self.model().index(row, col), w)
class dialog_savedgame_legacy(QWidget):
def directshow(self):
pass
def showsettingdialog(self, k):
dialog_setting_game(self, k)
def clicked2(self):
try:
idx = self.table.currentIndex().row()
savehook_new_list.pop(idx)
self.savelist.pop(idx)
self.model.removeRow(self.table.currentIndex().row())
except:
pass
def clicked3(self):
def call(uid):
if uid in self.savelist:
idx = self.savelist.index(uid)
self.savelist.pop(idx)
self.model.removeRow(idx)
self.newline(0, uid)
self.table.setCurrentIndex(self.model.index(0, 0))
addgamesingle(self, call, savehook_new_list)
def clicked(self):
startgamecheck(
self, self.model.item(self.table.currentIndex().row(), 2).savetext
)
def delayloadicon(self, k):
return getcolorbutton(
"",
"",
functools.partial(opendirforgameuid, k),
qicon=getExeIcon(uid2gamepath[k], cache=True),
)
def callback_leuse(self, k, use):
if use:
savehook_new_data[k]["launch_method"] = None
else:
savehook_new_data[k]["launch_method"] = "direct"
def newline(self, row, k):
keyitem = QStandardItem()
keyitem.savetext = k
self.model.insertRow(
row,
[
QStandardItem(),
QStandardItem(),
keyitem,
QStandardItem((savehook_new_data[k]["title"])),
],
)
self.table.setIndexWidget(
self.model.index(row, 0),
D_getsimpleswitch(
{"1": savehook_new_data[k].get("launch_method") != "direct"},
"1",
callback=functools.partial(self.callback_leuse, k),
),
)
self.table.setIndexWidget(
self.model.index(row, 1),
functools.partial(self.delayloadicon, k),
)
self.table.setIndexWidget(
self.model.index(row, 2),
D_getIconButton(
functools.partial(self.showsettingdialog, k), icon="fa.gear"
),
)
def __init__(self, parent) -> None:
# if dialog_savedgame._sigleton :
# return
# dialog_savedgame._sigleton=True
super().__init__(parent)
formLayout = QVBoxLayout(self) #
model = LStandardItemModel()
model.setHorizontalHeaderLabels(["转区", "", "设置", "游戏"]) # ,'HOOK'])
self.model = model
table = LazyLoadTableView(model)
table.horizontalHeader().setSectionResizeMode(
QHeaderView.ResizeMode.ResizeToContents
)
table.horizontalHeader().setStretchLastSection(True)
# table.setEditTriggers(QAbstractItemView.NoEditTriggers);
table.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
table.setSelectionMode((QAbstractItemView.SelectionMode.SingleSelection))
table.setWordWrap(False)
self.table = table
self.savelist = []
for row, k in enumerate(savehook_new_list): # 2
self.newline(row, k)
self.savelist.append(k)
self.table.starttraceir()
bottom = QHBoxLayout()
button = LPushButton("开始游戏")
self.button = button
button.clicked.connect(self.clicked)
button3 = LPushButton("添加游戏")
button3.clicked.connect(self.clicked3)
button2 = LPushButton("删除游戏")
button2.clicked.connect(self.clicked2)
bottom.addWidget(button)
bottom.addWidget(button2)
bottom.addWidget(button3)
_ = QLabel()
_.setFixedHeight(20)
_.setStyleSheet("background: transparent;")
formLayout.addWidget(_)
formLayout.addWidget(table)
formLayout.addLayout(bottom)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -139,10 +139,10 @@ class rangeadjust(Mainw):
# 由于使用movewindow而非qt函数导致内部执行绪有问题。
class rangeselct(QMainWindow):
class rangeselect(QMainWindow):
def __init__(self, parent=None):
super(rangeselct, self).__init__(parent)
super(rangeselect, self).__init__(parent)
self.setWindowFlags(
Qt.WindowType.FramelessWindowHint
| Qt.WindowType.WindowStaysOnTopHint
@ -254,7 +254,7 @@ screen_shot_ui = None
def rangeselct_function(callback, clickrelease, startauto):
global screen_shot_ui
if screen_shot_ui is None:
screen_shot_ui = rangeselct()
screen_shot_ui = rangeselect()
# 可能是由于使用win32移动窗口导致父翻译show/hide影响到他
screen_shot_ui.show()
screen_shot_ui.reset()

View File

@ -229,7 +229,7 @@ def setTab5lz(self):
D_getsimplecombobox(
alltransvis,
globalconfig,
"read_translator",
"read_translator2",
internal=alltrans,
),
"",

View File

@ -31,7 +31,8 @@ from gui.textbrowser import Textbrowser
from gui.rangeselect import rangeselct_function
from gui.usefulwidget import resizableframeless, getQMessageBox, LIconLabel
from gui.edittext import edittrans
from gui.dialog_savedgame import browserdialog, dialog_savedgame_integrated
from gui.dialog_savedgame import dialog_savedgame_integrated
from gui.dialog_savedgame_setting import browserdialog
from gui.dynalang import LDialog
@ -221,7 +222,7 @@ class ButtonBar(QFrame):
self.parent().setMinimumWidth(int(w))
class QUnFrameWindow(resizableframeless):
class TranslatorWindow(resizableframeless):
displayglobaltooltip = pyqtSignal(str)
displaylink = pyqtSignal(str)
displaymessagebox = pyqtSignal(str, str)
@ -831,7 +832,7 @@ class QUnFrameWindow(resizableframeless):
def __init__(self):
super(QUnFrameWindow, self).__init__(
super(TranslatorWindow, self).__init__(
None,
flags=Qt.WindowType.FramelessWindowHint
| Qt.WindowType.WindowMinimizeButtonHint,

View File

@ -0,0 +1,126 @@
import sqlite3, gobject, threading, time, windows
import time
import os, threading
from qtsymbols import *
from traceback import print_exc
from myutils.config import (
uid2gamepath,
findgameuidofpath,
savehook_new_data,
)
from myutils.hwnd import getpidexe
import windows
import gobject
class playtimemanager:
def __init__(self):
self.sqlsavegameinfo = sqlite3.connect(
gobject.getuserconfigdir("savegame.db"),
check_same_thread=False,
isolation_level=None,
)
try:
self.sqlsavegameinfo.execute(
"CREATE TABLE gameinternalid(gameinternalid INTEGER PRIMARY KEY AUTOINCREMENT,gamepath TEXT);"
)
self.sqlsavegameinfo.execute(
"CREATE TABLE traceplaytime_v4(id INTEGER PRIMARY KEY AUTOINCREMENT,gameinternalid INT,timestart BIGINT,timestop BIGINT);"
)
except:
pass
threading.Thread(target=self.checkgameplayingthread).start()
def querytraceplaytime_v4(self, gameuid):
gameinternalid = self.get_gameinternalid(uid2gamepath[gameuid])
return self.sqlsavegameinfo.execute(
"SELECT timestart,timestop FROM traceplaytime_v4 WHERE gameinternalid = ?",
(gameinternalid,),
).fetchall()
def get_gameinternalid(self, gamepath):
while True:
ret = self.sqlsavegameinfo.execute(
"SELECT gameinternalid FROM gameinternalid WHERE gamepath = ?",
(gamepath,),
).fetchone()
if ret is None:
self.sqlsavegameinfo.execute(
"INSERT INTO gameinternalid VALUES(NULL,?)", (gamepath,)
)
else:
return ret[0]
def resetgameinternal(self, fr, to):
_id = self.get_gameinternalid(fr)
self.sqlsavegameinfo.execute(
"UPDATE gameinternalid SET gamepath = ? WHERE (gameinternalid = ?)",
(to, _id),
)
def traceplaytime(self, gamepath, start, end, new):
gameinternalid = self.get_gameinternalid(gamepath)
if new:
self.sqlsavegameinfo.execute(
"INSERT INTO traceplaytime_v4 VALUES(NULL,?,?,?)",
(gameinternalid, start, end),
)
else:
self.sqlsavegameinfo.execute(
"UPDATE traceplaytime_v4 SET timestop = ? WHERE (gameinternalid = ? and timestart = ?)",
(end, gameinternalid, start),
)
def checkgameplayingthread(self):
self.__currentexe = None
self.__statistictime = time.time()
while True:
__t = time.time()
time.sleep(1)
_t = time.time()
def isok(gameuid):
# 可能开着程序进行虚拟机暂停导致一下子多了很多时间。不过测试vbox上应该没问题
maybevmpaused = (_t - __t) > 60
if not maybevmpaused:
savehook_new_data[gameuid]["statistic_playtime"] += _t - __t
if (not maybevmpaused) and (self.__currentexe == name_):
self.traceplaytime(
uid2gamepath[gameuid], self.__statistictime - 1, _t, False
)
else:
self.__statistictime = time.time()
self.__currentexe = name_
self.traceplaytime(
uid2gamepath[gameuid],
self.__statistictime - 1,
self.__statistictime,
True,
)
_hwnd = windows.GetForegroundWindow()
_pid = windows.GetWindowThreadProcessId(_hwnd)
try:
if len(gobject.baseobject.textsource.pids) == 0:
raise Exception()
if _pid in gobject.baseobject.textsource.pids or _pid == os.getpid():
isok(gobject.baseobject.textsource.gameuid)
else:
self.__currentexe = None
except:
name_ = getpidexe(_pid)
if not name_:
return
uids = findgameuidofpath(name_, findall=True)
try:
if len(uids):
for uid in uids:
isok(uid)
else:
self.__currentexe = None
except:
print_exc()

View File

@ -243,13 +243,11 @@ globalmessagelistener = utilsdll.globalmessagelistener
globalmessagelistener.argtypes = (c_void_p,)
dispatchcloseevent = utilsdll.dispatchcloseevent
setdwmextendframe = utilsdll.setdwmextendframe
setdwmextendframe.argtypes = (HWND,)
_SetTheme = utilsdll._SetTheme
_SetTheme.argtypes = HWND, c_bool, c_int
def SetTheme(hwnd, dark, backdrop):
_SetTheme(hwnd, dark, backdrop)
SetTheme = utilsdll._SetTheme
SetTheme.argtypes = HWND, c_bool, c_int
getprocesses = utilsdll.getprocesses

View File

@ -14,7 +14,7 @@
"read_raw": true,
"global_list_opened": true,
"read_trans": false,
"read_translator": 0,
"read_translator2": "",
"disappear_delay": 5,
"network": 1,
"useextrahtml": true,

View File

@ -29,7 +29,7 @@ include(generate_product_version)
set(VERSION_MAJOR 5)
set(VERSION_MINOR 26)
set(VERSION_PATCH 3)
set(VERSION_PATCH 4)
add_library(pch pch.cpp)
target_precompile_headers(pch PUBLIC pch.h)

View File

@ -1,4 +1,5 @@
#include "define.h"
#include <dwmapi.h>
typedef enum _WINDOWCOMPOSITIONATTRIB
{
@ -85,6 +86,13 @@ typedef BOOL(WINAPI *pfnSetWindowCompositionAttribute)(HWND, WINDOWCOMPOSITIONAT
DECLARE bool setAcrylicEffect(HWND hwnd, bool isEnableShadow)
{
// win7全都用areo
DWM_BLURBEHIND bb = {0};
bb.dwFlags = DWM_BB_ENABLE;
bb.fEnable = true;
bb.hRgnBlur = NULL;
DwmEnableBlurBehindWindow(hwnd, &bb);
DWORD gradientColor = 0x00FfFfFf; // ABGR
common
@ -94,9 +102,14 @@ DECLARE bool setAcrylicEffect(HWND hwnd, bool isEnableShadow)
accentPolicy.AccentFlags = accentFlags;
return setWindowCompositionAttribute(hwnd, &winCompAttrData);
}
DECLARE bool setAeroEffect(HWND hwnd, bool isEnableShadow)
{
DWM_BLURBEHIND bb = {0};
bb.dwFlags = DWM_BB_ENABLE;
bb.fEnable = true;
bb.hRgnBlur = NULL;
DwmEnableBlurBehindWindow(hwnd, &bb);
common
accentPolicy.AccentState = ACCENT_ENABLE_BLURBEHIND;
@ -106,6 +119,11 @@ DECLARE bool setAeroEffect(HWND hwnd, bool isEnableShadow)
}
DECLARE bool clearEffect(HWND hwnd)
{
DWM_BLURBEHIND bb = {0};
bb.dwFlags = DWM_BB_ENABLE;
bb.fEnable = false;
bb.hRgnBlur = NULL;
DwmEnableBlurBehindWindow(hwnd, &bb);
common
accentPolicy.AccentState = ACCENT_DISABLED;

View File

@ -150,13 +150,19 @@ static void SetWindowTheme(HWND hWnd, bool darkBorder, bool darkMenu) noexcept
RefreshImmersiveColorPolicyState();
FlushMenuThemes();
}
DECLARE void setdwmextendframe(HWND hwnd)
{
MARGINS mar{-1, -1, -1, -1};
DwmExtendFrameIntoClientArea(hwnd, &mar);
}
DECLARE void _SetTheme(
HWND _hWnd,
bool dark,
int backdrop)
{
// printf("%d %d\n",GetOSversion(),GetOSBuild());
if (GetOSversion() <= 6)//win7 x32 DwmSetWindowAttribute会崩直接禁了反正没用。不知道win8怎么样。
if (GetOSversion() <= 6) // win7 x32 DwmSetWindowAttribute会崩直接禁了反正没用。不知道win8怎么样。
return;
// auto _isBackgroundSolidColor = backdrop == WindowBackdrop::SolidColor;
// if (Win32Helper::GetOSVersion().Is22H2OrNewer() &&
@ -176,11 +182,6 @@ DECLARE void _SetTheme(
// return false;
// }
// 非常诡异这里marBACKDROP_MAPvalue的声明顺序会导致在win7-32位上崩溃原因未知。
MARGINS mar{-1, -1, -1, -1};
// 这个最重要不可以跳过否则transaprent会黑。win7无效仍然是黑的所以win7不可以使用QTWIN11主题。
DwmExtendFrameIntoClientArea(_hWnd, &mar);
// https://learn.microsoft.com/zh-cn/windows/win32/api/dwmapi/ne-dwmapi-dwm_systembackdrop_type
// 设置背景
static const DWM_SYSTEMBACKDROP_TYPE BACKDROP_MAP[] = {