qt, signal slots not connecting? - c++

I have a qdialog, with a buttonbox at the bottom; Why isn't this slot not getting fired when a "signal" occurs? The code look like the following:
std::unique_ptr<MW::GenStd> box(new MW::GenStd(&tOut, &tIn));
box->ui.ChoiceButtons->addButton ("Ask",
QDialogButtonBox::AcceptRole );
box->ui.ChoiceButtons->addButton ("OverWrite",
QDialogButtonBox::AcceptRole );
box->ui.ChoiceButtons->addButton ("merge",
QDialogButtonBox::AcceptRole );
box->ui.ChoiceButtons->addButton ("Skip",
QDialogButtonBox::RejectRole );
QObject::connect(box->ui.ChoiceButtons, SIGNAL(clicked(QPushButton* b)), box.get(), SLOT(OnClick(QPushButton* b)));
return box->exec();
Where MW::GenStd is a dialog box (and ui.ChoicButtons a buttonbox). The modal dialog is correctly displayed - however it doesn't seem to interact at all.. Pressing the buttons doesn't fire the event. The slot is declared like the following:
public slots:
void OnClick(QPushButton* b) {
auto s(b->text());
if (s == "Merge") {
setResult(2);
} else if (s == "Overwrite") {
setResult(1);
} else if (s == "Skip") {
setResult(0);
} else if (s == "Ask") {
setResult(3);
}
}
};
(I know it's terribly to do such a string comparison here, but it's just as a quick mock up test to validate the buttons). But debugging shows the function isn't ever reached!
EDIT: as suggested looking at the output showed a culprit:
Object::connect: No such signal QDialogButtonBox::clicked(QPushButton*) in AskGUISupport.cpp:150
However that seems totally strange as the QDialogButtonBox does have a clicked signal? documentation

Do not use variable names in connect:
QObject::connect(box->ui.ChoiceButtons, SIGNAL(clicked(QPushButton*)),
box.get(), SLOT(OnClick(QPushButton*)));

QDialogButtonBox has a signal clicked ( QAbstractButton * button ) so you need to define a slot void OnClick(QAbstractButton* b) and connect to it. Use QAbstractButton, not QPushButton.

QDialogButtonBox class does not has signal
clicked(QPushButton*).
It has clicked ( QAbstractButton*) insted.
You should be very precise in signatures, when using signals/slots mechanisms. Any casts not allowed, because Qt uses strings internally to check signatures.
You should use clicked (QAbstractButton*) signature and adjust your slot to accpet QAbstractButton*. Make a slot
void OnClick(QAbstractButton* b);

Related

Mouse right click option using eventFilter in Qt

