I'm working on the following Window with QT:
For my rows i have the following structure:
typedef struct
{
struct{
int x;
int y;
int width;
int height;
int layer;
int idx;
}outputSettings;
QDoubleSpinBox *xSpinBox;
QDoubleSpinBox *ySpinBox;
QDoubleSpinBox *heightSpinBox;
QDoubleSpinBox *widthSpinBox;
QDoubleSpinBox *layerSpinBox;
// Checkboxes
QCheckBox *channelCheckBox;
}myUI;
QVector<myUI> inputBoxes; // Create a row of input boxes per channel
I then create them in a for loop:
for(i = 0; i < inputChannels; ++i)
{
inputBoxes[i].channelCheckBox = new QCheckBox;
inputBoxes[i].channelCheckBox->setChecked(true);
inputBoxes[i].xSpinBox = new QDoubleSpinBox;
inputBoxes[i].xSpinBox->setRange(minXPos, maxXPos);
inputBoxes[i].xSpinBox->setSingleStep(1);
inputBoxes[i].xSpinBox->setValue(0);
inputBoxes[i].xSpinBox->setDecimals(0);
connect(inputBoxes[i].xSpinBox, SIGNAL(valueChanged(double)), this, SLOT(setXValue(double)));
inputBoxes[i].ySpinBox = new QDoubleSpinBox;
inputBoxes[i].ySpinBox->setRange(minYPos, maxYPos);
inputBoxes[i].ySpinBox->setSingleStep(1);
inputBoxes[i].ySpinBox->setValue(0);
inputBoxes[i].ySpinBox->setDecimals(0);
connect(inputBoxes[i].ySpinBox, SIGNAL(valueChanged(double)), this, SLOT(setYValue(double)));
...
Now i get stuck on the connect. I want to connect the valueChanged property of my spinboxes to my outputSettings struct. This struct will be my return type at the end.
I implemented the following slots:
public slots:
void setXValue(double x){inputBoxes[0].outputSettings.x = int(x);}
void setYValue(double y){inputBoxes[0].outputSettings.y = int(y);}
...
But here i don't know what vector item called the function. (currently i just entered inputBoxes[0] as a dummy)
My first idea was to add an extra parameter int channel. But then the connect doesn't work. So i tried to work around that with QMapper. But that doesn't seem to be a good option to me and i didn't really get it running.
I would largely appreciate if someone could help me out here or at least point me in the right direction.
Cheers.
Implement it by using a lambda function in your connect
connect(inputBoxes[i], static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged),
[i](double value)
{
// use i as your vector index here
handleDoubleSpinBoxChanged(i, value);
});
Then you can change your slot function to
void handleDoubleSpinBoxChanged(int i, double value)
{
inputBoxes[i].outputSettings.x = int(x);
}
Second option is to get the spin box index from the sender object
you will have to store it first inside your loop
inputBoxes[i].xSpinBox->setProperty("index",i);
Then you can get it
void MainWindow::setXValue(double d)
{
QDoubleSpinBox * sb = qobject_cast<QDoubleSpinBox *>(QObject::sender());
int iCallerVectorIndex = -1;
if (sb != Q_NULLPTR)
{
iCallerVectorIndex = sb->property("index").toInt(); // to get the caller index.
}
}
If I got you right, in your SLOT method you can call sender() to retrieve the object who emitted the signal. You can compare in a loop the spinBoxes of each of your inputBoxes to find out what caused the SLOT to execute, something like:
// in your SLOT method:
for (int i = 0; i < inputChannels; i++){
if (inputBoxes[i].xSpinBox == (QDoubleSpinBox *)sender()){
// the xSpinBox of the i-th inputBox emitted the signal
break();
}
}
You can also just make myUI a QObject and add slot function there.
You wouldnt need any indexes then.
typedef struct
{
Q_OBJECT
struct{
int x;
int y;
int width;
int height;
int layer;
int idx;
}outputSettings;
QDoubleSpinBox *xSpinBox;
QDoubleSpinBox *ySpinBox;
QDoubleSpinBox *heightSpinBox;
QDoubleSpinBox *widthSpinBox;
QDoubleSpinBox *layerSpinBox;
// Checkboxes
QCheckBox *channelCheckBox;
public slots:
setXValue(double);
setYValue(double);
}myUI;
Example of connect call:
connect(inputBoxes[i].ySpinBox, SIGNAL(valueChanged(double)), inputBoxes[i], SLOT(setYValue(double))
or you can call connect in constructor of myUI:
myUI() {
connect(xSpinBox, SIGNAL(valueChanged(double)),
this, SLOT(setXValue(double))
connect(ySpinBox, SIGNAL(valueChanged(double)),
this, SLOT(setYValue(double))
}
I think that would be much simpler and intuitive because your object is responsible to setting his own members and you dont need to remember any indexes.
Related
I am new to qt and I want to know how to make a dynamic menu.
I did get it to make new submenus but I don't know how I can implement the "triggered() function" of these dynamic made submenus, so that I have access to what happens if I want to click on such a new submenu.
Here what I have so far (with: vector<QString> = vec; and some .ui Window named "New_Window")
in mainwindow.cpp
in some function:
QMenu *menu = this->menuBar()->addMenu("Chat Members");
for (int i = 0; i < vec.size(); ++i){
QString name = vec.at(i);
QAction *act = menu->addAction(name);
New_Window* new_window = new New_Window;
QObject::connect(act,SIGNAL(triggered()),
new_window,SLOT(actionReaction()));
}
here is an example of how a signal slot with a dynamic interface works ,
class A is created after starting the program, then the user clicks on a button from class A, for example, a class A is created many times and we need to determine from what object we get a signal to press the button, so
class A : public QMainWindow
{
Q_OBJECT
public:
A(QWidget *parent = nullptr);
~A();
void setID(const int id);
void getId() const;
signals:
void onButtonPress(int ID);
private:
int mID;
};
here we create a new class A and store it in the vector in such a way,
QVector<A*> mCreatingClassA;
void createNewClassA
{
QVector<A*> mCreatingClassA;
....
A* a = new A();
int id = // create your unique ID
a->setId(id);
connect(a,SIGNAL(onButtonPress(int)),this,SLOT(onyourSlot(int)));
mCreatingClassA.push_back(a);
....
}
detect the object from which the signal was received)
void onyourSlot(int ID)
{
for (int i = 0; i < mCreatingClassA.size(); ++i) {
if(mCreatingClassA[i]->getId()==ID)
{
mCreatingClassA[i] // received a signal from this object
}
}
}
EDIT: I have read Passing and argument to a slot it's helpful, but doesn't address my issue of passing multiple references to a function I called via a signal-slot.
I'm currently working on a Qt application that essentially is a unit converter. I'm implementing it using QDoubleSpinBoxes as the input and the output. I am running into an issue that i'm looking for help with. The implementation idea is that the user will input a value of whatever they want to convert and it will, upon the user losing focus on the spinbox or hitting enter, populate the other unit type boxes with the answer.
Here is how I currently do it:
// creates a custom spinbox for each option
modifiedSpinbox *FahrenheitDblSpinbox = new modifiedSpinbox();
modifiedSpinbox *CelciusDblSpinbox = new modifiedSpinbox();
modifiedSpinbox *KelvinDblSpinbox = new modifiedSpinbox();
modifiedSpinbox *RankineDblSpinbox = new modifiedSpinbox();
// Creates a signal mapper that allows passing of a parameter
// to the convert functions anytime a number is entered
QSignalMapper *tempMapper = new QSignalMapper(this);
// Connects the spinbox editing complete signal with the mapper
connect(tempMapper, SIGNAL(mapped(int)),
this,SLOT(on_userInput(int whoSignaled)));
// Connects the mapper with the function that calls the convert class
connect(FahrenheitDblSpinbox, SIGNAL(editingFinished()),
tempMapper, SLOT(map()));
tempMapper->setMapping(FahrenheitDblSpinbox, 1);
The way I would like to implement this conversion function is to, on user finishing their input into a spinbox, have the code send a signal (editingFinished()) to a slot function which calls a converttools class that has the functions needed to do the actual conversion. The problem that i'm running in to is that I cannot figure out how to pass references for these spinbox objects to my converttools class so that I can set the spinbox values directly.
The closest i've come is to use QSignalMapper (seen above) to pass a single int or Qwidget to my slot function, not the objects I want.
I would like some advice as to how to pass multiple references to my custom class after a signal is emitted. I've looked though numerous questions here and still cant seem to figure out how to do this or a better way i'm not seeing.
Thanks!
QSignalMapper is obsolete in C++11. It's only a band-aid against the cumbersomeness of declaring functors in C++98. If you're using a C++11 compiler, you never have to use QSignalMapper.
Lambdas make it trivial - and you really don't need to pass much. Side note: ideally, you should use a modern C++11 units library.
Here's a complete example:
// https://github.com/KubaO/stackoverflown/tree/master/questions/tempconvert-42648860
#include <QtWidgets>
const char kUnit[] = "unit";
class MyWidget : public QWidget {
QFormLayout m_layout{this};
QDoubleSpinBox m_fahrenheit, m_celsius, m_kelvin;
QList<QDoubleSpinBox*> const m_spinBoxes{&m_fahrenheit, &m_celsius, &m_kelvin};
enum Unit { Fahrenheit, Celsius, Kelvin };
static double Q_DECL_RELAXED_CONSTEXPR fromK(Unit to, double val) {
if (to == Fahrenheit) return (val-273.15)*1.8 + 32.0;
else if (to == Celsius) return val - 273.15;
else return val;
}
static double Q_DECL_RELAXED_CONSTEXPR toK(Unit from, double val) {
if (from == Fahrenheit) return (val-32.0)/1.8 + 273.15;
else if (from == Celsius) return val + 273.15;
else return val;
}
void setTemp(Unit unit, double temp, QDoubleSpinBox * skip = nullptr) {
for (auto spin : m_spinBoxes) if (spin != skip) {
QSignalBlocker b{spin};
spin->setValue(fromK(unitOf(spin), toK(unit, temp)));
}
}
static Unit unitOf(QDoubleSpinBox * spin) {
return static_cast<Unit>(spin->property(kUnit).toInt());
}
public:
MyWidget(QWidget * parent = nullptr) : QWidget{parent} {
m_layout.addRow("Fahreneheit", &m_fahrenheit);
m_layout.addRow("Celsius", &m_celsius);
m_layout.addRow("Kelvin", &m_kelvin);
m_fahrenheit.setProperty(kUnit, Fahrenheit);
m_celsius.setProperty(kUnit, Celsius);
m_kelvin.setProperty(kUnit, Kelvin);
for (auto const spin : m_spinBoxes) {
auto const unit = unitOf(spin);
spin->setRange(fromK(unit, 0.), fromK(unit, 1000.));
connect(spin, static_cast<void(QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged),
[=]{ setTemp(unit, spin->value(), spin); });
}
setTemp(Celsius, 20.);
}
};
int main(int argc, char ** argv) {
QApplication app{argc, argv};
MyWidget ui;
ui.show();
return app.exec();
}
What you're looking for is:
Signals to carry the QDoubleSpinBox's assigned temperature
A conversion to a common temperature unit
A Signal to all other QDoubleSpinBoxs to update them
A conversion from common temperature unit to each QDoubleSpinBox's specific temperature unit
QSignalMapper is a bad choice here, because:
This class collects a set of parameterless signals, and re-emits them with integer, string or widget parameters corresponding to the object that sent the signal
So we cannot take in the assigned temperature. Instead lets start with a map<QDoubleSpinBox*, pair<function<double(double)>, function<double(double)>>> which will serve to map from a given QDoubleSpinBox to its "conversion to a common temperature unit" and "conversion from a common temperature unit", respectively.
We'll then build an object around this map looking something like this:
class SlotMapper : public QObject
{
Q_OBJECT
map<QDoubleSpinBox*, pair<function<double(double)>, function<double(double)>>> mapping;
public:
SlotMapper() = default;
SlotMapper(const map<QDoubleSpinBox*, pair<function<double(double)>, function<double(double)>>> mapping) : mapping(mapping) {};
AddMapping(QDoubleSpinBox* key, function<double(double)> valueFirst, function<double(double)> valueSecond) { mapping.insert_or_assign(key, make_pair(valueFirst, valueSecond)); }
void map(const double assignedTemperature) const {
const auto commonTemperatureUnit = mapping.at(QObject()::sender).first(assignedTemperature);
for(auto it = cbegin(mapping); it != cend(mapping); ++it) {
if(it->first != QObject()::sender) {
it->first->blockSignals(true);
it->first->setValue(it->second.second(commonTemperatureUnit));
it->first->blockSignals(false);
}
}
}
};
This object should be constructed with all necessary conversion functions. in your case that probably looks something like:
SlotMapper mySlotMapper(map<QDoubleSpinBox*, pair<function<double(double)>, function<double(double)>>>{ {FahrenheitDblSpinbox, make_pair([](const double param){ return (param - 32.0) * 5.0 / 9.0; }, [](const double param){ return param * 9.0 / 5.0 + 32.0; })},
{CelciusDblSpinbox, make_pair([](const double param){ return param; }, [](const double param){ return param; })},
{KelvinDblSpinbox, make_pair([](const double param){ return param - 273.15; }, [](const double param){ return param + 273.15; })},
{RankineDblSpinbox, make_pair([](const double param){ return (param - 491.67) * 5.0 / 9.0; }, [](const double param){ return (param + 273.15) * 9.0 / 5.0; })} });
As far as your connections, they'll look like:
connect(FahrenheitDblSpinbox, static_cast<void(QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged), &mySlotMapper, &SlotMapper::map);
connect(CelciusDblSpinbox, static_cast<void(QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged), &mySlotMapper, &SlotMapper::map);
connect(KelvinDblSpinbox, static_cast<void(QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged), &mySlotMapper, &SlotMapper::map);
connect(RankineDblSpinbox, static_cast<void(QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged), &mySlotMapper, &SlotMapper::map);
I have a list of functions with parameters. For each parameter I create a spinbox holding its value. Some functions have zero parameters others have n>1 parameters.
The code looks like this (simplified)
for (int i = 0; i < parameterList.size(); ++i) {
QString valueName = parameterList().at(i);
double value = parameter(valueName);
QDoubleSpinBox * spinbox = new QDoubleSpinBox();
QLabel * label = new QLabel();
label->setText(valueName);
spinbox->setValue(value);
// does NOT work, Slot need three parameters!
QObject::connect(spinbox, &QDoubleSpinBox::valueChanged,
this, &OnAmplitudeParameterChanged);
... add widgets to layout
}
However the slot needs to know which widgets was calling, the parameter name and its value. The signal however provides only a value.
The slot looks like this
OnAmplitudeParameterChanged(int index, QString name, double value)
How is this solved in Qt? I found a QSignalMapper class but not how this would solve my problem.
I would use a little lambda for that
auto func = [i, valueName, this](double value){
OnAmplitudeParameterChanged(i, valueName, value);
};
QObject::connect(spinbox, &QDoubleSpinBox::valueChanged, func);
EDIT
Jon Harper's answer inspired me to use the QObject::setProperty() as another interesting solution:
QDoubleSpinBox* spinbox = new QDoubleSpinBox();
spinbox->setProperty("myindex", i);
spinbox->setProperty("myname", valueName);
and then in your slot:
void SomeClass::OnAmplitudeParameterChanged(double value)
{
int index = sender()->property("myindex").toInt();
QString name = sender()->property("myname").toString();
}
But still, I would probably use the lambda anyway.
To expand on Wiki Wang's answer, use the combination of sender(), qobject_cast and the spinbox's object name:
In your code:
QString valueName = parameterList().at(i);
double value = parameter(valueName);
QDoubleSpinBox * spinbox = new QDoubleSpinBox();
spinbox->setObjectName(valueName);
Then in the slot:
void SomeClass::OnAmplitudeParameterChanged(double value)
{
QDoubleSpinBox *spinbox = qobject_cast<QDoubleSpinBox *>(sender());
if (spinbox && parameterList.contains(spinbox->objectName()) {
int value = parameter(spinbox->objectName());
// your code here
}
}
You can use QObject::sender() to get the caller spinbox in the slot function.
QDoubleSpinBox *spinbox = static_cast<QDoubleSpinBox *>(QObject::sender());
I'm using c++ Qt library and I want to do something which would do :
connect(actionB11, SIGNAL(triggered()), this, SLOT(SetSomething(1, 1)));
connect(actionB12, SIGNAL(triggered()), this, SLOT(SetSomething(1, 2)));
connect(actionB21, SIGNAL(triggered()), this, SLOT(SetSomething(2, 1)));
connect(actionB22, SIGNAL(triggered()), this, SLOT(SetSomething(2, 2)));
The code above doesnt work because SIGNAL function has to have same number and argument types as SLOT function.
Does exist a way how to do it? I dont want to have about 20 function as SetSomething11, SetSomething12 calling SetSomething(1, 1) etc.
In situations like this you have three simple options:
connect each QAction to its own slot (not good)
use a QSignalMapper
add each QAction to a QActionGroup and use the QActionGroup::triggered(QAction*) signal, coupled with setting each QAction's data (see QAction::setData() and QAction::data())
When you set the data for a QAction, you can only store one QVariant (i.e., one value). So if you want two values, I would recommend just creating a simple mapping, like this:
void Window::onActionGroupTriggered(QAction *action);
{
int i = action->data().toInt();
int a, b;
a = i / 10;
b = i - 10;
setSomething(a, b); // for example if i = 15, then a = 1 and b = 5
}
You may modify QAction class.
class QMyAction : public QAction
{
Q_OBJECT
QMyAction ( QObject * parent ) :
QAction(parent), _x(0), _y(0)
{
connect(this, SIGNAL(triggered(bool)), this, SLOT(re_trigger(bool)));
}
QMyAction ( const QString & text, QObject * parent ) :
QAction (text, parent), _x(0), _y(0)
{
connect(this, SIGNAL(triggered(bool)), this, SLOT(re_trigger(bool)));
}
QMyAction ( const QIcon & icon, const QString & text, QObject * parent ) :
QAction(icon, text, parent), _x(0), _y(0)
{
connect(this, SIGNAL(triggered(bool)), this, SLOT(re_trigger(bool)));
}
void setX(int x)
{
_x = x;
}
int getX()
{
return _x;
}
void setY(int y)
{
_y = y;
}
int getY()
{
return _y;
}
public slots:
void re_trigger(bool)
{
emit triggered(_x, _y);
}
signals:
void triggered(int,int);
private:
int _x;
int _y;
};
Now, you can connect triggered(int,int) to SetSomething(int,int). But, you have to set x and y. Unless, they will always be 0.
You cannot use constant in SLOT signature, you have to use types there. When connecting signal to slot the slot must have the same subset of parameters signal has, otherwise they cannot be connected and QObject::connect() will return false.
connect(actionB11, SIGNAL(triggered()),
this, SLOT(SetSomething()));
This slot takes no parameters, but you can use QObject::sender() to get pointer to the object, which emitted the signal. Then this pointer can be used to discriminate the source of the signal:
void SetSomething() {
switch(sender()) {
case actionB11;
// do something
break;
case actionB12;
// do something
break;
case actionB21;
// do something
break;
case actionB22;
// do something
break;
default:
// Exceptional situation
}
}
Alternatively you can use QSignalMapper to append additional discriminating parameters to slots.
I use QGraphicsScene of the Qt framework. Inside the scene I have some QGraphicsItems which the user can select and move.
I would like to have an info label where the current x and y coordinate of the currently moved selection (can consist of many items) is displayed.
I have tried with the signal changed of QGraphicsScene. But it is fired before the x() and y() property of the items is set to the new values. So the labels always show the second-to-last coordinates. If one moves the mouse slowly, the display is not very wrong. But with fast moves and sudden stops, the labels are wrong. I need a signal that is fired after the scene hast changed.
I have also tried to override the itemChange method of QGraphicsItem. But it is the same. It is fired before the change. (The new coordinates are inside the parameters of this method, but I need the new coordinates of all selected items at once)
I have also tried to override the mouseMove events of QGraphicsScene and of QGraphicsView but they, too, are before the new coordinates are set.
I did a test: I used a oneshot timer so that the labels are updated 100 ms after the signals. Then everything works fine. But a timer is no solution for me.
What can I do?
Make all items un-moveable and handle everything by my own?
QGraphicsItem::itemChange() is the correct approach, you were probably just checking the wrong flag. Something like this should work fine:
QVariant::myGraphicsItem( GraphicsItemChange change, const QVariant &value )
{
if( change == QGraphicsItem::ItemPositionHasChanged )
{
// ...
}
}
Note the use of QGraphicsItem::ItemPositionHasChanged rather than QGraphicsItem::ItemPositionChange, the former is called after the position changes rather than before.
The solution is to combine various things that you're already doing. Instrument itemChange, looking for and count the items with updated geometry. Once you've counted as many items as there are in the current selection, fire off a signal that will have everything ready for updating your status. Make sure you've set the QGraphicsItem::ItemSendsGeometryChanges flag on all your items!
This code was edited to remove the lag inherent in using a zero-timer approach. Below is a sscce that demonstrates it.
You create circles of random radius by clicking in the window. The selection is toggled with Ctrl-click or ⌘-click. When you move the items, a centroid diamond follows the centroid of the selected group. This gives a visual confirmation that the code does indeed work. When the selection is empty, the centroid is not displayed.
I've gratuitously added code to show how to leverage Qt's property system so that the items can be generic and leverage the notifier property of a scene if it has one. In its absence, the items simply don't notify, and that's it.
// https://github.com/KubaO/stackoverflown/tree/master/questions/scenemod-11232425
#include <QtWidgets>
const char kNotifier[] = "notifier";
class Notifier : public QObject
{
Q_OBJECT
int m_count = {};
public:
int count() const { return m_count; }
void inc() { m_count ++; }
void notify() { m_count = {}; emit notification(); }
Q_SIGNAL void notification();
};
typedef QPointer<Notifier> NotifierPointer;
Q_DECLARE_METATYPE(NotifierPointer)
template <typename T> class NotifyingItem : public T
{
protected:
QVariant itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value) override {
QVariant v;
if (change == T::ItemPositionHasChanged &&
this->scene() &&
(v=this->scene()->property(kNotifier)).isValid())
{
auto notifier = v.value<NotifierPointer>();
notifier->inc();
if (notifier->count() >= this->scene()->selectedItems().count()) {
notifier->notify();
}
}
return T::itemChange(change, value);
}
};
// Note that all you need to make Circle a notifying item is to derive from
// NotifyingItem<basetype>.
class Circle : public NotifyingItem<QGraphicsEllipseItem>
{
QBrush m_brush;
public:
Circle(const QPointF & c) : m_brush(Qt::lightGray) {
const qreal r = 10.0 + (50.0*qrand())/RAND_MAX;
setRect({-r, -r, 2.0*r, 2.0*r});
setPos(c);
setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable |
QGraphicsItem::ItemSendsGeometryChanges);
setPen({Qt::red});
setBrush(m_brush);
}
};
class View : public QGraphicsView
{
Q_OBJECT
QGraphicsScene scene;
QGraphicsSimpleTextItem text;
QGraphicsRectItem centroid{-5, -5, 10, 10};
Notifier notifier;
int deltaCounter = {};
public:
explicit View(QWidget *parent = {});
protected:
Q_SLOT void gotUpdates();
void mousePressEvent(QMouseEvent *event) override;
};
View::View(QWidget *parent) : QGraphicsView(parent)
{
centroid.hide();
centroid.setRotation(45.0);
centroid.setPen({Qt::blue});
centroid.setZValue(2);
scene.addItem(¢roid);
text.setPos(5, 470);
text.setZValue(1);
scene.addItem(&text);
setRenderHint(QPainter::Antialiasing);
setScene(&scene);
setSceneRect(0,0,500,500);
scene.setProperty(kNotifier, QVariant::fromValue(NotifierPointer(¬ifier)));
connect(¬ifier, &Notifier::notification, this, &View::gotUpdates);
connect(&scene, &QGraphicsScene::selectionChanged, ¬ifier, &Notifier::notification);
}
void View::gotUpdates()
{
if (scene.selectedItems().isEmpty()) {
centroid.hide();
return;
}
centroid.show();
QPointF centroid;
qreal area = {};
for (auto item : scene.selectedItems()) {
const QRectF r = item->boundingRect();
const qreal a = r.width() * r.height();
centroid += item->pos() * a;
area += a;
}
if (area > 0) centroid /= area;
auto st = QStringLiteral("delta #%1 with %2 items, centroid at %3, %4")
.arg(deltaCounter++).arg(scene.selectedItems().count())
.arg(centroid.x(), 0, 'f', 1).arg(centroid.y(), 0, 'f', 1);
this->centroid.setPos(centroid);
text.setText(st);
}
void View::mousePressEvent(QMouseEvent *event)
{
const auto center = mapToScene(event->pos());
if (! scene.itemAt(center, {})) scene.addItem(new Circle{center});
QGraphicsView::mousePressEvent(event);
}
int main(int argc, char *argv[])
{
QApplication app{argc, argv};
View v;
v.show();
return app.exec();
}
#include "main.moc"