Exposing to QML the serial port names from C++ - c++

I'm trying to expose the QSerialPort.available() through an Q_INVOKABLE QStringList availablePorts() function from a class I expose directly to QML in my main class.
Main:
qmlRegisterType<SerialPortManager>("com.MyApp.qml", 1, 0, "SerialPortManager");
SerialPortManager
class SerialPortManager : public QObject
{
Q_OBJECT
public slots:
Q_INVOKABLE virtual QStringList availablePorts() {
QList<QSerialPortInfo> portsAvailable = QSerialPortInfo::availablePorts();
QStringList names_PortsAvailable;
for(QSerialPortInfo portInfo : portsAvailable) {
names_PortsAvailable.append(portInfo.portName());
}
return names_PortsAvailable;
}
Which is not valid for a model type in QML because it raises Unable to assign QStringList to QQmlListModel* error.
QML
ComboBox {
model: serial.availablePorts()
}
SerialPortManager {
id: serial
}
So how do I get around this?

One solution is to return a QVariant as recommended by the docs, for this we use QVariant::fromValue()
#ifndef SERIALPORTMANAGER_H
#define SERIALPORTMANAGER_H
#include <QObject>
#include <QSerialPortInfo>
#include <QVariant>
class SerialPortManager : public QObject
{
Q_OBJECT
public:
Q_INVOKABLE static QVariant availablePorts() {
QList<QSerialPortInfo> portsAvailable = QSerialPortInfo::availablePorts();
QStringList names_PortsAvailable;
for(const QSerialPortInfo& portInfo : portsAvailable) {
names_PortsAvailable<<portInfo.portName();
}
return QVariant::fromValue(names_PortsAvailable);
}
};
#endif // SERIALPORTMANAGER_H

Related

setWindowState from QMainWindow from QWidget

I have a QMainWindow Application which also includes an QStackedWidget.
The pages of the QstackedWidget are promoted to ui widgets for example heating_widget.ui
If I use a button slot on my QMainWindow I can use this to get my application to fullscreen:
void SmartHome::on_fullscreen_on_clicked()
{
SmartHome::setWindowState(Qt::WindowFullScreen);
}
But how can I do this from a button which is in the heating_widget.cpp file?
Using:
void heating_widget::on_fullscreen_on_clicked()
{
SmartHome::setWindowState(Qt::WindowFullScreen);
}
obviously doesn't work and throws this error at me:
cannot call member function 'void
QWidget::setWindowState(Qt::WindowStates)' without object
SmartHome::setWindowState(Qt::WindowFullScreen);
I know this has something to do with parent() but I can't get it to work.
Do you have any idea?
My smarthome.h file:
#ifndef SMARTHOME_H
#define SMARTHOME_H
#include <QTime>
#include <QMainWindow>
namespace Ui {
class SmartHome;
}
class SmartHome : public QMainWindow
{
Q_OBJECT
public:
explicit SmartHome(QWidget *parent = 0);
~SmartHome();
private slots:
void on_Info_Button_clicked();
void on_News_Button_clicked();
void on_Heating_clicked();
void timerslot();
void on_Config_clicked();
void on_About_clicked();
public slots:
void setFullscreen();
private:
Ui::SmartHome *ui;
QTimer* myTimer;
};
#endif // SMARTHOME_H
My heating_widget.h :
#ifndef HEATING_WIDGET_H
#define HEATING_WIDGET_H
#include "smarthome.h"
#include <QWidget>
namespace Ui {
class heating_widget;
class SmartHome;
}
class heating_widget : public QWidget
{
Q_OBJECT
public:
explicit heating_widget(QWidget *parent = 0);
~heating_widget();
private slots:
void on_fullscreen_on_clicked();
private:
Ui::heating_widget *ui;
};
#endif // HEATING_WIDGET_H
and my heating.widget.cpp:
#include "heating_widget.h"
#include "ui_heating_widget.h"
#include "smarthome.h"
#include "iostream"
heating_widget::heating_widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::heating_widget)
{
ui->setupUi(this);
QObject::connect(ui->fullscreen_on, SIGNAL(clicked()), this , SLOT(SmartHome::setFullscreen()));
}
heating_widget::~heating_widget()
{
delete ui;
}
void heating_widget::on_fullscreen_on_clicked()
{
parentWidget()->setWindowState(Qt::WindowFullScreen);
std::cout<<"clicked"<<std::endl;
}
I would do it in the following way:
void heating_widget::on_fullscreen_on_clicked()
{
foreach(QWidget *widget, QApplication::topLevelWidgets()) {
if (auto mainWindow = qobject_cast<SmartHome *>(widget)) {
mainWindow->setWindowState(Qt::WindowFullScreen);
}
}
}
The idea is finding your main window among application top level widgets and change its state. This code can be called from anywhere in your application regardless of the windows hierarchy.

