第四章 常用界面组件的使用

在Qt类库中,所有界面组件类的字节或间接父类都是QWidget.

QWidget的父类是QObject与QPaintDevice.所以QWidget是多重继承的类.

QObject支持元对象系统,其信号与槽机制为编程中对象间通信提供了极大便利.

QPaintDevice是能使用QPainter类在绘图设备上绘图的类.

所有从QWidget继承而来的界面组件被称为widget组件,他们是构成GUI应用程序的窗口界面基本元素.

界面组件可以从窗口系统接收鼠标事件,键盘事件和其他事件,然后在屏幕上绘制自己.

常用的界面组件

按钮类组件

按钮类继承关系:

  • QAbstractButton:抽象类,提供按钮共有特性
    • QPushButton:普通按钮
      • QCommandLinkButton:单选按钮,多个互斥项间选择.
    • QToolButton:工具按钮
    • QRadioButton:单选按钮
    • QCheckBox:复选框
  • QDialogButttonBox:复合组件类,可设置为多个按钮组合

输入类组件

输入类组件继承关系:

  • QComboBox:下拉列表框,也称组合框
    • QFontComboBox:字体下拉列表框,自动从系统获取字体
  • QLineEdit:编辑框,用于输入单行文字
  • QFrame:基本控件的基类
    • QAbstractScrollArea:抽象类
      • QTextEdit:文本编辑框,支持富文本格式,包括markdown.
      • QPlainTextEdit:纯文本编辑器,支持多段落纯文本.
  • QAbstractSpinBox:抽象类
    • QSpinBox:整数输入框,用于输入整数或离散型数据.
    • QDoubleSpinBox:浮点数输入框.
    • QDateTimeEdit:允许用户编辑日期与时间
      • QDateEdit:日期编辑框
      • QTimeEdit:时间编辑框
  • QAbstractSlider:抽象类
    • QDial:表盘,用于在设定的范围内输入和显示数值.
    • QScrollBar:卷滚条,用于在大的显示区域内滑动.
    • QSlider:滑动条,具有设定的数值范围.
  • QKeySequenceEdit:按键序列编辑器,一般用于处理快捷键

显示类组件

显示类组件继承关系:

  • QFrame:基本控件的基类
    • QLabel:标签,用于显示文字,图片等
    • QAbstractScrollArea:抽象类
      • QTextEdit:文本编辑框,支持富文本格式,包括markdown.
        • QTextBrowser:文本浏览器,用于显示富文本格式内容
      • QGraphicsView:图形视图组件,图形/视图架构中的视图组件
    • QLCDNumber:LCD数字显示组件,模仿LCD显示效果的组件
  • QCalendarWidget:日历组件,用于显示日历
  • QProgressBar:进度条,用于表示某个操作的进度
  • QOpenGLWidget:OpenGL显示组件,用于在Qt程序启动OpenGL图形
  • QQuickWidget:QML显示组件,用于自动加载QML文件

容器类组件

容器类组件继承关系:

  • QGroupBox:分组框,具有标题和边框的容器组件
  • QFrame:框架组件,是具有边框的界面组件的父类
    • QAbstractScrollArea:抽象类
      • QScrollArea:卷滚区域,具有水平和垂直滚动条的容器
      • QMdiArea:MDI工作区组件,在MDI应用程序中用于管理多文档窗口
    • QToolBox:工具箱,垂直方向的多页容器组件
    • QStackedWidget:堆叠多页组件,没有标签栏的多页组件
  • QTabWidget:带标签栏的多页组件
  • QDockWidget:停靠组件,可以停靠在QMainWindow窗口的停靠区域,也可以浮动
  • QAxWidget:ActiveX显示组件,用于显示ActiveX控件

Item组件

Item组件继承关系:

  • QAbstractItemView
    • QListView
      • QUndoView
      • QListWidget
    • QTreeView
      • QTreeWidget
    • QTableView
      • QTableWidget
    • QColumnView

Item Views组件大多是用于模型/视图结果,每一种视图组件需要相应的一种模型用于存储数据.

Item Widgets组件类是相应Item Views组件类的子类,它们直接使用项(item)存储数据,称为相应视图类的便利类(convenience class)

其他界面类

还有一些界面组件并没有出现在组件面板里,例如常用的菜单栏(QMenuBar类),菜单(QMenu类),工具栏(QToolBar类),状态栏(QStatusBar类)等组件.

QWidget类的主要属性和接口函数

QWidget作为界面组件时的属性

属性名称 属性值类型 功能
enabled bool 组件的使能状态
geometry QRect 组件的几何形状
sizePolicy QSizePolicy 组件默认的布局特性
minimumSize QSize 组件最小尺寸
maximumSize QSize 组件最大尺寸
palette QPalette 组件的调色板
font QFont 组件使用的字体
cursor QCursor 鼠标光标移到组件上的形状
mouseTracking bool 组件是否响应鼠标移动
tabletTracking bool 组件是否响应平板追踪
focusPolicy Qt::FocusPolicy 组件的焦点策略
contextMenuPolicy Qt::ContextMenuPolicy 组建的上下文菜单策略
acceptDrops bool 组件是否接收拖动来的其他对象
toolTip QString 鼠标移动到组件上时,显示简短提示
statusTip QString 鼠标移动到组件上时,主窗口状态栏显示提示文字
autoFillBackground bool 组件背景是否自动填充
styleSheet QString 组建的样式表

组件负责默认布局特性的sizePolicy是QSizePolicy类型,定义了组件在水平和垂直方向的尺寸变化策略.

为了访问组件的sizePolicy,应当通过其Widget内部的sizePolicy()方法访问.

QSizePolicy是一个枚举类型,因而有以下枚举常量:

  • QSizePolicy::Fixed:固定尺寸,组件大小不改变
  • QSizePolicy::Minimum:最小尺寸,使用sizeHint()的返回值或minimumuSize作为最小尺寸.
  • QSizePolicy::Maximum:最大尺寸,使用sizeHint()的返回值或maximumuSize作为最大尺寸.
  • QSizePolicy::Preferred:首选尺寸,组件仍然可以调整,但是放大时不会超过sizeHint()返回的尺寸.
  • QSizePolicy::Expanding:可扩展尺寸,组件可扩展.
  • QSizePolicy::MinimumExpanding:最小可扩展尺寸,综合了可扩展尺寸与最小尺寸.
  • QSizePolicy::Ignored:忽略尺寸,sizeHint()函数的返回值被忽略,组件占据尽可能大的空间.

在使用QSizePolicy的时候,QWidget的sizeHint()函数会起到很大作用.

在组件的父组件尺寸发生变化时,sizeHint()返回组件的建议尺寸.

一般不需要修改组件的sizePolicy属性,使用其默认值即可.

部分部件的尺寸策略还进一步细分为水平策略与垂直策略

水平延伸因子与垂直延伸因子都是整数值,其取值范围在0~255.

QWidget作为窗口时的属性

属性 属性值类型 功能
windowTitle QString 窗口标题栏的文字
windowIcon QIcon 窗口标题上的图表
windowOpacity qreal 窗口的不透明度(范围0.0~1.0)
windowFilePath QString 窗口相关的含路径文件名
windowModified bool 显示窗口内文档是否被修改(*)
windowModality Qt::WindowModality 窗口的模态,表示窗口是否在上层
windowFlags Qt::WindowFlags 窗口的标志,是其一些值的组合

QWidget作为独立的窗口时,实际上还有一些与窗口显示有关的共用槽函数.

名称 功能
close() 关闭窗口
hide() 隐藏窗口
show() 显示窗口
showFullScreen() 以全屏方式显示窗口
showMaximized() 窗口最大化
showMinimized() 窗口最小化
showNormal() 恢复正常窗口

布局管理

在Qt Designer的组件面板里有用于布局管理的两组组件,Layouts与Spacers.

QLayout继承自QObject与QLayoutItem.是Layouts中所有组件的基类.QLayout不是从QWidget继承来的.

从QLayout继承而来的几个类是常用的布局管理类:

  • QVBoxLayout:垂直布局
  • QHBoxLayout:水平布局
  • QGridLayout:网格布局,使组件按行与列网格状布局
  • QFormLayout:表单布局,与Grid相似,但只有两列
  • QStackedLayout:堆叠布局,用于管理多个页面

任何布局类对象在可视化设计时都有layoutLeftMargin,layoutTopMargin,layoutRightMargin和layoutBottomMargin这个4个边距属性用于设置布局组件与父容器的4个边距的最小值.

下面是一个调整边距属性值及layoutSpacing的例子:

Dialog::Dialog(QWidget *parent)
    : QDialog(parent)
{
    this->windowTitle().clear();
    this->setWindowTitle(QString("this is my window"));

    QVBoxLayout* total_layout;
    total_layout= new QVBoxLayout;

    QGroupBox *btn_group;
    btn_group= new QGroupBox(this);
    btn_group->setGeometry(QRect(260,310,341,43));

    QPushButton* btn_1,*btn_2,*btn_3;
    btn_1= new QPushButton("button_1",btn_group);
    btn_2= new QPushButton("button_2",btn_group);
    btn_3= new QPushButton("button_3",btn_group);

    QHBoxLayout* btn_layout;
    btn_layout= new QHBoxLayout;
    btn_layout->setSpacing(10);
    btn_layout->setContentsMargins(15,25,5,5);
    btn_layout->addWidget(btn_1);
    btn_layout->addWidget(btn_2);
    btn_layout->addWidget(btn_3);

    total_layout->addLayout(btn_layout);
    this->setLayout(total_layout);
}

在上面的例子中,所有的QPushButton都以QGroupBox为父对象.

使用QGroupBox::setGeometry()设置了组件的几何形状.

使用QHBoxLayout::setSpacing()设置了组件间的最小间距.

使用QHBoxLayout::setContentMargins()设置了四个边距的值.

网格布局

可视化设计网格布局时一般是在一个容器组件内先摆放组件,使各组件的位置和大小与期望的效果大致相同,然后点击工具栏上的网格布局按钮进行网格布局.

除了4个边距属性,网格布局还有几个特有的属性:

  • layoutHorizontalSpacing:水平方向上组件最小间距
  • layoutVerticalSpacing:垂直方向上组件最小间距
  • layoutRowStretch:各行的延展因子
  • layoutColumnStretch:各列的延展因子
  • layoutRowMinimumHeight:各行的最小高度,单位为像素
  • layoutColumnMinimumWidth:各列的最小宽度,单位为像素
  • layoutSizeConstraint:布局的尺寸限制方式

下面是应用网格布局应用的一个例子:

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    QGroupBox *groupBox=new QGroupBox(this);
    groupBox->setGeometry(QRect(290,160,200,230));

    QGridLayout *gridLayout=new QGridLayout(groupBox);
    gridLayout->setHorizontalSpacing(7);
    gridLayout->setVerticalSpacing(12);
    gridLayout->setContentsMargins(10,10,-1,-1);

    QPushButton *pushButton=new QPushButton(groupBox);
    gridLayout->addWidget(pushButton,0,0,1,1);
    QPushButton *pushButton_2=new QPushButton(groupBox);
    gridLayout->addWidget(pushButton_2,0,1,1,1);
    QComboBox *comboBox=new QComboBox(groupBox);
    comboBox->addItem(QString());
    gridLayout->addWidget(comboBox,1,0,1,2);
    QPlainTextEdit *plainTextEdit=new QPlainTextEdit(groupBox);
    gridLayout->addWidget(plainTextEdit,2,0,1,2);
    this->setLayout(gridLayout);
}


其中,下面的语句表示将表格添加至0行0列,占据1行1列的位置.

    gridLayout->addWidget(pushButton,0,0,1,1);

其他语句以此类推.

分割条布局

实现分割条功能的类是QSplitter,分隔条可以实现水平分割或垂直分割.一般是在两个可以自由改变大小的组件间分割.

分割条主要有以下几个属性:

  • orientation:方向,即水平分割或垂直分割
  • opaqueResize:如果值为true,则拖动分割条时,组件是动态改变大小的.
  • handleWidth:进行分割操作的拖动条的宽度,单位为像素.
  • childrenCollapsible:表示进行分割操作时,子组件大小是否可以为0.

下面是一个使用分割条的例子:

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    QSplitter *splitter=new QSplitter(this);
    splitter->setOrientation(Qt::Horizontal);
    splitter->setOpaqueResize(true);
    splitter->setHandleWidth(8);
    splitter->setMinimumSize(QSize(350,200));
    splitter->setChildrenCollapsible(true);

    QGroupBox *groupBox=new QGroupBox(splitter);
    groupBox->setMinimumSize(QSize(10,0));
    splitter->addWidget(groupBox);
    QPlainTextEdit *plainTextEdit;
    plainTextEdit=new QPlainTextEdit;
    splitter->addWidget(plainTextEdit);

}


QChar字符

QChar是一个类,用于处理Qt中的字符.可以将其理解为一个加强版的char.

QChar有很多接口函数,常用的有:

  • isDigital():是否为数字
  • isLetter():是否为字母
  • isLetterOrNumber():是否为字母或数字
  • isLower():是否为小写数字
  • isUpper():是否为大写数字
  • isMark():是否为记号
  • isNonCharacter():是否为非文字字符
  • isNull():是否为’\0′
  • isNumber():判断字符是否为一个数,包括①之类的序号
  • isPrint():判断字符是否为可打印字符
  • isPunct():判断字符是否为标点符号
  • isSpace():判断字符是否为分割符号,如空格,制表符
  • isSymbol():判断字符是否为符号
  • toLower():返回小写形式
  • toUpper():返回大写形式
  • (char16_t)unicode():返回16位编码数值

