Q_ENUM external header - qt

I will have a rather large amounts of enums to be used in Qt c++ frontend, QML and also in c++ backend with no Qt libraries.
I am presenting two ways to do this, both works runtime. The funny thing is moc file generated for both solutions are identical. What is the QML engine use to lookup its QT_ENUMs if the MOC files alone aren't sufficient?
Therefore i make a header file with those enums defined and include the file "inline" to make sure it is part of the class. This works, but autocomplete in QML is missing (need to alt-tab back and forward and loose efficiency)
#include <QObject>
class ButtonEnums : public QObject
{
Q_OBJECT
public:
ButtonEnums(QObject* parent = nullptr);
#include "backend/app/messages_frontend.h"
Q_ENUM(EnTaskSelection)
};
Now i have to remember copy paste and get a larger header file then desired if i do this, but autocomplete in QML works:
#include <QObject>
class ButtonEnums : public QObject
{
Q_OBJECT
public:
ButtonEnums(QObject* parent = nullptr);
enum EnTaskSelection {
TASK_ONE = 0, //Strict indexes defined by main menu
TASK_TWO,
TASK_THREE,
TASK_FOUR
};
Q_ENUM(EnTaskSelection)
};

Related

How may I fix my error prone on_foo_bar slots

I have eight list widgets in a tab widget. They have similar names, and Designer's "Go to slot" mechanism has made links to slots it names (in the "private slots" section of "mainwindow.h") like:
void on_SR0listWidget_itemClicked(QListWidgetItem *item);
I saw warnings that "Slots named on_foo_bar are error-prone," and now I need to change their names in order to discover if that's the cause of the weird behaviour I'm getting.
I tried simply refactoring the names, but that stopped the slot code from working. I used Designer's graphical "Edit Signal/Slot" mechanism and was able to connect a newly added list widget's "itemClicked(QListWidgetItem *item)" signal to a newly added slot, and it looks OK in the graphical representation, but there's no debug message (that I set up in the Slot function) when an item is clicked.
I also use those widgets' "entered" signals, so there will be at least 16 to fix. I would write a script if it could be done by parsing the relevant files.
One example of exactly how to rename one of my replacement slots and connect an "item clicked" or "entered" signal to it (and where it should go) would be a great help.
Signals/slots setup through the designer rely on the names of the widgets involved. This can lead to problems if the widget names are changed. There are times when using the designer method will lead to code that compiles but doesn't actually make the connections you expect. This is why you are getting that warning.
You can get more reliable behavior by connecting the signals and slots programmatically. For example, let's say you have a class header such as:
#include <QMainWindow>
namespace Ui {
class MyWindow;
};
class QListWidgetItem;
class MyWindow : public QMainWindow {
Q_OBJECT
public:
explicit MyWindow(QWidget* parent = nullptr);
~MyWindow() override;
private:
void handleItemClicked(QListWidgetItem* item); // this is your slot
Ui::MyWindow* ui;
};
You can connect the signal/slot together in the cpp file like this:
#include "MyWindow.h"
#include "ui_MyWindow.h"
#include <QDebug>
MyWindow::MyWindow(QWidget* parent)
: QWidget(parent),
ui(new Ui::MyWindow()) {
ui->setupUi(this);
// connect() has many overloads, but in this case we are passing:
// 1. the object emitting the signal
// 2. the signal being emitted
// 3. the object whose slot we want to call
// 4. the slot to connect to
connect(
ui->listWidget, &QListWidget::itemClicked,
this, &MyWindow::handleItemClicked);
}
MyWindow::~MyWindow() {
delete ui;
}
void MyWindow::handleItemClicked(QListWidgetItem* item) {
qDebug() << "item clicked";
}
You can still use the designer to layout your UI - but prefer to manage connections directly in code rather than through the designer.

Q_ENUMS with enum in header file

