如何突出显示 QTreeView 中的某些项目(不继承)
How to highlight some items in a QTreeView (without inheriting)
我正在寻找一种无需重新实现 QTreeView 子类即可突出显示 QTreeView 行的方法。
我看过类似的问题:
- highlight-specific-substrings-in-a-qtreeview
- how-to-make-item-view-render-rich-html-text-in-qt
- set-bold-rows-in-a-qtreeview
但他们都使用委托。原因是我正在创建一个 search text in widgets 工具,它可以浏览所有小部件并查找并突出显示其中的文本。因此,我不能使用委托。
有什么可能的解决办法吗?
在上面画一个半透明的项目?
这是存储小部件的工具的代码以及在其中搜索文本的方法。
class GUI_EXPORT QgsSearchHighlightOptionWidget : public QObject
{
Q_OBJECT
public:
/**
* Constructor
* \param widget the widget used to search text into
*/
explicit QgsSearchHighlightOptionWidget( QWidget *widget = nullptr );
/**
* Returns if it valid: if the widget type is handled and if the widget is not still available
*/
bool isValid() { return mWidget && mValid; }
/**
* search for a text pattern and highlight the widget if the text is found
* \returns true if the text pattern is found
*/
bool searchHighlight( const QString &searchText );
/**
* reset the style to the original state
*/
void reset();
/**
* return the widget
*/
QWidget *widget() { return mWidget; }
bool eventFilter( QObject *obj, QEvent *event ) override;
private slots:
void widgetDestroyed();
private:
QPointer< QWidget > mWidget;
QString mStyleSheet;
bool mValid = true;
bool mChangedStyle = false;
std::function < bool( QString )> mTextFound = []( QString searchText ) {Q_UNUSED( searchText ); return false;};
bool mInstalledFilter = false;
};
QgsSearchHighlightOptionWidget::QgsSearchHighlightOptionWidget( QWidget *widget )
: QObject( widget )
, mWidget( widget )
{
if ( qobject_cast<QLabel *>( widget ) )
{
mStyleSheet = QStringLiteral( "QLabel { background-color: yellow; color: blue;}" );
mTextFound = [ = ]( QString searchText ) {return qobject_cast<QLabel *>( mWidget )->text().contains( searchText, Qt::CaseInsensitive );};
}
else if ( qobject_cast<QCheckBox *>( widget ) )
{
mStyleSheet = QStringLiteral( "QCheckBox { background-color: yellow; color: blue;}" );
mTextFound = [ = ]( QString searchText ) {return qobject_cast<QCheckBox *>( mWidget )->text().contains( searchText, Qt::CaseInsensitive );};
}
else if ( qobject_cast<QAbstractButton *>( widget ) )
{
mStyleSheet = QStringLiteral( "QAbstractButton { background-color: yellow; color: blue;}" );
mTextFound = [ = ]( QString searchText ) {return qobject_cast<QAbstractButton *>( mWidget )->text().contains( searchText, Qt::CaseInsensitive );};
}
else if ( qobject_cast<QGroupBox *>( widget ) )
{
mStyleSheet = QStringLiteral( "QGroupBox::title { background-color: yellow; color: blue;}" );
mTextFound = [ = ]( QString searchText ) {return qobject_cast<QGroupBox *>( mWidget )->title().contains( searchText, Qt::CaseInsensitive );};
}
else if ( qobject_cast<QTreeView *>( widget ) )
{
// TODO - style individual matching items
mTextFound = [ = ]( QString searchText )
{
QTreeView *tree = qobject_cast<QTreeView *>( mWidget );
if ( !tree )
return false;
QModelIndexList hits = tree->model()->match( tree->model()->index( 0, 0 ), Qt::DisplayRole, searchText, 1, Qt::MatchContains | Qt::MatchRecursive );
return !hits.isEmpty();
};
}
else
{
mValid = false;
}
if ( mValid )
{
mStyleSheet.prepend( "/*!search!*/" ).append( "/*!search!*/" );
QgsDebugMsgLevel( mStyleSheet, 4 );
connect( mWidget, &QWidget::destroyed, this, &QgsSearchHighlightOptionWidget::widgetDestroyed );
}
}
bool QgsSearchHighlightOptionWidget::searchHighlight( const QString &searchText )
{
bool found = false;
if ( !mWidget )
return found;
if ( !searchText.isEmpty() )
{
found = mTextFound( searchText );
}
if ( found && !mChangedStyle )
{
if ( !mWidget->isVisible() )
{
// show the widget to get initial stylesheet in case it's modified
QgsDebugMsg( QString( "installing event filter on: %1 (%2)" )
.arg( mWidget->objectName() )
.arg( qobject_cast<QLabel *>( mWidget ) ? qobject_cast<QLabel *>( mWidget )->text() : QString() ) );
mWidget->installEventFilter( this );
mInstalledFilter = true;
}
else
{
mWidget->setStyleSheet( mWidget->styleSheet() + mStyleSheet );
mChangedStyle = true;
}
}
return found;
}
bool QgsSearchHighlightOptionWidget::eventFilter( QObject *obj, QEvent *event )
{
if ( mInstalledFilter && event->type() == QEvent::Show && obj == mWidget )
{
mWidget->removeEventFilter( this );
mInstalledFilter = false;
// instead of catching the event and calling show again
// it might be better to use a timer to change the style
// after the widget is shown
#if 1
mWidget->show();
mWidget->setStyleSheet( mWidget->styleSheet() + mStyleSheet );
return true;
#else
QTimer::singleShot( 500, this, [ = ]
{
mWidget->setStyleSheet( mWidget->styleSheet() + mStyleSheet );
mChangedStyle = true;
} );
#endif
}
return QObject::eventFilter( obj, event );
}
void QgsSearchHighlightOptionWidget::reset()
{
if ( mWidget && mValid )
{
if ( mChangedStyle )
{
QString ss = mWidget->styleSheet();
ss.remove( mStyleSheet );
mWidget->setStyleSheet( ss );
mChangedStyle = false;
}
else if ( mInstalledFilter )
{
mWidget->removeEventFilter( this );
mInstalledFilter = false;
}
}
}
void QgsSearchHighlightOptionWidget::widgetDestroyed()
{
mWidget = nullptr;
mValid = false;
}
下面是从对话框实际注册小部件的代码:
void QgsOptionsDialogBase::registerTextSearchWidgets()
{
mRegisteredSearchWidgets.clear();
for ( int i = 0; i < mOptStackedWidget->count(); i++ )
{
Q_FOREACH ( QWidget *w, mOptStackedWidget->widget( i )->findChildren<QWidget *>() )
{
QgsSearchHighlightOptionWidget *shw = new QgsSearchHighlightOptionWidget( w );
if ( shw->isValid() )
{
QgsDebugMsgLevel( QString( "Registering: %1" ).arg( w->objectName() ), 4 );
mRegisteredSearchWidgets.append( qMakePair( shw, i ) );
}
else
{
delete shw;
}
}
}
}
看文档,不知道怎么直接弄。
这里有一些建议。希望一个适合你:
1) 你能用 QTreeWidget
代替 QTreeView
吗?
有了这个,它应该很容易。
使用 item
函数,例如setBackground()
在项目上。
2) 你过滤树而不是突出显示怎么样?使用 setRowHidden()
?
3) 如果您还没有,您也可以尝试使用 QTreeView::keyboardSearch()
看看它的作用。
4) 您可以使用 select*
并在您的搜索框中添加一个 next/previous 按钮。 IE。您在树中跳转,选择当前搜索结果。
我正在寻找一种无需重新实现 QTreeView 子类即可突出显示 QTreeView 行的方法。
我看过类似的问题:
- highlight-specific-substrings-in-a-qtreeview
- how-to-make-item-view-render-rich-html-text-in-qt
- set-bold-rows-in-a-qtreeview
但他们都使用委托。原因是我正在创建一个 search text in widgets 工具,它可以浏览所有小部件并查找并突出显示其中的文本。因此,我不能使用委托。
有什么可能的解决办法吗? 在上面画一个半透明的项目?
这是存储小部件的工具的代码以及在其中搜索文本的方法。
class GUI_EXPORT QgsSearchHighlightOptionWidget : public QObject
{
Q_OBJECT
public:
/**
* Constructor
* \param widget the widget used to search text into
*/
explicit QgsSearchHighlightOptionWidget( QWidget *widget = nullptr );
/**
* Returns if it valid: if the widget type is handled and if the widget is not still available
*/
bool isValid() { return mWidget && mValid; }
/**
* search for a text pattern and highlight the widget if the text is found
* \returns true if the text pattern is found
*/
bool searchHighlight( const QString &searchText );
/**
* reset the style to the original state
*/
void reset();
/**
* return the widget
*/
QWidget *widget() { return mWidget; }
bool eventFilter( QObject *obj, QEvent *event ) override;
private slots:
void widgetDestroyed();
private:
QPointer< QWidget > mWidget;
QString mStyleSheet;
bool mValid = true;
bool mChangedStyle = false;
std::function < bool( QString )> mTextFound = []( QString searchText ) {Q_UNUSED( searchText ); return false;};
bool mInstalledFilter = false;
};
QgsSearchHighlightOptionWidget::QgsSearchHighlightOptionWidget( QWidget *widget )
: QObject( widget )
, mWidget( widget )
{
if ( qobject_cast<QLabel *>( widget ) )
{
mStyleSheet = QStringLiteral( "QLabel { background-color: yellow; color: blue;}" );
mTextFound = [ = ]( QString searchText ) {return qobject_cast<QLabel *>( mWidget )->text().contains( searchText, Qt::CaseInsensitive );};
}
else if ( qobject_cast<QCheckBox *>( widget ) )
{
mStyleSheet = QStringLiteral( "QCheckBox { background-color: yellow; color: blue;}" );
mTextFound = [ = ]( QString searchText ) {return qobject_cast<QCheckBox *>( mWidget )->text().contains( searchText, Qt::CaseInsensitive );};
}
else if ( qobject_cast<QAbstractButton *>( widget ) )
{
mStyleSheet = QStringLiteral( "QAbstractButton { background-color: yellow; color: blue;}" );
mTextFound = [ = ]( QString searchText ) {return qobject_cast<QAbstractButton *>( mWidget )->text().contains( searchText, Qt::CaseInsensitive );};
}
else if ( qobject_cast<QGroupBox *>( widget ) )
{
mStyleSheet = QStringLiteral( "QGroupBox::title { background-color: yellow; color: blue;}" );
mTextFound = [ = ]( QString searchText ) {return qobject_cast<QGroupBox *>( mWidget )->title().contains( searchText, Qt::CaseInsensitive );};
}
else if ( qobject_cast<QTreeView *>( widget ) )
{
// TODO - style individual matching items
mTextFound = [ = ]( QString searchText )
{
QTreeView *tree = qobject_cast<QTreeView *>( mWidget );
if ( !tree )
return false;
QModelIndexList hits = tree->model()->match( tree->model()->index( 0, 0 ), Qt::DisplayRole, searchText, 1, Qt::MatchContains | Qt::MatchRecursive );
return !hits.isEmpty();
};
}
else
{
mValid = false;
}
if ( mValid )
{
mStyleSheet.prepend( "/*!search!*/" ).append( "/*!search!*/" );
QgsDebugMsgLevel( mStyleSheet, 4 );
connect( mWidget, &QWidget::destroyed, this, &QgsSearchHighlightOptionWidget::widgetDestroyed );
}
}
bool QgsSearchHighlightOptionWidget::searchHighlight( const QString &searchText )
{
bool found = false;
if ( !mWidget )
return found;
if ( !searchText.isEmpty() )
{
found = mTextFound( searchText );
}
if ( found && !mChangedStyle )
{
if ( !mWidget->isVisible() )
{
// show the widget to get initial stylesheet in case it's modified
QgsDebugMsg( QString( "installing event filter on: %1 (%2)" )
.arg( mWidget->objectName() )
.arg( qobject_cast<QLabel *>( mWidget ) ? qobject_cast<QLabel *>( mWidget )->text() : QString() ) );
mWidget->installEventFilter( this );
mInstalledFilter = true;
}
else
{
mWidget->setStyleSheet( mWidget->styleSheet() + mStyleSheet );
mChangedStyle = true;
}
}
return found;
}
bool QgsSearchHighlightOptionWidget::eventFilter( QObject *obj, QEvent *event )
{
if ( mInstalledFilter && event->type() == QEvent::Show && obj == mWidget )
{
mWidget->removeEventFilter( this );
mInstalledFilter = false;
// instead of catching the event and calling show again
// it might be better to use a timer to change the style
// after the widget is shown
#if 1
mWidget->show();
mWidget->setStyleSheet( mWidget->styleSheet() + mStyleSheet );
return true;
#else
QTimer::singleShot( 500, this, [ = ]
{
mWidget->setStyleSheet( mWidget->styleSheet() + mStyleSheet );
mChangedStyle = true;
} );
#endif
}
return QObject::eventFilter( obj, event );
}
void QgsSearchHighlightOptionWidget::reset()
{
if ( mWidget && mValid )
{
if ( mChangedStyle )
{
QString ss = mWidget->styleSheet();
ss.remove( mStyleSheet );
mWidget->setStyleSheet( ss );
mChangedStyle = false;
}
else if ( mInstalledFilter )
{
mWidget->removeEventFilter( this );
mInstalledFilter = false;
}
}
}
void QgsSearchHighlightOptionWidget::widgetDestroyed()
{
mWidget = nullptr;
mValid = false;
}
下面是从对话框实际注册小部件的代码:
void QgsOptionsDialogBase::registerTextSearchWidgets()
{
mRegisteredSearchWidgets.clear();
for ( int i = 0; i < mOptStackedWidget->count(); i++ )
{
Q_FOREACH ( QWidget *w, mOptStackedWidget->widget( i )->findChildren<QWidget *>() )
{
QgsSearchHighlightOptionWidget *shw = new QgsSearchHighlightOptionWidget( w );
if ( shw->isValid() )
{
QgsDebugMsgLevel( QString( "Registering: %1" ).arg( w->objectName() ), 4 );
mRegisteredSearchWidgets.append( qMakePair( shw, i ) );
}
else
{
delete shw;
}
}
}
}
看文档,不知道怎么直接弄。 这里有一些建议。希望一个适合你:
1) 你能用 QTreeWidget
代替 QTreeView
吗?
有了这个,它应该很容易。
使用 item
函数,例如setBackground()
在项目上。
2) 你过滤树而不是突出显示怎么样?使用 setRowHidden()
?
3) 如果您还没有,您也可以尝试使用 QTreeView::keyboardSearch()
看看它的作用。
4) 您可以使用 select*
并在您的搜索框中添加一个 next/previous 按钮。 IE。您在树中跳转,选择当前搜索结果。