求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Modeler   Code  
会员   
要资料
 
追随技术信仰

随时听讲座
每天看新闻
 
 
Qt 编程指南
第1章 Qt 开发环境
1.1 Qt 介绍
1.2 Qt 下载
1.3 Qt 在 Windows 下安装
1.4 Qt 在 Linux 下安装
1.5 认识开发工具
1.6 常见的名词术语
第2章 从Hello World开始
2.1 Hello World
2.2 Hello Qt
2.3 Hello Designer
2.4 Hello Creator
2.5 Qt程序调试
2.6 Qt帮助文档
第3章 字符串和字符编码
3.1 字符编码方式
3.2 Qt 程序字符编码
3.3 使用 QString
3.4 使用 QByteArray
第4章 信号和槽函数
4.1 元对象系统
4.2 使用原有的信号和槽
4.3 自定义信号和槽
4.4 系统属性
4.5 扩展阅读:ui_*.h代码
4.6 扩展阅读:moc_*.cpp代码
第5章 简单控件的使用
5.1 按钮类的控件
5.2 单行编辑控件
5.3 丰富文本编辑控件
5.4 其他输入控件
5.5 显示类的控件
5.6 Qt 资源文件
第6章 控件布局
6.1 传统窗口调整技术
6.2 水平和垂直布局器
6.3 网格布局器
6.4 表单布局器
6.5 控件尺寸调整策略
6.6 分裂器
第7章 文件和数据流
7.1 文件系统概览
7.2 基本文件读写QFile
7.3 文本流QTextStream
7.4 串行化数据流QDataStream
7.5 其他文件操作类
第8章 基于条目的控件
8.1 列表控件
8.2 表格控件
8.3 树形控件
8.4 基于条目控件的自定义特性
 

 
目录
基于条目控件的自定义特性
24 次浏览
2次  

本节介绍基于条目控件的定制特性,首先介绍条目的拖拽,列表控件、表格控件、树形控件内置了支持拖拽的特性,添加少许代码即可使用。然后介绍控件的右键菜单构造方 法,采用的方法是修改基类 QWidget 的 contextMenuPolicy属性,并添加弹出菜单槽函数,这种方法对于所有 QWidget 派生类控件都通用。最后简要介绍基于条目控件的样式表设置,定制高亮条目显示、双色交替行显示、表头和角按钮配色等。三个小节分别设置一个示例,学以致用。

8.4.1 条目的拖拽(Drag and Drop)

QListWidget、QTableWidget、 QTreeWidget 自带了拖拽条目的功能,拖拽通常有两种应用场景:

第一种场景是内部拖拽:

仅在控件内部使用,用于调整条目的先后顺序,实现控件内的条目移动功能。对于树形条目,内部移动还可以改变条目的父子关系,比如将子节点提升为兄弟节点。实现 控件内部拖拽只需要一句代码,设置拖拽模式为 QAbstractItemView::InternalMove,即

ui->treeWidget->setDragDropMode(QAbstractItemView::InternalMove);

 

对于列表控件、表格控件、树形控件,都是调用一样的函数 setDragDropMode(QAbstractItemView::InternalMove)。QAbstractItemView::InternalMove 只会在控件内部移动条目,不会复制条目,仅仅调整排列顺序或层级。

拖拽的第二种场景,就是跨界拖拽:

在控件之间拖拽,比如将 listWidget1 的条目 A 拖给 listWidget2,这种拖拽效果是复制一个新的条目A 给 listWidget2,不是移动,而是条目新建和数据复制。结果就是 listWidget1有自己的 A 条目, listWidget2 创建了新条目,新条目数据克隆自 listWidget1的 A 条目。这种模式也支持 listWidget1 的条目自己拖给自己,就是克隆新的条目A ,添加给 listWidget1 自身。不仅是同类型的条目控件之间可以拖拽,不同类型如QListWidget、QTableWidget、 QTreeWidget ,它们三者之间也可以互相拖拽条目。效果都是复制源条目,将新建的条目添加给接收拖拽的控件。

为基于条目的控件启用跨界拖拽功能,需要进行如下设置:

1)设置控件的 dragEnabled 属性为 true;

2)设置控件的视口 viewport() 的 acceptDrops 属性为 true;

3)为用户显示拖拽动作的鼠标效果,设置控件的 showDropIndicator属性为true;

4)设置控件的拖拽模式为能拖能拽的 QAbstractItemView::DragDrop。

拖拽条目一般都是单选模式,只拖拽一个。(其实多选模式可以拖拽多个,就是不太常见。)

对于单选模式拖拽条目,示例代码如下:

QListWidget *listWidget= new QListWidget(this);

listWidget->setSelectionMode(QAbstractItemView::SingleSelection); //单选模式

listWidget->setDragEnabled(true); //可以拖出源条目

listWidget->viewport()->setAcceptDrops(true); //可以接收拖入

listWidget->setDropIndicatorShown(true); //启用拖拽的显示效果

listWidget->setDragDropMode(QAbstractItemView::DragDrop); //使用能拖能拽的模式

 

对于基于条目的三种控件,内部拖拽是条目移动,跨界拖拽是条目新建并复制。我们下面通过简单示例学习拖拽功能。

