Qt之自定义标题栏拓展(十)

embedded/2024/12/20 6:36:38/

Qt开发 系列文章 - user-defined-titlebars(十)


目录

前言

一、方式一

1.效果演示

2.创建标题栏类

3.可视化UI设计

4.定义相关函数

5.使用标题栏类

二、方式二

1.效果演示

2.创建标题栏类

3.定义相关函数

4.使用标题栏类

总结


前言

Qt自带的窗口标题栏通常遵循操作系统的默认样式和布局,以确保在不同平台上都能提供一致且符合用户期望的用户体验,因此Qt自带的窗口标题栏无法自定义。但我们在Qt设计软件时,经常需要改变窗口标题栏的样式,以满足不同场合用户需求。本文紧接着上一篇博文Qt之修改窗口标题、图标以及自定义标题栏(九)-CSDN博客的基础上,在介绍几种实现自定义标题栏的方法,并提供简单示例。


一、方式一

上一篇博文Qt之修改窗口标题、图标以及自定义标题栏(九)实现该方式是通过直接手鲁编写标题栏UI代码,下面提供的方式是在可视化界面UI上来设计标题栏,简洁方便直观,然后绑定/链接到主窗口界面上。

1.效果演示

2.创建标题栏类

跟上一个有区别的是,在原有的项目上,添加新的类文件,选择带ui设计类的模版,选择如下。

然后,定义类和ui的名称为MyTitleBar,MyTitleBar类的头文件代码如下(示例):

/** 只提供与上一篇不一样的代码地方 **/
QT_BEGIN_NAMESPACE
namespace Ui { class MyTitleBar; }
QT_END_NAMESPACEclass MyTitleBar : public QWidget
{
private slots:// 按钮触发的槽;void on_m_pButtonClose_clicked();void on_m_pButtonMax_clicked();void on_m_pButtonRestore_clicked();void on_m_pButtonMin_clicked();
private:Ui::MyTitleBar *ui;
};

3.可视化UI设计

上面我们定义标题栏类的头文件,下面来在可视化界面UI上来设计标题栏,双击打开.ui文件,在图像化设计界面设计如下标题栏。

  

上面设计完后,Qt会自动生成相应的UI代码和头文件,相关类为Ui_MyTitleBar。

4.定义相关函数

设计好MyTitleBar的UI文件后,下面来说明相关功能函数。

#include "ui_mytitlebar.h"
#include "mytitlebar.h"MyTitleBar::MyTitleBar(QWidget *parent): QWidget(parent), ui(new Ui::MyTitleBar)
{// UI初始化ui->setupUi(this);// 初始化控件initControl();// 初始化标题栏色彩setBackgroundColor(m_colorR, m_colorG, m_colorB, m_isTransparent);// 加载本地样式 .css文件//loadStyleSheet("MyTitle");// 初始化标题栏色彩setBackgroundColor(230,232,250,0);setStyleSheet("QToolButton#m_pButtonMenu,QPushButton#m_pButtonMin,QPushButton#m_pButtonMax,QPushButton#m_pButtonClose{\border-radius:3px;\color:#324C6C;\padding:3px;\margin:0px;\background:none;\border-style:none;\}");setStyleSheet("QToolButton#m_pButtonMenu:hover,QPushButton#m_pButtonMin:hover,QPushButton#m_pButtonMax:hover{\color:#FFFFFF;\margin:1px 1px 2px 1px;\background-color:rgba(51,127,209,230);\}");
}
MyTitleBar::~MyTitleBar()
{
}
void MyTitleBar::initControl()
{m_colorR=0;m_colorG=153;m_colorB=153;m_isPressed=false;m_buttonType=MIN_MAX_BUTTON;m_windowBorderWidth=0;m_isTransparent=false;setFixedHeight(30);setWindowFlags(Qt::FramelessWindowHint);// 添加换肤菜单QStringList name;name << "银色" << "蓝色" << "浅蓝色" << "深蓝色";foreach (QString str, name) {QAction *action = new QAction(str, this);ui->m_pButtonMenu->addAction(action);connect(action, SIGNAL(triggered(bool)), this, SLOT(onButtonchangeStyle()));}
}
// 以下为按钮操作响应的槽
void MyTitleBar::onButtonchangeStyle()
{QAction *act = (QAction *)sender();QString name = act->text();//QString qssFile = "qss/blue.css";if (name == "银色") {//qssFile = "qss/silvery.css";setBackgroundColor(230,232,250,0);}else if (name == "蓝色"){//qssFile = "qss/blue.css";setBackgroundColor(50,76,108,0);}else if (name == "浅蓝色"){//qssFile = "qss/lightblue.css";setBackgroundColor(56,100,135,0);}else if (name == "深蓝色"){//qssFile = "qss/darkblue.css";setBackgroundColor(122,175,227,0);}//loadStyleSheet(qssFile);//emit signalchangeStyle(qssFile);
}
void MyTitleBar::on_m_pButtonClose_clicked()
{emit signalButtonCloseClicked();
}
void MyTitleBar::on_m_pButtonMax_clicked()
{ui->m_pButtonMax->setVisible(false);ui->m_pButtonRestore->setVisible(true);emit signalButtonMaxClicked();
}
void MyTitleBar::on_m_pButtonRestore_clicked()
{ui->m_pButtonRestore->setVisible(false);ui->m_pButtonMax->setVisible(true);emit signalButtonRestoreClicked();
}
void MyTitleBar::on_m_pButtonMin_clicked()
{emit signalButtonMinClicked();
}

