// Асинхронное выполнение функции load
QMetaObject::invokeMethod(this, "load", Qt::QueuedConnection);
// Выполнение функции load через 10 миллисекунд
QTimer::singleShot(10, this, SLOT(load()));
// Использование таймера с лямбда-выражением
QTimer::singleShot(10, [&]() {
load();
});
QTimer *timer = new QTimer(this);
timer->setSingleShot(true);
connect(timer, &QTimer::timeout, this, [timer, this] {
});
timer->start(5000);
Если вы хотите использовать Qt Creator для развёртывания приложений на Android, сначала необходимо настроить Android Studio таким образом, чтобы скомпилированное приложение могло работать на телефоне или эмуляторе.
После того как вы найдёте соответствующий метод в Qt, не забудьте изучить перегрузки этой функции. Вы можете обнаружить множество параметров, которые предоставляют различные возможности. Иногда это может привести к внезапному осознанию того, что Qt уже предоставляет вам готовые решения. Например, перегрузки функций QString и QColor имеют множество параметров, предоставляя вам множество функций, о которых вы могли только мечтать.
В файле pro можно указать информацию о версии программы, иконке, названии продукта, авторских правах и описании файла (поддерживается только в Qt5). На Windows при использовании qmake эта информация автоматически преобразуется в rc файл. Для более ранних версий Qt4 можно создать rc файл вручную.
# Версия программы
VERSION = 2025.10.01
# Иконка программы
RC_ICONS = main.ico
# Название продукта
QMAKE_TARGET_PRODUCT = quc
# Авторские права
QMAKE_TARGET_COPYRIGHT = feiyangqingyun
# Описание файла
QMAKE_TARGET_DESCRIPTION = QQ: 517216493 WX: feiyangqingyun
QMAKE_LFLAGS += /MANIFESTUAC:"level='requireAdministrator' uiAccess='false'" # Запуск от имени администратора
QMAKE_LFLAGS += /SUBSYSTEM:WINDOWS,"5.01" # Запуск в Windows XP в VS2013
TEMPLATE = app
MOC_DIR = temp/moc
RCC_DIR = temp/rcc
UI_DIR = temp/ui
OBJECTS_DIR = temp/obj
# Добавление строки для запуска файла с окном вывода отладки
CONFIG += console
Используйте QPainter::drawTiledPixmap для рисования плоского фона и QPainter::drawRoundedRect() для рисования прямоугольников с закруглёнными углами, вместо QPainter::drawRoundRect(). Эти две функции легко перепутать.
Укажите удаление старого стиля управления.
// Удаление старого стиля
style()->unpolish(ui->btn);
// Обязательно выполните следующую строку, иначе стиль не удалится
ui->btn->setStyleSheet("");
// Установите новый стиль для элемента управления
style()->polish(ui->btn);
widget->property(name); qDebug() << name << type << value; }
// Количество всех методов int methodCount = metaObject->methodCount(); // methodOffset — это начало пользовательских методов int methodOffset = metaObject->methodOffset(); // Цикл для получения пользовательских методов компонента, int i = 0 означает все методы for (int i = methodOffset; i < methodCount; ++i) { QMetaMethod metaMethod = metaObject->method(i); const char *name = metaMethod.name(); const char *type = metaMethod.typeName(); qDebug() << name << type; }
12. В Qt встроенные значки упакованы в QStyle, их около семидесяти, и их можно использовать напрямую.
```cpp
SP_TitleBarMenuButton,
SP_TitleBarMinButton,
SP_TitleBarMaxButton,
SP_TitleBarCloseButton,
SP_MessageBoxInformation,
SP_MessageBoxWarning,
SP_MessageBoxCritical,
SP_MessageBoxQuestion,
...
// Получение и использование следующим образом
QPixmap pixmap = this->style()->standardPixmap(QStyle::SP_TitleBarMenuButton);
ui->label->setPixmap(pixmap);
win32 {
contains(DEFINES, WIN64) {
DESTDIR = $$PWD/../bin64
} else {
DESTDIR = $$PWD/../bin32
}
}
Qt5 усилил множество проверок безопасности, если появляется сообщение setGeometry: Unable to set geometry, переместите видимость этого компонента после добавления его в макет.
Можно добавить компонент A в макет, затем установить этот макет для компонента B, что повышает гибкость комбинирования компонентов, например, можно добавить кнопку поиска слева или справа от текстового поля, установив для кнопки значок.
QPushButton *btn = new QPushButton;
btn->resize(30, ui->lineEdit->height());
QHBoxLayout *layout = new QHBoxLayout(ui->lineEdit);
layout->setMargin(0);
layout->addStretch();
layout->addWidget(btn);
Чтобы установить стиль для QLCDNumber, необходимо установить segmentstyle в flat, иначе эффекта не будет.
Умное использование findChildren позволяет найти все дочерние компоненты данного компонента. findChild используется для поиска одного.
// Поиск компонента с указанным именем класса objectName
QList<QWidget *> widgets = fatherWidget.findChildren<QWidget *>("widgetname");
// Поиск всех QPushButton
QList<QPushButton *> allPButtons = fatherWidget.findChildren<QPushButton *>();
// Поиск дочерних компонентов первого уровня, иначе будут просмотрены все дочерние элементы
QList<QPushButton *> childButtons = fatherWidget.findChildren<QPushButton *>(QString(), Qt::FindDirectChildrenOnly);
QTimer *timer = new QTimer; // QTimer наследует QObject
timer->inherits("QTimer"); // возвращает true
timer->inherits("QObject"); // возвращает true
timer->inherits("QAbstractButton"); // возвращает false
Используя механизм слабых свойств, можно хранить временные значения для передачи в суждениях. Можно перечислить все имена слабых свойств с помощью widget->dynamicPropertyNames(), а затем получить значение соответствующего слабого свойства с помощью widget->property("name").
Во время разработки, будь то из соображений удобства обслуживания или экономии памяти, следует иметь один qss-файл для хранения всех таблиц стилей, а не использовать setStyleSheet повсеместно. Если вы находитесь на стадии обучения или тестирования, вы можете напрямую установить таблицу стилей в пользовательском интерфейсе, щёлкнув правой кнопкой мыши, но для официальных проектов рекомендуется объединить их в один файл таблицы стилей qss для единообразного управления.
Если появляется ошибка Z-order assignment: is not a valid widget, откройте соответствующий ui-файл в Блокноте и удалите пустые места в теге .
Умение эффективно использовать второй параметр addItem в QComboBox для установки пользовательских данных может привести к множеству эффектов, которые можно извлечь с помощью itemData. Важно отметить, что вторым параметром является тип QVariant, поэтому он должен быть достаточно гибким, чтобы содержать универсальные данные, такие как структуры, позволяя передавать множество данных вместо одного. Например, в выпадающем списке для выбора номера студента можно связать имя студента, класс и оценки. Многие люди думают, что могут быть связаны только данные типа QString или int, потому что обычно используются именно эти два типа.
QStringList listVideoOpenInterval, listVideoOpenIntervalx;
listVideoOpenInterval << "0.0 秒" << "0.1 秒" << "0.3 秒" << "0.5 秒" << "1.0 秒" << "2.0 秒";
listVideoOpenIntervalx << "0" << "100" << "300" << "500" << "1000" << "2000";
for (int i = 0; i < listVideoOpenInterval.count(); ++i) {
ui->cboxVideoOpenInterval->addItem(listVideoOpenInterval.at(i), listVideoOpenIntervalx.at(i));
}
// Извлечение соответствующего значения
int indexVideoOpenInterval = ui->cboxVideoOpenInterval->currentIndex();
indexVideoOpenInterval = ui->cboxVideoOpenInterval->itemData(indexVideoOpenInterval).toInt();
При использовании модуля webengine необходимо включить QtWebEngineProcess.exe, папку translations и resources при публикации программы, иначе она не запустится должным образом.
В программах MFC, VB/C# и других оконных приложениях каждый компонент имеет свой дескриптор, который автоматически распознаётся при перемещении с помощью инструментов дескриптора, но в приложениях Qt по умолчанию каждому окну соответствует один дескриптор. Чтобы каждый компонент имел отдельный дескриптор, выполните следующие настройки в функции main.
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
a.setAttribute(Qt::AA_NativeWindows);
}
#if defined(Q_OS_ANDROID)
QAndroidService a(argc, argv);
return a.exec()
#else
QApplication a(argc, argv);
return a.exec();
#endif
*::down-arrow{}
*::menu-indicator{}
*::up-arrow:disabled{}
*::up-arrow:off{}
QMainWindow > .QWidget {
background-color: gainsboro;
background-image: url(:/images/xxoo.png);
background-position: top right;
background-repeat: no-repeat
}
// Написание для Qt4
./HelloQt -qws &
``` ```
//Qt5写法 xcb можно заменить на linuxfb eglfs vnc wayland и т. д., какой есть, тот и использовать для поочерёдного тестирования
./HelloQt --platform xcb
./HelloQt --platform linuxfb
./HelloQt --platform wayland
Если обнаружится, что комплект сборки в QtCreator работает неправильно (например, не может правильно распознать qmake или компилятор в среде, или при открытии проекта не получается создать каталог теневой сборки), необходимо найти два каталога (C:\Users\Administrator\AppData\Local\QtProject, C:\Users\Administrator\AppData\Roaming\QtProject) и удалить их. После удаления заново открыть QtCreator и настроить комплект сборки.
QMediaPlayer — это оболочка (также называемая фреймворком), которая зависит от локального декодера. По умолчанию он в основном воспроизводит MP4, но даже MP4 не может быть воспроизведён. Для поддержки других форматов необходимо загрузить и установить k-lite или LAV Filters (k-lite или LAV Filters относятся к Windows, для других систем ищите самостоятельно; похоже, что во встраиваемом Linux используется GStreamer (sudo apt-get install gstreamer1.0-libav ubuntu-restricted-extras), но полной проверки не было, и при ошибке выводится сообщение «Your GStreamer installation is missing a plug-in», требуется командная установка sudo apt-get install ubuntu-restricted-extras). Если вам нужен мощный проигрыватель, новичкам рекомендуется использовать vlc или mpv, а для универсального решения — ffmpeg (видео после декодирования можно использовать с QOpenGLWidget для рендеринга на GPU или преобразовать в QImage для отрисовки, аудиоданные можно воспроизвести с помощью QAudioOutput).
//pro中判断编译器版本
greaterThan(MSC_VER, 1900) {
}
//GCC编译器
#ifdef __GNUC__
#if __GNUC__ >= 3 // GCC3.0 以上
//MSVC编译器
#ifdef _MSC_VER
#if _MSC_VER >=1000 // VC++4.0 以上
#if _MSC_VER >=1100 // VC++5.0 以上
#if _MSC_VER >=1200 // VC++6.0 以上
#if _MSC_VER >=1300 // VC2003 以上
#if _MSC_VER >=1400 // VC2005 以上
#if _MSC_VER >=1500 // VC2008 以上
#if _MSC_VER >=1600 // VC2010 以上
#if _MSC_VER >=1700 // VC2012 以上
#if _MSC_VER >=1800 // VC2013 以上
#if _MSC_VER >=1900 // VC2015 以上
//Visual Studio版本与MSVC版本号的对应关系
MSC 1.0 _MSC_VER == 100
MSC 2.0 _MSC_VER == 200
MSC 3.0 _MSC_VER == 300
MSC 4.0 _MSC_VER == 400
MSC 5.0 _MSC_VER == 500
MSC 6.0 _MSC_VER == 600
MSC 7.0 _MSC_VER == 700
MSVC++ 1.0 _MSC_VER == 800
MSVC++ 2.0 _MSC_VER == 900
MSVC++ 4.0 _MSC_VER == 1000 (Developer Studio 4.0)
MSVC++ 4.2 _MSC_VER == 1020 (Developer Studio 4.2)
MSVC++ 5.0 _MSC_VER == 1100 (Visual Studio 97 version 5.0)
MSVC++ 6.0 _MSC_VER == 1200 (Visual Studio 6.0 version 6.0)
MSVC++ 7.0 _MSC_VER == 1300 (Visual Studio .NET 2002 version 7.0)
MSVC++ 7.1 _MSC_VER == 1310 (Visual Studio .NET 2003 version 7.1)
MSVC++ 8.0 _MSC_VER == 1400 (Visual Studio 2005 version 8.0)
MSVC++ 9.0 _MSC_VER == 1500 (Visual Studio 2008 version 9.0)
MSVC++ 10.0 _MSC_VER == 1600 (Visual Studio 2010 version 10.0)
MSVC++ 11.0 _MSC_VER == 1700 (Visual Studio 2012 version 11.0)
MSVC++ 12.0 _MSC_VER == 1800 (Visual Studio 2013 version 12.0)
MSVC++ 14.0 _MSC_VER == 1900 (Visual Studio 2015 version 14.0)
MSVC++ 14.1 _MSC_VER == 1910 (Visual Studio 2017 version 15.0)
MSVC++ 14.11 _MSC_VER == 1911 (Visual Studio 2017 version 15.3)
MSVC++ 14.12 _MSC_VER == 1912 (Visual Studio 2017 version 15.5)
MSVC++ 14.13 _MSC_VER == 1913 (Visual Studio 2017 version 15.6)
MSVC++ 14.14 _MSC_VER == 1914 (Visual Studio 2017 version 15.7)
MSVC++ 14.15 _MSC_VER == 1915 (Visual Studio 2017 version 15.8)
MSVC++ 14.16 _MSC_VER == 1916 (Visual Studio 2017 version 15.9)
MSVC++ 14.2 _MSC_VER == 1920 (Visual Studio 2019 Version 16.0)
MSVC++ 14.21 _MSC_VER == 1921 (Visual Studio 2019 Version 16.1)
MSVC++ 14.22 _MSC_VER == 1922 (Visual Studio 2019 Version 16.2)
MSVC++ 14.30 _MSC_VER == 1930 (Visual Studio 2022 Version 17.0)
MSVC++ 14.31 _MSC_VER == 1931 (Visual Studio 2022 Version 17.1)
MSVC++ 14.32 _MSC_VER == 1932 (Visual Studio 2022 Version 17.2)
//Borland C++
#ifdef __BORLANDC__
//Cygwin
#ifdef __CYGWIN__
#ifdef __CYGWIN32__
//mingw
#ifdef __MINGW32__
//windows
#ifdef _WIN32 //32bit
#ifdef _WIN64 //64bit
#ifdef _WINDOWS //图形界面程序
#ifdef _CONSOLE //控制台程序
//Windows(95/98/Me/NT/2000/XP/Vista)和Windows CE都定义了
#if (WINVER >= 0x030a) // Windows 3.1以上
#if (WINVER >= 0x0400) // Windows 95/NT4.0以上
#if (WINVER >= 0x0410) // Windows 98以上
#if (WINVER >= 0x0500) // Windows Me/2000以上
#if (WINVER >= 0x0501) // Windows XP以上
#if (WINVER >= 0x0600) // Windows Vista以上
//_WIN32_WINNT 内核版本
#if (_WIN32_WINNT >= 0x0500) // Windows 2000以上
#if (_WIN32_WINNT >= 0x0501) // Windows XP以上
#if (_WIN32_WINNT >= 0x0600) // Windows Vista以上
#打印版本信息
message(qt version: $$QT_VERSION)
#判断当前qt版本号
QT_VERSION = $$[QT_VERSION]
QT_VERSION = $$split(QT_VERSION, ".")
QT_VER_MAJ =
``` ```
while (model->canFetchMore()) {
model->fetchMore();
}
QString content = "测试中文";
QString note = content.toUtf8().toPercentEncoding();
Qt по умолчанию не поддерживает большие файлы ресурсов, например, при добавлении файла шрифта, это нужно включить в файле pro. CONFIG += resources_big
После наследования QWidget в Qt таблица стилей не работает. Есть три способа решения этой проблемы. Настоятельно рекомендуется первый способ.
void Widget::paintEvent(QPaintEvent *)
{
QStyleOption option;
option.initFrom(this);
QPainter painter(this);
style()->drawPrimitive(QStyle::PE_Widget, &option, &painter, this);
}
Иногда, когда на интерфейсе есть пружина, может потребоваться динамически изменить стратегию растяжения соответствующей пружины. Соответствующий метод — changeSize, многие люди будут искать его, начиная с set.
При использовании QFile не рекомендуется часто открывать файл для записи, а затем закрывать его. Например, если вывод журнала происходит с интервалом в 5 мс, производительность ввода-вывода сильно снижается. В таких случаях рекомендуется оставить файл открытым до подходящего момента, например, закрыть файл в деструкторе или при изменении даты и необходимости перезаписи журнала. Иначе частые открытия и закрытия файлов могут привести к зависаниям, особенно с большими файлами.
Во многих сетевых приложениях требуется настраивать пакеты сердцебиения для поддержания соединения. Иначе при отключении питания или некорректном завершении программы другая сторона может не сразу распознать это, или распознавание может занять много времени (обычно не менее 30 секунд). Необходимо реализовать тайм-аут обнаружения. Однако некоторые программы не предоставляют протокол сердцебиения, в этом случае нужно активировать систему поддержания активности на уровне сокетов. Этот метод подходит для TCP-соединений.
int fd = tcpSocket->socketDescriptor();
int keepAlive = 1; // Открываем свойство keepalive, значение по умолчанию: 0 (закрыто)
int keepIdle = 5; // Если в течение 5 секунд нет обмена данными, выполняется обнаружение, значение по умолчанию: 7200 (с)
int keepInterval = 2; // Интервал времени для отправки пакетов обнаружения составляет 2 секунды, значение по умолчанию: 75 (с)
int keepCount = 2; // Количество повторных попыток обнаружения, если все попытки неудачны, соединение считается потерянным, значение по умолчанию: 9 (раз)
setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&keepAlive, sizeof(keepAlive));
setsockopt(fd, SOL_TCP, TCP_KEEPIDLE, (void *)&keepIdle, sizeof(keepIdle));
setsockopt(fd, SOL_TCP, TCP_KEEPINTVL, (void *)&keepInterval, sizeof(keepInterval));
setsockopt(fd, SOL_TCP, TCP_KEEPCNT, (void *)&keepCount, sizeof(keepCount));
Обычно сообщение «Это приложение не запустилось, потому что оно не смогло найти или загрузить плагин платформы Qt» появляется после упаковки программы, причиной является то, что каталог плагина platforms не был упакован или был упакован неправильно.
Не рекомендуется включать китайский язык в tr, хотя текущая новая версия Qt поддерживает перевод с китайского на другие языки, но это нестандартно и неизвестно, кто этому научил (позже я обнаружил, что когда я только начинал изучать Qt, я также опубликовал несколько демонстраций в Интернете, и в то время tr включал китайский язык). Идея tr заключается в том, чтобы содержать английский текст, а затем переводить его на другие языки, такие как китайский. Сейчас многие начинающие разработчики злоупотребляют tr. Если перевод не требуется, лучше отключить tr. tr требует затрат, Qt по умолчанию считает, что он нужен для перевода, и выполняет специальную обработку.
Различия между Qt и Qt Creator
На вопрос о том, какая встраиваемая платформа Qt лучше, есть общий ответ (на момент 2018 года): imx6+335x более стабильны, с высокой производительностью используйте RK3288 RK3399, если хотите сэкономить, возьмите Allwinner H3, для развлечения можно попробовать Raspberry Pi или Orange Pi.
Для длинных блоков комментариев кода рекомендуется использовать #if 0 #endif для включения блока кода, вместо того чтобы выделять этот блок кода и комментировать его с помощью двойного слеша. Когда вам нужно открыть этот код снова, вам придётся снова выделить его и отменить комментирование. Если вы используете #if 0, вам просто нужно изменить 0 на 1, это значительно повышает эффективность разработки.
Существует множество способов упаковки и публикации приложений Qt. Начиная с Qt5, предоставляются инструменты для упаковки, такие как windeployqt (linuxdeployqt для Linux и macdeployqt для Mac), которые позволяют удобно упаковывать приложения. Однако эти инструменты не всегда идеальны, иногда они добавляют ненужные файлы, а иногда забывают упаковать нужные плагины, особенно в случае использования qml. Кроме того, они не распознают сторонние библиотеки. Если ваше приложение зависит от ffmpeg, вам нужно самостоятельно скопировать соответствующие библиотеки. Окончательный метод заключается в копировании вашего исполняемого файла в каталог bin в каталоге установки Qt, а затем упаковке всего вместе, удаляя ненужные компоненты, пока приложение не начнёт работать без проблем.
Анимация в Qt основана на таймере QElapsedTimer. Он генерирует данные в соответствии с определёнными правилами алгоритма, а затем обрабатывает свойства.
При рисовании круга без фона, только с цветом границы, можно заменить его рисованием дуги в 360 градусов, эффект будет таким же.
QRect rect(-radius, -radius, radius * 2, radius * 2);
// Можно выбрать один из двух методов, второй рисует дугу в 360 градусов = круг без фона
painter->drawArc(rect, 0, 360 * 16);
painter->drawEllipse(rect);
Не стоит относиться к указателю d как к чему-то загадочному, на самом деле это просто частный класс, определённый в файле реализации класса, используемый для хранения локальных переменных. Лично я рекомендую не использовать этот механизм в небольших проектах, так как он снижает читаемость кода и увеличивает сложность. Начинающие разработчики могут запутаться, когда им придётся разбираться в таком коде.
Многие разработчики при рисовании ограничиваются установкой одноцветной кисти для пера QPen, но на самом деле QPen позволяет устанавливать кисть. Это значительно увеличивает гибкость. Например, установив кисть для QPen, можно использовать различные градиенты, рисовать градиентные полосы прогресса и текст, вместо использования одного цвета.
Многие элементы управления имеют viewport, например QTextEdit, QTableWidget и QScrollArea. Иногда при работе с этими элементами управления напрямую они не работают, нужно настроить viewport(). Например, чтобы установить прозрачный фон области прокрутки, нужно использовать scrollArea->viewport()->setStyleSheet("background-color:transparent;"), а не scrollArea->setStyleSheet("QScrollArea{background-color:transparent;}"). Иногда, если для отслеживания мыши установлено значение setMouseTracking как true и на данном окне есть другие элементы управления, то при перемещении курсора на другой элемент управления события перемещения мыши в родительском классе (MouseMove) не распознаются. В этом случае необходимо использовать событие HoverMove. Сначала нужно установить:
setAttribute(Qt::WA_Hover, true);
QDateTime dateTime;
QString dateTime_str = dateTime.currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
// Из строки в миллисекунды (требуется полный формат года, месяца, дня, часа, минуты, секунды)
datetime.fromString("2011-09-10 12:07:50:541", "yyyy-MM-dd hh:mm:ss:zzz").toMSecsSinceEpoch();
// Из строки в секунды (требуется полный формат года, месяца, дня, часа, минуты, секунды)
datetime.fromString("2011-09-10 12:07:50:541", "yyyy-MM-dd hh:mm:ss:zzz").toTime_t();
// Из миллисекунд в год, месяц, день, час, минуту, секунду
datetime.fromMSecsSinceEpoch(1315193829218).toString("yyyy-MM-dd hh:mm:ss:zzz");
// Из секунд в год, месяц, день, час, минуту, секунду (если есть zzz, то будет 000)
datetime.fromTime_t(1315193829).toString("yyyy-MM-dd hh:mm:ss[:zzz]");
При использовании списков или массивов, таких как QList, QStringList, QByteArray, рекомендуется использовать метод at() для получения значений, а не оператор []. В официальной книге «C++ GUI Qt 4 программирование (второе издание)» этому уделяется особое внимание. Книга была написана основными разработчиками Qt, поэтому она достаточно авторитетна. Что касается сравнения скорости и эффективности использования at() и оператора [], то в интернете можно найти сравнения, проведённые другими пользователями. Оригинал текста находится на странице 212 книги. Текст описывает следующее: Qt использует неявное совместное использование для всех контейнеров и многих других классов. Неявное совместное использование гарантирует, что Qt не будет копировать данные, которые не предполагается изменять. Чтобы максимально использовать преимущества неявного совместного использования, можно принять две новые привычки программирования. Первая привычка заключается в том, чтобы использовать функцию at() при чтении из вектора или списка, а не оператора []. Это связано с тем, что классы контейнеров Qt не могут определить, будет ли оператор [] использоваться в левой части присваивания или в правой. Они предполагают наихудший случай и принудительно выполняют глубокое копирование. Функция at() не допускается в левой части присваивания.
Если вам нужно, чтобы после выполнения exec() в диалоговом окне выполнялся другой код, добавьте строку кода перед exec(). Иначе выполнение будет заблокировано.
QDialog dialog;
dialog.setWindowModality(Qt::WindowModal);
dialog.exec();
Для безопасного удаления объектов Qt рекомендуется использовать deleteLater вместо delete, так как deleteLater освобождает память в более подходящее время, в то время как delete может вызвать сбой программы. Для массового удаления объектов можно использовать qDeleteAll, например, qDeleteAll(btns).
В QTableView, если вам нужны пользовательские кнопки столбцов, флажки, выпадающие списки и т. д., вы можете использовать пользовательский делегат QItemDelegate. Если вы хотите отключить столбец, верните 0 в функции createEditor пользовательского делегата. Пользовательский делегат отображается при входе в режим редактирования. Если вы хотите, чтобы он отображался постоянно, вам нужно перегрузить функцию paint и использовать drawPrimitive или drawControl для рисования.
Когда вы ознакомитесь с несколькими методами, такими как drawPrimitive, drawControl, drawItemText, drawItemPixmap и т.д., соответствующими QApplication::style(), в сочетании со свойствами QStyleOption, вы сможете создавать различные пользовательские делегаты. Вы также можете напрямую использовать painter в функции paint для различных рисунков, таких как крутые таблицы, древовидные списки, выпадающие списки и т. д. QApplication::style()->drawControl четвёртый параметр метода, если он не установлен, нарисованный элемент управления не будет применять таблицу стилей.
С координатами в уме и всем остальным — painter. Настоятельно рекомендуется просмотреть, попробовать и понять все функции в файле заголовка qpainter.h во время изучения пользовательских чертежей управления. Здесь представлены все встроенные интерфейсы рисования Qt. Попробуйте все соответствующие параметры, и вы откроете для себя много нового. Это определённо пробудит ваш интерес к рисованию, подобно тому, как это делал бы мастер каллиграфии, свободно бродящий по миру рисования кода.
При использовании setItemWidget или setCellWidget иногда обнаруживается, что установленный элемент управления не центрирован, а выровнен по умолчанию влево и не растягивается автоматически. Для тех, кто стремится к совершенству, существует универсальный метод: поместите этот элемент управления в макет виджета, затем добавьте виджет в элемент, и проблема будет решена идеально. Кроме того, таким образом можно комбинировать несколько элементов управления для создания сложных элементов управления.
// Создание экземпляра индикатора выполнения
QProgressBar *progress = new QProgressBar;
// Добавление виджета + макета для умного центрирования
QWidget *widget = new QWidget;
QHBoxLayout *layout = new QHBoxLayout;
layout->setSpacing(0);
layout->setMargin(0);
layout->addWidget(progress);
widget->setLayout(layout);
ui->tableWidget->setCellWidget(0, 0, widget);
// Автоматический расчёт подходящего переднего плана на основе цвета фона
double gray = (0.299 * color.red() + 0.587 * color.green() + 0.114 * color.blue()) / 255;
QColor textColor = gray > 0.5 ? Qt::black : Qt::white;
#if (QT_VERSION < QT_VERSION_CHECK(5,0,0))
ui->tableView->horizontalHeader()->setResizeMode(0, QHeaderView::Fixed);
ui->treeView->header()->setResizeMode(0, QHeaderView::Fixed);
#else
ui->tableView->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Fixed);
ui->treeView->header()->setSectionResizeMode(0, QHeaderView::Fixed);
#endif
QColor color(255, 0, 0, 100);
qDebug() << color.name() << color.name(QColor::HexArgb);
// Вывод #ff0000 #64ff0000
if (variant.typeName() == "QColor") {
QColor color = variant.value<QColor>();
QFont font = variant.value<QFont>();
QString nodeValue = color.name(QColor::HexArgb);
}
Можно рассмотреть добавление глобального синглтона класса AppEvent, где будут храниться общие сигналы. Затем окно A может связать свои сигналы с AppEvent, а окно D может привязаться к сигналам AppEvent в соответствующих слотах. Это будет чисто, аккуратно и удобно в управлении.
Меню правой кнопки мыши для QTextEdit по умолчанию на английском языке. Если вы хотите видеть его на китайском, достаточно загрузить widgets.qm файл. В одной Qt программе можно использовать несколько файлов локализации без конфликтов.
В Qt есть глобальный сигнал переключения фокуса focusChanged, который можно использовать для создания пользовательского метода ввода. В Qt4 метод ввода контекста устанавливается по умолчанию, вы можете увидеть это, напечатав a.inputContext в функции main. Этот контекст метода ввода по умолчанию перехватывает два замечательных сигнала QEvent::RequestSoftwareInputPanel и QEvent::CloseSoftwareInputPanel, так что даже если вы установили глобальный фильтр событий, он не распознает эти два сигнала. Вам нужно выполнить a.setInputContext(0) в функции main, чтобы установить контекст метода ввода пустым. Начиная с версии Qt5.7, предоставляется встроенный метод ввода, который можно активировать, добавив qputenv("QT_IM_MODULE", QByteArray("qtvirtualkeyboard")) в начале функции main.
После Qt5.10 минимальная ширина столбца для табличных виджетов QTableWidget или QTableView была изменена на 15 (ранее было 0). Поэтому в новых версиях Qt, если вы установите слишком малую ширину столбцов, она не применится, вместо этого будет использоваться минимальная ширина. Чтобы установить меньшую ширину столбцов, вам нужно снова установить ui->tableView->horizontalHeader()->setMinimumSectionSize(0).
В исходном коде Qt встроено некоторое количество непубличных технологий, которые нельзя использовать напрямую, они спрятаны в секции private соответствующих модулей, таких как gui-private, widgets-private и т.д. Например, классы для работы с zip файлами QZipReader и QZipWriter находятся в модуле gui-private. Чтобы их использовать, необходимо добавить в pro файл строку QT += gui-private.
#include "QtGui/private/qzipreader_p.h"
#include "QtGui/private/qzipwriter_p.h"
QZipReader reader(dirPath);
QString path("");
// Распаковываем папку в текущий каталог
reader.extractAll(path);
// Имя папки
QZipReader::FileInfo fileInfo = reader.entryInfoAt(0);
// Распаковка файла
QFile file(filePath);
file.open(QIODevice::WriteOnly);
file.write(reader.fileData(QString::fromLocal8Bit("%1").arg(filePath)));
file.close();
reader.close();
QZipWriter *writer = new QZipWriter(dirPath);
// Добавляем папку
writer->addDirectory(unCompress);
// Добавляем файл
QFile file(filePath);
file.open(QIODevice::ReadOnly);
writer->addFile(data, file.readAll());
file.close();
writer->close();
Теоретически, отправка и получение данных через последовательный порт и сеть по умолчанию асинхронны, операционная система управляет этим автоматически, и интерфейс не блокируется. Утверждения о том, что передача данных блокирует интерфейс, неверны; реальное время занимает обработка данных, а не их передача. Для проектов с небольшими объёмами данных обычно не рекомендуется использовать потоки для обработки, поскольку управление потоками требует дополнительных затрат. Не стоит помещать всё в потоки, потоки не являются универсальным решением. Потоки нужны только для действительно ресурсоёмких операций, таких как кодирование и декодирование.
Получение ширины и высоты элемента управления в конструкторе может быть неправильным. Необходимо получать размеры после первого отображения элемента управления. Элементы управления устанавливают корректные значения ширины и высоты только после первого показа. Важно помнить, что это происходит после первого показа, а не после запуска программы или конструктора. Если после запуска программы некоторые элементы управления в контейнерах, таких как QTabWidget, не отображаются, попытка получить их ширину и высоту также может быть некорректной. Безопаснее всего получать размеры после первого показа.
Обработку базы данных рекомендуется проводить в главном потоке. Если необходимо выполнять операции с базой данных в другом потоке, важно помнить, что открывать базу данных также следует в этом потоке. Нельзя открыть базу данных в главном потоке и выполнять SQL запросы в дочернем потоке, это может вызвать проблемы.
В новой версии QTcpServer в 64-битной версии Qt функция incomingConnection может не вызываться. Это связано с изменением параметра функции incomingConnection в Qt5 с int на qintptr. Преимущество использования qintptr заключается в том, что в 32-битных системах он автоматически преобразуется в quint32, а в 64-битных — в quint64. Если в Qt5 продолжать использовать параметр int, то в 32-битной системе проблем не возникнет, а в 64-битной могут возникнуть ошибки. Поэтому для обеспечения совместимости с Qt4 и Qt5 необходимо использовать разные параметры.
#if (QT_VERSION > QT_VERSION_CHECK(5,0,0))
void incomingConnection(qintptr handle);
#else
void incomingConnection(int handle);
#endif
Qt поддерживает автоматическое связывание всех интерфейсных элементов управления, таких как QPushButton и QLineEdit, с соответствующими сигналами и слотами. Например, для сигнала нажатия кнопки можно реализовать слот on_pushButton_clicked().
Контрол QWebEngineView использует OpenGL, что может вызывать различные проблемы на некоторых компьютерах из-за низкого качества драйверов OpenGL. Например, при отображении в полноэкранном режиме может перестать работать правая кнопка мыши. В функции main необходимо включить программную отрисовку OpenGL.
#if (QT_VERSION > QT_VERSION_CHECK(5,4,0))
// Оба метода работают, Qt по умолчанию использует AA_UseDesktopOpenGL
QCoreApplication::setAttribute(Qt::AA_UseOpenGLES);
// QCoreApplication::setAttribute(Qt::AA_UseSoftwareOpenGL);
#endif
QApplication a(argc, argv);
Другой способ решения проблемы с неработающим контекстным меню при использовании QWebEngineView в полноэкранном режиме — сдвиг на один пиксель вверх.
QRect rect = qApp->desktop()->geometry();
rect.setY(-1);
rect.setHeight(rect.height());
this->setGeometry(rect);
QStyle::sliderValueFromPosition(minimum(), maximum(), event->x(), width());
// Загрузка таблицы соответствий английских и китайских названий свойств
QFile file(":/propertyname.txt");
if (file.open(QFile::ReadOnly)) {
// Скорость чтения с использованием QTextStream как минимум на 30% выше
#if 0
while(!file.atEnd()) {
QString line = file.readLine();
appendName(line);
}
#else
QTextStream in(&file);
while (!in.atEnd()) {
QString line = in.readLine();
appendName(line);
}
#endif
file.close();
}
На встроенном Linux, если установлено окно без рамки, и в этом окне есть текстовые поля и другие элементы, обнаруживается, что невозможно сфокусироваться для ввода. В этом случае необходимо активно активировать окно.
// Такой способ установки окна без рамки на встроенном устройстве не позволяет получить фокус
setWindowFlags(Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint | Qt::X11BypassWindowManagerHint);
// Необходимо активно активировать окно после show
w->show();
w->activateWindow();
Функция replace класса QString изменяет исходную строку. Помните, она возвращает новую строку после замены, но также изменяет исходную.
Эффекты, связанные с классом QGraphicsEffect, очень впечатляющие, они могут создавать множество эффектов, таких как прозрачность, градиент, тень и т.д., но этот класс сильно нагружает CPU. Если нет особой необходимости, не рекомендуется его использовать. Даже если он используется, это должно быть сделано в сценах, где компонент не будет часто перерисовываться в будущем, иначе это может привести к сбоям.
QString path = "C:/temp/test.txt";
path = QDir::toNativeSeparators(path);
// Вывод: C:\\temp\\test.txt
QString path = "C:\\temp\\test.txt";
path = QDir::toNativeSeparators(path);
// Вывод: C:/temp/test.txt
// Объявление сигналов и слотов в заголовке
signals:
void sig_test(int type, double value);
private slots:
void slot_test(int type, double value);
private:
Q_INVOKABLE void fun_test(int type, double value);
// Связывание сигналов и слотов в конструкторе
connect(this, SIGNAL(sig_test(int, double)), this, SLOT(slot_test(int, double)));
// Запуск сигналов и слотов при нажатии кнопки
void MainWindow::on_pushButton_clicked()
{
QMetaObject::invokeMethod(this, "sig_test", Q_ARG(int, 66), Q_ARG(double, 66.66));
QMetaObject::invokeMethod(this, "slot_test", Q_ARG(int, 88), Q_ARG(double, 88.88));
QMetaObject::invokeMethod(this, "fun_test", Q_ARG(int, 99), Q_ARG(double, 99.99));
}
// Выводит 66 66.66 и 88 88.88
void MainWindow::slot_test(int type, double value)
{
qDebug() << type << value;
}
// Выводит 99 99.99
void MainWindow::fun_test(int type, double value)
{
qDebug() << type << value;
}
Сигналы в Qt5 являются публичными и могут быть испускаемы напрямую там, где это необходимо, тогда как в Qt4 они были защищены и требовали определения публичной функции для их испускания.
Начиная с версии Qt 5.15, официальные дистрибутивы больше не включают установочные пакеты, предоставляя только исходный код. Можно скомпилировать самостоятельно или установить онлайн. Также можно скопировать установленные файлы на компьютер для использования в офлайн-режиме. Вероятно, компиляция различных версий каждый раз вызывает неудобства, и основной целью является сбор статистики об использовании пользователями, например, через онлайн-установку. Возможно, в будущем будет усилена коммерциализация.
Иногда нам нужно проверить, доступен ли определённый модуль в текущей версии Qt. Для этого мы можем использовать функцию qtHaveModule в Qt5 (введена для проверки наличия модулей). Если мы хотим проверить наличие модуля, добавленного через QT += в нашем проекте, мы можем использовать contains.
qtHaveModule(webenginewidgets) {
message("Текущий модуль Qt содержит webenginewidgets")
}
!qtHaveModule(webkit) {
message("Текущей версии Qt не найден модуль webkit")
}
contains(QT, network) {
message("В текущем проекте присутствует модуль network")
}
!contains(QT, widgets) {
message("Модуль widgets не добавлен в текущий проект")
}
QString s1 = R"(test\001.jpg)";
s1.replace("\\", "#");
qDebug()<< s1;
// Результат: test#001.jpg
При печати информации на Android рекомендуется использовать qInfo(), а не qDebug().
Точность таймера по умолчанию в Qt недостаточна высока (например, сохранение записи или файла каждые минуту, при использовании стандартного таймера может оказаться, что иногда запись сохраняется за 60 секунд, а иногда за 59 секунд. Если нужна более высокая точность, можно установить setTimerType(Qt::PreciseTimer). Qt предлагает два типа таймеров: QTimer и QObject с событием timeevent. Для QObject таймера нужно вызвать startTimer(interval, Qt::PreciseTimer):
Классы, связанные с QGraphicsEffect, потребляют много ресурсов CPU и могут вызывать конфликты при отрисовке, поэтому их использование не рекомендуется, за исключением редких случаев и мест, где отрисовка происходит нечасто.
Использование QSettings для работы с реестром может вызвать ошибку «QSettings: failed to set subkey „xxx“ (отказано в доступе)», если программа запущена не от имени администратора. Необходимо запустить программу от имени администратора вручную. ```cpp #include #include
bool checkPermission(const char *permission) { #ifdef Q_OS_ANDROID QtAndroid::PermissionResult result = QtAndroid::checkPermission(permission); if (result == QtAndroid::PermissionResult::Denied) { return false; } QFutureQtAndroidPrivate::PermissionResult resultFuture = QtAndroidPrivate::requestPermission(permission); if (resultFuture.resultAt(0) == QtAndroidPrivate::PermissionResult::Denied) { return false; } #else QFutureQtAndroidPrivate::PermissionResult result = QtAndroidPrivate::requestPermission(permission); if (result.resultAt(0) == QtAndroidPrivate::PermissionResult::Denied) { return false; } #endif return true; }
int main(int argc, char *argv[]) { QApplication a(argc, argv);
// 请求权限
checkPermission("android.permission.READ_EXTERNAL_STORAGE");
checkPermission("android.permission.WRITE_EXTERNAL_STORAGE");
return a.exec();
}
137. Qt重载qDebug输出自定义的信息。
```cpp
struct FunctionInfo {
QString function;
QString name;
QString groupEnabled;
QString action;
QString group;
friend QDebug operator << (QDebug debug, const FunctionInfo &functionInfo) {
QString info = QString("功能: %1 名称: %2 启用: %3 方法: %4 分组: %5")
.arg(functionInfo.function).arg(functionInfo.name).arg(functionInfo.groupEnabled)
.arg(functionInfo.action).arg(functionInfo.group);
debug << info;
return debug;
}
};
// Метод 1: в начале функции main добавьте следующую строку, начиная с версии 5.6
#if (QT_VERSION > QT_VERSION_CHECK(5,6,0))
QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
// После включения поддержки высокого масштабирования может потребоваться также включить следующее свойство
QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
#endif
// Метод 2: создайте файл qt.conf в каталоге исполняемого файла и добавьте следующий контент
[Platforms]
WindowsArguments = dpiawareness=0
// Следующая строка используется для решения проблемы отображения текста с «лесенкой» в Qt при высоком DPI
WindowsArguments = fontengine=freetype
// По состоянию на 2 февраля 2023 года, проверено JB (JB大佬), что разделение строк запятой не даёт эффекта, необходимо использовать запятую для разделения
WindowsArguments = dpiawareness=0, fontengine=freetype
// Метод 3: установите внутренние переменные окружения Qt в начале функции main
qputenv("QT_AUTO_SCREEN_SCALE_FACTOR", "1.5");
// Метод 4: новые версии Qt, такие как Qt5.14, исправили поддержку обработки экранов с высоким разрешением, не поддерживающих целочисленное масштабирование
qputenv("QT_ENABLE_HIGHDPI_SCALING", "1");
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
// Отключение масштабирования
// Тестирование показало, что AA_Use96Dpi работает нормально в версиях Qt начиная с 5.9, но в более ранних версиях, таких как 5.7, некоторые элементы управления могут некорректно работать при масштабировании 175%, например, QTextEdit. Чтобы исправить это, нужно обернуть их в виджет.
#if (QT_VERSION >= QT_VERSION_CHECK(5,0,0))
QApplication::setAttribute(Qt::AA_Use96Dpi);
#endif
#if (QT_VERSION >= QT_VERSION_CHECK(5,14,0))
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::Floor);
#endif
// В Qt6 AA_Use96Dpi не работает, необходимо установить принудительное масштабирование DPI следующим образом
qputenv("QT_FONT_DPI", "96");
// Устанавливаем отсутствие кнопок переключения для tabWidget
ui->tabWidget->setUsesScrollButtons(false);
// Устанавливаем отсутствие кнопок переключения для tabBar
ui->tabWidget->tabBar()->setUsesScrollButtons(false);
// Устанавливаем отсутствие кнопок переключения для всей системы вкладок
QTabBar{qproperty-usesScrollButtons:false;}
// Включаем автоматическое расширение вкладок (это было автоматически рассчитано ранее)
QTabBar{qproperty-expanding:false;}
// Делаем кнопки закрытия вкладок видимыми
QTabBar{qproperty-tabsClosable:true;}
// Другие свойства можно найти в заголовочном файле QTabBar, включая сюрпризы
// Этот метод позволяет установить любые свойства, определённые в Q_PROPERTY для всех визуальных классов
// Это действительно неожиданный способ настройки
QMainWindow::separator{width:1px;height:1px;margin:1px;padding:1px;background:#FF0000;}
static const char * const imgData[] = {
"15 11 6 1",
" c None",
"+ c #979797",
"@ c #C9C9C9",
"$ c #C1C1C1",
"b c None",
"d c None",
" $++++++++$ ",
"$+bbbbbbbb+$ ",
"+b $$ +$ ",
"+b $@ +$ ",
"+b +$",
"+b d+",
"+b d+$",
"+b $$ d+$ ",
"+b $@ d+$ ",
"$+dddddddd+$ ",
" $++++++++$ "};
// Теперь мы можем отобразить изображение со стрелкой напрямую
QImage img(imgData);
QLabel lab;
lab.setPixmap(QPixmap::fromImage(img));
lab.show();
int main(int argc, char *argv[]) {
// Нужно установить общий контекст, иначе при переключении из закреплённого состояния в плавающее окно QOpenGLWidget будет неработоспособно
#if (QT_VERSION >
``` ```cpp
// QT_VERSION_CHECK(5,4,0))
QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts);
#endif
QApplication a(argc, argv);
...
}
void QtHelper::setCode()
{
#if (QT_VERSION <= QT_VERSION_CHECK(5,0,0))
#if _MSC_VER
QTextCodec *codec = QTextCodec::codecForName("gbk");
#else
QTextCodec *codec = QTextCodec::codecForName("utf-8");
#endif
QTextCodec::setCodecForLocale(codec);
QTextCodec::setCodecForCStrings(codec);
QTextCodec::setCodecForTr(codec);
#else
QTextCodec *codec = QTextCodec::codecForName("utf-8");
QTextCodec::setCodecForLocale(codec);
#endif
}
//以前都是下面的方法
QFile file(":/qss/psblack.css");
if (file.open(QFile::ReadOnly)) {
QString qss = QLatin1String(file.readAll());
qApp->setStyleSheet(qss);
file.close();
}
//其实一行代码就行
qApp->setStyleSheet("file:///:/qss/psblack.css");
//特别说明,只支持qApp->setStyleSheet 不支持其他比如widget->setStyleSheet
//打印子类类名集合
void printObjectChild(const QObject *obj, int spaceCount)
{
qDebug() << QString("%1%2 : %3")
.arg("", spaceCount)
.arg(obj->metaObject()->className())
.arg(obj->objectName());
QObjectList childs = obj->children();
foreach (QObject *child, childs) {
printObjectChild(child, spaceCount + 2);
}
}
//拿到对话框进行设置和美化
QFileDialog *fileDialog = new QFileDialog(this);
fileDialog->setOption(QFileDialog::DontUseNativeDialog, true);
QLabel *lookinLabel = fileDialog->findChild<QLabel*>("lookInLabel");
lookinLabel->setText(QString::fromLocal8Bit("文件目录:"));
lookinLabel->setStyleSheet("color:red;");
//设置日期框默认值为空
QLineEdit *edit = ui->dateEdit->findChild<QLineEdit *>("qt_spinbox_lineedit");
if (!edit->text().isEmpty()) {
edit->clear();
}
QFileDialog *fileDialog = new QFileDialog(this);
//不设置此属性根本查找不到任何子元素,因为默认采用的系统对话框
fileDialog->setOption(QFileDialog::DontUseNativeDialog, true);
qDebug() << fileDialog->findChildren<QLabel *>();
//打印输出 QLabel(0x17e2ff68, name="lookInLabel"), QLabel(0x17e35f88, name="fileNameLabel"), QLabel(0x17e35e68, name="fileTypeLabel")
/**
* @brief $name$
* @param $param$
* @author feiyangqingyun
* @date $date$
*/
$ret$ $name$($param$)
{
$$
}
``` В эпоху Qt5, как говорят, произошли значительные улучшения в механизме работы сигналов и слотов.
- До Qt5 обычно писали connect так: connect(sender, SIGNAL(signalFunc()), receiver, SLOT(receiveFunc())). То есть при использовании connect нужно было оборачивать сигнал макросом SIGNAL, а функцию слота — макросом SLOT, чтобы механизм Moc от Qt мог их распознать.
- Даже если во время компиляции сигнала или слота не существовало или были неправильные параметры, ошибка не появлялась, но при выполнении возникала подсказка. Для статического языка вроде C++ это не очень удобно и мешает отладке.
- После Qt5 более рекомендуется использовать «запись с взятием адреса». При таком подходе, если при компиляции сигнал или слот не существует, компиляция не проходит, что служит своего рода проверкой на этапе компиляции и снижает вероятность ошибок.
- Если нет необходимости поддерживать совместимость с Qt4 из-за исторических проблем, лучше использовать новый подход. Он обеспечивает строгую проверку типов и поддерживает разнообразные способы записи.
- Для простых логических операций настоятельно рекомендуется использовать лямбда-выражения для непосредственной обработки.
```cpp
class MainWindow : public QMainWindow
{
Q_OBJECT
public: MainWindow(QWidget *parent = 0);
~MainWindow();
private:
Ui::MainWindow *ui;
private:
void test_fun();
private slots:
void test_slot();
};
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
// Ранний подход, работает со всеми версиями Qt, поддерживает только функции с ключевым словом slots
// connect(ui->pushButton, SIGNAL(clicked()), this, SLOT(test_fun()));
connect(ui->pushButton, SIGNAL(clicked()), this, SLOT(test_slot()));
// Новый подход, поддерживается в Qt5 и последующих версиях, работает с любыми функциями, не требует использования ключевого слова slots
connect(ui->pushButton, &QPushButton::clicked, this, &MainWindow::test_fun);
connect(ui->pushButton, &QPushButton::clicked, this, &MainWindow::test_slot);
// Альтернативный подход с использованием лямбда-выражений, позволяет выполнять код напрямую
connect(ui->pushButton, &QPushButton::clicked, [this] {test_fun();});
connect(ui->pushButton, &QPushButton::clicked, [this] {
qDebug() << "hello lambda";
});
// Лямбда с параметром
connect(ui->pushButton, &QPushButton::clicked, [&] (bool isCheck) {
qDebug() << "hello lambda" << isCheck;
});
// Заголовочный файл содержит signals:void sig_test(int i);
connect(this, &MainWindow::sig_test, [] (int i) {
qDebug() << "hello lambda" << i;
});
emit sig_test(5);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::test_fun()
{
qDebug() << "test_fun";
}
void MainWindow::test_slot()
{
qDebug() << "test_slot";
}
Подход «Боец»: содержимое qss пишется прямо в коде, где это необходимо. Различные элементы управления вызывают метод setStyleSheet для передачи содержимого таблицы стилей. Можно также изменить таблицу стилей через контекстное меню элемента управления.
Подход «Воин»: содержимое qss хранится в файле, который считывается для установки таблицы стилей. При публикации программы qss-файл включается в комплект.
Подход «Мастер»: qss-файл добавляется в файл ресурсов (qrc), компилируется непосредственно в исполняемый файл для предотвращения несанкционированных изменений.
Подход «Чародей»: в qss-файле определяются переменные, которые заменяются соответствующими значениями (например, цветами) при чтении файла. Это похоже на динамическую смену скинов.
Подход «Король»: хотя размещение таблицы стилей в отдельном файле упрощает изменение, оно также делает его уязвимым для несанкционированного доступа. Встраивание таблицы стилей в исполняемый файл ограничивает гибкость. Чтобы обновить таблицу стилей без перекомпиляции всего файла, используется команда rcc для компиляции ресурса в двоичный файл. Затем можно заменить этот двоичный файл для обновления стиля.
Подход «Император»: наследуя класс qstyle, можно реализовать все стили интерфейса и установить единый стиль для всей системы. Известная система UOS использует этот подход, запрещая использование таблиц стилей и выполняя всю отрисовку через painter.
// Использование двоичных файлов ресурсов в Qt
// Скомпилируйте qrc в двоичный файл rcc с помощью команды в консоли
rcc -binary main.qrc -o main.rcc
// Зарегистрируйте ресурс в приложении, обычно после запуска main
QResource::registerResource(qApp->applicationDirPath() + "/main.rcc");
// Предположим, что в окне есть дочерние элементы управления, и по умолчанию шрифт имеет размер 12 пикселей. Родительский класс — QWidget, имя родительского класса — Widget
// Эти методы установят шрифт только для главного окна, дочерние элементы не будут применять шрифт, требуется индивидуальный вызов setFont
QFont font;
font.setPixelSize(20);
this->setFont(font);
this->setStyleSheet("{font:26px;}");
this->setStyleSheet("QWidget{font:26px;}");
this->setStyleSheet("Widget{font:26px;}");
// Вот как правильно установить шрифт для всего элемента управления и его дочерних элементов с помощью таблицы стилей
this->setStyleSheet("font:26px;");
this->setStyleSheet("*{font:26px;}");
this->setStyleSheet("QWidget>*{font:26px;}");
this->setStyleSheet("Widget>*{font:26px;}");
// Глобальная установка шрифта
qApp->setFont(font);
// Функции pixel и setPixel доступны в любой версии Qt
// Функции pixelColor и setPixelColor доступны начиная с Qt 5.6
// Функция pixel возвращает значение цвета в формате QRgb, которое нужно преобразовать с помощью qRed, qGreen, qBlue, qAlpha
QImage image("1.png");
image = image.convertToFormat(QImage::Format_ARGB32);
int width = image.width();
int height = image.height();
// Перебираем каждый пиксель изображения
for (int x = 0; x < width; ++x) {
for (int y = 0; y < height; ++y) {
QString name = image.pixelColor(x, y).name();
// Заменяем все цвета, кроме белого, на красный
if (name != "#ffffff") {
image.setPixelColor(x, y, Qt::red);
}
}
}
// Сохраняем файл
image.save("2.png");
``` В приложениях, связанных с базами данных, если речь идёт только о версии для одного компьютера и нет особых требований (например, по указанию руководства или необходимости удалённого хранения данных), настоятельно рекомендуется использовать базу данных sqlite. Это вывод, полученный автором в результате бесчисленных сравнительных тестов и применения в N коммерческих проектах.
- Qt изначально имеет встроенную поддержку базы данных sqlite, нужно только включить плагин при публикации приложения (можно заметить, что файл динамической библиотеки плагина больше, чем у других типов баз данных, потому что исходный код базы данных скомпилирован напрямую, в то время как другие компилируют только исходный код плагина для промежуточного коммуникационного взаимодействия);
- скорость работы не имеет себе равных: при одинаковой структуре базы данных (структура таблиц, индексы и т. д.) скорость запросов, пакетных обновлений, транзакций базы данных и т.д. как минимум в три раза выше, чем у других систем, и этот разрыв становится всё более заметным с увеличением объёма данных;
- работа с данными объёмом в несколько десятков миллионов не представляет проблемы, при этом скорость и производительность остаются на приемлемом уровне. Не стоит верить слухам в интернете о том, что sqlite не поддерживает данные объёмом более десяти миллионов. Автор лично проверил работу с данными порядка миллиарда, но рекомендует использовать sqlite для данных объёмом до десятков миллионов, уделяя особое внимание проектированию таблиц базы данных и индексов;
- для других баз данных также следует учитывать различия версий, а формат источника данных ODBC может легко привести к ошибкам и сбоям выполнения;
- у базы данных sqlite есть несколько серьёзных недостатков: отсутствие поддержки шифрования, сетевого доступа, некоторых функций продвинутых баз данных и работы с огромными объёмами данных (порядка миллиардов), но для большинства проектов на Qt этого достаточно;
- удобство поддержки баз данных примерно следующее: sqlite > postgresql > mysql > odbc;
- если используется режим соединения с базой данных через источник данных ODBC, достаточно установить имя базы данных, соответствующее имени нового источника данных, затем настроить имя пользователя и пароль, установка хоста и порта не требуется, поскольку эти параметры уже настроены в источнике данных, здесь нужно только подтвердить информацию пользователя;
- источник данных ODBC бывает 32-битным и 64-битным. В диспетчере источников данных, если добавленный источник данных отображается как соответствующий только 32-битной или 64-битной платформе, ваше приложение Qt должно быть скомпилировано для соответствующей битовой версии, чтобы успешно подключиться. Если отображается 64-разрядная версия, подключение с использованием 32-разрядного приложения не удастся.
- 32-разрядное приложение Qt с соответствующей 32-разрядной динамической библиотекой libmysql может подключаться к 32/64-разрядному серверу MySQL, 64-разрядное приложение также может подключаться к 32/64-разрядному MySQL, просто необходимо предоставить соответствующую версию динамической библиотеки. Чтобы узнать, является ли MySQL 32-разрядным или 64-разрядным, используйте команду mysql.exe -V.
- Если драйвер не загружен (Driver not loaded) при наличии доступного и работоспособного драйвера MySQL, это, скорее всего, связано с неправильной версией скопированной динамической библиотеки libmysql или несоответствием битности.
Все эти выводы были сделаны автором в среде Qt, результаты могут быть неточными и предоставляются только для справки. Другие среды программирования, такие как C# и JAVA, не рассматриваются, так как различия могут быть связаны с эффективностью промежуточного коммуникационного процесса.
155. Qt 5.10 и более поздние версии предоставляют новые классы QRandomGenerator и QRandomGenerator64 для управления случайными числами, которые удобнее использовать, особенно для генерации случайных чисел в заданном диапазоне.
```cpp
// Ранний метод обработки: сначала инициализируйте семя случайного числа, затем получите случайное число
qsrand(QTime::currentTime().msec());
// Получить случайное число от 0 до 10
qrand() % 10;
// Получить случайное вещественное число от 0 до 1
qrand() / double(RAND_MAX);
// Новый метод обработки: поддерживается всеми версиями начиная с 5.10, включая qt6
QRandomGenerator::global()->bounded(10); // Генерирует целое число от 0 до 10
QRandomGenerator::global()->bounded(10.123); // Генерирует вещественное число от 0 до 10.123
QRandomGenerator::global()->bounded(10, 15); // Генерирует целое число от 10 до 15
// Метод, совместимый с версиями qt4-qt6 и более поздними: используйте стандартные функции случайных чисел C++
srand(QTime::currentTime().msec());
rand() % 10;
rand() / double(RAND_MAX);
// Общая формула: a — начальное значение, n — диапазон целых чисел
int value = a + rand() % n;
// Случайное число в диапазоне (min, max)
int value = min + 1 + (rand() % (max - min - 1));
// Случайное число в диапазоне (min, max]
int value = min + 1 + (rand() % (max - min + 0));
// Случайное число в диапазоне [min, max)
int value = min + 0 + (rand() % (max - min + 0));
// Случайное число в диапазоне [min, max]
int value = min + 0 + (rand() % (max - min + 1));
// Если вы генерируете случайные числа в потоке, время запуска потоков почти одинаково,
// возможно получение одинаковых случайных чисел. Даже установка случайного числа на текущее время не поможет,
// компьютер работает слишком быстро, вероятно, время будет одинаковым даже в пределах одной миллисекунды.
// Хитрость заключается в использовании идентификатора текущего потока в качестве начального значения перед функцией run.
// Время ненадёжно, идентификатор потока уникален.
// Помните, что преобразование void * в числовое значение должно выполняться с помощью long long,
// в 32-битном режиме можно использовать int, но в 64-битном — long, убедитесь, что используете quint64 напрямую.
srand((long long)currentThreadId());
qrand((long long)currentThreadId());
void frmMain::on_btnMenu_Max_clicked()
{
......
// После максимизации возникает ошибка: стиль при наведении не удаляется,
// требуется симуляция перемещения мыши
QEvent event(QEvent::Leave);
QApplication::sendEvent(ui->btnMenu_Max, &event);
}
greaterThan(QT_MAJOR_VERSION, 4): CONFIG += c++11
lessThan(QT_MAJOR_VERSION, 5): QMAKE_CXXFLAGS += -std=c++11
ui->textEdit->setUndoRedoEnabled(false);
// Способ 1: заполнение строк пробелами
ui->tabWidget->addTab(httpClient1, "Тест");
ui->tabWidget->addTab(httpClient1, "Управление персоналом");
ui->tabWidget->addTab(httpClient1, "Настройки системы");
// Способ 2: автоматическое определение минимальной ширины при изменении размера
void MainWindow::resizeEvent(QResizeEvent *e)
{
int count = ui->tabWidget->tabBar()->count();
int width = this->width() - 30;
QString qss = QString("QTabBar::tab{min-width:%1px;}").arg(width / count);
this->setStyleSheet(qss);
}
// Способ 3: установка глобального стиля, разная ширина для разного количества вкладок
QStringList list;
list << QString("QTabWidget[tabCount=\"2\"]>QTabBar::tab{min-width:%1px;}").arg(100);
list << QString("QTabWidget[tabCount=\"3\"]>QTabBar::tab{min-width:%1px;}").arg(70);
qApp->setStyleSheet(list.join(""));
// Установка свойства tabCount для автоматического определения ширины
ui->tabWidget->setProperty("tabCount", 2);
ui->tabWidget->setProperty("tabCount", 3);
// Способ 4: настоятельно рекомендуется — использование встроенного метода setExpanding
``` ```
//打印通信用的本地绑定地址和端口
qDebug() << socket->localAddress() << socket->localPort();
//打印通信服务器对方的地址和端口
qDebug() << socket->peerAddress() << socket->peerPort() << socket->peerName();
//udp客户端
QUdpSocket *socket = new QUdpSocket(this);
//绑定网卡和端口,没有绑定过才需要绑定
//采用端口是否一样来判断是为了方便可以直接动态绑定切换端口
if (socket->localPort() != 6005) {
socket->abort();
socket->bind(QHostAddress("192.168.1.2"), 6005);
}
//指定地址和端口发送数据
socket->writeDatagram(buffer, QHostAddress("192.168.1.3"), 6000);
//上面是Qt5可以使用bind,Qt4中的QTcpSocket的对应接口是protected的没法直接使用,需要继承类重新实现把接口放出来。
//Qt4中的QUdpSocket有bind函数是开放的,奇怪了,为何Qt4中独独QTcpSocket不开放。
TcpSocket *socket = new TcpSocket(this);
socket->setLocalAddress(QHostAddress("192.168.1.2"));
socket->setLocalPort(6005);
//Поменять местами оси XY
QCPAxis *yAxis = customPlot->yAxis;
QCPAxis *xAxis = customPlot->xAxis;
customPlot->xAxis = yAxis;
customPlot->yAxis = xAxis;
//Удалить легенду
customPlot->legend->removeItem(1);
//Объединить две кривые в замкнутую область
customPlot->graph(0)->setChannelFillGraph(customPlot->graph(1));
//Отключить сглаживание и сглаживание при перетаскивании
customPlot->setNoAntialiasingOnDrag(true);
customPlot->graph()->setAntialiased(false);
customPlot->graph()->setAntialiasedFill(false);
customPlot->graph()->setAntialiasedScatters(false);
//Установить быструю отрисовку для значительного ускорения рисования линий шириной более 1 пикселя
customPlot->setPlottingHint(QCP::phFastPolylines);
//Различные методы установки данных
customPlot->graph(0)->setData();
customPlot->graph(0)->data()->set();
//Установка различных стилей линий и данных
customPlot->graph()->setLineStyle(QCPGraph::lsLine);
customPlot->graph()->setScatterStyle(QCPScatterStyle::ssDot);
customPlot->graph()->setScatterStyle(QCPScatterStyle(shapes.at(i), 10));
//Можно также установить изображение или пользовательскую форму
customPlot->graph()->setScatterStyle(QCPScatterStyle(QPixmap("./sun.png")));
QPainterPath customScatterPath;
for (int i = 0; i < 3; ++i) {
customScatterPath.cubicTo(qCos(2 * M_PI * i / 3.0) * 9, qSin(2 * M_PI * i / 3.0) * 9, qCos(2 * M_PI * (i + 0.9) / 3.0) * 9, qSin(2 * M_PI * (i + 0.9) / 3.0) * 9, 0, 0);
}
customPlot->graph()->setScatterStyle(QCPScatterStyle(customScatterPath, QPen(Qt::black, 0), QColor(40, 70, 255, 50), 10));
//Изменить стиль стрелок на оси координат
customPlot->xAxis->setUpperEnding(QCPLineEnding::esSpikeArrow);
customPlot->yAxis->setUpperEnding(QCPLineEnding::esSpikeArrow);
//Установить фоновое изображение
customPlot->axisRect()->setBackground(QPixmap("./solarpanels.jpg"));
//На холсте также можно установить фоновое изображение
customPlot->graph(0)->setBrush(QBrush(QPixmap("./balboa.jpg")));
//Для всего элемента управления можно задать цвет заливки или изображение
customPlot->setBackground(QBrush(gradient));
//Установить цвет линии нулевой точки
customPlot->xAxis->grid()->setZeroLinePen(Qt::NoPen);
//Управление интерактивными возможностями, такими как масштабирование и выбор объектов
customPlot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectPlottables);
//Сгруппированный график
QCPBarsGroup *group = new QCPBarsGroup(customPlot);
QList<QCPBars*> bars;
bars << fossil << nuclear << regen;
foreach (QCPBars *bar, bars) {
//Установить ширину столбцов
bar->setWidth(bar->width() / bars.size());
group->append(bar);
}
//Установить интервал между группами
group->setSpacing(2);
//Рисование линий в обратном направлении
QVector<double> keys, values;
keys << 0 << 1 << 2 << 3 << 4 << 5 << 4 << 3;
values << 5 << 4 << 6 << 7 << 7 << 6 << 5 << 4;
customPlot->graph(0)->setData(keys, values, true);
//Включить построение в очереди для повышения производительности
customPlot->replot(QCustomPlot::rpQueuedReplot);
QCPAxis *axis = customPlot->xAxis; ```cpp
double lower = axis->range().lower;
double upper = axis->range().upper;
double origin = (upper - lower) / 2;
//设置刻度线按照设置优先而不是可读性优先
axis->ticker()->setTickStepStrategy(QCPAxisTicker::tssMeetTickCount);
//设置原点值为范围值的中心点
axis->ticker()->setTickOrigin(origin);
//下面演示如何在一个控件中多个不同的曲线对应不同坐标轴
//拿到图表布局对象
QCPLayoutGrid *layout = customPlot->plotLayout();
//实例化坐标轴区域
QCPAxisRect *axisRect = new QCPAxisRect(customPlot);
//拿到XY坐标轴对象
QCPAxis *xAxis = axisRect->axis(QCPAxis::atBottom);
QCPAxis *yAxis = axisRect->axis(QCPAxis::atLeft);
//将坐标轴指定行列位置添加到布局中
layout->addElement(i, 0, axisRect);
//添加对应的画布到指定坐标轴
QCPGraph *graph = customPlot->addGraph(xAxis, yAxis);
QString fileName = "c:/测试目录/1.txt";
//如果应用程序main函数中没有设置编码则默认采用系统的编码,可以直接通过toLocal8Bit转成正确的数据
const char *name = fileName.toLocal8Bit().constData();
//如果设置过了下面两句则需要主动转码
QTextCodec *codec = QTextCodec::codecForName("utf-8");
QTextCodec::setCodecForLocale(codec);
QTextCodec *code = QTextCodec::codecForName("gbk");
const char *name = code->fromUnicode(fileName).constData();
//推荐方式2以防万一保证绝对的正确,哪怕是设置过主程序的编码
//切记一旦设置过QTextCodec::setCodecForLocale会影响toLocal8Bit
//有时候可能还有下面这种情况
#ifdef Q_OS_WIN
#if defined(_MSC_VER) && (_MSC_VER >= 1400)
QTextCodec *code = QTextCodec::codecForName("utf-8");
#else
QTextCodec *code = QTextCodec::codecForName("gbk");
#endif
const char *name = code->fromUnicode(fileName).constData();
#else
const char *name = fileName.toUtf8().constData();
#endif
QString url = "file:///c:/1.html";
//浏览器控件打开本地网页文件
webView->setUrl(QUrl(url));
//打开本地网页文件,下面两种方法都可以
QDesktopServices::openUrl(QUrl::fromLocalFile(url));
QDesktopServices::openUrl(QUrl(url, QUrl::TolerantMode));
//局部的事件循环,不卡主界面
QEventLoop eventLoop;
//设置超时 5.15开始自带了超时时间函数 默认30秒
#if (QT_VERSION >= QT_VERSION_CHECK(5,15,0))
manager->setTransferTimeout(timeout);
#else
QTimer timer;
connect(&timer, SIGNAL(timeout()), &eventLoop, SLOT(quit()));
timer.setSingleShot(true);
timer.start(timeout);
#endif
QNetworkReply *reply = manager->get(QNetworkRequest(QUrl(url)));
connect(reply, SIGNAL(finished()), &eventLoop, SLOT(quit()));
eventLoop.exec();
if (reply->bytesAvailable() > 0 && reply->error() == QNetworkReply::NoError) {
//读取所有数据保存成文件
QByteArray data = reply->readAll();
QFile file(dirName + fileName);
if (file.open(QFile::WriteOnly | QFile::Truncate)) {
file.write(data);
file.close();
}
}
//如果是控制台程序则下面的QApplication换成QCoreApplication
//如果是quick/qml程序则下面的QApplication换成QGuiApplication
int main(int argc, char *argv[])
{
//можно использовать эту строку для тестирования встроенной виртуальной клавиатуры Qt
qputenv("QT_IM_MODULE", QByteArray("qtvirtualkeyboard"));
//设置不应用操作系统设置比如字体
QApplication::setDesktopSettingsAware(false);
#if (QT_VERSION >= QT_VERSION_CHECK(6,0,0))
//设置高分屏缩放舍入策略
QApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::Floor);
#endif
#if (QT_VERSION > QT_VERSION_CHECK(5,6,0))
//设置启用高分屏缩放支持
//要注意开启后计算到的控件或界面宽度高度可能都不对,全部需要用缩放比例运算下
QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
//设置启用高分屏图片支持
``` ```
locale = QLocale::Japanese;
//下面永远输出中文的周一到周日
locale.toString(QDateTime::currentDateTime(), "ddd");
//下面永远输出中文的星期一到星期日
locale.toString(QDateTime::currentDateTime(), "dddd");
// Создаем экземпляр модели таблицы базы данных
QSqlTableModel *model = new QSqlTableModel(this);
// Указываем имя таблицы
model->setTable("table");
// Устанавливаем сортировку столбцов
model->setSort(0, Qt::AscendingOrder);
// Устанавливаем режим редактирования
model->setEditStrategy(QSqlTableModel::OnManualSubmit);
// Выполняем запрос немедленно
model->select();
// Привязываем модель таблицы базы данных к таблице
ui->tableView->setModel(model);
// Тестирование показало, что в условиях фильтрации можно использовать не только предложение where, но и сортировку и ограничение
model->setFilter("1=1 order by id desc limit 100");
// Если в условиях фильтрации используется предложение сортировки, то метод setSort использовать нельзя
// Следующий код приведет к ошибке, возможно, из-за повторного добавления предложения order by в setSort
model->setSort(0, Qt::AscendingOrder);
model->setFilter("1=1 order by id desc limit 100");
// С помощью setFilter можно установить простое предложение where без необходимости добавлять 1=1
model->setFilter("name='张三'");
// Если есть другие предложения, такие как сортировка или ограничение, необходимо добавить 1=1 в начале
// Это означает сортировку по идентификатору в порядке возрастания и отображение записей с 5 по 15
model->setFilter("1=1 and name='张三' order by id asc limit 5,10");
// Несколько условий соединяются с помощью and
// Рекомендуется писать 1=1 перед любыми условиями фильтрации и заканчивать их символом ; для предотвращения ошибок
model->setFilter("1=1 and name='张三' and result>=70;");
// Это представляет собой запрос на выборку записей, где имя равно '张三', отсортированных по идентификатору в обратном порядке, начиная с 10-й записи до 110
model->setFilter("1=1 and name='张三' order by id desc limit 10,100;");
// Начинаем добавлять запись с третьей строки
model->insertRow(2);
// Заполняем только что добавленную строку, по умолчанию она пуста, и пользователь должен ввести данные вручную в таблицу
model->setData(model->index(2, 0), 100);
model->setData(model->index(2, 1), "张三");
// Подтверждаем изменения
model->submitAll();
// Удаляем четвертую строку
model->removeRow(3);
model->submitAll();
// В общем, после операций вставки, удаления или изменения необходимо вызвать model->submitAll() для фактического выполнения, иначе данные будут обновлены только в модели данных, а не в базе данных
// Отменяем изменения
model->revertAll();
Команда | Функция |
---|---|
sudo -s | Переключение на администратора, sudo -i изменяет текущий каталог |
apt install g++ | Установка программного пакета (требуется разрешение администратора), альтернатива — yum install |
cd /home | Переход в домашний каталог |
ls | Список всех каталогов и файлов в текущем каталоге |
ifconfig | Просмотр информации о сетевом интерфейсе, включая IP-адрес (в Windows — ipconfig) |
tar -zxvf bin.tar.gz | Распаковка файла в текущий каталог |
tar -jxvf bin.tar.xz | Распаковка файла в текущий каталог |
tar -zxvf bin.tar.gz -C /home | Распаковка файла в каталог /home (помните, что C пишется заглавными буквами) |
tar -zcvf bin.tar.gz bin | Сжатие каталога bin в файл tar.gz (сжатие обычно) |
tar -jcvf bin.tar.xz bin | Сжатие каталога bin в файл tar.xz (высокое сжатие, рекомендуется) |
tar -... | j z обозначает разные методы сжатия, x — распаковка, c — сжатие |
gedit 1.txt | Открытие текстового файла с помощью блокнота |
vim 1.txt | Открытие файла с помощью vim, часто сокращается до vi |
./configure make -j4 make install | Общая команда компиляции исходного кода, первый шаг — выполнение скрипта конфигурации, второй шаг — многопоточная компиляция, третий шаг — установка скомпилированных файлов |
./configure -prefix /home/liu/Qt-5.9.3-static -static -sql-sqlite -qt-zlib -qt-xcb -qt-libpng -qt-libjpeg -fontconfig -system-freetype -iconv -nomake tests -nomake examples -skip qt3d -skip qtdoc | Общая команда компиляции Qt |
./configure -static -release -fontconfig -system-freetype -qt-xcb -qt-sql-sqlite -qt-zlib -qt-libpng -qt-libjpeg -nomake tests -nomake examples -prefix /home/liu/qt/Qt5.6.3 | Компиляция Qt со статическими библиотеками и поддержкой китайского языка |
./configure -prefix /home/liu/Qt-5.9.3-static -static -release -nomake examples -nomake tests -skip qt3d | Упрощенная команда компиляции |
./configure --prefix=host --enable-static --disable-shared --disable-doc | Команда компиляции ffmpeg |
// Qt5 начал предоставлять контекстную информацию для вывода сообщений журнала, такую как текущий файл кода, номер строки и имя функции, связанные с напечатанным сообщением.
// Для вывода контекста в режиме release необходимо добавить DEFINES += QT_MESSAGELOGCONTEXT в pro-файл, который по умолчанию отключен в release.
// Помните, что не следует использовать qdebug и подобные команды в функциях логгирования, так как это приведет к бесконечной рекурсии.
// Обычно существует три способа перенаправления журнала:
// 1: Запись в файл журнала, такой как txt.
// 2: Сохранение в базу данных для последующей классификации и анализа.
// 3: Перенаправление в сеть, чтобы все печатные сообщения передавались через tcp при подключении к программе с помощью небольшого инструмента.
// Перенаправление журнала
#if (QT_VERSION >= QT_VERSION_CHECK(5,0,0))
void Log(QtMsgType type, const QMessageLogContext &context, const QString &msg)
#else
void Log(QtMsgType type, const char *msg)
#endif
{
// Добавляем блокировку, чтобы предотвратить сбой из-за слишком частых вызовов qdebug в многопоточном режиме
static QMutex mutex;
QMutexLocker locker(&mutex);
QString content;
// Здесь можно добавить разные заголовки в зависимости от типа
switch (type) {
case QtDebugMsg:
content = QString("%1").arg(msg);
break;
case QtWarningMsg:
content = QString("%1").arg(msg);
break;
case QtCriticalMsg:
content = QString("%1").arg(msg);
break;
case QtFatalMsg:
content = QString("%1").arg(msg);
break;
}
// Добавляем информацию о файле кода, номере строки и имени функции
#if (QT_VERSION >= QT_VERSION_CHECK(5,0,0)) ```cpp
//将内容传给函数进行处理
SaveLog::Instance()->save(content);
}
//安装日志钩子,输出调试信息到文件,便于调ровки
void SaveLog::start()
{
#if (QT_VERSION >= QT_VERSION_CHECK(5,0,0))
qInstallMessageHandler(Log);
#else
qInstallMsgHandler(Log);
#endif
}
//卸载日志钩子
void SaveLog::stop()
{
#if (QT_VERSION >= QT_VERSION_CHECK(5,0,0))
qInstallMessageHandler(0);
#else
qInstallMsgHandler(0);
#endif
}
Список захвата имеет следующие формы:
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
//按钮单击不带参数
connect(ui->pushButton, &QPushButton::clicked, [] {
qDebug() << "hello lambda";
});
//按钮单击带参数
connect(ui->pushButton, &QPushButton::clicked, [] (bool isCheck) {
qDebug() << "hello lambda" << isCheck;
});
//自定义信号带参数
connect(this, &MainWindow::sig_test, [] (int i, int j) {
qDebug() << "hello lambda" << i << j;
});
emit sig_test(5, 8);
}
//по крайней мере нужно включить qglobal.h, теоретически все классы Qt включают этот заголовочный файл, так что вы можете включить другие заголовки Qt, например, qobject.h
#include "qglobal.h"
#if (QT_VERSION >= QT_VERSION_CHECK(5,0,0))
#include "qscreen.h"
#else
#include "qdesktopwidget.h"
#endif
QString text = "xxxxx";
//при таком преобразовании могут возникнуть проблемы
char *data = text.toUtf8().data();
//преобразование в два этапа точно не вызовет проблем
QByteArray buffer = text.toUtf8();
char *data = buffer.data();
const char *data = buffer.constData();
void test(const char *text) {}
//разделение на два этапа гарантирует отсутствие проблем
QByteArray buffer = QString("xxx").toUtf8();
const char *text = buffer.constData();
test(text);
//можно напрямую передать в качестве аргумента
test(QString("xxx").toUtf8().constData());
Свойство mouseTracking используется для сохранения состояния включения отслеживания мыши. По умолчанию отслеживание мыши отключено.
Если отслеживание мыши отключено, соответствующий компонент будет получать только события перемещения мыши, когда нажата хотя бы одна кнопка мыши.
При включённом отслеживании мыши любой компонент будет получать события перемещения мыши. Метод hasMouseTracking()
используется для возврата информации о том, включена ли в данный момент функция отслеживания мыши.
Метод setMouseTracking(bool enable)
служит для установки состояния функции отслеживания мыши (включена или выключена).
Основная функция, связанная с отслеживанием мыши — mouseMoveEvent()
.
Свойство tabletTracking
сохраняет информацию о том, включено ли отслеживание планшета для компонента. По умолчанию это свойство не используется.
Если отслеживание планшета отключено, компонент будет получать события перемещения пера только при касании экрана пером или при нажатии хотя бы одной кнопки на пере.
При включённом отслеживании планшета компонент может получать события перемещения пера, когда перо находится рядом с экраном, но ещё не касается его.
Это можно использовать для отслеживания позиции пера и вспомогательных функций компонентов (таких как вращение и наклон), а также для предоставления информации об этих операциях графическому интерфейсу.
Метод hasTabletTracking()
возвращает информацию о текущем состоянии отслеживания планшета.
Метод setTabletTracking(bool enable)
используется для включения или выключения отслеживания планшета.
Основной функцией, связанной с отслеживанием планшета, является tabletEvent()
.
QTableWidget
, при вызове встроенных функций removeRow
, clearContents
и clear
, которые удаляют элементы и содержимое внутри, автоматически вызываются деструкторы item
или cellwidget
для освобождения ресурсов, поэтому нет необходимости освобождать их вручную.// Каждый раз, когда вы вызываете clearContents, предыдущие item будут автоматически очищены
ui->tableWidget->clearContents();
for (int i = 0; i < count; ++i) {
ui->tableWidget->setItem(i, 0, new QTableWidgetItem("aaa"));
ui->tableWidget->setItem(i, 1, new QTableWidgetItem("bbb"));
ui->tableWidget->setCellWidget(i, 2, new QPushButton("ccc"));
}
QListView
(QListWidget
), QTreeView
(QTreeWidget
) и QTableView
(QTableWidget
), можно использовать setChecked
, чтобы соответствующие элементы отображали эффект флажка. Многие люди (включая меня в прошлом) ошибочно полагают, что это элементы управления флажками. На самом деле это индикаторы соответствующих элементов управления. Поэтому, если вы хотите изменить стиль, нельзя просто установить стиль QCheckBox
и ожидать, что он сработает. Вместо этого вам нужно отдельно настроить стиль индикатора.QCheckBox::indicator,QGroupBox::indicator,QTreeWidget::indicator,QListWidget::indicator{
width:13px;
height:13px;
}
QCheckBox::indicator:unchecked,QGroupBox::indicator:unchecked,QTreeWidget::indicator:unchecked,QListWidget::indicator:unchecked{
image:url(:/qss/flatwhite/checkbox_unchecked.png);
}
...
QTableView
(использующего модель данных) и настройки ширины столбцов и названий столбцов в QTableWidget
, иногда обнаруживается, что они не работают. Оказывается, порядок установки кода имеет значение. Например, перед установкой ширины столбца необходимо сначала установить количество столбцов (setColumnWidth
), иначе столбцов не будет, и ширина столбца не будет иметь смысла. То же самое относится и к установке заголовков столбцов (setHorizontalHeaderLabels
). Сначала должно быть указано количество столбцов.void frmSimple::initForm()
{
// Создание экземпляра модели данных
model = new QStandardItemModel(this);
// Установка количества строк и столбцов
row = 100;
column = 10;
// Настройка названий столбцов и ширины столбцов
for (int i = 0; i < column; ++i) {
columnNames << QString("Столбец%1").arg(i + 1);
columnWidths << 60;
}
}
void frmSimple::on_btnLoad1_clicked()
{
// Сначала устанавливаем модель данных, иначе setColumnWidth не работает
ui->tableView->setModel(model);
// Устанавливаем количество столбцов, заголовки столбцов и ширину столбцов
model->setColumnCount(column);
// Простой способ установки заголовков столбцов
model->setHorizontalHeaderLabels(columnNames);
for (int i = 0; i < column; ++i) {
ui->tableView->setColumnWidth(i, columnWidths.at(i));
}
// Цикл добавления данных строк
QDateTime now = QDateTime::currentDateTime();
model->setRowCount(row);
for (int i = 0; i < row; ++i) {
for (int j = 0; j < column; ++j) {
QStandardItem *item = new QStandardItem;
// В последнем столбце отображается разница во времени
if (j == column - 1) {
item->setText(now.addSecs(i).toString("yyyy-MM-dd HH:mm:ss"));
} else {
item->setText(QString("%1_%2").arg(i + 1).arg(j + 1));
}
model->setItem(i, j, item);
}
}
}
void frmSimple::on_btnLoad2_clicked()
{
// Установка заголовков столбцов, количества столбцов и ширины столбцов
ui->tableWidget->setColumnCount(column);
// Простой способ установки заголовков столбцов
ui->tableWidget->setHorizontalHeaderLabels(columnNames);
for (int i = 0; i < column; ++i) {
ui->tableWidget->setColumnWidth(i, columnWidths.at(i));
}
// Добавление данных
QDateTime now =
``` ```
QDateTime::currentDateTime();
ui->tableWidget->setRowCount(row);
for (int i = 0; i < row; ++i) {
for (int j = 0; j < column; ++j) {
QTableWidgetItem *item = new QTableWidgetItem;
//最后一列显示时间区别开来
if (j == column - 1) {
item->setText(now.addSecs(i).toString("yyyy-MM-dd HH:mm:ss"));
} else {
item->setText(QString("%1_%2").arg(i + 1).arg(j + 1));
}
ui->tableWidget->setItem(i, j, item);
}
}
}
QStringList list;
list << "aaa" << "bbb" << "ccc";
// Добавление элемента в конец списка эквивалентно append
list.push_back("ddd");
// Добавление элемента в начало списка эквивалентно prepend
list.push_front("xxx");
// Добавление элемента в конец
list.append("ddd");
// Добавление элемента в начало
list.prepend("xxx");
// Вставка элемента на первую позицию эквивалентно prepend
list.insert(0, "xxx");
// Вывод QList("xxx", "aaa", "bbb", "ccc", "ddd")
qDebug() << list;
//qwindowdefs.h
typedef QList<QWidget *> QWidgetList;
typedef QList<QWindow *> QWindowList;
typedef QHash<WId, QWidget *> QWidgetMapper;
typedef QSet<QWidget *> QWidgetSet;
//qmetatype.h
typedef QList<QVariant> QVariantList;
typedef QMap<QString, QVariant> QVariantMap;
typedef QHash<QString, QVariant> QVariantHash;
typedef QList<QByteArray> QByteArrayList;
Поля и отступы в макетах Qt определяются автоматически в зависимости от разрешения экрана и коэффициента масштабирования системы. Например, при разрешении 1080P поля составляют 9 пикселей, а при 2K — 11 пикселей. Если вы скомпилировали программу на компьютере с разрешением 1080P и видите поля размером 6 или 9 пикселей, то при переходе на 2K или 4K разрешение они станут больше. Чтобы сохранить одинаковый вид приложения на всех разрешениях, необходимо вручную настроить эти значения. Здесь есть нюанс: если вы хотите оставить значение 9 для всех разрешений, нужно сначала изменить его на другое число, например, 10, а затем вернуть обратно к 9. Только так система поймёт, что вы действительно внесли изменения. В дизайнере свойств справа от значения есть маленькая серая стрелка для восстановления значений по умолчанию.
Поддержка высоких разрешений и масштабирования DPI в Qt становится всё более зрелой. Начиная с версии Qt5.6, можно включить поддержку высоких разрешений с помощью свойства AA_EnableHighDpiScaling. С версии 5.14 появилась возможность задавать стратегии масштабирования, такие как поддержка масштабирования с использованием дробных коэффициентов вместо целых чисел. В версии Qt6 это свойство включено по умолчанию и не может быть отключено. Часто нам нужны два режима: один, где высокие разрешения и масштабирование никогда не применяются, и другой, где они применяются автоматически.
// Режим, в котором высокие разрешения и масштабирование никогда не применяются
int main(int argc, char *argv[])
{
#if (QT_VERSION >= QT_VERSION_CHECK(5,0,0))
QApplication::setAttribute(Qt::AA_Use96Dpi);
#endif
#if (QT_VERSION >= QT_VERSION_CHECK(5,14,0))
QApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::Floor);
#endif
QApplication a(argc, argv);
....
return a.exec();
}
// Автоматический режим применения высоких разрешений и масштабирования
// Существует множество методов, но наиболее подходящим является использование файла конфигурации для задания стратегии масштабирования.
// Создайте файл qt.conf в том же каталоге, что и исполняемый файл
[Platforms]
WindowsArguments = dpiawareness=0
// Иногда требуется позволить пользователю выбирать стратегию. После включения поддержки высоких разрешений просто поместите файл qt.conf в тот же каталог, что и исполняемый файл. Даже если в коде установлено отключение поддержки высоких разрешений, это не повлияет. Стратегия из файла qt.conf будет иметь приоритет.
void QtHelperCore::sleep(int msec)
{
if (msec <= 0) {
return;
}
#if 1
// Неблокирующий метод задержки, рекомендуемый многими
QEventLoop loop;
QTimer::singleShot(msec, &loop, SLOT(quit()));
loop.exec();
#else
#if (QT_VERSION >= QT_VERSION_CHECK(5,0,0))
// Блокирующий метод задержки, может заморозить главный интерфейс, если используется в главном потоке
QThread::msleep(msec);
#else
// Неблокирующий метод задержки, не замораживает главный интерфейс
QTime endTime = QTime::currentTime().addMSecs(msec);
while (QTime::currentTime() < endTime) {
QCoreApplication::processEvents(QEventLoop::AllEvents, 100);
}
#endif
#endif
}
«Серебряная Галактика» Kylin ранних версий, например, V2, основана на FreeBSD, новые версии V4 и V10 — на Ubuntu.
Рассматривая историю развития Qt, мы видим почти непрерывный процесс разделения и объединения логики. Например, изначально UI-элементы управления, такие как QPushButton, находились в модуле QtGui. Позже, когда они стали неудобными в управлении и обновлении, был выделен отдельный модуль QtWidgets. В Qt6 классы QList и QVector были объединены в один класс, создавая впечатление воссоединения разделённого. Кроме того, некоторые математические функции и методы стандартной библиотеки C++ постепенно заменяются стандартными функциями C++, начиная с разделения и заканчивая унификацией.
Qt постоянно обновляется и совершенствуется, хотя качество нового кода явно уступает эпохе Nokia, но, по крайней мере, есть прогресс. Основные улучшения в настоящее время сосредоточены в модуле QML, а также в базовой части, поскольку как виджеты, так и QML используют общую базовую логику. Основа должна быть прочной и надёжной. За последние несколько лет я сравнивал производительность различных классов и функций в разных версиях Qt (от старых до новых) и обнаружил, что официальные веб-сайты предоставляют информацию о повышении производительности соответствующих классов и методов в новых версиях, что действительно соответствует действительности. Что касается того, насколько именно улучшилась производительность, я не могу точно сказать.
QStringList list1, list2;
QMap<QString, QString> map;
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
for (int i = 0; i < 100000; ++i) {
QString s1 = QString("%1").arg(i);
QString s2 = QString("A%1").arg(i);
list1 << s1;
list2 << s2;
map.insert(s1, s2);
}
}
void MainWindow::on_pushButton_clicked()
{
QElapsedTimer time;
time.start();
qDebug() << "111" << time.nsecsElapsed() << list2.at(list1.indexOf("9999"));
}
void MainWindow::on_pushButton_2_clicked()
{
QElapsedTimer time;
time.start();
qDebug() << "222" << time.nsecsElapsed() << map.value("9999");
}
CONFIG(debug, debug|release) {
win32: TARGET = $$join(TARGET,,,d)
mac: TARGET = $$join(TARGET,,,_debug)
unix:!mac: TARGET = $$join(TARGET,,,d)
}
#判断当前套件是debug还是release
CONFIG(debug, debug|release) {}
CONFIG(release, debug|release) {}
Наименование | Описание |
---|---|
QT += core gui | Добавляет необходимые модули в проект, влияет на автоматическое появление выпадающего списка при включении файлов в код. Если модуль не добавлен в файл pro, то автодополнение не будет работать. Обычно используется при создании пакетов для выпуска, соответствующие динамическим библиотекам, таким как Qt5Core.dll. |
TARGET = xxx | Определяет имя конечного целевого файла, который может быть исполняемым файлом или библиотекой. |
background:#FF0000; | |
} |
QSlider::add-page:vertical{ width:8px; background:#00FF00; }
QSlider::sub-page:vertical{ width:8px; background:#FF0000; }
QSlider::handle:vertical{ height:10px; background:#0000FF; }
### 22:211-220
211. При упорядочивании закреплённых окон некоторые редко используемые настройки QMainWindow легко забыть, поэтому рекомендуется просмотреть функции заголовка файла QMainWindow. Для подробного ознакомления со всеми параметрами закрепления см. статью [https://zhuanlan.zhihu.com/p/388544168](https://zhuanlan.zhihu.com/p/388544168).
```cpp
// Устанавливаем гибкие параметры для различных типов вложенности, например, вертикального и горизонтального расположения
// Эти настройки переопределяют параметры, заданные в setDockOptions ниже, поэтому важно соблюдать порядок
//this->setDockNestingEnabled(true);
// Задаем параметры закрепления, запрещаем перекрытие, разрешаем только перетаскивание и вложение
this->setDockOptions(AnimatedDocks | AllowNestedDocks);
// Определяем нижнюю левую область как область слева, нижнюю правую область как область справа, иначе нижняя область будет заполнена растягиванием
this->setCorner(Qt::BottomLeftCorner, Qt::LeftDockWidgetArea);
this->setCorner(Qt::BottomRightCorner, Qt::RightDockWidgetArea);
// Отображение текста
QString text = index.data(Qt::DisplayRole).toString();
// Выравнивание текста
int align = index.data(Qt::TextAlignmentRole).toInt();
// Шрифт текста
QFont font = index.data(Qt::FontRole).value<QFont>();
// Цвет переднего плана
QColor color = index.data(Qt::ForegroundRole).value<QColor>();
// Цвет фона
QColor color = index.data(Qt::BackgroundRole).value<QColor>();
void frmMain::dropEvent(QDropEvent *event)
{
QList<QUrl> urls = event->mimeData()->urls();
}
void frmMain::dragEnterEvent(QDragEnterEvent *event)
{
if(event->mimeData()->hasFormat("application/x-qabstractitemmodeldatalist")) {
event->setDropAction(Qt::MoveAction);
event->accept();
} else {
event->ignore();
}
}
Начиная с версии Qt5.6, встроенный браузер основан на движке webengine, и если требуется веб-интерактивность, то необходимо использовать файл qwebchannel.js, предоставленный Qt. Этот файл не рекомендуется изменять, поскольку поддержка webengine постоянно обновляется, и соответствующие файлы qwebchannel.js для каждой версии Qt различаются. Это означает, что вы должны использовать соответствующий файл qwebchannel.js для вашей версии Qt, который по умолчанию находится в каталоге C:\Qt\Qt5.12.11\Examples\Qt-5.12.11\webchannel\shared. После тестирования нескольких версий Qt было обнаружено, что использование более новой версии qwebchannel.js в более старой версии не работает, но более старая версия в более новой работает. Поэтому рекомендуется использовать соответствующую версию.
Для удаления пробелов из QString существует несколько сценариев, возможно, потребуется удалить пробелы из левой, правой или всех позиций.
// Удаление пробелов из строки -1=удаление пробелов слева 0=удаление всех пробелов 1=удаление пробелов справа 2=удаление начальных и конечных пробелов 3=удаление начальных и конечных, оставление одного пробела в середине
QString QtHelperData::trimmed(const QString &text, int type)
{
QString temp = text;
QString pattern;
if (type == -1) {
pattern = "^ +\\s*";
} else if (type == 0) {
pattern = "\\s";
//temp.replace(" ", "");
} else if (type == 1) {
pattern = "\\s* +$";
} else if (type == 2) {
temp = temp.trimmed();
} else if (type == 3) {
temp = temp.simplified();
}
// Использование регулярного выражения для удаления пробелов
if (!pattern.isEmpty()) {
#if (QT_VERSION >= QT_VERSION_CHECK(6,0,0))
temp.remove(QRegularExpression(pattern));
#else
temp.remove(QRegExp(pattern));
#endif
}
return temp;
}
// Тестовый код
QString text = " a b c d ";
// Результат: a b c d
QtHelper::trimmed(text, -1);
// Результат: abcd
QtHelper::trimmed(text, 0);
// Результат: a b c d
QtHelper::trimmed(text, 1);
// Результат: a b c d
QtHelper::trimmed(text, 2);
// Результат: a b c d
QtHelper::trimmed(text, 3);
// Также можно пропустить прокси-сервер через код
#include <QNetworkProxy>
QNetworkProxyFactory::setUseSystemConfiguration(false);
// Далее устанавливаем прокси-сервер
tcpSocket->setProxy(QNetworkProxy::NoProxy);
// Статья для чтения https://www.cnblogs.com/cppskill/p/11730452.html
// Начиная с 5.8, тип сокета по умолчанию — DefaultProxy, а не NoProxy, неизвестно почему.
Компиляция ffmpeg и qt в Linux системе довольно проста, даже для начинающих, при условии, что ваш локальный компилятор gcc g++ работает нормально.
Любой компилятор, включая встроенные, должен быть проверен на работоспособность, прежде чем использовать его. Например, g++ -v для проверки версии gcc и arm-linux-g++ -v для версии встроенного компилятора.
Чтобы проверить версию кросс-компилятора, используйте команду /opt/FriendlyARM/toolschain/4.5.1/bin/arm-linux-g++ -v.
Важно убедиться, что битность компилятора соответствует битности операционной системы. Обычно 32-битные компиляторы используются для кросс-компиляции на 32-битных системах, хотя 32-битные компиляторы могут использоваться на 64-битных системах после установки зависимостей, но это не рекомендуется, так как могут возникнуть проблемы. 64-битные компиляторы работают только на 64-битных системах.
Если вы установили переменные окружения, вы можете опустить длинные пути и просто ввести имя исполняемого файла. Если переменные окружения не установлены, вам придется вводить полный путь. Установка переменных окружения нужна только для того, чтобы компилятор автоматически нашёлся во время компиляции. На самом деле можно вообще не устанавливать переменные окружения, а указать расположение компилятора с помощью абсолютного пути.
Компиляция на Linux, будь то ffmpeg, qt или что-то ещё, всегда выполняется по общим шагам: первый шаг: ./configure
, второй шаг: make
, третий шаг: make install
.
Что касается конкретных параметров после configure
, обратитесь к руководству соответствующего исходного пакета. Поисковик выдаст множество результатов. Конечно, вы можете использовать значения по умолчанию и компилировать без каких-либо параметров. Компилятор автоматически примет параметры по умолчанию.
Команда кросс-компиляции ffmpeg: ./configure --prefix=host --enable-static --disable-shared --disable-doc --cross-prefix=/opt/FriendlyARM/toolschain/4.5.1/bin/arm-linux- --arch=arm --target-os=linux
Предварительные условия для кросс-компиляции qt: измените файл qmake.conf
в папке mkspecs/qws/linux-arm-g++
, если переменные окружения не установлены, задайте абсолютный путь к нужному компилятору и измените имя компилятора на нужное вам.
Например, изменение компилятора gcc: QMAKE_CC = /opt/FriendlyARM/toolschain/4.5.1/bin/arm-linux-gcc
Команда кросс-компиляции qt4.8.5: ./configure -prefix host -embedded arm -xplatform qws/linux-arm-g++ -release -opensource -confirm-license -qt-sql-sqlite -qt-gfx-linuxfb -plugin-sql-sqlit -no-qt3support -no-phonon -no-svg -no-webkit -no-javascript-jit -no-script -no-scripttools -no-declarative -no-declarative-debug -qt-zlib -no-gif -qt-libtiff -qt-libpng -no-libmng -qt-libjpeg -no-rpath -no-pch -no-3dnow -no-avx -no-neon -no-openssl -no-nis -no-cups -no-dbus -little-endian -qt-freetype -no-opengl -no-glib -nomake demos -nomake examples -nomake docs -nomake tools
Команда кросс-компиляции qt5.9.8: ./configure -prefix host -xplatform linux-arm-g++ -recheck-all -opensource -confirm-license -optimized-qmake -release -no-separate-debug-info -strip -shared -static -c++std c++1z -no-sse2 -pch -compile-examples -gui -widgets -no-dbus -no-openssl -no-cups -no-opengl -linuxfb -qt-zlib -qt-libpng -qt-libjpeg -qt-freetype
В целом, кросс-компиляция и обычная компиляция отличаются только тем, что нужно вручную указать путь к кросс-компилятору. Для ffmpeg это делается с помощью параметра --cross-prefix=
, а для qt, который является более сложным, через изменение файла конфигурации и указание имени файла конфигурации с помощью параметра -xplatform
.
Компиляция Qt6 довольно сложна, обычно используется cmake для компиляции. На Linux сначала используйте исходные коды cmake версии 3.19 и выше. Скомпилируйте и создайте cmake с помощью make, затем используйте cmake для компиляции qt и создания qmake, наконец, вызовите qmake для компиляции вашего проекта qt.
На самом деле, компиляция Qt нужна только для использования библиотек внутри него, поэтому нет необходимости использовать demo, doc, tool, example и т.д., что занимает много времени. Поэтому настоятельно рекомендуется удалять их при компиляции, что значительно ускоряет процесс компиляции.
Рекомендуется компилировать от имени обычного пользователя, включая распаковку исходных кодов, потому что библиотеки, скомпилированные таким образом, могут использоваться обычными пользователями. Если компиляция выполнена от имени root-администратора, то впоследствии потребуются права администратора.
Многие системы предоставляют возможность распаковки файлов с помощью контекстного меню правой кнопкой мыши, но это также возможно, хотя и медленнее. Рекомендуется использовать командную строку для распаковки и удаления каталогов.
Параметры компиляции Qt могут отличаться в каждой версии, поскольку код постоянно обновляется, и даже некоторые описания категорий могут измениться. Например, параметр -qt-xcb
был заменён на -xcb
в версии 5.15, а параметр -qt-sql-sqlite
был заменён на -qt-sqlite
. Обязательно прочитайте файл readme в исходном коде, где указаны минимальные требования к версии компиляции среды. После версии qt5 конкретные параметры конфигурации описаны в файле config в каталоге qtbase.
Если после компиляции появляется сообщение об ошибке GL/gl.h
, необходимо установить пакеты apt install libgl1-mesa-dev libglu1-mesa-dev
или yum install mesa-libGL-devel mesa-libGLU-devel
.
Описание параметров компиляции можно найти по ссылке https://blog.csdn.net/xi_gua_gua/article/details/53413930.
В Qt иногда возникают проблемы с загрузкой изображений, которые могут быть вызваны неправильным расширением файла. Например, изображение с расширением jpg может иметь расширение png, а изображение bmp может иметь расширение jpg. QImage и QPixmap загружают изображения, используя расширение файла для вызова соответствующего алгоритма анализа изображения. Это простой способ, но быстрый, так как не требуется анализировать конкретный внутренний формат изображения. Если вы хотите гарантировать успешную загрузку независимо от расширения файла, вам нужно обработать загрузку, читая данные изображения напрямую.
// Можно использовать изображение из ресурса или локальный файл
QString fileName = ":/test.png";
// Этот метод использует расширение файла для определения формата,
// но если расширение неправильное, загрузка не удастся
ui->label->setPixmap(QPixmap(fileName));
// Загрузка с использованием данных изображения гарантирует успех
QFile file(fileName);
file.open(QIODevice::ReadOnly);
QByteArray data = file.readAll();
// Обработка с использованием QImage
QImage img;
img.loadFromData(data);
// Также можно использовать этот метод
// QImage img = QImage::fromData(data);
ui->label->setPixmap(QPixmap::fromImage(img));
// Обработка с использованием QPixmap
QPixmap pix;
pix.loadFromData(data);
ui->label->setPixmap(pix);
Несколько интересных фактов о версиях Qt:
// Получение модели данных таблицы
QAbstractItemModel *model = ui->tableView->model();
// Активная установка на третью строку
ui->tableView->setCurrentIndex(model->index(3, 0));
// Активная установка на последнюю строку
ui->tableView->setCurrentIndex(model->index(model->rowCount() - 1, 0));
// Установка режима выбора поддерживает множественный выбор, другие значения перечисления см. в документации.
ui->tableView->setSelectionMode(QAbstractItemView::MultiSelection);
// Выбор всего
ui->tableView->selectAll();
// Отмена всех выбранных элементов
ui->tableView->clearSelection();
// Выбор строки, обратите внимание, что если строка выбрана, то после выполнения она будет отменена, и так далее. Этот дизайн очень умный, аплодисменты.
ui->tableView->selectRow(row);
// Выбор столбца, обратите внимание, что если столбец выбран, то после выполнения он будет отменён, и так далее. Этот дизайн очень умный, аплодисменты.
ui->tableView->selectColumn(column);
// Получение содержимого выбранной строки
QItemSelectionModel *selections = ui->tableView->selectionModel();
QModelIndexList selected = selections->selectedIndexes();
foreach (QModelIndex index, selected) {
qDebug() << index.row() << index.column() << index.data();
}
// Проверка кодировки файла 0=ANSI 1=UTF-16LE 2=UTF-16BE 3=UTF-8 4=UTF-8BOM
int DataCsv::findCode(const QString &fileName)
{
// Предполагаем кодировку по умолчанию utf8
int code = 3;
QFile file(fileName);
if (file.open(QIODevice::ReadOnly)) {
// Считываем 3 байта для проверки
QByteArray buffer = file.read(3);
quint8 b1 = buffer.at(0);
quint8 b2 = buffer.at(1);
quint8 b3 = buffer.at(2);
if (b1 == 0xFF && b2 == 0xFE) {
code = 1;
} else if (b1 == 0xFE && b2 == 0xFF) {
code = 2;
} else if (b1 == 0xEF && b2 == 0xBB && b3 == 0xBF) {
code = 4;
} else {
// Попытка преобразования в utf8, если доступно больше 0 символов, это означает кодировку ansi
QTextCodec::ConverterState state;
QTextCodec *codec = QTextCodec::codecForName("utf-8");
codec->toUnicode(buffer.constData(), buffer.size(), &state);
if (state.invalidChars > 0) {
code = 0;
}
}
file.close();
}
return code;
}
При выполнении запросов к удалённой базе данных иногда наблюдается низкая производительность, особенно при большом объёме данных в таблице. На локальном компьютере обработка данных происходит намного быстрее. Можно попробовать включить свойство только вперёд, query.setForwardOnly(true). В этом случае будет кэшироваться только одна порция данных, что значительно повысит эффективность запросов к удаленной базе данных. Говорят, что это может увеличить скорость в десятки или даже сотни раз. Конечно, это предполагает, что вам нужны только данные, полученные вперёд, если вам также нужны данные назад или вы используете их в модели данных QSqlQueryModel, вы не можете включить это свойство. Причина в том, что каждый раз, когда вы используете QSqlQuery для получения следующей записи, если вы не включаете свойство isForwardOnly (к сожалению, по умолчанию оно не включено), каждый раз будет выделяться новое пространство памяти для хранения уже посещённых и ещё не посещённых записей. Таким образом, каждый раз будет тратиться много места в памяти.
Рисование с использованием painter в Qt очень гибкое и мощное, с богатым интерфейсом, но для многих начинающих это всё ещё довольно сложно, особенно различные странные сложные форматы, которые хорошо описываются в html, например, управление межстрочным и межсимвольным интервалами и т. д. В таких случаях можно использовать QTextDocument для передачи содержимого в формате html в QPainter для рисования, что очень идеально, просто и мощно, включая некоторые математические формулы и т.д.
void Form::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
QTextDocument doc;
doc.setHtml(html);
// Устанавливаем ширину текста
doc.setTextWidth(200);
// Указываем область рисования
doc.drawContents(&painter, QRect(0, 0, 200, 70));
}
// Ниже установлено, что при наведении курсора мыши на выбранный элемент item цвет фона будет #00FF00
QTableView::item:selected{background:#FF0000;}
QTableView::item:hover{background:#00FF00;}
// Ниже установлено, что при наведении курсора мыши на выбранный элемент item цвет фона будет #FF0000
QTableView::item:hover{background:#00FF00;}
QTableView::item:selected{background:#FF0000;}
// Стиль левого верхнего угла легко упустить из виду
QTableCornerButton:section{background:#FF0000;}
// Минимальное требование к версии
!minQtVersion(5, 15, 2) {
message("Невозможно собрать инфраструктуру установщика Qt с версией $${QT_VERSION}.")
error("Используйте версию Qt не ниже 5.15.2.")
}
// Все три метода работают
ui->lineEdit->setSelection(0, 0);
ui->lineEdit->setCursorPosition(0);
// Способ таблицы стилей
"QLineEdit{qproperty-cursorPosition:0;}
До Qt 5.6 использовался webkit. Начиная с версии Qt 5.6, существует два случая: для компилятора mingw (Windows-система) соответствующая библиотека Qt больше не предоставляет модуль браузера.
В версиях Qt после 5.6 для систем Linux, Mac и других систем не существует ситуации отсутствия элемента управления браузером, везде используется webengine.
Только для Windows с компилятором mingw версия Qt не имеет модуля браузера. На самом деле, он есть во всех системах. Многие люди путаются в этом вопросе, думая, что только компилятор msvc имеет элемент управления браузером в Windows, но на самом деле это Qt-библиотека msvc для Windows, которая включает модуль браузера.
При установке Qt модуль webengine по умолчанию не отмечен, его необходимо установить вручную.
Не все версии Qt msvc имеют модуль веб-браузера webengine, даже если он отмечен, некоторые версии официально не компилируются, требуется самостоятельная компиляция. Необходимо проверить наличие файла Qt5WebEngine.dll в соответствующем каталоге установки Qt. 1. Если вам просто нужно восполнить недостаток модуля браузера в версии mingw, рекомендуется использовать miniblink.
Если вы хотите обеспечить совместимость с различными версиями и системами, рекомендуется использовать cef.
Если у вас нет исторического багажа, рекомендуется использовать webengine, который хорошо интегрирован с Qt.
Webkit и miniblink по умолчанию не поддерживают gpu, webengine по умолчанию использует gpu.
Qwebengine по умолчанию не поддерживает MP4, требуется самостоятельная перекомпиляция.
path = C:/Qt/mysql-5.7.30-winx64
INCLUDEPATH += $$path/include
win32:LIBS += -L$$path/lib -llibmysql
path = C:/app/Administrator/product/11.2.0/client_1
INCLUDEPATH += $$path/oci/include
win32:LIBS += -L$$path/oci/lib/msvc -loci
path = "C:/Program Files/PostgreSQL/13"
INCLUDEPATH += $$path/include
win32:LIBS += -L$$path/lib -llibpq
Приведённая выше запись поддерживает как mingw, так и msvc, процесс компиляции для других систем аналогичен. После компиляции в корневом каталоге диска, на котором находится ваш текущий исходный код, появится каталог plugins, а внутри него — каталог sqldrivers с соответствующими динамическими библиотеками скомпилированных плагинов. По умолчанию код драйвера плагина oracle написан в соответствии с функциями oracle12, если вы связываетесь с oracle11, вам нужно изменить две строки кода, чтобы компиляция прошла успешно. Откройте файл qsql_oci.cpp, примерно в строке 1559 кода есть функция OCIBindByPos2, измените её на OCIBindByPos, ниже ещё есть строка bindColumn.lengths, измените её на (ub2*)bindColumn.lengths.
void DbHelper::bindTable(const QString &dbType, QSqlTableModel *model, const QString &table)
{
//postgresql全部小写,oracle全部大写,这两个数据库严格区分表名字段名的大小写卧槽
QString flag = dbType.toUpper();
if (flag == "POSTGRESQL") {
model->setTable(table.toLower());
} else if (flag == "ORACLE") {
model->setTable(table.toUpper());
} else {
model->setTable(table);
}
}
QSqlDatabase database = QSqlDatabase::addDatabase("QMYSQL");
//database.setDatabaseName("dbtool");
database.setHostName("127.0.0.1");
database.setPort(3306);
database.setUserName("root");
database.setPassword("root");
if (database.open()) {
QSqlQuery query(database);
qDebug() << "删除数据库" << query.exec("drop database dbtool");
qDebug() << "创建数据库" << query.exec("create database dbtool");
if (query.exec("select * from userinfo")) {
while (query.next()) {
qDebug() << "查询数据库" << query.value(0);
}
}
} else {
qDebug() << "打开数据库" << database.lastError().text();
}
ui->tableView->setItemDelegateForColumn(0, new QItemDelegate);
//下面是终极大法
QVariant SqlQueryModel::data(const QModelIndex &index, int role) const
{
QVariant value = QSqlQueryModel::data(index, role);
//超过100万的数值会被科学计数显示需要这里转成字符串显示
if (role == Qt::DisplayRole) {
int result = value.toInt();
if (result >= 1000000) {
value = QString::number(result);
}
}
return value
} В базах данных MySQL есть различные движки, среди которых MyIsam не поддерживает транзакции базы данных. По умолчанию обычно используется именно этот движок, поэтому после использования метода transaction в Qt и последующей команды commit вы можете обнаружить, что операция не была успешной, хотя на самом деле всё прошло успешно, и результаты в базе данных соответствуют ожиданиям.
Есть два решения: первое — изменить движок базы данных на InnoDB, второе — выполнить проверку на наличие ошибки после commit: if (database.commit() || !database.lastError().isValid()). Если метод lastError() недоступен или не работает, это также указывает на успешное выполнение операции.
Если вы используете соединение с базой данных через ODBC, то достаточно установить три параметра: databaseName (имя базы данных), userName (имя пользователя) и password (пароль пользователя), поскольку при настройке источника данных уже были заданы соответствующие хост-адрес, порт и имя связанной базы данных. При использовании соединения через ODBC вам нужно только повторно проверить информацию о пользователе. Здесь важно отметить, что при установке параметра databaseName необходимо ввести имя, указанное в конфигурации источника данных.
После проведения многочисленных сравнительных тестов, включая операции вставки, удаления, пакетной обработки, запроса и постраничного просмотра, можно сделать вывод, что скорость отклика части базы данных Qt следующая: sqlite > postgresql > oracle > mysql > odbc. Для объёмов данных свыше миллиона результаты следующие: postgresql > oracle > mysql > sqlite > odbc. На уровне миллиардов записей: oracle > postgresql > остальные. Все эти тесты проводились на основе уровня знаний начинающих пользователей. Более продвинутые темы, такие как разделение баз данных и таблиц, сложные запросы, кэширование и базы данных в памяти, не использовались.
У MySQL есть две основные версии: MySQL 5.7 и MySQL 8. Согласно официальным заявлениям, версия 8 намного быстрее версии 5, но мои личные тесты показали обратное: версия 5.7 работает быстрее, чем версия 8, независимо от того, идёт ли речь о запросах или пакетной вставке данных. Результаты поиска в интернете также подтверждают это наблюдение. Многие пользователи сообщают, что версия 8 работает медленнее.
Существует ответвление MySQL под названием MariaDB, которое превосходит MySQL по чистоте и производительности. Говорят, что MariaDB превосходит MySQL во всех аспектах. Мои личные сравнительные тесты также подтвердили значительное улучшение производительности при пакетной вставке и запросах. Кроме того, MariaDB полностью совместима с MySQL, и даже файлы библиотек могут быть легко переименованы для использования. Например, libmariadb.dll можно переименовать в libmysql.dll, и он будет работать без проблем. Более того, размер MariaDB составляет всего одну восьмую от размера MySQL. Это значительное преимущество при публикации приложения.
Если ваше приложение использует Qt и MySQL, то версии библиотек, которые вы распространяете, должны соответствовать версии базы данных, с которой вы работаете, иначе вы можете потерять возможность транзакций. Вы можете проверить это, используя функцию database.driver()->hasFeature(QSqlDriver::Transactions).
QSqlTableModel очень хорошо инкапсулирован и не загружает все данные сразу, а загружает только те данные, которые необходимы при прокрутке. Даже при работе с таблицами, содержащими миллиард строк, скорость остаётся высокой, такой же, как и при работе с таблицами в несколько тысяч строк.
При подключении к сетевой базе данных, если у вас настроен прокси-сервер в локальной сети, например, для доступа к GitHub, вы можете столкнуться с проблемами подключения в программах баз данных Qt. Чтобы решить эту проблему, необходимо отключить использование локального прокси-сервера, установив QNetworkProxyFactory::setUseSystemConfiguration(false). Если не уделить этому должного внимания, это может вызвать проблемы.
### 24: 231–240
231. Несколько моментов о наследовании, полиморфизме, virtual и override в C++.
* Подклассы могут напрямую использовать переменные и функции, защищённые в базовом классе.
* Если функция базового класса не объявлена как virtual, а подкласс имеет такую же функцию, то происходит переопределение. Когда вызывается через указатель базового класса, вызывается функция базового класса; когда вызывается через указатель подкласса, вызывается функция подкласса.
* Если функция базового класса объявлена как virtual, и подкласс имеет ту же функцию, происходит перегрузка. Независимо от того, вызывается ли она через указатель базового класса или указатель подкласса, всегда вызывается функция подкласса.
* Добавление override к функции требует, чтобы одноимённая функция в подклассе была виртуальной, и она должна быть переопределена, иначе компиляция не удастся.
* Можно не добавлять virtual к функциям подкласса, рекомендуется добавлять override без virtual.
* Чистые виртуальные функции в базовом классе (virtual void play() = 0;) не требуют реализации в файле .cpp базового класса, но они должны быть реализованы в подклассе, иначе компиляция завершится ошибкой.
* Основное преимущество полиморфного наследования заключается в выделении общих черт, таких как общие переменные, методы и сигналы, в базовый класс, позволяя подклассам реализовывать свои специфические функции.
232. О том, как реализовать эффект выделения всей строки при наведении курсора на ячейку в QTableView и QTableWidget. Большинство решений в Интернете ориентированы на QTableWidget, а для QTableView используются делегаты или пользовательские средства рисования.
* Предварительное условие: установите поведение выбора для автоматического выбора всей строки, tableView->setSelectionBehavior(QAbstractItemView::SelectRows).
* Установите фильтр событий, определите модель данных в текущей координате, затем установите текущую модель как модель в точке зависания курсора. Этот хитрый приём может сэкономить много работы.
* Он работает как для QTableView, так и для QTableWidget.
233. Как избежать конфликтов ключевых слов с сигналами, слотами и другими элементами сторонних библиотек в Qt.
* Первый шаг: добавьте CONFIG += no_keywords в файл pro.
* Второй шаг: замените все существующие сигналы на Q_SIGNALS, слоты на Q_SLOTS и т. д. в проекте.
* Третий шаг: полностью перекомпилируйте проект, чтобы устранить конфликты ключевых слов.
234. Различие между операционными системами и аппаратными платформами в файле pro.
```cpp
win32 {}
unix {}
//Qt5 можно использовать linux{} Qt4 обязательно unix:!maxc{}
unix:!maxc{}
linux {}
maxc {}
android {}
wasm {}
//представляет 64-битную платформу
contains(QT_ARCH, x86_64) {}
//представляет платформу arm
contains(QT_ARCH, arm) || contains(QT_ARCH, arm64) {}
//универсальный метод, просто переключитесь на комплект печати QT_ARCH и посмотрите, какие символы
message($$QT_ARCH)
//Режимы масштабирования
enum ScaleMode {
//Автоматический режим (масштаб с сохранением пропорций, если изображение больше области отображения, иначе отображение в исходном размере)
ScaleMode_auto = 0,
//Обычный режим (всегда масштаб с сохранением пропорций)
ScaleMode_normal = 1,
//Режим заполнения (растягивание изображения для заполнения области отображения)
ScaleMode_fill = 2
};
//Возвращает центральную область, учитывая размер изображения, область виджета и размер рамки
static QRect getCenterRect(const QSize &imageSize, const QRect &widgetRect, int borderWidth = 2, const ScaleMode &scaleMode = ScaleMode_auto)
{
QSize newSize = imageSize;
QSize widgetSize = widgetRect.size() - QSize(borderWidth * 2, borderWidth * 2);
if (scaleMode == ScaleMode_auto) {
if (newSize.width() > widgetSize.width() || newSize.height() > widgetSize.height()) {
newSize.scale(widgetSize, Qt::KeepAspectRatio);
}
} else if (scaleMode == ScaleMode_normal) {
newSize.scale(widgetSize, Qt::KeepAspectRatio);
} else {
newSize = widgetSize;
}
int x = widgetRect.center().x() - newSize.width() / 2;
int y = widgetRect.center().y() - newSize.height() / 2;
return QRect(x, y, newSize.width(), newSize.height());
}
//Создаёт изображение подходящего размера, учитывая размер виджета и стратегию масштабирования
static void getScaledImage(QImage &image, const QSize &widgetSize, const ScaleMode &scaleMode = ScaleMode_auto, bool fast = true)
{ ```cpp
// Qt::SmoothTransformation;
if (scaleMode == ScaleMode_auto) {
if (image.width() > widgetSize.width() || image.height() > widgetSize.height()) {
image = image.scaled(widgetSize, Qt::KeepAspectRatio, mode);
}
} else if (scaleMode == ScaleMode_normal) {
image = image.scaled(widgetSize, Qt::KeepAspectRatio, mode);
} else {
image = image.scaled(widgetSize, Qt::IgnoreAspectRatio, mode);
}
}
// Файл test.h
// Следующая функция вызовет ошибку компиляции "повторное определение"
void test() {}
// Следующие четыре функции будут скопированы при каждом включении заголовочного файла
static void test1() {}
inline void test2() {}
static inline void test3() {}
inline static void test4() {}
// Правильное написание
class tt {
void test() {}
static void test1() {}
inline void test2() {}
static inline void test3() {}
inline static void test4() {}
}
//pro pri 文件
// Указывает на основную версию > 4 и младшую версию > 6, то есть версия >= 5.7
greaterThan(QT_MAJOR_VERSION, 4) {
greaterThan(QT_MINOR_VERSION, 6) {
DEFINES += qchart
}}
// Учитывая выпуск Qt6 и последующие версии Qt7, Qt8 и т.д., одного вышеуказанного условия недостаточно
// Указывает на основную версию > 5, то есть версия >= 6.0
greaterThan(QT_MAJOR_VERSION, 5) {
DEFINES += qchart
}
// Проверяет наличие определения и импортирует соответствующий модуль
contains(DEFINES, qchart) {
QT += charts
}
// Код файла
#ifdef qchart
// Выполнение нужного кода
#endif
// Изучение кода проектов Qt показывает, что можно напрямую проверять версии больше определённой, без необходимости многократной проверки основной версии
// Тестирование с версиями 4.8, 5.5, 5.7, 5.15, 6.2, 6.7 и т. д. работает нормально
// Недостатком является то, что передаваемая основная версия должна быть меньше или равна указанной в первом параметре основной версии
greaterThan(QT_MAJOR_VERSION, 4)|greaterThan(QT_MINOR_VERSION, 7) {
message(Текущая версия больше 4.7)
}
// Аналогично для проверки версии компилятора
// Также доступны QT_GCC_MAJOR_VERSION, QT_GCC_MINOR_VERSION, QT_CLANG_MAJOR_VERSION и QT_CLANG_MINOR_VERSION
// Вывод текущего имени компилятора QMAKE_CXX даёт результат cl / g++
// Вывод текущих определений компилятора QMAKE_COMPILER_DEFINES даёт результат _MSC_VER=1800 _WIN32 / __GNUC__ WIN32
// Вывод версии компилятора MSVC_VER даёт результат msvc2013=12.0 / msvc2015=14.0
// Установка закруглённости углов области фона
chart->setBackgroundRoundness(0);
// Настройка внутренних отступов
chart->setMargins(QMargins(0, 0, 0, 0));
// Настройка внешних отступов
chart->layout()->setContentsMargins(0, 0, 0, 0);
// Сжатие данных перед отправкой
QByteArray buffer = "...";
buffer = qCompress(buffer);
socket->write(buffer);
// Распаковка полученных данных
QByteArray data = socket->readAll();
data = qUncompress(data);
char data[2];
data[0] = 0x10;
data[1] = 25;
// Вывод двоичного представления "10000" "11001"
qDebug() << "Двоичное представление" << QString::number(data[0], 2) << QString::number(data[1], 2);
// Вывод пятеричного представления "31" "100"
qDebug() << "Пятеричное представление" << QString::number(data[0], 5) << QString::number(data[1], 5);
// Вывод десятичного представления "16" "25"
qDebug() << "Десятичное представление" << QString::number(data[0]) << QString::number(data[1]);
// Вывод шестнадцатеричного представления "10" "19"
qDebug() << "Шестнадцатеричное представление" << QString::number(data[0], 16) <<
``` ```cpp
QString::number(data[1], 16);
// Подключение к базе данных sqlite
QSqlDatabase database = QSqlDatabase::addDatabase("QSQLITE");
// Достаточно указать абсолютный путь к файлу базы данных
database.setDatabaseName("d:/test.db");
// Подключение к базе данных mysql
QSqlDatabase database = QSqlDatabase::addDatabase("QMYSQL");
database.setDatabaseName("test");
database.setHostName("127.0.0.1");
database.setPort(3306);
database.setUserName("root");
database.setPassword("root");
// Подключение к базе данных sqlserver
// Способ 1 через источник данных odbc, при условии, что источник данных должен быть настроен.
QSqlDatabase database = QSqlDatabase::addDatabase("QODBC");
database.setDatabaseName("источник данных");
database.setUserName("sa");
database.setPassword("123456");
// Способ 2 через строку драйвера, без настройки источника данных. Указание имени базы данных включает в себя IP-адрес хоста и порт, а также информацию о пользователе, поэтому все последующие настройки не требуются. Настоятельно рекомендуется этот метод.
QSqlDatabase database = QSqlDatabase::addDatabase("QODBC");
QStringList list;
list << QString("DRIVER={%1}").arg("SQL SERVER");
list << QString("SERVER=%1,%2").arg("127.0.0.1").arg(1433);
list << QString("DATABASE=%1").arg("test");
list << QString("UID=%1").arg("sa");
list << QString("PWD=%1").arg("123456");
database.setDatabaseName(list.join(";"));
// Подключение к базе данных postgresql
QSqlDatabase database = QSqlDatabase::addDatabase("QPSQL");
database.setDatabaseName("test");
database.setHostName("127.0.0.1");
database.setPort(5432);
database.setUserName("postgres");
database.setPassword("123456");
// Подключение к базе данных oracle
QSqlDatabase database = QSqlDatabase::addDatabase("QOCI");
database.setDatabaseName("test");
database.setHostName("127.0.0.1");
database.setPort(1521);
database.setUserName("system");
database.setPassword("123456");
// Подключение к базе данных kingbase (ядро — postgresql)
QSqlDatabase database = QSqlDatabase::addDatabase("QPSQL");
database.setDatabaseName("test");
database.setHostName("127.0.0.1");
database.setPort(54321);
database.setUserName("SYSTEM");
database.setPassword("123456");
// Через источник данных odbc подключение к различным базам данных, при условии, что источник данных настроен. Достаточно установить имя базы данных как имя источника данных, ввести имя пользователя и пароль, остальные настройки, такие как IP-адрес хоста и порт, не нужны.
QSqlDatabase database = QSqlDatabase::addDatabase("QODBC");
database.setDatabaseName("источник данных");
database.setUserName("system");
database.setPassword("123456");
// Чтобы гарантировать, что всегда будет только одна привязка, перед привязкой выполните отмену привязки один раз
disconnect(obj, SIGNAL(), this, SLOT());
connect(obj, SIGNAL(), this, SLOT());
// По совету опытных разработчиков из сообщества, оказывается, можно избежать этой проблемы, передав пятый параметр UniqueConnection в функцию connect. Согласно официальной документации, этот параметр фильтрует повторяющиеся сигналы.
connect(obj, SIGNAL(), this, SLOT(), Qt::UniqueConnection);
QWidget *widget;
// Используем new
widget = new QWidget;
// Освобождаем объект после использования
widget->deleteLater();
// Использование интеллектуального указателя
QScopedPointer<QWidget> widget;
// Просто используем new, освобождение ресурсов происходит автоматически
widget.reset(new QWidget);
При попытке вызвать setLayout для повторного установления макета в виджете, который уже имеет макет, Qt выдаёт предупреждение, сообщая, что макет уже существует и его необходимо удалить перед установкой нового. Рекомендуется использовать метод layout()->deleteLater() для безопасного удаления макета, однако в данном случае это не сработает, и потребуется использовать delete layout(). Это довольно странно.
При написании классов иногда возникает необходимость присваивать значения переменным и получать их. Обычно для этого используются функции setxxx и getxxx. Часто внутри этих функций всего одна строка кода, и тогда может возникнуть вопрос, почему бы просто не сделать переменные public и использовать их напрямую, сэкономив таким образом две функции и несколько строк кода. На самом деле использование функций set и get обусловлено необходимостью расширения функциональности. Например, если позже потребуется фильтровать присвоения значений или разрешить доступ только для чтения или записи, изменение правил затронет все места использования переменной. Это может привести к проблемам.
Что касается быстрого завершения потока, вызов terminate для принудительного завершения может вызвать проблемы. Обычно мы используем флаг для завершения потока. Однако, если выполнение функции занимает много времени или время ожидания в run слишком велико, поток может завершиться некорректно. В таких случаях можно рассмотреть стратегию разделения тела потока на части. Если проблема связана с длительным выполнением функции, можно добавить проверку флага остановки внутри функции. Если же проблема в длительном ожидании, можно разбить время ожидания на несколько коротких интервалов и проверять флаг остановки на каждом интервале. Это ускорит завершение потока без длительного ожидания.
void Thread::run()
{
while (!stopped) {
doTask();
// Здесь длительное ожидание может замедлить выход
// msleep(3000);
// Специально добавляем небольшую задержку и проверяем флаг остановки после каждой задачи
int count = 0; ```
while (!stopped) {
msleep(100);
count++;
//如果到了30次=30*100=3000毫秒也跳出
if (count == 30) {
break;
}
}
}
stopped = false;
}
void Thread::doTask()
{
while(1) {
if (stopped) {
return;
}
doTask1();
doTask2();
}
}
//Qt对有共同父类窗体的控件优化到了极致,下面生成了1000个widget才新增不到3mb的内存。
for (int i = 0; i < 1000; ++i) {
QWidget *w = new QWidget(this);
w->setGeometry(0, 0, 100, 100);
w->show();
}
QWidget *w1, *w2, *w3;
//将w1控件移到最前面相当于在该父窗体中置顶
w1->raise();
//将w1控件移到最后面相当于在该父窗体中置底
w1->lower();
//将w1控件移到w2控件下面
w1->stackUnder(w2);
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
~MainWindow();
protected:
void closeEvent(QCloseEvent *);
private slots:
void on_pushButton_clicked();
private:
Ui::MainWindow *ui;
QLabel *lab;
};
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)
{
ui->setupUi(this);
lab = new QLabel;
lab->resize(400, 300);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::closeEvent(QCloseEvent *)
{
//先把子窗体释放
lab->deleteLater();
}
void MainWindow::on_pushButton_clicked()
{
lab->show();
}
//下面这个会立即执行
QResizeEvent event(size(), size());
QApplication::sendEvent(this, &event);
//下面这个会立即执行
QResizeEvent *event = new QResizeEvent(size(), size());
QApplication::sendEvent(this, event);
//下面这个不会报错但是也不会执行因为事件对象是局部变量
QResizeEvent event(size(), size());
QApplication::postEvent(this, &event);
//下面的方式非常安全
QResizeEvent *event = new QResizeEvent(size(), size());
QApplication::postEvent(this, event);
//必须要先引入这个头文件
#include "qglobal.h"
#ifdef Q_OS_WIN
...
#else
...
#endif
#ifdef Q_CC_MSVC
#pragma execution_character_set("utf-8")
#endif
//QTimer::singleShot(1000, thread, SLOT(xxx()));
static QTimer *timer = NULL;
if (!timer) {
timer = new QTimer;
QObject::connect(timer, SIGNAL(timeout()), thread, SLOT(xxx()));
timer->setSingleShot(true);
timer->setInterval(1000);
}
timer->stop();
timer->start();
Иногда мы обнаруживаем, что после установки прозрачности фона на контроллере он становится чёрным. Вы можете попробовать установить значение прозрачности равное 1 вместо полной прозрачности равной 0. Это выглядит прозрачным, но сохраняет свойства окна. Если вы хотите отключить системную тень без рамки, вы можете установить свойство w.setWindowFlags(w.windowFlags() | Qt::NoDropShadowWindowHint).
В Qt фильтр событий представляет собой универсальное решение, особенно при установке фильтра событий для всего приложения, который позволяет получать все события. Например, можно получить событие отпускания кнопки мыши в системной панели заголовка и обработать перемещение всех окон без рамок. Я рекомендую использовать его только в случае крайней необходимости, так как он может снизить производительность, перехватывая все события в самом начале. Если после получения соответствующего события вы выполняете длительную обработку, это может привести к зависанию пользовательского интерфейса основного потока.
void AppInit::start()
{
qApp->installEventFilter(this);
}
bool AppInit::eventFilter(QObject *watched, QEvent *event)
{
if (event->type() == QEvent::NonClientAreaMouseButtonPress) {
qDebug() << "Система заголовка нажата";
} else if (event->type() == QEvent::NonClientAreaMouseButtonRelease) {
qDebug() << "Система заголовка отпущена";
}
QWidget *w = (QWidget *)watched;
if (!w->property("canMove").toBool()) {
return QObject::eventFilter(watched, event);
}
static QPoint mousePoint;
static bool mousePressed = false;
QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
if (mouseEvent->type() == При компиляции динамической библиотеки файлов в Linux может создаться множество символических ссылок (на значке есть маленькая стрелка `/libuntitled.so/libuntitled.so.1/libuntitled.so.1.0libuntitled.so.1.0.0`), что зачастую выглядит раздражающе. В Windows создаётся один файл, и вам нужно всего лишь добавить строку `CONFIG += plugin` в ваш pro или pri файл, чтобы создать только один libuntitled.so файл.
2023-04-02 Дополнение: также можно использовать `CONFIG += unversioned_libname unversioned_soname`, чтобы реализовать это. `unversioned_libname` используется для удаления различных версий номеров lib, а `unversioned_soname` — для удаления номеров версий из ссылок при компиляции (если не добавить этот параметр, хотя и будет создан libuntitled.so, но при компиляции ссылок будут выдаваться ошибки о зависимости с номерами версий).
Для получения более подробной информации см. [https://blog.csdn.net/gongjianbo1992/article/details/129889588](https://blog.csdn.net/gongjianbo1992/article/details/129889588).
О методе устранения ошибки, возникающей при онлайн-установке Qt, которая указывает на сетевую ошибку при загрузке «http://mirrors.aliyun.com...»: откройте командную строку и запустите программу установки, например, `C:\Users\Administrator>D:\Qt\Qt6\MaintenanceTool.exe`, затем добавьте параметр `--mirror https://mirrors.cloud.tencent.com/qt` вручную. Полная команда будет выглядеть так: `C:\Users\Administrator>D:\Qt\Qt6\MaintenanceTool.exe --mirror https://mirrors.cloud.tencent.com/qt`. Нажмите Enter для запуска. Аналогично, вы можете использовать другие внутренние адреса зеркал (Tencent Cloud https://mirrors.cloud.tencent.com/qt / Alibaba Cloud https://mirrors.aliyun.com/qt). Иногда обновления зеркал могут быть медленными, поэтому вы можете перейти на сайт https://mirrors.aliyun.com/qt/online/qtsdkrepository/, чтобы проверить, есть ли соответствующая версия.
С версии Qt6.4 мультимедийный модуль предоставляет ffmpeg в качестве декодера для использования (по умолчанию используется ffmpeg в версии 6.5). Вы можете изменить используемый декодер, установив переменную окружения. В первой строке функции main добавьте `qputenv("QT_MEDIA_BACKEND", "ffmpeg");`. Известная проблема заключается в том, что если выбрать ffmpeg, то временно не поддерживается китайский каталог и китайские названия (исправлено в версии 6.5.1), если требуется поддержка китайского языка, необходимо перейти на Windows.
```cpp
// Установка декодера на ffmpeg/поддерживается всеми системами
qputenv("QT_MEDIA_BACKEND", "ffmpeg");
// Для системы Windows
qputenv("QT_MEDIA_BACKEND", "windows");
// Для Linux системы
qputenv("QT_MEDIA_BACKEND", "gstreamer");
// Для Mac системы
qputenv("QT_MEDIA_BACKEND", "darwin");
// Для Android системы
qputenv("QT_MEDIA_BACKEND", "android");
По умолчанию QComboBox регулирует свою ширину в соответствии с шириной символов элементов. Если текст элемента длинный, ширина выпадающего списка становится широкой, что может привести к искажению интерфейса. Есть несколько способов предотвратить это:
QSizePolicy::Ignored
и поместите его в контейнер, убедившись, что другие элементы в макете контейнера имеют фиксированный размер или фиксированное заполнение. Это гарантирует, что выпадающий список не будет расширяться вместе с элементами.ui->comboBox->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLengthWithIcon)
. Когда ширина элемента превышает определённое значение, отображается многоточие. Чтобы применить эту политику ко всему проекту, добавьте в таблицу стилей следующую строку: qApp->setStyleSheet("QComboBox{qproperty-sizeAdjustPolicy:AdjustToMinimumContentsLengthWithIcon}");
. После этого все выпадающие списки в проекте будут автоматически применять эту политику.При сохранении float значения с использованием класса QSettings оно преобразуется в значение, начинающееся с @Variant
, которое трудно интерпретировать. Например, 1.0 = @Variant(\0\0\0\x87?\x80\0\0)
. Если вы хотите напрямую редактировать конфигурационный файл для изменения параметров, это становится сложным. Существует два решения:
set.setValue("SaveVideoRatio", QString::number(SaveVideoRatio));
.float SaveVideoRatio
на double SaveVideoRatio
. Рекомендуется первый метод, поскольку он не требует изменения типа данных и может быть выполнен путём редактирования одной строки кода. Однако следует отметить, что точность double отличается от float, например, float i = 0.1
станет double i = 0.10000000149011612
. В Qt6 эта проблема полностью решена, и преобразование больше не требуется.
Примерно с Qt5.12 был добавлен внешний вид платформы platformthemes. Это означает, что при упаковке и выпуске приложения необходимо включать его, чтобы применить стили оформления на уровне системы. Без него приложение может выглядеть устаревшим, как в стиле Windows 2000 на Windows.
Иногда после настройки автозапуска программы при запуске, если программа вызывает другую программу B через QProcess и программе B требуется прочитать конфигурационный файл из каталога, обнаруживается, что файл не может быть прочитан, потому что рабочий каталог по умолчанию после запуска отличается от каталога исполняемого файла (если запустить программу двойным щелчком, такой проблемы не возникнет, и текущий каталог будет установлен на каталог исполняемого файла автоматически). Поэтому нам нужно выполнить код QDir::setCurrent(qApp->applicationDirPath());
, чтобы явно указать операционной системе текущий каталог. Хотя функция setWorkingDirectory
в QProcess была протестирована, она оказалась неэффективной для программ, запущенных после старта, и вместо неё следует использовать QDir::setCurrent
. Однако у этого подхода есть побочный эффект: как только вызывается QDir::setCurrent
, все относительные пути в вашей программе будут относиться к этому пути, что может вызвать непредвиденные результаты.
271–280
В процессе программирования часто возникает необходимость преобразовать QString в char * или const char *. После преобразования в QByteArray используйте функции .data()
или .constData()
для выполнения преобразования. Важно отметить, что использование .data()
для преобразования в const char * не рекомендуется, хотя оно и работает без ошибок, так как выполняет глубокое копирование, увеличивая нагрузку на память. Если длина строки невелика, это не имеет большого значения, но если строка длинная, дополнительная нагрузка на память может быть значительной. Это хорошая практика программирования.
// Изучение кода показывает, что функция data имеет две перегрузки
inline char *QByteArray::data()
{ detach(); return d->data(); }
inline const char *QByteArray::data() const
{ return d->data(); }
inline const char *QByteArray::constData() const
{ return d->data(); }
QByteArray data = "abc";
// Глубокое копирование
char *d1 = data.data();
// Глубокое копирование
const char *d2 = data.data();
// Мелкое копирование
const char *d3 = data.constData();
// Глубокое копирование
test(data.data());
// Мелкое копирование
test(data.constData());
void test(const char *data)
{
}
// Относительно того, когда вызывать .data(), чтобы выполнить мелкое копирование, мастер Coolcode говорит, что это происходит, когда QByteArray объявлен как const
const QByteArray data;
// Мелкое копирование
const char *d = data.data();
// Мастер Coolcode добавляет: начиная с версии Qt 5.7, была введена функция qAsConst, предназначенная исключительно для тупого преобразования.
// Эта функция реализует функциональность std::as_const() из стандарта C++17, преобразуя изменяемое левое значение в константное левое значение.
// Добавление функции qAsConst было сделано для того, чтобы контейнеры Qt могли поддерживать основанные на диапазоне циклы в стандарте C++11.
// Функция в основном используется, чтобы избежать отсоединения контейнеров Qt в контексте неявного совместного использования.
QString s = "abc";
// Здесь будет выполнено глубокое копирование, что приведёт к снижению производительности
for (QChar ch : s)
// Здесь глубокого копирования не произойдёт
for (QChar ch : qAsConst(s))
// Ниже также выполняется мелкое копирование, но в программировании и в реальности объявление переменной как const не всегда легко достижимо
const QString s;
for (QChar ch : s) // Резюме: для контейнеров, реализованных в Qt, таких как QVector, QMap, QHash, QLinkedList, QList и т. д., если необходимо использовать цикл в форме for (var : container), используйте следующую форму:
for (var : qAsConst(container))
После компиляции и запуска программы на Ubuntu с новой версией Qt 6.5 появляется сообщение об ошибке qt.qpa.plugin: Could not load the Qt platform plugin "xcb" in "" even though it was found. Невозможно нормально отобразить оконные программы. Необходимо вручную установить соответствующие библиотеки xcb. Выполните команду sudo apt install libxcb*.
В некоторых случаях нам нужно выполнить определённую обработку перед QApplication a(argc, argv);, например, QApplication::setAttribute должен быть выполнен в первую очередь. Однако во многих случаях параметры этого параметра не могут быть жёстко заданы, поскольку условия среды могут сильно различаться. Желательно настроить их через файл конфигурации. Тогда возникает проблема: для нормального чтения файла конфигурации обычно требуется указать путь. Если это ./, то это, скорее всего, не текущий путь приложения. Если вы запускаете программу двойным щелчком мыши, то это точно текущий путь приложения, а если нет — это текущий путь системы. Это означает, что если вы запускаете приложение при загрузке системы или вызываете его запуск после загрузки с помощью system, QProcess и т.д., то путь может быть неправильным. Чтобы гарантировать правильность пути, необходимо получить первый аргумент main функции argv и получить путь, просмотрев код Qt.
// Программа сначала получает путь и имя приложения
static void getCurrentInfo(char *argv[], QString &path, QString &name);
// Программа сначала считывает значение узла файла конфигурации
static QString getIniValue(const QString &fileName, const QString &key);
static QString getIniValue(char *argv[], const QString &key, const QString &dir = QString());
void QtHelper::getCurrentInfo(char *argv[], QString &path, QString &name)
{
// Необходимо использовать fromLocal8Bit для обеспечения корректного отображения китайских путей
QString argv0 = QString::fromLocal8Bit(argv[0]);
QFileInfo file(argv0);
path = file.path();
name = file.baseName();
}
QString QtHelper::getIniValue(const QString &fileName, const QString &key)
{
QString value;
QFile file(fileName);
if (file.open(QFile::ReadOnly | QFile::Text)) {
while (!file.atEnd()) {
QString line = file.readLine();
if (line.startsWith(key)) {
line = line.replace("\n", "");
line = line.trimmed();
value = line.split("=").last();
break;
}
}
}
return value;
}
QString QtHelper::getIniValue(char *argv[], const QString &key, const QString &dir)
{
QString path, name;
QtHelper::getCurrentInfo(argv, path, name);
QString fileName = QString("%1/%2%3.ini").arg(path).arg(dir).arg(name);
return getIniValue(fileName, key);
}
int main(int argc, char *argv[])
{
int openGLType = QtHelper::getIniValue(argv, "OpenGLType").toInt();
QtHelper::initOpenGL(openGLType);
QApplication a(argc, argv);
...
}
class ItemDelegate : public QItemDelegate
{
Q_OBJECT
public:
explicit ItemDelegate(QObject *parent = 0);
protected:
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
};
#include "itemdelegate.h"
ItemDelegate::ItemDelegate(QObject *parent) : QItemDelegate(parent)
{
}
void ItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QStyleOptionViewItem option2 = option;
QColor color = index.data(Qt::ForegroundRole).value<QColor>();
if (color.isValid() && color != option.palette.color(QPalette::WindowText)) {
option2.palette.setColor(QPalette::HighlightedText, color);
}
QItemDelegate::paint(painter, option2, index);
}
// Устанавливаем этот делегат для всех ячеек
ui->tableWidget->setItemDelegate(new ItemDelegate);
// Определение в файле проекта
// Если в текущем комплекте есть мультимедийный модуль, импортируем мультимедийный модуль
qtHaveModule(multimedia) {QT += multimedia}
// Модуль уже был импортирован в файл проекта через QT += multimedia
contains(QT, multimedia) {}
// Определение в исходном файле
#ifdef QT_MULTIMEDIA_LIB
qDebug() << "мультимедийный модуль включён";
#else
qDebug() << "мультимедийный модуль не включён";
#endif
При установке прозрачного цвета фона для области MDI обнаруживается, что QMdiArea{background:transparent;} не работает, даже если указан цвет QMdiArea{background:#ff0000;} или QMdiArea{background-color:#ff0000;}. Оказывается, нужно использовать механизм слабых свойств. QMdiArea{qproperty-background:transparent;}.
Когда в стиле включён отключённый стиль *:disabled{xxx}, обнаруживается, что дочернее окно MDI не может быть растянуто. Вероятно, это внутренняя ошибка Qt. Решение состоит в том, чтобы просто сбросить стиль границы отключённого класса для MDI SubWindow. QMdiSubWindow:disabled{border:8px solid rgba(0,0,0,0);}.
При использовании QProcess для выполнения команды или запуска исполняемого файла стандартный метод не поддерживает пути с пробелами, такими как Program Files. Необходимо добавить двойные кавычки до и после пути. Возможно, внутренняя обработка приводит к сбою из-за разделения строки пробелами. Обычный путь с кавычками также может работать нормально, поэтому лучше добавить кавычки для надёжности.
QString cmd = "c:/Program Files/a.exe";
// Следующий код может вызвать сбой выполнения
``` ```
QProcess::startDetached(cmd);
// 前后加上引号就可以正常执行
cmd = "\"" + cmd + "\"";
QProcess::startDetached(cmd);
void MainWindow::on_pushButton_clicked()
{
QElapsedTimer timer;
timer.start();
QString s;
QString text = "abc";
for (int i = 0; i < 10000; ++i) {
s = text.at(0);
}
qDebug() << "Способ 1" << timer.nsecsElapsed();
}
void MainWindow::on_pushButton_2_clicked()
{
QElapsedTimer timer;
timer.start();
QString text = "abc";
for (int i = 0; i < 10000; ++i) {
QString s = text.at(0);
}
qDebug() << "Способ 2" << timer.nsecsElapsed();
}
// В режиме отладки способ 1 быстрее способа 2 в 6 раз+
// В релизной сборке способ 1 быстрее способа 2 в 30 раз+
void MainWindow::on_pushButton_clicked()
{
QElapsedTimer timer;
timer.start();
for (int i = 0; i < 10000; ++i) {
Test *t = new Test;
// t->setId(i);
// t->setName("test");
t->getName();
}
qDebug() << "Способ 1" << timer.nsecsElapsed();
}
void MainWindow::on_pushButton_2_clicked()
{
QElapsedTimer timer;
timer.start();
for (int i = 0; i < 10000; ++i) {
Test *t = new Test;
// t->setProperty("id", i);
// t->setProperty("name", "test");
t->property("name").toString();
}
qDebug() << "Способ 2" << timer.nsecsElapsed();
}
// Результаты сравнительного тестирования не зависят от типа данных и конкретного типа переменной/int и QString
// Производительность метода setProperty хуже, чем у метода setxxx в 3 раза+
// Метод property работает в 1.3 раза медленнее, чем метод getxxx
По умолчанию, когда вы наведете курсор на окно QDockWidget, появится контекстное меню для отображения или скрытия закреплённого модуля. Если вы хотите его убрать, то обнаружите, что установка Qt::NoContextMenu или перехват событий с помощью фильтра не работают. Необходимо установить dockWidget->setContextMenuPolicy(Qt::PreventContextMenu).
В Qt есть значения по умолчанию для margin (поля) и spacing (расстояния) в макете. Если эти значения не установлены, они будут автоматически подобраны в зависимости от среды выполнения. Например, на компьютерах с разрешением 1080P и 2K эти значения могут отличаться. Важно помнить, что значения, которые вы видите в дизайнере интерфейса, могут не совпадать со значениями во время выполнения на целевой платформе. Если вам нужно контролировать эти значения, вы можете их переопределить. Для этого можно использовать универсальный подход — стиль-посредник. Создайте подкласс QProxyStyle и настройте стиль заново. Этот метод также является мощным инструментом для контроля внешнего вида пользовательского интерфейса. В конечном итоге все стили qss будут отображаться через этот стиль, что означает, что вы можете переопределить и контролировать внешний вид всех компонентов.
// Также можно наследовать встроенные стили Qt, такие как QFusionStyle/QCleanlooksStyle
class QCustomStyle : public QProxyStyle
{
public:
int pixelMetric(PixelMetric metric, const QStyleOption *option = 0, const QWidget *widget = 0) const {
if (metric == QStyle::PM_LayoutHorizontalSpacing || metric == QStyle::PM_LayoutVerticalSpacing) {
// Установить горизонтальное и вертикальное расстояние в макете равным 10
return 10;
} else if (metric == QStyle::PM_ButtonMargin) {
// Установить поля всех кнопок равными 20
return 20;
}
return QProxyStyle::pixelMetric(metric, option, widget);
}
};
qApp->setStyle(new QCustomStyle);
Версия | Место определения | Режим | int | bool |
---|---|---|---|---|
Qt4.7/mingw | Заголовок | Debug | 7077464 | true |
Qt4.7/mingw | Заголовок | Release | 48 | true |
Qt4.7/mingw | Функция | Debug | 2162216 | false |
Qt4.7/mingw | Функция | Release | 0 | false |
Qt5.7/msvc | Заголовок | Debug | -1 | true |
Qt5.7/msvc | Заголовок | Release | -1 | true |
Qt5.7/msvc | Функция | Debug | 1898108572 | false |
Qt5.7/msvc | Функция | Release | 18872512 | true |
Qt6.5/mingw | Заголовок | Debug | -1305540880 | true |
Qt6.5/mingw | Заголовок | Release | -1124044992 | true |
Qt6.5/mingw | Функция | Debug | 0 | false |
Qt6.5/mingw | Функция | Release | 0 | false |
void frmXXX::doAction() { QAction *action = (QAction *)sender(); QString text = action->text(); if (text == "全部选中") { ui->tableView->selectAll(); } else if (text == "反向选中") { //找到所有选中的行集合 QList rows; QModelIndexList list = ui->tableView->selectionModel()->selectedRows(); int count = list.count(); for (int i = 0; i < count; ++i) { rows << list.at(i).row(); }
//先清空所有选中
ui->tableView->clearSelection();
//不在选中行集合的则选中
count = ui->tableView->model()->rowCount();
for (int i = 0; i < count; ++i) {
if (!rows.contains(i)) {
ui->tableView->selectRow(i);
}
}
} else if (text == "清空选中") {
ui->tableView->clearSelection();
}
}
```cpp
//表示安卓或者ios平台
android|ios {}
//表示非安卓和非ios平台
!android::!ios {}
//表示非ios系统的mac系统
maxc:!ios {}
//表示非mac系统的unix系统
unix:!macx {}
Многие проекты становятся всё больше и больше, и иногда можно столкнуться с ситуацией, когда код, который раньше работал нормально, вдруг начинает вызывать проблемы после добавления в текущий проект. Когда это происходит, рекомендуется использовать два подхода для решения проблемы:
Сейчас многие Linux-системы используют Wayland в качестве рабочего стола. Это может привести к проблеме, так как отсутствие системы координат делает невозможным перемещение и позиционирование окон без границ (обычно Qt6 принудительно использует Wayland по умолчанию, а в Qt5 используется xcb). Чтобы решить эту проблему, добавьте следующую строку перед функцией main: qputenv("QT_QPA_PLATFORM", "xcb");
Иногда после экспорта файла вы хотите открыть файл в файловом менеджере и выделить только что открытый файл, чтобы пользователь мог его обработать. Для этого нужно выполнить команду:
QString path = "file:///e:/1.txt";
QProcess::startDetached("explorer.exe", QStringList() << "/select," << path);
В сигналах currentItemChanged у QTreeWidget/QTableWidget при выполнении соответствующего метода clear также активируется этот сигнал, поэтому необходимо обратить на это особое внимание. Два параметра, связанные с этим сигналом, current/previous, обозначают текущий и предыдущий узлы. Значения обоих параметров могут быть пустыми, поэтому при обработке параметров слота для этого сигнала необходимо сначала проверить, являются ли они пустыми указателями. Если этого не сделать, это может привести к сбою программы.
О разнице между += и *= в Qt: += означает добавление без дублирования, а *= — добавление без дублирования. Рекомендуется использовать *=, хотя += также работает нормально.
QT += core gui
QT += core gui
message($$QT) //会打印 core gui core gui
QT *= core gui
QT *= core gui
message($$QT) //会打印 core gui
DEFINES += abc
DEFINES += abc
message($$DEFINES) //会打印 abc abc
DEFINES *= abc
DEFINES *= abc
message($$DEFINES) //会打印 abc
Что касается различения Linux-систем в файле pro, то в комплекте Qt4 нет поддержки тега linux, поэтому нужно использовать unix:!macx для обозначения. Поэтому, если требуется совместимость с Qt4, рекомендуется использовать unix:!macx.
//如果是linux上的Qt4套件则下面只会打印 unix linux
//如果是linux上的Qt5/Qt6套件则下面会打印 linux unix linux
linux {message(linux)}
unix {message(unix)}
unix:!macx {message(linux)}
Для некоторых кроссплатформенных проектов, особенно тех, которые требуют использования сторонних библиотек, необходимо идентифицировать и обрабатывать соответствующие файлы библиотеки в разных системных архитектурах.
#区分不同的系统
path_sys = win
win32 {
path_sys = win
}
linux {
path_sys = linux
}
#Qt4套件不认识linux标记
unix:!macx {
path_sys = linux
}
macx {
path_sys = mac
}
android {
path_sys = android
}
#区分不同的位数 x86_64/amd64/arm64/arm64-v8a
path_bit = 32
contains(QT_ARCH, x.*64) {
path_bit = 64
} else:contains(QT_ARCH, a.*64) {
path_bit = 64
} else:contains(QT_ARCH, a.*64.*) {
path_bit = 64
}
#对应系统和位数的库目录
path_lib = lib$$path_sys$$path_bit
//下面会打印 libwin32/libwin64/liblinux32/liblinux64/libmac32/libmac64/libandroid32/libandroid64
message($$path_lib)
//使用方式
INCLUDEPATH += $$PWD/include
LIBS += -L$$PWD/$$path_lib/ -lxxx
Если во время сборки обнаружено, что некоторые компоненты Qt не соответствуют требованиям текущего проекта, можно отключить проект, чтобы избежать сбоя компиляции.
#禁用项目后整个项目的代码文件是灰色的不可用,编译会跳过。
lessThan(QT_MAJOR_VERSION, 6) {
error("最低要求Qt6才能用")
}
При использовании QButtonGroup для добавления кнопок и необходимости использования сигнала buttonClicked(int), необходимо вручную указать номер кнопки при добавлении. В противном случае номера кнопок по умолчанию будут равны -1, что приведёт к несоответствию ожидаемым значениям при срабатывании сигнала buttonClicked(int).
QButtonGroup *btnGroup = new QButtonGroup(this);
connect(btnGroup, SIGNAL(buttonClicked(int)), ui->stackedWidget, SLOT(setCurrentIndex(int)));
//第二个参数指定按钮编号
btnGroup->addButton(ui->btn1, 0);
btnGroup->addButton(ui->btn2, 1);
Улучшение производительности графиков QCustomplot:
QDir::setCurrent("f:/");
QImage img(":/image/bg_novideo.png");
// Результаты изображения сохраняются в f:/1.jpg
img.save("1.jpg", "jpg");
img.save("./1.jpg", "jpg");
// Правильный подход
// Сначала запомните текущий каталог
QString path = QDir::currentPath();
QDir::setCurrent("f:/");
xxxxxxx выполнение задачи
// Вернитесь к исходному каталогу
QDir::setCurrent(path);
// Результаты изображения сохраняются в текущем каталоге
img.save("1.jpg", "jpg");
#include "qquickwindow.h"
int main(int argc, char *argv[])
{
QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGL);
QApplication a(argc, argv);
}
Теперь новые версии Qt доступны только для онлайн-загрузки. Иногда в процессе загрузки появляется сообщение об ошибке, вероятно, из-за отсутствия упаковки некоторых плагинов-модулей на соответствующих серверах. Обычно это модули, заканчивающиеся на TP. Эти модули обычно не используются, поэтому для успешной установки нужно зайти в раздел выбора версии Qt и плагинов, открыть узел Additional Libraries, снять флажки с ненужных модулей, особенно тех, которые заканчиваются на TP, и продолжить установку.
В Qt можно использовать qputenv и qgetenv для установки и получения системных переменных окружения. Можно установить соответствующие значения в коде или в системных переменных окружения, например, в Windows. Это позволяет гибко настраивать значения переменных. Если код не может быть изменён, можно попробовать установить значение переменной окружения, распознаваемое Qt, чтобы добиться желаемого эффекта. Важно отметить, что изменения в переменных окружения вступят в силу только после перезагрузки системы или перезапуска Qt Creator, возможно, из-за механизма кэширования.
// Устанавливаем отдельные дескрипторы для каждого окна
QApplication a(argc, argv);
a.setAttribute(Qt::AA_NativeWindows);
// Вышеуказанный метод устанавливает через код / иногда уже в исполняемом файле / невозможно изменить код
// После изучения кода стало известно, что предпочтение отдаётся чтению через qgetenv, есть ли флаг QT_USE_NATIVE_WINDOWS
// Если присутствует, то значение присваивается в соответствии со значением переменной QT_USE_NATIVE_WINDOWS
// Недостаток этого метода в том, что он применяется ко всем программам Qt
#if (QT_VERSION < QT_VERSION_CHECK(5,2,0))
// Универсальный метод, совместимый со всеми версиями Qt
QPushButton *searchButton = new QPushButton;
// Выполнение соответствующей обработки
connect(searchButton, SIGNAL(clicked(bool)), this, SLOT(search()));
searchButton->setMinimumWidth(30);
searchButton->setIcon(QIcon(":/main.ico"));
// Создание экземпляра макета для размещения кнопки
QHBoxLayout *layout = new QHBoxLayout(ui->lineEdit);
layout->setContentsMargins(0, 0, 1, 0);
// Добавление кнопки с указанием выравнивания
layout->addWidget(searchButton, 0, Qt::AlignRight);
// Установка внешних отступов текста / свободное место для кнопки
ui->lineEdit->setTextMargins(0, 0, searchButton->minimumWidth() + 3, 0);
#else
// Рекомендуемый метод проще в использовании
QAction *searchAction = new QAction(ui->lineEdit);
// Выполнение соответствующей обработки
connect(searchAction, SIGNAL(triggered(bool)), this, SLOT(search()));
searchAction->setIcon(QIcon(":/main.ico"));
// TrailingPosition указывает на правую сторону / также может быть LeadingPosition для левой стороны
ui->lineEdit->addAction(searchAction, QLineEdit::TrailingPosition);
#endif
Примерно с версии 6.5 при компиляции отладочной сборки популярного графического виджета QCustomPlot с использованием mingw возникает ошибка «too many sections/file too big». Однако при использовании release-сборки или другого компилятора проблем не возникает. Достаточно добавить в файл pro следующую строку: QMAKE_CXXFLAGS += -Wa,-mbig-obj.
Примерно с начала 2024 года инструмент онлайн-установки Qt по умолчанию не загружает установочные пакеты Qt5. Необходимо отметить опцию Archive в правом верхнем углу, затем отфильтровать результаты, нажав на кнопку Filter, после чего станут видны пакеты Qt5. Вероятно, официальный сайт стремится побудить нас использовать Qt6, постепенно отказываясь от поддержки Qt5. Жаль, что Qt6 не поддерживает Windows 7, хотя у этой операционной системы всё ещё много пользователей.
При использовании функции drawText в Qt для рисования текста, если используются координаты QPoint, важно помнить, что начало координат находится в левом нижнем углу (как указано в документации Qt: «The y-position is used as the baseline of the font»). Это отличается от других фреймворков разработки, таких как C#. При работе с другими платформами рекомендуется использовать функцию void drawText(const QRect &r, const QString &text), которая позволяет указать область для отрисовки. Эту особенность легко упустить из виду, что может привести к катастрофическим последствиям.
При открытии веб-страницы с использованием модуля веб-браузера webengine в Linux некоторые системы могут столкнуться с аварийными сбоями. Даже при запуске встроенного примера браузера simplebrowser страница не открывается должным образом. Причина кроется в мерах безопасности, связанных с песочницей. Чтобы решить эту проблему, необходимо установить переменную окружения. Просто добавьте строку qputenv("QTWEBENGINE_DISABLE_SANDBOX", "1") в начале функции main. 305. После установки флажка в QListWidgetItem иногда требуется сигнал уведомления при переключении флажков, чтобы можно было выполнить обработку. Здесь вы обнаружите, что среди всех сигналов QListWidget такого сигнала нет. Изучив исходный код функции setCheckState класса QListWidgetItem, вы узнаете, что отправляется сигнал dataChanged, который генерируется моделью данных QListWidget, так что всё становится проще.
void Form::on_listWidget_itemPressed(QListWidgetItem *item)
{
// Переключение состояния выбора при нажатии мыши
item->setCheckState(item->checkState() == Qt::Checked ? Qt::Unchecked : Qt::Checked);
}
void Form::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
{
// Поиск узла через текст, а не через выбранный узел, потому что можно установить флажок перед текущим элементом
QListWidgetItem *item;
QString text = topLeft.data().toString();
int count = ui->listWidget->count();
for (int i = 0; i < count; ++i) {
item = ui->listWidget->item(i);
if (item->text() == text) {
break;
}
}
// Обработка после нахождения соответствующего узла
}
При удалённом анализе проблем на компьютерах многих пользователей было обнаружено, что данные проблемы имеют общую причину: хотя некоторые файлы базы данных или конфигурационные файлы были явно изменены, при запуске программы старые данные всё равно отображались. Несмотря на тщательное изучение программы, источник проблемы не был найден. В конце концов выяснилось, что программа считывала файл, который не был изменён. Это произошло из-за наличия нескольких копий одного и того же каталога кода в среде пользователя. Открываемый каталог был другой копией, и все изменения не влияли на текущий исполняемый файл программы. В области больших данных и статистики такая ситуация встречается довольно часто, составляя примерно одну пятую часть всех случаев. Наличие нескольких копий кода часто неизбежно, например, для временного резервного копирования кода перед тестированием новых функций. Однако важно помнить о текущем состоянии проекта и проверять правильность каталога при возникновении проблем, чтобы убедиться, что используется правильный каталог кода.
При использовании большого количества сторонних библиотек можно заметить, что некоторые из них зависят от компилятора. Например, OpenCV, скомпилированный с помощью MSVC, может быть использован только с этим компилятором. Если попытаться использовать Mingw, то возникнет ошибка. Для успешного использования Mingw необходимо скомпилировать OpenCV с ним. С другой стороны, библиотеки ffmpeg не имеют такой зависимости, и могут быть использованы как с MSVC, так и с Mingw. Это связано с тем, что ffmpeg является чистым C проектом, в то время как OpenCV — это чистый C++ проект, который включает в себя проблемы ABI, характерные для C++. C++ — это сложный язык программирования, поддерживающий наследование и полиморфизм, поэтому компилятору необходимо определить точные правила вызова функций, включая соглашения о вызовах (различие имён функций, ввод параметров, управление стеком), возвращаемые типы и списки параметров. Разные компиляторы могут иметь разные правила для этих аспектов даже на одной архитектуре. Таким образом, библиотеки, скомпилированные из чистого C проекта, не требуют учёта компилятора, в то время как библиотеки C++ требуют этого. Автор QtAV создал новый проект MDK, написанный на чистом C, который предоставляет библиотеки для Mingw и MSVC. Библиотеки, скомпилированные любым компилятором, совместимы как с Mingw, так и с MSVC. Это преимущество чистого C подхода.
Иногда нам нужно записать файл на диск, но если соответствующий каталог не существует, запись может завершиться неудачно. В этом случае необходимо сначала проверить существование каталога. Если его нет, то создать его. QDir предоставляет два метода для создания каталогов: mkdir и mkpath. Раньше считалось, что эти методы выполняют одинаковые функции, подобно size и length, но это не так. mkdir создаёт только последний каталог в пути. Если родительский каталог не существует, создание завершается неудачей. Mkpath, с другой стороны, проверяет существование каждого уровня пути и создаёт недостающие каталоги. Рекомендуется использовать mkpath для обеспечения успешного создания всего пути.
QDir dir;
// Создание каталога "f:/path/dir"
dir.mkdir("f:/path/dir");
// Сначала будет создан "path", затем "dir"
dir.mkpath("f:/path/dir");
При добавлении JS файла в проект Widget и его компиляции может возникнуть ошибка qmlcache_loader.o:qmlcache_loader.cpp:(.text+0x32). Это происходит потому, что Qt по умолчанию включает qtquickcompiler для предварительной обработки всех JS файлов в ресурсах, ускоряя их загрузку в QML движок. Однако в некоторых случаях мы используем эти файлы не для загрузки в QML, а, например, в QtWebEngine для взаимодействия или выполнения JS функций в Widget для получения результатов. Можно отключить qtquickcompiler в настройках проекта Qt Creator или добавив строку CONFIG -= qtquickcompiler в файл pro. Эта проблема специфична для Qt и не присутствует в версии 5.15.2 и более поздних.
По умолчанию, новый QtCreator использует build директорию внутри исходного кода для компиляции. Ранее использовалась отдельная директория build-xxx на том же уровне, что и исходный код. Лично мне больше нравится предыдущий подход, когда все временные файлы компиляции хранятся отдельно от исходного кода. Их можно легко удалить, когда они не нужны, оставляя исходный код чистым. Qt Creator также предоставляет возможность настроить путь к директории компиляции в настройках. Перейдите в раздел «Настройки» → «Сборка и запуск» → «Свойства сборки по умолчанию» и измените путь к папке компиляции на ../build-%{Project:Name}-%{Kit:FileSystemName}-%{BuildConfig:Name}.
Несколько замечаний о задержках в потоковом мультимедиа.
Комплексная задержка: минимальная задержка может составлять 200–300 мс, например, для rtsp-видеопотока, если требуется высокая степень реалтайма, можно обойтись без кэширования и синхронизации аудио и видео, декодировать и воспроизводить сразу после получения. Для hls максимальная задержка обычно составляет около 5 секунд, а для flv — около 3 секунд.
Окончательный вывод: учитывая как реалтайм, так и поддерживаемые форматы аудио и видео, я лично рекомендую использовать потоковую передачу rtsp (поддерживаются самые удобные форматы аудио и видео, например, поддерживается кодек 265), а потоковую передачу в веб — ws-flv (поддерживается множество форматов, нет ограничения на шесть источников). Для потоковой передачи в исполняемый файл рекомендуется использовать rtsp (множество поддерживаемых форматов и наилучший реалтайм, возможна декодирование и воспроизведение с максимальной скоростью), а для веб-страницы хотя webrtc и обеспечивает наилучший реалтайм, но не поддерживает кодек 265, что усложняет задачу.
// Установка китайского языка в информационном окне
QMessageBox dialog(QMessageBox::Question, "询问", text);
dialog.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
dialog.button(QMessageBox::Yes)->setText("确 定");
dialog.button(QMessageBox::No)->setText("取 消");
return dialog.exec();
// Установка китайского языка в поле ввода
QInputDialog dialog;
dialog.setOkButtonText("确定");
dialog.setCancelButtonText("取消");
return dialog.exec();
// Установка китайского языка в диалоговом окне файла
QFileDialog dialog;
dialog.setOption(QFileDialog::DontUseNativeDialog, true);
QLabel *lookinLabel = dialog.findChild<QLabel*>("lookInLabel");
lookinLabel->setText("文件目录:");
// Вызов метода QtHelper::search(ui->treeWidget, "测试", 5);
void QtHelper::search(QTreeWidget *treeWidget, const QString &key, int level)
{
// Найти все совпадающие узлы
QList<QTreeWidgetItem *> items = treeWidget->findItems(key, Qt::MatchContains | Qt::MatchRecursive);
// Добавить совпадающие узлы в очередь / родительский узел узла также считается совпадением / иначе родительский узел скроет дочерние узлы, которые также будут скрыты
QList<QTreeWidgetItem *> itemAll;
foreach (QTreeWidgetItem *item, items) {
// Добавить всех родительских узлов текущего узла / несколько циклов соответствуют нескольким уровням
for (int i = 0; i < level; ++i) {
// Избегаем дублирования добавления
if (!itemAll.contains(item)) {
itemAll << item;
}
// Пустой указатель означает отсутствие родительского узла, выход из цикла
item = item->parent();
if (!item) {
break;
}
}
}
// Пройти по всем узлам / отобразить совпадающие узлы, иначе скрыть их
QTreeWidgetItemIterator it(treeWidget);
while (*it) {
(*it)->setHidden(!itemAll.contains(*it));
++it;
}
}
Универсальный метод: установите версию 5.15, найдите функцию, вызывающую ошибку, переключитесь на исходный файл заголовка и увидите соответствующие подсказки, такие как QT_DEPRECATED_X("Use sizeInBytes"), и новую функцию. Измените код в соответствии с этими подсказками. Некоторые функции были добавлены в версиях Qt5.7, 5.9, 5.10 и т. д., возможно, ваш проект всё ещё использует методы Qt4, но Qt6 полностью совместим с этими старыми методами. До Qt6 необходимо было использовать новые методы. PS: если сама функция является новой функцией в Qt6, этот метод не работает.
Qt6 разделил ядро core на core5compat, поэтому вам нужно добавить соответствующий модуль в pro-файл и включить соответствующий заголовок в коде.
// Добавление модуля в pro файл
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
greaterThan(QT_MAJOR_VERSION, 5): QT += core5compat
// Включение заголовка в код
#if (QT_VERSION >= QT_VERSION_CHECK(5,0,0))
#include <QtWidgets>
#endif
#if (QT_VERSION >= QT_VERSION_CHECK(6,0,0))
#include <QtCore5Compat>
#endif
#if (QT_VERSION >= QT_VERSION_CHECK(6,0,0))
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::Floor);
#endif
Существующие функции случайных чисел предлагают заменить их на QRandomGenerator. Чтобы обеспечить совместимость со всеми версиями qt, минимальные изменения заключаются в использовании случайных чисел C++, таких как srand вместо qsrand и rand вместо qrand. После проверки исходного кода выясняется, что они фактически инкапсулированы в случайные числа C++. Многие аналогичные инкапсуляции, такие как qSin, инкапсулируют sin.
light и dark в QColor заменены на lighter и darker соответственно, хотя методы lighter и darker всегда существовали.
fm.width в QFontMetricsF заменен на fm.horizontalAdvance начиная с версии 5.11.
Значения перечисления QPalette, Foreground = WindowText, Background = Window, где Foreground и Background больше не доступны, должны быть заменены на WindowText и Window соответственно. Аналогично, setTextColor заменен на setForeground.
delta() в QWheelEvent заменено на angleDelta().y(), а pos() заменено на position().
Модуль svg был разделен на svgwidgets, если вы используете этот модуль, вам нужно добавить QT += svgwidgets в pro. То же самое относится и к модулю opengl, который был разделен на openglwidgets. contentsMargins().left(),когда четыре значения одинаковы, по умолчанию все четыре значения будут одинаковыми. Аналогично, setMargin был удалён, полностью используется setContentsMargins.
Предыдущий QChar c = 0xf105 должен быть заменён на принудительное преобразование QChar c = (QChar)0xf105, неявное преобразование больше не поддерживается, иначе компиляция выдаст ошибку error: conversion from 'int' to 'QChar' is ambiguous.
Некоторые функции, такие как qSort, были заменены на std::sort из C++.
#if (QT_VERSION >= QT_VERSION_CHECK(6,0,0))
std::sort(ipv4s.begin(), ipv4s.end());
#else
qSort(ipv4s);
#endif
Qt::WA_NoBackground был заменён на Qt::WA_OpaquePaintEvent.
Класс QMatrix был упразднён, вместо него используется QTransform, функции класса в основном аналогичны, класс QTransform существовал в Qt4.
QTime для отсчёта времени был исключён, его следует заменить на QElapsedTimer, этот класс также присутствовал в Qt4.
Функция QApplication::desktop() была удалена, вместо неё используется QApplication::primaryScreen().
#if (QT_VERSION > QT_VERSION_CHECK(5,0,0))
#include "qscreen.h"
#define deskGeometry qApp->primaryScreen()->geometry()
#define deskGeometry2 qApp->primaryScreen()->availableGeometry()
#else
#include "qdesktopwidget.h"
#define deskGeometry qApp->desktop()->geometry()
#define deskGeometry2 qApp->desktop()->availableGeometry()
#endif
// Получение текущего индекса экрана
int QtHelper::getScreenIndex()
{
// Необходимо обработать несколько экранов
int screenIndex = 0;
#if (QT_VERSION >= QT_VERSION_CHECK(5,0,0))
int screenCount = qApp->screens().count();
#else
int screenCount = qApp->desktop()->screenCount();
#endif
if (screenCount > 1) {
// Нахождение экрана, на котором находится курсор
QPoint pos = QCursor::pos();
for (int i = 0; i < screenCount; ++i) {
#if (QT_VERSION >= QT_VERSION_CHECK(5,0,0))
if (qApp->screens().at(i)->geometry().contains(pos)) {
#else
if (qApp->desktop()->screenGeometry(i).contains(pos)) {
#endif
screenIndex = i;
break;
}
}
}
return screenIndex;
}
// Получение области экрана
QRect QtHelper::getScreenRect(bool available)
{
QRect rect;
int screenIndex = QtHelper::getScreenIndex();
if (available) {
#if (QT_VERSION >= QT_VERSION_CHECK(5,0,0))
rect = qApp->screens().at(screenIndex)->availableGeometry();
#else
rect = qApp->desktop()->availableGeometry(screenIndex);
#endif
} else {
#if (QT_VERSION >= QT_VERSION_CHECK(5,0,0))
rect = qApp->screens().at(screenIndex)->geometry();
#else
rect = qApp->desktop()->screenGeometry(screenIndex);
#endif
}
return rect;
}
// Установка ограничения на ввод только цифр и десятичных знаков
QString pattern = "^-?[0-9]+([.]{1}[0-9]+){0,1}$";
// Настройка проверки IP-адреса
QString pattern = "(2[0-5]{2}|2[0-4][0-9]|1?[0-9]{1,2})";
// Точное выражение QRegularExpression QRegularExpressionValidator доступно с версии 5.0 5.1
#if (QT_VERSION >= QT_VERSION_CHECK(6,0,0))
QRegularExpression regExp(pattern);
QRegularExpressionValidator *validator = new QRegularExpressionValidator(regExp, this);
#else
QRegExp regExp(pattern);
QRegExpValidator *validator = new QRegExpValidator(regExp, this);
#endif
lineEdit->setValidator(validator);
// Имитация колёсика мыши
#if (QT_VERSION < QT_VERSION_CHECK(6,0,0))
QWheelEvent wheelEvent(QPoint(0, 0), -scal, Qt::LeftButton, Qt::NoModifier);
#else
QWheelEvent wheelEvent(QPointF(0, 0), QPointF(0, 0), QPoint(0, 0), QPoint(0, -scal), Qt::LeftButton, Qt::NoModifier, Qt::ScrollBegin, false);
#endif
QApplication::sendEvent(widget, &wheelEvent);
// Прямое изменение значений колёсика мыши
QWheelEvent *whellEvent = (QWheelEvent *)event;
// Угол прокрутки, *8 — расстояние прокрутки мыши
#if (QT_VERSION < QT_VERSION_CHECK(6,0,0))
int degrees = whellEvent->delta() / 8;
#else
int degrees = whellEvent->angleDelta().x() / 8;
#endif
// Количество прокрутки,*15 — угол прокрутки мышью
int steps = degrees / 15;
init в QStyleOption был заменён на initFrom.
QVariant::Type был заменён на QMetaType::Type, предыдущий QVariant::Type фактически инкапсулировал QMetaType::Type.
Все версии QStyleOptionViewItemV2 V3 V4 и т. д. были исключены, временно можно использовать QStyleOptionViewItem в качестве замены.
Одна из перегруженных функций resolve в QFont была заменена на resolveMask.
Метод setIniCodec в QSettings был удалён, по умолчанию используется utf8, настройка не требуется.
Сигналы activated(QString) и currentIndexChanged(QString) в qcombobox были удалены, используется версия с параметром int index, затем значение получается через индекс. Лично я считаю, что удалять их не было необходимости. Модуль qtscript полностью исчез, хотя ещё с поздних версий Qt5 он был объявлен устаревшим и продолжал существовать до тех пор, пока его официально не упразднили в Qt6. Все виды анализа данных JSON были заменены на анализ класса qjson.
Многие перегруженные функции QString параметров методов append, indexOf, lastIndexOf и других подобных для QByteArray были упразднены. Чтобы передать QByteArray напрямую, нужно добавить .toUtf8() к исходной основе параметров. Это видно при просмотре исходного кода, где прежние параметры QString также преобразуются в .toUtf8(), а затем используются для сравнения.
Функции преобразования времени QDateTime toTime_t и setTime_t были переименованы в toSecsSinceEpoch и setSecsSinceEpoch соответственно. Эти два метода были добавлены в Qt5.8.
Функция pixmap для QLabel ранее была указателем (*pixmap()), теперь она заменена ссылкой (pixmap()).
В методе sortByColumn класса QTableWidget был удалён метод сортировки по умолчанию в порядке возрастания, теперь необходимо указать второй параметр, чтобы обозначить, будет ли сортировка по возрастанию или убыванию.
В модуле qtnetwork (для сокетов TCP/UDP) сигнал об ошибке error был заменён на errorOccurred. Однако в случае с websocket название осталось прежним — error.
#if (QT_VERSION >= QT_VERSION_CHECK(6,0,0))
connect(udpSocket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)), this, SLOT(error()));
connect(tcpSocket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)), this, SLOT(error()));
#else
connect(udpSocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(error()));
connect(tcpSocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(error()));
#endif
// Особенно обратите внимание, что в websocket всё ещё используется error
connect(webSocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(error()));
Модуль XmlPatterns больше не существует, вместо этого используется модуль xml для повторного анализа.
Изменился тип параметра nativeEvent.
#if (QT_VERSION >= QT_VERSION_CHECK(6,0,0))
bool nativeEvent(const QByteArray &eventType, void *message, qintptr *result);
#else
bool nativeEvent(const QByteArray &eventType, void *message, long *result);
#endif
QButtonGroup *btnGroup = new QButtonGroup(this);
#if (QT_VERSION >= QT_VERSION_CHECK(6,0,0))
connect(btnGroup, SIGNAL(idClicked(int)), ui->xstackWidget, SLOT(setCurrentIndex(int)));
#else
connect(btnGroup, SIGNAL(buttonClicked(int)), ui->xstackWidget, SLOT(setCurrentIndex(int)));
#endif
#if (QT_VERSION >= QT_VERSION_CHECK(6,0,0))
QWebEngineSettings *webSetting = QWebEngineProfile::defaultProfile()->settings();
#else
QWebEngineSettings *webSetting = QWebEngineSettings::defaultSettings();
#endif
#if (QT_VERSION >= QT_VERSION_CHECK(6,0,0))
void enterEvent(QEnterEvent *);
#else
void enterEvent(QEvent *);
#endif
// После подсказки от JasonWong, рекомендуется использовать override для всех виртуальных функций, переопределённых в дочернем классе.
// Это приведёт к ошибке компиляции, если функция или параметры родительского класса будут изменены, вместо того чтобы компилироваться, но работать некорректно, обвиняя Qt.
// Родительская функция
virtual void enterEvent(QEvent *event);
// Дочерний класс должен использовать override
void enterEvent(QEvent *event) override;
В Qt6 несколько классов были объединены. Например, QVector теперь является псевдонимом QList, что означает, что эти два класса являются одним и тем же классом без каких-либо различий. Возможно, Qt объединил преимущества обоих внутри и попытался переписать алгоритмы или другие процессы, чтобы избежать недостатков. Аналогично, QStringList теперь является псевдонимом для QList, это один и тот же класс без отдельного существования.
По умолчанию родительский класс конструктора QWidget в Qt4 равен 0, в Qt5 — Q_NULLPTR, а в Qt6 используется стандартный nullptr из C++ стандарта, а не пользовательское определение Qt Q_NULLPTR (то же самое относится и к Q_DECL_OVERRIDE, который был заменен на override и т. д.). Возможно, это сделано для того, чтобы окончательно избавиться от исторического бремени и принять будущее.
// Ниже приведены записи для Qt4/5/6
MainWindow(QWidget *parent = 0);
MainWindow(QWidget *parent = Q_NULLPTR);
MainWindow(QWidget *parent = nullptr);
// Просмотр исходного кода Qt показывает, что Q_NULLPTR определяется на основе компилятора
#ifdef Q_COMPILER_NULLPTR
# define Q_NULLPTR nullptr
#else
# define Q_NULLPTR NULL
#endif
// Qt поддерживает более старые версии, такие как Qt5/6, используя запись *parent = 0.
Поддержка мультимедийного модуля была добавлена в версии Qt6.2, но при компиляции с помощью mingw-компилятора всё ещё возникают проблемы. Проблема была решена только в версии 6.2.2. Согласно официальному объяснению, проблема связана с тем, что версия mingw-компилятора не поддерживается, а версия 6.2.2 использует новый mingw900_64, который поддерживает эту версию. Поэтому рекомендуется начать использовать новую версию Qt6 с версии 6.2.2.
Метод setCodec в QTextStream был заменён на setEncoding, параметры изменились, а функциональность стала более мощной. ``` stream.setEncoding(QStringConverter::Utf8); stream.setEncoding(QStringConverter::System); #endif
43. Функция поиска дочернего узла child у QModelIndex была удалена, но функция поиска родительского узла parent осталась. После изучения кода стало ясно, что предыдущая функция child была обёрткой функции model->index(row, column, QModelIndex).
```cpp
// Эти две функции эквивалентны. Если требуется совместимость с Qt456, используйте следующий метод:
QModelIndex index = indexParent.child(i, 0);
QModelIndex index = model->index(i, 0, indexParent);
// Эти две функции эквивалентны. Если требуется совместимость с Qt456, используйте следующий метод:
QModelIndex indexChild = index.child(i, 0);
QModelIndex indexChild = model->index(i, 0, index);
#if (QT_VERSION >= QT_VERSION_CHECK(5,0,0))
QPixmap pixmap = QApplication::primaryScreen()->grabWindow(widget->winId());
#else
QPixmap pixmap = QPixmap::grabWidget(widget->winId());
#endif
// Qt6 ранее поддерживал выполнение полной команды
QProcess p;
p.start("wmic cpu get Name");
// В Qt6 необходимо использовать следующий метод, который также совместим с Qt4, 5 и 6
p.start("wmic", QStringList() << "cpu" << "get" << "Name");
// Qt4/5 Установка выравнивания текста метки через таблицу стилей
ui->label->setStyleSheet("qproperty-alignment:AlignRight;");
// Qt4/5 Установка центрирования текста метки через таблицу стилей
ui->label->setStyleSheet("qproperty-alignment:AlignHCenter|AlignVCenter;");
// Qt6 Установка выравнивания текста метки вправо вверх через таблицу стилей (пересмотреть значение AlignRight = 2)
ui->label->setStyleSheet("qproperty-alignment:2;");
// Qt6 Центрирование текста метки через таблицу стилей (найти значение AlignHCenter|AlignVCenter = 0x04|0x80 = 0x84 = 132)
ui->label->setStyleSheet("qproperty-alignment:132;");
#if (QT_VERSION >= QT_VERSION_CHECK(6,2,0))
#define AudioInput QAudioSource
#define AudioOutput QAudioSink
#else
#define AudioInput QAudioInput
#define AudioOutput QAudioOutput
#endif
// Для использования достаточно new
AudioInput *input = new AudioInput(format, this);
#if (QT_VERSION >= QT_VERSION_CHECK(6,2,0))
#define QAudioInput QAudioSource
#define QAudioOutput QAudioSink
#endif
// Для использования достаточно new
QAudioInput *input = new QAudioInput(format, this);
Начиная с Qt6, CMake используется по умолчанию, поэтому в новых версиях Qt Creator при создании проекта по умолчанию выбирается CMake. Многие новички, впервые использующие его, могут быть озадачены тем, что структура сгенерированных проектов внезапно изменилась. Поэтому в процессе создания проекта рекомендуется выбрать qmake, и после первого выбора он будет использоваться по умолчанию.
В версии Qt6.4 функции count для классов QString и QByteArray были упразднены, вместо них используются функции size и length, что, вероятно, более точно.
Версия Qt6.4.1 содержит множество ошибок (BUG), которые настоятельно не рекомендуется использовать, например, отсутствие звука при использовании QAudioSink для воспроизведения звука https://bugreports.qt.io/browse/QTBUG-108383 и серьёзное искажение при масштабировании DPI https://bugreports.qt.io/browse/QTBUG-108593. Эти ошибки отсутствуют в версиях 6.4.0 и 6.5.0.
В версии Qt6.5 был удалён конструктор по умолчанию для класса QVariant. Теперь для предотвращения предупреждений необходимо использовать QVariant(QVariant::Invalid) вместо return QVariant(). При печати значения обнаруживается, что QVariant() сам по себе равен QVariant(QVariant::Invalid), поэтому для совместимости с Qt456 рекомендуется использовать QVariant(QVariant::Invalid).
package org.qt;
import org.qt.QtAndroidData;
public class QtAndroidTest
{
//需要通过实例来调用 测试发现不论 private public 或者不写都可以调用 我擦
private void printText()
{
System.out.println("printText");
}
public static void printMsg()
{
System.out.println("printMsg");
}
public static void printValue(int value)
{
System.out.println("printValue:" + value);
}
public static void setValue(float value1, double value2, char value3)
{
System.out.println("value1:" + value1 + " value2:" + value2 + " value3:" + value3);
}
public static int getValue()
{
return 65536;
}
public static int getValue(int value)
{
return value + 1;
}
public static void setMsg(String message)
{
System.out.println("setMsg:" + message);
}
public static String getMsg()
{
return "hello from java";
}
public static void setText(int value1, float value2, boolean value3, String message)
{
System.out.println("value1:" + value1 + " value2:" + value2 + " value3:" + value3 + " message:" + message);
}
public static String getText(int value1, float value2, boolean value3, String message)
{
//同时演示触发静态函数发给Qt
QtAndroidData.receiveData("message", "你好啊 java");
//下面两种办法都可以拼字符串
return "value1:" + value1 + " value2:" + value2 + " value3:" + value3 + " message:" + message;
//return "value1:" + String.valueOf(value1) + " value2:" + String.valueOf(value2) + " value3:" + String.valueOf(value3) + " message:" + message;
}
}
#include "androidtest.h"
//java类对应的包名+类名
#define className "org/qt/QtAndroidTest"
void AndroidTest::test()
{
jint a = 12;
jint b = 4;
//可以直接调用java内置类中的方法
jint max = QAndroidJniObject::callStaticMethod<jint>("java/lang/Math", "max", "(II)I", a, b);
//jclass javaMathClass = "java/lang/Math";
jdouble value = QAndroidJniObject::callStaticMethod<jdouble>("java/lang/Math", "random");
qDebug() << "111" << max << value;
}
void AndroidTest::printText()
{
QAndroidJniEnvironment env;
jclass clazz = env.findClass(className);
QAndroidJniObject obj(clazz);
obj.callMethod<void>("printText");
}
void AndroidTest::printMsg()
{
#if 0
//查看源码得知不传入jclass类的函数中内部会自动根据类名查找jclass
QAndroidJniEnvironment env;
jclass clazz = env.findClass(className);
QAndroidJniObject::callStaticMethod<void>(clazz, "printMsg");
#else
//没有参数和返回值可以忽略第三个参数
QAndroidJniObject::callStaticMethod<void>(className, "printMsg");
//QAndroidJniObject::callStaticMethod<void>(classNameTest, "printMsg", "()V");
#endif
}
void AndroidTest::printValue(int value)
{
QAndroidJniObject::callStaticMethod<jint>(className, "printValue", "(I)I", (jint)value);
}
void AndroidTest::setValue(float value1, double value2, char value3)
{
QAndroidJniObject::callStaticMethod<void>(className, "setValue", "(FDC)V", (jfloat)value1, (jdouble)value2, (jchar)value3);
}
int AndroidTest::getValue(int value)
{
//java类中有两个 getValue 函数 一个需要传参数
//jint result
``` Если библиотека представляет собой библиотеку Java (Java Library), то в результате компиляции получится файл jar, а имя пакета по умолчанию — com.example.lib.MyClass. Рекомендуется выбрать библиотеку Java, чтобы после компиляции не искать jar-файл среди множества других файлов.
- Третий шаг: придумайте название библиотеки, затем выберите минимальную версию SDK в соответствии с требованиями проекта —> готово.
- Четвёртый шаг: в только что созданном библиотечном проекте mylibrary найдите подпапку src/main/java/com.example.mylibrary и щёлкните правой кнопкой мыши для создания нового класса. Помните, что это не узел java или любой другой узел.
- Пятый шаг: напишите свои методы класса и функции.
```cpp
package com.example.mylibrary;
public class Test {
public static int add(int a, int b) {
return a + b;
}
}
int AndroidJar::add(int a, int b)
{
#ifdef Q_OS_ANDROID
const char *className = "com/example/mylibrary/Test";
jint result = QAndroidJniObject::callStaticMethod<jint>(className, "add", "(II)I", (jint)a, (jint)b);
return result;
#endif
}
Распознавание переключения между горизонтальной и вертикальной ориентацией экрана в Qt будет отражено в событии resizeEvent. Вы можете прочитать текущую ориентацию экрана (горизонтальную или вертикальную) после изменения размера и внести соответствующие изменения в интерфейс. Например, если элементы расположены сверху вниз, измените их расположение на слева направо.
Поскольку формат файла конфигурации AndroidManifest.xml для разных версий Qt отличается, шаблоны для высоких и низких версий несовместимы. Поэтому рекомендуется использовать файл AndroidManifest.xml, созданный для вашей версии Qt. Если вы используете пользовательский java-файл запуска, вам нужно заменить android:name="org.qtproject.qt5.android.bindings.QtActivity" в файле AndroidManifest.xml.
Если вы поместили jar-файл, скомпилированный в Android Studio, в каталог libs Qt-проекта и компиляция не удалась, выдавая сообщение об ошибке типа com.android.dx.cf.iface.ParseException: bad class file magic, это может быть связано с несоответствием версий JDK. Вам может потребоваться изменить версию JDK в проекте Android Studio. Например, если вы используете NDK r14, вам следует выбрать JDK 1.6 или 1.7. Как правило, более новые версии JDK совместимы со старыми, поскольку старые версии NDK не поддерживают JDK 1.8. Позже было обнаружено, что если вы создаете новую библиотеку Java (Java Library), такой проблемы не возникает. Но если вы выберете Android library, такая проблема может возникнуть.
Файл конфигурации проекта Android имеет фиксированное имя AndroidManifest.xml. Изменение его на другое имя не будет распознано. Не думайте, что изменение имени на другое приведет к тому, что оно не будет распознано должным образом.
Поле package="org.qtproject.example" в файле AndroidManifest.xml является именем пакета, которое также является уникальным идентификатором всего приложения apk. Если два apk имеют одинаковое имя пакета, они будут перезаписывать друг друга. Поэтому важно убедиться, что разные приложения имеют уникальные имена пакетов. Это имя пакета также определяет имя пакета для импорта в java-файлах, например import org.qtproject.example.R. Если имена пакетов отличаются, компиляция завершится неудачно.
Создание среды разработки Android с использованием новой версии qtc стало очень простым, в отличие от ранних версий, когда требовалось много времени и усилий для загрузки и установки различных компонентов. Теперь достаточно установить файл JDK (jdk_8.0.1310.11_64.exe), и все будет настроено по умолчанию за один шаг. Затем в настройках Qt для Android укажите путь установки JDK. Откройте файлы D:\Qt\Qt6\Tools\QtCreator\share\qtcreator\android\sdk_definitions.json и C:\Users\Administrator\AppData\Roaming\QtProject\qtcreator\android\sdk_definitions.json. Измените строку cmdline-tools;latest на cmdline-tools;6.0. Этот шаг очень важен, так как использование latest приведет к неполной загрузке SDK/NDK при автоматическом скачивании. После внесения изменений настройте каталог сохранения SDK, нажав кнопку Set Up SDK справа. Это автоматически загрузит множество файлов, включая каталог openssl внизу. Эти файлы можно легко скачать онлайн, и кнопка для скачивания доступна справа. После завершения загрузки вы сможете начать увлекательное путешествие по разработке приложений для Android.
Что касается взаимодействия со строковыми типами, вам нужно использовать Ljava/lang/String;. Помните, что точка с запятой в конце обозначает конец текущего объекта. ``` QJniObject result = QJniObject::callStaticObjectMethod(className, "getVersion", "()Ljava/lang/String;"); ,里面的分号必不可缺,要是换成 QJniObject result = QJniObject::callStaticObjectMethod(className, "getVersion", "()Ljava/lang/String") 执行是失败的。
可以通过 QJniObject::isClassAvailable("com/example/lib/MyClass") 来判断类是否可用。
在安卓的java类中,如果是独立的一个java类,常规的参数int/float/string之类的,在Qt中都非常容易传递参数,唯独窗体上下文context、活动窗体activity、两种参数比较麻烦,很多人就困在这里。其实Qt就提供了函数获取对应的实例,在Qt5中是QtAndroid::androidActivity()/QtAndroid::androidContext(),在Qt6中是QNativeInterface::QAndroidApplication::context().但是有没有发现,如果每次都这么传,如果很多函数都需要这个参数,感觉重复代码不少,所以强烈推荐第二个方法,新建的java类中,搞个private static Context context,然后提供一个public static setContext(Context context)函数,在主窗体activity开始创建的onCreate函数中,赋值xxx.setContext即可。这样就相当于在java这边传递好了对应的上下文或者窗体参数,然后暴露出来的接口就无须context这些参数了,省去了不少麻烦。
//java类中函数原型
public static void getBattery(Context context)
//qt5类中调用
QAndroidJniObject::callStaticMethod<void>("org/qt/QtAndroidReceiver", "getBattery", "(Landroid/content/Context;)V", QtAndroid::androidActivity().object());
//qt6类中调用
QJniObject::callStaticMethod<void>("org/qt/QtAndroidReceiver", "getBattery", "(Landroid/content/Context;)V", QNativeInterface::QAndroidApplication::context());
//方法2通过启动窗体赋值再调用
public class abc
{
private static Context context;
public static void setContext(Context context)
{
abc.context = context;
}
public static void getBattery()
{
abc.context.xxx;
}
}
//下面这个窗体作为启动窗体
public class QtAndroidActivity extends QtActivity
{
//必须在窗体创建的时候下面赋值
@Override
public void onCreate(Bundle savedInstanceState)
{
abc.setContext(getApplicationContext());
}
}
//qt类中调用就方便了/如果有很多个函数都需要传入context则效率可以大大提升
QJniObject::callStaticMethod<void>("org/qt/QtAndroidReceiver", "getBattery", "()V");
На самом деле это возможно, но из-за того, что компоненты должны быть независимыми, различные библиотеки и интерфейсы разделены. Это сделано для различных сценариев использования. Например, при декодировании нужно сначала получить информацию о потоке через avformat_find_stream_info, затем скопировать codecpar из информации потока в AVCodecContext. После чтения данных через av_read_frame, когда вызывается декодирование avcodec_send_packet/avcodec_receive_frame, нужен AVCodecContext, и этот процесс всегда одинаков. Почему ffmpeg не обрабатывает копирование внутри себя? Потому что AVCodecContext также может использоваться для кодирования, а параметры можно настраивать. Он также независим от AVStream и других компонентов, поскольку может выполнять кодирование и декодирование без AVStream, если ему предоставлена необходимая информация. По мере углубления использования вы поймёте, что такая конструкция — лучшая.
### 5 Qt-дизайн
**Опыт, собранный из книги «C++ Qt-дизайн», прочитанной вместе с официальной книгой «Программирование GUI Qt4 на C++».**
1. Обычно рекомендуется включать файлы заголовков Qt после файлов заголовков, не относящихся к Qt, так как Qt определяет множество символов, что упрощает избежание конфликтов имён и поиск файлов.
```cpp
#include "frminput2019.h"
#include "ui_frminput2019.h"
#include "qdatetime.h"
#include "qdebug.h"
#include "input2019.h"
#include "inputnumber.h"
// Не рекомендуется
for (int i = 0; i < 100; ++i) {
...
}
// Рекомендуется
const int count = 100;
for (int i = 0; i < count; ++i) {
...
}
WeChat: Kuma-NPC
При написании программы, если возникают проблемы, особенно в экстремальных условиях, рекомендуется найти их причину. Иногда неизбежно возникает подозрение, что проблема связана с самим Qt. Подозрения часто оказываются верными, но в 99,9% случаев проблема в конечном счёте связана с плохим кодом. Если сроки поджимают, можно временно решить проблему перезагрузкой или сбросом настроек. Например, можно установить таймер, потоки, сетевую коммуникацию для проверки состояния программы или определённых модулей и функций. Если что-то не работает, перезагрузите программу или компьютер. Во встроенных системах можно применить более радикальные методы, такие как перезапуск системы или отключение питания.
При написании программ важно учитывать несовместимость 32-битных и 64-битных библиотек. Проблемы возникают, когда 32-битные программы используют 64-битные библиотеки или наоборот. В Windows 64-разрядной системе можно запускать 32-разрядные программы, так как система предоставляет 32-разрядную среду выполнения. Обычно 32-разрядные библиотеки находятся в каталоге Program Files (x86). Чтобы определить разрядность своих Qt-библиотек, нужно проверить информацию в Qt Creator или посмотреть в комплекте сборки Qt. Начиная с Qt 5.14, 32-битные версии предоставляются реже, а в Qt 6.0 по умолчанию доступны только 64-битные версии. Это соответствует современным тенденциям, ведь в ближайшем будущем (по прогнозам, до 2030 года) доля 32-битных систем составит менее 1%. Поэтому рекомендуется использовать 64-битные библиотеки, отказавшись от устаревших 32-битных версий и систем XP.
О динамических и статических библиотеках в программах Qt:
В последних версиях Qt, начиная примерно с 5.15, офлайн-версии больше не предоставляются, требуется онлайн-установка через установщик. Поскольку серверы по умолчанию расположены за границей, многие пользователи сообщают о медленном скачивании. Для ускорения процесса можно использовать Fiddler5 (желательно китайскую версию для лучшего понимания), ввести urlreplace download.qt.io mirrors.ustc.edu.cn/qtproject/ в поле ввода внизу программы и нажать Enter. Затем можно установить Qt через онлайн-установщик, и мир внезапно станет прекрасным.
Qt — это потрясающий проект с огромным исходным кодом, разделённым на модули. Те, у кого достаточно времени, могут изучить конкретную реализацию в исходном коде. Если времени мало, рекомендуется обратить внимание на следующие классы: QObject, QWidget, QPainter, QString, QColor, QList, QVariant, QAbstractButton, QAbstractItemModel и qnamespace.h. Изучение их методов и свойств может значительно помочь в программировании.
И последнее: берегите свою жизнь и держитесь подальше от программирования. Желаю всем густой шевелюры, хорошего сна, стабильного настроения и финансового благополучия!
Название | URL |
---|---|
Qt/C++ 学习高级群 | 751439350 |
QtWidget 开源demo集合 | https://gitee.com/feiyangqingyun/QWidgetDemo |
QtQuick/Qml 开源demo集合 | https://gitee.com/jaredtao/TaoQuick |
QtQuick/Qml 开源demo集合 | https://gitee.com/zhengtianzuo/QtQuickExamples |
Название | URL |
---|---|
qtcn | http://www.qtcn.org |
豆子的空间 | https://www.devbean.net |
yafeilinux | http://www.qter.org |
feiyangqingyun | https://blog.csdn.net/feiyangqingyun |
Qt作品大全 | https://qtchina.blog.csdn.net/article/details/97565652 |
龚建波 | https://gongjianbo1992.blog.csdn.net/ |
乌托邦2号 | http://blog.csdn.net/taiyang1987912 |
foruok | http://blog.csdn.net/foruok |
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Комментарии ( 0 )