打开 QtCreator,新建一个 Qt Widgets Application 项目,在新建项目的向导里填写:

①项目名称 itemdragdrop,创建路径 D:\QtProjects\ch08,点击下一步;

②套件选择里面选择全部套件,点击下一步;

③基类选择 QWidget,点击下一步;

④项目管理不修改,点击完成。

我们打开 widget.ui 界面文件,按照下图拖入控件:

第一行是列表控件,默认对象名 listWidget;

第二行是树形控件,默认对象名 treeWidget;

第三行是表格控件,默认对象名 tableWidget;

第四行是两个单选按钮,“内部拖拽”radioButtonInter、“跨界拖拽”radioButtonOuter,第四行使用水平布局。

窗口整体使用垂直布局,尺寸 500*500 。

我们右击单选按钮,为两个单选按钮分别添加槽函数,选第二个 clicked(bool) :

三个条目控件的内容用代码编写,图形界面设置就到这,我们下面开始编辑头文件 widget.h的代码:

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QListWidget>//列表控件
#include <QTreeWidget>//树形控件
#include <QTableWidget>//表格控件

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();

private slots:
    void on_radioButtonInter_clicked(bool checked);

    void on_radioButtonOuter_clicked(bool checked);

private:
    Ui::Widget *ui;
    //设置 QAbstractItemView 派生类的跨界拖拽功能
    //对列表控件、树形控件、表格控件通用,C++多态性
    void SetOuterDragDrop( QAbstractItemView *view );

};

#endif // WIDGET_H

 

widget.h 添加了三个条目控件类的头文件引用;

然后是两个单选按钮的槽函数;

最后添加了一个设置启用跨界拖拽功能的函数,这个函数对三种条目控件都是通用的。

下面来看 widget.cpp源文件的内容,首先是头文件包含、构造函数、析构函数:

#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    //构造列表控件的条目
    for(int i=0; i<5; i++)
    {
        QListWidgetItem *itemL = new QListWidgetItem( ui->listWidget );
        itemL->setText( tr("listItem %1").arg(i) );
    }

    //设置树形控件2列
    ui->treeWidget->setColumnCount( 2 );
    //各列均匀拉伸
    ui->treeWidget->header()->setSectionResizeMode(QHeaderView::Stretch);
    //树形控件构造条目
    for(int i=0; i<5; i++)
    {
        QTreeWidgetItem *itemT = new QTreeWidgetItem( ui->treeWidget );
        itemT->setText(0, tr("treeItem %1, 0").arg(i) );
        itemT->setText(1, tr("t%1, 1").arg(i) );
    }

    //设置表格 3*3
    ui->tableWidget->setColumnCount( 3 );
    ui->tableWidget->setRowCount( 3 );
    //各列均匀拉伸
    ui->tableWidget->horizontalHeader()->setSectionResizeMode( QHeaderView::Stretch );
    //构造表格条目
    for(int i=0; i<3; i++)
    {
        for(int j=0; j<3; j++)
        {
            QTableWidgetItem *itemTA = new QTableWidgetItem();
            itemTA->setText( tr("tableItem %1, %2").arg(i).arg(j) );
            ui->tableWidget->setItem( i, j, itemTA );
        }
    }

    //默认选中内部移动模式
    ui->radioButtonInter->setChecked(true);
    on_radioButtonInter_clicked(true);//启用内部移动
}

Widget::~Widget()
{
    delete ui;
}

 

构造函数首先为列表控件新建了 5 个条目,按照行编号设置条目文本。

然后设置树形控件的列数为 2,设置树头视图使得各列均匀拉伸;

为树形控件新建了 5 个顶级条目,根据行号、列号设置 5 个条目的文本,每个条目 2 列文本。

然后设置表格为 3 行 3 列,设置水平表头使得各列均匀拉伸;

为表格新建 3*3 个条目,每个条目根据行号、列号设置文本。

最后设置“内部拖拽”单选按钮为选中状态,并调用该按钮槽函数设置三个条目控件为内部拖拽模式。

接下来看看“内部拖拽”单选按钮的槽函数:

void Widget::on_radioButtonInter_clicked(bool checked)
{
    if(checked)
    {
        //列表控件启用内部移动
        ui->listWidget->setDragDropMode(QAbstractItemView::InternalMove);
        //树形控件启用内部移动
        ui->treeWidget->setDragDropMode(QAbstractItemView::InternalMove);
        //表格控件启用内部移动
        ui->tableWidget->setDragDropMode(QAbstractItemView::InternalMove);
    }
}

 

这个函数非常简单,判断该按钮如果为选中状态,那么调用三个条目控件的 setDragDropMode()函数设置拖拽模式为内部移动。

下面看看“跨界拖拽”单选按钮的槽函数:

void Widget::on_radioButtonOuter_clicked(bool checked)
{
    if(checked)
    {
        //列表控件启用跨界拖拽
        SetOuterDragDrop(ui->listWidget);
        //树形控件启用跨界拖拽
        SetOuterDragDrop(ui->treeWidget);
        //表格控件启用跨界拖拽
        SetOuterDragDrop(ui->tableWidget);
    }
}

 

