当前位置: 首页 > news >正文

yolo自动化项目实例解析(七)自建UI--工具栏选项

在上一章我们基本实现了关于预览窗口的显示,现在我们主要完善一下工具栏菜单按键

一、添加工具栏ui

1、配置文件读取

我们后面要改的东西越来越多了,先加个变量文件方便我们后面调用

下面我们使用的config.get意思是从./datas/setting.ini文件中读取关键字PACKS_TASK对应的路径,如果没有的话默认值为./datas/Task/

vi state.py

# -*- coding: utf-8 -*-
import configparser
import json
import os
import sys# 创建 ConfigParser 对象
config = configparser.ConfigParser()
# 加载 INI 文件
config.read("./datas/setting.ini")#Task任务目录路径
PACKS_TASK = config.get('seting', 'PACKS_TASK', fallback='./datas/Task/')

2、工具栏添加按钮

    #添加任务包名为按钮 def add_tool_item(self, dir_):action_item = QWidgetAction(self)dir_ = dir_.replace("/", "\\")if dir_[-1] == "\\":dir_ = dir_[:-1]#这里把路径下的目录名取出来当作按钮名称bt_item = QPushButton(dir_.split("\\")[-1].strip())bt_item.setMaximumWidth(int(80 * ratio))#读取项目的说明打印出来try:with open(dir_ + "/" + "使用说明.txt", "r", encoding="utf-8") as f:txt = f.read()except:try:with open(dir_ + "/" + "使用说明.txt", "r", encoding="utf-8") as f:txt = f.read()except:txt = "无  \n(可以在任务包文件夹下创建一个 使用说明.txt 文件 来添加说明)"bt_item.setToolTip(dir_ + "\n使用说明:" + txt + "\n")bt_item.setStyleSheet("border: none; padding: 3px;")#绑定按钮的功能bt_item.clicked.connect(functools.partial(self.show_tool_item, dir_))#添加动作action_item.setDefaultWidget(bt_item)#按钮添加到工具栏self.toolBar.addAction(action_item)#按钮触发函数,先放着有个东西def show_tool_item(self, dir_):pass

3、自动触发添加

class MainWindow(QMainWindow, Ui_mainWindow):def __init__(self):...#添加任务包self.load_task_packs()#取任务包的路径,循环触发工具栏添加按钮def load_task_packs(self):self.toolBar.clear()packs = state.PACKS_TASK.split("#@@#")try:if len(packs) >= 1:for pack in packs:if pack != "":self.add_tool_item(pack)else:self.add_tool_item(state.PACKS_TASK)except:pass

4、工具栏移动、任务删除

这里在add_tool_item中添加了关于上下移动和删除的按钮,以及对应的触发函数

    def add_tool_item(self, dir_):action_item = QWidgetAction(self)dir_ = dir_.replace("/", "\\")if dir_[-1] == "\\":dir_ = dir_[:-1]#这里把路径下的目录名取出来当作按钮名称bt_item = QPushButton(dir_.split("\\")[-1].strip())bt_item.setMaximumWidth(int(80 * ratio))#读取项目的说明打印出来try:with open(dir_ + "/" + "使用说明.txt", "r", encoding="utf-8") as f:txt = f.read()except:try:with open(dir_ + "/" + "使用说明.txt", "r", encoding="utf-8") as f:txt = f.read()except:txt = "无  \n(可以在任务包文件夹下创建一个 使用说明.txt 文件 来添加说明)"bt_item.setToolTip(dir_ + "\n使用说明:" + txt + "\n")bt_item.setStyleSheet("border: none; padding: 3px;")#绑定按钮的功能bt_item.clicked.connect(functools.partial(self.show_tool_item, dir_))#添加动作action_item.setDefaultWidget(bt_item)#按钮添加到工具栏self.toolBar.addAction(action_item)menu = QMenu(self)action_menu_down = QAction('顺序 ↓', self)action_menu_down.triggered.connect(functools.partial(self.down_tool_item, len(self.toolBar.actions())))menu.addAction(action_menu_down)action_menu_up = QAction('顺序 ↑', self)action_menu_up.triggered.connect(functools.partial(self.up_tool_item, len(self.toolBar.actions())))menu.addAction(action_menu_up)action_menu_del = QAction('删除任务包', self)action_menu_del.triggered.connect(functools.partial(self.del_tool_item, action_item, bt_item, dir_))menu.addAction(action_menu_del)# 将菜单关联到工具栏上bt_item.setContextMenuPolicy(Qt.CustomContextMenu)bt_item.customContextMenuRequested.connect(lambda pos: menu.exec_(bt_item.mapToGlobal(pos)))self.change_tool_show_style(dir_)def del_tool_item(self, action_item, bt_item, dir_):self.toolBar.removeAction(action_item)if state.PATH_TASK.replace("/", "\\") == dir_:self.datas = []self.row = 0self.column = -1# 清空当前布局for i in reversed(range(self.g_box.count())):self.g_box.itemAt(i).widget().setParent(None)del bt_item, action_item  # 删除动作对象和bt对象txt_ = ""packs = state.PACKS_TASK.split("#@@#")if len(packs) >= 1:for pack in packs:if os.path.realpath(dir_) != os.path.realpath(pack) and pack != "":txt_ = txt_ + pack + "#@@#"state.PACKS_TASK = txt_print(f"成功移除{dir_}任务包")self.sg.mysig_tishi.emit(f"成功移除{dir_}任务包")def down_tool_item(self, idex):task_list = state.PACKS_TASK.split("#@@#")if idex < len(task_list) - 1:# 将指定索引位置的成员与其前一个成员交换位置task_list[idex], task_list[idex + 1] = task_list[idex + 1], task_list[idex]state.PACKS_TASK = ""for item in task_list:if item != "":state.PACKS_TASK += "#@@#" + itemself.load_task_packs()def up_tool_item(self, idex):task_list = state.PACKS_TASK.split("#@@#")if idex > 0:# 将指定索引位置的成员与其后一个成员交换位置task_list[idex], task_list[idex - 1] = task_list[idex - 1], task_list[idex]state.PACKS_TASK = ""for item in task_list:if item != "":state.PACKS_TASK += "#@@#" + itemself.load_task_packs()

