Signal/Slot interaction for two plugins in Qt - c++

So basically i have a little application that loads two plugins and connect them. First plugin after it's loaded it creates a label without any title which will be added to main window. Second plugin creates an action which will be added to a menu. So my app need just to load these plugins and to connect them . What i mean by connecting them ? i mean that label plugin contains a slot which will modify label's title , and action plugin has a signal declared. Application should connect action plugin signal with label slot. I do not know how to do it exactly. My guess is that in action plugin class implementation is to connect custom signal with a standart signal (triggered). But anyway this ways my app is not working as i expected. How can i make a correct connection in my app for a signal from one plugin and slot from another plugin ??
Here is my code for Label Plugin :
#include "LabelInterface.h"
class LabelPlugin : public LabelInterface {
Q_OBJECT
Q_INTERFACES(LabelInterface)
public:
QLabel* label;
QLabel* newLabel();
LabelPlugin() {}
~LabelPlugin() {}
public slots:
void setTextforLabel();
};
#include <QtGui>
#include "LabelPlugin.h"
QLabel* LabelPlugin::newLabel() {
label = new QLabel("");
return label;
}
void LabelPlugin::setTextforLabel() {
label->setText("This plugin works fine");
}
// Exporta plugin-ul
Q_EXPORT_PLUGIN2 (labelplugin,LabelPlugin)
Action Plugin :
#include "ActionInterface.h"
class ActionPlugin : public ActionInterface {
Q_OBJECT
Q_INTERFACES (ActionInterface)
public :
QAction* myAction;
QAction* newAction();
~ActionPlugin () {}
ActionPlugin () {}
public slots:
void send_signal();
signals :
void pushMyAction();
};
#include <QtGui>
#include "ActionPlugin.h"
QAction* ActionPlugin::newAction() {
myAction = new QAction("Show text",this);
return myAction;
}
void ActionPlugin::send_signal() {
qDebug ()<<"Here";
QAction::connect (this,SIGNAL(pushMyAction()),this,SIGNAL(triggered()));
}
Q_EXPORT_PLUGIN2 (actionplugin,ActionPlugin)
In my app , where i try to load plugins i have :
foreach (QString fileName, appDir.entryList(QDir::Files)) {
qDebug()<<QString(fileName);
QPluginLoader pluginLoader(appDir.absoluteFilePath(fileName));
QObject* plugin = pluginLoader.instance();
if (plugin) {
ActionInterface* myAction= qobject_cast<ActionInterface*>(plugin);
if (myAction) {
action_ = myAction;
pluginMenu->addAction(action_->newAction());
}
LabelInterface* myLabel = qobject_cast<LabelInterface*>(plugin);
if (myLabel) {
label_=myLabel;
layout->addWidget(label_->newLabel());
}
if (action_ && label_) {
qDebug()<<"both loaded";
action_->send_signal();
connect(action_, SIGNAL(pushMyAction()),label_, SLOT(setTextforLabel()));
}
else qDebug() << "seems one plugin is not loaded";
}
}

You need to be able to access a QObject instance from each plugin so you can use it in the connect call. I would add methods to your interfaces to do this. One pattern I've seen is an operator to convert the interface to a QObject pointer, like:
class MyInterface {
public:
virtual operator QObject*() = 0;
};
Opinions may vary on whether that's good style, but it solves the problem (if you don't like the operator, use a method called asQObject() or similar).

Related

Qt/C++ : get Ui QLineEdit text from another class

