I'm subclassing QSpinBox and trying to get the sub controls up/down arrows rectangle sizes as demonstrated here with python and pyside. However I get errors when trying to convert my code over to C++.
Here is working python example I'm trying to convert to Qt/C++
import sys
import os
from PySide import QtGui, QtCore
class SuperSpinner(QtGui.QSpinBox):
def __init__(self, parent=None):
super(SuperSpinner, self).__init__(parent)
def contextMenuEvent(self, event):
opt = QtGui.QStyleOptionSpinBox()
self.initStyleOption(opt)
r = QtCore.QRect()
for sc in (QtGui.QStyle.SC_SpinBoxUp, QtGui.QStyle.SC_SpinBoxDown):
r= r.united(self.style().subControlRect(QtGui.QStyle.CC_SpinBox, opt, sc, self))
if r.contains(event.pos()):
self.setValue(0)
self.selectAll()
else:
super(self.__class__, self).contextMenuEvent(event)
def main():
app = QtGui.QApplication(sys.argv)
ex = SuperSpinner()
ex.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
The Error Message in Qt when attempting to compile
Main bits of CPP file:
#include "intsuperspinbox.h"
#include <QMouseEvent>
#include <QStyleOptionSpinBox>
#include <QCommonStyle>
#include <QStyle>
#include <QDebug>
IntSuperSpinBox::IntSuperSpinBox(QWidget *parent) :
QSpinBox(parent)
{
setMouseTracking(true);
}
void IntSuperSpinBox::contextMenuEvent(QContextMenuEvent *event)
{
QStyleOptionSpinBox opt;
opt.initFrom(this);
// ERRORS HERE
QRect upRect(this->style()->subControlRect(QStyle::CC_SpinBox, opt, QStyle::SC_SpinBoxUp, this));
QSpinBox::contextMenuEvent(event);
}
The second parameter to QStyle::subControlRect should be of type QStyleOptionComplex* so you need...
QRect upRect(this->style()->subControlRect(QStyle::CC_SpinBox, &opt, QStyle::SC_SpinBoxUp, this));
Note &opt rather than simply opt.
Related
I have a bit of a strange error being caused by a seemingly simple problem.
In my source code I am attempting to use QQuickPaintedItem to render just the overall look of a QWidget derived class (QPushButton), and then painting it on to the QQuickPaintedItem
In doing so I ran into this error:
QQmlComponent: Created graphical object was not placed in the graphics
scene.
Followed by:
The program has unexpectedly finished.
Here's the context I am trying to create my special QQuickPaintedItem in along with the source code for it:
main.qml
import QtQuick 2.9
import com.particletool 1.0
import QtQuick.Particles 2.0
import QtQuick.Window 2.3
import QtQuick.Controls 2.2
import QtQuick.Dialogs 1.2
import QtQuick.Controls 1.4 as QQ1
Item {
id: root
width: Screen.width
height: Screen.height
WidgetInterface {
id: w
}
}
widgetInterface.h
#ifndef WIDGETINTERFACE_H
#define WIDGETINTERFACE_H
#include <QObject>
#include <QQuickItem>
#include <QQuickPaintedItem>
#include <QWidget>
#include <QPushButton>
#include <QtQuick>
class WidgetInterface : public QQuickPaintedItem
{
public:
WidgetInterface(QQuickItem *parent = nullptr, QWidget* renderWidget = nullptr);
QWidget* m_widget;
QPushButton* m_button;
protected:
void paint(QPainter* painter);
public slots:
void morphIntoButton(QString txt);
};
#endif // WIDGETINTERFACE_H
widgetinterface.cpp
#include "widgetinterface.h"
#include <QQuickPaintedItem>
#include <QObject>
#include <QPainter>
#include <QWidget>
#include <QPushButton>
WidgetInterface::WidgetInterface(QQuickItem *parent, QWidget* renderWidget) : QQuickPaintedItem(parent), m_widget(renderWidget)
{
morphIntoButton("test");
QQmlEngine::setObjectOwnership(m_button, QQmlEngine::JavaScriptOwnership);
}
void WidgetInterface::paint(QPainter *painter)
{
if (m_widget != nullptr) {
painter->end();
m_button->render(painter);
} else {
}
}
void WidgetInterface::morphIntoButton(QString txt)
{
m_widget->setGeometry(0,0, this->width(), this->height());
m_button = new QPushButton(m_widget);
m_button->setGeometry(m_widget->geometry());
m_button->setText(txt);
this->update(this->boundingRect().toRect());
}
main.cpp
#include <QtGui/QGuiApplication>
#include <QApplication>
#include <QtQml/QQmlApplicationEngine>
#include "src/interfaces/widgetinterface.h"
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
qmlRegisterType<WidgetInterface>("com.particletool", 1, 0, "WidgetInterface");
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
The expected result is a button being drawn on to the QML scene graph
Does anyone know how I can achieve this using some method? ( I know that its not supported by Qt, but I am trying to implement a way to make it happen so dont both with the "You can't" answers because there is a way I'm sure.
The expected result is a button being drawn on to the QML scene graph
class WidgetInterface : public QQuickPaintedItem
{
public:
WidgetInterface(QQuickItem *parent = Q_NULLPTR) :
QQuickPaintedItem(parent)
{
mButton = new QPushButton("TEST", Q_NULLPTR);
auto resize = [=](){
mButton->setGeometry(QRect(QPoint(), boundingRect().size().toSize()));
QPixmap p(mButton->size());
mButton->render(&p);
if(!p.isNull())
mButtonPix = p;
update();
};
connect(this, &QQuickPaintedItem::widthChanged, this, resize, Qt::QueuedConnection);
connect(this, &QQuickPaintedItem::heightChanged, this, resize, Qt::QueuedConnection);
}
~WidgetInterface() {
mButton->deleteLater();
}
protected:
void paint(QPainter* painter){
painter->drawPixmap(0, 0, mButtonPix);
}
private:
QPushButton* mButton;
QPixmap mButtonPix;
};
I'm trying to make an app where you can draw with your finger on a canvas.
To achieve this, I'm subclassing QWidget as MFCanvas, registered the class in QML with
qmlRegisterType<>(), implementing the virtual paintEvent(); function, and
drawing on it with a QPainter inside the paintEvent(); function.
The Problem:
Upon construction, the QPainter throws this warning:
QWidget::paintEngine: Should no longer be called
Then, serveral other related warnings are thrown:
QPainter::begin: Paint device returned engine == 0, type: 1
QPainter::setPen: Painter not active
No wonder: the QPainter didn't draw anything...
Also, am i supposed to call paintEvent(); by myself?
Or should it be called every frame by QWidget, and i somehow messed it up?
I searched the web, but all posts i found had either no answer to them, or they where
using something else than QWidget.
My Code:
mfcanvas.cpp:
#include "mfcanvas.h"
#include <QDebug>
#include <QPainter>
#include <QVector2D>
#include <QList>
MFCanvas::MFCanvas(QWidget *parent) : QWidget(parent)
{
paths = new QList<QList<QVector2D>*>();
current = NULL;
QWidget::resize(100, 100);
}
MFCanvas::~MFCanvas()
{
delete paths;
}
void MFCanvas::paintEvent(QPaintEvent *)
{
if(current!=NULL){
if(current->length() > 1){
QPainter painter(this);
painter.setPen(Qt::black);
for(int i = 1; i < current->length(); i++){
painter.drawLine(current->at(i-1).x(), current->at(i-1).y(), current->at(i).x(), current->at(i).y());
}
}
}
}
void MFCanvas::pressed(float x, float y)
{
if(current==NULL){
qDebug() << "null:"<<current;
current = new QList<QVector2D>();
current->append(QVector2D(x, y));
}else{
qDebug() << "current:"<<current;
}
paintEvent(NULL);
}
void MFCanvas::update(float x, float y)
{
current->append(QVector2D(x, y));
}
void MFCanvas::resize(int w, int h)
{
QWidget::resize(w, h);
}
main.cpp:
#include <QApplication>
#include <QQmlApplicationEngine>
#include <QtQml>
#include <QSurfaceFormat>
#include "creator.h"
#include "mfcanvas.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
qmlRegisterType<MFCanvas>("com.cpp.mfcanvas", 1, 0, "MFCanvas");
QQmlApplicationEngine engine;
QQmlComponent *component = new QQmlComponent(&engine);
QObject::connect(&engine, SIGNAL(quit()), QCoreApplication::instance(), SLOT(quit()));
Creator creator(component);
QObject::connect(component, SIGNAL(statusChanged(QQmlComponent::Status)), &creator, SLOT(create(QQmlComponent::Status)));
component->loadUrl(QUrl("qrc:///main.qml"));
int rv;
rv = app.exec();
delete component;
return rv;
}
creator.cpp:
#include "creator.h"
#include <QQuickWindow>
#include <QDebug>
Creator::Creator(QQmlComponent *component)
{
this->component = component;
}
void Creator::create(QQmlComponent::Status status)
{
if(status == QQmlComponent::Ready){
QObject *topLevel = component->create();
QQuickWindow::setDefaultAlphaBuffer(true);
QQuickWindow *window = qobject_cast<QQuickWindow *>(topLevel);
QSurfaceFormat surfaceFormat = window->requestedFormat();
window->setFormat(surfaceFormat);
window->show();
}
}
main.qml: (the important part)
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Controls.Styles 1.2
import QtQuick.Layouts 1.1
import QtQuick.Window 2.0
import com.cpp.mfcanvas 1.0
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("MFCanvas")
onSceneGraphInitialized: {
drawMenu.visible = true;
lineWidth.visible = true;
colorMenu.visible = true;
drawMenu.visible = false;
lineWidth.visible = false;
colorMenu.visible = false;
}
Rectangle {
id: main
anchors.fill: parent
property real toolsH: 15
property real iconW: 25
property real menuH: 8
property real menuW: 16
property real dpi: (Screen.logicalPixelDensity == undefined ? 6 : Screen.logicalPixelDensity) * 1.5
property color choosenColor: Qt.hsla(hue.value, saturation.value, luminance.value, 1)
Text {
anchors.centerIn: parent
font.pointSize: 60
text: "MFCanvas"
}
MFCanvas {
id: canvas
Component.onCompleted: {
canvas.resize(main.width, main.height);
}
}
//...
}
}
Tell me if you need any additional information.
Thank you in advance! =)
This is nicely explained here:
https://forum.qt.io/topic/64693
In short: do not try to paint from the input event handler directly,
but overload the paintEvent method in your widget instead and create the
QPainter there. Use the input event exclusively to modify the internal
data model and use QPainter in paintEvent to display it, on the output path.
In your mfcanvas.cpp, void MFCanvas::pressed(float x, float y) function, the line
paintEvent(NULL);
seems to be disturbing. Tried it in a similar code - I get the same error.
Proposed solution: using this->repaint() or this->update() instead of paintEvent(NULL) to repaint a widget seems to be more appropriate.
Possible explanation: looks like paintEvent() shouldn't be called this straightforward (like paintEvent() is called when paint() function is called). As far as I understand from the QPainter doc, the QPainter works together with the QPaintDevice and the QPaintEngine, these three form the basis for painting. The error QWidget::paintEngine: Should no longer be called puts it quite straight. The lines
QPainter::begin: Paint device returned engine == 0, type: 1
QPainter::setPen: Painter not active
probably indicate that there's no QPaintEngine provided by this painter's QPaintDevice (like QPaintDevice::paintEngine). One can assume that this QPaintEngine is generated or otherwise called to existence by the paint device itself, for example, when the paint() function is called on a widget.
I have found a simple solution myself:
Instead of deriving from QWidget, derive from QQuickPaintedItem. QQuickPaintedItem is a class that was made exactly for what i need: Painting on a QML-Element using a QPainter. Here is the Code (Narrowed down to the essential part):
mfcanvas.h:
class MFCanvas : public QQuickPaintedItem
{
Q_OBJECT
public:
explicit MFCanvas(QQuickItem *parent = 0);
~MFCanvas();
protected:
void paint(QPainter *painter);
mfcanvas.cpp:
void MFCanvas::paint(QPainter *painter)
{
painter->translate(-translation.x(), -translation.y());
//...
}
As you can see, a simple paint() function is provided which hands over a pointer to a QPainter, ready to use. =)
I'm a little new to QML and C++ in general, but I'm learning quick. I'm a bit stumped on something though. I'm trying to make an application that reads text from a QML TextInput text field, and writes the data to a text area. I'm not sure what is going wrong, but upon reading the text I get the error "Unable to assign QQuickTextInput to QString" - and I'm a bit stumped on how to write back to a text area. If someone could shed some light, that would be appreciated. I've already been through the QML/C++ binding page, and that doesn't seem to help. I'm wondering if that page was written for an older version of Qt... Here's my code thus far:
QML:
import QtQuick 2.0
import QtMultimedia 5.0
import QtQuick.Controls 1.1
import QtQuick.Window 2.1
import QtWinExtras 1.0
import QtQuick.Layouts 1.1
Rectangle {
id: main
width: 640
height: 480
signal onClicked_button
TextInput {
id: textbocks
objectName: textbocks
x: 280
y: 230
width: 80
height: 20
text: "Text Input"
font.pixelSize: 12
Button {
id: button1
x: -10
y: 45
text: "Button"
onClicked:main.onClicked_button()
}
}
TextField {
id: textField1
x: 257
y: 329
placeholderText: qsTr("Text Field")
}
}
header (class - originally designed to write back to text input):
#ifndef TEST_H
#define TEST_H
#include <QObject>
#include <QtCore>
#include <QString>
#include <QQuickView>
#include <QQuickItem>
#include <QQmlComponent> // for accessing QML from C++
#include <QQmlContext> // for accessing C++ from QML
class test : public QObject
{
Q_OBJECT
public:
explicit test(QObject *parent = 0);
signals:
public slots:
void test_button(){
QQuickView *view = new QQuickView(QUrl("test.qml"));
view->show();
QQuickItem *item = view->rootObject();
QObject *item = object->findChild<QObject*>("textbocks");
textbox->setProperty("text", str);
delete textbox;
return;}
private:
bool switched;
bool textOutput;
QString str = "Hello World";
};
#endif // TEST_H
And main .cpp file:
#include "test.h"
#include <QGuiApplication>
#include <QtCore>
#include <QObject>
#include <QQuickItem>
#include <QQmlComponent>
#include <QQmlContext>
#include <QQuickView>
#include <QDeclarativeEngine>
#include <QDeclarativeComponent>
#include <QDeclarativeContext>
test::test(QObject *parent) :
QObject(parent)
{
}
int main (int argc, char*argv[]) {
QGuiApplication app(argc, argv);
QQuickView *view = new QQuickView(QUrl("test.qml"));
view->show();
QQuickItem *item = view->rootObject();
test *funcs = new test();
// connect button signals to their slots:
QObject::connect(item, SIGNAL(buttonClicked_button()), funcs, SLOT(test_button()));
delete funcs;
return 0;
}
Any help would be appreciated. Some of this is still a bit abstract for me... Thanks!!
as I noticed you are creating two QQuickViews from the same QML file "test.qml" :
1.In the main function (main.cpp)
2.and in the TEST_BUTTON function (test.h)
which are different QObject .
Another issues are that :
you are assigning an object of type TextInput to the objectName property (test.qml) which is a String
you are deleting (as Yekmen mentioned earlier) the object (textbox (test.h) = textbocks (test.qml)) which in the mean time you are assigning to it the string str="hello world" via setProperty() function .
Ahh forgot one, the slot name in C++ side which must be identical to the one declared in QML
I think you should look this method :
Connections {
target: area
onClicked: foo(parameters)
}
For example : area is your c++ object.( qmlRegisterType(myclass) )
area send a signal and after you can lauch your function.
I think is more easy.
Maigcally I can't inherit from QWidget using PyQt 4:
from PyQt4.QtGui import QApplication, QMainWindow, QWidget
class MyWidget(QWidget):
pass
if __name__ == "__main__":
app = QApplication([])
window = QMainWindow()
window.resize(200, 200)
widget1 = MyWidget(window)
widget1.resize(100, 100)
widget1.setStyleSheet("background-color:#FFFFFF")
window.show()
app.exec_()
It doesn't work. I just can't see the widget. But, using QLabel instead of QWidget works.
Thanks.
Solution
Reimplement paintEvent:
class MyWidget(QWidget):
def paintEvent(self, event):
o = QStyleOption()
o.initFrom(self)
p = QPainter(self)
self.style().drawPrimitive(QStyle.PE_Widget, o, p, self)
You are subclassing correctly. The "problem" is just that the QWidget has the same background colour as the main window (your call to setStyleSheet is not working).
As proof, run this code from a terminal:
from PyQt4.QtGui import QApplication, QMainWindow, QWidget, QLabel
class MyWidget(QWidget):
def enterEvent(self, evt):
print 'a'
if __name__ == "__main__":
app = QApplication([])
window = QMainWindow()
window.resize(200, 200)
widget1 = MyWidget(window)
widget1.resize(100, 100)
widget1.setStyleSheet("background-color:#FFFFFF")
window.show()
app.exec_()
When you move your mouse into the first 100,100 pixel square, you will see the letter 'a' being printed in the terminal
I create a Sailfish app (using latest Sailfish SDK). I have a problem with exposing a C++ object to QML. It inherits QSettings,
class Settings : public QSettings
{
Q_OBJECT
/**/
public:
explicit Settings() : QSettings("Marcin Mielniczuk", "BigText") {}
~Settings() { qDebug() << "Dying"; }
/**/
};
I noticed that the destructor isn't called at all. (there's not destructor output)
I create the object like that:
import QtQuick 2.0
import Sailfish.Silica 1.0
import BigText 1.0
import "pages"
ApplicationWindow
{
initialPage: MainPage { }
Settings {id: settings}
}
My main.cpp is:
Q_DECL_EXPORT int main(int argc, char *argv[])
{
QScopedPointer<QGuiApplication> app(Sailfish::createApplication(argc, argv));
qmlRegisterType<Settings>("BigText", 1, 0, "Settings");
QScopedPointer<QQuickView> view(Sailfish::createView("main.qml"));
Sailfish::showView(view.data());
return app->exec();
}
What am I doing wrong?
/edit: Text not being printed isn't an actual problem - it's just an indicator of the problem. The QSettings sycing in the destructor doesn't work too.
EDIT2: Please note, that ApplicationWindow in I'm using Sailfish Silica, not QtQuick.Controls, and the window is shown ok. These components must be a somewhat different to the stock qt quick components.
There's nothing inherently wrong with your logic. Here is a simplified version of it. I can run it locally, and consistently get a Dying message on output every time the window is closed and the application terminates.
If you cannot figure it out, I suggest transforming this code into what you're doing until it fails.
By the way, this is surely just a snippet of something larger you're doing, but at least as far as the example goes, these scoped pointers aren't doing much.
main.qml
import QtQuick 2.0
import BigText 1.0
Item {
width: 300; height: 300
Settings {id: settings}
}
main.cpp
class Settings : public QSettings
{
Q_OBJECT
public:
Settings() : QSettings("Marcin Mielniczuk", "BigText") {}
~Settings() { qDebug() << "Dying"; }
};
int main(int argc, char *argv[])
{
QScopedPointer<QGuiApplication> app(new QGuiApplication(argc, argv));
qmlRegisterType<Settings>("BigText", 1, 0, "Settings");
QScopedPointer<QQuickView> view(new QQuickView());
view->setSource(QUrl::fromLocalFile("main.qml"));
view->show();
return app->exec();
}
You could try to put and id yo tour ApplicationWindow and explicity use the destroy() method at some time and see what happens.
You cannot use QQuickView with an ApplicationWindow class. Not only is your destructor not called, your constructor isn't either, because the loading doesn't ever succeed.
The code below works fine under Qt 5.1.1. Tested on both OS X 10.8 and Windows 7.
main.qrc
<RCC>
<qresource prefix="/">
<file>main.qml</file>
</qresource>
</RCC>
main.pro
QT += core gui qml quick
TARGET = qml-appwin-end-18597527
TEMPLATE = app
SOURCES += main.cpp
OTHER_FILES += main.qml
RESOURCES += main.qrc
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QSettings>
#include <QQuickWindow>
#include <QtQml>
#include <QDebug>
class Settings : public QSettings
{
Q_OBJECT
public:
Settings() : QSettings("Marcin Mielniczuk", "BigText") {}
~Settings() { qDebug() << "Dying"; }
};
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
qmlRegisterType<Settings>("BigText", 1, 0, "Settings");
engine.load(QUrl("qrc:/main.qml"));
QObject *topLevel = engine.rootObjects().value(0);
QQuickWindow *window = qobject_cast<QQuickWindow *>(topLevel);
window->show();
return app.exec();
}
#include "main.moc"
main.qml
import QtQuick 2.0
import QtQuick.Controls 1.0
import BigText 1.0
ApplicationWindow {
width: 300; height: 300
Settings {id: settings}
}
Nothing is wrong with my code. It's something with the SDK. Debugging shows
ASSERT: "QThread::currentThread() == QCoreApplication::instance()->thread()" in file debugger/qqmldebugserver.cpp, line 576
Then the program is aborted and therefore destructor not called.