7. 窗口组件

上一章节中,了解了Qt的部分基础控件和布局管理,这章我们来看看Qt中的功能稍微复杂点的窗口和窗口类部件。

窗口使用我们一般基于QWidget创建,如果是用做主窗口,使用QMainWindow创建,如果是创建顶级对话框,则基于QDialog创建。

这章将简单介绍下常用的窗口:QMainWindow、QWidget、QDialog(将会在下一章讲解), 并且会一起介绍下常用的窗口类部件-QListWidget、QStackedWidget、QTableWidget等等。

7.1. QMainWindow

QMainWindow类提供一个主窗口类,用于构建应用程序用户界面的框架。其默认布局如下:

mainwindow001

默认的QMainWindow窗口具有包含主菜单栏(QMenuBar),工具栏(QToolBar),(停靠窗口)QDockWidget和状态栏(QStatusBar),以及一个 (中央窗口)centralWidget。

7.1.1. 主窗口框架

QMainWindow中用QMenuBar(菜单栏)来显示菜单。

QMenuBar 类提供了一个水平菜单栏控件,由下拉菜单(QMenu)组成,菜单选项可以设置图标,菜单文本,快捷方式,状态文本,文字和工具提示等等属性。 我们可以使用addMenu()和removeMenu()来向控件栏添加或移除菜单。

在QMenu下我们可以继续添加子QMenu或者添加QAction, QAction 类是可以插入到小部件中的抽象用户界面操作,也就是菜单选项,是一个和用户交互的“动作”。

QAction通过绑定到槽函数来实现相关的用户操作。 使用addAction()和removeAction()可用来增加或移除QAction。 在Qt Designer有个专门的QAction栏,可以在QMenuBar上添加和删除QAction:

mainwindow002

Qt Designer 中可以直接点击菜单栏添加新的菜单选项,添加的选项会记录在下面的菜单窗口。 点击菜单窗口可对相关选项进行编辑,比如鼠标右键某个菜单可以跳转到相关的槽, Qt Designer 会在对象的头文件和源文件中创建相应的槽函数。

也可以通过代码来创建菜单和菜单选项:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
QMenu *mainMenu = new QMenu(this);                 //创建一个主菜单mainMenu
mainMenu->setTitle("mainMenu");

QAction *action1 = new QAction(mainMenu);          //在主菜单mainMenu下,添加action1
action1->setObjectName("action1");
action1->setText("action1");
connect(action1,SIGNAL(triggered()),this,SLOT(menu_clicked()));  //关联action1操作的槽函数

QAction *action2 = new QAction(mainMenu);           //在主菜单mainMenu下,添加action2
action2->setObjectName("action2");
action2->setText("action2");
connect(action2,SIGNAL(triggered()),this,SLOT(menu_clicked()));  //关联action2操作的槽函数
QList<QAction *> actionlist;
actionlist << action1 << action2;

QMenu *nextMenu = new QMenu(mainMenu);         //在mainMenu菜单下,创建一个子菜单nextMenu
nextMenu->setTitle("nextMenu&N");

QAction *action3 = new QAction(nextMenu);      //在子菜单nextMenu下,添加action3
connect(action3,SIGNAL(triggered()),this,SLOT(menu_clicked()));
action3->setObjectName("action3");
action3->setText("action3");

QAction *action4 = new QAction(nextMenu);      //在子菜单nextMenu下,添加action4
action4->setObjectName("action4");
action4->setText("action4");
connect(action4,SIGNAL(triggered()),this,SLOT(menu_clicked()));
nextMenu->addAction(action3);
nextMenu->addAction(action4);

mainMenu->addActions(actionlist);
mainMenu->addMenu(nextMenu);
ui->menuBar->addMenu(mainMenu); //往QMainWindow的菜单栏添加

上面的代码分别创建了四个菜单项,action1-action4,并创建了两个菜单mainMenu和nextMenu, 其中nextMenu作为mainMenu的二级菜单,最后将菜单添加到主窗口的水平菜单栏控件中。

QToolBar 提供了一个包含一组控件的可移动面板,可以分别停靠在主窗口的上下左右侧, 我们可以将我们创建的Action添加到QToolBar中,也可以通过addWidget()添加其他控件。

QStatusBar 可用来显示程序的状态信息,状态指示可分为临时,正常和永久。 临时消息通过showMessage()显示,可设置消息显示时间,永久消息可以通过创建一个QLabel, QProgressBar甚至是QToolButton来记录消息,并通过addWidget()或者addPermanentWidget()将消息添加到状态栏中。

