Lv100版本源码上传

This commit is contained in:
西瓜°
2024-11-04 14:45:20 +08:00
parent a98bdaf4e0
commit a6dbccdead
3431 changed files with 139917 additions and 75 deletions
+15
View File
@@ -0,0 +1,15 @@
from lanzou.api import LanZouCloud
from lanzou.api.types import *
from typing import List
class LZextends(LanZouCloud):
def __init__(self):
self.lzy = LanZouCloud()
self.lzy._host_url = 'https://pan.lanzouo.com'
self.lzy.available_domains = [
'lanzouo.com', # 2021-09-15 鲁ICP备15001327号-8
'lanzouw.com', # 2021-09-02 鲁ICP备15001327号-7
'lanzoui.com', # 2020-06-09 鲁ICP备15001327号-6
'lanzoux.com', # 2020-06-09 鲁ICP备15001327号-5
]
+378
View File
@@ -0,0 +1,378 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# -------------------------------
# File : calc_core
# DateTime : 2020/6/10 0010 21:30
# Author : Chen Ji
# Email : fzls.zju@gmail.com
# -------------------------------
from copy import *
from multiprocessing import Queue
from typing import List
from PublicReference.utils.minheap import MinHeap, expected_qsize
class CalcData():
def __init__(self):
self.是输出职业 = True
self.智慧产物限制 = 3
self.minheap_queue = None # type: Queue
self.角色属性A = None
# 被分配的计算范围
self.mode_index = 0 # 0-极速模式,1-套装模式,2-散件模式
# 在并发搜索任务组中分配到的开始序号:极速模式和套装模式时,任务组定义为按照原搜索组合算法得到的有效穿戴组合等;散件模式时,定义为0-level层串行搜索形成的所有子树
self.start_index = 0
self.end_index = 0 # 在并发搜索任务组中分配到的结束序号,描述同上
# 原始数据
self.装备选择状态: list[int] = [] # 各个装备的选择状况,第i个位置为0表示装备i未选择,否则表示已选择
self.拥有百变怪 = False # 是否勾选了百变怪
self.神话属性选项: list[int] = [
] # 每个神话四个选项,第4*k~4*k+3下标表示第k个神话的词条选择(k从0开始计数)
self.改造产物选项: list[int] = []
# 根据原始数据在计算线程计算出的一些数据
self.有效武器列表 = []
self.有效防具套装 = []
self.有效首饰套装 = []
self.有效特殊套装 = []
self.有效上链左套装 = []
self.有效镯下右套装 = []
self.有效环鞋指套装 = []
self.有效总套装列表 = [
self.有效防具套装, self.有效首饰套装, self.有效特殊套装, self.有效上链左套装, self.有效镯下右套装,
self.有效环鞋指套装
]
self.有效部位列表 = []
# 最终结果
self.minheap = None # type: MinHeap
def pre_calc_needed_data(self):
from PublicReference.equipment.equ_list import 总套装列表, 部位列表, equ
self.有效武器列表.clear()
for j in range(6):
self.有效总套装列表[j].clear()
self.有效部位列表.clear()
for i in range(12):
self.有效部位列表.append([])
for i in range(len(self.装备选择状态)):
if self.装备选择状态[i] == 1:
temp = equ.get_equ_by_id(i)
if temp.部位 == '武器':
self.有效武器列表.append(temp.名称)
for j in range(6):
if (temp.所属套装 in 总套装列表[j]) and (temp.所属套装
not in self.有效总套装列表[j]):
self.有效总套装列表[j].append(temp.所属套装)
self.有效部位列表[部位列表.index(temp.部位)].append(temp.名称)
count = 0
count2 = 0
for i in equ.get_equ_list():
if i.品质 == '神话':
if self.是输出职业 == True:
i.属性1选择 = self.神话属性选项[count * 4 + 0]
i.属性2选择 = self.神话属性选项[count * 4 + 1]
i.属性3选择 = self.神话属性选项[count * 4 + 2]
i.属性4选择 = self.神话属性选项[count * 4 + 3]
else:
i.属性1选择_BUFF = self.神话属性选项[count * 4 + 0]
i.属性2选择_BUFF = self.神话属性选项[count * 4 + 1]
i.属性3选择_BUFF = self.神话属性选项[count * 4 + 2]
i.属性4选择_BUFF = self.神话属性选项[count * 4 + 3]
count += 1
if self.是输出职业 == True and i.所属套装 == '智慧产物':
i.属性1选择 = self.改造产物选项[count2 * 4 + 0]
i.属性2选择 = self.改造产物选项[count2 * 4 + 1]
i.属性3选择 = self.改造产物选项[count2 * 4 + 2]
i.属性4选择 = self.改造产物选项[count2 * 4 + 3]
count2 += 1
pass
def calc_core(data: CalcData):
"""
:type minheap_queue: Queue
:type 角色属性A: 角色属性
:type 有效穿戴组合: List[str]
:type 有效穿戴套装: List[str]
:type 百变怪列表: str
"""
# 计算伤害具体算法
# 根据装备选择情况,预计算一些数据
data.pre_calc_needed_data()
# 寻找本任务对应的各个组合并调用计算函数
if data.mode_index in [0, 1]:
calc_speed_and_set_mode(data)
else:
calc_single_mode(data)
# 全部计算完后,将剩余结果传入结果队列
data.minheap_queue.put(deepcopy(data.minheap))
def calc_speed_and_set_mode(data):
from PublicReference.equipment.equ_list import 部位列表, equ
套装组合 = []
套装适用 = []
for a in data.有效防具套装:
for b in data.有效首饰套装:
for c in data.有效特殊套装:
# 533
套装组合.append([a, a, a, a, a, b, b, b, c, c, c])
套装适用.append([
a + '[2]', a + '[3]', a + '[5]', b + '[2]', b + '[3]',
c + '[2]', c + '[3]'
])
for a in data.有效防具套装:
for d in data.有效上链左套装:
for e in data.有效镯下右套装:
for f in data.有效环鞋指套装:
# 3332
套装组合.append([d, a, e, a, f, e, d, f, f, d, e])
套装适用.append([
a + '[2]', d + '[2]', d + '[3]', e + '[2]', e + '[3]',
f + '[2]', f + '[3]'
])
套装组合.append([a, a, e, a, f, e, d, f, f, d, e])
套装适用.append([
a + '[2]', d + '[2]', a + '[3]', e + '[2]', e + '[3]',
f + '[2]', f + '[3]'
])
套装组合.append([d, a, a, a, f, e, d, f, f, d, e])
套装适用.append([
a + '[2]', d + '[2]', d + '[3]', e + '[2]', a + '[3]',
f + '[2]', f + '[3]'
])
套装组合.append([d, a, e, a, a, e, d, f, f, d, e])
套装适用.append([
a + '[2]', d + '[2]', d + '[3]', e + '[2]', e + '[3]',
f + '[2]', a + '[3]'
])
if data.mode_index == 1:
for a in data.有效防具套装:
for b in data.有效首饰套装:
for c in data.有效特殊套装:
for d in data.有效防具套装:
if d != a:
# 2333 占套装模式90%计算量
套装组合.append([a, a, a, d, d, b, b, b, c, c, c])
套装组合.append([a, a, d, a, d, b, b, b, c, c, c])
套装组合.append([a, d, a, a, d, b, b, b, c, c, c])
套装组合.append([d, a, a, a, d, b, b, b, c, c, c])
套装组合.append([a, a, d, d, a, b, b, b, c, c, c])
套装组合.append([a, d, a, d, a, b, b, b, c, c, c])
套装组合.append([d, a, a, d, a, b, b, b, c, c, c])
套装组合.append([a, d, d, a, a, b, b, b, c, c, c])
套装组合.append([d, a, d, a, a, b, b, b, c, c, c])
套装组合.append([d, d, a, a, a, b, b, b, c, c, c])
for x in range(10):
套装适用.append([
a + '[2]', a + '[3]', d + '[2]', b + '[2]',
b + '[3]', c + '[2]', c + '[3]'
])
# 极速模式与套装模式
count = -1
current_index = -1
for temp in 套装组合:
count += 1
for k in [-1, 0, 5, 8]:
temp1 = []
sign = 0
if data.拥有百变怪:
sign2 = '空'
else:
sign2 = '无'
for x in range(11):
品质 = '史诗'
if k == x:
品质 = '神话'
index = equ.get_id_by_index(temp[x], 品质, 部位列表[x])
if data.装备选择状态[index] == 1:
sign += 1
else:
if sign2 == '空' and equ.get_equ_by_id(
index).品质 != '神话' and equ.get_equ_by_id(
index).所属套装 not in [
'精灵使的权能', '大自然的呼吸', '能量主宰'
]:
sign += 1
sign2 = equ.get_equ_by_id(index).名称
temp1.append(equ.get_equ_by_id(index).名称)
if sign == 11:
for i in data.有效武器列表:
current_index += 1
if current_index < data.start_index:
# 尚未到本工作线程需要计算的范围
continue
if current_index > data.end_index:
# 本工作线程需要计算的范围已全部完成,直接退出
return
calc_damage(temp1 + [i], 套装适用[count], sign2, data)
# logger.warning("consumer {} {} {} - {}".format(
# data.mode_index, data.start_index, data.end_index, current_index
# ))
pass
# 11层循环顺序
顺序 = [5, 8, 1, 3, 2, 4, 6, 9, 7, 10]
顺序字典 = {0: 0, 5: 1, 8: 2, 1: 3, 3: 4, 2: 5, 4: 6, 6: 7, 9: 8, 7: 9, 10: 10}
def 筛选(名称, x, 装备, 套装, 神话, 种类, data):
from PublicReference.equipment.equ_list import equ
i = equ.get_id_by_name(名称)
装备[x] = 名称
for k in 顺序[顺序字典[x]:]:
套装[k] = '无'
当前装备 = equ.get_equ_by_id(i)
if 当前装备.所属套装2 != '无':
套装[x] = 当前装备.所属套装2
else:
套装[x] = 当前装备.所属套装
count = []
num = 0
for j in 套装:
if j == '智慧产物':
num += 1
if num > 种类[1]:
return 1
elif (j != '无') and (j not in count):
count.append(j)
if len(count) > 种类[0]:
return 1
elif x == 10 and 种类[0] > 4:
种类[0] = max(min(种类[0], len(count)), 4)
if x == 0:
神话[5] = 0
神话[8] = 0
elif x == 5:
神话[8] = 0
if 当前装备.品质 == '神话':
神话[x] = 1
if sum(神话) > 1:
return 1
else:
神话[x] = 0
return 0
def calc_single_mode(data):
from PublicReference.equipment.equ_list import equ
current_index = -1
装备 = ['无'] * 12
套装 = ['无'] * 11
神话 = [0] * 11
种类 = [11, data.智慧产物限制]
for a1 in data.有效部位列表[0]:
if 筛选(a1, 0, 装备, 套装, 神话, 种类, data):
continue
for a2 in data.有效部位列表[5]:
if 筛选(a2, 5, 装备, 套装, 神话, 种类, data):
continue
for a3 in data.有效部位列表[8]:
if 筛选(a3, 8, 装备, 套装, 神话, 种类, data):
continue
for a4 in data.有效部位列表[1]:
if 筛选(a4, 1, 装备, 套装, 神话, 种类, data):
continue
current_index += 1
if current_index < data.start_index:
continue # 尚未到本工作线程需要计算的范围
if current_index > data.end_index:
return # 本工作线程需要计算的范围已全部完成,直接退出
for a5 in data.有效部位列表[3]:
if 筛选(a5, 3, 装备, 套装, 神话, 种类, data):
continue
for a6 in data.有效部位列表[2]:
if 筛选(a6, 2, 装备, 套装, 神话, 种类, data):
continue
for a7 in data.有效部位列表[4]:
if 筛选(a7, 4, 装备, 套装, 神话, 种类, data):
continue
for a8 in data.有效部位列表[6]:
if 筛选(a8, 6, 装备, 套装, 神话, 种类, data):
continue
for a9 in data.有效部位列表[9]:
if 筛选(a9, 9, 装备, 套装, 神话, 种类, data):
continue
for a10 in data.有效部位列表[7]:
if 筛选(a10, 7, 装备, 套装, 神话, 种类,
data):
continue
for a11 in data.有效部位列表[10]:
if 筛选(a11, 10, 装备, 套装, 神话, 种类,
data):
continue
套装字典 = {}
for i in 套装:
if i != '智慧产物' and i != '无':
套装字典[i] = 套装字典.get(
i, 0) + 1
套装名称 = []
for i in 套装字典.keys():
if 套装字典[i] >= 2 and (
i + '[2]'
) in equ.get_suit_name():
套装名称.append(i + '[2]')
if 套装字典[i] >= 3 and (
i + '[3]'
) in equ.get_suit_name():
套装名称.append(i + '[3]')
if 套装字典[i] >= 5 and (
i + '[5]'
) in equ.get_suit_name():
套装名称.append(i + '[5]')
for a12 in data.有效部位列表[11]:
装备[11] = a12
calc_damage(
装备, 套装名称, '空', data)
pass
def calc_damage(有效穿戴组合, 有效穿戴套装, 百变怪, data: CalcData):
角色属性A = deepcopy(data.角色属性A)
角色属性A.穿戴装备(有效穿戴组合, 有效穿戴套装)
if data.是输出职业:
damage = 角色属性A.伤害计算()
else:
damage = 角色属性A.BUFF计算(2)
data.minheap.add((damage, ) + tuple(角色属性A.装备栏) + (damage, ) +
tuple(角色属性A.套装栏) + (百变怪, ))
data.minheap.processed_result_count += 1
if data.minheap.processed_result_count >= data.minheap.batch_size:
# 动态调整批量大小
if data.minheap_queue.qsize() > expected_qsize:
data.minheap.update_batch_size()
# 同步
data.minheap_queue.put(deepcopy(data.minheap))
# 重置
data.minheap.reset()
+44
View File
@@ -0,0 +1,44 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# -------------------------------
# File : common
# DateTime : 2020/6/11 0011 16:43
# Author : Chen Ji
# Email : fzls.zju@gmail.com
# -------------------------------
import re
from typing import Iterable
from .log import *
# 格式化时间为比较美观的格式
def format_time(ftime):
days, remainder = divmod(ftime, 86400)
hours, remainder = divmod(remainder, 3600)
minutes, seconds = divmod(remainder, 60)
remaining_time_str = ""
if days > 0:
remaining_time_str += "{}d".format(int(days))
if days > 0 or hours > 0:
remaining_time_str += "{:02}h".format(int(hours))
if hours > 0 or minutes > 0:
remaining_time_str += "{:02}m".format(int(minutes))
remaining_time_str += "{:02.2f}s".format(seconds)
return remaining_time_str
def to_int(content, default=None):
if re.match("^-?\d+$", content):
return int(content)
return default
# 将小数转换为百分比字符串的表示方式
def to_percent(num, digits=0):
return str(int(round(num * 100, digits))) + "%"
# 将字符串数组批量格式化
def format_range(content: str, items: Iterable, *args, **kwargs):
return list(map(lambda i: content.format(i, *args, **kwargs), items))
+146
View File
@@ -0,0 +1,146 @@
import configparser
import os
import json
from PublicReference.utils.lan import Language
global 配置格式有误
global 窗口显示模式
global 切装模式
global 调试开关
global 输出数据
global 普雷武器显示
global 武器排名
global SkinVersion
global 觉醒开关
global 攻击目标
global 天劫光环
global 战术白字
global 天御套装
global 千蛛减防
global currentVersion
配置格式有误 = False
conf = configparser.ConfigParser()
# def get_real_resolution():
# """获取真实的分辨率"""
# try:
# from win32 import win32api, win32gui, win32print
# from win32.lib import win32con
# except:
# return 0,0
# else:
# hDC = win32gui.GetDC(0)
# # 横向分辨率
# w = win32print.GetDeviceCaps(hDC, win32con.DESKTOPHORZRES)
# # 纵向分辨率
# h = win32print.GetDeviceCaps(hDC, win32con.DESKTOPVERTRES)
# return w, h
def readConfig(filePath):
#基础设置##################################################################
try:
conf.read(filePath, encoding='utf-8')
except:
try:
conf.read(filePath, encoding='utf-8-sig')
except:
配置格式有误 = True
pass
def readDefault():
data = {}
with open("ResourceFiles/Config/config.json", encoding='utf-8') as fp:
setJson = json.load(fp)
num = 0
for i in setJson:
data[str(num)] = i['default']
num += 1
fp.close()
return data
def readSet(filePath):
data = readDefault()
if os.path.exists(filePath):
with open(filePath, encoding='utf-8') as fp:
setJson = json.load(fp)
data.update(setJson)
fp.close()
return data
setJson = readSet("ResourceFiles/Config/set.json")
窗口显示模式 = setJson['0']
普雷武器显示 = setJson['1']
切装模式 = setJson['2']
输出数据 = setJson['3']
调试开关 = setJson['4']
SkinVersion = ["DNFStyle", "LightStyle", "None"][setJson['5']]
觉醒开关 = setJson['6']
武器排名 = setJson['7']
自动检查更新 = setJson['8']
天劫光环 = setJson['9']
战术白字 = 0.40 + 0.05 * setJson['10']
天御套装 = setJson['11']
千蛛减防 = setJson['12']
多语言开关 = setJson['13']
if 输出数据 == 1:
if not os.path.exists('./数据记录'):
os.makedirs('./数据记录')
#多语言##################################################################
lan = Language()
defaultLanPaths = ['language_chn', 'language_kor']
languageJson = []
languagePath = "ResourceFiles/Config/language/{}.json".format(
defaultLanPaths[多语言开关])
lan.load_json(languagePath)
def trans(text, **kwargs):
return lan.trans(text, **kwargs)
#攻击目标##################################################################
readConfig(trans('ResourceFiles/Config/攻击目标.ini'))
# w, h = get_real_resolution()
# # 4K及以上才自动开启
# if w > 2048 and h > 1080:
# 窗口显示模式 = 1
# 怪物属性
攻击目标 = []
for i in range(100):
try:
temp = []
item = 'item' + str(i)
temp.append(conf.get(item, '名称'))
temp.append(conf.getint(item, '防御'))
temp.append(conf.getint(item, '火抗'))
temp.append(conf.getint(item, '冰抗'))
temp.append(conf.getint(item, '光抗'))
temp.append(conf.getint(item, '暗抗'))
攻击目标.append(temp)
except Exception as error:
break
if len(攻击目标) == 0:
攻击目标 = [['120沙袋(绿)', 443243, 0, 0, 0, 0]]
with open("ResourceFiles/Config/release_version.json") as fp:
versionInfo = json.load(fp)
currentVersion = versionInfo['version'].replace('-', '.')
# self.自动检查版本 = versionInfo['AutoCheckUpdate']
+398
View File
@@ -0,0 +1,398 @@
import multiprocessing
import queue
import threading
import time
import os
import math
from typing import Iterable
from PyQt5 import QtWidgets
from PyQt5 import QtGui
from PyQt5.QtCore import Qt, pyqtSignal
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import QUrl
from .log import logger
from .calc_core import CalcData
from .common import format_time
from .minheap import MinHeap, MinHeapWithQueue, batch_size
from .producer_consumer import producer, producer_data, thread_num, thread_task
from .storex import *
from copy import *
# from .MainWindow import *
import chardet
import sys
import platform
import ctypes
from ctypes import *
from PublicReference.utils.config import *
def resource_path(relative_path):
if getattr(sys, 'frozen', False): # 是否Bundle Resource
base_path = sys._MEIPASS
else:
base_path = os.path.abspath(".")
return os.path.join(base_path, relative_path)
preferred = None
try:
if platform.system() != "Windows":
dllPath = resource_path(
os.path.join("InternalFile", "DLL", "Preferred.dylib"))
preferred = ctypes.CDLL(dllPath)
# logger.info("Preferred included.")
else:
dllPath = resource_path(
os.path.join("InternalFile", "DLL", "Preferred.dll"))
preferred = ctypes.WinDLL(dllPath)
except Exception as e:
logger.error(e)
skillDataPath = resource_path("SkillData")
# 100级史诗套数据
防具套装 = ("古代祭祀的神圣仪式", "遗忘魔法师的馈赠", "天堂舞姬", "死亡阴影", "皇家裁决者宣言", "龙血玄黄", "贫瘠沙漠的遗产",
"炙炎之盛宴", "擎天战甲", "噩梦:地狱之路", "传奇铁匠-封神", "荆棘漫天", "永恒不息之路", "命运歧路",
"大自然的呼吸")
首饰套装 = ("上古尘封术式", "破晓曦光", "幸运三角", "精灵使的权能")
特殊套装 = ("军神的隐秘遗产", "灵宝:世间真理", "时间战争的残骸", "能量主宰")
上链左套装 = ("深渊窥视者", "圣者的黄昏", "坎坷命运", "吞噬愤怒")
镯下右套装 = ("黑魔法探求者", "时空旅行者", "穿透命运的呐喊", "狂乱追随者")
环鞋指套装 = ("地狱求道者", "次元旅行者", "天命无常", "悲剧的残骸")
部位列表 = ("上衣", "头肩", "下装", "腰带", "鞋", "手镯", "项链", "戒指", "耳环", "辅助装备", "魔法石",
"武器")
奥兹玛部位列表 = ("头肩", "腰带", "鞋", "项链", "魔法石")
奥兹玛套装 = ('阿斯特罗斯', '贝利亚斯', '雷德梅恩', '罗什巴赫', '泰玛特')
希洛克部位列表 = ("下装", "戒指", "辅助装备")
希洛克套装 = ('奈克斯', '暗杀者', '卢克西', '守门人', '洛多斯')
部位字典 = {
"上衣": 0,
"头肩": 1,
"下装": 2,
"腰带": 3,
"鞋": 4,
"手镯": 5,
"项链": 6,
"戒指": 7,
"耳环": 8,
"辅助装备": 9,
"魔法石": 10,
"武器": 11
}
颜色 = {
'神话': '#E0502F',
'史诗': '#FFB400',
'传说': '#FF7800',
'神器': '#FF00FF',
'稀有': '#B36BFF'
}
总套装列表 = [防具套装, 首饰套装, 特殊套装, 上链左套装, 镯下右套装, 环鞋指套装]
所有套装列表 = 防具套装 + 首饰套装 + 特殊套装 + 上链左套装 + 镯下右套装 + 环鞋指套装
def getQCSS(FileName):
try:
content = store.get("./ResourceFiles/Skins/" + SkinVersion + "/" +
FileName)
if content == None:
with open(
"./ResourceFiles/Skins/" + SkinVersion + "/" + FileName +
".qcss", "rb") as fp:
content = fp.read()
encoding = chardet.detect(content) or {}
content = content.decode(encoding.get("encoding") or "utf-8")
store.set(
"./ResourceFiles/Skins/" + SkinVersion + "/" + FileName,
content)
return content
except Exception as error:
return ''
# 部分控件样式
按钮样式 = getQCSS('按钮样式')
# if 按钮样式 == '' : 按钮样式 = 'QPushButton{font-size:12px;color:white;background-color:rgba(70,134,197,0.8);border:1px;border-radius:10px} QPushButton:hover{background-color:rgba(65,105,225,0.8)}'
按钮样式_warn = getQCSS('按钮样式_warn')
# if 按钮样式_warn == '' : 按钮样式_warn ='''QPushButton{font-size:14px;color:white;background-color:rgba(197,34,70,0.8);border:1px;border-radius:10px} QPushButton:hover{background-color:rgba(225,5,65,0.8)}'''
套装按钮样式 = getQCSS('套装按钮样式')
# if 套装按钮样式 == '' :套装按钮样式 = '''QPushButton{font-size:12px;color:white;background-color:rgba(70,134,197,0.8);border:1px;border-radius:5px;} QPushButton:hover{background-color:rgba(65,105,225,0.8)}'''
按钮样式2 = 'QPushButton{background-color:rgba(0,0,0,0);border:1px;border-radius:5px} QPushButton:hover{background-color:rgba(252,224,0,0.2)}'
按钮样式3 = '''QPushButton{font-size:13px;color:white;background-color:rgba(255,255,255,0.1);border:1px;border-radius:5px} QPushButton:hover{background-color:rgba(65,105,225,0.5)}
QPushButton::menu-indicator {image:none}'''
按钮样式 = getQCSS('按钮样式')
# if 按钮样式 == '' :按钮样式 = 'QPushButton{font-size:12px;color:white;background-color:grey;border:1px;border-radius:10px}'
复选框样式 = getQCSS('复选框样式')
# if 复选框样式 == '' :复选框样式 = "QCheckBox{font-size:12px;color:white;background-color:rgba(70,134,197,0.8);border:1px;border-radius:3px} QCheckBox:hover{background-color:rgba(65,105,225,0.8)}"
# 复选框样式 = getQCSS('复选框样式')
# if 复选框样式 == '' :复选框样式 = "QCheckBox{font-size:12px;color:white;background-color:grey;border:1px;border-radius:3px} QCheckBox:hover{background-color:rgba(65,105,225,0.8)}"
标签样式 = getQCSS('标签样式')
# if 标签样式 == '' :标签样式 = "QLabel{font-size:12px;color:white}"
页签样式 = getQCSS('页签样式')
# if 页签样式 == '' :页签样式 = '''QToolButton{font-size:13px;color:white;background-color:rgba(70,130,200,0.8)} QToolButton:hover{background-color:rgba(40,100,235,0.8)}'''
页签样式_选中 = getQCSS('页签样式_选中')
# if 页签样式_选中 == '' :页签样式_选中 = '''QToolButton{font-size:13px;color:white;background-color:rgba(200,30,30,0.8)} QToolButton:hover{background-color:rgba(235,0,0,0.8)}'''
下拉框样式 = getQCSS('下拉框样式')
# if 下拉框样式 == '' :下拉框样式 = '''QComboBox{font-size:12px;color:white;background-color:rgba(70,134,197,0.8);border:1px;border-radius:5px;}
# QComboBox:disabled{font-size:12px;color:white;background-color:grey;border:1px;border-radius:5px;} QComboBox QAbstractItemView::item {height: 18px;}
# QComboBox:hover{background-color:rgba(65,105,225,0.8)}
# QComboBox QAbstractItemView::item {height: 18px;background-color:white}
# QComboBox QAbstractItemView::item:hover,QComboBox QAbstractItemView::item:focus {background-color:rgba(179,206,255,1);color:black}
# QComboBox QScrollBar{
# background: white !important
# }
# QComboBox QScrollBar::handle{
# background: rgb(220,220,220) !important
# }
# '''
# 下拉框样式 = getQCSS('下拉框样式')
# if 下拉框样式 == '' :下拉框样式 = "QComboBox{font-size:12px;color:white;background-color:grey;border:1px;border-radius:5px;} QComboBox QAbstractItemView::item {height: 18px;}"
下拉框样式_down = getQCSS('下拉框样式_down')
# if 下拉框样式_down == '' :下拉框样式_down = '''QComboBox{font-size:12px;color:white;background-color:rgba(34,157,70,0.8);border:1px;border-radius:5px;} QComboBox:hover{background-color:rgba(5,185,65,0.8)}
# QComboBox QAbstractItemView::item {height: 18px;background-color:white}
# QComboBox QAbstractItemView::item:hover,QComboBox QAbstractItemView::item:focus {background-color:rgba(179,206,255,1) !important;color:black}
# QComboBox QScrollBar{
# background: white !important
# }
# QComboBox QScrollBar::handle{
# background: rgb(220,220,220) !important
# }
# '''
下拉框样式_warn = getQCSS('下拉框样式_warn')
# if 下拉框样式_warn == '' :下拉框样式_warn = '''QComboBox{font-size:12px;color:white;background-color:rgba(197,34,70,0.8);border:1px;border-radius:5px;} QComboBox:hover{background-color:rgba(225,5,65,0.8)}
# QComboBox QAbstractItemView::item {height: 18px;background-color:white}
# QComboBox QAbstractItemView::item:hover,QComboBox QAbstractItemView::item:focus {background-color:rgba(179,206,255,1)!important; color:black}
# QComboBox QScrollBar{
# background: white !important
# }
# QComboBox QScrollBar::handle{
# background: rgb(220,220,220) !important
# }
# '''
下拉框样式_detail = getQCSS('下拉框样式_detail')
输入框样式 = getQCSS('输入框样式')
# if 输入框样式 == '' :输入框样式 ='''QLineEdit{font-size:12px;color:white;background-color:rgba(70,134,197,0.8);border:1px;border-radius:3px} QLineEdit:hover{background-color:rgba(65,105,225,0.8)}'''
标签样式_2 = getQCSS('标签样式_2')
# if 标签样式_2 == '' :标签样式_2 = "QLabel{font-size:12px;color:yellow}"
文本框样式黄 = getQCSS('文本框样式黄')
# if 文本框样式黄 == '' :文本框样式黄 = "QLineEdit{font-size:12px;color:yellow;background-color:rgba(70,134,197,0.8);border:1px;border-radius:3px}"
文本框样式白 = getQCSS('文本框样式白')
# if 文本框样式白 == '' :文本框样式白 = "QLineEdit{font-size:12px;color:white;background-color:rgba(70,134,197,0.8);border:1px;border-radius:3px}"
滚动条样式 = getQCSS('滚动条样式')
标题背景 = getQCSS('标题背景')
标题字体 = getQCSS('标题字体')
# 滚动条样式 = '''/*QScrollBar Style*/
# /*纵向滚动条*/
# QScrollBar {
# background: transparent; /*背景透明*/
# width: 10px; /*宽度*/
# margin: 0px 0px 0px 0px; /**/
# padding-top: 5px; /*距离上面12px*/
# padding-bottom: 5px; /*距离底部12px*/
# }
# /*纵向滚动条上面的滑块*/
# QScrollBar::handle {
# background: rgba(245,245,245,50);
# width: 10px;
# border: none;
# }
# /*纵向滚动条下部分块*/
# QScrollBar::add-page:vertical {
# width: 10px;
# background: transparent;
# }
# /*横向滚动条后面部分块*/
# QScrollBar::add-page:horizontal {
# height: 10px;
# background: transparent;
# }
# /*纵向滚动条上面部分块*/
# QScrollBar::sub-page:vertical {
# width: 10px;
# background: transparent;
# }
# /*横向滚动条左部分块*/
# QScrollBar::sub-page:horizontal {
# height: 10px;
# background: transparent;
# }
# /*纵向滚动条顶部三角形位置*/
# QScrollBar::sub-line:vertical {
# height: 12px;
# width: 10px;
# background: transparent;
# subcontrol-position: top;
# }
# /*横向滚动条左侧三角形位置*/
# QScrollBar::sub-line:horizontal {
# height: 10px;
# width: 12px;
# background: transparent;
# subcontrol-position: left;
# }
# /*纵向滚动条下面三角形部分*/
# QScrollBar::add-line:vertical {
# height: 12px;
# width: 10px;
# background: transparent;
# subcontrol-position: bottom;
# }
# /*横向滚动条右边的三角形部分*/
# QScrollBar::add-line:horizontal {
# height: 10px;
# width: 12px;
# background: transparent;
# subcontrol-position: right;
# }
# '''
游戏滚动条样式模拟 = '''/*纵向滚动条*/
QScrollBar {
background: transparent; /*背景透明*/
width: 12px; /*宽度*/
margin: 0px 0px 0px 0px; /**/
padding-top: 16px; /*距离上面12px*/
padding-bottom: 16px; /*距离底部12px*/
padding-right:1px;
}
/*纵向滚动条上面的滑块*/
QScrollBar::handle {
background: rgb(36,36,36);
width: 12px;
border: 1px inset rgb(76,61,39);
}
/*纵向滚动条下部分块*/
QScrollBar::add-page:vertical {
width: 11px;
background: transparent;
}
/*横向滚动条后面部分块*/
QScrollBar::add-page:horizontal {
height: 11px;
background: transparent;
}
/*纵向滚动条上面部分块*/
QScrollBar::sub-page:vertical {
width: 11px;
background: transparent;
}
/*横向滚动条左部分块*/
QScrollBar::sub-page:horizontal {
height: 11px;
background: transparent;
}
/*纵向滚动条顶部三角形位置*/
QScrollBar::sub-line:vertical {
height: 12px;
width: 11px;
background: transparent;
subcontrol-position: top;
}
/*横向滚动条左侧三角形位置*/
QScrollBar::sub-line:horizontal {
height: 12px;
width: 11px;
background: transparent;
subcontrol-position: left;
}
/*纵向滚动条下面三角形部分*/
QScrollBar::add-line:vertical {
height: 12px;
width: 11px;
background: transparent;
subcontrol-position: bottom;
}
/*横向滚动条右边的三角形部分*/
QScrollBar::add-line:horizontal {
height: 12px;
width: 11px;
background: transparent;
subcontrol-position: right;
}
/*纵向滚动条向上的三角形小图标*/
QScrollBar::up-arrow:vertical {
image: url(ResourceFiles/img/UI/up.png);
padding-top:3px;
padding-right:2px;
}
QScrollBar::down-arrow:vertical {
image: url(ResourceFiles/img/UI/down.png);
padding-bottom:4px;
padding-right:2px;
}
'''
class MyQComboBox(QComboBox):
def __init__(self, win, useWheel=True):
super().__init__(win)
self.useWheel = useWheel
self.setView(QListView())
self.setStyleSheet(下拉框样式)
def addItems(self, texts: Iterable[str]):
for text in texts:
self.addItem(text)
pass
def addItem(self, text, userData=None):
if userData is None:
userData = text
text = trans(text)
super().addItem(text, userData)
pass
def wheelEvent(self, e: QtGui.QWheelEvent) -> None:
if self.useWheel:
return super().wheelEvent(e)
else:
pass
class MyQToolButton(QToolButton):
DoubleClickSig = pyqtSignal(str)
def mouseDoubleClickEvent(self, e): # 双击
sigContent = self.objectName()
self.DoubleClickSig.emit(sigContent)
class MyQLabel(QtWidgets.QLabel):
# 自定义信号, 注意信号必须为类属性
button_clicked_signal = pyqtSignal()
def __init__(self, parent=None):
super(MyQLabel, self).__init__(parent)
def mouseReleaseEvent(self, QMouseEvent):
self.button_clicked_signal.emit()
# 可在外部与槽函数连接
def connect_customized_slot(self, func):
self.button_clicked_signal.connect(func)
+161
View File
@@ -0,0 +1,161 @@
import types
import weakref
from copyreg import dispatch_table
def copy(x):
cls = type(x)
copier = _copy_dispatch.get(cls)
if copier:
return copier(x)
if issubclass(cls, type):
return _copy_immutable(x)
copier = getattr(cls, "__copy__", None)
if copier is not None:
return copier(x)
reductor = dispatch_table.get(cls)
if reductor is not None:
rv = reductor(x)
else:
reductor = getattr(x, "__reduce_ex__", None)
if reductor is not None:
rv = reductor(4)
else:
reductor = getattr(x, "__reduce__", None)
if reductor:
rv = reductor()
if isinstance(rv, str):
return x
return _reconstruct(x, None, *rv)
_copy_dispatch = d = {}
def _copy_immutable(x):
return x
for t in (type(None), int, float, bool, complex, str, tuple, bytes, frozenset,
type, range, slice, property, types.BuiltinFunctionType,
type(Ellipsis), type(NotImplemented), types.FunctionType,
weakref.ref):
d[t] = _copy_immutable
t = getattr(types, "CodeType", None)
if t is not None:
d[t] = _copy_immutable
d[list] = list.copy
d[dict] = dict.copy
d[set] = set.copy
d[bytearray] = bytearray.copy
def deepcopy(x):
cls = type(x)
copier = _deepcopy_dispatch.get(cls)
if copier is not None:
y = copier(x)
else:
if issubclass(cls, type):
y = _deepcopy_atomic(x)
else:
copier = getattr(x, "__deepcopy__", None)
reductor = dispatch_table.get(cls)
if reductor:
rv = reductor(x)
else:
reductor = getattr(x, "__reduce_ex__", None)
if reductor is not None:
rv = reductor(4)
else:
reductor = getattr(x, "__reduce__", None)
if reductor:
rv = reductor()
if isinstance(rv, str):
y = x
else:
y = _reconstruct(x, *rv)
return y
_deepcopy_dispatch = d = {}
def _deepcopy_atomic(x):
return x
d[type(None)] = _deepcopy_atomic
d[type(Ellipsis)] = _deepcopy_atomic
d[type(NotImplemented)] = _deepcopy_atomic
d[int] = _deepcopy_atomic
d[float] = _deepcopy_atomic
d[bool] = _deepcopy_atomic
d[complex] = _deepcopy_atomic
d[bytes] = _deepcopy_atomic
d[str] = _deepcopy_atomic
d[types.CodeType] = _deepcopy_atomic
d[type] = _deepcopy_atomic
d[range] = _deepcopy_atomic
d[types.BuiltinFunctionType] = _deepcopy_atomic
d[types.FunctionType] = _deepcopy_atomic
d[weakref.ref] = _deepcopy_atomic
d[property] = _deepcopy_atomic
def _deepcopy_list(x, deepcopy=deepcopy):
y = []
append = y.append
for a in x:
append(deepcopy(a))
return y
d[list] = _deepcopy_list
def _deepcopy_dict(x, deepcopy=deepcopy):
y = {}
for key, value in x.items():
y[deepcopy(key)] = deepcopy(value)
return y
d[dict] = _deepcopy_dict
def _reconstruct(x,
func,
args,
state=None,
listiter=None,
dictiter=None,
deepcopy=deepcopy):
y = func(*args)
if state is not None:
state = deepcopy(state)
if hasattr(y, '__setstate__'):
y.__setstate__(state)
else:
if isinstance(state, tuple) and len(state) == 2:
state, slotstate = state
else:
slotstate = None
if state is not None:
y.__dict__.update(state)
if slotstate is not None:
for key, value in slotstate.items():
setattr(y, key, value)
if listiter is not None:
for item in listiter:
item = deepcopy(item)
y.append(item)
if dictiter is not None:
for key, value in dictiter:
key = deepcopy(key)
value = deepcopy(value)
y[key] = value
return y
File diff suppressed because one or more lines are too long
+44
View File
@@ -0,0 +1,44 @@
from ctypes import sizeof
from operator import le
import os
import json
class Language():
def __init__(self):
self.dicts = {}
self.dicts[""] = ""
pass
def load_json(self, path):
if os.path.exists(path):
with open(path, encoding='utf-8') as fp:
self.dicts.update(json.load(fp))
fp.close()
def __getitem__(self, key):
return self.dicts.get(key, key)
## key: 原始文本 使用{word}的格式 可以格式化为对应的值
## **kwargs: 键值对可变参数 在{word} 之前 将#key转换为对应的value
def trans(self, text, **kwargs):
if isinstance(text, list):
return list(map(lambda i: self.trans(i, **kwargs), text))
text = str(text)
value = text
if self.dicts.__contains__(text):
value = self.dicts.get(text)
if value == '':
value = text
else:
for kw in kwargs.keys():
_old = "${}".format(kw)
_new = str(kwargs.get(kw))
text = text.replace(_old, _new)
try:
value = text.format_map(self)
except Exception:
# 后续待完善
return value
value = value.replace("$", "{}")
return value
+51
View File
@@ -0,0 +1,51 @@
import logging
import pathlib
import sys
from typing import Callable
from functools import wraps
###########################################################
# logging #
###########################################################
logFormatter = logging.Formatter(
"%(asctime)s %(levelname)-5.5s [%(name)s] %(funcName)s:%(lineno)d: %(message)s"
)
logger = logging.getLogger()
logger.setLevel(logging.DEBUG)
logger.name = "DNFCalculating"
log_directory = "logs"
try:
pathlib.Path(log_directory).mkdir(parents=True, exist_ok=True)
except PermissionError as err:
print("创建日志目录logs失败,请确认是否限制了基础的运行权限")
sys.exit(-1)
def try_except(show_exception_info=True,
show_last_process_result=True,
extra_msg="",
return_val_on_except=None) -> Callable:
def decorator(fun):
@wraps(fun)
def wrapper(*args, **kwargs):
try:
return fun(*args, **kwargs)
except Exception as e:
msg = f"执行{fun.__name__}({args}, {kwargs})出错了"
if extra_msg != "":
msg += ", " + extra_msg
# msg += check_some_exception(e, show_last_process_result)
logFunc = logger.error
if not show_exception_info:
logFunc = logger.debug
logFunc(msg, exc_info=e)
return return_val_on_except
return wrapper
return decorator
+76
View File
@@ -0,0 +1,76 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# -------------------------------
# File : minheap
# DateTime : 2020/6/10 0010 20:58
# Author : Chen Ji
# Email : fzls.zju@gmail.com
# -------------------------------
import time
# 用于实现排行的最小堆
from heapq import heapify, heappush, heappushpop
from multiprocessing import Queue
# 工作进程与主进程的数据传输配置
# 批量向主进程传输计算结果(已本地预处理过)的大小
batch_size = 100
# 倍增阶段的临界批量大小
batch_stage_double_upper_bound = 6400
# 累加阶段每次增加的批量大小
batch_linear_increase_size = 100
# 预期数据传输队列的最大大小
expected_qsize = 100
class MinHeap:
def __init__(self, top_n, batch_size):
self.h = []
self.length = top_n
heapify(self.h)
self.processed_result_count = 0
self.batch_size = batch_size
def add(self, element):
if len(self.h) < self.length:
heappush(self.h, element)
else:
heappushpop(self.h, element)
def getTop(self):
return sorted(self.h, reverse=True)
def merge(self, other):
"""
合并另一个堆
@type other MinHeap
"""
for elem in other.h:
self.add(elem)
self.processed_result_count += other.processed_result_count
self.batch_size = other.batch_size
def reset(self):
self.h = []
heapify(self.h)
self.processed_result_count = 0
def update_batch_size(self):
if self.batch_size < batch_stage_double_upper_bound:
self.batch_size *= 2
else:
self.batch_size += batch_linear_increase_size
class MinHeapWithQueue:
def __init__(self, name: str, minheap: MinHeap, minheap_queue: Queue):
self.name = name
self.minheap = minheap
self.minheap_queue = minheap_queue
self.start_time = time.time()
def process_results_per_second(self) -> float:
return float(self.minheap.processed_result_count) / (
time.time() - self.start_time + 1e-6)
@@ -0,0 +1,69 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# -------------------------------
# File : producer_consumer
# DateTime : 2020/6/7 0007 12:04
# Author : Chen Ji
# Email : fzls.zju@gmail.com
# -------------------------------
import multiprocessing
import traceback
from .log import logger
thread_num = multiprocessing.cpu_count()
thread_task = 4
class ProducerData:
def __init__(self):
self.work_queue = None # type: multiprocessing.JoinableQueue
self.calc_index = 0
self.produced_count = 0
producer_data = ProducerData()
def producer(*args):
producer_data.work_queue.put((producer_data.calc_index, args))
producer_data.produced_count += 1
# logger.info("producer put %3dth work into work queue", producer_data.produced_count)
def consumer(work_queue, work_func):
"""
@type work_queue: multiprocessing.JoinableQueue
"""
current_process = multiprocessing.current_process()
# logger.info("work thread={} started, ready to work".format(current_process))
current_calc_index = 0
processed_count = 0
continue_wrok = True
while continue_wrok:
try:
calc_index, args = work_queue.get()
if calc_index != current_calc_index:
current_calc_index = calc_index
processed_count = 0
processed_count += 1
# logger.info("work thread {} processing {}th work".format(current_process, processed_count))
work_func(*args)
except BrokenPipeError as error:
# 这个一般是程序退出的时候发生的,这种情况直接退出
logger.warning("work thread={} BrokenPipeError quit job".format(
current_process))
continue_wrok = False
except Exception as error:
logger.error(
"work thread {} error={} processed count={}\n detail {}".
format(current_process, error, processed_count,
traceback.print_exc()))
# logger.error("work thread {} error={} processed count={}\n".format(current_process, error, processed_count))
finally:
work_queue.task_done()
# logger.info("work thread ={} stopped, processed_count={}".format(current_process, processed_count))
+176
View File
@@ -0,0 +1,176 @@
##
##
## 用于存储数据,及组件间通信
# Author: Apateat
##
##
from copy import copy
import re
class Store:
def __init__(self):
self.__states = {}
self.last_state = None
pass
# 设置值
def set(self, key: str, value: any):
field = self.__get_field__(key)
field.setValue(value)
return self
# 获取值
def get(self, key: str, defaultValue: any = None):
field = self.__get_field__(key)
_value = field.getValue()
if _value is None:
_value = defaultValue
return _value
def __getitem__(self, __key: str):
return self.get(__key)
# 获取一个克隆值
def clone(self, key: str, defaultValue: any = None):
value = self.get(key, defaultValue)
return copy(value)
# 主动通知变化
def emit(self, key: str, value: any = None):
field = self.__get_field__(key)
field.emit(value)
return self
# 找到首个不为空的值
def first(self, *names):
value = None
for name in names:
value = self.get(name)
if value is not None:
break
return value
# 计算属性
def compute(self, key: str, getter, setter=None):
field = self.__get_field__(key)
field.apply(setter)
field.getter = getter
return self
# 获取值并设置值
def use(self, key: str, handle):
if callable(handle):
value = handle(self.get(key))
self.set(key, value)
return self
# 批量修改整个集合
def convert(self, key: str, handle):
return self.use(key, lambda value: list(map(handle, value)))
# 绑定对象的属性
def bind(self, obj: object, name: str, key=None, defaultValue: any = None):
def getter():
return getattr(obj, name)
def setter(value):
setattr(obj, name, value)
# 如果属性不存在 则初始化
if hasattr(obj, name) is False or defaultValue is not None:
setattr(obj, name, defaultValue)
return self.compute(key, getter, setter)
# 根据指定的条件删除
def delete(self, pattern=None):
match = lambda _: True
if pattern is not None:
if callable(pattern):
match = pattern
elif isinstance(pattern, str):
match = lambda s: re.match(pattern, s)
keys = list(filter(match, self.__states.keys()))
for key in keys:
self.__states.pop(key)
return self
# 根据指定的条件导出
def exports(self, match=lambda _: True):
result = {}
for key in self.__states.keys():
if match(key):
result[key] = self.get(key)
return result
# 导入
def imports(self, pairs: dict = {}, prefix=''):
for key in pairs.keys():
self.set(prefix + str(key), pairs[key])
return self
# 只读属性
def const(self, key: str, value: any):
return self.compute(key, lambda: value)
# 监听值的变化
def watch(self, key: str, valueChanged):
return self.compute(key, None, valueChanged)
def linstener(self, key: str):
def change(value):
self.set(key, value)
pass
return change
def __get_field__(self, key: str):
field = None
if re.search("\{\w+\}", key):
key = key.format_map(self)
if self.__states.__contains__(key):
field = self.__states[key]
else:
field = Store.Field()
self.__states[key] = field
self.last_state = (key, field)
return field
class Field:
def __init__(self, value=None):
self.__value = None
self.getter = None
self.valueChangeds = []
self.locked = False
self.setValue(value)
pass
def setValue(self, value):
if self.locked:
return
self.locked = True
self.__value = value
self.emit(value)
self.locked = False
return self
def getValue(self):
if callable(self.getter):
self.__value = self.getter()
return self.__value
def emit(self, value=None):
if value is None:
value = self.getValue()
for valueChanged in self.valueChangeds:
if (callable(valueChanged)):
valueChanged(value)
return
def apply(self, valueChanged):
if callable(valueChanged):
self.valueChangeds.append(valueChanged)
store = Store()
+18
View File
@@ -0,0 +1,18 @@
import hashlib
import sys
from .storex import store
def get_mac_address():
try:
import wmi
uniqueCode = store.get("uniqueCode")
if uniqueCode == None:
w = wmi.WMI()
uniqueCode = hashlib.md5(
w.Win32_DiskDrive()[0].SerialNumber.encode(
encoding='UTF-8')).hexdigest()
store.set("uniqueCode", uniqueCode)
return uniqueCode
except:
return ''
+185
View File
@@ -0,0 +1,185 @@
# 使用次数统计脚本
import os
import platform
import threading
import uuid
from functools import wraps
from typing import Any, Callable, Tuple
from urllib.parse import quote_plus
from .config import currentVersion
import sys
import requests
from .log import logger, try_except
# ---------------- 需要自行调整的配置 --------------------------------
# Google Analytics 统计代码的UA,在 https://analytics.google.com/ 申请新的ua,具体流程可百度
GA_TRACKING_ID = "UA-212229431-1"
# ua名称,自定义
USER_AGENT = "DNFCalculating"
# 应用名称,自定义
APP_NAME = "DNFCalculating"
# 网址域名,自定义
DOCUMENT_HOSTNAME = "dnf-calculating.com"
# 版本号,可以使用现在的版本变量
APP_VERSION = currentVersion
# ---------------- 枚举定义 --------------------------------
GA_API_URL = "https://www.google-analytics.com/collect"
GA_REPORT_TYPE_EVENT = "event"
GA_REPORT_TYPE_PAGE_VIEW = "page_view"
# ---------------- utils --------------------------------
def async_call(cb, *args, **params):
threading.Thread(target=cb, args=args, kwargs=params, daemon=True).start()
def get_cid():
return "{}-{}".format(platform.node(), uuid.getnode())
def get_resolution() -> str:
width, height = get_screen_size()
return f"{width}x{height}"
def get_screen_size() -> Tuple[int, int]:
"""
:return: 屏幕宽度和高度
"""
if not is_windows():
return 1920, 1080
import win32api
width, height = win32api.GetSystemMetrics(0), win32api.GetSystemMetrics(1)
return width, height
def is_windows() -> bool:
return platform.system() == "Windows"
# ---------------- 接口封装 --------------------------------
def increase_counter(name: Any = "",
ga_type=GA_REPORT_TYPE_EVENT,
ga_category="",
ga_misc_params: dict = None):
if "main.py" in sys.argv[0]:
return
name = str(name)
if name == "":
raise AssertionError("increase_counter name not set")
async_call(increase_counter_sync_google_analytics, name, ga_type,
ga_category, ga_misc_params)
@try_except(show_exception_info=False)
def increase_counter_sync_google_analytics(name: str, ga_type: str,
ga_category: str,
ga_misc_params: dict):
# 上报谷歌分析 v3
logger.debug(f"report to google analytics v3, name = {name}")
if ga_type == GA_REPORT_TYPE_EVENT:
if ga_category == "":
# 如果ga_category为空,则尝试从name中解析,假设name中以/分隔的第一个部分作为ga_category
parts = name.split('/', 1)
if len(parts) == 2:
ga_category, name = parts
else:
ga_category = "counter"
track_event(ga_category, name, ga_misc_params)
elif ga_type == GA_REPORT_TYPE_PAGE_VIEW:
track_page(name, ga_misc_params)
else:
logger.error(f"unknow ga_type={ga_type}")
# ---------------- Google Analytics 上报脚本 --------------------------------
# note: 查看数据地址 https://analytics.google.com/analytics/web/#/
# note: 当发现上报失败时,可以将打印的post body复制到 https://ga-dev-tools.web.app/hit-builder/ 进行校验,看是否缺了参数,或者有参数不符合格式
# note: 参数文档 https://developers.google.com/analytics/devguides/collection/protocol/v1/parameters
headers = {
"user-agent": USER_AGENT,
}
common_data = {
'v': '1', # API Version.
'tid': GA_TRACKING_ID, # Tracking ID / Property ID.
'cid': get_cid(
), # Anonymous Client Identifier. Ideally, this should be a UUID that is associated with particular user, device, or browser instance.
'ua': USER_AGENT,
'an': APP_NAME,
'av': APP_VERSION,
'ds': 'app',
'sr': get_resolution(),
}
@try_except(show_exception_info=False)
def track_event(category: str,
action: str,
label=None,
value=0,
ga_misc_params: dict = None):
if ga_misc_params is None:
ga_misc_params = {}
data = {
**common_data,
't': 'event', # Event hit type.
'ec': category, # Event category.
'ea': action, # Event action.
'el': label, # Event label.
'ev': value, # Event value, must be an integer
**ga_misc_params, # 透传的一些额外参数
}
res = requests.post(GA_API_URL, data=data, headers=headers, timeout=10)
logger.debug(f"request body = {res.request.body}")
@try_except(show_exception_info=False)
def track_page(page: str, ga_misc_params: dict = None):
if ga_misc_params is None:
ga_misc_params = {}
page = quote_plus(page)
data = {
**common_data,
't': 'pageview', # Event hit type.
'dh': DOCUMENT_HOSTNAME, # Document hostname.
'dp': page, # Page.
'dt': "", # Title.
**ga_misc_params, # 透传的一些额外参数
}
res = requests.post(GA_API_URL, data=data, timeout=10)
logger.debug(f"request body = {res.request.body}")
# ---------------- test --------------------------------
def test():
increase_counter("test_event", GA_REPORT_TYPE_EVENT)
increase_counter("test_page_view", GA_REPORT_TYPE_PAGE_VIEW)
# track_event("example", "test")
track_page("/example/test_quote")
os.system("PAUSE")
if __name__ == '__main__':
test()
+17
View File
@@ -0,0 +1,17 @@
# 水印附近背景色-排行榜界面
watermark_surrounding_backgroud_color_ranking = (21, 39, 65)
# 水印附近背景色-详情界面
watermark_surrounding_backgroud_color_detail = (27, 14, 6)
# 水印与背景色的偏差值
watermark_delta = -5
# 考虑到图片压缩,水印色彩允许的上下偏移值
watermark_tolerance_value = 1
# 替换为显著颜色
reverse_color = (255, 255, 255)
def make_watermark_qt_color_string(
watermark_surrounding_backgroud_color: tuple):
color_str = ', '.join((str(rgb + watermark_delta)
for rgb in watermark_surrounding_backgroud_color))
return f"color:rgb({color_str})"
File diff suppressed because it is too large Load Diff