Hexo

  • Home

  • Tags

  • Archives

  • Search

RM开发

Posted on 2021-12-19
Symbols count in article: 662 | Reading time ≈ 1 mins.
uint32_t lut_cal(uint32_t *lut, uint32_t x_base, uint32_t x_step, uint32_t index_base,
uint32_t in_max, uint32_t in_data, uint32_t lut_sz) {

uint32_t index_off = (in_data - x_base) >> x_step;
uint32_t index = index_off + index_base;
assert(index < lut_sz && index >= 0);
uint32_t y0 = lut[index];
uint32_t y1 = lut[min(index + 1, lut_sz - 1)];
uint32_t frac = in_data - ((index_off << x_step) + x_base);
uint32_t y;
assert(y1 >= y0);
if (in_data <= in_max) {
y = (((y1 - y0) * frac) >> x_step) + y0;
} else {
y = y0;
}
return y;
}

PyQt5

Posted on 2021-12-17 | Edited on 2021-12-29
Symbols count in article: 2.5k | Reading time ≈ 2 mins.

1. PyQt5 tutorial ZetCode 笔记

  1. Date and time
    1. Current date and time
      a. Qt.ISOData
      b. DefaultLocaleLongDate
    2. UTC time
    3. Number of days
    4. Difference in days
    5. Date arithmetic
    6. Daylight saving time
    7. Unix epoch
    8. Julian day
      便于时间相减,直接计算天数
  2. Hello, world
    1. Simple example
    2. Icon
    3. Tooltip
      a. Btn.sizeHint()
    4. closeEvent
      qbtn.clicked.connect(QApplication.instance().quit)
    5. Message box
    6. Center
  3. Menus and toolbar
    1. Statusbar
    2. Menu
    3. Sub menu
    4. Check menu
    5. Context menu
    6. Toolbar
    7. Main window
  4. Layout
    1. QHBoxlayout
    2. QGridLayout
  5. Event and signal
    1. LED demo
    2. Key event handler
    3. Event object, mouse event
    4. Push btn event sender
    5. Emitting signal
  6. Dialog
    1. QDialog
    2. QcolorDialog and QFrame
    3. QFontDialog, btn.setSizePolicy
    4. QFileDialog, Qaction.triggered, QMainWindow
  7. Qwidge1
    1. QCheckBox
    2. Toggle button, color demo, Qcolor.setRed
    3. Qslider
    4. QProgressBar
    5. QCalendarWidge
  8. QWidget2
    1. Qpixmap, Qlabel.setPixmap
    2. QLineEdit
    3. Qsplitter
    4. QComBox 复选框
  9. Drag and Drop 稍微还是有些不理解
    1. Qdrag
    2. QMineData
    3. dragEnterEvent仅获取对应的类型
    4. dropEvent设置drop时的动作
  10. QPainter
    1. Draw text
    2. Draw point
    3. Draw color
    4. drawRect, setBrush
    5. Qpen(line, curves, rectangle)
    6. Qbrush
    7. BezierCurve

2. Python and PyQt: Building a GUI Desktop Calculator 笔记

MVC典型流程,先画View,再设计控制逻辑Control,最后再设计计算模型Model,分步骤实现。

3. Qt Designer and Python: Build Your GUI Applications Faster 笔记

coding一个相当完整的mainwindow

4. 进一步熟悉

书籍《PyQt快速开发与实践》中介绍了一些基本内容,后续可以进一步掌握,包括设计师的使用,基础组件,布局管理,高级组件等,还是要结合实战进行更深入的学习和使用才行。

5. PythonQwt

QWT可以用于生成各种统计图,给具有专业技术背景的程序提供GUI组件和实用类,以基于2D方式的窗体不见来显示数据。

PythonQwt
Qwgt User’s Guide 6.2.0
Qt之Qwt曲线绘制

5.1. curve fitting

event_filter_demo.py

制作曲线调整的工具,为加速开发就基于event filter demo修改,可以适配7条曲线的调试和保存,主要改动如下:

  1. 支持鼠标和键盘拖动曲线,固定Y方向
  2. 显示7条曲线,可隐藏
  3. 支持曲线的导入和导出,暂时指定文件名

6. 参考链接

  1. The complete PyQt5 tutorial — Create GUI applications with Pyth on
  2. PyQt5 Reference Guide 官方文档
  3. PyQt5 tutorial ZetCode
  4. PyQt-Examples Github contains more in-depth examples for different topics
  5. PyQt5-Tutorial-Examples Sources and images for ZetCode’s PyQt5 tutorial
  6. PyQt5图形界面编程(目录) 知乎
  7. realpython的几个相关课程
    1. Python and PyQt: Building a GUI Desktop Calculator 入门制作一个计算器,手把手指导,很不错
    2. Qt Designer and Python: Build Your GUI Applications Faster QtDesigner的简单使用
    3. PyQt Layouts: Create Professional-Looking GUI Applications
    4. Build a Bulk File Rename Tool With Python and PyQt
    5. Python and PyQt: Creating Menus, Toolbars, and Status Bars
    6. Handling SQL Databases With PyQt: The Basics
    7. Use PyQt’s QThread to Prevent Freezing GUIs
    8. Build a Contact Book With Python, PyQt, and SQLite
    9. 相关的repo materials
  8. Qt Designer Manual
  9. [ PyQt入门教程 ] Qt Designer工具的使用 使用qtdesigner实现登录框和业务逻辑代码

Qt之QTimer

Posted on 2019-05-12 | Edited on 2021-12-13
Symbols count in article: 2.1k | Reading time ≈ 2 mins.
  • 第10篇 Qt5基础(十)Qt定时器和随机数
  • QT之 QTimer使用方法
  • Qt 之 QProgressBar

效果图如下,定时器设置为100ms触发,进度条也随之滚进。

MainWindow.h

#pragma once

#include <QtWidgets/QMainWindow>
#include "ui_MainWindow.h"
#include <QTimer>
#include <QProgressBar>

class MainWindow : public QMainWindow
{
Q_OBJECT

public:
MainWindow(QWidget *parent = Q_NULLPTR);

private:
Ui::MainWindowClass ui;
QTimer *timer_;
QProgressBar *progress_;
private slots:
void UpdateProgress();
};

MainWindow.cpp

#include "MainWindow.h"
#include <QPushButton>
#include <QLayout>

MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent) {
ui.setupUi(this);
timer_ = new QTimer(this);
timer_->setInterval(100);

progress_ = new QProgressBar(this);
progress_->setRange(0, 100);
progress_->setValue(50);

QPushButton *okBtn = new QPushButton(this);
okBtn->setText(tr("ok"));

QPushButton *cancelBtn = new QPushButton(this);
cancelBtn->setText(tr("cancel"));

QHBoxLayout *btnLayout = new QHBoxLayout();
btnLayout->addWidget(okBtn);
btnLayout->addWidget(cancelBtn);
QVBoxLayout *mainLayout = new QVBoxLayout();
mainLayout->addWidget(progress_);
mainLayout->addLayout(btnLayout);

QWidget *widget = new QWidget(this);
//setCentralWidget(widget);
//centralWidget()->setLayout(mainLayout);

widget->setLayout(mainLayout);
setCentralWidget(widget);

connect(okBtn, &QPushButton::clicked, timer_, static_cast<void (QTimer::*)()> (&QTimer::start));
connect(cancelBtn, &QPushButton::clicked, timer_, &QTimer::stop);
connect(timer_, &QTimer::timeout, this, &MainWindow::UpdateProgress);
}