I'm trying to make a simple test to use an UI object made with "Qt Design" but I'm pretty new to Qt and C++.
I've got a quite simple Ui : 3 LineEdits and 1 PushButton :
IMAGE : the UI window
I've a Client Class which is supposed to control Ui. It connects the QPushButton and it should get the content from QLineEdit.
But the result in QDebug is always the same when I push the Button, even when I change QlineEdit field: "Client connected : "" : 0 "
Moreover, if I use on_pushButton_clicked made with QtDesign, it will display the real values of QlineEdits.
Why the QStrings are always the same ? Am I passing a copy of the initial object ? How to solve that ?
Is it the good way to make a ViewController ? Else, what is the good way?
Client.cpp
#include "client.h"
#include "mainwindow.h"
#include "logwindow.h"
Client::Client()
{
LogWindow* w1 = new LogWindow();
MainWindow* w2 = new MainWindow();
_stack = new QStackedWidget();
_stack->addWidget(w1);
connect(w1->getButton(),SIGNAL(clicked()),this,SLOT(connexion()));
_stack->addWidget(w2);
_stack->show();
}
//When the button is Pushed, gets the content from QlineEdits and prints them
void Client::connexion()
{
QString ip=(LogWindow (_stack->currentWidget())).getIP();
int port=((LogWindow (_stack->currentWidget())).getPort()).toInt();
socket = new QTcpSocket(this);
socket->connectToHost(ip, port);
_stack->setCurrentIndex((_stack->currentIndex()+1)%_stack->count());
qDebug() <<"Client connected : " << ip << ":"<<port;
}
And a class made automatically by Qt :
LogWindow.cpp
#include "logwindow.h"
#include "ui_logwindow.h"
LogWindow::LogWindow(QWidget *parent) :
QWidget(parent),
ui(new Ui::LogWindow)
{
ui->setupUi(this);
}
LogWindow::~LogWindow()
{
delete ui;
}
QPushButton* LogWindow::getButton()
{
return ui->pushButton;
}
QString LogWindow::getIP()
{
//LineEdit named "IP_text"
return ui->IP_text->text();
}
QString LogWindow::getPort()
{
//LineEdit named "Port_text"
return ui->Port_text->text();
}
LogWindow.h
namespace Ui {
class LogWindow;
}
class LogWindow : public QWidget
{
Q_OBJECT
public:
explicit LogWindow(QWidget *parent = 0);
~LogWindow();
QPushButton* getButton();
QString getIP();
QString getPort();
private slots:
void on_pushButton_clicked();
private:
Ui::LogWindow *ui;
};
Thuga solved it :
In Client::connexion you are creating a new instance of LogWindow.
Make LogWindow* w1 a member variable of your Client class, if you want
to access it in other Client's member functions as well.
There is not much to complain about, except that _stack is a widget
without a parent, so you must make sure you destroy it when you don't
need it anymore (for example call delete _stack; in the destructor).
Most beginners would have tried to make the ui variable public to get
the data from IP_text, but you did correctly, by making the
LogWindow::getIP function.
If you don't want to expose ui->pushButton outside of your class, you
can make a signal for your LogWindow class, and connect the clicked
signal of ui->pushButton to that signal. You can connect signals to
signals, it doesn't have to be a slot.

setVisible return false when QWidget is integrated in QQuickPaintedItem

I was facing one issue with the Widget which is integrated in the QQuickPaintedItem class. When I have Widget integrated in the QQuickPaintedItem, QWidget::isVisible will return false. If I tried to set QWidget::setVisible(true) then it will open another window, which I do not want in my scenario.
Is there any way to get QWidget::isVisible return true so that my child widgets (In my actual scenario, we have 5 layer of parent child hierarchy) will also works fine when I say QWidget::show()?
I have created the scenario similar to it as below.
Header file:
class MyItem: public QQuickPaintedItem{
Q_OBJECT
public:
explicit MyItem(QQuickItem *parent = 0);
void paint(QPainter *painter);
~MyItem();
Q_INVOKABLE void initButton();
protected:
virtual void mousePressEvent( QMouseEvent* event );
private:
QPushButton* bp;
};
source file:
MyItem::MyItem(QQuickItem *parent)
: QQuickPaintedItem(parent)
{
bp = new QPushButton("Hello");
}
MyItem::~MyItem()
{
delete bp;
}
void MyItem::paint(QPainter *painter){
bp->render(painter, QPoint(), QRegion(), QPushButton::DrawWindowBackground | QPushButton::DrawChildren);
}
void MyItem::mousePressEvent( QMouseEvent* event )
{
qDebug() << Q_FUNC_INFO << bp->isVisible();
}
Thanks for help in advance...!!!
I don't know why you want to do this.
Qt do not support to embed a QWidget into a Qt Quick Item in Qt5(Qt Quick 2).
In your code, QWidget is a seperate Window, and you Qt Quick item is in it's own Window.
If you want your Qt Quick item behavior like a Button, you should use Qt Quick's Button control or write one yourself.
If you really want to embed a QWidget into Qt Quick's control tree, you can use Qt Quick 1(Qt4.7/8) instead. Check out QGraphicsProxyWidget's document.

how to access events of a specific QML control from c++

