python读取excel文件-单元格拖拽互换内容,自动统计

 


代码如下:

import sys
import pandas as pd
from PyQt5.QtWidgets import QApplication, QMainWindow, QTableView, QVBoxLayout, QWidget
from PyQt5.QtCore import Qt, QAbstractTableModel, QModelIndex, QMimeData

class DataFrameModel(QAbstractTableModel):
def __init__(self, df):
super().__init__()
self.df = df
self.sum_row_index = self.df.shape[0] - 1
self.sum_col_index = self.df.shape[1] - 3 # Adjusted because we added two columns
self.df[f'Column_{self.df.shape[1]}'] = None
self.recalculate_all_sum_cells()

def recalculate_all_sum_cells(self):
# 确保 DataFrame 大小足够
if self.sum_row_index >= self.df.shape[0]:
self.df = pd.concat([self.df, pd.DataFrame(index=range(self.df.shape[0], self.sum_row_index + 1))],
ignore_index=True)
if self.sum_col_index + 2 >= self.df.shape[1]:
self.df = pd.concat([self.df, pd.DataFrame(columns=range(self.df.shape[1], self.sum_col_index + 3))],
axis=1)

# 计算行的总和
for row in range(self.sum_row_index):
self.df.iloc[row, self.sum_col_index] = self.df.iloc[row, 1:self.sum_col_index].apply(pd.to_numeric,
errors='coerce').sum()

self.df.iloc[self.sum_row_index, self.sum_col_index] = '合计'
numbers = []
# 计算总和和平均值
for row in range(0, self.sum_row_index, 2):
b_group_sum = pd.to_numeric(self.df.iloc[row + 1, self.sum_col_index],
errors='coerce') if row + 1 < self.sum_row_index else 0
# 计算 total_sum
if row % 2 == 0: # 第一次循环
numbers.append(b_group_sum)
print(numbers)

# 使用循环每次取两个数字相加
for i in range(0, len(numbers) - 1, 2): # 步长为2,以确保每次取两个元素
if i < len(numbers) and i + 1 < len(numbers): # 确保索引不越界
total_sum = numbers[i] + numbers[i + 1]
wucha = numbers[i] - numbers[i + 1]
print(i, "误差", wucha)
print(i, "合计", total_sum,wucha)