Qt emit signal from a class to class

I've tried to emit a custom signal login() from my loginmanager class to the mainwindow. The signal is fired on the loginButtonClicked slot, and to my understand on the signal/slot mechanism, it should be able to capture any signal fired event and "look" for the corresponding slot to be execute. But it doesn't work as what I've think.
The connect function returns 1, which means it is able to be implemented in the moc file, and it DOES work if i run the m_LoginManager->setLogin() which fires the login() signal.
But what I prefer is the signal is emitted by the loginButton, and pass to the mainwindow to be process (in this case, init()).
Please correct me if I'm wrong.
Below are the code.
loginmanager.cpp
LoginManager::LoginManager(QWidget * parent) : QWidget(parent)
{
ui.setupUi(this);
connect(ui.loginButton, SIGNAL(clicked()), this, SLOT(loginButtonClicked());
}
LoginManager::~LoginManager()
{
}
void LoginManager::setLogin()
{
emit login();
}
void LoginManager::loginButtonClicked()
{
setLogin();
}
loginmanager.hpp
#include <QWidget>
#include "ui_loginmanager.h"
class DatabaseManager;
class SettingManager;
class LoginManager : public QWidget
{
Q_OBJECT
public:
LoginManager(QWidget * parent = Q_NULLPTR);
~LoginManager();
void setLogin();
signals:
void login();
public slots:
void loginButtonClicked();
private:
Ui::LoginManager ui;
};
mainwindow.hpp
#include <QtWidgets/QMainWindow>
#include "ui_safeboxmanager.h"
class SafeboxManager : public QMainWindow
{
Q_OBJECT
public:
SafeboxManager(QWidget *parent = 0);
~SafeboxManager();
public slots:
void init();
private:
Ui::SafeboxManagerClass ui;
LoginManager* m_LoginManager;
};
#endif // SAFEBOXMANAGER_H
mainwindow.cpp
#include "safeboxmanager.hpp"
#include "loginmanager.hpp"
SafeboxManager::SafeboxManager(QWidget *parent)
: QMainWindow(parent)
{
ui.setupUi(this);
m_LoginManager = new LoginManager();
ui.mainToolBar->setEnabled(false);
ui.tableWidget->setEnabled(false);
connect(m_LoginManager, SIGNAL(login()), this, SLOT(init()));
//m_LoginManager->setLogin() << this work
}
SafeboxManager::~SafeboxManager()
{
}
void SafeboxManager::init()
{
ui.mainToolBar->setEnabled(true);
ui.tableWidget->setEnabled(true);
}
SafeboxManager and LoginManager objects must live long enough. Check life times.

Pass custom C++ object to Qml error (no properties)

I am trying to write code that will pass some data from C++ engine to the Qml scripts via signal, but it looks like that I doing some thing wrong, because when I receive signal in Qml my object don't any method or properties! Look at that code:
Signaller - class that invoke signal:
signaller.h:
class Signaller : public QObject
{
Q_OBJECT
public:
explicit Signaller(QObject *parent = 0);
Q_INVOKABLE void invokeSignal();
signals:
void mysignal(TestClass test);
public slots:
};
signaller.cpp:
Signaller::Signaller(QObject *parent) :
QObject(parent)
{
}
void Signaller::invokeSignal()
{
TestClass s;
emit mysignal(s);
}
TestClass - class that will be passed to Qml engine, and which must have test method in Qml script:
Test.h:
class TestClass : public QObject
{
Q_OBJECT
public:
explicit TestClass(QObject *parent = 0);
TestClass(const TestClass& obj);
~TestClass();
Q_INVOKABLE void test();
signals:
public slots:
};
Q_DECLARE_METATYPE(TestClass)
Test.cpp:
TestClass::TestClass(QObject *parent) :
QObject(parent)
{
qDebug()<<"TestClass::TestClass()";
}
TestClass::TestClass(const TestClass &obj) :
QObject(obj.parent())
{
qDebug()<<"TestClass::TestClass(TestClass &obj)";
}
TestClass::~TestClass()
{
qDebug()<<"TestClass::~TestClass()";
}
void TestClass::test()
{
qDebug()<<"TestClass::test";
}
Those 2 classes also registered in main function:
int main(int argc, char *argv[])
{
// SailfishApp::main() will display "qml/template.qml", if you need more
// control over initialization, you can use:
//
// - SailfishApp::application(int, char *[]) to get the QGuiApplication *
// - SailfishApp::createView() to get a new QQuickView * instance
// - SailfishApp::pathTo(QString) to get a QUrl to a resource file
//
// To display the view, call "show()" (will show fullscreen on device).
qmlRegisterType<TestClass>("Test", 1, 0, "Test");
qmlRegisterType<Signaller>("Signaller", 1, 0, "Signaller");
return SailfishApp::main(argc, argv);
}
That is my Qml file with test code:
import Signaller 1.0
Page {
Signaller {
id: sig
Component.onCompleted: sig.invokeSignal()
onMysignal: {
console.log("signal",typeof test);
console.log(typeof test.test);
}
}
}
And the log:
[D] TestClass::TestClass:6 - TestClass::TestClass()
[D] TestClass::TestClass:12 - TestClass::TestClass(TestClass &obj)
[D] TestClass::TestClass:12 - TestClass::TestClass(TestClass &obj)
[D] onMysignal:41 - signal object
[D] onMysignal:42 - undefined
[D] TestClass::~TestClass:18 - TestClass::~TestClass()
[D] TestClass::~TestClass:18 - TestClass::~TestClass()
As you can see from log, TestClass.test field is empty after passing to the Qml.
What I am doing wrong?
You are passing a QObject derived object by value through the signal/slot system - you should never do this (docs). Create it on the heap and send it's pointer through.
In this case you'll probably want it's lifetime controlled via QML, you can set that by calling QQmlEngine::setObjectOwnership( s, QQmlEngine::JavaScriptOwnership).
QObject should not be used by value in signals and slots. Also, It is a bad idea to implement a copy constructor for a QObject subclass when QObject itself hides its copy constructor.
Thus change your signals to pass pointers to QObject and It will be fine. There is a short and good reference on how to communicate between C++ and Qml
ps: You don't need to register classes which declare the Q_OBJECT macro.
This is an example I made for myself for testing C++ <--> QML interaction:
//File: animal.h
#ifndef ANIMAL_H
#define ANIMAL_H
#include <QObject>
class Animal : public QObject
{
Q_OBJECT
Q_PROPERTY(QString animal_name READ get_animal_name WRITE set_animal_name)
public:
explicit Animal(QObject *parent = 0);
QString get_animal_name();
void set_animal_name(QString p_name);
private:
QString animal_name;
};
#endif // ANIMAL_H
//File: animal.cpp
#include "animal.h"
Animal::Animal(QObject *parent) : QObject(parent)
{
}
void Animal::set_animal_name(QString p_name) {
animal_name=p_name;
}
QString Animal::get_animal_name() {
return animal_name;
}
//File: zoo.h
#ifndef ZOO_H
#define ZOO_H
#include <QObject>
class Animal;
class Zoo : public QObject
{
Q_OBJECT
public:
explicit Zoo(QObject *parent = 0);
Q_INVOKABLE Animal* get_animal_by_index(int index);
Q_INVOKABLE void add_animal(Animal *a);
Q_INVOKABLE void dump_animal_info(Animal *a);
Q_INVOKABLE void emit_signal();
signals:
void some_signal(Animal *animal_object);
public slots:
private:
QList<Animal*> animals;
};
#endif // ZOO_H
//File: zoo.cpp
#include <QDebug>
#include "zoo.h"
#include "animal.h"
Zoo::Zoo(QObject *parent) : QObject(parent)
{
Animal *a;
a=new Animal();
a->set_animal_name("Black Bear");
add_animal(a);
a=new Animal();
a->set_animal_name("Gray Wolf");
add_animal(a);
}
Animal* Zoo::get_animal_by_index(int index) {
return animals.at(index);
}
void Zoo::add_animal(Animal *a) {
animals.append(a);
}
void Zoo::dump_animal_info(Animal *a) {
qWarning() << "animal_name=" << a->get_animal_name();
}
void Zoo::emit_signal() {
Animal *a;
a=animals.at(0);
emit some_signal(a);
}
//File: main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include "animal.h"
#include "zoo.h"
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
qmlRegisterType<Zoo>("zoo",1,0,"Zoo");
qmlRegisterType<Animal>("zoo.animal",1,0,"Animal");
QQmlApplicationEngine engine;
engine.load(QUrl(QLatin1String("qrc:/main.qml")));
return app.exec();
}
//File: main.qml
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.0
import zoo 1.0
import zoo.animal 1.0
ApplicationWindow {
visible: true
width: 640; height: 480; title: qsTr("Zoo")
Zoo {
id: zoopark
}
Column {
Button {
text: "Test C++ <--> QML data exchange by Method"
onClicked: {
var animal=zoopark.get_animal_by_index(1);
zoopark.dump_animal_info(animal);
}
}
Button {
text: "Text C++ <--> QML data exchage by Signal"
onClicked: {
zoopark.emit_signal();
}
}
}
Connections {
target: zoopark
onSome_signal: {
console.log('signal received');
console.log('from qml: animal name=' + animal_object.animal_name)
console.log('dumping animal info from c++:')
zoopark.dump_animal_info(animal_object)
}
}
function process_signal() {
console.log('from qml animal name=' + animal_object.animal_name)
zoopark.dump_animal_info(animal_object)
}
}

