Why does my attempt at connecting a pushbutton to a lambda fail? - c++

I have some issues while trying to use lambda expression to make connections between a pushbutton and a function I want to call when I click the button.
I am using Qt 5.6, with the compiler MinGW 4.9.2 (the default one). My code is the following :
In mainwindow.cpp :
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
initBuildings();
initPage();
for (int i(0); i<buildings.size(); ++i) {
connect(static_cast<QAbstractButton*>(widgetlist.at(i).at(2)), &QAbstractButton::clicked, [this, i]() {
buildings.at(i).buy(amountMultiplier);});
}
}
void MainWindow::initBuildings()
{
Building b1 = Building("Building 1",100,1,200);
Building b2 = Building("Building 2",1000,10,2000);
buildings.append(b1);
buildings.append(b2);
}
void MainWindow::initPage()
{
for (int i(0); i<buildings.size(); i++) {
QList<QWidget *> buttons;
QLabel *namelabel = new QLabel(buildings.at(i).getName());
QLabel *amountlabel = new QLabel;
QPushButton *buybutton = new QPushButton(this);
QPushButton *upgradebutton = new QPushButton(this);
amountlabel->setFixedSize(50,40);
buybutton->setFixedSize(100,40);
upgradebutton->setFixedSize(100,40);
buttons.append(namelabel);
buttons.append(amountlabel);
buttons.append(buybutton);
buttons.append(upgradebutton);
widgetlist.append(buttons);
}
}
In mainwindow.h :
#include <QMainWindow>
#include <QScrollArea>
#include <QList>
#include <building.h>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private:
void initBuildings();
void initPage();
Ui::MainWindow *ui;
int amountMultiplier;
QList<Building> buildings;
QList<QList<QWidget*>> widgetlist;
};
And "Building" is a class I have created which does not inherit from another class. The function I want to use is a public funtion of this class:
void buy(int amount) const;
It doesn't compile and I get several errors :
no matching function for call to 'MainWindow::connect(QAbstractButton*, void (QAbstractButton::*)(bool), MainWindow::MainWindow(QWidget*)::<lambda()>)
invalid use of incomplete type 'struct QtPrivate::QEnableIf< false, QMetaObject::Connection>
cannot convert '<lambda closure object>MainWindow::MainWindow(QWidget*)::< lambda()>{((MainWindow*)this), i}' (type 'MainWindow::MainWindow(QWidget*)::< lambda()>') to type 'const QObject*
I tried to change the lambda capture list, or to change the way I get the value in the lists but it doesn't change anything and I don't figure what is the problem. Maybe I am wrong in the use of the lambda ?

Two problems:
buildings.at() returns a const Building &, and the buy method is not const. You must index the buildings using [] instead.
The type returned from widgetlist.at(i).at(2) is definitely not QPushButton* - if it was, the code would compile. Even the error message indicates what the issue is:
no matching function for call to 'MainWindow::connect(QWidget* const&, void (QAbstractButton::*)(bool), [...])
This compiles:
// https://github.com/KubaO/stackoverflown/tree/master/questions/lambda-list-37615204
#include <QtWidgets>
struct Building {
void buy() {}
};
class Class : public QObject {
QList<Building> m_buildings;
QList<QList<QWidget*>> m_widgets;
public:
Class() {
for (int i = 0; i<m_buildings.size(); ++i)
connect(static_cast<QAbstractButton*>(m_widgets.at(i).at(2)), &QAbstractButton::clicked, [=] {
m_buildings[i].buy();
});
}
};
int main() {}
If you wish an extra measure of safety in face of programming errors on your part, replace the static_cast with a qobject_cast, it'll then abort if you cast a non-button instead of doing something possibly misleading.

According to the documentation, your lambda should accept a bool argument.

Related

Linking a QWidget to another QWidget new created

