引言
实现下面这样一个功能,点击界面的添加按钮,增加一行班级和学生的信息,刚才被点击行的按钮上的文字由添加变为删除,按钮文字为删除,点击的时候可以删除被点击行的所有控件,选中那个班级后会自动更新对应班级的学生列表,当添加的控件行太多,会自动出现垂直滚动条。
实现
下面附上完整的代码,该程序,采用了样式来设置了控件的外观,qss文件一起附上。
程序的编译环境:msvc2017 64bit,使用的QtCreate5.13.2。
程序的结构:
创建基于QDialog的项目,然后向其中添加文件,其它不多说,下面看具体实现。
main.cpp
#include "instructioneditdialog.h"
#include <QApplication>
#include <QFile>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QFile file(":/new/prefix1/myStyle.qss");
if (file.open(QIODevice::ReadOnly)) {
QString strText = file.readAll();
a.setStyleSheet(strText);
file.close();
}
InstructionEditDialog w;
w.show();
return a.exec();
}
defineData.h
#ifndef DEFINEDATA_H
#define DEFINEDATA_H
#include <QString>
typedef struct schoolSituation
{
QString className;//屏幕组号
QString studentsName;//场景id
}ST_SCHOOL;
typedef ST_SCHOOL stuSchoolSituation;
#endif // DEFINEDATA_H
deleteaddbtn.h
#ifndef DELETEADDBTN_H
#define DELETEADDBTN_H
#include <QPushButton>
/*****************************
功能描述:
1.按钮文字为添加,发送添加命令行,
2.按钮文字为删除,发送删除命令行。
*******************************/
class DeleteAddBtn : public QPushButton
{
Q_OBJECT
public:
explicit DeleteAddBtn(int id, QWidget *parent = nullptr);
~DeleteAddBtn();
protected:
void mousePressEvent(QMouseEvent *event);//点击添加文字的按钮,发送添加指令信号,点击删除按钮,发送删除被点击行的控件信号
void paintEvent(QPaintEvent *event);//设置样式
signals:
void sigAddConstructionCtrl();//添加命令行控件
void sigDelConstructionCtrl(int &id);//删除被点击行的命令行控件,id-被点击按钮的id
private:
int m_id;//按钮的id
};
#endif // DELETEADDBTN_H
deleteaddbtn.cpp
#include "deleteaddbtn.h"
#include <QMouseEvent>
#include <QStyleOption>
#include <QPainter>
DeleteAddBtn::DeleteAddBtn(int id,QWidget *parent)
:QPushButton(parent)
{
m_id = id;
setAttribute(Qt::WA_StyledBackground);//设置样式生效
}
DeleteAddBtn::~DeleteAddBtn()
{
}
void DeleteAddBtn::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton) {
if (text() == QStringLiteral("添加")) {
setText(QStringLiteral("删除"));
emit sigAddConstructionCtrl();
}else if (text() == QStringLiteral("删除")) {
emit sigDelConstructionCtrl(m_id);
}
}
// QPushButton::mousePressEvent(event);//删除所在行控件后,会继续回到此处,但是此时该类已经被析构了
}
void DeleteAddBtn::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event);
QStyleOption opt;
opt.init(this);
QPainter p(this);
style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
QPushButton::paintEvent(event);
}
specificorderform.h
#ifndef SPECIFICORDERFORM_H
#define SPECIFICORDERFORM_H
#include "defineData.h"
#include <QWidget>
#include <QMap>
/*******************************
功能描述:
1.设置班级下拉列表为指定的列表,
2.根据班级列表中选中的值,设置学生的列表,
2.设置combox默认初始选中值,
3.获取combox选中的值。
********************************/
namespace Ui {
class SpecificOrderForm;
}
class SpecificOrderForm : public QWidget
{
Q_OBJECT
public:
explicit SpecificOrderForm(QWidget *parent = nullptr);
~SpecificOrderForm();
stuSchoolSituation getOrderChoosedText();//获取指令选中的sid,sceneId
protected:
void initComboBoxList();//初始化下拉列表的参数
void setComboxList();//设置下拉列表
private slots:
void on_classComboBox_currentTextChanged(const QString &arg1);//sid选中后,sceneid列表对应调整(sid没有变,sceneid也不变其列表)
private:
Ui::SpecificOrderForm *ui;
QStringList m_studentStrList;//场景id列表
QMap<QString,QStringList> m_classStudentsMap;//sid和scenid映射表
};
#endif // SPECIFICORDERFORM_H
specificorderform.cpp
#include "specificorderform.h"
#include "ui_specificorderform.h"
#include <QStyledItemDelegate>
#include <QList>
SpecificOrderForm::SpecificOrderForm(QWidget *parent) :
QWidget(parent),
ui(new Ui::SpecificOrderForm)
{
ui->setupUi(this);
initComboBoxList();
setComboxList();
QStyledItemDelegate *delegate = new QStyledItemDelegate();//下拉项的高度起作用,似乎没有作用
ui->classComboBox->setItemDelegate(delegate);
ui->studentComboBox->setItemDelegate(delegate);
setWindowFlags(Qt::FramelessWindowHint);
setAttribute(Qt::WA_TranslucentBackground);
}
SpecificOrderForm::~SpecificOrderForm()
{
delete ui;
}
stuSchoolSituation SpecificOrderForm::getOrderChoosedText()
{
stuSchoolSituation tempClass;
tempClass.className = ui->classComboBox->currentText();
tempClass.studentsName = ui->studentComboBox->currentText();
return tempClass;
}
void SpecificOrderForm::initComboBoxList()
{
QStringList students;
students.append(QStringLiteral("胡姬"));
students.append(QStringLiteral("张三"));
students.append(QStringLiteral("李虎"));
students.append(QStringLiteral("王宁"));
students.append(QStringLiteral("紫荆"));
students.append(QStringLiteral("梅斯卡通"));
m_classStudentsMap.insert(QStringLiteral("一班"),students);
students.clear();
students.append(QStringLiteral("胡微姬"));
students.append(QStringLiteral("张上三"));
students.append(QStringLiteral("李虎上"));
students.append(QStringLiteral("王宁"));
students.append(QStringLiteral("紫荆史蒂夫"));
students.append(QStringLiteral("梅斯卡通"));
m_classStudentsMap.insert(QStringLiteral("二班"),students);
students.clear();
students.append(QStringLiteral("阿叔"));
students.append(QStringLiteral("神盾局弟"));
students.append(QStringLiteral("电视剧"));
students.append(QStringLiteral("四道口"));
students.append(QStringLiteral("圣诞节"));
students.append(QStringLiteral("可颂"));
m_classStudentsMap.insert(QStringLiteral("三班"),students);
}
void SpecificOrderForm::setComboxList()
{
QStringList classStrList;
QList<QString> classList = m_classStudentsMap.keys();
for (int j = 0 ; j < classList.size() ; ++j) {
QString strClass = classList[j];
classStrList.append(strClass);
}
ui->classComboBox->addItems(classStrList);
if (classStrList.size() != 0) {
ui->classComboBox->setCurrentText(classStrList.at(0));
}
}
void SpecificOrderForm::on_classComboBox_currentTextChanged(const QString &arg1)
{
if (m_studentStrList.size() != 0) {
m_studentStrList.clear();
ui->studentComboBox->clear();
}
QMap<QString,QStringList>::iterator it;
for (it = m_classStudentsMap.begin() ; it != m_classStudentsMap.end() ;++it) {
if (arg1 == it.key()) {
m_studentStrList = it.value();
}
}
ui->studentComboBox->addItems(m_studentStrList);
if (m_studentStrList.size() != 0) {
ui->studentComboBox->setCurrentText(m_studentStrList.at(0));
}
}
instructioneditdialog.h
#ifndef INSTRUCTIONEDITDIALOG_H
#define INSTRUCTIONEDITDIALOG_H
#include "specificorderform.h"
#include "deleteaddbtn.h"
#include <QDialog>
#include <QQueue>
#include <QMap>
#include <QVBoxLayout>
/*****************************
功能描述:
1.添加命令行控件,
2.删除命令行,
3.获取编辑的按钮名字和选中的命令集,
4.记录并更新创建的指令行控件的id,
5.调整命令行控件的布局,“太多”增加滚动条,
6.设置按钮的样式。
*******************************/
typedef struct cmdCtrl
{
SpecificOrderForm *order;//指令
DeleteAddBtn *btn;//指令后的按钮
}ST_CMDCTRL;
typedef ST_CMDCTRL stuCmdCtrl;
namespace Ui {
class InstructionEditDialog;
}
class InstructionEditDialog : public QDialog
{
Q_OBJECT
public:
explicit InstructionEditDialog(QWidget *parent = nullptr);
~InstructionEditDialog();
protected:
void setVBlayout();//设置滚动区域的垂直布局
void createCommandLine();//创建命令行
void adjustLayoutHeight(int num);//调整布局的高度
void setPushBtnStyle(DeleteAddBtn *delBtn);//设置添加/删除按钮的样式
private slots:
void on_confirmBtn_clicked();//获取名称和被添加的指令
void on_cancelBtn_clicked();//清空行编辑器,窗口关闭
void onDelConstructionCtrl(int &id);//删除对应id的命令行控件
signals:
void sigNameInstructionSet(QString &name,QList<stuSchoolSituation> &cmdSet);//发送按钮名称,sid和sceneid的指令集
private:
Ui::InstructionEditDialog *ui;
SpecificOrderForm *m_orderForm;//命令行控件
DeleteAddBtn *m_pushBtn;//删除添加按钮
QQueue<int> m_deletedId;//保存删除的指令行id,当其中不为空,创建指令行从队列中取id,直到为空重新从上一次的id增加
int m_lastId;//保存上一次的id
int m_id;//创建指令行的id,也为按钮id
int m_count;//记录当前的指令行数目
const int m_vSpacing;//控件间垂直间距
bool m_isRecount;//指令行按钮的id是否重新计数
QVBoxLayout *m_vLayout;//滚动区域的垂直布局
QMap<int,stuCmdCtrl> m_orderMap;//保存指令控件,键-id,值-id对应的指令和按钮
};
#endif // INSTRUCTIONEDITDIALOG_H
instructioneditdialog.cpp
#include "instructioneditdialog.h"
#include "ui_instructioneditdialog.h"
InstructionEditDialog::InstructionEditDialog(QWidget *parent) :
QDialog(parent),m_vSpacing(5),
ui(new Ui::InstructionEditDialog)
{
ui->setupUi(this);
m_id = 0;
m_count = 0;
m_lastId = 0;
m_isRecount = false;
setVBlayout();
createCommandLine();
setWindowFlag(Qt::FramelessWindowHint);
setAttribute(Qt::WA_TranslucentBackground);
}
InstructionEditDialog::~InstructionEditDialog()
{
delete ui;
}
void InstructionEditDialog::setVBlayout()
{
QWidget *widget = ui->scrollAreaWidgetContents;
m_vLayout = new QVBoxLayout(widget);
m_vLayout->setSpacing(m_vSpacing);
m_vLayout->setContentsMargins(5,5,5,5);
}
void InstructionEditDialog::adjustLayoutHeight(int num)
{
int height = num * 30 + (num + 1) * m_vSpacing;//30为每行控件的高度
ui->scrollAreaWidgetContents->setFixedHeight(height);
}
void InstructionEditDialog::setPushBtnStyle(DeleteAddBtn *delBtn)
{
QString strStyle;
strStyle = "DeleteAddBtn{background-color:#040f60;color:#FFFFFF;"
"font-size: 18px;font-family: Microsoft YaHei;}"
"DeleteAddBtn:pressed{background-color:#071fbd;}";
delBtn->setStyleSheet(strStyle);
}
void InstructionEditDialog::createCommandLine()
{
++m_count;
if (m_deletedId.size() != 0) {
m_id = m_deletedId.front();//first()
m_deletedId.removeFirst();
if (m_deletedId.count() == 0) {
m_isRecount = true;
}
}else {
if (m_isRecount) {
m_id = m_lastId;
}
++m_id;
m_lastId = m_id;
}
m_orderForm = new SpecificOrderForm(ui->scrollAreaWidgetContents);
m_pushBtn = new DeleteAddBtn(m_id,ui->scrollAreaWidgetContents);
m_pushBtn->setText(QStringLiteral("添加"));
setPushBtnStyle(m_pushBtn);
connect(m_pushBtn,&DeleteAddBtn::sigAddConstructionCtrl,this,&InstructionEditDialog::createCommandLine);
connect(m_pushBtn,&DeleteAddBtn::sigDelConstructionCtrl,this,&InstructionEditDialog::onDelConstructionCtrl);
QHBoxLayout *layout = new QHBoxLayout;
layout->addWidget(m_orderForm);
layout->addWidget(m_pushBtn);
m_vLayout->addLayout(layout);
adjustLayoutHeight(m_count);
stuCmdCtrl tempOrder;
tempOrder.order = m_orderForm;
tempOrder.btn = m_pushBtn;
m_orderMap.insert(m_id,tempOrder);
}
void InstructionEditDialog::on_confirmBtn_clicked()
{
QString strName = ui->namelineEdit->text();
QList<stuSchoolSituation> sidSceneList;
QMap<int,stuCmdCtrl>::iterator it;
for (it = m_orderMap.begin() ; it != m_orderMap.end() ; ++it) {
stuCmdCtrl tempCtrl = it.value();
stuSchoolSituation tempSidMap = tempCtrl.order->getOrderChoosedText();
sidSceneList.append(tempSidMap);
}
emit sigNameInstructionSet(strName,sidSceneList);
accept();
}
void InstructionEditDialog::on_cancelBtn_clicked()
{
ui->namelineEdit->clear();
reject();
}
void InstructionEditDialog::onDelConstructionCtrl(int &id)
{
if (m_deletedId.size() == 0) {
m_isRecount = false;
}
QMap<int,stuCmdCtrl>::iterator it;
for (it = m_orderMap.begin() ; it != m_orderMap.end() ; ++it) {
if (id == it.key()) {
m_deletedId.append(id);//删除后添加会出现id已被释放
stuCmdCtrl tempCtrl;
tempCtrl = it.value();
delete tempCtrl.order;
delete tempCtrl.btn;
m_orderMap.erase(it);
break;
}
}
--m_count;
adjustLayoutHeight(m_count);
}
instructioneditdialog.ui
specificorderform.ui
下面是样式文件:
/*学生情况编辑框背景*/
QWidget#SpecificOrderForm
{
background-color:transparent;
border:none;
}
/*指令行指令标签*/
QWidget#SpecificOrderForm>QLabel#label,QLabel#label_2,QLabel#label_3
{
font-size: 18px;
font-family: Microsoft YaHei;
font-weight: 400;
color: #FFFFFF;
}
/*班级和学生列表*/
QWidget#SpecificOrderForm>QComboBox#classComboBox,QComboBox#studentComboBox
{
background-color: #121650;
border:2px solid #0b54f0;
font-size: 14px;
font-family: Microsoft YaHei;
font-weight: 400;
color: #FFFFFF;
}
/*下拉列表框*/
QWidget#SpecificOrderForm>QComboBox QAbstractItemView
{
background-color:#55557f;
outline: 1px solid #ffaa7f; /*选定项的虚框*/
selection-background-color:#121650;
color:#FFFFFF;
}
/*下拉列表想的高度*/
QWidget#SpecificOrderForm>QComboBox QAbstractItemView::item
{
height: 20px;
}
/*选中项*/
QWidget#SpecificOrderForm>QComboBox QAbstractItemView::item:selected
{
background-color:#3c3cb6;
}
/*指令编辑框*/
QDialog#InstructionEditDialog>QWidget#widget
{
background-color:#062977;
border:2px solid #0b54f0;
}
/*按钮名称标签*/
QWidget#widget>QLabel#label
{
font-size: 18px;
font-family: Microsoft YaHei;
font-weight: 400;
color: #FFFFFF;
}
/*按钮名称*/
QWidget#widget>QLineEdit#namelineEdit
{
background-color:transparent;
border-radius:2px;
border:2px solid #0b54f0;
font-size: 16px;
font-family: Microsoft YaHei;
font-weight: 400;
color: #FFFFFF;
}
/*滚动区域*/
QWidget#widget>QScrollArea#scrollArea
{
border:none;
background-color:transparent;
}
/*滚动区域的垂直滚动条的主体,滑道*/
QWidget#widget>QScrollArea#scrollArea QScrollBar:vertical
{
width: 10px;/*滚动条宽度*/
background-color: transparent;
border-radius: 5px;
margin:0px,0px,0px,0px;
padding-top:0px;/*上、下箭头预留位置*/
padding-bottom:0px;
border:none;
}
/*上箭头背景*/
QWidget#widget>QScrollArea#scrollArea QScrollBar::add-line:vertical
{
background-color:transparent;
}
/*下箭头背景*/
QWidget#widget>QScrollArea#scrollArea QScrollBar::sub-line:vertical
{
background-color:transparent;
}
/*滑块下拉后,滑块上面的背景*/
QWidget#widget>QScrollArea#scrollArea QScrollBar::add-page:vertical
{
background-color:transparent;
}
/*滑块上拉后,滑块下面的背景*/
QWidget#widget>QScrollArea#scrollArea QScrollBar::sub-page:vertical
{
background-color:transparent;
}
/*滚动区域的滚动条的滑块*/
QWidget#widget>QScrollArea#scrollArea QScrollBar::handle:vertical
{
background-color: #5FC6DD;
opacity: 0.5;
border-radius: 5px;
width:10px;
min-height:20;
}
/*鼠标滑过滚动条*/
QWidget#widget>QScrollArea#scrollArea QScrollBar::handle:vertical:hover
{
background-color: #00FBFF;
opacity: 0.5;
border-radius: 5px;
}
/*滚动区域窗口*/
QScrollArea#scrollArea QWidget#scrollAreaWidgetContents
{
background-color:transparent;
border:none;
}
/*确定,取消按钮*/
QWidget#widget>QPushButton
{
background-color:transparent;
border: 1px solid #FFFFFF;
border-radius: 6px;
font-size: 14px;
font-family: Microsoft YaHei;
font-weight: 400;
color: #FFFFFF;
}
/*点击确定,取消按钮*/
QWidget#widget>QPushButton:pressed
{
background-color: #00FBFF;
opacity: 0.9;
border-radius: 6px;
font-size: 14px;
font-family: Microsoft YaHei;
font-weight: 400;
color: #000337;
}
以上便是完整的代码。其中需要记录一下便是,当按钮被点击后,发送删除信号,来删除包含滋生在内的控件,因为是在鼠标点击事件中发送的删除控件信号,槽函数接收到信号删除控件之后,其中包含发送删除信号的按钮,然后继续返回到被删按钮的鼠标点击事件中,继续往下执行代码,这里将QPushButton::mousePressEvent(event);这句注释掉,因为按钮此时已被释放了,执行这句后程序会崩溃。当然这里这么解释可能不是很准确,但是确实和发送删除信号的按钮已经被删除有关。