What should my function be a member of in Qt? - c++

I'm making a simple GUI with Qt Creator.
I'm trying to make an 'openFile' function in the mainwindow.cpp file, but I don't know whether to make this function a member of the mainwindow itself, a separate static function, or a member of a widget.
This function is simple really, it just calls a QFileDialog, gets the path to a file, and opens it, like every open file command on the menu bar you've ever seen.
Now here's the major problem I am having. I tried making this function a member of mainwindow so that I can access ui elements from within the function with ui->QTableWidget for instance, but I can't properly connect the signal to the slot if I do that. I get a whole slew of errors, because in order to do that I need a reference to the mainwindow since connect has the syntax connect(sender, signal, receiver, slot)
I then resorted to making the function separate on its own, but then it has no access to any of the ui elements.
How should I resolve this problem?
EDIT
Here is what I am doing in the constructor.
//Constructor
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
QObject::connect(ui->actionOpen, &QAction::triggered, *parent, &MainWindow::openFile);
ui->tableWidget->setColumnCount(16);
}
Here is the resulting error:
C:\workspace\audioPipe\audioPipe\mainwindow.cpp:76: error: C2664: 'QMetaObject::Connection QObject::connect<void(__cdecl QAction::* )(bool),void(__cdecl MainWindow::* )(void)>(const QAction *,Func1,const MainWindow *,Func2,Qt::ConnectionType)' : cannot convert parameter 3 from 'QWidget' to 'const MainWindow *'
with
[
Func1=void (__cdecl QAction::* )(bool),
Func2=void (__cdecl MainWindow::* )(void)
]
No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called

The compiler is telling you that as a third parameter to connect(), it expects a pointer to a MainWindow, i.e. a const MainWindow *. You however pass *parent, which is of type QWidget. Furthermore, parent is the wrong object anyway, since that is the parent of the MainWindow object, however you want to pass the MainWindow object itself - and that is this.
So something like the following should work:
QObject::connect(ui->actionOpen, &QAction::triggered, this, &MainWindow::openFile);

Related

Qt connect doesn't recognize with lambda expression

I'm designed a QTableWidget with QPushButton, I would like to connect these buttons with a slot to hide some rows.
I'm using a lambda expression to pass a number of a row. But the compiler doesn't recognized this expression :
connect(this->ui->tableWidget->cellWidget(i,0),&QPushButton::clicked,[this,i]{hideRows(i);});
I have this error:
error: no matching function for call to 'SoftwareUdpater::MainWidget::connect(QWidget*, void (QAbstractButton::*)(bool), SoftwareUdpater::MainWidget::displayTable()::<lambda(int)>)'
The function hideRows(int) is declared as a function. And, as a slot, it doesn't work,
CONFIG += c++11 is added in pro file,
My class MainWidget inherits from QWidget,
Q_OBJECT is added in the header.
So I don't udnerstand why connect() is not recognized by Qt 5.9.1 MinGw 32bit.
Edit: [this,i]() instead of [this](const int i) for the lambda expression
Your connection is wrong. You can't connect a function that doesn't take parameters (clicked()) with a function that takes parameters (your lambda). To verify that this is the case, just do this:
connect(this->ui->tableWidget->cellWidget(i,0),&QPushButton::clicked,[this](){});
And see that it will compile. You have to make your design in such a way that signals and slots are compatible.
Also avoid using lambdas in signals and slots. Read the caveats here.
I was reading your comments on the accepted answer and noticed the root of the problem: This error is being thrown because the effective type of the object — as supplied to QObject::connect; i.e QWidget in your case — does not define the referenced signal QPushButton::clicked.
What likely happened is that the QPushButton pointer was cast into a QWidget and then that pointer was given to connect instead of the original which defines the signal.
Cast the pointer back to a QPushButton * and the error should go away.

Qt::WA_DeleteOnClose