void MainWindow::UpdateProgress() {
int curVal = progress_->value();
++curVal;
if (curVal >= 100) {
curVal = 0;
}
progress_->setValue(curVal);
}

main.cpp

#include "MainWindow.h"
#include <QtWidgets/QApplication>

int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}

Qt多线程

Posted on 2019-05-12 | Edited on 2021-12-13
Symbols count in article: 4.5k | Reading time ≈ 4 mins.

这两天在弄Qt的多线程,简要记录一些心得。

ref

  • Qt使用多线程的一些心得——1.继承QThread的多线程使用方法 CSDN 写得还是挺不错的,比较实用
  • Qt使用多线程的一些心得——2.继承QObject的多线程使用方法 CSDN

简述

Qt有两种多线程方法,一种是继承QThread的run函数,另一种是把继承于QObject的类转移到新的Thread里。Qt4.8一般采用继承QThread的方法,但在4.8之后官方建议采用第二种方法,使用会更加灵活一些。
网络上较多的方法是采用第一种,包括很多书籍教程也是,这里把两种方法都进行下记录。

通常,应用程序都是在一个线程中执行的,但当调用一个好事操作,例如大批量Io或大量矩阵变换等CPU密集操作时,用于界面常常会冻结,可以使用多线程来解决这些问题。

优势

  1. 提高应用程序的响应速度,尤其对于GUI来说,如果某个操作耗时很长,整个系统都在等待,将不能响应鼠标、键盘、菜单等操作。利用多线程可以将耗时操作放到一个新的线程中,避免该问题。
  2. 使多CPU更加有效
  3. 改善程序结构,将复杂的进行拆成多个线程,成为独立或半独立的运行部分,有利于代码的理解和维护。

不足

  1. 行为无法预期,多次执行结果可能不尽相同
  2. 多线程的执行顺序无法保证,与操作系统地调度策略和线程优先级有关
  3. 多线程的切换可能发生在任何时间和地点
  4. 对代码的敏感度较高,细微修改都可能造成意向不到的结果

继承QThread

使用Qthread时有一个重要的规则,QThread只有run函数是在新线程里的,其他所有函数都在QThread生成的线程里。之前某个写法一直阻塞UI就是因为我在前天函数进行了操作,但其他函数并不是在子线程中的。所以要注意以下两点:

  1. 如果QThread是在ui所在线程里成成,那么QThread的其他非run函数都是和ui一样的,要确保耗时的操作都在run函数中完成。
    在UI线程下调用QThread的非run函数和执行普通函数没有区别。但要注意,如果这2. 个函数对线程的某个变量进行变更,并且该变量在run函数中也会使用,就要注意同步的问题,因为虽然都是QThread的类里,但run和非run函数实际上两个不同线程进行的,之间可能有竞争。

任何继承于QThread的线程都是通过继承QThread的run函数来实现多线程的,因此,必须重写QThread的run函数,把复杂逻辑写在QThread的run函数中。

参考博客提供的代码,如下,稍微替换了下命名风格。该类继承自QThread,包含了Qt类的常见内容,包括普通方法,信号和槽,还有run函数。其中SetSth, GetSth, DoSth函数都进行了500ms的延迟,用于验证在QThread::run()之外调用QThread的成员函数并会在新的线程中运行。

ThreadFromQThread.h

#pragma once

#include <QThread>
#include <QMutex>

class ThreadFromQThread : public QThread {
Q_OBJECT

signals:
void Msg(const QString& info);
void Progress(int present);

public:
ThreadFromQThread(QObject* par);
~ThreadFromQThread();
void SetSth();
void GetSth();
void Set_runCnt(int count);
void run();
void DoSth();
private:
int runCnt_;
QMutex mutex_;
bool canRun_;

public slots:
void StopImmediately();
};

ThreadFromQThread.cpp

#include "ThreadFromQThread.h"
#include <QDebug>
#include <QMutex>
ThreadFromQThread::ThreadFromQThread(QObject* par) : QThread(par)
, runCnt_(20) {
}

ThreadFromQThread::~ThreadFromQThread() {
qDebug() << "ThreadFromQThread::~ThreadFromQThread()";
}

void ThreadFromQThread::SetSth() {
msleep(500);
QString str = QString("%1->%2,thread id:%3").arg(__FUNCTION__).arg(__FILE__).arg((int)QThread::currentThreadId());
emit Msg(str);
}

void ThreadFromQThread::GetSth() {
msleep(500);
emit Msg(QString("%1->%2,thread id:%3").arg(__FUNCTION__).arg(__FILE__).arg((int)QThread::currentThreadId()));
}

void ThreadFromQThread::Set_runCnt(int count) {
runCnt_ = count;
emit Msg(QString("%1->%2,thread id:%3").arg(__FUNCTION__).arg(__FILE__).arg((int)QThread::currentThreadId()));
}

void ThreadFromQThread::run() {
int cnt = 0;
QString str = QString("%1->%2,thread id:%3").arg(__FILE__).arg(__FUNCTION__).arg((int)QThread::currentThreadId());
emit Msg(str);
while (1) {
sleep(1); // 共20秒完成一次进度条,不过doSth还会暂停一点时间
++cnt;
emit Progress(((float)cnt / runCnt_) * 100);
emit Msg(QString("ThreadFromQThread::run times:%1").arg(cnt));
DoSth();
if (runCnt_ == cnt) {
break;
}
{
QMutexLocker locker(&mutex_);
if (!canRun_)
return;
}
}
}

void ThreadFromQThread::DoSth() {
msleep(500);
emit Msg(QString("%1->%2,thread id:%3").arg(__FUNCTION__).arg(__FILE__).arg((int)QThread::currentThreadId()));
}

void ThreadFromQThread::StopImmediately() {
QMutexLocker locker(&mutex_);
canRun_ = false;
}

博主提供了如下的UI界面,用于验证:

  1. UI线程中调用SetSth和GetSth,是否会阻塞UI线程
  2. 调用quit函数和exit是否会停止子线程
  3. 调用terminte是否会停止子线程

基本效果如下

UI界面上提供一个进度条,由主程序的定时器控制,每100ms触发一次,用于证明UI线程是否阻塞。
第二个进度条由子线程控制。
run按钮会运行子线程。
点击getSth, setSth, doSth等按钮时候,都是调用子线程类中的非run函数,可以看到对应的thread ID其实并不是子线程的ID。
并且点击quit,thread没有任何响应,QThread在不调用exec()的情况下的exit和quit函数不会起作用。
点击terminate按钮,线程立即终止。