Refresh QML Listview from Slot

I have problem with the refreshing Listview in QML I looked through many solutions but there is nothing concrete i want from event "onClicked" refresh all listview but how to do this?
sourcecode: http://s000.tinyupload.com/?file_id=86538244635919176055
main.cpp
#include <QApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QDebug>
#include "message.h"
#include "dataobject.h"
int main(int argc, char *argv[]) {
QList<QObject*> dataList;
dataList.append(new DataObject("1"));
dataList.append(new DataObject("2"));
dataList.append(new DataObject("3"));
QApplication app(argc, argv);
QQmlApplicationEngine engine;
Message msg;
msg.setListInstance(&dataList);
auto root_context = engine.rootContext();
root_context->setContextProperty("message",&msg);
//root_context->setContextProperty("myModel", QVariant::fromValue(dataList));
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
QQmlContext *ctxt = new QQmlContext(engine.rootContext());
//ctxt->setContextProperty("myModel", QVariant::fromValue(dataList));
return app.exec();
}
message.h
#ifndef MESSAGE_H
#define MESSAGE_H
#include <QQmlListProperty>
#include <QObject>
class Message : public QObject {
Q_OBJECT
Q_PROPERTY(QQmlListProperty<QObject> myModel READ getList NOTIFY listChanged)
public:
explicit Message(QObject *parent = 0);
~Message();
void setListInstance(QList<QObject *> *dataList){ list = dataList; }
QQmlListProperty<QObject> myModel() const;
public slots:
void refreshLista();
QQmlListProperty<QObject> getList();
private:
QList<QObject *> *list;
signals:
void listChanged();
};
#endif // MESSAGE_H
message.cpp
#include "message.h"
#include "dataobject.h"
#include <QDebug>
Message::Message(QObject *parent):QObject(parent){}
Message::~Message(){}
void Message::refreshLista(){
list->append(new DataObject("44444"));
emit listChanged();
qDebug() << " REFRESH LISTA ";
}
QQmlListProperty<QObject> Message::getList(){
return QQmlListProperty<QObject>(this, *list);
}
dataobject.h
#ifndef DATAOBJECT_H
#define DATAOBJECT_H
#include <QObject>
class DataObject : public QObject { Q_OBJECT
Q_PROPERTY( QString title READ title WRITE setTitle NOTIFY info)
public:
DataObject(QObject * parent = 0 );
DataObject(const QString &_title,QObject * parent=0 );
QString title() const;
void setTitle(const QString &);
signals:
void info();
private:
QString m_id;
QString m_title;
};
#endif // DATAOBJECT_H
dataobject.cpp
#include "dataobject.h"
DataObject::DataObject(QObject * parent): QObject(parent){}
DataObject::DataObject(const QString &_title,QObject * parent)
:QObject(parent),m_title(_title)
{}
QString DataObject::title() const { return m_title;}
void DataObject::setTitle(const QString &title) {
if ( title != m_title ) { m_title = title; emit info();}
}
Two things to do:
Message has to modify the model, therefore Message needs the instance of the model.
When model is changed, emits signal to QML so QML can reload data.
Assume that your Message class is responsible for the model. First, pass the model to Message.
class Message : public QObject {
//...
private:
QList<QObject *> *list;
public:
void setListInstance(QList<QObject *> *dataList){ list = dataList; }
}
//main.cpp
msg.setListInstance(&dataList);
You can easily change the content of model now:
void Message::refreshLista(){ list->append(new DataObject("new")); /*whatever*/}
However, QML won't reload the model because setContextProperty("myModel", QVariant::fromValue(dataList)); cannot emit signals. Remove this line from main.cpp and create a new property in Message instead:
class Message : public QObject {
Q_OBJECT
Q_PROPERTY(QQmlListProperty<QObject> myModel READ getList NOTIFY listChanged)
public:
QQmlListProperty<QObject> getList();
signals:
void listChanged();
}
In the implementation, create a QQmlListProperty and emits property-changed signal when necessary.
void Message::refreshLista(){
list->append(new DataObject("new"));
emit listChanged();
}
QQmlListProperty<QObject> Message::getList(){
return QQmlListProperty<QObject>(this, *list);
}
Finally, the ListView in QML should bind to message.myModel instead of myModel:
ListView {
width: 400; height: 300;
model: message.myModel
//...
}

