ubuntu环境下 基于Python 打包的 批量命令行可视化操作工具 GUI

news2025/5/21 8:24:49

文章目录

    • 一.需求:
    • 二.原理支撑:
    • 三.简单Demo
    • 四.封装成GUI
      • 1.依赖库
      • 2.代码
    • 五.打包成可执行文件
    • 六.命令行的配置
    • 七.运行效果

一.需求:

作为测试工程师,为了到现场高效的调试,部署工作,需要一个可视化的工具,避免繁琐的记命令行和敲命令行,直接把命令行集成到可视化工具,运行某些命令时,点击按钮即可实现。

  1. 命令行配置简单,可动态添加命令行
  2. 支持多窗口运行
  3. 支持多命令行运行
  4. 支持命令行的顺序运行

二.原理支撑:

python os库下的system()函数

system("gnome-terminal --window -e 'bash -c \"pwd;ls;exec bash\"' --tab -e 'bash -c \"pwd; exec bash\"'");

其中:
gnome-terminal --window -e 'bash -c "pwd;ls; — 开启一个终端窗口 并运行 pwd和 ls
【先运行pwd后,再运行ls,同一个窗口不同命令用“;”间隔】

–tab -e ‘bash -c “pwd; exec bash”’" ---- 同一个窗口下开启另外一个tab 并运行指令 pwd

三.简单Demo

import os

# 启动ROS2必须操作的前置条件, 运行ros2指令前需要添加的。
ros_path = "source /opt/ros/humble/setup.bash "


# 这些是一些语法格式的需要 不需要修改
win_cmd = "gnome-terminal --window -e 'bash -c \"" + ros_path
tab_cmd = " --tab -e 'bash -c \"" + ros_path
end_cmd = ";exec bash\"'"

def run_win(cmd):
    # 运行的第一个窗口终端
    cmd = win_cmd+cmd+end_cmd
    return cmd

def run_tab(cmd, t):
    # 运行的标签终端
    delay_t = "sleep {};".format(str(t))
    cmd = tab_cmd+delay_t+cmd+end_cmd
    return cmd

# t 指令运行之间的时间间隔
def run_cmd(cmds, t = 2):
    # 合成命令
    cmd  = run_win(cmds[0])
    for i in range(1, len(cmds)):
        cmd = cmd + run_tab(cmds[i], t*i)
    #print(cmd)
    # 运行指令
    os.system(cmd)


# 单窗口多指令 “;” 间隔
cmds = ["export TURTLEBOT3_MODEL=waffle;export GAZEBO_MODEL_PATH=$GAZEBO_MODEL_PATH:/opt/ros/humble/share/turtlebot3_gazebo/models;ros2 launch nav2_bringup tb3_simulation_launch.py headless:=False"]

# 多窗口多指令 列表 , 间隔
# cmds = ["export TURTLEBOT3_MODEL=waffle","export GAZEBO_MODEL_PATH=$GAZEBO_MODEL_PATH:/opt/ros/humble/share/turtlebot3_gazebo/models","ros2 launch nav2_bringup tb3_simulation_launch.py headless:=False"]

run_cmd(cmds)

四.封装成GUI

1.依赖库

PyQt5          5.15.6
PyYAML         5.4.1
pyinstaller    6.13.0

2.代码

import sys
from PyQt5.QtWidgets import QMainWindow,QApplication,QVBoxLayout
from PyQt5.QtWidgets import QSystemTrayIcon, QPushButton,QMenu,QAction,QGridLayout
from PyQt5.QtGui import QIcon
# from system_hotkey import SystemHotkey # 热键#
from Ui_tools import Ui_MainWindow
from PyQt5.QtCore import Qt
import os
import yaml

# 启动ROS2必须操作的前置条件, 运行ros2指令前需要添加的。
ros_path = "source /opt/ros/humble/setup.bash;"


# 这些是一些语法格式的需要 不需要修改
win_cmd = "gnome-terminal --window -e 'bash -c \"" + ros_path
tab_cmd = " --tab -e 'bash -c \"" + ros_path
end_cmd = ";exec bash\"'"

def run_win(cmd):
    # 运行的第一个窗口终端
    cmd = win_cmd+cmd+end_cmd
    return cmd

def run_tab(cmd, t):
    # 运行的标签终端
    delay_t = "sleep {};".format(str(t))
    cmd = tab_cmd+delay_t+cmd+end_cmd
    return cmd