这个函数也是超级简单的,检查按钮如果为选中状态,那就为三个条目控件调用 SetOuterDragDrop()函数。

实际干活的是下面的 SetOuterDragDrop() 函数:

//启用跨界拖拽
void Widget::SetOuterDragDrop(QAbstractItemView *view)
{
    view->setSelectionMode(QAbstractItemView::SingleSelection); //单选模式
    view->setDragEnabled(true);  //可以拖出源条目
    view->viewport()->setAcceptDrops(true); //视口可以接收拖入
    view->setDropIndicatorShown(true); //启用拖拽的显示效果
    view->setDragDropMode(QAbstractItemView::DragDrop); //使用能拖能拽的模式
}

 

这个函数对 QAbstractItemView 派生类对象是通用的,QListWidget、QTableWidget、 QTreeWidget 都是它的派生类(孙辈的派生类)。该函数第一句是设置选中模式为单选;第二句是设置可以拖出源条目;第三句是设置控件的视口(显示可见部分)可以接收条目拖入;第四句是显示拖拽的鼠标效果;最后是设置拖拽模式为 QAbstractItemView::DragDrop,能够拖出和拽入。

通过单独的 SetOuterDragDrop() 函数设置跨界拖拽,避免为每个条目控件都敲五局代码,省了很多事,以后不管几个条目控件,都只需要调用SetOuterDragDrop() 函数就能设置跨界拖拽了。

本示例代码讲解到这,下面构建运行该程序,程序启动时效果如下图:

测试内部拖拽功能,对三个控件内部进行拖拽:

列表控件的内部拖拽只改变条目的先后顺序;树形控件的内部拖拽不仅可以改变先后顺序,也可以改变层级关系;

测试表格控件内部拖拽,将(0,0)单元格条目拖到(0,1)单元格后,(0,0)单元格被清空,

(0,1)单元格填上了 "tableItem 0,0" 文本,表格控件的内部拖拽相当于剪切+粘贴,会清空源单元格,覆盖目的单元格。

我们点击“跨界拖拽”按钮,进入跨界拖动的模式:

我们把树形控件的条目"treeItem 4,0 t4,1" 拖到列表控件和表格控件里面的底部空白位置,可以看到进入列表控件后被拆成两个条目,占据列表控件的末尾两行;拖给表格控件后,拆成两列,是两个单元格条目。

示例中列表控件只有 1 列,树形控件 2 列,表格控件 3 列,它们之间是可以互相拖入条目的,但是一般不建议列数不同的控件互相拖拽条目。这里只是测试功能,实际程序中要尽量在列数相同的情况下拖拽。读者还可以进行其他拖拽测试,我们下面小节开始讲解右键菜单的创建,这个知识稍微提前了一点讲解,但是很实用。

8.4.2 自定义右键菜单(ContextMenu)

右键菜单功能很常见,我们添加槽函数的操作就是通过右键菜单实现的。在 Qt 库中,每一条菜单项称为QAction,多条菜单项组成一个菜单,菜单类是 QMenu。菜单项 QAction 不仅可以用于菜单,工具栏的按钮也是QAction 实现的。QAction 常用构造函数如下:

QAction(const QString & text, QObject * parent)

QAction(const QIcon & icon, const QString & text, QObject * parent)

 

菜单项可以有文本和图标,第一个构造函数只有文本,第二个构造函数同时带了图标和文本。菜单项一般直接以窗口为parent,方便全局管理。

用户点击菜单项触发如下信号:

void QAction::triggered(bool checked = false) //点击菜单项触发信号

 

参数里 checked 一般用不到,只有在设置菜单项可以勾选的时候才用。菜单项设置勾选功能是通过下面函数实现:

void QAction::setCheckable(bool)

 

单独的菜单项是不能弹出显示的,需要将菜单项添加到菜单或工具栏才能使用。菜单 QMenu 的构造函数如下:

QMenu(QWidget * parent = 0)

QMenu(const QString & title, QWidget * parent = 0)

 

菜单本身可以有一个文本,比如 QtCreator 拥有多个菜单,第一个是“文件”菜单,“文件”两个字就是菜单的文本,如下图所示:

通常情况下,由多个 QAction 组成一个 QMenu,然后由多个 QMenu 组成一个QMenuBar。

QtCreator 有 8 个 QMenu 菜单,共同组成一行 QMenuBar(菜单条,或叫菜单栏),QMenuBar以后到了主窗口程序章节再详细讲解,本小节只是初步学一下右键菜单功能。

右键菜单通常不显示菜单自身的文本,只会弹出各个菜单项。创建了 QAction 对象后,将其添加到菜单的函数如下:

void QMenu::addAction(QAction * action)

 

QMenu 还有其他 方便添加菜单项的函数,这里不一一列举了,以后主窗口程序章节会详细讲解这个类。

QMenu 的基类是 QWidget,本身也属于一个控件,弹出菜单一般使用如下两个函数:

void QMenu::popup(const QPoint & p, QAction * atAction = 0) //异步弹出菜单

QAction * QMenu::​exec(const QPoint & p, QAction * action = 0)//同步弹出菜单

这两个函数弹出菜单的效果是一样的,唯一的差别是 exec() 函数会返回被用户点击的菜单项指针,如果用户没有点击菜单项,那么返回NULL。