centralWidget是我们在QMainWindow放置控件和窗口的地方,centralWidget支持具有单个(SDI)或多个(MDI)文档界面。

关于QToolBar、QStatusBar、centralWidget类的更多的信息参考下Qt 帮助文档。

7.1.2. 测试例程

我们使用QMainWindow,创建一个窗口,模仿下最基本的文本编辑器,只是有文本操作等选项,仅仅用于学习下QMainWindow的相关知识。

使用Qt Creator新建一个工程,需要注意是基于QMainWindow的类,勾选form窗体:

mainwindow03.png

创建工程后,右击工程目录添加资源文件(添加下后面使用Icon图标),具体文件参考下例程文件。

1、在Qt Designer界面,调整窗口的大小,然后在菜单栏(QMenuBar)上添加菜单(QMenu)。 例程是添加了三个,分别是 文件(&F)编辑(&E)帮助(&H) :

mainwindow03.png

2、在菜单中添加QAction,并设置图标,快捷键(直接键盘录入)等操作。例如:在文件目录下创建一个 新建 操作,然后设置下

mainwindow03.png

3、右击添加工具栏(可以改变工具栏位置),然后在工具栏添加QAction,只需把QAction拖到工具栏即可(或者使用代码ui->toolBar->addWidget()添加QAction到工具栏):

mainwindow03.png

4、在中央窗口(centralWidget)添加一个编辑框,添加一段文本,然后添加信号与槽。 文件的创建相关操作(这里实际是没有文件创建和保存,只是模拟下),我们直接右击QAction,然后点击转到槽:

mainwindow03.png

其他的一些QAction,使用信号与槽编辑器,新建连接,关联到plainTextEdit的公共槽函数,进行一些文本的操作:

mainwindow03.png

5、在mainwindow中初始化窗口,添加一些无法可视化设计的组件,然后关联相关信号与槽。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
//窗口菜单栏添加手动一个
QAction *aboutQtAct = new QAction(this);
aboutQtAct->setObjectName("action_about");
aboutQtAct->setText("About &Qt");
aboutQtAct->setIcon(QIcon(":/about.png"));
connect(aboutQtAct, &QAction::triggered, qApp, &QApplication::aboutQt); //关联action_about操作的槽函数
ui->menu->addAction(aboutQtAct);

//窗口工具栏上添加工具,字体和字号,以及退出
ui->toolBar->addSeparator();      //工具栏上增加分隔条

fontSize = new QSpinBox(this);    //QSpinBox来设置plain_textedit字号
fontSize->setMinimum(10);
fontSize->setMaximum(30);
fontSize->setValue(ui->plain_textedit->font().pointSize()); //设置plain_textedit字号
fontSize->setMinimumWidth(30);
ui->toolBar->addWidget(fontSize);      //添加字号选择到工具栏

fontName = new QFontComboBox(this);    //字体名称ComboBox
fontName->setMinimumWidth(50);
ui->toolBar->addWidget(fontName);      //添加字体选择到工具栏

//窗口状态栏添加信息
labFile = new QLabel(this);            //用于显示当前
labFile->setMinimumWidth(40);
labFile->setText("文本状态");
ui->statusbar->addWidget(labFile);     //添加到状态栏

labRow = new QLabel(this);             //状态栏里显示行
labRow->setMinimumWidth(10);
labRow->setText("行: ");
ui->statusbar->addWidget(labRow); //添加到状态栏

labColumn = new QLabel(this);     //状态栏里显示列
labColumn->setMinimumWidth(10);
labColumn->setText("列: ");
ui->statusbar->addWidget(labColumn); //添加到状态栏

labInfo=new QLabel(this);  //用于显示字体名称的标签
labInfo->setText("字体名称:"+fontName->currentFont().family());
ui->statusbar->addPermanentWidget(labInfo); //添加到状态栏

//关联fontSize值的改变和设置字号
connect(fontSize,SIGNAL(valueChanged(int)), this, SLOT(do_fontSize_changed(int)));
//关联fontName选择字体的改变和设置文本框字体
connect(fontName,SIGNAL(currentFontChanged(QFont)), this, SLOT(do_fontSelected(QFont)));

6、编译测试,使用lubancat_rk套件编译,并远程运行:

mainwindow03.png

完整程序参考配套例程:lubancat_qt_tutorial_code/QmainWindow

7.2. QWidget

