Lv100版本源码上传
This commit is contained in:
@@ -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
|
||||
]
|
||||
@@ -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()
|
||||
@@ -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))
|
||||
@@ -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']
|
||||
@@ -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)
|
||||
@@ -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
@@ -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
|
||||
@@ -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
|
||||
@@ -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))
|
||||
@@ -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()
|
||||
@@ -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 ''
|
||||
@@ -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()
|
||||
@@ -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
Reference in New Issue
Block a user