I want to use the elements of an enum as items in a QComboBox. I can do so if the enum is defined in the same class but I would like to use an enum defined in a header file. My goal is to use the enum directly from the header file, without altering the header file. Moreover, I would like my code to adapt to a changing enum, both in the name of the elements and in the number of them.
I have found this answer and this link that have helped me understand how to populate a QComboBox with an enum. The solution in the answer works for me but only if the enum is defined in the class.
The basic code I would like to implement looks like:
definitions.h
#ifndef _DEFINITIONS_H_
#define _DEFINITIONS_H_
typedef enum
{
FIRST = 0,
SECOND,
THIRD
} elements;
#endif
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "definitions.h"
class MainWindow : public QMainWindow
{
Q_OBJECT
Q_ENUMS(elements);
public:
explicit MainWindow(QWidget *parent = 0);
QComboBox *comboBox;
};
#endif
mainwindow.cc
#include "mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
{
int index = metaObject()->indexOfEnumerator("elements");
QMetaEnum metaEnum = metaObject()->enumerator(index);
for (int i = 0; i < metaEnum.keyCount(); i++)
comboBox->addItem(metaEnum.valueToKey(i));
}
This code does not encounter any issue at compilation on runtime but has no effect at all. Defining the enum in MainWindow class works like a charm.
It does not look like a visibility issue because I can use the elements of the enum in my code and I have not encountered any compilation problem.
I have tried redefining my enum in mainwindow.h with typedef without success:
typedef elements new_elements
I have also tried replacing Q_ENUMS with Q_ENUM as suggested by the answer below but the result is the same.
Is it possible to use Q_ENUMS/Q_ENUM with an enum defined in a header file?
What should I do different?
You can't do that. Q_ENUMS need a meta object provided either by the QObject class or the Q_GADGET macro, that's why it works when the enum is declared in a class derived from a QObject.
Since Qt 5.8, a new macro was introduced Q_ENUM_NS that makes this possible:
This macro registers an enum type with the meta-object system. It must be placed after the enum declaration in a namespace that has the Q_NAMESPACE macro. It is the same as Q_ENUM but in a namespace.
You can do something like that:
namespace MyNamespace {
Q_NAMESPACE
enum elements {
FIRST = 0,
SECOND,
THIRD
};
Q_ENUM_NS(elements)
}
See this post for more information.
Updates
Regarding the last comment, you can create a new enumeration and associate each element with the value of the new one.
class MainWindow : public QMainWindow {
Q_OBJECT
public:
enum elements {
FIRST = ::elements::FIRST,
SECOND = ::elements::SECOND,
THIRD = ::elements::THIRD,
};
Q_ENUM(elements);
}
Then, for a function like:
void use_enum(elements e);
// Call it with the original one
use_enum(elements::FIRST);
// Call it with the new one, you just created
use_enum(MainWindow::elements::FIRST);
Check the Q_ENUM that registers automatically as metatype as it is said in the documentation What's New in Qt 5.5:
Added Q_ENUM to replace Q_ENUMS, which allows to get a QMetaEnum at compile time using QMetaEnum::fromType. Such enums are now automatically registered as metatypes, and can be converted to strings within QVariant, or printed as string by qDebug().

How to report errors from custom QML components?

I've made a MyComponent QML component with some property:
Q_PROPERTY(PendingList* list MEMBER list)
and a function:
Q_INVOKABLE void send();
It can be created in QML:
MyComponent {
id: myComponent
}
When I call somewhere the myComponent.send() while the list property is not defined, how can I properly report the problem in the stderr? I'd like to see the *.qml file name and line number where send() was called or the line number of the myComponent creation.
Is there a proper way to maybe get the QML stack trace or generate QQmlError or throw an exception that will be handled by the QML engine?
Will have to use some private stuff.
QTDD14 - Categorized logging in QML - Giuseppe D'Angelo
Git repository with the slides and the code: qmllogging
Short Version
Add to CMakeLists.txt:
include_directories(${Qt5Quick_PRIVATE_INCLUDE_DIRS})
Add some QV8Engine stuff to your Q_INVOKABLE function or slot:
#include <QtQml>
#include <private/qv4engine_p.h>
#include <private/qv8engine_p.h>
void Logger::log(const QString &message)
{
const QV4::StackFrame frame = QV8Engine::getV4(m_engine)->currentStackFrame();
QMessageLogger(qPrintable(frame.source),
frame.line,
qPrintable(frame.function)).warning("%s", qPrintable(message));
}
Geting the engine for that function:
QQuickView view;
m_engine = view.engine();
Also set the message pattern to actually show the line number (somewhere in the beginning of main.cpp is fine):
qSetMessagePattern("%{file}:%{line} - %{message}");

Can I subclass QEvent?

I tried to make a class which is a subclass of QEvent, but I got error after building.
My steps,
1. Create a project using Qt console template
2. create the following code
#ifndef MYEVENT_H
#define MYEVENT_H
#include <QEvent>
#include <QObject>
class MyEvent : public QEvent
{
Q_OBJECT
public:
explicit MyEvent();
signals:
public slots:
};
#endif
//CPP File
MyEvent::MyEvent() :
QEvent(QEvent::User)
{
}
moc_MyEvent.cpp:70:21: error: invalid use of non-static data member 'd_ptr'
return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject;
~~~~~~~~~^~~~~
moc_MyEvent.cpp:70:21: error: 'd_ptr' is a protected member of 'QObject'
../../../../../../Qt5.1.0/5.1.0/clang_64/include/QtCore/qobject.h:411:33: note: declared protected here
QScopedPointer<QObjectData> d_ptr;
^
Qt5
Mac OSX 10.8.4
How do I solve it and why? Thanks.
Dcow gives corrent answer.
Your mistake is that QEvent does not inherit from QObject, and you try to do it. You should not use Q_OBJECT macros or you should interhit your class from QObject too. But it's dark side.
First of all WHY? You should write why you need this, I'm sure that your problem solution which you are trying to fix is just wrong!
Secondly problem is Q_OBJECT macro. QEvent is not a QObject so this macro is not applicable and this is why you have this error.
As already pointed out: No Q_OBJECT. But let me add something and point your attention to a rarely used and widely unknown macro: Q_GADGET
Almost a Q_OBJECT for non-QObjects.
From the Qt Docs:
Use Q_GADGET instead of Q_OBJECT to enable the meta object system's
support for enums in a class that is not a QObject subclass. Q_GADGET
makes a class member, staticMetaObject, available. staticMetaObject is
of type QMetaObject and provides access to the enums declared with
Q_ENUMS. Q_GADGET is provided only for C++.
Comes handy from time to time.