其中值得注意的是一个静态函数QChar::fromLatin1(),其用于将Latin1字符转换为QChar字符.

与之对应的,QChar有一个toLatin1()函数用于将QChar字符转换为Latin1字符.

提供一个使用例子:

QString str="String";
QChar ch=QChar::fromLatin1('Q');
str[0]=ch;

与之相似的,在QChar与UTF-16值转换方面有unicode()方法与QChar::fromUsc2(char16_t c)函数.

QString字符串

QString是Qt中的一个类,用于存储字符串.一般来说,Qt使用到字符串的地方都使用QString.

而一般的std::string与QString互转需要通过QString::fromStdString(str)与qstr.toStdString().

QString是由QChar组成的一个串,QChar使用的是UTF-16编码.

QString使用隐式共享来减少内存占用.这意味着只有在修改一个字符串的时候,这个字符串才会复制.

QString的创建与初始化

QString可以直接通过const char*初始化.

QString str="Hello World";

QString在被创建与初始化后,其存储的就是一个QChar的字符数组.可以对其进行索引.

QString str="Hello";
QChar ch0=str[0];

QString字符串常用操作

可以使用加法运算符进行字符串拼接.

QString str1="Hello ";
QString str2="World";
Qstring str3=str1+str2;

也可以通过append()在字符串后添加字符串,通过prepend()在字符串前添加字符串.

QString str1="igg";
str1.preend(QString("n"));
str1.append(QString("a")); 

front()返回第一个字符,back()返回最后一个字符.

left(n)返回前n个字符,right(n)返回后n个字符.注意:转义符视为一个字符处理.

mid(pos,n)返回字符串中的字符数,pos为起始位置,n为返回字符的个数.

sliced()与mid()功能相同,但是其不判断是否在边界内.

函数section()用于从字符串中提取sep作为分隔符,从start段到end段的字符串.

例如:

QString str1="学生姓名,男,2004-09-15,汉族,湖南",str2;
str2=str1.section(",",0,0);//学生姓名
str2=str1.section(",",1,1);//男
str2=str1.section(",",1,2);//男,2004-09-15
str2=str1.section(",",4,4);//湖南

isNull()和isEmpty()作用相似,但是isNull()会对’\0’进行判定,而isEmpty()判定’\0’为true.

QString str1="",str2;
str1.isNull();//false
str1.isEmpty();//true
str2.isNull();//true
str2.isEmpty();//true

count(),若带有参数,可以统计某个字符在字符串中出现的次数.若不带参数,count(),size()与length()都返回字符串长度.

clear()函数清空当前字符串,使字符串为NULL.

resize()用于改变字符串长度,但如果长度短于当前字符串则会截断;长度长于当前字符串则会导致未初始化的内存.

如果resize内含有参数,那么则使用该QChar填充字符串填充部分.

与resize()不同,函数fill()则将字符串中每个字符都用一个新字符替换.其同样可以通过参数调整字符串长度.

indexOf()与lastIndexOf()的功能是在字符串内查找某个字符串首次与最后一次出现的位置.

contains()判断当前字符串是否包含某个字符串.可通过Qt::CaseInsensitive表示是否分辨大小写.

endsWith()和startWith()判断字符串是否以某个字符串开头或结尾.

count()用于统计当前字符串中某个字符串出现的次数.可以设置Qt::CaseInsensitive是否分辨大小写.

函数toUpper()和toLower()用于将字符串内的字母全部转换为大写字母或小写字母.

trimmed()和simplified(),trimmed()会去掉字符串首尾的空格,函数simplified()不仅去掉首尾空格,还会将中间的连续空格用单个空格替换.

chop(n)去掉字符串末尾的n个字符,如果n大于实际意义,字符串内容就变为空.

insert(pos,str),函数insert用于在某个位置插入一个字符串.如果pos大于实际意义,字符串会自动补充空格填充.

replace(pos,n,after)用于从某个位置开始替换n个字符.

其还有一种形式replace(before,after)用于替换所有before字符串为after.可以指定Qt::CaseInsensitive分辨大小写.

remove(pos,n)的功能为从字符串pos开始的位置移出n个字符.若超出实际意义,则把pos后面的字符都移除.

同样的,其还有另一种参数模式,remove(ch),即移除字符串中某个字符出现的所有实例.

QString字符串与数值转换

QString有一些接口函数用于将字符串转换为整数:

参数ok用于获取返回值,表示转换是否成功.

  • toInt(bool* ok,int base=10)
  • toUInt(bool* ok,int base=10)
  • toLong(bool* ok,int base=10)
  • toUInt(bool* ok,int base=10)
  • toShort(bool* ok,int base=10)
  • toUShort(bool* ok,int base=10)
  • toLongLong(bool* ok,int base=10)
  • toULongLong(bool* ok,int base=10)

其也还有两个接口函数用于将字符串转换为浮点数:

  • toFloat(bool* ok)
  • toDouble(bool* ok)

函数setNum()则用于将整数或浮点数转换为字符串.

  • setNum(int n,int base=10)
  • setNum(float n,char format=’g’,int precision=6)
格式字符 格式字符含义 精度位数含义
e,E 科学计数法 基数小数点后位数
f 自然计数法 小数点后的有效位数
g,G 根据情况自动调节 小数点前后的数字位数之和

QString还有一个静态函数number(),其与setNum类似,但是是静态函数形式

  • QString::number(long n,int base)
  • QString::number(double n,char format,precision)

静态函数asprintf()用于构造格式化输出字符串.

  • QString QString::asprintf(const char cformat*,…)

但是注意,asprintf输出%s会导致乱码,只能使用UTF-8编码的const char*字符串来作为参数.

也就是说,QString型的字符串应当通过QString::data()来表现.

例如:

QString str1=QString::asprintf("Year=%d,Month=%02d",2024,04);
QString str2="哈尔滨工程大学 拓荒者学社"
QString str3=QString::asprintf("欢迎来到%s",str2.toLocal8Bit().data());
QString str4=QString::asprintf("PI=%.10f",M_PI);//3.1415926536

arg()函数是QString的成员函数,用于格式化输出各种数据的字符串,其功能与asprintf类似.

但是arg函数通过形式为%n的占位符来对应实参,这与一般printf不同.

int year=2024,month=2,day=23;
QString str=QString("%1年,%2月,%3日").arg(year).arg(month).arg(day);

QSpinBox和QDoubleSpinBox

属性名称 功能
prefix 数字显示的前缀
suffix 数字现实的后缀
buttonSysbols 右侧调节按钮的符号
text 只读属性,SpinBox内的所有文字
cleanText 只读属性,不带前后缀的所有文字
minimum 数值范围的最小值
maximum 数值范围的最大值
singleStep 单步步长改变值
stepType 步长类型,单一步长或自适应步长
value 当前显示的值
displayIntegerBase QSpinBox使用的进制
decimals QDoubleSpinBox显示的小数位数

对于默认值value的相关操作,存在两个相关的函数:

  • int QSpinBox::value()
  • void QSpinBox::setValue(int val)

对于相关的读写范围,还有一个函数setRange(),用于同时设置最小值与最大值.

  • void QSpinBox::setRange(int minimum,int maximum)

QSpinBox还有两个特有的信号,信号定义如下:

  • void QSpinBox::valueChanged(int i)
  • void QSpinBox::textChanged(const QString &text)

信号valueChanged()在value变化时被发射,传递的参数为变化之后的数值.

信号textChanged()在显示的文字发生变化的时候被发射.

下面是使用QSpinBox的一个实例:

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    //ui->setupUi(this);

    lbTest1= new QLabel(QString("电源电动势"));
    lbTest2= new QLabel(QString("反应物浓度"));
    lbTest3= new QLabel(QString("十六进制值测试"));

    sbTest1= new QSpinBox;
    sbTest2= new QSpinBox;
    sbTest3= new QSpinBox;
    sbTest1->setSuffix(QString("mV"));
    sbTest1->setValue(10);
    sbTest2->setSuffix(QString("mod/L"));
    sbTest2->setValue(1);
    sbTest3->setSuffix(QString("值"));
    sbTest3->setValue(28);
    sbTest3->setDisplayIntegerBase(16);

    txlTest= new QLineEdit;
    txlTest->setText(QString("这里会显示内容"));
    connect(sbTest1,SIGNAL(valueChanged(int)),this,SLOT(SpinBoxToLineEdit(int)));

    QGridLayout* gridTest= new QGridLayout;
    gridTest->addWidget(lbTest1,0,0,1,1);
    gridTest->addWidget(lbTest2,1,0,1,1);
    gridTest->addWidget(lbTest3,3,0,1,1);
    gridTest->addWidget(sbTest1,0,1,1,1);
    gridTest->addWidget(sbTest2,1,1,1,1);
    gridTest->addWidget(sbTest3,3,1,1,1);
    gridTest->addWidget(txlTest,2,0,1,2);

    this->setLayout(gridTest);
}
void Widget::SpinBoxToLineEdit(int i)
{
    this->txlTest->clear();
    this->txlTest->insert(QString("电源电动势改变后:%1").arg(i));
    return;
}

常用的按钮控件

按钮是界面上常用的组件,常用的4种按钮控件是:普通按钮(QPushButton),工具按钮(QToolButton),单选按钮(QRadioButton),复选框(QCheckBox)

这四种按钮都是继承于QAbstractButton类的.

QAbstractButton主要属性

属性 属性值类型 功能
text QString 按钮的显示文字
icon QIcon 按钮的图标
shortcut QKeySequence 按钮的快捷键
checkable bool 按钮是否可复选
checked bool 按钮是否复选状态
autoExclusive bool 在一个布局或容器组件内的同类按钮是否互斥
autoRepeat bool 是否自动重复发射信号
  • QPushButton的checkable默认为false
  • QRadioButton和QCheckBox的checkable属性默认为true
  • QCheckBox的autoExclusive属性默认为false
  • QRadioButton的autoExclusive属性默认为true

QPushButton的新增属性

属性 属性值类型 功能
autoDefault bool 按钮是否为自动默认按钮
default bool 按钮是否为默认按钮
flat bool 按钮是否没有边框

QCheckBox新增了一个tristate属性,QRadioButton没有新的属性.

QAbstractButton定义的新信号:

  • void clicked(bool checked)//点击按钮时
  • void pressed()//按下空格或左键
  • void released()//释放空格或左键
  • void toggled(bool checked)//按钮的checked属性变化

QPushButton和QRadioButton没有定义新信号.

QCheckBox定义了一个新信号:

  • void QCheckBox::stateChanged(int state)

下面提供了一个使用button的例子:
mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private:
    Ui::MainWindow *ui;
private slots:
    void on_btnLeft();
    void on_btnMiddle();
    void on_btnRight();

    void on_btnBF();
    void on_btnIT();
    void on_btnUL();

    void on_chkRead(bool checked);
    void on_chkEnabled(bool checked);
    void on_chkClean(bool checked);

    void on_radBlack();
    void on_radBlue();
    void on_radRed();

signals:

};
#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    connect(ui->btnLeft,SIGNAL(clicked(bool)),this,SLOT(on_btnLeft()));
    connect(ui->btnRight,SIGNAL(clicked(bool)),this,SLOT(on_btnRight()));
    connect(ui->btnMiddle,SIGNAL(clicked(bool)),this,SLOT(on_btnMiddle()));

    connect(ui->btnBF,SIGNAL(clicked(bool)),this,SLOT(on_btnBF()));
    connect(ui->btnIT,SIGNAL(clicked(bool)),this,SLOT(on_btnIT()));
    connect(ui->btnUL,SIGNAL(clicked(bool)),this,SLOT(on_btnUL()));

    connect(ui->radBlack,SIGNAL(clicked(bool)),this,SLOT(on_radBlack()));
    connect(ui->radBlue,SIGNAL(clicked(bool)),this,SLOT(on_radBlue()));
    connect(ui->radRed,SIGNAL(clicked(bool)),this,SLOT(on_radRed()));

    connect(ui->chkClean,SIGNAL(clicked(bool)),this,SLOT(on_chkClean(bool)));
    connect(ui->chkEnabled,SIGNAL(clicked(bool)),this,SLOT(on_chkEnabled(bool)));
    connect(ui->chkRead,SIGNAL(clicked(bool)),this,SLOT(on_chkRead(bool)));
}

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

void MainWindow::on_btnLeft()
{
    ui->pltEdit->setAlignment(Qt::AlignLeft);
}

void MainWindow::on_btnMiddle()
{
    ui->pltEdit->setAlignment(Qt::AlignCenter);
}

void MainWindow::on_btnRight()
{
    ui->pltEdit->setAlignment(Qt::AlignRight);
}

void MainWindow::on_btnBF()
{
    static bool flag=true;
    QFont font= ui->pltEdit->font();
    font.setBold(flag);
    ui->pltEdit->setFont(font);
    flag=!flag;
}

void MainWindow::on_btnIT()
{
    static bool flag=true;
    QFont font= ui->pltEdit->font();
    font.setItalic(flag);
    ui->pltEdit->setFont(font);
    flag=!flag;
}