不知道按钮为什么时灵时不灵,不重要先忽略

二、工具栏触发函数

1、更新变量

# -*- coding: utf-8 -*-
import configparser
import json
import os
import sys# 创建 ConfigParser 对象
config = configparser.ConfigParser()
# 加载 INI 文件
config.read("./datas/setting.ini")#Task任务目录路径
PACKS_TASK = config.get('seting', 'PACKS_TASK', fallback='./datas/Task/')PATH_TASK = config.get('seting', 'PATH_TASK', fallback='./datas/Task/')PROVIDERS = config.get('seting', 'PROVIDERS', fallback="""["CUDAExecutionProvider", "CPUExecutionProvider"]""")
PROVIDERS = json.loads(PROVIDERS.replace("'", '"'))
LIANZHAOFUWU = config.get('seting', 'LIANZHAOFUWU', fallback='./datas/jiaoben/躺宝连招插件.exe')
DUANGKOUHAO = config.get('seting', 'DUANGKOUHAO', fallback='29943')
GAME_TITLE = config.get('seting', 'GAME_TITLE', fallback='原神')
LIANZHAO = config.get('seting', 'LIANZHAO', fallback='阵容1草神2久岐忍3钟离4雷神.txt')
PATH_TASK = config.get('seting', 'PATH_TASK', fallback='./datas/Task/')
PATH_JIAOBEN = config.get('seting', 'PATH_JIAOBEN', fallback='./datas/JiaoBen/')
PATH_JUESE = config.get('seting', 'PATH_JUESE', fallback='./datas/img/juese/')
PATH_ADDRESS = config.get('seting', 'PATH_ADDRESS', fallback='./datas/img/address/')
WEIGHTS = config.get('seting', 'WEIGHTS', fallback='./datas/yolov5s_320.onnx')
IMGSIZE_WIDTH = int(config.get('seting', 'IMGSIZE_WIDTH', fallback='320'))
IMGSIZE_HEIGHT = int(config.get('seting', 'IMGSIZE_HEIGHT', fallback='320'))
WINDOW_WIDTH = int(config.get('seting', 'WINDOW_WIDTH', fallback="640"))
WINDOW_HEIGHT = int(config.get('seting', 'WINDOW_HEIGHT', fallback="900"))
WINDOW_LEFT = int(config.get('seting', 'WINDOW_LEFT', fallback="0"))
WINDOW_TOP = int(config.get('seting', 'WINDOW_TOP', fallback="300"))CMD_WIDTH = int(config.get('seting', 'CMD_WIDTH', fallback="800"))
CMD_HEIGHT = int(config.get('seting', 'CMD_HEIGHT', fallback="400"))
CMD_LEFT = int(config.get('seting', 'CMD_LEFT', fallback="500"))
CMD_TOP = int(config.get('seting', 'CMD_TOP', fallback="300"))ON_SHUTDOWN = int(config.get('seting', 'ON_SHUTDOWN', fallback="0"))
ON_JIXING = int(config.get('seting', 'ON_JIXING', fallback="0"))
ON_NEXTPACK = int(config.get('seting', 'ON_NEXTPACK', fallback="0"))
ON_LIANZHAOBUJIANCE = int(config.get('seting', 'ON_LIANZHAOBUJIANCE', fallback="0"))
ON_STARTWITH = int(config.get('seting', 'ON_STARTWITH', fallback="0"))
TIMEOUT_DAGUAI = int(config.get('seting', 'TIMEOUT_DAGUAI', fallback='120'))

2、保存窗口配置

    def save_ini_seting(self):try:hwnd = ctypes.windll.kernel32.GetConsoleWindow()if hwnd:rect = win32gui.GetWindowRect(hwnd)x, y, w, h = rect[0], rect[1], rect[2] - rect[0], rect[3] - rect[1]else:x, y, w, h = state.CMD_LEFT, state.CMD_TOP, state.CMD_WIDTH, state.CMD_HEIGHT# 创建 ConfigParser 对象config = configparser.ConfigParser()# 添加节和键-值对config['seting'] = {"GAME_TITLE": state.GAME_TITLE,'LIANZHAO': state.LIANZHAO,'LIANZHAOFUWU': state.LIANZHAOFUWU,'DUANGKOUHAO': state.DUANGKOUHAO,'PATH_TASK': state.PATH_TASK,'PATH_JIAOBEN': state.PATH_JIAOBEN,'PACKS_TASK': state.PACKS_TASK,'WEIGHTS': state.WEIGHTS,'PROVIDERS': state.PROVIDERS,'IMGSIZE_WIDTH': str(state.IMGSIZE_WIDTH),'IMGSIZE_HEIGHT': str(state.IMGSIZE_HEIGHT),'WINDOW_WIDTH': str(self.width()),'WINDOW_HEIGHT': str(self.height()),'WINDOW_LEFT': str(self.x()),'WINDOW_TOP': str(self.y()),'CMD_WIDTH': str(w),'CMD_HEIGHT': str(h),'CMD_LEFT': str(x),'CMD_TOP': str(y),'ON_SHUTDOWN': str(state.ON_SHUTDOWN),'ON_JIXING': str(state.ON_JIXING),'ON_NEXTPACK': str(state.ON_NEXTPACK),'ON_STARTWITH': str(state.ON_STARTWITH),'ON_LIANZHAOBUJIANCE': str(state.ON_LIANZHAOBUJIANCE),'TIMEOUT_DAGUAI': str(state.TIMEOUT_DAGUAI)}# 写入配置到 INI 文件with open("./datas/setting.ini", 'w') as configfile:config.write(configfile)except:pass

