Accessing a delegate's input value from the model - qt

I'm using a custom QTableView with a custom QAbstractTableModel and a QItemDelegate. I'd need to access the contents of the delegate's editor while the user is editing it, and after several attempts, I couldn't find anything satisfying.
Indeed, I've tried several things.
First: trying to access the delegate's current input (created through createEditor) through a property defined in QItemDelegate but... it seems that none exists. That's why I tried to add a QWidget* editor property and setting it in the createEditor.
Unfortunately, QItemDelegate's createEditor is supposed to be const, which makes me unable to set my property there (and since I don't control what calls createEditor, I can't do it before or after).
I don't really know what to do here. Actually, I also needed to know when the user started (or stopped) editing the cell content, which I eventually achieved by creating two const signals (editingStarted and editingStopped). I could probably create a const editorOpened(QWidget*) signal but it just feels bad and ugly...
I can't believe nothing "official" exists to achieve what I'm trying to do, hence this question. If I have everything wrong from the beginning, I'd be glad to know. If you have any other ideas, please suggest.
EDIT: Here is a minimal working example
MainWindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
};
#endif // MAINWINDOW_H
MainWindow.cpp
#include "mainwindow.h"
#include <QTableView>
#include "mytableview.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent)
{
auto tableView = new MyTableView(this);
setCentralWidget(tableView);
}
MainWindow::~MainWindow()
{
}
MyItemDelegate.h
#ifndef MYITEMDELEGATE_H
#define MYITEMDELEGATE_H
#include <QItemDelegate>
#include <QLineEdit>
#include <QStandardItemModel>
class MyItemDelegate : public QItemDelegate
{
Q_OBJECT
public:
MyItemDelegate(QObject* parent);
virtual QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const;
virtual void onCloseEditor();
virtual ~MyItemDelegate() = default;
signals:
// Const signals trick
void editingStarted() const;
void editingFinished() const;
void editorOpened(const QWidget*) const;
};
#endif // MYITEMDELEGATE_H
MyItemDelegate.cpp
#include "myitemdelegate.h"
MyItemDelegate::MyItemDelegate(QObject* parent) : QItemDelegate(parent)
{
connect(this, &QItemDelegate::closeEditor, this, &MyItemDelegate::onCloseEditor);
}
QWidget* MyItemDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
auto lineEdit = new QLineEdit(parent);
emit editingStarted();
emit editorOpened(lineEdit);
return lineEdit;
}
void MyItemDelegate::onCloseEditor()
{
emit editingFinished();
}
MyTableView.h
#ifndef MYTABLEVIEW_H
#define MYTABLEVIEW_H
#include <QTableView>
#include <QDebug>
#include "myitemdelegate.h"
class MyTableView : public QTableView
{
Q_OBJECT
public:
explicit MyTableView(QWidget *parent = nullptr);
signals:
public slots:
};
#endif // MYTABLEVIEW_H
MyTableView.cpp
#include "mytableview.h"
MyTableView::MyTableView(QWidget *parent) : QTableView(parent)
{
MyItemDelegate* delegate = new MyItemDelegate(this);
QStandardItemModel* model = new QStandardItemModel(this);
setItemDelegate(delegate);
setModel(model);
QList<QList<QStandardItem*>> items;
for(int i = 0; i < 10; i++)
{
items << QList<QStandardItem*>();
for (int j = 'A'; j < 'E'; j++)
items[i] << new QStandardItem(QString("%1,%2").arg(i).arg(static_cast<char>(j)));
}
for (const auto& row : items)
model->appendRow(row);
connect(delegate, &MyItemDelegate::editingStarted, []() {
qDebug() << "Editing started";
});
connect(delegate, &MyItemDelegate::editingFinished, []() {
qDebug() << "Editing finished";
});
connect(delegate, &MyItemDelegate::editorOpened, [](const QWidget* editor) {
auto lineEdit = qobject_cast<const QLineEdit*>(editor);
connect(lineEdit, &QLineEdit::textChanged, [](const QString& text) {
qDebug() << text;
});
});
}
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}