QWidget类是大部分用户界面对象的基类, 我们前面一章节学习了一些基础的控件,大部分都是继承于QWidget类,这里学习下单独作为窗口的QWidget(顶层窗口),以及QWidget类派生的窗口类。

提示

一般而言,当一个组件没有嵌入到其他组件中,则把这个组件叫顶层窗口或者窗口;当一个窗口嵌入到其它窗口中,则它本身的标题栏会隐藏,就叫作控件或者子窗口。

QWidget作窗口时相关的属性:

属性

值/类型

说明

windowFilePath

QString

窗口或者控件的文件路径

windowFlags

Qt::WindowFlags

窗口风格的组合

windowIcon

QIcon

窗口的图标

windowModality

Qt::WindowModality

窗口被模态窗口(当前窗口)给屏蔽了

windowModified

bool

表示窗口中的文档是否被修改

windowOpacity

double

窗口的透明度,取值范围是0~1(默认是1,不透明)

windowTitle

QString

窗口标题文字

窗口的几何图形属性(图片来自于 ):

mainwindow01

frameGeometry,frameSize,x,y,pos 是框架的几何区域和大小属性,框架是指窗口的最外层。 geometry,width,height,size,rect 是内部绘图区域的相关属性。

QWidget中定义的信号:

  • void customContextMenuRequested(const QPoint &pos) // 窗口组件上右击时触发信号,一般用于创建组件的快捷菜单

  • void windowIconChanged(const QIcon &icon) // 窗口图标改变触发信号

  • void windowTitleChanged(const QString &title) // 窗口标题改变触发信号

关于Qwidget类的公共函数或者其他属性参考下 这里 或者直接查看Qt Assistant帮助文档。

7.2.1. QFrame

QFrame 继承自QWidget,作为有框架的窗口的基类。QFrame类可以直接用于创建没有任何内容的简单占位符框架。

widget002

框架样式由框架形状和阴影样式指定,阴影样式用于在视觉上将框架与周围的小部件分开。 这些属性可以使用setFrameStyle()函数一起设置,并可以使用frameStyle()读取。

框架形状为NoFrame,Box,Panel,StyledPanel,HLine和VLine ; 阴影样式为Plain,Raised和Sunken。可以在qframe.h中查看。

qframe.h
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
enum Shape {
    NoFrame  = 0, // no frame
    Box = 0x0001, // rectangular box
    Panel = 0x0002, // rectangular panel
    WinPanel = 0x0003, // rectangular panel (Windows)
    HLine = 0x0004, // horizontal line
    VLine = 0x0005, // vertical line
    StyledPanel = 0x0006 // rectangular panel depending on the GUI style
};

enum Shadow {
    Plain = 0x0010, // plain line
    Raised = 0x0020, // raised shadow effect
    Sunken = 0x0030 // sunken shadow effect
};

我们也可以通过如下两个方法来设置框架阴影和形状。

setFrameShadow(QFrame::Shadow)
setFrameShape(QFrame::Shape)

框架小部件具有描述边框厚度的三个属性:lineWidth,midLineWidth和frameWidth。

  • lineWidth是框架边框的宽度。可以对其进行修改以自定义框架的外观。

  • midLineWidt指定了帧中间多余的线的宽度,该线使用第三种颜色来获得特殊的3D效果。请注意,仅针对凸起,凹陷的Box,HLine和VLine框架绘制中线。

  • frameWidth由框架样式确定,并且frameWidth()函数用于获取为所用样式定义的值。

框架和框架内容之间的边距可以使用QWidget::setContentsMargins()函数自定义。

7.2.2. QGroupBox

组合框带标题的组箱形框架。其顶部有一个标题,内部可以容纳各种窗口或控件。

widget003

QGroupBox还允许您设置标题(通常在构造函数中设置)和标题的alignment属性来控制标题显示位置。 组合框运行设置checkable来设置组合框中的子窗口是否启用。

您可以通过启用flat属性来最大程度地减少组框的空间消耗,启用此属性会导致删除组合框的左,右和下边缘。

QGroupBox中通常使用QCheckBox或QRadioButton, 使用QRadioButton时候,启用autoExclusive可以实现多个选项的单选效果。

7.2.3. QToolBox

QToolBox 类提供一列带标签的窗口,点击标签可以实现窗口收缩切换。

widget004

每个标签都带有一个窗口,窗口为QWidget,允许任意添加控件。标签可以设置标签显示文字,Icon以及窗口内容。