这两个函数参数也是一样的,第一个是菜单显示位置的坐标 p,注意 p 是以屏幕左上角为原点(0,0),而控件反馈的坐标一般是相对控件自己的内部坐标,需要用转换函数**widget->mapToGlobal( p ) ,将 p 转换为屏幕坐标。第二个参数action 是指显示菜单时保证菜单项 action 恰好显示在 p 点位置,方便用户优先选择action 菜单项。

QWidget 派生类控件都支持自定义右键菜单,但是默认没有启用这个功能,要启用这个功能,需要进行两步操作:

(1)设置控件的 contextMenuPolicy 属性数值为 Qt::CustomContextMenu,即自定义右键菜单模式;

(2)为请求弹出右键菜单的信号添加槽函数,该信号原型为:

void QWidget::customContextMenuRequested(const QPoint & pos) //pos 是相对控件自身的坐标,需要转为全局屏幕坐标弹菜单

为请求右键菜单信号添加槽函数后,在槽函数里执行菜单的 popup() 或 exec() 就能弹出菜单了。

菜单类 QMenu 主要是显示用途,而实际的功能是通过菜单项 QAction 的槽函数实现,一般每个QAction 都对应一个槽函数,该槽函数关联到 QAction::triggered() 信号。

下面我们通过一个示例,为列表控件添加自定义的菜单,实现添加、编辑、删除条目和清空所有条目的功能。

打开 QtCreator,新建一个 Qt Widgets Application 项目,在新建项目的向导里填写:

①项目名称 contextmenu,创建路径 D:\QtProjects\ch08,点击下一步;

②套件选择里面选择全部套件,点击下一步;

③基类选择 QWidget,点击下一步;

④项目管理不修改,点击完成。

我们打开 widget.ui 界面文件,按照下图拖入控件:

第一行是标签,文本为“请用右键菜单操作:”;

第二行是列表控件,默认对象名 listWidget 。

窗口整体使用垂直布局,尺寸 400*300 。本例子的功能都使用代码实现,下面首先编辑 widget.h 文件代码:

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QListWidget>//列表控件
#include <QMenu>//菜单
#include <QAction>//菜单项

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();

    //添加槽函数
public slots:
    //弹出右键菜单的槽函数
    void onCustomContextMenuRequested(const QPoint & pos);
    //添加条目菜单项的槽函数
    void onAddItemTriggered();
    //编辑条目菜单项的槽函数
    void onEditItemTriggered();
    //删除条目菜单项的槽函数
    void onDelItemTriggered();
    //清空所有条目的菜单项槽函数
    void onClearAllTriggered();

private:
    Ui::Widget *ui;
    //保存右键菜单的指针
    QMenu *m_menuContext;
    //创建菜单并关联信号和槽函数
    void CreateMenu();
};

#endif // WIDGET_H

 

widget.h 文件先添加了列表控件、菜单、菜单项的头文件包含;

在窗口类里面手动添加了 public slots,总共五个槽函数:

第一个槽函数用于关联控件的请求弹出右键菜单信号;

后面四个槽函数对应四个菜单项的点击信号;

最后添加了菜单指针 m_menuContext,以及创建右键菜单、关联信号和槽的函数 CreateMenu()。

下面来分段查看源文件 widget.cpp 代码,首先是头文件包含、构造函数、析构函数:

#include "widget.h"
#include "ui_widget.h"
#include <QMessageBox>
#include <QDebug>

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    //创建菜单,并关联信号和槽函数
    CreateMenu();
}

Widget::~Widget()
{
    delete ui;
}

 

widget.cpp 添加了消息框和调试打印头文件,构造函数只添加了一句 CreateMenu()函数调用。该函数具体代码如下:

void Widget::CreateMenu()
{
    //创建右键菜单对象
    m_menuContext = new QMenu(tr("ContextMenu")); //右键菜单其实不显示ContextMenu文本

    //创建“添加条目”菜单项并添加到菜单
    QAction *actAdd = new QAction(tr("添加条目"), this);
    m_menuContext->addAction( actAdd );
    //创建“编辑条目”菜单项并添加到菜单
    QAction *actEdit = new QAction(tr("编辑条目"), this);
    m_menuContext->addAction( actEdit );
    //创建“删除条目”菜单项并添加到菜单
    QAction *actDel = new QAction(tr("删除条目"), this);
    m_menuContext->addAction( actDel );
    //创建“清空所有”菜单项并添加到菜单
    QAction *actClearAll = new QAction(tr("清空所有"), this);
    m_menuContext->addAction( actClearAll );

    //设置列表控件可以有自定义右键菜单
    ui->listWidget->setContextMenuPolicy( Qt::CustomContextMenu );
    //关联弹出菜单信号
    connect(ui->listWidget, SIGNAL(customContextMenuRequested(QPoint)),
            this, SLOT(onCustomContextMenuRequested(QPoint)) );

    //为四个菜单项关联点击信号到槽函数
    connect(actAdd, SIGNAL(triggered()), this, SLOT(onAddItemTriggered()));
    connect(actEdit, SIGNAL(triggered()), this, SLOT(onEditItemTriggered()));
    connect(actDel, SIGNAL(triggered()), this, SLOT(onDelItemTriggered()));
    connect(actClearAll, SIGNAL(triggered()), this, SLOT(onClearAllTriggered()));
    //创建完毕
    return;
}

 