3、获取工作栏动作

    def change_tool_show_style(self, dir_):# 获取工具栏上的所有动作actions_list = self.toolBar.actions()# 遍历处理每个动作for action in actions_list:# 在这里对每个动作进行处理bt_item = action.defaultWidget()if dir_ == bt_item.toolTip().replace("/", "\\").split("\n")[0]:bt_item.setStyleSheet("border-width: 1px; padding: 3px;")else:bt_item.setStyleSheet("border: none; padding: 3px;")

4、更新任务-路由

后续这里打算通过判断脚本的id来选择不同的任务,比如说我们下面注释的内容中,我们会通过工具栏中目录下的jiaoben.ini 配置文件中的type 去判断这个任务具体是要做什么

   def update_tasks(self):try:self.datas = []self.row = 0self.column = -1# 清空当前布局for i in reversed(range(self.g_box.count())):self.g_box.itemAt(i).widget().setParent(None)# 遍历文件夹下有哪些目录# 判断这个路径是否存在if not os.path.exists(state.PATH_TASK):state.PATH_TASK = "./datas/Task/"# # 创建 ConfigParser 对象# config_main = configparser.ConfigParser()# # 加载 INI 文件## config_main.read(os.path.join(state.PATH_TASK, "细节参数.ini"))# self.run_times = int(config_main.get('seting', 'run_times', fallback='1'))# self.action_run_times.setText(f"设置:当前任务包 执行次数:{self.run_times}")## # 获取文件夹列表# folders = [item for item in os.listdir(state.PATH_TASK) if#            os.path.isdir(os.path.join(state.PATH_TASK, item))]# # 将文件夹名称中的数字提取出来,并按照数字大小排序# sorted_folders = sorted(folders, key=lambda x: extract_number(x))# for item in sorted_folders:#     item_path = os.path.join(state.PATH_TASK, item)#     if os.path.isdir(item_path):#         # 创建 ConfigParser 对象#         config = configparser.ConfigParser()#         # 加载 INI 文件#         config.read(os.path.join(item_path, "jiaoben.ini"))#         if config.get('seting', 'type', fallback='1') == "2":#             # 副本任务#             self.add_taskfuben(item_path, config)#         elif config.get('seting', 'type', fallback='1') == "3":#             # 脚本任务#             self.add_taskjiaoben(item_path, config)#         elif config.get('seting', 'type', fallback='1') == "4":#             # 脚本任务#             self.add_xuanjue(item_path, config)#         elif config.get('seting', 'type', fallback='1') == "5":#             # 脚本任务#             self.add_xuanlianzhao(item_path, config)#         elif config.get('seting', 'type', fallback='1') == "6":#             self.add_taskpy(item_path, config)#         else:#             # 锄地任务#             self.add_task(item_path, config)except Exception:print(f"请选择任务文件夹,目前没有这个文件夹{state.PATH_TASK}")

5、保存

    def save(self):for idex in range(len(self.datas)):self.returnPressed_name(idex)# 重写closeEvent方法,在窗口关闭时调用quit()退出应用程序for i, data in enumerate(self.datas):dir_ = os.path.join(state.PATH_TASK, data["name"])self.save_ini(dir_, data)self.update_tasks()self.save_ini_seting()

6、补全点击后触发函数

    def show_tool_item(self, dir_):self.save()state.PATH_TASK = dir_self.save_ini_seting()self.update_tasks()self.change_tool_show_style(dir_)print(f"成功设置{state.PATH_TASK}为当前任务文件夹")

7、全量代码