# 每隔4行输入一个值
self.df.iloc[(i // 2) * 4 + 0, self.sum_col_index + 1] = total_sum # 使用整数除法来匹配行索引
avg_sum = total_sum / 2
# 确保写入的是合计行
self.df.iloc[(i // 2) * 4 + 0, self.sum_col_index + 2] = avg_sum

self.df.iloc[(i // 2) * 4 + 0, self.sum_col_index + 3] = wucha


def rowCount(self, parent=QModelIndex()):
return self.df.shape[0]

def columnCount(self, parent=QModelIndex()):
return self.df.shape[1]

def data(self, index, role=Qt.DisplayRole):
if role == Qt.DisplayRole:
value = self.df.iloc[index.row(), index.column()]
if index.column() == self.sum_col_index and value == 0.0:
return ''
return '' if pd.isna(value) else str(value)
return None

def headerData(self, section, orientation, role=Qt.DisplayRole):
if role == Qt.DisplayRole:
if orientation == Qt.Horizontal:
if section == self.sum_col_index:
return '合计'
if section == self.sum_col_index + 1:
return '总和'
if section == self.sum_col_index + 2:
return '总和/2'
if section == self.sum_col_index + 3:
return '误差'
return str(self.df.columns[section])
if orientation == Qt.Vertical:
return str(self.df.index[section])
return None

def flags(self, index):
return Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled | Qt.ItemIsDropEnabled

def mimeTypes(self):
return ['application/vnd.text.index']

def mimeData(self, indexes):
mimedata = QMimeData()
encoded_data = ''
for index in indexes:
if index.isValid():
encoded_data += f'{index.row()} {index.column()} '
mimedata.setData('application/vnd.text.index', encoded_data.encode())
return mimedata

def dropMimeData(self, data, action, row, column, parent):
if action == Qt.IgnoreAction:
return True

if data.hasFormat('application/vnd.text.index'):
encoded_data = data.data('application/vnd.text.index').data().decode()
parts = list(map(int, encoded_data.split()))

if len(parts) == 2:
source_row, source_column = parts
if row == -1:
row = parent.row()
if column == -1:
column = parent.column()

if row < 0 or row >= self.df.shape[0] or column < 0 or column >= self.df.shape[1]:
return False

self.df.iloc[source_row, source_column], self.df.iloc[row, column] = self.df.iloc[row, column], \
self.df.iloc[source_row, source_column]

self.recalculate_all_sum_cells()

self.dataChanged.emit(self.index(0, 0), self.index(self.rowCount() - 1, self.columnCount() - 1))
return True
return False

class ExcelEditor(QMainWindow):
def __init__(self, df):
super().__init__()
self.df = df
self.initUI()

def initUI(self):
self.setWindowTitle('南麂岛Jason-EXCEL读取-拖拽自动计算')
self.setGeometry(100, 100, 1100, 900)

self.tableView = QTableView()
self.model = DataFrameModel(self.df)
self.tableView.setModel(self.model)
self.tableView.setDragEnabled(True)
self.tableView.setAcceptDrops(True)
self.tableView.setDropIndicatorShown(True)

layout = QVBoxLayout()
layout.addWidget(self.tableView)

container = QWidget()
container.setLayout(layout)
self.setCentralWidget(container)

if __name__ == '__main__':
app = QApplication(sys.argv)

df = pd.read_excel('分组数据+格式.xlsx', header=0)
df = df.fillna('')

# 添加合计列
df = pd.concat([df, pd.DataFrame(index=df.index, columns=['合计', '总和', '总和/2'])], axis=1)

editor = ExcelEditor(df)
editor.show()

sys.exit(app.exec_())

EXCEL利用按钮进行任意单元格的内容互换

 

主要通过开发者工具来操作

代码如下:

Private Sub CommandButton1_Click()

    Dim cell1 As Range

    Dim cell2 As Range

    Dim temp As Variant

    Set cell1 = Application.InputBox("请选择第一个单元格", "选择单元格", Type:=8)

    Set cell2 = Application.InputBox("请选择第二个单元格", "选择单元格", Type:=8)


    Dim originalCell1Value As Variant

    Dim originalCell2Value As Variant

    originalCell1Value = cell1.Value

    originalCell2Value = cell2.Value


    temp = cell1.Value

    cell1.Value = cell2.Value

    cell2.Value = temp


    If MsgBox("操作是否正确?", vbYesNo + vbQuestion) = vbNo Then

        ' 恢复原始数据

        cell1.Value = originalCell1Value

        cell2.Value = originalCell2Value

    End If

End Sub

py文件导成exe文件报错的解决方案

这个错误可能是由于`pyinstaller`在打包过程中没有正确处理`setuptools`的依赖关系。为了解决这个问题,你可以尝试以下几个步骤:


1. **更新`setuptools`**:

   确保你的`setuptools`是最新版本。你可以使用以下命令更新`setuptools`:

   ```bash

   pip install --upgrade setuptools

   ```


2. **更新`pyinstaller`**:

   同样,确保`pyinstaller`也是最新版本。使用以下命令更新`pyinstaller`:

   ```bash

   pip install --upgrade pyinstaller

   ```


3. **重新打包**:

   在更新了`setuptools`和`pyinstaller`之后,尝试重新打包你的应用程序:

   ```bash

   pyinstaller --onefile --noconsole your_script.py

   ```


4. **检查`pyinstaller`配置**:

   有时,`pyinstaller`的配置文件可能需要手动编辑以正确处理某些库。你可以检查`pyinstaller`的配置文件(通常位于`PyInstaller/hooks`目录下),看看是否有关于`setuptools`或`pkg_resources`的特殊处理。


5. **使用`--hidden-import`参数**:

   如果上述步骤不起作用,你可以尝试在`pyinstaller`命令中使用`--hidden-import`参数来显式指定缺失的模块:

   ```bash

   pyinstaller --onefile --noconsole --hidden-import pkg_resources.extern your_script.py

   ```


6. **检查环境**:

   确保你的打包环境与运行环境一致。有时,虚拟环境或不同的Python版本可能会导致打包问题。


如果问题仍然存在,你可能需要更详细地检查`pyinstaller`的输出日志,或者在社区论坛和问答网站上寻求帮助。提供更多的错误信息和上下文通常有助于解决问题。

python+mysql搭建不同角色登入验证,完成数据导出和导入操作





import tkinter as tk
from tkinter import messagebox, filedialog
import pandas as pd
import pymysql

def login():
username = entry_username.get()
password = entry_password.get()
role = role_var.get()

if username and password and role:
try:
conn = pymysql.connect(
host='127.0.0.1',
user='root',
password='root',
database='chaxu'
)
cursor = conn.cursor()
if role == "学生":
cursor.execute("SELECT * FROM student WHERE student_user=%s AND student_password=%s", (username, password))
elif role == "教师":
cursor.execute("SELECT * FROM teacher WHERE teacher_user=%s AND teacher_password=%s", (username, password))
result = cursor.fetchone()
cursor.close()
if result:
messagebox.showinfo("登录成功", f"欢迎使用 {result[3]} 工具")
open_main_window(conn, role)
else:
messagebox.showerror("错误", "用户名或密码错误")
except pymysql.MySQLError as e:
messagebox.showerror("错误", f"无法连接到数据库: {str(e)}")
else:
messagebox.showerror("错误", "请输入所有必填信息")


def open_main_window(conn, role):
main_window = tk.Toplevel()
main_window.title("数据导入导出工具")
main_window.geometry("500x400")

#做导出操作,注意主键值不能重复
def import_data():
file_path = filedialog.askopenfilename(title="选择Excel文件", filetypes=[("Excel files", "*.xlsx")])
if file_path:
try:
# 指定student_number列应该被读取为整型
data = pd.read_excel(file_path, converters={'student_number': int})
cursor = conn.cursor()
for _, row in data.iterrows():
if role == "学生":
# 确保student_number被转换为整型
student_data = (
int(row['student_number']), row['student_class'], row['student_sex'], row['student_name'],
row['student_user'], row['student_password'])
cursor.execute(
"INSERT INTO student (student_number, student_class, student_sex, student_name, student_user, student_password) VALUES (%s, %s, %s, %s, %s, %s)", student_data
)
elif role == "教师":
# 确保student_number被转换为整型
teacher_data = (
int(row['teacher_number']), row['teacher_class'], row['teacher_sex'], row['teacher_name'],
row['teacher_user'], row['teacher_password'])
cursor.execute(
"INSERT INTO teacher (teacher_number, teacher_class, teacher_sex, teacher_name, teacher_user, teacher_password) VALUES (%s, %s, %s, %s, %s, %s)",teacher_data
)
conn.commit()
cursor.close()
# 将消息显示在Text
text_area.insert(tk.END, "数据已成功导入数据库\n")
text_area.see(tk.END)
text_area.update_idletasks()
except Exception as e:
messagebox.showerror("错误", f"导入数据时发生错误: {str(e)}")
#做导出操作
def export_data():
cursor = conn.cursor()
if role == "学生":
cursor.execute("SELECT * FROM student")
elif role == "教师":
cursor.execute("SELECT * FROM teacher")
rows = cursor.fetchall()
columns = [desc[0] for desc in cursor.description]
cursor.close()
data = pd.DataFrame(rows, columns=columns)
export_path = filedialog.asksaveasfilename(defaultextension=".xlsx", filetypes=[("Excel files", "*.xlsx")])
if export_path:
data.to_excel(export_path, index=False)
text_area.insert(tk.END, "数据已成功导出数据库\n")
text_area.see(tk.END)


# 创建一个Frame来容纳按钮,导入数据,导出数据,+text显示
button_frame = tk.Frame(main_window)
button_frame.pack(pady=10)

# 创建导入按钮
import_button = tk.Button(button_frame, text="导入数据", command=lambda: import_data())
import_button.pack(side=tk.LEFT, padx=5) # 左侧按钮,右侧间距5

# 创建导出按钮
export_button = tk.Button(button_frame, text="导出数据", command=lambda: export_data())
export_button.pack(side=tk.LEFT, padx=5) # 右侧按钮,左侧间距5

# 创建一个Text小部件,用于显示消息
text_area = tk.Text(main_window, width=50, height=3)
text_area.pack(expand=True, fill='both')

# 主窗口
root = tk.Tk()
root.title("登录")
root.geometry("500x300")
welcome_label = tk.Label(root, text="欢迎使用Jason工具", font=("Helvetica", 20))
welcome_label.pack(pady=10)
role_var = tk.StringVar(value="学生")
# 将角色、学生和教师的lable标签
frame = tk.Frame(root)
frame.pack(pady=5)

label_role = tk.Label(frame, text="角色:")
label_role.pack(side=tk.LEFT, padx=5)

radio_student = tk.Radiobutton(frame, text="学生", variable=role_var, value="学生")
radio_student.pack(side=tk.LEFT, padx=5)

radio_teacher = tk.Radiobutton(frame, text="教师", variable=role_var, value="教师")
radio_teacher.pack(side=tk.LEFT, padx=5)

label_username = tk.Label(root, text="用户名:")
label_username.pack(pady=5)
entry_username = tk.Entry(root)
entry_username.pack(pady=5)

label_password = tk.Label(root, text="密码:")
label_password.pack(pady=5)
entry_password = tk.Entry(root, show='*')
entry_password.pack(pady=5)

login_button = tk.Button(root, text="登录", command=login)
login_button.pack(pady=10)
root.mainloop()







python字符串常见的处理函数有哪些?

 len(str): 返回字符串的长度。

str.lower() 和 str.upper(): 将字符串转换为小写或大写。
str.strip(): 去除字符串两端的空格。
str.split(sep=None): 根据指定的分隔符将字符串分割成列表。
str.join(iterable): 将可迭代对象中的字符串元素连接起来,形成一个新的字符串。
str.replace(old, new): 将字符串中的旧字符串替换为新字符串。
str.find(sub): 返回子字符串在字符串中第一次出现的位置,如果没有找到则返回-1。
str.count(sub): 返回子字符串在字符串中出现的次数。
str.startswith(prefix) 和 str.endswith(suffix): 检查字符串是否以指定的前缀或后缀开始或结束。
str.isalpha(), str.isdigit(), str.isalnum(): 检查字符串是否只包含字母、数字或字母和数字。
-----------------------------------------------------------------------------------------------------------------------
# 定义一个字符串
s = " Hello, World! "
# 计算字符串长度
print(len(s)) # 输出: 17
# 转换为小写
print(s.lower()) # 输出: " hello, world! "
# 转换为大写
print(s.upper()) # 输出: " HELLO, WORLD! "
# 去除两端空格
print(s.strip()) # 输出: "Hello, World!"
# 分割字符串
print(s.split()) # 输出: ['Hello,', 'World!']
# 连接字符串
words = ['Hello,', 'World!']
print(", ".join(words)) # 输出: "Hello, World!"
# 替换字符串
print(s.replace("World", "Python")) # 输出: " Hello, Python! "
# 查找子字符串
print(s.find("World")) # 输出: 8
# 统计子字符串出现次数
print(s.count("l")) # 输出: 3
# 检查字符串是否以指定字符串开始或结束
print(s.startswith("Hello")) # 输出: False
print(s.endswith("World!")) # 输出: False
# 检查字符串是否只包含字母、数字或字母和数字
print("hello123".isalpha()) # 输出: False
print("hello123".isdigit()) # 输出: False
print("hello123".isalnum()) # 输出: True

爬虫南京几个学校贴吧的分享代码-简单教程(不能获利)

import requests
from lxml import etree
import time
import re
import json


# 数据采集
class data_spider:
def __init__(self):
self.headers = {
"Host": "tieba.baidu.com",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
"Cookie": 'BAIDUID=7C5F80F92A43BB0552547127F614C0DB:FG=1; BAIDUID_BFESS=7C5F80F92A43BB0552547127F614C0DB:FG=1; BAIDU_WISE_UID=wapp_1716602884591_936; arialoadData=false; BDUSS=Y5cTQyRnJWd2xFOWdYM2E5LU5IRUcxUjhmbU9xcHZLSEltWlItaW5UflIxWGhtRVFBQUFBJCQAAAAAAQAAAAEAAAC-sX0tAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANFIUWbRSFFmSU; BDUSS_BFESS=Y5cTQyRnJWd2xFOWdYM2E5LU5IRUcxUjhmbU9xcHZLSEltWlItaW5UflIxWGhtRVFBQUFBJCQAAAAAAQAAAAEAAAC-sX0tAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANFIUWbRSFFmSU; STOKEN=de20c67d13fb68350918d959c4405cbf1f30263ddad073ecbba301ee2a8dbbfc; NO_UNAME=1; ZFY=RSPxEJ8pBnVBq99zgBD8UqjinNcfrwYa2UzW6VU4pj4:C; wise_device=0; Hm_lvt_98b9d8c2fd6608d564bf2ac2ae642948=1716602884,1716605018; USER_JUMP=-1; BA_HECTOR=218gag050k04048ga0aga08l5cn6f71j52k2t1u; st_key_id=17; Hm_lpvt_98b9d8c2fd6608d564bf2ac2ae642948=1716605070; XFI=b5aa7110-1a40-11ef-b36f-b564a56959c0; ab_sr=1.0.1_ZGQwNjQxMTAyZjRmMTYyOTYxNmM3ZmQ1YjE1NDg0YmEyZjM1NzA5ZDA3YmZjZDMxYzhhMjhiOGQ4NmUzNjJiODFkMjhkN2M1NWU4ZjhlMmVlNDY2ZjIxMGVlYTAyYWQ2Njg0YWEyYzYxYmFhMTllYTEwNTgzNzMyMTg1ZjU3OGExNmYzNjEyNTgwOGUzMjU2NmVmZGM5MWZjNmM2ZWQ4ZWJmNzJkN2Q1ZDYyZTBiZDQ0NjZhN2JmNjVmMGRiZWIx; st_data=194567ccfbef90a0256950e978fbc04a88934d65aa5ead5e6682a994abe9a2a0941db906192496d0561160a400df5fd4d549b5f44a5a9a6b89fe5c1088dd4b33dd344ccd12ae68ee8172c95f985c933b0aed0081021977f52b758c82e5ef9228d1161086553ce87a9b70d76b26262a61947390f097c38c9e73295f97c9f5213099a828bbe4b1a59cb4d11bcb888e0e74; st_sign=1beeff43; XFCS=76481F3588960D951014668C5E0D1B684675B8B7E112D4AEC7171321D0129BB5; XFT=oC5w917R6661E8PescegEfT78DgIyhOe/YOQCVDeqf8=; RT="z=1&dm=baidu.com&si=71088702-f617-4002-9329-6bf26e2fc498&ss=lwlh19sm&sl=16&tt=13ac&bcn=https%3A%2F%2Ffclog.baidu.com%2Flog%2Fweirwood%3Ftype%3Dperf&ld=1axaq&ul=1ecg7"',
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36 Edg/125.0.0.0"
}
self.tieba_items = []
self.saver = open('tieba_info.json', 'w', encoding='utf8')
self.tieba_name = None

# 采集百度贴吧的数据
def spider_tieba(self, tieba_name, base_url):
# 目标地址
self.tieba_name = tieba_name
self.spider_tieba_list(base_url)

# 时间转换
def get_time_convert(self, timeStr):
if (re.match(r'^\d{1,2}:\d{1,2}$', timeStr) != None):
day = time.strftime('%Y-%m-%d', time.localtime(time.time()))
timeStr = day + ' ' + timeStr + ':00'
elif (re.match(r'^\d{4}-\d{1,2}$', timeStr) != None):
day = time.strftime('%d', time.localtime(time.time()))
timeStr = timeStr + '-' + day + ' 00:00:00'
elif (re.match(r'^\d{1,2}-\d{1,2}$', timeStr) != None):
day = time.strftime('%Y', time.localtime(time.time()))
timeStr = day + '-' + timeStr + ' 00:00:00'
return timeStr

# 过滤表情
def filter_emoji(self, desstr, restr=''):
try:
co = re.compile(u'[\U00010000-\U0010ffff]')
except re.error:
co = re.compile(u'[\uD800-\uDBFF][\uDC00-\uDFFF]')
return co.sub(restr, desstr)

# 采集百度贴吧列表数据
def spider_tieba_list(self, url):
print(url)
time.sleep(2)
response = requests.get(url, headers=self.headers)
try:
response_txt = str(response.content, 'utf-8')
except Exception as e:
response_txt = str(response.content, 'gbk')
# response_txt = str(response.content,'utf-8')
bs64_str = re.findall(
r'<code class="pagelet_html" id="pagelet_html_frs-list/pagelet/thread_list" style="display:none;">[.\n\S\s]*?</code>',
response_txt)

bs64_str = ''.join(bs64_str).replace(
'<code class="pagelet_html" id="pagelet_html_frs-list/pagelet/thread_list" style="display:none;"><!--', '')
bs64_str = bs64_str.replace('--></code>', '')
html = etree.HTML(bs64_str)
# 标题列表
title_list = html.xpath('//div[@class="threadlist_title pull_left j_th_tit "]/a[1]/@title')
# 链接列表
link_list = html.xpath('//div[@class="threadlist_title pull_left j_th_tit "]/a[1]/@href')
# 发帖人
creator_list = html.xpath('//div[@class="threadlist_author pull_right"]/span[@class="tb_icon_author "]/@title')
# 发帖时间
create_time_list = html.xpath(
'//div[@class="threadlist_author pull_right"]/span[@class="pull-right is_show_create_time"]/text()')

for i in range(len(title_list)):
item = dict()
item['create_time'] = create_time_list[i]
if item['create_time'] == '广告':
continue
item['create_time'] = self.get_time_convert(item['create_time'])
item['title'] = self.filter_emoji(title_list[i])
item['link'] = 'https://tieba.baidu.com' + link_list[i]
if i < len(creator_list): # 添加检查
item['creator'] = self.filter_emoji(creator_list[i]).replace('主题作者: ', '')
else:
item['creator'] = '没名字' # 或者设置一个默认值
item['content'] = self.filter_emoji(item['title'])
item['school'] = self.tieba_name
self.tieba_items.append(item)
# 保存帖子数据
self.saver.writelines([json.dumps(item, ensure_ascii=False) + '\n' for item in self.tieba_items])
self.saver.flush()
self.tieba_items.clear()

# 如果有下一页继续采集下一页
nex_page = html.xpath('//a[@class="next pagination-item "]/@href')
if len(nex_page) > 0:
next_url = 'https:' + nex_page[0]

# 抓取 10000 条数据
if float(next_url.split('=')[-1]) < 2000:
try:
self.spider_tieba_list(next_url)
time.sleep(1)
except:
pass


if __name__ == "__main__":
data_spider = data_spider()

school_urls = {
'南京邮电大学': 'https://tieba.baidu.com/f?kw=南京邮电大学&ie=utf-8',
'南京财经大学': 'https://tieba.baidu.com/f?kw=南京财经大学&ie=utf-8',
'南京审计大学': 'https://tieba.baidu.com/f?kw=南京审计大学&ie=utf-8',
'南京信息职业技术学院': 'https://tieba.baidu.com/f?kw=南京信息职业技术学院&ie=utf-8',
'南京工业职业技术大学': 'https://tieba.baidu.com/f?kw=南京工业职业技术大学&ie=utf-8',
'南京理工大学紫金学院': 'https://tieba.baidu.com/f?kw=南京理工大学紫金学院&ie=utf-8',
}

for school in school_urls:
print("==> 抓取 {} 的贴吧数据".format(school))
data_spider.spider_tieba(school, school_urls[school])

python一键启动代码,让桌面跟简洁

 

代码如下:

import tkinter as tk
from tkinter import ttk, filedialog, messagebox
from tkinter.simpledialog import askstring
import os
import json

class QuickLaunchApp:
def __init__(self, root):
self.root = root
self.root.title("Jason快速启动")
self.root.geometry("280x600")
self.style = ttk.Style(self.root)
self.style.theme_use('clam') # 使用现代样式
self.style.configure('.', background='#F0FFFF') # 设置背景颜色为天蓝色
self.style.configure('TNotebook', tabmargins=[2, 5, 2, 0]) # 设置标签页的边距
self.style.configure('TNotebook.Tab', font=('Arial', 11, 'bold')) # 设置标签页的字体


# 创建一个Notebook小部件来管理类目
self.notebook = ttk.Notebook(self.root)
self.notebook.pack(expand=True, fill='both')
style = ttk.Style()
style.configure('TNotebook.Tab', font=('Arial ', 11, 'bold'))
style.configure('TNotebook', tabmargins=[2, 5, 2, 0]) # 设置标签页的边距

# 创建文件菜单
self.menu_bar = tk.Menu(self.root)
self.root.config(menu=self.menu_bar)

self.file_menu = tk.Menu(self.menu_bar, tearoff=0)
self.menu_bar.add_cascade(label="文件", menu=self.file_menu)
self.file_menu.add_command(label="添加快捷方式", command=self.add_shortcut)
self.file_menu.add_command(label="添加类目", command=self.add_category) # 添加类目菜单项
self.file_menu.add_separator()
self.file_menu.add_command(label="退出", command=self.root.quit)

# 加载已保存的快捷方式和类目
self.categories, self.shortcuts = self.load_data()
self.create_categories()

def add_shortcut(self):
# 打开文件对话框选择快捷方式文件
shortcut_path = filedialog.askopenfilename(title="选择快捷方式", filetypes=[("快捷方式", "*.lnk")])
if shortcut_path and os.path.exists(shortcut_path):
# 显示类目选择对话框
category = self.select_category()
if category:
if category not in self.shortcuts:
self.shortcuts[category] = []
self.shortcuts[category].append(shortcut_path)
self.save_data()
self.refresh_categories()
else:
messagebox.showerror("错误", "无效的快捷方式文件。")

def add_category(self):
# 添加类目
category_name = askstring("添加类目", "请输入类目名称:")
if category_name and category_name not in self.categories:
self.categories.append(category_name)
self.shortcuts[category_name] = []
self.save_data()
self.refresh_categories()

def select_category(self):
# 创建一个新的Toplevel窗口
dialog = tk.Toplevel(self.root)
dialog.title("选择类目")
dialog.geometry("260x120") # 设置弹窗大小

# 设置字体大小为12
font = ("TkDefaultFont", 11)
dialog.option_add("*Font", font)

# 创建一个StringVar来保存选择的类目
selected_category = tk.StringVar(dialog)
selected_category.set(self.categories[0] if self.categories else "")

# 创建一个OptionMenu来选择类目
option_menu = ttk.Combobox(dialog, textvariable=selected_category, values=self.categories, state="readonly")
option_menu.pack(pady=10, padx=10)

# 创建一个按钮来确认选择
button = ttk.Button(dialog, text="提交", command=dialog.destroy)
button.pack(pady=10)

# 设置弹窗在屏幕中央显示,并覆盖主窗口
dialog.update_idletasks()
width = dialog.winfo_width()
height = dialog.winfo_height()
x = self.root.winfo_x()
y = self.root.winfo_y()
dialog.geometry('{}x{}+{}+{}'.format(width, height, x, y))

# # 设置弹窗在屏幕中央显示
# dialog.update_idletasks()
# width = dialog.winfo_width()
# height = dialog.winfo_height()
# x = (dialog.winfo_screenwidth() // 2) - (width // 2)
# y = (dialog.winfo_screenheight() // 2) - (height // 2)
# dialog.geometry('+{}+{}'.format(x, y))

# 设置样式
# style = ttk.Style(dialog)
# style.theme_use('clam') # 使用现代样式
# style.configure('.', background='#f0f0f0') # 设置背景颜色
# style.configure('TButton', foreground='#000000', background='#ffffff', font=('TkDefaultFont', 13, 'bold')) # 设置按钮样式
# style.configure('TCombobox', background='#ffffff') # 设置Combobox样式

# 等待用户选择类目
dialog.focus_set()
dialog.grab_set() # 禁用主窗口
dialog.wait_window() # 等待弹窗关闭

# 返回用户选择的类目
return selected_category.get()

def create_categories(self):
# 创建类目并添加按钮
self.refresh_categories()

def refresh_categories(self):
# 刷新类目和按钮
for tab in self.notebook.tabs():
self.notebook.forget(tab)
for category in self.categories:
frame = ttk.Frame(self.notebook)
self.notebook.add(frame, text=category)
self.update_category(category, frame)

def update_category(self, category, frame=None):
# 更新指定类目的按钮
if frame is None:
frame = self.notebook.nametowidget(self.notebook.select())
for widget in frame.winfo_children():
widget.destroy()
for shortcut in self.shortcuts.get(category, []):
# label = os.path.basename(shortcut)
label = os.path.splitext(os.path.basename(shortcut))[0]
button = ttk.Button(frame, text=label, command=lambda p=shortcut: os.startfile(p))
button.pack(fill=tk.X, padx=5, pady=2)

def load_data(self):
# 加载保存的类目和快捷方式
try:
with open("shortcuts.json", "r", encoding='utf-8') as file:
data = json.load(file)
return list(data.keys()), data
except FileNotFoundError:
return [], {}

def save_data(self):
# 保存类目和快捷方式到文件
with open("shortcuts.json", "w", encoding='utf-8') as file:
json.dump(self.shortcuts, file, ensure_ascii=False, indent=4)

if __name__ == "__main__":
root = tk.Tk()
root.title("快速启动")

# 获取屏幕的宽度和高度
screen_width = root.winfo_screenwidth()
screen_height = root.winfo_screenheight()

# 计算窗口的位置
window_width = 300 # 窗口宽度
window_height = 600 # 窗口高度
x = screen_width // 2 - window_width // 2 + 750 # 向右偏移100像素
y = screen_height // 2 - window_height // 2 -100

# 设置窗口位置
root.geometry(f"{window_width}x{window_height}+{x}+{y}")
app = QuickLaunchApp(root)
root.mainloop()