void MainWindow::on_btnUL()
{
    static bool flag=true;
    QFont font= ui->pltEdit->font();
    font.setUnderline(flag);
    ui->pltEdit->setFont(font);
    flag=!flag;
}

void MainWindow::on_chkRead(bool checked)
{
    ui->pltEdit->setReadOnly(checked);
}

void MainWindow::on_chkEnabled(bool checked)
{
    ui->pltEdit->setEnabled(checked);
}

void MainWindow::on_chkClean(bool checked)
{
    ui->pltEdit->setClearButtonEnabled(checked);
}

void MainWindow::on_radBlack()
{
    QPalette plet= ui->pltEdit->palette();
    plet.setColor(QPalette::Text,Qt::black);
    ui->pltEdit->setPalette(plet);
}

void MainWindow::on_radBlue()
{
    QPalette plet= ui->pltEdit->palette();
    plet.setColor(QPalette::Text,Qt::blue);
    ui->pltEdit->setPalette(plet);
}

void MainWindow::on_radRed()
{
    QPalette plet= ui->pltEdit->palette();
    plet.setColor(QPalette::Text,Qt::red);
    ui->pltEdit->setPalette(plet);
}

QSlider和QProgressBar

QAbstractSlider是QSlider,QScrollBar和QDial的父类,它定义了这几个类共有的一些属性和接口函数.

QAbstractSlider的属性

属性 属性值类型 功能
minimum int 数据范围的最小值
maximum int 数据范围的最大值
singleStep int 变化最小步长
pageStep int 按PgUp与PgDn变化的数值
value int 组件的当前值
sliderPosition int 滑块的位置
tracking bool 改变value是否改变slider位置
orientation Qt::Orientation 滑动条的方向,水平或垂直
invertedAppearance bool 显示方式是否反向
invertedControls bool 反向按键控制

QAbstractSlider的接口函数主要是属性的读写函数,还有一个常用函数setRange()用于设置最大值与最小值.

  • void QAbstractSlider::setRange(int min,int max)

QAbstractSlider类的信号主要有:

  • void actionTriggered(int action)//滑动条触发一些动作
  • void rangeChanged(int min,int max)//minimum或maximum值变化时
  • void sliderMoved(int value)//用户按住鼠标拖动滑块时
  • void sliderPressed()//在滑块上按下鼠标时
  • void sliderReleased()//在滑块上释放鼠标时
  • void valueChanged(int value)//value值改变时

action表示动作的类型,用枚举类型QAbstractSlider::SliderAction的值表示.如SliderToMinimum表示拖动到最小值.

如果tracking属性被设置为false时,valueChanged()仅在鼠标拖动结束时发射.

QSlider一般用于滑动输入数值数据的组件,其新定义的属性有两个:

  • tickPosition:标尺刻度的显示位置,属性值为枚举类型QSlider::TickPosition.
  • tickInterval:标尺刻度的间隔值.

QScrollBar没有新定义的属性,一般与文本编辑器或容器组件组合使用.起滚卷条的作用.

QDial表示表盘式组件,通过旋转表盘获得输入值.QDial定义了3个新的属性:

  • notchesVisible:表盘外围的刻度线是否可见.
  • notchTarget:表盘刻度间的间隔像素值.
  • wrapping:表盘上首尾刻度是否连贯.

QProgressBar表示进度条组件,一般以百分比数据来显示进度.其父类为QWidget.

其几个不易理解的属性如下:

  • textDirection:文字的方向.
  • format:显示文字的格式.

QProgressBar类的接口函数主要是属性的读写函数.QProgressBar还有两个常用的与属性无关的函数:

  • void QProgressBar::setRange(int minimum,int maximum)
  • void QProgressBar::reset()

下面是一个使用滚动条的实例:

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    connect(ui->dial,SIGNAL(valueChanged(int)),this,SLOT(do_valueChange(int)));
    connect(ui->progressBar,SIGNAL(valueChanged(int)),this,SLOT(do_valueChange(int)));
    connect(ui->scrollBar,SIGNAL(valueChanged(int)),this,SLOT(do_valueChange(int)));
    connect(ui->slider,SIGNAL(valueChanged(int)),this,SLOT(do_valueChange(int)));
    connect(ui->spinBox,SIGNAL(valueChanged(int)),this,SLOT(do_valueChange(int)));

    ui->dial->setNotchesVisible(true);
    do_valueChange(10);
}

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

void MainWindow::do_valueChange(int value)
{
    ui->dial->setValue(value);
    ui->progressBar->setValue(value);
    ui->scrollBar->setValue(value);
    ui->slider->setValue(value);
    ui->spinBox->setValue(value);
}

日期时间数据

日期和时间是经常遇到的数据类型.Qt提供了三个类用于表示和处理数据类型:

  • QTime:表示时间数据的类.
  • QDate:表示日期数据的类.
  • QDateTime:表示时间和日期数据的类.

这三个类都没有父类.为了在界面上输入和现实日期时间的数据,Qt定义了几个用于日期时间数据处理的界面类:

  • QTimeEdit:编辑和显示时间的组件类
  • QDateEdit:编辑和显示日期的组件类
  • QDateTimeEdit:编辑和显示日期的组件类.
  • QCalendarWidget:一个用日历形式显示和选择日期的组件类.

QTime类适用于存储与操作时间数据的类,时间数据包含小时,分钟,秒,毫秒.QTime总是24小时制,不区分AM/PM.

QTime初始化时间数据的函数定义如下:

  • QTime::QTime(int h,int m,int s=0,int ms=0)

还可以使用静态函数QTime::currentTime()创建一个QTime对象,并将其初始化为系统当前时间.

QTime类的主要接口函数

函数原型 功能
int hour() 返回当前时间的小时
int minute() 返回当前时间的分钟
int second() 返回当前时间的秒
int misec() 返回当前时间的毫秒
bool setHMS(int h,int m,int s,int ms=0) 设置当前时间的数据
int msecSinceStartOfDay() 返回时间从00:00:00开始的毫秒数
QTime addSecs(int s) 当前时间延后s秒后的时间
int secsTo(QTime t) 返回一个与当前时间相差t秒的秒数
QString toString(const QString& format) 将当前时间按照format格式转换为字符串

QDate是用于存储和操作日期数据的类,日期数据包含年月日数据.

与QTime类似,可以在初始化时为其提供数据,也可以使用静态函数QDate::currentDate()获取系统当前日期.

QDate类的主要接口函数

函数原型 功能
int year() 返回当前日期年数据
int month() 返回当前日期月数据
int day() 返回当前日期日数据
int dayOfWeek() 返回当前日期在一周中的日期
int dayOfYear() 返回当前日期在一年中是第多少天
bool setDate(int year,int month,int day) 设置日期的年日月数据
void getDate(int* year,int* month,int* day) 通过指针变量,返回年日月数据
QDate addYears(int nyears) 返回一个较时间迟n年的QDate变量
QDate addMonths(int nmonths) 返回一个较时间迟n月的QDate变量
QDate addDays(qint64 ndays) 返回一个较时间迟n天的QDate变量
qint64 daysTo(QDate d) 返回一个与当前日期差d天的天数
QString toString(const QString&format) 将当前日期按照format格式转换为字符串

此外,QDate还有一个静态函数isLeapYear()可以判断某年是否为闰年:

  • bool QDate::isLeapYear(int year)

QDateTime类的主要接口函数

函数原型 功能
QDate date() 返回当前日期时间数据的日期数据
QTime time() 返回当前日期时间数据的时间数据
qint64 toMSecsSinceEpoch() 返回与UTC时间1970-01-01T00:00:00:00.000相差的毫秒数
void setMSecsSinceEpoch(qint64 msecs) 设置与UTC时间1970-01-01T00:00:00:00.000相差的毫秒数
qint64 toSecsSinceEpoch() 返回与UTC时间1970-01-01T00:00:00:00.000相差的秒数
void setSecsSinceEpoch(qint64) 设置与UTC时间1970-01-01T00:00:00:00.000相差的秒数
QString toString(const QString& format) 将当前日期时间按照format设置的格式转换为字符串
QDateTime toUTC() 将当前时间转换为UTC时间

QDateTime有两个静态函数用于返回系统当前时间:

  • QDateTime QDateTime::currentDateTime()
  • QDateTime QDateTime::currentDateTimeUtc()

QTime,QDate,QDateTime都有一个函数toString(),用于将当前的日期时间数据转换为字符串.

同样的,QTime,QDate,QDateTime都有一个静态函数fromString().用于将字符串转换为相应类的对象.

  • QString QDateTime::toString(const QString&format,QCalendar cal=QCalendar())
  • QDateTime QDateTime::fromString(const QString&string,const QString&format,QCalendar cal=QCalendar())

用于表示日期时间的常用格式字符及含义

格式字符 含义
d 天1~31
dd 天01~31
M 月1~12
MM 月01~12
yy 年00~99
yyyy 年0000~9999
h 小时023或112
hh 小时0023或0112
H 小时0~23
HH 小时00~23
m 分钟0~59
mm 分钟00~59
s 秒0~59
ss 秒00~59
z 毫秒0~999
zzz 毫秒000~999
AP或A 使用AM/PM显示
ap或a 使用am/pm显示

而且上述字符仅作为一个占位符使用,其其他内容不影响其字符串format.

QtDesigner有3个用于编辑日期时间数据的界面组件,如QTimeEdit,QDateEdit,QDateTimeEdit.

而QDateTimeEdit是QDateEdit和QTimeEdit的父类,而QDateTimeEdit的父类是QAbstractSpinBox.

所以其实日期时间编辑框的特性与QSpinBox的有些相似.

QDateTimeEdit的主要属性有:

  • currentSection:光标所在的输入段,是枚举类型QDateTimeEdit::Section.
  • currentSecitonIndex:用序号表示的光标所在的段.
  • calendarPopup:是否允许弹出一个日历选择框.会将上下调节按钮变成一个下拉按钮.
  • displayFormat:日期时间数据的显示格式.

QDateTimeEdit常用的接口函数就是读取或设置日期时间数据的函数:

  • QDateTime dateTime()
  • void setDateTime(const QDateTime&dateTime)
  • QDate date()
  • void setDate(QDate date)
  • QTime time()
  • void setTime(QTime time)

QDateTimeEdit有3个信号:

  • void dateChanged(QDate date)
  • void timeChanged(QTime time)
  • void dateTimeChanged(const QDateTime& datetime)

QCalendarWidget则是一个用于选择日期的日历组件.

QCalendarWidget的几个常见接口函数有:

  • void showToday()
  • void showSelectedDate()
  • QDate selectedDate()
  • void setSelectedDate(QDate date)

QCalendarWidget有4个信号:

  • void activated(QDate date)
  • void clicked(QDate date)
  • void currentPageChanged(int year,int month)
  • void selectionChanged()

选择的日期变化时,QCalendarWidget会发射selectionChanged信号.

下面给出了一个使用QDateTime,QCalendarWidget等的例子:
mainWindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private:
    Ui::MainWindow *ui;
private slots:
    void on_btnReset();
    void on_btnClear();
    void on_btnTime();
    void on_btnDate();
    void on_btnDateTime();
    void on_btnChange();

    void on_timeEdit();
    void on_dateEdit();
    void on_dateTimeEdit();
    void on_calendar();
};
#endif // MAINWINDOW_H

mainWindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    connect(ui->btnClear,SIGNAL(clicked(bool)),this,SLOT(on_btnClear()));
    connect(ui->btnReset,SIGNAL(clicked(bool)),this,SLOT(on_btnReset()));
    connect(ui->btnTime,SIGNAL(clicked(bool)),this,SLOT(on_btnTime()));
    connect(ui->btnDate,SIGNAL(clicked(bool)),this,SLOT(on_btnDate()));
    connect(ui->btnDateTime,SIGNAL(clicked(bool)),this,SLOT(on_btnDateTime()));
    connect(ui->btnChange,SIGNAL(clicked(bool)),this,SLOT(on_btnChange()));

    connect(ui->dateEdit,SIGNAL(dateChanged(QDate)),this,SLOT(on_dateEdit()));
    connect(ui->timeEdit,SIGNAL(timeChanged(QTime)),this,SLOT(on_timeEdit()));
    connect(ui->dateTimeEdit,SIGNAL(dateTimeChanged(QDateTime)),this,SLOT(on_dateTimeEdit()));
    connect(ui->calendar,SIGNAL(selectionChanged()),this,SLOT(on_calendar()));
}

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

void MainWindow::on_btnReset()
{
    ui->calendar->clearFocus();
    ui->dateEdit->clear();
    ui->timeEdit->clear();
    ui->dateTimeEdit->clear();
    ui->pltEdit->clear();
    ui->lineEdit->clear();
}

void MainWindow::on_btnClear()
{
    ui->lineEdit->clear();
    ui->pltEdit->clear();
}

void MainWindow::on_btnTime()
{
    QTime TM1(13,24,5);
    QString str=TM1.toString("HH:mm:ss");
    ui->pltEdit->appendPlainText(QString("Time1:%1").arg(str));
    QTime TM2= TM1.addSecs(150);
    str= TM2.toString("HH:mm:ss");
    ui->pltEdit->appendPlainText(QString("Time1 add 150secs:%1").arg(str));
    TM2= QTime::currentTime();
    str=TM2.toString("HH:mm:ss zzz");
    ui->pltEdit->appendPlainText(QString("Current Time:%1").arg(str));
}