QToolBox允许使用addItem()添加标签或使用insertItem()在特定位置插入标签。标签总数由count()获取。 delete()用于删除标签,也可以使用removeItem()从QToolBox中删除标签。

currentIndex()返回QToolBox当前选中的标签索引,使用setCurrentIndex()切换当前选中的标签。

7.2.4. QScrollArea

QScrollArea 类提供了另一个窗口小部件的滚动视图。

滚动区域用于显示框架内子窗口小部件的内容。如果窗口小部件超出框架的大小,则视图可以提供滚动条,以便可以查看子窗口小部件的整个区域。

缩放图像时,滚动区域可以提供必要的滚动条:滚动条的外观取决于当前设置的滚动条策略。可以使用QAbstractScrollArea继承的功能来控制滚动条的外观。

widget005

7.2.5. QStackedWidget

QStackedWidget 类提供了多个Widget堆叠在一起,每个Widget叫做页,每次只能有一个Widget可见。

widget006

在QtDesigner中设计的时候,我们可以通过鼠标右键来添加/删除页,每个页可以单独进行UI设计。 我们可以通过currentIndex()和setCurrentIndex()获取和设置当前页(当前显示Widget)。

在代码中我们还可以通过addWidget()和removeWidget()来新增和删除页。

lubancat_qt_tutorial_code/QtWidget/Control_2/mainwindows.cpp
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//按键控制页面切换
void MainWindow::on_btn_stackedWidget_toggle_clicked()
{
    int page=ui->stackedWidget_demo->currentIndex();

    if(++page>=ui->stackedWidget_demo->count())
        page=0;

    ui->stackedWidget_demo->setCurrentIndex(page);
}

//将label插入页
void MainWindow::on_btn_stackedWidget_insert_clicked()
{
    QLabel *label= new QLabel();
    label->setText(QString("page%1").arg(ui->stackedWidget_demo->count()+1));
    label->setAlignment(Qt::AlignCenter);
    ui->stackedWidget_demo->addWidget(label);
    ui->stackedWidget_demo->setCurrentIndex(ui->stackedWidget_demo->indexOf(label));
}

//删除当前页
void MainWindow::on_btn_stackedWidget_delete_clicked()
{
    ui->stackedWidget_demo->removeWidget(ui->stackedWidget_demo->currentWidget());
}

在例程中,有三个按键,我们通过来实现QStackedWidget当前显示窗口的切换以及插入、删除页。

7.2.6. QTabWidget

QTabWidget 类提供了一堆标签式的窗口。

widget007

默认情况下,标签栏显示在页面区域上方,但是可以使用setTabPosition()来设置标签栏的显示的位置。 每个选项卡都与一个不同的窗口(称为页)相关联。页面区域中仅显示当前页面,其他所有页面均被隐藏。

和QStackedWidget用法差不多,currentIndex()和setCurrentIndex()用来获取和设置当前页面索引。 addTab()和removeTab()用来新增和删除页。

lubancat_qt_tutorial_code/QtWidget/Control_2/mainwindows.cpp
1
2
3
4
void MainWindow::on_cbox_tabWidget_currentIndexChanged(int index)
{
    ui->tabWidget->setTabPosition((QTabWidget::TabPosition)index);
}

例程中,当我们改变下拉选框的选项时标签栏的位置会随之改变。

7.2.7. QListWidget

QListWidget 类提供了带有列表项的列表视图窗口。

widget008

QListWidget是一个便利类,提供与QListView提供的列表视图类似的列表视图, 但具有用于添加和删除项目的经典基于Item的界面。

QListWidget使用内部模型来管理列表中的每个QListWidgetItem, QListWidgetItem就是QListWidget中具体某一列。

有两种方式构建QListWidgetItem,

lubancat_qt_tutorial_code/QtWidget/Control_2/mainwindows.cpp
1
2
3
4
5
6
//方式1,listWidget作为item父窗口
new QListWidgetItem(tr("text"), listWidget);

//方式2,将item插入到listWidget
QListWidgetItem *newItem = new QListWidgetItem;
listWidget->insertItem(row, newItem);

QListWidgetItem 提供 setText(),setCheckState(), setFlags() 等等方法来设置item属性。

标志

描述

Qt::NoItemFlags

0

它没有设置任何属性

Qt::ItemIsSelectable

1

可以选择

Qt::ItemIsEditable

2

可以编辑

Qt::ItemIsDragEnabled

4

可以拖动它

Qt::ItemIsDropEnabled

8

可以用作放置目标

