Skip to content

Commit 0f41186

Browse files
committed
Update to v1.2
1 parent 305dae8 commit 0f41186

File tree

6 files changed

+450
-178
lines changed

6 files changed

+450
-178
lines changed
File renamed without changes.

biscuit/main.py

Lines changed: 250 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,250 @@
1+
"""
2+
@ Name: KomFetch
3+
@ Author: ElofHew
4+
@ Version: 1.2
5+
@ Description: A tool to display system information on Quarter OS.
6+
@ Date: 2025-07-17
7+
@ Last Modified: 2025-07-28
8+
@ License: GNU General Public License v3.0
9+
@ Repository: https://github.com/ElofHew/komfetch
10+
@ Class: Biscuit Application Package
11+
"""
12+
13+
try:
14+
# 导入所需模块
15+
import os
16+
import sys
17+
import json
18+
import shutil
19+
import time as tm
20+
import platform
21+
from colorama import Fore, Back, Style, init
22+
except ImportError as e:
23+
print(f"Error: {e}")
24+
exit()
25+
except Exception as e:
26+
print(f"Error: {e}")
27+
exit()
28+
29+
# 初始化颜色模块
30+
init(autoreset=True)
31+
32+
qos_big_logo = """
33+
%%%%%%%%%%
34+
%%%%%%%%%%%%%%%%%%%%
35+
%%%%%%%%%%%%%%%%%%%%%%%%%%
36+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
37+
%%%%%%%%%%%% %%%%%%%%%%%%
38+
%%%%%%%%%% %%%%%%%%%%
39+
%%%%%%%%% %%%%%%%%%
40+
%%%%%%%%% %%%%%%%%%
41+
%%%%%%%% %%%% %%%%%%%%
42+
%%%%%%%% %%%%%%%% %%%%%%%%
43+
%%%%%%%% %%%%%%%% %%%%%%%%
44+
%%%%%%%% %%%%% %%%%%%%%
45+
%%%%%%%%% %%% %%%%%%%%%
46+
%%%%%%%%% %%%%%%%%%%%%%%
47+
%%%%%%%%%% %%%%%%%%%%%%%
48+
%%%%%%%%%%%% %%%%%%%%%%%%
49+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
50+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
51+
%%%%%%%%%%%%%%%%%%%% %%%%%%%%
52+
%%%%%%%%%% %%%%
53+
"""
54+
# 喜不喜欢我们Quarter OS的比格楼狗[doge]
55+
56+
def get_package_value():
57+
"""获取Quarter OS中,Biscuit和Shizuku的软件包数量"""
58+
# 定义全局变量:biscuit_value和shizuku_value
59+
global biscuit_value, shizuku_value
60+
# 获取Biscuit软件包数量
61+
try:
62+
with open(os.path.join("..", "..", "..", "system", "shell", "apps.json"), "r") as biscuit_sys_file:
63+
biscuit_sys_data = json.load(biscuit_sys_file)
64+
biscuit_value_sys = len(biscuit_sys_data)
65+
with open(os.path.join("..", "..", "shell", "apps.json"), "r") as biscuit_3rd_file:
66+
biscuit_3rd_data = json.load(biscuit_3rd_file)
67+
biscuit_value_3rd = len(biscuit_3rd_data)
68+
biscuit_value = biscuit_value_sys + biscuit_value_3rd
69+
except (FileNotFoundError, json.JSONDecodeError, TypeError, KeyError, IndexError, AttributeError, ValueError, OSError, IOError):
70+
# 若文件不存在或格式错误,则置0
71+
biscuit_value = 0
72+
except Exception as e:
73+
# 其他错误,打印错误信息并退出程序
74+
print(f"{Fore.RED}Error: {e}{Style.RESET_ALL}")
75+
sys.exit(1)
76+
# 获取Shizuku软件包数量
77+
try:
78+
with open(os.path.join("..", "..", "shizuku", "apps.json"), "r") as shizuku_file:
79+
shizuku_data = json.load(shizuku_file)
80+
shizuku_value = len(shizuku_data)
81+
except (FileNotFoundError, json.JSONDecodeError, TypeError, KeyError, IndexError, AttributeError, ValueError, OSError, IOError):
82+
# 若文件不存在或格式错误,则置0
83+
shizuku_value = 0
84+
except Exception as e:
85+
# 其他错误,打印错误信息并退出程序
86+
print(f"{Fore.RED}Error: {e}{Style.RESET_ALL}")
87+
sys.exit(1)
88+
89+
def get_logo_text(i, logo_lines_num, qos_logo_lines, qos_logo_line_width):
90+
"""获取logo的每一行文字"""
91+
logo_text = f"{Fore.BLUE}{qos_logo_lines[i]}{Style.RESET_ALL}" if i < logo_lines_num else " " * qos_logo_line_width
92+
return logo_text
93+
94+
def get_cut_length(i, infos_num, terminal_width, qos_logo_line_width, infos, double):
95+
"""获取info的一行文字截断处理后的文字"""
96+
if i < infos_num:
97+
used_space = qos_logo_line_width if double else 0 + infos[i][2] + 1 # 最后留一个空字符
98+
free_space = terminal_width - used_space # 供info[i][1]使用的剩余空间
99+
dynamical_space = len(infos[i][1]) # 计算当前值需要占用的空间
100+
combined_space = dynamical_space - free_space # 计算需要截断的字符长度(后面的部分)
101+
if combined_space > 0: # 计算结果大于0,说明需要截断
102+
dyna_words = infos[i][1][0:dynamical_space - combined_space] # 截断后的字符串
103+
else: # 计算结果小于等于0,说明不需要截断
104+
dyna_words = infos[i][1] # 直接输出原字符串
105+
info_text = infos[i][0] + dyna_words # 键 + 值
106+
else:
107+
info_text = " " * (terminal_width - qos_logo_line_width) # 若info_num大于实际数量,则填充空白
108+
return info_text
109+
110+
def main():
111+
"""主函数"""
112+
# 获取参数
113+
args = sys.argv[1:] if len(sys.argv) > 1 else []
114+
mode = args[0] if args else "--default"
115+
116+
# 获取Quarter OS的配置文件数据
117+
try:
118+
config_path = os.path.join("..", "..", "config", "config.json")
119+
with open(config_path, "r") as qos_config_file:
120+
config_data = json.load(qos_config_file)
121+
qos_name = config_data.get("name", "other")
122+
qos_version = config_data.get("version", "?")
123+
qos_vercode = config_data.get("vercode", "?")
124+
qos_node = config_data.get("system_name", "qos")
125+
qos_path = config_data.get("qos_path", "unknown path")
126+
qos_activate = config_data.get("activate_statue", False)
127+
qos_edition = config_data.get("qos_edition", "unknown edition")
128+
last_login = config_data.get("last_login", "user")
129+
if qos_name.lower().replace(" ", "") != "quarteros":
130+
raise ValueError # 不是Quarter OS,提示错误并退出程序
131+
except ValueError:
132+
# 提示信息
133+
print(f"{Fore.LIGHTGREEN_EX}This tool only works on Quarter OS version Alpha 0.2.2 or later.{Style.RESET_ALL}")
134+
sys.exit(1)
135+
except FileNotFoundError:
136+
print(f"{Fore.RED}Error: Config file not found.{Style.RESET_ALL}")
137+
sys.exit(1)
138+
except json.JSONDecodeError:
139+
print(f"{Fore.RED}Error: Config file is not a valid JSON file.{Style.RESET_ALL}")
140+
sys.exit(1)
141+
except Exception as e:
142+
print(f"{Fore.RED}Error: {e}{Style.RESET_ALL}")
143+
sys.exit(1)
144+
145+
get_package_value() # 获取Biscuit和Shizuku的软件包数量
146+
147+
# 从动态数据中获取信息转换为实打实的字符串
148+
item_OS_name = str(f"{qos_name} {qos_version} ({qos_vercode})")
149+
item_qos_path = str(qos_path)
150+
item_qos_login = str(f"{last_login}@{qos_node}")
151+
item_qos_shell = str(f"KomShell - {qos_version}")
152+
item_qos_pks = str(f"Biscuit({biscuit_value}) | Shizuku({shizuku_value})")
153+
item_qos_edition = str(qos_edition)
154+
item_sys_platform = str(f"{platform.system()} {platform.release()}")
155+
item_sys_version = str(platform.version())
156+
item_sys_name = str(platform.node())
157+
item_sys_arch = str(platform.machine())
158+
item_sys_cpu = str(platform.processor())
159+
item_py_ver = str(platform.python_version())
160+
item_py_exec = str(sys.executable)
161+
162+
# 准备输出信息的一些数据变量
163+
qos_logo_lines = qos_big_logo.split('\n') # 将logo分割成行(应该是22行)
164+
qos_logo_line_width = len(qos_logo_lines[0]) # logo每一行的长度(单位:字符)
165+
terminal_width = shutil.get_terminal_size().columns # 获取终端宽度(单位:字符)
166+
167+
# 输出信息的键(以列表形式存储)
168+
infos = [
169+
[" ", "", 1],
170+
[f"{Fore.LIGHTMAGENTA_EX}KomFetch System Fetcher{Fore.RESET}", "", 23],
171+
["-" * 23, "", 23],
172+
[f"{Fore.YELLOW}OS: {Style.RESET_ALL}", item_OS_name, 4],
173+
[f"{Fore.YELLOW}Path: {Style.RESET_ALL}", item_qos_path, 6],
174+
[f"{Fore.YELLOW}Login: {Style.RESET_ALL}", item_qos_login, 7],
175+
[f"{Fore.YELLOW}Shell: {Style.RESET_ALL}", item_qos_shell, 7],
176+
[f"{Fore.YELLOW}Packages: {Style.RESET_ALL}", item_qos_pks, 10],
177+
[f"{Fore.YELLOW}Edition: {Style.RESET_ALL}", item_qos_edition if qos_activate else "", 9],
178+
[f"{Fore.CYAN}Platform: {Style.RESET_ALL}", item_sys_platform, 10],
179+
[f"{Fore.CYAN}Version: {Style.RESET_ALL}", item_sys_version, 9],
180+
[f"{Fore.CYAN}Node: {Style.RESET_ALL}", item_sys_name, 6],
181+
[f"{Fore.CYAN}Arch: {Style.RESET_ALL}", item_sys_arch, 6],
182+
[f"{Fore.CYAN}CPU: {Style.RESET_ALL}", item_sys_cpu, 5],
183+
[f"{Fore.LIGHTRED_EX}Python: {Style.RESET_ALL}", item_py_ver, 8],
184+
[f"{Fore.LIGHTRED_EX}Exec: {Style.RESET_ALL}", item_py_exec, 6],
185+
[" ", "", 1],
186+
[f"{Fore.LIGHTGREEN_EX}Fetched at {tm.strftime('%Y-%m-%d %H:%M:%S', tm.localtime())}{Style.RESET_ALL}", "", 30],
187+
[" ", "", 1],
188+
[f"{Back.BLACK} {Back.RED} {Back.GREEN} {Back.YELLOW} {Back.BLUE} {Back.MAGENTA} {Back.CYAN} {Back.WHITE} {Style.RESET_ALL}", "", 24],
189+
[f"{Back.LIGHTBLACK_EX} {Back.LIGHTRED_EX} {Back.LIGHTGREEN_EX} {Back.LIGHTYELLOW_EX} {Back.LIGHTBLUE_EX} {Back.LIGHTMAGENTA_EX} {Back.LIGHTCYAN_EX} {Back.LIGHTWHITE_EX} {Style.RESET_ALL}", "", 24],
190+
[" ", "", 1]
191+
]
192+
# 其中,0为info的键,1为info的值,2为键的长度
193+
194+
# Logo和info的行数
195+
logo_lines_num = len(qos_logo_lines)
196+
infos_num = len(infos)
197+
198+
# 确定需要打印的行数(分两列,哪一列长取哪一列)
199+
if logo_lines_num > infos_num: # logo行数大于info行数,则需要补充空行
200+
need_lines_num = logo_lines_num
201+
else: # logo行数小于等于info行数,则直接输出
202+
need_lines_num = infos_num
203+
204+
max_line_width = qos_logo_line_width + 30 + 1 # 最大行宽度()
205+
206+
def default_mode():
207+
"""默认模式:终端宽度足够时打印两列,否则logo在上,info在下,组成一列。"""
208+
if terminal_width >= max_line_width: # 终端宽度大于等于最大行宽度,则打印两列
209+
# 循环打印一行logo和一条info
210+
for i in range(need_lines_num):
211+
logo_text = get_logo_text(i, logo_lines_num, qos_logo_lines, qos_logo_line_width)
212+
info_text = get_cut_length(i, infos_num, terminal_width, qos_logo_line_width, infos, True)
213+
line_text = logo_text + info_text # 组合成一行
214+
print(line_text.ljust(terminal_width)) # 左对齐,右边用空格填充,打印出来
215+
else: # 终端宽度小于最大行宽度,则打印一列
216+
# 先循环打印logo,然后循环打印info
217+
for i1 in range(logo_lines_num):
218+
logo_text = get_logo_text(i1, logo_lines_num, qos_logo_lines, qos_logo_line_width)
219+
print(logo_text.ljust(terminal_width)) # 左对齐,右边用空格填充,打印出来
220+
for i2 in range(infos_num):
221+
info_text = get_cut_length(i2, infos_num, terminal_width, qos_logo_line_width, infos, False)
222+
print(info_text.ljust(terminal_width)) # 左对齐,右边用空格填充,打印出来
223+
# 各输出各的
224+
225+
def stdout_mode():
226+
"""标准输出模式:打印一列,只有info自成一列,且没有颜色块。"""
227+
# 循环打印一行info
228+
for i in range(infos_num - 3): # 这个-3是为了去掉最后的颜色块
229+
info_text = get_cut_length(i, infos_num, terminal_width, qos_logo_line_width, infos, False)
230+
print(info_text.ljust(terminal_width)) # 左对齐,右边用空格填充,打印出来
231+
232+
if mode == "-h": # 打印帮助信息
233+
print(f"{Fore.LIGHTGREEN_EX}Usage: komfetch [mode]{Style.RESET_ALL}")
234+
print(f"{Fore.CYAN}Mode:{Style.RESET_ALL}")
235+
print(f"{Fore.CYAN} (No Argument): Default mode.{Style.RESET_ALL}")
236+
print(f"{Fore.CYAN} --default: Default mode, print two columns.{Style.RESET_ALL}")
237+
print(f"{Fore.CYAN} --stdout: Standard output mode, print one column.{Style.RESET_ALL}")
238+
print(f"{Fore.CYAN} -h: Print help information.{Style.RESET_ALL}")
239+
print()
240+
sys.exit(0)
241+
elif mode == "--default": # 默认模式,打印两列
242+
default_mode()
243+
elif mode == "--stdout": # 标准输出模式,打印一列
244+
stdout_mode()
245+
else: # 其他参数,打印错误信息
246+
print(f"{Fore.RED}Error: Invalid argument.{Style.RESET_ALL}")
247+
sys.exit(1)
248+
249+
if __name__ == "__main__":
250+
main()

0 commit comments

Comments
 (0)