void MainWindow::on_btnDate()
{
    QDate DT1(2021,7,6);
    QString str=DT1.toString("yyyy-MM-dd");
    ui->pltEdit->appendPlainText(QString("Date1:%1").arg(str));
    QDate DT2;
    DT2.setDate(2021,8,25);
    str=DT2.toString("yyyy-MM-dd");
    ui->pltEdit->appendPlainText(QString("Date2:%1").arg(str));
    DT2=QDate::currentDate();
    str=DT2.toString("yyyy-MM-dd");
    ui->pltEdit->appendPlainText(QString("Current Date:%1").arg(str));
}

void MainWindow::on_btnDateTime()
{
    QDateTime DT1=QDateTime::currentDateTime();
    QString str=DT1.toString("yyyy-MM-dd hh:mm:ss zzz");
    ui->pltEdit->appendPlainText(QString("Current DateTime:%1").arg(str));
    QDate dt= DT1.date();
    str=dt.toString("yyyy-MM-dd");
    ui->pltEdit->appendPlainText(QString("DT1 Date:%1").arg(str));
    QTime tm=DT1.time();
    str=tm.toString("hh:mm:ss zzz");
    ui->pltEdit->appendPlainText(QString("DT1 Time:%1").arg(str));
    qint64 MS=DT1.toSecsSinceEpoch();
    ui->pltEdit->appendPlainText(QString("Secs Since:%1").arg(MS));
    MS+=120;
    DT1.setSecsSinceEpoch(MS);
    str=DT1.toString("yyyy-MM-dd hh:mm:ss zzz");
    ui->pltEdit->appendPlainText(QString("DT1 After120s:%1").arg(str));
}

void MainWindow::on_btnChange()
{
    static bool calendar_flag=true;
    ui->dateTimeEdit->setCalendarPopup(calendar_flag);
    calendar_flag=!calendar_flag;
}

void MainWindow::on_timeEdit()
{
    QTime tm= ui->timeEdit->time();
    ui->dateTimeEdit->setTime(tm);
}

void MainWindow::on_dateEdit()
{
    QDate dt= ui->dateEdit->date();
    ui->lineEdit->setText(QString("%1").arg(dt.toString("yyyy-MM-dd")));
    ui->dateTimeEdit->setDate(dt);
    ui->calendar->setSelectedDate(dt);
    ui->calendar->showSelectedDate();
}

void MainWindow::on_dateTimeEdit()
{
    QDateTime datetime= ui->dateTimeEdit->dateTime();
    ui->lineEdit->setText(QString("%1").arg(datetime.date().toString("yyyy-MM-dd")));
    ui->dateEdit->setDate(datetime.date());
    ui->timeEdit->setTime(datetime.time());
    ui->calendar->setSelectedDate(datetime.date());
    ui->calendar->showSelectedDate();
    ui->pltEdit->appendPlainText(QString("Now Time:%1").arg(datetime.toString("yyyy-MM-dd hh:mm:ss zzz")));
}

void MainWindow::on_calendar()
{
    QDate dt=ui->calendar->selectedDate();
    ui->dateEdit->setDate(dt);
    ui->dateTimeEdit->setDate(dt);
    ui->lineEdit->setText(dt.toString("yyyy-MM-dd"));
}

QTimer和QElapsedTimer

QTimer是软件计时器,其父类是QObject.其主要功能是设置以毫秒为单位的定时周期.

当定时器启动后,定时溢出时QTimer发射timeout()信号.

QTimer类的主要属性如下:

QTimer类的主要属性

属性 属性值类型 功能
interval int 定时周期,单位是毫秒
singleShot bool 定时器是否为单次计时
timerType Qt::TimerType 定时器精度类型
active bool 返回定时器是否正在运行
remainingTime int 到发生定时溢出的剩余时间

属性timeType表示定时器的精度类型,设置函数setTimerType()的原型定义如下:

  • void QTimer::setTimerType(Qt::TimerType atype)

参数atype是枚举类型Qt::TimerType,有以下几个枚举值,默认为Qt::CoarseTimer:

  • Qt::PreciseTimer:精确计时器,精度保持在毫秒级
  • Qt::CoarseTimer:粗糙计时器,定时误差保持在周期值的5%内
  • Qt::VeryCoarseTimer:非常粗糙的定时器,精度保持在秒级.

QTimer有几个公有槽函数用于启动和停止定时器:

  • void QTimer::start()//启动定时器
  • void QTimer::start(int msec)//启动定时器,并设置定时周期
  • void QTimer::stop()//停止定时器

QTimer只有一个timeout()信号,其原型如下:

  • void QTimer::timeout()

QTimer还有一个静态函数singleShot(),用于创建和启动单次定时器,并且将定时器的timeout()信号与指定的槽函数关联.

这个函数有多种参数形式,其中一种函数原型定义如下:

  • void QTimer::singleShot(int msec,Qt::TimerType,const QObject* receiver,const char* member)

QElapsedTimer则用于快速计算两个时间的间隔时间,它没有父类,不支持Qt的元对象系统,所以只有接口函数.

QElapsedTimer的接口函数有:

  • void start()
  • qint64 elapsed()
  • qint64 nsecsElapsed()
  • qint64 restart()

函数elapsed()的返回值是自上次start()之后计时器的运行时间,单位是毫秒.

函数nsecsElapsed()的返回值也是指上次start()之后计时器的运行时间,但单位是纳秒.

函数restart()返回从上次启动计时器到现在的时间,单位是毫秒,然后重启计时器.

下面是一个使用QTimer的实例:

mainWindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QTime>
#include <QTimer>
#include <QElapsedTimer>

QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private:
    Ui::MainWindow *ui;
    QTimer* m_timer;
    QElapsedTimer m_counter;
private slots:
    void do_timer_timeout();
    void do_timer_shot();

    void do_btnStart();
    void do_btnStop();
    void do_btnOneShot();
};
#endif // MAINWINDOW_H

mainWindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    m_timer= new QTimer(this);
    m_timer->stop();
    m_timer->setTimerType(Qt::CoarseTimer);
    ui->radCoarse->setChecked(true);
    connect(m_timer,SIGNAL(timeout()),this,SLOT(do_timer_timeout()));
    connect(ui->btnStart,SIGNAL(clicked(bool)),this,SLOT(do_btnStart()));
    connect(ui->btnStop,SIGNAL(clicked(bool)),this,SLOT(do_btnStop()));
    connect(ui->btnOneShot,SIGNAL(clicked(bool)),this,SLOT(do_btnOneShot()));
}

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

void MainWindow::do_timer_timeout()
{
    QApplication::beep();
    QTime curTime= QTime::currentTime();
    ui->LCDHour->display(curTime.hour());
    ui->LCDMin->display(curTime.minute());
    ui->LCDSec->display(curTime.second());
    if(m_timer->isSingleShot()){
        int tmMsec= m_counter.elapsed();
        QString str=QString("流逝的时间:%1毫秒").arg(tmMsec);
        ui->labelTime->setText(str);
        ui->btnStop->setEnabled(false);
    }
}

void MainWindow::do_timer_shot()
{
    QApplication::beep();
    int tmMsec= m_counter.elapsed();
    QString str=QString("流逝的时间:%1毫秒").arg(tmMsec);
    ui->labelTime->setText(str);
    ui->btnOneShot->setEnabled(true);
}

void MainWindow::do_btnStart()
{
    m_timer->setInterval(ui->spinBox->value());
    if(ui->radCoutinue->isChecked())
        m_timer->setSingleShot(false);
    else
        m_timer->setSingleShot(true);

    if(ui->radPrecise->isChecked())
        m_timer->setTimerType(Qt::PreciseTimer);
    else if(ui->radCoutinue->isChecked())
        m_timer->setTimerType(Qt::CoarseTimer);
    else
        m_timer->setTimerType(Qt::VeryCoarseTimer);

    m_timer->start();
    m_counter.start();
    ui->btnStart->setEnabled(false);
    ui->btnOneShot->setEnabled(false);
    ui->btnStop->setEnabled(true);
}

void MainWindow::do_btnStop()
{
    m_timer->stop();
    int tmMsec= m_counter.elapsed();
    int ms= tmMsec%1000;
    int sec= tmMsec/1000;
    QString str= QString("流逝的时间:%1秒%2毫秒").arg(sec).arg(ms,3,10,QChar('0'));
    ui->labelTime->setText(str);
    ui->btnStart->setEnabled(true);
    ui->btnOneShot->setEnabled(true);
    ui->btnStop->setEnabled(false);
}

void MainWindow::do_btnOneShot()
{
    int intv= ui->spinBox->value();
    QTimer::singleShot(intv,Qt::PreciseTimer,this,&MainWindow::do_timer_shot);
    m_counter.start();
    ui->btnOneShot->setEnabled(false);
}

QComboBox

QComboBox是下拉列表框组件,它可以提供下拉列表供用户选择输入,也可以提供编辑框用于输入文字.

所以QComboBox也被称为组合框.

QComboBox类的主要属性

属性 属性值类型 功能
editable bool 是否可编辑,若为false则只可使用下拉栏
currentText QString 当前显示的文字
currentIndex int 当前选中项的序号
maxVisibleItems int 下拉列表中显示最大条数,若超过将出现卷滚条
maxCount int 下拉列表中最大项数
insertPolicy InsertPolicy 用户编辑的新文字插入列表的方式,为枚举类型QComboBox::InsertPolicy
placeholderText QString 占位文字
duplicatesEnabled bool 是否允许列表中出现重复的项
modelColumn int 下拉列表中的数据在数据模型中的列编号

QComboBox使用模型/视图结构存储和显示下拉列表的数据,下拉列表的数据实际上存储在QStandardItemModel模型里.下拉列表使用QListView的子类组件显示.modelColumn属性表示下拉列表现实的数据在模型中的列编号.

QComboBox的几个信号原型如下:

  • void activated(int index)
  • void currentIndexChanged(int index)
  • void currentTextChanged(const QString &text)
  • void editTextChanged(const QString &text)
  • void highlighted(int index)
  • void textActivated(const QString &text)
  • void textHighlighted(const QString &text)

下面是使用QComboBox的一个实例:

mainWindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private:
    Ui::MainWindow *ui;
private slots:
    //void on_btnSimpleInit();
    void on_pushButton_5_clicked(bool checked);
    void on_btnSimpleInit_clicked(bool checked);
    void on_chkEditable_clicked(bool checked);
    void on_btnClearList_clicked(bool checked);
    void on_btnDataInit_clicked(bool checked);

    void comChanged(const QString& arg1);
    void on_comData_currentIndexChanged(int index);
    void on_chkReadOnly_clicked(bool checked);
};
#endif // MAINWINDOW_H

mainWindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
}

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

void MainWindow::on_pushButton_5_clicked(bool checked)
{
    ui->pltEdit->clear();
}


void MainWindow::on_btnSimpleInit_clicked(bool checked)
{
    QIcon icon;
    icon.addFile(":/img/icon.jpg");
    ui->comSimple->clear();
    for(int i=0;i<20;i++)
        ui->comSimple->addItem(icon,QString("城市%1").arg(i));
}


void MainWindow::on_chkEditable_clicked(bool checked)
{
    ui->comSimple->setEditable(checked);
}


void MainWindow::on_btnClearList_clicked(bool checked)
{
    ui->comSimple->clear();
}


void MainWindow::on_btnDataInit_clicked(bool checked)
{
    ui->comData->clear();
    QMap<QString,int>city_zone;
    city_zone.insert(QString("北京"),10);
    city_zone.insert(QString("上海"),20);
    city_zone.insert(QString("广州"),30);
    city_zone.insert(QString("长沙"),40);
    city_zone.insert(QString("成都"),50);
    for(auto iter:city_zone.keys())
        ui->comData->addItem(iter,city_zone.value(iter));
}

void MainWindow::comChanged(const QString &arg1)
{
    ui->pltEdit->appendPlainText(arg1);
}


void MainWindow::on_comData_currentIndexChanged(int index)
{
    Q_UNUSED(index);
    QString str= ui->comData->currentText()+ui->comData->currentData().toString();
    ui->pltEdit->appendPlainText(str);
}


void MainWindow::on_chkReadOnly_clicked(bool checked)
{
    ui->pltEdit->setEnabled(!checked);
}

QMainWindow和QAction

QMainWindow是主窗口类,具有菜单栏,工具栏,状态栏等主窗口常见的界面元素.

要设计主窗口上的菜单栏,工具栏,按钮的下拉菜单,组建的快捷菜单等需要用到QAction类.

窗口界面可视化设计

创建一个GUI项目,在向导中选择窗口基类为QMainWindow,新建窗口类的名称会被自动设置为MainWindow.

QAction的父类是QObject,所以支持Qt的元对象系统.在UI可视化设计时就可以创建Action,使用设计好的Action可以创建菜单项和工具按钮.

Qt Designer界面下方有一个Action编辑器,可以在这个编辑器里可视化设计Action.

在Action编辑器中,编辑Action的对话框,包括以下一些设置内容:

  • Text:Action的显示文字
  • Object name:Action的对象名称
  • ToolTip:当鼠标光标停留时的提示
  • Icon:Action的图标
  • Checkable:Action是否可被复选
  • ShortCut:Action的快捷键.

