I want to make a process list window, so I traverse the process list, but I don’t know how to get the icon and display it in the listWidget
This is my code:
#include <windows.h>
#include <psapi.h>
#include <tlhelp32.h>
#include <iostream>
HANDLE hSnap = CreateToolhelp32Snapshot(PROCESS_ALL_ACCESS, 0);
if( hSnap != INVALID_HANDLE_VALUE )
{
PROCESSENTRY32 pe;
pe.dwSize = sizeof(pe);
if( Process32First(hSnap, &pe) )
{
DWORD curID = GetCurrentThreadId();
do{
if(pe.th32ProcessID && pe.th32ProcessID != curID)
{
QListWidgetItem *it = new QListWidgetItem(
/* icon */
PaddingZero(QString::number(pe.th32ProcessID, 16)).toUpper() +"-"+ QString::fromWCharArray(pe.szExeFile),
ui->listWidget
);
ui->listWidget->addItem(it);
//printf("name: %ls, id: %d\n",pe.szExeFile,pe.th32ProcessID);
}
}while( Process32Next(hSnap, &pe) );
}
}
CloseHandle(hSnap);
How can I get the ico of each process.
After a few days, I finally found the answer
HANDLE hSnap = CreateToolhelp32Snapshot(PROCESS_ALL_ACCESS, 0);
if( hSnap != INVALID_HANDLE_VALUE )
{
PROCESSENTRY32 pe;
pe.dwSize = sizeof(pe);
if( Process32First(hSnap, &pe) )
{
do{
if(pe.th32ProcessID != GetCurrentProcessId())
{
// https://forum.qt.io/topic/62866/getting-icon-from-external-applications/5
// https://stackoverflow.com/questions/63653786/error-using-getmodulefilenameexa-function-in-qt?noredirect=1#comment112561229_63653786
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pe.th32ProcessID);
wchar_t lpFilename[1024];
GetModuleFileNameExW(hProcess, NULL, lpFilename, sizeof(lpFilename));
CloseHandle(hProcess);
QFileInfo fin( QString::fromWCharArray(lpFilename) );
QFileSystemModel *model = new QFileSystemModel;
model->setRootPath(fin.path());
QIcon ic = model->fileIcon(model->index(fin.filePath()));
QListWidgetItem *it = new QListWidgetItem(
ic,
PaddingZero(QString::number(pe.th32ProcessID, 16)).toUpper() +
"-" +
QString::fromWCharArray(pe.szExeFile),
ui->list
);
ui->list->addItem(it);
//printf("name: %ls, id: %d\n",pe.szExeFile,pe.th32ProcessID);
}
}while( Process32Next(hSnap, &pe) );
}
}
CloseHandle(hSnap);
Related
I am looking for a way to highlight rows of a QTreeView without reimplementing a QTreeView subclass.
I have seen similar questions:
highlight-specific-substrings-in-a-qtreeview
how-to-make-item-view-render-rich-html-text-in-qt
set-bold-rows-in-a-qtreeview
But they all use delegate. The reason is that I am creating a search text in widgets tool which browse all widgets and find and highlights text in them. Hence, I cannot use delegate.
Is there any possible solution?
Painting a semi-transparent item above it?
Here is the code of the tool which stores the widget and the way to search for text in them.
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;
}
And here is the code to actually register the widgets from the dialog:
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;
}
}
}
}
Reading the docs, I don't know how to do it directly.
Here are some suggestions. Hopefully one works for you:
1) Could you use QTreeWidget instead of QTreeView ?
With this, it should be easy.
Use the item functions and e.g. setBackground() on the item.
2) How about you filter tree instead of highlighting? Using setRowHidden()?
3) If you haven't already, you could also try using QTreeView::keyboardSearch() and see what this does.
4) you could use select* and add a next/previous button to your search box. I.e. you hop through the tree selecting the current search result.
Context:
I have a set of file like that:
alias
USER = usr_name
PASSWORD = pass
...
alias2
...
I have a combobox to choose which file to display in a qtableview. The below is the slot called when the value of the combobox changes.
void paramTableModel::switchTable(QString env)
{
// is there an environment defined
// --------------------------------------
if(env.compare("") != 0)
{
// does the param file exist ?
// ---------------------------
QString file_path = "/path/to/" + env + "/path/to/file;
QFile param_file(file_path);
if( param_file.exists() && param_file.open(QIODevice::ReadOnly))
{
QString program_decrypt = "/path/to/decrypt";
QStringList args;
args << file_path;
QProcess* process = new QProcess();
process->setReadChannel(QProcess::StandardOutput);
process->start(program_decrypt,args);
process->waitForFinished(-1);
QTextStream in(&process->readAll());
//start resetting model (notify the view )
beginResetModel();
this->table.clear();
std::set<QString> uniq;
// parse file to model
// -------------------
while( !in.atEnd() )
{
QString line = in.readLine();
// some new UT ?
// -------------------
if( !line.isNull() && line.trimmed().compare("") != 0 && !line.contains("="))
{
line = line.trimmed();
std::vector<QString> row;
if(uniq.find(line) == uniq.end())
{
uniq.insert(line);
row.push_back(line);
QString user_line = in.readLine().trimmed();
QString password_line = in.readLine().trimmed();
QString server_line = in.readLine().trimmed();
if( user_line.startsWith("USER") )
{
user_line = user_line.split('=').at(1).trimmed();
}
if( password_line.startsWith("PASSWORD") )
{
password_line = password_line.split('=').at(1).trimmed();
}
if( server_line.startsWith("SERVER") )
{
server_line = server_line.split('=').at(1).trimmed();
}
row.push_back(user_line);
row.push_back(password_line);
row.push_back(server_line);
this->table.push_back(row);
}
else
{
QMessageBox msgBox;
msgBox.setText(QString("%1 is already defined, ingnoring").arg(line));
msgBox.exec();
}
}
}
param_file.close();
endResetModel();
delete process;
}
}
}
Most of the time, my view is properly refreshed. And from time to time, this line segfaults:
QString line = in.readLine();
I have found no pattern to reproduce the bug in a systematic way. I can click back and forth between two files in the combobox, it will work ten, twelve, twenty times and then segfault.
EDIT:
This does work.
void paramTableModel::switchTable(QString env)
{
// is there an environment defined
// --------------------------------------
if(env.isEmpty())
return;
// does the param file exist ?
// --------------------------------
QString file_path = "/path/to/env/" + env + "/path/to/file.dat";
QFile param_file(file_path);
if( param_file.exists() && param_file.open(QIODevice::ReadOnly))
{
// Call QProcess, fx_decrypt, read standard output
QString program_decrypt = "/path/to/decrypt";
QStringList args;
args << file_path;
QProcess process;
process.setReadChannel(QProcess::StandardOutput);
process.start(program_decrypt,args);
process.waitForFinished(-1);
//QTextStream in(&process->readAll());
QString out = process.readAll();
QStringList list_out = out.split("\n",QString::SkipEmptyParts);
//start resetting model (notify the view )
beginResetModel();
this->table.clear();
std::set<QString> uniq;
// parse file to model
// -----------------------
//while( !in.atEnd() )
int counter = 0;
while(counter < list_out.size())
{
//QString line = in.readLine();
QString line = list_out.at(counter);
// some new UT ?
// -------------------
if( !line.isNull() && !line.trimmed().isEmpty() != 0 && !line.contains("="))
{
line = line.trimmed();
std::vector<QString> row;
if(uniq.find(line) == uniq.end())
{
uniq.insert(line);
row.push_back(line);
/**QString user_line = in.readLine().trimmed();
QString password_line = in.readLine().trimmed();
QString server_line = in.readLine().trimmed();*/
QString user_line = list_out.at(++counter).trimmed();
QString password_line = list_out.at(++counter).trimmed();
QString server_line = list_out.at(++counter).trimmed();
if( user_line.startsWith("USER") )
{
user_line = user_line.split('=').at(1).trimmed();
}
if( password_line.startsWith("PASSWORD") )
{
password_line = password_line.split('=').at(1).trimmed();
}
if( server_line.startsWith("SERVER") )
{
server_line = server_line.split('=').at(1).trimmed();
}
row.push_back(user_line);
row.push_back(password_line);
row.push_back(server_line);
this->table.push_back(row);
}
else
{
QMessageBox msgBox;
msgBox.setText(QString("%1 is already defined, ingnoring").arg(line));
msgBox.exec();
}
}
++counter;
}
endResetModel();
}
}
Trying to get the why of the bug, following #Kuba Ober suggestion
I've tried to copy the first version into a main function looping indefinitely over all my environments:
But it does work fine!
#include <QtCore/QCoreApplication>
#include <QProcess>
#include <QTextStream>
#include <set>
#include <vector>
#include <QDebug>
#include <QFile>
#include <deque>
#include <QDir>
#include <QStringList>
std::deque< std::vector < QString > > table;
void f( QString file_path )
{
// does the param file exist ?
// --------------------------------
QFile param_file(file_path);
if( param_file.exists() && param_file.open(QIODevice::ReadOnly))
{
// Call QProcess, fx_decrypt, read standard output
QString program_decrypt = "/path/to/decrypt";
QStringList args;
args << file_path;
QProcess* process = new QProcess();
process->setReadChannel(QProcess::StandardOutput);
process->start(program_decrypt,args);
process->waitForFinished(-1);
QTextStream in(&process->readAll());
//start resetting model (notify the view )
table.clear();
std::set<QString> uniq;
// parse file to model
// -----------------------
while( !in.atEnd() )
{
QString line = in.readLine();
// some new UT ?
// -------------------
if( !line.isNull() && line.trimmed().isEmpty() != 0 && !line.contains("="))
{
line = line.trimmed();
std::vector<QString> row;
if(uniq.find(line) == uniq.end())
{
uniq.insert(line);
row.push_back(line);
QString user_line = in.readLine().trimmed();
QString password_line = in.readLine().trimmed();
QString server_line = in.readLine().trimmed();
if( user_line.startsWith("USER") )
{
user_line = user_line.split('=').at(1).trimmed();
}
if( password_line.startsWith("PASSWORD") )
{
password_line = password_line.split('=').at(1).trimmed();
}
if( server_line.startsWith("SERVER") )
{
server_line = server_line.split('=').at(1).trimmed();
}
row.push_back(user_line);
row.push_back(password_line);
row.push_back(server_line);
table.push_back(row);
}
else
{
qDebug() << QString("%1 is already defined, ingnoring").arg(line);
}
}
}
param_file.close();
delete process;
}
}
QStringList getEnvList()
{
QDir dir("/path/to/env/");
QStringList list_env;
foreach(QString folder, dir.entryList(QStringList(),QDir::AllDirs | QDir::NoDotAndDotDot))
{
QFile app_file( dir.absolutePath() + '/' + folder + '/' + "file.dat" );
if (app_file.exists())
{
list_env.push_back(folder);
}
}
return list_env;
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
int counter = 0;
while(true)
{
foreach ( QString s, getEnvList() )
{
qDebug() << counter++;
qDebug() << s;
f(s);
}
}
return a.exec();
}
I have no idea, why the two below codes never crash while the first one does...
I have a file which has absolute filepaths listed, 1 per line. The listed files are in order, so all files in e.g. the /Documents/ dir will be listed after eachother in the file.
What I want to do is to place all these files in a QTreeWidget in a nice hierarchic structure, just like a normal filesystem. How would I do that from my file of absolute paths I have?
This is how far I've gotten with my coding on this:
QFile file(FILENAME_ENCRYPTED);
QString line;
QDir dir;
QTreeWidgetItem *item;
if (file.open(QIODevice::ReadOnly)) {
QTextStream stream( &file );
do {
line = stream.readLine();
if (!line.isNull()) {
dir = QDir(line);
item = new QTreeWidgetItem();
item->setText(0, dir.dirName());
this->ui->treeWidget->addTopLevelItem(item);
}
} while (!line.isNull());
}
file.close();
This works fine, but it only lists all the filenames after eachother. I guess I have to do some recursive function but recursion is not my best friend, I prefer iteration! Could someone give me a push in the right direction? :)
No recursion should be necessary. You can use QString::split() to split the file path into separate QStrings in a QStringList based on a separator (i.e., "/"), then iterate through each QString to determine the file structure.
EDIT: Here is an example:
#include <QtGui>
const QString s1 = "Docs/Testing/textFile1.txt";
const QString s2 = "Docs/Testing/textFile2.txt";
const QString s3 = "Docs/Testing/textFile3.txt";
const QString s4 = "Docs/Testing/AnotherFolder/textFile4.txt";
const QString s5 = "ThisIsGonnaBeCrazy/WholeNewFolder/AndAnother/file.pdf";
const QString s6 = "ThisIsGonnaBeCrazy/file.doc";
class MainWindow : public QMainWindow
{
public:
MainWindow()
{
QTreeWidget *treeWidget = new QTreeWidget;
QStringList fileNames;
fileNames << s1 << s2 << s3 << s4 << s5 << s6;
QTreeWidgetItem *topLevelItem = NULL;
foreach (const QString &fileName, fileNames)
{
QStringList splitFileName = fileName.split("/");
// add root folder as top level item if treeWidget doesn't already have it
if (treeWidget->findItems(splitFileName[0], Qt::MatchFixedString).isEmpty())
{
topLevelItem = new QTreeWidgetItem;
topLevelItem->setText(0, splitFileName[0]);
treeWidget->addTopLevelItem(topLevelItem);
}
QTreeWidgetItem *parentItem = topLevelItem;
// iterate through non-root directories (file name comes after)
for (int i = 1; i < splitFileName.size() - 1; ++i)
{
// iterate through children of parentItem to see if this directory exists
bool thisDirectoryExists = false;
for (int j = 0; j < parentItem->childCount(); ++j)
{
if (splitFileName[i] == parentItem->child(j)->text(0))
{
thisDirectoryExists = true;
parentItem = parentItem->child(j);
break;
}
}
if (!thisDirectoryExists)
{
parentItem = new QTreeWidgetItem(parentItem);
parentItem->setText(0, splitFileName[i]);
}
}
QTreeWidgetItem *childItem = new QTreeWidgetItem(parentItem);
childItem->setText(0, splitFileName.last());
}
setCentralWidget(treeWidget);
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
Note that you can use QTreeWidgetItem::setData() to set the file name for each file if you like. My example does not do that, though.
This works for me:
QStringList tokens = path.split ( '/' );
QTreeWidgetItem* treeWidgetItem = NULL;
for ( int32_t j = 0 ; j < packageTreeWidget->topLevelItemCount(); ++j )
{
if ( packageTreeWidget->topLevelItem ( j )->text ( 0 ) == tokens.at ( 0 ) )
{
treeWidgetItem = packageTreeWidget->topLevelItem ( j );
break;
}
}
if ( treeWidgetItem == NULL )
{
treeWidgetItem = new QTreeWidgetItem;
treeWidgetItem->setText ( 0, tokens.at ( 0 ) );
packageTreeWidget->addTopLevelItem ( treeWidgetItem );
}
for ( int32_t j = 1; j < tokens.size(); ++j )
{
int32_t k;
for ( k = 0 ; k < treeWidgetItem->childCount(); ++k )
{
if ( treeWidgetItem->child ( k )->text ( 0 ) == tokens.at ( j ) )
{
treeWidgetItem = treeWidgetItem->child ( k );
break;
}
}
if ( k == treeWidgetItem->childCount() )
{
QTreeWidgetItem* newTreeWidgetItem = new QTreeWidgetItem;
newTreeWidgetItem->setText ( 0, tokens.at ( j ) );
treeWidgetItem->addChild ( newTreeWidgetItem );
treeWidgetItem = newTreeWidgetItem;
}
}
The index 'i' is missing from the loops because is the one I used to iterate the file paths, replace 'packageTreeWidget' with the name of your Tree Widget, I am using this to display the contents of a zip-like package file.
I am looking for a widget in Qt which is similar to MFC's IP address control. Does anyone know of such a widget, or perhaps how I can create one?
I have no idea what's an MFC IP Widget, but looks like it is a Widget to enter an IP address.
You need to use a QLineEdit with a inputMask "000.000.000.000;_"
QLineEdit *ipEdit = new QLineEdit();
ipEdit->setInputMask("000.000.000.000;_");
ipEdit->show();
A little improvement of jpo38's code...
#include <QFrame>
#include <QLineEdit>
#include <QIntValidator>
#include "stdint.h"
#include <QHBoxLayout>
#include <QFont>
#include <QLabel>
#include <QKeyEvent>
class IPCtrl : public QFrame
{
Q_OBJECT
public:
IPCtrl(QWidget *parent = 0);
~IPCtrl();
virtual bool eventFilter( QObject *obj, QEvent *event );
public slots:
void slotTextChanged( QLineEdit* pEdit );
signals:
void signalTextChanged( QLineEdit* pEdit );
private:
enum
{
QTUTL_IP_SIZE = 4,// число октетов IP адресе
MAX_DIGITS = 3 // число символов в LineEdit
};
QLineEdit *(m_pLineEdit[QTUTL_IP_SIZE]);
void MoveNextLineEdit (int i);
void MovePrevLineEdit (int i);
};
IPCtrl::IPCtrl(QWidget *parent) : QFrame(parent)
{
setFrameShape( QFrame::StyledPanel );
setFrameShadow( QFrame::Sunken );
QHBoxLayout* pLayout = new QHBoxLayout( this );
setLayout( pLayout );
pLayout->setContentsMargins( 0, 0, 0, 0 );
pLayout->setSpacing( 0 );
for ( int i = 0; i != QTUTL_IP_SIZE; ++i )
{
if ( i != 0 )
{
QLabel* pDot = new QLabel( ".", this );
pDot->setStyleSheet( "background: white" );
pLayout->addWidget( pDot );
pLayout->setStretch( pLayout->count(), 0 );
}
m_pLineEdit[i] = new QLineEdit( this );
QLineEdit* pEdit = m_pLineEdit[i];
pEdit->installEventFilter( this );
pLayout->addWidget( pEdit );
pLayout->setStretch( pLayout->count(), 1 );
pEdit->setFrame( false );
pEdit->setAlignment( Qt::AlignCenter );
QFont font = pEdit->font();
font.setStyleHint( QFont::Monospace );
font.setFixedPitch( true );
pEdit->setFont( font );
QRegExp rx ( "^(0|[1-9]|[1-9][0-9]|1[0-9][0-9]|2([0-4][0-9]|5[0-5]))$" );
QValidator *validator = new QRegExpValidator(rx, pEdit);
pEdit->setValidator( validator );
}
setMaximumWidth( 30 * QTUTL_IP_SIZE );
connect( this, SIGNAL(signalTextChanged(QLineEdit*)),
this, SLOT(slotTextChanged(QLineEdit*)),
Qt::QueuedConnection );
}
IPCtrl::~IPCtrl()
{
}
void IPCtrl::slotTextChanged( QLineEdit* pEdit )
{
for ( unsigned int i = 0; i != QTUTL_IP_SIZE; ++i )
{
if ( pEdit == m_pLineEdit[i] )
{
if ( ( pEdit->text().size() == MAX_DIGITS && pEdit->text().size() == pEdit->cursorPosition() ) || ( pEdit->text() == "0") )
{
// auto-move to next item
if ( i+1 != QTUTL_IP_SIZE )
{
m_pLineEdit[i+1]->setFocus();
m_pLineEdit[i+1]->selectAll();
}
}
}
}
}
bool IPCtrl::eventFilter(QObject *obj, QEvent *event)
{
bool bRes = QFrame::eventFilter(obj, event);
if ( event->type() == QEvent::KeyPress )
{
QKeyEvent* pEvent = dynamic_cast<QKeyEvent*>( event );
if ( pEvent )
{
for ( unsigned int i = 0; i != QTUTL_IP_SIZE; ++i )
{
QLineEdit* pEdit = m_pLineEdit[i];
if ( pEdit == obj )
{
switch ( pEvent->key() )
{
case Qt::Key_Left:
if ( pEdit->cursorPosition() == 0 )
{
// user wants to move to previous item
MovePrevLineEdit(i);
}
break;
case Qt::Key_Right:
if ( pEdit->text().isEmpty() || (pEdit->text().size() == pEdit->cursorPosition()) )
{
// user wants to move to next item
MoveNextLineEdit(i);
}
break;
case Qt::Key_0:
if ( pEdit->text().isEmpty() || pEdit->text() == "0" )
{
pEdit->setText("0");
// user wants to move to next item
MoveNextLineEdit(i);
}
emit signalTextChanged( pEdit );
break;
case Qt::Key_Backspace:
if ( pEdit->text().isEmpty() || pEdit->cursorPosition() == 0)
{
// user wants to move to previous item
MovePrevLineEdit(i);
}
break;
case Qt::Key_Comma:
case Qt::Key_Period:
MoveNextLineEdit(i);
break;
default:
emit signalTextChanged( pEdit );
break;
}
}
}
}
}
return bRes;
}
void IPCtrl::MoveNextLineEdit(int i)
{
if ( i+1 != QTUTL_IP_SIZE )
{
m_pLineEdit[i+1]->setFocus();
m_pLineEdit[i+1]->setCursorPosition( 0 );
m_pLineEdit[i+1]->selectAll();
}
}
void IPCtrl::MovePrevLineEdit(int i)
{
if ( i != 0 )
{
m_pLineEdit[i-1]->setFocus();
m_pLineEdit[i-1]->setCursorPosition( m_pLineEdit[i-1]->text().size() );
//m_pLineEdit[i-1]->selectAll();
}
}
I agree with little_su: QLineEdit with input mask does not look and behaves as nice as the standard Windows IP control. I worked out a complete QWidget-based IP control embedding 4 QLineEdit and 3 QLabel (for dots). It just looks and behaves perfectly as MFC/Windows IP controls.
Here is the code:
class IPCtrl : public QFrame
{
typedef QFrame baseClass;
Q_OBJECT
public:
IPCtrl(QWidget *parent);
~IPCtrl();
#define QTUTL_IP_SIZE 4
virtual bool eventFilter( QObject *obj, QEvent *event );
public slots:
void slotTextChanged( QLineEdit* pEdit );
signals:
void signalTextChanged( QLineEdit* pEdit );
private:
QLineEdit *(m_pLineEdit[QTUTL_IP_SIZE]);
static std::string getIPItemStr( unsigned char item );
};
class IPItemValidator : public QIntValidator
{
public:
IPItemValidator( QObject* parent ) : QIntValidator( parent )
{
setRange( 0, UCHAR_MAX );
}
~IPItemValidator() {}
virtual void fixup( QString & input ) const
{
if ( input.isEmpty() )
input = "0";
}
};
IPCtrl::IPCtrl(QWidget *parent) : baseClass(parent)
{
setFrameShape( QFrame::StyledPanel );
setFrameShadow( QFrame::Sunken );
QHBoxLayout* pLayout = new QHBoxLayout( this );
setLayout( pLayout );
pLayout->setContentsMargins( 0, 0, 0, 0 );
pLayout->setSpacing( 0 );
for ( int i = 0; i != QTUTL_IP_SIZE; ++i )
{
if ( i != 0 )
{
QLabel* pDot = new QLabel( ".", this );
pDot->setStyleSheet( "background: white" );
pLayout->addWidget( pDot );
pLayout->setStretch( pLayout->count(), 0 );
}
m_pLineEdit[i] = new QLineEdit( this );
QLineEdit* pEdit = m_pLineEdit[i];
pEdit->installEventFilter( this );
pLayout->addWidget( pEdit );
pLayout->setStretch( pLayout->count(), 1 );
pEdit->setFrame( false );
pEdit->setAlignment( Qt::AlignCenter );
QFont font = pEdit->font();
font.setStyleHint( QFont::Monospace );
font.setFixedPitch( true );
pEdit->setFont( font );
pEdit->setValidator( new IPItemValidator( pEdit ) );
}
setMaximumWidth( 30 * QTUTL_IP_SIZE );
connect( this, SIGNAL(signalTextChanged(QLineEdit*)),
this, SLOT(slotTextChanged(QLineEdit*)),
Qt::QueuedConnection );
}
IPCtrl::~IPCtrl()
{
}
std::string IPCtrl::getIPItemStr( unsigned char item )
{
std::strstream str;
str << (int) item;
str << std::ends;
return str.str();
}
void IPCtrl::slotTextChanged( QLineEdit* pEdit )
{
for ( unsigned int i = 0; i != QTUTL_IP_SIZE; ++i )
{
if ( pEdit == m_pLineEdit[i] )
{
if ( pEdit->text().size() == getIPItemStr( UCHAR_MAX ).size() &&
pEdit->text().size() == pEdit->cursorPosition() )
{
// auto-move to next item
if ( i+1 != QTUTL_IP_SIZE )
{
m_pLineEdit[i+1]->setFocus();
m_pLineEdit[i+1]->selectAll();
}
}
}
}
}
bool IPCtrl::eventFilter(QObject *obj, QEvent *event)
{
bool bRes = baseClass::eventFilter(obj, event);
if ( event->type() == QEvent::KeyPress )
{
QKeyEvent* pEvent = dynamic_cast<QKeyEvent*>( event );
if ( pEvent )
{
for ( unsigned int i = 0; i != QTUTL_IP_SIZE; ++i )
{
QLineEdit* pEdit = m_pLineEdit[i];
if ( pEdit == obj )
{
switch ( pEvent->key() )
{
case Qt::Key_Left:
{
if ( pEdit->cursorPosition() == 0 )
{
// user wants to move to previous item
if ( i != 0 )
{
m_pLineEdit[i-1]->setFocus();
m_pLineEdit[i-1]->setCursorPosition( m_pLineEdit[i-1]->text().size() );
}
}
break;
}
case Qt::Key_Right:
{
if ( pEdit->text().isEmpty() ||
(pEdit->text().size() == pEdit->cursorPosition()) )
{
// user wants to move to next item
if ( i+1 != QTUTL_IP_SIZE )
{
m_pLineEdit[i+1]->setFocus();
m_pLineEdit[i+1]->setCursorPosition( 0 );
}
}
break;
}
default:
{
emit signalTextChanged( pEdit );
}
}
break;
}
}
}
}
return bRes;
}
A little improvement of Tugo's code... I cannot comment so...
classic array to std::array
setter and getter
remove styleSheet, u can use your own
ipctrl.h :
#pragma once
#include <QFrame>
#include <array>
/// Thanx to https://stackoverflow.com/a/11358560/8524139
class QLineEdit;
class IPCtrl : public QFrame
{
Q_OBJECT
enum
{
QTUTL_IP_SIZE = 4, // число октетов IP адресе
MAX_DIGITS = 3 // число символов в LineEdit
};
public:
IPCtrl(QWidget *parent = 0);
~IPCtrl();
virtual bool eventFilter(QObject *obj, QEvent *event);
std::array<quint8, QTUTL_IP_SIZE> getIP() const;
void setIP(std::array<quint8, QTUTL_IP_SIZE> ipAddr);
signals:
void signalTextChanged(QLineEdit *pEdit);
private:
std::array<QLineEdit *, QTUTL_IP_SIZE> m_pLineEdit;
void slotTextChanged(QLineEdit *pEdit);
void moveNextLineEdit(int i);
void movePrevLineEdit(int i);
};
ipctrl.cpp :
#include "ipctrl.h"
#include <QHBoxLayout>
#include <QIntValidator>
#include <QKeyEvent>
#include <QLabel>
#include <QLineEdit>
IPCtrl::IPCtrl(QWidget *parent) : QFrame(parent)
{
setFrameShape(QFrame::StyledPanel);
setFrameShadow(QFrame::Sunken);
QHBoxLayout *pLayout = new QHBoxLayout(this);
setLayout(pLayout);
pLayout->setContentsMargins(0, 0, 1, 0);
pLayout->setSpacing(0);
for (int i = 0; i != QTUTL_IP_SIZE; ++i)
{
if (i != 0)
{
QLabel *pDot = new QLabel(".", this);
pLayout->addWidget(pDot);
pLayout->setStretch(pLayout->count(), 0);
}
m_pLineEdit.at(i) = new QLineEdit(this);
QLineEdit *pEdit = m_pLineEdit.at(i);
pEdit->installEventFilter(this);
pLayout->addWidget(pEdit);
pLayout->setStretch(pLayout->count(), 1);
pEdit->setFrame(false);
pEdit->setAlignment(Qt::AlignCenter);
QFont font = pEdit->font();
font.setStyleHint(QFont::Monospace);
font.setFixedPitch(true);
pEdit->setFont(font);
QRegExp rx("^(0|[1-9]|[1-9][0-9]|1[0-9][0-9]|2([0-4][0-9]|5[0-5]))$");
QValidator *validator = new QRegExpValidator(rx, pEdit);
pEdit->setValidator(validator);
}
setMaximumWidth(30 * QTUTL_IP_SIZE);
connect(this, &IPCtrl::signalTextChanged, this, &IPCtrl::slotTextChanged, Qt::QueuedConnection);
}
IPCtrl::~IPCtrl()
{
}
void IPCtrl::slotTextChanged(QLineEdit *pEdit)
{
for (unsigned int i = 0; i != QTUTL_IP_SIZE; ++i)
{
if (pEdit == m_pLineEdit.at(i))
{
if ((pEdit->text().size() == MAX_DIGITS && pEdit->text().size() == pEdit->cursorPosition())
|| (pEdit->text() == "0"))
{
// auto-move to next item
if (i + 1 != QTUTL_IP_SIZE)
{
m_pLineEdit.at(i + 1)->setFocus();
m_pLineEdit.at(i + 1)->selectAll();
}
}
}
}
}
bool IPCtrl::eventFilter(QObject *obj, QEvent *event)
{
bool bRes = QFrame::eventFilter(obj, event);
if (event->type() == QEvent::KeyPress)
{
QKeyEvent *pEvent = dynamic_cast<QKeyEvent *>(event);
if (pEvent)
{
for (unsigned int i = 0; i != QTUTL_IP_SIZE; ++i)
{
QLineEdit *pEdit = m_pLineEdit[i];
if (pEdit == obj)
{
switch (pEvent->key())
{
case Qt::Key_Left:
if (pEdit->cursorPosition() == 0)
{
// user wants to move to previous item
movePrevLineEdit(i);
}
break;
case Qt::Key_Right:
if (pEdit->text().isEmpty() || (pEdit->text().size() == pEdit->cursorPosition()))
{
// user wants to move to next item
moveNextLineEdit(i);
}
break;
case Qt::Key_0:
if (pEdit->text().isEmpty() || pEdit->text() == "0")
{
pEdit->setText("0");
// user wants to move to next item
moveNextLineEdit(i);
}
emit signalTextChanged(pEdit);
break;
case Qt::Key_Backspace:
if (pEdit->text().isEmpty() || pEdit->cursorPosition() == 0)
{
// user wants to move to previous item
movePrevLineEdit(i);
}
break;
case Qt::Key_Comma:
case Qt::Key_Period:
moveNextLineEdit(i);
break;
default:
emit signalTextChanged(pEdit);
break;
}
}
}
}
}
return bRes;
}
std::array<quint8, IPCtrl::QTUTL_IP_SIZE> IPCtrl::getIP() const
{
std::array<quint8, QTUTL_IP_SIZE> ipAddr;
std::transform(m_pLineEdit.cbegin(), m_pLineEdit.cend(), ipAddr.begin(),
[](const QLineEdit *lineEdit) -> quint8 { return lineEdit->text().toUInt(); });
return ipAddr;
}
void IPCtrl::setIP(std::array<quint8, IPCtrl::QTUTL_IP_SIZE> ipAddr)
{
for (auto i = 0; i != QTUTL_IP_SIZE; ++i)
{
m_pLineEdit.at(i)->setText(QString::number(ipAddr.at(i)));
}
}
void IPCtrl::moveNextLineEdit(int i)
{
if (i + 1 != QTUTL_IP_SIZE)
{
m_pLineEdit.at(i + 1)->setFocus();
m_pLineEdit.at(i + 1)->setCursorPosition(0);
m_pLineEdit.at(i + 1)->selectAll();
}
}
void IPCtrl::movePrevLineEdit(int i)
{
if (i != 0)
{
m_pLineEdit.at(i - 1)->setFocus();
m_pLineEdit.at(i - 1)->setCursorPosition(m_pLineEdit[i - 1]->text().size());
// m_pLineEdit[i-1]->selectAll();
}
}
Problem: I'm looking example about creating model ( based on QAbstractItemModel ) to QTreeView, but can't find sane codes. Qt examples are based on QStandardModel, which is not very useful and complex, Internet examples are based on python?! codes... Other information can't gave me the right directions to go. So, here is what I have:
std::map
typedef std::map< CompanyData, std::vector< ContractorData >, LessData< CompanyData > > Companies;
Here it's data example (CompanyName + ContractorsNames):
[Microsoft]*
[Bill Gates]
[Steve Balmer]
[...]
[Apple]*
[Steve Jobs - R.I.P.]
[Wozniak]
[OtherStuff]*
...
where * means - expandable item (parent)
And all that I need it to create QTreeView with this data above!
Can any one help?
Many thanks!
So, as no posts was done here, I post here my own solution (also used text filtering):
void ContractorsFilter::onCustomFilterChanged( const QString& text )
{
try
{
struct MatchFilter
{
// data
QString filter_;
Companies& filtered_recipients_;
// methods
MatchFilter( const QString& _filter, Companies& _recipients )
: filter_( _filter )
, filtered_recipients_( _recipients )
{
filtered_recipients_.clear();
}
void operator()( const Companies::value_type& val ) const
{
bool isFound = false;
std::vector< ContractorData >::const_iterator con_i( val.second.begin() ), con_e( val.second.end() );
for( ; con_i != con_e; ++con_i )
{
const QString contractorName = (*con_i).name;
if( contractorName.contains( filter_, Qt::CaseInsensitive ) )
{
filtered_recipients_[ val.first ].push_back( (*con_i) );
isFound = true;
}
}
const QString companyName = val.first.name;
if( companyName.contains( filter_, Qt::CaseInsensitive ) )
{
filtered_recipients_[ val.first ].push_back( ContractorData() );
}
}
};
struct FillView
{
// data
QFont boldFont;
QBrush whiteBrush;
QStandardItemModel* model_;
// methods
FillView( QStandardItemModel* model )
: model_( model )
{
model_->clear();
}
void operator ()( const Companies::value_type& val ) const
{
struct AppendContractors
{
// data
QStandardItem* parent_;
// methods
AppendContractors( QStandardItem* _parent = 0 )
: parent_( _parent )
{}
bool isEmpty( const ContractorData& contractor ) const
{
return contractor.id.isEmpty();
}
void operator()( const std::vector< ContractorData >::value_type& contractor ) const
{
if( !isEmpty( contractor ) )
{
QStandardItem *item = 0;
QList< QStandardItem* > line;
line << ( item = new QStandardItem( QIcon( ACCOUNT_ITEM_ICON ), contractor.name ) );
item->setSizeHint( QSize( 0, 25 ) );
parent_->appendRow( line );
}
}
};
QStandardItem *parentItem = model_->invisibleRootItem();
// добавляем новую компанию + контрагента
QList< QStandardItem* > line;
line << ( parentItem = new QStandardItem( QIcon( COMPANY_ITEM_ICON ), val.first.name ) );
parentItem->setSizeHint( QSize( 0, 25 ) );
model_->appendRow( line );
std::for_each( val.second.begin(), val.second.end(), AppendContractors( parentItem ) );
}
};
// удаляем символ(ы), которые не фильтруются
// формируется новая таблица, которая и будет использоваться моделью для отображения
std::for_each( data_.begin(), data_.end(),
MatchFilter( text, filter_data_ ) );
// вывод отфильтрованных контрагентов
std::for_each( filter_data_.begin(), filter_data_.end(),
FillView( model_ ) );
ui_.treeView->expandAll();
}
catch( const std::exception& e )
{
Core::errLog( "ContractorsFilter::onCustomFilterChanged", e.what() );
throw;
}
}
PS: type Companies is a
typedef std::map< CompanyData, ContractorsData, LessData< CompanyData > > Companies;
where CompanyData, ContractorsData are simple structures...
Have a nice day!