The following solution maybe fits your needs. I just defined a new signal inside the delegate and connected to it inside the class owning the delegate.
MyItemDelegate.h
#ifndef MYITEMDELEGATE_H
#define MYITEMDELEGATE_H
#include <QStyledItemDelegate>
class MyItemDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
MyItemDelegate(QObject* parent);
QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const;
virtual ~MyItemDelegate() = default;
signals:
void valueChanged(const QString&);
};
#endif // MYITEMDELEGATE_H
MyItemDelegate.cpp
#include "myitemdelegate.h"
#include <QLineEdit>
MyItemDelegate::MyItemDelegate(QObject* parent) : QStyledItemDelegate(parent)
{
}
QWidget* MyItemDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
auto lineEdit = new QLineEdit(parent);
connect(lineEdit, &QLineEdit::textChanged, this, &MyItemDelegate::valueChanged);
return lineEdit;
}
MyTableView.cpp
#include "mytableview.h"
#include <QStandardItemModel>
MyTableView::MyTableView(QWidget *parent) : QTableView(parent)
{
MyItemDelegate* delegate = new MyItemDelegate(this);
QStandardItemModel* model = new QStandardItemModel(this);
setItemDelegate(delegate);
setModel(model);
QList<QList<QStandardItem*>> items;
for(int i = 0; i < 10; i++)
{
items << QList<QStandardItem*>();
for (int j = 'A'; j < 'E'; j++)
items[i] << new QStandardItem(QString("%1,%2").arg(i).arg(static_cast<char>(j)));
}
for (const auto& row : items)
model->appendRow(row);
connect(delegate, &MyItemDelegate::valueChanged, [](auto v) { qDebug() << v; });
}

Related

Can a QListView detect a specific QString and therefore automatically trigger a slot?

