Skip to content

Commit

Permalink
updata
Browse files Browse the repository at this point in the history
  • Loading branch information
liruixl committed Jun 9, 2019
1 parent e00a63d commit eb00a7e
Show file tree
Hide file tree
Showing 17 changed files with 1,838 additions and 6 deletions.
147 changes: 143 additions & 4 deletions cpp/QT.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,33 @@

# 信号槽连接

5个重载:

```c++
QMetaObject::Connection connect(const QObject *, const char *,
const QObject *, const char *,
Qt::ConnectionType);

QMetaObject::Connection connect(const QObject *, const QMetaMethod &,
const QObject *, const QMetaMethod &,
Qt::ConnectionType);

QMetaObject::Connection connect(const QObject *, const char *,
const char *,
Qt::ConnectionType) const; //默认接收者为this

//指向成员函数的指针
QMetaObject::Connection connect(const QObject *, PointerToMemberFunction,
const QObject *, PointerToMemberFunction,
Qt::ConnectionType)

//最后一个参数是Functor类型。这个类型可以接受 static 函数、全局函数以及 Lambda 表达式。
QMetaObject::Connection connect(const QObject *, PointerToMemberFunction,
Functor);
```
1)Qt4和Qt5都可以使用这种连接方式
```c++
Expand Down Expand Up @@ -115,7 +142,7 @@ public:

[QAbstractTableModel](https://doc.qt.io/qt-5/qabstracttablemodel.html) requires the implementation of three abstract methods.

## data()
## data/rowCount/columnCount

```c++
// mymodel.cpp
Expand Down Expand Up @@ -238,6 +265,15 @@ QVariant MyModel::headerData(int section, Qt::Orientation orientation, int role)
## Delegates
一般地,视图将数据向用户进行展示并且处理通用的输入。但是,对于某些特殊要求(比如这里的要求必须输入数字),则交予委托完成。这些组件提供输入功能,同时也能渲染某些特殊数据项。委托的接口由`QAbstractItemDelegate`定义。在这个类中,委托通过`paint()`和`sizeHint()`两个函数渲染用户内容(也就是说,你必须自己将渲染器绘制出来)。为使用方便,从 4.4 开始,Qt 提供了另外的基于组件的子类:`QItemDelegate`和`QStyledItemDelegate`。默认的委托是`QStyledItemDelegate`。二者的区别在于绘制和向视图提供编辑器的方式。`QStyledItemDelegate`使用当前样式绘制,并且能够使用 Qt Style Sheet(我们会在后面的章节对 QSS 进行介绍),因此我们推荐在自定义委托时,使用`QStyledItemDelegate`作为基类。不过,除非自定义委托需要自己进行绘制,否则,二者的代码其实是一样的。
继承`QStyledItemDelegate`需要实现以下几个函数:
+ `createEditor()`:返回一个组件。该组件会被作为用户编辑数据时所使用的编辑器,从模型中接受数据,返回用户修改的数据。
+ `setEditorData()`:提供上述组件在显示时所需要的默认值。
+ `setModelData()`:返回给模型用户修改过的数据。
+ `updateEditorGeometry()`:确保上述组件作为编辑器时能够完整地显示出来。
数据在单元格中显示为文本或复选框,并作为文本或复选框进行编辑。提供这些演示和编辑服务的组件称为*委托*。 View使用一个默认的委托。
需要重写:
Expand All @@ -251,14 +287,16 @@ The [paint()](https://doc.qt.io/qt-5/qabstractitemdelegate.html#paint) functio

```c++
QWidget *createEditor(QWidget *parent,
const QStyleOptionViewItem &option,
const QModelIndex &index) const override;
const QStyleOptionViewItem &option,
const QModelIndex &index) const override;
//返回一个组件,用户在上面编辑
```
The [createEditor()](https://doc.qt.io/qt-5/qabstractitemdelegate.html#createEditor) function is called when the user **starts editing** an item. return a editor.
```c++
void setEditorData(QWidget *editor, const QModelIndex &index) const override;
//初始化组件的值
```

The [setEditorData()](https://doc.qt.io/qt-5/qabstractitemdelegate.html#setEditorData) function is called when an editor is created to **initialize it** with data from the model.
Expand All @@ -267,9 +305,25 @@ The [setEditorData()](https://doc.qt.io/qt-5/qabstractitemdelegate.html#setEdit
void setModelData(QWidget *editor,
QAbstractItemModel *model,
const QModelIndex &index) const override;
//提交数据给模型
```
The [setModelData()](https://doc.qt.io/qt-5/qabstractitemdelegate.html#setModelData) function is called to commit data from the editor **to the model** when editing is finished.
The [setModelData()](https://doc.qt.io/qt-5/qabstractitemdelegate.html#setModelData) function is called to **commit data from the editor to the model** when editing is finished.
```c++
void updateEditorGeometry(QWidget *editor,
const QStyleOptionViewItem &option, const QModelIndex &index) const override;
```

`editor->setGeometry(option.rect);`

由于我们的编辑器只有一个数字输入框,所以只是简单将这个输入框的大小设置为单元格的大小(由`option.rect`提供)。如果是复杂的编辑器,我们需要根据单元格参数(由`option`提供)、数据(由`index`提供)结合编辑器(由`editor`提供)计算编辑器的显示位置和大小。





```c++
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override;
Expand All @@ -279,9 +333,94 @@ The `sizeHint()` function returns an **item's preferred size**.
# 事件
## Qt事件
Qt程序是事件驱动的, 程序的每个动作都是由幕后某个事件所触发
Qt事件的发生和处理成为程序运行的主线,存在于程序整个生命周期
## 常见Qt事件类型
键盘事件:按键按下和松开
鼠标事件:鼠标移动,鼠标按键的按下和松开
拖放事件:用鼠标进行拖放
滚轮事件:鼠标滚轮滚动
绘屏事件:重绘屏幕的某些部分
定时事件:定时器到时
焦点事件:键盘焦点移动
进入和离开事件:鼠标移入widget之内,或是移出
移动事件:widget的位置改变
大小改变事件:widget的大小改变
显示和隐藏事件:widget显示和隐藏
窗口事件:窗口是否为当前窗口
# QWidget
QWidget类是所有用户界面对象(User Interface Object)的基类。
## composite widget
当一个widget用作容器以对多个子窗口小部件进行分组时,它被称为复合widget。By default, composite widgets which do not provide a size hint will be sized according to the space requirements of their child widgets.
## Events
通常,widgets响应用户操作引起的事件。Qt会通过调用 specific event handler functions 将事件传递给widgets,事件是QEvent的子类的实例,其中包含了关于事件信息
### common events
+ paintEvent()
is called whenever the widget needs to be repainted. Every widget displaying custom content must implement it. Painting using a QPainter can only take place in a paintEvent() or a function called by a paintEvent().
+ resizeEvent()
is called when the widget has been resized.
+ mousePressEvent()
is called when a mouse button is pressed while the mouse cursor is inside the widget.
+ mouseReleaseEvent()
is called when a mouse button is released. A widget receives mouse release events when it has received the corresponding mouse press event. This means that if the user presses the mouse inside your widget, then drags the mouse somewhere else before releasing the mouse button, your widget receives the release event. There is one exception: if a popup menu appears while the mouse button is held down, this popup immediately steals the mouse events.
+ mouseDoubleClickEvent()
is called when the user double-clicks in the widget. If the user double-clicks, the widget receives a mouse press event, a mouse release event, (a mouse click event,) a second mouse press, this event and finally a second mouse release event. (Some mouse move events may also be received if the mouse is not held steady during this operation.) **It is not possible to distinguish a click from a double-click until the second click arrives**. (This is one reason why most GUI books recommend that double-clicks be an extension of single-clicks, rather than trigger a different action.)
### keyboard
+ keyPressEvent() is called whenever a key is pressed, and again when a key has been held down long enough for it to auto-repeat. The Tab and Shift+Tab keys are only passed to the widget if they are not used by the focus-change mechanisms. To force those keys to be processed by your widget, you must reimplement QWidget::event().
+ focusInEvent() is called when the widget gains keyboard focus (assuming you have called setFocusPolicy()). Well-behaved widgets indicate that they own the keyboard focus in a clear but discreet way.
+ focusOutEvent() is called when the widget loses keyboard focus.
### 不常见less common
+ mouseMoveEvent()只要按住鼠标按钮鼠标移动,就会调用mouseMoveEvent()。 这在拖放操作期间非常有用。 如果你调用setMouseTracking(true),即使没有按下任何按钮,也会得到鼠标移动事件。
+ wheelEvent() is called whenever the user turns the mouse wheel while the widget has the focus.
+ enterEvent() is called when the mouse enters the widget's screen space. (This excludes screen space owned by any of the widget's children.)
+ leaveEvent() is called when the mouse leaves the widget's screen space. If the mouse enters a child widget it will not cause a leaveEvent().
+ moveEvent() is called when the widget has been moved relative to its parent.
+ **closeEvent()** is called when the user closes the widget (or when close() is called).
+ **showEvent()**
# 图片显示
Qt助手:`image view`关键字。
Label相关:scaledContents、resize()、asjustSize()、
# 鼠标画矩形
<https://blog.csdn.net/seanwang_25/article/details/18667871>
<https://www.cnblogs.com/lifeng-blog/p/9057509.html>
<https://blog.csdn.net/cutter_point/article/details/43087497>
<http://www.qtcn.org/bbs/simple/?t53303.html>
# 其他
<https://blog.csdn.net/u012627502/article/details/26814049>
146 changes: 146 additions & 0 deletions cpp/QT1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
# 对象树

![1554376724281](assets/1554376724281.png)

# 信号的overload

```c++
void (QSpinBox:: *spinBoxSignal)(int) = &QSpinBox::valueChanged;

//这个函数指针如果用以下2句代码替换:
void (*spinBoxSignal)(int);
spinBoxSignal = &QSpinBox::valueChanged;
```

运行时就会指示:
error: no matches converting function ‘valueChanged’ to type ‘void (*)(int)’spinBoxSignal = &QSpinBox::valueChanged;

为什么会这样呢?valueChanged的原型不就是 void valueChanged(int)吗?

答:

成员函数指针需要类实例去调用,因此成员函数指针和全局函数指针是不兼容的。你修改之后的代码:void (*spinBoxSignal)(int); 定义了一个全局函数指针,是不能用成员函数 QSpinBox::valueChanged 进行赋值的,这也就是为什么编译器会报无法转换的错误。

# layout析构问题

QHBoxLayout对象也是new出来的,并且没有父对象,这种情况还需要delete吗?我自己的代码里在类里面定义了QHBoxLayout*成员对象,在类的构造函数里new出来,没有指定父对象,需要在类的析构函数中delete,并置空吗?

layout 这类对象,在调用 QWidget::setLayout() 函数时,会将这个 layout 的 parent 自动设置为该 widget(查阅文档,有 reparent 一段描述)。因此,如果你的 parent 能够一直追溯到一个可管理的组件(也就是能够被正确 delete 的对象),就不需要自己 delete 这个 layout,否则应该自己管理。

# exec

为什么建立pushbutton等widget时就不需要exec(), show()等, QDialog就需要呢,是因为它比较特殊吗?

对话框是一个独立的窗口,需要有自己的事件循环,接收用户响应,而按钮之类只是组件,是由窗口的事件循环支持的。

# tr

main函数中不能直接使用`tr()`??

tr()函数是定义在QObject里面的,所有使用了Q_OBJECT宏的类都自动具有tr()的函数。和connect函数一样,只有继承了QObject所以能够直接使用。

# &

通常使用含字符'&'的字符串为按钮的显示名称,如果设置按钮的text为"&Cancel", 即设置text, setText("&Cancel");或创建时QPushButton *pushButton = new QPushButton (QObject::tr("&Cancel")); Qt的编译器会将字符'&'后的'C'在显示时下方多一下划线,表明'C'为该按钮的快捷键,通过"Alt+c"操作来实现对pushButton的点击。

有可能是你没有添加 Q_OBJECT 宏。这个函数是 Q_OBJECT 宏展开的。或者你的类不是 QObject 子类,这样的话就要用 QObject::tr() 这样的静态函数。

# [=]Lamada函数

```c++
connect(textEdit, &QTextEdit::textChanged, [=]() {
this->setWindowModified(true);
});//中的[=]()是什么意思?
```
# 事件处理
现在我们可以总结一下 Qt 的事件处理,实际上是有五个层次:
1. 重写`paintEvent()`、`mousePressEvent()`等事件处理函数。这是最普通、最简单的形式,同时功能也最简单。
2. 重写`event()`函数。`event()`函数是所有对象的事件入口,`QObject`和`QWidget`中的实现,默认是把事件传递给特定的事件处理函数。
3. 在特定对象上面安装事件过滤器。该过滤器仅过滤该对象接收到的事件。
4. 在`QCoreApplication::instance()`上面安装事件过滤器。该过滤器将过滤所有对象的所有事件,因此和`notify()`函数一样强大,但是它更灵活,因为可以安装多个过滤器。全局的事件过滤器可以看到 disabled 组件上面发出的鼠标事件。全局过滤器有一个问题:只能用在主线程。
5. 重写`QCoreApplication::notify()`函数。这是最强大的,和全局事件过滤器一样提供完全控制,并且不受线程的限制。但是全局范围内只能有一个被使用(因为`QCoreApplication`是单例的)。
```c++
class Label : public QWidget
{
public:
Label()
{
installEventFilter(this);
}
bool eventFilter(QObject *watched, QEvent *event)
{
if (watched == this) {
if (event->type() == QEvent::MouseButtonPress) {
qDebug() << "eventFilter";
}
}
return false;
}
protected:
void mousePressEvent(QMouseEvent *)
{
qDebug() << "mousePressEvent";
}
bool event(QEvent *e)
{
if (e->type() == QEvent::MouseButtonPress) {
qDebug() << "event";
}
return QWidget::event(e);
}
};
class EventFilter : public QObject
{
public:
EventFilter(QObject *watched, QObject *parent = 0) :
QObject(parent),
m_watched(watched)
{
}
bool eventFilter(QObject *watched, QEvent *event)
{
if (watched == m_watched) {
if (event->type() == QEvent::MouseButtonPress) {
qDebug() << "QApplication::eventFilter";
}
}
return false;
}
private:
QObject *m_watched;
};
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
Label label;
app.installEventFilter(new EventFilter(&label, &label));
label.show();
return app.exec();
}
```

结果:

```
QApplication::eventFilter
eventFilter
event
mousePressEvent
```

# 头文件循环包含

这个问题我一般是这么解决的: 1.header1.h文件中进行类声明:class ClassA;(因为.h中只用到了类的指针,因此不需要类的具体定义); 2.在source1.cpp中,包含ClassA的定义文件header2.h(因为.cpp里会用到类的函数等)

<https://blog.csdn.net/hazir/article/details/38600419>
Loading

0 comments on commit eb00a7e

Please sign in to comment.