Qt custom SLOT of subclass not recognised by CONNECT - c++

Background
I am writing a media player application in Qt. I have subclassed QMediaPlayer and created a new SLOT which is capable of interpreting an int and passing it as qint64 to QMediaPlayer::SetPosition(qint64).
QMediaPlayer::PositionChanged fires a signal to the cslider slider_playback (a horizontal slider subclass). This makes the slider move as a song is playedback. There are also some subclassed labels (clabel) which receive signals regarding the song duration and song playback position.
Problem
The problem i have occurs when I build & run, I receive the following error:
Starting /home/daniel/DeveloperProjects/build-Player-Desktop_Qt_5_9_1_GCC_64bit-Debug/Player...
QObject::connect: No such slot QMediaPlayer::set_playback_position(int) in ../Player/mainwindow.cpp:23
QObject::connect: (sender name: 'slider_playback')
The slider should be able to control the position of the playback. The offending line of code is preceeded by a '// Player seek' comment in the file.
I think the error indicates that the base class is being referred to for the SLOT, whereas the slots is actually part ofthe derived class.
Why is this error occuring and what action would resolve the issue? The slots in my clabel and cslider classes work without issue. The difference is that those classes have constructors and destructors. I have not implemented a constructor in the QMediaPlayer subclass as I do not want to override the base class constructor.
cmediaplayer.h (full file)
#ifndef CMEDIAPLAYER_H
#define CMEDIAPLAYER_H
#include <QMediaPlayer>
//#include <QObject>
class cmediaplayer : public QMediaPlayer
{
Q_OBJECT
public slots:
void set_playback_position(int);
};
#endif // CMEDIAPLAYER_H
cmediaplayer.cpp (full file)
#include "cmediaplayer.h"
void cmediaplayer::set_playback_position(int v) {
this->setPosition( (qint64)v );
}
mainwindow.h (full file)
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QMediaPlayer>
#include "cmediaplayer.h"
#include "clabel.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private:
Ui::MainWindow *ui;
QPalette m_pal;
QString media_file_str="/usr/share/example-content/Ubuntu_Free_Culture_Showcase/Jenyfa Duncan - Australia.ogg";
//QMediaPlayer * player ;
cmediaplayer * player; // My custom type
private slots:
void on_pushButton_pressed();
void on_pushButton_released();
void on_button_playback_clicked();
};
mainwindow.cpp (full file)
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow) {
ui->setupUi(this);
//player = new QMediaPlayer;
player = new cmediaplayer; // My custom type
// set max time on playback slider
connect(player, SIGNAL(durationChanged(qint64)), ui->slider_playback, SLOT(set_qint64_max(qint64)));
// st max time on playback label
connect(player, SIGNAL(durationChanged(qint64)), ui->label_track_length, SLOT(setIntText(qint64)));
// set slider playback position
connect(player, SIGNAL(positionChanged(qint64)), ui->label_track_position, SLOT(setIntText(qint64)));
// Player seek
connect(ui->slider_playback,
SIGNAL(valueChanged(int)),
player,
SLOT(set_playback_position(int)));
// Player volume
connect(ui->slider_volume,SIGNAL(valueChanged(int)),player,SLOT(setVolume(int)));
ui->slider_volume->setValue(50); // set player initial value
}
MainWindow::~MainWindow() {
delete ui;
delete player;
//delete playlist;
}
void MainWindow::on_pushButton_pressed() {
m_pal=this->palette().color(QPalette::Background);
QPalette pal=palette();
pal.setColor(QPalette::Background,Qt::gray);
this->setAutoFillBackground(true);
this->setPalette(pal);
player->setMedia(QUrl::fromLocalFile(media_file_str));
player->setPlaybackRate(1);
player->play();
}
void MainWindow::on_pushButton_released() {
QPalette pal=m_pal;
this->setAutoFillBackground(true);
this->setPalette(pal);
//player->stop();
}
void MainWindow::on_button_playback_clicked()
{
//player->play();
}