# t 指令运行之间的时间间隔
def run_cmd(cmds, t = 2):
    # 合成命令
    cmd  = run_win(cmds[0])
    for i in range(1, len(cmds)):
        cmd = cmd + run_tab(cmds[i], t*i)
    print("命令合成",cmd)
    # 运行指令
    os.system(cmd)


# # 单窗口多指令 “;” 间隔
# cmds = ["export TURTLEBOT3_MODEL=waffle;export GAZEBO_MODEL_PATH=$GAZEBO_MODEL_PATH:/opt/ros/humble/share/turtlebot3_gazebo/models;ros2 launch nav2_bringup tb3_simulation_launch.py headless:=False"]
# # 多窗口多指令 列表 , 间隔
# # cmds = ["export TURTLEBOT3_MODEL=waffle","export GAZEBO_MODEL_PATH=$GAZEBO_MODEL_PATH:/opt/ros/humble/share/turtlebot3_gazebo/models","ros2 launch nav2_bringup tb3_simulation_launch.py headless:=False"]
# run_cmd(cmds)


# 按钮样式设置
btn_style = """
            QPushButton {
                background-color: #87CEEB; /* 背景颜色 */
                border-radius: 10px; /* 圆角 */
                color: #000000; /* 文字颜色 */
                font-size: 25px; /* 字体大小 */
                font-family: "Microsoft YaHei"; /* 字体类型 */
                padding: 10px 10px; /* 内边距 */
            }

            QPushButton:hover {
                background-color: #00FFFF; /* 鼠标悬停时的背景颜色 */
            }

            QPushButton:pressed {
                background-color: #00BFFF; /* 按下时的背景颜色 */
            }
        """
show_style ="""
            QPushButton {
                background-color: #B0C4DE; /* 背景颜色 */
                border-radius: 10px; /* 圆角 */
                color: #000000; /* 文字颜色 */
                font-size: 1px; /* 字体大小 */
                font-family: "Microsoft YaHei"; /* 字体类型 */
                padding: 10px 10px; /* 内边距 */
                background-image: url({0});
                background-position: center center;
            }
            QPushButton:hover {
                background-color: #808080; /* 鼠标悬停时的背景颜色 */
            }
            QPushButton:pressed {
                background-color: #696969; /* 按下时的背景颜色 */
            }                        
        """

current_style = """
            QPushButton {
                background-color: #00BFFF; /* 背景颜色 */
                border-radius: 10px; /* 圆角 */
                color: #000000; /* 文字颜色 */
                font-size: 25px; /* 字体大小 */
                font-family: "Microsoft YaHei"; /* 字体类型 */
                padding: 10px 10px; /* 内边距 */
            }
        """



# 获取当前目录
current_dir = os.getcwd()

# 获取data文件夹的路径
data_dir = os.path.join(current_dir, 'data')

def get_dirs():
    '''
        获取当前目录下的data文件夹下的所有分区,及分区下的所有文件夹
        返回类型:列表下的字典
            [{'专业区': []}, {'办公区': ['a2345',  'WPS']}, {'工具区': []}, {'检测区': ['DiskGenius']}]
    '''
    result_list = []

    # 获取data文件夹中所有文件夹的名称
    result_list = [name for name in os.listdir(data_dir) if os.path.isdir(os.path.join(data_dir, name))]

    return result_list

