Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

战绩搜索历史 #72

Merged
merged 3 commits into from
Sep 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion app/common/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
from PyQt5.QtCore import QLocale

from qfluentwidgets import (qconfig, QConfig, ConfigItem, FolderValidator, BoolValidator,
OptionsConfigItem, OptionsValidator, ConfigSerializer, RangeConfigItem, RangeValidator)
OptionsConfigItem, OptionsValidator, ConfigSerializer, RangeConfigItem, RangeValidator,
EnumSerializer)


class Language(Enum):
Expand Down Expand Up @@ -76,6 +77,15 @@ class Config(QConfig):

enableCloseToTray = ConfigItem(
"General", "EnableCloseToTray", None, OptionsValidator([None, True, False]))

searchHistory = ConfigItem(
"Other", "SearchHistory", ""
)

enableCheckUpdate = ConfigItem("General",
"EnableCheckUpdate", True,
BoolValidator())

# enableCopyPlayersInfo = ConfigItem("Functions", "EnableCopyPlayersInfo",
# False, BoolValidator())

Expand Down
1 change: 1 addition & 0 deletions app/common/icons.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ class Icon(FluentIconBase, Enum):
TEAM = 'Team'
SETTING = 'Setting'
FILTER = 'Filter'
UPDATE = 'Update'

def path(self, theme=Theme.AUTO):
return f'./app/resource/icons/{self.value}_{getIconColor(theme)}.svg'
28 changes: 28 additions & 0 deletions app/common/util.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import requests

from app.common.config import cfg, VERSION


class Github:
def __init__(self, user="Zzaphkiel", repositories="Seraphine"):
self.API = "http://api.github.com"

self.user = user
self.repositories = repositories
self.sess = requests.session()

def getReleasesInfo(self):
url = f"{self.API}/repos/{self.user}/{self.repositories}/releases/latest"
return self.sess.get(url, verify=False).json()

def checkUpdate(self):
"""
检查版本更新
@return: 有更新 -> info, 无更新 -> None
"""
info = self.getReleasesInfo()
if info.get("tag_name")[1:] != VERSION:
return info
return None

github = Github()
91 changes: 91 additions & 0 deletions app/components/search_line_edit.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
from PyQt5.QtCore import Qt, QEvent, QAbstractItemModel
from PyQt5.QtWidgets import QCompleter, QAction
from PyQt5.uic.properties import QtCore, QtGui
from qfluentwidgets import SearchLineEdit as QSearchLineEdit
from qfluentwidgets.components.widgets.line_edit import CompleterMenu

from app.common.config import cfg


class MyCompleterMenu(CompleterMenu):

def eventFilter(self, obj, e):
if e.type() != QEvent.KeyPress:
return super().eventFilter(obj, e)

# redirect input to line edit
self.lineEdit.event(e)
self.view.event(e)

if e.key() == Qt.Key_Escape:
self.close()
if e.key() in [Qt.Key_Enter, Qt.Key_Return]:
self.lineEdit.searchButton.click()
self.close()

return True

def setCompletion(self, model: QAbstractItemModel):
""" set the completion model """
items = []
for i in range(model.rowCount()):
for j in range(model.columnCount()):
items.append(model.data(model.index(i, j)))

if self.items == items and self.isVisible():
return False

self.clear()
self.items = items

# add items
for i in items:
self.addAction(QAction(i, triggered=lambda c, x=i: self.__onItemSelected(x)))

return True

def __onItemSelected(self, text):
self.lineEdit.setText(text)
self.activated.emit(text)
self.lineEdit.searchButton.click()


class SearchLineEdit(QSearchLineEdit):
def __init__(self, parent=None):
super().__init__(parent)

completer = QCompleter([], self)
completer.setCaseSensitivity(Qt.CaseInsensitive)
completer.setMaxVisibleItems(10)
completer.setFilterMode(Qt.MatchFlag.MatchFixedString)
completer.setCompletionRole(Qt.DisplayRole)
completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion)
self.setCompleter(completer)

def _showCompleterMenu(self):
if not self.completer():
return

model = cfg.get(cfg.searchHistory)
if not model:
return

model = model.split(",")
self.completer().model().setStringList(model)

# create menu
if not self._completerMenu:
self._completerMenu = MyCompleterMenu(self)
self._completerMenu.activated.connect(self._completer.activated)

