I am trying to run some code when I select a new index in a QTreeView
In RoverPlanner.h
namespace Ui {
class RoverPlanner;
}
class RoverPlanner : public QWidget
{
Q_OBJECT
public:
explicit RoverPlanner(QWidget *parent = nullptr);
void save_paths_from_tree(QTreeView* treeView);
void load_paths_into_tree(QTreeView* treeView);
std::vector<cuarl_path::Path> get_paths(const char* filename) const;
void update_segment_editor();
cuarl_path::Segment* addSegment();
~RoverPlanner();
private Q_SLOTS:
void treeSelectionChanged(const QModelIndex& prevIndex, const QModelIndex& nextIndex);
private:
Ui::RoverPlanner *ui;
};
In RoverPlanner.cpp
RoverPlanner::RoverPlanner(QWidget *parent) :
QWidget(parent),
ui(new Ui::RoverPlanner)
{
ui->setupUi(this);
QPushButton* btnLoadPaths = this->findChild<QPushButton*>("btn_load_paths");
QPushButton* btnSavePaths = this->findChild<QPushButton*>("btn_save_paths");
QPushButton* btnExecutePath = this->findChild<QPushButton*>("btn_execute_path" );
QPushButton* btnAddSegment = this->findChild<QPushButton*>("btn_add_to_path");
QTreeView* treeView = this->findChild<QTreeView*>("tree_paths");
connect(btnLoadPaths, &QPushButton::clicked, this, [=]() { load_paths_into_tree(treeView); });
connect(treeView->selectionModel(), &QItemSelectionModel::currentChanged, this, &RoverPlanner::treeSelectionChanged); // This does not seem to properly bind the index chan
}
void RoverPlanner::treeSelectionChanged(const QModelIndex &prevIndex, const QModelIndex &nextIndex) {
std::cout << "Test" << std::endl;
}
//other functions
When I click on the items, it does not output anything in the console
I'm confused because this seems to be the way to correctly connect the treeview selected index changed. What did I do wrong?
selectionModel gets replaced each time a new model is set for QTreeView.
void QAbstractItemView::setModel(QAbstractItemModel *model):
This function will create and set a new selection model, replacing any model that was previously set with setSelectionModel().
That means you need to reconnect the &QItemSelectionModel::currentChanged signal each time you set a new model.
Related
I have been researching for a while how to store images loaded on a QGraphicsView into rows of a QTableView using a QPushButton in a programmatic way but the information I found so far are not that many.
I have 1 QGraphicsView, a QPushButton (Send button) and a QTableView and a QLineEdit. When I upload images using the load button I show them both on the QGraphicsView and on the QLineEidt (I show the path of the image), if I click the Send button, the text of the QLineEdit should be added in the first row of the QTableView (which is happening) and the image should be stored inside the QTableView.
However, the image on the QGraphicsView is not being stored to the QTableView and nothing is being passed.
Currently this is what happens:
The expected behavior would be:
I created an ItemDelegate class that takes care of the resizing of the image on the QGraphicsView to be stored inside the QTableView
That part is shown below:
This is the mainwindow.h
#include <QGraphicsView>
#include <QGraphicsScene>
#include "imagedelegate.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
void addData();
void paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const;
private slots:
void on_sendBtn_clicked();
void on_loadBtn_clicked();
private:
Ui::MainWindow *ui;
QStandardItemModel *model;
QGraphicsScene *leftScene;
};
#endif // MAINWINDOW_H
and here is the mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "imagedelegate.h"
#include <QGraphicsPixmapItem>
#include <QBuffer>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
leftScene = new QGraphicsScene(this);
ui->graphicsView->setScene(leftScene);
ui->graphicsView->show();
model = new QStandardItemModel();
ui->tableView->setModel(model);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::addData()
{
// Path on the first column
QStandardItem *pathAItem = new QStandardItem(ui->lineEdit->text());
// Image on the second column - not working yet
//QStandardItem *image1 = new QStandardItem(/*ui->graphicsView->*/);
QPixmap image1;
QByteArray img1Array;
QBuffer buffer1(&img1Array);
buffer1.open(QIODevice::WriteOnly);
image1.save(&buffer1, "PNG");
QList<QStandardItem*> row;
row << pathAItem;
model->setColumnCount(1);
model->appendRow(row);
}
void MainWindow::on_sendBtn_clicked()
{
addData();
}
void MainWindow::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const
{
QString colName = index.model()->headerData(index.column(), Qt::Horizontal).toString();
if(colName == "image1")
{
QPixmap iconPix;
if(!iconPix.loadFromData(index.model()->data(index).toByteArray())) {
}
iconPix = iconPix.scaledToHeight(32);
painter->drawPixmap(option.rect.x(),option.rect.y(),iconPix);
} else {
// QStyledItemDelegate::paint(painter, option, index);
}
}
The entire code will compile if you copy and paste so that you can see the issue I have.
Please shed light on this matter.
I wanted to answer to this question hoping that could also be useful to others. As suggested by Jeremy Friesner, the best (and fast in comparison to a QItemDelegate) way to send images into a QTableView using a QPushButton is to modify the void MainWindow::addData() function by using a QImage and pass it to a setData(QVariant(QPixmap::fromImage), Qt::DecorationRole) so that the entire function can be written as follows:
FIRST OPTION:
void MainWindow::on_sendBtn_clicked()
{
addData();
}
void MainWindow::addData()
{
QStandardItem *pathAItem = new QStandardItem(ui->pathLineEdit_A->text());
QStandardItem *pathBItem = new QStandardItem(ui->pathLineEdit_B->text());
QImage image1(ui->graphicsViewLeft->grab().toImage());
QStandardItem *item1 = new QStandardItem();
item1->setData(QVariant(QPixmap::fromImage(image1.scaled(42,42, Qt::KeepAspectRatio,Qt::SmoothTransformation))), Qt::DecorationRole);
ui->bookMarkTableView->setModel(model);
QImage image2(ui->graphicsViewRight->grab().toImage());
QStandardItem *item2 = new QStandardItem();
item2->setData(QVariant(QPixmap::fromImage(image2.scaled(42,42, Qt::KeepAspectRatio,Qt::SmoothTransformation))), Qt::DecorationRole);
ui->bookMarkTableView->setModel(model);
QList<QStandardItem*> row;
row << pathAItem << pathBItem << item1 << item2;
model->appendRow(row);
}
SECOND OPTION
If it is necessary to use a QItemDelgate I am posting that part of the code too (it is working as I already tried it):
In the imagedelegate.h is necessary to provide a QSize as follows:
class ImageDelegate : public QStyledItemDelegate
{
public:
ImageDelegate(QObject * parent = nullptr);
void paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const;
QSize sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index) const;
After that on your imagedelegate.cpp the implementation is:
#include "imagedelegate.h"
ImageDelegate::ImageDelegate(QObject * parent) : QStyledItemDelegate(parent)
{}
QSize ImageDelegate::sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index) const
{
return QSize(32,32);
Q_UNUSED(option);
Q_UNUSED(index);
}
void ImageDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const
{
qDebug() << (index.model()->headerData(index.column(), Qt::Horizontal).toString());
QString colName = index.model()->headerData(index.column(), Qt::Horizontal).toString();
if(colName == "image1" || colName == "image2")
{
QPixmap iconPix;
if(!iconPix.loadFromData(index.model()->data(index).toByteArray())) {
}
iconPix = iconPix.scaledToHeight(32);
painter->drawPixmap(option.rect.x(),option.rect.y(),iconPix);
} else {
QStyledItemDelegate::paint(painter, option, index);
}
}
In my case I had two columns in which I needed to save the images, so you can expand it for how many columns as you like and I also set a QSize of (32,32) but this is up to the developer.
I hope this will save your programming time and this is the final result! :)
I am having a delete button in every row. I am trying to use clicked() SIGNAL of QTableview to get the current index and then do something accordingly, but this slot is not called in this case. For some reason it doesn't work, am I making some mistake in connecting clicked() SIGNAL?
void MyClass::myFunction()
{
ComboBoxItemDelegate* myDelegate;
myDelegate = new ComboBoxItemDelegate();
model = new STableModel(1, 8, this);
filterSelector->tableView->setModel(model);
filterSelector->tableView->setItemDelegate(myDelegate);
connect(filterSelector->tableView, SIGNAL(clicked(const QModelIndex&)), this, SLOT(slotHandleDeleteButton(const QModelIndex&)));
exec();
}
void MyClass::slotHandleDeleteButton(const QModelIndex& index)
{
if(index.column() == 8)
model->removeRow(index.row());
}
One of the solutions can look like this:
class Button : public QPushButton
{
Q_OBJECT
public:
Button(int row, QWidget *parent = 0) : QPushButton(parent), m_row(row)
{
connect(this, SIGNAL(clicked()), this, SLOT(onClicked()));
}
signals:
void clicked(int row);
private slots:
void onClicked()
{
emit clicked(m_row);
}
private:
int m_row;
};
Button class contains a custom signal clicked(int) with the row number as an argument. To use it in your table view you need to do:
Button *btn = new Button(rowNumber, this);
connect(btn, SIGNAL(clicked(int)), this, SLOT(onButtonClicked(int)));
I currently have a QtableView that is attached to a QstandardItemModel. I want to insert a clickable Qlabel in the last column of each row. Initially I wanted to go with a QPushButton but it turns out that it requires extra overhead so now I am planning to go with a clickable Qlabel. Any suggetsions on how I could do that ? Also I would appreciate it if someone could sugget options that are available for simulating click events in a TableView using QStandardItemModel
connect(ui.tableView,SIGNAL(clicked(const QModelIndex& ) ),
this,SLOT( itemClicked(const QModelIndex& ) ) );
slot:
void itemClicked( const QModelIndex& idx) {
int row = idx.row();
int column = idx.column();
}
if you really need just a clickable label you can create class derived from QLabel and add custom signals to handle click event:
class CustomWidget : public QLabel {
Q_OBJECT
public:
explicit CustomWidget(const QString& text, QWidget *parent = 0);
signals:
void released(void);
void clicked(void);
protected:
void mousePressEvent(QMouseEvent* e);
void mouseReleaseEvent(QMouseEvent* e);
private:
bool mousePressed;
};
CustomWidget::CustomWidget(const QString& text, QWidget* parent)
: QLabel(text, parent), mousePressed(false) {
}
void CustomWidget::mousePressEvent(QMouseEvent* e) {
mousePressed = true;
}
void CustomWidget::mouseReleaseEvent(QMouseEvent* e) {
emit released();
if(mousePressed) {
emit clicked();
mousePressed = false;
}
}
full code snippet:
http://www.qtcentre.org/archive/index.php/t-42296.html?s=e9f0fd408147a1cd1048f252967895a0
In my Qt application, I want to add a new item dynamically into a listview. Besides I also used Signal & Slot to transfer data between forms so I have created 2 following forms:
mainwindow.h
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
public slots:
void ReceivedData(QString item);
private slots:
void on_btnAdd_clicked();
void on_btnCancel_clicked();
private:
Ui::MainWindow *ui;
void SetUpListName();
};
addform.h
class AddForm : public QDialog
{
Q_OBJECT
public:
explicit AddForm(QWidget *parent = 0);
~AddForm();
signals:
void SendData(QString item);
private slots:
void on_pushButton_clicked();
void on_pushButton_2_clicked();
private:
Ui::AddForm *ui;
MainWindow *main_window;
};
mainwindow.cpp
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
SetUpListName();
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::SetUpListName()
{
// Add 5 new elements
for (int i = 0; i < 5; i++) {
QString item = "Item " + QString::number(i);
ui->lwListItem->addItem(item);
}
}
void MainWindow::on_btnAdd_clicked()
{
// Open Add Form
AddForm add;
add.setModal(true);
add.exec();
}
void MainWindow::on_btnCancel_clicked()
{
this->close();
}
void MainWindow::ReceivedData(QString item)
{
// Check to receive data
qDebug() << "Item: " << item;
// Add a new item to list items
ui->lwListItem->addItem(item);
}
addform.cpp
AddForm::AddForm(QWidget *parent) :
QDialog(parent),
ui(new Ui::AddForm)
{
ui->setupUi(this);
main_window = new MainWindow();
connect(this, SIGNAL(SendData(QString)), main_window, SLOT(ReceivedData(QString)));
}
AddForm::~AddForm()
{
delete ui;
}
void AddForm::on_pushButton_clicked()
{
// Send data via Signal & Slot
emit SendData(ui->txtName->text());
}
void AddForm::on_pushButton_2_clicked()
{
this->close();
}
When I run the application, I got the data from Add form but the list view doesn’t add this item.
Does someone have any solutions?
Thanks!
P/S: You can download my source code at here
You are connecting the signal to the wrong object's slot. In the constructor of AddForm, you are creating a new MainWindow and connecting the signal to it's slot which means that the signal does not reach your real MainWindow, and the ReceivedData slot is adding the item to the wrong QListWidget. What you should do is this:
void MainWindow::on_btnAdd_clicked()
{
// Open Add Form
AddForm add;
connect(&add, SIGNAL(SendData(QString)), this, SLOT(ReceivedData(QString)));
add.setModal(true);
add.exec();
}
and remove the creation of a new MainWindow and corresponding connect call from the constructor of AddForm.
I was wondering if anybody could tell me whats actually going on here. I have included the header and implementation file for a simple config dialog. The problem is in the updateAutoSaveGroupBox slot I cannot access or change any properties of my widgets on the page. I.E. I want to make some widgets disabled if a check box has not been checked but when I try to set them I get a read access violation. Any help with this matter would be greatly appreciated. The problems exsist in these two lines(commented out so it will run for now without throwing an exception).
//autoSaveLabel->setDisabled(autoSaveIsEnabled);
//autoSaveSpinBox->setDisabled(getAutoSaveIsEnabled());
configWidget.h
class EnigmaConfigGeneralEnvironmentWidget : public QWidget
{
Q_OBJECT
public:
explicit EnigmaConfigGeneralEnvironmentWidget(QWidget *parent = 0);
~EnigmaConfigGeneralEnvironmentWidget();
signals:
void setAutoSaveIsEnabledSignal(bool autoSaveIsEnabled);
public slots:
void setAutoSaveIsEnabled(bool autoSaveIsEnabled){_AutoSaveIsenabled = autoSaveIsEnabled;}
bool getAutoSaveIsEnabled(){return _AutoSaveIsenabled;}
void updateAutoSaveGroupBox(bool autoSaveIsEnabled);
private:
void makeConnections();
void readSettings();
void writeSettings();
void createMainWidget();
QGroupBox *uiGroupBox;
QStringList localList;
QLabel *localLabel;
QComboBox *localeComboBox;
QHBoxLayout *localSelectionHLayout;
QGroupBox *systemGroupBox;
QCheckBox *autoSaveCheckBox;
QLabel *autoSaveLabel;
QSpinBox *autoSaveSpinBox;
QHBoxLayout *autoSaveHLayout;
bool _AutoSaveIsenabled;
};
ConfigWidget.cpp
#include "enigmaconfiggeneralenvironmentwidget.h"
#include <QtWidgets>
EnigmaConfigGeneralEnvironmentWidget::EnigmaConfigGeneralEnvironmentWidget(QWidget *parent) :
QWidget(parent)
{
makeConnections();
readSettings();
createMainWidget();
}
EnigmaConfigGeneralEnvironmentWidget::~EnigmaConfigGeneralEnvironmentWidget()
{
writeSettings();
}
void EnigmaConfigGeneralEnvironmentWidget::makeConnections()
{connect(this,SIGNAL(setAutoSaveIsEnabledSignal(bool)),this,SLOT(setAutoSaveIsEnabled(bool)) );
connect(this,SIGNAL(setAutoSaveIsEnabledSignal(bool)),this,SLOT(updateAutoSaveGroupBox(bool)) );
}
void EnigmaConfigGeneralEnvironmentWidget::readSettings()
{
QSettings settings;
settings.beginGroup(tr("UI.Config.Environment.General"));
bool autoSaveIsEnabled = settings.value("autoSaveIsEnabled",bool(true)).toBool();
setAutoSaveIsEnabledSignal(autoSaveIsEnabled);
settings.endGroup();
}
void EnigmaConfigGeneralEnvironmentWidget::writeSettings()
{
QSettings settings;
settings.beginGroup(tr("UI.Config.Environment.General"));
settings.setValue("autoSaveIsEnabled",getAutoSaveIsEnabled());
settings.endGroup();
}
void EnigmaConfigGeneralEnvironmentWidget::createMainWidget()
{
localList.append(tr("Danish - ???"));
localList.append(tr("English - Australia"));
localList.append(tr("English - Canada"));
localList.append(tr("English - USA"));
localList.append(tr("English - UK"));
localList.append(tr("Finnish - Finland"));
localList.append(tr("French - Canada"));
localList.append(tr("French - France"));
localList.append(tr("Norwegian - ???"));
localList.append(tr("Swedish - ???"));
uiGroupBox = new QGroupBox();
uiGroupBox->setTitle(tr("UI Settings"));
localLabel= new QLabel();
localLabel->setText(tr("Select a language: "));
localeComboBox = new QComboBox();
localeComboBox->addItems(localList);
localSelectionHLayout = new QHBoxLayout(uiGroupBox);
localSelectionHLayout->addWidget(localLabel);
localSelectionHLayout->addWidget(localeComboBox);
systemGroupBox = new QGroupBox();
systemGroupBox->setTitle(tr("System Settigns"));
autoSaveCheckBox = new QCheckBox();
autoSaveCheckBox->setText(tr("Auto-Save Enabled: "));
autoSaveCheckBox->setChecked(getAutoSaveIsEnabled());
connect(autoSaveCheckBox,SIGNAL(clicked(bool)),this,SIGNAL(setAutoSaveIsEnabledSignal(bool)));
autoSaveLabel = new QLabel(this);
autoSaveLabel->setText(tr("Auto-Save Interval is Every: "));
autoSaveSpinBox = new QSpinBox();
autoSaveSpinBox->setSuffix(tr("Mins."));
autoSaveSpinBox->setAccelerated(true);
autoSaveHLayout = new QHBoxLayout(systemGroupBox);
autoSaveHLayout->addWidget(autoSaveCheckBox);
autoSaveHLayout->addWidget(autoSaveLabel);
autoSaveHLayout->addWidget(autoSaveSpinBox);
QVBoxLayout *vLayout = new QVBoxLayout(this);
vLayout->addWidget(uiGroupBox);
vLayout->addWidget(systemGroupBox);
}
void EnigmaConfigGeneralEnvironmentWidget::updateAutoSaveGroupBox(bool autoSaveIsEnabled)
{
qDebug() << "debug " << autoSaveIsEnabled;
//autoSaveLabel->setDisabled(autoSaveIsEnabled);
//autoSaveSpinBox->setDisabled(getAutoSaveIsEnabled());
}
Spotted it:
void EnigmaConfigGeneralEnvironmentWidget::readSettings()
{
QSettings settings;
settings.beginGroup(tr("UI.Config.Environment.General"));
bool autoSaveIsEnabled = settings.value("autoSaveIsEnabled",bool(true)).toBool();
setAutoSaveIsEnabledSignal(autoSaveIsEnabled);
settings.endGroup();
}
You are calling void setAutoSaveIsEnabledSignal(bool) here (the signal), not void setAutoSaveIsEnabled(bool) (the actual setter). Hence the member variable is still uninitialized.
Reminder for yourself: Don't name signals as if they were setters, use e.g. void autoSaveIsEnabledChanged(bool)