QTreeWidget icons issue - c++

I want to display users avatars to treewidget items as icons. I get avatars path and other users info from the database and store it in QVector with the structure.
MyDB *employeesAccountsDB = new MyDB();
connect(employeesAccountsDB, &MyDB::accountInfo, [this](QVector<AccountData> dataVector) {
for (int i = 0; i < dataVector.size(); i++) {
QTreeWidgetItem *item = new QTreeWidgetItem(treeWidget);
item->setSizeHint(0, QSize(40, 40));
item->setText(0, dataVector[i].avatarPath);
item->setText(1, dataVector[i].username);
item->setText(2, dataVector[i].email);
item->setText(3, dataVector[i].group);
emit avatarPath(dataVector[i].avatarPath);
}
dataVector.clear();
});
Info is a GUI class.
NetManager *avatarNetManager = new NetManager();
connect(this, &Info::avatarPath, avatarNetManager, &NetManager::getAvatar);
connect(avatarNetManager, &NetManager::image, [this](QPixmap avatar, int index) {
QIcon avatarIcon;
if (!avatar.isNull()) {
avatarIcon = avatar.scaled(80, 80);
} else {
avatarIcon = QPixmap(":/Icon/default_avatar.png").scaled(80, 80);
}
qDebug() << index;
treeWidget->topLevelItem(index)->setIcon(0, avatarIcon);
});
int index = 0; // initialized as global variable in the `NetManager` constructor
void NetManager::getAvatar(QString path, int index)
{
networkManager = new QNetworkAccessManager(this);
networkManager->get(QNetworkRequest(QUrl(path)));
connect(networkManager, &QNetworkAccessManager::finished, [this](QNetworkReply *avatarNetReply) {
QPixmap avatarPixmap;
avatarPixmap.loadFromData(avatarNetReply->readAll());
emit image(avatarPixmap, index);
index++;
avatarNetReply->close();
avatarNetReply->deleteLater();
networkManager->deleteLater();
emit finished();
});
}
For example, first icon is Ok, third icon should be on the second item and second icon should be on third item.
I need something to maintain the order of the icons.
All works well but the avatars don't relate to the users. Any ideas how to collect and retrieve them to the appropriate user? Thanks.
Updated:
INDEX: 2
QPixmap(QSize(1280, 720),depth=32,devicePixelRatio=1,cacheKey=0xee000002d0)
INDEX: 0
QPixmap(QSize(380, 400),depth=32,devicePixelRatio=1,cacheKey=0xf000000190)
INDEX: 1
QPixmap(QSize(277, 400),depth=32,devicePixelRatio=1,cacheKey=0xf200000190)
INDEX: 0
QPixmap(QSize(380, 400),depth=32,devicePixelRatio=1,cacheKey=0xed00000190)
INDEX: 2
QPixmap(QSize(1280, 720),depth=32,devicePixelRatio=1,cacheKey=0xf0000002d0)
INDEX: 1
QPixmap(QSize(277, 400),depth=32,devicePixelRatio=1,cacheKey=0xf200000190)
Indices change every time application launches.
Code:
MyDB *employeesAccountsDB = new MyDB();
connect(employeesAccountsDB, &MyDB::accountInfo, [this](QVector<AccountData> dataVector) {
for (int i = 0; i < dataVector.size(); i++) {
QTreeWidgetItem *item = new QTreeWidgetItem(treeWidget);
item->setSizeHint(0, QSize(40, 40));
item->setText(0, dataVector[i].avatarPath);
item->setText(1, dataVector[i].username);
item->setText(2, dataVector[i].email);
item->setText(3, dataVector[i].group);
emit avatarPath(dataVector[i].avatarPath, i);
}
dataVector.clear();
});
void NetManager::getAvatar(QString path, int index)
{
networkManager = new QNetworkAccessManager(this);
networkManager->get(QNetworkRequest(QUrl(path)));
connect(networkManager, &QNetworkAccessManager::finished, [this, index](QNetworkReply *avatarNetReply) {
QPixmap avatarPixmap;
avatarPixmap.loadFromData(avatarNetReply->readAll());
emit image(avatarPixmap, index);
avatarNetReply->close();
avatarNetReply->deleteLater();
networkManager->deleteLater();
emit finished();
});
}
NetManager *avatarNetManager = new NetManager();
connect(this, &Info::avatarPath, avatarNetManager, &NetManager::getAvatar);
connect(avatarNetManager, &NetManager::image, [this](QPixmap avatar, int index) {
QIcon avatarIcon;
if (!avatar.isNull()) {
avatarIcon = avatar.scaled(80, 80);
} else {
avatarIcon = QPixmap(":/Icon/default_avatar.png").scaled(80, 80);
}
qDebug() << index;
treeWidget->topLevelItem(index)->setIcon(0, avatarIcon);
});