总结一下:

  1. 正确终止子线程
    在子线程类中添加bool变量,主线程修改该变量然后在线程的run函数中判断是否退出。注意要用QMutexLocker枷锁判断,避免线程冲突。
  2. 正确启动线程
    1. 全局线程
      方法一:在窗口中创建,在窗口析构时要先调用线程的wait函数,等待子线程完全结束
      方法二:创建时连接connect(thread, &QThread::finished, &QObject::deleteLater;,父对象为NULL,在线程结束时自行销毁(消息循环中确认没有该对象后析构)
    2. 局部线程
      采用以上方法二即可
  3. 重复创建子线程时,可能要注意重复生成还有析构,以及时切换到新线程中完成任务,参考代码。

继承QObject

Qt官方比较推荐继承QObject的方法来实现,更加灵活。
QObject是Qt框架的基本类,但凡涉及到信号槽有关的类都是继承于QObject。QObject是一个功能异常强大的类,它提供了Qt关键技术信号和槽的支持以及事件系统的支持,同时它提供了线程操作的接口,也就是QObject是可以选择不同的线程里执行的。
QObject的线程转移函数是:void moveToThread(QThread * targetThread) ,通过此函数可以把一个顶层Object(就是没有父级)转移到一个新的线程里。
QThread非常容易被新手误用,主要是QThread自身并不生存在它run函数所在的线程,而是生存在旧的线程中,此问题在上一篇重点描述了。由于QThread的这个特性,导致在调用QThread的非run函数容易在旧线程中执行。

正则表达式

Posted on 2019-05-09 | Edited on 2021-12-13
Symbols count in article: 2.1k | Reading time ≈ 2 mins.

ref

这个工具还是得找个机会熟悉起来,用的机会太少了,但感觉很有必要。

  • 正则表达式 - 教程 菜鸟教程
  • 正则表达式30分钟入门教程
  • 正则表达式(RegEx)——快速参考
  • 正则表达式 - 元字符
  • Regular Expression Language - Quick Reference
  • 正则表达式 知乎
  • 最实用的正则表达式整理
  • 《JavaScript 正则表达式迷你书》问世了!
  • 学习正则表达式有哪些入门和进阶的书籍? 知乎
  • 正则表达式系列总结
  • 你是如何学会正则表达式的?

正则表达式示例

  • 正则表达式 - 示例 菜鸟教程
  • 正则表达式在线测试 菜鸟教程
  • 常用正则表达式 jb51
  • 最全的常用正则表达式大全——包括校验数字、字符、一些特殊的需求等等
  • 知道这20个正则表达式,能让你少写1,000行代码

测试工具

  • regular epressions 101 在线测试工具
  • Regexper 正则表达式可视化工具
  • Regester 正则表达式测试工具

相关介绍

基本概念

正则表达式(Regular Expression)是一种文本模式,包括普通字符(例如,a 到 z 之间的字母)和特殊字符(称为”元字符”)。

正则表达式使用单个字符串来描述、匹配一系列匹配某个句法规则的字符串。

为什么使用

典型的搜索和替换操作要求您提供与预期的搜索结果匹配的确切文本。虽然这种技术对于对静态文本执行简单搜索和替换任务可能已经足够了,但它缺乏灵活性,若采用这种方法搜索动态文本,即使不是不可能,至少也会变得很困难。

通过使用正则表达式,可以:

  • 测试字符串内的模式。
    例如,可以测试输入字符串,以查看字符串内是否出现电话号码模式或信用卡号码模式。这称为数据验证。
  • 替换文本。
    可以使用正则表达式来识别文档中的特定文本,完全删除该文本或者用其他文本替换它。
  • 基于模式匹配从字符串中提取子字符串。
    可以查找文档内或输入域内特定的文本。

基本语法

正则表达式是由普通字符(例如字符 a 到 z)以及特殊字符(称为”元字符”)组成的文字模式。模式描述在搜索文本时要匹配的一个或多个字符串。正则表达式作为一个模板,将某个字符模式与所搜索的字符串进行匹配。

  • 普通字符
    普通字符包括没有显式指定为元字符的所有可打印和不可打印字符。这包括所有大写和小写字母、所有数字、所有标点符号和一些其他符号。
  • 特殊字符
    所谓特殊字符,就是一些有特殊含义的字符,许多元字符要求在试图匹配它们时特别对待。若要匹配这些特殊字符,必须首先使字符”转义”,即,将反斜杠字符\ 放在它们前面。
  • 限定符
    用来指定正则表达式的一个给定组件必须要出现多少次才能满足匹配。有*或+或?或{n}或{n,}或{n,m}共6种。

  • 定位符
    使您能够将正则表达式固定到行首或行尾。它们还使您能够创建这样的正则表达式,这些正则表达式出现在一个单词内、在一个单词的开头或者一个单词的结尾。

    定位符用来描述字符串或单词的边界,^和$分别指字符串的开始与结束,\b描述单词的前或后边界,\B表示非单词边界。

  • 选择
    用圆括号将所有选择项括起来,相邻的选择项之间用|分隔。

  • 反向引用
    对一个正则表达式模式或部分模式两边添加圆括号将导致相关匹配存储到一个临时缓冲区中,所捕获的每个子匹配都按照在正则表达式模式中从左到右出现的顺序存储。缓冲区编号从1开始,最多可存储99个捕获的子表达式。每个缓冲区都可以使用\n访问,其中n为一个标识特定缓冲区的一位或两位十进制数。

常用的元字符

字符 描述
x|y 匹配 x 或 y。例如,’z|food’ 能匹配 “z” 或 “food”。’(z|f)ood’ 则匹配 “zood” 或 “food”。
\b 匹配一个单词边界,也就是指单词和空格间的位置。例如, ‘er\b’ 可以匹配”never” 中的 ‘er’,但不能匹配 “verb” 中的 ‘er’。
\cx 匹配由 x 指明的控制字符。例如, \cM 匹配一个 Control-M 或回车符。x 的值必须为 A-Z 或 a-z 之一。否则,将 c 视为一个原义的 ‘c’ 字符。
\d 匹配一个数字字符。等价于 [0-9]。
\D 匹配一个非数字字符。等价于 [^0-9]。
\s 匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [ \f\n\r\t\v]。
\S 匹配任何非空白字符。等价于 [^ \f\n\r\t\v]。
. 匹配除换行符(\n、\r)之外的任何单个字符。要匹配包括 ‘\n’ 在内的任何字符,请使用像”(.|\n)”的模式。
\w 匹配字母、数字、下划线。等价于’[A-Za-z0-9_]’。
\un 匹配 n,其中 n 是一个用四个十六进制数字表示的 Unicode 字符。例如, \u00A9 匹配版权符号 (?)。
(?=E) 表示表示表达式后紧随E才匹配, 例如要只在using后面是naemspace时才匹配using,用using(?=\s+namespace)
(?!E) 表示表示表达式后不跟随E才匹配

编程规范

Posted on 2019-05-07 | Edited on 2021-12-13
Symbols count in article: 2.9k | Reading time ≈ 3 mins.
  • Google 开源项目风格指南 (中文版) 包含5个中文版的风格指南
  • Google C++ Style Guide
  • C++ 风格指南 - 内容目录
  • 一张图总结Google C++编程规范(Google C++ Style Guide)

华为的编程规范参考了很多的Google编程规范。

有一张图很好的总结了Google的编程规范

Google编程规范笔记

头文件

  1. 避免使用前置声明,用头文件
    优点:减少include,节省编译时间,减少重新编译头文件的可能
    缺点:隐藏了依赖关系
    前置声明是不完整类型
  2. 10行代码下才适合内联,不适用于循环和析构函数
  3. include顺序
    对应的头文件;C系统文件;C++系统文件;其他库的头文件;本项目的头文件
    对应头文件放在最前面,可以在build的时候及时发现错误

作用域

  1. 命名空间内不缩进
  2. 鼓励在cc文件中使用匿名命名空间和static声明,但不能在h文件中使用;(匿名空间其实就是文件作用域,类似于C中的static声明的作用域)
  3. cc文件中的变量不会被外部文件引用时,使用匿名空间或者static,因为具有内部链接性,不会在其他文件中访问到;(尽可能限制作用域范围,避免污染)
  4. 静态成员函数或非成员函数最好放在命名空间中,静态成员应该是和类相关的,如果不相关最好放在命名空间中,尽量单独形成编译单元
  5. 禁止定义静态储存周期非POD变量,构造、析构和初始化可能是不确定的

类

  1. 类定义中单参数的构造函数应该用explicit标记
  2. 优先组合,而非继承;虽然继承可以复用代码,但耦合性太高,散布在父子类之间的代码理解起来更不易;不能重写非虚函数;基类可能有自己的数据成员,对理解有挑战,实际很难拿捏准;
  3. 不要重载,除非确有需要,因为容易混淆,如重载
  4. 不在构造函数中做过多逻辑相关的初始化

函数

  1. 入参在前,出参在后
  2. 输入参数是值参或const引用(不要使用非const引用,容易误解),输出参数为指针

其他

  1. 尽少使用默认参数,改用重载
  2. 不使用C++异常,得不偿失,虽然允许高层决定底层中不可能的失败,减少使用错误代码,但实际上常安全很困难,异常可能会扰乱程序的正常流程
  3. 禁用RTTI。RTTI虽然允许C++在运行时识别对象的类型,通过dynamic_cast和typeid
  4. 用static_cast替代C风格的值转换
  5. 只在记录日志时使用流:否则容易混乱IO类型
  6. 内建整型仅用int,否则使用<stdint.h>中长度精确的整型
  7. 尽可能用sizeof(varname)代替sizeof(type),因为变量类型改变时会自动更新
  8. C++ 11中用nullptr,03用NULL
  9. 注意64位可移植型,例如对齐和指针大小等
  10. 预处理宏

    1. 不在.h中使用宏
    2. 在马上使用时才定义
  11. 不使用复杂的模板编程

    1. 对不熟练C++的人难理解
    2. 编译出错的信息友好
    3. 重构工具难发挥
    4. 维护成本大于简洁的接口
    5. 对外接口不使用模板,仅在内部实现使用

命名约定

  1. 文件名通通小写,下划线可用可不用
  2. 类型命名(类,结构体,类型定义,模板参数,枚举)使用大小写风格,首字母大写——这样可区分类与对象
  3. 变量、函数参数、成员数据均小写,下划线连接,类变量成员下划线结尾,结构体的不用
  4. 声明为const或constexpr的变量,k开头,大小写混合,静态变量同
  5. 函数也是大小写混合,大写开头,并且缩写字母也是首字母大写

    AddTableEntry()
    DeleteUrl()
    OpenFileOrDie()
  6. 命名空间全小写

  7. 枚举类型采用常量风格,旧的不用改

    enum UrlTableErrors {
    kOK = 0,
    kErrorOutOfMemory,
    kErrorMalformedInput,
    };
    enum AlternateUrlTableErrors {
    OK = 0,
    OUT_OF_MEMORY = 1,
    MALFORMED_INPUT = 2,
    };
  1. 宏命名:全大写,下划线连接
  2. 行尾空格两格注释
  3. TODO

    // TODO(kl@gmail.com): Use a "*" here for concatenation operator.
    // TODO(Zeke) change this to use relations.
    // TODO(bug 12345): remove the "Last visitors" feature
  4. 注释是为了看懂,不是炫耀语言水平,中英皆可

  5. 未被使用的参数注释掉void Circle::Rotate(double /*radians*/) {}
  6. if可单行,一行可不用花括号,分支中使用了花括号,其他都得加
  7. 空循环体用花括号
  8. 预处理不缩进,即使在缩进块内
  9. public等缩进1格
  10. 命名空间不缩进
  11. :前后有空格

规则特例

  1. 现有不合规的代码网开一面

Qt

  • Qt5开发及实例(完整版) P111
  1. 与Google的编码规范基本一致
    1. 成员函数、成员变量命名风格,不过成员变量按照驼峰命名,小写开头,增加下划线
    2. 存取函数按照Get_var等进行
  2. 控件统一在后面加后缀
  3. V和H不需要细分,控件的细分不需要,只需要表达出是哪个控件就好,剩下的知识控件布局的问题,一般复杂度还可以。
class InputDlg: public QDialog
{
Q_OBJECT

public:
InputDlg(QWidget *parent = 0);

private slots:
void ChangeName();
void ChangeSex();
void ChangeAge();

private:
QLabel *nameLabel1;
QLabel *sexLabel1;
QLabel *ageLabel1;
QLabel *nameLabel2;
QLabel *sexLabel2;
QLabel *ageLabel2;
QPushButton *nameBtn;
QPushButton *sexBtn;
QPushButton *scoreBtn;
QGridLayout *mainLayout;
}

InputDlg::InputDlg(QWidget *parent = 0): QWidget(parent)
{
QLabel *queryLabel = new QLabel(tr("Query"));
QLineEdit *queryEdit = new QLineEdit();
QTableView *resView = new QTableView();

QHBoxLayout *queryLayout = new QHBoxLayout();
QVBoxLayout *mainLayout = new QVBoxLayout();
}

曲线调整

Posted on 2019-05-07 | Edited on 2021-12-13
Symbols count in article: 4.1k | Reading time ≈ 4 mins.

有一段代码是利用高斯函数来做曲线拟合的,比较简单和方便,调试起来可能也会简单些。首先利用高斯函数生成0~256之间的sigma为75的曲线,第一个点按照1024归一化。大致如下

然后利用标准正太分布公式来计算给定sigma和mean的高斯曲线。

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cstdio>
#include <cmath>

const int STD_GS_LUT_Bit = 8;
const int STD_GS_Sigma = 75;
const int STD_GS_Mean = 0;
const int STD_GS_PRCS = 10; // prcs of output
const int STD_GS_LUT_Sz = (1 << STD_GS_LUT_Bit) + 1;
static int STD_GS_LUT[STD_GS_LUT_Sz];

int LUTInter(int val, int valBit, int *LUT, int LUTBit) {
const int LUTSz = (1 << LUTBit) + 1;
const int shift = valBit - LUTBit;
const int index = val >> shift;
if (index >= LUTSz - 1) { // index + 1>= LUTSz -> index + 1 <= LUTSz - 1 -> index + 1 < LUTSz
return LUT[LUTSz - 1];
} else {
int ret = LUT[index] + ((LUT[index + 1] - LUT[index]) * (val - (index << shift)) >> shift);
return ret;
}
}

void LUTInterFromSTD_GS(int *LUT, int LUTSz = 33, int mean = 0, int sigma = 80, int weight = 50) {
const int STD_LUT_Bit = 8;
const int PRCS = 10;
int shift = PRCS;
const int scl = (STD_GS_Sigma << shift) / sigma;
for (int i = 0; i < LUTSz; ++i) {
int val = abs((i << 8) / LUTSz - mean) * scl >> 8;
LUT[i] = (weight * LUTInter(val, shift, STD_GS_LUT, STD_GS_LUT_Bit) + (1 << (shift - 1))) >> shift;
}
}

void CalcSTD_GS(int *LUT, int sz, int prcs, int sigma, int mean) {
// prcs 10
// sigma U6.8
// mean U6.8

double var = sigma / 255.0 * sigma / 255.0;
double u = mean / 255;
for (int i = 0; i < sz; ++i) {
double val = i * 1.0 / sz;
LUT[i] = round((1 << prcs) * exp(-(val - u) * (val - u) / (2 * var)));
}
}

void printmat(const int *p, int len, const char *filename = "otuput.txt") {
freopen(filename, "w", stderr);
for (int i = 0; i < len; ++i)
fprintf(stderr, "%4d ", p[i]);
fprintf(stderr, "\n");
}

void ut() {
CalcSTD_GS(STD_GS_LUT, STD_GS_LUT_Sz, STD_GS_PRCS, STD_GS_Sigma, STD_GS_Mean);
printmat(STD_GS_LUT, STD_GS_LUT_Sz, "LUT_STD_GS.txt");
int lut[33];
LUTInterFromSTD_GS(lut, 33, 0, 80, 128);
printmat(lut, 33, "lut.txt");
}

int main() {
ut();

return 0;
}
clr;

x = 0:256;
y = normpdf(x, 0, 75);
y = (y / y(1) * 1024);

LUT = [1024 1024 1024 1023 1023 1022 1021 1020 1018 1017 1015 1013 1011 1009 1007 1004 1001 998 995 992 989 985 982 978 974 969 965 961 956 951 946 941 936 931 925 920 914 908 902 896 890 884 878 871 864 858 851 844 837 830 823 816 808 801 793 786 778 771 763 755 747 739 731 724 716 708 699 691 683 675 667 659 651 642 634 626 618 609 601 593 585 577 569 560 552 544 536 528 520 512 504 496 488 480 473 465 457 449 442 434 427 419 412 405 397 390 383 376 369 362 355 348 342 335 328 322 315 309 303 297 290 284 278 272 267 261 255 250 244 239 233 228 223 218 213 208 203 198 193 189 184 180 175 171 167 163 159 155 151 147 143 139 136 132 129 125 122 118 115 112 109 106 103 100 97 95 92 89 87 84 82 79 77 75 72 70 68 66 64 62 60 58 56 55 53 51 50 48 46 45 43 42 41 39 38 37 36 34 33 32 31 30 29 28 27 26 25 24 23 22 22 21 20 19 19 18 17 17 16 15 15 14 14 13 13 12 12 11 11 10 10 10 9 9 8 8 8 8 7 7 7 6 6 6 6 5 5 5 5 5 4 4 4 4 4 3 3 ];

figure(1);
plot(x, y);
title('gauss curve, \sigma = 75, first point norm to 1024');
% plot(x, y, x, LUT);
d = y - LUT;
max(abs(d))


x = 0:8:256;
y = normpdf(x, 0, 80);
y = (y / y(1) * 128);

LUT = [ 128 128 126 123 119 115 109 102 95 89 81 74 66 59 52 45 39 34 29 24 20 17 14 11 9 7 6 5 3 3 2 2 1 ];
figure(2);
plot(x, y, 'b', x, LUT, 'r');
d = y - LUT;
max(abs(d))

C、C++基础

Posted on 2019-05-06 | Edited on 2021-12-19
Symbols count in article: 1.4k | Reading time ≈ 1 mins.

工作中的工程主要是C,不过公共的库函数设计上面存在一些问题,例如全局变量设计不合适、函数声明不合理、部分函数定义有问题,导致可移植性有点差,改成cpp文件后,报了很多错误,这里记录一些容易错误的地方,也顺带回顾一下基础知识。


全局变量的声明和使用(跨文件)

  • How do I use extern to share variables between source files?
  • What is the difference between a definition and a declaration?

错误的参考

  • C语言:全局变量在多个c文件中公用的方法

上面的链接中Jonathan Leffler已经回答的很好了。对于声明和定义来说:

  • 一个变量被声明是指告知编译器,有个某种类型的变量会被使用,但编译不会为它分配内存。
  • 一个变量并定义是指编译器会分配对应变量的存储空间。

一个变量可以被多次声明,但在给定的作用域范围内只能定义一次。一个变量的定义可以是一种声明,反之不是。

最佳实践方式是使用包含extern关键字的头文件来声明变量,然后用某个包含该头文件的源文件对该变量进行定义。

指导原则:

  • 头文件中值包含extern方式声明的变量,不要使用static或其他不合格的定义(静态全局变量只在本地文件中可以访问,因此有多个副本)
  • 对于任何变量,仅在一个头文件中声明(SPOT-Single Point of Truth)
  • 不要在在源文件中使用extern方式声明变量,源文件中应该用include头文件的方式来声明他们。——我司的实践规范,但实际代码并没有这么遵循,这样代码不好维护。
  • 对于任何变量,只能有一个源文件进行定义,并且建议初始化为0。
  • 头文件和源文件中的声明和定义应该保持一致。
  • 不需要在函数中用extern来声明变量。
  • 尽量避免全局变量。

例如有file3.h、file1.c和file2.c三个文件(头文件和源文件名称一致会更好):

file3.h

extern int global_variable;  /* Declaration of the variable */

file1.c

#include "file3.h"  /* Declaration made available here */
#include "prog1.h" /* Function declarations */

/* Variable defined here */
int global_variable = 37; /* Definition checked against declaration */

int increment(void) { return global_variable++; }

file2.c

#include "file3.h"
#include "prog1.h"
#include <stdio.h>

void use_it(void)
{
printf("Global variable: %d\n", global_variable++);
}

相关问题锦集

  1. C++ 11为什么引入nullptr?
    在C++中NULL定义为0,在函数匹配时会有二义性

Qt资源汇总

Posted on 2019-05-04 | Edited on 2021-12-13
Symbols count in article: 1.6k | Reading time ≈ 1 mins.

最近在工作中使用到Qt,所以准备复习一下Qt相关的东西,网络上关于Qt的东西还是比较杂,仅仅记录下目前看到可能会实用的东西,同时按照自己的方式整理下,便于后续深入了解。

ref:

  1. Qt 资料大全 CSDN

Qt官网

  • Qt官网
  • Qt下载 Qt源码也在这里面下载
  • Qt所有版本下载
  • Qt官方发布下载
  • Qt wiki

Github & Third-Party

  • Qt Project github 官方镜像,各种强大的Qt项目及丰富的Qt库
  • Awesome Qt
  • Awesome C++/[]
  • inqlude Qt库存档
  • 免费的编程中文书籍索引

社区论坛

国外

  • Qt-Centre 很全面的国外网站,包含forum、wiki、docs和blogs等
  • Qt-forum 知名的Qt论坛
  • Linux-Apps 可以找到很多免费的 Qt 应用,获得源码来学习、研究。
  • Qt Software 提供第三方商业软件和开源软件的Qt用户社区
  • QUIt Coding

国内

  • Qt论坛 CSDN
  • QTCN 开发网
  • Qter 开源社区 里面富含 Qter 们开发的实用开源项目和作品,以及一系列优秀的原创教程 - 图文并茂、简单易学

blog

  • Qt 相关博客的汇集者
  • 一去丶二三里
  • 豆子
  • 1+1=10
  • QtDebug

书籍

  • QmlBook - A Book about Qt5
    • 英文版
    • 中文版
  • C++ Primer
  • C++ GUI Qt4编程
    Trolltech 的 Qt 培训教材,生动、全面、深刻地阐明了 Qt 程序的设计理念,轻松创建跨平台的解决方案。
  • Qt 高级编程
    阐述 Qt 高级编程技术的书籍。以工程实践为主旨,是对 Qt 现有的 700 多个类和上百万字参考文档中部分关键技术深入、全面的讲解和探讨。
  • Python Qt GUI 快速编程
    讲述如何利用 Python 和 Qt 开发 GUI 应用程序的原理、方法和关键技术。结构合理,内容详实,适合用作对Python、Qt 和 PyQt 编程感兴趣的童鞋。
  • C++ Qt 设计模式
    利用跨平台开源软件开发框架 Qt 阐释了 C++ 和设计模式中的主要思想,既复习了设计模式,又学了 C++/Qt,对于使用其它框架也是一个非常有用的参考。
  • Qt5 开发实战
    在全面阐述 Qt 基本功能的基础上,对新增的功能和服务进行了重点介绍。同时运用大量示例,集中讲解了应用程序的开发方法、技巧和必需的 API。
  • Qt5 开发及实例
    以 Qt5.4 为平台,循序渐进,在介绍开发环境的基础上,系统介绍 Qt5 应用程序的开发技术,通过实例介绍和讲解内容,将知识和能力融为一体。一般能够在比较短的时间内掌握 Qt5 应用技术。
  • Qt on Android 核心编程
    基于 Qt 5.2,详细讲述如何在移动平台 Android 上使用 Qt 框架进行开发。无论是专注于传统的桌面软件开发,还是希望尝试使用 Qt 在 Android 平台开发,都可以从中获得重要的知识与实例。
  • Qt Quick 核心编程
    着力于 QML 语言基础、事件、Qt Quick 基本元素,辅以简要的 ECMAScript(JavaScript)语言介绍,能够快速熟悉 Qt Quick 的基本知识和开发过程,详尽入微。
  • Qt Creator快速入门
    基于 Qt Creator 编写,全面涉及 Qt Quick;植根于 Qt 网络博客教程,可无限更新;对每个知识点详尽讲解,并设计了示例程序。
  • Qt5 编程入门
    基于 Qt5.3 编写,全面涉及 Qt Quick;植根于 Qt 网络博客教程,可无限更新;对每个知识点详尽讲解,并设计了示例程序。
  • Qt 5.9 C++开发指南
    倾心倾力之作《Qt 5.9 C++开发指南》

qt小结

Posted on 2019-05-02 | Edited on 2021-12-13
Symbols count in article: 11k | Reading time ≈ 10 mins.

重新温习一下Qt的教程,回顾下相关知识

  • 第1篇 Qt5基础(一)Qt开发环境的搭建和hello world
  • Qt快速入门系列教程目录

Qt开发环境的搭建和helloworld

  1. release版本
  2. 配置icon

Qt多窗口程序

  1. 在界面上显示的中文仍然使用tr(“中文”),便于使用Qt预言家来翻译整个软件要显示的字符创。
  2. 通过UI来设置信号和槽
  3. 在QtDesigner界面可以设置信号和槽
  4. QMessageBox
  5. lineEdit设置占位符
  6. lineEdit的trim和clear
  7. 类的前置声明

设置信号和槽的方式:

  1. F4拖动
  2. 右键转到槽,会要求选择信号
  3. QtDesigner中选择加号
  4. 纯代码设置

添加菜单图标

  1. 菜单如果方无法输入中文,可以复制和粘贴
  2. 添加资源文件
    1. 避免外部文件的问题
    2. 压缩
  3. 菜单栏、菜单项

布局管理器

  1. QSplitter
  2. Vertical Layout

实现Qt文本编辑功能

实现新建、保存和另存为三个功能

实现Qt文本查找功能

  1. dialog添加layout布局
  2. refactor功能,在.h中添加声明,然后直接在.cpp中实现定义
  3. %1作为占位符,在显示时会被arg()中的参数替换掉

    QMessageBox::warning(this, tr("查找"),
    tr("找不到%1").arg(str));
  4. 文件定位方法,使用定位器。

设置主窗口状态栏

  1. 添加动作状态提示statusTip
  2. 显示其他临时信息

Qt键盘、鼠标事件的处理

Qt定时器和随机数

定时器,一种是定时器事件,另一种是使用信号和槽。一般使用了多个定时器时最好使用定时器事件来处理。

2D绘图

渐变填充

绘制文字

绘制路径

绘制图片

QImage、QPixmap、QBitmap和QPicture,它们都是常用的绘图设备。其中QImage主要用来进行I/O处理,它对I/O处理操作进行了优化,而且可以用来直接访问和操作像素;QPixmap主要用来在屏幕上显示图像,它对在屏幕上显示图像进行了优化;QBitmap是QPixmap的子类,用来处理颜色深度为1的图像,即只能显示黑白两种颜色;QPicture用来记录并重演QPainter命令。

QPixmap 缩放、旋转、扭曲等

Qt坐标系统

双缓冲绘图

这一部分并没有完成,源代码也没有正确运行,如果默认都有双缓冲的话,那一开始的代码是怎么回事。

2D绘图部分窗口、视口的研究

图形视图提供大量定制的2D图形项进行管理和相互作用,便于控制多个图形以及之间的相互作用。主要包括Scene、View和Item。

QDrag
QMineData
QCursor

数据库 SQL模块

这一部分目前涉及比较少,暂时不深入研究

该模块可以分为

  • 用户接口层:用户接口层的几个类实现了将数据库中的数据链接到窗口部件上,它们是更高层次的抽象,即便不熟悉SQL也可以操作数据库
    QSqlQueryModel、QSqlTableModel和QSqlRelationalTableModel
  • SQL接口层:SQL接口层提供了对数据库的访问
    QSqlDatabase、QSqlQuery、A=QSqlError、QSqlField、QSqlIndex和QSqlRecord
  • 驱动层:驱动层为具体的数据库和SQL接口层之间提供了底层的桥梁
    QSqlDriver、QSqlDriverCreator、SQqlDriverCraetorBase、QSqlDriverPlugin和QSqlResult

简介

安装MySQL数据库

利用QSqlQuery类执行SQL语句

有界面和按钮之间的信号和槽控制

  1. 创建
  2. 操作结果
  3. 使用变量
  4. 批处理
  5. 事务操作:保证一个复杂的操作的原子性就是对于一个数据库操作序列,这些操作要么全部做完,要么一条也不做,它是一个不可分割的工作单位。

SQL查询模型QSqlQueryModel

简单操作和自定义(读写)

XML

  1. 使用DOM读取XML文档
  2. 使用DOM创建和操作XML文档,包含UI操控
  3. Qt中的SAX,另一种读取XML文档的方法,速度更快
  4. 使用流读写XML:QXmlStreamReader和QXmlStreamWriter,是SAX地替代品

总的来说,QT提供了3种读取XML文档的方法:

  1. QXmlStreamReader:一种快速的基于流的方式访问XML文档,特别使用于一次解析
  2. DOM(Document Object Model):将整个XML文档读入内存,构建一个树结构,允许程序在树上向前向后移动导航,可以允许多次解析。
  3. SAX(Simple API for XML):提供大量虚函数,以事件的形式处理XML,是为了解决DOM得到内存占用问题提出的。

生成XML文档方法:

  1. QXmlStreamWriter:最快最方便的XML方法
  2. DOM,首先在内存中生成DOM树,然后将DOM树写入文件,临时生成树再写入比较麻烦。
  3. 手动生成XML

Qt元对象和属性系统

Qt 的元对象系统(Meta-Object System)提供了对象之间通信的信号与槽机制、运行时类型信息和动态属性系统。

元对象系统由以下三个基础组成:

  1. QObject 类是所有使用元对象系统的类的基类。
  2. 在一个类的 private 部分声明 Q_OBJECT宏,使得类可以使用元对象的特性,如动态属性、信号与槽。
  3. MOC(元对象编译器)为每个 QObject 的子类提供必要的代码来实现元对象系统的特性。

Qt中的基本数据类型

  • QT基本数据类型

编译时遇到的一些错误

  • error LNK2001: 无法解析的外部符号 “public: virtual struct QMetaObject const * __thiscall Widget::metaObject

信号和槽

  • qt5中信号和槽的新语法 cnblogs
  • New Signal Slot Syntax
  • C++_之Qt的信号和槽的详解 cnblogs
  • QT实现信号与槽之间传递QVector类型的数据 CSDN 可以不用这么麻烦
  • QT子线程与主线程的信号槽通信 传结构体
  • Passing QVector from worker thread to main thread via signal/slot 传QVector
  • 信号与槽的新语法(Qt5) CSDN
  • 概述

所谓信号槽,实际就是观察者模式。当某个事件发生之后,比如,按钮检测到自己被点击了一下,它就会发出一个信号(signal)。这种发出是没有目的的,类似广播。如果有对象对这个信号感兴趣,它就会使用连接(connect)函数,意思是,将想要处理的信号和自己的一个函数(称为槽(slot))绑定来处理这个信号。也就是说,当信号发出时,被连接的槽函数会自动被回调。这就类似观察者模式:当发生了感兴趣的事件,某一个操作就会被自动触发。
信号和槽是Qt特有的信息传输机制,是Qt设计程序的重要基础,它可以让互不干扰的对象建立一种联系。
槽的本质是类的成员函数,其参数可以是任意类型的。和普通C++成员函数几乎没有区别,它可以是虚函数;也可以被重载;可以是公有的、保护的、私有的、也可以被其他C++成员函数调用。唯一区别的是:槽可以与信号连接在一起,每当和槽连接的信号被发射的时候,就会调用这个槽。Qt5中的新语法可以将信号connect到QObject的任何成员方法,不仅仅是定义的槽。

使用

Qt5中的使用方式:

#include <QApplication>
#include <QPushButton>

int main(int argc, char *argv[])
{
QApplication app(argc, argv);

QPushButton button("Quit");
QObject::connect(&button, &QPushButton::clicked,
&app, &QApplication::quit);
button.show();
return app.exec();
}

connect()函数最常用的一般形式:

connect(sender, signal, receiver, slot);

参数:

  • sender:发出信号的对象
  • signal:发送对象发出的信号
  • receiver:接收信号的对象
  • slot:接收对象在接收到信号之后所需要调用的函数

信号槽要求信号和槽的参数一致,所谓一致,是参数类型一致。如果不一致,允许的情况是,槽函数的参数可以比信号的少,即便如此,槽函数存在的那些参数的顺序也必须和信号的前面几个一致起来。这是因为,你可以在槽函数中选择忽略信号传来的数据(也就是槽函数的参数比信号的少),但是不能说信号根本没有这个数据,你就要在槽函数中使用(就是槽函数的参数比信号的多,这是不允许的)。

Qt4的使用方式:

int main(int argc, char *argv[]) 
{
QApplication a(argc, argv);
QPushButton *button = new QPushButton("Quit");
connect(button, SIGNAL(clicked()), &a, SLOT(quit()));
button->show();
return a.exec();
}

这里使用了SIGNAL和SLOT这两个宏,将两个函数名转换成了字符串。注意到connect()函数的signal和slot都是接受字符串,一旦出现连接不成功的情况,Qt4是没有编译错误的(因为一切都是字符串,编译期是不检查字符串是否匹配),而是在运行时给出错误。这无疑会增加程序的不稳定性。

Qt中使用了新的信号和槽语法,作为旧语法的一种补充。

旧语法
qt5将继续支持旧的语法去连接,在QObject对象上定义信号和槽函数,及任何继承QObjec的对象(包含QWidget)

connect(sender, SIGNAL (valueChanged(QString,QString)),receiver, SLOT (updateValue(QString)) );

自定义信号和槽

使用 Qt 的信号槽,实现一个报纸和订阅者的例子:
有一个报纸类Newspaper,有一个订阅者类Subscriber。Subscriber可以订阅Newspaper。这样,当Newspaper有了新的内容的时候,Subscriber可以立即得到通知。

newspaper.h

#include <QObject>
class Newspaper : public QObject
{
Q_OBJECT
public:
Newspaper(const QString & name) :
m_name(name)
{
}

void send()
{
emit newPaper(m_name);
}

signals:
void newPaper(const QString &name);

private:
QString m_name;
};

reader.h

#include <QObject>
#include <QDebug>

class Reader : public QObject
{
Q_OBJECT
public:
Reader() {}

void receiveNewspaper(const QString & name)
{
qDebug() << "Receives Newspaper: " << name;
}
};

main.cpp

#include <QCoreApplication>
#include "newspaper.h"
#include "reader.h"

int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);