import configparser
import ctypes
import functools
import os
import re
import sysimport win32gui
# 导入PyQt5模块
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *import state
# 导入UI文件生成的类
from ui.show import Ui_DockWidget
from ui.main import Ui_mainWindow
def extract_number(folder_name):numbers = re.findall(r'\d+', folder_name)return int(numbers[0]) if numbers else float('inf')class MySignal(QObject):# 无参数信号,可能用于触发显示某个路径或轨迹的操作mysig_show_xunlu = pyqtSignal()# 无参数信号,可能与YOLOv模型有关,用于触发显示模型输出或其他相关操作mysig_show_yolov = pyqtSignal()# 定义FormShow类,继承自QDockWidget和Ui_DockWidget
class FormShow(QDockWidget, Ui_DockWidget):def __init__(self, parent=None):super(QDockWidget, self).__init__(parent)  # 调用父类构造函数self.parent = parent  # 保存父窗口引用self.setParent(parent)  # 设置父窗口self.setupUi(self)  # 初始化UI界面self.set_ui()  # 自定义设置UI界面# 设置窗口标题self.setWindowTitle("检测预览")# 设置标签控件属性self.lb_yolov.setScaledContents(True)self.lb_yolov.setAlignment(Qt.AlignCenter)self.lb_xunlu.setAlignment(Qt.AlignCenter)# 移动窗口位置self.move(0, 0)# 设置窗口保持在最顶层self.setWindowFlags(Qt.WindowStaysOnTopHint)# 计算窗口大小self.window_height = int(270 * ratio)self.window_width = int(480 * ratio)# 设置窗口最小尺寸self.setMinimumSize(self.window_width, self.window_height * 2)# 连接按钮点击事件self.bt_jia.clicked.connect(self.clicked_jia)self.bt_jian.clicked.connect(self.clicked_jian)# 自定义UI设置def set_ui(self):pass  # 此处可添加更多UI设置# 按钮“加”点击事件处理def clicked_jia(self):# 如果窗口高度加上增量后超过屏幕高度,则返回if self.window_height + 108 * ratio > 1080 * ratio:return# 更新窗口大小self.window_height += int(108 * ratio)self.window_width += int(190 * ratio)# 设置标签固定高度self.lb_xunlu.setFixedHeight(self.window_height)# 根据窗口宽度调整标签位置if self.width() >= self.lb_yolov.width() + self.lb_xunlu.width():self.lb_yolov.move(self.lb_xunlu.width(), 0)else:self.lb_yolov.move(0, self.lb_xunlu.height())# 设置标签固定大小self.lb_yolov.setFixedHeight(self.window_height)self.lb_yolov.setFixedWidth(self.window_width)# 按钮“减”点击事件处理def clicked_jian(self):# 如果窗口高度减去增量后小于最小高度,则返回if self.window_height - 108 * ratio < 270 * ratio:return# 更新窗口大小self.window_height -= int(108 * ratio)self.window_width -= int(190 * ratio)# 设置标签固定高度self.lb_xunlu.setFixedHeight(self.window_height)# 根据窗口宽度调整标签位置if self.width() >= self.lb_yolov.width() + self.lb_xunlu.width():self.lb_yolov.move(self.lb_xunlu.width(), 0)else:self.lb_yolov.move(0, self.lb_xunlu.height())# 设置标签固定大小self.lb_yolov.setFixedHeight(self.window_height)self.lb_yolov.setFixedWidth(self.window_width)# 重写关闭事件def closeEvent(self, event):# 关闭窗口时取消主窗口上的动作选中状态self.parent.action_isShow.setChecked(False)# 重写调整大小事件def resizeEvent(self, event):# 根据窗口宽度调整标签位置if self.width() >= self.lb_yolov.width() + self.lb_xunlu.width():self.lb_yolov.move(self.lb_xunlu.width(), 0)else:self.lb_yolov.move(0, self.lb_xunlu.height())# 定义MainWindow类,继承自QMainWindow和Ui_mainWindow
class MainWindow(QMainWindow, Ui_mainWindow):def __init__(self):super(MainWindow, self).__init__()  # 调用父类构造函数self.setupUi(self)  # 设置UI布局self.retranslateUi(self)  # 重新翻译UI组件的文字# 修改窗口样式为黑灰色,高亮self.set_ui()# 设置窗口标题self.setWindowTitle(f"修改主页标题")# 设置窗口尺寸self.resize(640, 900)# 设置窗口起始位置self.move(0, 300)# 创建网格布局self.g_box = QGridLayout()# 创建一个中间部件self.container_widget = QWidget()# 将网格布局设置给中间部件self.container_widget.setLayout(self.g_box)# 设置布局左上对齐self.g_box.setAlignment(Qt.AlignTop | Qt.AlignLeft)# 设置布局边距self.g_box.setContentsMargins(0, 0, 0, 0)# 设置布局左对齐self.g_box.setAlignment(Qt.AlignTop)# 将中间部件设置为QScrollArea的子部件self.sa_main.setWidget(self.container_widget)# 获取纵向滚动条控件vertical_scrollbar = self.findChild(QScrollArea)self.v_scrollbar = vertical_scrollbar.verticalScrollBar()# 设置窗口保持在最顶层self.setWindowFlags(Qt.WindowStaysOnTopHint)# 初始化变量self.row = 0self.column = -1self.run_times = 1self.column_step = 310# 获取主窗口的状态栏对象self.statusbar = self.statusBar()# 设置状态栏文本self.statusbar.showMessage('欢迎使用')# 初始化数据列表self.datas = []# 创建一个计时器,用于延迟布局self.timer = QTimer(self)self.timer.setSingleShot(True)  # 设置为单次触发self.timer.timeout.connect(self.update_layout)# 绑定信号槽self.connect_set()# 创建定时器  用于周期显示图片self.timer = QTimer(self)self.timer.timeout.connect(self.on_timer_timeout)self.timer.start(5000)  # 每5秒触发一次#添加任务包self.load_task_packs()def load_task_packs(self):self.toolBar.clear()packs = state.PACKS_TASK.split("#@@#")try:if len(packs) >= 1:for pack in packs:if pack != "":self.add_tool_item(pack)else:self.add_tool_item(state.PACKS_TASK)except:passdef add_tool_item(self, dir_):action_item = QWidgetAction(self)dir_ = dir_.replace("/", "\\")if dir_[-1] == "\\":dir_ = dir_[:-1]#这里把路径下的目录名取出来当作按钮名称bt_item = QPushButton(dir_.split("\\")[-1].strip())bt_item.setMaximumWidth(int(80 * ratio))#读取项目的说明打印出来try:with open(dir_ + "/" + "使用说明.txt", "r", encoding="utf-8") as f:txt = f.read()except:try:with open(dir_ + "/" + "使用说明.txt", "r", encoding="utf-8") as f:txt = f.read()except:txt = "无  \n(可以在任务包文件夹下创建一个 使用说明.txt 文件 来添加说明)"bt_item.setToolTip(dir_ + "\n使用说明:" + txt + "\n")bt_item.setStyleSheet("border: none; padding: 3px;")#绑定按钮的功能bt_item.clicked.connect(functools.partial(self.show_tool_item, dir_))#添加动作action_item.setDefaultWidget(bt_item)#按钮添加到工具栏self.toolBar.addAction(action_item)menu = QMenu(self)action_menu_down = QAction('顺序 ↓', self)action_menu_down.triggered.connect(functools.partial(self.down_tool_item, len(self.toolBar.actions())))menu.addAction(action_menu_down)action_menu_up = QAction('顺序 ↑', self)action_menu_up.triggered.connect(functools.partial(self.up_tool_item, len(self.toolBar.actions())))menu.addAction(action_menu_up)action_menu_del = QAction('删除任务包', self)action_menu_del.triggered.connect(functools.partial(self.del_tool_item, action_item, bt_item, dir_))menu.addAction(action_menu_del)# 将菜单关联到工具栏上bt_item.setContextMenuPolicy(Qt.CustomContextMenu)bt_item.customContextMenuRequested.connect(lambda pos: menu.exec_(bt_item.mapToGlobal(pos)))self.change_tool_show_style(dir_)def del_tool_item(self, action_item, bt_item, dir_):self.toolBar.removeAction(action_item)if state.PATH_TASK.replace("/", "\\") == dir_:self.datas = []self.row = 0self.column = -1# 清空当前布局for i in reversed(range(self.g_box.count())):self.g_box.itemAt(i).widget().setParent(None)del bt_item, action_item  # 删除动作对象和bt对象txt_ = ""packs = state.PACKS_TASK.split("#@@#")if len(packs) >= 1:for pack in packs:if os.path.realpath(dir_) != os.path.realpath(pack) and pack != "":txt_ = txt_ + pack + "#@@#"state.PACKS_TASK = txt_print(f"成功移除{dir_}任务包")self.sg.mysig_tishi.emit(f"成功移除{dir_}任务包")def down_tool_item(self, idex):task_list = state.PACKS_TASK.split("#@@#")if idex < len(task_list) - 1:# 将指定索引位置的成员与其前一个成员交换位置task_list[idex], task_list[idex + 1] = task_list[idex + 1], task_list[idex]state.PACKS_TASK = ""for item in task_list:if item != "":state.PACKS_TASK += "#@@#" + itemself.load_task_packs()def up_tool_item(self, idex):task_list = state.PACKS_TASK.split("#@@#")if idex > 0:# 将指定索引位置的成员与其后一个成员交换位置task_list[idex], task_list[idex - 1] = task_list[idex - 1], task_list[idex]state.PACKS_TASK = ""for item in task_list:if item != "":state.PACKS_TASK += "#@@#" + itemself.load_task_packs()def save_ini_seting(self):try:hwnd = ctypes.windll.kernel32.GetConsoleWindow()if hwnd:rect = win32gui.GetWindowRect(hwnd)x, y, w, h = rect[0], rect[1], rect[2] - rect[0], rect[3] - rect[1]else:x, y, w, h = state.CMD_LEFT, state.CMD_TOP, state.CMD_WIDTH, state.CMD_HEIGHT# 创建 ConfigParser 对象config = configparser.ConfigParser()# 添加节和键-值对config['seting'] = {"GAME_TITLE": state.GAME_TITLE,'LIANZHAO': state.LIANZHAO,'LIANZHAOFUWU': state.LIANZHAOFUWU,'DUANGKOUHAO': state.DUANGKOUHAO,'PATH_TASK': state.PATH_TASK,'PATH_JIAOBEN': state.PATH_JIAOBEN,'PACKS_TASK': state.PACKS_TASK,'WEIGHTS': state.WEIGHTS,'PROVIDERS': state.PROVIDERS,'IMGSIZE_WIDTH': str(state.IMGSIZE_WIDTH),'IMGSIZE_HEIGHT': str(state.IMGSIZE_HEIGHT),'WINDOW_WIDTH': str(self.width()),'WINDOW_HEIGHT': str(self.height()),'WINDOW_LEFT': str(self.x()),'WINDOW_TOP': str(self.y()),'CMD_WIDTH': str(w),'CMD_HEIGHT': str(h),'CMD_LEFT': str(x),'CMD_TOP': str(y),'ON_SHUTDOWN': str(state.ON_SHUTDOWN),'ON_JIXING': str(state.ON_JIXING),'ON_NEXTPACK': str(state.ON_NEXTPACK),'ON_STARTWITH': str(state.ON_STARTWITH),'ON_LIANZHAOBUJIANCE': str(state.ON_LIANZHAOBUJIANCE),'TIMEOUT_DAGUAI': str(state.TIMEOUT_DAGUAI)}# 写入配置到 INI 文件with open("./datas/setting.ini", 'w') as configfile:config.write(configfile)except:passdef change_tool_show_style(self, dir_):# 获取工具栏上的所有动作actions_list = self.toolBar.actions()# 遍历处理每个动作for action in actions_list:# 在这里对每个动作进行处理bt_item = action.defaultWidget()if dir_ == bt_item.toolTip().replace("/", "\\").split("\n")[0]:bt_item.setStyleSheet("border-width: 1px; padding: 3px;")else:bt_item.setStyleSheet("border: none; padding: 3px;")def show_tool_item(self, dir_):self.save()state.PATH_TASK = dir_self.save_ini_seting()self.update_tasks()self.change_tool_show_style(dir_)print(f"成功设置{state.PATH_TASK}为当前任务文件夹")def save(self):for idex in range(len(self.datas)):self.returnPressed_name(idex)# 重写closeEvent方法,在窗口关闭时调用quit()退出应用程序for i, data in enumerate(self.datas):dir_ = os.path.join(state.PATH_TASK, data["name"])self.save_ini(dir_, data)self.update_tasks()self.save_ini_seting()def update_tasks(self):try:self.datas = []self.row = 0self.column = -1# 清空当前布局for i in reversed(range(self.g_box.count())):self.g_box.itemAt(i).widget().setParent(None)# 遍历文件夹下有哪些目录# 判断这个路径是否存在if not os.path.exists(state.PATH_TASK):state.PATH_TASK = "./datas/Task/"# # 创建 ConfigParser 对象# config_main = configparser.ConfigParser()# # 加载 INI 文件## config_main.read(os.path.join(state.PATH_TASK, "细节参数.ini"))# self.run_times = int(config_main.get('seting', 'run_times', fallback='1'))# self.action_run_times.setText(f"设置:当前任务包 执行次数:{self.run_times}")## # 获取文件夹列表# folders = [item for item in os.listdir(state.PATH_TASK) if#            os.path.isdir(os.path.join(state.PATH_TASK, item))]# # 将文件夹名称中的数字提取出来,并按照数字大小排序# sorted_folders = sorted(folders, key=lambda x: extract_number(x))# for item in sorted_folders:#     item_path = os.path.join(state.PATH_TASK, item)#     if os.path.isdir(item_path):#         # 创建 ConfigParser 对象#         config = configparser.ConfigParser()#         # 加载 INI 文件#         config.read(os.path.join(item_path, "jiaoben.ini"))#         if config.get('seting', 'type', fallback='1') == "2":#             # 副本任务#             self.add_taskfuben(item_path, config)#         elif config.get('seting', 'type', fallback='1') == "3":#             # 脚本任务#             self.add_taskjiaoben(item_path, config)#         elif config.get('seting', 'type', fallback='1') == "4":#             # 脚本任务#             self.add_xuanjue(item_path, config)#         elif config.get('seting', 'type', fallback='1') == "5":#             # 脚本任务#             self.add_xuanlianzhao(item_path, config)#         elif config.get('seting', 'type', fallback='1') == "6":#             self.add_taskpy(item_path, config)#         else:#             # 锄地任务#             self.add_task(item_path, config)except Exception:print(f"请选择任务文件夹,目前没有这个文件夹{state.PATH_TASK}")def on_timer_timeout(self):# 每隔一段时间自动显示图片self.sg.mysig_show_xunlu.emit()self.sg.mysig_show_yolov.emit()# 绑定信号槽def connect_set(self):# 创建一个顶级菜单self.menu = self.menuBar()self.menu_view = self.menu.addMenu("视图")# 创建一个动作self.action_isShow = QAction("显示检测结果窗口", self)self.action_isShow.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_F11))self.action_isShow.triggered.connect(self.hotkey_isShow)self.action_isShow.setCheckable(True)self.action_isShow.setChecked(True)self.menu_view.addAction(self.action_isShow)# 打开预测窗口self.hotkey_isShow()self.sg = MySignal()self.sg.mysig_show_xunlu.connect(self.show_xunlu)self.sg.mysig_show_yolov.connect(self.show_yolov)def show_xunlu(self, image_path="./datas/111.png"):# 加载本地图片pixmap = QPixmap(image_path)# 设置图片到 lb_xunlu 控件self.fromshow.lb_xunlu.setPixmap(pixmap)# 设置宽度固定,高度自适应self.fromshow.window_width = int(self.fromshow.window_height * pixmap.width() / pixmap.height())self.fromshow.lb_xunlu.setFixedHeight(self.fromshow.window_height)self.fromshow.lb_xunlu.setFixedWidth(self.fromshow.window_width)self.fromshow.lb_xunlu.setScaledContents(True)def show_yolov(self, image_path="./datas/111.png"):# 加载本地图片pixmap1 = QPixmap(image_path)# 设置图片到 lb_yolov 控件self.fromshow.lb_yolov.setPixmap(pixmap1)# 根据窗口宽度调整 lb_yolov 的位置if self.fromshow.width() >= self.fromshow.lb_yolov.width() + self.fromshow.lb_xunlu.width():self.fromshow.lb_yolov.move(self.fromshow.lb_xunlu.width(), 0)else:self.fromshow.lb_yolov.move(0, self.fromshow.lb_xunlu.height())# 打开预测窗口def hotkey_isShow(self):# 尝试创建悬浮窗口实例if not hasattr(self, "fromshow"):self.fromshow = FormShow(self)  # 创建悬浮窗口实例self.addDockWidget(Qt.TopDockWidgetArea, self.fromshow)  # 添加悬浮窗口到主窗口# 更新布局def update_layout(self):try:# 获取主窗口的宽度width = self.centralWidget().width()# 计算每行可以容纳的组件数量num_per_row = (width) // (self.column_step * ratio)if num_per_row < 1:num_per_row = 1# 清空当前布局for i in reversed(range(self.g_box.count())):self.g_box.itemAt(i).widget().setParent(None)# 重新添加组件到网格布局for i, data in enumerate(self.datas):self.row = int(i // num_per_row)self.column = int(i % num_per_row)self.g_box.addWidget(data["f_item"], self.row, self.column)except:pass# 设置主窗口样式def set_ui(self):# 设置主题样式为 Flatwhitefrom qt_material import apply_stylesheetapply_stylesheet(app, theme='dark_pink.xml')# 设置窗口样式表self.setStyleSheet("""QScrollBar::handle:horizontal {background-color: #A50F2C;  /* 设置滑块颜色 */}QScrollBar::handle:horizontal:hover {background-color: #FF1744;  /* 设置滑块颜色 */}QPushButton:hover {background-color: #DFC472;  /* 设置颜色 */}QPlainTextEdit {padding: 0px;margin: 0px;}QPushButton {padding: 0px;margin: 1px;}""" + "font-family: {}; font-size: {}pt;".format(font.family(), font.pointSize()))# 主程序入口
if __name__ == '__main__':# 获取屏幕宽度和高度screen_width = ctypes.windll.user32.GetSystemMetrics(0)screen_height = ctypes.windll.user32.GetSystemMetrics(1)# 初始化QT应用app = QApplication(sys.argv)# 计算分辨率比例ratio = screen_width / 2560# 设置全局字体大小base_font_size = 13new_font_size = int(base_font_size * ratio)font = QFont("Arial", new_font_size)# 创建主窗口实例window_main = MainWindow()# 显示主窗口window_main.show()# 监听消息不关闭sys.exit(app.exec_())