该函数首先创建菜单对象,保存到成员变量 m_menuContext;

然后分别创建了四个菜单项“添加条目”、“编辑条目”、“删除条目”、“清空所有”,并添加给菜单m_menuContext;

接着设置列表控件的右键菜单策略为 Qt::CustomContextMenu ,即使用自定义的右键菜单;

关联 listWidget 请求弹出右键菜单的信号到槽函数;

最后将四个菜单项的点击信号关联到对应的槽函数上。

下面来看弹出右键菜单的槽函数代码:

//弹出右键菜单的槽函数
void Widget::onCustomContextMenuRequested(const QPoint & pos)
{
    //控件内的相对坐标转为屏幕坐标
    //是列表控件发出的信号,就用列表控件的转换函数
    QPoint screenPos = ui->listWidget->mapToGlobal( pos );
    //弹出菜单
    QAction *actRet = m_menuContext->exec( screenPos );
    if(NULL != actRet)//检查非空才能使用该指针
    {
        qDebug()<<tr("返回的菜单项:") + actRet->text();
    }
}

 

由于弹菜单的请求是由列表控件发出的,所以我们首先在该函数里使用列表控件的转换函数 mapToGlobal(),

将参数里的 pos 转为屏幕绝对坐标的 screenPos ;

然后调用菜单的 exec() 函数显示右键菜单;

m_menuContext->exec( screenPos ) 函数的返回值是用户点击的菜单项指针,如果没有点击就返回NULL,

我们对返回值进行非空判断,对于非空指针打印菜单项的文本。

上面的槽函数仅仅是显示出右键菜单,并不具有实际的操作功能,实际的功能是通过四个菜单项关联的槽函数逐个实现的。

下面来看第一个“添加条目”菜单项的槽函数:

//添加条目菜单项的槽函数
void Widget::onAddItemTriggered()
{
    QListWidgetItem *itemNew = new QListWidgetItem(tr("新建条目"));
    //设置可以编辑
    itemNew->setFlags( itemNew->flags() | Qt::ItemIsEditable );
    //添加给控件
    ui->listWidget->addItem( itemNew );
    //设置新条目为选中的条目
    ui->listWidget->setCurrentItem( itemNew );
    //显示条目的编辑框
    ui->listWidget->editItem( itemNew );
}

 

该函数新建一个条目,默认文本“新建条目”,设置条目的双击可编辑标志位;

添加条目给列表控件,并设置新条目为当前选中状态;

最后自动开启新条目的编辑框,方便用户直接编辑新条目的文本。

因为条目设置了双击可编辑标志位,用户以后也可以双击编辑,而不需要使用单独的编辑控件来设置文本。

第二个是“编辑条目”菜单项的槽函数:

//编辑条目菜单项的槽函数
void Widget::onEditItemTriggered()
{
    //获取选中的条目
    QListWidgetItem *curItem = ui->listWidget->currentItem();
    if(NULL == curItem)
    {
        qDebug()<<tr("没有选中的条目。");
        return; //返回
    }
    //设置选中条目可以编辑
    curItem->setFlags( curItem->flags() | Qt::ItemIsEditable );
    //显示选中条目的编辑框
    ui->listWidget->editItem( curItem );
}

 

该函数首先获取选中的条目,如果没有选中的条目就打印信息并返回。

如果有选中的条目,设置该条目为双击可编辑状态,并显示该条目的编辑框,这样用户就可以修改条目文本了。

当用户点击其他位置,该条目的编辑框失去焦点时,列表控件自动关闭条目的编辑框。

第三个是“删除条目”菜单项的槽函数:

//删除条目菜单项的槽函数
void Widget::onDelItemTriggered()
{
    //获取选中的条目
    QListWidgetItem *curItem = ui->listWidget->currentItem();
    if(NULL == curItem)
    {
        qDebug()<<tr("没有选中的条目。");
        return; //返回
    }
    //删除条目
    delete curItem; curItem = NULL;
}

 

该函数比较简单,获取当前选中的条目,如果指针为空不处理;

如果指针不空,那么删除该条目,并将指针置空。

最后是“清空所有”菜单项的槽函数:

//清空所有条目的菜单项槽函数
void Widget::onClearAllTriggered()
{
    //判断条目个数,如果没条目不需要操作,直接返回
    int nCount = ui->listWidget->count();
    if(nCount < 1)
    {
        return;
    }
    //提示Yes、No询问消息框,获取返回值,防止用户误操作全删
    //如果用户选“Yes”就全删,否则不操作
    int buttonRet = QMessageBox::question(this, tr("清空所有"), tr("请确认是否清空所有条目?"));
    if( QMessageBox::Yes == buttonRet )//用户选择了“Yes”
    {
        ui->listWidget->clear();
    }
    else //否则不处理
    {
        return;
    }
}

 

该函数首先获取列表控件的条目数量,如果没有条目就不处理,直接返回。

