I've got a hashmap ReferenceMap in a Qt serial terminal program.
The program receives a key to ReferenceMap in form of a reference followed by a value. It then uses the Qt hashmap insert function to insert the value. It's very neat and I would like to keep that part of the program.
The problem is that I would like to to do different things when different new values are coming in. I therefore would like to emit a signal when a value associated with a special place in the referencemap is changed.
According to the guide below, an object that emits a signal can easily be created.
http://qt-project.org/doc/qt-4.8/signalsandslots.html
I think I got a solution, however, and want to discuss wether it's a neat way or not.
I could create a class in like this
class Sfloat : public QObject
{
Q_OBJECT
public:
explicit Sfloat(QObject *parent = 0);
float Get();
void SetValue(float value);
private:
float MyFloat;
signals:
void ValueChanged(float newValue);
public slots:
};
The ReferenceMap could then be set to contain references to SFloat object like,
ReferenceMap(Key, &SFloat).
When I have the key, I could get the object reference, call SetValue() and a signal would be emitted. However, is this the best way to solve it?
I don't think that's a good idea. You are wrapping every float of your hash with a QObject which sounds very heavy to me.
You can hap the whole HashMap inside the QObject and emit the signal from there when the value you are interested in has changed.
class MyHashMap : public QObject
{
Q_OBJECT
public:
explicit MyHashMap(QObject *parent = 0);
float Get(const QString& key) const;
void SetValue(const QString& key, float value)
{
// only emit value changed if the value has actually changed
QHashMap<QString, float>::iterator it = myHashMap.find(key);
if(it != myHashMap.end()){
if(it.value != value){
myHashMap[key] = value;
emit ValueChanged(key, value);
}
}
}
private:
QHashMap<QString, float> myHashMap;
signals:
void ValueChanged(const QString& key, float newValue);
};
According to your comment, you want to update a label when a value has changed. Simply add all the labels to a QHashMap, then connect the ValueChanged signal to a slot that updates the right label:
class MyWidget : public QWidget
{
Q_OBJECT
public:
explicit MyWidget(QWidget *parent = 0)
{
...
...
myLabels["label1"] = myLabel1;
myLabels["label2"] = myLabel2;
QObject::connect(myLabels, SIGNAL(ValueChanged(const QString&, float)),
this, OnValueChanged(const QString&, float));
}
public slots:
void OnValueChanged(const QString& key, float newValue){
QHashMap<QString, QLabel*>::iterator it = myLabels.find(key);
if(it != myLabels.end()){
it.value()->setText(QString::number(newValue));
}
}
private:
QHashMap<QString, QLabel*> myLabels;
};
Related
I have a very simple class with 2 properties; key and value:
KeyValue.h:
class KeyValue : public QObject
{
Q_OBJECT
Q_PROPERTY(QString key READ getKey WRITE setKey NOTIFY keyChanged)
Q_PROPERTY(QString value READ getValue WRITE setValue NOTIFY valueChanged)
public:
KeyValue(const QString& key, const QString& value, QObject* parent = 0);
signals:
void keyChanged();
void valueChanged();
private:
QString _key;
QString _value;
QString getKey() const;
QString getValue() const;
void setKey(const QString& key);
void setValue(const QString& value);
};
Q_DECLARE_METATYPE(KeyValue)
In another class I would like a property containing a list of KeyValue objects, so I can use this list as a model in QML.
Controller.h
class Controller : public QObject
{
Q_OBJECT
Q_PROPERTY(QList<KeyValue*> items READ getItems NOTIFY itemsChanged)
public:
explicit Controller(QObject* parent = 0);
signals:
void itemsChanged();
private:
QList<KeyValue*> getItems() const;
};
I want to be able to use this in QML the following way:
import QtQuick 2.7
import customqml 1.0
Item{
Controller{
id: controller
}
Repeater{
model: controller.items
Text{
text: modelData.key + ": " + modelData.value
}
}
}
Both classes are registered in my main.cpp file:
qmlRegisterType<KeyValue>("customqml", 1, 0, "KeyValue");
qmlRegisterType<Controller>("customqml", 1, 0, "Controller");
The above code does not work, bacause I apparently can't expose a QList to QML directly. I have tried using QAbstractItemModel and QQmlListProperty, but I was unable to get it to work. Can anyone point me in the right direction?
My primary issues are the type of the items property in the Controller class and the return value of the getItems method.
I'm using Qt 5.9 if that makes any difference.
Note:
The getters and setters are generally public except for exceptions so move it to the public part
The classes that inherit from QObject do not need QMetaType because when you want to transfer data of that class the pointers are used.
Not all data types are supported by QML through Q_PROPERTY, so a possible solution is to export through known classes such as
QList<QObject *>:
class Controller : public QObject
{
Q_OBJECT
Q_PROPERTY(QList<QObject *> items READ getItems NOTIFY itemsChanged)
public:
explicit Controller(QObject *parent = nullptr);
QList<QObject *> getItems() const;
signals:
void itemsChanged();
private:
QList<KeyValue *>key_values_list;
};
...
QList<QObject *> Controller::getItems() const
{
QObjectList l;
for(auto e: key_values_list)
l << e;
return l;
}
QVariantList:
class Controller : public QObject
{
Q_OBJECT
Q_PROPERTY(QVariantList items READ getItems NOTIFY itemsChanged)
public:
explicit Controller(QObject *parent = nullptr);
QVariantList getItems() const;
signals:
void itemsChanged();
private:
QList<KeyValue *>key_values_list;
};
...
QVariantList Controller::getItems() const
{
QVariantList l;
for(auto e: key_values_list)
l.append(QVariant::fromValue(e));
return l;
}
Other options is to implement a model, the following example shows only a read-only model:
keyvaluemodel.h
#ifndef KEYVALUEMODEL_H
#define KEYVALUEMODEL_H
#include "keyvalue.h"
#include <QAbstractListModel>
class KeyValueModel : public QAbstractListModel
{
Q_OBJECT
public:
explicit KeyValueModel(QObject *parent = nullptr)
: QAbstractListModel(parent)
{
key_values_list = {new KeyValue{"k", "v"}, new KeyValue{"k2", "v2"}};
}
int rowCount(const QModelIndex &parent = QModelIndex()) const override
{
if (parent.isValid())
return 0;
return key_values_list.length();
}
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override
{
if (!index.isValid())
return QVariant();
if(index.row() >= 0 && index.row() < rowCount())
return QVariant::fromValue(key_values_list[index.row()]);
return QVariant();
}
private:
QList<KeyValue* >key_values_list;
};
#endif // KEYVALUEMODEL_H
class Controller : public QObject
{
Q_OBJECT
Q_PROPERTY(KeyValueModel* items READ getItems NOTIFY itemsChanged)
public:
explicit Controller(QObject *parent = nullptr);
KeyValueModel* getItems() const;
signals:
void itemsChanged();
private:
KeyValueModel *model;
};
...
Text{
text: display.key + ": " + display.value
}
...
And in a similar way you can implement a QQmlListProperty, in the docs there are many examples.
If you want a sophisticated model with adding/deleting objects and altering data you should look into subclassing QAbstractListModel.
As a simple but less flexible way you can use a QVariantList and make your Controller class a value type. You need:
The macro Q_DECLARE_METATYPE(Controller) at the end of the Controller header file.
A copy constructor for Controller
A default constructor for Controller
Q_PROPERTY Type and return value are then QVariantList.
I have this class I intended to use in the context of a qml engine so in order to use property binding I setted up these Q_PROPERY macros. I want to use the MEMBER keyword and have the notify signal emitted automatically.
class InterfaceBackend : public QObject
{
Q_OBJECT
Q_PROPERTY(quint8 current_view MEMBER m_current_view NOTIFY sCurrentViewChanged)
Q_PROPERTY(quint8 future_view MEMBER m_future_view NOTIFY sFutureViewChanged)
public:
explicit InterfaceBackend(QObject *parent = 0);
~InterfaceBackend();
quint8 getCurrentView() { return this->m_current_view; }
quint8 getFutureView() { return this->m_future_view; }
private:
quint8 m_current_view;
quint8 m_future_view;
QByteArray m_selected_language;
public slots:
void onLanguageSelected(QByteArray language);
private slots:
signals:
void sCurrentViewChanged(quint8 current_view);
void sFutureViewChanged(quint8 future_view);
};
InterfaceBackend::InterfaceBackend(QObject *parent) : QObject(parent)
{
this->setObjectName("backend");
QObject::connect(this, &InterfaceBackend::sFutureViewChanged, []() {qDebug() << "sFutureViewChanged";});
this->m_current_view=1;
this->m_future_view=1;
}
InterfaceBackend::~InterfaceBackend()
{
}
void InterfaceBackend::onLanguageSelected(QByteArray language)
{
this->m_selected_language=language;
this->m_future_view=2;
}
qt docs say:
A NOTIFY signal is optional. If defined, it should specify one existing signal in that class that is emitted whenever the value of the property changes. NOTIFY signals for MEMBER variables must take zero or one parameter, which must be of the same type as the property. The parameter will take the new value of the property. The NOTIFY signal should only be emitted when the property has really been changed, to avoid bindings being unnecessarily re-evaluated in QML, for example. Qt emits automatically that signal when needed for MEMBER properties that do not have an explicit setter
But whenever I call the slot the signals never gets called nor the property is updated in the qml model, what's wrong!?
To give an answer that is technically more accurate:
The MEMBER in Q_PROPERTY will tell the moc (Meta object compiler) that when accessing the property via the meta object it should use the member directly instead of a getter or setter method. So the moc will the generate a setter method internally that sets the member and emits the signal - it basically just does the work of writing getters/setters for you. Since changing a member needs to emit the change signal, this is automatically done when the property is written from the meta object system. So, calling:
backend->setProperty("future_view", future_view);
will correctly emit the changed signal. This is the only guarantee that is given when using MEMBER. Changes, that are done via the meta property will trigger the change signal. This means if you would set future_view from QML directly, without the onLanguageSelected method, it would actually work.
In your example however you directly write a value to the member inside a special method - This will not trigger the signal automatically! (I mean, how should Qt even know you did that). So what you need to do is whenever you change the value of your member you need to emit the change signal yourself:
void onLanguageSelected(QByteArray language)
{
this->m_selected_language=language;
this->m_future_view=2;
emit sFutureViewChanged();
}
Edit: If you were trying prevent the properties from beeing written directly from QML, using MEMBER will not work! Use a getter instead and only register the getter with the property. Use the same code as above to write and change the properties:
Q_PROPERTY(quint8 future_view READ futureView NOTIFY sFutureViewChanged)
As you can see in this article:
New keyword in Q_PROPERTY: MEMBER let you bind a property to a class member without requiring to have a getter or a setter.
So you should remove your getters, and your result code will look like this
class InterfaceBackend : public QObject
{
Q_OBJECT
Q_PROPERTY(quint8 current_view MEMBER m_current_view NOTIFY sCurrentViewChanged)
Q_PROPERTY(quint8 future_view MEMBER m_future_view NOTIFY sFutureViewChanged)
public:
explicit InterfaceBackend(QObject *parent = 0)
: QObject(parent)
{
this->setObjectName("backend");
QObject::connect(this, &InterfaceBackend::sFutureViewChanged, []() { qDebug() << "sFutureViewChanged";});
this->m_current_view=1;
emit sCurrentViewChanged();
this->m_future_view=1;
emit sFutureViewChanged();
}
~InterfaceBackend() = default;
private:
quint8 m_current_view;
quint8 m_future_view;
QByteArray m_selected_language;
public slots:
void onLanguageSelected(QByteArray language) {
this->m_selected_language=language;
this->m_future_view=2;
emit sFutureViewChanged();
}
signals:
void sCurrentViewChanged();
void sFutureViewChanged();
};
Every Q_PROPERTY must have READ public method and WRITE public slot, also the signal will never automatically emited, you should emit it whenever MEMBER change.
class InterfaceBackend : public QObject
{
Q_OBJECT
Q_PROPERTY(quint8 current_view MEMBER m_current_view READ currentView WRITE setCurrentView NOTIFY sCurrentViewChanged)
Q_PROPERTY(quint8 future_view MEMBER m_future_view READ futureView WRITE setFutureView NOTIFY sFutureViewChanged)
public:
explicit InterfaceBackend(QObject *parent = 0);
~InterfaceBackend();
quint8 currentView() const
{
return m_current_view;
}
quint8 futureView() const
{
return m_future_view;
}
private:
quint8 m_current_view;
quint8 m_future_view;
QByteArray m_selected_language;
public slots:
void onLanguageSelected(QByteArray language);
void setCurrentView(quint8 current_view)
{
if (m_current_view == current_view)
return;
m_current_view = current_view;
emit sCurrentViewChanged(m_current_view);
}
void setFutureView(quint8 future_view)
{
if (m_future_view == future_view)
return;
m_future_view = future_view;
emit sFutureViewChanged(m_future_view);
}
private slots:
signals:
void sCurrentViewChanged(quint8 current_view);
void sFutureViewChanged(quint8 future_view);
};
InterfaceBackend::InterfaceBackend(QObject *parent) : QObject(parent)
{
this->setObjectName("backend");
QObject::connect(this, &InterfaceBackend::sFutureViewChanged, []() {qDebug() << "sFutureViewChanged";});
this->m_current_view=1;
this->m_future_view=1;
}
InterfaceBackend::~InterfaceBackend()
{
}
void InterfaceBackend::onLanguageSelected(QByteArray language)
{
this->m_selected_language=language;
this->m_future_view=2;
}
Spending some time, I got it working as I need, althought this is EXTREMELY UGLY and BY NO MEANS DECLARATIVE I think it is the only way to achive double binding, and maybe it will be helpful for other people approaching the problem. Note this is not an accepted answer, I still hope someone somewhere, perhaps in the future if and when Qt will be updated, could come up with a far more elegant solution.
Main disadvantages: longer and verbose code, one has to use setters everywhere and (try not forget)
The property system doesn't do anything automatically in the end, just states wich properties are exposed and wich are their getters/setters and change signals
c++
class InterfaceController : public QObject
{
Q_OBJECT
Q_PROPERTY(quint8 current_view READ getCurrentView WRITE setCurrentView NOTIFY sCurrentViewChanged)
Q_PROPERTY(quint8 future_view READ getFutureView WRITE setFutureView NOTIFY sFutureViewChanged)
public:
explicit InterfaceController(QObject *parent = 0);
//getters
quint8 getCurrentView() { return this->m_current_view; }
quint8 getFutureView() { return this->m_future_view; }
//setters
Q_INVOKABLE void setCurrentView(quint8 current_view) { if(this->m_current_view!=current_view) {this->m_current_view=current_view; emit sCurrentViewChanged(this->m_current_view);} }
Q_INVOKABLE void setFutureView(quint8 future_view) { if(this->m_future_view!=future_view) {this->m_future_view=future_view; emit sFutureViewChanged(this->m_future_view);} }
private:
quint8 m_current_view;
quint8 m_future_view;
QByteArray m_selected_language;
public slots:
void onLanguageSelected(QByteArray language);
private slots:
signals:
void sCurrentViewChanged(quint8 current_view);
void sFutureViewChanged(quint8 future_view);
};
InterfaceController::InterfaceController(QObject *parent) : QObject(parent)
{
this->m_current_view=1;
this->m_future_view=1;
}
void InterfaceController::onLanguageSelected(QByteArray language)
{
this->m_selected_language=language;
this->setFutureView(2);
}
QML
id: root
property int current_view: 1
property int future_view: 1
Connections {
target: root
onCurrent_viewChanged: { backend.setCurrentView(current_view); }
onFuture_viewChanged: { backend.setFutureView(future_view); }
}
Connections {
target: backend
onSCurrentViewChanged: { if(root.current_view!=current_view) {root.current_view=current_view;} }
onSFutureViewChanged: { if(root.future_view!=future_view) {root.future_view=future_view;} }
}
I have the following code:
void AppMPhase::closeEvent(QCloseEvent *closeEvent) {
QMessageBox* dialog = new QMessageBox(this);
dialog->setText("Warning: Initial configuration changed\nDo you want to restore it ?");
dialog->setIcon(QMessageBox::Warning);
dialog->addButton(QMessageBox::Yes);
dialog->addButton(QMessageBox::No);
connect(dialog, SIGNAL(buttonClicked(QAbstractButton*)), this, SLOT(restoreInitialConfig(QAbstractButton*)));
dialog->exec();
}
void AppMPhase::restoreInitialConfig(QAbstractButton *button) {
if(!button->text().compare(QString("Yes"))) {
// restore Config
}
else {
// don't restore
}
MainWindow::closeEvent(closeEvent);
}
with AppMPhase the derived class of MainWindow
I would like to call the method closeEvent of the base class MainWindow in the "restoreInitialConfig" method, to close the window after we restore the config.
Is it possible ? it's different from the other questions I have seen because the method closeEvent is overriden in the AppMPhase class.
the AppMPhase class:
class AppMPhase : public QtUi::MainWindow
{
Q_OBJECT
public:
AppMPhase(QWidget *const parent = Q_NULLPTR, const Qt::WindowFlags flags = 0);
~AppMPhase();
int readConfigFromExternFile(QString path);
int readCalibrationDate(QString path);
QSize sizeHint() const Q_DECL_OVERRIDE;
QtWrapperTestManager * testManager;
public Q_SLOTS:
void show();
public slots:
void currentTestFinished();
void createTest(unsigned int,QString);
void restoreInitialConfig(QAbstractButton* button);
signals:
void notifyPageTestCurrentTestFinished();
private:
enum AppPage
{
PAGE_START,
PAGE_ABOUT
};
bool isTestMangaerCreated;
std::map<QString, std::map<std::string, std::string> > conversion_Table_Cable_Ref_sensorParamMap;
Pages::GenericAppPage * appPage(const int index) const;
QToolButton * appPageButton(const int index) const;
virtual void closeEvent(QCloseEvent *closeEvent) Q_DECL_OVERRIDE;
The MainWindow class:
class SHARED_EXPORT_LIB_QT_UI MainWindow : public QMainWindow
{
Q_OBJECT
signals:
void aboutToClose();
public slots:
virtual void show();
private slots:
void changeLanguage(const QString &language);
public:
MainWindow(QWidget *const parent = Q_NULLPTR, const Qt::WindowFlags flags = 0);
virtual ~MainWindow();
protected:
void showCopyright(const QString ©RightHeader);
void showLanguageSelector(const QStringList &languages);
void setupUi(const MainPageList &pageList);
void setupUi(QWidget *centerWidget = Q_NULLPTR);
virtual void closeEvent(QCloseEvent *closeEvent) Q_DECL_OVERRIDE;
const Ui::MainWindow *const _ui;
MainPageItemList _mainPageItemList;
};
Thanks in advance.
Flo
There is a far easier way to achieve what you're wanting to do, without having to use signals and slots, and call base class functions from your slot.
It is possible to do the restoration directly from within your closeEvent handler.
This is made possible by the fact that QMessageBox::exec returns an integer code which matches one of the values in the StandardButton enum, depending on what button was pressed.
That then allows you to call restoreInitialConfig directly from within your closeEvent handler, as you know which button was pressed.
void AppMPhase::closeEvent(QCloseEvent* ev)
{
int res = QMessageBox(
QMessageBox::Icon::Warning,
"Restore configuration?",
"Warning: Initial configuration changed\nDo you want to restore it?",
QMessageBox::Yes | QMessageBox::No,
this).exec();
if (res == QMessageBox::Yes)
restoreInitialConfig();
MainWindow::closeEvent(ev);
}
Note that this also simplifies your restoreInitialConfig function, as there is no need to check button text, you know the answer was yes.
Note also I made use of this QMessageBox constructor which makes it very easy to create simple message boxes.
I would like to create a signal in my main class foo so that a static method in a different class could emit it.I just started of with QT so I am a bit confused. I currently have the following code
class Foo : public QMainWindow
{
Q_OBJECT
public:
Foo(QWidget *parent = 0, Qt::WFlags flags = 0);
~Foo();
signals:
void UpdateSignal(int val);
private slots:
void MySlot(int val);
};
Foo::Foo(QWidget *parent, Qt::WFlags flags): QMainWindow(parent, flags)
{
//How do I connect Bfoo::somemethod() here. I know its suppose to be like
connect(xx,SIGNAL(UpdateSignal(int)),this, SLOT(MySlot(int)));
ui.setupUi(this);
}
void Foo::MySlot(int val)
{
//Do something..
}
Now I have this class
Class Bfoo
{
static void somemethod()
{
emit UpdateSignal(12);
}
}
Any suggestions on how the static somemethod() could emit the UpdateSignal
When you emit signal it is necessary to know which object is emitting it. This is because signals are not implemented to be messages between different classes but messages between instances of (possibly different) classes.
Secondly, signals are protected methods. They are not accessible for external users. What you can do is define public method in Foo which will do the emission:
void Foo:EmitUpdateSignal(int x) {
emit UpdateSignal(x);
}
And then in your Bfoo::somemethod() you need to pass object which will emit signal:
void BFoo::somemethod(Foo &f) {
f.EmitUpdateSignal(12);
}
However, notice what you are doing. You emit signal which is connected to the slot in the same instance. This suggests design flaw but I cannot give any hints without more details about what are you going to achieve.
Following case:
Let's say there is a binary library which defines the class "Base", and many subclasses ("Derivative1", "Derivative2" etc) of it.
I want to extend these subclasses in my own code, but because my extensions are the same for all subclasses as they only deal with parts of Base, it would be tedious to subclass every Derivative class and add the same code over and over again.
My first idea was to simply write a class template which would do the work for me, but because the library I'm dealing with is Qt, QObject has foiled me.
My second idea was to use macros to generate each class structure, but this was also thwarted by moc.
The "reparent" in the title is because I thought of deriving from Base and creating BaseExtended, and then somehow tell the compiler to reparent every Derivative to this extended class. Isn't there a way to for example declare the "Base" in "BaseExtended" virtual, and then just write
class Derivative1Extended : public Derivative1, public BaseExtended {}
and have the virtual Base in BaseExtended point to the Base in Derivative1, thus basically "squeezing ing" my extensions between Base and Derivative1?
(By the way, I tried to keep the above as generic as possible, but what I'm actually doing is trying add signals for "focusIn" and "focusOut" to every QWidget derivative without writing the same code over and over again for every QWidget subclass I use)
EDIT:
For reference, here's my current implementation:
// qwidgetfs.h
class QLineEditFS : public QLineEdit
{
Q_OBJECT
private:
void focusInEvent(QFocusEvent *);
void focusOutEvent(QFocusEvent *);
public:
QLineEditFS(QWidget *parent = 0);
signals:
void focusReceived(QWidgetFS::InputType);
void focusLost();
};
// qwidgetfs.cpp
QLineEditFS::QLineEditFS(QWidget *parent /*= 0*/)
: QLineEdit(parent)
{}
void QLineEditFS::focusInEvent(QFocusEvent *e)
{
QLineEdit::focusInEvent(e);
emit focusReceived(QWidgetFS::InputText);
}
void QLineEditFS::focusOutEvent(QFocusEvent *e)
{
QLineEdit::focusOutEvent(e);
emit focusLost();
}
And this repeated for QSpinBoxFS, QComboBoxFS, QCheckBoxFS and so on...
Instead I would like to just define this logic in a common class QWidgetFS, and then "inject" it into other classes derived from QWidget
I'm not sure you'll really be able to do what you are suggesting without modifying Qt and recompiling it.
Perhaps to do what you want, you could use event filters installed on the objects that you want to handle focus events from?
little test app:
header:
class FocusEventFilter : public QObject
{
Q_OBJECT
public:
FocusEventFilter(QObject* parent)
: QObject(parent)
{}
Q_SIGNALS:
void focusIn(QWidget* obj, QFocusEvent* e);
void focusOut(QWidget* obj, QFocusEvent* e);
protected:
bool eventFilter(QObject *obj, QEvent *e);
};
class testfocus : public QMainWindow
{
Q_OBJECT
public:
testfocus(QWidget *parent = 0, Qt::WFlags flags = 0);
~testfocus();
public Q_SLOTS:
void onFocusIn(QWidget*, QFocusEvent*);
void onFocusOut(QWidget*, QFocusEvent*);
private:
Ui::testfocusClass ui;
};
Implementation
#include <QFocusEvent>
#include "testfocus.h"
bool FocusEventFilter::eventFilter(QObject *obj, QEvent *e)
{
if (e->type() == QEvent::FocusIn) {
bool r = QObject::eventFilter(obj, e);
QFocusEvent *focus = static_cast<QFocusEvent*>(e);
QWidget* w = qobject_cast<QWidget*>(obj);
if (w) {
emit focusIn(w, focus);
}
return r;
}
else if (e->type() == QEvent::FocusOut) {
bool r = QObject::eventFilter(obj, e);
QFocusEvent *focus = static_cast<QFocusEvent*>(e);
QWidget* w = qobject_cast<QWidget*>(obj);
if (w) {
emit focusOut(w, focus);
}
return r;
}
else {
// standard event processing
return QObject::eventFilter(obj, e);
}
}
testfocus::testfocus(QWidget *parent, Qt::WFlags flags)
: QMainWindow(parent, flags)
{
ui.setupUi(this);
FocusEventFilter* filter = new FocusEventFilter(this);
ui.lineEdit->installEventFilter(filter);
ui.lineEdit_2->installEventFilter(filter);
connect(filter, SIGNAL(focusIn(QWidget*, QFocusEvent*)), this, SLOT(onFocusIn(QWidget*, QFocusEvent*)));
connect(filter, SIGNAL(focusOut(QWidget*, QFocusEvent*)), this, SLOT(onFocusOut(QWidget*, QFocusEvent*)));
}
testfocus::~testfocus()
{
}
void testfocus::onFocusIn(QWidget* obj, QFocusEvent*)
{
obj->setStyleSheet("background-color:#aaaaff;");
}
void testfocus::onFocusOut(QWidget* obj, QFocusEvent*)
{
obj->setStyleSheet("background-color:#ffaaaa;");
}
Of course, YMMV. You could always have a separate filter per object. This method means you can avoid deriving from everything. Not as efficient but it should work.
You may be able to do what you want in the event filter itself rather than using signals/slots.
I have done stuff like this in the past with templates. The problem is that you can't use signals.
I'm typing this up without a compiler so please be kind :):
template<typename T>
class FSWidget: public T
{
public:
FSWidget()
{
_delegate = NULL;
}
setDelegate(FSDelegate *delegate)
{
_delegate = delegate;
}
protected:
virtual void focusInEvent(QFocusEvent *e)
{
T::focusInEvent(e);
if (_delegate) {
_delegate->focusInEvent(this);
}
}
virtual void focusOutEvent(QFocusEvent *e)
{
T::focusOutEvent(e);
if (_delegate) {
_delegate->focusOutEvent(this);
}
}
private:
FSDelegate *_delegate;
};
So, the advantage is when you need to use this you can basically create a class like this:
FSWidget<QLineEdit *> lineEdit = new FSWidget<QLineEdit *>;
lineEdit->setDelegate(delegate);
You can put in whatever you want instead of QLineEdit and it will work.
And then teh FSDelegate could be just an interface that you mix into whatever class needs to act on the info. It could be one of these:
class FSDelegate
{
public:
virtual void focusInEvent(QWidget *w) = 0;
virtual void focusOutEvent(QWidget *w) = 0;
};
If you're always doing the same thing in on focusInEvent and focusOutEvents, you can implement these functions and make a real Mixin class.
Hopefully this can avoid some code duplication for you.