Newspaper newspaper("Newspaper A");
Reader reader;
QObject::connect(&newspaper, &Newspaper::newPaper,
&reader, &Reader::receiveNewspaper);
newspaper.send();
return app.exec();
}

Newspaper类继承了QObject类。只有继承了QObject类的类,才具有信号槽的能力。所以,为了使用信号槽,必须继承QObject。凡是QObject类(不管是直接子类还是间接子类),都应该在第一行代码写上Q_OBJECT。不管是不是使用信号槽,都应该添加这个宏。这个宏的展开将为我们的类提供信号槽机制、国际化机制以及 Qt 提供的不基于 C++ RTTI 的反射能力。

Newspaper类的 public 和 private 代码块都比较简单,只不过它新加了一个 signals。signals块所列出的,就是该类的信号。信号就是一个个的函数名,返回值是void(因为无法获得信号的返回值,所以也就无需返回任何值),参数是该类需要让外界知道的数据。信号作为函数名,不需要在cpp函数中添加任何实现。

Newspaper类的send()函数比较简单,只有一个语句emit newPaper(m_name);。emit是Qt对C++的扩展,是一个关键字(其实也是一个宏)。emit的含义是发出,也就是发出newPaper()信号。感兴趣的接收者会关注这个信号,可能还需要知道是哪份报纸发出的信号?所以,我们将实际的报纸名字m_name当做参数传给这个信号。当接收者连接这个信号时,就可以通过槽函数获得实际值。这样就完成了数据从发出者到接收者的一个转移。

