When applying a LevelAdjust effect to an image, Qt doesn't seem to handle the edges properly.
I have a set of svg icons, all drawn in black. I would like to change their color easily on the fly.
I am experimenting with the LevelAdjust effect from QtGraphicalEffects 1.12. According to the doc, using minimumOutput should shift the color curve in a linear fashion: blacks will correspond to minimumOutput, white will be white, and colors in between will be a prorated version of minimumOutput.
If I try the following:
Rectangle {
height: 100
width: 100
Image {
id: iconImage
source: "qrc:/images/circles.svg"
anchors.centerIn: parent
width: 0.8 * parent.width
height: width
sourceSize.width: width
sourceSize.height: width
fillMode: Image.PreserveAspectFit
}
LevelAdjust {
anchors.fill: iconImage
source: iconImage
minimumOutput: "#0098FE8F"
}
}
with my svg being as simple as possible:
<?xml version="1.0" encoding="UTF-8"?>
<svg version="1.0" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<circle cx="50" cy="50" r="50" fill-rule="evenodd" style="paint-order:normal"/>
</svg>
then the inside of the circle takes the expected colour. However, the edges are simply wrong. When rendering the circle, it looks like Qt does some anti-aliasing. But these extra pixels are ignored. I end up with edges which look wrong, but are also darker than the new colour (magnified result here):
I would have expected their colour to be shifted as needed too.
The result is the same on windows, and Android. Am I doing anything wrong?
Edit on 2019/09/05:
Based on the accepted answer and suggestion below, I created the following ColoredImage.qml component:
import QtQuick 2.0
import QtGraphicalEffects 1.12
Item {
id: imageWrapper
property bool colored: true
property string color: "#FFFF0000"
// Replicate some Image properties:
property string source
property var fillMode: Image.PreserveAspectFit
property var status: image.status
property real progress: image.progress
Image {
id: image
source: imageWrapper.source
anchors.fill: parent
sourceSize.width: width
sourceSize.height: height
fillMode: imageWrapper.fillMode
visible: !imageWrapper.colored
}
ColorOverlay {
id: overlay
anchors.fill: image
source: image
color: imageWrapper.color
visible: imageWrapper.colored
}
}
Thanks again!
In your iconImage, you must set:
visible: false
You are seeing the original black icon under the LevelAdjust item through its semi-transparent anti-aliased edges.
Related
I am using a VideoOutput item, which I have placed inside a Rectangle. This VideoOutput goes on top of the Rectangle, of its border and its rounded corners.
Rectangle{
radius: 12
width: 200
height: width
border.color: black
border.width: 15
VideoOutput {
fillMode: VideoOutput.PreserveAspectCrop
anchors.fill: parent
source: myCamera
autoOrientation: true
}
}
Camera {
id: myCamera
focus {
focusMode: CameraFocus.FocusContinuous
focusPointMode: CameraFocus.FocusPointAuto
}
captureMode: Camera.CaptureViewfinder
}
I intentionaly made the border thick so it really shows what is going on. The border is hidden by the VideoOutput, and the rounded corners are ignored.
I can imagine that the VideoOutput item would use some acceleration tricks, so it might not play well with standard QML item behaviour. But is there a way get this to work, without adding a filter element, or is this the expected behaviour?
imagine that we have arrow image as here:
but we want to make from this image an animation:
question
This not very elegant animation was created from a set of different arrow images.
But i want to make same animation by qml. Do you know any components(preferably qml but if you do not know, you can advise me qt classes) with which help i can reach that?
And another question, does it make sense from the point of view of performance to search any ways to reach that instead of combine set of images to gif-format?
Updating based on new information that the arrow has a background.
You need to crop out the arrow to an image file with a transparent background and have the background as a separate image.
From there, you can use a similar concept as below, but with the arrow growing. I believe you will be able to find an image mode that fits the image vertically and does not manipulate it horizontally. I would start by trying fillMode: Image.TileHorizontally.
Treat this as pseudo code, I don't have access to a computer to run this on at the moment.
Image
{
id: backgroundImage
source: "background.png"
Image
{
id: arrowWithTransparentBackground
fillMode: Image.TileHorizontally
source: "arrow.png"
anchors.left: parent.left
anchors.top: parent.top
anchors.bottom: parent.bottom
SequentialAnimation on width {
loops: Animation.Infinite
PropertyAnimation { from 0: to: parent.width }
}
}
}
Old answer for reference, before knowing there was a background to the image:
QML very nicely animates size changes. You could easily have a rectangle on top of your image anchored to the left side that is white and grows from 0 to arrow.width.
Treat this as pseudo code, I don't have access to a computer to run this on at the moment.
Image
{
id: arrowImage
source: "arrow.png"
Rectangle
{
color: "white"
anchors.left: parent.left
anchors.top: parent.top
anchors.bottom: parent.bottom
SequentialAnimation on width {
loops: Animation.Infinite
PropertyAnimation { from 0: to: parent.width }
}
}
}
I am looking at the QML Style for the virtual keyboard. What is the purpose of keyboardDesignWidth and Height? I seem to have a lot of trouble managing the width and height of the keyboard and can never set it to how I want it. Setting the keyboardHeight and Width directly also does not help much.
The issue is that the component background size is somehow computed behind the scenes. So, even when I have the keyboard buttons and size how I want, the extraneous background covers some of my other control and it is very difficult to have a fine grained control over the size of the keyboard.
What is the right way to control the width and size of the virtual keyboard directly?
To Quote from the Documentation
The keyboard size is automatically calculated from the available width; that is, the keyboard maintains the aspect ratio specified by the current style. Therefore the application should only set the width and y coordinates of the InputPanel, and not the height.
So if you want to have a specific height, you need to set the width accordingly.
What is the right way to control the width and size of the virtual keyboard directly?
e.g.
InputPanel {
anchors.fill: parent
anchors.leftMargin: 100
anchors.rightMargin: 100
}
e.g.
InputPanel {
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom
width: 30
}
So what's the deal with the keyboardDesignHeight/Width? Well this seems to be the dimensions of the keyboard, when it is not necessary to scale it:
scaleHint : real
The keyboard style scale hint. This value is determined by dividing keyboardHeight by keyboardDesignHeight. All pixel dimensions must be proportional to this value.
This property is readonly!
So setting those will not disable the automatic resizing of your input panel in dependence of the width.
You might use them maybe, to calculate a ratio, and from this set the width to achieve your desired height.
Maybe this example helps you to understand this property:
import QtQuick 2.6
import QtQuick.Window 2.0
import QtQuick.Controls 2.0
import QtQuick.VirtualKeyboard 2.0
ApplicationWindow {
id:appwindow
visible: true
width: 800
height: 600
title: qsTr("Test")
InputPanel {
id: ip
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom
width: 800
Component.onCompleted: console.log(Object.keys(ip.keyboard.style).sort())
}
Slider {
id: sl
from: 0
to: 3000
}
Binding {
target: ip.keyboard.style
property: 'keyboardDesignHeight'
value: sl.value
}
}
I would like to set the minimum width and height of my QML Application window, so that the content item is fully visible (not clipped).
import QtQuick 2.5
import QtQuick.Controls 1.4
ApplicationWindow {
visible: true
width: 100
height: 100
title: "test"
minimumWidth: circle.width
minimumHeight: circle.height // + menuBar.height
menuBar: MenuBar {
Menu {
title: qsTr("File")
MenuItem {
text: qsTr("Exit")
onTriggered: Qt.quit();
}
}
}
Rectangle {
id: circle
anchors.centerIn: parent
width: 200
height: 200
color: "red"
radius: width * 0.5
}
}
Here is the result:
As you can see, setting the minimum width works fine. The minimum height seems to be off by the height of the menu bar. The problem is, adding something like menuBar.height does not work as this property does not exist.
So the question is: how do I set the size of the ApplicationWindow, so that the content item (given by width/height or implicitWidth/implicitHeight) is not clipped?
Note: In reality, instead of a red circle, the content item serves as a game canvas, which I would like to resize dynamically.
As always with the old QtQuick.Controls 1.x the only way to help yourself is, to look at the (undocumented/internal) properties. For the MenuBar those are:
objectName
menus
__contentItem
__parentWindow
__isNative
style
__style
__menuBarComponent
objectNameChanged
menusChanged
nativeChanged
contentItemChanged
styleChanged
__styleChanged
__menuBarComponentChanged
__contentItem seems to be interesting, and it features a height - as soon as it is instantiated.
So we can define the height of the ApplicationWindow as such:
minimumHeight: contentItem.childrenRect.height
+ (menuBar.__contentItem ? menuBar.__contentItem.height : 0)
I have a QML window with a nested RowLayout. In the inner row I have two images. The source .png files for these images are (intentionally) rather large. When I attempt to set the height property on these images to make them smaller, they are still drawn large.
Desired Appearance:
Actual Appearance:
The only way I have been able to get them to be small is to set the sourceSize.height:100 instead of height:100; however, this is not what I want. I want them to be able to scale up and down without reloading.
How can I fix my QML so that the images take on the height of their containing RowLayout?
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.3
ApplicationWindow {
width:600; height:300
visible:true
Rectangle {
color:'red'
anchors { top:header.bottom; bottom:footer.top; left:parent.left; right:parent.right }
}
header:RowLayout {
id:header
spacing:0
height:100; width:parent.width
RowLayout {
id:playcontrol
Layout.minimumWidth:200; Layout.maximumWidth:200; Layout.preferredWidth:200
height:parent.height
Image {
// I really want these to take on the height of their row
source:'qrc:/img/play.png'
width:100; height:100
fillMode:Image.PreserveAspectFit; clip:true
}
Image {
source:'qrc:/img/skip.png'
width:100; height:100
fillMode:Image.PreserveAspectFit; clip:true
}
}
Rectangle {
color:'#80CC00CC'
Layout.minimumWidth:200
Layout.preferredWidth:parent.width*0.7
Layout.fillWidth:true; Layout.fillHeight:true
height:parent.height
}
}
footer:Rectangle { height:100; color:'blue' }
}
When using layouts, never specify the width or height of the item; use the Layout attached properties instead. The layout itself will set the width and height, effectively overriding whatever you set.
So, for your images, replace
width:100; height:100
with
Layout.preferredWidth: 100
Layout.preferredHeight: 100
This is documented here. Specifically, the width and height are only used as a "final fallback", and they won't behave as you'd expect.
There are other places in your code where this occurs:
playcontrol sets height: parent.height (filling the width and height of the parent is the default behaviour for layouts, so this shouldn't be necessary anyway).
The Rectangle within the playcontrol layout also sets height: parent.height.