相关文章:

yolo自动化项目实例解析(七)自建UI--工具栏选项

在上一章我们基本实现了关于预览窗口的显示&#xff0c;现在我们主要完善一下工具栏菜单按键 一、添加工具栏ui 1、配置文件读取 我们后面要改的东西越来越多了&#xff0c;先加个变量文件方便我们后面调用 下面我们使用的config.get意思是从./datas/setting.ini文件中读取关键…...

贝锐洋葱头浏览器随时随地访问教务系统,轻松搞定选课

教育网的“拥堵”早已是老生常谈&#xff0c;学生数量庞大、上网时间集中、带宽有限&#xff0c;导致网络速度慢。尤其是从外部网络访问教育网时&#xff0c;更是因为跨运营商的缘故变得缓慢。 而学校内网也是类似的情况&#xff0c;课余时间和上课时间的网络使用情况差别巨大…...

django drf to_internal_value

使用场景 用于将接收到的输入转换为内部可用的数据形式&#xff1b; 例子 to_internal_value主要在反序列化时用到&#xff0c;其作用处理API请求携带的数据&#xff0c;对其进行验证并转化为Python的数据类型。 假如我们的API客户端通过请求提交了额外的数据&#xff0c;比…...

map(lambda x: x[0], sorted(count.items(), key=lambda x: (-x[1], x[0]))[:n])