I have fixed it by QString variable to hold the avatar path and QStringList to hold other user info and when image is retrieved from the path I do the following:
MyDB *employeesAccountsDB = new MyDB();
connect(employeesAccountsDB, &MyDB::accountInfo, [this](QPixmap avatar, QStringList accountInfo) {
QIcon avatarIcon;
if (!avatar.isNull()) {
avatarIcon = avatar.scaled(80, 80);
} else {
avatarIcon = QPixmap(":/Icon/default_avatar.png").scaled(80, 80);
}
QTreeWidgetItem *item = new QTreeWidgetItem(treewidget);
item->setIcon(0, avatarIcon);
item->setSizeHint(0, QSize(40, 40));
for (int i = 0; i < accountInfo.size(); i++) {
item->setText(i, accountInfo.at(i));
}
});
All works well.

Related

Custom Widgets which inherits from QTreeWidget crashes while destructing?

Recently I've met some problems about QT.As we have a lot of custom widgets (inherit from QT's basic widgets such as QWidget,QTreeWidget...),they worked fine at runtime,BUT,IT IS EASY TO CRASH when exiting program or destructing them,I don't know IF THERE ANYTHING WRONG or inappropriate while implementing a widget which inherits from a basic widget.
Here is an example:
ChatTreeWidget.cpp:
#include "ChatTreeWidget.h"
#include <QPicture>
#include <QDebug>
ChatTreeWidget::ChatTreeWidget(QWidget *parent)
:QTreeWidget(parent)
{
setRootIsDecorated(false);
setIndentation(0);
setHeaderHidden(true);
setIconSize(QSize(30, 30));
setFocusPolicy(Qt::NoFocus);
setObjectName(QString("chatTreeWidget"));
setStyleSheet("#chatTreeWidget{border:0px;}"
"QTreeWidget::item{border-bottom:1px solid #d9d9d9;padding-left:0px;}"
"QTreeWidget::item:hover{background-color: #def0ff;}"
"QTreeWidget::item:selected{background-color:#c2e2fd;}");
connect(this, &QTreeWidget::itemExpanded, this, [=](QTreeWidgetItem *item)
{
auto indexvar = item->data(0, Qt::UserRole).toInt();
indexvar -= 10000;
if (indexvar > m_iconList.size() || indexvar < 0)
return;
auto lbl = m_iconList.at(indexvar);
lbl->setPixmap(QPixmap(":/Widgets/Resources/chattree/sanjiao1.png"));
});
connect(this, &QTreeWidget::itemCollapsed, this, [=](QTreeWidgetItem *item)
{
auto indexvar = item->data(0, Qt::UserRole).toInt();
indexvar -= 10000;
if (indexvar > m_iconList.size() || indexvar < 0)
return;
auto lbl = m_iconList.at(indexvar);
lbl->setPixmap(QPixmap(":/Widgets/Resources/chattree/sanjiao2.png"));
});
connect(this, &QTreeWidget::itemClicked, this, [=](QTreeWidgetItem *item, int column) {
auto indexvar = item->data(0, Qt::UserRole).toInt();
indexvar -= 10000;
if (indexvar > m_iconList.size() || indexvar < 0)
return;
if (item->isExpanded())
{
setItemExpanded(item, false);
}
else
{
setItemExpanded(item, true);
}
});
setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
}
ChatTreeWidget::~ChatTreeWidget()
{
qDebug() << "123123";
}
QTreeWidgetItem* ChatTreeWidget::addTreeWidgetTopItem(QString str)
{
return insertTreeWidgetTopItem(topLevelItemCount(), str);
}
QTreeWidgetItem* ChatTreeWidget::insertTreeWidgetTopItem(int index, QString str)
{
QWidget *widget = new QWidget(this);
QLabel *textLabel = new QLabel(widget);
QHBoxLayout *hlayout = new QHBoxLayout(widget);
QLabel *iconLabel = new QLabel(widget);
QSpacerItem *spacer = new QSpacerItem(20, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
textLabel->setText(str);
textLabel->setStyleSheet(u8"font-family: \"Microsoft YaHei\"");
hlayout->addWidget(textLabel);
hlayout->addSpacerItem(spacer);
iconLabel->setPixmap(QPixmap(":/Widgets/Resources/chattree/sanjiao2.png"));
hlayout->addWidget(iconLabel);
hlayout->setContentsMargins(5, 0, 10, 0);
widget->setLayout(hlayout);
m_iconList.append(iconLabel);
QTreeWidgetItem *rootItem = new QTreeWidgetItem();
rootItem->setData(0, Qt::UserRole, m_index);
rootItem->setSizeHint(0, QSize(30, 30));
m_index++;
this->insertTopLevelItem(index, rootItem);
setItemWidget(rootItem, 0, widget);
rootItem->setBackground(0, Qt::green);
setExpandsOnDoubleClick(false);
return rootItem;
}
void ChatTreeWidget::setRootItemStyleSheet(QTreeWidgetItem *item,QString css)
{
auto widgets = itemWidget(item, 0);
widgets->setStyleSheet(css);
}
void ChatTreeWidget::setRootItemStyleSheet(QString css)
{
for (qint32 i = 0; i < topLevelItemCount(); i++)
{
auto rootItem = topLevelItem(i);
auto widgets = itemWidget(rootItem,0);
widgets->setStyleSheet(css);
}
}
this code is trying to implement a custom-treewidget to display chat members,so it inherits the QTreeWidget (once I thought I should implement it with QTreeView,but as I'm stupid and lazy,I used QTreeWidget Directly),and uses a special style-sheet to change display style.and as it's a public widget,it offers some interfaces to add top-level items.there is an example image:
when I destruct this widget (I' sure that this widget is token from UI or layout) and even sometimes QT's automatic destructing will cause a crash.It happens very frequently but not everytime.
the two main ways of destroying the widget are:
1.Add it to some Ui,and let it managed by that UI.
2.delete by hand:
chatTreeWidget->setParent(0);
chatTreeWidget->disconnect();
chatTreeWidget->deleteLater();
both of them can cause a random crash.
Is there anything wrong? or it's just a bug?
Thanks for any kind of help!

Getting a widget by string

In a for loop, I can construct a string.
foreach (...) {
QString str = "pushButton" + QString::number(count);
// this gives pushButton1
// Now I want to get the button widget that has this string as it's variable name
// and modify it in some way, e.g. change it's button caption
str->setText("New Title");
count ++;
}
Is this possible? if so, then how
edited: here's how i created the buttons
for (int i=0; i<total; i++) {
QPushButton *btn = new QPushButton();
btn->setObjectName("pushButton" + QString::number(i));
ui->horizontalLayout->addWidget(btn);
}
You can get the button through the parent and objectName, in your case the parent is this, so you should use the following:
QWidget* p = ui->horizontalLayout->parentWidget();
for(int count=0; count<total; count++){
const QString str = "pushButton" + QString::number(count);
QPushButton* btn = p->findChild<QPushButton *>(str);
btn->setText("someText");
}

How can i add new row to the existing QTablewidget

Problem in getting mouseEvent on QTableWidget, this code is to create a window with tabelwidget and mouseclickevent, when i click right button of mouse then i got two action event options named "add" and "delete",
i want to add new rows with 3 columns when i click "add" event function, and delete the last row when i click on "delete" event function,
(sorry for my english), any help is appreciated.
#include "notepad.h"
#include <QMessageBox>
#include <QTableView>
#include <QMouseEvent>
Notepad::Notepad()
{
test() ;
add_action = new QAction(tr("Add cell"), this);
add_action->setIcon(QIcon("add.jpg"));
Delete_action = new QAction(tr("Delete cell"), this);
Delete_action->setIcon(QIcon("delete.jpg"));
connect(Delete_action, SIGNAL(triggered()), this, SLOT(Delete()));
connect(add_action, SIGNAL(triggered()), this, SLOT(add()));
centralWidget()->setAttribute(Qt::WA_TransparentForMouseEvents);
centralWidget()->setAttribute(Qt::WA_MouseTracking,true);
setMouseTracking(true);
}
void Notepad::test()
{
QTableWidget* table = new QTableWidget();
QTableWidgetItem* tableItem = new QTableWidgetItem();
table->setRowCount(1);
table->setColumnCount(3);
table->setItem(0,0,new QTableWidgetItem());
table->setItem(0,1,new QTableWidgetItem());
table->setItem(0,2,new QTableWidgetItem());
table->setMouseTracking(true);
table->viewport()->setMouseTracking(true);
table->installEventFilter(this);
table->viewport()->installEventFilter(this);
table->setSelectionBehavior(QAbstractItemView::SelectRows);
table->setSelectionMode(QAbstractItemView::SingleSelection);
tableItem->setFlags(tableItem->flags() ^ Qt::ItemIsEditable);
table->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
setCentralWidget(table);
}
void Notepad::mouseReleaseEvent (QMouseEvent * event )
{
QMessageBox* msgBox;
if(event->button() == Qt::RightButton)
{
QMouseEvent *mouseEvent = static_cast<QMouseEvent*> (event);
QMenu *menu = new QMenu(this);
menu->addAction(add_action);
menu->addAction(Delete_action);
menu->exec(mouseEvent->globalPos());
}
}
void Notepad::add()
{
QTableWidget* table = new QTableWidget();
test();
table->setColumnCount(3);*/
int newRow = table->rowCount();
int newcol = table->columnCount();
qDebug() << newRow;
for (int row ; row < newRow+1 ; ++row)
{
QWidget *parent;
QStyleOptionViewItem option;
for (int column = 0; column < 3; ++column)
{
table->insertRow( table->rowCount());
table->insertColumn( newcol );
}
}
setCentralWidget(table);
centralWidget()->setAttribute(Qt::WA_TransparentForMouseEvents);
setMouseTracking(true);
}
void Notepad::Delete()
{
QTableWidget* table = new QTableWidget();
add();
int row=table->rowCount();
if (int i= row){
table->removeRow(i);
}
setCentralWidget(table);
centralWidget()->setAttribute(Qt::WA_TransparentForMouseEvents);
setMouseTracking(true);
}
You have 2 questions in one: how to create a context menu and how to add a row.
How to create a context menu.
Step 1: create a menu once. In constructor, for example.
// _menu is a class member.
_menu = new QMenu(this);
_menu->addAction(add_action);
_menu->addAction(Delete_action);
Step 2: show it in corresponding event:
void Notepad::contextMenuEvent(QContextMenuEvent *event)
{
_menu->exec(event->globalPos());
}
How to add a row.
At first, you need a table as a class member. Why do you create a new instance every time? They are different objects! You create a new table widget on each add call!
At second, you need to increase rows count and to fill a new row:
void Notepad::add()
{
const int newIndex = _table->rowCount();
_table->setRowCount(newIndex + 1);
// Fill data in row with index newIndex.
...
}

Qt couting checked buttons made dynamically

I am trying to count how many buttons have been checked
void pracownik2::on_pushButton_4_clicked()
{
this->setWindowTitle("EKRAN");
QWidget *centralWidget = new QWidget;
int licznik=1;
QString licz;
QString kolumny = ui->lineEdit->text();
QString wiersze = ui->lineEdit_2->text();
QPushButton *button[wiersze.toInt()][kolumny.toInt()];
QGridLayout *controlsLayout = new QGridLayout;
for(int i=0;i<wiersze.toInt();i++)
{
for(int j=0;j<kolumny.toInt();j++)
{
licz = QString::number(licznik);
licznik++;
button[i][j] = new QPushButton(licz);
button[i][j]->setCheckable(1);
controlsLayout->addWidget(button[i][j], i, j);
}
}
QPushButton *okej = new QPushButton("Zatwierdź");
QPushButton *anul = new QPushButton("Anuluj");
controlsLayout->addWidget(okej, wiersze.toInt(), 0);
controlsLayout->addWidget(anul, wiersze.toInt(), 1);
controlsLayout->setHorizontalSpacing(0);
controlsLayout->setVerticalSpacing(0);
centralWidget->setLayout(controlsLayout);
setCentralWidget(centralWidget);
for(int i=0;i<wiersze.toInt();i++)
{
for(int j=0;j<kolumny.toInt();j++)
{
connect(button[i][j],SIGNAL(toggled(bool)),this,SLOT(tescik()));
}
}
connect(anul,SIGNAL(clicked()),this,SLOT(close()));
connect(okej,SIGNAL(clicked()),this,SLOT(okay2()));
}
void pracownik2::tescik()
{
miejsca++;
}
void pracownik2::okay2()
{
QString m=QString::number(miejsca);
QMessageBox::information(this,"elo","wybranych miejsc: " + m);
}
If i check buttons number 1,2,3 and press okay button it shows that 3 buttons have been checked BUT if I check button number 1 and uncheck it and press okay it shows that 2 buttons have been checked. How to make my variable increment only if I check button, not also when I uncheck it?
I'm sorry for the code edit, just couldn't make it look better
The signal toggled has a boolean parameter, add it to your slot, and adjust count according to it. Change connect:
connect(button[i][j],SIGNAL(toggled(bool)),this,SLOT(tescik(bool)));
And change slot:
void pracownik2::tescik(bool t) {
if (t) miejsca++;
else miejsca--;
}

QPushButton in QTableView

I'm working on a project where i'm trying to add a QPushButton into a QTableView.
also i want to connect that button to open a document from a database. So far i added the button and i wrote the connect statement for it but when i click on the button nothing happened.
here is my code
void MainWindow::DocumentTable()
{
tableview = new QTableView;
query = new QSqlQueryModel(this);
signalMapper = new QSignalMapper(this);
foreach(it,treeWidget->selectedItems())
{
for (int col=0; col< it->columnCount(); ++col)
{
qDebug() << col << it->text(col);
QSqlQuery qry;
qry.prepare("select * from document where Folno=:Folno");
qry.bindValue(":Folno", it->text(col));
qry.exec();
query->setQuery(qry);
tableview->setModel(query);
tableview->setEditTriggers(QAbstractItemView::NoEditTriggers);
for (int i = 0; i< 1000; i++)
{
button= new QPushButton("Open Document");
tableview->setIndexWidget(tableview->model()->index(i, 0), button);
signalMapper->setMapping(button, i);
}
connect(signalMapper, SIGNAL(mapped(int)), this, SLOT(imageFROMdatabase()));
tableview->show();
Docwidget= new QDockWidget(this);
Docwidget->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
Docwidget->setWidget(tableview);
addDockWidget(Qt::RightDockWidgetArea,Docwidget);
Docwidget->show();
if(!query->submit())
{
QMessageBox::warning(0,"Error",query->lastError().text());
}
db.close();
}
}
}
and this is the slot function
void MainWindow::imageFROMdatabase()
{
QSqlQuery imageQuery;
imageQuery.prepare("SELECT * from doc_page where doc_no=:doc_no and f_number=:f_number");
imageQuery.bindValue(":doc_no", 1);
imageQuery.bindValue(":f_number",1);
imageQuery.exec();
imageQuery.next();
if( imageQuery.lastError().isValid())
{
QMessageBox::warning(0,"Error",imageQuery.lastError().text());
// QSqlDatabase::database().rollback();
}
else
{
// QByteArray ba1 = imageQuery.value(1).toByteArray();
QPixmap pic;
pic.loadFromData( ba);
scrollArea = new QScrollArea;
scrollArea->setBackgroundRole(QPalette::Dark);
scrollArea->setEnabled(true);
QString fileName = QFileDialog::getOpenFileName(this,"Open Image File",QDir::currentPath());
QImage image(fileName);
scene = new QGraphicsScene();
view = new QGraphicsView(scene);
item = new QGraphicsPixmapItem(QPixmap::fromImage(image));
scene->addItem(item);
xwidget= new QDockWidget(this);
xwidget->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
xwidget->setWidget(view);
addDockWidget(Qt::RightDockWidgetArea,xwidget);
xwidget->show();
db.close();
}
}
please tell if anything wrong in these codes.
The slot method is not same as signal method. They should have exact equal signatures. qDebug() should have generated some warnings for you. Read outputs carefully. imageFROMDatabase() method should accept an integer as its input too. Signals are not real functions. They are used to trigger another function which is matched in signature with them.
connect(signalMapper, SIGNAL(mapped(int)), this, SLOT(imageFROMdatabase()));
Change it like this :
connect(signalMapper, SIGNAL(mapped(int)), this, SLOT(imageFROMdatabase(int)));
ans also :
void MainWindow::imageFROMdatabase( int x ) { ... }
Also if the signals and slots are not in a thread read the manual enum Qt::ConnectionType