Qt Quick Controls 2 hangs when overwriting Button.qml - qt

I'm trying to implement my own style and for this I want to override the built-in objects. Here's what I'm doing:
// main.cpp
QQuickStyle::setStyle("mystyle");
and
// mystyle/Button.qml
import QtQuick 2.5
import QtQuick.Controls 2.1 as Controls
Controls.Button {
background: Rectangle {
color: "green"
}
}
and
// qml.qrc
<RCC>
<qresource prefix="/">
<file>main.qml</file>
<file>mystyle/CheckBox.qml</file>
</qresource>
</RCC>
According to the docs I believe this should work automagically using file selectors.
However, my app hangs on startup. My guess is that I fall into a recursive import. How do I do this correctly?

The Qt Quick Controls 2 styling system is based on QML type registration. When you run your app with mystyle, the type known as QtQuick.Controls.Button IS mystyle/Button.qml. Therefore mystyle/Button.qml cannot inherit QtQuick.Controls.Button. It cannot inherit itself.
This is basically the same as writing the following C++:
// button.h
#include "button.h"
class Button : public Button {};
A bit radicalized, but easy to understand analogy. :)
What you can do is to have (My)Button.qml somewhere, let it inherit QtQuick.Controls.Button, and don't register it as a Qt Quick Controls 2 but simply import the folder. This is what we call as "Customizing Qt Quick Controls 2".

The style must be configured before loading QML that imports Qt Quick Controls.It is not possible to change the style after the QML types have been registered. setStyle()
Apply the existing style in your custom style's qml files.
Note: It is recommended to use QQmlApplicationEngine, which internally creates a QQmlFileSelector instance. This is all that is needed to take QML file selectors into use.
See: Creating Custom Styles

Related

declare global property in QML for other QML files