Qt creates a new class that implements the actual connection between the slots and the signals but many times this one is not updated. The classes they refer to have a name similar to moc_xxx.cpp and these are created in the build folder.
To force them to update we must click on the make clean submenu that is located in the Menu Build of QtCreator, and then run qmake in the same menu.
Or you can manually remove the build folder and compile it back

Perhaps, it could help somebody. I had a similar issue when trying to use a SLOT function in the connect call. Even though the slot was declared and defined in the subclass, the connect function not recognize it, issuing a message which says that the slot function is not part of the base class.
In my case, the problem was that I didn't place the Q_OBJECT in the beginning of the class declaration. After that, I had to clean (otherwise, various errors occur in compilation step) and build the project again to had the slot function working properly.

Related

Qt connect signals and slots of different windows/ mirror a lineEdit text on two windows

I'm new to any form of programming but have to do a project with Qt for my "programming for engineers" course where we simultaneously learn the basics of c++.
I have to display a text from one lineEdit to a lineEdit in another window.
I have a userWindow that opens from the mainWindow and in this userWindow I have a lineEdit widget that displays the current selected user as a QString (from a QDir object with .dirName() ). But now I have to display the same String in a lineEdit in the mainWindow as well.
From what I've read I have to do this with "connect(...)" which I have done before with widgets inside a single .cpp file but now I need to connect a ui object and signal from one window to another and I'm struggling.
My idea /what I could find in the internet was this:
userWindow.cpp
#include "userwindow.h"
#include "ui_userwindow.h"
#include "mainwindow.h"
#include <QDir>
#include <QMessageBox>
#include <QFileDialog>
#include <QFileInfo>
QDir workingUser; //this is the current selected user. I tried defining it in userWindow.h but that wouldn't work how I needed it to but that's a different issue
userWindow::userWindow(QWidget *parent) : //konstruktor
QDialog(parent),
ui(new Ui::userWindow)
{
ui->setupUi(this);
QObject::connect(ui->outLineEdit, SIGNAL(textChanged()), mainWindow, SLOT(changeText(workingUser) //I get the error " 'mainWIndow' does not refer to a value "
}
[...]
mainWindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QDir>
#include <QMainWindow>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
public slots:
void changeText(QDir user); //this is the declaration of my custom SLOT (so the relevant bit)
private slots:
void on_userButton_clicked();
void on_settingsButton_clicked();
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "userwindow.h"
#include "settingswindow.h"
#include "click_test_target.h"
#include "random_number_generator.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
[...]
}
[...]
//here I define the slot
void MainWindow::changeText(QDir user)
{
QString current = user.dirName();
ui->userLine->insert("The current working directory is: "); //"userLine" is the lineEdit I want to write the text to
ui->userLine->insert(current);
}
I know I'm doing something wrong with the object for the SLOT but can't figure out how to do it correctly.
If anyone could help me I would be very grateful.
Alternatively: perhaps there is another way to mirror the text from one lineEdit to another over multiple windows. If anybody could share a way to do this I would be equally grateful. Is there maybe a way to somehow define the variable "QDir workingUser;" in such a way as to be able to access and overwrite it in all of my .cpp files?
Thank you in advance for any help. Regards,
Alexander M.
"I get the error 'mainWindow' does not refer to a value"
I don't see you having any "mainWindow" named variable anywhere,
but you also mentioned that the MainWindow is the parent, which means you could get reference anytime, like:
MainWindow *mainWindow = qobject_cast<MainWindow *>(this->parent());
Also, your signal-handler (changeText(...) slot) should take QString as parameter (instead of QDir), this way you handle how exactly the conversion is handled, in case users type some random text in input-field (text-edit).
void changeText(const QString &input);
Finally, you either need to specify type:
QObject::connect(ui->outLineEdit, SIGNAL(textChanged(QString)), mainWindow, SLOT(changeText(QString));
Or, use the new Qt-5 syntax:
connect(ui->outLineEdit, &QLineEdit::textChanged,
mainWindow, &MainWindow::changeText);
You can create new signal (same params as lineEdit's textChanged) to userWindow which is connected to the textChanged signal of the lineEdit. Then connect that signal userWindow to mainWindow's slot.
//In userWindow.h
signals:
void textChanged(const QString&);
//In userWindow.cpp
connect(ui.lineEdit, &QLineEdit::textChanged, this, &userWindow::textChanged);
//In mainWindow.cpp
connect(userWindow, &userWindow::textChanged, this, &mainWindow::onTextChanged
Then in onTextChanged write the same text to mainWindow's lineEdit

Initializing a Ui pointer From a QMainWindow class to a QDialog Class

I'm really stuck on one problem that I want to solve. the problem is that I have a Class for QMainWindow which holds the Ui variable for that form. Now I want to be able to edit that Form using the Ui variable in that class on a QDialog cpp file. I probably sound really stupid and I really have no idea how I should explain this, but I have code which maybe can help.
MainWindow.h:
#include "ui_mainwindow.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
protected:
Ui::MainWindow *ui;
}
MainWindow.cpp:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "dialog.h"
Dialog *dialog;
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_pushButton_clicked()
{
dialog = new Dialog(this);
dialog->show();
}
QDialog.cpp:
#include "ui_mainwindow.h"
#include "mainwindow.h"
#include "dialog.h"
#include "ui_dialog.h"
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
}
Dialog::~Dialog()
{
delete ui;
}
Ui::MainWindow *mainui;
void Dialog::on_pushbutton_clicked(){
mainui->label->setText("test");
}
So as you can see from the above code, it shows that I have a pointer to the Ui variable however its uninitialised, therefore it would lead to a SIGSEGV error, so how to do Initialize this pointer? any help here is highly appreciated, and even though this is probably really simple I just don't know what to do. (I have looked at other questions but I couldn't quite grasp what to do, so please explain what I am to do before linking me to a similar question. Also, I have left out the Dialog.h file as I didn't think it was needed, please tell me if I need to show it, thanks!).
Generally in C++ you should practice what is called encapsulation - keep data inside a class hidden from others that don't need to know about it. It's not good to have multiple pointers to the UI object as now all those other objects have to know how the main window UI is implemented.
In this case, what I would recommend is to use Qt's signals and slots mechanism to allow the dialog to tell the main window what you need it to do. That has the advantage that if you add more dialogs, or change how things are implemented in the main window, you don't need to alter the signal slot mechanism, and the details are hidden cleanly.
So - for your dialog, add a signal like this in the header file
class Dialog : QDialog
{
Q_OBJECT
signals:
void setTextSignal(QString text);
}
and in your main window header, add a slot.
class MainWindow : public QMainWindow
{
Q_OBJECT
public slots:
void setTextSlot(const QString &text);
}
now in your method where the button is pressed,
void Dialog::on_pushbutton_clicked()
{
emit setTextSignal("test");
}
and in your main window
void MainWindow::setTextSlot(const QString &text)
{
mainUi->label->setText(text);
}
The final part is to connect the signal and slot together, which you would do in your main window function where you create the dialog:
void MainWindow::on_pushButton_clicked()
{
dialog = new Dialog(this);
connect(dialog, SIGNAL(setTextSignal(QString)), this, SLOT(setTextSlot(QString)));
dialog->show();
}
You can see there are many advantages to this; the Dialog no longer needs a pointer to the main window UI, and it makes your code much more flexible (you can have other objects connected to the signals and slots as well).
Short answere - your can't! If you want to create a new instance of the ui, you would have to do:
MainWindow::Ui *ui = new MainWindow::UI();
ui->setupUi(this);
However, the this-pointer for a UI created for a QMainWindow based class must inherit QMainWindow - thus, you can't.
In general, it is possible if you create your Ui based on a QWidget instead of a QMainWindow, since both inherit QWidget.
Alternativly, you could try the following:
QMainWindow *subWindow = new QMainWindow(this);
subWindow->setWindowFlags(Qt::Widget);
MainWindow::Ui *ui = new MainWindow::UI();
ui->setupUi(subWindow );
//... add the mainwindow as a widget to some layout
But I would guess the result will look weird and may not even work in the first place.

qt qslider not smooth

I have a qslider to control the zooming of a map like this:
connect(ui->zoomSlider, SIGNAL(valueChanged(int)), ui->map, SLOT(SetZoom(int)));
However, because this online-map response relatively slow.
I found that the qslider's response also becomes very slow which means when you slide the slider, it's position won't change until suddenly it jump to the position where you release your mouse.
How could I solve this?
One possible solution to delay processing of your signal is to connect it with slot by using Qt::QueuedConnection.
connect(ui->zoomSlider, SIGNAL(valueChanged(int)), ui->map, SLOT(SetZoom(int)), Qt::QueuedConnection);
With Qt::QueuedConnection emitted valueChanged signal event will be not processed at the time of generation, as it happens with directly connected signals. Event will be added to the event loop queue. This is how Qt::QueuedConnection is implemented inside Qt.
Specially for Nejat to test this approach it's possible to use following code:
MainWindow.h:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
void signalReceived();
void signalReceivedQueued();
void buttonPressed();
signals:
void directConnectedSignal();
void queuedConnectedSignal();
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
MainWindow.cpp:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
connect(this, SIGNAL(directConnectedSignal()), SLOT(signalReceived()), Qt::DirectConnection);
connect(this, SIGNAL(queuedConnectedSignal()), SLOT(signalReceivedQueued()), Qt::QueuedConnection);
connect(ui->pushButton, SIGNAL(pressed()), SLOT(buttonPressed()));
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::signalReceived()
{
qDebug() << "signalReceived";
}
void MainWindow::signalReceivedQueued()
{
qDebug() << "signalReceivedQueued";
}
void MainWindow::buttonPressed()
{
emit queuedConnectedSignal();
emit directConnectedSignal();
}
If you run code snippet above you will get following output on button press:
signalReceived
signalReceivedQueued
Queued signal is emitted first, but received last. And this can be used in your case to prioritize processing of emitted signals.
However most of all using of queued connection will not help you, because user emits slider event too frequently and UI will freeze in any case. So, I can suggest following:
Determine why exactly UI is freezes, what part of code freezing it.
Try to avoid freezing by asynchronous calls or by moving logic into separate thread, or by using QtConcurrent
If you really can't control the way how map is scaled in your webpage, try to ignore all events generated by the QSlider and react only on last generated in 500 ms interval, for example.

Qt Creator C++: Storing lineEdit inputs in array

This might come off as a very stupid question, but I have to write a very simple program, consisting of a line edit and push button.
This program has to be able to take inputs(numbers) from the user in the line edit area and every time the push button is pressed, store the number values within an array of size 10.
I have some experience with C++ but am just generally very confused with the GUI aspects of Qt. The GUI stuff is just way over my head so I apologize again if this is trivial! But if anyone could help me figure out how to do this you would be the coolest.
Thanks!
Things need to do
set a button and a lineedit either in ui or in constructor of mywindows.
set validator for int.
declare a slot for button, to pass the int in lineEdit to your data structure.
fixed array is not enough to store all the user input, you need to think about whether you need is a list, a dynamic array, a fixed array of stack or queue.
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QList>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
void on_pushButton_clicked();
private:
Ui::MainWindow *ui;
QList<int> m_list;
};
#endif // MAINWINDOW_H
mainwindows.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
ui->lineEdit->setValidator(new QIntValidator(0, 9999, this));
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_pushButton_clicked()
{
m_list.append(ui->lineEdit->displayText().toInt());
qDebug() << m_list;
}
Put lineEdit and button on form in Qt Designer or create it by yourself:
Create slot in header:
private slots:
void mySlot();
//do connection in dialog's constructor
connect(ui->pushButton, SIGNAL(clicked()), this, SLOT(mySlot()));
//Write slot
void Dialog::mySlot()
{
QString str = ui->lineEdit->text()
//now str contains all your text you can put it in different arrays.
}
text() return QString, but if you sure that it is double, then call toDouble() method
QString str = ui->lineEdit->text().replace(",",".")
double var = str.toDouble();
Moreover you can set QDoubleValidator to lineEdit and user can input only double in this lineEdit. Search in web what validator is and use setValidator() method to set it to lineEdit.

Qt window wont close using "this->close()" from other class

I will start off by explaining my main goal. I have a main window with 7 buttons on it(amongst other things), when you hit each button, it closes out the current window and opens up a new window. All the windows will have the same 7 buttons, so you can go between each window. With all windows having the exact same 7 buttons, I wanted to set up a function that each class can call to set up each button and connect to a slot() in my mainwindow.cpp(called setupSubsystemButtons in example below). However, I can't seem to get the window to close using the standard "this->close()"...it works when I go from the main window to another window(the main window closes) but when I go from a different window to say the home window, the different window doesn't close. Suggestions would be greatly appreciated. My guess is that my understanding of "this" when it comes to calling slots in another class is wrong.
mainwindow.cpp( the parts that are relevant)
void MainWindow::ECSgeneralScreen()
{
ECSgeneralCommand *ECSgeneral = new ECSgeneralCommand;
this->close();
ECSgeneral->show();
//opens up the ECS screen
}
void MainWindow::homeScreen()
{
MainWindow *home = new MainWindow;
this->close();
home->show();
//opens up the ECS screen
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::setupSubsystemButtons(QGridLayout *layout)
{
//Push Button Layout
homeScreenButton = new QPushButton("Home");
layout->addWidget(homeScreenButton, 3, 11);
connect(homeScreenButton, SIGNAL(clicked()), this, SLOT(homeScreen()));
ECSgeneralScreenButton = new QPushButton("General");
layout->addWidget(ECSgeneralScreenButton,5,11);
connect(ECSgeneralScreenButton, SIGNAL(clicked()), this, SLOT(ECSgeneralScreen()));
}
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QtWidgets>
#include <QDialog>
namespace Ui {
class MainWindow;
}
class MainWindow : public QDialog
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
QWidget *window;
void setupSubsystemButtons(QGridLayout *layout);
~MainWindow();
private slots:
public slots:
void ECSgeneralScreen();
void homeScreen();
};
#endif // MAINWINDOW_H
ecsgeneralcommandWindow
include "ecsgeneralcommand.h"
#include "mainwindow.h"
#include <QtWidgets>
#include <QtCore>
ECSgeneralCommand::ECSgeneralCommand(MainWindow *parent) : QDialog(parent)
{
QGridLayout *layout = new QGridLayout;
QWidget::setFixedHeight(600);
QWidget::setFixedWidth(650);
...
//Setup Subsystem Buttons
test.setupSubsystemButtons(layout);
setLayout(layout);
}
ecsgeneralcommandWindow header
#ifndef ECSGENERALCOMMAND_H
#define ECSGENERALCOMMAND_H
#include <QDialog>
#include <QMainWindow>
#include <QtWidgets>
#include <QObject>
#include "mainwindow.h"
class ECSgeneralCommand : public QDialog
{
Q_OBJECT
public:
explicit ECSgeneralCommand(MainWindow *parent = 0);
private:
MainWindow test;
public slots:
};
#endif // ECSGENERALCOMMAND_H
Slots are just normal functions. When Qt invokes a slot, it ends up calling the appropriate receiver's method. In other words, this equals to the value of the 3rd argument of your connect statements. You passed this there, so the receiver is MainWindow object. E.g. MainWindow::homeScreen method always tries to close MainWindow. If it is already hidden, this action takes no effect.
You should either have a slot in each window class and connect buttons to appropriate receivers, or use a pointer to the currently active window instead of this when calling close(). But your architecture is strange in the first place. Why would you need to create these buttons for each window? It is reasonable to create them once and use in all windows. Also hiding and showing windows is not necessary. You can create one main window with buttons and a QStackedWidget that will contain the content of all other windows. Maybe you can even use QTabWidget instead of these buttons.