5.使用标题栏类

在用户的构造函数上面添加对标题栏类的使用,跟上一篇博文一样。

二、方式二

方式一实现自定义标题栏时,定义一个标题栏类,在生成标题栏时,会覆盖原有的MenuBar和ToolBar,方式二改进后,修改标题栏不覆盖,效果如下。

1.效果演示

2.创建标题栏类

跟上一篇博文一样,在原有的项目上,添加C++新的类文件,选择如下。

然后定义标题栏类,代码部分如下(示例):

#include <QDialog>
#include <QMutex>class QLabel;
class QPushButton;
class QToolButton;
class QVBoxLayout;
class QHBoxLayout;
class QFrame;
class QSpacerItem;
class QLineEdit;
class QComboBox;
class QAbstractButton;
class QUIWidget : public QDialog#endif
{Q_OBJECTQ_ENUMS(Style)Q_PROPERTY(QString title READ getTitle WRITE setTitle)Q_PROPERTY(Qt::Alignment alignment READ getAlignment WRITE setAlignment)
public:explicit QUIWidget(QWidget *parent = 0);~QUIWidget();
public Q_SLOTS://设置主窗体void setMainWidget(QWidget *mainWidget);//设置部件图标void setIcon(QUIWidget::Widget widget, QChar str, quint32 size = 9);//设置部件图片void setPixmap(QUIWidget::Widget widget, const QString &file, const QSize &size = QSize(16, 16));//设置部件是否可见void setVisible(QUIWidget::Widget widget, bool visible = true);//设置只有关闭按钮void setOnlyCloseBtn();//设置标题栏高度void setTitleHeight(int height);//设置按钮统一宽度void setBtnWidth(int width);//设置标题及文本样式void setTitle(const QString &title);void setAlignment(Qt::Alignment alignment);
};

3.定义相关函数

1.初始化函数

在标题栏类构造函数上,初始化如下设置,代码如下。