Qt::ItemIsUserCheckable

16

用户可以选中它,也可以不选中它

Qt::ItemIsEnabled

32

用户可以与该项目进行交互

Qt::ItemIsAutoTristate

64

项的状态取决于其子项的状态。这样可以自动管理QTreeWidget中父项的状态(检查是否选中了所有子项;如果选中了所有子项,则不选中;如果仅选中了一些子项,则部分选中)

Qt::ItemIsTristate

ItemIsAutoTristate

此枚举值已弃用。改用Qt :: ItemIsAutoTristate

Qt::ItemNeverHasChildren

128

该物品永远不会有子物品。这仅用于优化目的

Qt::ItemIsUserTristate

256

用户可以在三个单独的状态之间循环。这个值是在Qt 5.5中添加的

例如在例程中,item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEditable |Qt::ItemIsUserCheckable |Qt::ItemIsEnabled);这个item就能够被点击被选中被编辑。

QListWidget的操作无外乎就是对列表的item进行添加,修改,删除。

  • 添加item可以使用addItem(),insertItem();

  • 删除时需先获取item,然后delete。

  • 修改,currentItem(),setCurrentItem()可以获取当前的列和item的位置,再对item进行修改。同时也可以通过信号和槽,如当前QListWidget中当前item发生改变会产生currentItemChanged()信号,item被点击会产生itemClicked()等信号,通过这些信号我们可以获取受影响的item并进行相关操作。

lubancat_qt_tutorial_code/Control_2/mainwindows.cpp
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
void MainWindow::on_btn_listWidget_add_clicked()
{
    QListWidgetItem *item=new QListWidgetItem();

    item->setText(QString("当前列:%1").arg(ui->listWidget->count()));
    if(ui->cbox_listWidget_style->currentText()=="style 1")
    {
        item->setIcon(QIcon(":/images/ic_hand_30.png"));
    }
    else if((ui->cbox_listWidget_style->currentText()=="style 2"))
    {
        item->setCheckState(Qt::Unchecked);
    }
    else if((ui->cbox_listWidget_style->currentText()=="style 3"))
    {
        item->setIcon(QIcon(":/images/ic_handle.png"));
        item->setCheckState(Qt::Checked);
    }

    if (ui->cbox_edit->isChecked())
        item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEditable |Qt::ItemIsUserCheckable |Qt::ItemIsEnabled);
    else
        item->setFlags(Qt::ItemIsSelectable |Qt::ItemIsUserCheckable |Qt::ItemIsEnabled);

    ui->listWidget->addItem(item);
}
void MainWindow::on_btn_listWidget_insert_clicked()
{
    QListWidgetItem *item=new QListWidgetItem();
    ...
    ui->listWidget->insertItem(ui->listWidget->currentRow(),item);
}

void MainWindow::on_btn_listWidget_delete_clicked()
{
    QListWidgetItem* item=ui->listWidget->takeItem(ui->listWidget->currentRow());
    delete item;
}

void MainWindow::on_btn_listWidget_clear_clicked()
{
    ui->listWidget->clear();
}

在例程中,演示了列表的添加、插入,删除和清空。

7.2.8. QTreeWidget

QTreeWidget 类提供使用预定义树模型的树视图窗口。

QTreeWidget类是一个便利类,它为标准树小部件提供一个经典的基于Item的界面, 此类基于Qt的Model/View体系结构,并使用默认模型来保存Item。

树结构在客观世界中广泛存在,如人类社会的族谱和各种社会组织机构都可用树形象表示。 树在计算机领域中也得到广泛应用,如在编译源程序如下时,可用树表示源源程序如下的语法结构。 又如在数据库系统中,树型结构也是信息的重要组织形式之一。 一切具有层次关系的问题都可用树来描述。

在不使用Model/View框架的时候,使用QTreeWidget可以很简单的创建树结构的窗口以 一种更灵活的方法涉及将QTreeView与标准项目模型结合在一起。从而实现数据存储和表示相分别。

目录就是一种典型的树结构

widget009

提示

点击“选择目录”,尽量选择目录文件较少的目录。

例程中,我们使用QTreeWidget展示某个目录下的所有文件和文件夹

