Senders objectName is absent | QT & Cpp - c++

I have created a little UI with a simple toolbar. I am initing the toolbar within the initToolbarfunction. This function is within a Class inheriting from QMainWindow.
void Main_Frame::initToolBar() {
rectangle.setText(rectangle_text);
circle.setText(circle_text);
triangle.setText(triangle_text);
triangle.setObjectName(triangle_id);
circle.setObjectName(circle_id);
rectangle.setObjectName(rectangle_id);
toolBar.addAction(&triangle);
toolBar.addAction(&circle);
toolBar.addAction(&rectangle);
connect(
&toolBar, &QToolBar::actionTriggered, this, [=]() {
process_toolbar_ac_evt(*sender());
});
}
I want any tool bar events to be processed through process_toolbar_ac_ect(QObject obj). Within that method, I want to decide what action (within the toolbar) has been triggered.I want to do this by the objectname. Therefore I have given any action an object name. But when I call sender().objectName() I get an empty QString.
My suggestion is, that sender returns a pointer to on of my actions that I put to the toolbar. If this is the case, why I get an empty QString on the sender()?
void Main_Frame::process_toolbar_ac_evt(QObject &evt) {
if (evt.objectName() == circle_id) {
// If circle action has clicked, to this ...
}
}

As you are connecting to one of QToolBar's signals the sender() will be your tool bar object.
Simply use the QAction argument that is passed in the QToolBar::actionTriggered signal. That's what it is for.
NB: Avoid QObject::sender() wherever possible. It virtually breaks the desired decoupling achieved by signals and slots.

Related

How to add hyperlinks in Qt without QLabel?

I have some labels and layouts nested inside a QWidget to build a part of a sidebar. Each QWidget is its own section and one component currently looks like this:
To my understanding, you can only set hyperlinks with QLabel, but I'm trying to get the whole area between the white lines clickable. This is including the icon and the whitespace. Is there any way to achieve this?
This got marked as a duplicate to the opposite of what I was asking, so I'd like to reiterate that I'm trying to implement a hyperlink without QLabel.
You can easily have a widget open a link on click:
class Link : public QWidget {
Q_OBJECT
public:
Link(QUrl url, QWidget p = nullptr) : QWidget(p), _url(url) {}
QUrl _url;
void mouseReleaseEvent(QMouseEvent *) { QDesktopServices::openUrl(_url); }
}
You can avoid any extra signals and connections, and have each link widget store its own link internally, the url can be set on construction and changed at any time. Not using signals and slots makes it easier to change the link too, without having to disconnect previous connections.
IMO going for a signals and slots solution is only justified when you want different arbitrary behavior. In this case you always want the same - to open a particular link, so you might as well hardcode that and go for an easier and more computationally efficient solution.
I would just manually catch the SIGNAL for clicked() and use desktop services to open the url in code.
bool QDesktopServices::openUrl ( const QUrl & url ) [static]
Opens the given url in the appropriate Web browser for the user's desktop environment, and returns true if successful; otherwise returns false.
http://doc.qt.io/qt-4.8/signalsandslots.html
Using this type of syntax, or in the designer, you can also connect a signal to a slot.
connect(widgetThatRepresentsURL, SIGNAL(clicked()),
handlerThatWillOpenTheURL, SLOT(clicked_on_url()));
For widgets that don't have a signal set up for clicked (or whatever event you are interested in), you can subclass the widget in question and reimplement...
void QWidget::mousePressEvent ( QMouseEvent * event ) [virtual protected]
Specifically for creating a signal, there is emit. I've used this in the past like the following
void Cell::focusInEvent(QFocusEvent *e)
{
emit focus(this, true);
QLineEdit::focusInEvent(e);
}
with the following in the header
signals:
void focus(Cell *, bool);

Qt C++ : How to get event target object?

There is a container Widget instance A and there are contained Widget instances C1, C2, C3, ... etc within A.
There is a slot that handles Widget A's action_triggreed() signal.
Is there a way to determine which of the target Widgets C1, C2, C3, ... was clicked?
The reason for this necessity is that there are numerous contained widgets, and it doesn't make sense to use connect() method on each, which would require 50+ lines of extra connect statements, one for each!
For example: Consider a QToolBox with lots of Buttons. How would you determine which Button is pressed by using a QToolBox action_triggered or similar signals, without using signals and slots for individual Buttons separately?
You can use the QObject::sender() function.
void A::triggeredSlot() {
QObject* obj = sender();
QButton* but = qobject_cast<QButton*>(obj);
}
Just use the correct types etc.
There are a few general approaches.
The first one is straightforward - you should connect each widget's signal to the slot and check what sender() is. Yes, lines of code for connecting.
The second one is to use a kind of mapper. Every widget is connected to the mapper and only mapper is connected to the target slot. Lines of code again, at least in the part where you connect widgets to the mapper. There is a generic QSignalMapper or you can use more specific one suited for your widgets. For example, if they are buttons then you can use QButtonGroup class. Every button is registered in the group and only one signal/slot connection is required.
QButtonGroup group;
group->addButton(buttonC1,C1_ID);
...
group->addButton(buttonC1,Cn_ID);
connect(&group,SIGNAL(buttonClicked(QAbstractButton*),this,SLOT(buttonClicked(QAbstractButton*));
The third approach is to detect mouse event only on the mother's widget A and then iterate over all its children and find which one is under mouse. Less code, you can easily add new widgets, but the cost is iterating over all widgets in runtime. Below is an example. Note, that you can add specific QObject names or properties to the widgets C1... so that you could filter them if you are interested only in a part of children widgets of the given type.
void mousePressEvent(QMouseEvent* event)
{
if (event->button()==Qt::LeftButton)
{
QList<QToolButton*> buttons=findChildren<QToolButton*>(); // you can also use specific object names on the widgets under your interest
foreach (QToolButton* button, buttons)
{
if (button->underMouse()) // you could try isDown() for button, but I'm not sure if that will work here
{
emit buttonClicked(button);
break;
}
}
}
}
Well, you could try another approach also. Detect the mouse event, get cursor position and find the child widget on that position. Not costly in runtime.
void mousePressEvent(QMouseEvent* event)
{
if (event->button()==Qt::LeftButton)
{
QPoint pt=mapFromGlobal(QCursor::pos());
QWidget* child=childAt(pt);
if (child)
{
emit childClicked(child);
}
}
}

Transfer Data Between Two Children Using Qt Signals and Slots (C++)

In Qt C++, I have a parent object MainWindow. MainWindow has (among others) 2 member objects: vector, and Serial.
Some relevant members of GUI:
QPushButton pushButton;
int itemNumber;
Relevant members of Serial:
void send(int number)
When GUI's member pushButton is clicked, I want to pass GUI's member 'pushButton' as the argument to the function Serial::send(int number). I see one option of doing something like
connect(GUI,SIGNAL(clicked()),this,SLOT(customSlot()));
in my MainWindow function. However, then I don't know which item in my vector it came from, so I don't know which itemNumber to grab (they're all different).
I also thought maybe I'd do it in the GUI class, but that doesn't have access to the Serial::send(int number) method. I don't want a Serial object in each GUI, because it just won't work that way at this point.
How can I pass information from the child object GUI up to the parent object MainWindow from a signal?
Thanks!
Use QObject::sender() function inside a slot to get the object witch sent a signal.
Of course, you need some appropriate cast method:
void someSlot() {
QPushButton *sender = qobject_cast<QPushButton *>(sender());
...
}

How can I catch the change of a child Widget in Qt?

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...

Qt can I connect signals/slots to self in constructor?

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.