待补充
Win11,QT5.14.2
所有控件示例使用Loader载入到界面中
Loader
Allows dynamic loading of a subtree from a URL or Component.
import QtQuick 2.12
import QtQuick.Window 2.12
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Loader {
anchors.fill: parent
id: rootLoader
property string homeUrl: "./XXX.qml"
source: homeUrl
clip: true
}
}
先记录一下个人觉得最麻烦的一个控件,主要参考了官方demo,Simple Tree Model Example,和腾讯课堂的qml课程
思路: 从官方demo中可以了解到,TreeView的model只能是来自于C++类,且必须是继承于QAbstractItemModel,这个类中有以下5个纯虚函数必须实现;
除了上面这个类,我们还需要自己定义树的节点,demo中的名字是TreeItem;树有三种表示方法,即双亲表示法、孩子表示法、孩子兄弟表示法,但是在实际使用中,为了便于操作,我们是有冗余的,节点中不仅有指向孩子的指针数组,还有指向双亲节点的parent指针,为了保存节点的数据,当然,还有数据域;
文字描述太多,也不清楚,show the code to you
#pragma once
// C lib import
#include
// Qt lib import
#include
#include
#include
#include
//可以增加函数,实现 增删改查
class TreeItem
{
private:
//父节点指针
TreeItem *parentItem;
//孩子指针
QList<TreeItem*> childItems;
//数据,可能存在多列数据,所以使用QList类型
QList<QString> itemData;
public:
TreeItem(const QList<QString> &data, TreeItem *parentItem = nullptr);
~TreeItem();
//添加孩子
void appendChild(TreeItem *child);
//删除孩子
void removeChild(int x);
//返回第x个孩子
TreeItem *child(int x);
//孩子数量
int childCount() const;
//总列数
int columnCount() const;
//返回对应列的数据
QVariant data(int column) const;
//当前行数,表示是第几个孩子
int row() const;
//父节点
TreeItem *parent();
void setParent(TreeItem *parent);
};
#include "TreeItem.h"
TreeItem::TreeItem(const QList<QString> &data, TreeItem *parentItem)
{
itemData = data;
qDebug()<<itemData.value(0);
this->parentItem = parentItem;
}
TreeItem::~TreeItem()
{
qDeleteAll(childItems);
}
void TreeItem::appendChild(TreeItem *item)
{
item->setParent(this);
childItems.append(item);
}
void TreeItem::removeChild(int x)
{
childItems.removeAt(x);
}
TreeItem *TreeItem::child(int x)
{
return childItems.value(x);
}
int TreeItem::childCount() const
{
return childItems.count();
}
int TreeItem::columnCount() const
{
return itemData.count();
}
QVariant TreeItem::data(int column) const
{
return itemData.value(column);
}
TreeItem *TreeItem::parent()
{
assert(parentItem);
return parentItem;
}
void TreeItem::setParent(TreeItem *parent)
{
parentItem = parent;
}
int TreeItem::row() const
{
if (!parentItem) {
return 0;
}
return parentItem->childItems.indexOf(const_cast<TreeItem*>(this));
}
#pragma once
#include
#include
#include
#include "TreeItem.h"
class TreeModel: public QAbstractItemModel
{
Q_OBJECT
private:
TreeItem *rootItem;
public:
TreeModel(QObject *parent = NULL);
~TreeModel();
//Returns the data stored under the given role for the item referred to by the index.
QVariant data(const QModelIndex &index, int role) const;
Qt::ItemFlags flags(const QModelIndex &index) const;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
//Returns the index of the item in the model specified by the given row, column and parent index.
//When reimplementing this function in a subclass,
//call createIndex() to generate model indexes that other components can use to refer to items in your model.
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;
QModelIndex parent(const QModelIndex &index) const;
//Returns the number of rows under the given parent.
//When the parent is valid it means that rowCount is returning the number of children of parent.
int rowCount(const QModelIndex &parent = QModelIndex()) const;
//Returns the number of columns for the children of the given parent.
//In most subclasses, the number of columns is independent of the parent.
int columnCount(const QModelIndex &parent = QModelIndex()) const;
void insert(QString value, TreeItem * parentItem = nullptr);
//Returns the model's role names.
QHash<int, QByteArray> roleNames() const;
public slots:
QAbstractItemModel *model();
};
#include "TreeModel.h"
TreeModel::TreeModel(QObject *parent) :
QAbstractItemModel(parent)
{
//定义根节点
rootItem = new TreeItem({"treeRoot"});
//一级节点
auto item1 = new TreeItem({QStringLiteral("A")}, rootItem);
auto item2 = new TreeItem({QStringLiteral("B")}, rootItem);
auto item3 = new TreeItem({QStringLiteral("C")}, rootItem);
rootItem->appendChild(item1);
rootItem->appendChild(item2);
rootItem->appendChild(item3);
//
auto item11 = new TreeItem({"test11"}, item1);
auto item12 = new TreeItem({"test12"}, item1);
item1->appendChild(item11);
item1->appendChild(item12);
}
void TreeModel::insert(QString value, TreeItem *parentItem){
auto temp = new TreeItem({value}, parentItem);
rootItem->appendChild(temp);
}
TreeModel::~TreeModel()
{
delete rootItem;
}
int TreeModel::columnCount(const QModelIndex &parent) const
{
if (parent.isValid())
{
return static_cast<TreeItem*>(parent.internalPointer())->columnCount();
}
else
{
return rootItem->columnCount();
}
}
//自定义roleName
QHash<int, QByteArray> TreeModel::roleNames() const
{
//virtual QHash roleNames() const
//qt中原来就有一些roleName,为了不把原先有的覆盖到,key需要从Qt::UserRole+1算起;
QHash<int, QByteArray> names(QAbstractItemModel::roleNames());
names[Qt::UserRole + 1] = "name";
names[Qt::UserRole + 2] = "other";
return names;
}
QVariant TreeModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
{
return QVariant();
}
switch (role)
{
case Qt::UserRole + 1:
{
return static_cast<TreeItem*>(index.internalPointer())->data(0);
}
case Qt::UserRole + 2:
{
return static_cast<TreeItem*>(index.internalPointer())->data(1);
}
case Qt::DisplayRole:
{
return static_cast<TreeItem*>(index.internalPointer())->data(index.column());
}
default:
{
return QVariant();
}
}
}
Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const
{
if (!index.isValid())
{
return 0;
}
return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
}
QVariant TreeModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
{
return rootItem->data(section);
}
return QVariant();
}
QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent) const
{
if (!hasIndex(row, column, parent))
{
return QModelIndex();
}
TreeItem *parentItem;
if (!parent.isValid())
{
parentItem = rootItem;
}
else
{
parentItem = static_cast<TreeItem*>(parent.internalPointer());
}
TreeItem *childItem = parentItem->child(row);
if (childItem)
{
return createIndex(row, column, childItem);
}
else
{
return QModelIndex();
}
}
QModelIndex TreeModel::parent(const QModelIndex &index) const
{
if (!index.isValid())
{
return QModelIndex();
}
TreeItem *childItem = static_cast<TreeItem*>(index.internalPointer());
TreeItem *parentItem = childItem->parent();
if (parentItem == rootItem)
{
return QModelIndex();
}
return createIndex(parentItem->row(), 0, parentItem);
}
int TreeModel::rowCount(const QModelIndex &parent) const
{
TreeItem *parentItem;
if (parent.column() > 0)
{
return 0;
}
if (!parent.isValid())
{
parentItem = rootItem;
}
else
{
parentItem = static_cast<TreeItem*>(parent.internalPointer());
}
return parentItem->childCount();
}
QAbstractItemModel *TreeModel::model()
{
return this;
}
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <TreeModel.h>
#include <QQmlContext>
#include <QTextCodec>
#include "TreeModel.h"
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QTextCodec *codec = QTextCodec::codecForName("UTF-8");
QTextCodec::setCodecForLocale(codec);
QQmlApplicationEngine engine;
/// qml与c++交互
engine.rootContext()->setContextProperty("treeModel", new TreeModel);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
import QtQuick 2.0
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import QtQml.Models 2.2
Rectangle{
property variant nodePic: ["qrc:/pic/pic/pan.png","qrc:/pic/pic/dir.png","qrc:/pic/pic/file.png"]
id: root
color: parent.color
TreeView{
id: myTree
anchors.fill: parent
style: treeViewStyle
selection: sel
//隐藏列头,可以注释掉运行看效果
headerVisible: false
Component.onCompleted: {
//从TreeModel获取model
model = treeModel.model()
}
//节点代理
itemDelegate: Item{
id: treeItem
//节点前的图片
Image {
id: nodeImg
height: parent.height
//不同级的节点图片宽度不一样
width: {
if(styleData.depth === 0){
return parent.width / 6
}else if(styleData.depth === 1){
return parent.width / 8
}else{
return parent.width / 6
}
}
//不同级的节点图片不一样
source: {
if(styleData.depth === 0){
return nodePic[0]
}else if(styleData.depth === 1){
return nodePic[1]
}else {
return nodePic[2]
}
}
}
Text{
id:itemText
anchors.left: nodeImg.right
anchors.leftMargin: 4
anchors.bottom: parent.bottom
//显示来自model的文本
text: styleData.value
//选中时字体颜色切换
color: styleData.selected ? "red":"black"
//选中时字体大小改变
font.pointSize: styleData.selected ? 10 : 9
}
//节点代理的鼠标事件
MouseArea{
id: itemMosue
hoverEnabled: true
anchors.fill: parent
onClicked:{
//点击了文字,选中该节点
sel.setCurrentIndex(styleData.index, 0x0010)
//切换节点的展开状态
if(styleData.isExpanded){
myTree.collapse(styleData.index)
}
else{
myTree.expand(styleData.index)
}
//添加子节点
console.log("点击了: " + itemText.text)
}
}
}
//定义列
TableViewColumn {
title: qsTr("资源管理软件")
//显示的元素
role: "name"
//列的宽
width: 200
}
}
//自定义添加选中
ItemSelectionModel {
id: sel
}
//树的自定义样式
Component {
id: treeViewStyle
TreeViewStyle {
//节点间隔
indentation: 30
//节点的展开标记图
branchDelegate: Image {
id:image
source: styleData.isExpanded ? "qrc:/pic/pic/collapse.png" : "qrc:/pic/pic/expansion.png"
width: 9
height: 15
anchors.top: parent.top
anchors.topMargin: 2
}
//行代理
rowDelegate: Rectangle {
height: 20
//这里决定了选中的颜色和背景颜色
color: styleData.selected? "red" : root.color
}
}
}
}
参考了福优学苑qml课程
Provides a surface that can be “flicked”.
The Flickable item places its children on a surface that can be dragged and flicked, causing the view onto the child items to scroll. This behavior forms the basis of Items that are designed to show large numbers of child items, such as ListView and GridView.
In traditional user interfaces, views can be scrolled using standard controls, such as scroll bars and arrow buttons. In some situations, it is also possible to drag the view directly by pressing and holding a mouse button while moving the cursor. In touch-based user interfaces, this dragging action is often complemented with a flicking action, where scrolling continues after the user has stopped touching the view.
Flickable does not automatically clip its contents. If it is not used as a full-screen item, you should consider setting the clip property to true.
import QtQuick 2.14
import QtQuick.Layouts 1.14
import QtQuick.Controls 2.14
Rectangle{
anchors.fill: parent
Flickable{
id: flick
width: parent.width
height: parent.height
contentWidth: flick.width
contentHeight: rect1.height + rect2.height + rect3.height
//ScrollBar.vertical: ScrollBar { }
Rectangle{
id: rect1
anchors.top: parent.top
anchors.topMargin: 10
width: parent.width - 50
anchors.leftMargin: 50 / 2
anchors.rightMargin: 50 / 2
height: 100
color: "red"
}
Rectangle{
id: rect2
anchors.top: rect1.bottom
anchors.topMargin: 10
width: parent.width - 50
anchors.leftMargin: 50 / 2
anchors.rightMargin: 50 /2
height: 300
color: "green"
}
Rectangle{
id: rect3
anchors.top: rect2.bottom
anchors.topMargin: 10
width: parent.width - 50
anchors.leftMargin: 50 / 2
anchors.rightMargin: 50 /2
height: 400
color: "blue"
}
}
//添加一个滚动条按钮
Rectangle
{
id: rect;
width: 10;
anchors.right: flick.right;
color: "#6D665C";
//flick.visibleArea.heightRatio = flick.height / flick.contentHeight
height: flick.visibleArea.heightRatio * flick.height;
//滚动条会随着图片移动而移动,因为图片和滚动条应该都是等比例下降,所以换算如下
y: flick.visibleArea.yPosition * flick.height;
//给滚动条按钮添加槽函数,可以拖动按钮移动图片
MouseArea
{
id: mouseA;
anchors.fill: rect;
//让滚动条可以在垂直方向拖动
drag.target: rect;
drag.axis: Drag.YAxis;
drag.minimumY: 0;
drag.maximumY: flick.height - rect.height;
onPressed:
{
rect.color = "#A4D3EE";
}
onReleased:
{
rect.color = "#6D665C";
}
//不可以用flick.visibleArea.yPosition,因为是只读属性
onMouseYChanged:
{
flick.contentY = rect.y / flick.height * flick.contentHeight;
}
}
Component.onCompleted:
{
console.log("QML MouseArea\'s C++ type - ", mouseA);
}
}
}
参考https://blog.csdn.net/weixin_41849730/article/details/108278378
简化了上面的部分代码,直接使用自带的滚动条
ScrollBar is an interactive bar that can be used to scroll to a specific position. A scroll bar can be either vertical or horizontal, and can be attached to any Flickable, such as ListView and GridView.
从官方文档中可以了解到,只有继承与Flickable的控件才可以使用滚动条
import QtQuick 2.14
import QtQuick.Layouts 1.14
import QtQuick.Controls 2.14
Rectangle{
anchors.fill: parent
//color: "gray"
Flickable{
id: flick
ScrollBar.vertical: ScrollBar{}
anchors.horizontalCenter: parent.horizontalCenter
anchors.fill: parent
//必须设置contectWidth和contentHeight,否则,滚动条不生效
contentWidth: rectRoot.width
contentHeight: rectRoot.height
Rectangle{
id: rectRoot
width: parent.width - 20
anchors.top: parent.top
anchors.topMargin: 10
height: 1000
//color: "gray"
Rectangle{
id: rect1
width: parent.width
height: 100
color: "red"
}
Rectangle{
id: rect2
anchors.top: rect1.bottom
anchors.topMargin: 10
width: parent.width
height: 300
color: "green"
}
Rectangle{
id: rect3
anchors.top: rect2.bottom
anchors.topMargin: 10
width: parent.width
height: 400
color: "blue"
}
}
}
}
改进一下,将Rectangle两边留出空白区域,并将其居中;
尝试了一下,Flickable的子项中使用anchors.horizontalCenter: parent.horizontalCenter无效,所以我往下放了一下
import QtQuick 2.14
import QtQuick.Layouts 1.14
import QtQuick.Controls 2.14
Rectangle{
anchors.fill: parent
Flickable{
id: flick
ScrollBar.vertical: ScrollBar{}
anchors.fill: parent
contentWidth: rectRoot.width
contentHeight: rectRoot.height
Rectangle{
id: rectRoot
width: parent.width - 10
anchors.top: parent.top
anchors.topMargin: 10
//anchors.horizontalCenter: parent.horizontalCenter
height: 1000
color: "gray"
Rectangle{
id: rect1
width: parent.width - 20
anchors.horizontalCenter: parent.horizontalCenter
height: 100
color: "red"
}
Rectangle{
id: rect2
anchors.top: rect1.bottom
anchors.topMargin: 10
anchors.horizontalCenter: parent.horizontalCenter
width: parent.width - 20
height: 300
color: "green"
}
Rectangle{
id: rect3
anchors.top: rect2.bottom
anchors.topMargin: 10
width: parent.width - 20
anchors.horizontalCenter: parent.horizontalCenter
height: 400
color: "blue"
}
}
}
}
列布局
import QtQuick 2.14
import QtQuick.Layouts 1.14
import QtQuick.Controls 2.14
Rectangle{
anchors.fill: parent
Flickable{
id: flick
ScrollBar.vertical: ScrollBar{}
anchors.fill: parent
contentWidth: rectRoot.width
contentHeight: rectRoot.height
ColumnLayout{
id: rectRoot
width: parent.width - 10
height: 1000
spacing: 2
Rectangle {
Layout.alignment: Qt.AlignCenter
color: "red"
Layout.preferredWidth: parent.width - 20
Layout.preferredHeight: 300
}
Rectangle {
Layout.alignment: Qt.AlignCenter
color: "blue"
Layout.preferredWidth: parent.width - 20
Layout.preferredHeight: 300
}
Rectangle {
Layout.alignment: Qt.AlignCenter
color: "green"
Layout.preferredWidth: parent.width - 20
Layout.preferredHeight: 300
}
}
}
}
表格布局
Provides a way of dynamically arranging items in a grid.
与定位器Grid的不同在于,布局管理器GridLayout可以自动调节控件的尺寸以适应页面的大小
import QtQuick 2.14
import QtQuick.Layouts 1.14
import QtQuick.Controls 2.14
Rectangle{
anchors.fill: parent
Flickable{
id: flick
ScrollBar.vertical: ScrollBar{}
anchors.fill: parent
contentWidth: rectRoot.width
contentHeight: rectRoot.height
ColumnLayout{
id: rectRoot
width: parent.width - 10
height: 1000
spacing: 2
GridLayout {
Layout.alignment: Qt.AlignCenter
//color: "red"
Layout.preferredWidth: parent.width - 20
Layout.preferredHeight: 300
columns: 4
rows: 2
flow: GridLayout.LeftToRight
//id
Text{
text: "id"
Layout.alignment: Qt.AlignCenter
}
TextField{
Layout.alignment: Qt.AlignCenter
}
//标志
Text{
text: "flag"
Layout.alignment: Qt.AlignCenter
}
ComboBox{
Layout.alignment: Qt.AlignCenter
}
//描述
Text{
text: "描述"
Layout.alignment: Qt.AlignCenter
}
Rectangle{
width: 500
height: 200
Layout.columnSpan: 3
Layout.rowSpan: 2
Layout.alignment: Qt.AlignTop | Qt.AlignLeft
border.color: "gray"
border.width: 1
TextArea{
anchors.fill: parent
width: parent.width
TextArea.flickable : TextArea
}
}
}
Rectangle {
Layout.alignment: Qt.AlignCenter
color: "blue"
Layout.preferredWidth: parent.width - 20
Layout.preferredHeight: 300
}
Rectangle {
Layout.alignment: Qt.AlignCenter
color: "green"
Layout.preferredWidth: parent.width - 20
Layout.preferredHeight: 300
}
}
}
}
import QtQuick 2.14
import QtQuick.Layouts 1.14
import QtQuick.Controls 2.14
Rectangle{
anchors.fill: parent
Flickable{
id: flick
ScrollBar.vertical: ScrollBar{}
//anchors.fill: parent
width: 450
height: parent.height
contentWidth: rectRoot.width
contentHeight: rectRoot.height
anchors.horizontalCenter: parent.horizontalCenter
ColumnLayout{
id: rectRoot
width: parent.width - 10
height: 1000
spacing: 2
GridLayout {
Layout.alignment: Qt.AlignCenter
//color: "red"
Layout.preferredWidth: parent.width - 20
Layout.preferredHeight: 300
columns: 4
rows: 2
flow: GridLayout.LeftToRight
//
Text{
text: "id"
Layout.alignment: Qt.AlignCenter
}
TextField{
Layout.alignment: Qt.AlignCenter
}
//标志
Text{
text: "flag"
Layout.alignment: Qt.AlignCenter
}
ComboBox{
Layout.alignment: Qt.AlignCenter
}
//描述
Text{
text: "描述"
Layout.alignment: Qt.AlignCenter
}
Rectangle{
width: rectRoot.width - 100
height: 200
Layout.columnSpan: 3
Layout.rowSpan: 2
Layout.alignment: Qt.AlignTop | Qt.AlignLeft
border.color: "gray"
border.width: 1
TextArea{
anchors.fill: parent
width: parent.width
TextArea.flickable : TextArea
}
}
}
Rectangle {
Layout.alignment: Qt.AlignCenter
color: "blue"
Layout.preferredWidth: parent.width - 20
Layout.preferredHeight: 300
}
Rectangle {
Layout.alignment: Qt.AlignCenter
color: "green"
Layout.preferredWidth: parent.width - 20
Layout.preferredHeight: 300
}
}
}
}
这里是限制了Flickable的宽度,将Flickable控件居中,实现了页面的居中显示;但是相应地,滚动条也被移动到屏幕中间了,这不是我想要的;如果需要修改的话,估计需要手工编写滚动条;
import QtQuick 2.14
import QtQuick.Layouts 1.14
import QtQuick.Controls 2.14
Rectangle{
anchors.fill: parent
Flickable{
id: flick
ScrollBar.vertical: ScrollBar{}
width: 450
height: parent.height
contentWidth: rectRoot.width
contentHeight: rectRoot.height
anchors.horizontalCenter: parent.horizontalCenter
ColumnLayout{
id: rectRoot
width: parent.width - 10
height: 1000
spacing: 2
GridLayout {
Layout.alignment: Qt.AlignCenter
//color: "red"
Layout.preferredWidth: parent.width - 20
Layout.preferredHeight: 300
columns: 2
rows: 2
flow: GridLayout.LeftToRight
RowLayout{
Layout.alignment: Qt.AlignLeft
spacing: 5
//
Text{
text: "id"
}
TextField{
}
}
RowLayout{
Layout.alignment: Qt.AlignLeft
spacing: 5
//标志
Text{
text: "flag"
}
ComboBox{
model: ["First", "Second", "Third"]
}
}
RowLayout{
Layout.alignment: Qt.AlignLeft
spacing: 5
Layout.columnSpan: 2
//描述
Text{
text: "描述"
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
}
Rectangle{
Layout.fillWidth: true //自动填充剩余的宽度
height: 200
Layout.alignment: Qt.AlignLeft
border.color: "gray"
border.width: 1
TextArea{
anchors.fill: parent
}
}
}
}
Rectangle {
Layout.alignment: Qt.AlignCenter
color: "blue"
Layout.preferredWidth: parent.width - 20
Layout.preferredHeight: 300
}
Rectangle {
Layout.alignment: Qt.AlignCenter
color: "green"
Layout.preferredWidth: parent.width - 20
Layout.preferredHeight: 300
}
}
}
}
表格控件
Provides a list view with scroll bars, styling and header sections.
import QtQuick 2.14
import QtQuick.Layouts 1.14
import QtQuick.Controls 2.14
import QtQuick.Controls 1.4
TableView{
anchors.fill: parent
model: ListModel {
id: libraryModel
ListElement {
title: "A Masterpiece"
author: "Gabriel"
}
ListElement {
title: "Brilliance"
author: "Jens"
}
ListElement {
title: "Outstanding"
author: "Frederik"
}
}
property var columnWidth: parent.width / 2
TableViewColumn {
role: "title"
title: "Title"
width: columnWidth
}
TableViewColumn {
role: "author"
title: "Author"
width: columnWidth
}
}
增加一列,在delegate中添加Button,在Button中写clicked()信号的槽函数,删除选中的列
import QtQuick 2.14
import QtQuick.Layouts 1.14
import QtQuick.Controls 2.14
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
TableView{
anchors.fill: parent
model: ListModel {
id: libraryModel
ListElement {
title: "A Masterpiece"
author: "Gabriel"
}
ListElement {
title: "Brilliance"
author: "Jens"
}
ListElement {
title: "Outstanding"
author: "Frederik"
}
}
property var columnWidth: parent.width / 3
TableViewColumn {
role: "title"
title: "Title"
width: columnWidth
}
TableViewColumn {
role: "author"
title: "Author"
width: columnWidth
}
TableViewColumn {
title: "delete";
width: columnWidth
delegate: Button{
text: "删除"
onClicked: {
console.log("Row: "+styleData.row + " Column: " + styleData.column)
libraryModel.remove(styleData.row)
}
}
}
}
来自官方文档
import QtQuick 2.0
Flow {
anchors.fill: parent
anchors.margins: 4
spacing: 10
Text { text: "Text"; font.pixelSize: 40 }
Text { text: "items"; font.pixelSize: 40 }
Text { text: "flowing"; font.pixelSize: 40 }
Text { text: "inside"; font.pixelSize: 40 }
Text { text: "a"; font.pixelSize: 40 }
Text { text: "Flow"; font.pixelSize: 40 }
Text { text: "item"; font.pixelSize: 40 }
}
思路:动态设置两块区域的宽度,折叠的时候将宽度置0,显示的时候重新设置为默认值
import QtQuick 2.0
Rectangle{
id: root
anchors.fill: parent
Rectangle{
id: rectLeft
anchors.left: parent.left
width: parent.width / 5
height: parent.height
color: "blue"
}
property var flag: true
Rectangle{
id: rectRight
anchors.left: rectLeft.right
width: parent.width - rectLeft.width
height: parent.height
color: "red"
MouseArea{
anchors.fill: parent
onClicked: {
//anchors.fill = parent
if(flag == true){
rectLeft.width = 0
rectRight.width = root.width
flag = false
}
else{
rectLeft.width = root.width / 5
rectRight.width = root.width - rectLeft.width
flag = true
}
console.log("单击")
}
}
}
}
思路:width变化采用线性动画;
这样做存在一个问题,就是启动的时候也有动画
import QtQuick 2.0
//区域折叠
Rectangle{
id: root
anchors.fill: parent
property var completed: false
property var hided: false
Rectangle {
id: rectLeft
anchors.left: parent.left
width: parent.width / 5
height: parent.height
color: "blue"
Behavior on width {
NumberAnimation { duration: 300 }
}
}
Rectangle {
id: rectRight
anchors.left: rectLeft.right
width: parent.width - rectLeft.width
height: parent.height
color: "red"
Behavior on width{
NumberAnimation { duration: 300 }
}
MouseArea{
anchors.fill: parent
onClicked: {
//anchors.fill = parent
if(hided == false){
rectLeft.width = 0
rectRight.width = root.width
hided = true
}
else{
rectLeft.width = root.width / 5
rectRight.width = root.width - rectLeft.width
hided = false
}
console.log("点击")
}
}
}
}
下面的代码没有实现动画效果,只是记录一下自定义信号的使用
signal <signalName>[([<type> <parameter name>[, …]])]
function <functionName>([<parameterName>[, ...]]) { <body> }
import QtQuick 2.0
//区域折叠
Rectangle{
id: root
anchors.fill: parent
property var completed: false
property var hided: false
//宽度改变
signal userWidthChanged()
Rectangle {
id: rectLeft
anchors.left: parent.left
width: parent.width / 5
height: parent.height
color: "blue"
// Behavior on width {
// NumberAnimation { duration: 500 }
// }
}
Connections{
target: rectLeft
onWidthChanged: {
console.log("widthChanged")
}
}
Rectangle {
id: rectRight
anchors.left: rectLeft.right
width: parent.width - rectLeft.width
height: parent.height
color: "red"
// Behavior on width{
// NumberAnimation { duration: 500 }
// }
MouseArea{
anchors.fill: parent
onClicked: {
//anchors.fill = parent
if(hided == false){
rectLeft.width = 0
rectRight.width = root.width
hided = true
}
else{
rectLeft.width = root.width / 5
rectRight.width = root.width - rectLeft.width
hided = false
}
root.userWidthChanged() //抛出信号
console.log("点击")
}
}
}
Component.onCompleted: {
completed = true
}
}
主要是通过Item中的state属性实现
左右两个色块,点击切换左边色块的visible属性
import QtQuick 2.14
Rectangle{
id: root
anchors.fill: parent
state: "showall"
Rectangle{
id: leftRect
anchors.left: parent.left
height: parent.height
width: parent.width / 2
}
Rectangle{
id: rightRect
anchors.left: leftRect.right
width: parent.width - leftRect.width
height: parent.height
color: "red"
}
MouseArea{
//z: 1
anchors.fill: parent
//状态切换
onClicked: {
console.log("点击")
if(root.state === "showall")
root.state = "hideleft"
else
root.state = "showall"
}
}
//状态描述
states: [
State {
name: "showall"
PropertyChanges {
target: leftRect
color: "green"
visible: true
}
PropertyChanges {
target: rightRect
color: "red"
visible: true
}
},
State {
name: "hideleft"
PropertyChanges {
target: leftRect
visible: false
}
PropertyChanges {
target: rightRect
color: "red"
visible: true
}
}
]
}
import QtQuick 2.12
import QtQuick.Controls 2.14
Column{
//visual: true
spacing: 5
TextField{
id: text1
text: text2.text
width: 60
height: 50
}
TextField{
id: text2
width: 60
height: 50
text: text1.text
}
}
上面的代码可以实现简单的双向绑定
也可以绑定到自定义属性上,更灵活一些
import QtQuick 2.12
import QtQuick.Controls 2.14
Column{
id: root
//visual: true
spacing: 5
property var myText: "test"
TextField{
id: text1
text: myText
width: 60
height: 50
}
TextField{
id: text2
width: 60
height: 50
text: myText
}
Button{
id: button1
width: 60
height: 50
text: "点击"
onClicked: {
if(root.myText === "test")
root.myText = "test2"
else
root.myText = "test"
console.log(root.myText)
}
}
}
QML中调用Https接口会出现qt.network.ssl: QSslSocket::connectToHostEncrypted: TLS initialization failed 错误,暂时还没有找到解决办法
// 提供基于Rest的数据访问服务
// url: 访问地址
// callBack: 回调函数
function request(url, callBack) {
print('request:')
var xhr = new XMLHttpRequest()
// 第二种请求方式 使用onload
xhr.onload = function(){
console.log(xhr.responseText)
callBack(xhr.responseText)
//return xhr.responseText
}
xhr.open("POST", url);
xhr.send()
}
function requestWithOutCallBack(url) {
print('request:')
var xhr = new XMLHttpRequest()
// 第二种请求方式 使用onload
xhr.onload = function(){
console.log(xhr.responseText)
callBack(xhr.responseText)
//return xhr.responseText
}
xhr.open("POST", url, true);
xhr.send()
}
import QtQuick 2.14
import QtQuick.Layouts 1.14
import QtQuick.Controls 2.14
import "./ComTools.js" as ComTools
Rectangle{
id: comTestPage
anchors.fill: parent
Button{
width: 60
height: 50
text: "访问"
onClicked: ComTools.requestWithOutCallBack("http://xxx") //这里是自己做的一个rest api,可以替换成开源的http接口
}
}
通过webapi访问数据库
//查询全表
function selectList(serverAddress, tableName, callBack){
var operation = "selectList"
print(operation + ': ')
var xhr = new XMLHttpRequest()
//组合成地址
var url = serverAddress + tableName + "/" + operation
// 第二种请求方式 使用onload
xhr.onload = function(){
console.log(xhr.responseText)
callBack(xhr.responseText)
}
xhr.open("POST", url)
xhr.send()
}
import QtQuick 2.14
import QtQuick.Layouts 1.14
import QtQuick.Controls 2.14
import "./ComTools.js" as ComTools
import "./DataAccess.js" as DataAccess
Rectangle{
id: comTestPage
anchors.fill: parent
Button{
id: button1
width: 60
height: 50
text: "访问"
onClicked: {
DataAccess.selectList("http://xxx:8081/", "tableName", function setText(text){ text1.text = text})
}
Rectangle{
anchors.left: button1.right
anchors.leftMargin: 5
width: 100
height: 50
Text{
id: text1
text: "123"
anchors.fill: parent
}
}
}
//下面这种写法等价
onClicked: {
DataAccess.selectList("http://xxx:8081/", "yyy", setText)
}
function setText(text){ text1.text = text}
}
把函数提出来也是可以的;
访问后端服务的基本代码
//查询全表
function selectList(serverAddress, tableName, callBack){
let operation = "selectList"
print(operation + ': ')
//组合成地址
let url = serverAddress + "/" + tableName + "/" + operation
let xhr = new XMLHttpRequest()
// 第二种请求方式 使用onload
xhr.onload = function(){
console.log(xhr.responseText)
callBack(xhr.responseText)
}
xhr.open("POST", url)
xhr.send()
}
//条件查询
//key--数据库的字段名字, value--字段的值
function selectByMap(serverAddress, tableName, key, value, callBack){
let operation = "selectByMap"
print(operation + ': ')
//组合成地址
let url = serverAddress + "/" + tableName + "/" + operation + "/" + key + "/" + value
let xhr = new XMLHttpRequest()
// 第二种请求方式 使用onload
xhr.onload = function(){
console.log(xhr.responseText)
callBack(xhr.responseText)
}
xhr.open("POST", url)
xhr.send()
}
//按id查询
function selectById(serverAddress, tableName, id, callBack){
let operation = "selectById"
print(operation + ': ')
//组合成地址
let url = serverAddress + "/" + tableName + "/" + operation + "/" + id
let xhr = new XMLHttpRequest()
// 第二种请求方式 使用onload
xhr.onload = function(){
console.log(xhr.responseText)
callBack(xhr.responseText)
}
xhr.open("POST", url)
xhr.send()
}
//按id删除
//userName--用户名, userPwd--密码
function deleteById(serverAddress, tableName, id, userName, userPwd, callBack){
let operation = "deleteById"
print(operation + ': ')
//组合成地址
let url = serverAddress + "/" + tableName + "/" + operation + "/" + id
//发送内容
let userRequest = {
className: tableName,
cmd: operation,
user: userName,
password: userPwd,
data: {}
}
//console.log(userRequest.className)
let xhr = new XMLHttpRequest()
// 第二种请求方式 使用onload
xhr.onload = function(){
console.log(xhr.responseText)
callBack(xhr.responseText)
}
xhr.open("POST", url)
xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
xhr.send(JSON.stringify(userRequest))
}
//按id更新
//userRequest--用户请求的json对象
function updateById(serverAddress, userRequest, callBack){
let operation = "updateById"
print(operation + ': ')
//组合成地址
let url = serverAddress + "/" + userRequest.className + "/" + operation
//console.log(userRequest.className)
let xhr = new XMLHttpRequest()
// 第二种请求方式 使用onload
xhr.onload = function(){
console.log(xhr.responseText)
callBack(xhr.responseText)
}
xhr.open("POST", url)
xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
xhr.send(JSON.stringify(userRequest))
}
//插入
function insert(serverAddress, userRequest, callBack){
let operation = "insert"
print(operation + ': ')
//组合成地址
let url = serverAddress + "/" + userRequest.className + "/" + operation
//console.log(userRequest.className)
let xhr = new XMLHttpRequest()
// 第二种请求方式 使用onload
xhr.onload = function(){
console.log(xhr.responseText)
callBack(xhr.responseText)
}
xhr.open("POST", url)
xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
xhr.send(JSON.stringify(userRequest))
}
//得到最大的id值
function getMaxId(serverAddress, tableName, callBack){
let operation = "getMaxId"
print(operation + ': ')
//组合成地址
let url = serverAddress + "/" + tableName + "/" + operation
let xhr = new XMLHttpRequest()
// 第二种请求方式 使用onload
xhr.onload = function(){
console.log(xhr.responseText)
callBack(xhr.responseText)
}
xhr.open("POST", url)
xhr.send()
}
本质上就是把依赖的动态库放到你指定的文件夹里面去;这样在其他电脑上运行时,就不需要再安装qt的开发环境了;
cd /d D:/xxx/xxx/release
windeployqt xxx.exe
windeployqt xxx.exe -qmldir C:\Qt\Qt5.14.2\5.14.2\mingw73_32\qml