QUIWidget::QUIWidget(QWidget *parent) : QDialog(parent)
{//定义标题栏字体文本编码this->setTranslator(":/qm/qt_zh_CN.qm");this->setCode();this->initControl();this->initForm();//设置标题栏标题
#ifdef csdsetTitle("**软件");
#elsesetTitle("csd演示版");
#endif//设置标题文本居中setAlignment(Qt::AlignCenter);//设置窗体可拖动大小setSizeGripEnabled(true);//设置换肤下拉菜单可见setVisible(QUIWidget::BtnMenu, true);//设置标题栏高度setTitleHeight(30);//设置按钮宽度setBtnWidth(30);//设置软件左上角图标--没有图标时显示图形字体setPixmap(QUIWidget::Lab_Ico, "ico/butterfly.ico", QSize(30,30));//默认样式风格bluesetStyle(":/qss/blue.css");QFont font;font.setPointSize(12);this->setFont(font);
}
void QUIWidget::initControl()
{this->setObjectName(QString::fromUtf8("QUIWidget"));this->resize(900, 750);verticalLayout1 = new QVBoxLayout(this);verticalLayout1->setSpacing(0);verticalLayout1->setContentsMargins(11, 11, 11, 11);verticalLayout1->setObjectName(QString::fromUtf8("verticalLayout1"));verticalLayout1->setContentsMargins(1, 1, 1, 1);widgetMain = new QWidget(this);widgetMain->setObjectName(QString::fromUtf8("widgetMain"));widgetMain->setStyleSheet(QString::fromUtf8(""));verticalLayout2 = new QVBoxLayout(widgetMain);verticalLayout2->setSpacing(0);verticalLayout2->setContentsMargins(11, 11, 11, 11);verticalLayout2->setObjectName(QString::fromUtf8("verticalLayout2"));verticalLayout2->setContentsMargins(0, 0, 0, 0);widget_title = new QWidget(widgetMain);widget_title->setObjectName(QString::fromUtf8("widget_title"));QSizePolicy sizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);sizePolicy.setHorizontalStretch(0);sizePolicy.setVerticalStretch(0);sizePolicy.setHeightForWidth(widget_title->sizePolicy().hasHeightForWidth());widget_title->setSizePolicy(sizePolicy);widget_title->setMinimumSize(QSize(0, 30));horizontalLayout4 = new QHBoxLayout(widget_title);horizontalLayout4->setSpacing(0);horizontalLayout4->setContentsMargins(11, 11, 11, 11);horizontalLayout4->setObjectName(QString::fromUtf8("horizontalLayout4"));horizontalLayout4->setContentsMargins(0, 0, 0, 0);lab_Ico = new QLabel(widget_title);lab_Ico->setObjectName(QString::fromUtf8("lab_Ico"));QSizePolicy sizePolicy1(QSizePolicy::Minimum, QSizePolicy::Preferred);sizePolicy1.setHorizontalStretch(0);sizePolicy1.setVerticalStretch(0);sizePolicy1.setHeightForWidth(lab_Ico->sizePolicy().hasHeightForWidth());lab_Ico->setSizePolicy(sizePolicy1);lab_Ico->setMinimumSize(QSize(30, 0));lab_Ico->setAlignment(Qt::AlignCenter);horizontalLayout4->addWidget(lab_Ico);lab_Title = new QLabel(widget_title);lab_Title->setObjectName(QString::fromUtf8("lab_Title"));QSizePolicy sizePolicy2(QSizePolicy::Expanding, QSizePolicy::Preferred);sizePolicy2.setHorizontalStretch(0);sizePolicy2.setVerticalStretch(0);sizePolicy2.setHeightForWidth(lab_Title->sizePolicy().hasHeightForWidth());lab_Title->setSizePolicy(sizePolicy2);lab_Title->setAlignment(Qt::AlignLeading | Qt::AlignLeft | Qt::AlignVCenter);horizontalLayout4->addWidget(lab_Title);widget_menu = new QWidget(widget_title);widget_menu->setObjectName(QString::fromUtf8("widget_menu"));sizePolicy1.setHeightForWidth(widget_menu->sizePolicy().hasHeightForWidth());widget_menu->setSizePolicy(sizePolicy1);horizontalLayout = new QHBoxLayout(widget_menu);horizontalLayout->setSpacing(0);horizontalLayout->setContentsMargins(11, 11, 11, 11);horizontalLayout->setObjectName(QString::fromUtf8("horizontalLayout"));horizontalLayout->setContentsMargins(0, 0, 0, 0);btnMenu = new QToolButton(widget_menu);btnMenu->setObjectName(QString::fromUtf8("btnMenu"));QSizePolicy sizePolicy3(QSizePolicy::Fixed, QSizePolicy::Expanding);sizePolicy3.setHorizontalStretch(0);sizePolicy3.setVerticalStretch(0);sizePolicy3.setHeightForWidth(btnMenu->sizePolicy().hasHeightForWidth());btnMenu->setSizePolicy(sizePolicy3);btnMenu->setMinimumSize(QSize(30, 0));btnMenu->setMaximumSize(QSize(30, 16777215));btnMenu->setFocusPolicy(Qt::NoFocus);btnMenu->setPopupMode(QToolButton::InstantPopup);horizontalLayout->addWidget(btnMenu);btnMenu_Min = new QPushButton(widget_menu);btnMenu_Min->setObjectName(QString::fromUtf8("btnMenu_Min"));QSizePolicy sizePolicy4(QSizePolicy::Minimum, QSizePolicy::Expanding);sizePolicy4.setHorizontalStretch(0);sizePolicy4.setVerticalStretch(0);sizePolicy4.setHeightForWidth(btnMenu_Min->sizePolicy().hasHeightForWidth());btnMenu_Min->setSizePolicy(sizePolicy4);btnMenu_Min->setMinimumSize(QSize(30, 0));btnMenu_Min->setMaximumSize(QSize(30, 16777215));btnMenu_Min->setCursor(QCursor(Qt::ArrowCursor));btnMenu_Min->setFocusPolicy(Qt::NoFocus);horizontalLayout->addWidget(btnMenu_Min);btnMenu_Max = new QPushButton(widget_menu);btnMenu_Max->setObjectName(QString::fromUtf8("btnMenu_Max"));sizePolicy3.setHeightForWidth(btnMenu_Max->sizePolicy().hasHeightForWidth());btnMenu_Max->setSizePolicy(sizePolicy3);btnMenu_Max->setMinimumSize(QSize(30, 0));btnMenu_Max->setMaximumSize(QSize(30, 16777215));btnMenu_Max->setCursor(QCursor(Qt::ArrowCursor));btnMenu_Max->setFocusPolicy(Qt::NoFocus);horizontalLayout->addWidget(btnMenu_Max);btnMenu_Close = new QPushButton(widget_menu);btnMenu_Close->setObjectName(QString::fromUtf8("btnMenu_Close"));sizePolicy3.setHeightForWidth(btnMenu_Close->sizePolicy().hasHeightForWidth());btnMenu_Close->setSizePolicy(sizePolicy3);btnMenu_Close->setMinimumSize(QSize(30, 0));btnMenu_Close->setMaximumSize(QSize(30, 16777215));btnMenu_Close->setCursor(QCursor(Qt::ArrowCursor));btnMenu_Close->setFocusPolicy(Qt::NoFocus);horizontalLayout->addWidget(btnMenu_Close);horizontalLayout4->addWidget(widget_menu);verticalLayout2->addWidget(widget_title);widget = new QWidget(widgetMain);widget->setObjectName(QString::fromUtf8("widget"));verticalLayout3 = new QVBoxLayout(widget);verticalLayout3->setSpacing(0);verticalLayout3->setContentsMargins(11, 11, 11, 11);verticalLayout3->setObjectName(QString::fromUtf8("verticalLayout3"));verticalLayout3->setContentsMargins(0, 0, 0, 0);verticalLayout2->addWidget(widget);verticalLayout1->addWidget(widgetMain);connect(this->btnMenu_Min, SIGNAL(clicked(bool)), this, SLOT(on_btnMenu_Min_clicked()));connect(this->btnMenu_Max, SIGNAL(clicked(bool)), this, SLOT(on_btnMenu_Max_clicked()));connect(this->btnMenu_Close, SIGNAL(clicked(bool)), this, SLOT(on_btnMenu_Close_clicked()));
}void QUIWidget::initForm()
{//设置图形字体setIcon(QUIWidget::Lab_Ico, QChar(0xf099), 11); //设置左上角图标-图形字体setIcon(QUIWidget::BtnMenu, 0xf0d7);setIcon(QUIWidget::BtnMenu_Min, 0xf068);setIcon(QUIWidget::BtnMenu_Max, 0xf067);setIcon(QUIWidget::BtnMenu_Close, 0xf00d);//设置标题及对齐方式setTitle("QUI Demo");setAlignment(Qt::AlignLeft | Qt::AlignVCenter);setVisible(QUIWidget::BtnMenu, false);mainWidget = 0;max = false;location = this->geometry();this->setProperty("form", true);this->setWindowFlags(Qt::FramelessWindowHint | Qt::WindowSystemMenuHint | Qt::WindowMinMaxButtonsHint);//第一个参数是设置无边框。第二个参数是允许任务栏按钮右键菜单,第三个参数是允许最小化最大化与还原this->setWindowFlags(Qt::FramelessWindowHint);//任务栏单击图标后最小化后图标会消失,无法解决,因此不让最小化//绑定事件过滤器监听鼠标移动this->installEventFilter(this);this->widget_title->installEventFilter(this);//添加换肤菜单QStringList name;name << "银色" << "蓝色" << "浅蓝色" << "深蓝色" << "灰色" << "浅灰色" << "深灰色" << "黑色"<< "浅黑色" << "深黑色" << "PS黑色" << "黑色扁平" << "白色扁平" << "浅紫色" << "橙色";foreach (QString str, name) {QAction *action = new QAction(str, this);this->btnMenu->addAction(action);connect(action, SIGNAL(triggered(bool)), this, SLOT(changeStyle()));}
}