I want to declare a global property in a config file and use it in other files. for example declare mainbg in:
Style.qml:
property color mainbg: 'red'
and use it in other QML files (like view.qml and main.qml). How can I do this work?
Use a QML Singleton.
Please reference "Approach 2" on this page
-- The ugly QTBUG-34418 comments are mine.
These are the pieces you need:
Style.qml
pragma Singleton
import QtQuick 2.0
QtObject {
property color mainbg: 'red'
}
qmldir
This file must be in the same folder as the singleton .qml file (Style.qml in our example) or you must give a relative path to it. qmldir may also need to be included by the .qrc resource file. More information about qmldir files can be found here.
# qmldir
singleton Style Style.qml
How to Reference
import QtQuick 2.0
import "." // this is needed when referencing singleton object from same folder
Rectangle {
color: Style.mainbg // <- there it is!!!
width: 240; height 160
}
This approach is available since Qt5.0. You need a folder import statement even if referencing the QML singleton in the same folder. If is the same folder, use: import "." This is the bug that I documented on the qt-project page (see QTBUG-34418, singletons require explicit import to load qmldir file).
Basically, if you don't need property binding (if you value is a constant and will not need to be notifiable on change) you can define it in a Javascript shared library, like this :
// MyConstants.js
.pragma library
var mainbg = "red";
And use it in QML like this :
import "MyConstants.js" as Constants
Rectangle {
color: Constants.mainbg;
}
But the bad side of this are :
- no strong typing (JS doesn't really know about types) so you could put anything even if it is not a color.
- and if you change mainbg, the Item using it won't be notified about the change and will keep the old value
So if you need type checking and binding/change notify, simply declare your property as a member of the root object in you main.qml, and it will be accessible from everywhere in the QML application, because the property will in fact be directly registered into the Qml Context object, which is global by definition.
Hope it helps.
You can create a js file and import it to all of the files that have to use this property.
js file:
//Note: you only need '.pragma library' if you are planning to
//change this variable from multiple qml files
.pragma library
var globalVariable = 20;
qml file:
import "test.js" as Global
Rectangle {
id: main
width: 300; height: 400
Component.onCompleted: {
console.log( Global.globalVariable)
//you can also change it
Global.globalVariable = 5
}
}
Adding some contribute to #pixelgrease answer, I found another technique that doesn't require the path relative import ".", workarounding the bug QTBUG-34418. This is useful especially if one has qmldir and singleton class in a different place than the qml file where the singleton is used. The technique requires defining a proper module inside the tree structure: the module is then resolved by adding the parent path of the module to the QML engine with QmlEngine::addImportPath(moduleParentPath). For example:
qml/
├── <ModuleName>/
│ ├── <ClassName>.qml
│ ├── qmldir
In main.cpp you have then:
QQmlApplicationEngine engine;
engine.addImportPath("qrc:/qml"); // Can be any directory
engine.load("qrc:/qml/main.qml");
If you use resources, qml.qrc:
<RCC>
<qresource prefix="/">
(...)
<file>qml/main.qml</file>
<file>qml/MySingletons/MySingleton.qml</file>
<file>qml/MySingletons/qmldir</file>
</qresource>
</RCC>
In qmldir:
module MySingletons
singleton MySingleton 1.0 MySingleton.qml
In main.qml, or any other qml file in a different directory:
import MySingletons 1.0
Then you use MySingleton class as usual. I attached the example MySingletonWithModule.7z to bug QTBUG-34418 for reference.
Add this property in main and you can access it in any qml,this may not be the correct way but this works.
or if you want to group the property add them in a qml
include that qml in main and give an id,now you can access this property using that id
main.qml
Item{
width:10
height:10
Model{
id:globdldata
}
}
Model.qml
Item {
property color mainbg: 'red'
}
you can use globdldata.mainbg anywhere
You can always create a new QML object file that contains the properties that you want shared across qml files. Just import it the same way you would any QML object and you have access to properties. Now, if you want to be able to modify these properties and have the changes shared across instances things get a lot trickier and you will most likely want to resort to some sort of solution using the .pragma library js files. Unless you want to write some sort of C++ alternative.

How to display embedded html in Qt WebView

I am trying to get the Qt WebView to display an html file that is embedded as a Qt resource, but I can't seem to get it to work. I created a new Qt Quick application and added a simple qml file:
import QtQuick 2.0
import QtWebKit 3.0
Rectangle {
id: content
width: 800
height: 600
color: "black"
WebView {
id: webView
anchors.fill: parent
url: "qrc:/res/test.html"
}
}
I then created (using the Designer) a resource file that looks like this:
<RCC>
<qresource prefix="/res">
<file>test.html</file>
</qresource>
</RCC>
and created a simple test.html file (in the same directory as the .qrc file):
<html>
<head><title>Hello</title></head>
<body>
<h1>Hello World!</h1>
</body>
</html>
The result is just a blank white window. If I use a regular url (http://www.stackoverflow.com) in the qml file as the url it works - the page is displayed. If I use the name of an embedded image (qrc:/qt-project.org/mac/cursors/images/pluscursor.png) that image is displayed.
It looks to me as if the html file is indeed added (it is at least listed when I enumerate the embedded resources), but my understanding of the Qt resource system is limited, so I may very well have misunderstood something fundamental.
Can anyone tell me what I am doing wrong?
Update: I have verified that the behavior is the same if I attempt to tell the web view to load the url from C++ as well. I have also verified that the resource is indeed embedded - I can open and read the resource using a QResource. Also, this does not appear to be specific to Qt5: http://qt-project.org/forums/viewthread/18181 (someone having a similar problem with Qt 4.8).
First , make sure that resource file is compiled correctly on compile folder (a .RCC file or a qrc_<Resource name>.cpp)
Second, make sure your file path is correct (in my case qrc:/images/resource/html/test.html). You can open QRC file and right-click on html file and then click on Copy Resource Path to Clipboard.
Third, Note that some urls needs more time for loading completely.
Finally, I test this scenario and it works very well (Using Qt 5.1.0)
Good luck - S.M.Mousavi
I don't thing it's possible opening resource from qrc directly in WebView (I've tried all url variants). What I'm doing now is copying the file to the local temp directory and then opening.
QString helpHTMLFile = QStandardPaths::writableLocation(QStandardPaths::TempLocation);
helpHTMLFile.append(QDir::separator());
helpHTMLFile.append("software_manual.html");
QFile(helpHTMLFile).remove();
QFile(":/software_manual.html").copy(helpHTMLFile);
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("pathToFile", "file:///"+helpHTMLFile);
Then QML looks like:
WebView {
anchors.fill: parent
url: pathToFile
}
Ok, here's how I ended up solving this. I ended up using a plain widget instead of a qml based interface.
I then had to read the first html page out from the resources manually and provide the base url. After that, subsequent pages and resources that are embedded as resources load fine.
Here's the code in question:
QResource res(":/html/index.html");
ui->webView->setHtml(reinterpret_cast<const char *>(res.data()), QUrl("qrc:/html/"));
where webView is a QWebView. My .qrc file looks like this:
<RCC>
<qresource prefix="/">
<file>html/index.html</file>
</qresource>
</RCC>
This works in Qt 5.5.0.
The quickest way to load a resource file is using XMLHttpRequest and loadHtml().
This works in Qt 5.10.1 for mobile platforms.
import QtQuick 2.10
import QtQuick.Controls 2.3
import QtWebView 1.1
Page {
anchors.fill: parent
WebView {
id: webView
anchors.fill: parent
Component.onCompleted: {
var resource = 'qrc:/path/to/your/resource.html';
var xhr = new XMLHttpRequest;
xhr.open('GET', resource);
xhr.onreadystatechange = function() {
if (xhr.readyState === XMLHttpRequest.DONE) {
var response = xhr.responseText;
webView.loadHtml(response);
}
};
xhr.send();
}
}
}
why not just use
url: "res/test.html"
instead of
url: "qrc:/res/test.html"
?
Then you would not need to use any resource files
Embedded resources URLs loading support by web engine is available in latest Qt at least for WebEngineView in QML. I was not able to find official documentation about this, but one official sample loads page from resources:
WebEngineView {
url: "qrc:/index.html"
...
}
It works for me with Qt 5.8.0 as well.
You can put below line just after QApplication a(argc, argv);
QtWebView::initialize();
Just use Path-To-File & If you saved it in project directory, just set name of file
url: "/res/test.html"
& if file is in project directory
url: "test.html"

Why doesn't setting the pixmap of a QLabel work?

I've created a subclass of QLabel that I intend to use in a QGraphicsView. It serves as a movable "point" that one can click on and drag around the graphics view.
Creating the custom class and having it displayed in the graphics view hasn't been an issue; however, trying to get the custom QLabel to paint itself with the image I want isn't happening. The constructor for my custom QLabel class is like so:
TrackerPoint::TrackerPoint(QWidget *parent) :
QLabel(parent)
{
this->setFixedSize( 40, 40 );
QPixmap pixmap( ":/images/target.png" );
this->setPixmap( pixmap );
this->setMask( pixmap.mask() );
}
I've ensured that the images directory exists in the working directory that the application is run from. If it is relevant at all, my QRC file is like so:
<RCC>
<qresource prefix="/images">
<file>images/target.png</file>
</qresource>
</RCC>
I've been trying to deal with this problem for days -- any ideas as to why the image isn't appearing would be lovely. (Does it have to do with the fact that I'm setting a pixmap in the constructor of the QLabel?)
You have:
<qresource prefix="/images">
<file>images/target.png</file>
</qresource>
I think that this will result in a double images in the resource path, i.e. :/images/images/target.png. To fix that, either remove the prefix="/images" or put alias="target.png" in the file tag.
To make it clearer where the error is, you could write your code so that it uses QPixmap::load, since that can be checked for errors:
QPixmap pixmap;
if (!pixmap.load( ":/images/target.png" )) {
qWarning("Failed to load images/target.png");
}
this->setPixmap( pixmap );
Or you could go even further and use QImageReader which can give detailed error messages.

How to know that widget is currently is running in Qt Designer

How can I in code of the custom Qt widget know that it is currently instantiated in Qt designer?
Use case:
I build a complex custom widget that has several child widgets like QPushButton, QLabel etc.
As application logic require, when widget is created most of those sub component are not visible but in design time when I put it on a form I would like to see them.
To be able to play with style sheet at design time.
Currently what I get is a empty is only a result of constructor - minimal view (actually empty in my case).
What I am looking for is to be able to do something like
MyQWidget::(QWidget *parent)
{
....
if(isRunningInDesigner())
{
myChildWidget1->setVisible(true);
myChildWidget2->setVisible(true);
myChildWidget3->setVisible(true);
}
else
{
myChildWidget1->setVisible(false);
myChildWidget2->setVisible(false);
myChildWidget3->setVisible(false);
}
....
}
So what should I put in to this bool isRunningInDesigner() ?
From the Qt Designer manual:
To give custom widgets special behavior in Qt Designer, provide an implementation of the initialize() function to configure the widget construction process for Qt Designer specific behavior. This function will be called for the first time before any calls to createWidget() and could perhaps set an internal flag that can be tested later when Qt Designer calls the plugin’s createWidget() function.
Those are methods from the QDesignerCustomWidgetInterface plugin interface. In short: you tell the widget to behave differently when Qt Designer asks your plugin to create instances of your custom widget.

Adding button in Actionscript code : Using Flex Builder

I created a new actionscript project using Flex Builder 3 and tried
to run the following file. I get this error :
Definitions: fl.controls:Button could not be found.
All I want to do is, to add a button to the application.
How could I do it?
package {
import PaperBase;
import org.papervision3d.objects.primitives.Cone;
import fl.controls.Button;
import fl.controls.Label;
import fl.events.ComponentEvent;
public class cone1 extends PaperBase {
public var cone:Cone = new Cone();
protected var sceneWidth:Number;
protected var sceneHeight:Number;
public function cone1() {
sceneWidth = stage.stageWidth
sceneHeight = stage.stageHeight;
init(sceneWidth*0.5,sceneHeight*0.5);//position of the cone
}
override protected function init3d():void {
cone.scale = 5;
cone.pitch(-40)
default_scene.addChild(cone);
}
override protected function processFrame():void {
cone.yaw(1);//rotation speed
}
}
}
The fl.* package is part of Flash Professional, not Flex. For Flex you should be using the components that are part of the mx.* package.
Now, that being said, I'm fairly sure it is possible to use Flash components in Flex. I'm just not sure how it's done off the top of my head.
Also, you don't need an actual button component to get a "button like" ui element - any class that extends InteractiveObject will do. This incldes Sprite and MovieClip.
Branden is correct the fl package is a part of the Flash IDE..I am not sure either but you may be able to add the package to your class path if you know where the package resides on your file system..i am guessing somewhere in C:/program files/adobe/flash
if you want to use components in flex builder I think you need make a flex project not an actionscript project
and change your imports to:
import mx.controls.Button;
import mx.controls.Label;
import mx.events.FlexEvent;
Also if you do not need to use components either you can use a Sprite for a button like branden said and you could just use a TextField for a label.
another option if you have the flash IDE is to make a SimpleButton, press F8 select button, click enter. then give it a linkage name by right clickin it in the library panel and selecting linkage name. then export the .swf and put the swf in the src folder for your project and embed it like this:
[Embed(source="flashfile.swf")]
public var myButton:Class;
You may even be able to export the Flash IDE components this way but not sure...actually I am not 100% positive if the [Embed] meta data works in an actionscript project or just flex projects so you will have to check and see.
It depends what version of the IDE you have, for CS4 and Mac the location would be /Applications/Adobe Flash CS4/Common/First Run/Classes
Add that folder or the relevant folder for your installation/OS to your classpath in flashbuilder/eclipse and it will interpret the class calls fine.
It makes sense if you're coding pure actionscript and dont want to use flex components, or employing a mixed coding and designing in IDE approach.
#philip the embed tag cannot be used in pure actionscript
Not sure why you would want to, but if you need to import the flash libs into flex,try dragging what you want to the stage in flash and exporting as a .swc file to import into your flex project.

Resources