被解析的代码行 map(lambda x: x[0], sorted(count.items(), keylambda x: (-x[1], x[0]))[:n])假设的输入 假设我们有以下的 count 字典&#xff0c;其中包括一些字符串及其对应的计数&#xff1a; count {apple: 3,banana: 1,orange: 2,grape: 2 }1. count.items() 首先…...

灰度重心法求取图像重心

1 概述 灰度重心法(Gray-scale Center of Mass Method)是一种在图像处理和计算机视觉中常用的方法。这种方法主要用于确定图像中物体的质心或重心位置,特别是在灰度图像中。 灰度重心法的基本思想是,根据图像中每个像素的灰度值及其位置信息来计算一个加权重心,这个重心…...

Go Mail设置指南:如何提升发送邮件效率?

Go Mail使用技巧与配置教程&#xff1f;如何用Go Mail实现发信&#xff1f; 随着工作负载的增加&#xff0c;如何高效地发送和管理邮件成为了许多职场人士面临的挑战。AokSend将为您提供一份详细的Go Mail设置指南&#xff0c;帮助您提升发送邮件的效率&#xff0c;让您的邮件…...

kali的tplmap使用报错解决

问题 当我们直接使用kali下的tplmap时报错了。 Tplmap 0.5 Automatic Server-Side Template Injection Detection and Exploitation Tool Testing if GET parameter name is injectable Exiting: module collections has no attribute Mapping 这是因为tplmap要求的版本…...