# add menu items
changed = self._completerMenu.setCompletion(self.completer().completionModel())
self._completerMenu.setMaxVisibleItems(self.completer().maxVisibleItems())

# show menu
if changed:
self._completerMenu.popup()

def focusInEvent(self, e):
self._showCompleterMenu()
super().focusInEvent(e)
32 changes: 32 additions & 0 deletions app/components/update_message_box.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from PyQt5.QtWidgets import QLabel
from qfluentwidgets import MessageBox, MessageBoxBase, SmoothScrollArea, SubtitleLabel, BodyLabel, TextEdit, TitleLabel, \
CheckBox

from app.common.config import VERSION, cfg


class UpdateMessageBox(MessageBoxBase):
def __init__(self, info, parent=None):
super().__init__(parent=parent)
self.titleLabel = TitleLabel(self.tr('Update detected'), self)
self.titleLabel.setContentsMargins(5, 0, 5, 0)
self.content = BodyLabel(self.tr(f'{VERSION} -> {info.get("tag_name")[1:]}'), self)
self.content.setContentsMargins(8, 0, 5, 0)

textEdit = TextEdit(self)
textEdit.setFixedWidth(int(self.width() * .6))
textEdit.setMarkdown(self.tr(info.get("body")))
textEdit.setReadOnly(True)

checkBox = CheckBox()
checkBox.setText("Don't remind me again")
checkBox.clicked.connect(lambda: cfg.set(cfg.enableCheckUpdate, not checkBox.isChecked(), True))

self.viewLayout.addWidget(self.titleLabel)
self.viewLayout.addWidget(self.content)
self.viewLayout.addWidget(textEdit)
self.viewLayout.addWidget(checkBox)

self.yesButton.setText("Download")


46 changes: 39 additions & 7 deletions app/lol/connector.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,50 @@
requests.packages.urllib3.disable_warnings()


def slowly():
def decorator(func):
def wrapper(*args, **kwargs):
while connector.tackleFlag.is_set():
time.sleep(.2)

connector.slowlyFlag.set()
res = func(*args, **kwargs)
connector.slowlyFlag.clear()
return res
return wrapper
return decorator


def tackle():
def decorator(func):
def wrapper(*args, **kwargs):
connector.tackleFlag.set()
res = func(*args, **kwargs)
connector.tackleFlag.clear()
return res
return wrapper
return decorator


def retry(count=5, retry_sep=0.5):
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(count):
try:
# 低优先级请求未结束时, 避免server队列过长
# 若负载过高导致请求失败, 则在触发 retry 间隙为高优先级请求让行
while connector.slowlyFlag.is_set():
time.sleep(.2)

res = func(*args, **kwargs)
except:
time.sleep(retry_sep)
continue
else:
break
else:
# FIXME 任何异常都将以 timeout 抛出
connector.timeoutApi = func.__name__
raise Exception("Exceeded maximum retry attempts.")

return res
Expand All @@ -39,9 +71,12 @@ def __init__(self):
self.token = None
self.url = None

self.flag = threading.Event()
self.tackleFlag = threading.Event()
self.slowlyFlag = threading.Event()
self.manager = None

self.timeoutApi = None

def start(self, pid):
process = psutil.Process(pid)
cmdline = process.cmdline()
Expand Down Expand Up @@ -206,11 +241,9 @@ def getSummonerByPuuid(self, puuid):

return res

@retry()
@slowly()
@retry(10, 1)
def getSummonerGamesByPuuidSlowly(self, puuid, begIndex=0, endIndex=4):
while self.flag.is_set():
time.sleep(.2)

params = {"begIndex": begIndex, "endIndex": endIndex}
res = self.__get(
f"/lol-match-history/v1/products/lol/{puuid}/matches", params
Expand All @@ -221,9 +254,9 @@ def getSummonerGamesByPuuidSlowly(self, puuid, begIndex=0, endIndex=4):

return res["games"]

@tackle()
@retry()
def getSummonerGamesByPuuid(self, puuid, begIndex=0, endIndex=4):
self.flag.set()
params = {"begIndex": begIndex, "endIndex": endIndex}
res = self.__get(
f"/lol-match-history/v1/products/lol/{puuid}/matches", params
Expand All @@ -232,7 +265,6 @@ def getSummonerGamesByPuuid(self, puuid, begIndex=0, endIndex=4):
if "games" not in res:
raise SummonerGamesNotFound()

self.flag.clear()
return res["games"]

@retry()
Expand Down
Loading