在Action编辑器中创建一个Action后,属性编辑器就会显示这几个Action的属性.一些属性说明如下:

  • text:用Action创建菜单项时显示的文字.
  • iconText:这是用Action创建工具按钮时按钮上显示的文字.
  • statusTip:这是鼠标移到Action上时的提示.
  • shortcutContext:这是Action快捷键的有效响应范围.
  • autoRepeat:当快捷键一直按下时,Action是否自动重复执行.
  • menuRole:在macOS上才有作用的功能.
  • iconVisibleInMenu:表示菜单项上是否需显示Action图标.
  • shortcutVisuableInContextMenu:表示使用Action创建右键快捷菜单时,是否显示快捷键.
  • priority:表示Action在UI上的优先级.


工具栏对应QToolBar类,选择一个工具栏后,在属性编辑器中可对其属性进行设置.

QToolBar类的主要属性

属性名称 属性值类型 含义与作用
movable bool 工具栏是否可移动
allowedAreas Qt::ToolBarAreas 工具栏可以放置的窗口区域
orientation Qt::Orientation 工具栏的方向
iconSize QSize 图标的大小
toolButtonStyle Qt::ToolButtonStyle 工具按钮样式
floatable bool 工具栏是否可浮动

属性toolButtonStyle的取值决定了工具按钮显示的样式,属性值Qt::ToolButtonStyle有以下几种枚举值.

  • Qt::ToolButtonIconOnly:只显示图标
  • Qt::ToolButtonTextOnly:只显示文字
  • Qt::ToolButtonTextBesideIcon:文字显示在图标旁边
  • Qt::ToolButtonTextUnderIcon:文字显示在图标下面
  • Qt::ToolButtonFollowStyle:由QStyle样式定义

QAction的类大部分接口函数是用于属性读写的函数.

QAction还定义了一些信号和公有槽.QAction主要的信号有:

  • void changed()//Action的text等属性发生改变时
  • void checkableChanged(bool checkable)//checkable的属性变化时
  • void enabledChanged(bool enabled)//enabled属性值变化时
  • void hovered()//鼠标移动至该Action上时
  • void toggled(bool checked)//checked属性值变化时
  • void triggered(bool checked=false)//点击此Action时
  • void visibleChanged()//visible属性值变化时

当我们点击Action创建的菜单项/工具按钮或按下Action的快捷键时,QAction属性发射triggered()信号.

当Action的checked属性变化时,Action会发射toggled(bool)信号.

QAction定义了一些公有槽,这些公有槽可以在程序中直接调用或connect.

  • void hover()//触发hovered()信号
  • void trigger()//触发triggered()信号
  • void resetEnabled()//复位enabled为默认值
  • void setChecked(bool)//设置checked属性的值
  • void setDisabled(bool b)//设置enabled属性的值
  • void setVisible(bool)//设置visible属性的值
  • void toggle()//反转checked属性的值

在UI可视化设计时,可以用Action可视化地创建工具栏上的按钮,但是不能可视化地在工具栏上放置其他组件.

QToolBar提供了接口函数,可以通过代码在工具栏上添加组件,从而灵活地设计工具栏.

  • void addAction(QAction *action)//添加一个Action
  • QAction *addWidget(QWidget *widget)//添加一个界面组件
  • QAction *insertWidget(QAction *before,QWidget *widget)//插入一个界面组件
  • QAction *addSeparator()//添加一个分隔条
  • QAction *insertSeparator(QAction *before)//插入一个分隔条

主窗口上的状态栏对应的是QStatusBar类,在UI可视化设计时,不能在状态栏上放置任何组件,而只能通过其接口函数向状态栏添加组件.

QStatusBar有两个函数用于添加组件,其原型如下:

  • void addWidget(QWidget *widget,int stretch=0)//添加正常组件
  • void addPermanentWidget(QWidget *widget,int strech=0)//添加永久组件

QStatusBar类有两个公有槽,可以显示和清楚临时消息,定义如下:

  • void showMessage(const QString &message,int timeout=0)//显示临时消息
  • void clearMessage()//清除临时消息

如果一个Action的statusTip属性不为空,当鼠标移到这个Action创建的菜单或按钮上时,状态栏就会自动显示这个Action的statusTip属性的内容:当鼠标移出时,临时消息就会被自动清除.

下面是一个使用QAction,QToolBar,QStatusBar的综合例子:

mainWindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QActionGroup>
#include <QLabel>
#include <QFontComboBox>
#include <QSpinBox>
#include <QProgressBar>
#include <QTextCharFormat>

QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();
private:
    QLabel* lbFile;
    QProgressBar* progressBar;
    QSpinBox* spinFontSize;
    QLabel* lbFontSize;
    QFontComboBox* fcomFont;
private slots:
    void on_actNewFile_triggered();

    void on_actOpenFile_triggered();

    void on_actSaveFile_triggered();

    void on_pltEdit_copyAvailable(bool b);

    void on_pltEdit_selectionChanged();

    void on_actBlod_triggered(bool checked);

    void on_actItalian_triggered(bool checked);

    void on_actUnderline_triggered(bool checked);

    void on_spinFontSize(int fontSize);

    void on_fcomCombo(QFont font);
private:
    Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H

mainWindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    //中英界面互斥
    ui->setupUi(this);
    QActionGroup *acgLanguage = new QActionGroup(this);
    acgLanguage->addAction(ui->actChinese);
    acgLanguage->addAction(ui->actEnglish);
    acgLanguage->setExclusive(true);

    //创建一些无法通过GUI界面设计的组件
    spinFontSize=new QSpinBox(this);
    spinFontSize->setMinimum(5);
    spinFontSize->setMaximum(50);
    spinFontSize->setValue(ui->pltEdit->font().pointSize());
    spinFontSize->setMinimumWidth(50);
    QLabel* lbSpinFont= new QLabel(QString("字号"),this);
    QLabel* lbFontComboBoxFont= new QLabel(QString("字体"),this);
    fcomFont= new QFontComboBox(this);
    fcomFont->setMaximumWidth(80);
    fcomFont->setFont(ui->pltEdit->font());
    ui->toolBar->addWidget(lbSpinFont);
    ui->toolBar->addWidget(spinFontSize);
    ui->toolBar->addWidget(lbFontComboBoxFont);
    ui->toolBar->addWidget(fcomFont);
    ui->toolBar->addSeparator();
    ui->toolBar->addAction(ui->actClose);
    ui->toolBar->addSeparator();

    //状态栏相关
    lbFile= new QLabel(this);
    lbFile->setMaximumWidth(150);
    lbFile->setText(QString("文件名:"));
    ui->statusbar->addWidget(lbFile);
    progressBar= new QProgressBar(this);
    progressBar->setMinimum(5);
    progressBar->setMaximum(50);
    progressBar->setValue(ui->pltEdit->font().pointSize());
    ui->statusbar->addWidget(progressBar);
    lbFontSize= new QLabel(QString("Permanent"),this);
    ui->statusbar->addPermanentWidget(lbFontSize);

    //信号与槽
    connect(spinFontSize,SIGNAL(valueChanged(int)),this,SLOT(on_spinFontSize(int)));
    connect(fcomFont,SIGNAL(currentFontChanged(QFont)),this,SLOT(on_fcomCombo(QFont)));
}

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

void MainWindow::on_actNewFile_triggered()
{
    ui->pltEdit->clear();
    ui->pltEdit->document()->setModified(false);
    lbFile->setText("文件名:新建文件");
}


void MainWindow::on_actOpenFile_triggered()
{
    lbFile->setText(QString("正在打开文件"));
    for(int i=1;i<99;i++){
        progressBar->setValue(i);
        _sleep(1);
    }
    lbFile->setText(QString("文件名:新建文件"));
    progressBar->setValue(ui->pltEdit->font().pointSize());
}


void MainWindow::on_actSaveFile_triggered()
{
    ui->pltEdit->clear();
    ui->pltEdit->document()->setModified(false);
    QString str= lbFile->text();
    lbFile->setText("文件已保存");
    _sleep(2);
    lbFile->setText(str);
}


void MainWindow::on_pltEdit_copyAvailable(bool b)
{
    ui->actCopy->setEnabled(b);
    ui->actCut->setEnabled(b);
    ui->actPaste->setEnabled(ui->pltEdit->canPaste());
}


void MainWindow::on_pltEdit_selectionChanged()
{
    QTextCharFormat fmt= ui->pltEdit->currentCharFormat();
    ui->actBlod->setChecked(fmt.font().bold());
    ui->actItalian->setChecked(fmt.font().italic());
    ui->actUnderline->setChecked(fmt.font().underline());
    spinFontSize->setValue(fmt.font().pointSize());
    fcomFont->setCurrentFont(fmt.font());
}


void MainWindow::on_actBlod_triggered(bool checked)
{
    QTextCharFormat fmt= ui->pltEdit->currentCharFormat();
    if(checked)
        fmt.setFontWeight(QFont::Bold);
    else
        fmt.setFontWeight(QFont::Normal);
    ui->pltEdit->setCurrentCharFormat(fmt);
}


void MainWindow::on_actItalian_triggered(bool checked)
{
    QTextCharFormat fmt= ui->pltEdit->currentCharFormat();
    fmt.setFontItalic(checked);
    ui->pltEdit->setCurrentCharFormat(fmt);
}


void MainWindow::on_actUnderline_triggered(bool checked)
{
    QTextCharFormat fmt= ui->pltEdit->currentCharFormat();
    fmt.setFontUnderline(checked);
    ui->pltEdit->setCurrentCharFormat(fmt);
}

void MainWindow::on_spinFontSize(int fontSize)
{
    QTextCharFormat fmt=ui->pltEdit->currentCharFormat();
    fmt.setFontPointSize(fontSize);
    progressBar->setValue(fontSize);
    ui->pltEdit->setCurrentCharFormat(fmt);
}

void MainWindow::on_fcomCombo(QFont font)
{
    QTextCharFormat fmt=ui->pltEdit->currentCharFormat();
    fmt.setFont(font);
    ui->pltEdit->setCurrentCharFormat(fmt);
    lbFontSize->setText(QString("字体:%1").arg(font.family()));
}

在此还需要补充QPlainTextEdit相关的一些使用.

QPlainTextEdit组件定义了许多用于编辑操作的槽函数,其也有一些信号函数:

  • void blockCountChanged(int newBlockCount)//段落数变化时
  • void copyAvailable(bool yes)//有文字被选择或取消选择时
  • void cursorPositionChanged(bool changed)//光标位置变化时
  • void modificationChanged(bool changed)//文档的修改状态变化时
  • void redoAvailable(bool available)//redo操作状态变化时
  • void selectionChanged()//选择的内容变化时
  • void textChanged()//文档内容变化时
  • void undoAvailable(bool available)//undo操作状态变化时
  • void updateRequest(const QRect &rect,int dy)//需要更新显示时

QToolButton和QListWidget

Qt中用于处理项数据(item data)的组件有两类:

一类是Item Views组件,包括QListView,QTreeView,QTableView等;

另一类是Item Widgets组件,包括QListWidget,QTreeWidget,QTableWidget等.

Item Widgets组件直接将数据存储在每一个项里,一个项存储了文字,文字的格式定义,图标,用户数据等内容.

QToolBox是工具箱组件类,工具箱是一种垂直分页的多页容器组件.在UI可视化设计时,在工具箱组件上点击鼠标右键调出快捷菜单,可以分别使用Insert Page和Delete Page菜单项添加和删除页面.

工具箱的每一个页面都是一个QWidget组件,在页面的工作区可以放置任何其他界面组件.

QToolBox有一个信号,其中index是当前页面序号:

  • void QToolBox::currentChanged(int index)

对于QListWidget,其有的QListWidgetItem有一个标识变量flags,用于设置列表项的特性.

flags是枚举类型Qt::ItemFlag的枚举值的组合:

  • Selectable:列表项可被选择,Qt::ItemIsSelectable
  • Editable:列表项可被编辑,Qt::ItemIsEditable
  • DragEnabled:列表项可以被拖动,Qt::ItemIsDragEnabled
  • DropEnabled:列表项可以接收拖放的项,Qt::ItemIsDropEnabled
  • UserCheckable:列表项可以被复选,Qt::ItemIsUserCheckable
  • Enabled:列表项可用,Qt::ItemIsEnabled
  • Tristate:自动改变列表项复选状态,Qt::ItemIsAutoTristate

QToolButton继承于一般的QAbstractButton,并在此基础上有新增的属性:

  • popupMode属性:属性值为QToolButton::ToolButtonPopupMode.这个属性决定了弹出菜单的模式.
    • QToolButton::DelayedPopup:按钮上没有任何附加的显示内容.如果按钮有下拉菜单,则按下按钮并延时一会儿后才显示下拉菜单.
    • QToolButton::MenuButtonPopup:会在按钮右侧显示一个带箭头的下拉按钮.
    • QToolButton::InstantPopup:会在按钮右下角显示一个很小的下拉箭头图标.
  • toolButtonStyle属性:属性值是枚举类型Qt::ToolButtonTextBesideIcon.比欧仕工具按钮上文字标的显式方式.
  • autoRaise属性:如果设置为true,按钮就没有边框,鼠标移动到按钮上才显示边框.
  • arrowType属性:属性值是枚举类型Qt::ArrowType,默认值Qt::NoArrow.不会在上面显示内容.