DAY16||513.找树左下角的值 |路径总和|从中序与后序遍历序列构造二叉树

513.找树左下角的值 题目&#xff1a;513. 找树左下角的值 - 力扣&#xff08;LeetCode&#xff09; 给定一个二叉树的 根节点 root&#xff0c;请找出该二叉树的 最底层 最左边 节点的值。 假设二叉树中至少有一个节点。 示例 1: 输入: root [2,1,3] 输出: 1示例 2: 输入: […...

使用jQuery处理Ajax

使用jQuery处理Ajax HTTP协议 超文本传输协议&#xff08;HTTP&#xff0c;HyperText Transfer Protocol)是互联网上应用最为广泛的一种网络协议 设计HTTP最初的目的是为了提供一种发布和接收HTML页面的方法 所有的WWW文件都必须遵守这个标准 一次HTTP操作称为一个事务&am…...

uni-app App版本更新

效果图&#xff1a; 前言 在移动应用开发中&#xff0c;确保用户能够及时更新到最新版本是非常重要的。本文将介绍如何在 uni-app 中实现 App 整包更新功能&#xff0c;并提供相关代码示例以帮助理解。 代码实现 2.1 引入模块 首先&#xff0c;我们需要引入用于处理更新的模块…...

Python Web 与低代码/无代码平台的深度融合

Python Web 与低代码/无代码平台的深度融合 目录 &#x1f680; 低代码与无代码平台的兴起&#x1f517; Python 与低代码平台集成&#x1f310; 低代码开发的最佳实践&#x1f4ca; 数据集成与自动化 1. &#x1f680; 低代码与无代码平台的兴起 低代码和无代码平台的出现&…...

js 如何监听 body 内容是否改变

如果您想监听body内容的变化&#xff0c;并作出响应&#xff0c;可以使用MutationObserver。以下是一个简单的例子&#xff0c;它会在body内容变化时在控制台输出一条消息&#xff1a; // 创建一个观察者对象 const observer new MutationObserver(function(mutations, obser…...

python: 数字类型的一些函数

len(str) round(x, d) 对x进行四舍五入保留小数点后d位 round&#xff08;3.45&#xff0c;1&#xff09; 即 3.5 pow(x, y) # x的y次幂. x ** y pow(x, y[,z]) # 幂余 &#xff08; x ** y) % z print(pow(3, pow(3, 99), 10000)) #4587 浮点数…...

MapReduce学习与理解

MapReduce为google分布式三驾马车之一。分别为《The Google File System》、《MapReduce: Simplified Data Processing on Large Clusters》、《Bigtable: A Distributed Storage System for Structured Data》。三遍论文奠定了分布式存储和计算的基础。本篇文章来说说mapreduc…...

Animal objDog = new Dog()和 Dog objDog = new Dog()的区别