2.功能函数

具体设置部件图标、设置部件图片、设置部件是否可见、设置标题栏高度、设置按钮统一宽度、设置标题及文本样式等等设置,代码如下。

void QUIWidget::setIcon(QUIWidget::Widget widget, QChar str, quint32 size)
{if (widget == QUIWidget::Lab_Ico) {IconHelper::Instance()->setIcon(this->lab_Ico, str, size);} else if (widget == QUIWidget::BtnMenu) {IconHelper::Instance()->setIcon(this->btnMenu, str, size);} else if (widget == QUIWidget::BtnMenu_Min) {IconHelper::Instance()->setIcon(this->btnMenu_Min, str, size);} else if (widget == QUIWidget::BtnMenu_Max) {IconHelper::Instance()->setIcon(this->btnMenu_Max, str, size);} else if (widget == QUIWidget::BtnMenu_Close) {IconHelper::Instance()->setIcon(this->btnMenu_Close, str, size);}
}void QUIWidget::setPixmap(QUIWidget::Widget widget, const QString &file, const QSize &size)
{QPixmap pix = QPixmap(file);//按照宽高比自动缩放pix = pix.scaled(size, Qt::KeepAspectRatio);if (widget == QUIWidget::Lab_Ico) {this->lab_Ico->setPixmap(pix);} else if (widget == QUIWidget::BtnMenu) {this->btnMenu->setIcon(QIcon(file));} else if (widget == QUIWidget::BtnMenu_Min) {this->btnMenu_Min->setIcon(QIcon(file));} else if (widget == QUIWidget::BtnMenu_Max) {this->btnMenu_Max->setIcon(QIcon(file));} else if (widget == QUIWidget::BtnMenu_Close) {this->btnMenu_Close->setIcon(QIcon(file));}
}void QUIWidget::setVisible(QUIWidget::Widget widget, bool visible)
{if (widget == QUIWidget::Lab_Ico) {this->lab_Ico->setVisible(visible);} else if (widget == QUIWidget::BtnMenu) {this->btnMenu->setVisible(visible);} else if (widget == QUIWidget::BtnMenu_Min) {this->btnMenu_Min->setVisible(visible);} else if (widget == QUIWidget::BtnMenu_Max) {this->btnMenu_Max->setVisible(visible);} else if (widget == QUIWidget::BtnMenu_Close) {this->btnMenu_Close->setVisible(visible);}
}void QUIWidget::setOnlyCloseBtn()
{this->btnMenu->setVisible(false);this->btnMenu_Min->setVisible(false);this->btnMenu_Max->setVisible(false);
}void QUIWidget::setTitleHeight(int height)
{this->widget_title->setFixedHeight(height);
}void QUIWidget::setBtnWidth(int width)
{this->lab_Ico->setFixedWidth(width);this->btnMenu->setFixedWidth(width);this->btnMenu_Min->setFixedWidth(width);this->btnMenu_Max->setFixedWidth(width);this->btnMenu_Close->setFixedWidth(width);
}void QUIWidget::setTitle(const QString &title)
{if (this->title != title) {this->title = title;this->lab_Title->setText(title);this->setWindowTitle(this->lab_Title->text());}
}void QUIWidget::setAlignment(Qt::Alignment alignment)
{if (this->alignment != alignment) {this->alignment = alignment;this->lab_Title->setAlignment(alignment);}
}void QUIWidget::on_btnMenu_Min_clicked()
{this->showMinimized();
}void QUIWidget::on_btnMenu_Max_clicked()
{if (max) {this->setGeometry(location);} else {location = this->geometry();this->setGeometry(qApp->desktop()->availableGeometry());}max = !max;
}void QUIWidget::on_btnMenu_Close_clicked()
{close();
}