lubancat_qt_tutorial_code/Widget/mainwindows.cpp
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
void MainWindow::on_btn_treeWidget_path_clicked()
{
    QString curPath=QDir::currentPath();
    QString dlgTitle="选择文件夹";
    QString rootpath = QFileDialog::getExistingDirectory(this,dlgTitle,curPath);
    if (rootpath.isEmpty())
        return;

    QTreeWidgetItem* root = new QTreeWidgetItem(QStringList()<<rootpath);
    root->setIcon(0, QIcon(":/images/notepad/ic_file.png"));
    root->setCheckState(1, Qt::Checked);
    searchFile(root,rootpath);

    ui->treeWidget->addTopLevelItem(root);
}

QFileInfoList MainWindow::searchFile(QTreeWidgetItem *root,QString path)
{
    QDir dir(path);
    QDir file(path);
    file.setFilter(QDir::Files | QDir::Hidden | QDir::NoSymLinks);
    file.setSorting(QDir::Size | QDir::Reversed);
    QFileInfoList fileList = file.entryInfoList();

    //当前目录的file
    foreach(auto file, fileList)
    {
        QTreeWidgetItem* child = new QTreeWidgetItem(QStringList()<<file.fileName());
        child->setIcon(0, QIcon(":/images/notepad/ic_setting.png"));
        child->setCheckState(1, Qt::Checked);
        root->addChild(child);
    }

    QFileInfoList fileListCur=dir.entryInfoList(QDir::Files | QDir::Hidden | QDir::NoSymLinks);
    QFileInfoList folderList = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);


    //当前目录的dir
    foreach(auto folder, folderList)
    {
        QTreeWidgetItem* childroot = new QTreeWidgetItem(QStringList()<<folder.fileName());
        childroot->setIcon(0, QIcon(":/images/notepad/menu_icon.png"));
        childroot->setCheckState(1, Qt::Checked);
        root->addChild(childroot);
        QFileInfoList fileListChild = searchFile(childroot,folder.absoluteFilePath());
        fileListCur.append(fileListChild);
        fileListCur.append(folder.fileName());
    }

    return fileListCur;
}

void MainWindow::on_btn_treeWidget_delete_clicked()
{
    QTreeWidgetItem* item = ui->treeWidget->currentItem();
    delete item;
}
  • 首先选择我们想要展示的目录,定义QTreeWidgetItem来记录目录下的文件和文件夹,

  • 第一步将选择的目录作为根目录, 添加到QTreeWidgetItem中;

  • 然后使用searchFile()遍历该目录,将目录下的文件作为子节点添加到QTreeWidgetItem;

  • 其次遍历文件夹,将文件夹也作为子节点添加到QTreeWidgetItem;

  • 遍历文件夹的时候,递归调用searchFile(),将当前文件夹下一级的文件和文件夹作为次一级的子节点添加到QTreeWidgetItem;

直至我们选择要展示的目录中的所有文件和文件夹都添加到QTreeWidgetItem,再调用addTopLevelItem() 将记录下的所有item显示在QTreeWidget中。

7.2.9. QTableWidget

QTableWidget 类提供具有默认模型的基于Item的表视图窗口。 表格小部件为应用程序提供了标准的表格显示工具。QTableWidget中的Item为QTableWidgetItem。

widget010

操作QTableWidget表格:

lubancat_qt_tutorial_code/widget/mainwindows.cpp
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
// 初始化一个QTableWidget表格,添加行和列
void MainWindow::on_btn_tableWidget_init_clicked()
{
    ui->tableWidget->setRowCount(ui->spinBox_row->value());
    ui->tableWidget->setColumnCount(ui->spinBox_col->value());
}

// 添加行
void MainWindow::on_btn_tableWidget_addrow_clicked()
{
    int curRow=ui->tableWidget->currentRow();
    ui->tableWidget->insertRow(curRow);
}

// 添加列
void MainWindow::on_btn_tableWidget_addcol_clicked()
{
    int curCol=ui->tableWidget->currentColumn();
    ui->tableWidget->insertColumn(curCol);
}

// 删除行
void MainWindow::on_btn_tableWidget_delrow_clicked()
{
    int curRow=ui->tableWidget->currentRow();
    ui->tableWidget->removeRow(curRow);
}

// 删除列
void MainWindow::on_btn_tableWidget_delcol_clicked()
{
    int curCol=ui->tableWidget->currentColumn();
    ui->tableWidget->removeColumn(curCol);
}

// 清除表格item
void MainWindow::on_btn_tableWidget_cleartable_clicked()
{
    ui->tableWidget->clear();
}

// 清除表格内容
void MainWindow::on_btn_tableWidget_cleardate_clicked()
{
    ui->tableWidget->clearContents();
}