I have a specific string on a QLineEdit, this string is passed to a QListView via QPushButton. Those strings are choices of a QComboBox and they are very specific:
1) "[ INFO] Minimum Distance: 5",
2) "[ INFO] Minimum Distance: 10" and
3) "[ INFO] Minimum Distance: 15"
Here a perfectly working minimal verifiable example if you need to test it.
Currently I can successfully change the color of the QGraphicsView but by clicking or double-clicking on the QString entered in the QListView.
The problem: How can I detect the specific QString content inside a QListView in order to change the background color of a QGraphicsView?
What I mean I don't want to click or double-click on the entry of the QListView but I would like the QListView to see that there is a string "[ INFO] Minimum Distance: 5" and therefore change the color of the QGraphicsView automatically without me clicking or double-clicking on the QListView entry.
After I "Go to slot" my choices are the following below:
Below the MVE working code, you can copy /paste on your machine and it will work:
mainwindow.h
#include <QMainWindow>
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QGraphicsTextItem>
#include <QStringListModel>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
void changeColorDetection();
void updateListView();
void updateListView(const QString & message);
private slots:
void on_pushButton_clicked();
void on_listView_entered(const QModelIndex &index);
void on_listView_activated(const QModelIndex &index);
private:
Ui::MainWindow *ui;
QGraphicsView *mView;
QGraphicsScene *mScene;
QGraphicsTextItem *mText;
StringList *newString;
QStringListModel *model;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
mView = new QGraphicsView();
mScene = new QGraphicsScene();
ui->graphicsView->setScene(mScene);
QFont font;
font.setPixelSize(10);
font.setBold(false);
font.setFamily("Calibri");
mText = new QGraphicsTextItem;
mText->setPos(150,70);
mScene->addText(tr("Boat outside alarm area"))->setDefaultTextColor(Qt::black);
model = new QStringListModel();
ui->listView->setModel(model);
ui->listView->setEditTriggers(QAbstractItemView::NoEditTriggers);
emptyIndex();
connect(ui->listView, SIGNAL(loggingUpdated()), this, SLOT(updateListView(const QString &)));
connect(ui->graphicsView, SIGNAL(clicked(const QModelIndex &)), this, SLOT(on_listView_activated(const QModelIndex &index)));
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::updateListView(const QString & message)
{
if(model->insertRow(model->rowCount())) {
QModelIndex index = model->index(model->rowCount() - 1, 0);
model->setData(index, message);
ui->listView->scrollTo(index);
}
}
void MainWindow::on_pushButton_clicked()
{
QString str = ui->lineEdit->text();
model->insertRow(model->rowCount());
QModelIndex index = model->index(model->rowCount()-1);
model->setData(index, str);
ui->listView->scrollToBottom();
}
void MainWindow::on_comboBox_currentIndexChanged(const QString &arg1)
{
QString list = ui->comboBox->currentText();
ui->lineEdit->setText(list);
Q_UNUSED(arg1)
}
void MainWindow::on_listView_activated(const QModelIndex &index)
{
QStringList allStrings = model->stringList();
QString last = allStrings.last();
if(last.startsWith("[ INFO] Minimum Distance: 5"))
{
ui->graphicsView->setBackgroundBrush(QColor(Qt::red));
}
else if(last.startsWith("[ INFO] Minimum Distance: 10"))
{
ui->graphicsView->setBackgroundBrush(QColor(Qt::yellow));
}
else if(last.startsWith("[ INFO] Minimum Distance: 15"))
{
ui->graphicsView->setBackgroundBrush(QColor(Qt::green));
}
Q_UNUSED(index)
}
EDIT 2
mainwindow.h
#include <QMainWindow>
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QGraphicsTextItem>
#include <QStringListModel>
#include "listview.h"
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
public slots:
void setGraphicViewColor(QColor c);
private:
Ui::MainWindow *ui;
QGraphicsView *mView;
QGraphicsScene *mScene;
QGraphicsTextItem *mText;
QStringListModel *model;
ListView *myListView;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
mView = new QGraphicsView();
mScene = new QGraphicsScene();
ui->graphicsView->setScene(mScene);
QFont font;
font.setPixelSize(10);
font.setBold(false);
font.setFamily("Calibri");
mText = new QGraphicsTextItem;
mText->setPos(150,70);
mScene->addText(tr("Boat outside alarm area"))->setDefaultTextColor(Qt::black);
model = new QStringListModel();
ui->listView->setModel(model);
ui->listView->setEditTriggers(QAbstractItemView::NoEditTriggers);
connect(ui->listView, SIGNAL(changeColor(QColor)), this, SLOT(setGraphicViewColor(QColor)));
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::setGraphicViewColor(QColor c)
{
qDebug() << "Update your graphicsView backgroundBrush" << c;
ui->graphicsView->setBackgroundBrush(Qt::green);
}
listview.h
#ifndef LISTVIEW_H
#define LISTVIEW_H
#include <QListView>
#include <QStringListModel>
class ListView : public QListView
{
Q_OBJECT
public:
ListView(QWidget *parent = nullptr);
signals:
void changeColor(QColor c);
protected:
void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles = QVector<int>()) override;
};
#endif // LISTVIEW_H
listview.cpp
#include "listview.h"
ListView::ListView(QWidget *parent)
: QListView(parent)
{}
void ListView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles)
{
QListView::dataChanged(topLeft, bottomRight, roles);
/**
* Assuming that you have just one item changed
* So topLeft == bottomRight
*/
if (topLeft.row() == model()->rowCount()-1){
QString last = topLeft.data().toString();
if(last.startsWith("[ INFO] Minimum Distance: 5")) {
emit changeColor(Qt::red);
} else if(last.startsWith("[ INFO] Minimum Distance: 10")) {
emit changeColor(Qt::yellow);
} else if(last.startsWith("[ INFO] Minimum Distance: 15")) {
emit changeColor(Qt::green);
}
}
}
Below the error and a screenshot of the ui that does not detect the change event:
The output of the .ui:
What I have done so far:
I have been doinf a lot of research about this problem and came across this source which was useful but could not solve the problem, but in addition it seems to use a QModelIndex and I am not sure this is exactly what I need for this small project.
Also I read this source which was useful to establish and capture the specific and unique string but in terms of changing colors I could not solve that.
Thank you very much for pointing in the right direction for solving this issue.
If I well understand, you want to change the backgroundBrush of your graphicsView if the last item of your QStringListModel starts with your specific strings.
To detect this, you can subclass QListView:
listview.h:
#ifndef LISTVIEW_H
#define LISTVIEW_H
#include <QListView>
#include <QStringListModel>
class ListView : public QListView
{
Q_OBJECT
public:
ListView(QWidget *parent = nullptr);
signals:
void changeColor(QColor c);
protected:
void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles = QVector<int>()) override;
};
#endif // LISTVIEW_H
listview.cpp:
#include "listview.h"
ListView::ListView(QWidget *parent)
: QListView(parent)
{
}
void ListView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles)
{
QListView::dataChanged(topLeft, bottomRight, roles);
/**
* Assuming that you have just one item changed
* So topLeft == bottomRight
*/
if (topLeft.row() == model()->rowCount()-1){
QString last = topLeft.data().toString();
if(last.startsWith("[ INFO] Minimum Distance: 5")) {
emit changeColor(Qt::red);
} else if(last.startsWith("[ INFO] Minimum Distance: 10")) {
emit changeColor(Qt::yellow);
} else if(last.startsWith("[ INFO] Minimum Distance: 15")) {
emit changeColor(Qt::green);
}
}
}
Now you have all what you need, but you need to add a slot to connect the signal of your custom ListView with your QGraphicsView::brush
// Add this in your mainwindows.h
public slots:
void setGraphicViewColor(QColor c);
// This in the ctor of your MainWindow:
connect(ui->listView, SIGNAL(changeColor(QColor)), this, SLOT(setGraphicViewColor(QColor)));
// And the implementation of your custom slot in mainwindows.cpp
void MainWindow::setGraphicViewColor(QColor c)
{
qDebug() << "Update your graphicsView backgroundBrush" << c;
//ui->graphicsView->setBackgroundBrush(c);
}

