I have 2 classes: first of them is inheritor of QWidget and the second one is inheritor of the first class. When i launch my program i get 2 windows of first class. Why? And another question. Second::Second(QWidget *pwgt): First(pwgt) - is this string correct? i.e. should i send pwgt to constructor of the first class?
firstclass.h
#ifndef FIRSTCLASS_H
#define FIRSTCLASS_H
#include <QtGui>
class First: public QWidget
{
Q_OBJECT
protected:
QLabel *firstText;
QPushButton *firstButton;
public:
First(QWidget *pwgt = 0);
};
#endif // FIRSTCLASS_H
firstclass.cpp
#include "firstclass.h"
First::First(QWidget *pwgt)
{
firstText = new QLabel("First Class Text");
firstButton = new QPushButton("First Class Button");
QVBoxLayout *lay = new QVBoxLayout;
lay->addWidget(firstText);
lay->addWidget(firstButton);
this->setLayout(lay);
}
secondclass.h
#ifndef SECONDCLASS_H
#define SECONDCLASS_H
#include <QtGui>
#include "firstclass.h"
class Second: public First
{
Q_OBJECT
private:
QLabel *secondText;
QPushButton *secondButton;
public:
Second(QWidget *pwgt = 0);
public slots:
void changeText();
};
#endif // SECONDCLASS_H
secondclass.cpp
#include "secondclass.h"
Second::Second(QWidget *pwgt): First(pwgt)
{
secondText = new QLabel("Second Class Text");
secondButton = new QPushButton("Second Class Button");
QVBoxLayout *lay = new QVBoxLayout;
lay->addWidget(secondText);
lay->addWidget(secondButton);
connect(secondButton, SIGNAL(clicked()), this, SLOT(changeText()));
this->setLayout(lay);
}
void Second::changeText()
{
firstText->setText("From second class");
}
main.cpp
#include "firstclass.h"
#include "secondclass.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
First first;
Second second;
first.show();
second.show();
return a.exec();
}
The problem is that you set the layout with setLayout(lay); in the First class' constructor. This is correct, especially if you create objects of the First class. However when you create objects of the Second class, the First class constructor still called. In the following code:
First first; // Calls the First class constructor
Second second; // Calls both First and Second class constructors
As Qt documentation states that
If there already is a layout manager installed on this widget, QWidget won't let you install another.
This means that once you set the layout in the First class constructor, the attempt to set it again in the Second class ignored. As a result you see the first layout in both cases.
WRT your second question: yes, usually you have to pass the argument to the base class constructor. You even had to pass the parent to the QWidget class in your First class constructor:
First::First(QWidget *pwgt) : QWidget(pwgt) {}
You call setLayout twice on your second widget - once in the First constructor and then again in the Second constructor. But the second one will be ignored: http://qt-project.org/doc/qt-4.8/qwidget.html#setLayout
Why are you inheriting like this anyway, if they're supposed to look completely different.
Related
I basically just want to use multiple derived classes to change a member variable of a base class and to forward that value to qml using qproperty, but emitting the signal isn't working and I am not able to make the signal static
car.h
#include <QObject>
class Car : public QObject{
Q_OBJECT
Q_PROPERTY(int seats MEMBER m_seats NOTIFY updateSeats)
public:
explicit Car(QObject *parent = 0);
~Car();
static int m_seats;
signals:
void updateSeats();
};
car.cpp
#include "car.h"
Car::Car(QObject *parent) :
QObject(parent)
{
}
int Car::m_seats = 0;
Car::~Car(){}
toyota.h
#include "car.h"
class Toyota : public Car{
Q_OBJECT
public:
explicit Toyota(QObject *parent = 0);
~Toyota();
Q_INVOKABLE void foundCar();
};
toyota.cpp
#include "toyota.h"
Toyota::Toyota(QObject *parent)
{
}
Toyota::foundCar(){
m_seats = 4;
emit updateSeats(); // This isn't working
}
Toyota::~Toyota(){}
main.cpp
#include <QGuiApplication>
#include "car.h"
#include "toyota.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
Car car = new Car();
Toyota ty = new Toyota();
QQmlApplicationEngine engine;
QQmlContext *ctxt = engine.rootContext();
ctxt->setContextProperty("car", car);
ctxt->setContextProperty("toyota", ty);
engine.load(QUrl(QLatin1String("qrc:/main.qml")));
app.exec();
engine.quit();
}
In qml when I print car.seats after invoking the foundCar function
main.qml
toyota.foundCar()
console.log(car.seats)
the output is still 0. The reason the signal being emitted is not updating is probably because it is from a different object (Toyota). So how can I emit the signal of the base class from the derived class?
Your toyota and car properties are two separate ones.
You have to read the seats property from toyota:
main.qml
toyota.foundCar()
console.log(toyota.seats) //<--- here
Update after comment:
Ok, that's a different approach, but in that case I would set the car property to the toyota pointer:
main.cpp
Car car = new Toyota();
ctxt->setContextProperty("car", ty);
ctxt->setContextProperty("toyota", ty);
This probably can be fitted in a overarching class (something like car_manager or car_store) in which you have a list of available cars and a function to select one car as the current, then you update the generic car or current property of that overarching class.
I say this because you will get nasty code when you want to work from the root context and furthermore, root properties don't signal that they are changed
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"
Issue Resolved: Q_OBJECT macro was necessary and proper signal slot declarations are also important for any other handles.
I am unable to focus on any input type widgets like QTextEdit,QListWidget etc.
Note: There are no compile time or runtime errors.
Update: QSplitter is working properly! I have a QListWidget, whose items I click but they are highlighted only when I make the next move with the splitter.
I have a MainWindow class derived from QMainWindow as declared in main_window.h:
class MainWindow : public QMainWindow{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
//some other members like menu and statusbar here
}
I have another class called Stack derived from QWidget defined in stack.h:
class Stack: public QWidget{
public:
Stack(QWidget *parent=0);
//some other members
}
Constructor of Stack as in stack.cpp :
Stack::Stack(QWidget *parent):QWidget(parent){
main = new QHBoxLayout;
handle = new QSplitter;
setupList();
setupScreens();
//above functions add the widgets to the handle splitter
main->addWidget(handle);
setLayout(main);
}
If i open up this widget in a separate window from the MainWindow using test->show(), the things work as expected/as i want.
But doing this in the MainWindow constructor, renders it unclickable.
MainWindow::MainWindow(QWidget *parent):QMainWindow(parent){
Stack *test = new Stack(this);
//test->show();
setCentralWidget(test);
}
This is strange. Why am i not able to focus any widget that can take input e.g. QTextEdit,QListWidget or click any QPushButton widget?
Please compile following code, it was working..you are getting focus and edit on QTextEdit...
stack.h
#include <QWidget>
class Stack: public QWidget
{
Q_OBJECT
public:
Stack(QWidget *parent = 0);
~Stack(void);
};
stack.cpp
#include "Stack.h"
#include<QTextEdit>
#include<QHBoxLayout>
Stack::Stack(QWidget *parent):QWidget(parent){
QHBoxLayout* main = new QHBoxLayout;
QTextEdit *test = new QTextEdit;
main->addWidget(test);
//other things added to main layout
setLayout(main);
}
Stack::~Stack(void)
{
}
mainwindow1.h
#ifndef MAINWINDOW1_H
#define MAINWINDOW1_H
#include <QtGui/QMainWindow>
//#include "ui_mainwindow1.h"
class Mainwindow1 : public QMainWindow
{
Q_OBJECT
public:
Mainwindow1(QWidget *parent = 0, Qt::WFlags flags = 0);
~Mainwindow1();
private:
//Ui::Mainwindow1Class ui;
};
#endif // MAINWINDOW1_H
mainwindow1.cpp
#include "mainwindow1.h"
#include "Stack.h"
#include <QTextEdit>
Mainwindow1::Mainwindow1(QWidget *parent, Qt::WFlags flags)
: QMainWindow(parent, flags)
{
Stack *test = new Stack;
setCentralWidget(test);
}
Mainwindow1::~Mainwindow1()
{
}
main.cpp
#include "mainwindow1.h"
#include <QtGui/QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Mainwindow1 w;
w.show();
return a.exec();
}
If some1 would find this looking for answer on how to set focus on input widget from UI in QT5 you can just use:
ui->plainTextEdit->setFocus();
I'm new to C++ and QT, I'm using QT Creator, I created a QT Widget project named nGui, added a QT-QT Designer Form Class named mydlg, I've been trying to create a button in a window, when you click it opens another window while the original windows keeps showing. And here's my codes, but it always show the error: 'my2'was not declared in this scope. I have declared 'my2' in widget.h, and I included the widget.h file in mydlg.cpp, I don't know where is wrong, can someone help me out? Thank you so much!
mydlg.cpp
#include "mydlg.h"
#include "ui_mydlg.h"
myDlg::myDlg(QWidget *parent) :
QDialog(parent),
ui(new Ui::myDlg)
{
ui->setupUi(this);
}
myDlg::~myDlg()
{
delete ui;
}
void myDlg::on_pushButton_clicked()
{
my2.show();
}
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include"mydlg.h"
namespace Ui
{
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
private:
Ui::Widget *ui;
myDlg my2;
private slots:
void on_pushButton_clicked();
};
#endif // WIDGET_H
main.cpp
#include <QtGui/QApplication>
#include "widget.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
my2 is declared in the Widget class, but you are trying to access it in a member function of the myDlg class.
Try replacing my2.show() with show()
When you are writing in the myDlg class you are writing the behaviour of every object that can be instantiated from that class (including my2).
It does not make sense to refer to my2 within the MyDlg class then, since someone else using your class could instead instantiate another object called (for example) my3 with it instead. What you want to do is tell the compiler when on _pushButton_clicked() is called on an object of class MyDlg go ahead and call the show() function on the same object. You can do this by writing this->show() or simply show().
I'm just starting with Qt. I would like to make a GUI with custom widgets that can be used to display/edit my custom datatypes . I would also like to be able to dynamically create/destroy these widgets (without destroying the underlying data).
I've tried to accomplish this by storing a list of my data items (dataContainer) in the main Window class, and passing a pointer to a dataContainer to the constructor of my widget (customWidget), which stores this pointer. Then customWidget can alter the underlying data via this pointer.
The code below allows the user to repeatedly add dataContainer instances to the list and edit each of their "names". When I run this, everything works fine as long I don't actually edit the name, but if I do edit the name and then click the "Add" button, I get a segfault. This segfault occurs during the destructor of customWidget during delete myWidget; in Window::add().
Two questions:
Why does this code crash? Am I not allowed to dynamically create/destroy widgets like this?
Is there a more appropriate way to accomplish this?
I apologize if there's something basic I'm missing here, but I've been up-and-down lots of forums and tutorials, and not found anything helpful.
main.cpp
#include <QApplication>
#include "window.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
Window mainWindow;
mainWindow.show();
return app.exec();
}
window.h
#ifndef WINDOW_H
#define WINDOW_H
#include <QWidget>
#include "customwidget.h"
class Window : public QWidget {
Q_OBJECT
public:
Window();
public slots:
void add();
private:
// the main data structure
QList<dataContainer> dataList;
// the widget that displays the custom data
customWidget *myWidget;
QVBoxLayout *mainLayout;
QPushButton *addButton;
};
#endif
window.cpp
#include <QtGui>
#include "window.h"
Window::Window(){
// start with a single piece of data in the list
dataContainer newData;
dataList.append(newData);
// create layout container
mainLayout = new QVBoxLayout(this);
mainLayout->setAlignment(Qt::AlignTop);
// make the Add button, and connect its clicked() SIGNAL
// to our add() SLOT
addButton=new QPushButton(tr("Add"),this);
connect(addButton,SIGNAL(clicked()),this,SLOT(add()));
mainLayout->addWidget(addButton);
// create a custom widget to display our data and
// give it a pointer to the data it will display/modify
myWidget = new customWidget(this,&(dataList.last()) );
mainLayout->addWidget(myWidget);
setLayout(mainLayout);
}
void Window::add(){
// create a new piece of data, and add to the list
dataContainer newData;
dataList.append(newData);
// debug: show the current list
qDebug() << "Data List(" << dataList.size() << ")";
for (int i=0;i<dataList.size();i++){
qDebug() << dataList[i].getName().c_str();
}
// delete the old widget
delete myWidget;
// and make a new widget with the new data
myWidget = new customWidget(this,&(dataList.last()) );
mainLayout->addWidget(myWidget);
}
#include "window.moc"
customwidget.h
#ifndef CUSTOMWIDGET_H
#define CUSTOMWIDGET_H
#include <QWidget>
#include <QtGui>
#include <string>
class dataContainer {
private:
std::string name;
public:
dataContainer(){name="oooh";};
std::string getName()const{return name;};
std::string setName(const std::string& n){name=n;};
};
class customWidget : public QWidget {
Q_OBJECT
private:
dataContainer *data;
public slots:
void nameChangedSlot(const QString&);
public:
customWidget(QWidget *parent, dataContainer *d);
};
#endif
customwidget.cpp
#include "customwidget.h"
customWidget::customWidget(QWidget *parent, dataContainer *d) : QWidget(parent) {
// store a pointer to the data that we want to alter
data = d;
// create an edit box and initialize it with the data name
QVBoxLayout *mainLayout=new QVBoxLayout(this);
QLineEdit *edit=new QLineEdit(QString::fromStdString(data->getName()),this);
mainLayout->addWidget(edit);
connect(edit,SIGNAL(textChanged(const QString&)),this,SLOT(nameChangedSlot(const QString&)));
}
void customWidget::nameChangedSlot(const QString& name){
// alter the contents of our data based on the changed string
data->setName(name.toStdString());
}
#include "customwidget.moc"
A widget that is added to a layout can not be deleted directly. Instead, you can try below code which deletes a specific widget and corresponding layout item from layout:
QLayoutItem* item;
while ( ( item = mainLayout->takeAt( 0 ) ) != NULL )
{
if(item->widget() == myWidget){
delete item->widget();
delete item;
}
}