EDIT: Not related to signals/slots/connect. Problem was constructor calling constructor.
There might be a better way to do this - I'd be interested in hearing those...
I have MyClass that is derived from a QLabel. I want to pass more data about the derived class back in the signal than what the base signal does. So I made a slot to intercept the customContextMenuRequested signal and emit a revised one that has more data.
When I try to connect up this signal in the constructor, then my slot never gets called. But if I move the Policy and connect lines out to the parent widget(not class hierarchy parent) so they execute after MyClass is fully constructed, then my slot will get called. But I always want that to be connected for this class and it seems like something I would want in it's constructor rather than counting on the parent class to remember to do it.
Is there something I'm doing wrong? Or a better way to add data to a signal?
MyClass::MyClass() : QLabel()
{
QFont currFont = font();
currFont.setPointSize(15);
setFont(currFont);
setBackgroundRole(QPalette::Mid);
std::cout << "connecting customContextMenuRequested" << std::endl;
/** PROBLEM START */
setContextMenuPolicy(Qt::CustomContextMenu);
// Is there anything wrong with connecting from "this" to "this" in a constructor?
QObject::connect(this, SIGNAL(customContextMenuRequested(const QPoint&)),
this, SLOT(addCellDataToMenuContextRequest(const QPoint&)));
/* PROBLEM END **/
}
MyClass::MyClass(QString &cellString, int row, int col)
: QLabel(cellString)
{
MyClass();
setRow(row);
setCol(col);
}
// This one is a slot
void MyClass::addCellDataToMenuContextRequest(const QPoint& pos)
{
// This doesn't get printed if I connect in my constructor,
// but it does print if I do the same connect from a parent widget.
std::cout << "called addCellDataToMenuContextRequest" << std::endl;
emit customContextMenuRequestedForCell(pos, _row, _col);
}
So I would like the parent widget to just look for customContextMenuRequestedForCell but right now, the parent widget seems to need to be responsible for customContextMenuRequested as well.
Actually, you CAN call (sort of) another constructor if you are using C++11. It's called delegating constructor. But I don't think that will make the problem go away. Your issue seems to be that meta object is not fully constructed when connect() is called. Also, you'll probably need to move to Qt 5 for C++11 to work.
The solution is to delay the connection until the object is fully constructed. You can start a timer with an interval of zero. It will trigger on the next event loop processing which will certainly be after your object is fully constructed.
Then in your timerEvent, make the connection and kill the timer.
EDIT: Didn't see your edit. Looks like you find the solution. Ignore this then. :)
BTW. You didn't call another constructor. You created a temporary MyClass object.
A way to make this "cleaner" would be to reimplement QWidget::mouseReleaseEvent() within MyClass. The way to implement it would be, if the QMouseEvent type passed to mouseReleaseEvent is not a right-click mouse release, call QLabel::mouseReleaseEvent(event). If it is a right-click mouse release event, you can emit your custom signal. This gives the benefit of using the existing mouse button release handling code given by QLabel/QWidget, while allowing you to intercept the one case where you want to emit the custom signal.
EDIT Oh, and be sure to call event->accept() after your mouseReleaseEvent handles the custom case.
Related
I already finished the implementation I wanted, but I was wondering if this is the right way. I'm confused about the 'is-a' relationship in inheritance.
Background:
I wanted to put a QStackedWidget with four widget pages in the main window.
However, as I implemented it, I realized that I need common functions of pages, such as moving to the next or previous or first page, or moving to the first page when a timeout is triggered after a certain period of time. So I decided to create a parent widget from which my custom widget would inherit.
So, my code like:
// pageWidget.h
// this parent class has no UI
class pageWidget : public QWidget {
Q_OBJECT;
public:
pageWidget(int timeout, QWidget *parent = nullptr); // set the first parameter to the timer
signals:
void goNext();
void goPrev();
void goHome();
private:
// emit goHome() when this timer triggered, start in `showEvent()`, stop in `hideEvent()`
QTimer *timer;
}
// pageOne.h
// this child class has UI with some buttons
// and connected the clicked signal of the buttons to the signal such as `goNext()`.
class pageOne : public pageWidget{
Q_OBJECT;
}
// mainWindow.cpp
mainWindow::mainWindow(...)
{
...
connect(ui->page_1, &pageWidget::goNext, this, &mainWindow::onGoNext);
connect(ui->page_2, &pageWidget::goPrev, this, &mainWindow::onGoPrev);
// other signals such as `goPrev()` and other pages are connected same way.
}
void mainWindow::onGoPrev()
{
ui->stackedWidget->setCurrentIndex(ui->stackedWidget->currentIndex() - 1);
}
It works fine. However, when the goPrev() signal is received on the first page(index 0), currentIndex() - 1 is called (meaning the index is set to -1), but QStackedWidget ignores it, so nothing happens. But it doesn't make any sense, it's rather unnecessary behavior, so I shouldn't do it. Therefore, the goPrev() signal on the first page is not connected, and there is no button for it.
The same goes for the timer that returns to the first page when time elapses. If the time received as an parameter in the constructor of the parent class is 0, the timer is not started, and the first page passes 0.
So the child class(pageOne) 'can' work like the parent class(pageWiget) if desired, but it is also true that some features of the parent class are missing. Of course, just because I don't click a button doesn't mean it isn't a button. But if ​button that never clicks, it's a label or something else. And it feels like 'I can do this, but I should not' rather than 'I can do this but I don't use it'. It's like using an array to implement a set that doesn't allow duplicates.
So, Finally, here's my question :
Is this still an 'is-a' relationship? Is public inheritance the right way?
I think this was a stupid question. My thoughts after sleeping for a while is that the behavior of the pageWidget is until it emits a signal, and all page can behavior whether it's the first page or the last page. Whether to use the emitted signal or not is an entirely different matter. Next time I'll sleep a bit more and think again so I don't ask stupid questions.
A google search gives these as the top three results:
Qt: Connect inside constructor - Will slot be invoked before object is initialized?
Qt can I connect signals/slots to self in constructor?
QT Connect Signal Slot and initialize in constructor
According to those, it seems like it ought to "just work" like anything else. But this code doesn't:
EditorList::EditorList(..., QWidget* parent) :
QWidget(parent)
{
...
Processing* processing = Processing::getInstance();
connect(this, SIGNAL(reorderDelete(DataSequence*,ListType,QList<int>)), processing, SLOT(reorderDelete(DataSequence*,ListType,QList<int>)));
...
buttonDelete = new QPushButton(this);
connect(buttonDelete, SIGNAL(clicked(bool)), this, SLOT(buttonDeleteClick()));
...
}
...
void EditorList::buttonDeleteClick()
{
...
QList<int> locations;
...
emit reorderDelete(mySequence, myListType, locations); //breakpoint 1 here
}
//-----------------------------------------------------------------
void Processing::reorderDelete(DataSequence* sequence, ListType listType, QList<int> locations)
{
if(sequence) //breakpoint 2 here
{
sequence->reorderDelete(listType, locations);
}
}
The reason for this structure, instead of calling mySequence->reorderDelete directly, is to have it done in Processing's thread instead of the UI's. I hope I haven't stripped out too much detail to show the problem; this is a rather large project.
When I click my delete button, I hit breakpoint 1 (so far, so good), but I don't hit breakpoint 2. My other signals/slots work across threads, but their connects are not in constructors. I want to make this one automatic so that every instance is "just connected" without having to remember to do it. Can I not do that?
Okay, I got it. Leaving up for others to find.
According to this, my ListType enum was blocking the system from making the connection. It only works with system-known datatypes because emitting a SIGNAL actually stores a copy for the SLOT(s) to read later. I knew that, but I thought it was more like a stack frame that could take anything. Apparently not.
It also works to put a call to qRegisterMetaType<ListType>("ListType"); somewhere before the connect. (I put it in my main window's constructor.) This makes the datatype known so that the connection can work anyway.
I'm hitting both breakpoints now.
Make sure you have used Q_OBJECT macros in your class
I'm having some trouble, I'm fairly new to Qt and C++ and was testing the waters so to say. Ill try to describe my problem as follows. I have a LineEdit QLineEdit and this edit has a connection which looks like this:
connect(my_lineedit, SIGNAL (textEdited(QString)),this, SLOT (handleEdits()));
The handleEdits() method gets called and does the following:
Disconnect the previous Signal from my_lineedit
Create a new QLineEdit which gets a new signal and calls handleAddedEdits()
Adds the newely created Edit to my layout.
The stated above works fine Im just telling you this so you get the picture.
Now in the new method which I called handleAddedEdits() I want kinda the same procedure to happen again.
Disconnect the Signal which calls handleAddedEdits() from the Edit which invoked this method in the first place.
Create a fresh QLineEdit
Add this to my layout.
The problem is: in the first case my_lineedit is declared in my class so I can freely refer to it and and remove the signal as I wish. In the second case I have a QLineEdit which was created dynamically in the handleEdits() method and is the "Sender". My Question is, how can I refer to the "Sender Object" ro remove the Signal from the dynamically created edit?
You need to use QObject::sender() method in your receiver's slot:
For cases where you may require information on the sender of the
signal, Qt provides the QObject::sender() function, which returns a
pointer to the object that sent the signal.
handleAddedEdits()
{
QObject* obj = sender();
disconnect( obj, SIGNAL(textEdited(QString)), 0, 0 );
//...
}
In my project, I have 40 QPushButtons all put into a QButtonGroup like this:
QButtonGroup* group = new QButtonGroup(this);
group->addButton(ui->slot_0);
group->addButton(ui->slot_1);
//...
group->addButton(ui->slot_38);
group->addButton(ui->slot_39);
Each button is a QPushButton that I made checkable. That way only one button can be checked at a time. All works great, but how can I "make a slot" when one of the buttons becomes checked? I don't want to have 40 different slots, one for each button all to end up doing essentially the same thing. Is there any way I can just use the QButtonGroup I put them in?
As Jamin and Nikos stated: you should create your own slot to handle the signal emitted by QButtonGroup. It could be something like this:
In the header file:
public slots:
void buttonWasClicked(int);
In the *.cpp file:
void MainWindow::buttonWasClicked(int buttonID)
{
cout << "You have clicked button: " << buttonID << endl;
}
And in the code responsible for creation of the MainWindow (i.e. in constructor but not necessairly) there should be this line:
connect(group, SIGNAL(buttonClicked(int)), this, SLOT(buttonWasClicked(int)));
Be aware that since Qt5 the connect syntax has changed. The syntax I used here is from Qt4. It still works but is deprecated now (for more information please refer to New Signal Slot Syntax in Qt 5). Moreover I would suggest going through QButtonGroup class reference as there are other available signals which could suit your needs better than the one I've chosen.
BR
The documentation for QButtonGroup shows a QButtonGroup::buttonClicked() signal - have you already tried that one?
The signal comes in two variants - one that gives the QPushButton as a parameter (as a QAbstractButton), and one that gives the ID of the button in the group.
You can use connect() to setup signal and slot connections in your C++ code.
Sometime during the initialization of your window's class (perhaps in the constructor), call this:
connect(myButtonGroup, SIGNAL(buttonClicked(QAbstractButton*)), this, SLOT(theSlotThatYouWrite(QAbstractButton*));
Where myButtonGroup is probably this->ui->nameOfTheButtonGroup, and theSlotThatYouWrite is a function that you write in your own code, that belongs to your window's class, that returns void and takes a signal QAbstractButton* as a parameter (since that's what this specific signal gives as an argument).
Make sure theSlotThatYouWrite is under the label "private slots:" or "public slots:" in your class's interface.
Here's a screenshot of actual usage of some signals and slots in my own code.
Signals and Slots is something very important to learn, but can be bit of a hill to climb when first trying to understand it!
I have some dynamically added QWidgets and I want to carry out some task when they are changed.
I think I can't use connect() because I also need the name of the QWidget that triggered the change.
How can I also see which QWidget was changed and still catch the value change event with a common handler?
The quick-and-dirty way is to use connect() as usual, and then in your slot method, call sender() to find out which object sent the signal. For example:
// slot method that you've connected all of your widgets' stateChanged(int) signals to
void MyClass :: someWidgetsStateChanged(int newState)
{
const QObject * s = sender();
if (dynamic_cast<const QCheckBox *>(s) == _myFirstCheckbox) printf("First checkbox is now %s\n", newState?"Checked":"unchecked");
else if (dynamic_cast<const QCheckBox *>(s) == _mySecondCheckbox) printf("Second checkbox is now %s\n", newState?"Checked":"unchecked");
[... and so on...]
}
Note that the reason this is considered "dirty" is that it breaks encapsulation. In particular, the someWidgetsStateChanged() method above now behaves differently depending on which object generated the signal, and so if e.g. at some point in the future you connected a QPushButton::clicked() (or whatever) to that same slot, you'd probably have to update the someWidgetsStateChanged() implementation to handle it appropriately. Still, this works and doesn't require a lot of effort to implement.
Use this to catch events before they are passed to QObject subclass instances:
http://qt-project.org/doc/qt-4.8/qobject.html#installEventFilter
After some additional thinking I arrived at saying why not to extend these controllers?
So that I could hook them to the parent object using parent() or using a custom constructor.
It requires potentially though that I define them as friend classes...