如果有条目,那么先弹出询问对话框 QMessageBox::question() ,

询问对话框默认是 Yes 和 No 两个按钮,用户点击任意一个按钮都会返回该按钮的常量值,

Yes 按钮对应的数值就是 QMessageBox::Yes。

当用户点击了 Yes 按钮时,说明用户确认要清空所有条目,那么执行清空操作;

如果用户点击了 No或者两个都不点击,直接关闭询问对话框,那么不处理,保留所有条目。

这个函数针对用户可能的误操作做了询问处理,防止用户误操作导致所有条目都被删除。

一般删除全部内容之类的操作,程序都要弹出询问对话框,提醒用户确认一下是否真的全部删除。

如果不进行询问,用户误操作把几个小时编辑的内容全删了,那样用户就抓狂了。

这个示例的代码讲解到这,我们构建运行示例,通过右键菜单添加几个条目,然后测试“清空所有”菜单项的功能:

如果点击 Yes 按钮就真的全清空了,如果点击 No 或者不点击这两个按钮,直接点右上角 X 关闭对话框,那么条目都会保留。

读者还可以测试其他菜单项功能,这里不截图了。我们下面小节对基于条目的控件简单设置几个样式表,定制一下外观。

8.4.3 基于条目控件的样式表(Style Sheet)

本小节以表格控件为例,介绍基于条目的控件常用的样式表设置,涵盖表格控件各个部分的定制显示。

(1)表格整体的前景色、背景色

一般所有的控件都有前景色、背景色,设置方法都是一样的,样式表代码举例:

color: darkblue;

background-color: cyan;

 

color 就是前景色,上面设置为深蓝色;background-color 就是背景色,上面设置为青色。

(2)双色交替行的设置

如果希望表格奇偶行的颜色不同,两种颜色交替显示行的背景色,那么需要开启双色交替行的显示:

tableWidget->setAlternatingRowColors(true ); //这是C++代码

 

序号为偶数的行使用默认背景 background-color 颜色填充,序号为奇数的行使用alternate-background-color 。

一般不用修改默认背景色,只修改交替背景色即可以,比如样式表代码:

alternate-background-color: skyblue;

 

这样就是白色背景行与天蓝色背景行交替显示。如果不设置样式表,默认交替的 alternate-background-color是浅灰色的。

(3)高亮选中条目的颜色设置

高亮条目单独有前景色和背景色可以设置,样式表举例:

selection-color: red;

selection-background-color: yellow;

 

selection-color 就是高亮条目前景色,上面样式表就是红色文本;selection-background-color是高亮条目的背景色,上面举例的样式表就是黄色背景填充。

(4)表格的网格线颜色设置

列表控件和树形控件都没有网格线,只有表格控件有网格线,样式表举例:

gridline-color: darkgreen;

 

上面就是将网格线设置成 深绿色。

(5)表头的颜色设置

树形控件和表格控件都有头部,头部的本质是 QHeaderView 类,我们设置该类的前景色、背景色就能修改表头配色,样式表举例:

QHeaderView{
    color: darkblue;
    background-color: cyan;
}

 

注意要用类名 QHeaderView 和大括号把前景色、背景色的样式表文本包裹起来,这样限定修改表头的配色,而不会影响表格控件整体的配色。

(6)角按钮的颜色设置

表格左上角有个特殊的角按钮,点击它会选中表格全部内容,比如下图左上角红色的部分就是表格的角按钮:

角按钮既不属于水平表头,也不属于垂直表头,而是单独的类 QTableCornerButton。这个角按钮没有函数可以获取它的指针,但是可以配置它的显示颜色。

设置角按钮的配色,需要同时设置背景色和边框的颜色,如果不设置边框颜色,在有些窗口主题里面会看不到角按钮的背景色效果。设置角按钮配色的样式表举例:

QTableCornerButton::section {
    background: red;
    border: 2px outset red;
}

 

上面设置角按钮的背景色为红色,边框宽度2像素,边框也是红色。

(7)配置所有条目的颜色

基于条目的控件都可以配置条目颜色,通过 **Widget::item 设置条目配色,样式表举例:

QTableWidget::item{
    color: darkblue;
    background-color: cyan;
}

 

这会配置所有条目的前景色为深蓝,即文字颜色,背景色用青色填充。注意,这个条目配色会覆盖掉双色交替行、高亮选中条目的配色,一般不要单独设置 ::item 颜色,那样会看不到高亮选中的颜色了。

(8)配置滚动条的颜色

滚动条的类是 QScrollBar,我们对该类设置前景色和背景色即可,样式表举例:

QScrollBar{
    color: yellow;
    background-color: green;
}

 

上面配置滚动条用绿色背景填充,然后用前景色显示滚动条两端的三角形箭头颜色,如下图所示:

(9)加与不加 类名大括号 的区别

我们设置表格控件的前景色和背景色时,可以有两种写法,第一种不加类名和大括号:

color: red;

background-color: yellow;

 

如果直接把上面文本设置为表格控件样式表,那么显示效果如下:

可以看到表格控件内嵌的所有子控件颜色全部改变了,无论是角按钮、水平表头、垂直表头、单元格、水平滚动条、垂直滚动条等等都变色了。不加类名和大括号时,样式表对表格控件所有组成部分都生效。