3.窗口关联

打开主函数文件,在Main函数上将主窗口(用户)链接到标题栏窗口上,关联起来,代码如下。

int main(int argc, char *argv[])
{QApplication a(argc, argv);//定义用户窗口MainWindow *w = new MainWindow;//定义标题栏QUIWidget qui;//将主窗体链接/关联到标题栏窗口上qui.setMainWidget(w);qui.show();return a.exec();
}
void QUIWidget::setMainWidget(QWidget *mainWidget)
{//一个QUI窗体对象只能设置一个主窗体if (this->mainWidget == 0) {//将子窗体添加到布局this->widget->layout()->addWidget(mainWidget);//自动设置大小resize(mainWidget->width(), mainWidget->height() + this->widget_title->height());this->mainWidget = mainWidget;this->mainWidget->installEventFilter(this);}
}

4.使用标题栏类

因为在Main函数上,已经将主窗体链接/关联到标题栏窗口上,因此在用户层无需关注/设置标题栏相关参数。这里和方式一不一样的是,方式是将标题栏类作为主窗口的私有变量使用,而方式二只是将两个窗口界面关联起来而已。

至此方式二讲解完毕,该种方式有个缺点是需要手鲁UI设计代码。更快捷的是,通过可视化界面UI来设计标题栏,然后绑定/链接到主窗口界面上,跟方式一一样。