除了与读写相关的一些接口函数,QToolButton类还有两个主要的函数:

  • setDefaultAction()函数:这个函数用于为工具按钮设置关联的Action
    • void QToolButton::setDefaultAction(QAction*action)
  • setMenu()函数:这个函数用于为工具按钮设置下拉菜单,其函数原型定义如下:
    • void QToolButton::setMenu(QMenu*menu)

QMenu是菜单类,它直接从QWidget继承而来.在Qt Designer中我们可以通过Action窗口创建菜单栏.

QMenu是管理菜单的类,它的父类是QWidget,菜单实际上是一种窗口.创建菜单主要会用到一下几个函数:

  • void QWidget::addAction(QAction*action)//添加Action
  • QAction *QMenu::addMenu(QMenu *menu)//添加菜单
  • QAciton *QMenu::addSeparator()//添加一个分隔条

显示菜单可以使用exec()函数,其函数原型定义如下:

  • QAction *QMenu::exec(const QPoint&p,QAction*action=nullptr)

参数p表示菜单左上角坐标,显示鼠标右键快捷菜单时,通常使用鼠标光标的当前位置QCursor::pos()作为参数p的值.

QListWidget组件的列表项是QListWidgetItem对象.QListWidgetItem没有父类,所以没有属性,但是它有一些读取函数与设置函数:

QListWidgetItem类的主要接口函数

读取函数 设置函数 数据类型 设置函数的功能
text() setText() QString 设置项的文字
icon() setIcon() QIcon 设置项的图标
data() setData() QVariant 为项的不同角色设置数据
flags() setFlags() Qt::ItemFlag 设置项的特性,是Qt::ItemFlag的组合
checkState() setCheckState() Qt::CheckState 设置项的复选状态
isSelected() setSelected() bool 设置为当前项

QListWidget的主要接口函数则见下:

分组 函数名 功能
添加或删除项 void addItem() 添加一个项
void addItems() 一次添加多个项
void insertItem() 在某一行前面插入一个项
void insertItems() 在某一行前面一次插入多个项
QListWidgetItem *takeItem() 从列表组件中移除一个项,并返回这个项
的对象指针,但不从内存中删除这个项
void clear() 移除列表中所有的项,并从内存中删除
项的访问 QListWidgetItem *currentItem() 返回当前项
void setCurrentItem() 设置当前项
QListWidgetItem *item() 根据行号返回一个项
QListWidgetItem *itemAt() 根据屏幕坐标返回项
int currentRow() 返回当前行的行号
void setCurrentRow() 设置当前行
int row() 返回一个项所在行号
int count() 返回列表组件中项的个数
排序 void setSortingEnabled() 设置列表是否可排序
bool isSortingEnabled() 列表是否可排序
void sortItems() 对列表按照指定方式排序

QListWidget组件中的每一行是一个QListWidgetItem对象.

QListWidgetItem的函数setFlags()用于设置项的一些特性.其函数原型定义如下:

  • void QListWidgetItem::setFlags(Qt::ItemFlags flags)

QListWidget定义的信号比较多,各信号的定义如下:

  • void currentItemChanged(QListWidget *current,QListWidgetItem *previous)
  • void currentRowChanged(int currentRow)
  • void currentTextChanged(const QString& currentText)
  • void itemSelectionChanged()
  • void itemChanged(QListWidgetItem *item)
  • void itemActivated(QListWidgetItem *item)
  • void itemEntered(QListWidgetItem *item)
  • void itemPressed(QListWidgetItem *item)
  • void itemClicked(QListWidget *item)
  • void itemDoubleClicked(QListWidgetItem *item)

每个继承自QWidget的类都有customContexrMenuRequested()信号,在一个组件上点击鼠标右键时,组件发射这个信号,用于请求创建快捷菜单.

要是QWidget组件在点击鼠标右键时发射customContextMenuRequested()信号,还需要设置contextMenuPolicy属性:

  • Qt::NoContextMenu:组件没有快捷菜单,由其父容器组件处理
  • Qt::PreventContextMenu:阻止快捷菜单,并且点击鼠标右键事件也不会交给父容器组件处理.
  • Qt::DefaultContextMenu:默认的快捷菜单,QWidget::contextMenuEvent()事件被自动处理.
  • Qt::ActionsContextMenu:自动根据QWidget::actions()返回的Action列表创建并显示快捷菜单.
  • Qt::CustomContextMenu::组件发射customContextMenuRequested()信号,由用户编程实现快捷菜单.

下面是一个综合使用QToolButton和QListWidget的例子:

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    connect(ui->actQuit,SIGNAL(triggered(bool)),this,SLOT(close()));

    ui->toolButton->setDefaultAction(ui->actInitList);
    ui->toolButton_2->setDefaultAction(ui->actDeleteList);
    ui->toolButton_3->setDefaultAction(ui->actAddItem);
    ui->toolButton_5->setDefaultAction(ui->actInsertItem);
    ui->toolButton_6->setDefaultAction(ui->actDeleteItem);

    ui->tbnSelAll_2->setDefaultAction(ui->actSelectAll);
    ui->tbnSelNone_2->setDefaultAction(ui->actSelectNone);
    ui->tbnSelInv_2->setDefaultAction(ui->actSelectInv);

    QMenu* menuSelection=new QMenu(this);
    menuSelection->addAction(ui->actSelectAll);
    menuSelection->addAction(ui->actSelectInv);
    menuSelection->addAction(ui->actSelectNone);

    ui->tbnSelItems->setPopupMode(QToolButton::MenuButtonPopup);
    ui->tbnSelItems->setMenu(menuSelection);
    ui->tbnSelItems->setDefaultAction(ui->actSelection);
}

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

void MainWindow::on_actInitList_triggered()
{
    ui->listWidget_2->clear();
    QIcon icon(QString(":/img/img/redo.jpg"));
    bool chk=ui->chkEditable_2->isChecked();
    for(int i=0;i<10;i++){
        QListWidgetItem* aItem= new QListWidgetItem();
        aItem->setIcon(icon);
        aItem->setText(QString("Item %1").arg(i));
        aItem->setCheckState(Qt::Checked);
        if(chk)
            aItem->setFlags(Qt::ItemIsEditable|Qt::ItemIsSelectable|Qt::ItemIsUserCheckable|Qt::ItemIsEnabled);
        else
            aItem->setFlags(Qt::ItemIsSelectable|Qt::ItemIsUserCheckable|Qt::ItemIsEnabled);
        ui->listWidget_2->addItem(aItem);
    }
}


void MainWindow::on_actDeleteList_triggered()
{
    ui->listWidget_2->clear();
}


void MainWindow::on_actAddItem_triggered()
{
    QIcon icon(QString(":/img/img/redo.jpg"));
    bool chk=ui->chkEditable_2->isChecked();
    QListWidgetItem* aItem= new QListWidgetItem();
    aItem->setIcon(icon);
    aItem->setText(QString("New Item"));
    aItem->setCheckState(Qt::Checked);
    if(chk)
        aItem->setFlags(Qt::ItemIsEditable|Qt::ItemIsSelectable|Qt::ItemIsUserCheckable|Qt::ItemIsEnabled);
    else
        aItem->setFlags(Qt::ItemIsSelectable|Qt::ItemIsUserCheckable|Qt::ItemIsEnabled);
    ui->listWidget_2->insertItem(ui->listWidget_2->currentRow(),aItem);
}


void MainWindow::on_actDeleteItem_triggered()
{
    int row= ui->listWidget_2->currentRow();
    delete ui->listWidget_2->takeItem(row);
}


void MainWindow::on_actInsertItem_triggered()
{
    QIcon icon(QString(":/img/img/redo.jpg"));
    bool chk=ui->chkEditable_2->isChecked();
    QListWidgetItem* aItem= new QListWidgetItem();
    aItem->setIcon(icon);
    aItem->setText(QString("New Item"));
    aItem->setCheckState(Qt::Checked);
    if(chk)
        aItem->setFlags(Qt::ItemIsEditable|Qt::ItemIsSelectable|Qt::ItemIsUserCheckable|Qt::ItemIsEnabled);
    else
        aItem->setFlags(Qt::ItemIsSelectable|Qt::ItemIsUserCheckable|Qt::ItemIsEnabled);
    ui->listWidget_2->addItem(aItem);
}



void MainWindow::on_listWidget_2_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous)
{
    QString str="";
    if(current!=NULL){
        str+="当前项:"+current->text();
        if(previous!=NULL)
            str+=" 前一项:"+previous->text();
        ui->litItemEdit_2->setText(str);
    }
    ui->plainTextEdit->appendPlainText(QString("currentChanged()信号被发射"));
}


void MainWindow::on_checkBox_clicked(bool checked)
{
    ui->listWidget_2->setSortingEnabled(checked);
    ui->toolButton_7->setEnabled(checked);
    ui->toolButton_8->setEnabled(checked);
}


void MainWindow::on_toolButton_7_clicked(bool checked)
{
    ui->listWidget_2->sortItems(Qt::AscendingOrder);
}


void MainWindow::on_toolButton_8_clicked(bool checked)
{
    ui->listWidget_2->sortItems(Qt::DescendingOrder);
}


void MainWindow::on_pushButton_clicked()
{
    ui->plainTextEdit->clear();
}


void MainWindow::on_pushButton_2_clicked()
{
    ui->plainTextEdit->appendPlainText(QString(""));
}


void MainWindow::on_listWidget_2_itemDoubleClicked(QListWidgetItem *item)
{
    ui->plainTextEdit->appendPlainText(QString("doubleClicked信号发射")+item->text());
}


void MainWindow::on_listWidget_2_customContextMenuRequested(const QPoint &pos)
{
    Q_UNUSED(pos);
    QMenu* menuList= new QMenu(this);
    menuList->addAction(ui->actInitList);
    menuList->addAction(ui->actDeleteItem);
    menuList->addAction(ui->actDeleteList);
    menuList->addAction(ui->actInsertItem);
    menuList->addAction(ui->actAddItem);
    menuList->addSeparator();
    menuList->addAction(ui->actSelectAll);
    menuList->addAction(ui->actSelectNone);
    menuList->addAction(ui->actSelectInv);
    menuList->exec(QCursor::pos());
    delete menuList;
}


void MainWindow::on_actSelectAll_triggered()
{
    int cnt= ui->listWidget_2->count();
    for(int i=0;i<cnt;i++){
        QListWidgetItem *aItem=ui->listWidget_2->item(i);
        aItem->setCheckState(Qt::Checked);
    }
}


void MainWindow::on_actSelectNone_triggered()
{
    int cnt= ui->listWidget_2->count();
    for(int i=0;i<cnt;i++){
        QListWidgetItem *aItem=ui->listWidget_2->item(i);
        aItem->setCheckState(Qt::Unchecked);
    }
}


void MainWindow::on_actSelectInv_triggered()
{
    int cnt= ui->listWidget_2->count();
    for(int i=0;i<cnt;i++){
        QListWidgetItem *aItem=ui->listWidget_2->item(i);
        if(aItem->checkState()==Qt::Checked)
            aItem->setCheckState(Qt::Unchecked);
        else if(aItem->checkState()==Qt::Unchecked)
            aItem->setCheckState(Qt::Checked);
    }
}



QTreeWidget

QTreeWidget简介

QTreeWidget是一种Item Widget组件.QTreeWidget组件被称为树形组件,它的项(item)被称为节点,一个树形组件内的所有节点组成的结构被称为目录树.

树形组件适合显示具有层级结构的数据,例如,Windows资源管理器中显示的文件系统.

  • QTreeWidget树形组件:实例使用树形组件管理目录与图片文件,可以添加或删除节点.
  • QDockWidget停靠组件:QDockWidget是可以在窗口上停靠或桌面上层浮动的组件.
  • QLabel标签组件:窗口右侧是一个QScrollArea组件,在它上面放置一个标签,为标签设置一个QPixmap对象显示图片.通过QPixmap的接口函数可进行图片缩放.

QDockWidget的主要属性有:

  • floating属性:表示停靠区组件是否处于浮动状态.
  • features属性:停靠区组件的特性.其为Qt::DockWidgetAreas枚举值的组合.
  • allowedAreas属性:允许停靠的区域,可以设置在窗口左侧,右侧,顶部,底部停靠,也可以设置不允许停靠.
  • windowTitle属性:停靠区窗口的标题.

一个QTreeWidget组件的显示内容分为表头和目录树两部分,表头和目录树都是QTreeWidgetItem对象.

QTreeWidget可以使用QTreeWidgetItem或简单的文字作为表头.

如果只是简单地设置表头文字,可以使用函数setHeaderLabels()将字符串列表作为表头各列的标题.

  • void QTreeWidget::setHeaderLabels(const QStringList &labels)

如果使用QTreeWidget作为表头,则可以使用函数setHeaderItem()设计表头,还可以使用headerItem()返回.

  • void QTreeWidget::setHeaderItem(QTreeWidgetItem *item)
  • QTreeWidgetItem *QTreeWidget::headerItem()

如果使用QTreeWidgetItem对象作为表头,就可以通过QTreeWidgetItem的接口函数设置表头属性.

目录树里一行就是一个节点,节点是QTreeWidgetItem对象.节点可以有直接点,子节点就是下一级的节点.