如果我们用类名 QTableWidget 和大括号包裹住样式表文本,比如:

QTableWidget{
    color: red;
    background-color: yellow;
}

 

将该文本设置为表格控件的样式表,显示效果如下:

这次角按钮、两个表头、两个滚动条都没有变色。颜色改变的只有单元格以及右下角空白位置、狭缝等部位。

我们这里将角按钮、两个表头、两个滚动条称为表格控件的子控件区域;

单元格、右下角空白位置、狭缝等称为表格控件的直辖区域。

将样式表文本用类名和大括号包裹之后,样式就仅对表格控件的直辖区域生效,而子控件区域不会生效。子控件区域一般有自己的样式表定制方式,就是用子控件的类名和大括号包裹,在大括号里配置子控件颜色。

在配置控件颜色时,可以使用类名大括号包裹的方式,也可以不用类名大括号包裹,根据显示效果需求和用户爱好来定。

如果需要一体化配色,那么就不需要类名大括号包裹;如果希望子控件区域和直辖区域分别配色,那么就用类名大括号包裹样式表,注意子控件区域的配色总是需要子控件类名和大括号包裹。

另外说明一下:如果表格控件配置了整体的前景色和背景色,子控件也单独配置了前景色和背景色,那么在该子控件区域内优先使用单独配置的子控件颜色,即子控件配置优先。对于同一个颜色配置项,比如第一次指定前景色为红色,第二次指定前景色为蓝色,那么后配置的蓝色生效,即对同一颜色配置项,后配置的优先。

本小节样式表的内容介绍到这,下面通过一个简单例子测试几个样式表配色。

打开 QtCreator,新建一个 Qt Widgets Application 项目,在新建项目的向导里填写:

①项目名称 tablestyle,创建路径 D:\QtProjects\ch08,点击下一步;

②套件选择里面选择全部套件,点击下一步;

③基类选择 QWidget,点击下一步;

④项目管理不修改,点击完成。

我们打开 widget.ui 界面文件,按照下图拖入控件:

第一行是表格控件,默认对象名 tableWidget;

第二行和第三行总共六个按钮,文本对象名分别为:

“双色交替行”pushButtonAlternatingRowColors、“选中条目定制”pushButtonSelectionCustom、“所有条目定制”pushButtonItemCustom,

“角按钮定制”pushButtonCornerButtonCustom,“表头定制”pushButtonHeaderCustom、“清空样式表”pushButtonClearStyle;

六个按钮使用网格布局,窗口整体使用垂直布局,窗口尺寸 440*330 。

布局完成后,我们为六个按钮逐一添加槽函数:

本例子的功能都用代码实现,首先是头文件 widget.h 的代码:

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();

private slots:
    void on_pushButtonAlternatingRowColors_clicked();

    void on_pushButtonSelectionCustom_clicked();

    void on_pushButtonItemCustom_clicked();

    void on_pushButtonCornerButtonCustom_clicked();

    void on_pushButtonHeaderCustom_clicked();

    void on_pushButtonClearStyle_clicked();

private:
    Ui::Widget *ui;
};

#endif // WIDGET_H

 

六个按钮的槽函数是通过右键菜单添加的,文件代码没有手动添加代码,保持原样即可。

下面分段来看源文件 widget.cpp 的代码,首先是头文件包含、构造函数、析构函数:

#include "widget.h"
#include "ui_widget.h"
#include <QDebug>

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    //设置行列 4*4
    ui->tableWidget->setColumnCount(4);
    ui->tableWidget->setRowCount(4);
    //设置表格水平头,各列均匀分布
    ui->tableWidget->horizontalHeader()->setSectionResizeMode( QHeaderView::Stretch );
    //新建表格条目
    for(int i=0; i<4; i++)
    {
        for(int j=0; j<4; j++)
        {
            //新建条目,并根据行号、列号设置文本
            QTableWidgetItem *itemNew = new QTableWidgetItem();
            itemNew->setText( tr("tableItem %1, %2").arg(i).arg(j) );
            ui->tableWidget->setItem( i, j, itemNew );
        }
    }
    //构建完毕
}

Widget::~Widget()
{
    delete ui;
}

 

构造函数里首先设置表格为 4 列 4 行,设置水平表头,使得表格各列均匀拉伸;

然后用两重循环为表格创建条目,条目的文本根据行号、列号设置。

下面来看第一个按钮“双色交替行”对应的槽函数:

//本例子都用类名和大括号包住里面的内容
void Widget::on_pushButtonAlternatingRowColors_clicked()
{
    //启用双色交替行显示
    ui->tableWidget->setAlternatingRowColors( true );
    //定制样式表,交替行采用天蓝色,表格的网格线用深绿色
    QString strStyle = " QTableWidget{ alternate-background-color: skyblue; "
                    "gridline-color: darkgreen; } " ;
    //添加给表格控件,旧的样式表保留
    ui->tableWidget->setStyleSheet( ui->tableWidget->styleSheet() + strStyle );
}

 

该函数首先设置表格控件为双色交替行显示;

然后设置样式表的文本,设置交替行的颜色为天蓝色,设置网格线的颜色为深绿;