Exposing child's properties in the parent

I want to create a custom widget that contains a QSpinBox. I want the custom widget to expose some of QSpinBox's properties as its own so it can work conveniently in Designer.
Is there a convenient way to do this kind of property proxying in Qt?
I want to reiterate that the relationship between my custom widget and the QSpinBox is containment, not inheritance. I am using pyqt, but will happily accept a pure qt answer.
To clarify, I want to create variations of standard widgets that have some extra decorations. For example, I will be adding a toggleable icon to sit beside my QSpinBox. However I still want to be able to configure the QSpinBox in designer (ie, set the suffix, upper and lower limits, increment, etc).
Alrite, so here's a way to do it.
It's better than writing a function manually for each property. Basically, you write a macro that expands the code for you.
I have a doubleSpinBox in my widget and its value is changed whenever I change the property. And you emit a signal whenever the property changes, which is connected to the doubleSpinBox->setValue(). Tested and works perfectly.
Complete code at this link.
#ifndef MYWIDGET_H_
#define MYWIDGET_H_
#include <QtGui/QWidget>
#include <QtGui/QLabel>
#include "ui_mywidgetform.h"
#define MYPROPERTY_SET_FUNC(_PROPCLASS_, _PROP_PARAM_NAME_, _PROP_SET_FUNC_NAME_, _PROP_NOTIFY_) \
void _PROP_SET_FUNC_NAME_(_PROPCLASS_ _PROP_PARAM_NAME_) \
{ \
emit _PROP_NOTIFY_(_PROP_PARAM_NAME_); \
}
#define MYPROPERTY_GET_FUNC(_PROPCLASS_, _PROP_READ_FUNC_NAME_, _PROP_VARIABLE_)\
_PROPCLASS_ _PROP_READ_FUNC_NAME_() const \
{ return _PROP_VARIABLE_; }
class MyWidget : public QWidget
{
Q_OBJECT
public:
MyWidget(QWidget *parent = 0);
~MyWidget();
enum Accuracy {Good, Bad};
signals:
void doubleSpinBoxValueChanged(double);
void accuracyChanged(Accuracy);
private:
Q_PROPERTY(Accuracy accuracy READ accuracy WRITE setAccuracy NOTIFY accuracyChanged)
Q_PROPERTY(double doubleSpinBoxValue READ doubleSpinBoxValue WRITE setDoubleSpinBoxValue)
private:
Accuracy m_accuracy; //custom class property
Ui::Form m_ui;
public:
//Getter functions
MYPROPERTY_GET_FUNC (double, doubleSpinBoxValue, m_ui.doubleSpinBox->value())
MYPROPERTY_GET_FUNC (Accuracy, accuracy, m_accuracy)
//Setter functions
MYPROPERTY_SET_FUNC(Accuracy, accuracy, setAccuracy, accuracyChanged)
MYPROPERTY_SET_FUNC(double, doubleSpinBoxValue, setDoubleSpinBoxValue, doubleSpinBoxValueChanged)
};
#endif // MYWIDGET_H_
Here is a PyQt version inspired by blueskin. The main operational difference here is that the custom widget class is composed at runtime rather than compile time. The advantage to this is that we can programmatically inspect the target metaobject and use that to generate the proxy properties.
Now PyQt is compiled from c++, so I have to believe that it is also possible to generate qt types at runtime in c++. I am betting that would be hellish, however.
from PyQt4 import QtGui, QtCore
def makeProxyProperty(childname, childtype, childpropname):
metaobject = childtype.staticMetaObject
metaproperty = metaobject.property(metaobject.indexOfProperty(childpropname))
def getter(self):
return metaproperty.read(getattr(self, childname))
def setter(self, val):
return metaproperty.write(getattr(self, childname), val)
return QtCore.pyqtProperty(metaproperty.typeName(), fget=getter, fset=setter)
class Widget1(QtGui.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.spinbox = QtGui.QSpinBox()
self.checkbox = QtGui.QCheckBox()
layout = QtGui.QHBoxLayout()
layout.addWidget(self.checkbox)
layout.addWidget(self.spinbox)
self.setLayout(layout)
spinbox_suffix = makeProxyProperty("spinbox", QtGui.QSpinBox, "suffix")
spinbox_prefix = makeProxyProperty("spinbox", QtGui.QSpinBox, "prefix")
spinbox_minimum = makeProxyProperty("spinbox", QtGui.QSpinBox, "minimum")
spinbox_maximum = makeProxyProperty("spinbox", QtGui.QSpinBox, "maximum")
So to make things a bit clear,
you will be using this custom widget in the designer.
You need the custom widget to have properties that expose QSpinBox properties as the widget's properties
So,
basically you would want to create a property in your custom widget
using 'Q_PROPERTY' and use setter and getter methods to change the
QSpinBox's properties I hope this example would help
http://doc.qt.nokia.com/latest/properties.html#a-simple-example

Resources