class MyMainWindow(QMainWindow,Ui_MainWindow): 
    def __init__(self,parent =None):
        super(MyMainWindow,self).__init__(parent)
        # 读取命令行配置文件
        
        self.yaml_dict = None
        self.yaml_table = []
        self.yaml_table_current = None # 当前选中的组项
        self.read_yaml()
        # 初始化界面
        self.createTrayIcon()
        # 热键
        # self.hk_switch = SystemHotkey()
        # 绑定快捷键
        # self.hk_switch.register(('control','alt',"z"),callback=lambda x:self.hotkey_callback())
        # 创建按钮
        self.create_menu_button()
        # 第一次加载触发
        self.first_load()
    
    # 读取当前cmd_conf.yaml文件
    def read_yaml(self):
        with open('cmd_conf.yaml', 'r', encoding='utf-8') as f:
            result = yaml.load(f.read(), Loader=yaml.FullLoader)
        table_list = list(result.keys()) # 获取字典的键
        self.yaml_dict = result
        self.yaml_table = table_list



    # 托盘设置
    def createTrayIcon(self):
        # 创建窗口
        self.setupUi(self)
        self.setWindowIcon(QIcon("tray.png"))
        self.setWindowTitle("Cmd Visual V1.0")
        # 创建系统托盘图标
        self.tray_icon = QSystemTrayIcon(self)
        self.tray_icon.setIcon(QIcon("tray.png"))
        self.tray_icon.setVisible(True)

        # 设置托盘图标的菜单
        menu = QMenu()
        show_action = QAction("显示窗口", menu)
        quit_action = QAction("退出", menu)
        menu.addAction(show_action)
        menu.addAction(quit_action)
        self.tray_icon.setContextMenu(menu)

        # 绑定菜单事件
        show_action.triggered.connect(self.show)
        quit_action.triggered.connect(self.close)

        # 创建一个表格布局
        self.grid_layout = QGridLayout()

        


    # 事件绑定
    def hotkey_callback(self):
        #判断当前窗口状态
        if self.isHidden():
            self.show()
            self.activateWindow()
        else:
            self.hide()

    # 列表创建菜单按钮
    def create_menu_button(self):
        layout_menu = QVBoxLayout()
        for i,menu_i in enumerate(self.yaml_table):
            menu_i = QPushButton(menu_i)
            # 设置手形
            menu_i.setCursor(Qt.PointingHandCursor)
            # 设置背景色为浅蓝色,圆角,选中时为深蓝色
            menu_i.setStyleSheet(btn_style)
            # 设置最小高度
            menu_i.setMinimumHeight(40)
            # 绑定事件
            menu_i.clicked.connect(self.button_clicked)
            # 添加到布局里
            layout_menu.addWidget(menu_i)
            if(i==0):
                menu_i.setStyleSheet(current_style)
        self.frame.setLayout(layout_menu)
    

    # 创建展示区按钮
    def create_show_button(self, subName):
        temp_list = []
        # self.temp_dir = os.path.join(data_dir, subName)
        # 获取data文件夹中所有文件夹的名称
        temp_list = self.yaml_dict.get(subName).keys()
        temp_list = list(temp_list)
        # print(temp_list)
        # # 根据列表创建按钮
        self.buttons = [QPushButton(f"{i}") for i in temp_list]

        # 在表格布局中添加按钮
        exe_icon = QIcon()
        for i, button in enumerate(self.buttons):
             button.setCursor(Qt.PointingHandCursor)
             # button.setStyleSheet(show_style)
             button.setMinimumSize(128, 50)
             button.setMaximumSize(128, 50)
             self.grid_layout.addWidget(button, i // 6, i % 6)
             # 绑定事件
             button.clicked.connect(self.start_exe)

        # # 添加到主窗口
        self.frame_2.setLayout(self.grid_layout)
    
    # 第一次加载时触发
    def first_load(self):
        self.create_show_button(self.yaml_table[0])
        self.yaml_table_current = self.yaml_table[0]

    # 按钮点击事件
    def button_clicked(self):
        #判断是哪个按钮的事件
        sender = self.sender()
        self.yaml_table_current = sender.text()
        # 获取self.frame中QVBoxLayout里的所有按钮
        for button in self.frame.findChildren(QPushButton):

            # 判断当前按钮是否为点击按钮
            if button == sender:
                # 设计当前按钮的背景色为深蓝色
                sender.setStyleSheet(current_style)
                # 清除frame2上的控件
                for i in reversed(range(self.grid_layout.count())): 
                    self.grid_layout.itemAt(i).widget().setParent(None)
                
                # 这里触发切换展示区的功能按钮
                self.create_show_button(sender.text())
            else:
                # 设置其他按钮保存原样
                button.setStyleSheet(btn_style)

    # 点击启动事件
    def start_exe(self):
        cmd_list = []
        # 获取当前按钮的文本
        current_test = self.sender().text()
        # 获取当前触发的指令
        current_cmd = self.yaml_dict.get(self.yaml_table_current).get(current_test)
        # 判断是多窗口指令【列表类型】,还是单窗口指令【字符串】
        if isinstance(current_cmd, list):
            cmd_list = current_cmd
        else:
            cmd_list.append(current_cmd)
            print(cmd_list)
        # 运行当前指令
        run_cmd(cmd_list)
        


if __name__ == "__main__":
    app = QApplication(sys.argv)
    myWin = MyMainWindow()
    myWin.show()
    sys.exit(app.exec_())    

五.打包成可执行文件

这里的代码 xxx.py 改成 上面代码的文件名称

pyinstaller -F -w  xxx.py

六.命令行的配置

一级:主题
子任务: shell指令
其中:
主题 -左侧标题栏的名称
子任务-按钮名称
shell指令-一个或在一组的指令,根据需求来
注意:
(1)指令之间有前后关系的,可以在同一窗口运行多指令;
(2)无则可配置成多窗口,独立运行;
(3)指令内部有空格,需要用双引号括起,避免与yaml语法冲突。

# 第一个工具栏
建图:
    # 多窗口指令   用  "-"  间隔多窗口指令
    录制包:
      - "ls"
      - "pwd"
      - "ls -l"
      - "ros2"
      - "cd ~"
    # 单窗口多指令  用  ";" 间隔多条指令
    创建地图: "ls;pwd;ros2"
    
    核对地图: "ros2"

# 第二个工具栏
自驾:
    打开可视化: "ls"


# 第三个工具栏
边界:
    打开配置文件: "ls"
    运行脚本: "pwd"
    拷贝到车端: "ros2"
    打开车端配置文件: "ls"


超声波:
    打开配置文件: "cd ~/legion_framework/modules/perception/uss/uss_detection/bin/conf/perception/uss/uss_detection"


数据可视化:
    ros_bag: 

七.运行效果

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2380586.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

谷歌宣布推出 Android 的新安全功能,以防止诈骗和盗窃

在上周二的 Android Show 上,也就是Google I/O 开发者大会之前,谷歌宣布了 Android 的全新安全和隐私功能。这些新功能包括对通话、屏幕共享、消息、设备访问和系统级权限的全新保护。谷歌希望通过这些功能保护用户免遭诈骗,在设备被盗或被攻…

Qt/C++编写音视频实时通话程序/画中画/设备热插拔/支持本地摄像头和桌面

一、前言 近期有客户提需求,需要在嵌入式板子上和电脑之间音视频通话,要求用Qt开发,可以用第三方的编解码组件,能少用就尽量少用,以便后期移植起来方便。如果换成5年前的知识储备,估计会采用纯网络通信收发…

Android trace presentFence屏幕显示的帧

Android trace presentFence屏幕显示的帧 presentFence :当帧成功显示到屏幕时,present fence就会signal。 FrameMissed/GpuFrameMissed/HwcFrameMissed表示上一次合成的结果,当SurfaceFlinger合成后显示到屏幕上,present fence就…

c++ 类的语法4

测试析构函数、虚函数、纯虚函数&#xff1a; void testClass5() {class Parent {public:Parent(int x) { cout << "Parent构造: " << x << endl; }~Parent() {cout << "调用Parent析构函数" << endl;}virtual string toSt…

NMOS和PMOS的区别

1 区分NMOS和PMOS&#xff1a;衬底箭头指向G级的是NMOS&#xff0c;衬底箭头背向G级的是PMOS 2 区分D和S级&#xff1a;针对NMOS&#xff0c;体二极管的正方向为S级&#xff1b;针对PMOS&#xff0c;体二极管正的方向为D级 3 区分电流方向&#xff1a;针对NMOS&#xff0c;电…

java云原生实战之graalvm 环境安装

windows环境安装 在Windows环境下安装GraalVM并启用原生镜像功能时&#xff0c;需要Visual Studio的组件支持。具体要点如下&#xff1a; 核心依赖&#xff1a; 需要安装Visual Studio 2022或更新版本&#xff0c;并确保勾选以下组件&#xff1a; "使用C的桌面开发"…

2025年电工杯新规发布-近三年题目以及命题趋势

电工杯将于2025.5.23 周五早八正式开赛&#xff0c;该竞赛作为上半年度竞赛规模最大的竞赛&#xff0c;因免报名费、一级学会承办等因素&#xff0c;被众多高校认可。本文将在从2025年竞赛新规、历史赛题选题分析、近年优秀论文分享、竞赛模板分析等进行电工杯备赛&#xff0c;…

替换word中的excel

PostMapping("/make/report/target/performance/first") public AjaxResult makeTargetReportFirst(RequestBody MakeReportDTO makeReportDTO) {Map<String, String> textReplaceMap new HashMap<>();// 替换日期LocalDateTime nowData LocalDateTime…

大模型服务如何实现高并发与低延迟

写在前面 大型语言模型(LLM)正以前所未有的速度渗透到各行各业,从智能客服、内容创作到代码生成、企业知识库,其应用场景日益丰富。然而,将这些强大的 AI 能力转化为稳定、高效、可大规模应用的服务,却面临着巨大的挑战,其中高并发处理能力和低响应延迟是衡量服务质量的…

OBS Studio:windows免费开源的直播与录屏软件

OBS Studio是一款免费、开源且跨平台的直播与录屏软件。其支持 Windows、macOS 和 Linux。OBS适用于&#xff0c;有直播需求的人群或录屏需求的人群。 Stars 数64,323Forks 数8413 主要特点 推流&#xff1a;OBS Studio 支持将视频实时推流至多个平台&#xff0c;如 YouTube、…

经典面试题:TCP 三次握手、四次挥手详解

在网络通信的复杂架构里&#xff0c;“三次握手”与“四次挥手”仿若一座无形的桥梁&#xff0c;它们是连接客户端与服务器的关键纽带。这座“桥梁”不仅确保了连接的稳固建立&#xff0c;还保障了连接的有序结束&#xff0c;使得网络世界中的信息能够顺畅、准确地流动。 在面…

高光谱数据处理技术相关

一、Savitzky-Golay(SG)平滑 1. 基本概念 Savitzky-Golay(SG)平滑是一种基于局部多项式拟合的卷积算法,主要用于信号处理(如光谱、色谱数据)的去噪和平滑。其核心思想是通过滑动窗口内的多项式拟合来保留信号的原始特征(如峰形、宽度),同时抑制高频噪声。 2. 技术原…

机器视觉的PVC卷对卷丝印应用

在现代工业制造领域&#xff0c;PVC卷对卷丝印工艺凭借其高效、灵活的特点&#xff0c;广泛应用于广告制作、包装印刷、电子产品装饰等多个行业。然而&#xff0c;在高速连续的丝印过程中&#xff0c;如何确保印刷图案的精准定位、色彩一致性以及质量稳定性&#xff0c;一直是困…

LabVIEW数据库使用说明

介绍LabVIEW如何在数据库中插入记录以及执行 SQL 查询&#xff0c;适用于对数据库进行数据管理和操作的场景。借助 Database Connectivity Toolkit&#xff0c;可便捷地与指定数据库交互。 各 VI 功能详述 左侧 VI 功能概述&#xff1a;实现向数据库表中插入数据的操作。当输入…

25考研经验贴(11408)

声明&#xff1a;以下内容都仅代表个人观点 数学一&#xff08;130&#xff09; 25考研数学一难度介绍&#xff1a;今年数学一整体不难&#xff0c;尤其是选填部分&#xff0c;大题的二型线面和概率论大题个人感觉比较奇怪&#xff0c;其他大题还是比较容易的。.26如何准备&a…

java中的Filter使用详解

Filter&#xff08;过滤器&#xff09;是 Java Web 开发的核心组件之一&#xff0c;用于在请求到达 Servlet 或响应返回客户端之前进行拦截和处理。以下是其核心功能、使用方法和实际场景的详细解析&#xff1a; 一、Filter 的作用与原理 核心作用 Filter 充当请求与响应之间的…

css使用clip-path属性切割显示可见内容

1. 需求 想要实现一个渐变的箭头Dom&#xff0c;不想使用svg、canvas去画&#xff0c;可以考虑使用css的clip-path属性切割显示内容。 2. 实现 <div class"arrow">箭头 </div>.arrow{width: 200px;height: 60px;background-image: linear-gradient(45…

新京东,正在成为一种生活方式

出品|何玺排版|叶媛 一个新京东&#xff0c;正在从“心”诞生。 2025年2月11日之前&#xff0c;如果问京东是做什么的&#xff0c;相信大多数人会回答京东是电商平台&#xff0c;卖家电数码日用百货的。现在&#xff0c;如果问京东是做什么的&#xff0c;相信大家的回答不在是…

Linux 文件(2)

文章目录 1. 文件描述符1.1 文件描述符是什么1.2 文件描述符如何分配 2 重定向2.1 输出重定向2.2 输入重定向2.3 使用dup2进行重定向 3. 文件、父子进程和进程替换 1. 文件描述符 1.1 文件描述符是什么 什么是文件描述符呢&#xff1f; 我们先来看之前所介绍的系统级别的文件…

基于Springboot + vue3实现的工商局商家管理系统

项目描述 本系统包含管理员、商家两个角色。 管理员角色&#xff1a; 用户管理&#xff1a;管理系统中所有用户的信息&#xff0c;包括添加、删除和修改用户。 许可证申请管理&#xff1a;管理商家的许可证申请&#xff0c;包括搜索、修改或删除许可证申请。 许可证审批管理…