// 自动调整行与列
void MainWindow::on_btn_tableWidget_adjust_clicked()
{
    ui->tableWidget->resizeColumnsToContents();
    ui->tableWidget->resizeRowsToContents();
}

// 在当前选中的表格,插入item
void MainWindow::on_btn_tableWidget_insertitem_clicked()
{
    for(int i=0; i< ui->tableWidget->columnCount();i++ )
    {
        for(int j=0; j<ui->tableWidget->rowCount(); j++)
        {
            QTableWidgetItem *item = new QTableWidgetItem();
            item->setText(QString("item(%1,%2)").arg(i+1).arg(j+1));
            ui->tableWidget->setItem(i,j,item);
        }
    }
}

void MainWindow::on_cbox_table_title_clicked(bool checked)
{
    ui->tableWidget->verticalHeader()->setVisible(checked); //隐藏列表头
    ui->tableWidget->horizontalHeader()->setVisible(checked); //隐藏行表头
}
void MainWindow::on_cbox_tableWidget_EditTrigger_currentIndexChanged(int index)
{
    ui->tableWidget->setEditTriggers(QAbstractItemView::EditTrigger(index));
}

void MainWindow::on_cbox_tableWidget_SelectionBehavior_currentIndexChanged(int index)
{
    ui->tableWidget->setSelectionBehavior(QAbstractItemView::SelectionBehavior(index));
}

void MainWindow::on_cbox_tableWidget_SelectionMode_currentIndexChanged(int index)
{
    ui->tableWidget->setSelectionMode(QAbstractItemView::SelectionMode(index));
}

// 显示当前选中的item的行和列
void MainWindow::on_tableWidget_cellClicked(int row, int column)
{
    ui->lab_pos_x->setText(QString::number(row+1));
    ui->lab_pos_y->setText(QString::number(column+1));
}

// 显示当前点击选中的item的内容
void MainWindow::on_tableWidget_itemClicked(QTableWidgetItem *item)
{
    ui->lab_content->setText(item->text());
}

在例程中,实现了一组对QTableWidget的操作,item的插入和清除; 除此以外还有多Item的选择模式,选择行为,item编辑的触发方式进行了设置。 绑定了tableWidget的两个信号,cellClicked(int row, int column)和itemClicked(QTableWidgetItem *item);用来显示当前点击的item序列和item内容。

7.3. 其他窗口

7.3.1. QDockWidget

QDockWidget即为可停靠窗口,也可以作为桌面上的顶级窗口浮动,具体位置可以看下前面QMainWindow的图片。

widget011

例程中左侧放置了一系列按键的窗口就是QDockWidget,我们可以拖动它停靠在主窗口的上下左右不同的地方,同时也可以独立显示在桌面上。

QDockWidget由标题栏和内容区域组成。标题栏显示停靠小部件窗口标题,有浮动按钮和关闭按钮。 根据QDockWidget的状态,浮动和关闭按钮可能被禁用或根本不显示,标题栏和按钮的外观取决于使用的样式。

QDockWidget充当其子窗口小部件的包装,该子窗口小部件由setWidget()设置。 自定义大小提示,最小和最大大小以及大小策略应在子窗口小部件中实现。 QDockWidget将尊重它们,调整其自身的约束以包括框架和标题。 不应在QDockWidget本身上设置大小限制,因为它们会根据是否停靠而改变;停靠的QDockWidget没有框架和较小的标题栏。

lubancat_qt_tutorial_code/widget/mainwindows.cpp
1
ui->dockWidget->setVisible(true);

代码中可以使用setVisible()来显示/隐藏QDockWidget。

7.3.2. QMdiArea

QMdiArea小部件提供了一个在其中显示MDI窗口的区域。

本质上,QMdiArea的功能类似于MDI窗口的窗口管理器。 例如,它绘制自己管理的窗口,并以级联或平铺模式排列它们。 QMdiArea通常用作QMainWindow中的中心小部件,以创建MDI应用程序。

widget012

与顶级窗口的窗口管理器不同,QMdiArea支持所有窗口标志(Qt :: WindowFlags),只要当前窗口小部件样式支持该标志即可。 如果样式不支持特定标志(例如WindowShadeButtonHint),则仍可以使用showShaded()来为窗口着色。

在QMdiArea子窗口的实例QMdiSubWindow。它们通过addSubWindow()添加到MDI区域。 通常将传递给内部控件的QWidget传递给此函数,但是也可以直接传递QMdiSubWindow。 该类继承了QWidget,并且可以使用与普通顶层相同的API编程时的窗口。QMdiSubWindow也具有特定于MDI窗口的行为。 有关更多详细信息,请参见QMdiSubWindow类描述。