Reader类更简单。因为这个类需要接受信号,所以我们将其继承了QObject,并且添加了Q_OBJECT宏。后面则是默认构造函数和一个普通的成员函数。Qt5中,任何成员函数、static 函数、全局函数和 Lambda 表达式都可以作为槽函数。与信号函数不同,槽函数必须自己完成实现代码。槽函数就是普通的成员函数,因此作为成员函数,也会受到public、private 等访问控制符的影响。(如果信号是private的,这个信号就不能在类的外面连接,也就没有任何意义。)

小结:

  • 发送者和接收者都需要是QObject的子类(当然,槽函数是全局函数、Lambda 表达式等无需接收者的时候除外);
  • 使用 signals 标记信号函数,信号是一个函数声明,返回 void,不需要实现函数代码;
  • 槽函数是普通的成员函数,作为成员函数,会受到 public、private、protected 的影响;
  • 使用 emit 在恰当的位置发送信号;
  • 使用QObject::connect()函数连接信号和槽。
  • 任何成员函数、static 函数、全局函数和 Lambda 表达式都可以作为槽函数
  • 槽函数需要和信号一致(参数,返回值),由于信号都是没有返回值,所以槽函数一定没有返回值

信号槽的更多用法

  • 一个信号可以和多个槽相连
      如果是这种情况,这些槽会一个接一个的被调用,但是它们的调用顺序是不确定的。
  • 多个信号可以连接到一个槽
      只要任意一个信号发出,这个槽就会被调用。

  • 一个信号可以连接到另外的一个信号
      当第一个信号发出时,第二个信号被发出。除此之外,这种信号-信号的形式和信号-槽的形式没有什么区别。

  • 槽可以被取消链接
      这种情况并不经常出现,因为当一个对象delete之后,Qt自动取消所有连接到这个对象上面的槽。
  • 使用Lambda 表达式
      在使用 Qt 5 的时候,能够支持 Qt 5 的编译器都是支持 Lambda 表达式的。
      我们的代码可以写成下面这样:

    QObject::connect(&newspaper, static_cast<void (Newspaper:: *)
    (const QString &)>(&Newspaper::newPaper),
    [=](const QString &name)
    { /* Your code here. */ }
    );

    在连接信号和槽的时候,槽函数可以使用Lambda表达式的方式进行处理。