Is there a way of accessing signals(such as clicked()) of a QML control such as a button, from c++. Assume that I have the memory address of that specific control. I just want to simulate a click event from c++ code.
Easy. You just create a slot in a C++ object that has a QObject base to it, make sure its registered as a QML type, then you "instantiate" it in the QML document, and connect the desired signal to the C++ object through QML using connect() and handle the logic from the C++ side.
Example:
I have a Rectangle I want to get the onWidthChanged signal from and use it in my class ShapeTracker which tracks when shapes change or whatever
in main.cpp:
#include "shapetracker.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
/* this is where you register ShapeTracker as a QML type that can be
accessed through the QML engine even though its a C++ QObject */
qmlRegisterType<ShapeTracker>("my_cpp_classes", 1, 0, "ShapeTracker");
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
then in
main.qml
import QtQuick 2.6
/* import the C++ class you registered into this QML document */
import my_cpp_classes 1.0
Window {
visible: true
Item {
/* set up a parent item that has a signal which is exposed to
the ShapeTracker object and the Rectangle you want to track */
id: myRootItem
signal rectangleChanged(var newWidth)
/* Heres our special Rectangle from QML that will send a signal when
its width changes */
Rectangle {
id: specialRectangle
width: 250
/* send the signal rectangleChanged(width) from our parent item
root item whenever width changes
*/
onWidthChanged: function() { rectangleChanged(width); }
}
/* Special Button that when clicked enlarges the Rectangle but
10px */
Button {
id: mySpecialButton
onClicked: { click_handler(mouse); }
function click_handler(mouse): {
if (specialRectangle.width < 500)
specialRectangle.width += 10;
}
}
/* Heres the actual ShapeTracker instance, which exists
as a QObject inside of the QML context, but has its methods
which are declared in C++, and exposed to the QML engine */
ShapeTracker {
id: myShapeTracker
/* similar to a constructor, but more like a callback */
Component.onCompleted: {
/* connect our signal from the parent root Item called
"rectangleChanged" to the ShapeTracker's Slot called
"myRectangleChangeHandler" */
myRootItem.rectangleChanged.connect(myShapeTracker.myRectangleChangeHandler);
/* connect send_mouse_click to the click_handler of
the button */
myShapeTracker.send_mouse_click.connect(mySpecialButton.click_handler)
}
}
}
}
in shapetracker.h you simply add a new slot with the name myRectangleChangeHandler and it will receive that signal whenever it is send via QML to be processed via C++
class ShapeTracker : public QObject {
Q_OBJECT
public:
ShapeTracker(QObject *parent = 0 );
signal:
void send_mouse_click(QMouseEvent *event);
public slots:
void myRectangleChangeHandler(QVariant newWidth) {
/* Perform a mouse click on our QML Object mySpecialButton
using QQuickItem::mousePressEvent and sending it via
signal back to QML */
QMouseEvent myEvent(QEvent::MouseButtonPress, QPointF(1,1), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
QMouseEvent* pressEvent = QQuickItem::mousePressEvent(&myEvent);
emit send_mouse_click(pressEvent);
}
};
In Summary, you expose a C++ QObject to QML, then you use
object.signal.connect(cppObject.desired_slot)
To connect them -- all the extra stuff was for a functional example in case anyone needs it later
In reality, you don't even need this functionality because anything happening in an onClick event could just as easily be put into any other property such on
Rectangle {
id: rect
signal customClick(var var1)
onCustomClick : { console.log(var1); }
}
Item {
rect.customClick(1);
}
The easy way would be to call all the receiving SLOTS manually. But that would be tedious and error prone.
You might try implementing a sub-class of QObject that has one slot onClicked() which emits the signal clicked() and use it as a shim between the button and elements controlled by the button. Connect the button clicked() to the new object onClicked() and then connect the new object to the original receivers. Then calling onClicked() would trigger the behavior.
This is a very simple example, and I haven't run it through the compiler.
ButtonShim.hpp
#include <QObject>
class ButtonShim : public QObject {
Q_OBJECT
public:
ButtonShim(QObject *parent = 0);
virtual ~ButtonShim();
public slots:
void onClicked();
signals:
void clicked();
};
ButtonShim.cpp
#include "ButtonShim.hpp"
ButtonShim::ButtonShim(QObject *parent) : QObject(parent) {
}
ButtonShim::~ButtonShim() {
}
void ButtonShim::onClicked() {
// All we do here is emit the clicked signal.
emit clicked();
}
SomeFile.cpp
#include <bb/cascades/Button>
#include "ButtonShim.hpp"
...
ButtonShim * pButtonShim = new ButtonShim(pButton); // pButtonShim will live as long as pButton
bool c = connect(pButton, SIGNAL(clicked()), pButtonShim, SLOT(onClicked()));
c = connect(pButtonShim, SIGNAL(clicked()), pSomeObject, SLOT(onButtonClicked()));
...
// to simulate a click of pButton
pButtonShim->onClicked();
SomeFile.qml
// assuming ButtonShim has been exposed to QML from your application
...
attachedObjects: [
ButtonShim {
id: buttonShim
onClicked: {
clickedLabel.text = "I've been clicked";
}
}
]
...
Label {
id: clickedLabel
text: "I haven't been clicked"
}
Button {
text: "Click Me"
onClicked: {
buttonShim.onClicked();
}
}
I think that you can look at code for tests. There they get object from QML file loaded into engine.
If you have an QObject you can just call signal because, AFAIR
public signals:
void clicked();
is expanded by moc into
public:
void clicked();

Change label text from another class using Qt signals and slots

I'm trying to change text of a class Label from another class. I have class MainWindow, which contains Label.
I also have a Bot class from which I wanna change the value of label.
I'm trying to create signal and slots but I have no idea where to start.
I created signal and slots like so:
//in mainwindow.h
signals:
void changeTextSignal();
private slots:
void changeText();
//in mainwindow.cpp
void MainWindow::changeText(){
this->label->setText("FooBar");
}
But I have no idea how to connect a signal to be able to change Label's text from another class.
Read up on Qt signal-slot mechanism. If I understand you correctly, you are trying to signal from Bot to MainWindow that the Label text needs to change. Here's how you do it...
//bot.h
class Bot
{
Q_OBJECT;
//other stuff here
signals:
void textChanged(QString);
public:
void someFunctionThatChangesText(const QString& newtext)
{
emit textChanged(newtext);
}
}
//mainwindow.cpp
MainWindow::MainWindow
{
//do other stuff
this->label = new QLabel("Original Text");
mybot = new Bot; //mybot is a Bot* member of MainWindow in this example
connect(mybot, SIGNAL(textChanged(QString)), this->label, SLOT(setText(QString)));
}
void MainWindow::hello()
{
mybot->someFunctionThatChangesText("Hello World!");
}

reimplement the shouldInterruptJavaScript() in Qt (C++)

According to the help of Qt for QWebPage [Slot ShoudInteruptJavaScript], located here:
This function is called when a JavaScript program is running for a long period of time.
If the user wanted to stop the JavaScript the implementation should return true; otherwise false.
The default implementation executes the query using QMessageBox::information with QMessageBox::Yes and QMessageBox::No buttons.
Warning: Because of binary compatibility constraints, this function is not virtual. If you want to provide your own implementation in a QWebPage subclass, reimplement the shouldInterruptJavaScript() slot in your subclass instead. QtWebKit will dynamically detect the slot and call it.
I don't want qt show a message when javascript runnig for long period of time.
So, how can i reimplement ShoudInteruptJavaScript? and where should i create it?
Please show me a sample
Thanks
All the info you need is in the documentation.
Create a new custom class that inherits from QWebPage, make sure it's a Q_OBJECT to receive signals.
class MyFunkyPage : public QWebPage {
Q_OBJECT
public slots:
bool shouldInterruptJavaScript() {
QApplication::processEvents(QEventLoop::AllEvents, 42);
// Ignore the error
return false;
}
};
Set the page of your QWebView to a custom subclass of QWebPage.
setPage(new MyFunkyPage());
Then when your page gets this signal it won't stop the script from executing, and it won't show a dialog.
The MyFunkyPage solution potentially leaks memory and causes crashes because the object passed to setPage has no parent and setPage does not take ownership. Instead,
class QWebPageWithoutJsWarning : public QWebPage {
Q_OBJECT
public:
QWebPageWithoutJsWarning(QObject* parent = 0) : QWebPage(parent) {}
public slots:
bool shouldInterruptJavaScript() {
return false;
}
};
Set the page of your QWebView to the custom subclass of QWebPage, parented on the WebView,
void suppressJSWarning(QWebView& webView) {
webView.setPage(new QWebPageWithoutJsWarning(&webView));
}
#anson-mackeracher almost had it right.
Qt needs it to be a private slot, not a public one. Here's what works for my class:
class MyFunkyPage : public QWebPage {
Q_OBJECT
private slots:
bool shouldInterruptJavaScript() {
// Ignore the error (return true to kill the runaway JavaScript)
return false;
}
};
Set the page of your QWebView to a custom subclass of QWebPage.
setPage(new MyFunkyPage());
I just tested this with Qt 4.8.4 and it works like a charm. I didn't need the processEvents call.