I have defined macros for members that I want to access from a structure. I don't want to type cast to any another data type.
Example:
#define LABLE ui->lable->setText("NumbVal")
#define LABLE1 ui->lEditCaliCLFltBst->setText("UNDER PROCESS")
if (EditMode[LOC_04]!=0) { LABLE; } else { LABLE1; }
I want to access this LABLE variable from a structure. But what if I have a larger number of EditMode array entried - I can't make my program lenthy I just want to access through them through a structure.
What you are showing should be, at the very least, functions.
For example:
class Foo : public QWidget {
QScopedPointer<Ui::Foo> ui; // Don't use a raw pointer!
enum { LOC_04, LOC_END };
int m_editMode[LOC_END];
void lable1() { ui->lable->setText("NumbVal"); }
void lable2() { ui->lEditCaliCLFltBst->setText("UNDER PROCESS"); }
...
void f() {
...
if (EditMode[LOC_04]!=0) lable1(); else lable2();
...
}
}
With the little code you've shown, I infer that you have an interface that can be in various states, and those states are indicated through multiple user interface elements. This is what QStateMachine is for.
The example below demonstrates the following:
The use of a state machine to control the appearance of the user interface in each state.
The user interface has two parallel states: the m_editState and the m_boldState. The states are parallel, meaning that the state machine is in both of those states at the same time. Imagine this was in a text editor of some sort.
The edit state can be in one of two substates: m_edit1 and m_edit2. Similarly, the bold state can be in two states: m_boldOn and m_boldOff.
Clicking the buttons switches the states, and modifies the indications on the labels.
Concise setup of a user interface without using the UI designer.
Direct use of QObject members in a QObject, without explicit heap storage. Note the absence of a single explicit new and delete in the entire code. This shouldn't be an end unto itself, but it certainly helps avoid some pitfalls of unmanaged pointers, and it halves the number of heap allocations per each object. This pattens also works great when you put all the members in a pimpl class.
A reasonably concise way of repeating some code for elements of a constant list, created in place. This is pre-C++11 code.
Referring back to your original code, perhaps the EditMode could be represented by a set of states. If there are multiple aspects of the EditMode, they'd be represented by parallel states - perhaps each entry in EditMode would be a parallel state. Without knowing anything else about what you intend to achieve, it's hard to tell.
#include <QApplication>
#include <QLabel>
#include <QPushButton>
#include <QStateMachine>
#include <QGridLayout>
class Widget : public QWidget {
QGridLayout m_layout;
QLabel m_label1, m_label2, m_label3;
QPushButton m_button1, m_button2, m_button3;
QStateMachine m_machine;
QState m_editState, m_boldState, m_edit1, m_edit2, m_boldOn, m_boldOff;
public:
Widget(QWidget * parent = 0) : QWidget(parent), m_layout(this),
m_label1("--"), m_label2("--"), m_label3("--"),
m_button1("Edit State 1"), m_button2("Edit State 2"), m_button3("Toggle Bold State"),
m_editState(&m_machine), m_boldState(&m_machine),
m_edit1(&m_editState), m_edit2(&m_editState),
m_boldOn(&m_boldState), m_boldOff(&m_boldState)
{
m_layout.addWidget(&m_label1, 0, 0);
m_layout.addWidget(&m_label2, 0, 1);
m_layout.addWidget(&m_label3, 0, 2);
m_layout.addWidget(&m_button1, 1, 0);
m_layout.addWidget(&m_button2, 1, 1);
m_layout.addWidget(&m_button3, 1, 2);
m_edit1.assignProperty(&m_label1, "text", "Edit State 1");
m_edit2.assignProperty(&m_label2, "text", "Edit State 2");
m_boldOn.assignProperty(&m_label3, "text", "Bold On");
m_boldOff.assignProperty(&m_label3, "text", "Bold Off");
m_editState.setInitialState(&m_edit1);
m_boldState.setInitialState(&m_boldOff);
foreach (QState * s, QList<QState*>() << &m_edit1 << &m_edit2) {
s->addTransition(&m_button1, SIGNAL(clicked()), &m_edit1);
s->addTransition(&m_button2, SIGNAL(clicked()), &m_edit2);
}
m_boldOn.addTransition(&m_button3, SIGNAL(clicked()), &m_boldOff);
m_boldOff.addTransition(&m_button3, SIGNAL(clicked()), &m_boldOn);
m_machine.setGlobalRestorePolicy(QState::RestoreProperties);
m_machine.setChildMode(QState::ParallelStates);
m_machine.start();
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
Related
Forgive me, for I know this is likely to be a very simple question, but I need another set of eyes. I've got a Checkbox on my GUI, and the state of the box (on/off) will directly alter my interrupt service routine. This seems super-easy, but I cannot use:
this->ui->checkBox_2->isChecked();
as a validator, because of the "invalid use of "this" is non-member function"
beyond that, I've attempted to just save the value of stateChanged(int arg1)
to some pointer or variable that I can call withing my ISR, but I suppose I'm having scope difficulties.
Any suggestions are welcome!
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
connect(this->ui->pushButton_8,SIGNAL(clicked()),this,SLOT(on_pushButton_8_clicked()));
//connect(this->ui->checkBox_2,SIGNAL(clicked(bool)),this,SLOT(myInterruptRIGHT));
//connect(this->ui->checkBox_2,SIGNAL(clicked(bool)),this,SLOT(myInterruptLEFT));
// connect(on_checkBox_2_stateChanged(int arg1)(),SIGNAL(clicked(bool checked)),this,SLOT(myInterruptRIGHT));
ui->pushButton->setText("STOP");
ui->verticalSlider->setMinimum(6);
ui->verticalSlider->setMaximum(8);
}
void MainWindow::on_checkBox_2_stateChanged(int arg1)
{
QCheckBox *cb2 = new QCheckBox;
connect(cb2,SIGNAL(stateChanged(int)),this,SLOT(on_checkBox_2_stateChanged(int)));
int sensor = digitalRead(23);
//Does a bunch of stuff
}
void myInterruptRIGHT (void)
{
//if(this->ui->checkBox_2->isChecked())
if(ui->checkBox_2->isChecked())
{ //Does stuff
}
else
{ //more stuff
}
}
PI_THREAD(RightTop)
{
for(;;)
{
wiringPiISR(23,INT_EDGE_RISING,&myInterruptRIGHT);
}
}
I apologize for the sloppish code, I've been testing a bunch of different things and nothing has proven very effective.
Problem 1: MainWindow::on_checkBox_2_stateChanged creates the checkbox and connects itself to slot of checkbox signal? Make sure the checkbox created somewhere in constructor or maybe in UI pre-designed part of code.
Problem 2: PI_THREAD does not seem to be Qt thread to catch a signal on it with the slot. You still can do something about it for your embedded application.
Please note that I of course could not test the solution but I did apply similar techniques in real applications. You may consider std::atomic<T> as well.
class MainWindow : public QMainWindow
{
public:
QAtomicInt& atomicChkBox2State() {return m_chkBox2StateAtomic;}
//// snip
private:
QAtomicInt m_chkBox2StateAtomic;
//// snip
};
void MainWindow::on_checkBox_2_stateChanged(int state)
{
m_chkBox2StateAtomic = state;
}
// where you create MainWindow you need to get that pointer somehow
static MainWindow* s_pMainWnd;
MainWindow* getMainWindow() {return s_pMainWnd;} // declare it in the .h file
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
s_pMainWnd = &w; // take an address
w.show();
return a.exec();
}
// and that ISR should read the atomic variable in case if does poll for check:
void myInterruptRIGHT (void)
{
// now poll the atomic variable reflecting the state of checkbox
if(getMainWindow()->atomicChkBox2State())
{ //Does stuff
}
else
{ //more stuff
}
}
That is for ISR that constantly polling the atomic variable when called by the system interrupt vector. If that Interrupt Service Routine supposed to get very own terminate signal pushed from the checkbox then we cannot answer that without knowing more about your embedded platform "how to terminate the ISR from the 'side' not by system interrupt".
I have a widget W deriving from QFrame with layout set to an instance of QVBoxLayout. I wonder if the following resizeEvent implementation is correct or is it going to cause an infinite loop:
void W::resizeEvent(QResizeEvent *event) {
for (/* some condition based on the new size of this widget */) {
// Infinite loop or not?
qobject_cast<QVBoxLayout *>(layout())->addWidget(new QWidget());
}
}
So far it worked for me, is this by pure luck?
This is okay. W owns a QLayout which owns QWidget. Adding the QWidget to the QLayout does not change the size of W. You see this all the time. For example, if you place a child widget in a parent and the parent is too small, the child widget will be clipped. Stately differently, the size of the parent does not stretch to accommodate the size of the child. I believe your code would be a typical way to hide or show widgets based on the size of the parent (for example, when the window size changes).
Painting and constructing a hierarchy of widgets are two different things. So, adding QWidgets is just fine, but using QPainter directly in resizeEvent not.
Hierarchy of QWidgets
A hierarchy of QWidgets derivatives (QLineEdit, QPushButton, ...) is a high level specification of how the graphical user interface should look like and may be ordered using QLayout items.
Painting
Painting (using QPainter) is the process of actually drawing something on the screen and is purely done in the virtual function QWidget::paintEvent. Every derivative of QWidget should provide an implementation of this empty base function. The default derivatives (QLineEdit, ...) provide an implementation of paintEvent based on their current state (size of the widget, current text for a QLineEdit, ...) and the current QStyle object, which is typically automatically set based on your OS, but may be changed programmatically using QWidget::setStyle or QApplication::setStyle. A repaint can be requested using QWidget::update.
"Should not/need not" vs "may not"
The sentence "No drawing need be (or should be) done inside this handler." is meant for people implementing a custom QWidget (with a new implementation of paintEvent) to make it clear that you should not implement your painting here, but that a paintEvent will be automatically triggered.
"Should not/need not" is some advice, they do not write "may not". So, if you for some reason (ex. real-time applications) want an immediate screen refreshment, you may invoke a repaint immediately using repaint, resulting in paintEvent being called during resizeEvent. As long as all the QPainter operations on a QWidget are inside a paintEvent (as required by the warning in the QPainter documentation), everything is just fine.
Adding widgets to the layout, using addWidget, within the resizeEvent function is not a problem as it does not instantly trigger a drawing.
You can easily verify this by compiling and executing this simple project:
dialog.h:
#pragma once
#include <QDialog>
class Dialog : public QDialog
{
Q_OBJECT
public:
Dialog(QWidget *parent = 0);
~Dialog();
void resizeEvent(QResizeEvent *event);
void paintEvent(QPaintEvent *event);
private:
bool resizing;
};
dialog.cpp:
#include "dialog.h"
#include <QResizeEvent>
#include <QVBoxLayout>
#include <QPushButton>
#include <QDebug>
Dialog::Dialog(QWidget *parent)
: QDialog(parent),
resizing(false)
{
new QVBoxLayout(this);
}
Dialog::~Dialog()
{
}
void Dialog::resizeEvent(QResizeEvent *event)
{
resizing = true;
if ( event->size().width() == event->size().height() )
{
qDebug() << "Adding widget";
// Infinite loop or not?
layout()->addWidget(new QPushButton());
}
resizing = false;
}
void Dialog::paintEvent(QPaintEvent *event)
{
if ( resizing )
{
qDebug() << "Painting while resizing widget";
}
}
main.cpp:
#include "dialog.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Dialog w;
w.show();
return a.exec();
}
When you run the program, resize the dialog to make it be square (width==height), some buttons are inserted ("Adding widget" is printed to the console), but you'll never see "Painting while resizing widget" message. This is most likely because addWidget sets a dirty display flag that is processed later by the framework. It invalidates the display, but does not repaint it right away.
So what you are doing is fine and does not violate the framework requirement ("No drawing need be (or should be) done inside this handler.").
However, if you are not confident (maybe the painting could be operated right away on different OS, or in future Qt versions....you can't be sure), you can also delay the insertion by emitting a signal connected to a slot using Qt::QueuedConnection, this slot would be executed "later" and then do the call to addWidget, guaranteeing that it's done outside the resizeEvent function.
I'm trying to add to qstatemachine a property with string and an enum, but the result from the machine.property() is empty.
QStatemachine m_machine;
idle->assignProperty(&m_machine, "state", Z_IDLE);
cool->assignProperty(&m_machine, "state", Z_COOL);
start_z->assignProperty(&m_machine, "state", Z_START);
QVariant st = m_machine->property("state");
QString s = st.toString();
I tried to show my approach to see if that can work or no.
Update:
idle = new QState();
start_z = new QState();
lock = new QState();
connect(this, SIGNAL(machine_exec()), this, SLOT(idle_exec()));
connect(this, SIGNAL(machine_exec()), this, SLOT(start_z_exec()));
connect(this, SIGNAL(machine_exec()), this, SLOT(sample_exec()));
m_machine->addState(idle);
m_machine->addState(start_z);
m_machine->addState(lock);
idle->assignProperty(m_machine, "state", Z_IDLE);
cool->assignProperty(m_machine, "state", Z_COOL);
start_z->assignProperty(m_machine, "state", Z_START);
idle->addTransition(this, SIGNAL(machineToStart()), start_z);
cool->addTransition(this, SIGNAL(machineToMotDn()), motDn);
motDn->addTransition(this, SIGNAL(machineToFini()), fini);
void MeasController::idle_exec()
{
qDebug()<<"idle_exec";
emit machineToStart();
}
void MeasController::start_z_exec()
{
qDebug()<<"start_z_exec";
QVariant s = m_machine->property("state");
qDebug()<<"property value"<<s.toString();
if (m_machine->property("state") == Z_START) {
emit machineStartToSample();
}
}
The properties are assigned on state transitions only, so if you check the value of the property before the machine was started and the event loop had a chance to run, you won't get a valid value.
Additionally, by using the name of the property multiple times, you're liable to commit a typo. Instead of string literals, you should be using named constants for property names - and they should be sufficiently different that random typos will get caught. I.e. property names like kStateA and kStateB are probably too similar - a typo would be silent.
So, I'd change the code to be:
static const char kState[] = "state";
QStatemachine m_machine;
idle->assignProperty(&m_machine, kState, Z_IDLE);
cool->assignProperty(&m_machine, kState, Z_COOL);
start_z->assignProperty(&m_machine, kState, Z_START);
qDebug() << m_machine->property(kState).toString();
Also note that this notion of a single current state only applies if your machine is non-hierarchical. A general HSM, as implemented by QStateMachine, is always in a set of states, available from configuration(). Most realistic machines should be hierarchical, so your approach won't work.
It is also unclear that there's any use for the _exec methods, as well as the explicit signals used to transition between states.
Below is a complete example of approaches that you might find useful.
First, let's start with how one might concisely specify your MeasController:
class MeasController : public QObject {
Q_OBJECT
QStateMachine m_machine{this};
NamedState
m_idle {"Idle", &m_machine},
m_start_z {"Start Z", &m_machine},
m_active_z{"Active Z", &m_machine},
m_downMove{"DownMove", &m_machine};
Transition
m_toStartZ{&m_idle, &m_start_z},
m_toDownMove{&m_active_z, &m_downMove};
Delay
m_d1{&m_start_z, &m_active_z, 1000},
m_d2{&m_downMove, &m_active_z, 2000};
QList<QState*> states() const { return m_machine.findChildren<QState*>(); }
public:
MeasController(QObject * parent = 0) : QObject(parent) {
for (auto state : states())
connect(state, &QState::entered, [state]{ qDebug() << state->objectName(); });
m_machine.setInitialState(&m_idle);
m_machine.start();
}
Q_SLOT void startZ() { m_toStartZ.trigger(); }
Q_SLOT void moveDown() { m_toDownMove.trigger(); }
};
This is a complete specification of the state machine. The states and their transitions and other behaviors are given as members of MeasController, and are thus easy to find in one place. For ease of debugging, debug output is provided upon each state being entered. Slots are provided to trigger behaviors from outside of the class without having to expose the internals.
Let's now look at the idioms defined by the NamedState, Transition and Delay classes.
A named state idiom, similar to Qt 3's QObject taking name in the constructor, is useful for debugging at the very least. If you want to assign any other properties of the state, you can do so in this class as well. Since the states have unique variable names and are members of the parent class any integer identifiers aren't necessary:
// https://github.com/KubaO/stackoverflown/tree/master/questions/state-properties-36745219
#include <QtWidgets>
class NamedState : public QState { Q_OBJECT
public:
NamedState(const char * name, QStateMachine * parent) : QState(parent) {
setObjectName(QString::fromUtf8(name));
}
};
A transition class is useful to give a concise way of specifying the structure of your state machine as simple member definitions of Transition instances:
struct Transition : public QObject { Q_OBJECT
public:
Transition(QState * source, QState * destination) : QObject(source->machine()) {
source->addTransition(this, &Transition::trigger, destination);
}
Q_SIGNAL void trigger();
};
I'm sure that your state machine has more complex behaviors, but generally it helps to factor out a certain behavior into its own class. E.g. say you want a state transition that occurs after a delay:
class Delay : public Transition { Q_OBJECT
int m_delay;
QBasicTimer m_timer;
void timerEvent(QTimerEvent * ev) {
if (m_timer.timerId() != ev->timerId()) return;
m_timer.stop();
trigger();
}
public:
Delay(QState * s, QState * d, int ms) : Transition(s, d), m_delay(ms) {
connect(s, &QState::entered, this, [this]{ m_timer.start(m_delay, this);});
}
};
Finally, we can offer a simple UI to test and visualize the behavior of the machine. The debug output is duplicated in a QPlainTextEdit for ease of use.
int main(int argc, char ** argv) {
QApplication app{argc, argv};
MeasController ctl;
QWidget w;
QGridLayout layout{&w};
QPushButton start{"Start"};
QPushButton moveDown{"Move Down"};
QPlainTextEdit log;
log.setReadOnly(true);
layout.addWidget(&start, 0, 0);
layout.addWidget(&moveDown, 0, 1);
layout.addWidget(&log, 1, 0, 1, 2);
QObject::connect(&start, &QPushButton::clicked, &ctl, &MeasController::startZ);
QObject::connect(&moveDown, &QPushButton::clicked, &ctl, &MeasController::moveDown);
static QtMessageHandler handler = qInstallMessageHandler(
+[](QtMsgType t, const QMessageLogContext& c, const QString & msg){
static QPointer<QPlainTextEdit> log{[]{
for (auto w : qApp->topLevelWidgets())
for (auto log : w->findChildren<QPlainTextEdit*>()) return log;
Q_ASSERT(false);
}()};
if (log) log->appendPlainText(msg);
handler(t, c, msg);
});
w.show();
return app.exec();
}
#include "main.moc"
This concludes the complete example. This is a longer example in a somewhat similar style.
The code below displays thumbnails in a left pane. When a thumbnail is clicked, the full-size image appears in the right pane.
I have the impression that even though this code is rather brief, it is not the most natural way to do this task in Qt. Am I reinventing the wheel? Are there Model-View classes that are more suitable for this task?
// main.cpp
#include "PixmapPair.h"
#include <QLabel>
#include <QWidget>
#include <QApplication>
#include <QSplitter>
#include <QGridLayout>
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
QSplitter* page = new QSplitter;
QGridLayout* gridLayout = new QGridLayout;
QWidget* leftPane = new QWidget(page);
leftPane->setLayout(gridLayout);
QLabel* rightPane = new QLabel(page);
PixmapPair pair1(":/images/ocean.jpg", gridLayout, rightPane);
PixmapPair pair2(":/images/forest.jpg", gridLayout, rightPane);
page->setWindowTitle("Images");
page->show();
return app.exec();
}
// PixmapPair.h
#include <QPixmap>
#include <QIcon>
#include <QLabel>
#include <QPushButton>
#include <QGridLayout>
class PixmapPair : public QObject
{
Q_OBJECT
public:
PixmapPair(QString file, QGridLayout * gridLayout, QLabel* rp)
: rightPane(rp), largePixmap(file)
{
smallPixmap = largePixmap.scaled(QSize(100,100), Qt::KeepAspectRatio, Qt::SmoothTransformation);
QPushButton* pushButton = new QPushButton;
pushButton->setIcon(QIcon(smallPixmap));
pushButton->setFlat(true);
pushButton->setIconSize(QSize(100,100));
QObject::connect(pushButton, SIGNAL(clicked()), SLOT(displayInRightPane()));
gridLayout->addWidget(pushButton);
}
public slots:
void displayInRightPane()
{
rightPane->setPixmap(largePixmap);
}
private:
QLabel* rightPane;
QPixmap largePixmap;
QPixmap smallPixmap;
};
The left part of the SplitView is basically a list presenting all the available pictures. Qt provides a way to handle this using the model/view pattern.
The class for showing a list is a QListView, it will do the job automatically based on a model given with the function setModel().
This function requires a QAbstractItemModel, since this class is a pure abstract one we will need to create a custom class deriving from it.
Inheriting from it will require a lot of glue code but thankfully Qt already provides a class that takes care of most of it when we want to represent a list, it is called QAbstractListModel.
So I created an ImageListModel like this :
///////////////////////
// imagelistmodel.h ///
#ifndef IMAGELISTMODEL_H
#define IMAGELISTMODEL_H
#include <QAbstractListModel>
#include <QPixmap>
struct PixmapPair
{
QString _file;
QPixmap _small;
QPixmap _large;
};
class ImageListModel : public QAbstractListModel
{
Q_OBJECT
public:
// QAbstractItemModel retrieves various information (like text, color, ...)
// from the same index using roles. We can define custom ones, however to
// avoid a clash with predefined roles, ours must start at Qt::UserRole.
// All numbers below this one are reserved for Qt internals.
enum Roles
{
LargePixmapRole = Qt::UserRole + 1
};
explicit ImageListModel(std::initializer_list<QString> files, QObject *parent = 0);
virtual ~ImageListModel();
// QAbstractItemModel interface ===========================
public:
int rowCount(const QModelIndex &parent) const;
QVariant data(const QModelIndex &index, int role) const;
// ========================================================
private:
QList<PixmapPair*> _data;
};
#endif // IMAGELISTMODEL_H
///////////////////////
// imagelistmodel.cpp /
#include "imagelistmodel.h"
ImageListModel::ImageListModel(std::initializer_list<QString> files, QObject *parent)
: QAbstractListModel(parent)
{
auto iter = files.begin();
while (iter != files.end())
{
QPixmap large(*iter);
PixmapPair *pair = new PixmapPair();
pair->_file = *iter;
pair->_large = large;
pair->_small = large.scaled(100, 100, Qt::KeepAspectRatio, Qt::SmoothTransformation);
_data.append(pair);
++iter;
}
}
ImageListModel::~ImageListModel()
{
qDeleteAll(_data);
}
int ImageListModel::rowCount(const QModelIndex &parent) const
{
// This function should return the number of rows contained in the parent
// parameter, the parent parameter is used for trees in order to retrieve the
// number of rows contained in each node. Since we are doing a list each element
// doesn't have child nodes so we return 0
// By convention an invalid parent means the topmost level of a tree. In our case
// we return the number of elements contained in our data store.
if (parent.isValid())
return 0;
else
return _data.count();
}
QVariant ImageListModel::data(const QModelIndex &index, int role) const
{
if (index.isValid())
{
switch (role)
{
case Qt::DecorationRole:
{
// DecorationRole = Icon show for a list
return _data.value(index.row())->_small;
}
case Qt::DisplayRole:
{
// DisplayRole = Displayed text
return _data.value(index.row())->_file;
}
case LargePixmapRole:
{
// This is a custom role, it will help us getting the pixmap more
// easily later.
return _data.value(index.row())->_large;
}
}
}
// returning a default constructed QVariant, will let Views knows we have nothing
// to do and we let the default behavior of the view do work for us.
return QVariant();
}
///////////////////////
Our list is now ready and we are almost done.
// main.cpp ///////////
#include <QApplication>
#include <QSplitter>
#include <QLabel>
#include <QListView>
#include "imagelistmodel.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QSplitter page;
QListView *imageList = new QListView(&page);
imageList->setModel(new ImageListModel({ "ocean.jpg", "forest.jpg" }, imageList));
// We tell the list view to show our icon, this mode will call the data function
// of our model with the role : DecorationRole.
imageList->setViewMode(QListView::IconMode);
// We want our list to show data vertically
imageList->setFlow(QListView::TopToBottom);
// We allow only one selection at a time in the list
imageList->setSelectionMode(QListView::SingleSelection);
QLabel *imagePresenter = new QLabel(&page);
// We connect to the signal emitted when the selection is changed
// to update the image presenter.
QObject::connect(imageList->selectionModel(), &QItemSelectionModel::selectionChanged, [imageList, imagePresenter] {
QModelIndex selectedIndex = imageList->selectionModel()->selectedIndexes().first();
// We use our custom role here to retrieve the large image using the selected
// index.
imagePresenter->setPixmap(selectedIndex.data(ImageListModel::LargePixmapRole).value<QPixmap>());
});
page.setWindowTitle("Images");
page.show();
return a.exec();
}
Advantages for this solution are:
- We can easily add filtering by wrapping our custom ListModel into a QSortFilterProxyModel.
- No need to create and manage a lot of buttons.
- The model never needs to know who shows it on screen.
- The QListView will autoscroll if necessary.
- Using a custom role allows us to easily retrieve the large image. If we added the large image in another column, it would show when using this model with a QTableView and when we want retrieve it from the selected index we would have to create a new index pointing to the right column. (Not really hard but require a little more code, and prone to error if we wrap the model in a ProxyModel)
Lambda explanation
For the lambda in C++ the full syntax is:
[CAPTURES]\(PARAMETERS\)->RESULT {FUNCTION}.
Between brackets we capture variables to be able to use them inside the FUNCTION without having to pass them as parameters.
The PARAMETERS between parenthesis have the same signification as any other function, if omitted the lambda takes no parameters.
RESULT is the return type of the FUNCTION and can be omitted.
FUNCTION the body to execute
In this example I decided to ignore the parameters given by the signal so I omitted the parenthesis. I use the captured controls to retrieve the user selection and update the picture shown.
I have this snippet of the code:
#include <QApplication>
#include <QFont>
#include <QPushButton>
#include <QWidget>
class MyWidget : public QWidget
{
public:
MyWidget(QWidget *parent = 0);
};
MyWidget::MyWidget(QWidget *parent)
: QWidget(parent)
{
setFixedSize(200, 120);
QPushButton *quit = new QPushButton(tr("Quit"), this);
quit->setGeometry(62, 40, 75, 30);
quit->setFont(QFont("Times", 18, QFont::Bold));
connect(quit, SIGNAL(clicked()), qApp, SLOT(quit()));
}
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MyWidget widget;
widget.show();
return app.exec();
}
can somebody please explain what exactly is going on in this line
MyWidget(QWidget *parent = 0);
a little bit difficult understand what is this parent, thanks in advance
That is an argument to the constructor with a default argument (NULL since NULL is defined as 0 as per the c++ standard). Default meaning passing no parameter is the same as passing NULL.
Since Qt's widgets are arranged in a hierarchal system (parent -> child relationships) parent is the widget which is the "owner" or "container" of the current one (NULL means no parent aka a root widget of sorts). For GUI items a widget will often have the widget it is contained in as its parent.
This is advantageous since when a parent is deleted, it will delete any children is has automatically removing the need for much of the memory management that comes with c++.
The parent argument is for giving parents to new widgets. When given, it is useful for Qt to manage the object tree. (To automatically delete child objects.) It also has the concrete visible effect of "attaching" a new widget to another widget (ie, the parent). In your code however, the parent argument is not ever given, causing the widget to appear as a top level window and not to be deleted by Qt automatically. (It would not require deletion by Qt anyway in that code though.)
Its a 0 pointer (think NULL without the type), or in Qt terms, "no parent".