目录树里最上层的节点称为顶层节点,顶层节点没有父节点.目录树里可以有任意多个顶层节点.相关函数如下:

  • int topLevelItemCount()//返回顶层节点个数
  • void addTopLevelItem(QTreeWidgetItem *item)//添加一个顶层节点
  • void insertTopLevelItem(int index, QTreeWidgetItem *item)//插入一个顶层节点
  • int indexOfTopLevelItem(QTreeWidgetItem *item)//返回一个顶层节点的索引号
  • QTreeWidgetItem *topLevelItem(int index)//根据index返回一个顶层节点
  • QTreeWidgetItem *takeTopLevelItem(int index)//移除一个顶层节点,但是不删除

获得一个顶层节点后,就可以访问它的所有子节点.所有次级节点都直接或间接挂靠在某个顶层节点下面.

目录树中还有一个隐藏的根节点,其可以看作所有顶层节点的父节点.通过invisibleRootItem()可以访问它:

  • QTreeWidgetItem *QTreeWidget::invisibleRootItem().

使用这个结点就可以通过QTreeWidgetItem类的接口函数访问所有顶层节点.

QTreeWidget也还有一些其他的常用函数:

  • int columnCount()//返回表头列数
  • void setColumnCount(int columns)//设置表头列数
  • void sortItems(int columns,Qt::SortOrder order)//将目录树按照某一列排序
  • int sortColumn()//返回用于排序的列的编号
  • QTreeWidgetItem *currentItem()//返回当前节点
  • QList<QTreeWidgetItem*> selectedItems()//返回选择的节点列表

如果树形组件允许多选,函数selectedItems()会返回选择的节点的列表.通过QTreeWidget的上层父类QAbstractItemView的selectionMode属性能够设置选择模式,可以设置为多选.

QTreeWidget有如下几个共有槽函数:

  • void clear()//清除整个目录树
  • void collapseItem(const QTreeWidgetItem *item)//折叠节点
  • void expandItem(const QTreeWidget *item)//展开节点
  • void scrollToItem(const QTreeWidget *item,QAbstractItemView::ScrollHint hint=EnsureVisible)
    //用于保证节点item可见,必要时自动移动卷滚条

QTreeWidget类有如下几个信号:

  • void currentItemChanged(QTreeWidgetItem *current,QTreeWidgetItem *previous)
    //当选中项变化时发射
  • void itemActivated(QTreeWidgetItem *item,int column)//双击节点或按下enter时
  • void itemChanged(QTreeWidgetItem *item,int column)//其中项改变
  • void itemClicked(QTreeWidgetItem *item,int column)//其中项被单击或双击
  • void itemCollapsed(QTreeWidgetItem *item)//节点折叠时
  • void itemDoubleClicked(QTreeWidgetItem *item,int column)//其中项被双击
  • void itemEntered(QTreeWidgetItem *item,int column)//鼠标光标移动到节点上拖动时
  • void itemExpanded(QTreeWidgetItem *item)//节点展开时
  • void itemPressed(QTreeWidgetItem *item,int column)//用户选中了项
  • void itemSelectionChanged()//用户选择的项改变

为了更好地理解,这里需要对几个信号的发射条件进行区分:

currentItemChanged()信号在当前节点发生变化时被发射,current是当前节点,previous是之前节点.
itemChanged()信号在某节点的某一列的属性发生变化时被发射.
itemClicked()信号在点击节点时被发射,不管当前节点的行和列有没有变化都会触发此信号.
itemSelectionChanged()信号在用户选择的节点发生变化时被发射.

下面给出一个测试程序说明这些差异:

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    for(int i=0;i<10;i++){
        QTreeWidgetItem* new_item=new QTreeWidgetItem();
        new_item->setText(0,QString("编号"));
        new_item->setTextAlignment(0,Qt::AlignHCenter);
        new_item->setTextAlignment(0,Qt::AlignVCenter);
        new_item->setCheckState(0,Qt::Checked);
        new_item->setText(1,QString("姓名"));
        new_item->setTextAlignment(1,Qt::AlignHCenter);
        new_item->setTextAlignment(1,Qt::AlignVCenter);
        new_item->setCheckState(1,Qt::Checked);
        ui->treeWidget->addTopLevelItem(new_item);
    }
}

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

void MainWindow::on_treeWidget_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous)
{
    ui->plainTextEdit->appendPlainText(QString("currentItemChanged:%1").arg(ui->treeWidget->invisibleRootItem()->indexOfChild(current)));
    return;
}


void MainWindow::on_treeWidget_itemActivated(QTreeWidgetItem *item, int column)
{
    ui->plainTextEdit->appendPlainText(QString("itemActivated:%1").arg(ui->treeWidget->invisibleRootItem()->indexOfChild(item)));
    return;
}


void MainWindow::on_treeWidget_itemChanged(QTreeWidgetItem *item, int column)
{
    ui->plainTextEdit->appendPlainText(QString("itemChanged:%1").arg(ui->treeWidget->invisibleRootItem()->indexOfChild(item)));
    return;
}


void MainWindow::on_treeWidget_itemClicked(QTreeWidgetItem *item, int column)
{
    ui->plainTextEdit->appendPlainText(QString("itemClicked:%1").arg(ui->treeWidget->invisibleRootItem()->indexOfChild(item)));
    return;
}


void MainWindow::on_treeWidget_itemCollapsed(QTreeWidgetItem *item)
{
    ui->plainTextEdit->appendPlainText(QString("itemCollapsed:%1").arg(ui->treeWidget->invisibleRootItem()->indexOfChild(item)));
    return;
}


void MainWindow::on_treeWidget_itemDoubleClicked(QTreeWidgetItem *item, int column)
{
    ui->plainTextEdit->appendPlainText(QString("itemDoubleClicked:%1").arg(ui->treeWidget->invisibleRootItem()->indexOfChild(item)));
    return;
}


void MainWindow::on_treeWidget_itemEntered(QTreeWidgetItem *item, int column)
{
    ui->plainTextEdit->appendPlainText(QString("itemEntered:%1").arg(ui->treeWidget->invisibleRootItem()->indexOfChild(item)));
    return;
}


void MainWindow::on_treeWidget_itemExpanded(QTreeWidgetItem *item)
{
    ui->plainTextEdit->appendPlainText(QString("itemExpanded:%1").arg(ui->treeWidget->invisibleRootItem()->indexOfChild(item)));
    return;
}


void MainWindow::on_treeWidget_itemPressed(QTreeWidgetItem *item, int column)
{
    ui->plainTextEdit->appendPlainText(QString("itemPressed:%1").arg(ui->treeWidget->invisibleRootItem()->indexOfChild(item)));
    return;
}


void MainWindow::on_treeWidget_itemSelectionChanged()
{
    ui->plainTextEdit->appendPlainText(QString("itemSelectionChanged"));
    return;
}

QTreeWidgetItem简介

QTreeWidget组件的表头和目录树节点都是QTreeWidgetItem类对象,对目录树节点的操作主要通过QTreeWidgetItem类的接口函数实现.

QTreeWidgetItem类没有父类,它只用来存储结点的数据和各种属性.绘制目录树由QTreeWidget实现.

QTreeWidgetItem类有多种参数形式的构造函数,较简单的一种定义如下:

  • QTreeWidgetItem(int type=Type)

可以传递一个整数表示结点的类型,可以通过成员函数type()返回这个类型.

在创建节点时,还可以传递字符串列表作为节点各列的文字:

  • QTreeWidgetItem(const QStringList &strings,int type=Type)

也可以在某个节点下创建子节点:

  • QTreeWidgetItem(QTreeWidgetItem *parent,int type=Type)

还可以直接在树形组件里创建顶层节点:

  • QTreeWidgetItem(QTreeWidget *parent,int type=Type)

创建一个节点后,可以通过一些函数对其进行操作:

  • void setBackground(int column,const QBrush &brush)
  • void setForeground(int column,const QBrush &brush)
  • void setText(int column,const QString &text)
  • void setTextAlignment(int column,const QString &toolTip)
  • void setStatusTip(int column,const QString &statusTip)
  • void setIcon(int column,const QIcon &icon)
  • void setCheckState(int column,Qt::CheckState state)
  • void setFont(int column,const QFont &font)

Qt::Alignment的取值有:

  • Qt::AlignLeft:水平方向靠左
  • Qt::AlignRight:水平方向靠右
  • Qt::AlignTop:垂直方向靠顶部
  • Qt::AlignButton:垂直方向靠底部
  • Qt::AlignHCenter:水平方向居中
  • Qt::AlignVCenter:垂直方向居中
  • Qt::AlignCenter:水平垂直方向居中
  • Qt::AlignJustify:水平方向调整间距两端对齐

上面设置函数当然有相应的读取函数,同样需要传递参数column.

QTreeWidgetItem还有一个函数setData()用于为节点中的某一列设置用户数据,这个数据是不显示在界面上的.

  • void setData(int column,int role,const QVariant &value)
  • QVariant data(int column,int role)

参数role是用户数据角色,可以使用常量Qt::UserRole定义第一个用户数据,使用Qt::UserRole+1定义第二个用户数据.

用户数据是QVariant类型,可以存储各种类型的数据.

QTreeWidgetItem的一些常用的接口函数有:

  • int type()//返回创建节点时设置的type
  • setFlags(Qt::ItemFlags flags)//设置结点的标志
  • Qt::ItemFlags flags()//读取节点的标志
  • void setExpanded(bool expand)
  • bool isExpanded()
  • void setDisabled(bool disabled)
  • bool isDisabled()
  • void setHidden(bool hide)
  • bool isHidden()
  • void setSelected(bool select)
  • bool isSelected()

注意,函数type()所返回的是创建节点时传递的type参数,节点创建后就不能更改.

Qt::ItemFlags的取值有:

  • Qt::NoItemFlags//没有标志
  • Qt::ItemIsSelectable//节点可以被选中
  • Qt::ItemIsEditable//节点可以编辑
  • Qt::ItemIsDragEnabled//节点可以被拖动
  • Qt::ItemIsDropEnabled//节点可以接收被拖动的对象
  • Qt::ItemIsUserCheckable//节点可以被复选
  • Qt::ItemIsEnabled//节点可用
  • Qt::ItemIsAutoTristate//自动决定三种复选状态
  • Qt::ItemIsUserTristate//用户决定三种复选状态
  • Qt::ItemNeverHasChildren//不允许有子节点

而且这些节点可以用|组合.

一个节点可以有任意多个子节点,可以添加,插入或移除节点.

QTreeWidgetItem类中用于操作子节点的接口函数主要有以下几个:

  • void addChild(QTreeWidgetItem *child)//添加一个子节点
  • QTreeWidgetItem *child(int index)//根据序号返回一个子节点
  • int childCount()//返回子节点的个数
  • int indexOfChild(QTreeWidgetItem *child)//返回一个子节点的索引号
  • void insertChild(int index,QTreeWidgetItem *child)//插入一个子节点
  • void removeChile(QTreeWidget *child)//移除一个子节点
  • QTreeWidgetItem *takeChild(int index)//移除一个子节点,并返回节点指针
  • QTreeWidgetItem *parent()//返回父节点

QLabel和Qpixmap的使用

对于QPixmap类变量,QPixamp::load()直接从一个文件加载图片.

QPixmap类存储图片数据,它有以下几个函数可以用于缩放图片:

  • scaledToHeight(int height):返回一个缩放后的图片副本,图片按height缩放
  • scaledToWidth(int width):返回一个缩放后的图片副本,图片按width缩放
  • scaled(int width,int height):返回一个缩放后的图片副本,默认不保持比例

设置图片到标签的,函数原型为:void QLabel::setPixmap(const QPixmap&)

将标签放在一个QScrollArea组件上,当图片过大时,QScrollArea将会出现卷滚条.

QDockWidget的操作

程序运行时,窗口上的QDockWidget组件可以被拖动.

QDockWidget类有如下几个信号:

  • void allowedAreasChanged(Qt::DockWidgetAreas allowedAreas)//allowedAreas属性值变化时
  • void dockLocationChanged(Qt::DockWidgetArea area)//移动到其他停靠区时
  • void featuresChanged(QDockWidget::DockWidgetFeatures features)//features属性变化时
  • void topLevelChanged(bool topLevel)//floating属性值变化时
  • void visiblityChanged(bool visible)//visible属性值变化时

下面最终给出综合使用QTreeWidget与QPixmap的例子:

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QLabel>
#include <QSpinBox>
#include <QTreeWidgetItem>

QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots:
    void changeItemCaption(QTreeWidgetItem *item);

    void displayImage(QTreeWidgetItem* item);

    void on_actAddFolder_triggered();

    void on_actAddFile_triggered();

    void on_actDeleteNode_triggered();

    void on_treeWidget_2_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous);

    void on_actForeachNode_triggered();

    void on_actSuitWidth_triggered();

    void on_actSuitHeight_triggered();

    void on_actReal_triggered();

    void on_actBigger_triggered();

    void on_actSmaller_triggered();

    void on_actVisiable_triggered(bool checked);

    void on_actFloating_triggered(bool checked);

private:
    QLabel *lbFileName,*lbNodeText;
    QSpinBox *spinRadio;
    QPixmap m_pixmap;
    float radio;

    Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H

mainindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    ui->treeWidget_2->clear();

    //header
    QTreeWidgetItem *header=new QTreeWidgetItem();
    header->setText(0,"目录与文件");
    header->setText(1,"节点类型");
    header->setText(2,"最后修改日期");

    header->setTextAlignment(0,Qt::AlignHCenter|Qt::AlignVCenter);
    header->setTextAlignment(1,Qt::AlignHCenter|Qt::AlignVCenter);

    ui->treeWidget_2->setHeaderItem(header);

    //root node
    QIcon icon(QString(":/icons/img/close.jpg"));
    QTreeWidgetItem *topItem= new QTreeWidgetItem();
    topItem->setIcon(0,icon);
    topItem->setText(0,"/...");
    topItem->setText(1,"根目录");
    topItem->setText(2,"");
    topItem->setFlags(Qt::ItemIsSelectable|
        Qt::ItemIsUserCheckable|
        Qt::ItemIsEnabled|
        Qt::ItemIsAutoTristate
    );
    topItem->setCheckState(0,Qt::Checked);
    ui->treeWidget_2->addTopLevelItem(topItem);

    //status bar
    lbNodeText= new QLabel("节点标题",this);
    lbNodeText->setMinimumWidth(200);
    ui->statusbar->addWidget(lbNodeText);

    spinRadio=new QSpinBox(this);
    spinRadio->setRange(0,2000);
    spinRadio->setValue(100);
    spinRadio->setSuffix("%");
    spinRadio->setReadOnly(true);
    spinRadio->setButtonSymbols(QAbstractSpinBox::NoButtons);
    ui->statusbar->addPermanentWidget(spinRadio);

    lbFileName=new QLabel("文件名",this);
    ui->statusbar->addPermanentWidget(lbFileName);
}


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

void MainWindow::changeItemCaption(QTreeWidgetItem *item)
{
    QString str="*"+item->text(0);
    item->setText(0,str);
    if(item->childCount()>0)
        for(int i=0;i<item->childCount();i++)
            changeItemCaption(item->child(i));
    else
        return;
}

void MainWindow::displayImage(QTreeWidgetItem *item)
{
    QString filename=item->data(0,Qt::UserRole).toString();
    lbFileName->setText(filename);
    lbNodeText->setText(item->text(0));
    m_pixmap.load(filename);
    ui->actSuitWidth->trigger();

    ui->actSuitHeight->setEnabled(true);
    ui->actSuitWidth->setEnabled(true);
    ui->actBigger->setEnabled(true);
    ui->actSmaller->setEnabled(true);
    ui->actReal->setEnabled(true);
}

#include <QFileDialog>
#include <QVariant>
void MainWindow::on_actAddFolder_triggered()
{
    QString dir= QFileDialog::getExistingDirectory();
    if(dir.isEmpty())
        return;
    QTreeWidgetItem *parItem=ui->treeWidget_2->currentItem();
    if(parItem==nullptr)
        return;
    if(parItem->text(1)!="图片"){
        int cnt=dir.length();
        int i=dir.lastIndexOf("/");
        QString nodeName=dir.right(cnt-i-1);
        QTreeWidgetItem *item=new QTreeWidgetItem();
        QIcon icon(QString(":/icons/img/close.jpg"));
        item->setIcon(0,icon);
        item->setText(0,nodeName);
        item->setText(1,"目录");
        item->setText(2,"");
        item->setFlags(Qt::ItemIsSelectable|
            Qt::ItemIsUserCheckable|
            Qt::ItemIsEnabled|
            Qt::ItemIsAutoTristate
        );
        item->setData(0,Qt::UserRole,QVariant(dir));
        item->setCheckState(0,Qt::Checked);
        parItem->addChild(item);
    }
}


void MainWindow::on_actAddFile_triggered()
{
    QStringList files=QFileDialog::getOpenFileNames(this,"选择文件","","Images(*.jpg;*.png)");
    if(files.isEmpty())
        return;

    QTreeWidgetItem *parItem,*item;
    item=ui->treeWidget_2->currentItem();

    if(item->text(1)!="图片")
        parItem=item;
    else
        parItem=item->parent();

    for(int i=0;i<files.size();i++){
        QString fileName= files.at(i);
        QFileInfo fileInfo(fileName);
        QString nodeText=fileInfo.fileName();
        QDateTime dateTime=fileInfo.lastModified();
        QTreeWidgetItem *item=new QTreeWidgetItem();
        QIcon icon(fileInfo.filePath());
        item->setIcon(0,icon);
        item->setText(0,nodeText);
        item->setText(1,"图片");
        item->setText(2,dateTime.toString("yyyy-MM-dd"));
        item->setFlags(Qt::ItemIsSelectable|
                       Qt::ItemIsUserCheckable|
                       Qt::ItemIsEnabled|
                       Qt::ItemIsAutoTristate
                       );
        item->setData(0,Qt::UserRole,QVariant(fileName));
        item->setCheckState(0,Qt::Checked);
        parItem->addChild(item);
    }
    parItem->setExpanded(true);
}


void MainWindow::on_actDeleteNode_triggered()
{
    QTreeWidgetItem *item,*parItem;
    item=ui->treeWidget_2->currentItem();
    parItem=item->parent();

    if(item!=nullptr){
        parItem->removeChild(item);
        delete item;
    }
    else
        return;
}


void MainWindow::on_treeWidget_2_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous)
{
    if(current==nullptr||current==previous)
        return;

    QString itemType=current->text(1);
    if(itemType=="根目录"){
        ui->actAddFile->setEnabled(true);
        ui->actAddFolder->setEnabled(true);
        ui->actDeleteNode->setEnabled(false);
    }
    else if(itemType=="目录"){
        ui->actAddFile->setEnabled(true);
        ui->actAddFolder->setEnabled(true);
        ui->actDeleteNode->setEnabled(true);
    }
    else if(itemType=="图片"){
        ui->actAddFile->setEnabled(false);
        ui->actAddFolder->setEnabled(true);
        ui->actDeleteNode->setEnabled(true);
        displayImage(current);
    }
}


void MainWindow::on_actForeachNode_triggered()
{
    QTreeWidgetItem *item,*parItem;
    item=ui->treeWidget_2->currentItem();
    changeItemCaption(item);
}


void MainWindow::on_actSuitWidth_triggered()
{
    int width=ui->scrollArea->width()-30;
    int realWidth=m_pixmap.width();
    radio=float(width)/realWidth;

    spinRadio->setValue(radio*100);
    QPixmap pix=m_pixmap.scaledToWidth(width);
    ui->label->setPixmap(pix);
}


void MainWindow::on_actSuitHeight_triggered()
{
    int height=ui->scrollArea->height()-30;
    int realHeight=m_pixmap.height();
    radio=float(height)/realHeight;

    spinRadio->setValue(radio*100);
    QPixmap pix=m_pixmap.scaledToHeight(height);
    ui->label->setPixmap(pix);
}


void MainWindow::on_actReal_triggered()
{
    int height=ui->scrollArea->height()-30;
    int realHeight=m_pixmap.height();
    radio=float(height)/realHeight;

    spinRadio->setValue(radio*100);
    QPixmap pix=m_pixmap.scaledToHeight(realHeight);
    ui->label->setPixmap(pix);
}


void MainWindow::on_actBigger_triggered()
{
    radio*=1.2;
    int width=radio*m_pixmap.width();
    int height=radio*m_pixmap.height();
    ui->label->setPixmap(m_pixmap.scaled(width,height));
    spinRadio->setValue(radio*100);
}


void MainWindow::on_actSmaller_triggered()
{
    radio*=0.8;
    int width=radio*m_pixmap.width();
    int height=radio*m_pixmap.height();
    ui->label->setPixmap(m_pixmap.scaled(width,height));
    spinRadio->setValue(radio*100);
}


void MainWindow::on_actVisiable_triggered(bool checked)
{
    ui->dockWidget_2->setVisible(checked);
}


void MainWindow::on_actFloating_triggered(bool checked)
{
    ui->dockWidget_2->setFloating(checked);
}



QTableWidget

QTableWidget是QTableView的便利类,类QTableWidget的属性和接口主要是父类中定义的.

QTableWidget还新增了两个属性,rowCount表示数据区行数,columnCount表示数据区列数.

QTableWidget还定义了如下的几个公有槽函数:

  • void insertColumn(int column)//在列号column处插入空行
  • void removeColumn(int column)//移除列号column的一列
  • void insertRow(int row)//在行号row处插入空行
  • void removeRow(int row)//移除行号row的一行

QTableWidget表格的一个单元格通常关联一个QTableWidgetItem对象.

要为表格的一个单元格设置项,一般是先创建一个QTableWidgetItem对象item,设置各种属性,然后通过setItem()将其设置至某个单元格.其函数原型如下:

  • void setItem(int row,int column,QTableWidgetItem *item)

要移除一个单元格关联的项,可以使用含税takeItem()来实现,其函数原型如下:

  • QTableWidgetItem *takeItem(int row,int column)

函数takeItem会移除单元格关联的项,并返回这个项的对象指针,但是不删除这个对象.

QTableWidget定义了两个公有槽:

  • void clear()
  • void clearContents()

QTableWidget表格数据区有一个当前单元格,也就是获得输入焦点的单元格.相关函数如下:

当前单元格关联的QTableWidgetItem对象就是当前项,可以返回当前项的对象指针.也可以设置某个QTableWidgetItem对象为当前项,以改变当前单元格的位置.

  • int currentRow()//返回当前单元格的行号
  • int currentColumn()//返回当前单元格的列号
  • void setCurrentCell(int row,int column)//通过行号和列号设置当前单元格位置
  • QTableWidgetItem *currentItem()//返回当前项
  • void setCurrentItem(QTableWidgetItem *item)//设置当前项,改变当前单元格位置
  • QTableWidgetItem *item(int row,int column)//通过行号和列号返回项
  • int row(const QTableWidgetItem *item)//返回一个项的行号
  • int column(const QTableWidgetItem *item)//返回一个项的列号
  • void setHorizontalHeaderItem(int column)//为某列设置项
  • QTableWidgetItem *horizontalHeaderItem(int column)//返回column列的表头项
  • QTableWidgetItem *takeHorizontalHeaderItem(int column)//移除column列的表头项
  • void setHorizontalHeaderLabels(const QStringList &labels)
    //使用字符串列表的每一行作为水平表头每一列的标题
  • setVerticalHeaderItem(int row,QTableWidgetItem *item)//设置row的表头项
  • QTableWidgetItem *verticalHeaderItem(int row)//返回row的表头项
  • QTableWidgetItem *takeVerticalHeaderItem(int row)//移除row的表头项
  • void setVerticalHeaderLabels(const QStringList &labels)//用一个字符串列表设置表头标题

QTableWidget定义了较多的信号:

  • void cellActivated(int row,int column)//单元格被激活时
  • void cellChanged(int row,int column)//单元格的数据改变时
  • void cellClicked(int row,int column)//在单元格上单击鼠标时
  • void cellDoubleClicked(int row,int column)//在单元格上双击鼠标时
  • void cellEntered(int row,int column)//鼠标移动到一个单元格上时
  • void cellPressed(int row,int column)//在单元格上按下鼠标左键或右键时
  • void currentCellChanged(int currentRow,int currentColumn,int previousRow,int previousColumn)
    //当前单元格发生切换时
  • void itemActivated(QTableWidgetItem *item)//项被激活时
  • void itemChanged(QTableWidgetItem *item)//项内容改变时
  • void itemClicked(QTableWidgetItem *item)//项被点击
  • void itemDoubleClicked(QTableWidgetItem *item)//项被双击
  • void itemEntered(QTableWidgetItem *item)//鼠标移动到一个项上时
  • void itemPressed(QTableWidgetItem *item)//在项上按下鼠标左键或右键时
  • void currentItemChanged(QTableWidgetItem *current,QTableWidgetItem *previous)
    //当前项发生改变时
  • void itemSelectionChanged()//选择的项发生变化时

QTableWidgeItem关联的每个单元格都需要关联一个QTableWidgetItem对象.

QTableWidgetItem有多种参数形式的构造函数,其中的三种构造函数定义如下:

  • QTableWidgetItem(const QIcon &icon,const QString &text,int type=Type)
  • QTableWidgetItem(const QString &text,int type=Type)
  • QTableWidgetItem(int type=Type)

创建QTableWidgetItem对象后,通过QTableWidget::setItem()函数可以将其设置为某个单元格的项.

QTableWidgetItem对象有一些接口函数用于设置项的特性:

  • void setText(const QString &text)//设置单元格显示文字
  • void setTextAlignment(int alignment)//设置文字对其方式
  • void setBackground(const QBrush &brush)//设置背景色
  • void setForeground(const QBrush &brush)//设置前景色
  • void setFont(const QFont &font)//设置字体
  • void setIcon(const QIcon &icon)//设置图标
  • void setCheckState(Qt::CheckState state)//设置复选状态
  • void setToolTip(const QString &toolTip)//设置toolTip
  • void setStatusTip(const QString &statusTip)//设置statusTip
  • void setFlags(Qt::ItemFlags flags)//设置标志
  • void setData(int role,const QVariant &value)//设置数据
  • int row()//返回项所在单元格行号
  • int column()//返回项所在单元格的列号
  • void setSelected(bool select)//设置项的选中状态
  • bool isSelected()//项是否被选中
  • QTableWidget *tableWidget()//返回项所在的QTableWidget对象指针

后记:好长的一章,一章更比四章强( )

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。