文章目录 1、Animal objDog new Dog()和 Dog objDog new Dog()的区别1. **对象类型&#xff08;引用类型&#xff09;**2. **调用和可用成员**3. **示例代码来说明**使用示例总结 2、Animal objDog new Dog();不能调用dog的方法和属性是为什么&#xff1f;原因解析解决方法小…...

springboot引入netty

配置类 import cn.hutool.core.thread.ThreadUtil; import io.netty.bootstrap.ServerBootstrap; import io.netty.buffer.PooledByteBufAllocator; import io.netty.channel.*; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChanne…...

PWM基础与信号控制

1. 什么是PWM&#xff1f; PWM&#xff08;Pulse Width Modulation&#xff0c;脉宽调制&#xff09;是一种通过改变信号的占空比来控制电压输出的技术。简单来说&#xff0c;PWM信号由一系列高低电平组成&#xff0c;通过调节高电平持续的时间比例&#xff0c;可以控制信号的…...

nvm,一款nodejs版本管理工具

背景 在工作中&#xff0c;我们可能同时在进行2个或者多个不同的项目开发&#xff0c;每个项目的需求不同&#xff0c;进而不同项目必须依赖不同版本的NodeJS运行环境&#xff0c;这种情况下&#xff0c;对于维护多个版本的node将会是一件非常麻烦的事情&#xff0c;nvm就是为…...

数据处理与统计分析篇-day11-RFM模型案例

会员价值度模型介绍 会员价值度用来评估用户的价值情况&#xff0c;是区分会员价值的重要模型和参考依据&#xff0c;也是衡量不同营销效果的关键指标之一。 价值度模型一般基于交易行为产生&#xff0c;衡量的是有实体转化价值的行为。常用的价值度模型是RFM RFM模型是根据…...

【PostgreSQL】PostgreSQL数据库允许其他IP连接到数据库(Windows Linux)

要让PostgreSQL数据库允许其他IP连接到数据库&#xff0c;需要进行以下几个步骤的配置&#xff1a; 1. 修改postgresql.conf文件 首先&#xff0c;需要修改PostgreSQL的主配置文件postgresql.conf&#xff0c;允许数据库监听所有IP的连接请求。 1.1 找到postgresql.conf文件…...

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇&#xff0c;在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下&#xff1a; 【Note】&#xff1a;如果你已经完成安装等操作&#xff0c;可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作&#xff0c;重…...

设计模式和设计原则回顾

设计模式和设计原则回顾 23种设计模式是设计原则的完美体现,设计原则设计原则是设计模式的理论基石, 设计模式 在经典的设计模式分类中(如《设计模式:可复用面向对象软件的基础》一书中),总共有23种设计模式,分为三大类: 一、创建型模式(5种) 1. 单例模式(Sing…...

(十)学生端搭建

本次旨在将之前的已完成的部分功能进行拼装到学生端&#xff0c;同时完善学生端的构建。本次工作主要包括&#xff1a; 1.学生端整体界面布局 2.模拟考场与部分个人画像流程的串联 3.整体学生端逻辑 一、学生端 在主界面可以选择自己的用户角色 选择学生则进入学生登录界面…...

2025 后端自学UNIAPP【项目实战:旅游项目】6、我的收藏页面

代码框架视图 1、先添加一个获取收藏景点的列表请求 【在文件my_api.js文件中添加】 // 引入公共的请求封装 import http from ./my_http.js// 登录接口&#xff08;适配服务端返回 Token&#xff09; export const login async (code, avatar) > {const res await http…...

深度学习习题2

1.如果增加神经网络的宽度&#xff0c;精确度会增加到一个特定阈值后&#xff0c;便开始降低。造成这一现象的可能原因是什么&#xff1f; A、即使增加卷积核的数量&#xff0c;只有少部分的核会被用作预测 B、当卷积核数量增加时&#xff0c;神经网络的预测能力会降低 C、当卷…...

【JavaSE】多线程基础学习笔记

多线程基础 -线程相关概念 程序&#xff08;Program&#xff09; 是为完成特定任务、用某种语言编写的一组指令的集合简单的说:就是我们写的代码 进程 进程是指运行中的程序&#xff0c;比如我们使用QQ&#xff0c;就启动了一个进程&#xff0c;操作系统就会为该进程分配内存…...

jmeter聚合报告中参数详解

sample、average、min、max、90%line、95%line,99%line、Error错误率、吞吐量Thoughput、KB/sec每秒传输的数据量 sample&#xff08;样本数&#xff09; 表示测试中发送的请求数量&#xff0c;即测试执行了多少次请求。 单位&#xff0c;以个或者次数表示。 示例&#xff1a;…...

Qemu arm操作系统开发环境

使用qemu虚拟arm硬件比较合适。 步骤如下&#xff1a; 安装qemu apt install qemu-system安装aarch64-none-elf-gcc 需要手动下载&#xff0c;下载地址&#xff1a;https://developer.arm.com/-/media/Files/downloads/gnu/13.2.rel1/binrel/arm-gnu-toolchain-13.2.rel1-x…...

git: early EOF

macOS报错&#xff1a; Initialized empty Git repository in /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core/.git/ remote: Enumerating objects: 2691797, done. remote: Counting objects: 100% (1760/1760), done. remote: Compressing objects: 100% (636/636…...

算术操作符与类型转换:从基础到精通

目录 前言&#xff1a;从基础到实践——探索运算符与类型转换的奥秘 算术操作符超级详解 算术操作符&#xff1a;、-、*、/、% 赋值操作符&#xff1a;和复合赋值 单⽬操作符&#xff1a;、--、、- 前言&#xff1a;从基础到实践——探索运算符与类型转换的奥秘 在先前的文…...