Make GUI able to compile on both Qt5 and Qt6 (#1576)

* Use QtPropertyBrowser for Qt5/6

* Fix cmake for python-console for consistency

* Make GUI compile for both Qt5 and Qt6

* Fix crash on init with Wayland on Qt6

* Cleanup

* disable deprecation warnings for now

* Relaxed cmake check for initial Qt6 test
This commit is contained in:
Miodrag Milanović 2025-10-15 12:19:20 +02:00 committed by GitHub
parent c7836625b9
commit 17d42e41db
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 155 additions and 38 deletions

View File

@ -1,11 +1,3 @@
CMAKE_MINIMUM_REQUIRED(VERSION 3.13)
PROJECT(QtPropertyBrowser)
##################### Look for required libraries ######################
# Add QT dependencies
FIND_PACKAGE(Qt5Widgets REQUIRED)
######################### Add Primary Targets ##########################
ADD_SUBDIRECTORY(src)

View File

@ -23,8 +23,13 @@ set(_RESOURCES
qtpropertybrowser.qrc
)
QT5_WRAP_UI(_UI_SRCS ${_UI_FORMS})
QT5_ADD_RESOURCES(_QRC_SRCS ${_RESOURCES})
if (Qt6_FOUND)
QT6_WRAP_UI(_UI_SRCS ${_UI_FORMS})
QT6_ADD_RESOURCES(_QRC_SRCS ${_RESOURCES})
else()
QT5_WRAP_UI(_UI_SRCS ${_UI_FORMS})
QT5_ADD_RESOURCES(_QRC_SRCS ${_RESOURCES})
endif()
set(TARGET_NAME ${PROJECT_NAME})
@ -34,8 +39,14 @@ add_library(${TARGET_NAME} STATIC
${_QRC_SRCS}
)
target_compile_options(${TARGET_NAME} PRIVATE -Wno-deprecated-declarations)
if (MSVC)
target_compile_options(${TARGET_NAME} PRIVATE /wd4457 /wd4718)
endif()
target_link_libraries(${TARGET_NAME} Qt5::Widgets)
if (Qt6_FOUND)
target_link_libraries(${TARGET_NAME} Qt6::Widgets)
else()
target_link_libraries(${TARGET_NAME} Qt5::Widgets)
endif()

View File

@ -0,0 +1,12 @@
#pragma once
#include <QtGlobal>
#if QT_VERSION_MAJOR >= 6
#include <QRegularExpression>
#include <QRegularExpressionValidator>
// QRegExp / QRegExpValidator aliases
using QRegExp = QRegularExpression;
using QRegExpValidator = QRegularExpressionValidator;
#endif

View File

@ -1545,7 +1545,11 @@ QtCharEdit::QtCharEdit(QWidget *parent)
{
QHBoxLayout *layout = new QHBoxLayout(this);
layout->addWidget(m_lineEdit);
#if QT_VERSION_MAJOR >= 6
layout->setContentsMargins(0,0,0,0);
#else
layout->setMargin(0);
#endif
m_lineEdit->installEventFilter(this);
m_lineEdit->setReadOnly(true);
m_lineEdit->setFocusProxy(this);
@ -1669,7 +1673,13 @@ void QtCharEdit::keyReleaseEvent(QKeyEvent *e)
void QtCharEdit::paintEvent(QPaintEvent *)
{
QStyleOption opt;
#if QT_VERSION_MAJOR >= 6
opt.rect = this->rect();
opt.state = isEnabled() ? QStyle::State_Enabled : QStyle::State_None;
opt.palette = this->palette();
#else
opt.init(this);
#endif
QPainter p(this);
style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
}
@ -2240,7 +2250,13 @@ bool QtColorEditWidget::eventFilter(QObject *obj, QEvent *ev)
void QtColorEditWidget::paintEvent(QPaintEvent *)
{
QStyleOption opt;
#if QT_VERSION_MAJOR >= 6
opt.rect = this->rect();
opt.state = isEnabled() ? QStyle::State_Enabled : QStyle::State_None;
opt.palette = this->palette();
#else
opt.init(this);
#endif
QPainter p(this);
style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
}
@ -2462,7 +2478,13 @@ bool QtFontEditWidget::eventFilter(QObject *obj, QEvent *ev)
void QtFontEditWidget::paintEvent(QPaintEvent *)
{
QStyleOption opt;
#if QT_VERSION_MAJOR >= 6
opt.rect = this->rect();
opt.state = isEnabled() ? QStyle::State_Enabled : QStyle::State_None;
opt.palette = this->palette();
#else
opt.init(this);
#endif
QPainter p(this);
style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
}

View File

@ -41,6 +41,8 @@
#ifndef QTPROPERTYBROWSER_H
#define QTPROPERTYBROWSER_H
#include "qt5compat.h"
#include <QWidget>
#include <QtCore/QSet>

View File

@ -262,7 +262,13 @@ void QtBoolEdit::mousePressEvent(QMouseEvent *event)
void QtBoolEdit::paintEvent(QPaintEvent *)
{
QStyleOption opt;
#if QT_VERSION_MAJOR >= 6
opt.rect = this->rect();
opt.state = isEnabled() ? QStyle::State_Enabled : QStyle::State_None;
opt.palette = this->palette();
#else
opt.init(this);
#endif
QPainter p(this);
style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
}
@ -274,7 +280,11 @@ QtKeySequenceEdit::QtKeySequenceEdit(QWidget *parent)
{
QHBoxLayout *layout = new QHBoxLayout(this);
layout->addWidget(m_lineEdit);
#if QT_VERSION_MAJOR >= 6
layout->setContentsMargins(0,0,0,0);
#else
layout->setMargin(0);
#endif
m_lineEdit->installEventFilter(this);
m_lineEdit->setReadOnly(true);
m_lineEdit->setFocusProxy(this);
@ -408,7 +418,13 @@ void QtKeySequenceEdit::keyReleaseEvent(QKeyEvent *e)
void QtKeySequenceEdit::paintEvent(QPaintEvent *)
{
QStyleOption opt;
#if QT_VERSION_MAJOR >= 6
opt.rect = this->rect();
opt.state = isEnabled() ? QStyle::State_Enabled : QStyle::State_None;
opt.palette = this->palette();
#else
opt.init(this);
#endif
QPainter p(this);
style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
}

View File

@ -1229,7 +1229,11 @@ public:
struct Data
{
#if QT_VERSION_MAJOR >= 6
Data() : regExp(QRegularExpression(QStringLiteral(".*")))
#else
Data() : regExp(QString(QLatin1Char('*')), Qt::CaseSensitive, QRegExp::Wildcard)
#endif
{
}
QString val;
@ -1357,7 +1361,11 @@ void QtStringPropertyManager::setValue(QtProperty *property, const QString &val)
if (data.val == val)
return;
#if QT_VERSION_MAJOR >= 6
if (data.regExp.isValid() && !data.regExp.match(val).hasMatch())
#else
if (data.regExp.isValid() && !data.regExp.exactMatch(val))
#endif
return;
data.val = val;
@ -5854,8 +5862,13 @@ void QtFontPropertyManager::setValue(QtProperty *property, const QFont &val)
return;
const QFont oldVal = it.value();
#if QT_VERSION_MAJOR >= 6
if (oldVal == val && oldVal.resolve(val) == val)
return;
#else
if (oldVal == val && oldVal.resolve() == val.resolve())
return;
#endif
it.value() = val;

View File

@ -132,9 +132,9 @@ public:
protected:
void mouseMoveEvent(QMouseEvent *event) override;
void leaveEvent(QEvent *event) override;
void keyPressEvent(QKeyEvent *event) override;
void mousePressEvent(QMouseEvent *event) override;
void drawRow(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
void keyPressEvent(QKeyEvent *event);
void mousePressEvent(QMouseEvent *event);
void drawRow(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
Q_SIGNALS:
void hoverPropertyChanged(QtBrowserItem *item);
@ -160,7 +160,8 @@ void QtPropertyEditorView::drawRow(QPainter *painter, const QStyleOptionViewItem
hasValue = property->hasValue();
}
if (!hasValue && m_editorPrivate->markPropertiesWithoutValue()) {
const QColor c = option.palette.color(QPalette::Dark);
QColor c = m_editorPrivate->calculatedBackgroundColor(m_editorPrivate->indexToBrowserItem(index));
if (!c.isValid()) c = option.palette.color(QPalette::Dark);
painter->fillRect(option.rect, c);
opt.palette.setColor(QPalette::AlternateBase, c);
} else {
@ -379,7 +380,8 @@ void QtPropertyEditorDelegate::paint(QPainter *painter, const QStyleOptionViewIt
}
QColor c;
if (!hasValue && m_editorPrivate->markPropertiesWithoutValue()) {
c = opt.palette.color(QPalette::Dark);
c = m_editorPrivate->calculatedBackgroundColor(m_editorPrivate->indexToBrowserItem(index));
if (!c.isValid()) c = opt.palette.color(QPalette::Dark);
opt.palette.setColor(QPalette::Text, opt.palette.color(QPalette::BrightText));
} else {
c = m_editorPrivate->calculatedBackgroundColor(m_editorPrivate->indexToBrowserItem(index));
@ -486,7 +488,11 @@ static QIcon drawIndicatorIcon(const QPalette &palette, QStyle *style)
void QtTreePropertyBrowserPrivate::init(QWidget *parent)
{
QHBoxLayout *layout = new QHBoxLayout(parent);
#if QT_VERSION_MAJOR >= 6
layout->setContentsMargins(0,0,0,0);
#else
layout->setMargin(0);
#endif
m_treeWidget = new QtPropertyEditorView(parent);
m_treeWidget->setEditorPrivate(this);
m_treeWidget->setIconSize(QSize(18, 18));
@ -610,7 +616,6 @@ void QtTreePropertyBrowserPrivate::propertyInserted(QtBrowserItem *index, QtBrow
newItem->setFlags(newItem->flags() | Qt::ItemIsEditable);
newItem->setExpanded(true);
updateItem(newItem);
}

View File

@ -986,8 +986,13 @@ QtVariantPropertyManager::QtVariantPropertyManager(QObject *parent)
QtStringPropertyManager *stringPropertyManager = new QtStringPropertyManager(this);
d_ptr->m_typeToPropertyManager[QVariant::String] = stringPropertyManager;
d_ptr->m_typeToValueType[QVariant::String] = QVariant::String;
#if QT_VERSION_MAJOR >= 6
d_ptr->m_typeToAttributeToAttributeType[QVariant::String][d_ptr->m_regExpAttribute] =
QMetaType::QRegularExpression; // int value for the type
#else
d_ptr->m_typeToAttributeToAttributeType[QVariant::String][d_ptr->m_regExpAttribute] =
QVariant::RegExp;
#endif
connect(stringPropertyManager, SIGNAL(valueChanged(QtProperty *, const QString &)),
this, SLOT(slotValueChanged(QtProperty *, const QString &)));
connect(stringPropertyManager, SIGNAL(regExpChanged(QtProperty *, const QRegExp &)),

View File

@ -2,7 +2,13 @@ cmake_minimum_required( VERSION 2.8 )
project( PythonInterpreter )
set(CMAKE_CXX_STANDARD 11)
find_package(Qt5 COMPONENTS Core Widgets REQUIRED)
find_package(Qt6 COMPONENTS Core Widgets REQUIRED)
if (Qt6_FOUND)
message(STATUS "Using Qt6")
else()
message(STATUS "Using Qt5")
find_package(Qt5 COMPONENTS Core Widgets REQUIRED)
endif()
find_package( PythonLibs REQUIRED )
include_directories( ${PYTHON_INCLUDE_DIRS} )
@ -10,7 +16,12 @@ include_directories( ${PYTHON_INCLUDE_DIRS} )
add_executable( test_python_interpreter test_python_interpreter.cpp Interpreter.cpp )
target_link_libraries( test_python_interpreter ${PYTHON_LIBRARIES} )
qt5_wrap_cpp( Console_MOC Console.h )
if (Qt6_FOUND)
qt6_wrap_cpp( Console_MOC Console.h )
else()
qt5_wrap_cpp( Console_MOC Console.h )
endif()
add_executable( test_console test_console.cpp
Console.cpp ${Console_MOC}
ColumnFormatter.cpp
@ -22,7 +33,12 @@ add_executable( test_console test_console.cpp
ParseMessage.cpp
)
target_compile_definitions( test_console PRIVATE QT_NO_KEYWORDS)
target_link_libraries( test_console Qt5::Widgets ${PYTHON_LIBRARIES} )
if (Qt6_FOUND)
target_link_libraries( test_console Qt6::Widgets ${PYTHON_LIBRARIES} )
else()
target_link_libraries( test_console Qt5::Widgets ${PYTHON_LIBRARIES} )
endif()
add_executable( test_parse_helper test_parse_helper.cpp
ParseHelper.cpp

View File

@ -197,8 +197,14 @@ else()
endif()
if (BUILD_GUI)
# Find the Qt5 libraries
# Find the Qt6/5 libraries
find_package(Qt6 COMPONENTS Core Widgets OpenGL OpenGLWidgets QUIET)
if (Qt6_FOUND)
message(STATUS "Using Qt6")
else()
message(STATUS "Using Qt5")
find_package(Qt5 COMPONENTS Core Widgets OpenGL REQUIRED)
endif()
# For higher quality backtraces
set(CMAKE_ENABLE_EXPORTS ON)

View File

@ -27,10 +27,17 @@ set(GUI_SOURCES
${family}/mainwindow.h
)
qt5_add_resources(GUI_QT_RESOURCES
if (Qt6_FOUND)
qt6_add_resources(GUI_QT_RESOURCES
base.qrc
${family}/nextpnr.qrc
)
)
else()
qt5_add_resources(GUI_QT_RESOURCES
base.qrc
${family}/nextpnr.qrc
)
endif()
add_library(nextpnr-${target}-gui OBJECT
${GUI_SOURCES}
@ -54,14 +61,17 @@ target_include_directories(nextpnr-${target}-gui PRIVATE
${CMAKE_SOURCE_DIR}/3rdparty/qtimgui
)
target_link_libraries(nextpnr-${target}-gui PUBLIC
Qt5::Widgets
)
if (Qt6_FOUND)
target_link_libraries(nextpnr-${target}-gui PUBLIC Qt6::Widgets)
target_link_libraries(nextpnr-${target}-gui PRIVATE Qt6::OpenGL Qt6::OpenGLWidgets)
else()
target_link_libraries(nextpnr-${target}-gui PUBLIC Qt5::Widgets)
target_link_libraries(nextpnr-${target}-gui PRIVATE Qt5::OpenGL)
endif()
target_link_libraries(nextpnr-${target}-gui PRIVATE
nextpnr-${target}-defs
nextpnr_version
Qt5::OpenGL
QtPropertyBrowser
pybind11::headers
)

View File

@ -96,6 +96,7 @@ Application::Application(int &argc, char **argv, bool noantialiasing) : QApplica
QSurfaceFormat fmt;
if (!noantialiasing)
fmt.setSamples(10);
fmt.setRenderableType(QSurfaceFormat::OpenGL);
fmt.setProfile(QSurfaceFormat::CoreProfile);
// macOS is very picky about this version matching
// the version of openGL used in ImGuiRenderer

View File

@ -20,6 +20,7 @@
#include "designwidget.h"
#include <QAction>
#include <QActionGroup>
#include <QApplication>
#include <QGridLayout>
#include <QLineEdit>

View File

@ -22,7 +22,6 @@
#include <QApplication>
#include <QCoreApplication>
#include <QDesktopWidget>
#include <QDir>
#include <QFileInfo>
#include <QImageWriter>
@ -36,6 +35,12 @@
#include "log.h"
#include "mainwindow.h"
#if QT_VERSION_MAJOR >= 6
#define EVENT_POS(event) ((event)->position())
#else
#define EVENT_POS(event) ((event)->pos())
#endif
NEXTPNR_NAMESPACE_BEGIN
FPGAViewWidget::FPGAViewWidget(QWidget *parent)
@ -728,7 +733,7 @@ void FPGAViewWidget::mousePressEvent(QMouseEvent *event)
lastDragPos_ = event->pos();
}
if (btn_left && !shift) {
auto world = mouseToWorldCoordinates(event->x(), event->y());
auto world = mouseToWorldCoordinates(EVENT_POS(event).x(), EVENT_POS(event).y());
auto closestOr = pickElement(world.x(), world.y());
if (!closestOr) {
// If we clicked on empty space and aren't holding down ctrl,
@ -765,8 +770,8 @@ void FPGAViewWidget::mouseMoveEvent(QMouseEvent *event)
bool btn_left = event->buttons() & Qt::LeftButton;
if (btn_right || btn_mid || (btn_left && shift)) {
const int dx = event->x() - lastDragPos_.x();
const int dy = event->y() - lastDragPos_.y();
const int dx = EVENT_POS(event).x() - lastDragPos_.x();
const int dy = EVENT_POS(event).y() - lastDragPos_.y();
lastDragPos_ = event->pos();
auto world = mouseToWorldDimensions(dx, dy);
@ -776,7 +781,7 @@ void FPGAViewWidget::mouseMoveEvent(QMouseEvent *event)
return;
}
auto world = mouseToWorldCoordinates(event->x(), event->y());
auto world = mouseToWorldCoordinates(EVENT_POS(event).x(), EVENT_POS(event).y());
auto closestOr = pickElement(world.x(), world.y());
// No elements? No decal.
if (!closestOr) {
@ -794,8 +799,8 @@ void FPGAViewWidget::mouseMoveEvent(QMouseEvent *event)
QMutexLocker locked(&rendererArgsLock_);
rendererArgs_->hoveredDecal = closest.decal(ctx_);
rendererArgs_->changed = true;
rendererArgs_->x = event->x();
rendererArgs_->y = event->y();
rendererArgs_->x = EVENT_POS(event).x();
rendererArgs_->y = EVENT_POS(event).y();
if (closest.type == ElementType::BEL) {
rendererArgs_->hintText = std::string("BEL\n") + ctx_->getBelName(closest.bel).str(ctx_);
CellInfo *cell = ctx_->getBoundBelCell(closest.bel);