子窗口在获得键盘焦点时或在调用setFocus()时变为活动状态。用户通过以通常的方式移动焦点来激活窗口。 当活动窗口更改时,MDI区域会发出subWindowActivated()信号,而activeSubWindow()函数将返回活动子窗口。 便捷函数subWindowList()返回所有子窗口的列表。

例如,可以在包含窗口列表的弹出菜单中使用此信息。子窗口按当前WindowOrder排序。 这是用于subWindowList()和用于activateNextSubWindow()和activatePreviousSubWindow()。 另外,在通过cascadeSubWindows()和tileSubWindows()级联或平铺窗口时使用它。

QMdiArea为子窗口提供了两种内置的布局策略:cascadeSubWindows()和tileSubWindows()。 两者都是插槽,可轻松连接到菜单条目。

lubancat_qt_tutorial_code/Control_2/mainwindows.cpp
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
void MainWindow::on_btn_mdiArea_add_clicked()
{
    QLabel *label= new QLabel(this);
    QString str(QString("subwindow %1").arg(ui->mdiArea->subWindowList().count()));
    label->setObjectName(str);
    label->setWindowTitle(str);
    label->setText(str);
    label->setAlignment(Qt::AlignCenter);
    ui->mdiArea->addSubWindow(label);
    label->show();
}

void MainWindow::on_btn_mdiArea_close_clicked()
{
    ui->mdiArea->closeActiveSubWindow();
}

void MainWindow::on_btn_mdiArea_closeAll_clicked()
{
    ui->mdiArea->closeAllSubWindows();
}

void MainWindow::on_cbox_mdiArea_layout_currentIndexChanged(int index)
{
    switch (index) {
    case 0:
        ui->mdiArea->cascadeSubWindows();
        break;
    case 1:
        ui->mdiArea->tileSubWindows();
        break;
    default:
        ui->mdiArea->cascadeSubWindows();
        break;
    }
}

void MainWindow::on_cbox_mdiArea_model_currentIndexChanged(int index)
{
    ui->mdiArea->setViewMode(QMdiArea::ViewMode(index));
}

void MainWindow::on_mdiArea_subWindowActivated(QMdiSubWindow *arg1)
{
    if (ui->mdiArea->subWindowList().count()==0)
        ui->statusBar->showMessage("所有窗口均被关闭");
    else
    {
        QLabel *lab = static_cast<QLabel*>(ui->mdiArea->currentSubWindow()->widget());
        ui->statusBar->showMessage(lab->objectName());
        qDebug()<<lab->text();
    }
}

7.4. 例程测试

例程展示了Qt中的窗口控件,并提供了一些简单的示例应用。

7.4.1. 编程思路

  • 创建一个简单工程;

  • 使用Qt Designer添加控件和布局;

  • 关联信号的槽。

  • 首先创建一个空工程;

  • 在UI放置stackedWidget作为主窗口,dockWidget作为辅助窗口;

  • 将我们要演示的窗口控件分别添加到stackedWidget中;相关的控件的操作放置在dockWidget中;

  • 通过菜单栏对stackedWidget显示页面进行切换,显示不同窗口控件。

  • dockWidget中的控件通过信号绑定到自定义的槽;

  • 在自定义的槽函数中实现该窗口控件的常规操作。

7.4.2. 代码构建

  • Ubuntu 上选择 Desktop Qt 5.15.2 GCC 64bit 套件,编译运行;

  • LubanCat 板卡上运行选择 lubancat_rk 套件,只编译。

build001

7.4.3. 运行测试

7.4.3.1. PC实验

直接点击编译并运行程序,结果如下:

widget013

7.4.3.2. LubanCat板卡上测试

通过SCP或者NFS将编译好的程序拷贝到LubanCat上

NFS环境搭建 参考这里

编译好的程序在 lubancat_qt_tutorial_code/build-qwidget-LubanCat_RK-Debug/ 目录中,通过scp命令将编译好的程序传输到鲁班猫板卡:

# 使用scp命令传输文件
scp root@192.168.103.120:/home/lubancat_qt_tutorial_code/app_bin/qwidget /usr/local/qt-app/

然后配置好环境(测试是使用桌面系统显示),运行程序:

cat@lubancat:~$ ./qwidget -platform xcb

在LubanCat运行程序,并执行Qwidget程序。

widget014