I am try to, by pressing a button in the main QWidget, to create a new QWidget. In that new created QWidget I want to have a button connected to a slot of the main QWidget.
class UI : public QWidget
{
public:
UI(){connection();};
private:
QPushButton* all = new QPushButton{ "ALL" };
void connection(){
QObject::connect(all,QPushButton::clicked,[](){
SmallGUI* s=new SmallGUI{};
s->show();
});
}
void something(){
//something
}
and the second class
class SmallGUI1 :
public QWidget
{
public:
SmallGUI(){connection();};
private:
QPushButton* N =new QPushButton;
void connection(){
//to connect N to something()
}
I want to connect N to something() .
Before we start, there are some other problems with you code.
Note that in your second class, the constructor is not named the same as the class, which will cause some... Problems.
You also forgot to put a parent for your buttons (which may thus cause some unexpected results) AND for your Widgets (which is again not a good idea).
So, that being said, let us get to the main topic.
I tend to only put prototypes and declare the attributes in the .h file to make the code clearer, but you may of course adapt it to your needs or to your own programming convention.
There are several ways to do something like this, but the simplest one should look like this :
SmallGUI1.h :
#include "UI.h" //The file in which the class UI is declared
//OR :
//class UI; //If you need to include this file in UI.h
class SmallGUI1 : public QWidget{
Q_OBJECT //Q_OBJECT macro, check the doc for more explainations about it
public:
explicit SmallGUI1(UI *parent = nullptr); //Explicit means that this constructor cannot be used for implicit casts
~SmallGUI1();//Destructor needed because I only put the constructor above
private:
QPushButton* N; //Not very good looking to initialize attributes in the .h in my opinion, but works fine.
}
SmallGUI1.cpp :
SmallGUI1::SmallGUI1(UI *parent) : QWidget(parent){
N = new QPushButton(tr("Some text on the button") , this); //tr to enable translation on this string
//************* If I understood your question correctly, this is what you are looking for *************
connect(N , &QPushButton::clicked , parent , &UI::doSomething); //Select the signal you want
/*
Some code here
*/
show();
}
SmallGUI1::~SmallGUI1(){qDeleteAll(children());}
UI.h :
class UI : public QWidget{
Q_OBJECT
public:
explicit UI(QWidget *parent = nullptr);
~UI();
private:
QPushButton* all;
private slots :
void createSmallGUI1();
void doSomething();
}
UI.cpp :
#include "SmallGUI1.h"
UI::UI(QWidget *parent) : QWidget(parent){
all = new QPushButton(tr("ALL") , this);
connect(all , &QPushButton::clicked , this , &UI::createSmallGUI1);
/*
Some code here
*/
}
UI::~UI(){qDeleteAll(children());}
void UI::createSmallGUI1(){SmallGUI1 *gui = new SmallGUI1(this);}
void UI::doSomething(){
/*
Clever code here
*/
}
You can define the second widget as a child of the main widget to make things easier:
class UI : public QWidget {
...
private:
SmallGUI* s;
...
and then initialize it in the UI constructor, along with your all button. You can initially hide the child widget or disable it:
UI() {
all = new QPushButton{"ALL", this};
setWindowTitle("UI"); // just for clarification
s = new SmallGUI(this);
s->hide();
connection();
};
and 'show' it with button clicked signal
connect(all, &QPushButton::clicked, s, &SmallGUI::show);
Doing so gives you the option to connect the clicked signal of your N button to the something function in the parent class
connect(s->N, &QPushButton::clicked, this, &UI::something);
The complete program would be as follows,
#include <QApplication>
#include <QMessageBox>
#include <QPushButton>
#include <QWidget>
class SmallGUI : public QWidget {
public:
SmallGUI(QWidget* parent) : QWidget(parent) {
N = new QPushButton{"btn2", this};
connection();
};
QPushButton* N;
private:
void connection(){};
};
class UI : public QWidget {
public:
UI() {
all = new QPushButton{"ALL", this};
setWindowTitle("UI"); // just for clarification
s = new SmallGUI(this);
s->hide();
connection();
};
private:
SmallGUI* s;
QPushButton* all;
void connection() {
connect(all, &QPushButton::clicked, s, &SmallGUI::show);
connect(s->N, &QPushButton::clicked, this, &UI::something);
}
void something() { QMessageBox::information(this, "Hello", "Hello"); }
};
int main(int argc, char* argv[]) {
QApplication a(argc, argv);
UI w;
w.show();
return a.exec();
}
It is not good idea to connect to parent's slots from "nested" class, since SmallGUI1 will be tied to class UI.
Here is better solution, I think:
class UI : public QWidget
{
public:
UI(){connection();};
private:
QPushButton* all = new QPushButton{ "ALL" };
void connection(){
QObject::connect(all,QPushButton::clicked,[](){
SmallGUI1* s=new SmallGUI1;
connect(s,&USmallGUI1::button_clicked,this,&UI::something);
s->show();
});
}
void something(){
//something
}
And SmallGUI1 class:
class SmallGUI1 :
public QWidget
{
public:
SmallGUI1(){connection();};
signals:
void button_clicked();
private:
QPushButton* N;
void connection(){
//to connect N to something()
N = new QPushButton;
connect(N,&QPushButton::clicked,this,&SmallGUI1::button_clicked)
}
This way, you are connecting QPusButton::clicked signal from SmallGUI1 to the signal SmallGUI1::button_clicked(). Dont need to implement additional slot, just connect signal to signal.
And in UI you are connecting button_clicked() signal to the slot dosomething()
DONT FORGET THE CONSTRUCTOR OF SmallGUI1! In your code, SmallGUI() will be just a method, which will not be called when SmallGUI1 is instantiated, and you have to call it by yourself.

component is not detecting in slot with new instantiation in Qt

application in Qt 5.2.1. here is interface :-
Here is goto cell dialog header file :-
#ifndef GOTOCELLDIALOG_H
#define GOTOCELLDIALOG_H
#include <QDialog>
#include <QtWidgets>
namespace Ui {
class gotocellDialog;
}
class gotocellDialog : public QDialog
{
Q_OBJECT
public:
explicit gotocellDialog(QWidget *parent = 0);
~gotocellDialog();
private slots:
void on_lineEditCellLocation_textChanged(const QString &arg1);
private:
Ui::gotocellDialog *ui;
};
#endif // GOTOCELLDIALOG_H
and here is gotocell.cpp
#include "gotocelldialog.h"
#include "ui_gotocelldialog.h"
gotocellDialog::gotocellDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::gotocellDialog)
{
ui->setupUi(this);
ui->pushButtonOK->setEnabled(false);
QRegExp regexp("[A-Za-z][1-9][0-9]{0,2}");
ui->lineEditCellLocation->setValidator(new QRegExpValidator(regexp, this));
}
gotocellDialog::~gotocellDialog()
{
delete ui;
}
void gotocellDialog::on_lineEditCellLocation_textChanged(const QString &arg1)
{ui->pushButtonOK->setEnabled(ui->lineEditCellLocation->hasAcceptableInput());
}
here another file mainwindow creating new gotocell object but unable to access its(gotocell's) component lineEditCellLocation
void spMainWindow::gotocell()
{
gotocelldlg = new gotocellDialog(this);
if(gotocelldlg->exec())
{
QString str = gotocelldlg->lineEditCellLocation->text.toUpper();
spsheet->setCurrentCell(str.mid(1).toInt() - 1,
str[0].unicode() - 'A');
}
}
here error is showing as :-
/opt/project/Qt/spreadsheet/spmainwindow.cpp:81: error: 'class gotocellDialog' has no member named 'lineEditCellLocation'
/opt/project/Qt/spreadsheet/spmainwindow.cpp:-1: In member function 'void spMainWindow::sort()':
here i want to access gotocelldialog's lineEditCellLocation but unable to get access.
I think, that this is just a small typo:
Replace the following line
QString str = gotocelldlg->lineEditCellLocation->text.toUpper();
with
QString str = ui->lineEditCellLocation->text.toUpper();
For your next posts you should include a minimal reproducible examples. This also includes the post of your ui file. Otherwise, it is very difficult for others to reproduce your problem.
finally i got its solution i did like this :-\
gotocelldlg = new gotocellDialog(this);
if(gotocelldlg->exec())
{
QLineEdit *ledit = findChild<QLineEdit*>("lineEditCellLocation");
QString str = ledit->text().toUpper();
spsheet->setCurrentCell(str.mid(1).toInt() - 1,
str[0].unicode() - 'A');
}

Does using C++ Lambda functions as slots in Qt help to preserve binary compatibility of a library?

I am concerned about making possible a library of widgets developed under Qt 5.9 to be upgraded in the future without having to recompile the code that already uses it. Of course I've started with the PImpl idiom and the Qt version of it described here and here.
However while trying to adapt my code, I came up with the idea, that instead of adding new data members and moving them to a separate private class, I could use the Qt's signal/slot mechanism with lambda functions and have only local variables. Let's illustrate the idea with the following example:
Variant A:
class Foo : public QWidget
{
Q_OBJECT
public:
explicit Foo(QWidget *parent = nullptr);
private:
// A bunch of data members
QPushButton *m_button;
QLineEdit *m_lineEdit;
QCheckBox *m_checkBox;
QString m_str;
private slots:
void on_pushButtonClicked();
void on_checkBoxStateChanged(int state);
};
Foo::Foo(QWidget *parent) :
QWidget(parent),
m_button(new QPushButton("Click me", this));
m_lineEdit(new QLineEdit(this)),
m_checkBox(new QCheckBox(this)),
m_str("Initial text")
{
connect(button, &QPushButton::clicked, this, &Foo::on_pushButtonClicked);
connect(checkBox, &QCheckBox::stateChanged, this, &Foo::on_checkBoxStateChanged);
}
Foo::on_pushButtonClicked()
{
m_str = m_lineEdit->text();
m_lineEdit->setDisabled(m_checkBox->isChecked());
}
Foo::on_checkBoxStateChanged(int state)
{
m_button->setText(state == Qt::Checked ? m_str : "Click me")
}
Variant B:
class Foo : public QWidget
{
Q_OBJECT
public:
explicit Foo(QWidget *parent = nullptr);
};
Foo::Foo(QWidget *parent) : QWidget(parent)
{
QPushButton *button = new QPushButton("Click me", this);
QLineEdit *lineEdit = new QLineEdit(this);
QCheckBox *checkBox = new QCheckBox(this);
QString str("Initial text");
connect(button, &QPushButton::clicked, [=](){
str = lineEdit->text();
lineEdit->setDisabled(checkBox->isChecked());
});
connect(checkBox, &QCheckBox::stateChanged, [=](int state){
button->setText(state == Qt::Checked ? str : "Click me")
});
}
So, for Variant B - apart from being more compact, it does not contain any class data members, so there are no variables to hide, hence no need for a D-pointer. The binary compatibility is still guaranteed though (or is it?), if in the future the constructor is reimplemented with additional local variables used in the same signal/slot manner. Am I right to think this will work or such an approach won't do the trick at all?
Note: For more info about using lambdas as slots in Qt check the comment by #Igor Tandetnik here.
I came up with the idea, that instead of adding new data members and moving them to a separate private class [...]
That's the wrong way to think about it. The interface has no data members. Whatever members you have, go directly into the PIMPL. You don't "move" anything, you don't add them in the wrong place to start with.
Furthermore, heap allocations of members that have the same lifetime as the parent object is a premature pessimization. Store them by value in the PIMPL.
[...] I could use the Qt's signal/slot mechanism with lambda functions and have only local variables
This won't work as soon as you need to store something more than QObject children without abusing the property system.
It's not a flexible approach, and it's really not hard to do it correctly. Qt establishes all the necessary patterns. See this question for some details.
Classes that you don't intend to derive from don't need separate Class_p.h headers. You can add the ClassPrivate definition to the beginning of the Class.cpp file itself.
// Foo.h
#include <QWidget>
class FooPrivate;
class Foo : public QWidget {
Q_OBJECT
Q_DECLARE_PRIVATE(Foo)
QScopedPointer<FooPrivate> const d_ptr;
public:
explicit Foo(QWidget *parent = {});
~Foo();
protected:
Foo(FooPrivate &, QWidget *parent = {}); // for expansion
};
// Bar.h
#include "Foo.h"
class BarPrivate;
class Bar : public Foo {
Q_OBJECT
Q_DECLARE_PRIVATE(Bar)
Q_PROPERTY(int data READ data)
public:
explicit Bar(QWidget *parent = {});
~Bar();
int data() const;
protected:
Bar(BarPrivate &, QWidget *parent = {}); // for expansion
};
// Foo_p.h
#include "Foo.h"
class FooPrivate {
Q_DECLARE_PUBLIC(Foo)
Q_DISABLE_COPY(Foo) // usually desired
Foo * const q_ptr;
public:
QVBoxLayout m_layout{q_ptr};
QPushButton m_button{q_ptr->tr("Hello!")};
QLineEdit m_lineEdit;
QCheckBox m_checkBox{q_ptr->tr("Active")};
void on_pushButtonClicked();
void on_checkBoxStateChanged(int state);
explicit FooPrivate(Foo *);
virtual ~FooPrivate() {} // we're meant to be derived from!
};
// Bar_p.h
#include "Foo_p.h"
#include "Bar.h"
class BarPrivate : public FooPrivate {
Q_DECLARE_PUBLIC(Bar)
public:
int m_data = 44;
explicit BarPrivate(Bar *);
};
// Foo.cpp
#include "Foo_p.h"
Foo::Foo(QWidget * parent) :
Foo(*new FooPrivate(this), parent)
{}
Foo::Foo(FooPrivate & d_ptr, QWidget * parent) :
QWidget(parent),
d_ptr(d_ptr)
{}
Foo::~Foo() {}
FooPrivate::FooPrivate(Foo * q_ptr) :
q_ptr(q_ptr)
{
m_layout.addWidget(&m_button);
m_layout.addWidget(&m_lineEdit);
m_layout.addWidget(&m_checkBox);
connect(&m_button, &QPushButton::clicked, [=]{ on_pushButtonClicked(); });
connect(&m_checkBox, &QCheckBox::stateChanged, [=](int s){ on_checkBoxStateChanged(s); });
}
// Bar.cpp
#include "Bar_p.h"
Bar::Bar(QWidget * parnet) :
Bar(*new BarPrivate(this), parent)
{}
Bar::Bar(BarPrivate & d_ptr, QWidget * parent) :
Foo(d_ptr, parent)
{}
Bar::~Bar() {}
BarPrivate::BarPrivate(Bar * q_ptr) :
FooPrivate(q_ptr)
{}
int Bar::data() const {
Q_D(const Bar);
return d->m_data;
}

Qt connect between two classes not working

I have two classes: HoodPlanner and DrawRoad. Both classes inherit from QWidget and the the UI so I can access all UI elements in both classes.
Here's a snipped of both classes:
HoodPlanner:
#ifndef HOODPLANNER_H
#define HOODPLANNER_H
#include <ui_hoodplanner.h>
#include "drawroad.h"
#include <QtWidgets>
class HoodPlanner : public QWidget, private Ui::HoodPlanner
{
Q_OBJECT
private:
// (...)
public:
explicit HoodPlanner(QWidget * = 0);
// (...)
private slots:
// (...)
};
#endif // HOODPLANNER_H
DrawRoad:
#ifndef DRAWROAD_H
#define DRAWROAD_H
#include <ui_hoodplanner.h>
#include <QtWidgets>
class DrawRoad : public QWidget, private Ui::HoodPlanner
{
Q_OBJECT
private:
// (...)
public:
explicit DrawRoad(QWidget *parent = 0);
public slots:
void drawSC4Street();
};
#endif // DRAWROAD_H
In HoodPlanner I have a connect that will be emitted whenever the itemSelection changes in table_ts2. I have the slot stored in DrawRoad, but inserting a reference to the DrawRoad object in third position brings up errors.
HoodPlanner::HoodPlanner(QWidget *parent) : QWidget(parent)
{
setupUi(this);
// (...)
DrawRoad *drawRoad = new DrawRoad;
connect(table_ts2, SIGNAL(itemSelectionChanged()), &drawRoad, SLOT(drawSC4Street()));
}
Snipped from DrawRoad.cpp:
#include "drawroad.h"
DrawRoad::DrawRoad(QWidget *parent) : QWidget(parent)
{
}
void DrawRoad::drawSC4Street()
{
if(!table_ts2->selectionModel()->hasSelection()) return;
// (...)
}
And the following errors occur whenever I run the app:
hoodplanner.cpp:12: Fehler: no matching function for call to 'HoodPlanner::connect(QTableWidget*&, const char [24], DrawRoad**, const char [17])'
mingw492_32\include\QtCore\qobject.h:213: Fehler: no type named 'Object' in 'struct QtPrivate::FunctionPointer<const char*>'
(and lots of notes)
The errors don't help me at all and I've searched in several topics for a solution, but none of them had my problem.
Have you tried
HoodPlanner::HoodPlanner(QWidget *parent) : QWidget(parent)
{
setupUi(this);
// (...)
DrawRoad *drawRoad = new DrawRoad;
connect(table_ts2, SIGNAL(itemSelectionChanged()), drawRoad, SLOT(drawSC4Street()));
}
You declrared drawRoad as a pointer:
DrawRoad *drawRoad = new DrawRoad;
And then you pass its address to connect() with &drawRoad.
In this case you are passing a pointer to a pointer address. Try without the &

Pass instance of class to a another constructor that adds its object to a list owned by passed instance

As the title says I want to create an object of class Note and add its pointer to a list of the object of class Traymenu. I am missing the whole thing I guess, please take a look on how I call the Note's constructor in traymenus's newNote and what I am doing in note.h.
traymenu.h:
#ifndef TRAYMENU_H
#define TRAYMENU_H
#include <QSystemTrayIcon>
#include <QIcon>
#include <QPixmap>
#include <QMenu> //in use for context menu
#include <QList>
#include "note.h"
class Traymenu : public QSystemTrayIcon
{
public:
Traymenu();
~Traymenu();
void createMainContextMenu();
void newNote(QWidget, Traymenu);
void exitProgram();
private:
QSystemTrayIcon mainIcon;
QMenu mainContextMenu;
QList<Note> notelist; //List that holds references to Note objects
//template argument 1 is invalid
};
#endif // TRAYMENU_H
traymenu.cpp:
#include "traymenu.h"
#include <QDebug>
Traymenu::Traymenu(){
mainIcon.setIcon(QIcon(QPixmap("C:\\program.png")));
mainIcon.setVisible(true);
mainIcon.show();
createMainContextMenu();
}
Traymenu::~Traymenu(){
}
void Traymenu::newNote(){
Note(Traymenu *this); //HOW TO PASS THE TRAYMENU INSTANC TO NOTE???
}
void Traymenu::exitProgram(){
delete this; //deletes traymenu object (icon disappears)
}
void Traymenu::createMainContextMenu(){
QAction *actionNewNote = mainContextMenu.addAction("Neue Notiz");
mainContextMenu.addSeparator();
QAction *actionExitProgram = mainContextMenu.addAction("Programm beenden");
actionNewNote->setIcon(QIcon("C:\\new.ico"));
actionNewNote->setIconVisibleInMenu(true);
//Qt5 new signal connection: http://qt-project.org/wiki/New_Signal_Slot_Syntax
QObject::connect(actionNewNote,&QAction::triggered,this,&Traymenu::newNote);
QObject::connect(actionExitProgram,&QAction::triggered,this,&Traymenu::exitProgram);
mainIcon.setContextMenu(&mainContextMenu);
}
note.h:
#ifndef NOTE_H
#define NOTE_H
#include <QWidget>
#include "traymenu.h"
namespace Ui{
class Note;
}
class Note : public QWidget
{
public:
Note(QWidget *parent = 0, Traymenu *trayMenuIn);
~Note();
void appendNoteToNotelist();
private:
Q_OBJECT
Ui::Note *ui;
Traymenu *pTraymenu = &trayMenuIn; //trayMenuIn was not declared in this scope
//Why declare a formal parameter?
};
#endif // NOTE_H
note.cpp:
#include "note.h"
#include "ui_note.h"
Note::Note(QWidget *parent, Traymenu *trayMenuIn) :
QWidget(parent),
ui(new Ui::Note)
{
ui->setupUi(this);
Note::appendNoteToNotelist();
}
Note::~Note()
{
delete ui;
}
void Note::appendNoteToNotelist(){
pTraymenu.append(&ui);
}
I list each problem followed by an illustrative snippet of your mistake.
QObjects are not copyable. You can't pass their instances anywhere. You can't store their instances in most containers. std::list is a notable example. QWidgets are QObjects, too. You can only pass QObjects by pointer, or by reference. To store QObjects in containers, you must store a smart pointer, e.g. std::unique_ptr or std::shared_ptr or QSharedPointer to an instance created on the heap.
void newNote(QWidget, Traymenu);
Calling delete this inside a method should be done with utmost care; except for special circumstances it's just wrong. If you're new to C++, the rule of thumb is: it's always wrong. If you want to delete an object instance that you're sure is on the heap, you can call deleteLater. It will perform the deletion once the control returns to the event loop.
The typical way of exiting an application is by calling QCoreApplication::quit().
QObject::connect(actionExitProgram,&QAction::triggered,this,&Traymenu::exitProgram);
Parameters with default values must come after parameters without default values.
Note(QWidget *parent = 0, Traymenu *trayMenuIn);
When you pass a parameter in a function/method call, you don't need to provide the types again.
Note(Traymenu *this);
When holding references to QObjects whose lifetime is not well controlled, you should use QPointer to avoid dangling pointer references. A QPointer resets itself to zero when the object is deleted elsewhere, thus giving you a clean crash (as opposed to undefined and possibly very misleading behavior).
Traymenu *pTraymenu
Initialization of class members should be done in default member initializers, and/or an initialization list. Your code below has nothing to do with formal parameters:
Traymenu *pTraymenu = &trayMenuIn;
After all those fixes, and some others, the code looks like below. You could compile it as a self-contained, single file - it works, although you never show the notes, so they remain invisible.
// https://github.com/KubaO/stackoverflown/tree/master/questions/note-tray-21753641
#include <QtWidgets>
#include <list>
// Note.h
namespace Ui{
class Note {
public:
void setupUi(QWidget *) {} // dummy for sscce.org
};
}
class TrayMenu;
class Note : public QWidget
{
Q_OBJECT
public:
Note(TrayMenu *trayMenu, QWidget *parent = {});
private:
Ui::Note m_ui;
QPointer<TrayMenu> m_traymenu;
};
// TrayMenu.h
class Note;
class TrayMenu : public QObject {
Q_OBJECT
public:
TrayMenu();
void createMainContextMenu();
void newNote();
private:
QSystemTrayIcon m_mainIcon;
QMenu m_mainContextMenu;
std::list<Note> m_notes;
};
// TrayMenu.cpp
template <int N> auto decode64(const char (&arg)[N], int rows) {
auto const raw = QByteArray::fromBase64(QByteArray::fromRawData(arg, N-1));
QImage img((const quint8 *)raw.data(), rows, rows, raw.size()/rows, QImage::Format_MonoLSB);
img = std::move(img).convertToFormat(QImage::Format_Indexed8);
img.setColor(1, qRgba(0, 0, 0, 0)); // make transparent
return img;
}
// convert baseline_language_black_18dp.png -flatten -negate -monochrome mono:-|base64 -b80
static const char language_d64[] =
"/////w//////D/////8P/z/A/w//DwD+D/8BAPwP/wEA+A9/IEbgDz8cjuEPPxyPww8fHg+HDw+PHw8P"
"DwAAAA8PAAAADw8AAAAPx8c/Pg7Hzz8+DsfHHz4Ox4c/Pg7Hxz8/DsfHPz4ODwAAAA4PAAAADw8AAAAP"
"H48fjw8fHg+HDz8cj4MPPxiH4Q9/IMbgD/8AAPAP/wMA/A//DwD/D/8/4P8P/////w//////D/////8P";
static const auto language_icon = decode64(language_d64, 36);
// convert baseline_note_add_black_18dp.png -flatten -negate -monochrome mono:-|base64 -b80
static const char note_add_d64[] =
"/////w//////D/////8PfwDA/w8/AMD/Dz8AAP8PPwAY/w8/ADD8Dz8AePwPPwDw8A8/APjhDz8A8OMP"
"PwDwxw8/AJDCDz8AAMAPPwAAwA8/AATADz8AD8APPwAGwA8/AA/ADz8ABsAPP/D/wA8/8P/ADz/w/8AP"
"PwAOwA8/AAfADz8ADsAPPwAGwA8/AAbADz8AAMAPPwAAwA8/AADAD38AAOAP/////w//////D/////8P";
static const auto note_add_icon = decode64(note_add_d64, 36);
TrayMenu::TrayMenu() {
m_mainIcon.setIcon(QPixmap::fromImage(language_icon));
m_mainIcon.setVisible(true);
m_mainIcon.show();
createMainContextMenu();
}
void TrayMenu::newNote() {
m_notes.emplace_back(this);
m_notes.back().show();
}
void TrayMenu::createMainContextMenu() {
auto *actionNewNote = m_mainContextMenu.addAction("Neue Notiz");
m_mainContextMenu.addSeparator();
auto *actionExitProgram = m_mainContextMenu.addAction("Programm beenden");
actionNewNote->setIcon(QPixmap::fromImage(note_add_icon));
actionNewNote->setIconVisibleInMenu(true);
QObject::connect(actionNewNote, &QAction::triggered, this, &TrayMenu::newNote);
QObject::connect(actionExitProgram, &QAction::triggered, QCoreApplication::quit);
m_mainIcon.setContextMenu(&m_mainContextMenu);
}
// Note.cpp
Note::Note(TrayMenu *trayMenu, QWidget *parent) :
QWidget(parent),
m_traymenu(trayMenu)
{
m_ui.setupUi(this);
}
// main.cpp
int main(int argc, char ** argv) {
QApplication app(argc, argv);
TrayMenu menu;
return app.exec();
}
#include "main.moc"