I am learning Qt and trying some examples in the book "Foundations of Qt Development".
In the book, there is a section teaching Single Document Interface with an example creating a simple app like a notepad.
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
setAttribute(Qt::WA_DeleteOnClose);
setWindowTitle(QString("%1[*] - %2").arg("unnamed").arg("SDI"));
connect(ui->docWidget->document(), SIGNAL(modificationChanged(bool)), this, SLOT(setWindowModified(bool)));
createActions();
createMenu();
createToolbars();
statusBar()->showMessage("Done");
}
The book said that "Setting the windows attribute to Qt::WA_DeleteOnClose so that Qt takes care of deleting the window from memory as soon as it is closed.
How it works?
Because if i use setAttribute(Qt::WA_DeleteOnClose);, when I end the program, there is a Debug Assertion Failed warning:_BLOCK_TYPEIS_VALID(pHead->nBlockUse).
There is no problem if the setAttribute is removed.
Qt takes care of the deletion by itself if you set all the parenting right (if you create a new QObject/QWidget set the parent in the constructor). If the parent will be destructed, then the children will be too. In your main file, you can create the mainwindow on the stack, such that it will be destructed at the end of scope.
To call addToolbar you don't need this->, since it is a method of the class anyway.
The toolbar ptr should be a member to access it easily later on. But initialize it with nullptr (or NULL if you don't have c++11) in the initialization list of the constructor to know whether it is initialized or not.
The addToolBar call should work. A workaround would be to create a QToolBar on your own and add the pointer to the MainWindow using another addToolBar overload.

error C2664: 'App *const' to 'QWidget *'

I'm trying to put an animated gif into my program.
However, when I follow the proper syntax
QMovie *hit1=new QMovie("BadExplosion.gif");
QLabel *processLabel=new QLabel(this);
processLabel->setMovie(hit1);
hit1->start();
in the
void TestApp::draw()
{
//this code and other drawing code here
}
I run into the error
error C2664: 'QLabel::QLabel(QWidget *, Qt::WindowFlags)' : Cannot convert parameter 1 from 'TestApp *const' to 'QWidget *' on the line
QLabel *processLabel=new QLabel(this);
Any ideas? Thanks!
EDIT: TestApp is a custom class.
If TestApp is a custom class, then it's perfectly normal that this code doesn't work.
Every UI element of Qt may take a parameter at construction, which is the QWidget that will act as a parent. This parent will notably have the responsibility for handling its children deletion. You should read more about this in the Qt Documentation (esp. the doc for QWidget constructor).
In your case, you shouldn't pass this to the QLabel constructor. You must pass another widget that'll become this QLabel parent.
The compiler shows clearly this problem with the message you got. It waits for a QWidget, but got your class instead (which doesn't inherit QWidget at any point).

C++ Qt Derived Classes

I'm trying to learn Qt and C++ and having some trouble understanding the C++ this keywork. I've seen examples where a class is derived from QMainWindow and then within the class member functions, a QMenu is added. One example is the "Simple menu" program described on this page:
http://www.zetcode.com/gui/qt4/menusandtoolbars/
In that example, a quit action is created with
QAction *quit = new QAction("&Quit", this);
However, imagine I want to also derive a class from QMenu and use that to create my menu.
mymenu.h
class MainWindow; // forward declaration
class MyMenu : QMenuBar
{
public:
MyMenu(MainWindow *main_window);
};
mymenu.cpp
#include "mymenu.hpp"
MyMenu::MyMenu(MainWindow *main_window) : QMenuBar()
{
QAction *quit = new QAction("&Quit", main_window); // Notice here I replaced
// 'this' with 'main_window'
QMenu = *file;
file = menuBar()->addMenu("&File");
file->addAction(quit);
connect(quit, SIGNAL(triggered()), qApp, SLOT(quit()));
}
Unfortunately this doesn't work because QAction expects a QObject as a parent. All that being said, there are a couple things that don't make sense to me:
If the class MainWindow inherits from QMainWindow, doesn't that make 'MainWindow' a QObject?
What is the difference between passing 'this' to QAction from within the class MainWindow, as opposed to passing 'main_window' which is (as far as I can tell) also a pointer to the instance from within the MyMenu class?
I apologize for such a long winded question, but if any of you have made it to the end with me, I would love any suggestions as to what I am missing here. The end goal here is just to create a derived class of QMenu (MyMenu here) and add it to the QMainWindow derived class (MainWindow here) existing in a separate class. Thank you for your time.
If the class MainWindow inherits from QMainWindow, doesn't that make 'MainWindow' a QObject?
Yes, MainWindow is a QMainWindow which is a QObject (you can see this by browsing the inheritance tree on the API docs).
You have only forward declared MainWindow. Since the compiler does not have a definition for the class MainWindow it can only do miminal things with a pointer to MainWindow. In order for the compiler to "know" that MainWindow is a QMainWindow which is a QObject, you must provide a class definition for MainWindow. You can solve your compiler error with:
#include "MainWindow.h"
No dynamic cast is needed
Also, in Qt land to make something "really" a QObject you should put the Q_OBJECT macro on the object:
class MyMenu : QMenuBar
{
Q_OBJECT
public:
MyMenu(MainWindow *main_window);
};
It might save you a few headaches later on if you ever plan to use the object for signal/slots or other Qt stuff.
What is the difference between passing 'this' to QAction from within the class MainWindow, as opposed to passing 'main_window' which
is (as far as I can tell) also a pointer to the instance from within
the MyMenu class?
this is a pointer to your custom MyMenu class which is also a QMenuBar. main_window is a pointer to your custom MainMenu class which is also a a QMainMenu. So, two different objects in memory. The second argument of the QAction constructor takes a pointer to a parent widget. The parent widget is responsible for managing the memory of its children. Since it takes a QObject its reasonable to pass in either this or main_menu.
Also you should probably pass a parent to the QMenu constructor.
MyMenu::MyMenu(MainWindow *main_window) : QMenuBar(main_window)
This way MyMenu is correctly deleted when the MainWindow is deleted.
The usual Qt paradigm is:
MyMenu::MyMenu(<arg1>, <arg2>, ... QObject * parent) : QMenuBar(parent)
But in this case forwarding along main_window is good enough.

Qt Removing a QGraphicsItem from QGraphicsScene with singleShot Timer?

While it is possible to automatically remove a QGraphicsTextItem from a scene using a timer and Qt's signal-slot mechanism like
QTimer::singleShot(1000, QGraphicsTextItem*, SLOT(deleteLater()));
other graphical objects (QGraphicsItem, QGraphicsEllipseItem) seem to not inherit QObject and as such cause an error when compiling:
error: C2664: 'QTimer::singleShot': Konvertierung des Parameters 2 von
'QGraphicsEllipseItem *' in 'QObject *' nicht m”glich
(conversion / cast of parameter 2 ... not possible)
Since I would like text and some graphics shown together for a limited time, my question is:
How I can achieve automatic, timed removal of the above-mentioned 'other' objects?
QGraphicsItems don't inherit QObject normally. You would need to subclass QGraphicsEllipseItem like so:
class AutoHidingItem : public QObject, public QGraphicsEllipseItem
{
Q_OBJECT
// ...
}
Or you would simply have to have your scene keep track of the items to hide, and then hide it when it needs to. (Make a slot in your subclassed scene or view that hides or deletes the item.)
EDIT: #thuga pointed out that QGraphicsEllipseItem doesn't inherit QObject, while QGraphicsTextItem does already. Edited answer to show this.
Hope that helps.