diff --git a/.gitignore b/.gitignore index 2e635ba..5c0fa89 100644 --- a/.gitignore +++ b/.gitignore @@ -139,3 +139,4 @@ history.db commies/ test/ src/data/cache_word +src/data/local_read.db diff --git a/src/component/list/comic_list_widget.py b/src/component/list/comic_list_widget.py index bf184b6..d934f68 100644 --- a/src/component/list/comic_list_widget.py +++ b/src/component/list/comic_list_widget.py @@ -115,9 +115,9 @@ def AddBookByLocal(self, v, category=""): widget.picNum = v.picCnt widget.url = v.file if len(v.eps) > 0: - title += "{}".format("(" + str(len(v.eps)) + "E)") + fontColor = "{}".format("(" + str(len(v.eps)) + "E)") else: - title += "{}".format("(" + str(v.picCnt) + "P)") + fontColor = "{}".format("(" + str(v.picCnt) + "P)") if v.lastReadTime: categories = "{} {}".format(ToolUtil.GetUpdateStrByTick(v.lastReadTime), Str.GetStr(Str.Looked)) @@ -132,7 +132,9 @@ def AddBookByLocal(self, v, category=""): widget.categoryLabel.setVisible(True) widget.toolButton.setVisible(False) - widget.nameLable.setText(title) + # widget.nameLable.setText(title) + widget.SetTitle(title,fontColor) + item = QListWidgetItem(self) item.setFlags(item.flags() & ~Qt.ItemIsSelectable) item.setSizeHint(widget.sizeHint()) @@ -200,13 +202,15 @@ def AddBookItem(self, _id, title, categoryStr="", url="", path="", likesCount="" widget.starButton.setVisible(True) else: widget.starButton.setVisible(False) - + fontColor = "" if pagesCount: - title += "{}".format("("+str(pagesCount)+"P)") - if finished: - title += "{}".format("({})".format(Str.GetStr(Str.ComicFinished))) + fontColor += "{}".format("("+str(pagesCount)+"P)") + # if finished: + # fontColor += "{}".format("({})".format(Str.GetStr(Str.ComicFinished))) + + # widget.nameLable.setText(title) - widget.nameLable.setText(title) + widget.SetTitle(title,fontColor) item = QListWidgetItem(self) item.setFlags(item.flags() & ~Qt.ItemIsSelectable) item.setSizeHint(widget.sizeHint()) diff --git a/src/component/scroll/read_scroll.py b/src/component/scroll/read_scroll.py index 94797cc..fbf062c 100644 --- a/src/component/scroll/read_scroll.py +++ b/src/component/scroll/read_scroll.py @@ -1,40 +1,228 @@ +import weakref + from PySide6.QtCore import QPropertyAnimation, QEasingCurve, QAbstractAnimation from PySide6.QtWidgets import QScrollBar from qt_owner import QtOwner +from tools.str import Str +from view.read.read_enum import ReadMode class ReadScroll(QScrollBar): - def __init__(self): + def __init__(self, parent): QScrollBar.__init__(self) - self.animation = QPropertyAnimation() - self.animation.setTargetObject(self) - self.animation.setPropertyName(b"value") - self.scrollTime = 1000 - self.animation.setDuration(self.scrollTime) - self.animation.setEasingCurve(QEasingCurve.Linear) - self.animationValue = self.value() - self.backTick = 0 - self.laveValue = 0 - self.lastV = 0 - self.animation.finished.connect(self.Finished) + self._owner = weakref.ref(parent) + self.scrollTime = 200 + self.maxScrollTme = 500 + self.maxRate = 10 + + self.ani = QPropertyAnimation() + self.ani.setTargetObject(self) + self.ani.setPropertyName(b"value") + self.ani.setEasingCurve(QEasingCurve.Linear) + self.ani.setDuration(self.scrollTime) + self.__value = self.value() + self.ani.finished.connect(self.Finished) + self.setSingleStep(1) + # self.oldupdateCurrentValue = self.ani.updateCurrentValue + # self.ani.updateCurrentValue = self.UpdateCurrentValue + # self.ani.valueChanged.connect(self.AniValueChange) + self.priV = 0 + + self.lastAniStartV = 0 + self.lastAniEndV = 0 + self.lastScrollTime = 0 + # self.valueChanged.connect(self.ValueChange) + self.oldv = 0 + + # def ValueChange(self,value): + # self.oldv = value + # print("2, {}".format(value)) + + # def AniValueChange(self, value): + # print("1, {}".format(value)) + + # + # def UpdateCurrentValue(self, value): + # print("2, {}".format(value)) + # self.oldupdateCurrentValue(value) + + def ResetAniValueByAdd(self, oldV, addV): + # print("reset1 setV, {}".format(oldV+addV)) + QScrollBar.setValue(self, oldV+addV) + changeV = self.lastAniEndV - self.lastAniStartV + laveV = self.lastAniEndV - oldV + scrollTime = 0 + if changeV != 0: + scrollTime = min(self.maxScrollTme, int(laveV / changeV * self.lastScrollTime)) + if scrollTime > 0: + self.ani.stop() + self.StartAni(self.value(), self.lastAniEndV, scrollTime) + else: + self.ani.stop() + self.StartAni(self.value(), self.value(), self.scrollTime) + + def AniValueByAdd(self, addV): + changeV = self.lastAniEndV - self.lastAniStartV + addV2 = self.lastAniEndV - self.value() + self.lastAniStartV = self.value() + if addV <= 0 : + v = max(self.maxRate * addV, addV + addV2) + else: + v = min(self.maxRate * addV, addV + addV2) + self.lastAniEndV = self.lastAniStartV + v + laveV = self.lastAniEndV - self.lastAniStartV + + scrollTime = 0 + if changeV != 0: + scrollTime = min(self.maxScrollTme, int(laveV / changeV * self.lastScrollTime)) + if scrollTime <= 0: + scrollTime = self.scrollTime + + # print("add setV, {}".format(self.lastAniEndV)) + self.ani.stop() + self.StartAni(self.value(), self.lastAniEndV, scrollTime) + + # def AniValueChange(self, value): + # print("{}, {}".format(value, value - self.priV)) + # self.priV = value + + @property + def frame(self): + return self._owner() + + @property + def scrollArea(self): + return self._owner().scrollArea + + @property + def labelSize(self): + return self.scrollArea.labelSize + + @property + def readImg(self): + return self.scrollArea.readImg def Finished(self): - QtOwner().owner.readView.frame.scrollArea.OnValueChange(self.value()) + self.OnValueChange(self.value()) def StopScroll(self): - self.backTick = 0 - self.animation.stop() - - def Scroll(self, value, time=0): - if self.animation.state() == QAbstractAnimation.State.Running: - self.animation.stop() - oldValue = self.value() - self.animation.setStartValue(oldValue) - if not time: - self.animation.setDuration(self.scrollTime) + self.ani.stop() + # + # def Scroll(self, value, time=0): + # if self.ani.state() == QAbstractAnimation.State.Running: + # self.ani.stop() + # oldValue = self.value() + # self.ani.setStartValue(oldValue) + # if not time: + # self.ani.setDuration(self.scrollTime) + # else: + # self.ani.setDuration(time) + # self.ani.setEndValue(oldValue + value) + # self.ani.start() + + def ForceSetValue(self, value): + self.ani.stop() + # print("force setV, {}".format(value)) + QScrollBar.setValue(self, value) + self.StartAni(value, value, self.scrollTime) + + def StartAni(self, start, end, duration): + self.lastAniEndV = end + self.lastAniStartV = start + self.lastScrollTime = duration + self.ani.setStartValue(start) + self.ani.setEndValue(end) + self.ani.setDuration(duration) + self.ani.start() + + def setValue(self, value: int): + # print("setV, {}".format(value)) + if value == self.value(): + return + + # stop running animation + if self.ani.state() == self.ani.State.Running: + self.AniValueByAdd(value-self.value()) + else: + self.ani.stop() + self.StartAni(self.value(), value, self.scrollTime) + + def scrollValue(self, value: int): + """ scroll the specified distance """ + # self.__value += value + # self.__value = max(self.minimum(), self.__value) + # self.__value = min(self.maximum(), self.__value) + self.setValue(self.value()+value) + + # def scrollTo(self, value: int): + # """ scroll to the specified position """ + # self.__value = value + # self.__value = max(self.minimum(), self.__value) + # self.__value = min(self.maximum(), self.__value) + # self.setValue(self.__value) + + def OnValueChange(self, value): + addValue = value - self.__value + # self.UpdateScrollBar(value) + self.__value = value + + if not ReadMode.isScroll(self.scrollArea.initReadMode): + return + + curPictureSize = self.labelSize.get(self.readImg.curIndex) + nextPictureSize = self.labelSize.get(self.readImg.curIndex + 1, 0) + changeIndex = self.readImg.curIndex + if self.scrollArea.initReadMode == ReadMode.RightLeftScroll: + newValue = value + self.scrollArea.width() + while True: + ## 切换上一图片 + if addValue > 0 and newValue >= nextPictureSize: + if changeIndex <= 0: + break + changeIndex -= 1 + # print(self.readImg.curIndex) + + # self.scrollArea.changeLastPage.emit(self.readImg.curIndex) + + ## 切换下一图片 + elif addValue < 0 and newValue < curPictureSize: + if changeIndex >= self.readImg.maxPic - 1: + break + changeIndex += 1 + # print(self.readImg.curIndex) + # self.scrollArea.changeNextPage.emit(self.readImg.curIndex) + else: + break + curPictureSize = self.labelSize.get(changeIndex) + nextPictureSize = self.labelSize.get(changeIndex + 1, 0) else: - self.animation.setDuration(time) - self.animation.setEndValue(oldValue + value) - self.animation.start() + while True: + ## 切换上一图片 + if addValue < 0 and value < curPictureSize: + if changeIndex <= 0: + break + changeIndex -= 1 + # print("last page, addv:{}, val:{}, cur:{}, next:{}".format(addValue, value, curPictureSize, nextPictureSize)) + # self.scrollArea.changeLastPage.emit(self.readImg.curIndex) + ## 切换下一图片 + elif addValue > 0 and value >= nextPictureSize: + if changeIndex >= self.readImg.maxPic - 1: + break + changeIndex += 1 + # print("next page, addv:{}, val:{}, cur:{}, next:{}".format(addValue, value, curPictureSize, nextPictureSize)) + # self.scrollArea.changeNextPage.emit(self.readImg.curIndex) + else: + break + curPictureSize = self.labelSize.get(changeIndex) + nextPictureSize = self.labelSize.get(changeIndex + 1, 0) + if self.readImg.curIndex == changeIndex: + return + elif self.readImg.curIndex > changeIndex: + self.readImg.curIndex = changeIndex + self.scrollArea.changeLastPage.emit(self.readImg.curIndex) + elif self.readImg.curIndex < changeIndex: + self.readImg.curIndex = changeIndex + self.scrollArea.changeNextPage.emit(self.readImg.curIndex) + return diff --git a/src/component/scroll/smooth_scroll.py b/src/component/scroll/smooth_scroll.py index 11723e4..de6caaa 100644 --- a/src/component/scroll/smooth_scroll.py +++ b/src/component/scroll/smooth_scroll.py @@ -19,35 +19,36 @@ def __init__(self): self.lastWheelEvent = None self.scrollStamps = deque() self.stepsLeftQueue = deque() - self.smoothMoveTimer = QTimer(self) + + # self.smoothMoveTimer = QTimer(self) self.smoothMode = SmoothMode(SmoothMode.LINEAR) - self.smoothMoveTimer.timeout.connect(self.__smoothMove) + # self.smoothMoveTimer.timeout.connect(self.__smoothMove) self.qEventParam = [] def setSMoothMode(self, smoothMode): """ 设置滚动模式 """ self.smoothMode = smoothMode - def wheelEvent(self, e): - # 将当前时间点插入队尾 - now = QDateTime.currentDateTime().toMSecsSinceEpoch() - self.scrollStamps.append(now) - while now - self.scrollStamps[0] > 500: - self.scrollStamps.popleft() - # 根据未处理完的事件调整移动速率增益 - accerationRatio = min(len(self.scrollStamps) / 15, 1) - self.qEventParam = (e.position(), e.globalPosition(), e.buttons()) - # 计算步数 - self.stepsTotal = self.fps * self.duration / 1000 - # 计算每一个事件对应的移动距离 - delta = e.angleDelta().y() * self.stepRatio - if self.acceleration > 0: - delta += delta * self.acceleration * accerationRatio - # 将移动距离和步数组成列表,插入队列等待处理 - self.stepsLeftQueue.append([delta, self.stepsTotal]) - # 定时器的溢出时间t=1000ms/帧数 - self.smoothMoveTimer.start(1000 // self.fps) - return False + # def wheelEvent(self, e): + # # 将当前时间点插入队尾 + # now = QDateTime.currentDateTime().toMSecsSinceEpoch() + # self.scrollStamps.append(now) + # while now - self.scrollStamps[0] > 500: + # self.scrollStamps.popleft() + # # 根据未处理完的事件调整移动速率增益 + # accerationRatio = min(len(self.scrollStamps) / 15, 1) + # self.qEventParam = (e.position(), e.globalPosition(), e.buttons()) + # # 计算步数 + # self.stepsTotal = self.fps * self.duration / 1000 + # # 计算每一个事件对应的移动距离 + # delta = e.angleDelta().y() * self.stepRatio + # if self.acceleration > 0: + # delta += delta * self.acceleration * accerationRatio + # # 将移动距离和步数组成列表,插入队列等待处理 + # self.stepsLeftQueue.append([delta, self.stepsTotal]) + # # 定时器的溢出时间t=1000ms/帧数 + # self.smoothMoveTimer.start(1000 // self.fps) + # return False def __smoothMove(self): """ 计时器溢出时进行平滑滚动 """ diff --git a/src/component/widget/comic_item_widget.py b/src/component/widget/comic_item_widget.py index cb7bb5f..97e5ddd 100644 --- a/src/component/widget/comic_item_widget.py +++ b/src/component/widget/comic_item_widget.py @@ -1,5 +1,5 @@ from PySide6.QtCore import Qt, QSize, Signal -from PySide6.QtGui import QPixmap, QIcon, QFont +from PySide6.QtGui import QPixmap, QIcon, QFont, QFontMetrics from PySide6.QtWidgets import QWidget from config import config @@ -21,9 +21,9 @@ def __init__(self, isCategory=False): self.picNum = 0 self.category = "" + self.index = 0 self.url = "" self.path = "" - self.index = 0 # TODO 如何自适应 if not isCategory: rate = Setting.CoverSize.value @@ -75,8 +75,66 @@ def __init__(self, isCategory=False): self.isWaifu2xLoading = False self.isLoadPicture = False + def SetTitle(self, title, fontColor): + self.title = title + if Setting.NotCategoryShow.value: + self.categoryLabel.setVisible(False) + + if Setting.TitleLine.value == 0: + self.nameLable.setVisible(False) + elif Setting.TitleLine.value == 1: + self.nameLable.setWordWrap(False) + self.nameLable.setText(title + fontColor) + elif Setting.TitleLine.value > 3: + self.nameLable.setText(title+fontColor) + else: + title2 = self.ElidedLineText(fontColor) + self.nameLable.setText(title2) + + def ElidedLineText(self, fontColor): + line = Setting.TitleLine.value + if line <= 0 : + line = 2 + f = QFontMetrics(self.nameLable.font()) + if (line == 1): + return f.elidedText(self.title + fontColor, Qt.ElideRight, self.nameLable.maximumWidth()) + + strList = [] + start = 0 + isEnd = False + for i in range(1, len(self.title)): + if f.boundingRect(self.title[start:i]).width() >= self.nameLable.maximumWidth()-10: + strList.append(self.title[start:i]) + if len(strList) >= line: + isEnd = True + break + start = i + + if not isEnd: + strList.append(self.title[start:]) + + if not strList: + strList.append(self.title) + + # strList[-1] = strList[-1] + fontColor + + hasElided = True + endIndex = len(strList) - 1 + endString = strList[endIndex] + if f.boundingRect(endString).width() < self.nameLable.maximumWidth() -10: + strList[endIndex] += fontColor + hasElided = False + + if (hasElided): + if len(endString) > 8 : + endString = endString[0:len(endString) - 8] + "..." + fontColor + strList[endIndex] = endString + else: + strList[endIndex] += fontColor + return "".join(strList) + def GetTitle(self): - return self.nameLable.text() + return self.title def SetPicture(self, data): self.picData = data @@ -103,8 +161,8 @@ def SetWaifu2xData(self, data): newPic = pic.scaled(self.picLabel.width()*radio, self.picLabel.height()*radio, Qt.KeepAspectRatio, Qt.SmoothTransformation) self.picLabel.setPixmap(newPic) - def SetPictureErr(self, st): - self.picLabel.setText(Str.GetStr(st)) + def SetPictureErr(self, status): + self.picLabel.setText(Str.GetStr(status)) def paintEvent(self, event) -> None: if self.url and not self.isLoadPicture and config.IsLoadingPicture: diff --git a/src/component/widget/main_widget.py b/src/component/widget/main_widget.py index ee8ea05..7cbe542 100644 --- a/src/component/widget/main_widget.py +++ b/src/component/widget/main_widget.py @@ -10,14 +10,15 @@ Main = None -if sys.platform == "win32" and Setting.IsUseTitleBar.value: +if Setting.IsUseTitleBar.value: try: from interface.ui_main_windows import Ui_MainWindows - from .windows.frame_less_widget import FrameLessWidget + # from .windows.frame_less_widget import FrameLessWidget + from .qframelesswindow import FramelessMainWindow - class MainWidget(FrameLessWidget, Ui_MainWindows): + class MainWidget(FramelessMainWindow, Ui_MainWindows): def __init__(self): - FrameLessWidget.__init__(self) + FramelessMainWindow.__init__(self) Ui_MainWindows.__init__(self) self.setupUi(self) self.totalStackWidget.setAttribute(Qt.WA_TranslucentBackground) @@ -26,17 +27,17 @@ def __init__(self): def showFullScreen(self): self.widget.setVisible(False) self.verticalLayout.setContentsMargins(0, 0, 0, 0) - return FrameLessWidget.showFullScreen(self) + return FramelessMainWindow.showFullScreen(self) def showNormal(self): self.widget.setVisible(True) self.verticalLayout.setContentsMargins(3, 3, 3, 3) - return FrameLessWidget.showNormal(self) + return FramelessMainWindow.showNormal(self) def showMaximized(self): self.widget.setVisible(True) self.verticalLayout.setContentsMargins(3, 3, 3, 3) - return FrameLessWidget.showMaximized(self) + return FramelessMainWindow.showMaximized(self) def setSubTitle(self, text): self.widget.subTitle.setText(text) diff --git a/src/component/widget/navigation_widget.py b/src/component/widget/navigation_widget.py index d70b227..a6f4c68 100644 --- a/src/component/widget/navigation_widget.py +++ b/src/component/widget/navigation_widget.py @@ -20,7 +20,9 @@ def __init__(self, parent=None): super().__init__(parent) QtTaskBase.__init__(self) self.setupUi(self) - self.resize(260, 800) + if Setting.IsUseTitleBar.value: + self.scrollArea.setFixedHeight(300) + # self.resize(260, 800) self.__ani = QPropertyAnimation(self, b"geometry") self.__connect = None self.pictureData = "" @@ -44,6 +46,12 @@ def __init__(self, parent=None): propertiesOne.setScrollMetric(QScrollerProperties.VerticalOvershootPolicy, QScrollerProperties.OvershootAlwaysOff) propertiesOne.setScrollMetric(QScrollerProperties.HorizontalOvershootPolicy, QScrollerProperties.OvershootAlwaysOff) QScroller.scroller(self.scrollArea).setScrollerProperties(propertiesOne) + self.proxyImgName.clicked.connect(self.OpenProxy) + self.proxyName.clicked.connect(self.OpenProxy) + + def OpenProxy(self): + QtOwner().OpenProxy() + self.UpdateProxyName() def SwitchOffline(self, state): QtOwner().isOfflineModel = state diff --git a/src/component/widget/qframelesswindow/__init__.py b/src/component/widget/qframelesswindow/__init__.py new file mode 100644 index 0000000..c3c145f --- /dev/null +++ b/src/component/widget/qframelesswindow/__init__.py @@ -0,0 +1,53 @@ +""" +PyQt5-Frameless-Window +====================== +A cross-platform frameless window based on pyqt5, support Win32, Linux and macOS. + +Documentation is available in the docstrings and +online at https://pyqt-frameless-window.readthedocs.io. + +Examples are available at https://github.com/zhiyiYo/PyQt-Frameless-Window/tree/master/examples. + +:copyright: (c) 2021 by zhiyiYo. +:license: GPLv3, see LICENSE for more details. +""" + +__version__ = "0.3.8" + +import sys + +from PySide6.QtWidgets import QDialog, QMainWindow + +from .titlebar import TitleBar, TitleBarButton, SvgTitleBarButton, StandardTitleBar, TitleBarBase + +if sys.platform == "win32": + from .windows import AcrylicWindow + from .windows import WindowsFramelessWindow as FramelessWindow + from .windows import WindowsWindowEffect as WindowEffect +elif sys.platform == "darwin": + from .mac import AcrylicWindow + from .mac import MacFramelessWindow as FramelessWindow + from .mac import MacWindowEffect as WindowEffect +else: + from .linux import LinuxFramelessWindow as FramelessWindow + from .linux import LinuxWindowEffect as WindowEffect + + AcrylicWindow = FramelessWindow + + +class FramelessDialog(QDialog, FramelessWindow): + """ Frameless dialog """ + + def __init__(self, parent=None): + FramelessWindow.__init__(self, parent) + self.titleBar.minBtn.hide() + self.titleBar.maxBtn.hide() + self.titleBar.setDoubleClickEnabled(False) + self.windowEffect.disableMaximizeButton(self.winId()) + + +class FramelessMainWindow(QMainWindow, FramelessWindow): + """ Frameless main window """ + + def __init__(self, parent=None): + FramelessWindow.__init__(self, parent) \ No newline at end of file diff --git a/src/component/widget/qframelesswindow/_rc/__init__.py b/src/component/widget/qframelesswindow/_rc/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/component/widget/qframelesswindow/_rc/resource.py b/src/component/widget/qframelesswindow/_rc/resource.py new file mode 100644 index 0000000..7594bd7 --- /dev/null +++ b/src/component/widget/qframelesswindow/_rc/resource.py @@ -0,0 +1,73 @@ +# -*- coding: utf-8 -*- + +# Resource object code +# +# Created by: The Resource Compiler for PyQt5 (Qt v5.15.2) +# +# WARNING! All changes made in this file will be lost! + +from PySide6 import QtCore + +qt_resource_data = b"\ +\x00\x00\x01\x0e\ +\x3c\ +\x73\x76\x67\x20\x77\x69\x64\x74\x68\x3d\x22\x34\x35\x70\x74\x22\ +\x20\x68\x65\x69\x67\x68\x74\x3d\x22\x33\x30\x70\x74\x22\x20\x76\ +\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\x31\x22\x20\x76\x69\x65\ +\x77\x42\x6f\x78\x3d\x22\x30\x20\x30\x20\x31\x35\x2e\x38\x37\x35\ +\x20\x31\x30\x2e\x35\x38\x33\x22\x20\x78\x6d\x6c\x6e\x73\x3d\x22\ +\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\ +\x67\x2f\x32\x30\x30\x30\x2f\x73\x76\x67\x22\x3e\x0d\x0a\x20\x3c\ +\x67\x20\x66\x69\x6c\x6c\x3d\x22\x6e\x6f\x6e\x65\x22\x20\x73\x74\ +\x72\x6f\x6b\x65\x3d\x22\x23\x30\x30\x30\x22\x20\x73\x74\x72\x6f\ +\x6b\x65\x2d\x77\x69\x64\x74\x68\x3d\x22\x2e\x31\x37\x36\x33\x39\ +\x22\x3e\x0d\x0a\x20\x20\x3c\x70\x61\x74\x68\x20\x64\x3d\x22\x6d\ +\x36\x2e\x31\x32\x39\x35\x20\x33\x2e\x36\x36\x30\x31\x20\x33\x2e\ +\x32\x36\x33\x32\x20\x33\x2e\x32\x36\x33\x32\x7a\x22\x2f\x3e\x0d\ +\x0a\x20\x20\x3c\x70\x61\x74\x68\x20\x64\x3d\x22\x6d\x39\x2e\x33\ +\x39\x32\x37\x20\x33\x2e\x36\x36\x30\x31\x2d\x33\x2e\x32\x36\x33\ +\x32\x20\x33\x2e\x32\x36\x33\x32\x7a\x22\x2f\x3e\x0d\x0a\x20\x3c\ +\x2f\x67\x3e\x0d\x0a\x3c\x2f\x73\x76\x67\x3e\x0d\x0a\ +" + +qt_resource_name = b"\ +\x00\x10\ +\x0a\xb5\xe4\x07\ +\x00\x71\ +\x00\x66\x00\x72\x00\x61\x00\x6d\x00\x65\x00\x6c\x00\x65\x00\x73\x00\x73\x00\x77\x00\x69\x00\x6e\x00\x64\x00\x6f\x00\x77\ +\x00\x09\ +\x06\x98\x8e\xa7\ +\x00\x63\ +\x00\x6c\x00\x6f\x00\x73\x00\x65\x00\x2e\x00\x73\x00\x76\x00\x67\ +" + +qt_resource_struct_v1 = b"\ +\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\ +\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x02\ +\x00\x00\x00\x26\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\ +" + +qt_resource_struct_v2 = b"\ +\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\ +\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x02\ +\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x26\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\ +\x00\x00\x01\x85\x86\x8c\x35\x15\ +" + +qt_version = [int(v) for v in QtCore.qVersion().split('.')] +if qt_version < [5, 8, 0]: + rcc_version = 1 + qt_resource_struct = qt_resource_struct_v1 +else: + rcc_version = 2 + qt_resource_struct = qt_resource_struct_v2 + +def qInitResources(): + QtCore.qRegisterResourceData(rcc_version, qt_resource_struct, qt_resource_name, qt_resource_data) + +def qCleanupResources(): + QtCore.qUnregisterResourceData(rcc_version, qt_resource_struct, qt_resource_name, qt_resource_data) + +qInitResources() diff --git a/src/component/widget/qframelesswindow/_rc/resource.qrc b/src/component/widget/qframelesswindow/_rc/resource.qrc new file mode 100644 index 0000000..8c60155 --- /dev/null +++ b/src/component/widget/qframelesswindow/_rc/resource.qrc @@ -0,0 +1,5 @@ + + + title_bar/close.svg + + \ No newline at end of file diff --git a/src/component/widget/qframelesswindow/_rc/title_bar/close.svg b/src/component/widget/qframelesswindow/_rc/title_bar/close.svg new file mode 100644 index 0000000..6f1f03b --- /dev/null +++ b/src/component/widget/qframelesswindow/_rc/title_bar/close.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/component/widget/qframelesswindow/demo.py b/src/component/widget/qframelesswindow/demo.py new file mode 100644 index 0000000..578b03f --- /dev/null +++ b/src/component/widget/qframelesswindow/demo.py @@ -0,0 +1,73 @@ +# coding:utf-8 +import sys + +from PySide6.QtCore import Qt +from PySide6.QtGui import QColor, QPixmap, QIcon +from PySide6.QtWidgets import QApplication, QLabel + +from component.widget.qframelesswindow import FramelessWindow, StandardTitleBar + + +class CustomTitleBar(StandardTitleBar): + """ Custom title bar """ + + def __init__(self, parent): + super().__init__(parent) + + # customize the style of title bar button + self.minBtn.setHoverColor(Qt.white) + self.minBtn.setHoverBackgroundColor(QColor(0, 100, 182)) + self.minBtn.setPressedColor(Qt.white) + self.minBtn.setPressedBackgroundColor(QColor(54, 57, 65)) + + # use qss to customize title bar button + self.maxBtn.setStyleSheet(""" + TitleBarButton { + qproperty-hoverColor: white; + qproperty-hoverBackgroundColor: rgb(0, 100, 182); + qproperty-pressedColor: white; + qproperty-pressedBackgroundColor: rgb(54, 57, 65); + } + """) + + +class Window(FramelessWindow): + + def __init__(self, parent=None): + super().__init__(parent=parent) + # change the default title bar if you like + self.setTitleBar(CustomTitleBar(self)) + + self.label = QLabel(self) + self.label.setScaledContents(True) + self.label.setPixmap(QPixmap("screenshot/shoko.png")) + + self.setWindowIcon(QIcon("screenshot/logo.png")) + self.setWindowTitle("PyQt-Frameless-Window") + self.setStyleSheet("background:white") + + self.titleBar.raise_() + + def resizeEvent(self, e): + # don't forget to call the resizeEvent() of super class + super().resizeEvent(e) + length = min(self.width(), self.height()) + self.label.resize(length, length) + self.label.move( + self.width() // 2 - length // 2, + self.height() // 2 - length // 2 + ) + + +if __name__ == "__main__": + # enable dpi scale + QApplication.setHighDpiScaleFactorRoundingPolicy( + Qt.HighDpiScaleFactorRoundingPolicy.PassThrough) + QApplication.setAttribute(Qt.AA_EnableHighDpiScaling) + QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps) + + # run app + app = QApplication(sys.argv) + demo = Window() + demo.show() + sys.exit(app.exec()) diff --git a/src/component/widget/qframelesswindow/linux/__init__.py b/src/component/widget/qframelesswindow/linux/__init__.py new file mode 100644 index 0000000..35e5ba8 --- /dev/null +++ b/src/component/widget/qframelesswindow/linux/__init__.py @@ -0,0 +1,85 @@ +# coding:utf-8 +from PySide6.QtCore import QCoreApplication, QEvent, Qt +from PySide6.QtGui import QMouseEvent +from PySide6.QtWidgets import QWidget + +from ..titlebar import TitleBar +from ..utils.linux_utils import LinuxMoveResize +from .window_effect import LinuxWindowEffect + + +class LinuxFramelessWindow(QWidget): + """ Frameless window for Linux system """ + + BORDER_WIDTH = 5 + + def __init__(self, parent=None): + super().__init__(parent=parent) + self.windowEffect = LinuxWindowEffect(self) + self.titleBar = TitleBar(self) + self._isResizeEnabled = True + + self.updateFrameless() + QCoreApplication.instance().installEventFilter(self) + + self.titleBar.raise_() + self.resize(500, 500) + + def resizeEvent(self, e): + super().resizeEvent(e) + self.titleBar.resize(self.width(), self.titleBar.height()) + + def updateFrameless(self): + self.setWindowFlags(self.windowFlags() | Qt.FramelessWindowHint) + + def setTitleBar(self, titleBar): + """ set custom title bar + + Parameters + ---------- + titleBar: TitleBar + title bar + """ + self.titleBar.deleteLater() + self.titleBar.hide() + self.titleBar = titleBar + self.titleBar.setParent(self) + self.titleBar.raise_() + + def setResizeEnabled(self, isEnabled: bool): + """ set whether resizing is enabled """ + self._isResizeEnabled = isEnabled + + def eventFilter(self, obj, event): + et = event.type() + if et != QEvent.MouseButtonPress and et != QEvent.MouseMove or not self._isResizeEnabled: + return False + + edges = Qt.Edges() + pos = QMouseEvent(event).globalPos() - self.pos() + if pos.x() < self.BORDER_WIDTH: + edges |= Qt.LeftEdge + if pos.x() >= self.width()-self.BORDER_WIDTH: + edges |= Qt.RightEdge + if pos.y() < self.BORDER_WIDTH: + edges |= Qt.TopEdge + if pos.y() >= self.height()-self.BORDER_WIDTH: + edges |= Qt.BottomEdge + + # change cursor + if et == QEvent.MouseMove and self.windowState() == Qt.WindowNoState: + if edges in (Qt.LeftEdge | Qt.TopEdge, Qt.RightEdge | Qt.BottomEdge): + self.setCursor(Qt.SizeFDiagCursor) + elif edges in (Qt.RightEdge | Qt.TopEdge, Qt.LeftEdge | Qt.BottomEdge): + self.setCursor(Qt.SizeBDiagCursor) + elif edges in (Qt.TopEdge, Qt.BottomEdge): + self.setCursor(Qt.SizeVerCursor) + elif edges in (Qt.LeftEdge, Qt.RightEdge): + self.setCursor(Qt.SizeHorCursor) + else: + self.setCursor(Qt.ArrowCursor) + + elif obj in (self, self.titleBar) and et == QEvent.MouseButtonPress and edges: + LinuxMoveResize.starSystemResize(self, event.globalPos(), edges) + + return super().eventFilter(obj, event) diff --git a/src/component/widget/qframelesswindow/linux/window_effect.py b/src/component/widget/qframelesswindow/linux/window_effect.py new file mode 100644 index 0000000..372abfb --- /dev/null +++ b/src/component/widget/qframelesswindow/linux/window_effect.py @@ -0,0 +1,141 @@ +# coding:utf-8 + +class LinuxWindowEffect: + """ Linux window effect """ + + def __init__(self, window): + self.window = window + + def setAcrylicEffect(self, hWnd, gradientColor="F2F2F230", isEnableShadow=True, animationId=0): + """ set acrylic effect for window + + Parameter + ---------- + hWnd: int or `sip.voidptr` + window handle + + gradientColor: str + hexadecimal acrylic mixed color, corresponding to RGBA components + + isEnableShadow: bool + whether to enable window shadow + + animationId: int + turn on blur animation or not + """ + pass + + def setMicaEffect(self, hWnd, isDarkMode=False, isAlt=False): + """ Add mica effect to the window (Win11 only) + + Parameters + ---------- + hWnd: int or `sip.voidptr` + Window handle + + isDarkMode: bool + whether to use dark mode mica effect + + isAlt: bool + whether to use mica alt effect + """ + pass + + def setAeroEffect(self, hWnd): + """ add Aero effect to the window + + Parameter + ---------- + hWnd: int or `sip.voidptr` + Window handle + """ + pass + + def setTransparentEffect(self, hWnd): + """ set transparent effect for window + + Parameters + ---------- + hWnd : int or `sip.voidptr` + Window handle + """ + pass + + def removeBackgroundEffect(self, hWnd): + """ Remove background effect + + Parameters + ---------- + hWnd : int or `sip.voidptr` + Window handle + """ + pass + + def addShadowEffect(self, hWnd): + """ add shadow to window + + Parameter + ---------- + hWnd: int or `sip.voidptr` + Window handle + """ + pass + + def addMenuShadowEffect(self, hWnd): + """ add shadow to menu + + Parameter + ---------- + hWnd: int or `sip.voidptr` + Window handle + """ + pass + + @staticmethod + def removeMenuShadowEffect(hWnd): + """ Remove shadow from pop-up menu + + Parameters + ---------- + hWnd: int or `sip.voidptr` + Window handle + """ + pass + + def removeShadowEffect(self, hWnd): + """ Remove shadow from the window + + Parameters + ---------- + hWnd: int or `sip.voidptr` + Window handle + """ + pass + + @staticmethod + def addWindowAnimation(hWnd): + """ Enables the maximize and minimize animation of the window + + Parameters + ---------- + hWnd : int or `sip.voidptr` + Window handle + """ + + @staticmethod + def disableMaximizeButton(hWnd): + """ Disable the maximize button of window + + Parameters + ---------- + hWnd : int or `sip.voidptr` + Window handle + """ + + def enableBlurBehindWindow(self, hWnd): + """ enable the blur effect behind the whole client + Parameters + ---------- + hWnd: int or `sip.voidptr` + Window handle + """ \ No newline at end of file diff --git a/src/component/widget/qframelesswindow/mac/__init__.py b/src/component/widget/qframelesswindow/mac/__init__.py new file mode 100644 index 0000000..7ae8517 --- /dev/null +++ b/src/component/widget/qframelesswindow/mac/__init__.py @@ -0,0 +1,93 @@ +# coding:utf-8 +import Cocoa +import objc +from PySide6.QtCore import QEvent, Qt +from PySide6.QtWidgets import QWidget + +from ..titlebar import TitleBar +from .window_effect import MacWindowEffect + + +class MacFramelessWindow(QWidget): + """ Frameless window for Linux system """ + + def __init__(self, parent=None): + super().__init__(parent=parent) + self.windowEffect = MacWindowEffect(self) + # must enable acrylic effect before creating title bar + if isinstance(self, AcrylicWindow): + self.windowEffect.setAcrylicEffect(self.winId()) + + self.titleBar = TitleBar(self) + self._isResizeEnabled = True + + self.updateFrameless() + + self.resize(500, 500) + self.titleBar.raise_() + + def updateFrameless(self): + """ update frameless window """ + view = objc.objc_object(c_void_p=self.winId().__int__()) + self.__nsWindow = view.window() + + # hide system title bar + self.__hideSystemTitleBar() + + def setTitleBar(self, titleBar): + """ set custom title bar + + Parameters + ---------- + titleBar: TitleBar + title bar + """ + self.titleBar.deleteLater() + self.titleBar.hide() + self.titleBar = titleBar + self.titleBar.setParent(self) + self.titleBar.raise_() + + def setResizeEnabled(self, isEnabled: bool): + """ set whether resizing is enabled """ + self._isResizeEnabled = isEnabled + + def resizeEvent(self, e): + super().resizeEvent(e) + self.titleBar.resize(self.width(), self.titleBar.height()) + + def paintEvent(self, e): + super().paintEvent(e) + self.__hideSystemTitleBar() + + def changeEvent(self, event): + super().changeEvent(event) + if event.type() == QEvent.WindowStateChange: + self.__hideSystemTitleBar() + + def __hideSystemTitleBar(self): + # extend view to title bar region + self.__nsWindow.setStyleMask_( + self.__nsWindow.styleMask() | Cocoa.NSFullSizeContentViewWindowMask) + self.__nsWindow.setTitlebarAppearsTransparent_(True) + + # disable the moving behavior of system + self.__nsWindow.setMovableByWindowBackground_(False) + self.__nsWindow.setMovable_(False) + + # hide title bar buttons and title + self.__nsWindow.setShowsToolbarButton_(False) + self.__nsWindow.setTitleVisibility_(Cocoa.NSWindowTitleHidden) + self.__nsWindow.standardWindowButton_(Cocoa.NSWindowCloseButton).setHidden_(True) + self.__nsWindow.standardWindowButton_(Cocoa.NSWindowZoomButton).setHidden_(True) + self.__nsWindow.standardWindowButton_(Cocoa.NSWindowMiniaturizeButton).setHidden_(True) + + +class AcrylicWindow(MacFramelessWindow): + """ A frameless window with acrylic effect """ + + def __init__(self, parent=None): + super().__init__(parent) + self.setAttribute(Qt.WA_TranslucentBackground) + self.windowEffect.setAcrylicEffect(self.winId()) + self.setStyleSheet("background: transparent") diff --git a/src/component/widget/qframelesswindow/mac/window_effect.py b/src/component/widget/qframelesswindow/mac/window_effect.py new file mode 100644 index 0000000..56cf202 --- /dev/null +++ b/src/component/widget/qframelesswindow/mac/window_effect.py @@ -0,0 +1,162 @@ +# coding:utf-8 +import objc +import Cocoa +from PySide6.QtWidgets import QMacCocoaViewContainer +from ..utils.mac_utils import getNSWindow + +class MacWindowEffect: + """ Mac OS window effect """ + + def __init__(self, window): + self.window = window + + def setAcrylicEffect(self, hWnd, gradientColor="F2F2F230", isEnableShadow=True, animationId=0): + """ set acrylic effect for window + + Parameter + ---------- + hWnd: int or `sip.voidptr` + window handle + + gradientColor: str + hexadecimal acrylic mixed color, corresponding to RGBA components + + isEnableShadow: bool + whether to enable window shadow + + animationId: int + turn on blur animation or not + """ + frame = Cocoa.NSMakeRect( + 0, 0, self.window.width(), self.window.height()) + visualEffectView = Cocoa.NSVisualEffectView.new() + visualEffectView.setAutoresizingMask_( + Cocoa.NSViewWidthSizable | Cocoa.NSViewHeightSizable) # window resizable + visualEffectView.setFrame_(frame) + visualEffectView.setState_(Cocoa.NSVisualEffectStateActive) + + # https://developer.apple.com/documentation/appkit/nsvisualeffectmaterial + visualEffectView.setMaterial_(Cocoa.NSVisualEffectMaterialPopover) + visualEffectView.setBlendingMode_( + Cocoa.NSVisualEffectBlendingModeBehindWindow) + + nsWindow = getNSWindow(self.window.winId()) + content = nsWindow.contentView() + container = QMacCocoaViewContainer(0, self.window) + content.addSubview_positioned_relativeTo_( + visualEffectView, Cocoa.NSWindowBelow, container) + + def setMicaEffect(self, hWnd, isDarkMode=False, isAlt=False): + """ Add mica effect to the window (Win11 only) + + Parameters + ---------- + hWnd: int or `sip.voidptr` + Window handle + + isDarkMode: bool + whether to use dark mode mica effect + + isAlt: bool + whether to use mica alt effect + """ + self.setAcrylicEffect(hWnd) + + def setAeroEffect(self, hWnd): + """ add Aero effect to the window + + Parameter + ---------- + hWnd: int or `sip.voidptr` + Window handle + """ + self.setAcrylicEffect(hWnd) + + def setTransparentEffect(self, hWnd): + """ set transparent effect for window + + Parameters + ---------- + hWnd : int or `sip.voidptr` + Window handle + """ + pass + + def removeBackgroundEffect(self, hWnd): + """ Remove background effect + + Parameters + ---------- + hWnd : int or `sip.voidptr` + Window handle + """ + pass + + def addShadowEffect(self, hWnd): + """ add shadow to window + + Parameter + ---------- + hWnd: int or `sip.voidptr` + Window handle + """ + getNSWindow(hWnd).setHasShadow_(True) + + def addMenuShadowEffect(self, hWnd): + """ add shadow to menu + + Parameter + ---------- + hWnd: int or `sip.voidptr` + Window handle + """ + self.addShadowEffect(hWnd) + + @staticmethod + def removeMenuShadowEffect(hWnd): + """ Remove shadow from pop-up menu + + Parameters + ---------- + hWnd: int or `sip.voidptr` + Window handle + """ + getNSWindow(hWnd).setHasShadow_(False) + + def removeShadowEffect(self, hWnd): + """ Remove shadow from the window + + Parameters + ---------- + hWnd: int or `sip.voidptr` + Window handle + """ + getNSWindow(hWnd).setHasShadow_(False) + + @staticmethod + def addWindowAnimation(hWnd): + """ Enables the maximize and minimize animation of the window + + Parameters + ---------- + hWnd : int or `sip.voidptr` + Window handle + """ + + @staticmethod + def disableMaximizeButton(hWnd): + """ Disable the maximize button of window + + Parameters + ---------- + hWnd : int or `sip.voidptr` + Window handle + """ + + def enableBlurBehindWindow(self, hWnd): + """ enable the blur effect behind the whole client + Parameters + ---------- + hWnd: int or `sip.voidptr` + Window handle + """ \ No newline at end of file diff --git a/src/component/widget/qframelesswindow/titlebar/__init__.py b/src/component/widget/qframelesswindow/titlebar/__init__.py new file mode 100644 index 0000000..1c7cdcf --- /dev/null +++ b/src/component/widget/qframelesswindow/titlebar/__init__.py @@ -0,0 +1,157 @@ +# coding:utf-8 +import sys + +from PySide6.QtCore import QEvent, Qt +from PySide6.QtGui import QIcon +from PySide6.QtWidgets import QHBoxLayout, QLabel, QWidget + +from ..utils import startSystemMove +from .title_bar_buttons import (CloseButton, MaximizeButton, MinimizeButton, + SvgTitleBarButton, TitleBarButton) + + +class TitleBarBase(QWidget): + """ Title bar base class """ + + def __init__(self, parent): + super().__init__(parent) + self.minBtn = MinimizeButton(parent=self) + self.closeBtn = CloseButton(parent=self) + self.maxBtn = MaximizeButton(parent=self) + + self._isDoubleClickEnabled = True + + self.resize(200, 32) + self.setFixedHeight(32) + + # connect signal to slot + self.minBtn.clicked.connect(self.window().showMinimized) + self.maxBtn.clicked.connect(self.__toggleMaxState) + self.closeBtn.clicked.connect(self.window().close) + + self.window().installEventFilter(self) + + def eventFilter(self, obj, e): + if obj is self.window(): + if e.type() == QEvent.WindowStateChange: + self.maxBtn.setMaxState(self.window().isMaximized()) + return False + + return super().eventFilter(obj, e) + + def mouseDoubleClickEvent(self, event): + """ Toggles the maximization state of the window """ + if event.button() != Qt.LeftButton or not self._isDoubleClickEnabled: + return + + self.__toggleMaxState() + + def mouseMoveEvent(self, e): + if sys.platform != "win32" or not self.canDrag(e.pos()): + return + + startSystemMove(self.window(), e.globalPos()) + + def mousePressEvent(self, e): + if sys.platform == "win32" or not self.canDrag(e.pos()): + return + + startSystemMove(self.window(), e.globalPos()) + + def __toggleMaxState(self): + """ Toggles the maximization state of the window and change icon """ + if self.window().isMaximized(): + self.window().showNormal() + else: + self.window().showMaximized() + + def _isDragRegion(self, pos): + """ Check whether the position belongs to the area where dragging is allowed """ + width = 0 + for button in self.findChildren(TitleBarButton): + if button.isVisible(): + width += button.width() + + return 0 < pos.x() < self.width() - width + + def _hasButtonPressed(self): + """ whether any button is pressed """ + return any(btn.isPressed() for btn in self.findChildren(TitleBarButton)) + + def canDrag(self, pos): + """ whether the position is draggable """ + return self._isDragRegion(pos) and not self._hasButtonPressed() + + def setDoubleClickEnabled(self, isEnabled): + """ whether to switch window maximization status when double clicked + + Parameters + ---------- + isEnabled: bool + whether to enable double click + """ + self._isDoubleClickEnabled = isEnabled + + + +class TitleBar(TitleBarBase): + """ Title bar with minimize, maximum and close button """ + + def __init__(self, parent): + super().__init__(parent) + self.hBoxLayout = QHBoxLayout(self) + + # add buttons to layout + self.hBoxLayout.setSpacing(0) + self.hBoxLayout.setContentsMargins(0, 0, 0, 0) + self.hBoxLayout.setAlignment(Qt.AlignVCenter | Qt.AlignLeft) + self.hBoxLayout.addStretch(1) + self.hBoxLayout.addWidget(self.minBtn, 0, Qt.AlignRight) + self.hBoxLayout.addWidget(self.maxBtn, 0, Qt.AlignRight) + self.hBoxLayout.addWidget(self.closeBtn, 0, Qt.AlignRight) + + +class StandardTitleBar(TitleBar): + """ Title bar with icon and title """ + + def __init__(self, parent): + super().__init__(parent) + # add window icon + self.iconLabel = QLabel(self) + self.iconLabel.setFixedSize(20, 20) + self.hBoxLayout.insertSpacing(0, 10) + self.hBoxLayout.insertWidget(1, self.iconLabel, 0, Qt.AlignLeft) + self.window().windowIconChanged.connect(self.setIcon) + + # add title label + self.titleLabel = QLabel(self) + self.hBoxLayout.insertWidget(2, self.titleLabel, 0, Qt.AlignLeft) + self.titleLabel.setStyleSheet(""" + QLabel{ + background: transparent; + font: 13px 'Segoe UI'; + padding: 0 4px + } + """) + self.window().windowTitleChanged.connect(self.setTitle) + + def setTitle(self, title): + """ set the title of title bar + + Parameters + ---------- + title: str + the title of title bar + """ + self.titleLabel.setText(title) + self.titleLabel.adjustSize() + + def setIcon(self, icon): + """ set the icon of title bar + + Parameters + ---------- + icon: QIcon | QPixmap | str + the icon of title bar + """ + self.iconLabel.setPixmap(QIcon(icon).pixmap(20, 20)) diff --git a/src/component/widget/qframelesswindow/titlebar/title_bar_buttons.py b/src/component/widget/qframelesswindow/titlebar/title_bar_buttons.py new file mode 100644 index 0000000..7c52f6d --- /dev/null +++ b/src/component/widget/qframelesswindow/titlebar/title_bar_buttons.py @@ -0,0 +1,305 @@ +# coding:utf-8 +from enum import Enum + +from PySide6.QtCore import QFile, QPointF, QRectF, Qt, Property +from PySide6.QtGui import QColor, QPainter, QPainterPath, QPen +from PySide6.QtWidgets import QAbstractButton +from PySide6.QtSvg import QSvgRenderer +from PySide6.QtXml import QDomDocument + +from .._rc import resource + + +class TitleBarButtonState(Enum): + """ Title bar button state """ + NORMAL = 0 + HOVER = 1 + PRESSED = 2 + + +class TitleBarButton(QAbstractButton): + """ Title bar button """ + + def __init__(self, parent=None): + super().__init__(parent=parent) + self.setCursor(Qt.ArrowCursor) + self.setFixedSize(46, 32) + self._state = TitleBarButtonState.NORMAL + + # icon color + self._normalColor = QColor(0, 0, 0) + self._hoverColor = QColor(0, 0, 0) + self._pressedColor = QColor(0, 0, 0) + + # background color + self._normalBgColor = QColor(0, 0, 0, 0) + self._hoverBgColor = QColor(0, 0, 0, 26) + self._pressedBgColor = QColor(0, 0, 0, 51) + + def setState(self, state): + """ set the state of button + + Parameters + ---------- + state: TitleBarButtonState + the state of button + """ + self._state = state + self.update() + + def isPressed(self): + """ whether the button is pressed """ + return self._state == TitleBarButtonState.PRESSED + + def getNormalColor(self): + """ get the icon color of the button in normal state """ + return self._normalColor + + def getHoverColor(self): + """ get the icon color of the button in hover state """ + return self._hoverColor + + def getPressedColor(self): + """ get the icon color of the button in pressed state """ + return self._pressedColor + + def getNormalBackgroundColor(self): + """ get the background color of the button in normal state """ + return self._normalBgColor + + def getHoverBackgroundColor(self): + """ get the background color of the button in hover state """ + return self._hoverBgColor + + def getPressedBackgroundColor(self): + """ get the background color of the button in pressed state """ + return self._pressedBgColor + + def setNormalColor(self, color): + """ set the icon color of the button in normal state + + Parameters + ---------- + color: QColor + icon color + """ + self._normalColor = QColor(color) + self.update() + + def setHoverColor(self, color): + """ set the icon color of the button in hover state + + Parameters + ---------- + color: QColor + icon color + """ + self._hoverColor = QColor(color) + self.update() + + def setPressedColor(self, color): + """ set the icon color of the button in pressed state + + Parameters + ---------- + color: QColor + icon color + """ + self._pressedColor = QColor(color) + self.update() + + def setNormalBackgroundColor(self, color): + """ set the background color of the button in normal state + + Parameters + ---------- + color: QColor + background color + """ + self._normalBgColor = QColor(color) + self.update() + + def setHoverBackgroundColor(self, color): + """ set the background color of the button in hover state + + Parameters + ---------- + color: QColor + background color + """ + self._hoverBgColor = QColor(color) + self.update() + + def setPressedBackgroundColor(self, color): + """ set the background color of the button in pressed state + + Parameters + ---------- + color: QColor + background color + """ + self._pressedBgColor = QColor(color) + self.update() + + def enterEvent(self, e): + self.setState(TitleBarButtonState.HOVER) + super().enterEvent(e) + + def leaveEvent(self, e): + self.setState(TitleBarButtonState.NORMAL) + super().leaveEvent(e) + + def mousePressEvent(self, e): + if e.button() != Qt.LeftButton: + return + + self.setState(TitleBarButtonState.PRESSED) + super().mousePressEvent(e) + + def _getColors(self): + """ get the icon color and background color """ + if self._state == TitleBarButtonState.NORMAL: + return self._normalColor, self._normalBgColor + elif self._state == TitleBarButtonState.HOVER: + return self._hoverColor, self._hoverBgColor + + return self._pressedColor, self._pressedBgColor + + normalColor = Property(QColor, getNormalColor, setNormalColor) + hoverColor = Property(QColor, getHoverColor, setHoverColor) + pressedColor = Property(QColor, getPressedColor, setPressedColor) + normalBackgroundColor = Property( + QColor, getNormalBackgroundColor, setNormalBackgroundColor) + hoverBackgroundColor = Property( + QColor, getHoverBackgroundColor, setHoverBackgroundColor) + pressedBackgroundColor = Property( + QColor, getPressedBackgroundColor, setPressedBackgroundColor) + + +class SvgTitleBarButton(TitleBarButton): + """ Title bar button using svg icon """ + + def __init__(self, iconPath, parent=None): + """ + Parameters + ---------- + iconPath: str + the path of icon + + parent: QWidget + parent widget + """ + super().__init__(parent) + self._svgDom = QDomDocument() + self.setIcon(iconPath) + + def setIcon(self, iconPath): + """ set the icon of button + + Parameters + ---------- + iconPath: str + the path of icon + """ + f = QFile(iconPath) + f.open(QFile.ReadOnly) + self._svgDom.setContent(f.readAll()) + f.close() + + def paintEvent(self, e): + painter = QPainter(self) + painter.setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform) + color, bgColor = self._getColors() + + # draw background + painter.setBrush(bgColor) + painter.setPen(Qt.NoPen) + painter.drawRect(self.rect()) + + # draw icon + color = color.name() + pathNodes = self._svgDom.elementsByTagName('path') + for i in range(pathNodes.length()): + element = pathNodes.at(i).toElement() + element.setAttribute('stroke', color) + + renderer = QSvgRenderer(self._svgDom.toByteArray()) + renderer.render(painter, QRectF(self.rect())) + + +class MinimizeButton(TitleBarButton): + """ Minimize button """ + + def paintEvent(self, e): + painter = QPainter(self) + color, bgColor = self._getColors() + + # draw background + painter.setBrush(bgColor) + painter.setPen(Qt.NoPen) + painter.drawRect(self.rect()) + + # draw icon + painter.setBrush(Qt.NoBrush) + pen = QPen(color, 1) + pen.setCosmetic(True) + painter.setPen(pen) + painter.drawLine(18, 16, 28, 16) + + +class MaximizeButton(TitleBarButton): + """ Maximize button """ + + def __init__(self, parent=None): + super().__init__(parent) + self._isMax = False + + def setMaxState(self, isMax): + """ update the maximized state and icon """ + if self._isMax == isMax: + return + + self._isMax = isMax + self.setState(TitleBarButtonState.NORMAL) + + def paintEvent(self, e): + painter = QPainter(self) + color, bgColor = self._getColors() + + # draw background + painter.setBrush(bgColor) + painter.setPen(Qt.NoPen) + painter.drawRect(self.rect()) + + # draw icon + painter.setBrush(Qt.NoBrush) + pen = QPen(color, 1) + pen.setCosmetic(True) + painter.setPen(pen) + + r = self.devicePixelRatioF() + painter.scale(1/r, 1/r) + if not self._isMax: + painter.drawRect(int(18*r), int(11*r), int(10*r), int(10*r)) + else: + painter.drawRect(int(18*r), int(13*r), int(8*r), int(8*r)) + x0 = int(18*r)+int(2*r) + y0 = 13*r + dw = int(2*r) + path = QPainterPath(QPointF(x0, y0)) + path.lineTo(x0, y0-dw) + path.lineTo(x0+8*r, y0-dw) + path.lineTo(x0+8*r, y0-dw+8*r) + path.lineTo(x0+8*r-dw, y0-dw+8*r) + painter.drawPath(path) + + +class CloseButton(SvgTitleBarButton): + """ Close button """ + + def __init__(self, parent=None): + super().__init__(":/qframelesswindow/close.svg", parent) + self.setHoverColor(Qt.white) + self.setPressedColor(Qt.white) + self.setHoverBackgroundColor(QColor(232, 17, 35)) + self.setPressedBackgroundColor(QColor(241, 112, 122)) diff --git a/src/component/widget/qframelesswindow/utils/__init__.py b/src/component/widget/qframelesswindow/utils/__init__.py new file mode 100644 index 0000000..8313586 --- /dev/null +++ b/src/component/widget/qframelesswindow/utils/__init__.py @@ -0,0 +1,40 @@ +# coding:utf-8 +import sys + +if sys.platform == "win32": + from .win32_utils import WindowsMoveResize as MoveResize +elif sys.platform == "darwin": + from .mac_utils import MacMoveResize as MoveResize +else: + from .linux_utils import LinuxMoveResize as MoveResize + + +def startSystemMove(window, globalPos): + """ resize window + + Parameters + ---------- + window: QWidget + window + + globalPos: QPoint + the global point of mouse release event + """ + MoveResize.startSystemMove(window, globalPos) + + +def starSystemResize(window, globalPos, edges): + """ resize window + + Parameters + ---------- + window: QWidget + window + + globalPos: QPoint + the global point of mouse release event + + edges: `Qt.Edges` + window edges + """ + MoveResize.starSystemResize(window, globalPos, edges) diff --git a/src/component/widget/qframelesswindow/utils/linux_utils.py b/src/component/widget/qframelesswindow/utils/linux_utils.py new file mode 100644 index 0000000..d6188ba --- /dev/null +++ b/src/component/widget/qframelesswindow/utils/linux_utils.py @@ -0,0 +1,168 @@ +# coding: utf-8 +from enum import Enum + +import xcffib as xcb +from PySide6 import sip +from PySide6.QtCore import QPointF, Qt, QEvent, QPoint +from PySide6.QtGui import QMouseEvent +from PySide6.QtWidgets import QWidget, QApplication +from PySide6.QtX11Extras import QX11Info +from xcffib.xproto import (ButtonIndex, ButtonMask, ButtonReleaseEvent, + ClientMessageData, ClientMessageEvent, EventMask, + xprotoExtension) + + +class WindowMessage(Enum): + """ Window message enum class """ + # refer to: https://specifications.freedesktop.org/wm-spec/1.1/x170.html + _NET_WM_MOVERESIZE_SIZE_TOPLEFT = 0 + _NET_WM_MOVERESIZE_SIZE_TOP = 1 + _NET_WM_MOVERESIZE_SIZE_TOPRIGHT = 2 + _NET_WM_MOVERESIZE_SIZE_RIGHT = 3 + _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT = 4 + _NET_WM_MOVERESIZE_SIZE_BOTTOM = 5 + _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT = 6 + _NET_WM_MOVERESIZE_SIZE_LEFT = 7 + _NET_WM_MOVERESIZE_MOVE = 8 + _NET_WM_MOVERESIZE_SIZE_KEYBOARD = 9 + _NET_WM_MOVERESIZE_MOVE_KEYBOARD = 10 + _NET_WM_MOVERESIZE_CANCEL = 11 + + +class LinuxMoveResize: + """ Tool class for moving and resizing window """ + + moveResizeAtom = None + + @classmethod + def sendButtonReleaseEvent(cls, window, globalPos): + """ send button release event + + Parameters + ---------- + window: QWidget + window to be moved or resized + + globalPos: QPoint + the global point of mouse release event + """ + globalPos = QPointF(QPointF(globalPos) * + window.devicePixelRatio()).toPoint() + pos = window.mapFromGlobal(globalPos) + + # open the connection to X server + conn = xcb.wrap(sip.unwrapinstance(QX11Info.connection())) + windowId = int(window.winId()) + xproto = xprotoExtension(conn) + + # refer to: https://www.x.org/releases/X11R7.5/doc/libxcb/tutorial/ + event = ButtonReleaseEvent.synthetic( + detail=ButtonIndex._1, + time=xcb.CurrentTime, + root=QX11Info.appRootWindow(QX11Info.appScreen()), + event=windowId, + child=xcb.NONE, + root_x=globalPos.x(), + root_y=globalPos.y(), + event_x=pos.x(), + event_y=pos.y(), + state=ButtonMask._1, + same_screen=True, + ) + xproto.SendEvent(True, windowId, EventMask.ButtonRelease, event.pack()) + conn.flush() + + @classmethod + def startSystemMoveResize(cls, window, globalPos, message): + """ resize window + + Parameters + ---------- + window: QWidget + window to be moved or resized + + globalPos: QPoint + the global point of mouse release event + + message: int + window message + """ + cls.sendButtonReleaseEvent(window, globalPos) + + globalPos = QPointF(QPointF(globalPos) * + window.devicePixelRatio()).toPoint() + + # open the connection to X server + conn = xcb.wrap(sip.unwrapinstance(QX11Info.connection())) + xproto = xprotoExtension(conn) + + if not cls.moveResizeAtom: + cls.moveResizeAtom = xproto.InternAtom( + False, len("_NET_WM_MOVERESIZE"), "_NET_WM_MOVERESIZE").reply().atom + + union = ClientMessageData.synthetic([ + globalPos.x(), + globalPos.y(), + message, + ButtonIndex._1, + 0 + ], "I"*5) + event = ClientMessageEvent.synthetic( + format=32, + window=int(window.winId()), + type=cls.moveResizeAtom, + data=union + ) + xproto.UngrabPointer(xcb.CurrentTime) + xproto.SendEvent( + False, + QX11Info.appRootWindow(QX11Info.appScreen()), + EventMask.SubstructureRedirect | EventMask.SubstructureNotify, + event.pack() + ) + conn.flush() + + @classmethod + def startSystemMove(cls, window, globalPos): + """ move window """ + if QX11Info.isPlatformX11(): + cls.startSystemMoveResize( + window, globalPos, WindowMessage._NET_WM_MOVERESIZE_MOVE.value) + else: + window.windowHandle().startSystemMove() + event = QMouseEvent(QEvent.MouseButtonRelease, QPoint(-1, -1), + Qt.LeftButton, Qt.NoButton, Qt.NoModifier) + QApplication.instance().postEvent(window.windowHandle(), event) + + @classmethod + def starSystemResize(cls, window, globalPos, edges): + """ resize window + + Parameters + ---------- + window: QWidget + window + + globalPos: QPoint + the global point of mouse release event + + edges: `Qt.Edges` + window edges + """ + if not edges: + return + + if QX11Info.isPlatformX11(): + messageMap = { + Qt.TopEdge: WindowMessage._NET_WM_MOVERESIZE_SIZE_TOP, + Qt.TopEdge | Qt.LeftEdge: WindowMessage._NET_WM_MOVERESIZE_SIZE_TOPLEFT, + Qt.TopEdge | Qt.RightEdge: WindowMessage._NET_WM_MOVERESIZE_SIZE_TOPRIGHT, + Qt.BottomEdge: WindowMessage._NET_WM_MOVERESIZE_SIZE_BOTTOM, + Qt.BottomEdge | Qt.LeftEdge: WindowMessage._NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT, + Qt.BottomEdge | Qt.RightEdge: WindowMessage._NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT, + Qt.LeftEdge: WindowMessage._NET_WM_MOVERESIZE_SIZE_LEFT, + Qt.RightEdge: WindowMessage._NET_WM_MOVERESIZE_SIZE_RIGHT, + } + cls.startSystemMoveResize(window, globalPos, messageMap[edges].value) + else: + window.windowHandle().startSystemResize(edges) diff --git a/src/component/widget/qframelesswindow/utils/mac_utils.py b/src/component/widget/qframelesswindow/utils/mac_utils.py new file mode 100644 index 0000000..a339f63 --- /dev/null +++ b/src/component/widget/qframelesswindow/utils/mac_utils.py @@ -0,0 +1,72 @@ +# coding:utf-8 +from ctypes import c_void_p + +import Cocoa +import objc +from PySide6.QtCore import QT_VERSION_STR +from PySide6.QtWidgets import QWidget +from Quartz.CoreGraphics import (CGEventCreateMouseEvent, + kCGEventLeftMouseDown, kCGMouseButtonLeft) + +QT_VERSION = tuple(int(v) for v in QT_VERSION_STR.split('.')) + + +class MacMoveResize: + """ Tool class for moving and resizing Mac OS window """ + + @staticmethod + def startSystemMove(window: QWidget, globalPos): + """ resize window + + Parameters + ---------- + window: QWidget + window + + globalPos: QPoint + the global point of mouse release event + """ + if QT_VERSION >= (5, 15, 0): + window.windowHandle().startSystemMove() + return + + nsWindow = getNSWindow(window.winId()) + + # send click event + cgEvent = CGEventCreateMouseEvent( + None, kCGEventLeftMouseDown, (globalPos.x(), globalPos.y()), kCGMouseButtonLeft) + clickEvent = Cocoa.NSEvent.eventWithCGEvent_(cgEvent) + + if clickEvent: + nsWindow.performWindowDragWithEvent_(clickEvent) + + # CFRelease(cgEvent) + + @classmethod + def starSystemResize(cls, window, globalPos, edges): + """ resize window + + Parameters + ---------- + window: QWidget + window + + globalPos: QPoint + the global point of mouse release event + + edges: `Qt.Edges` + window edges + """ + pass + + +def getNSWindow(winId): + """ convert window handle to NSWindow + + Parameters + ---------- + winId: int or `sip.voidptr` + window handle + """ + view = objc.objc_object(c_void_p=c_void_p(int(winId))) + return view.window() diff --git a/src/component/widget/qframelesswindow/utils/win32_utils.py b/src/component/widget/qframelesswindow/utils/win32_utils.py new file mode 100644 index 0000000..b5e7139 --- /dev/null +++ b/src/component/widget/qframelesswindow/utils/win32_utils.py @@ -0,0 +1,310 @@ +# coding:utf-8 +from ctypes import Structure, byref, sizeof, windll, c_int +from ctypes.wintypes import DWORD, HWND, LPARAM, RECT, UINT +from platform import platform +import sys + +import win32api +import win32con +import win32gui +import win32print +from PySide6.QtCore import QOperatingSystemVersion +from PySide6.QtGui import QGuiApplication +from win32comext.shell import shellcon + + +def isMaximized(hWnd): + """ Determine whether the window is maximized + + Parameters + ---------- + hWnd: int or `sip.voidptr` + window handle + """ + windowPlacement = win32gui.GetWindowPlacement(hWnd) + if not windowPlacement: + return False + + return windowPlacement[1] == win32con.SW_MAXIMIZE + + +def isFullScreen(hWnd): + """ Determine whether the window is full screen + + Parameters + ---------- + hWnd: int or `sip.voidptr` + window handle + """ + if not hWnd: + return False + + hWnd = int(hWnd) + winRect = win32gui.GetWindowRect(hWnd) + if not winRect: + return False + + monitorInfo = getMonitorInfo(hWnd, win32con.MONITOR_DEFAULTTOPRIMARY) + if not monitorInfo: + return False + + monitorRect = monitorInfo["Monitor"] + return all(i == j for i, j in zip(winRect, monitorRect)) + + +def isCompositionEnabled(): + """ detect if dwm composition is enabled """ + bResult = c_int(0) + windll.dwmapi.DwmIsCompositionEnabled(byref(bResult)) + return bool(bResult.value) + + +def getMonitorInfo(hWnd, dwFlags): + """ get monitor info, return `None` if failed + + Parameters + ---------- + hWnd: int or `sip.voidptr` + window handle + + dwFlags: int + Determines the return value if the window does not intersect any display monitor + """ + monitor = win32api.MonitorFromWindow(hWnd, dwFlags) + if not monitor: + return + + return win32api.GetMonitorInfo(monitor) + + +def getResizeBorderThickness(hWnd, horizontal=True): + """ get resize border thickness of widget + + Parameters + ---------- + hWnd: int or `sip.voidptr` + window handle + + dpiScale: bool + whether to use dpi scale + """ + window = findWindow(hWnd) + if not window: + return 0 + + frame = win32con.SM_CXSIZEFRAME if horizontal else win32con.SM_CYSIZEFRAME + result = getSystemMetrics(hWnd, frame, horizontal) + getSystemMetrics(hWnd, 92, horizontal) + + if result > 0: + return result + + thickness = 8 if isCompositionEnabled() else 4 + return round(thickness*window.devicePixelRatio()) + + +def getSystemMetrics(hWnd, index, horizontal): + """ get system metrics """ + if not hasattr(windll.user32, 'GetSystemMetricsForDpi'): + return win32api.GetSystemMetrics(index) + + dpi = getDpiForWindow(hWnd, horizontal) + return windll.user32.GetSystemMetricsForDpi(index, dpi) + + +def getDpiForWindow(hWnd, horizontal=True): + """ get dpi for window + + Parameters + ---------- + hWnd: int or `sip.voidptr` + window handle + + dpiScale: bool + whether to use dpi scale + """ + if hasattr(windll.user32, 'GetDpiForWindow'): + return windll.user32.GetDpiForWindow(hWnd) + + hdc = win32gui.GetDC(hWnd) + if not hdc: + return 96 + + dpiX = win32print.GetDeviceCaps(hdc, win32con.LOGPIXELSX) + dpiY = win32print.GetDeviceCaps(hdc, win32con.LOGPIXELSY) + win32gui.ReleaseDC(hWnd, hdc) + if dpiX > 0 and horizontal: + return dpiX + elif dpiY > 0 and not horizontal: + return dpiY + + return 96 + + +def findWindow(hWnd): + """ find window by hWnd, return `None` if not found + + Parameters + ---------- + hWnd: int or `sip.voidptr` + window handle + """ + if not hWnd: + return + + windows = QGuiApplication.topLevelWindows() + if not windows: + return + + hWnd = int(hWnd) + for window in windows: + if window and int(window.winId()) == hWnd: + return window + + +def isGreaterEqualVersion(version): + """ determine if the windows version ≥ the specifics version + + Parameters + ---------- + version: QOperatingSystemVersion + windows version + """ + return QOperatingSystemVersion.current() >= version + + +def isGreaterEqualWin8_1(): + """ determine if the windows version ≥ Win8.1 """ + return isGreaterEqualVersion(QOperatingSystemVersion.Windows8_1) + + +def isGreaterEqualWin10(): + """ determine if the windows version ≥ Win10 """ + return isGreaterEqualVersion(QOperatingSystemVersion.Windows10) + + +def isGreaterEqualWin11(): + """ determine if the windows version ≥ Win10 """ + return isGreaterEqualVersion(QOperatingSystemVersion.Windows10) and sys.getwindowsversion().build >= 22000 + + +def isWin7(): + """ determine if the windows version is Win7 """ + return "Windows-7" in platform() + + +class APPBARDATA(Structure): + _fields_ = [ + ('cbSize', DWORD), + ('hWnd', HWND), + ('uCallbackMessage', UINT), + ('uEdge', UINT), + ('rc', RECT), + ('lParam', LPARAM), + ] + + +class Taskbar: + + LEFT = 0 + TOP = 1 + RIGHT = 2 + BOTTOM = 3 + NO_POSITION = 4 + + AUTO_HIDE_THICKNESS = 2 + + @staticmethod + def isAutoHide(): + """ detect whether the taskbar is hidden automatically """ + appbarData = APPBARDATA(sizeof(APPBARDATA), 0, + 0, 0, RECT(0, 0, 0, 0), 0) + taskbarState = windll.shell32.SHAppBarMessage( + shellcon.ABM_GETSTATE, byref(appbarData)) + + return taskbarState == shellcon.ABS_AUTOHIDE + + @classmethod + def getPosition(cls, hWnd): + """ get the position of auto-hide task bar + + Parameters + ---------- + hWnd: int or `sip.voidptr` + window handle + """ + if isGreaterEqualWin8_1(): + monitorInfo = getMonitorInfo( + hWnd, win32con.MONITOR_DEFAULTTONEAREST) + if not monitorInfo: + return cls.NO_POSITION + + monitor = RECT(*monitorInfo['Monitor']) + appbarData = APPBARDATA(sizeof(APPBARDATA), 0, 0, 0, monitor, 0) + positions = [cls.LEFT, cls.TOP, cls.RIGHT, cls.BOTTOM] + for position in positions: + appbarData.uEdge = position + if windll.shell32.SHAppBarMessage(11, byref(appbarData)): + return position + + return cls.NO_POSITION + + appbarData = APPBARDATA(sizeof(APPBARDATA), win32gui.FindWindow( + "Shell_TrayWnd", None), 0, 0, RECT(0, 0, 0, 0), 0) + if appbarData.hWnd: + windowMonitor = win32api.MonitorFromWindow( + hWnd, win32con.MONITOR_DEFAULTTONEAREST) + if not windowMonitor: + return cls.NO_POSITION + + taskbarMonitor = win32api.MonitorFromWindow( + appbarData.hWnd, win32con.MONITOR_DEFAULTTOPRIMARY) + if not taskbarMonitor: + return cls.NO_POSITION + + if taskbarMonitor == windowMonitor: + windll.shell32.SHAppBarMessage( + shellcon.ABM_GETTASKBARPOS, byref(appbarData)) + return appbarData.uEdge + + return cls.NO_POSITION + + +class WindowsMoveResize: + """ Tool class for moving and resizing Mac OS window """ + + @staticmethod + def startSystemMove(window, globalPos): + """ resize window + + Parameters + ---------- + window: QWidget + window + + globalPos: QPoint + the global point of mouse release event + """ + win32gui.ReleaseCapture() + win32api.SendMessage( + int(window.winId()), + win32con.WM_SYSCOMMAND, + win32con.SC_MOVE | win32con.HTCAPTION, + 0 + ) + + @classmethod + def starSystemResize(cls, window, globalPos, edges): + """ resize window + + Parameters + ---------- + window: QWidget + window + + globalPos: QPoint + the global point of mouse release event + + edges: `Qt.Edges` + window edges + """ + pass diff --git a/src/component/widget/qframelesswindow/windows/__init__.py b/src/component/widget/qframelesswindow/windows/__init__.py new file mode 100644 index 0000000..51d298e --- /dev/null +++ b/src/component/widget/qframelesswindow/windows/__init__.py @@ -0,0 +1,199 @@ +# coding:utf-8 +import sys +from ctypes import cast +from ctypes.wintypes import LPRECT, MSG + +import win32api +import win32con +import win32gui +from PySide6.QtCore import Qt +from PySide6.QtGui import QCloseEvent, QCursor +from PySide6.QtWidgets import QApplication, QWidget + +from ..titlebar import TitleBar +from ..utils import win32_utils as win_utils +from ..utils.win32_utils import Taskbar +from .c_structures import LPNCCALCSIZE_PARAMS +from .window_effect import WindowsWindowEffect + + +class WindowsFramelessWindow(QWidget): + """ Frameless window for Windows system """ + + BORDER_WIDTH = 5 + + def __init__(self, parent=None): + super().__init__(parent=parent) + self.windowEffect = WindowsWindowEffect(self) + self.titleBar = TitleBar(self) + self._isResizeEnabled = True + + self.updateFrameless() + + # solve issue #5 + self.windowHandle().screenChanged.connect(self.__onScreenChanged) + + self.resize(500, 500) + self.titleBar.raise_() + + def updateFrameless(self): + """ update frameless window """ + if not win_utils.isWin7(): + self.setWindowFlags(self.windowFlags() | Qt.FramelessWindowHint) + elif self.parent(): + self.setWindowFlags(self.parent().windowFlags() | Qt.FramelessWindowHint | Qt.WindowMinMaxButtonsHint) + else: + self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowMinMaxButtonsHint) + + # add DWM shadow and window animation + self.windowEffect.addWindowAnimation(self.winId()) + if not isinstance(self, AcrylicWindow): + self.windowEffect.addShadowEffect(self.winId()) + + def setTitleBar(self, titleBar): + """ set custom title bar + + Parameters + ---------- + titleBar: TitleBar + title bar + """ + self.titleBar.deleteLater() + self.titleBar.hide() + self.titleBar = titleBar + self.titleBar.setParent(self) + self.titleBar.raise_() + + def setResizeEnabled(self, isEnabled: bool): + """ set whether resizing is enabled """ + self._isResizeEnabled = isEnabled + + def resizeEvent(self, e): + super().resizeEvent(e) + self.titleBar.resize(self.width(), self.titleBar.height()) + + def nativeEvent(self, eventType, message): + """ Handle the Windows message """ + msg = MSG.from_address(message.__int__()) + if not msg.hWnd: + return super().nativeEvent(eventType, message) + + if msg.message == win32con.WM_NCHITTEST and self._isResizeEnabled: + pos = QCursor.pos() + xPos = pos.x() - self.x() + yPos = pos.y() - self.y() + w = self.frameGeometry().width() + h = self.frameGeometry().height() + + # fixes issue https://github.com/zhiyiYo/PyQt-Frameless-Window/issues/98 + bw = 0 if win_utils.isMaximized(msg.hWnd) or win_utils.isFullScreen(msg.hWnd) else self.BORDER_WIDTH + lx = xPos < bw + rx = xPos > w - bw + ty = yPos < bw + by = yPos > h - bw + if lx and ty: + return True, win32con.HTTOPLEFT + elif rx and by: + return True, win32con.HTBOTTOMRIGHT + elif rx and ty: + return True, win32con.HTTOPRIGHT + elif lx and by: + return True, win32con.HTBOTTOMLEFT + elif ty: + return True, win32con.HTTOP + elif by: + return True, win32con.HTBOTTOM + elif lx: + return True, win32con.HTLEFT + elif rx: + return True, win32con.HTRIGHT + elif msg.message == win32con.WM_NCCALCSIZE: + if msg.wParam: + rect = cast(msg.lParam, LPNCCALCSIZE_PARAMS).contents.rgrc[0] + else: + rect = cast(msg.lParam, LPRECT).contents + + isMax = win_utils.isMaximized(msg.hWnd) + isFull = win_utils.isFullScreen(msg.hWnd) + + # adjust the size of client rect + if isMax and not isFull: + ty = win_utils.getResizeBorderThickness(msg.hWnd, False) + rect.top += ty + rect.bottom -= ty + + tx = win_utils.getResizeBorderThickness(msg.hWnd, True) + rect.left += tx + rect.right -= tx + + # handle the situation that an auto-hide taskbar is enabled + if (isMax or isFull) and Taskbar.isAutoHide(): + position = Taskbar.getPosition(msg.hWnd) + if position == Taskbar.LEFT: + rect.top += Taskbar.AUTO_HIDE_THICKNESS + elif position == Taskbar.BOTTOM: + rect.bottom -= Taskbar.AUTO_HIDE_THICKNESS + elif position == Taskbar.LEFT: + rect.left += Taskbar.AUTO_HIDE_THICKNESS + elif position == Taskbar.RIGHT: + rect.right -= Taskbar.AUTO_HIDE_THICKNESS + + result = 0 if not msg.wParam else win32con.WVR_REDRAW + return True, result + + return super().nativeEvent(eventType, message) + + def __onScreenChanged(self): + hWnd = int(self.windowHandle().winId()) + win32gui.SetWindowPos(hWnd, None, 0, 0, 0, 0, win32con.SWP_NOMOVE | + win32con.SWP_NOSIZE | win32con.SWP_FRAMECHANGED) + + +class AcrylicWindow(WindowsFramelessWindow): + """ A frameless window with acrylic effect """ + + def __init__(self, parent=None): + super().__init__(parent=parent) + self.__closedByKey = False + self.setStyleSheet("AcrylicWindow{background:transparent}") + + def updateFrameless(self): + super().updateFrameless() + self.windowEffect.enableBlurBehindWindow(self.winId()) + + if win_utils.isWin7() and self.parent(): + self.setWindowFlags(self.parent().windowFlags() | Qt.FramelessWindowHint | Qt.WindowMinMaxButtonsHint) + else: + self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowMinMaxButtonsHint) + + self.windowEffect.addWindowAnimation(self.winId()) + + if win_utils.isWin7(): + self.windowEffect.addShadowEffect(self.winId()) + self.windowEffect.setAeroEffect(self.winId()) + else: + self.windowEffect.setAcrylicEffect(self.winId()) + if win_utils.isGreaterEqualWin11(): + self.windowEffect.addShadowEffect(self.winId()) + + def nativeEvent(self, eventType, message): + """ Handle the Windows message """ + msg = MSG.from_address(message.__int__()) + + # handle Alt+F4 + if msg.message == win32con.WM_SYSKEYDOWN: + if msg.wParam == win32con.VK_F4: + self.__closedByKey = True + QApplication.sendEvent(self, QCloseEvent()) + return False, 0 + + return super().nativeEvent(eventType, message) + + def closeEvent(self, e): + if not self.__closedByKey or QApplication.quitOnLastWindowClosed(): + self.__closedByKey = False + return super().closeEvent(e) + + # system tray icon + self.__closedByKey = False + self.hide() diff --git a/src/component/widget/qframelesswindow/windows/c_structures.py b/src/component/widget/qframelesswindow/windows/c_structures.py new file mode 100644 index 0000000..bcb20df --- /dev/null +++ b/src/component/widget/qframelesswindow/windows/c_structures.py @@ -0,0 +1,153 @@ +# coding:utf-8 +from ctypes import POINTER, Structure, c_int +from ctypes.wintypes import DWORD, HWND, ULONG, POINT, RECT, UINT, BOOL, HRGN +from enum import Enum + + +class WINDOWCOMPOSITIONATTRIB(Enum): + WCA_UNDEFINED = 0 + WCA_NCRENDERING_ENABLED = 1 + WCA_NCRENDERING_POLICY = 2 + WCA_TRANSITIONS_FORCEDISABLED = 3 + WCA_ALLOW_NCPAINT = 4 + WCA_CAPTION_BUTTON_BOUNDS = 5 + WCA_NONCLIENT_RTL_LAYOUT = 6 + WCA_FORCE_ICONIC_REPRESENTATION = 7 + WCA_EXTENDED_FRAME_BOUNDS = 8 + WCA_HAS_ICONIC_BITMAP = 9 + WCA_THEME_ATTRIBUTES = 10 + WCA_NCRENDERING_EXILED = 11 + WCA_NCADORNMENTINFO = 12 + WCA_EXCLUDED_FROM_LIVEPREVIEW = 13 + WCA_VIDEO_OVERLAY_ACTIVE = 14 + WCA_FORCE_ACTIVEWINDOW_APPEARANCE = 15 + WCA_DISALLOW_PEEK = 16 + WCA_CLOAK = 17 + WCA_CLOAKED = 18 + WCA_ACCENT_POLICY = 19 + WCA_FREEZE_REPRESENTATION = 20 + WCA_EVER_UNCLOAKED = 21 + WCA_VISUAL_OWNER = 22 + WCA_HOLOGRAPHIC = 23 + WCA_EXCLUDED_FROM_DDA = 24 + WCA_PASSIVEUPDATEMODE = 25 + WCA_USEDARKMODECOLORS = 26 + WCA_CORNER_STYLE = 27 + WCA_PART_COLOR = 28 + WCA_DISABLE_MOVESIZE_FEEDBACK = 29 + WCA_LAST = 30 + + +class ACCENT_STATE(Enum): + """ Client area status enumeration class """ + ACCENT_DISABLED = 0 + ACCENT_ENABLE_GRADIENT = 1 + ACCENT_ENABLE_TRANSPARENTGRADIENT = 2 + ACCENT_ENABLE_BLURBEHIND = 3 # Aero effect + ACCENT_ENABLE_ACRYLICBLURBEHIND = 4 # Acrylic effect + ACCENT_ENABLE_HOSTBACKDROP = 5 # Mica effect + ACCENT_INVALID_STATE = 6 + + +class ACCENT_POLICY(Structure): + """ Specific attributes of client area """ + + _fields_ = [ + ("AccentState", DWORD), + ("AccentFlags", DWORD), + ("GradientColor", DWORD), + ("AnimationId", DWORD), + ] + + +class WINDOWCOMPOSITIONATTRIBDATA(Structure): + _fields_ = [ + ("Attribute", DWORD), + # Pointer() receives any ctypes type and returns a pointer type + ("Data", POINTER(ACCENT_POLICY)), + ("SizeOfData", ULONG), + ] + + +class DWMNCRENDERINGPOLICY(Enum): + DWMNCRP_USEWINDOWSTYLE = 0 + DWMNCRP_DISABLED = 1 + DWMNCRP_ENABLED = 2 + DWMNCRP_LAS = 3 + + +class DWMWINDOWATTRIBUTE(Enum): + DWMWA_NCRENDERING_ENABLED = 1 + DWMWA_NCRENDERING_POLICY = 2 + DWMWA_TRANSITIONS_FORCEDISABLED = 3 + DWMWA_ALLOW_NCPAINT = 4 + DWMWA_CAPTION_BUTTON_BOUNDS = 5 + DWMWA_NONCLIENT_RTL_LAYOUT = 6 + DWMWA_FORCE_ICONIC_REPRESENTATION = 7 + DWMWA_FLIP3D_POLICY = 8 + DWMWA_EXTENDED_FRAME_BOUNDS = 9 + DWMWA_HAS_ICONIC_BITMAP = 10 + DWMWA_DISALLOW_PEEK = 11 + DWMWA_EXCLUDED_FROM_PEEK = 12 + DWMWA_CLOAK = 13 + DWMWA_CLOAKED = 14 + DWMWA_FREEZE_REPRESENTATION = 15 + DWMWA_PASSIVE_UPDATE_MODE = 16 + DWMWA_USE_HOSTBACKDROPBRUSH = 17 + DWMWA_USE_IMMERSIVE_DARK_MODE = 18 + DWMWA_WINDOW_CORNER_PREFERENCE = 19 + DWMWA_BORDER_COLOR = 20 + DWMWA_CAPTION_COLOR = 21 + DWMWA_TEXT_COLOR = 22 + DWMWA_VISIBLE_FRAME_BORDER_THICKNESS = 23 + DWMWA_LAST = 24 + + +class MARGINS(Structure): + _fields_ = [ + ("cxLeftWidth", c_int), + ("cxRightWidth", c_int), + ("cyTopHeight", c_int), + ("cyBottomHeight", c_int), + ] + + +class MINMAXINFO(Structure): + _fields_ = [ + ("ptReserved", POINT), + ("ptMaxSize", POINT), + ("ptMaxPosition", POINT), + ("ptMinTrackSize", POINT), + ("ptMaxTrackSize", POINT), + ] + + +class PWINDOWPOS(Structure): + _fields_ = [ + ('hWnd', HWND), + ('hwndInsertAfter', HWND), + ('x', c_int), + ('y', c_int), + ('cx', c_int), + ('cy', c_int), + ('flags', UINT) + ] + + +class NCCALCSIZE_PARAMS(Structure): + _fields_ = [ + ('rgrc', RECT*3), + ('lppos', POINTER(PWINDOWPOS)) + ] + + +LPNCCALCSIZE_PARAMS = POINTER(NCCALCSIZE_PARAMS) + + +class DWM_BLURBEHIND(Structure): + _fields_ = [ + ('dwFlags', DWORD), + ('fEnable', BOOL), + ('hRgnBlur', HRGN), + ('fTransitionOnMaximized', BOOL), + ] diff --git a/src/component/widget/qframelesswindow/windows/window_effect.py b/src/component/widget/qframelesswindow/windows/window_effect.py new file mode 100644 index 0000000..7a437a3 --- /dev/null +++ b/src/component/widget/qframelesswindow/windows/window_effect.py @@ -0,0 +1,260 @@ +# coding:utf-8 +import sys +import warnings +from ctypes import POINTER, byref, c_bool, c_int, pointer, sizeof, WinDLL +from ctypes.wintypes import DWORD, LONG, LPCVOID + +import win32api +import win32con +import win32gui + +from .c_structures import (ACCENT_POLICY, ACCENT_STATE, DWMNCRENDERINGPOLICY, + DWMWINDOWATTRIBUTE, MARGINS, + WINDOWCOMPOSITIONATTRIB, + WINDOWCOMPOSITIONATTRIBDATA, DWM_BLURBEHIND) +from ..utils.win32_utils import isGreaterEqualWin10, isGreaterEqualWin11, isCompositionEnabled + + +class WindowsWindowEffect: + """ Windows window effect """ + + def __init__(self, window): + self.window = window + + # Declare the function signature of the API + self.user32 = WinDLL("user32") + self.dwmapi = WinDLL("dwmapi") + self.SetWindowCompositionAttribute = self.user32.SetWindowCompositionAttribute + self.DwmExtendFrameIntoClientArea = self.dwmapi.DwmExtendFrameIntoClientArea + self.DwmEnableBlurBehindWindow = self.dwmapi.DwmEnableBlurBehindWindow + self.DwmSetWindowAttribute = self.dwmapi.DwmSetWindowAttribute + + self.SetWindowCompositionAttribute.restype = c_bool + self.DwmExtendFrameIntoClientArea.restype = LONG + self.DwmEnableBlurBehindWindow.restype = LONG + self.DwmSetWindowAttribute.restype = LONG + + self.SetWindowCompositionAttribute.argtypes = [ + c_int, + POINTER(WINDOWCOMPOSITIONATTRIBDATA), + ] + self.DwmSetWindowAttribute.argtypes = [c_int, DWORD, LPCVOID, DWORD] + self.DwmExtendFrameIntoClientArea.argtypes = [c_int, POINTER(MARGINS)] + self.DwmEnableBlurBehindWindow.argtypes = [c_int, POINTER(DWM_BLURBEHIND)] + + # Initialize structure + self.accentPolicy = ACCENT_POLICY() + self.winCompAttrData = WINDOWCOMPOSITIONATTRIBDATA() + self.winCompAttrData.Attribute = WINDOWCOMPOSITIONATTRIB.WCA_ACCENT_POLICY.value + self.winCompAttrData.SizeOfData = sizeof(self.accentPolicy) + self.winCompAttrData.Data = pointer(self.accentPolicy) + + def setAcrylicEffect(self, hWnd, gradientColor="F2F2F299", enableShadow=True, animationId=0): + """ Add the acrylic effect to the window + + Parameters + ---------- + hWnd: int or `sip.voidptr` + Window handle + + gradientColor: str + Hexadecimal acrylic mixed color, corresponding to four RGBA channels + + isEnableShadow: bool + Enable window shadows + + animationId: int + Turn on matte animation + """ + if not isGreaterEqualWin10(): + warnings.warn("The acrylic effect is only available on Win10+") + return + + hWnd = int(hWnd) + gradientColor = ''.join(gradientColor[i:i+2] for i in range(6, -1, -2)) + gradientColor = DWORD(int(gradientColor, base=16)) + animationId = DWORD(animationId) + accentFlags = DWORD(0x20 | 0x40 | 0x80 | 0x100) if enableShadow else DWORD(0) + self.accentPolicy.AccentState = ACCENT_STATE.ACCENT_ENABLE_ACRYLICBLURBEHIND.value + self.accentPolicy.GradientColor = gradientColor + self.accentPolicy.AccentFlags = accentFlags + self.accentPolicy.AnimationId = animationId + self.winCompAttrData.Attribute = WINDOWCOMPOSITIONATTRIB.WCA_ACCENT_POLICY.value + self.SetWindowCompositionAttribute(hWnd, pointer(self.winCompAttrData)) + + def setMicaEffect(self, hWnd, isDarkMode=False, isAlt=False): + """ Add the mica effect to the window (Win11 only) + + Parameters + ---------- + hWnd: int or `sip.voidptr` + Window handle + + isDarkMode: bool + whether to use dark mode mica effect + + isAlt: bool + whether to enable mica alt effect + """ + if not isGreaterEqualWin11(): + warnings.warn("The mica effect is only available on Win11") + return + + hWnd = int(hWnd) + margins = MARGINS(-1, -1, -1, -1) + self.DwmExtendFrameIntoClientArea(hWnd, byref(margins)) + + self.winCompAttrData.Attribute = WINDOWCOMPOSITIONATTRIB.WCA_ACCENT_POLICY.value + self.accentPolicy.AccentState = ACCENT_STATE.ACCENT_ENABLE_HOSTBACKDROP.value + self.SetWindowCompositionAttribute(hWnd, pointer(self.winCompAttrData)) + + if isDarkMode: + self.winCompAttrData.Attribute = WINDOWCOMPOSITIONATTRIB.WCA_USEDARKMODECOLORS.value + self.SetWindowCompositionAttribute(hWnd, pointer(self.winCompAttrData)) + + if sys.getwindowsversion().build < 22523: + self.DwmSetWindowAttribute(hWnd, 1029, byref(c_int(1)), 4) + else: + self.DwmSetWindowAttribute(hWnd, 38, byref(c_int(4 if isAlt else 2)), 4) + + self.DwmSetWindowAttribute(hWnd, 20, byref(c_int(1*isDarkMode)), 4) + + def setAeroEffect(self, hWnd): + """ Add the aero effect to the window + + Parameters + ---------- + hWnd: int or `sip.voidptr` + Window handle + """ + hWnd = int(hWnd) + self.winCompAttrData.Attribute = WINDOWCOMPOSITIONATTRIB.WCA_ACCENT_POLICY.value + self.accentPolicy.AccentState = ACCENT_STATE.ACCENT_ENABLE_BLURBEHIND.value + self.SetWindowCompositionAttribute(hWnd, pointer(self.winCompAttrData)) + + def removeBackgroundEffect(self, hWnd): + """ Remove background effect + + Parameters + ---------- + hWnd: int or `sip.voidptr` + Window handle + """ + hWnd = int(hWnd) + self.accentPolicy.AccentState = ACCENT_STATE.ACCENT_DISABLED.value + self.SetWindowCompositionAttribute(hWnd, pointer(self.winCompAttrData)) + + def addShadowEffect(self, hWnd): + """ Add DWM shadow to window + + Parameters + ---------- + hWnd: int or `sip.voidptr` + Window handle + """ + if not isCompositionEnabled(): + return + + hWnd = int(hWnd) + margins = MARGINS(-1, -1, -1, -1) + self.DwmExtendFrameIntoClientArea(hWnd, byref(margins)) + + def addMenuShadowEffect(self, hWnd): + """ Add DWM shadow to menu + + Parameters + ---------- + hWnd: int or `sip.voidptr` + Window handle + """ + if not isCompositionEnabled(): + return + + hWnd = int(hWnd) + self.DwmSetWindowAttribute( + hWnd, + DWMWINDOWATTRIBUTE.DWMWA_NCRENDERING_POLICY.value, + byref(c_int(DWMNCRENDERINGPOLICY.DWMNCRP_ENABLED.value)), + 4, + ) + margins = MARGINS(-1, -1, -1, -1) + self.DwmExtendFrameIntoClientArea(hWnd, byref(margins)) + + def removeShadowEffect(self, hWnd): + """ Remove DWM shadow from the window + + Parameters + ---------- + hWnd: int or `sip.voidptr` + Window handle + """ + hWnd = int(hWnd) + self.DwmSetWindowAttribute( + hWnd, + DWMWINDOWATTRIBUTE.DWMWA_NCRENDERING_POLICY.value, + byref(c_int(DWMNCRENDERINGPOLICY.DWMNCRP_DISABLED.value)), + 4, + ) + + @staticmethod + def removeMenuShadowEffect(hWnd): + """ Remove shadow from pop-up menu + + Parameters + ---------- + hWnd: int or `sip.voidptr` + Window handle + """ + hWnd = int(hWnd) + style = win32gui.GetClassLong(hWnd, win32con.GCL_STYLE) + style &= ~0x00020000 # CS_DROPSHADOW + win32api.SetClassLong(hWnd, win32con.GCL_STYLE, style) + + @staticmethod + def addWindowAnimation(hWnd): + """ Enables the maximize and minimize animation of the window + + Parameters + ---------- + hWnd : int or `sip.voidptr` + Window handle + """ + hWnd = int(hWnd) + style = win32gui.GetWindowLong(hWnd, win32con.GWL_STYLE) + win32gui.SetWindowLong( + hWnd, + win32con.GWL_STYLE, + style + | win32con.WS_MINIMIZEBOX + | win32con.WS_MAXIMIZEBOX + | win32con.WS_CAPTION + | win32con.CS_DBLCLKS + | win32con.WS_THICKFRAME, + ) + + @staticmethod + def disableMaximizeButton(hWnd): + """ Disable the maximize button of window + + Parameters + ---------- + hWnd : int or `sip.voidptr` + Window handle + """ + hWnd = int(hWnd) + style = win32gui.GetWindowLong(hWnd, win32con.GWL_STYLE) + win32gui.SetWindowLong( + hWnd, + win32con.GWL_STYLE, + style & ~win32con.WS_MAXIMIZEBOX, + ) + + def enableBlurBehindWindow(self, hWnd): + """ enable the blur effect behind the whole client + Parameters + ---------- + hWnd: int or `sip.voidptr` + Window handle + """ + blurBehind = DWM_BLURBEHIND(1, True, 0, False) + self.DwmEnableBlurBehindWindow(int(hWnd), byref(blurBehind)) diff --git a/src/config/config.py b/src/config/config.py index cf563d2..610b998 100644 --- a/src/config/config.py +++ b/src/config/config.py @@ -50,9 +50,9 @@ Issues2 = "https://hub.ggo.icu/tonquer/picacg-qt/issues" Issues3 = "https://hub.fastgit.xyz/tonquer/picacg-qt/issues" -UpdateVersion = "v1.4.8" -RealVersion = "v1.4.8" -TimeVersion = "2024-1-12" +UpdateVersion = "v1.4.9" +RealVersion = "v1.4.9" +TimeVersion = "2024-4-6" Waifu2xVersion = "1.1.6" @@ -78,14 +78,14 @@ # Waifu2x相关 Waifu2xUrl = "https://github.com/tonquer/picacg-qt/discussions/76" -Address = ["188.114.98.153", "104.21.91.145"] # 分类2,3 Ip列表 -AddressIpv6 = ["2606:4700:d:28:dbf4:26f3:c265:73bc", "2a06:98c1:3120:ca71:be2c:c721:d2b5:5dbf"] +# Address = ["188.114.98.153", "104.21.91.145"] # 分类2,3 Ip列表 +# AddressIpv6 = ["2606:4700:d:28:dbf4:26f3:c265:73bc", "2a06:98c1:3120:ca71:be2c:c721:d2b5:5dbf"] -ImageServer2 = 's3.picacomic.com' # 分流2 使用的图片服务器 -ImageServer2Jump = 'img.picacomic.com' # 分流2 跳转的图片服务器 +# ImageServer2 = 's3.picacomic.com' # 分流2 使用的图片服务器 +# ImageServer2Jump = 'img.picacomic.com' # 分流2 跳转的图片服务器 -ImageServer3 = 'storage.diwodiwo.xyz' # 分流3 使用的图片服务器 -ImageServer3Jump = 'img.diwodiwo.xyz' # 分流3 使用的图片服务器 +# ImageServer3 = 'storage.diwodiwo.xyz' # 分流3 使用的图片服务器 +# ImageServer3Jump = 'img.diwodiwo.xyz' # 分流3 使用的图片服务器 ProxyApiDomain = "bika-api.ggo.icu" ProxyImgDomain = "bika-img.ggo.icu" @@ -100,14 +100,13 @@ ImageDomain = [ "s3.picacomic.com", - # "storage.diwodiwo.xyz", + "storage.diwodiwo.xyz", # "img.diwodiwo.xyz", "storage1.picacomic.com", - "img.tipatipa.xyz", - "img.picacomic.com", + # "img.tipatipa.xyz", + # "img.picacomic.com", "storage.tipatipa.xyz", # "pica-pica.wikawika.xyz", "www.picacomic.com", "storage-b.picacomic.com", - -] +] \ No newline at end of file diff --git a/src/config/global_config.py b/src/config/global_config.py new file mode 100644 index 0000000..d3af0b0 --- /dev/null +++ b/src/config/global_config.py @@ -0,0 +1,115 @@ +from config.setting import Setting +from tools.log import Log +from tools.singleton import Singleton + + +class GlobalItem(object): + def __init__(self, default): + self.value = default + self.def_value = default + + def is_same(self): + return self.value == self.def_value + + def set_value(self, value): + if isinstance(self.def_value, int): + self.value = int(value) + elif isinstance(self.def_value, list) and isinstance(value, str): + self.value = value.split(",") + else: + self.value = value + + +class GlobalConfig: + Ver = GlobalItem(0) + LocalProxyIndex = [2, 3] + Address = GlobalItem(["104.21.91.145", "188.114.98.153"]) + AddressIpv6 = GlobalItem(["2606:4700:d:28:dbf4:26f3:c265:73bc", "2a06:98c1:3120:ca71:be2c:c721:d2b5:5dbf"]) + ImageUrl = GlobalItem("s3.picacomic.com") + ImageServerList = GlobalItem(["s3.picacomic.com", "storage.diwodiwo.xyz", "storage-b.picacomic.com", "storage1.picacomic.com"]) + ImageJumList = GlobalItem(["img.picacomic.com", "img.diwodiwo.xyz"]) + + def __init__(self): + pass + + @staticmethod + def GetAddress(index): + if index in GlobalConfig.LocalProxyIndex: + i = GlobalConfig.LocalProxyIndex.index(index) + if Setting.PreIpv6.value > 0: + return GlobalConfig.AddressIpv6.value[i] + else: + return GlobalConfig.Address.value[i] + else: + return "" + # + # @staticmethod + # def GetImageServer(index): + # if index in GlobalConfig.LocalProxyIndex: + # i = GlobalConfig.LocalProxyIndex.index(index) + # return GlobalConfig.ImageServerList.value[i] + # else: + # return "" + + @staticmethod + def GetImageAdress(index): + if index in GlobalConfig.LocalProxyIndex: + i = GlobalConfig.LocalProxyIndex.index(index) + if Setting.PreIpv6.value > 0: + return GlobalConfig.AddressIpv6.value[i] + else: + return GlobalConfig.Address.value[i] + else: + return "" + + @staticmethod + def LoadSetting(): + try: + for k, v in dict(Setting.GlobalConfig.value).items(): + Log.Debug("load global setting, k={}, v={}".format(k, v)) + value = getattr(GlobalConfig, k) + if isinstance(value, GlobalItem) : + value.set_value(v) + except Exception as es: + Log.Error(es) + pass + + @staticmethod + def SaveSetting(): + saveData = {} + try: + for name in dir(GlobalConfig): + value = getattr(GlobalConfig, name) + if isinstance(value, GlobalItem) and not value.is_same(): + saveData[name] = value.value + Setting.GlobalConfig.SetValue(saveData) + except Exception as es: + Log.Error(es) + pass + + @staticmethod + def SetSetting(k, v): + value = getattr(GlobalConfig, k) + if isinstance(value, GlobalItem): + Log.Info("set setting, k:{}, v:{}".format(k, v)) + value.set_value(v) + GlobalConfig.SaveSetting() + + # 下载配置文件 + @staticmethod + def UpdateSetting(data): + allKvs = {} + for v in data.replace("\r", "").split("\n"): + if not v: + continue + [k, v2] = v.split("=") + allKvs[k] = v2 + ver = int(allKvs.get("Ver", 0)) + if ver > GlobalConfig.Ver.value: + Log.Info("update setting, {}".format(allKvs)) + for name, value in allKvs.items(): + item = getattr(GlobalConfig, name) + if isinstance(item, GlobalItem): + item.set_value(value) + GlobalConfig.SaveSetting() + pass diff --git a/src/config/setting.py b/src/config/setting.py index 56eab20..fffcc5d 100644 --- a/src/config/setting.py +++ b/src/config/setting.py @@ -79,7 +79,13 @@ class Setting: Language = SettingValue("GeneraSetting", 0, False, ["Auto", "zh", "hk", "en"]) # ch-zh ch-hk eu ThemeIndex = SettingValue("GeneraSetting", 0, False, ["Auto", "dark", "light"]) # LogIndex = SettingValue("GeneraSetting", 0, False, ["warn", "info", "debug"]) # Warn Info Debug + + LogDirPath = SettingValue("GeneraSetting", "", False) + CoverSize = SettingValue("GeneraSetting", 100, False) # + TitleLine = SettingValue("GeneraSetting", 2, False) # + NotCategoryShow = SettingValue("GeneraSetting", 0, False) # + CategorySize = SettingValue("GeneraSetting", 80, False) # ScaleLevel = SettingValue("GeneraSetting", 0, True, ["Auto", 100, 125, 150, 175, 200]) IsUseTitleBar = SettingValue("GeneraSetting", 1, True) @@ -99,6 +105,7 @@ class Setting: PreferCDNIP = SettingValue("ProxySetting", "104.18.227.172", False) IsUseHttps = SettingValue("ProxySetting", 1, False) PreIpv6 = SettingValue("ProxySetting", 0, False) + LastProxyResult = SettingValue("ProxySetting", {}, False) ProxySelectIndex = SettingValue("ProxySetting", 1, False) ProxyImgSelectIndex = SettingValue("ProxySetting", 1, False) @@ -152,6 +159,7 @@ class Setting: IsPreUpdate = SettingValue("Other", 0, False) SaveCacheAddress = SettingValue("Other", "104.21.91.145", False) IsReDownload = SettingValue("Other", 0, False) + GlobalConfig = SettingValue("Other", "", False) @staticmethod def InitLoadSetting(): @@ -219,6 +227,9 @@ def GetConfigPath(): @staticmethod def GetLogPath(): import sys + if Setting.LogDirPath.value: + return Setting.LogDirPath.value + if sys.platform == "win32": return "logs" else: diff --git a/src/db/book.db b/src/db/book.db index e8c3f67..14699b2 100644 Binary files a/src/db/book.db and b/src/db/book.db differ diff --git a/src/interface/ui_download.py b/src/interface/ui_download.py index c58b3f8..d3d43f8 100644 --- a/src/interface/ui_download.py +++ b/src/interface/ui_download.py @@ -23,11 +23,40 @@ class Ui_Download(object): def setupUi(self, Download): if not Download.objectName(): Download.setObjectName(u"Download") - Download.resize(707, 361) + Download.resize(820, 361) self.gridLayout_2 = QGridLayout(Download) self.gridLayout_2.setObjectName(u"gridLayout_2") self.gridLayout = QGridLayout() self.gridLayout.setObjectName(u"gridLayout") + self.tableWidget = QTableWidget(Download) + if (self.tableWidget.columnCount() < 11): + self.tableWidget.setColumnCount(11) + __qtablewidgetitem = QTableWidgetItem() + self.tableWidget.setHorizontalHeaderItem(0, __qtablewidgetitem) + __qtablewidgetitem1 = QTableWidgetItem() + self.tableWidget.setHorizontalHeaderItem(1, __qtablewidgetitem1) + __qtablewidgetitem2 = QTableWidgetItem() + self.tableWidget.setHorizontalHeaderItem(2, __qtablewidgetitem2) + __qtablewidgetitem3 = QTableWidgetItem() + self.tableWidget.setHorizontalHeaderItem(3, __qtablewidgetitem3) + __qtablewidgetitem4 = QTableWidgetItem() + self.tableWidget.setHorizontalHeaderItem(4, __qtablewidgetitem4) + __qtablewidgetitem5 = QTableWidgetItem() + self.tableWidget.setHorizontalHeaderItem(5, __qtablewidgetitem5) + __qtablewidgetitem6 = QTableWidgetItem() + self.tableWidget.setHorizontalHeaderItem(6, __qtablewidgetitem6) + __qtablewidgetitem7 = QTableWidgetItem() + self.tableWidget.setHorizontalHeaderItem(7, __qtablewidgetitem7) + __qtablewidgetitem8 = QTableWidgetItem() + self.tableWidget.setHorizontalHeaderItem(8, __qtablewidgetitem8) + __qtablewidgetitem9 = QTableWidgetItem() + self.tableWidget.setHorizontalHeaderItem(9, __qtablewidgetitem9) + __qtablewidgetitem10 = QTableWidgetItem() + self.tableWidget.setHorizontalHeaderItem(10, __qtablewidgetitem10) + self.tableWidget.setObjectName(u"tableWidget") + + self.gridLayout.addWidget(self.tableWidget, 4, 0, 1, 1) + self.horizontalLayout = QHBoxLayout() self.horizontalLayout.setObjectName(u"horizontalLayout") self.horizontalSpacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) @@ -72,36 +101,7 @@ def setupUi(self, Download): self.horizontalLayout.addWidget(self.radioButton) - self.gridLayout.addLayout(self.horizontalLayout, 2, 0, 1, 1) - - self.tableWidget = QTableWidget(Download) - if (self.tableWidget.columnCount() < 11): - self.tableWidget.setColumnCount(11) - __qtablewidgetitem = QTableWidgetItem() - self.tableWidget.setHorizontalHeaderItem(0, __qtablewidgetitem) - __qtablewidgetitem1 = QTableWidgetItem() - self.tableWidget.setHorizontalHeaderItem(1, __qtablewidgetitem1) - __qtablewidgetitem2 = QTableWidgetItem() - self.tableWidget.setHorizontalHeaderItem(2, __qtablewidgetitem2) - __qtablewidgetitem3 = QTableWidgetItem() - self.tableWidget.setHorizontalHeaderItem(3, __qtablewidgetitem3) - __qtablewidgetitem4 = QTableWidgetItem() - self.tableWidget.setHorizontalHeaderItem(4, __qtablewidgetitem4) - __qtablewidgetitem5 = QTableWidgetItem() - self.tableWidget.setHorizontalHeaderItem(5, __qtablewidgetitem5) - __qtablewidgetitem6 = QTableWidgetItem() - self.tableWidget.setHorizontalHeaderItem(6, __qtablewidgetitem6) - __qtablewidgetitem7 = QTableWidgetItem() - self.tableWidget.setHorizontalHeaderItem(7, __qtablewidgetitem7) - __qtablewidgetitem8 = QTableWidgetItem() - self.tableWidget.setHorizontalHeaderItem(8, __qtablewidgetitem8) - __qtablewidgetitem9 = QTableWidgetItem() - self.tableWidget.setHorizontalHeaderItem(9, __qtablewidgetitem9) - __qtablewidgetitem10 = QTableWidgetItem() - self.tableWidget.setHorizontalHeaderItem(10, __qtablewidgetitem10) - self.tableWidget.setObjectName(u"tableWidget") - - self.gridLayout.addWidget(self.tableWidget, 5, 0, 1, 1) + self.gridLayout.addLayout(self.horizontalLayout, 1, 0, 1, 1) self.gridLayout_2.addLayout(self.gridLayout, 0, 0, 1, 1) @@ -119,13 +119,6 @@ def setupUi(self, Download): def retranslateUi(self, Download): Download.setWindowTitle(QCoreApplication.translate("Download", u"\u4e0b\u8f7d", None)) - self.redownloadRadio.setText(QCoreApplication.translate("Download", u"\u4e0b\u8f7d\u5931\u8d25\u540e1\u5206\u949f\u81ea\u52a8\u91cd\u8bd5", None)) - self.updateNew.setText(QCoreApplication.translate("Download", u"\u66f4\u65b0\u6240\u6709New\u7ae0\u8282", None)) - self.pushButton.setText(QCoreApplication.translate("Download", u"\u5168\u90e8\u5f00\u59cb\u4e0b\u8f7d", None)) - self.pushButton_3.setText(QCoreApplication.translate("Download", u"\u5168\u90e8\u6682\u505c\u4e0b\u8f7d", None)) - self.pushButton_2.setText(QCoreApplication.translate("Download", u"\u5168\u90e8\u5f00\u59cb\u8f6c\u6362", None)) - self.pushButton_4.setText(QCoreApplication.translate("Download", u"\u5168\u90e8\u6682\u505c\u8f6c\u6362", None)) - self.radioButton.setText(QCoreApplication.translate("Download", u"\u4e0b\u8f7d\u81ea\u52a8\u8fdb\u884cWaifu2x\u8f6c\u6362", None)) ___qtablewidgetitem = self.tableWidget.horizontalHeaderItem(0) ___qtablewidgetitem.setText(QCoreApplication.translate("Download", u"id", None)); ___qtablewidgetitem1 = self.tableWidget.horizontalHeaderItem(1) @@ -148,5 +141,12 @@ def retranslateUi(self, Download): ___qtablewidgetitem9.setText(QCoreApplication.translate("Download", u"\u8f6c\u6362\u8017\u65f6", None)); ___qtablewidgetitem10 = self.tableWidget.horizontalHeaderItem(10) ___qtablewidgetitem10.setText(QCoreApplication.translate("Download", u"\u8f6c\u6362\u72b6\u6001", None)); + self.redownloadRadio.setText(QCoreApplication.translate("Download", u"\u4e0b\u8f7d\u5931\u8d25\u540e1\u5206\u949f\u81ea\u52a8\u91cd\u8bd5", None)) + self.updateNew.setText(QCoreApplication.translate("Download", u"\u66f4\u65b0\u6240\u6709New\u7ae0\u8282", None)) + self.pushButton.setText(QCoreApplication.translate("Download", u"\u5168\u90e8\u5f00\u59cb\u4e0b\u8f7d", None)) + self.pushButton_3.setText(QCoreApplication.translate("Download", u"\u5168\u90e8\u6682\u505c\u4e0b\u8f7d", None)) + self.pushButton_2.setText(QCoreApplication.translate("Download", u"\u5168\u90e8\u5f00\u59cb\u8f6c\u6362", None)) + self.pushButton_4.setText(QCoreApplication.translate("Download", u"\u5168\u90e8\u6682\u505c\u8f6c\u6362", None)) + self.radioButton.setText(QCoreApplication.translate("Download", u"\u4e0b\u8f7d\u81ea\u52a8\u8fdb\u884cWaifu2x\u8f6c\u6362", None)) # retranslateUi diff --git a/src/interface/ui_local.py b/src/interface/ui_local.py index 4ddd1d8..8325946 100644 --- a/src/interface/ui_local.py +++ b/src/interface/ui_local.py @@ -27,7 +27,7 @@ class Ui_Local(object): def setupUi(self, Local): if not Local.objectName(): Local.setObjectName(u"Local") - Local.resize(628, 334) + Local.resize(626, 327) self.gridLayout_2 = QGridLayout(Local) self.gridLayout_2.setObjectName(u"gridLayout_2") self.gridLayout_3 = QGridLayout() diff --git a/src/interface/ui_local_all.py b/src/interface/ui_local_all.py index f97b0dd..f1e86ef 100644 --- a/src/interface/ui_local_all.py +++ b/src/interface/ui_local_all.py @@ -25,7 +25,7 @@ class Ui_LocalAll(object): def setupUi(self, LocalAll): if not LocalAll.objectName(): LocalAll.setObjectName(u"LocalAll") - LocalAll.resize(543, 300) + LocalAll.resize(541, 293) self.verticalLayout = QVBoxLayout(LocalAll) self.verticalLayout.setObjectName(u"verticalLayout") self.horizontalLayout_4 = QHBoxLayout() diff --git a/src/interface/ui_local_favorite.py b/src/interface/ui_local_favorite.py new file mode 100644 index 0000000..e68f92f --- /dev/null +++ b/src/interface/ui_local_favorite.py @@ -0,0 +1,171 @@ +# -*- coding: utf-8 -*- + +################################################################################ +## Form generated from reading UI file 'ui_local_favorite.ui' +## +## Created by: Qt User Interface Compiler version 6.2.4 +## +## WARNING! All changes made in this file will be lost when recompiling UI file! +################################################################################ + +from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale, + QMetaObject, QObject, QPoint, QRect, + QSize, QTime, QUrl, Qt) +from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor, + QFont, QFontDatabase, QGradient, QIcon, + QImage, QKeySequence, QLinearGradient, QPainter, + QPalette, QPixmap, QRadialGradient, QTransform) +from PySide6.QtWidgets import (QApplication, QComboBox, QFrame, QGridLayout, + QHBoxLayout, QLabel, QLineEdit, QListWidgetItem, + QPushButton, QSizePolicy, QSpacerItem, QSpinBox, + QWidget) + +from component.list.comic_list_widget import ComicListWidget + +class Ui_LocalFavorite(object): + def setupUi(self, LocalFavorite): + if not LocalFavorite.objectName(): + LocalFavorite.setObjectName(u"LocalFavorite") + LocalFavorite.resize(628, 334) + self.gridLayout_2 = QGridLayout(LocalFavorite) + self.gridLayout_2.setObjectName(u"gridLayout_2") + self.gridLayout_3 = QGridLayout() + self.gridLayout_3.setObjectName(u"gridLayout_3") + self.bookList = ComicListWidget(LocalFavorite) + self.bookList.setObjectName(u"bookList") + self.bookList.setStyleSheet(u"") + + self.gridLayout_3.addWidget(self.bookList, 0, 0, 1, 1) + + + self.gridLayout_2.addLayout(self.gridLayout_3, 1, 0, 1, 1) + + self.gridLayout_4 = QGridLayout() + self.gridLayout_4.setObjectName(u"gridLayout_4") + self.horizontalLayout = QHBoxLayout() + self.horizontalLayout.setObjectName(u"horizontalLayout") + self.msgLabel = QLabel(LocalFavorite) + self.msgLabel.setObjectName(u"msgLabel") + + self.horizontalLayout.addWidget(self.msgLabel) + + self.sortKeyCombox = QComboBox(LocalFavorite) + self.sortKeyCombox.addItem("") + self.sortKeyCombox.setObjectName(u"sortKeyCombox") + self.sortKeyCombox.setEnabled(True) + self.sortKeyCombox.setMinimumSize(QSize(100, 0)) + + self.horizontalLayout.addWidget(self.sortKeyCombox) + + self.sortIdCombox = QComboBox(LocalFavorite) + self.sortIdCombox.addItem("") + self.sortIdCombox.addItem("") + self.sortIdCombox.setObjectName(u"sortIdCombox") + self.sortIdCombox.setEnabled(True) + + self.horizontalLayout.addWidget(self.sortIdCombox) + + self.horizontalSpacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) + + self.horizontalLayout.addItem(self.horizontalSpacer) + + self.line_2 = QFrame(LocalFavorite) + self.line_2.setObjectName(u"line_2") + self.line_2.setFrameShape(QFrame.VLine) + self.line_2.setFrameShadow(QFrame.Sunken) + + self.horizontalLayout.addWidget(self.line_2) + + self.nums = QLabel(LocalFavorite) + self.nums.setObjectName(u"nums") + self.nums.setMinimumSize(QSize(0, 30)) + + self.horizontalLayout.addWidget(self.nums) + + self.pages = QLabel(LocalFavorite) + self.pages.setObjectName(u"pages") + + self.horizontalLayout.addWidget(self.pages) + + self.line = QFrame(LocalFavorite) + self.line.setObjectName(u"line") + self.line.setFrameShape(QFrame.VLine) + self.line.setFrameShadow(QFrame.Sunken) + + self.horizontalLayout.addWidget(self.line) + + self.line_4 = QFrame(LocalFavorite) + self.line_4.setObjectName(u"line_4") + self.line_4.setFrameShape(QFrame.VLine) + self.line_4.setFrameShadow(QFrame.Sunken) + + self.horizontalLayout.addWidget(self.line_4) + + self.spinBox = QSpinBox(LocalFavorite) + self.spinBox.setObjectName(u"spinBox") + self.spinBox.setMinimumSize(QSize(50, 30)) + self.spinBox.setMinimum(1) + self.spinBox.setMaximum(1) + + self.horizontalLayout.addWidget(self.spinBox) + + self.line_3 = QFrame(LocalFavorite) + self.line_3.setObjectName(u"line_3") + self.line_3.setFrameShape(QFrame.VLine) + self.line_3.setFrameShadow(QFrame.Sunken) + + self.horizontalLayout.addWidget(self.line_3) + + self.jumpButton = QPushButton(LocalFavorite) + self.jumpButton.setObjectName(u"jumpButton") + self.jumpButton.setMinimumSize(QSize(0, 30)) + + self.horizontalLayout.addWidget(self.jumpButton) + + + self.gridLayout_4.addLayout(self.horizontalLayout, 0, 0, 1, 1) + + + self.gridLayout_2.addLayout(self.gridLayout_4, 2, 0, 1, 1) + + self.widget = QWidget(LocalFavorite) + self.widget.setObjectName(u"widget") + self.horizontalLayout_2 = QHBoxLayout(self.widget) + self.horizontalLayout_2.setObjectName(u"horizontalLayout_2") + self.label = QLabel(self.widget) + self.label.setObjectName(u"label") + + self.horizontalLayout_2.addWidget(self.label) + + self.lineEdit = QLineEdit(self.widget) + self.lineEdit.setObjectName(u"lineEdit") + + self.horizontalLayout_2.addWidget(self.lineEdit) + + + self.gridLayout_2.addWidget(self.widget, 0, 0, 1, 1) + + + self.retranslateUi(LocalFavorite) + self.jumpButton.clicked.connect(LocalFavorite.JumpPage) + + QMetaObject.connectSlotsByName(LocalFavorite) + # setupUi + + def retranslateUi(self, LocalFavorite): + LocalFavorite.setWindowTitle(QCoreApplication.translate("LocalFavorite", u"\u6536\u85cf", None)) + self.msgLabel.setText("") + self.sortKeyCombox.setItemText(0, QCoreApplication.translate("LocalFavorite", u"\u6536\u85cf\u65f6\u95f4", None)) + + self.sortIdCombox.setItemText(0, QCoreApplication.translate("LocalFavorite", u"\u964d\u5e8f", None)) + self.sortIdCombox.setItemText(1, QCoreApplication.translate("LocalFavorite", u"\u5347\u5e8f", None)) + + self.nums.setText(QCoreApplication.translate("LocalFavorite", u"\u6536\u85cf\u6570\uff1a", None)) + self.pages.setText(QCoreApplication.translate("LocalFavorite", u"\u9875", None)) + self.jumpButton.setText(QCoreApplication.translate("LocalFavorite", u"\u8df3\u8f6c", None)) +#if QT_CONFIG(shortcut) + self.jumpButton.setShortcut(QCoreApplication.translate("LocalFavorite", u"Return", None)) +#endif // QT_CONFIG(shortcut) + self.label.setText(QCoreApplication.translate("LocalFavorite", u"\u641c\u7d22\uff1a", None)) + # retranslateUi + diff --git a/src/interface/ui_login_proxy_widget.py b/src/interface/ui_login_proxy_widget.py index 85844ef..b3aef26 100644 --- a/src/interface/ui_login_proxy_widget.py +++ b/src/interface/ui_login_proxy_widget.py @@ -20,14 +20,15 @@ QLineEdit, QPushButton, QRadioButton, QSizePolicy, QSpacerItem, QVBoxLayout, QWidget) +from component.box.wheel_combo_box import WheelComboBox from component.scroll_area.smooth_scroll_area import SmoothScrollArea class Ui_LoginProxyWidget(object): def setupUi(self, LoginProxyWidget): if not LoginProxyWidget.objectName(): LoginProxyWidget.setObjectName(u"LoginProxyWidget") - LoginProxyWidget.resize(495, 483) - LoginProxyWidget.setMinimumSize(QSize(450, 0)) + LoginProxyWidget.resize(577, 473) + LoginProxyWidget.setMinimumSize(QSize(550, 0)) self.gridLayout = QGridLayout(LoginProxyWidget) self.gridLayout.setSpacing(12) self.gridLayout.setObjectName(u"gridLayout") @@ -36,7 +37,7 @@ def setupUi(self, LoginProxyWidget): self.scrollArea.setWidgetResizable(True) self.scrollAreaWidgetContents = QWidget() self.scrollAreaWidgetContents.setObjectName(u"scrollAreaWidgetContents") - self.scrollAreaWidgetContents.setGeometry(QRect(0, 0, 458, 483)) + self.scrollAreaWidgetContents.setGeometry(QRect(0, 0, 540, 512)) self.verticalLayout = QVBoxLayout(self.scrollAreaWidgetContents) self.verticalLayout.setObjectName(u"verticalLayout") self.horizontalLayout_11 = QHBoxLayout() @@ -163,6 +164,25 @@ def setupUi(self, LoginProxyWidget): self.verticalLayout.addLayout(self.horizontalLayout_9) + self.horizontalLayout_3 = QHBoxLayout() + self.horizontalLayout_3.setObjectName(u"horizontalLayout_3") + self.label_9 = QLabel(self.scrollAreaWidgetContents) + self.label_9.setObjectName(u"label_9") + + self.horizontalLayout_3.addWidget(self.label_9) + + self.imgCombox = WheelComboBox(self.scrollAreaWidgetContents) + self.imgCombox.setObjectName(u"imgCombox") + + self.horizontalLayout_3.addWidget(self.imgCombox) + + self.horizontalSpacer_2 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) + + self.horizontalLayout_3.addItem(self.horizontalSpacer_2) + + + self.verticalLayout.addLayout(self.horizontalLayout_3) + self.testSpeedButton = QPushButton(self.scrollAreaWidgetContents) self.testSpeedButton.setObjectName(u"testSpeedButton") @@ -177,110 +197,110 @@ def setupUi(self, LoginProxyWidget): self.gridLayout_2 = QGridLayout() self.gridLayout_2.setObjectName(u"gridLayout_2") - self.label_api_2 = QLabel(self.scrollAreaWidgetContents) - self.label_api_2.setObjectName(u"label_api_2") + self.label_img_5 = QLabel(self.scrollAreaWidgetContents) + self.label_img_5.setObjectName(u"label_img_5") - self.gridLayout_2.addWidget(self.label_api_2, 3, 1, 1, 1) + self.gridLayout_2.addWidget(self.label_img_5, 5, 3, 1, 1) - self.label_2 = QLabel(self.scrollAreaWidgetContents) - self.label_2.setObjectName(u"label_2") - self.label_2.setAlignment(Qt.AlignCenter) + self.radioButton_3 = QRadioButton(self.scrollAreaWidgetContents) + self.radioApiGroup = QButtonGroup(LoginProxyWidget) + self.radioApiGroup.setObjectName(u"radioApiGroup") + self.radioApiGroup.addButton(self.radioButton_3) + self.radioButton_3.setObjectName(u"radioButton_3") - self.gridLayout_2.addWidget(self.label_2, 1, 0, 1, 1) + self.gridLayout_2.addWidget(self.radioButton_3, 4, 0, 1, 1) self.radioButton_2 = QRadioButton(self.scrollAreaWidgetContents) - self.radioApiGroup = QButtonGroup(LoginProxyWidget) - self.radioApiGroup.setObjectName(u"radioApiGroup") self.radioApiGroup.addButton(self.radioButton_2) self.radioButton_2.setObjectName(u"radioButton_2") self.gridLayout_2.addWidget(self.radioButton_2, 3, 0, 1, 1) - self.cdn_api_ip = QLineEdit(self.scrollAreaWidgetContents) - self.cdn_api_ip.setObjectName(u"cdn_api_ip") - self.cdn_api_ip.setMaximumSize(QSize(120, 16777215)) + self.cdn_img_ip = QLineEdit(self.scrollAreaWidgetContents) + self.cdn_img_ip.setObjectName(u"cdn_img_ip") + self.cdn_img_ip.setMaximumSize(QSize(120, 16777215)) - self.gridLayout_2.addWidget(self.cdn_api_ip, 8, 1, 1, 1) + self.gridLayout_2.addWidget(self.cdn_img_ip, 8, 3, 1, 1) - self.label_api_1 = QLabel(self.scrollAreaWidgetContents) - self.label_api_1.setObjectName(u"label_api_1") + self.radio_img_6 = QRadioButton(self.scrollAreaWidgetContents) + self.radioImgGroup = QButtonGroup(LoginProxyWidget) + self.radioImgGroup.setObjectName(u"radioImgGroup") + self.radioImgGroup.addButton(self.radio_img_6) + self.radio_img_6.setObjectName(u"radio_img_6") - self.gridLayout_2.addWidget(self.label_api_1, 2, 1, 1, 1) + self.gridLayout_2.addWidget(self.radio_img_6, 6, 2, 1, 1) - self.radioButton_5 = QRadioButton(self.scrollAreaWidgetContents) - self.radioApiGroup.addButton(self.radioButton_5) - self.radioButton_5.setObjectName(u"radioButton_5") + self.radioButton_6 = QRadioButton(self.scrollAreaWidgetContents) + self.radioApiGroup.addButton(self.radioButton_6) + self.radioButton_6.setObjectName(u"radioButton_6") - self.gridLayout_2.addWidget(self.radioButton_5, 5, 0, 1, 1) + self.gridLayout_2.addWidget(self.radioButton_6, 6, 0, 1, 1) - self.label_api_3 = QLabel(self.scrollAreaWidgetContents) - self.label_api_3.setObjectName(u"label_api_3") + self.radio_img_3 = QRadioButton(self.scrollAreaWidgetContents) + self.radioImgGroup.addButton(self.radio_img_3) + self.radio_img_3.setObjectName(u"radio_img_3") - self.gridLayout_2.addWidget(self.label_api_3, 4, 1, 1, 1) + self.gridLayout_2.addWidget(self.radio_img_3, 4, 2, 1, 1) - self.radioButton_1 = QRadioButton(self.scrollAreaWidgetContents) - self.radioApiGroup.addButton(self.radioButton_1) - self.radioButton_1.setObjectName(u"radioButton_1") - self.radioButton_1.setChecked(True) + self.label_11 = QLabel(self.scrollAreaWidgetContents) + self.label_11.setObjectName(u"label_11") + self.label_11.setAlignment(Qt.AlignLeading|Qt.AlignLeft|Qt.AlignVCenter) - self.gridLayout_2.addWidget(self.radioButton_1, 2, 0, 1, 1) + self.gridLayout_2.addWidget(self.label_11, 8, 0, 1, 1) - self.label_api_5 = QLabel(self.scrollAreaWidgetContents) - self.label_api_5.setObjectName(u"label_api_5") + self.radioButton_4 = QRadioButton(self.scrollAreaWidgetContents) + self.radioApiGroup.addButton(self.radioButton_4) + self.radioButton_4.setObjectName(u"radioButton_4") - self.gridLayout_2.addWidget(self.label_api_5, 5, 1, 1, 1) + self.gridLayout_2.addWidget(self.radioButton_4, 7, 0, 1, 1) - self.cdn_img_ip = QLineEdit(self.scrollAreaWidgetContents) - self.cdn_img_ip.setObjectName(u"cdn_img_ip") - self.cdn_img_ip.setMaximumSize(QSize(120, 16777215)) + self.label_img_6 = QLabel(self.scrollAreaWidgetContents) + self.label_img_6.setObjectName(u"label_img_6") - self.gridLayout_2.addWidget(self.cdn_img_ip, 8, 3, 1, 1) + self.gridLayout_2.addWidget(self.label_img_6, 6, 3, 1, 1) - self.label_api_4 = QLabel(self.scrollAreaWidgetContents) - self.label_api_4.setObjectName(u"label_api_4") + self.label_8 = QLabel(self.scrollAreaWidgetContents) + self.label_8.setObjectName(u"label_8") - self.gridLayout_2.addWidget(self.label_api_4, 7, 1, 1, 1) + self.gridLayout_2.addWidget(self.label_8, 8, 2, 1, 1) - self.label_img_3 = QLabel(self.scrollAreaWidgetContents) - self.label_img_3.setObjectName(u"label_img_3") + self.cdn_api_ip = QLineEdit(self.scrollAreaWidgetContents) + self.cdn_api_ip.setObjectName(u"cdn_api_ip") + self.cdn_api_ip.setMaximumSize(QSize(120, 16777215)) - self.gridLayout_2.addWidget(self.label_img_3, 4, 3, 1, 1) + self.gridLayout_2.addWidget(self.cdn_api_ip, 8, 1, 1, 1) - self.label_img_5 = QLabel(self.scrollAreaWidgetContents) - self.label_img_5.setObjectName(u"label_img_5") + self.label_api_3 = QLabel(self.scrollAreaWidgetContents) + self.label_api_3.setObjectName(u"label_api_3") - self.gridLayout_2.addWidget(self.label_img_5, 5, 3, 1, 1) + self.gridLayout_2.addWidget(self.label_api_3, 4, 1, 1, 1) - self.label_8 = QLabel(self.scrollAreaWidgetContents) - self.label_8.setObjectName(u"label_8") + self.label_api_2 = QLabel(self.scrollAreaWidgetContents) + self.label_api_2.setObjectName(u"label_api_2") - self.gridLayout_2.addWidget(self.label_8, 8, 2, 1, 1) + self.gridLayout_2.addWidget(self.label_api_2, 3, 1, 1, 1) - self.radio_img_1 = QRadioButton(self.scrollAreaWidgetContents) - self.radioImgGroup = QButtonGroup(LoginProxyWidget) - self.radioImgGroup.setObjectName(u"radioImgGroup") - self.radioImgGroup.addButton(self.radio_img_1) - self.radio_img_1.setObjectName(u"radio_img_1") - self.radio_img_1.setChecked(True) + self.label_img_3 = QLabel(self.scrollAreaWidgetContents) + self.label_img_3.setObjectName(u"label_img_3") - self.gridLayout_2.addWidget(self.radio_img_1, 2, 2, 1, 1) + self.gridLayout_2.addWidget(self.label_img_3, 4, 3, 1, 1) - self.radio_img_2 = QRadioButton(self.scrollAreaWidgetContents) - self.radioImgGroup.addButton(self.radio_img_2) - self.radio_img_2.setObjectName(u"radio_img_2") + self.label_img_1 = QLabel(self.scrollAreaWidgetContents) + self.label_img_1.setObjectName(u"label_img_1") - self.gridLayout_2.addWidget(self.radio_img_2, 3, 2, 1, 1) + self.gridLayout_2.addWidget(self.label_img_1, 2, 3, 1, 1) - self.label_img_4 = QLabel(self.scrollAreaWidgetContents) - self.label_img_4.setObjectName(u"label_img_4") + self.label_7 = QLabel(self.scrollAreaWidgetContents) + self.label_7.setObjectName(u"label_7") + self.label_7.setAlignment(Qt.AlignCenter) - self.gridLayout_2.addWidget(self.label_img_4, 7, 3, 1, 1) + self.gridLayout_2.addWidget(self.label_7, 1, 2, 1, 1) - self.radio_img_4 = QRadioButton(self.scrollAreaWidgetContents) - self.radioImgGroup.addButton(self.radio_img_4) - self.radio_img_4.setObjectName(u"radio_img_4") + self.label_6 = QLabel(self.scrollAreaWidgetContents) + self.label_6.setObjectName(u"label_6") + self.label_6.setAlignment(Qt.AlignCenter) - self.gridLayout_2.addWidget(self.radio_img_4, 7, 2, 1, 1) + self.gridLayout_2.addWidget(self.label_6, 1, 1, 1, 1) self.radio_img_5 = QRadioButton(self.scrollAreaWidgetContents) self.radioImgGroup.addButton(self.radio_img_5) @@ -288,79 +308,79 @@ def setupUi(self, LoginProxyWidget): self.gridLayout_2.addWidget(self.radio_img_5, 5, 2, 1, 1) - self.radioButton_4 = QRadioButton(self.scrollAreaWidgetContents) - self.radioApiGroup.addButton(self.radioButton_4) - self.radioButton_4.setObjectName(u"radioButton_4") + self.label_4 = QLabel(self.scrollAreaWidgetContents) + self.label_4.setObjectName(u"label_4") + self.label_4.setAlignment(Qt.AlignCenter) - self.gridLayout_2.addWidget(self.radioButton_4, 7, 0, 1, 1) + self.gridLayout_2.addWidget(self.label_4, 1, 3, 1, 1) - self.radioButton_3 = QRadioButton(self.scrollAreaWidgetContents) - self.radioApiGroup.addButton(self.radioButton_3) - self.radioButton_3.setObjectName(u"radioButton_3") + self.radioButton_1 = QRadioButton(self.scrollAreaWidgetContents) + self.radioApiGroup.addButton(self.radioButton_1) + self.radioButton_1.setObjectName(u"radioButton_1") + self.radioButton_1.setChecked(True) - self.gridLayout_2.addWidget(self.radioButton_3, 4, 0, 1, 1) + self.gridLayout_2.addWidget(self.radioButton_1, 2, 0, 1, 1) - self.radio_img_3 = QRadioButton(self.scrollAreaWidgetContents) - self.radioImgGroup.addButton(self.radio_img_3) - self.radio_img_3.setObjectName(u"radio_img_3") + self.radio_img_4 = QRadioButton(self.scrollAreaWidgetContents) + self.radioImgGroup.addButton(self.radio_img_4) + self.radio_img_4.setObjectName(u"radio_img_4") - self.gridLayout_2.addWidget(self.radio_img_3, 4, 2, 1, 1) + self.gridLayout_2.addWidget(self.radio_img_4, 7, 2, 1, 1) - self.label_11 = QLabel(self.scrollAreaWidgetContents) - self.label_11.setObjectName(u"label_11") - self.label_11.setAlignment(Qt.AlignLeading|Qt.AlignLeft|Qt.AlignVCenter) + self.label_api_5 = QLabel(self.scrollAreaWidgetContents) + self.label_api_5.setObjectName(u"label_api_5") - self.gridLayout_2.addWidget(self.label_11, 8, 0, 1, 1) + self.gridLayout_2.addWidget(self.label_api_5, 5, 1, 1, 1) - self.label_6 = QLabel(self.scrollAreaWidgetContents) - self.label_6.setObjectName(u"label_6") - self.label_6.setAlignment(Qt.AlignCenter) + self.label_2 = QLabel(self.scrollAreaWidgetContents) + self.label_2.setObjectName(u"label_2") + self.label_2.setAlignment(Qt.AlignCenter) - self.gridLayout_2.addWidget(self.label_6, 1, 1, 1, 1) + self.gridLayout_2.addWidget(self.label_2, 1, 0, 1, 1) - self.label_img_2 = QLabel(self.scrollAreaWidgetContents) - self.label_img_2.setObjectName(u"label_img_2") + self.label_img_4 = QLabel(self.scrollAreaWidgetContents) + self.label_img_4.setObjectName(u"label_img_4") - self.gridLayout_2.addWidget(self.label_img_2, 3, 3, 1, 1) + self.gridLayout_2.addWidget(self.label_img_4, 7, 3, 1, 1) - self.label_7 = QLabel(self.scrollAreaWidgetContents) - self.label_7.setObjectName(u"label_7") - self.label_7.setAlignment(Qt.AlignCenter) + self.label_api_6 = QLabel(self.scrollAreaWidgetContents) + self.label_api_6.setObjectName(u"label_api_6") - self.gridLayout_2.addWidget(self.label_7, 1, 2, 1, 1) + self.gridLayout_2.addWidget(self.label_api_6, 6, 1, 1, 1) - self.label_4 = QLabel(self.scrollAreaWidgetContents) - self.label_4.setObjectName(u"label_4") - self.label_4.setAlignment(Qt.AlignCenter) + self.label_img_2 = QLabel(self.scrollAreaWidgetContents) + self.label_img_2.setObjectName(u"label_img_2") - self.gridLayout_2.addWidget(self.label_4, 1, 3, 1, 1) + self.gridLayout_2.addWidget(self.label_img_2, 3, 3, 1, 1) - self.label_img_1 = QLabel(self.scrollAreaWidgetContents) - self.label_img_1.setObjectName(u"label_img_1") + self.radio_img_1 = QRadioButton(self.scrollAreaWidgetContents) + self.radioImgGroup.addButton(self.radio_img_1) + self.radio_img_1.setObjectName(u"radio_img_1") + self.radio_img_1.setChecked(True) - self.gridLayout_2.addWidget(self.label_img_1, 2, 3, 1, 1) + self.gridLayout_2.addWidget(self.radio_img_1, 2, 2, 1, 1) - self.radioButton_6 = QRadioButton(self.scrollAreaWidgetContents) - self.radioApiGroup.addButton(self.radioButton_6) - self.radioButton_6.setObjectName(u"radioButton_6") + self.label_api_4 = QLabel(self.scrollAreaWidgetContents) + self.label_api_4.setObjectName(u"label_api_4") - self.gridLayout_2.addWidget(self.radioButton_6, 6, 0, 1, 1) + self.gridLayout_2.addWidget(self.label_api_4, 7, 1, 1, 1) - self.radio_img_6 = QRadioButton(self.scrollAreaWidgetContents) - self.radioImgGroup.addButton(self.radio_img_6) - self.radio_img_6.setObjectName(u"radio_img_6") + self.label_api_1 = QLabel(self.scrollAreaWidgetContents) + self.label_api_1.setObjectName(u"label_api_1") - self.gridLayout_2.addWidget(self.radio_img_6, 6, 2, 1, 1) + self.gridLayout_2.addWidget(self.label_api_1, 2, 1, 1, 1) - self.label_api_6 = QLabel(self.scrollAreaWidgetContents) - self.label_api_6.setObjectName(u"label_api_6") + self.radio_img_2 = QRadioButton(self.scrollAreaWidgetContents) + self.radioImgGroup.addButton(self.radio_img_2) + self.radio_img_2.setObjectName(u"radio_img_2") - self.gridLayout_2.addWidget(self.label_api_6, 6, 1, 1, 1) + self.gridLayout_2.addWidget(self.radio_img_2, 3, 2, 1, 1) - self.label_img_6 = QLabel(self.scrollAreaWidgetContents) - self.label_img_6.setObjectName(u"label_img_6") + self.radioButton_5 = QRadioButton(self.scrollAreaWidgetContents) + self.radioApiGroup.addButton(self.radioButton_5) + self.radioButton_5.setObjectName(u"radioButton_5") - self.gridLayout_2.addWidget(self.label_img_6, 6, 3, 1, 1) + self.gridLayout_2.addWidget(self.radioButton_5, 5, 0, 1, 1) self.verticalLayout.addLayout(self.gridLayout_2) @@ -410,43 +430,44 @@ def retranslateUi(self, LoginProxyWidget): self.checkLabel.setText(QCoreApplication.translate("LoginProxyWidget", u"\u672a\u68c0\u6d4b\u5230\u7cfb\u7edf\u4ee3\u7406", None)) self.ipv6Check.setText(QCoreApplication.translate("LoginProxyWidget", u"\u4f18\u5148\u4f7f\u7528IPV6\u5206\u6d41\uff08\u5206\u6d412\u30013\u652f\u6301\uff09", None)) self.httpsBox.setText(QCoreApplication.translate("LoginProxyWidget", u"\u542f\u7528Https\uff08\u5982\u679c\u51fa\u73b0\u8fde\u63a5\u88ab\u91cd\u7f6e\uff0c\u5efa\u8bae\u5173\u95ed\u8bd5\u8bd5\uff09", None)) + self.label_9.setText(QCoreApplication.translate("LoginProxyWidget", u"\u56fe\u7247\u5730\u5740\u9009\u62e9\uff1a", None)) self.testSpeedButton.setText(QCoreApplication.translate("LoginProxyWidget", u"\u6d4b\u901f", None)) - self.label_api_2.setText("") - self.label_2.setText(QCoreApplication.translate("LoginProxyWidget", u"Api\u5206\u6d41", None)) + self.label_img_5.setText("") + self.radioButton_3.setText(QCoreApplication.translate("LoginProxyWidget", u"\u5206\u6d413", None)) self.radioButton_2.setText(QCoreApplication.translate("LoginProxyWidget", u"\u5206\u6d412", None)) - self.label_api_1.setText("") -#if QT_CONFIG(tooltip) - self.radioButton_5.setToolTip(QCoreApplication.translate("LoginProxyWidget", u"\u6240\u4ee5\u5206\u6d41\u4e0d\u53ef\u4f7f\u7528\u65f6\uff0c\u81ea\u52a8\u89e3\u9501", None)) -#endif // QT_CONFIG(tooltip) - self.radioButton_5.setText(QCoreApplication.translate("LoginProxyWidget", u"JP\u53cd\u4ee3\u5206\u6d41", None)) + self.radio_img_6.setText(QCoreApplication.translate("LoginProxyWidget", u"US\u53cd\u4ee3\u5206\u6d41", None)) + self.radioButton_6.setText(QCoreApplication.translate("LoginProxyWidget", u"US\u53cd\u4ee3\u5206\u6d41", None)) + self.radio_img_3.setText(QCoreApplication.translate("LoginProxyWidget", u"\u5206\u6d413", None)) + self.label_11.setText(QCoreApplication.translate("LoginProxyWidget", u" CDN\u5730\u5740 ", None)) + self.radioButton_4.setText(QCoreApplication.translate("LoginProxyWidget", u"CDN\u5206\u6d41", None)) + self.label_img_6.setText("") + self.label_8.setText(QCoreApplication.translate("LoginProxyWidget", u"CDN\u5730\u5740", None)) self.label_api_3.setText("") - self.radioButton_1.setText(QCoreApplication.translate("LoginProxyWidget", u"\u5206\u6d411", None)) - self.label_api_5.setText("") - self.label_api_4.setText("") + self.label_api_2.setText("") self.label_img_3.setText("") - self.label_img_5.setText("") - self.label_8.setText(QCoreApplication.translate("LoginProxyWidget", u"CDN\u5730\u5740", None)) - self.radio_img_1.setText(QCoreApplication.translate("LoginProxyWidget", u"\u5206\u6d411", None)) - self.radio_img_2.setText(QCoreApplication.translate("LoginProxyWidget", u"\u5206\u6d412", None)) - self.label_img_4.setText("") - self.radio_img_4.setText(QCoreApplication.translate("LoginProxyWidget", u"CDN\u5206\u6d41", None)) + self.label_img_1.setText("") + self.label_7.setText(QCoreApplication.translate("LoginProxyWidget", u"\u56fe\u7247\u5206\u6d41", None)) + self.label_6.setText(QCoreApplication.translate("LoginProxyWidget", u"\u5ef6\u8fdf", None)) #if QT_CONFIG(tooltip) self.radio_img_5.setToolTip(QCoreApplication.translate("LoginProxyWidget", u"\u6240\u4ee5\u5206\u6d41\u4e0d\u53ef\u4f7f\u7528\u65f6\uff0c\u81ea\u52a8\u89e3\u9501", None)) #endif // QT_CONFIG(tooltip) self.radio_img_5.setText(QCoreApplication.translate("LoginProxyWidget", u"JP\u53cd\u4ee3\u5206\u6d41", None)) - self.radioButton_4.setText(QCoreApplication.translate("LoginProxyWidget", u"CDN\u5206\u6d41", None)) - self.radioButton_3.setText(QCoreApplication.translate("LoginProxyWidget", u"\u5206\u6d413", None)) - self.radio_img_3.setText(QCoreApplication.translate("LoginProxyWidget", u"\u5206\u6d413", None)) - self.label_11.setText(QCoreApplication.translate("LoginProxyWidget", u" CDN\u5730\u5740 ", None)) - self.label_6.setText(QCoreApplication.translate("LoginProxyWidget", u"\u5ef6\u8fdf", None)) - self.label_img_2.setText("") - self.label_7.setText(QCoreApplication.translate("LoginProxyWidget", u"\u56fe\u7247\u5206\u6d41", None)) self.label_4.setText(QCoreApplication.translate("LoginProxyWidget", u"\u901f\u5ea6", None)) - self.label_img_1.setText("") - self.radioButton_6.setText(QCoreApplication.translate("LoginProxyWidget", u"US\u53cd\u4ee3\u5206\u6d41", None)) - self.radio_img_6.setText(QCoreApplication.translate("LoginProxyWidget", u"US\u53cd\u4ee3\u5206\u6d41", None)) + self.radioButton_1.setText(QCoreApplication.translate("LoginProxyWidget", u"\u5206\u6d411", None)) + self.radio_img_4.setText(QCoreApplication.translate("LoginProxyWidget", u"CDN\u5206\u6d41", None)) + self.label_api_5.setText("") + self.label_2.setText(QCoreApplication.translate("LoginProxyWidget", u"Api\u5206\u6d41", None)) + self.label_img_4.setText("") self.label_api_6.setText("") - self.label_img_6.setText("") + self.label_img_2.setText("") + self.radio_img_1.setText(QCoreApplication.translate("LoginProxyWidget", u"\u5206\u6d411", None)) + self.label_api_4.setText("") + self.label_api_1.setText("") + self.radio_img_2.setText(QCoreApplication.translate("LoginProxyWidget", u"\u5206\u6d412", None)) +#if QT_CONFIG(tooltip) + self.radioButton_5.setToolTip(QCoreApplication.translate("LoginProxyWidget", u"\u6240\u4ee5\u5206\u6d41\u4e0d\u53ef\u4f7f\u7528\u65f6\uff0c\u81ea\u52a8\u89e3\u9501", None)) +#endif // QT_CONFIG(tooltip) + self.radioButton_5.setText(QCoreApplication.translate("LoginProxyWidget", u"JP\u53cd\u4ee3\u5206\u6d41", None)) self.label_3.setText(QCoreApplication.translate("LoginProxyWidget", u"CDN\u8bbe\u7f6e\u8bf7\u770b\u8bf4\u660e\u83b7\u53d6", None)) self.commandLinkButton.setText(QCoreApplication.translate("LoginProxyWidget", u"\u8bf4\u660e", None)) # retranslateUi diff --git a/src/interface/ui_navigation.py b/src/interface/ui_navigation.py index 2dc9a47..567f37a 100644 --- a/src/interface/ui_navigation.py +++ b/src/interface/ui_navigation.py @@ -15,9 +15,9 @@ QFont, QFontDatabase, QGradient, QIcon, QImage, QKeySequence, QLinearGradient, QPainter, QPalette, QPixmap, QRadialGradient, QTransform) -from PySide6.QtWidgets import (QApplication, QButtonGroup, QFrame, QHBoxLayout, - QLabel, QPushButton, QSizePolicy, QSpacerItem, - QToolButton, QVBoxLayout, QWidget) +from PySide6.QtWidgets import (QApplication, QButtonGroup, QCommandLinkButton, QFrame, + QHBoxLayout, QLabel, QPushButton, QSizePolicy, + QSpacerItem, QToolButton, QVBoxLayout, QWidget) from component.button.switch_button import SwitchButton from component.label.head_label import HeadLabel @@ -28,7 +28,7 @@ class Ui_Navigation(object): def setupUi(self, Navigation): if not Navigation.objectName(): Navigation.setObjectName(u"Navigation") - Navigation.resize(248, 496) + Navigation.resize(376, 772) Navigation.setStyleSheet(u"") self.verticalLayout_2 = QVBoxLayout(Navigation) self.verticalLayout_2.setSpacing(6) @@ -103,7 +103,7 @@ def setupUi(self, Navigation): self.horizontalLayout_3.addWidget(self.label_4) - self.proxyName = QLabel(self.widget) + self.proxyName = QCommandLinkButton(self.widget) self.proxyName.setObjectName(u"proxyName") self.horizontalLayout_3.addWidget(self.proxyName) @@ -118,7 +118,7 @@ def setupUi(self, Navigation): self.horizontalLayout_5.addWidget(self.label_6) - self.proxyImgName = QLabel(self.widget) + self.proxyImgName = QCommandLinkButton(self.widget) self.proxyImgName.setObjectName(u"proxyImgName") self.horizontalLayout_5.addWidget(self.proxyImgName) @@ -148,12 +148,18 @@ def setupUi(self, Navigation): self.verticalLayout.addWidget(self.line_4) + self.verticalSpacer = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding) + + self.verticalLayout.addItem(self.verticalSpacer) + self.scrollArea = SmoothScrollArea(self.widget) self.scrollArea.setObjectName(u"scrollArea") - self.scrollArea.setWidgetResizable(True) + self.scrollArea.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn) + self.scrollArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) + self.scrollArea.setWidgetResizable(False) self.scrollAreaWidgetContents = QWidget() self.scrollAreaWidgetContents.setObjectName(u"scrollAreaWidgetContents") - self.scrollAreaWidgetContents.setGeometry(QRect(0, -431, 211, 792)) + self.scrollAreaWidgetContents.setGeometry(QRect(0, 0, 339, 800)) self.verticalLayout_3 = QVBoxLayout(self.scrollAreaWidgetContents) self.verticalLayout_3.setSpacing(6) self.verticalLayout_3.setObjectName(u"verticalLayout_3") @@ -173,7 +179,7 @@ def setupUi(self, Navigation): sizePolicy1.setVerticalStretch(0) sizePolicy1.setHeightForWidth(self.collectButton.sizePolicy().hasHeightForWidth()) self.collectButton.setSizePolicy(sizePolicy1) - self.collectButton.setMinimumSize(QSize(150, 40)) + self.collectButton.setMinimumSize(QSize(150, 0)) self.collectButton.setFocusPolicy(Qt.NoFocus) icon = QIcon() icon.addFile(u":/images/menu/Contact.png", QSize(), QIcon.Normal, QIcon.Off) @@ -189,7 +195,7 @@ def setupUi(self, Navigation): self.myCommentButton.setObjectName(u"myCommentButton") sizePolicy1.setHeightForWidth(self.myCommentButton.sizePolicy().hasHeightForWidth()) self.myCommentButton.setSizePolicy(sizePolicy1) - self.myCommentButton.setMinimumSize(QSize(150, 40)) + self.myCommentButton.setMinimumSize(QSize(150, 0)) self.myCommentButton.setFocusPolicy(Qt.NoFocus) self.myCommentButton.setStyleSheet(u"") icon1 = QIcon() @@ -206,7 +212,7 @@ def setupUi(self, Navigation): self.lookButton.setObjectName(u"lookButton") sizePolicy1.setHeightForWidth(self.lookButton.sizePolicy().hasHeightForWidth()) self.lookButton.setSizePolicy(sizePolicy1) - self.lookButton.setMinimumSize(QSize(150, 40)) + self.lookButton.setMinimumSize(QSize(150, 0)) self.lookButton.setFocusPolicy(Qt.NoFocus) self.lookButton.setIcon(icon) self.lookButton.setIconSize(QSize(32, 32)) @@ -232,7 +238,7 @@ def setupUi(self, Navigation): self.indexButton.setObjectName(u"indexButton") sizePolicy1.setHeightForWidth(self.indexButton.sizePolicy().hasHeightForWidth()) self.indexButton.setSizePolicy(sizePolicy1) - self.indexButton.setMinimumSize(QSize(150, 40)) + self.indexButton.setMinimumSize(QSize(150, 0)) self.indexButton.setFocusPolicy(Qt.NoFocus) self.indexButton.setIcon(icon) self.indexButton.setIconSize(QSize(32, 32)) @@ -249,7 +255,7 @@ def setupUi(self, Navigation): self.searchButton.setObjectName(u"searchButton") sizePolicy1.setHeightForWidth(self.searchButton.sizePolicy().hasHeightForWidth()) self.searchButton.setSizePolicy(sizePolicy1) - self.searchButton.setMinimumSize(QSize(150, 40)) + self.searchButton.setMinimumSize(QSize(150, 0)) self.searchButton.setFocusPolicy(Qt.NoFocus) self.searchButton.setIcon(icon) self.searchButton.setIconSize(QSize(32, 32)) @@ -265,7 +271,7 @@ def setupUi(self, Navigation): self.msgButton.setObjectName(u"msgButton") sizePolicy1.setHeightForWidth(self.msgButton.sizePolicy().hasHeightForWidth()) self.msgButton.setSizePolicy(sizePolicy1) - self.msgButton.setMinimumSize(QSize(150, 40)) + self.msgButton.setMinimumSize(QSize(150, 0)) self.msgButton.setFocusPolicy(Qt.NoFocus) self.msgButton.setIcon(icon) self.msgButton.setIconSize(QSize(32, 32)) @@ -281,7 +287,7 @@ def setupUi(self, Navigation): self.categoryButton.setObjectName(u"categoryButton") sizePolicy1.setHeightForWidth(self.categoryButton.sizePolicy().hasHeightForWidth()) self.categoryButton.setSizePolicy(sizePolicy1) - self.categoryButton.setMinimumSize(QSize(150, 40)) + self.categoryButton.setMinimumSize(QSize(150, 0)) self.categoryButton.setFocusPolicy(Qt.NoFocus) self.categoryButton.setIcon(icon) self.categoryButton.setIconSize(QSize(32, 32)) @@ -295,7 +301,7 @@ def setupUi(self, Navigation): self.rankButton.setObjectName(u"rankButton") sizePolicy1.setHeightForWidth(self.rankButton.sizePolicy().hasHeightForWidth()) self.rankButton.setSizePolicy(sizePolicy1) - self.rankButton.setMinimumSize(QSize(150, 40)) + self.rankButton.setMinimumSize(QSize(150, 0)) self.rankButton.setFocusPolicy(Qt.NoFocus) self.rankButton.setIcon(icon) self.rankButton.setIconSize(QSize(32, 32)) @@ -309,7 +315,7 @@ def setupUi(self, Navigation): self.chatButton.setObjectName(u"chatButton") sizePolicy1.setHeightForWidth(self.chatButton.sizePolicy().hasHeightForWidth()) self.chatButton.setSizePolicy(sizePolicy1) - self.chatButton.setMinimumSize(QSize(150, 40)) + self.chatButton.setMinimumSize(QSize(150, 0)) self.chatButton.setFocusPolicy(Qt.NoFocus) self.chatButton.setIcon(icon) self.chatButton.setIconSize(QSize(32, 32)) @@ -323,7 +329,7 @@ def setupUi(self, Navigation): self.chatNewButton.setObjectName(u"chatNewButton") sizePolicy1.setHeightForWidth(self.chatNewButton.sizePolicy().hasHeightForWidth()) self.chatNewButton.setSizePolicy(sizePolicy1) - self.chatNewButton.setMinimumSize(QSize(150, 40)) + self.chatNewButton.setMinimumSize(QSize(150, 0)) self.chatNewButton.setFocusPolicy(Qt.NoFocus) self.chatNewButton.setIcon(icon) self.chatNewButton.setIconSize(QSize(32, 32)) @@ -337,7 +343,7 @@ def setupUi(self, Navigation): self.gameButton.setObjectName(u"gameButton") sizePolicy1.setHeightForWidth(self.gameButton.sizePolicy().hasHeightForWidth()) self.gameButton.setSizePolicy(sizePolicy1) - self.gameButton.setMinimumSize(QSize(150, 40)) + self.gameButton.setMinimumSize(QSize(150, 0)) self.gameButton.setFocusPolicy(Qt.NoFocus) self.gameButton.setIcon(icon) self.gameButton.setIconSize(QSize(32, 32)) @@ -351,7 +357,7 @@ def setupUi(self, Navigation): self.friedButton.setObjectName(u"friedButton") sizePolicy1.setHeightForWidth(self.friedButton.sizePolicy().hasHeightForWidth()) self.friedButton.setSizePolicy(sizePolicy1) - self.friedButton.setMinimumSize(QSize(150, 40)) + self.friedButton.setMinimumSize(QSize(150, 0)) self.friedButton.setFocusPolicy(Qt.NoFocus) self.friedButton.setIcon(icon) self.friedButton.setIconSize(QSize(32, 32)) @@ -377,7 +383,7 @@ def setupUi(self, Navigation): self.downloadButton.setObjectName(u"downloadButton") sizePolicy1.setHeightForWidth(self.downloadButton.sizePolicy().hasHeightForWidth()) self.downloadButton.setSizePolicy(sizePolicy1) - self.downloadButton.setMinimumSize(QSize(150, 40)) + self.downloadButton.setMinimumSize(QSize(150, 0)) self.downloadButton.setFocusPolicy(Qt.NoFocus) self.downloadButton.setIcon(icon) self.downloadButton.setIconSize(QSize(32, 32)) @@ -391,7 +397,7 @@ def setupUi(self, Navigation): self.localReadButton.setObjectName(u"localReadButton") sizePolicy1.setHeightForWidth(self.localReadButton.sizePolicy().hasHeightForWidth()) self.localReadButton.setSizePolicy(sizePolicy1) - self.localReadButton.setMinimumSize(QSize(0, 40)) + self.localReadButton.setMinimumSize(QSize(150, 0)) self.localReadButton.setFocusPolicy(Qt.NoFocus) self.localReadButton.setIcon(icon) self.localReadButton.setIconSize(QSize(32, 32)) @@ -405,7 +411,7 @@ def setupUi(self, Navigation): self.convertButton.setObjectName(u"convertButton") sizePolicy1.setHeightForWidth(self.convertButton.sizePolicy().hasHeightForWidth()) self.convertButton.setSizePolicy(sizePolicy1) - self.convertButton.setMinimumSize(QSize(150, 40)) + self.convertButton.setMinimumSize(QSize(150, 0)) self.convertButton.setFocusPolicy(Qt.NoFocus) self.convertButton.setIcon(icon) self.convertButton.setIconSize(QSize(32, 32)) @@ -419,7 +425,7 @@ def setupUi(self, Navigation): self.waifu2xButton.setObjectName(u"waifu2xButton") sizePolicy1.setHeightForWidth(self.waifu2xButton.sizePolicy().hasHeightForWidth()) self.waifu2xButton.setSizePolicy(sizePolicy1) - self.waifu2xButton.setMinimumSize(QSize(150, 40)) + self.waifu2xButton.setMinimumSize(QSize(150, 0)) self.waifu2xButton.setFocusPolicy(Qt.NoFocus) self.waifu2xButton.setIcon(icon) self.waifu2xButton.setIconSize(QSize(32, 32)) @@ -428,10 +434,6 @@ def setupUi(self, Navigation): self.verticalLayout_3.addWidget(self.waifu2xButton) - self.verticalSpacer = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding) - - self.verticalLayout_3.addItem(self.verticalSpacer) - self.scrollArea.setWidget(self.scrollAreaWidgetContents) self.verticalLayout.addWidget(self.scrollArea) diff --git a/src/interface/ui_read_tool.py b/src/interface/ui_read_tool.py index 891bdf0..b310999 100644 --- a/src/interface/ui_read_tool.py +++ b/src/interface/ui_read_tool.py @@ -40,7 +40,7 @@ def setupUi(self, ReadImg): self.scrollArea22.setWidgetResizable(True) self.scrollAreaWidgetContents = QWidget() self.scrollAreaWidgetContents.setObjectName(u"scrollAreaWidgetContents") - self.scrollAreaWidgetContents.setGeometry(QRect(0, 0, 310, 825)) + self.scrollAreaWidgetContents.setGeometry(QRect(0, -27, 310, 825)) self.verticalLayout_2 = QVBoxLayout(self.scrollAreaWidgetContents) self.verticalLayout_2.setObjectName(u"verticalLayout_2") self.verticalLayout = QVBoxLayout() @@ -538,8 +538,8 @@ def retranslateUi(self, ReadImg): self.comboBox.setItemText(7, QCoreApplication.translate("ReadImg", u"\u7b49\u5bbd\u6a21\u5f0f", None)) self.zoomLabel.setText(QCoreApplication.translate("ReadImg", u"\u7f29\u653e\uff08120%\uff09", None)) - self.label_7.setText(QCoreApplication.translate("ReadImg", u"\u81ea\u52a8\u6eda\u52a8/\u7ffb\u9875", None)) - self.label_10.setText(QCoreApplication.translate("ReadImg", u"\u6eda\u52a8\u901f\u5ea6\uff08\u50cf\u7d20\uff09\uff1a", None)) + self.label_7.setText(QCoreApplication.translate("ReadImg", u"\u6eda\u52a8/\u7ffb\u9875", None)) + self.label_10.setText(QCoreApplication.translate("ReadImg", u"\u6eda\u52a8\u901f\u5ea6\uff1a", None)) self.label_11.setText(QCoreApplication.translate("ReadImg", u"\u7ffb\u9875\u901f\u5ea6\uff08\u79d2\uff09\uff1a", None)) self.pushButton_2.setText(QCoreApplication.translate("ReadImg", u"\u9690\u85cf", None)) self.fullButton.setText(QCoreApplication.translate("ReadImg", u"\u5168\u5c4f", None)) diff --git a/src/interface/ui_setting_new.py b/src/interface/ui_setting_new.py index 65b01ab..4a0aacf 100644 --- a/src/interface/ui_setting_new.py +++ b/src/interface/ui_setting_new.py @@ -29,7 +29,7 @@ class Ui_SettingNew(object): def setupUi(self, SettingNew): if not SettingNew.objectName(): SettingNew.setObjectName(u"SettingNew") - SettingNew.resize(880, 789) + SettingNew.resize(946, 789) SettingNew.setStyleSheet(u"") self.verticalLayout_2 = QVBoxLayout(SettingNew) self.verticalLayout_2.setObjectName(u"verticalLayout_2") @@ -83,7 +83,7 @@ def setupUi(self, SettingNew): self.scrollArea.setWidgetResizable(True) self.scrollAreaWidgetContents = QWidget() self.scrollAreaWidgetContents.setObjectName(u"scrollAreaWidgetContents") - self.scrollAreaWidgetContents.setGeometry(QRect(0, -541, 661, 2891)) + self.scrollAreaWidgetContents.setGeometry(QRect(0, 0, 727, 3043)) self.scrollAreaWidgetContents.setStyleSheet(u"") self.verticalLayout_4 = QVBoxLayout(self.scrollAreaWidgetContents) self.verticalLayout_4.setObjectName(u"verticalLayout_4") @@ -471,6 +471,56 @@ def setupUi(self, SettingNew): self.verticalLayout_17.addLayout(self.horizontalLayout_22) + self.label_44 = QLabel(self.frame_14) + self.label_44.setObjectName(u"label_44") + font2 = QFont() + font2.setPointSize(12) + font2.setBold(True) + self.label_44.setFont(font2) + + self.verticalLayout_17.addWidget(self.label_44) + + self.horizontalLayout_34 = QHBoxLayout() + self.horizontalLayout_34.setObjectName(u"horizontalLayout_34") + self.titleLineBox = WheelComboBox(self.frame_14) + self.titleLineBox.addItem("") + self.titleLineBox.addItem("") + self.titleLineBox.addItem("") + self.titleLineBox.addItem("") + self.titleLineBox.addItem("") + self.titleLineBox.setObjectName(u"titleLineBox") + + self.horizontalLayout_34.addWidget(self.titleLineBox) + + self.horizontalSpacer_32 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) + + self.horizontalLayout_34.addItem(self.horizontalSpacer_32) + + + self.verticalLayout_17.addLayout(self.horizontalLayout_34) + + self.label_45 = QLabel(self.frame_14) + self.label_45.setObjectName(u"label_45") + self.label_45.setFont(font2) + + self.verticalLayout_17.addWidget(self.label_45) + + self.horizontalLayout_35 = QHBoxLayout() + self.horizontalLayout_35.setObjectName(u"horizontalLayout_35") + self.categoryBox = WheelComboBox(self.frame_14) + self.categoryBox.addItem("") + self.categoryBox.addItem("") + self.categoryBox.setObjectName(u"categoryBox") + + self.horizontalLayout_35.addWidget(self.categoryBox) + + self.horizontalSpacer_33 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) + + self.horizontalLayout_35.addItem(self.horizontalSpacer_33) + + + self.verticalLayout_17.addLayout(self.horizontalLayout_35) + self.verticalLayout_4.addWidget(self.frame_14) @@ -1192,6 +1242,11 @@ def setupUi(self, SettingNew): self.horizontalLayout_10.addWidget(self.setDirButton) + self.setLogDirButton = QPushButton(self.frame_11) + self.setLogDirButton.setObjectName(u"setLogDirButton") + + self.horizontalLayout_10.addWidget(self.setLogDirButton) + self.horizontalSpacer_8 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) self.horizontalLayout_10.addItem(self.horizontalSpacer_8) @@ -1303,6 +1358,37 @@ def setupUi(self, SettingNew): self.verticalLayout_13.addLayout(self.horizontalLayout_19) + self.horizontalLayout_31 = QHBoxLayout() + self.horizontalLayout_31.setObjectName(u"horizontalLayout_31") + self.label_42 = QLabel(self.frame_11) + self.label_42.setObjectName(u"label_42") + self.label_42.setMinimumSize(QSize(80, 0)) + + self.horizontalLayout_31.addWidget(self.label_42) + + self.logLabel = QLabel(self.frame_11) + self.logLabel.setObjectName(u"logLabel") + self.logLabel.setMinimumSize(QSize(150, 0)) + + self.horizontalLayout_31.addWidget(self.logLabel) + + self.openLogDir = QPushButton(self.frame_11) + self.openLogDir.setObjectName(u"openLogDir") + + self.horizontalLayout_31.addWidget(self.openLogDir) + + self.horizontalSpacer_28 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) + + self.horizontalLayout_31.addItem(self.horizontalSpacer_28) + + + self.verticalLayout_13.addLayout(self.horizontalLayout_31) + + self.horizontalLayout_30 = QHBoxLayout() + self.horizontalLayout_30.setObjectName(u"horizontalLayout_30") + + self.verticalLayout_13.addLayout(self.horizontalLayout_30) + self.verticalLayout_4.addWidget(self.frame_11) @@ -1380,10 +1466,21 @@ def retranslateUi(self, SettingNew): self.showCloseButton1.setText(QCoreApplication.translate("SettingNew", u"\u5173\u95ed\u540e\u6700\u5c0f\u5316\u5230\u6258\u76d8", None)) self.label_2.setText(QCoreApplication.translate("SettingNew", u"\u5c01\u9762\u663e\u793a\u5927\u5c0f\uff08\u9ed8\u8ba4\u4e3a100%\uff09\uff1a", None)) self.label_29.setText(QCoreApplication.translate("SettingNew", u"\u5206\u7c7b\u5c01\u9762\u5927\u5c0f\uff1a", None)) + self.label_44.setText(QCoreApplication.translate("SettingNew", u"\u6807\u9898\u663e\u793a\u884c\u6570", None)) + self.titleLineBox.setItemText(0, QCoreApplication.translate("SettingNew", u"\u4e0d\u663e\u793a", None)) + self.titleLineBox.setItemText(1, QCoreApplication.translate("SettingNew", u"1\u884c", None)) + self.titleLineBox.setItemText(2, QCoreApplication.translate("SettingNew", u"2\u884c", None)) + self.titleLineBox.setItemText(3, QCoreApplication.translate("SettingNew", u"3\u884c", None)) + self.titleLineBox.setItemText(4, QCoreApplication.translate("SettingNew", u"\u5168\u90e8", None)) + + self.label_45.setText(QCoreApplication.translate("SettingNew", u"\u5206\u7c7b\u663e\u793a", None)) + self.categoryBox.setItemText(0, QCoreApplication.translate("SettingNew", u"\u663e\u793a", None)) + self.categoryBox.setItemText(1, QCoreApplication.translate("SettingNew", u"\u4e0d\u663e\u793a", None)) + self.label_10.setText(QCoreApplication.translate("SettingNew", u"\u65e5\u5fd7\u7b49\u7ea7\uff1a", None)) self.logutton0.setText(QCoreApplication.translate("SettingNew", u"Warn", None)) self.logutton1.setText(QCoreApplication.translate("SettingNew", u"Info", None)) - self.logutton2.setText(QCoreApplication.translate("SettingNew", u"Debug", None)) + self.logutton2.setText(QCoreApplication.translate("SettingNew", u"Debug\uff08\u8be5\u8bbe\u7f6e\u5185\u542b\u654f\u611f\u6570\u636e\uff0clog\u6587\u4ef6\u8bf7\u52ff\u5206\u4eab\uff09", None)) self.proxyLabel.setText(QCoreApplication.translate("SettingNew", u"\u4ee3\u7406", None)) self.label_7.setText(QCoreApplication.translate("SettingNew", u"Http\u4ee3\u7406\uff1a", None)) self.proxy0.setText(QCoreApplication.translate("SettingNew", u"\u65e0\u4ee3\u7406", None)) @@ -1461,6 +1558,7 @@ def retranslateUi(self, SettingNew): self.saveNameButton2.setText(QCoreApplication.translate("SettingNew", u"\u4f5c\u8005\u540d\u5355\u72ec\u76ee\u5f55\uff08\u5982\u65e0\u4f5c\u8005\u540d\u5c06\u653e\u5165default\u76ee\u5f55\uff09", None)) self.label_21.setText(QCoreApplication.translate("SettingNew", u"\u4e0b\u8f7d\u548c\u7f13\u5b58\u8def\u5f84\uff08\u7f13\u5b58\u6587\u4ef6\u9700\u81ea\u5df1\u624b\u52a8\u6e05\u9664\uff09", None)) self.setDirButton.setText(QCoreApplication.translate("SettingNew", u"\u8bbe\u7f6e\u76ee\u5f55", None)) + self.setLogDirButton.setText(QCoreApplication.translate("SettingNew", u"\u8bbe\u7f6e\u65e5\u5fd7\u76ee\u5f55", None)) self.label_3.setText(QCoreApplication.translate("SettingNew", u"\u4e0b\u8f7d", None)) self.downloadDir.setText("") self.openDownloadDir.setText(QCoreApplication.translate("SettingNew", u"\u6253\u5f00\u76ee\u5f55", None)) @@ -1473,5 +1571,8 @@ def retranslateUi(self, SettingNew): self.label_5.setText(QCoreApplication.translate("SettingNew", u"Waifu2x\u7f13\u5b58", None)) self.waifu2xDir.setText("") self.openWaifu2xDir.setText(QCoreApplication.translate("SettingNew", u"\u6253\u5f00\u76ee\u5f55", None)) + self.label_42.setText(QCoreApplication.translate("SettingNew", u"\u65e5\u5fd7", None)) + self.logLabel.setText("") + self.openLogDir.setText(QCoreApplication.translate("SettingNew", u"\u6253\u5f00\u76ee\u5f55", None)) # retranslateUi diff --git a/src/qt_owner.py b/src/qt_owner.py index 52db1d8..4b31c8d 100644 --- a/src/qt_owner.py +++ b/src/qt_owner.py @@ -78,6 +78,19 @@ def CopyText(self, text): from tools.str import Str QtOwner().ShowMsg(Str.GetStr(Str.CopySuc)) + def OpenProxy(self): + from view.user.login_view import LoginView + loginView = LoginView(QtOwner().owner, False) + loginView.tabWidget.setCurrentIndex(3) + loginView.tabWidget.removeTab(0) + loginView.tabWidget.removeTab(0) + loginView.tabWidget.removeTab(0) + loginView.loginButton.setText(Str.GetStr(Str.Save)) + loginView.show() + + loginView.closed.connect(QtOwner().owner.navigationWidget.UpdateProxyName) + return + @property def owner(self): from view.main.main_view import MainView diff --git a/src/requirements.txt b/src/requirements.txt index d7f51e5..e4a0c50 100644 --- a/src/requirements.txt +++ b/src/requirements.txt @@ -1,9 +1,10 @@ -PySide6>=6.1,<6.5 -Pillow>=9.5 -PySocks>=1.7 -natsort>=8.2 -requests>=2.26 -urllib3>=2 +PySide6==6.2.4 +websocket-client==0.59.0 +requests==2.26.0 +urllib3==1.25.11 + +pillow==8.3.2 waifu2x-vulkan==1.1.6 -websocket-client>=1.6 -# pywin32>=302 +Pysocks==1.7.1 +natsort==8.2.0 +# pywin32==302 \ No newline at end of file diff --git a/src/server/req.py b/src/server/req.py index 4c22757..2d376d5 100644 --- a/src/server/req.py +++ b/src/server/req.py @@ -1,5 +1,6 @@ import base64 import os +import platform import random import uuid from datetime import datetime, timedelta @@ -57,8 +58,11 @@ def __str__(self): # headers["authorization"] = "**********" params = dict() params.update(self.params) - if Setting.LogIndex.value == 1 and "password" in params: - params["password"] = "******" + ## 脱敏数据 + # if self.__class__.__name__ in ["LoginReq", "RegisterReq"]: + # params = "******" + # if Setting.LogIndex.value == 1 and "password" in params: + # params["password"] = "******" return "{}, url:{}, method:{}, params:{}".format(self.__class__.__name__, self.url, self.method, params) @@ -357,6 +361,7 @@ def __init__(self, isPre=False): method = "GET" data = dict() data["version"] = config.UpdateVersion + data["platform"] = platform.platform() if not isPre: url = config.AppUrl + "/version.txt?" else: @@ -373,13 +378,27 @@ def __init__(self, newVersion): method = "GET" data = dict() data["version"] = config.UpdateVersion + data["platform"] = platform.platform() url = config.AppUrl + "/{}.txt?".format(newVersion) url += ToolUtil.DictToUrl(data) super(self.__class__, self).__init__(url, {}, {}, method) self.isParseRes = False self.useImgProxy = False - - + + +# 检查更新配置 +class CheckUpdateConfigReq(ServerReq): + def __init__(self): + method = "GET" + data = dict() + data["version"] = config.UpdateVersion + data["platform"] = platform.platform() + url = config.AppUrl + "/config.txt?" + url += ToolUtil.DictToUrl(data) + super(self.__class__, self).__init__(url, {}, {}, method) + self.isParseRes = False + self.useImgProxy = False + # 检查更新 class CheckUpdateDatabaseReq(ServerReq): def __init__(self, url): @@ -489,12 +508,17 @@ def __init__(self): if SpeedTestReq.Index >= len(SpeedTestReq.URLS): SpeedTestReq.Index = 0 method = "Download" + host = ToolUtil.GetUrlHost(url) + if host in config.ApiDomain and Setting.ProxySelectIndex.value == 5: + self.headers.pop("user-agent") + self.proxyUrl = config.ProxyApiDomain + header = ToolUtil.GetHeader(url, method) header['cache-control'] = 'no-cache' header['expires'] = '0' header['pragma'] = 'no-cache' self.isReload = False - self.resetCnt = 2 + self.resetCnt = 1 self.isReset = False super(self.__class__, self).__init__(url, header, {}, method) diff --git a/src/server/res.py b/src/server/res.py index 8040742..8fb75e2 100644 --- a/src/server/res.py +++ b/src/server/res.py @@ -4,8 +4,9 @@ class BaseRes(object): - def __init__(self, data, isParseRes) -> None: + def __init__(self, data, isParseRes, reqName) -> None: super().__init__() + self.reqName = reqName self.raw = data self.data = {} self.code = 0 @@ -25,6 +26,9 @@ def __str__(self): data = self.GetText() else: data = "" + # 脱敏数据 + # if self.reqName in ["LoginReq", "RegisterReq"]: + # return "code:{}, raw:{}".format(self.code, data.replace("\n", "")[:20]) return "code:{}, raw:{}".format(self.code, data.replace("\n", "")) def GetText(self): diff --git a/src/server/server.py b/src/server/server.py index 82a84db..4d166e7 100644 --- a/src/server/server.py +++ b/src/server/server.py @@ -11,6 +11,7 @@ import server.req as req import server.res as res from config import config +from config.global_config import GlobalConfig from qt_owner import QtOwner from task.qt_task import TaskBase from tools.log import Log @@ -81,6 +82,7 @@ def __init__(self) -> None: self.session = requests.session() self.address = "" self.imageServer = "" + self.imageAddress = "" self.token = "" self._inQueue = Queue() @@ -131,13 +133,14 @@ def RunDownload(self): Log.Error(es) pass - def UpdateDns(self, address, imageAddress): - self.imageServer = imageAddress + def UpdateDns(self, address, imageUrl, imageAdress): + self.imageServer = imageUrl self.address = address + self.imageAddress = imageAdress AllDomain = config.ApiDomain[:] - AllDomain.append(config.ImageServer2) - AllDomain.append(config.ImageServer2Jump) + # AllDomain.append(config.ImageServer2) + # AllDomain.append(config.ImageServer2Jump) # AllDomain.append(config.ImageServer3Jump) for domain in AllDomain: if is_ipaddress(address): @@ -145,10 +148,16 @@ def UpdateDns(self, address, imageAddress): elif not address and domain in host_table: host_table.pop(domain) - for domain in config.ImageDomain: - if is_ipaddress(imageAddress): - host_table[domain] = imageAddress - elif not imageAddress and domain in host_table: + for domain in GlobalConfig.ImageServerList.value: + if is_ipaddress(imageAdress): + host_table[domain] = imageAdress + elif not imageAdress and domain in host_table: + host_table.pop(domain) + + for domain in GlobalConfig.ImageJumList.value: + if is_ipaddress(imageAdress): + host_table[domain] = imageAdress + elif not imageAdress and domain in host_table: host_table.pop(domain) # 换一个,清空pool @@ -162,7 +171,7 @@ def __DealHeaders(self, request, token): request.headers["authorization"] = token host = ToolUtil.GetUrlHost(request.url) - if self.imageServer and host in config.ImageDomain: + if self.imageServer and host in GlobalConfig.ImageServerList.value: if not is_ipaddress(self.imageServer): request.url = request.url.replace(host, self.imageServer) @@ -170,6 +179,7 @@ def __DealHeaders(self, request, token): request.url = request.url.replace("https://", "http://") if request.proxyUrl: + host = ToolUtil.GetUrlHost(request.url) request.url = request.url.replace(host, request.proxyUrl+"/"+host) # host = ToolUtil.GetUrlHost(request.url) @@ -258,14 +268,14 @@ def Post(self, task): if request.headers == None: request.headers = {} - task.res = res.BaseRes("", False) + task.res = res.BaseRes("", False, task.req.__class__.__name__) if request.file: r = self.session.post(request.url, proxies=request.proxy, headers=request.headers, files=request.file, timeout=task.timeout, verify=False) else: r = self.session.post(request.url, proxies=request.proxy, headers=request.headers, data=json.dumps(request.params), timeout=task.timeout, verify=False) - task.res = res.BaseRes(r, request.isParseRes) + task.res = res.BaseRes(r, request.isParseRes, task.req.__class__.__name__) return task def Put(self, task): @@ -276,9 +286,9 @@ def Put(self, task): if request.headers == None: request.headers = {} - task.res = res.BaseRes("", False) + task.res = res.BaseRes("", False, task.req.__class__.__name__) r = self.session.put(request.url, proxies=request.proxy, headers=request.headers, data=json.dumps(request.params), timeout=60, verify=False) - task.res = res.BaseRes(r, request.isParseRes) + task.res = res.BaseRes(r, request.isParseRes, task.req.__class__.__name__) return task def Get(self, task): @@ -289,9 +299,9 @@ def Get(self, task): if request.headers == None: request.headers = {} - task.res = res.BaseRes("", False) + task.res = res.BaseRes("", False, task.req.__class__.__name__) r = self.session.get(request.url, proxies=request.proxy, headers=request.headers, timeout=task.timeout, verify=False) - task.res = res.BaseRes(r, request.isParseRes) + task.res = res.BaseRes(r, request.isParseRes, task.req.__class__.__name__) return task def Download(self, request, token="", backParams="", isASync=True): @@ -337,6 +347,7 @@ def _Download(self, task): Log.Info("request reset:{} -> backId:{}, {}".format(task.req.resetCnt, task.bakParam, task.req)) history = [] + # oldHost = ToolUtil.GetUrlHost(request.url) for i in range(10): r = self.session.get(request.url, proxies=request.proxy, headers=request.headers, timeout=task.timeout, verify=False, allow_redirects=False, stream=True) @@ -351,7 +362,7 @@ def _Download(self, task): Log.Info("request 301 reset:{} -> backId:{}, {}".format(task.req.resetCnt, task.bakParam, task.req)) history.append(r) - self.__DealHeaders(request, "") + # self.__DealHeaders(request, "") else: break r.history = history @@ -387,6 +398,7 @@ def TestSpeed(self, request, bakParams=""): self.__DealHeaders(request, "") task = Task(request, bakParams) task.timeout = 5 + self._downloadQueue.put(task) def TestSpeedPing(self, request, bakParams=""): diff --git a/src/server/sql_server.py b/src/server/sql_server.py index b2252d7..0469d16 100644 --- a/src/server/sql_server.py +++ b/src/server/sql_server.py @@ -511,6 +511,10 @@ def Search2(wordList, isTitle, isAuthor, isDes, isTag, isCategory, isCreator, ca else: orWords.append(words) + if not andWords and not exclude: + orWords = [] + orWords.append(wordList) + data = "" data2 = "" for word in orWords: diff --git a/src/server/user_handler.py b/src/server/user_handler.py index e57add0..d6a4935 100644 --- a/src/server/user_handler.py +++ b/src/server/user_handler.py @@ -304,7 +304,6 @@ def __call__(self, task): return if task.res.raw.status_code != 200: return - verData = task.res.GetText() info = verData.replace("v", "").split(".") version = int(info[0]) * 100 + int(info[1]) * 10 + int(info[2]) * 1 @@ -339,7 +338,26 @@ def __call__(self, task): finally: if task.bakParam: TaskBase.taskObj.taskBack.emit(task.bakParam, pickle.dumps(data)) - + + +@handler(req.CheckUpdateConfigReq) +class CheckUpdateConfigHandler(object): + def __call__(self, task): + data = {"st": task.status, "data": ""} + try: + if not task.res.GetText() or task.status == Status.NetError: + return + if task.res.raw.status_code != 200: + return + + data["data"] = task.res.GetText() + except Exception as es: + pass + finally: + if task.bakParam: + TaskBase.taskObj.taskBack.emit(task.bakParam, pickle.dumps(data)) + + @handler(req.SpeedTestReq) class SpeedTestHandler(object): def __call__(self, backData): diff --git a/src/start.py b/src/start.py index cd530c1..a1d380d 100644 --- a/src/start.py +++ b/src/start.py @@ -90,7 +90,6 @@ if config.CanWaifu2x: waifu2x_vulkan.stop() sys.exit(-111) - sts = app.exec() socket.close() main.Close() diff --git a/src/task/task_convert_epub.py b/src/task/task_convert_epub.py new file mode 100644 index 0000000..99418b3 --- /dev/null +++ b/src/task/task_convert_epub.py @@ -0,0 +1,13 @@ + + +class TaskConvertEpub(object): + def __init__(self): + pass + + def start(self): + return + + +if __name__ == "__main__": + t = TaskConvertEpub() + t.start() diff --git a/src/task/task_download.py b/src/task/task_download.py index 89d9b48..1d5d28a 100644 --- a/src/task/task_download.py +++ b/src/task/task_download.py @@ -22,6 +22,8 @@ class QtDownloadTask(object): Error = Str.Error Cache = Str.Cache SpaceEps = Str.SpaceEps + UnderReviewBook = Str.UnderReviewBook + # Str.UnderReviewBook def __init__(self, downloadId=0): self.downloadId = downloadId @@ -204,32 +206,45 @@ def HandlerDownload(self, data, v): return isReset = False + st = data['st'] if data["st"] != Status.Ok: task.resetCnt += 1 + if data['st'] == Status.UnderReviewBook: + self.SetTaskStatus(taskId, backData, task.UnderReviewBook) + return + # 失败了 - if task.resetCnt >= 5: + if task.resetCnt >= 10: self.SetTaskStatus(taskId, backData, task.Error) return isReset = True else: + # 防止死循环 + if task.resetCnt >= 50: + self.SetTaskStatus(taskId, backData, task.Error) + return task.status = newStatus + info = BookMgr().GetBook(task.bookId) if task.status == task.Waiting: - isReset or self.SetTaskStatus(taskId, backData, task.Reading) + # isReset or self.SetTaskStatus(taskId, backData, task.Reading) if not info: if task.isLocal: task.isLocal = False + task.resetCnt += 1 self.AddSqlTask("book", task.bookId, SqlServer.TaskTypeCacheBook, self.HandlerDownload, (taskId, task.Waiting)) else: + task.resetCnt += 1 self.AddHttpTask(req.GetComicsBookReq(task.bookId), self.HandlerDownload, (taskId, task.Reading), task.cleanFlag) return task.status = task.Reading if task.status == task.Reading: - isReset or self.SetTaskStatus(taskId, backData, task.ReadingEps) + # isReset or self.SetTaskStatus(taskId, backData, task.ReadingEps) if info.maxLoadEps <= 0: + task.resetCnt += 1 self.AddHttpTask(req.GetComicsBookEpsReq(task.bookId), self.HandlerDownload, (taskId, task.Reading), task.cleanFlag) return @@ -239,13 +254,16 @@ def HandlerDownload(self, data, v): if task.epsId not in info.epsDict: loadPage = (info.epsCount - task.epsId - 1) // info.epsLimit + + task.resetCnt += 1 self.AddHttpTask(req.GetComicsBookEpsReq(task.bookId, loadPage+1), self.HandlerDownload, (taskId, task.Reading), task.cleanFlag) return task.status = task.ReadingEps if task.status == task.ReadingEps: - isReset or self.SetTaskStatus(taskId, backData, task.ReadingPicture) + # isReset or self.SetTaskStatus(taskId, backData, task.ReadingPicture) if task.epsId not in info.epsDict: + Log.Warn("eps space error, book_id:{}, eps_id:{}".format(task.bookId, task.epsId)) self.SetTaskStatus(taskId, backData, task.SpaceEps) return @@ -253,14 +271,17 @@ def HandlerDownload(self, data, v): assert isinstance(epsInfo, BookEps) if epsInfo.isSpace: + Log.Warn("eps space error, book_id:{}, eps:{}".format(task.bookId, epsInfo)) self.SetTaskStatus(taskId, backData, task.SpaceEps) return if epsInfo.maxPicPages <= 0: + task.resetCnt += 1 self.AddHttpTask(req.GetComicsBookOrderReq(task.bookId, task.epsId+1), self.HandlerDownload, (taskId, task.ReadingEps), task.cleanFlag) return if task.index not in epsInfo.pics: loadPage = task.index // epsInfo.picLimit + 1 + task.resetCnt += 1 self.AddHttpTask(req.GetComicsBookOrderReq(task.bookId, task.epsId+1, loadPage), self.HandlerDownload, (taskId, task.ReadingPicture), task.cleanFlag) return @@ -269,6 +290,11 @@ def HandlerDownload(self, data, v): epsInfo = info.epsDict[task.epsId] assert isinstance(epsInfo, BookEps) if epsInfo.isSpace: + Log.Warn("eps space error, book_id:{}, eps:{}".format(task.bookId, epsInfo)) + self.SetTaskStatus(taskId, backData, task.SpaceEps) + return + if epsInfo.maxPics <= 0: + Log.Warn("eps maxPics error, book_id:{}, eps:{}".format(task.bookId, epsInfo)) self.SetTaskStatus(taskId, backData, task.SpaceEps) return @@ -351,7 +377,7 @@ def HandlerTaskSt(self, downloadId, data): task.status = st # print("st:{} {}".format(task.status, data)) status = task.status - if status == task.Downloading or status == task.Error or status == task.SpaceEps or status == task.Cache: + if status == task.Downloading or status == task.Error or status == task.SpaceEps or status == task.Cache or status == task.UnderReviewBook: self.ClearDownloadTask(downloadId) except Exception as es: Log.Error(es) diff --git a/src/tools/book.py b/src/tools/book.py index 310b1ff..05cc6c3 100644 --- a/src/tools/book.py +++ b/src/tools/book.py @@ -20,7 +20,7 @@ def __init__(self): self.title = "" # 章节名 self.order = 0 # 排序 self.id = "" # id - self.maxPics = 1 # 总页数 + self.maxPics = 0 # 总页数 self.pics = {} # 图片 self.curLoadPicPages = set() @@ -28,6 +28,9 @@ def __init__(self): self.isSpace = False # 某些章节出现了空白的问题,导致死循环了 self.picLimit = 0 + def __str__(self): + return "order:{}, maxPics:{}, pics:{}, curLoadPicPages:{}, maxPicPages:{}, picLimit:{}".format(self.order, self.maxPics, len(self.pics), self.curLoadPicPages, self.maxPicPages, self.picLimit) + # 一本书 class Book(object): @@ -197,6 +200,7 @@ def AddBookEpsPicInfoBack(self, backData): # 空白章节 if epsInfo.maxPics == 0: + Log.Warn("eps space, book_id:{}, data:{}".format(bookId, r.GetText())) epsInfo.isSpace = True # 重新初始化 diff --git a/src/tools/str.py b/src/tools/str.py index 8caa242..4cd2fcb 100644 --- a/src/tools/str.py +++ b/src/tools/str.py @@ -77,6 +77,7 @@ class Str: ErrorPath = 4005 # "错误的路径" NotPictureFile = 4006 # "没有发现图片文件" FileLock = 4007 # "文件已加密" + SpacePic = 4008 # "文件已加密" CvSuccess = 5001 # "完成" CvReading = 5002 # 获取信息 @@ -237,6 +238,7 @@ class Str: ImportDouble = 152 # 导入多章节目录 ImportLocal = 153 # 导入本地漫画中 NotUpdateEps = 154 # 没有可更新的章节 + CurRead = 155 # 正在看 @classmethod @@ -311,6 +313,7 @@ def Reload(cls): cls.strDict[cls.ErrorPath] = QCoreApplication.translate("cls.obj", "错误的路径", None) cls.strDict[cls.NotPictureFile] = QCoreApplication.translate("cls.obj", "没有发现图片文件", None) cls.strDict[cls.FileLock] = QCoreApplication.translate("cls.obj", "文件已加密", None) + cls.strDict[cls.SpacePic] = QCoreApplication.translate("cls.obj", "空白章节", None) cls.strDict[cls.CvSuccess] = QCoreApplication.translate("cls.obj", "完成", None) cls.strDict[cls.CvReading] = QCoreApplication.translate("cls.obj", "获取信息", None) @@ -463,6 +466,7 @@ def Reload(cls): cls.strDict[cls.ImportDouble] = QCoreApplication.translate("cls.obj", "导入多章节目录", None) cls.strDict[cls.ImportLocal] = QCoreApplication.translate("cls.obj", "导入到本地漫画中", None) cls.strDict[cls.NotUpdateEps] = QCoreApplication.translate("cls.obj", "没有可更新章节", None) + cls.strDict[cls.CurRead] = QCoreApplication.translate("cls.obj", "正在看", None) @classmethod def GetStr(cls, enumType, defualt=""): diff --git a/src/tools/user.py b/src/tools/user.py index d745a51..53745d3 100644 --- a/src/tools/user.py +++ b/src/tools/user.py @@ -58,48 +58,48 @@ def address(self): def address(self, value): self.server.address = value - def InitBack(self, backData): - try: - if backData.status == Status.Ok and backData.res.status == "ok": - - from config.setting import Setting - if len(backData.res.addresses) >= 1: - # 只更新分流三 第一个地址 - config.Address[1] = backData.res.addresses[0] - Setting.SaveCacheAddress.SetValue(backData.res.addresses[0]) - # self.address = backData.res.addresses[0] - Log.Info("初始化成功, Ips:{}, {}".format(backData.res.addresses, config.Address)) - # 需要更新DNS - if Setting.ProxySelectIndex.value == 3: - imageServer = config.ImageServer3 - address = config.Address[1] - if not Setting.PreIpv6.value: - self.server.UpdateDns(address, imageServer) - self.initRes = backData.res - return Status.Ok - else: - Log.Info("初始化失败, info:{}".format(backData.res)) - return Status.Error - except Exception as es: - Log.Error(es) - return Status.Error - - def InitImageServer(self, backData): - try: - if self.server.address: - self.server.imageServer = self.imageServer - if backData.res.code == 200: - # 选择了分流才设置 - # if self.server.address: - # self.server.imageServer = ToolUtil.GetUrlHost(backData.res.data["imageServer"]) - # Log.Info("初始化图片服务器成功, info:{}".format(self.server.imageServer)) - - return Status.Ok - else: - return Status.Error - except Exception as es: - Log.Error(es) - return Status.Error + # def InitBack(self, backData): + # try: + # if backData.status == Status.Ok and backData.res.status == "ok": + # + # from config.setting import Setting + # if len(backData.res.address) >= 1: + # # 只更新分流三 第一个地址 + # config.Address[1] = backData.res.address[0] + # Setting.SaveCacheAddress.SetValue(backData.res.address[0]) + # # self.address = backData.res.addresses[0] + # Log.Info("初始化成功, Ips:{}, {}".format(backData.res.address, config.Address)) + # # 需要更新DNS + # if Setting.ProxySelectIndex.value == 3: + # imageServer = config.ImageServer3 + # address = config.Address[1] + # if not Setting.PreIpv6.value: + # self.server.UpdateDns(address, imageServer) + # self.initRes = backData.res + # return Status.Ok + # else: + # Log.Info("初始化失败, info:{}".format(backData.res)) + # return Status.Error + # except Exception as es: + # Log.Error(es) + # return Status.Error + + # def InitImageServer(self, backData): + # try: + # if self.server.address: + # self.server.imageServer = self.imageServer + # if backData.res.code == 200: + # # 选择了分流才设置 + # # if self.server.address: + # # self.server.imageServer = ToolUtil.GetUrlHost(backData.res.data["imageServer"]) + # # Log.Info("初始化图片服务器成功, info:{}".format(self.server.imageServer)) + # + # return Status.Ok + # else: + # return Status.Error + # except Exception as es: + # Log.Error(es) + # return Status.Error def SetUserInfo(self, userId, passwd): self.userId = userId diff --git a/src/view/download/download_item.py b/src/view/download/download_item.py index e65409a..7bb022a 100644 --- a/src/view/download/download_item.py +++ b/src/view/download/download_item.py @@ -21,6 +21,7 @@ class DownloadItem(QtTaskBase): Pause = Str.Pause Error = Str.Error SpaceEps = Str.SpaceEps + UnderReviewBook = Str.UnderReviewBook Converting = Str.Converting ConvertSuccess = Str.ConvertSuccess diff --git a/src/view/download/download_status.py b/src/view/download/download_status.py index 395ef47..13e6513 100644 --- a/src/view/download/download_status.py +++ b/src/view/download/download_status.py @@ -28,11 +28,15 @@ def SetNewStatus(self, task, status, statusMsg=""): self._SetTaskWait(task) elif status == task.Pause: self._SetTaskPause(task) - elif status == task.Error or status == task.SpaceEps: + elif status == task.Error or status == task.SpaceEps or status == task.UnderReviewBook: + task.speedStr = "" + task.speed = 0 self._SetDownloadTaskNone(task) elif status == task.Downloading: self._SetTaskDownloading(task) elif status == task.Success: + task.speedStr = "" + task.speed = 0 self._SetDownloadTaskNone(task) else: assert False @@ -48,7 +52,7 @@ def SetNewCovertStatus(self, task, status, msg=""): self._SetTaskConvertWait(task) elif status == task.Pause: self._SetTaskConvertPause(task) - elif status == task.Error or status == task.SpaceEps: + elif status == task.Error or status == task.SpaceEps or status == task.UnderReviewBook: self._SetTaskConvertNone(task) elif status == task.Converting: self._SetTaskConverting(task) @@ -207,6 +211,12 @@ def DownloadStCallBack(self, data, taskId): self.UpdateTableItem(task) elif st == Str.SpaceEps: self.SetNewStatus(task, task.SpaceEps) + elif st == Str.UnderReviewBook: + from tools.book import BookMgr + info = BookMgr().GetBook(task.bookId) + if info: + task.title = info.title + self.SetNewStatus(task, task.UnderReviewBook) else: self.SetNewStatus(task, task.Error) return diff --git a/src/view/download/download_view.py b/src/view/download/download_view.py index 0a37c0a..42c4817 100644 --- a/src/view/download/download_view.py +++ b/src/view/download/download_view.py @@ -186,7 +186,7 @@ def SendLocalBack(self, books, bookID): def AddDownload(self, bookId, downloadIds): if not downloadIds: return - + Log.Info("add download, book_id={}, eps={}".format(bookId, downloadIds)) if bookId not in self.downloadDict: task = DownloadItem() task.bookId = bookId @@ -369,14 +369,14 @@ def SelectMenu(self, pos): menu.addAction(openDirAction) menu.addAction(selectEpsAction) assert isinstance(task, DownloadItem) - if task.status in [task.Pause, task.Error, task.SpaceEps]: + if task.status in [task.Pause, task.Error, task.SpaceEps, task.UnderReviewBook]: menu.addAction(startAction) elif task.status in [task.Downloading, task.Waiting]: menu.addAction(pauseAction) if task.convertStatus in [task.Converting, task.Waiting]: menu.addAction(pauseConvertAction) - elif task.convertStatus in [task.Pause, task.Error, task.SpaceEps]: + elif task.convertStatus in [task.Pause, task.Error, task.SpaceEps, task.UnderReviewBook]: menu.addAction(startConvertAction) else: menu = QMenu(self.tableWidget) diff --git a/src/view/help/help_view.py b/src/view/help/help_view.py index bdb54f0..c07827a 100644 --- a/src/view/help/help_view.py +++ b/src/view/help/help_view.py @@ -10,6 +10,7 @@ from PySide6.QtWidgets import QWidget, QMessageBox from config import config +from config.global_config import GlobalConfig from config.setting import Setting from interface.ui_help import Ui_Help from qt_owner import QtOwner @@ -69,6 +70,7 @@ def SwitchCheckPre(self): Setting.IsPreUpdate.SetValue(int(self.preCheckBox.isChecked())) def Init(self): + # self.InitUpdateConfig() self.CheckDb() # self.UpdateDbInfo() @@ -106,8 +108,22 @@ def InitUpdateBack(self, raw): def InitUpdateInfoBack(self, raw): data = raw.get("data") self.SetNewUpdate(self.updateBackUrl[self.checkUpdateIndex], Str.GetStr(Str.CurVersion) + config.UpdateVersion + ", "+ Str.GetStr(Str.CheckUpdateAndUp) + "\n\n" + data) - - + + def InitUpdateConfig(self): + self.AddHttpTask(req.CheckUpdateConfigReq(), self.InitUpdateConfigBack) + + def InitUpdateConfigBack(self, raw): + try: + st = raw.get("st") + if st != Str.Ok: + return + data = raw.get("data") + if not data: + return + GlobalConfig.UpdateSetting(data) + except Exception as es: + Log.Error(es) + def CheckDb(self): self.AddSqlTask("book", "", SqlServer.TaskCheck, self.CheckDbBack) diff --git a/src/view/main/main_view.py b/src/view/main/main_view.py index 4e4719e..25063e2 100644 --- a/src/view/main/main_view.py +++ b/src/view/main/main_view.py @@ -1,6 +1,6 @@ from functools import partial -from PySide6.QtCore import Qt, QEvent, QPoint, Signal, QTimer +from PySide6.QtCore import Qt, QEvent, QPoint, Signal, QTimer, QSize from PySide6.QtGui import QIcon, QMouseEvent, QGuiApplication, QFont from PySide6.QtWidgets import QButtonGroup, QToolButton, QLabel @@ -10,6 +10,7 @@ from component.system_tray_icon.my_system_tray_icon import MySystemTrayIcon from component.widget.main_widget import Main from config import config +from config.global_config import GlobalConfig from config.setting import Setting from qt_owner import QtOwner from server import req @@ -36,7 +37,7 @@ def __init__(self): # self.setAttribute(Qt.WA_TranslucentBackground) self.setAttribute(Qt.WA_QuitOnClose, True) self.timer = QTimer() - self.timer.setInterval(10000) + self.timer.setInterval(1000) # self.timer.timeout.connect(self.AfterStartSuc) screens = QGuiApplication.screens() # print(screens[0].geometry(), screens[1].geometry()) @@ -46,6 +47,7 @@ def __init__(self): desktop = screens[Setting.ScreenIndex.value].geometry() self.adjustSize() + self.myInitSize = QSize(desktop.width() // 4 * 3, desktop.height() // 4 * 3) self.resize(desktop.width() // 4 * 3, desktop.height() // 4 * 3) self.move(self.width() // 8+desktop.x(), max(0, desktop.height()-self.height()) // 2+desktop.y()) print(desktop.size(), self.size()) @@ -64,6 +66,7 @@ def __init__(self): self.subStackWidget.setCurrentIndex(0) self.settingView.LoadSetting() + GlobalConfig.LoadSetting() self.searchView.searchTab.hide() self.searchView2.searchWidget.hide() @@ -150,6 +153,7 @@ def RetranslateUi(self): self.navigationWidget.retranslateUi(self.navigationWidget) def Init(self): + print(self.size()) IsCanUse = False self.downloadView.Init() self.InitApiProxy() @@ -204,7 +208,7 @@ def Init(self): if Setting.IsUpdate.value: self.helpView.InitUpdate() - + self.helpView.InitUpdateConfig() self.searchView.InitWord() self.msgLabel = MsgLabel(self) self.msgLabel.hide() @@ -217,13 +221,13 @@ def Init(self): # self.timer.start() # def AfterStartSuc(self): - # self.timer.stop() - # QtOwner().SetFont(self) + # # self.timer.stop() + # self.resize(self.myInitSize.width(), self.myInitSize.height()) def InitApiProxy(self): request = req.InitReq() request.proxy = {} - self.AddHttpTask(request) + # self.AddHttpTask(request) def ClearTabBar(self): for toolButton in self.toolButtons: diff --git a/src/view/read/read_graphics.py b/src/view/read/read_graphics.py index 14a33da..e30ffb1 100644 --- a/src/view/read/read_graphics.py +++ b/src/view/read/read_graphics.py @@ -1,7 +1,8 @@ import time from PySide6.QtCore import Qt, QEvent, QPoint, Signal, QRect, QFile -from PySide6.QtGui import QPainter, QFont, QPixmap, QFontMetrics, QWheelEvent +from PySide6.QtGui import QPainter, QFont, QPixmap, QFontMetrics, QWheelEvent, QSurfaceFormat +from PySide6.QtOpenGLWidgets import QOpenGLWidget from PySide6.QtWidgets import QGraphicsView, QFrame, QGraphicsItem, QGraphicsScene, \ QGraphicsPixmapItem, QGraphicsProxyWidget, QScroller, QAbstractSlider, QApplication @@ -27,15 +28,17 @@ def __init__(self, parent=None): QGraphicsView.__init__(self, parent) SmoothScroll.__init__(self) - self.vScrollBar = ReadScroll() + self.vScrollBar = ReadScroll(parent) self.vScrollBar.setOrientation(Qt.Orientation.Vertical) self.setVerticalScrollBar(self.vScrollBar) + # self.animation.finished.connect(self.Finished) - self.hScrollBar = ReadScroll() + self.hScrollBar = ReadScroll(parent) self.hScrollBar.setOrientation(Qt.Orientation.Horizontal) self.setHorizontalScrollBar(self.hScrollBar) + # self.horizontalScrollBar().valueChanged.connect(self.HValueChange) self.changeLastPage.connect(self.ChangeLastPage) self.changeNextPage.connect(self.ChangeNextPage) @@ -56,6 +59,11 @@ def __init__(self, parent=None): self.setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform) self.setCacheMode(QGraphicsView.CacheBackground) # self.setCacheMode(QGraphicsView.CacheNone) + self.openGl = QOpenGLWidget() + f = QSurfaceFormat() + f.setSamples(4) + self.openGl.setFormat(f) + self.setViewport(self.openGl) self.setViewportUpdateMode(QGraphicsView.FullViewportUpdate) # self.setViewportUpdateMode(QGraphicsView.SmartViewportUpdate) @@ -91,13 +99,13 @@ def __init__(self, parent=None): # QScroller.scroller(self).setScrollerProperties(properties) self.labelSize = {} # index: value - self.oldValue = 0 + # self.oldValue = 0 self.resetImg = False # QScroller.scroller(self).stateChanged.connect(self.StateChanged) - self.verticalScrollBar().actionTriggered.connect(self.OnActionTriggered) + # self.verticalScrollBar().actionTriggered.connect(self.OnActionTriggered) - self.horizontalScrollBar().actionTriggered.connect(self.OnActionTriggered) + # self.horizontalScrollBar().actionTriggered.connect(self.OnActionTriggered) self.verticalScrollBar() self.startPos = QPoint() self.labelWaifu2xState = {} @@ -105,6 +113,7 @@ def __init__(self, parent=None): QtOwner().owner.WindowsSizeChange.connect(self.ResetSize) self.isLastPageMode = False # 是否切换到上一页 + @property def readImg(self): return self.parent().readImg @@ -171,19 +180,26 @@ def wheelEvent(self, e: QWheelEvent): if self.verticalScrollBar().value() <= 5: self.parent().qtTool.LastPage() return - - return SmoothScroll.wheelEvent(self, e) + if e.angleDelta().y() < 0: + value = int(self.qtTool.scrollSpeed.value()) + else: + value = - int(self.qtTool.scrollSpeed.value()) + if self.initReadMode != ReadMode.RightLeftScroll: + self.vScrollBar.scrollValue(value) + else: + self.hScrollBar.scrollValue(value) def Scale(self, ratio): + oldValue = self.verticalScrollBar().value() self.scale(ratio, ratio) self.ScaleResetVer() for k, v in self.labelSize.items(): self.labelSize[k] *= ratio if self.qtTool.stripModel == ReadMode.UpDown: - self.verticalScrollBar().setValue(self.verticalScrollBar().value() * ratio) + self.verticalScrollBar().ForceSetValue( oldValue* ratio) elif ReadMode.isScroll(self.qtTool.stripModel): - self.horizontalScrollBar().setValue(self.horizontalScrollBar().value() * ratio) + self.horizontalScrollBar().ForceSetValue(oldValue * ratio) self.ResetMaxNum() def ScaleReset(self, oldScaleCnt): @@ -200,9 +216,9 @@ def ScaleReset(self, oldScaleCnt): else: ratio = 0.9 if self.qtTool.stripModel == ReadMode.UpDown: - self.verticalScrollBar().setValue(self.verticalScrollBar().value() * ratio) + self.verticalScrollBar().ForceSetValue(self.verticalScrollBar().value() * ratio) elif ReadMode.isScroll(self.qtTool.stripModel): - self.horizontalScrollBar().setValue(self.horizontalScrollBar().value() * ratio) + self.horizontalScrollBar().ForceSetValue(self.horizontalScrollBar().value() * ratio) newV2 = self.verticalScrollBar().value() # print(oldV, newV, newV2) # if oldScaleCnt != 0: @@ -220,14 +236,14 @@ def ResetSize(self): def ScaleResetVer(self): if not ReadMode.isScroll(self.initReadMode) or self.initReadMode == ReadMode.UpDown: if self.scaleCnt != 0: - self.horizontalScrollBar().setValue((self.horizontalScrollBar().maximum() / 2)) + self.horizontalScrollBar().ForceSetValue((self.horizontalScrollBar().maximum() / 2)) else: - self.horizontalScrollBar().setValue(0) + self.horizontalScrollBar().ForceSetValue(0) else: if self.scaleCnt != 0: - self.verticalScrollBar().setValue((self.verticalScrollBar().maximum() / 2)) + self.verticalScrollBar().ForceSetValue((self.verticalScrollBar().maximum() / 2)) else: - self.verticalScrollBar().setValue(0) + self.verticalScrollBar().ForceSetValue(0) def SetLabel(self, label, i): text = str(i + 1) @@ -263,10 +279,10 @@ def ScrollValue(self, value): if self.initReadMode == ReadMode.Samewight and self.vScrollBar.value() >= self.vScrollBar.maximum(): self.ScrollNextPage() else: - self.vScrollBar.Scroll(value, 100) + self.vScrollBar.scrollValue(value) # QScroller.scroller(self).scrollTo(QPoint(0, self.verticalScrollBar().value() + value), 500) else: - self.hScrollBar.Scroll(value, 100) + self.hScrollBar.scrollValue(value) # QScroller.scroller(self).scrollTo(QPoint(self.horizontalScrollBar().value() + value, 0), 500) return @@ -353,9 +369,9 @@ def eventFilter(self, obj, ev) -> bool: self.ScrollNextPage() return False - def StateChanged(self, state): - if state == QScroller.State.Inactive: - self.OnValueChange(self.GetScrollBar().value()) + # def StateChanged(self, state): + # if state == QScroller.State.Inactive: + # self.OnValueChange(self.GetScrollBar().value()) # return print(state) def GetScrollBar(self): @@ -370,77 +386,17 @@ def GetOtherScrollBar(self): else: return self.verticalScrollBar() - def OnActionTriggered(self, action): - if action != QAbstractSlider.SliderMove: - return - self.OnValueChange(self.GetScrollBar().value()) - - def OnValueChange(self, value): - addValue = value - self.oldValue - # self.UpdateScrollBar(value) - self.oldValue = value - - if not ReadMode.isScroll(self.initReadMode): - return - - curPictureSize = self.labelSize.get(self.readImg.curIndex) - nextPictureSize = self.labelSize.get(self.readImg.curIndex + 1, 0) - if self.initReadMode == ReadMode.RightLeftScroll: - while True: - newValue = value + self.width() - ## 切换上一图片 - if addValue > 0 and value >= nextPictureSize: - if self.readImg.curIndex <= 0: - QtOwner().ShowMsg(Str.GetStr(Str.AlreadyLastPage)) - return - self.readImg.curIndex -= 1 - # print(self.readImg.curIndex) - self.changeLastPage.emit(self.readImg.curIndex) - - ## 切换下一图片 - elif addValue < 0 and newValue < curPictureSize: - if self.readImg.curIndex >= self.readImg.maxPic - 1: - QtOwner().ShowMsg(Str.GetStr(Str.AlreadyNextPage)) - return - self.readImg.curIndex += 1 - # print(self.readImg.curIndex) - self.changeNextPage.emit(self.readImg.curIndex) - else: - break - curPictureSize = self.labelSize.get(self.readImg.curIndex) - nextPictureSize = self.labelSize.get(self.readImg.curIndex + 1, 0) - else: - while True: - ## 切换上一图片 - if addValue < 0 and value < curPictureSize: - if self.readImg.curIndex <= 0: - QtOwner().ShowMsg(Str.GetStr(Str.AlreadyLastPage)) - return - self.readImg.curIndex -= 1 - # print("last page, addv:{}, val:{}, cur:{}, next:{}".format(addValue, value, curPictureSize, nextPictureSize)) - self.changeLastPage.emit(self.readImg.curIndex) - - ## 切换下一图片 - elif addValue > 0 and value >= nextPictureSize: - if self.readImg.curIndex >= self.readImg.maxPic - 1: - QtOwner().ShowMsg(Str.GetStr(Str.AlreadyNextPage)) - return - self.readImg.curIndex += 1 - # print("next page, addv:{}, val:{}, cur:{}, next:{}".format(addValue, value, curPictureSize, nextPictureSize)) - self.changeNextPage.emit(self.readImg.curIndex) - else: - break - - curPictureSize = self.labelSize.get(self.readImg.curIndex) - nextPictureSize = self.labelSize.get(self.readImg.curIndex + 1, 0) - return + # def OnActionTriggered(self, action): + # if action != QAbstractSlider.SliderMove: + # return + # self.OnValueChange(self.GetScrollBar().value()) def InitAllQLabel(self, maxNum, curIndex): if not maxNum: return - self.verticalScrollBar().setValue(0) + self.verticalScrollBar().ForceSetValue(0) # self.horizontalScrollBar().setValue(0) - self.oldValue = 0 + # self.oldValue = 0 self.qtTool.CloseScrollAndTurn() # if self.initReadMode and self.readImg.stripModel != self.initReadMode: if self.initReadMode: @@ -488,8 +444,8 @@ def InitAllQLabel(self, maxNum, curIndex): proxy.setPos(value/self.GetScaleCntRatio(), 0) value = self.labelSize.get(curIndex) - self.oldValue = value - self.GetScrollBar().setValue(value) + # self.oldValue = value + self.GetScrollBar().ForceSetValue(value) # print(value) # print(self.labelSize) elif self.initReadMode in [ReadMode.LeftRight, ReadMode.Samewight]: @@ -506,15 +462,20 @@ def InitAllQLabel(self, maxNum, curIndex): self.ResetMaxNum() return - def ResetMaxNum(self): + def ResetMaxNum(self, addHeight=0): maxNum = self.maxPic if maxNum <= 0: return if self.initReadMode == ReadMode.RightLeftScroll: maxSize = max(self.width(), self.labelSize.get(-1, 0) - self.width()) + 50 + oldV = self.GetScrollBar().value() self.graphicsScene.setSceneRect(0, 0, maxSize, self.height()) self.GetScrollBar().setMaximum(maxSize) + if addHeight > 0: + self.GetScrollBar().ResetAniValueByAdd(oldV, addHeight) + else: + self.GetScrollBar().ForceSetValue(oldV+ addHeight) self.ScaleResetVer() elif self.initReadMode in [ReadMode.LeftRightScroll]: maxSize = max(self.width(), self.labelSize.get(maxNum, 0) - self.width()) + 50 @@ -522,14 +483,20 @@ def ResetMaxNum(self): oldV = self.GetScrollBar().value() self.graphicsScene.setSceneRect(0, 0, maxSize, self.height()) self.GetScrollBar().setMaximum(maxSize) - self.GetScrollBar().setValue(oldV) + if addHeight > 0: + self.GetScrollBar().ResetAniValueByAdd(oldV, addHeight) + else: + self.GetScrollBar().ForceSetValue(oldV + addHeight) self.ScaleResetVer() elif self.initReadMode in [ReadMode.UpDown]: maxSize = max(self.height(), self.labelSize.get(maxNum, 0) - self.height()) + 50 oldV = self.GetScrollBar().value() self.graphicsScene.setSceneRect(0, 0, self.width(), maxSize) self.GetScrollBar().setMaximum(maxSize) - self.GetScrollBar().setValue(oldV) + if addHeight > 0: + self.GetScrollBar().ResetAniValueByAdd(oldV, addHeight) + else: + self.GetScrollBar().ForceSetValue(oldV) self.ScaleResetVer() else: self.graphicsScene.setSceneRect(0, 0, self.width(), max(self.height(), @@ -786,29 +753,31 @@ def UpdateOtherHeight(self, index, oldHeight, height): return oldValue = self.GetScrollBar().value() - + needAddHeight = 0 if self.initReadMode == ReadMode.RightLeftScroll: indexList = list(range(index - 1, -1, -1)) indexList.append(-1) # TODO 需要重新定位 curPixcurSize = self.labelSize.get(index) if curPixcurSize <= oldValue: - self.GetScrollBar().setValue(oldValue + addHeight2) - self.oldValue = self.GetScrollBar().value() + needAddHeight = addHeight + # self.GetScrollBar().ResetAniValueByAdd(oldValue, addHeight2) + # self.oldValue = self.GetScrollBar().value() elif self.initReadMode in [ReadMode.LeftRightScroll, ReadMode.UpDown]: indexList = list(range(index + 1, self.readImg.maxPic)) indexList.append(self.readImg.maxPic) curPixcurSize = self.labelSize.get(index) # TODO 需要重新定位 - if curPixcurSize <= oldValue: - self.GetScrollBar().setValue(oldValue + addHeight2) - self.oldValue = self.GetScrollBar().value() + if curPixcurSize < oldValue: + needAddHeight = addHeight + # self.GetScrollBar().ResetAniValueByAdd(oldValue, addHeight2) + # self.oldValue = self.GetScrollBar().value() else: self.ResetMaxNum() if self.initReadMode in [ReadMode.Samewight, ReadMode.LeftRight] and self.isLastPageMode: - self.verticalScrollBar().setValue(self.verticalScrollBar().maximum()) + self.verticalScrollBar().ForceSetValue(self.verticalScrollBar().maximum()) return for lenSize, i in enumerate(indexList): @@ -834,13 +803,13 @@ def UpdateOtherHeight(self, index, oldHeight, height): else: proxy.setPos(oldPos.x() + addHeight, oldPos.y()) - self.ResetMaxNum() + self.ResetMaxNum(needAddHeight) return def ResetScrollValue(self, index): if ReadMode.isScroll(self.initReadMode): value = self.labelSize.get(index) - self.GetScrollBar().setValue(value) + self.GetScrollBar().ForceSetValue(value) # 上一页 def ChangeLastPage(self, index): @@ -875,7 +844,7 @@ def ChangePage(self, oldIndex, index): for newIndex in range(oldIndex + 1, min(oldIndex + config.PreLoading, self.maxPic)): self.SetPixIem(newIndex, None) value = self.labelSize.get(index) - self.GetScrollBar().setValue(value) + self.GetScrollBar().ForceSetValue(value) self.isLastPageMode = False self.ReloadImg() return @@ -914,7 +883,7 @@ def ChangeScale(self, scale=1): def ReloadImg(self): if self.initReadMode in [ReadMode.LeftRightDouble, ReadMode.RightLeftDouble2, ReadMode.RightLeftDouble, ReadMode.LeftRight, ReadMode.Samewight]: - self.verticalScrollBar().setValue(0) + self.verticalScrollBar().ForceSetValue(0) # self.UpdateAllPixIem() self.readImg.CheckClearProcess() self.readImg.ShowImgAll() diff --git a/src/view/read/read_tool.py b/src/view/read/read_tool.py index 0e4b25d..81bf0f8 100644 --- a/src/view/read/read_tool.py +++ b/src/view/read/read_tool.py @@ -626,15 +626,15 @@ def TimeOut(self): if self.stripModel == ReadMode.UpDown: if self.imgFrame.scrollArea.vScrollBar.value() >= self.imgFrame.scrollArea.vScrollBar.maximum(): self.CloseScrollAndTurn() - self.imgFrame.scrollArea.vScrollBar.Scroll(value) + self.imgFrame.scrollArea.vScrollBar.scrollValue(value) elif self.stripModel == ReadMode.LeftRightScroll: if self.imgFrame.scrollArea.hScrollBar.value() >= self.imgFrame.scrollArea.hScrollBar.maximum(): self.CloseScrollAndTurn() - self.imgFrame.scrollArea.hScrollBar.Scroll(value) + self.imgFrame.scrollArea.hScrollBar.scrollValue(value) elif self.stripModel == ReadMode.RightLeftScroll: if self.imgFrame.scrollArea.hScrollBar.value() <= self.imgFrame.scrollArea.hScrollBar.minimum(): self.CloseScrollAndTurn() - self.imgFrame.scrollArea.hScrollBar.Scroll(-value) + self.imgFrame.scrollArea.hScrollBar.scrollValue(-value) else: self._NextPage() pass \ No newline at end of file diff --git a/src/view/setting/setting_view.py b/src/view/setting/setting_view.py index b7409e8..a32e77c 100644 --- a/src/view/setting/setting_view.py +++ b/src/view/setting/setting_view.py @@ -71,6 +71,9 @@ def __init__(self, parent=None): self.downNoise.currentIndexChanged.connect(partial(self.CheckRadioEvent, Setting.DownloadNoise)) self.encodeSelect.currentTextChanged.connect(partial(self.CheckRadioEvent, Setting.SelectEncodeGpu)) self.threadSelect.currentIndexChanged.connect(partial(self.CheckRadioEvent, Setting.Waifu2xCpuCore)) + self.titleLineBox.currentIndexChanged.connect(partial(self.CheckRadioEvent, Setting.TitleLine)) + self.categoryBox.currentIndexChanged.connect(partial(self.CheckRadioEvent, Setting.NotCategoryShow)) + self.fontBox.currentTextChanged.connect(partial(self.CheckRadioEvent, Setting.FontName)) self.fontSize.currentTextChanged.connect(partial(self.CheckRadioEvent, Setting.FontSize)) self.fontStyle.currentIndexChanged.connect(partial(self.CheckRadioEvent, Setting.FontStyle)) @@ -93,12 +96,14 @@ def __init__(self, parent=None): self.downloadButton.clicked.connect(partial(self.MoveToLabel, self.downloadLabel)) self.setDirButton.clicked.connect(self.SelectSavePath) + self.setLogDirButton.clicked.connect(self.SelectSaveLogPath) self.openDownloadDir.clicked.connect(partial(self.OpenDir, self.downloadDir)) self.openChatDir.clicked.connect(partial(self.OpenDir, self.chatDir)) self.openCacheDir.clicked.connect(partial(self.OpenDir, self.cacheDir)) self.openWaifu2xDir.clicked.connect(partial(self.OpenDir, self.waifu2xDir)) + self.openLogDir.clicked.connect(partial(self.OpenDir, self.logLabel)) - self.openProxy.clicked.connect(self.OpenProxy) + self.openProxy.clicked.connect(QtOwner().OpenProxy) # TODO # self.languageButton3.setVisible(False) @@ -242,6 +247,8 @@ def InitSetting(self): self.readScale.setValue(Setting.LookScale.value) self.readModel.setCurrentIndex(Setting.LookModel.value) + self.categoryBox.setCurrentIndex(Setting.NotCategoryShow.value) + self.titleLineBox.setCurrentIndex(Setting.TitleLine.value) self.tileComboBox.setCurrentIndex(Setting.Waifu2xTileSize.value) self.coverCheckBox.setChecked(Setting.CoverIsOpenWaifu.value) self.coverNoise.setCurrentIndex(Setting.CoverLookNoise.value) @@ -254,18 +261,6 @@ def InitSetting(self): self.downModel.setCurrentIndex(Setting.DownloadModel.value) self.SetDownloadLabel() - def OpenProxy(self): - loginView = LoginView(QtOwner().owner, False) - loginView.tabWidget.setCurrentIndex(3) - loginView.tabWidget.removeTab(0) - loginView.tabWidget.removeTab(0) - loginView.tabWidget.removeTab(0) - loginView.loginButton.setText(Str.GetStr(Str.Save)) - loginView.show() - - loginView.closed.connect(QtOwner().owner.navigationWidget.UpdateProxyName) - return - def retranslateUi(self, SettingNew): Ui_SettingNew.retranslateUi(self, SettingNew) self.SetDownloadLabel() @@ -417,6 +412,14 @@ def SelectSavePath(self): Setting.SavePath.SetValue(url) self.SetDownloadLabel() + def SelectSaveLogPath(self): + url = QFileDialog.getExistingDirectory(self, Str.GetStr(Str.SelectFold)) + if url: + Setting.LogDirPath.SetValue(url) + Log.Init() + + self.SetDownloadLabel() + def SetDownloadLabel(self): url = Setting.SavePath.value if not url: @@ -426,6 +429,8 @@ def SetDownloadLabel(self): self.cacheDir.setText(os.path.join(url, config.CachePathDir)) self.waifu2xDir.setText(os.path.join(os.path.join(url, config.CachePathDir), config.Waifu2xPath)) + self.logLabel.setText(Setting.GetLogPath()) + def OpenDir(self, label): QDesktopServices.openUrl(QUrl.fromLocalFile(label.text())) return diff --git a/src/view/tool/local_fold_view.py b/src/view/tool/local_fold_view.py index 05e091d..bbe783e 100644 --- a/src/view/tool/local_fold_view.py +++ b/src/view/tool/local_fold_view.py @@ -121,7 +121,7 @@ def _SwitchEdit(self): for i in range(self.listWidget.count()): item = self.listWidget.item(i) w = self.listWidget.itemWidget(item) - if i == 0: + if i <= 0: continue w.SetEditEnable(True) # item.setFlags(item.flags() & ~Qt.ItemIsSelectable) @@ -195,6 +195,13 @@ def _DoAddItem(self, item): name = w.lineEdit.text() if not name: return + if name == Str.GetStr(Str.All): + QtOwner().ShowError(Str.GetStr(Str.AlreadyHave)) + return + if name == Str.GetStr(Str.CurRead): + QtOwner().ShowError(Str.GetStr(Str.AlreadyHave)) + return + if name in self.categoryBook: QtOwner().ShowError(Str.GetStr(Str.AlreadyHave)) return diff --git a/src/view/tool/local_read_view.py b/src/view/tool/local_read_view.py index 8f0bfb0..50ddee6 100644 --- a/src/view/tool/local_read_view.py +++ b/src/view/tool/local_read_view.py @@ -10,7 +10,7 @@ from interface.ui_index import Ui_Index from interface.ui_local import Ui_Local from qt_owner import QtOwner -from server import Status, time +from server import Status, time from task.qt_task import QtTaskBase from task.task_local import LocalData from tools.str import Str @@ -63,14 +63,16 @@ def __init__(self, parent=None): self.bookList.ReDownloadPicture = self.LoadingPicture self.bookList.LoadCallBack = self.LoadNextPage self.lastPath = "" - + self.sortKeyCombox.setCurrentIndex(1) self.sortIdCombox.currentIndexChanged.connect(self.Init) self.sortKeyCombox.currentIndexChanged.connect(self.Init) + self.setAcceptDrops(True) self.categoryBook = {} self.bookCategory = {} self.tagsList.clicked.connect(self.ClickTagsItem) self.curSelectCategory = "" + self.isCurRead = False self.bookList.isMoveMenu = True self.bookList.MoveHandler = self.MoveCallBack self.bookList.openMenu = True @@ -79,6 +81,8 @@ def __init__(self, parent=None): self.delAll.clicked.connect(self.ShowDelAll) self.searchText = "" + self.sortAllBookIds = [] + def SearchTextChange(self, text): self.searchText = text self.InitBook() @@ -87,6 +91,9 @@ def Init(self): self.categoryBook, self.bookCategory = self.db.LoadCategory() self.tagsList.clear() self.tagsList.AddItem(Str.GetStr(Str.All), True) + item = self.tagsList.AddItem(Str.GetStr(Str.CurRead), True) + if self.isCurRead: + item.setSelected(True) for category in self.categoryBook.keys(): item = self.tagsList.AddItem(category, True) @@ -107,44 +114,56 @@ def InitBook(self): oldV = oldDict.get(v.id) if oldV: v.CopyData(oldV) + self.ResetSortBookIds() self.ShowPages() - @time_me - def ShowPages(self, page=1): + def ResetSortBookIds(self): if self.curSelectCategory: allBookId = self.categoryBook.get(self.curSelectCategory, []) else: allBookId = self.allBookInfos.keys() - allBookId2 = self.db.Search(self.searchText) showIds = list(set(allBookId) & set(allBookId2)) + self.sortAllBookIds = [] + allBookInfos = [] + for i in showIds: + v = self.allBookInfos.get(i) + if v: + if self.isCurRead: + if v.lastReadTime > 0: + allBookInfos.append(v) + else: + allBookInfos.append(v) + + for v in self.ToSortData(allBookInfos): + if v.id in showIds: + self.sortAllBookIds.append(v.id) + + @time_me + def ShowPages(self, page=1): showLen = 30 - maxPage = len(showIds) // showLen + 1 - showStart = (page-1)*showLen - showEnd = page * showLen - 1 + maxPage = len(self.sortAllBookIds) // showLen + 1 + showStart = (page - 1) * showLen + showEnd = page * showLen + self.spinBox.setValue(page) self.spinBox.setMaximum(maxPage) self.bookList.UpdatePage(page, maxPage) self.pages.setText(self.bookList.GetPageStr()) - self.nums.setText("{}:{} ".format(Str.GetStr(Str.FavoriteNum), len(self.allBookInfos))) - sortShowIds = [] - for v in self.ToSortData(list(self.allBookInfos.values())): - if v.id in showIds: - sortShowIds.append(v.id) + self.nums.setText("{}:{} ".format(Str.GetStr(Str.FavoriteNum), len(self.sortAllBookIds))) - showIds2 = sortShowIds[showStart:showEnd] + showIds2 = self.sortAllBookIds[showStart:showEnd] for id in showIds2: v = self.allBookInfos.get(id) if v: categoryList = self.bookCategory.get(v.id, []) categoryStr = ",".join(categoryList) - if not self.searchText or self.searchText in v.title: self.bookList.AddBookByLocal(v, categoryStr) return def LoadNextPage(self): - self.ShowPages(self.bookList.page+1) + self.ShowPages(self.bookList.page + 1) def JumpPage(self): page = int(self.spinBox.text()) @@ -162,11 +181,20 @@ def ClickTagsItem(self, modelIndex): widget = self.tagsList.itemWidget(item) text = widget.text() # print(text) + self.isCurRead = False if text == "+": self.OpenFavoriteFold() - elif text == Str.GetStr(Str.All): + elif index == 0: self.curSelectCategory = "" self.Init() + self.sortIdCombox.setCurrentIndex(0) + self.sortKeyCombox.setCurrentIndex(1) + elif index == 1: + self.curSelectCategory = "" + self.isCurRead = True + self.Init() + self.sortIdCombox.setCurrentIndex(0) + self.sortKeyCombox.setCurrentIndex(0) else: self.curSelectCategory = text self.Init() @@ -182,7 +210,7 @@ def ToSortData(self, value): if sortKeyID == 0: datas.sort(key=lambda a: a.lastReadTime, reverse=isRevert) elif sortKeyID == 2: - datas = natsorted(datas, key=lambda a:a.title, reverse=isRevert) + datas = natsorted(datas, key=lambda a: a.title, reverse=isRevert) else: datas.sort(key=lambda a: a.addTime, reverse=isRevert) return datas @@ -344,8 +372,10 @@ def CheckAction1LoadBack(self, st, books, url): self.AddDataToDB(v.id) QtOwner().ShowMsg("已添加{}本到书架, {}本存在已忽略".format(addNum, alreadyNum)) self.lineEdit.setText("") + self.isCurRead = False self.sortIdCombox.setCurrentIndex(0) self.sortKeyCombox.setCurrentIndex(1) + self.Init() return # 导入单本Zip @@ -379,11 +409,12 @@ def CheckAction4(self): # 批量导入下载目录 def ImportDownloadDirs(self, dirs): + self.curSelectCategory = "" self.AddLocalTaskLoad(LocalData.Type6, dirs, "", self.CheckAction1LoadBack) return - + def dragEnterEvent(self, event): - if(event.mimeData().hasUrls()): + if (event.mimeData().hasUrls()): event.acceptProposedAction() else: event.ignore() @@ -392,8 +423,8 @@ def dragMoveEvent(self, evemt): return def dropEvent(self, event): - mimeData = event.mimeData() - if(mimeData.hasUrls()): + mimeData = event.mimeData() + if (mimeData.hasUrls()): urls = mimeData.urls() QtOwner().ShowLoading() fileNames = [str(i.toLocalFile()) for i in urls] diff --git a/src/view/user/login_proxy_widget.py b/src/view/user/login_proxy_widget.py index 3ebe44c..fde7825 100644 --- a/src/view/user/login_proxy_widget.py +++ b/src/view/user/login_proxy_widget.py @@ -6,6 +6,7 @@ from PySide6.QtGui import QDesktopServices from config import config +from config.global_config import GlobalConfig from config.setting import Setting from interface.ui_login_proxy_widget import Ui_LoginProxyWidget from qt_owner import QtOwner @@ -34,6 +35,8 @@ def __init__(self): self.radioApiGroup.setId(self.radioButton_4, 4) self.radioApiGroup.setId(self.radioButton_5, 5) self.radioApiGroup.setId(self.radioButton_6, 6) + # self.radioApiGroup.setId(self.radioButton_7, 7) + # self.radioApiGroup.setId(self.radioButton_8, 8) self.radioImgGroup.setId(self.radio_img_1, 1) self.radioImgGroup.setId(self.radio_img_2, 2) @@ -41,7 +44,9 @@ def __init__(self): self.radioImgGroup.setId(self.radio_img_4, 4) self.radioImgGroup.setId(self.radio_img_5, 5) self.radioImgGroup.setId(self.radio_img_6, 6) - config.Address[1] = Setting.SaveCacheAddress.value + # self.radioImgGroup.setId(self.radio_img_7, 7) + # self.radioImgGroup.setId(self.radio_img_8, 8) + # config.Address[1] = Setting.SaveCacheAddress.value self.LoadSetting() self.UpdateServer() @@ -57,15 +62,42 @@ def __init__(self): self.radio_img_5.setEnabled(True) self.maxNum = 6 + self.lastResult = {} + self.LoadHistory() def Init(self): self.LoadSetting() + self.InitImgUrlList() proxy = urllib.request.getproxies() if isinstance(proxy, dict) and proxy.get("http"): self.checkLabel.setVisible(False) else: self.checkLabel.setVisible(True) + def InitImgUrlList(self): + self.imgCombox.clear() + self.imgCombox.addItem("") + if GlobalConfig.ImageUrl.value not in GlobalConfig.ImageServerList.value: + GlobalConfig.ImageUrl.value = "" + for index, name in enumerate(GlobalConfig.ImageServerList.value): + self.imgCombox.addItem(name) + if name == GlobalConfig.ImageUrl.value: + self.imgCombox.setCurrentIndex(index+1) + return + + def LoadHistory(self): + if not Setting.LastProxyResult.value: + return + try: + for k, v in Setting.LastProxyResult.value.items(): + if hasattr(self, k): + getattr(self, k).setText(str(v)) + except Exception as es: + Log.Error(es) + + def SaveHistory(self): + Setting.LastProxyResult.SetValue(dict(self.lastResult)) + def ClickButton(self): self.SaveSetting() @@ -85,14 +117,19 @@ def SetEnabled(self, enabled): self.radioButton_4.setEnabled(enabled) self.radioButton_5.setEnabled(enabled) self.radioButton_6.setEnabled(enabled) + # self.radioButton_7.setEnabled(enabled) + # self.radioButton_8.setEnabled(enabled) self.radio_img_1.setEnabled(enabled) self.radio_img_2.setEnabled(enabled) self.radio_img_3.setEnabled(enabled) self.radio_img_4.setEnabled(enabled) self.radio_img_5.setEnabled(enabled) self.radio_img_6.setEnabled(enabled) + # self.radio_img_7.setEnabled(enabled) + # self.radio_img_8.setEnabled(enabled) self.httpsBox.setEnabled(enabled) self.ipv6Check.setEnabled(enabled) + self.imgCombox.setEnabled(enabled) if not self.isShowProxy5: self.radio_img_5.setEnabled(False) @@ -112,11 +149,15 @@ def SpeedTest(self): self.speedTest = [("", "", False, False, 1)] i = 2 - adress = config.AddressIpv6[0] if self.ipv6Check.isChecked() else config.Address[0] - self.speedTest.append((adress, config.ImageServer2, False, False, i)) + + adress = GlobalConfig.GetAddress(2) + image = GlobalConfig.GetImageAdress(2) + self.speedTest.append((adress, image, False, False, i)) i += 1 - adress1 = config.AddressIpv6[1] if self.ipv6Check.isChecked() else config.Address[1] - self.speedTest.append((adress1, config.ImageServer3, False, False, i)) + + adress1 = GlobalConfig.GetAddress(3) + image = GlobalConfig.GetImageAdress(3) + self.speedTest.append((adress1, image, False, False, i)) i += 1 PreferCDNIP = self.cdn_api_ip.text() @@ -132,9 +173,21 @@ def SpeedTest(self): self.speedTest.append(("", "", False, (config.ProxyApiDomain2, config.ProxyImgDomain2), i)) i += 1 + # adress1 = GlobalConfig.GetAddress(7) + # image = GlobalConfig.GetImageServer(7) + # self.speedTest.append((adress1, image, False, False, i)) + # i += 1 + # + # adress1 = GlobalConfig.GetAddress(8) + # image = GlobalConfig.GetImageServer(8) + # self.speedTest.append((adress1, image, False, False, i)) + # i += 1 + self.SetEnabled(False) self.needBackNum = 0 self.speedPingNum = 0 + + self.lastResult.clear() self.StartSpeedPing() def StartSpeedPing(self): @@ -176,7 +229,9 @@ def StartSpeedPing(self): else: self.SetSock5Proxy(False) - Server().UpdateDns(address, imageProxy) + # imageAdress = GlobalConfig.GetImageAdress(i) + imgUrl = self.imgCombox.currentText() + Server().UpdateDns(address, imgUrl, imageProxy) self.pingBackNumCnt[i] = 0 self.pingBackNumDict[i] = [0, 0, 0] request1 = deepcopy(request) @@ -191,7 +246,8 @@ def SpeedTestPingBack(self, raw, v): i, backNum = v data = raw["data"] st = raw["st"] - label = getattr(self, "label_api_" + str(i)) + objectName = "label_api_" + str(i) + label = getattr(self, objectName) if float(data) > 0.0: self.pingBackNumDict[i][backNum] = int(float(data)) label.setText("{}".format(str(int(float(data))) + "ms")) @@ -211,10 +267,12 @@ def SpeedTestPingBack(self, raw, v): else: sumSt = data if sumCnt >= 1: - label.setText("{}".format(str(int(float(sumData/sumCnt))) + "ms") ) + text = "{}".format(str(int(float(sumData/sumCnt))) + "ms") + label.setText(text) else: - label.setText("{}".format(Str.GetStr(int(sumSt))) ) - + text = "{}".format(Str.GetStr(int(sumSt))) + label.setText(text) + self.lastResult[objectName] = text self.speedPingNum += 1 self.StartSpeedPing() return @@ -234,6 +292,7 @@ def StartSpeedTest(self): self.UpdateServer() self.SetEnabled(True) self.CheckShow5() + self.SaveHistory() return address, imageProxy, isHttpProxy, isProxyUrl, i = self.speedTest[self.speedIndex] @@ -267,8 +326,8 @@ def StartSpeedTest(self): self.SetSock5Proxy(True) else: self.SetSock5Proxy(False) - - Server().UpdateDns(address, imageProxy) + imgUrl = self.imgCombox.currentText() + Server().UpdateDns(address, imgUrl, imageProxy) self.AddHttpTask(lambda x: Server().TestSpeed(request, x), self.SpeedTestBack, i) return @@ -279,7 +338,10 @@ def SpeedTestBack(self, raw, i): data = "{}".format(Str.GetStr(st)) else: data = "{}".format(data) - label = getattr(self, "label_img_" + str(i)) + objectName = "label_img_" + str(i) + label = getattr(self, objectName) + self.lastResult[objectName] = data + label.setText(data) self.speedIndex += 1 self.StartSpeedTest() @@ -307,12 +369,15 @@ def LoadSetting(self): self.httpsBox.setChecked(Setting.IsUseHttps.value) self.httpLine.setText(Setting.HttpProxy.value) self.sockEdit.setText(Setting.Sock5Proxy.value) - button = getattr(self, "radioButton_{}".format(Setting.ProxySelectIndex.value)) - button.setChecked(True) - button = getattr(self, "proxy_{}".format(int(Setting.IsHttpProxy.value))) - button.setChecked(True) - button = getattr(self, "radio_img_{}".format(int(Setting.ProxyImgSelectIndex.value))) - button.setChecked(True) + button = getattr(self, "radioButton_{}".format(Setting.ProxySelectIndex.value), None) + if button: + button.setChecked(True) + button = getattr(self, "proxy_{}".format(int(Setting.IsHttpProxy.value)), None) + if button: + button.setChecked(True) + button = getattr(self, "radio_img_{}".format(int(Setting.ProxyImgSelectIndex.value)), None) + if button: + button.setChecked(True) self.cdn_api_ip.setText(Setting.PreferCDNIP.value) self.cdn_img_ip.setText(Setting.PreferCDNIPImg.value) @@ -326,35 +391,20 @@ def LoadSetting(self): self.radio_img_5.setEnabled(True) def UpdateServer(self): - if Setting.ProxySelectIndex.value == 1: - address = "" - elif Setting.ProxySelectIndex.value == 2: - address = config.AddressIpv6[0] if Setting.PreIpv6.value > 0 else config.Address[0] - elif Setting.ProxySelectIndex.value == 3: - address = config.AddressIpv6[1] if Setting.PreIpv6.value > 0 else config.Address[1] - elif Setting.ProxySelectIndex.value == 5: - address = "" - elif Setting.ProxySelectIndex.value == 6: - address = "" - else: + address = GlobalConfig.GetAddress(Setting.ProxySelectIndex.value) + # imageServer = GlobalConfig.GetImageServer(Setting.ProxyImgSelectIndex.value) + imageAdress = GlobalConfig.GetImageAdress(Setting.ProxyImgSelectIndex.value) + imageServer = GlobalConfig.ImageUrl.value + if Setting.ProxySelectIndex.value == 4: address = Setting.PreferCDNIP.value - if Setting.ProxyImgSelectIndex.value == 1: - imageServer = "" - elif Setting.ProxyImgSelectIndex.value == 2: - imageServer = config.ImageServer2 - elif Setting.ProxyImgSelectIndex.value == 3: - imageServer = config.ImageServer3 - elif Setting.ProxyImgSelectIndex.value == 5: - imageServer = "" - elif Setting.ProxyImgSelectIndex.value == 6: - imageServer = "" - else: - imageServer = Setting.PreferCDNIPImg.value + if Setting.ProxyImgSelectIndex.value == 4: + # imageServer = Setting.PreferCDNIPImg.value + imageAdress = Setting.PreferCDNIPImg.value QtOwner().settingView.SetSock5Proxy() - Server().UpdateDns(address, imageServer) - Log.Info("update proxy, apiSetId:{}, imgSetID:{}, image server:{}, address:{}".format(Setting.ProxySelectIndex.value, Setting.ProxyImgSelectIndex.value, Server().imageServer, Server().address)) + Server().UpdateDns(address, imageServer, imageAdress) + Log.Info("update proxy, apiSetId:{}, imgSetID:{}, image server:{}:{}, address:{}".format(Setting.ProxySelectIndex.value, Setting.ProxyImgSelectIndex.value, Server().imageServer, Server().imageAddress, Server().address)) def SaveSetting(self): Setting.PreferCDNIP.SetValue(self.cdn_api_ip.text()) @@ -366,7 +416,7 @@ def SaveSetting(self): Setting.ProxyImgSelectIndex.SetValue(self.radioImgGroup.checkedId()) Setting.IsUseHttps.SetValue(int(self.httpsBox.isChecked())) Setting.PreIpv6.SetValue(int(self.ipv6Check.isChecked())) - + GlobalConfig.SetSetting("ImageUrl", self.imgCombox.currentText()) # QtOwner().settingView.SetSettingV("Proxy/ProxySelectIndex", config.ProxySelectIndex) # QtOwner().settingView.SetSettingV("Proxy/PreferCDNIP", config.PreferCDNIP) # QtOwner().settingView.SetSettingV("Proxy/Http", httpProxy) diff --git a/ui/component/ui_login_proxy_widget.ui b/ui/component/ui_login_proxy_widget.ui index 398130d..4d81e0c 100644 --- a/ui/component/ui_login_proxy_widget.ui +++ b/ui/component/ui_login_proxy_widget.ui @@ -6,13 +6,13 @@ 0 0 - 495 - 483 + 577 + 473 - 450 + 550 0 @@ -33,8 +33,8 @@ 0 0 - 458 - 483 + 540 + 512 @@ -217,6 +217,33 @@ + + + + + + 图片地址选择: + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + @@ -233,21 +260,21 @@ - - + + - - + + - Api分流 - - - Qt::AlignCenter + 分流3 + + radioApiGroup + @@ -260,8 +287,8 @@ - - + + 120 @@ -270,55 +297,72 @@ - - + + - + US反代分流 + + radioImgGroup + - - - - 所以分流不可使用时,自动解锁 - + + - JP反代分流 + US反代分流 radioApiGroup - - + + - + 分流3 + + radioImgGroup + - - + + - 分流1 + CDN地址 - - true + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + CDN分流 radioApiGroup - - + + - - + + + + CDN地址 + + + + + 120 @@ -327,72 +371,52 @@ - - + + - - + + - - + + - - + + - CDN地址 + - - + + - 分流1 - - - true + 图片分流 - - radioImgGroup - - - - - - - 分流2 + + Qt::AlignCenter - - radioImgGroup - - - + + - + 延迟 - - - - - - CDN分流 + + Qt::AlignCenter - - radioImgGroup - @@ -408,122 +432,125 @@ - - + + - CDN分流 + 速度 + + + Qt::AlignCenter - - radioApiGroup - - - + + - 分流3 + 分流1 + + + true radioApiGroup - - + + - 分流3 + CDN分流 radioImgGroup - - + + - CDN地址 - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + - - + + - 延迟 + Api分流 Qt::AlignCenter - - + + - - + + - 图片分流 + - - Qt::AlignCenter + + + + + + - - + + - 速度 + 分流1 - - Qt::AlignCenter + + true + + radioImgGroup + - - + + - - + + - US反代分流 + - - radioApiGroup - - - + + - US反代分流 + 分流2 radioImgGroup - - - - + + + + 所以分流不可使用时,自动解锁 - - - - - + JP反代分流 + + radioApiGroup + @@ -559,6 +586,11 @@
component.scroll_area.smooth_scroll_area.h
1 + + WheelComboBox + QComboBox +
component.box.wheel_combo_box.h
+
@@ -588,7 +620,7 @@ - + diff --git a/ui/component/ui_navigation.ui b/ui/component/ui_navigation.ui index 6c3deeb..5e62a9f 100644 --- a/ui/component/ui_navigation.ui +++ b/ui/component/ui_navigation.ui @@ -6,8 +6,8 @@ 0 0 - 248 - 496 + 376 + 772 @@ -141,7 +141,7 @@
- + @@ -159,7 +159,7 @@ - + @@ -188,18 +188,37 @@ + + + + Qt::Vertical + + + + 20 + 40 + + + + + + Qt::ScrollBarAlwaysOn + + + Qt::ScrollBarAlwaysOff + - true + false 0 - -431 - 211 - 792 + 0 + 339 + 800 @@ -236,7 +255,7 @@ 150 - 40 + 0 @@ -277,7 +296,7 @@ 150 - 40 + 0 @@ -321,7 +340,7 @@ 150 - 40 + 0 @@ -376,7 +395,7 @@ 150 - 40 + 0 @@ -426,7 +445,7 @@ 150 - 40 + 0 @@ -473,7 +492,7 @@ 150 - 40 + 0 @@ -520,7 +539,7 @@ 150 - 40 + 0 @@ -561,7 +580,7 @@ 150 - 40 + 0 @@ -602,7 +621,7 @@ 150 - 40 + 0 @@ -643,7 +662,7 @@ 150 - 40 + 0 @@ -684,7 +703,7 @@ 150 - 40 + 0 @@ -725,7 +744,7 @@ 150 - 40 + 0 @@ -780,7 +799,7 @@ 150 - 40 + 0 @@ -820,8 +839,8 @@ - 0 - 40 + 150 + 0 @@ -851,7 +870,6 @@ - @@ -863,7 +881,7 @@ 150 - 40 + 0 @@ -904,7 +922,7 @@ 150 - 40 + 0 @@ -934,19 +952,6 @@ - - - - Qt::Vertical - - - - 20 - 40 - - - - diff --git a/ui/component/ui_read_tool.ui b/ui/component/ui_read_tool.ui index 04a50d4..08f5a1e 100644 --- a/ui/component/ui_read_tool.ui +++ b/ui/component/ui_read_tool.ui @@ -41,7 +41,7 @@ 0 - 0 + -27 310 825 @@ -531,7 +531,7 @@ color: #ee2a24 - 自动滚动/翻页 + 滚动/翻页 Qt::AlignCenter @@ -549,7 +549,7 @@ - 滚动速度(像素): + 滚动速度: diff --git a/ui/ui_download.ui b/ui/ui_download.ui index 7f71b90..91d79a9 100644 --- a/ui/ui_download.ui +++ b/ui/ui_download.ui @@ -6,7 +6,7 @@ 0 0 - 707 + 820 361 @@ -16,7 +16,66 @@ - + + + + + id + + + + + 时间 + + + + + 标题 + + + + + 下载进度 + + + + + 下载章节 + + + + + 下载速度 + + + + + 下载状态 + + + + + 转换进度 + + + + + 转换章节 + + + + + 转换耗时 + + + + + 转换状态 + + + + + @@ -88,65 +147,6 @@ - - - - - id - - - - - 时间 - - - - - 标题 - - - - - 下载进度 - - - - - 下载章节 - - - - - 下载速度 - - - - - 下载状态 - - - - - 转换进度 - - - - - 转换章节 - - - - - 转换耗时 - - - - - 转换状态 - - - - diff --git a/ui/ui_local.ui b/ui/ui_local.ui index 5d07ddf..1f28143 100644 --- a/ui/ui_local.ui +++ b/ui/ui_local.ui @@ -6,8 +6,8 @@ 0 0 - 628 - 334 + 626 + 327 diff --git a/ui/ui_local_all.ui b/ui/ui_local_all.ui index ad2923e..51e6052 100644 --- a/ui/ui_local_all.ui +++ b/ui/ui_local_all.ui @@ -6,8 +6,8 @@ 0 0 - 543 - 300 + 541 + 293 diff --git a/ui/ui_local_favorite.ui b/ui/ui_local_favorite.ui new file mode 100644 index 0000000..f9960a3 --- /dev/null +++ b/ui/ui_local_favorite.ui @@ -0,0 +1,219 @@ + + + LocalFavorite + + + + 0 + 0 + 628 + 334 + + + + 收藏 + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + + + + 100 + 0 + + + + + 收藏时间 + + + + + + + + true + + + + 降序 + + + + + 升序 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Vertical + + + + + + + + 0 + 30 + + + + 收藏数: + + + + + + + + + + + + + + Qt::Vertical + + + + + + + Qt::Vertical + + + + + + + + 50 + 30 + + + + 1 + + + 1 + + + + + + + Qt::Vertical + + + + + + + + 0 + 30 + + + + 跳转 + + + Return + + + + + + + + + + + + + + 搜索: + + + + + + + + + + + + + + ComicListWidget + QListWidget +
component.list.comic_list_widget.h
+
+
+ + + + jumpButton + clicked() + LocalFavorite + JumpPage() + + + 439 + 280 + + + 361 + 291 + + + + + + JumpPage() + RefreshDataFocus() + +
diff --git a/ui/ui_setting_new.ui b/ui/ui_setting_new.ui index 40be88d..04d5566 100644 --- a/ui/ui_setting_new.ui +++ b/ui/ui_setting_new.ui @@ -6,7 +6,7 @@ 0 0 - 880 + 946 789
@@ -95,9 +95,9 @@ 0 - -541 - 661 - 2891 + 0 + 727 + 3043 @@ -824,6 +824,109 @@ + + + + + 12 + true + + + + 标题显示行数 + + + + + + + + + + 不显示 + + + + + 1行 + + + + + 2行 + + + + + 3行 + + + + + 全部 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + 12 + true + + + + 分类显示 + + + + + + + + + + 显示 + + + + + 不显示 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + @@ -882,7 +985,7 @@ - Debug + Debug(该设置内含敏感数据,log文件请勿分享) logGroup @@ -2278,6 +2381,13 @@ + + + + 设置日志目录 + + + @@ -2493,6 +2603,59 @@ + + + + + + + 80 + 0 + + + + 日志 + + + + + + + + 150 + 0 + + + + + + + + + + + 打开目录 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + @@ -2530,12 +2693,12 @@ + - - - + +