Mapping a Qt base class signal to a slot in a derived class

I am having a problem with Qt signals and slots. I am just learning Qt but I have lots of C++ experience. I have derived a class from QTreeView and I want to handle the columnResized signal. The slot is never being called and I am seeing this in the 'Application Output':
QObject::connect: No such signal TRecListingView::columnResized(int,int,int) in ../ec5/reclistingwidget.cpp:142
The class declaration looks like this:
class TRecListingView : public QTreeView
{
Q_OBJECT
public:
TRecListingView(QWidget *parent, TTopicPtr topic);
~TRecListingView();
private slots:
void onColumnResized(int index, int oldsize, int newsize);
private:
TRecListingModel *Model = 0;
};
In the constructor I am doing this:
connect(this,SIGNAL(columnResized(int,int,int)),
this,SLOT(onColumnResized(int,int,int)));
I had this working earlier before I implemented the derived class. Then I was mapping the signal to a slot in the parent widget.
I have tried running qmake and rebuilding the project. I also tried this:
QTreeView *tv = this;
connect(tv,SIGNAL(columnResized(int,int,int)),
this,SLOT(onColumnResized(int,int,int)));
columnResized is not a signal, but slot, so you cannot connect to it.
Instead you can connect to the QHeaderView::sectionResized
connect(this->horizontalHeader(),SIGNAL(sectionResized(int,int,int)),
this, SLOT(onColumnResized(int,int,int)));
Because it is not a signal:
From documentation:
void QTreeView::columnResized ( int column, int oldSize, int newSize ) [protected slot]
Try reimplement it:
#include <QTreeView>
#include <QHeaderView>
#include <QTimer>
#include <QDebug>
class TRecListingView : public QTreeView
{
Q_OBJECT
public:
TRecListingView(QWidget *parent=0):
QTreeView(parent)
{
QTimer::singleShot(0, this, SLOT(fixHeader()));
}
public slots:
void fixHeader()
{
QHeaderView *hv = new QHeaderView(Qt::Horizontal, this);
hv->setHighlightSections(true);
this->setHeader(hv);
hv->show();
}
protected slots:
void columnResized(int a, int b, int col)
{
qDebug() << "This is called";
}
public slots:
};
Simple usage:
TRecListingView trec;
QStringList stringList;
stringList << "#hello" << "#quit" << "#bye";
QStringListModel *mdl = new QStringListModel(stringList);
trec.setModel(mdl);
trec.show();
Now it works properly and when you resize header, you'll see many This is called strings.