新旧语法对比

新语法:连接到QObject成员
下面是一种新的方式来连接两个QObjects:

connect(sender, &Sender::valueChanged,receiver, &Receiver::updateValue );

它支持:

  • 编译期间检查信号和槽是否存在,它们的类型,及Q_OBJECT是否丢失
  • 参数能被typedef或不同命名空间指定。
  • 如果有隐式转换的参数,会自动转换类型。比如QString到QVariant
  • 它可以连接QObject的任何成员方法,不仅仅是定义的槽。

旧版本的不足原因是旧语法connect接收的是两个字符串:

bool QObject::connect ( const QObject * sender, const char * signal, const QObject * receiver, const char * method, Qt::ConnectionType type = Qt::AutoConnection ) [static]

例如:

connect(slider, SIGNAL(valueChanged(int)), spinbox, SLOT(setValue(int)));

编译预处理后就是:

connect(slider, "2valueChanged(int)", spinbox, "1setValue(int)");

这将导致:

  • 即使信号和槽不存在,编译不会出问题。只有运行时会给出警告并返回false,可是大部分用户并不检查返回值。
  • 参数必须匹配,比如信号参数是 int,槽参数是 double,语法将会 connect 失败
  • 参数类型必须字面上一样,比如说都是int,但是其中一个typedef了一下,或者namespace修饰不同,都会导致连接失败。

