lee-romantic 's Blog
Learning is a journey, not a destination.
Toggle navigation
lee-romantic 's Blog
主页
About Me
归档
标签
Qt 学习问题
2025-11-12 12:34:47
4
0
0
lee-romantic
# 1、showMaximized()和 showNormal()函数要配套使用 实现全屏最大化以及还原的槽函数`on_btnMenu_Max_clicked()`,如果不配套使用,比如将`showNormal()`与`setGeometry()`混合使用,将会出现无法还原的问题。同理,混合使用`showMaximized()`和`setGeometry()`也会出现问题: ``` void frmMain::on_btnMenu_Max_clicked() { static bool max = false; static QRect location = this->geometry(); if (max) { this->setGeometry(location); //showNormal(); } else { location = this->geometry(); // 全屏方式一: // this->setGeometry(QUIHelper::getScreenRect()); //全屏方式二: showMaximized()似乎只能生效一次,第二次就失效了,除非配合上面的showNormal或者下面的hide()就不会有问题 // hide(); showMaximized(); } this->setProperty("canMove", max); max = !max; } ``` 猜测可能是`showMaximized()`和`showNormal()`内部设置了同一个标志位变量,所以要配套使用,有待看源码证实! #2、Qt中textEdit的总是自动获得焦点 这说明textEdit的焦点策略是strong policy或者tab policy,改成click policy即可。 #3、关于enter键 Key_Enter是小键盘的确认键,Key_Return是大键盘的回车键。二者缺一不可。 ``` void Widget::keyPressEvent(QKeyEvent *ev) { if(ev->key() == Qt::Key_Enter || ev->key() == Qt::Key_Return) { on_pushButton_clicked(); } QWidget::keyPressEvent(ev); } ``` # 4、窗体close()和hide()函数的区别 - `hide`只是隐藏窗体。不会发送任何信号。 - `close`一般也是隐藏窗口。 - 但是它会发送`QCloseEvent`事件。你可以重写`void QWidget::closeEvent(QCloseEvent * event) [virtual protected]`,可以隐藏widget或者不隐藏。 - `Qt::WA_DeleteOnClose`标志还会影响窗体在内存中的状态,如果设置了该标志,窗体就会被删除,而`hide`则不会。 - 最后主窗体的`close`会导致整个程序的退出,而`hide`明显不会。 - PS:再对一个窗体调用`close`函数后,如果再调用`show()`,这个窗体又会被显示出来。 参考:https://blog.csdn.net/weixin_50617754/article/details/114284028 # 5、Qt出现`error: invalid use of incomplete type 'class Ui::XPDR' 最开始是想新建一个叫xpdr的界面类,后来又改成XPDR。改了之后`ui_xpdr.h`中还是小写的xpdr,导致一直出现如题错误。临时的解决办法就是,进入到生成的`ui_xpdr.h`中,手动修改xpdr为XPDR再重新编译。 我们清理掉所有的编译产物,包括user文件,再通过uic手动编译`uic xpdr.ui -o ui_xpdr.h`,发现生成的文件中还是小写的`xpdr`,说明不是ide缓存导致的问题。其实[参考链接](https://blog.csdn.net/m0_60324722/article/details/130557764?spm=1001.2101.3001.6650.7&utm_medium=distribute.pc_relevant.none-task-blog-2~default~BlogCommendFromBaidu~Rate-7-130557764-blog-124481778.235%5Ev38%5Epc_relevant_anti_t3_base&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2~default~BlogCommendFromBaidu~Rate-7-130557764-blog-124481778.235%5Ev38%5Epc_relevant_anti_t3_base&utm_relevant_index=8),本质的原因是没有将ui文件中的`QObjectName`从`xpdr`改为`XPDR`,修改后`ui_xpdr.h`中的"xpdr"就是大写的了。 # 6、QTableWidget和QTableView的区别 - `QTableWidget是QTableView的子类`,主要的区别是`QTableView可以使用自定义的数据模型来显示内容`(也就是先要通过setModel来绑定数据源),而`QTableWidget则只能使用标准的数据模型`,并且其单元格数据是QTableWidgetItem的对象来实现的(也就是不需要数据源,将逐个单元格内的信息填好即可)。 - 体现在QTableView类中有setModel成员函数,而到了QTableWidget类中,该成员函数变成了私有。使用QTableWidget就离不开QTableWidgetItem。QTableWidgetItem用来表示表格中的一个单元格,正个表格都需要用逐个单元格构建起来。 - 参考[链接](https://zhuanlan.zhihu.com/p/630893072?utm_id=0) # 7、关于Qt模型视图框架:`QStyledItemDelegate` - 当在 Qt 项目视图中显示来自模型的数据时,单个项目由委托绘制。 - 此外,当一个项目被编辑时,它提供了一个编辑器小部件,小部件在编辑时放置在项目视图的顶部。 - `QStyledItemDelegate` 是所有 Qt 项目视图的默认委托,并在创建它们时安装在它们上。 - 模型中项目的数据被分配了一个 `ItemDataRole`。项目可以为每个角色存储一个 `QVariant`。 `QStyledItemDelegate` 实现了用户期望的最常见数据类型的显示和编辑,包括布尔值、整数和字符串。 - 数据将根据它们在模型中的角色而不同地绘制。下面是委托可以为每个角色处理的角色和数据类型: - Qt::BackgroundRole:QBrush - Qt::CheckStateRole:Qt::CheckState - Qt::DecorationRole:QIcon、QPixmap、QImage、QColor - Qt::DisplayRole:QString 和带有字符串表示的类型 - Qt::EditRole:详见 QItemEditorFactory - Qt::FontRole:QFont - Qt::SizeHintRole:QSize - Qt::TextAlignmentRole:Qt::Alignment - Qt::ForegroundRole:QBrush - 参考[链接1](https://blog.csdn.net/kenfan1647/article/details/119754324) 以及参考[链接2](https://www.cnblogs.com/zhangdewang/archive/2018/07/09/9285316.html) # 8、设置QWidget的透明度和渐变色 参考[链接](https://zhuanlan.zhihu.com/p/652895512) # 9、qt报错***Fault tolerant heap shim applied to current process. This is usually due toprevious - win+r输入cmd打开【命令提示符】,输入regedit打开注册表,然后根据如下路径`HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers\`找到自己程序的那个.exe,然后删除。 - 然后在根据路径`\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\FTH`,找到Enabled,设置为0 - 之后以管理员身份运行cmd,输入`Rundll32.exe fthsvc.dll,FthSysprepSpecialize` - 最后关闭qt creator,然后把该项目之前自动编译产生的文件都删除,然后重新打开qt导入该项目即可。 (ps:猜测执行了第一步应该就可以解决了,有待验证) - 参考[链接](https://blog.csdn.net/changyana/article/details/129105338) 参考[链接](https://blog.csdn.net/TianYanRen111/article/details/119571388?spm=1001.2101.3001.6661.1&utm_medium=distribute.pc_relevant_t0.none-task-blog-2~default~CTRLIST~PayColumn-1-119571388-blog-129105338.235%5Ev38%5Epc_relevant_anti_t3_base&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2~default~CTRLIST~PayColumn-1-119571388-blog-129105338.235%5E # 10、Qt不能为不同控件设置同一个阴影对象,setGraphicsEffect 当对两个不同的控件调用setGraphicsEffect设置同一个QGraphicsDropShadowEffect对象时,只有后设置的才会生效,前一个控件的阴影效果会消失。 参考[链接](https://blog.csdn.net/xjcwzp/article/details/103820430) v38%5Epc_relevant_anti_t3_base&utm_relevant_index=1) # 11、Qt Creator界面无法直接点击布局键对`QStackedwidget`子页面进行布局的问题 - `Qt Designer`这里确实反人类,对于`QStackedwidget`子页面进行布局只能如下操作,而不能直接点击布局键: - 1、设置布局只能点击 `stackWidget对象->右键菜单->布局->选择` - 2、这个布局只对当前子`page`生效,并且子`page`中必须要有一个子控件,否则菜单全是灰的; - 3、如果你希望设置其它 子page的布局,那么你先点击到该子页面,让`stackWidget` 的当前子页面为此子页面,然后设置步骤一,那么此时设置的才是这个子页面的布局。 参考[链接](https://blog.csdn.net/HQ354974212/article/details/127357072) # 12、Qt QLabel设置删除线(四种方法) - 可以通过qt designer设置qlabel的删除线属性 - 可以通过html标签设置 ``` ui->label->setText("<u>下划线</u>"); ui->label->setText("<s>删除线</s>"); ``` - 可以通过Qfont设置 ``` QFont font; font.setUnderline(true);//下划线 font.setStrikeOut(true);//删除线 ui->label->setFont(font); ``` - 可以通过QSS设置 ``` ui->label->setStyleSheet("text-decoration:underline;");//下划线 ui->label->setStyleSheet("text-decoration:line-through;");//删除线 ``` 参考[链接](https://blog.csdn.net/lxj362343/article/details/103679726) #13、编码问题 C++11引入了u8"utf-8字符串",u"utf-16字符串",U"utf-32字符串"。以及R"(非转义字符串)"可直接写不用转义的字符串,和对应的utf版本u8R"(utf-8非转义字符串)",uR"(utf-16非转义字符串)",UR"(utf-32字符串)"。 参考[链接](https://blog.csdn.net/peterbig/article/details/124728388) # 14、QMap遍历方式 - 迭代器遍历 ``` QMap<QString, int>::iterator itor; for (itor = map.begin(); itor != map.end(); ++itor) { qDebug() << itor.key() << ":" << itor.value(); } ``` ``` // const迭代器,不改变元素 for (QMap<QString, int>::const_iterator itor = map.constBegin(); itor != map.constEnd(); ++itor) { qDebug() << itor.key() << ":" << itor.value(); } ``` ``` // 注意这里的迭代器是java风格的哦,即指向的不是元素而是元素前面的间隙,不是std风格 QMapIterator<QString, int> itor(map); while (itor.hasNext()) { qDebug() << itor.key() << ":" << itor.value(); } ``` - C++11的for循环遍历 ``` for (auto &pair : map.toStdMap()) { qDebug() << pair.first << ":" << pair.second; } ``` ``` for (const auto &key : map.keys()) { qDebug() << key << ":" << map.value(key); } ``` - Qt中的Q_FOREACH循环遍历 ``` foreach(const QString &key, map.keys()) { qDebug() << key << ":" << map.value(key); } ``` - std::for_each()算法遍历 ``` std::for_each(map.constBegin(), map.constEnd(), [](const auto &item) { qDebug() << item.key() << ":" << item.value(); }); ``` 参考[链接](https://blog.csdn.net/weixin_42326676/article/details/130902418) # 15、QTableView使用代理向表格中添加控件 Delegate.h ``` #ifndef DELEGATE_H #define DELEGATE_H #include <QStyledItemDelegate> #include <QItemDelegate> #include <QModelIndex> #include <QPainter> #include <QWidget> #define COMBOXCOL 1 class Delegate : public QItemDelegate { Q_OBJECT public: Delegate(QObject *parent = nullptr); ~Delegate(); void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const; QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const; void setEditorData(QWidget *editor, const QModelIndex &index) const; void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const; }; #endif // DELEGATE_H ``` Delegate.cpp ``` #include "combobox_itemdelegate.h" #include <QComboBox> Delegate::Delegate(QObject *parent) : QItemDelegate(parent) { } Delegate::~Delegate() { } void Delegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { QItemDelegate::paint(painter, option, index); } QSize Delegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const { return QItemDelegate::sizeHint(option, index); } QWidget *Delegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const { if (index.isValid() && index.column() == COMBOXCOL) { QComboBox *editor = new QComboBox(parent); editor->setEditable(true); editor->installEventFilter(const_cast<Delegate *>(this)); return editor; } else { return QItemDelegate::createEditor(parent, option, index); } } void Delegate::setEditorData(QWidget *editor, const QModelIndex &index) const { if (index.isValid() && index.column() == COMBOXCOL) { QString value = index.model()->data(index, Qt::DisplayRole).toString(); QComboBox *combox = static_cast<QComboBox *>(editor); combox->addItem("男"); combox->addItem("女"); combox->setCurrentText(value); } else { QItemDelegate::setEditorData(editor, index); } } void Delegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { if (index.isValid() && index.column() == COMBOXCOL) { QComboBox *combox = static_cast<QComboBox *>(editor); model->setData(index, combox->currentText()); } else { QItemDelegate::setModelData(editor, model, index); } } ``` 调用: ``` MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); tableview = new QTableView(this->centralWidget()); tableview->resize(500,500); tableviewModel = new QStandardItemModel; tableview->setModel(tableviewModel); QStringList headerList; headerList<<"姓名"<<"性别"<<"年龄"; tableviewModel->setHorizontalHeaderLabels(headerList); tableviewModel->setItem(0,0,new QStandardItem("张三")); tableviewModel->setItem(1,0,new QStandardItem("李四")); tableviewModel->setItem(2,0,new QStandardItem("王二")); tableview->setSelectionBehavior(QAbstractItemView::SelectRows);//选择一行 tableview->setItemDelegateForColumn(1, new Delegate(this));//添加QCombox代理 } ``` 也可以直接使用setIndexWidget方法添加: ``` m_button = new QPushButton("Download"); //触发下载按钮的槽函数 connect(m_button, SIGNAL(clicked(bool)), this, SLOT(ClickDownloadButton())); m_button->setProperty("row", i); //为按钮设置row属性 m_button->setProperty("fileName", fileInfo.at(i).fileName); //为按钮设置fileName属性 //m_button->setProperty("column", col) ui->downloadTable->setIndexWidget(model->index(model->rowCount() - 1, 3), m_button); ``` 参考[链接](https://blog.51cto.com/xiaohaiwa/5681291) # 16、lambda表达式和const的相关问题 使用lambda函数的时候出现下面问题: ``` xpdrSetting.cpp:199:29: 'this' argument to member function 'addFile' has type 'const QIcon', but function is not marked const qicon.h:104:10: 'addFile' declared here ``` 代码是: ``` connect(switch_button, &QPushButton::clicked, this, [=](){ if (on_off_text->text() == "开") { icon.addFile(":/general_icons/general_icons/pic_off@2x.png"); on_off_text->setText("关"); } else { icon.addFile(":/general_icons/general_icons/pic_on@2x.png", QSize(64,40)); on_off_text->setText("开"); } switch_button->setIcon(icon); }); ``` 参考[链接](https://blog.csdn.net/u013377887/article/details/108887633),我们知道,`const 修饰成员函数, 根本上是修饰了 this 指针。`因此,就是不知道这里`icon`变量为什么就成了`const`变量了。起初怀疑是`QIcon`类的拷贝赋值函数将icon变成了const的,发现并不是。最后,发现其实是`lambda`函数的默认规则,即`lambda`表达式对应的函数是`const`的, 其捕获的变量都变成`const`的了, ,自然在函数体内部是无法对捕获变量的值进行修改的。 那么我实在是想要修改怎么办呢? **按值引用捕获即可,这样就能修改变量并且修改的是外部变量。只有引用捕获才是修改了外部变量,值捕获哪怕加上mutable关键字也不能改变外部变量。** 另外,默认情况下,由`lambda`产生类当中的调用运算符是一个const成员函数,所以值捕获的值都成为了const常量(引用捕获则不是这样),导致其不能被修改。而如果加上`mutable`相当于去掉`const`,自然可以进行修改。但要注意,加了`mutable`之后,值捕获在加了mutable的情况下只是内部捕获的变量可以被修改了而已,外部变量不变。 可以[参考](https://blog.csdn.net/u013620306/article/details/127890253)。 特别的,针对在`class`内部的某个成员函数中,`this`指针只能被值捕获,但是捕获`this`后,相当于捕获了所有内部成员,也可直接修改这些成员变量,无需`mutable`关键字或引用捕获(当然,`this`指针本身也无法引用捕获)。 ``` connect(switch_button, &QPushButton::clicked, this, [=]() mutable { if (on_off_text->text() == "开") { icon.addFile(":/general_icons/general_icons/pic_off@2x.png"); on_off_text->setText("关"); } else { icon.addFile(":/general_icons/general_icons/pic_on@2x.png", QSize(64,40)); on_off_text->setText("开"); } switch_button->setIcon(icon); }); ``` 且变量捕获是在定义时捕获的,不是运行时, 这点可参考[链接](https://blog.csdn.net/weixin_39640298/article/details/84996642)以及[stackOverflow](https://stackoverflow.com/questions/11599461/c11-lambda-capture-by-value-captures-at-declaration-point)上的例子: ``` #include <iostream> int main(int argc, char **argv){ int value = 0; auto incr_value = [&value]() { value++; }; auto print_value = [ value]() { std::cout << value << std::endl; }; incr_value(); print_value(); // 打印的结果是0,而不是1 return 0; } ``` `lambda`表达式的捕获只发生在它所声明/定义的地方,而不是它所调用的地方,即对于一个`lambda`,捕获行为只发生一次,即它被`声明/定义`时。虽然它被定义和被调用可能在同一个函数内部(调用可能发生在别的函数内部,例如`lambda`作为别的函数的参数),但是捕获只发生在定义时,所以在`lambda`的定义之后出现的局部变量是不会被捕获的。 下面的例子,才是符合程序员的本来想法的: ``` #include <iostream> int main(int argc, char **argv){ int value = 0; auto incr_value = [&value]() mutable { value++; }; // mutable去掉也可 auto print_value = [&value]() { std::cout << value << std::endl; }; incr_value(); print_value(); // 这样引用捕获,不是拷贝捕获,打印的结果就会是1,而不是0 return 0; } ``` # 17、不要使用QStackWidget::findChildren得到列表顺序 使用QStackWidget::findChildren得到的widget列表,和widget的index顺序是对不上的。解决办法参考下面代码: ``` // 直接使用findChildren有坑,不是按index顺序的, 因此只适用于不要求顺序的操作 //QList<QTableWidget*> table_widgets = ui->adsb_setting_stackedWidget->findChildren<QTableWidget*>(); // 这样通过widget(i)得到的列表,才是符合index顺序的 QList<QTableWidget*> table_widgets; for (int i = 0; i < ui->adsb_setting_stackedWidget->count(); i++) { table_widgets << ui->adsb_setting_stackedWidget->widget(i)->findChild<QTableWidget*>(); } ``` [参考链接](https://codeleading.com/article/59666094077/) # 18、调用`installEventFilter`后控件不显示了 - 原因可能是`eventFiler`函数中返回了`true`。在`eventFilter`函数内`return true`了,就不会再执行`paint`事件了,将`return true`改为`return false`即可。 - 另一种原因是同理的,也可能是省略了`return QWidget::eventFilter(watched, event);`, 导致无法执行`paint`事件。 - eventFiler代码为: ``` bool MenuWidget::eventFilter(QObject *watched, QEvent *event){ QToolButton* btn = (QToolButton*)watched; if (btns.contains(btn)){ if (event->type() == QEvent::KeyPress) { Qt::Key key = (Qt::Key)static_cast<QKeyEvent*>(event)->key(); if (key == Qt::Key_Enter || key == Qt::Key_Return) { // do something } } } // 不能省略,因为需要执行包括后续的paint事件 return QWidget::eventFilter(watched, event); } ``` 参考[链接](https://www.cnblogs.com/Gregg/p/16976133.html) # 19、调用setGeometry不生效 - 当执行`setGeometry()` 时会对当前空间的`WA_WState_Created`属性进行判断,用来查看当前组件是否已经创建了`Window`,如果发现没有创建,那默认就会调用`create()`方法进行`window` 创建,这就导致了`QT`在创建`window`时自动校正当前`window`的`gemoetry`,之前设置的`setGeometry()` 就会被覆盖掉,无法生效! - 解决办法:首先可以采用在`setGeometry()`之后调用一下`setVisible(true)`来解决。既然第一次显示窗口时会默认触发一次`create()`,那就想办法在先将窗体显示出来,在设置移动位置。 参考:https://www.modb.pro/db/632947 # 20、如何查看Qt源码 最好是使用"source insight", 效果还行。 [参考](https://blog.csdn.net/cuipanliang/article/details/132649668) # 21、QWidget::setContentsMargin为什么有时候不生效 - Qt助手中是这么描述`QWidget::setContentsMargin`的,也就是说是否使用还是要看QWidget子类的具体实现。源码看起来QWidget::setContentsMargin也会调用layout中的update函数进行更新, 但是还是有一些区别(虽然看不懂)。一些子类比如QPushButton,包括QWidget本身确实是没有margin的,自然也就不会生效了,但是`QLabel,QLineEdit`等就会生效: ``` Sets the margins around the contents of the widget to have the sizes left, top, right, and bottom. The margins are used by the layout system, and may be used by subclasses to specify the area to draw in ``` - 因此,确实需要设置Margin,我们一般可以使用其layout的setContentsMargin函数,不过需要注意layout的setContentsMargin函数不会发送event,但是qwidget的setContentsMargin函数会产生resize事件,这是主要的区别。 参考链接:http://www.qtcn.org/bbs/read-htm-tid-58351.html # 22、Qt中delete的问题(对象树) - 解释: - 在每一个Qt对象中,都有一个链表,这个链表保存有它所有子对象的指针,一个Qt对象及其子对象的所有的链表就构成了对象树。 - 当创建一个新的Qt对象的时候,如果把另外一个Qt对象指定为这个对象的父对象, 那么父对象就会在它的子对象链表中加入这个子对象的指针。另外,对于任意一个Qt对象而言,在其生命周期的任何时候,都还可以通过`setParent`函数 重新设置它的父对象。 - 当一个父对象在被`delete`的时候,它会自动的把它所有的子对象全部`delete`。当一个子对象在`delete`的时候,会把它自己 从它的父对象的子对象链表中删除。(因此,在父对象的析构函数中`delete`子对象,也不会带来什么问题,但没必要这样做。) - 重复delete的问题[参考](https://blog.csdn.net/GUOMZH/article/details/129465179): 对于栈内存的子对象在父对象前面定义,由于系统的栈内存释放机制,系统会首先释放父对象内存,接着系统释放子对象内存,问题在于,父对象在释放时其本身的对象树机制会去释放子对象内存,从而产生子对象内存被二次释放的问题,并导致崩溃。例子如下: ``` #include "widget.h" #include <QApplication> int main(int argc, char *argv[]) { QApplication a(argc, argv); QPushButton button; Widget w; button.setParent(&w); button.setText("QT崩溃"); w.show(); return a.exec(); } ``` 在本例中,button和w按照顺序存放在栈里,而我们栈有先进后出的特性,正因如此,我们的程序结束后,系统会先释放w的内存,再释放button的内存,在释放w的内存时候由于 button变量绑定了父变量w(button.setParent(&w);),button也会和w一起释放,因此button在本例中会进行两次释放,从而导致奔溃。 **解决办法**:`先定义父对象,接着定义子对象`,这样将存入栈的顺序改变,让button后存入栈,也就是把button定义在w的后面,以至于程序结束后,先释放button再释放w。就不会有二次释放的情况。 参考:https://www.cnblogs.com/foxhengxing/archive/2010/12/24/1916355.html # 23、Qt MSVC为啥出现乱码 - 23.1 原因: - **源字符集与实际文件编码不一致,或者执行字符集与代码不一致**。 - 注:`Qt`源字符集与执行字符集均默认使用`utf-8`,而msvc默认使用的是`gbk`,或者`utf8-bom`。 - 23.2 基本概念 - 源字符集 `(source-charset) `:源码文件是使用何种编码保存的; - 执行字符集 `(execution-charset)` :可执行程序内保存的是何种编码(程序执行时内存中字符串编码); - `MSVC `编译器在没有指定**源字符集**和**执行字符集** 的时候,默认使用的字符集都是 `GBK`; 当编译器在认为**源字符集** 和**执行字符集**不一致的时候,在生成可执行程序的时候,会有字符转码的行为, 也就是用**源字符集**来解释字符编码,然后用**执行字符集** 来编码成可执行文件,这个时候可执行文件的就需要用**执行字符集**格式才能正确解释出来。 - 23.3 建议: - `source-charset `根据实际文件格式设置好; - `execution-charset `根据代码中的调用 `QString`的具体哪种方法设置(比如执行字符集设置的是 `UTF-8` 就使用 `QString()`,执行字符集设置的是 `GBK` ,且Local字符集为GBK,就使用 `QString::fromLocal8bit()`这种,[可以参考](https://blog.csdn.net/libaineu2004/article/details/19245205/))。 - 23.4 例子: - 题目 - (1)`Linux `系统下使用 `gcc `作为编译器,已知在不特地设置` source-charset `和` execution-charset` 的时候,默认都是 `UTF-8` 项目的文件格式是` GB2312`, 代码中使用的是` QString()`, 在 `Linux `下使用 `gcc `编译会出现乱码吗,如果出现乱码应该如何解决? - (2)这个项目如果 `git clone `到 `windows` 下使用` MSVC` 编译会出现乱码吗? 如果出现如何解决? - 参考解答: - 这两种情况下,都会出现乱码: - (1)文件格式不对,需要指定`source-charset `编码格式为GBK, `Linux` 下指定 `source-charset `编码格式, 和 `windows `下有点区别: `QMAKE_CXXFLAGS += -finput-charset=gbk` - (2)`windows`下`msvc`默认执行字符集为`gbk`,这里使用的`QString()`,需要指定执行字符集为`utf-8`才行,`windows` 下指定 `execution-charset `编码格式 `QMAKE_CXXFLAGS += /execution-charset:utf-8` - [参考链接](https://blog.csdn.net/qq_20821119/article/details/120170353) # 24、关于C++ 模板类编译过程中出现“undefined reference to”问题 问题:`C++`在使用模板(`template`)类的时候,如果将类的成员函数的声明和实现分别放在.h头文件和`.cpp`源文件中,编译时会报错`undefined reference xxx`,找不到对应成员函数。 ``` // 线程池,定义成模板类,为了代码的复用 template <typename T> class ThreadPool { ... public: bool append(T *request); ... }; ``` ``` template <typename T> bool ThreadPool<T>::append(T *request) { // 操作工作队列时一定要加锁,因为它被所有线程共享 queue_locker_.lock(); if (work_queue_.size() > max_requests_) { queue_locker_.unlock(); return false; } work_queue_.push_back(request); queue_locker_.unlock(); queue_sta_.post(); return true; } ``` - 原因: `template`其实是一种类似语法糖的东西,C++中每一个对象所占用的空间大小,是在编译的时候就确定的,在模板类没有真正的被使用之前,编译器是无法知道,模板类中使用模板类型的对象的所占用的空间的大小的。只有模板被真正使用的时候,编译器才知道,模板套用的是什么类型,应该分配多少空间。这也就是模板类为什么只是称之为模板,而不是泛型的缘故。 即`ThreadPool<int>`和`Thread<HttpConn>`是两个不同的类型,其成员函数也是两个不同的成员函数。 在编译`thread_pool.cpp`时,编译器会去查找对类`Thread<HttpConn>`的声明,如果找不到这个声明,那么就报错了。 - 解决方案 在成员函数的实现的代码所在的源文件的开头,声明该类,即添加: ``` template class ThreadPool<HttpConn>; ``` 或者将函数的实现也写在头文件中(不推荐)。 [参考](https://www.cnblogs.com/zwyyy456/p/17479524.html) # 25、Qt中的setWindow和setViewPort - 窗口和视口([参考](http://www.lgwimonday.cn/archives/1383)) - 窗口:逻辑环境中的一个矩形框,使用逻辑坐标,图中世界坐标和窗口坐标外围的虚线框即为窗口。窗口具有原点和长宽,这也就决定了窗口的位置和大小。窗口存在的意义即是为了确定人类世界中的某个物体需要显示的位置和范围。如我要显示地图中的江苏部分,我就需要将窗口的原点位置设置为江苏,长宽设置为江苏省的外接矩形大小。如果我要显示地图中的中国部分,则将窗口扩大,原点设置为中国,长宽设置为中国的外接矩形大小。在视口不变的情况下,扩大窗口其实就是相当于对地图进行了缩小。 - 视口:设备环境中的一个矩形框,使用物理坐标,和设备的大小密切相关,超出设备外的视口区域不予显示。视口存在的意义是指定在显示设备的哪个地方,以多大的范围完全显示指定的窗口内容。 - 使用widget作为绘图设备时,比如后面参考链接中的一样,缩放`widget`的时候,`setWindow`后`window size`就不会变,同理`setViewPort`后 `viewport大小`不会变,都不设置时`window size`和`viewport size`均为`widget`的`size`大小,会同时随着`widget`的缩放而进行改变。 - Qt中的逻辑坐标系统和物理坐标系统是通过`setWindow`和`setViewport`函数来设定,二者的参数都是矩形`QRect()`: - 逻辑坐标系统才是`QPainter`绘制图像的地方,`setWindow()` 是确定逻辑坐标系统上需要显示的附近区域,相当于在地图上定位显示位置。`setViewport`改变的是物理坐标系统,即确定要显示的内容在物理设备上的哪个区域开始显示,要注意,无论是视口还是窗口,其大小都不是用来作为"截取"的,而是视口和窗口各自长宽来共同确定显示的分辨率。 - 视口和窗口的大小比值就相当于在地图上缩放时,确定放大倍数。这有点像是地图放大缩小时,setWindow选择一个在逻辑坐标系上的网格grid,setViewport选择与之对应的在绘图设备上的grid大小,这两个grid对应起来,就确定了分辨率和如何显示。`因此,setWindow并不是截取图片,而setViewport也无法限制具体的显示内容, 视口和窗口只能确定放大缩小后, 逻辑坐标系和绘图设备上显示的对应位置关系,和大小分辨率,而到底显示多少内容,由外部的绘图设备,如widget的大小确定。` - 代码演示: ``` // window_viewport.h #ifndef WINDOW_VIEWPORT_H #define WINDOW_VIEWPORT_H #include <QObject> #include <QWidget> class window_viewport : public QWidget { Q_OBJECT public: window_viewport(QWidget *parent = Q_NULLPTR); protected: void paintEvent(QPaintEvent *); }; #endif // WINDOW_VIEWPORT_H ``` ``` // window_viewport.cpp #include "window_viewport.h" #include <QPaintEvent>> #include <QPainter> #include <QDebug> window_viewport::window_viewport(QWidget *parent): QWidget(parent) { this->resize(600, 600); }; void window_viewport::paintEvent(QPaintEvent *event) { QPainter painter(this); //viewport表示物体在绘图设备(物理坐标系统,类似图像坐标系)上的显示位置和大小 //painter.setViewport(0, 0, 0.5 * window()->width(), 0.5 * window()->height()); painter.setViewport(300, 300, 100, 100); //表示截取(x,y)附近的区域用以显示, 与viewport视口的大小比值为显示的分辨率 painter.setWindow(500, 500, 100, 100); // 虽然(500, 500)附近已经没有绘图了,但是其对应的视口位置比较靠近右下处,this->window()窗口大小又是够的,导致完整的绘图还是可以显示出来,且显示比例是1:1。可以看到这跟地图缩放很像,可以类比地图来理解 // 在逻辑坐标上绘制 painter.setPen(QPen(Qt::blue,5)); painter.drawRect(0,0,400,400); painter.drawLine(0,0,400,400); qDebug()<< "viewport w&h:" <<painter.viewport().width(); qDebug()<<painter.viewport().height(); qDebug()<<"wind w&h"<< painter.window().width(); // 不一定等于this->window()窗口大小 qDebug()<<painter.window().height(); // painter的窗口和视口默认和this->window()的大小位置相等, 左上角均为逻辑坐标原点 qDebug() << "this window w&h:" << this->window()->width(); qDebug() << this->window()->height(); } ``` - [文字参考](https://blog.csdn.net/weixin_39583140/article/details/92798127) - [举例参考](https://blog.csdn.net/u012442719/article/details/50923012) # 26、QObject的子对象调用deleteLater()的问题 - 在Alyce项目中,自定义`SequentialMap`中的子对象`_signal_emitter`的一个`QObject`子对象,然而在`SequentialMap`的析构函数中调用`_signal_emitter->deleteLater()`的时候,可能会出现如下问题: ``` pure virtual method called QWaitCondition: Destroyed while threads are still waiting QWaitCondition: Destroyed while threads are still waiting terminate called without an active exception ``` - 原因应该是: 对象生命周期的问题:如果在线程中使用了一个`QObject`子类对象,并且这个对象的生命周期与QThread对象不同步,则可能会导致该错误。例如,在析构函数中删除`QObject`子类对象时,如果线程仍在运行,则该`QObject`子类对象将被删除并释放内存,但线程尝试访问已经释放的内存块时,就会出现该错误。 - 解决办法:同步`QObject`子类对象的生命周期:确保`QObject`子类对象与其所属的`QThread`具有相同的生命周期。例如,在创建`QObject`子类对象时,将其分配给`QThread`的父对象。 [参考链接](https://linuxcpp.0voice.com/?id=78721) # 27、Qt中的信号与槽机制 - `connect`做了什么事情 通过 `sender` 和 `signal `得到 `signal_index`。 `slot `被包装成 `QtPrivate::QSlotObjectBase `对象 `slotObj`。 将 `signal_index` 和 `slotObj `等信息包装成 `QObjectPrivate::Connection `对象 `c`,然后把对象 `c` 添加(`addConnection`) 到 `sender `里面去。包装成 `QObjectPrivate::Connection `对象 `c` 并 `addConnection `这个过程是线程安全的。 ``` c->sender = s; c->signal_index = signal_index; QThreadData *td = r->d_func()->threadData; td->ref(); c->receiverThreadData.storeRelaxed(td); c->receiver.storeRelaxed(r); c->slotObj = slotObj; c->connectionType = type; c->isSlotObject = true; if (types) { c->argumentTypes.storeRelaxed(types); c->ownArgumentTypes = false; } ``` - `connect `保存的数据结构,在信号发出的时候,是怎么用上这个保存的数据结构的? 首先,编译出来的` moc_xxx.cpp `里面有个 `staticMetaObject`,通过它求出发送的信号的下标 `signal_index`。 然后通过 `signal_index `得到该信号绑定的 `ConnectionList`。 然后遍历得到的 `ConnectionList`,从中取出每一个` Connection `进行处理。 通过判断每一个` Connection `的 `connectionType` 和 `receiverThreadData` 的` threadId` 分别进行相应的处理。 - 队列绑定是怎么实现的?信号槽跨线程是怎么实现的?它怎么知道是否在同一个线程? 改变了 `threadData` 这个结构体的数据,例如` threadid`。 在` QObject.cpp `里面有个函数叫 `doActivate`,该函数通过在 `connect` 的时候包装的 `QObjectPrivate::Connection `信息来判断是否在同一个线程。 `Connection `里面的 `connectionType `如果是 `Qt::QueuedConnection `或者是 `Qt::AutoConnection `且不在同一个线程,那么会当前信号调用封装成 `QMetaCallEvent` 对象,并 `postEvent `到接受者的事件队列去。 不在同一个线程是通过` Connection` 的 `receiverThreadData `的 `threadId` (信号接收者) 和 `QThread::currentThreadId()`(发送信号者)对比得知。 - or 循环一直 `connect` 会内存泄漏吗?`connect` 的数据是怎么存的?`connect` 一万次就发送一万次信号? 会内存泄漏,因为它内部缓存的数据接口是链表。 `connect` 一万次就发送一万次信号。 - 槽函数的响应顺序是? 由于 `connect` 内部缓存的数据接口是链表,因此,同一个信号多个槽函数时,槽函数响应的顺序就是绑定的顺序(单线程情况下。如果在多线程情况下,执行的顺序是不确定的,因为主要依赖事件循环,并不一定能够确定多线程的顺序)。 参考:https://blog.csdn.net/github_38647413/article/details/130098563 部分源码解析可以参考:https://blog.csdn.net/m0_60259116/article/details/127537477 # 28、Ubuntu下安装Qt - 下载 Qt在线安装工具 访问`Qt`官方网站 `https://www.qt.io/download-open-source`下载`Qt Online Installer`。该工具可以帮助您选择要安装的`Qt`组件和版本。 - 添加执行权限 ``` chmod +x qt-unified-linux-x64-4.6.0-online.run ``` - 下载(阿里云镜像https://mirrors.aliyun.com/qt/)并根据提示操作安装即可。 ``` ./qt-unified-linux-x64-4.6.0-online.run --mirror https://mirrors.cloud.tencent.com/qt/ ``` 更新时,可以使用该工具进行加速下载: `MaintenanceTool.exe --mirror https://mirrors.cloud.tencent.com/qt/` 参考:https://blog.csdn.net/cheungxiongwei/article/details/131703475 - 如果出现问题: ``` From 6.5.0, xcb-cursor0 or libxcb-cursor0 is needed to load the Qt xcb platform plugin. Could not load the Qt platform plugin “xcb” in “” even though it was found. This application failed to start because no Qt platform plugin could be initialized. Reinstalling the application may fix this problem. Available platform plugins are: wayland-egl, xcb, linuxfb, vnc, minimalegl, vkkhrdisplay, offscreen, eglfs, minimal, wayland. ``` 则说明需要安装缺少的xcb-cursor库: ``` sudo apt-get update sudo apt-get install libxcb-cursor0 ``` 原文链接:https://blog.csdn.net/m0_64405299/article/details/137184349 # 29、Failed to initialize NVML: Driver/library version mismatch https://blog.csdn.net/qq_41076797/article/details/124909408 # 30、Qt 窗体位置函数的区别(如x(),y(),width(),rect(),geometry()等) Qt提供了很多关于获取窗体位置及显示区域大小的函数,如`x()、y()`和`pos()、rect()、size()和geometry()`等。几种函数的主要区别如下: - `x()、y()`和`pos()`函数的作用都是获得整个窗体左上角的坐标位置,即整个窗体左上角相对于父控件的`xy`位置,如果没有父控件,则相对于桌面。 - `frameGeometry()`函数和`geometry()`函数相对应。 - `frameGeometry()`函数获得的是整个窗体的左上顶点和长宽值,而`geometry()`函数获得的是窗体内中央区域(即显示内容的主体区域, 或称用户区域)的左上顶点坐标及长宽值。 - 直接调用`width()`和`height()`函数获得的是中央区域的长宽值。 - `rect()`和`size()`函数获得的结果也是对象窗体的中央区域而言的。 - `size()`函数获得的是中央区域的长宽值。 - `rect()`函数和`geometry()`函数相同都是返回一个`QRect`对象,这两个函数获得的长宽值相同,只是左上顶点的坐标不一样,`geometry()`函数获得的左上顶点的坐标是相对于父窗体的坐标而言的,而`rect()`函数获得的矩形,左上顶点的坐标始终为`(0,0)`。 原文链接:https://blog.csdn.net/luolaihua2018/article/details/109987092 # 31、Qt出现某个文件"规则太多"问题: 如题,Qt提示出现: ``` NMAKE:-1: warning: U4004: 目标“debug\moc_receiver_modes_mask.cpp”的规则太多 ``` 检查pro文件中,是否重复包含了`receiver_modes_mask.h`文件。 # 32、Qt出现某个小窗口一闪而过的问题 - 可能性一: 在函数内部,窗口对象内存是栈上的,且调用了show()函数显示,那么在函数退出后,窗口对象栈内存清除后,自然会一闪而过。 **解决办法**要么在堆上分配内存,要么使用阻塞的exec调用:[参考](https://blog.csdn.net/qq_38204686/article/details/140162453) ``` Form w; w.show(); QEventLoop loop; connect(&w, &Form::closed, &loop, &QEventLoop::quit); loop.exec(); qDebug() << "exec"; ``` - 可能性二: - **原因** - 这也是Arya项目中的问题,由于其中一个控件需要根据逻辑设置显示和隐藏,因此在创建控件时主动调用了setVisible(true), 当然这是画蛇添足,导致后面的一闪而过。 - 这个问题通常是因为Qt在显示一个窗口时,会先计算窗口的布局,然后执行一个重绘的过程 正常情况下,调用setVisible哪怕画蛇添足也不会出什么问题,但是由于程序初始化时创建了很多窗口,主窗口重绘耗时较多时,就有可能出现:控件调用setVisible(true)后将会立即显示一个该控件小窗口(但是该控件并未绘制出来,只是一个小窗口而已),然后又被主窗口重绘给抹除掉,产生一个"脏东西"一闪而过的现象。 - 如果这个窗口非常小,可能在重绘过程中,窗口的一部分还没有完全呈现出来,就被另一个事件(比如计算窗口布局的结果)影响,导致窗口重新绘制,之前的绘制结果被清除,造成一闪而过。 - **解决办法:** - 控件创建时,不要去设置其可见性为true,控件默认本来就是可见的(在相关槽函数逻辑中设置) ``` MyLineEditWidget* commb_lineedit = new MyLineEditWidget("Common-B register", "00", Oritentation_vertical); //commb_lineedit->setVisible(true); // 此处设置可见性,强制控件可见,这可能导致小窗口一闪而过 ``` - 如果你的窗口很小,可以尝试在显示窗口之前,先调用QCoreApplication::processEvents()。这将允许Qt处理当前事件队列中的所有事件,包括窗口的重绘事件: ``` window->setVisible(true); QCoreApplication::processEvents(); ``` - 另一个解决方法是使用QWidget::show()和QWidget::raise()来显示窗口,并将其置于顶部。 ``` window->show(); window->raise(); ``` - 如果你的窗口是一个对话框,你可以调用QDialog::open()而不是直接设置可见性。 ``` if (window->exec() == QDialog::Accepted) { // 用户接受了对话框 } ``` - 如果你的窗口很小,可以尝试设置一个合适的初始大小,或者在显示前调整它的大小。 ``` window->resize(QSize(width, height)); window->setVisible(true); ``` # 33、Qt 自定义表格代理、自定义表头时的绘制问题 - 总结: - 自定义表格、表头绘制线条式,关闭抗锯齿,线条宽度设置为1像素 - drawLine时不需要`+QPointF(0, 1)`或者`+QPointF(1, 0)` - 详细可参考同名博客 # 34. Qt出现“Could not parse application stylesheet” 解析styleSHeet出现错误, 原因可能是QSS格式错误, 包括少写了";"或者"}"等, 或者可清除编译产物后,重新构建项目, 还可能是资源路径错误(比如图片名称包含"@", 这个特别坑, 从codeDesign上导出的2倍分辨率图片名称就是包含"@2x"的, 这直接会导致找不到图片!!!) 参考: [链接标题](https://blog.csdn.net/qq_37860271/article/details/119608985?utm_medium=distribute.pc_relevant.none-task-blog-2~default~baidujs_baidulandingword~default-0-119608985-blog-108847129.235%5Ev43%5Epc_blog_bottom_relevance_base8&spm=1001.2101.3001.4242.1&utm_relevant_index=3) [链接标题](https://blog.csdn.net/blqzj214817/article/details/108847129) #35、Qt使用变量名"interface"出错: - 现象 当使用"interface"作为变量名时,可能出现错误: ``` error: C2332: “struct”: 缺少标记名 error: C2144: 语法错误:“<unnamed-tag>”的前面应有“)” error: C2144: 语法错误:“<unnamed-tag>”的前面应有“;” error: C2059: 语法错误:“,” error: C2059: 语法错误:“)” error: C2238: 意外的标记位于“;”之前 ``` 原因是Qt在某个地方将interface设置了关键字,比较无语的点是,将interface作为变量名,Qt Creator并不会直接提示错误,而是在编译时可能出现上述错误。 参考下面链接,可能与"objbase.h"有关,在出错的头文件中包含头文件"objbase.h"后,QtCreator就会直接提示错误: ``` error: Invalid parameter name: 'struct' is a keyword ``` - 来自deepseek: - 在 Qt 中,objbase.h 是 Windows 平台上的一个系统头文件(属于 COM/OLE 相关 API),Qt 本身并不直接依赖或实现它。但 Qt 在与 Windows COM 组件交互时(如 ActiveQt 模块)可能会间接用到它。系统头文件:objbase.h 是 Windows SDK 的一部分,定义了 COM(Component Object Model)的基础 API,如 IUnknown 接口、GUID 相关函数等。 - Qt 的间接使用:当 Qt 需要与 COM 交互(例如通过 QAxObject 操作 ActiveX 控件)时,会包含此头文件,但普通 Qt 开发通常不直接涉及它。 - 关于 interface 关键字的实现: - 在 Windows 中,objbase.h 通过宏将 interface 定义为 struct(因为 COM 接口本质是虚函数表): `#define interface struct`; - Qt 在非 Windows 平台自行定义 interface 为 struct,确保代码可移植。 - 具体的, windows中真正的定义在"combaseapi.h"中168-169行: ``` #define __STRUCT__ struct #define interface __STRUCT__ ``` - 参考: https://blog.csdn.net/dyf1983/article/details/80084938
上一篇:
部分Qt+OpenGL+CUDA+多线程问题总结
下一篇:
LayoutEdit相关基础
0
赞
4 人读过
新浪微博
微信
腾讯微博
QQ空间
人人网
提交评论
立即登录
, 发表评论.
没有帐号?
立即注册
0
条评论
More...
文档导航
没有帐号? 立即注册