I have QGraphicsView, which has many QGraphicsItem. I am trying to create a right click menu on these QGraphicsItem. Right click menu has multiple options. But only 1st option works. It means, if I click on 2nd option, it does not work. If I change the sequence ( means 1st one will go to 2nd position, and 2nd one will come to 1st position ) then still 2nd one will not work.
bool myClass::eventFilter(QObject *watched, QEvent *event)
{
switch(event->type())
{
case QEvent::ContextMenu:
{
foreach(QGraphicsItem* pItem, _scene->items())
{
if(pItem->isUnderMouse())
{
QMouseEvent *mouseEvent = static_cast<QMouseEvent*> (event);
menu = new QMenu(this);
myMenu = menu->addMenu("Copy");
myMenu ->addAction(Name);
myMenu ->addAction(Address);
if(Name == menu->exec(mouseEvent->globalPos()))
{
// logic
}
if(Address == menu->exec(mouseEvent->globalPos()))
{
// logic
}
}
}
}
}
Always works only 1st mouse right click option. Why is so ?
The usual way to do something like this is to override the QGraphicsItem::mouseReleaseEvent() or QGraphicsItem::mousePressEvent() function of your item class.
This way, you won't have to do anything (no looping, etc...), it is already handled by the event loop.
Here you can find a simple example:
void MyItem::mouseReleaseEvent(QGraphicsSceneMouseEvent * event)
{
if(event->button() == Qt::RightButton)
{
QMenu my_menu;
// Build your QMenu the way you want
my_menu.addAction(my_first_action);
my_menu.addAction(my_second_action);
//...
my_menu.exec(event->globalPos());
}
}
From the Qt documentation:
Note that all signals are emitted as usual. If you connect a QAction to a slot and call the menu's exec(), you get the result both via the signal-slot connection and in the return value of exec().
You just need to QObject::connect() the QActions you added to the context menu to the proper slots (here goes the "logic") and the job is done.
If you prefer to check the returned value by yourself, you just have to get the returned QAction* once and for all (only one call to QMenu::exec()) and branch on it.
For example:
void MyItem::mouseReleaseEvent(QGraphicsSceneMouseEvent * event)
{
if(event->button() == Qt::RightButton)
{
QMenu my_menu;
// Build your QMenu the way you want
my_menu.addAction(my_first_action);
my_menu.addAction(my_second_action);
//...
QAction * triggered = my_menu.exec(event->globalPos());
if(triggered == my_first_action)
{
// Do something
}
else if(triggered == my_second_action)
{
// Do some other thing
}
//...
}
}
I would personnally prefer to stick with the signal-slot connections instead that manually handling the returned value, especially since each QAction is most likely to be already connected to its corresponding slot.

How to dynamically change tabs sizes in Qt?

I'm a student working on my first Qt program and I need some help. I'm developing a musical instruments simulator. I have a mainwindow and 2 classes: DrumsWindow and KeysWindow simulating drums and keys and having different sizes (drums are 798x532, keys are 953x306). I have created a tabWidget in the main window and inserted my DrumsWindow and KeysWindow into it:
ui->tabWidget->insertTab(0, &dw, "Drums");
ui->tabWidget->insertTab(1, &kw, "Synth");
if (ui->tabWidget->currentIndex() == 0){
this->resize(798, 532);
ui->tabWidget->resize(798, 532);
}
if (ui->tabWidget->currentIndex() == 1){
this->resize(953, 306);
ui->tabWidget->resize(953, 306);
}
This code is from the MainWindow constructor. It works, there are two tabs in the main window showing drums and keys. However, those "if" statements make only the first opened tab of the proper size. When I click on the Synth tab, window size remains the same (while I need it to be changed). So, this is what I made to solve the problem. First, I created new slots in the MainWindow class:
void MainWindow::drumsTabClicked()
{
ui->tabWidget->setCurrentIndex(0);
this->resize(798, 532);
ui->tabWidget->resize(798, 532);
}
void MainWindow::keysTabClicked()
{
ui->tabWidget->setCurrentIndex(1);
this->resize(953, 306);
ui->tabWidget->resize(953, 306);
}
And then, I connected them to signals:
connect(ui->tabWidget, SIGNAL(tabBarClicked(0)), this, SLOT(drumsTabClicked()));
connect(ui->tabWidget, SIGNAL(tabBarClicked(1)), this, SLOT(keysTabClicked()));
But still, it doesn't work. Could you, please, explain how to resize the main window when the user clicks on a tab?
You should connect it like this:
connect(ui->tabWidget, SIGNAL(tabBarClicked(int)), this, SLOT(onTabBarClicked(int)));
Then do your stuff in this slot and seperate them with if:
void MainWindow::tabBarClicked(int index)
{
if(!index)
{
this->resize(798, 532);
ui->tabWidget->resize(798, 532);
}
else
{
this->resize(953, 306);
ui->tabWidget->resize(953, 306);
}
}
Also you can create this connection automatically with right click on UI form tab bar and select go to slot tabBarClicked.
Thank you guys, you helped me a lot. Here's what I did.
mainwindow.h: added a slot
private slots:
void onTabBarClicked(int);
mainwindow.cpp: added the realization of the slot
void MainWindow::onTabBarClicked(int index){
if (index == 0){
this->resize(798, 532);
ui->tabWidget->resize(798, 532);
}
if (index == 1){
this->resize(953, 306);
ui->tabWidget->resize(953, 306);
}
}
And connected the slot and the signal using Qt5 syntax:
connect(ui->tabWidget, &QTabWidget::tabBarClicked, this, &MainWindow::onTabBarClicked);
So, now it works!

Processing mouse wheel events in Qt4.8

In Qt5 it can be done in QML like this.
MouseArea {
onWheel: {
if (wheel.modifiers & Qt.ControlModifier) {
adjustZoom(wheel.angleDelta.y / 120);
}
}
}
How to achieve the same in Qt4.8?
Looks like I'm reinventing the bicycle but here is how it works for now.
I have a new QWidget that catches the wheelEvent and sends a new signal.
void WheelEventCarrier::wheelEvent(QWheelEvent *event)
{
emit sendWheelEvent(event->delta()/120);
}
I define a new signal in QML and process it ibid.
signal wheelEvent(int delta)
onWheelEvent:
{
if(delta > 0)
tag_meas_mod.zoomIn(true);
else
tag_meas_mod.zoomOut(true);
}
I wrap my widget around all my UI and connect the two signals.
WheelEventCarrier carrier;
UI.setParent(&carrier);
QObject::connect(&carrier,
SIGNAL(sendWheelEvent(int)),
viewer.rootObject(),
SIGNAL(wheelEvent(int)));
carrier.show();
Hope you can point me to a better solution.

How to dynamically retranslate all widgets in application through a menu?

I am working on a Qt project consisting in a QMainWindow and multiple Qt and non-Qt classes. Many of them use QStrings with tr() that are translated with Qt Linguist. The language change (QTranslator load & install/QTranslator load & remove) is triggered by QActions in the app's menu.
I have read the official Qt documentation concerning dynamic translation, and it basically suggests the following overload:
void MainWindow::changeEvent(QEvent *event)
{
if (event->type() == QEvent::LanguageChange) {
titleLabel->setText(tr("Document Title"));
... // all my tr() QStrings here
okPushButton->setText(tr("&OK"));
} else
QWidget::changeEvent(event);
}
The problem I am facing is that the QStrings to translate are many (58 in QMainWindow alone), and several are filled at runtime as well, through user interaction; e. g. myFunction(a,b) below is called through a QPushButton:
void MainWindow::myFunction(MyClassA a, MyClassB b)
{
...
if(b.myCondition() == 0)
{
...
// below is the problem
myLabel->setText(myLabel->text() + QString("\n" + a->getName() + tr(" gagne ") + exp + tr(" points d'expérience")));
}
else
{
myLabel->setText(QString(tr("something else")));
}
...
}
So I hardly see how I can include this type of QString in the changeEvent() method above. What about the classes outside of MainWindow, which also have QStrings to translate but are not QWidget (so no changeEvent overload possible) ?
I have read that there's another way to use this method, with a UI form:
void MainWindow::changeEvent(QEvent* event)
{
if (event->type() == QEvent::LanguageChange)
{
ui.retranslateUi(this);
}
...
}
But this involves that I am using a UI form in my project, which I am not doing (all widgets are created in code).
I tried to export my MainWindow in a UI form, but when I try to include the generated header into the project, I get the following error:
ui_fenetreprincipale.h:32: error: qmainwindowlayout.h: No such file or directory
Thank you by advance for any suggestion to select the best way to translate my application.
Organize your code so that all the setting of translatable strings is done in one method in each class.
eg give every class that has translatable strings a setTrs() method that actually sets the strings.
class A
{
void setTrs()
{
okPushButton->setText(tr("&OK"));
}
}
//--------------
class B
{
int _trCond;
void myFunction(MyClassA a, MyClassB b)
{
_trCond = b.myCondition();
setTrs();
}
void setTrs()
{
if(_trCond == 0)
myLabel->setText(myLabel->text() + QString("\n" + a->getName() + tr(" gagne ") + exp + tr(" points d'expérience")));
else
myLabel->setText(QString(tr("something else")));
}
Then whenever the language for the application changes (eg connect to a menu entry selection, or MainWindow::event() or however the required language can change), you have to manually call the setTrs method of each of these objects
eg
void MainWindow::changeEvent(QEvent *event)
{
if (event->type() == QEvent::LanguageChange)
{
setTrs();
objA.setTrs();
objB.setTrs();
}
}
Even more elegant would be to store the objects in a QList and just iterate through it calling the setTrs method on each element in turn
I had the same problem, menus not translating, fixed completely by creating and installing the QTranslator...
QScopedPointer<QApplication> app(new QApplication(argc, argv));
QTranslator myappTranslator;
myappTranslator.load(QString("Languages/de"))
app->installTranslator(&myappTranslator);
...before creating and showing the main window...
MainWindow *mainWin;
mainWin = new MainWindow(&splash);
mainWin->show();

How to get the released button inside MouseReleaseEvent in Qt

In MouseReleaseEvent(QMouseEvent *e), is there a way to know which button was released without using a new variable ? I mean something like in the MousePressEvent(QMouseEvent *e) with e.buttons().
I tried e.buttons() in the releaseEvent it's not working (which is logical).
e is already a variable. Just use:
void mouseReleaseEvent(QMouseEvent *e)
{
if (e->button() == Qt::LeftButton) // Left button...
{
// Do something related to the left button
}
else if (e->button() == Qt::RightButton) // Right button...
{
// Do something related to the right button
}
else if (e->button() == Qt::MidButton) // Middle button...
{
// Do something related to the middle button
}
}
A switch statement also works. I prefer the series of if -- else if because they make it easier to handle evente modifiers, i.e., e->modifiers() in order to check for alt or control clicks. The series of if's is short enough not to create any burden on the program.
EDIT: Note that you should use the button() function, not its plural buttons() version. See the explanation in #Merlin069 answer.
The problem in the posted code is this: -
if(e->buttons() & Qt::LeftButton)
As the Qt documentation states for the release event: -
... For mouse release events this excludes the button that caused the event.
The buttons() function will return the current state of the buttons, so since this is a release event, the code will return false, as it's no longer pressed.
However, the documentation for the button() function states:-
Returns the button that caused the event.
So you can use the button() function here.