最后把文本设置为表格的样式表,并且用字符串拼接保留了控件原有旧的样式表。

本小节的例子都用类名和大括号包住样式表,因为后面需要对子控件进行定制。

如果样式表需要设置多次或子控件需要定制,那么一般建议用类名和大括号包裹样式表。

下面来看第二个按钮“选中条目定制”对应的槽函数:

void Widget::on_pushButtonSelectionCustom_clicked()
{
    //selection-color 是选中条目的前景色
    //selection-background-color 是选中条目的背景色
    QString strStyle = " QTableWidget{ selection-color: red; "
            "selection-background-color: yellow; } ";
    //添加给表格控件,旧的样式表保留
    ui->tableWidget->setStyleSheet( ui->tableWidget->styleSheet() + strStyle );
    //设置当前条目为高亮色
    QTableWidgetItem *curItem = ui->tableWidget->currentItem();
    if(NULL != curItem)
    {
        curItem->setSelected(true); //标上选中的高亮色
    }
}

 

该函数先设置样式表文本,选中高亮条目的前景色为红色(文字颜色),背景用黄色填充;

然后将文本设置为表格控件的样式表,并保留了旧的样式表;

最后获取当前条目(带虚线框的条目),如果非空,就将该条目设置为选中状态(虚框+高亮颜色),方便直接查看设置的选中高亮条目效果。

下面来看第三个按钮“所有条目定制”对应的槽函数:

void Widget::on_pushButtonItemCustom_clicked()
{
    // QTableWidget::item 就是所有条目的样式表配置
    // color 是前景色,background-color 是背景色
    QString strStyle = " QTableWidget::item{ "
            "color: blue; "
            "background-color: lightgreen; "
            "} " ;
    //设置给表格控件,QTableWidget::item 样式表与前面两个函数的样式表冲突
    ui->tableWidget->setStyleSheet( ui->tableWidget->styleSheet() + strStyle );
}

 

该函数比较简单,设置样式表文本,然后将文本设置为表格控件的样式表,注意 QTableWidget::item 样式表会与前面两个函数的样式表效果有冲突,实际运行时 QTableWidget::item 样式表会覆盖前面的双色交替行、选中高亮颜色的配置。

下面来看第四个按钮“角按钮定制”对应的槽函数:

void Widget::on_pushButtonCornerButtonCustom_clicked()
{
    // QTableCornerButton::section 就是设置表格左上角的按钮风格
    QString strStyle =  " QTableCornerButton::section{ "
       " background: green;  "
       " border: 2px outset green; "
       "} " ;
    //添加给表格控件,旧的样式表保留
    ui->tableWidget->setStyleSheet( ui->tableWidget->styleSheet() + strStyle );
}

 

该函数设置样式表文本,注意角按钮的样式表需要同时设置背景色和边框颜色,保证角按钮能显示出配置的颜色;

然后把文本设置为表格控件的样式表,并保留之前旧的样式表效果。

下面来看第五个按钮“表头定制”对应的槽函数:

void Widget::on_pushButtonHeaderCustom_clicked()
{
    //QHeaderView 就是表头的类,定制该类的样式表
    //前景色背景色的一般都是 color   background-color
    QString strStyle = " QHeaderView{ "
            "color: darkblue; "
            "background-color: cyan; "
            "} " ;
    //添加给表格控件,旧的样式表保留
    ui->tableWidget->setStyleSheet( ui->tableWidget->styleSheet() + strStyle );
}

 

该函数也是设置样式表文本,然后把文本设置为表格控件的样式表,并保留之前旧的样式表效果。

表格的水平表头和垂直表头都是 QHeaderView 类,该样式表对两个表头都管用。

下面来看第六个按钮“清空样式表”对应的槽函数:

void Widget::on_pushButtonClearStyle_clicked()
{
    //打印旧的样式表信息
    qDebug()<<"old style sheets: \r\n"<<ui->tableWidget->styleSheet()<<endl;
    //置空样式表
    ui->tableWidget->setStyleSheet("");
    //取消双色交替行的显示
    ui->tableWidget->setAlternatingRowColors( false );
}

 

这个清空函数打印了原本的样式表,然后将表格控件的样式表设置为空文本,顺便将双色交替行的显示效果关了。

示例的代码讲解到这,我们构建运行该例子,点击程序左边四个按钮,看到效果:

双色交替行、选中高亮颜色、角按钮、表头的颜色均变成我们代码里设置的颜色了。

这时候如果我们点击“所有条目定制”按钮,看到下图效果:

双色交替行和选中高亮色的效果都没了,全被 QTableWidget::item 样式表覆盖了。一般不要轻易配置 QTableWidget::item 样式表颜色,会与双色交替行和高亮选中色冲突。

本节和本章的内容到这,我们下一章讲解数据容器,比如列表、链表、向量、集合、映射等等,就是程序常用的数据结构类。图形程序内部也经常使用各种数据结构,数据结构是程序开发的基础。使用合适的数据结构,能够为程序的开发提供便利。

 


您可以捐助,支持我们的公益事业。

1元 10元 50元





认证码: 验证码,看不清楚?请点击刷新验证码 必填



24 次浏览
2次