how to get drop events of object which is placed in graphic scene?

i have graphic view(QGraphics view) setted scene(QGraphic scene) i am dropping objects on scene its working fine, i have to assign parameter for dropped object by dragging parameter from parameter list .i have implemented drag drop events of object.but when i am dragging parameter from param list non- acceptance symbol on object.how to assign param to object by dropping ? Any other suggestions and examples are welcome where i can get ideas to implementation.
image of gui
speedometer.cpp
#include <QMimeData>
SpeedoMeter::SpeedoMeter( QWidget *parent ):
QwtDial( parent ),
d_label( "km/h" )
{
setAcceptDrops(true);
}
void SpeedoMeter::dragEnterEvent(QDragEnterEvent *event)
{
if (event->mimeData()->hasFormat(paramlistMimeType()))
{
qDebug()<<"dragenter event in speedo" ;
event->accept();
}
}
void SpeedoMeter::dragMoveEvent(QDragMoveEvent *event)
{
if (event->mimeData()->hasFormat(paramlistMimeType()))
{
qDebug()<<"dragmove event in speedo" ;
event->acceptProposedAction();
}
}
void SpeedoMeter::dropEvent(QDropEvent *event)
{
if (event->mimeData()->hasFormat(paramlistMimeType()))
{
qDebug()<<"dragmove event in speedo" ;
event->accept();
}
}
The following example shows how to implement the logic to accept the drag-and-drop:
speedometer.h
#ifndef SPEEDOMETER_H
#define SPEEDOMETER_H
#include <qwt_dial.h>
class SpeedoMeter : public QwtDial
{
public:
SpeedoMeter(QWidget *parent=nullptr);
protected:
void dragEnterEvent(QDragEnterEvent *event);
void dropEvent(QDropEvent *event);
void dragMoveEvent(QDragMoveEvent *event);
void paintEvent(QPaintEvent *event);
private:
QString d_label;
};
#endif // SPEEDOMETER_H
speedometer.cpp
#include "speedometer.h"
#include <qwt_dial_needle.h>
#include <QDragEnterEvent>
#include <QMimeData>
#include <QPainter>
SpeedoMeter::SpeedoMeter(QWidget *parent):
QwtDial(parent),
d_label( "km/h" )
{
setAcceptDrops(true);
QwtDialSimpleNeedle *nd = new QwtDialSimpleNeedle(QwtDialSimpleNeedle::Arrow, Qt::white, Qt::red);
setNeedle(nd);
setValue(80);
}
void SpeedoMeter::dragEnterEvent(QDragEnterEvent *event)
{
if(event->mimeData()->hasFormat("application/x-qabstractitemmodeldatalist"))
event->acceptProposedAction();
}
void SpeedoMeter::dropEvent(QDropEvent *event)
{
const QMimeData *mimedata = event->mimeData();
if(mimedata->hasFormat("application/x-qabstractitemmodeldatalist")){
QString text;
// https://stackoverflow.com/questions/1723989/how-to-decode-application-x-qabstractitemmodeldatalist-in-qt-for-drag-and-drop
QByteArray encoded = mimedata->data("application/x-qabstractitemmodeldatalist");
QDataStream stream(&encoded, QIODevice::ReadOnly);
while (!stream.atEnd()) {
int row, col;
QMap<int, QVariant> roleDataMap;
stream >> row >> col >> roleDataMap;
if(roleDataMap.contains(Qt::DisplayRole)){
text = roleDataMap[Qt::DisplayRole].toString();
break;
}
}
// your text
d_label = text;
update();
}
}
void SpeedoMeter::dragMoveEvent(QDragMoveEvent *event)
{
if(event->mimeData()->hasFormat("application/x-qabstractitemmodeldatalist"))
event->accept();
}
void SpeedoMeter::paintEvent(QPaintEvent *event)
{
// https://stackoverflow.com/questions/43904204/qwt-dial-show-unit
QwtDial::paintEvent(event);
QPainter painter(this);
painter.setPen(Qt::black);
QFont font;
font.setPointSize(11);
painter.setFont(font);
QString text = QString("%1 %2").arg(value()).arg(d_label);
QPoint c = rect().center();
QSize Size = painter.fontMetrics().size(Qt::TextSingleLine, text);
painter.drawText(QPointF(c.x() -Size.width()/2, c.y() + 2.5*Size.height()), text);
}
main.cpp
#include "speedometer.h"
#include <QApplication>
#include <QGraphicsView>
#include <QHBoxLayout>
#include <QListWidget>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QWidget w;
QHBoxLayout *layout = new QHBoxLayout(&w);
QListWidget listWidget;
listWidget.setDragDropMode(QAbstractItemView::DragOnly);
listWidget.addItems({"km/h", "ft/s", "m/s", "miles/h"});
QGraphicsView view;
QGraphicsScene scene;
view.setScene(&scene);
SpeedoMeter speed;
scene.addWidget(&speed);
layout->addWidget(&listWidget);
layout->addWidget(&view);
w.show();
return a.exec();
}
In the following link you can find the complete example.