总结

博文中相应的工程代码Qt-Case.zip 利用Qt开发软件进行编的例程,为博文提供案例-CSDN文库。


http://www.ppmy.cn/embedded/147202.html

相关文章

AQS源码学习

一、park/unpark阻塞唤醒线程 LockSupport是JDK中用来实现线程阻塞和唤醒的工具。使用它可以在任何场合使线程阻塞&#xff0c;可以指定任何线程进行唤醒&#xff0c;并且不用担心阻塞和唤醒操作的顺序&#xff0c;但要注意连续多次唤醒的效果和一次唤醒是一样的。JDK并发包下…

安全删除硬件并弹出媒体(弹出显卡)问题处理

“安全删除硬件”图标点开之后&#xff0c;出现弹出显卡问题 解决办法&#xff1a; 本人是华硕笔记本电脑&#xff0c;这仅仅是我的解决办法&#xff0c;仅供参考&#xff01; 更新/安装“触控板驱动程序” 以下以华硕为例&#xff1a; 去电脑官网下&#xff0c;下载“触控…

【双指针】算法题(一)

【双指针】算法题&#xff08;一&#xff09; 前言&#xff1a; 本章只有两道算法题&#xff1a;移动零、复写零。 常见的双指针有两种形式&#xff0c;一种是对撞指针&#xff0c;一种是左右指针。 **对撞指针&#xff1a;**一般用于顺序结构中&#xff0c;也称左右指针。 …

【Python使用】嘿马头条项目从到完整开发教程第9篇:缓存,1 缓存穿透【附代码文档】

本教程的知识点为:简介 1. 内容 2. 目标 产品效果 ToutiaoWeb虚拟机使用说明 数据库 理解ORM 作用 思考&#xff1a; 使用ORM的方式选择 数据库 SQLAlchemy操作 1 新增 2 查询 all() 数据库 分布式ID 1 方案选择 2 头条 使用雪花算法 &#xff08;代码 toutiao-backend/common/…

【中标麒麟服务器操作系统实例分享】java应用DNS解析异常分析及处理

了解更多银河麒麟操作系统全新产品&#xff0c;请点击访问 麒麟软件产品专区&#xff1a;https://product.kylinos.cn 开发者专区&#xff1a;https://developer.kylinos.cn 文档中心&#xff1a;https://documentkylinos.cn 情况描述 中标麒麟服务器操作系统V7运行在 ARM虚…

无管理员权限 LCU auth-token、port 获取(全网首发 go)

一&#xff1a; 提要&#xff1a; 参考项目&#xff1a; https://github.com/Zzaphkiel/Seraphine 想做一个 lol 查战绩的软件&#xff0c;并且满足自己的需求&#xff08;把混子和大爹都表示出来&#xff09;&#xff0c;做的第一步就是获取 lcu token &#xff0c;网上清一色…

白话java设计模式

创建模式 单例模式&#xff08;Singleton Pattern&#xff09;&#xff1a; 就是一次创建多次使用&#xff0c;它的对象不会重复创建&#xff0c;可以全局来共享状态。 工厂模式&#xff08;Factory Method Pattern&#xff09;&#xff1a; 可以通过接口来进行实例化创建&a…

快速在远程服务器执行命令、批量在多个服务器执行命令(基于sshpass的自定义脚本fastsh)

在日常服务器操作中&#xff0c;很多时候我们需要同时操作多个服务器。特别对于那些每个服务器都需要操作相同命令的场景&#xff0c;不断的切换命令会话窗口会比较麻烦。基于此&#xff0c;编写了本文中的 fastsh 脚本用于轻度解决这种问题&#xff0c;提高一定的便利性。 使…