而在新语法中避免掉了这些问题。但它不支持:

  • 更复杂的语法?你需要指定你的对象类型、
  • 非常复杂的语法,比如重载,参见后面。
  • 在槽的中默认参数不再被支持。

很典型的例如QSpinBox重载了信号valueChanged(),因此不能将

connect(mySpinBox, SIGNAL(valueChanged(int)), mySlider, SLOT(setValue(int));

简单转换为

connect(
mySpinBox, &QSpinBox::valueChanged,
mySlider, &QSlider::setValue
);

需要使用强制类型转换

connect(
mySpinBox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
mySlider, &QSlider::setValue
);

不过这种显示转换可能还是会让某些错误跳过编译器,可以容如下赋值临时变量的方法来保持编译期的检查:

void (QSpinBox::* mySignal)(int) = &QSpinBox::valueChanged;
connect(
mySpinBox, mySignal,
mySlider, &QSlider::setValue
);

当然还有一些其他方法,但最好就是不要重载信号和槽。

传递非基本数据类型

正常情况下信号和槽只能传递通用数据类型,例如QVector、QString和结构体等不能传递,编译可能不会出错,但槽函数不会被调用,需要对复杂数据做一些包装处理才行。可以使用

qRegisterMetaType<QVector<float> >("QVector<float>");

class Msg
{
int int_info_;
std::string str_info; // 不知道是否还要对string再注册下
}

qRegisterMetaType<Msg>("Msg");

lambda表达式

C++11中的Lambda表达式用于定义并创建匿名的函数对象,以简化编程工作。首先看一下Lambda表达式的基本构成:

[函数对象参数](操作符重载函数参数)mutable或exception ->返回值{函数体}
  1. 函数对象参数;
      [],标识一个Lambda的开始,这部分必须存在,不能省略。函数对象参数是传递给编译器自动生成的函数对象类的构造函数的。函数对象参数只能使用那些到定义Lambda为止时Lambda所在作用范围内可见的局部变量(包括Lambda所在类的this)。函数对象参数有以下形式:

    • 空。没有使用任何函数对象参数。
    • =。函数体内可以使用Lambda所在作用范围内所有可见的局部变量(包括Lambda所在类的this),并且是值传递方式(相当于编译器自动为我们按值传递了所有局部变量)。
    • &。函数体内可以使用Lambda所在作用范围内所有可见的局部变量(包括Lambda所在类的this),并且是引用传递方式(相当于编译器自动为我们按引用传递了所有局部变量)。
    • this。函数体内可以使用Lambda所在类中的成员变量。
    • a。将a按值进行传递。按值进行传递时,函数体内不能修改传递进来的a的拷贝,因为默认情况下函数是const的。要修改传递进来的a的拷贝,可以添加mutable修饰符。
    • &a。将a按引用进行传递。
    • a, &b。将a按值进行传递,b按引用进行传递。
    • =,&a, &b。除a和b按引用进行传递外,其他参数都按值进行传递。
    • &, a, b。除a和b按值进行传递外,其他参数都按引用进行传递。

      int m = 0, n = 0;
      [=] (int a) mutable { m = ++n + a; }(4);
      [&] (int a) { m = ++n + a; }(4);

      [=,&m] (int a) mutable { m = ++n + a; }(4);
      [&,m] (int a) mutable { m = ++n + a; }(4);

      [m,n] (int a) mutable { m = ++n + a; }(4);
      [&m,&n] (int a) { m = ++n + a; }(4);
  2. 操作符重载函数参数;
    标识重载的()操作符的参数,没有参数时,这部分可以省略。参数可以通过按值(如:(a,b))和按引用(如:(&a,&b))两种方式进行传递。

  3. 可修改标示符;
    mutable声明,这部分可以省略。按值传递函数对象参数时,加上mutable修饰符后,可以修改按值传递进来的拷贝(注意是能修改拷贝,而不是值本身)。
  4. 错误抛出标示符;
    exception声明,这部分也可以省略。exception声明用于指定函数抛出的异常,如抛出整数类型的异常,可以使用throw(int)
  5. 函数返回值;
    ->返回值类型,标识函数返回值的类型,当返回值为void,或者函数体中只有一处return的地方(此时编译器可以自动推断出返回值类型)时,这部分可以省略。
  6. 是函数体;
    {},标识函数的实现,这部分不能省略,但函数体可以为空。
布局是QT界面当中必不可少的一环。我们常常使用QHBoxLayout、QVBoxLayout等进行布局。然而有个问题是,在MainWindow并不能直接进行布局。

-[QT笔记——在QMainWindow内直接添加Layout行不通](https://blog.csdn.net/qq_26399665/article/details/52695042)
发现在QMainWindow中添加的控件不显示,还有QToolBar也可能。

原因是该窗口类中已经有一个Layout对象了,这时候再setLayout是无效的。有三种方法:
1. 调用layout()方法,返回这个窗口内部的layout对象,删除后再setLayout设置自己的Layout对象
2. 直接使用该Layout对象
3. 创建子窗口对象,在这个子窗口对象中使用Layout对象。例如在QMainWindow中,通常是创建一个窗口对象后,调用setCentralWidget来安装该窗口,然后在该窗口中使用自己的Layout。

QWidget *widget = new QWidget();
// method 1
//setCentralWidget(widget);
//centralWidget()->setLayout(mainLayout);

// method 2
widget->setLayout(mainLayout);
setCentralWidget(widget);
## utils QMessageBox::information(this, tr("Information"), str);
123

Astray Wu

24 posts
2 tags
RSS
© 2021 Astray Wu | 80k | 1:13
Powered by Hexo v3.9.0
|
Theme – NexT.Muse v6.7.0
|
0%