How to make slot for multiple QPushButtons?

From Qvector is set the number of buttons and their names (text). After cliking on the button I need to reseave text on it and display in lineEdit.
The header file:
#ifndef DIALOG_H
#define DIALOG_H
#include <QDialog>
#include <QWidget>
#include <QVector>
#include <QLineEdit>
#include <QPushButton>
#include <QHBoxLayout>
#include <QVBoxLayout>
class Dialog : public QDialog
{
Q_OBJECT
public:
Dialog(QWidget *parent = 0);
~Dialog();
private slots:
void Buttons(QVector<QString>&);
private:
QVector<QPushButton*>button;
QString mline;
QLineEdit *line;
QVBoxLayout *layout;
QAction *Clicked;
};
#endif // DIALOG_H
The source file:
#include "dialog.h"
Dialog::Dialog(QWidget *parent): QDialog(parent)
{
this->setFixedSize(this->minimumSize());
line = new QLineEdit(this);
layout = new QVBoxLayout(this);
layout->addWidget(line);
QVector<QString>v;
v.append("a");
v.append("b");
v.append("c");
v.append("5");
v.append("45");
Buttons(v);
for(int i=0;i<button.size();i++)
layout->addWidget(button[i]);
setLayout(layout);
}
Dialog::~Dialog(){}
void Dialog::Buttons(QVector<QString>&vec)
{
if(!button.isEmpty())
button.clear();
for(int i=0; i<vec.size();i++)
{
button.append(new QPushButton(this));
button[i]->setText(vec[i]);
button[i]->show();
}
}
For it I'm not using user interface (ui), and couldn't make slots for all the buttons. How can it be done?
You can use QButtonGroup to id the buttons and consolidate the signals from all the buttons to a single slot
Example
QButtonGroup myButtongroup;
connect(&myButtonGroup,SIGNAL(buttonClicked(QAbstractButton*),this, SLOT(myButtonClicked (QAbstractButton*)));
for(int i=0; i<vec.size();i++)
{
QString buttonName = vec[i];
myButtonGroup.addButton(new QPushButton(buttonName,this),i);
//Get the button using myButtonGroup.button(i) to add to your layout
// You can add the buttons to the layout right here to elimate one more loop
layout.addWidget(myButtonGroup.button(i));
}
//Slot for button clicked
void Dialog::myButtonClicked(QAbstractButton *myButton)
{
line->setText(myButton->text());// Adding the button name to line edit
}
For more QButtonGroup signals refer the documentation
You can use signal/slot in your case. The signal/slot is related to QObject. It does not matter whether you use QT Designer or not. In your code,
for(int i=0; i<vec.size();i++)
{
button.append(new QPushButton(this));
connect(button[i], SIGNAL( clicked() ), this, SLOT(OnButtonClicked()));
button[i]->setText(vec[i]);
button[i]->show();
}
Maintain a QList of QPushButton references that you add and use "signals and slots" to register the clicked() signal of each button to a single slot. Inside the function, iterate the QList of QPushButton by comparing with QObject::sender() and identify the source.
#include <QApplication>
#include <QVBoxLayout>
#include <QPushButton>
#include <QLabel>
class TestWidget: public QWidget
{
Q_OBJECT
private:
QLabel *label;
QVBoxLayout *mainLayout;
QList<QPushButton*> mButtonList;
public:
TestWidget(QWidget *parent=nullptr) : QWidget(parent)
{
mainLayout = new QVBoxLayout(this);
label = new QLabel;
mainLayout->addWidget(label);
this->setLayout(mainLayout);
}
void addButton(QString buttonName)
{
QPushButton *button = new QPushButton(buttonName);
QObject::connect(button, SIGNAL(clicked(bool)), this, SLOT(buttonClicked(bool)));
mButtonList.append(button);
mainLayout->addWidget(button);
}
public slots:
void buttonClicked(bool event)
{
Q_UNUSED(event)
static_cast<QPushButton*>(QObject::sender())->setFocus();
for (int i = 0; i < mButtonList.size(); ++i)
{
if (mButtonList.at(i) == sender())
{
label->setText(QString(mButtonList.at(i)->text()));
break;
}
}
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
TestWidget wid;
wid.addButton("First Button");
wid.addButton("Second Button");
wid.addButton("Third Button");
wid.addButton("Fourth Button");
wid.show();
return a.exec();
}
#include "main.moc"
The above sample code along with *.pro file is available in github

QStandardItem converts unsigned int to int after editing

I create a QTableview with a QStandardItemModel, after editing the QStandardItem
the type changed from unsigned int to int.
This behavior just happen to unsigned int and just while the user is editing it, other datatypes stay.
window.cpp
#include "window.h"
#include "ui_window.h"
#include <QTableView>
#include <QStandardItem>
#include <QDebug>
Window::Window(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::Window)
{
ui->setupUi(this);
QTableView *tblview = new QTableView(this);
model = new QStandardItemModel(0,0);
tblview->setModel(model);
QStandardItem *data=new QStandardItem;
data->setEditable(true);
data->setData(QVariant((uint)1), Qt::DisplayRole);
model->setItem(0, 0, data);
tblview->show();
QModelIndex index = model->index( 0, 0, QModelIndex() );
tblview->setGeometry(0,0,200,200);
//result QVariant(uint, 1)
qDebug() << model->data(index);
connect(model, SIGNAL(itemChanged(QStandardItem*)), this, SLOT(dataChanged(QStandardItem*)));
}
Window::~Window()
{
delete ui;
}
void Window::dataChanged(QStandardItem* stditem)
{
//result
//QVariant(int, 3)
//expected result
//QVariant(uint, 3)
qDebug() << model->data(stditem->index());
}
window.h
#ifndef WINDOW_H
#define WINDOW_H
#include <QMainWindow>
#include <QStandardItem>
namespace Ui {
class Window;
}
class Window : public QMainWindow
{
Q_OBJECT
public:
explicit Window(QWidget *parent = 0);
~Window();
private:
Ui::Window *ui;
QStandardItemModel* model;
private slots:
void dataChanged(QStandardItem*);
};
#endif // WINDOW_H
The second qDebug() does not print nothing because you do not define the role. This will work:
qDebug() << stditem->data(Qt::DisplayRole);
Now concerning the conversion from an uint QVariant to an int after the edit. This is natural and can be explained as follows:
First you have a QVariant that is uint
QVariant v = QVariant((uint) 5)); // It is uint now...
After the edit, the model changes its value with the int value that is entered
v = QVariant(10); // Now v is not uint anymore but int
In order to avoid it you should subclass the QStandardItemModel, and reimplement the setData function. There you should explicitly cast the new value to uint.

QT hangs my toolbar and its buttons

I've created 2 classes, each:
has QWidget as a parent
has Q_OBJECT macros
inits some actions, creates menubar and toolbar, and connects actions to them
Then I created the main program file, created QMainWindow, and init my two classes by qmainwnd pointer.
And what I've got - menu works, the second toolbar works, but the first toolbar (created by class 1) doesn't respond to mouse clicks.
What happed with it? Why I can not even move this first toolbar or click its buttons?
Here a sample:
main.cpp
#include <QApplication>
#include <QMainWindow>
#include "CModDocument.h"
#include "CModEditor.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QMainWindow wnd;
// init CA
CModDocument a(&wnd);
// init CB
CModEditor b(&wnd);
wnd.show();
return app.exec();
}
CModDocument.h
#ifndef CMODDOCUMENT_H
#define CMODDOCUMENT_H
#include <QWidget>
QT_BEGIN_NAMESPACE
class QAction;
class QToolBar;
class QMainWindow;
QT_END_NAMESPACE
class CModDocument: public QWidget
{
Q_OBJECT
public:
CModDocument(QWidget *parent = 0);
QMainWindow *getMainWnd();
public slots:
void newFile();
void open();
bool save();
bool saveAs();
private:
void createActions();
void createToolBars();
void interCom();
QMainWindow *mainWnd;
QToolBar *fileToolBar;
QAction *newAct;
QAction *openAct;
QAction *saveAct;
QAction *saveAsAct;
};
#endif // CMODDOCUMENT_H
CModDocument.cpp
#include <QtGui>
#include <QDebug>
#include "CModDocument.h"
CModDocument::CModDocument( QWidget *parent )
: QWidget( parent )
, mainWnd( (QMainWindow*)parent )
{
createActions();
createToolBars();
interCom();
mainWnd->statusBar()->showMessage(tr("Ready"));
}
void CModDocument::newFile()
{
qDebug() << "newFile";
}
void CModDocument::open()
{
qDebug() << "open";
}
bool CModDocument::save()
{
qDebug() << "save";
bool retVal;
return retVal;
}
bool CModDocument::saveAs()
{
qDebug() << "saveAs";
bool retVal;
return retVal;
}
void CModDocument::createActions()
{
newAct = new QAction(tr("&New"), this);
newAct->setShortcuts(QKeySequence::New);
newAct->setStatusTip(tr("Create a new file"));
openAct = new QAction(tr("&Open..."), this);
openAct->setShortcuts(QKeySequence::Open);
openAct->setStatusTip(tr("Open an existing file"));
saveAct = new QAction(tr("&Save"), this);
saveAct->setShortcuts(QKeySequence::Save);
saveAct->setStatusTip(tr("Save the document to disk"));
saveAsAct = new QAction(tr("Save &As..."), this);
saveAsAct->setShortcuts(QKeySequence::SaveAs);
saveAsAct->setStatusTip(tr("Save the document under a new name"));
}
void CModDocument::createToolBars()
{
fileToolBar = mainWnd->addToolBar(tr("File"));
fileToolBar->addAction(newAct);
fileToolBar->addAction(openAct);
fileToolBar->addAction(saveAct);
}
void CModDocument::interCom()
{
connect(newAct, SIGNAL(triggered()), this, SLOT(newFile()));
connect(openAct, SIGNAL(triggered()), this, SLOT(open()));
connect(saveAct, SIGNAL(triggered()), this, SLOT(save()));
connect(saveAsAct, SIGNAL(triggered()), this, SLOT(saveAs()));
}
CModEditor.h
#ifndef CMODEDITOR_H
#define CMODEDITOR_H
#include <QWidget>
// #include "CModEdiWidget.h"
QT_BEGIN_NAMESPACE
class QMainWindow;
class QAction;
class QMenu;
class QMenuBar;
class QToolBar;
QT_END_NAMESPACE
class CModEditor : public QWidget
{
Q_OBJECT
public:
CModEditor(QWidget *parent = 0);
public slots:
void cut();
private:
void createActions();
void createToolBars();
void interCom();
QAction *cutAct;
QToolBar *editToolBar;
// parent
QMainWindow *mainWnd;
};
#endif
CModEditor.cpp
#include <QtGui>
#include <QDebug>
#include "CModEditor.h"
CModEditor::CModEditor(QWidget *parent)
: QWidget(parent)
, mainWnd( (QMainWindow*)parent )
{
createActions();
createToolBars();
interCom();
}
void CModEditor::cut()
{
qDebug() << "cut";
}
void CModEditor::createActions()
{
cutAct = new QAction(tr("Cu&t"), this);
cutAct->setShortcuts(QKeySequence::Cut);
cutAct->setStatusTip(tr("Cut the current selection's contents to the clipboard"));
}
void CModEditor::createToolBars()
{
editToolBar = mainWnd->addToolBar(tr("Edit"));
editToolBar->addAction(cutAct);
editToolBar->setIconSize(QSize(16, 16));
}
void CModEditor::interCom()
{
connect(cutAct, SIGNAL(triggered()), this, SLOT(cut()));
}
build.pro
CONFIG -= app_bundle
HEADERS = CModDocument.h CModEditor.h
SOURCES = CModDocument.cpp CModEditor.cpp main.cpp
The problem seems to be that you are trying to configure the QMainWindow as the parent to two QWidgets. I modified your main() function as follows and it worked:
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QMainWindow wnd;
QWidget w;
// init CA
CModDocument a(&wnd, &w);
// init CB
CModEditor b(&wnd, &w);
wnd.show();
return app.exec();
}
Notice the new QWidget w that parents a and b. I'm not even sure my approach is adequate (it probably isn't). I think it is better to add a QLayout to w and add a and b to that QLayout. Then you could set w as wnd's central widget.

Resources