I am making a GUI for my app, using package lxn/walk.
I'm trying to figure out how to place elements by pixels. My code is like this:
package main
import (
"github.com/lxn/walk"
. "github.com/lxn/walk/declarative"
)
var edit *walk.Label
func main() {
MainWindow{
Title: "FetchTest",
MinSize: Size{600, 400},
Layout: VBox{},
Children: []Widget{
Label{
AssignTo: &edit,
Text: "Hello",
},
PushButton{
Text: "GET DATA",
OnClicked: func() {
},
},
},
}.Run()
edit.SetBounds(walk.Rectangle{10, 5, 50, 50})
}
But this doesn't work since the code that sets the position of label is not executing.
Where to use edit.SetBounds(walk.Rectangle{10, 5, 50, 50} so the element is shown at the given coordinates?
I'm not familiar with walk, but maybe MainWindow.Run() only returns when the window is closed? You could try the approach used in the walk "filebrowser" example: call Create to set up the window, do any additional initialization, and then call Run.
Related
script1.js:
function run() {
const chromeApp = Application('Google Chrome');
const window = chromeApp.windows[0];
console.log(window.name());
window.bounds = {
x: 0,
y: 0,
width: 500,
height: 500,
};
chromeApp.activate();
}
Run:
osascript -l JavaScript script1.js
And it works
script2.js:
function run() {
const systemEvents = Application('System Events');
const ap = systemEvents.processes().find(ap => ap.name() === 'Google Chrome');
console.log(ap.name());
const window = ap.windows[0];
console.log(window.name());
window.bounds = {
x: 0,
y: 0,
width: 500,
height: 500,
};
}
Run:
osascript -l JavaScript script1.js
It does NOT work:
script2.js: execution error: Error: Error: Can't set that. (-10006)
But I really need to get script2.js work. Because in my real application, I don't know the application name in advance and I need to fetch the process dynamically base on user interaction. Because I don't know application name, I cannot use script1.js.
Any input is appreciate!
function run() {
const systemEvents = Application('System Events');
const p = systemEvents.processes().find(ap => ap.frontmost() === true);
const ap = Application (p.bundleIdentifier());
const window = ap.windows[0];
window.bounds = {
x: 0,
y: 0,
width: 500,
height: 500,
};
}
It’s years since I’ve had the displeasure of using GUI Scripting so didn’t immediately spot the real problem.
The real problem is that System Events is a bloated badly designed mess that shoehorns a dozen libraries’ worth of functionality into one #BigBallOfMud.
SE includes Cocoa Scripting’s Standard Suite window class definition, including a standard bounds property, but doesn’t actually implement it (since SE has no windows of its own). So while SE’s dictionary claims windows elements have a bounds property, trying to get/set that property throws an error. It is very confusing.
Therefore, ignore the window class definition in SE’s Standard Suite, and only look at the window class definition in its Process Suite (aka GUI Scripting). It doesn’t have a bounds property; instead it has separate position and size properties:
position (list of number or missing value) : the position of the window
size (list of number or missing value) : the size of the window
Here is code that works:
tell application "System Events"
set ap to first process whose name is "Firefox"
set win to window 1 of ap
-- set bounds of win to {100, 100, 600, 600} -- this doesn't actually work
set position of win to {100, 100}
set size of win to {500, 500}
end tell
In other words, just because an app’s dictionary says it supports something doesn’t mean it actually does. Trying to figure out an app’s brokenness while also wrestling with JXA’s brokenness is at best a Sisyphean’ exercise.
Figure out how you get your code working in AppleScript first; if it doesn’t work there then you know it’s the app that’s the problem. If you still want to use JXA, then you can attempt to port your working code to that afterwards (although generally I don’t recommend wasting time on JXA as it’s crippled and abandoned, and the whole AppleScript stack is slowly dying anyway).
Inspired by Robert's answer, I found that the following works:
function run() {
const systemEvents = Application('System Events');
const p = systemEvents.processes().find(ap => ap.name() === 'Google Chrome');
console.log(p.name());
const app = Application(p.name());
const window = app.windows[0];
window.bounds = {
x: 0,
y: 0,
width: 500,
height: 500,
};
app.activate();
}
So I can convert a Process into Application by name: const app = Application(p.name()); Then script2.js become script1.js.
But it doesn't always work. For example, the following doesn't work:
function run() {
const systemEvents = Application('System Events');
const p = systemEvents.processes().find(ap => ap.frontmost() === true);
console.log(p.name());
const app = Application(p.name());
const window = app.windows[0];
window.bounds = {
x: 0,
y: 0,
width: 500,
height: 500,
};
app.activate();
}
If the frontmost application is Visual Studio Code. Because the process name will be "Electron" and convert process to application will be wrong.
I'd like to find a way to convert process to application by ID. I am still investigating and I will post updates later.
Update: please check the accepted answer.
You can set the position and size to a two-dimensional array. Because Application("Name of App") doesn't always seem to work, it's best to go through System Events.
let sys = Application("System Events")
let app = sys.applicationProcesses
.whose({ name: "AppYouWant"})[0]
app[0].windows[0].size = [200, 200]
app[0].windows[0].position = [200, 200]
I have an image like this one:
http://www.imagemagick.org/Usage/basics/result.gif
(it contains two rows of individual data: three square images in the upper row, and below another square and two 'empty' squares)
I want to use it inside a repeater, so that I get four image buttons, each with one of the subimages.
Is there a "texture-atlas" modules in QML?
I only found http://doc.qt.io/qt-5/qml-qtquick-sprite.html and I hope there is something that is better for my use-case.
As I said in comment I would do that using QQuickImageProvider since that's the fastest and the least memory intensive way. But QML is very flexible and you can do with it whatever you want. As for your question you can do as following:
Tile.qml
import QtQuick 2.9
Item {
id: container
property alias source: img.source
property int cellWidth: 1
property int cellHeight: 1
property int slideIndex: 0
clip: true
Image {
id: img
property int cols: img.paintedWidth / container.cellWidth
property int rows: img.paintedHeight / container.cellHeight
x: -container.cellWidth * Math.floor(container.slideIndex % cols)
y: -container.cellHeight * Math.floor(container.slideIndex / cols)
}
}
and usage:
import QtQuick 2.9
import QtQuick.Window 2.2
Window {
id: window
title: "Test"
visible: true
width: 600
height: 400
Row {
spacing: 10
anchors.centerIn: parent
Repeater {
model: 4
Tile {
width: 32
height: 32
cellWidth: 32
cellHeight: 32
slideIndex: index
source: "http://www.imagemagick.org/Usage/basics/result.gif"
}
}
}
}
The idea is to wrap the entire image into a container and clip it appropriately. Here I use index-based access to a slide, you can do that as you want.
Let's look at this very simple sample application, built with QT 5.9 on a Windows 10:
import QtQuick 2.7
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.0
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
ColumnLayout {
anchors.left: parent.left
anchors.leftMargin: 20
anchors.right: parent.right
anchors.rightMargin: 20
Text {
id: text
text: "This is a sample Text"
}
ComboBox {
model: [
"A",
"B",
"C"
]
}
Text {
text: "Another Text"
}
TextField {
anchors.left: parent.left
anchors.right: parent.right
text: "User Input"
}
}
}
If I run it without any further modifications from the QT Creator, I get a very weird relationship between the Font-Size of the Text and the ComboBox and TextField blocks. It looks like this:
The text is too small, and the ComboBoxes (and their Fonts) are HUGE.
If I change the main function to set the default font size explicitely to the system font size using this code (It's the same when I hardcode the setPointSizeF to 12, which is the supposed standard size on windows):
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
auto font = app.font();
QFontInfo fi(font.defaultFamily());
font.setPointSizeF(fi.pointSizeF());
app.setFont(font);
QQmlApplicationEngine engine;
engine.load(QUrl(QLatin1String("qrc:/main.qml")));
It looks like this:
Now the "Relative Dimensions" are more balanced, but overall everything is just "too big". Additionally, if I'm opening the ComboBox, I get again very small text:
Did I miss to set some default here? How can I achieve a more balanced look that fit's better into the Operating Systems' native font sizes?
The combobox delegates use a different font to the application default.
Delegate font can be changed to match the rest of the application as follows:
ComboBox {
id: control
delegate: ItemDelegate {
width: control.width
text: control.textRole ? (Array.isArray(control.model) ? modelData[control.textRole] : model[control.textRole]) : modelData
font.weight: control.currentIndex === index ? Font.DemiBold : Font.Normal
font.family: control.font.family
font.pointSize: control.font.pointSize
highlighted: control.highlightedIndex === index
hoverEnabled: control.hoverEnabled
}
}
Leave the font sizes as defaults and instead set the widths of your items. You could explicitly set the width of the ComboBox and TextField, or if you want to use the ColumnLayout to have consistent sizing of all the items, see example below,
import QtQuick 2.7
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.0
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
ColumnLayout {
anchors.left: parent.left
anchors.leftMargin: 20
width: text.width
Text {
id: text
text: "This is a sample Text"
}
ComboBox {
Layout.fillWidth: true
model: [
"A",
"B",
"C"
]
}
Text {
text: "Another Text"
}
TextField {
Layout.fillWidth: true
text: "User Input"
}
}
}
You can simply try setting: font.pointSize: 12 in both the combobox and text fields. This works for me on Qt 5.9 in Windows 10. I am still trying to figure how to change the font size inside the combobox drop down; will expand this answer when I know.
QT5.5, QML:
I am using the example "Qt Quick Examples - Drag and Drop"
In this example, you could see that if we drag the red tile "1", it appears under other tiles when we drag it.
I don't like this effect since when we drag one item, I hope it always appears on the top of whole GUI.
What I tried is:
Once the mouse is pressed, we set the item's z as the largest value. And when the mouse is released, set the z to the small value (or better we could save the original value and reset it back to its original valule)
MouseArea {
id: mouseArea
width: 64; height: 64
anchors.centerIn: parent
drag.target: tile
onPressed: {
tile.z = 100;
}
onReleased: {
tile.z = 0;
parent = tile.Drag.target !== null ? tile.Drag.target : root
}
}
But the code does not work.
Actually by using console.log, I could see the z value changes, but the dragging ghost image still appears at the bottom.
So I guess when onPressed is implemented, the drag mechanism has used its original z value and gets no chance to access the updated z value.
So any idea to have a more decent drag movement?
Thanks!
The z value of items only applies to siblings and the immediate (direct) parent:
Items with a higher stacking value are drawn on top of siblings with a lower stacking order. Items with the same stacking value are drawn bottom up in the order they appear. Items with a negative stacking value are drawn under their parent's content.
Let's use a small example to test the parent/child scenario:
import QtQuick 2.3
import QtQuick.Window 2.0
Window {
visible: true
width: 200
height: 200
title: qsTr("Hello World")
flags: Qt.FramelessWindowHint
Rectangle {
color: "salmon"
width: 64
height: 64
anchors.centerIn: parent
Text {
text: "1"
}
Rectangle {
color: "steelblue"
x: 32
y: 32
width: 64
height: 64
Text {
text: "2"
}
Rectangle {
color: "orchid"
x: 16
y: -16
width: 64
height: 64
z: -2
Text {
text: "3"
}
}
}
}
}
We set the z value of the third Rectangle to -2, hoping that it would go behind the first one, but since it's a child of the second and not the first, it's out of reach. This is what's happening in the drag and drop example: the items are too far apart in terms of ancestory.
To elaborate further on this, let's take DragTile.qml from the Drag and Drop example and modify it in a similar way to yours (this is a nicer way of achieving the same thing, by the way):
states: State {
when: mouseArea.drag.active
ParentChange { target: tile; parent: root }
AnchorChanges { target: tile; anchors.verticalCenter: undefined; anchors.horizontalCenter: undefined }
PropertyChanges {
target: tile
z: 100
}
}
This also won't work. To see what's going on, we can use an awesome little environment variable called QSG_VISUALIZE. Specifically, we want to use the overdraw visualisation, which gives us a nice rotating 3D box containing our scene. Even with our PropertyChanges above, you can see that the stacking order of the item is unchanged:
To ensure that the item is rendered on top of everything, you need to parent it to an item that is actually above everything. We can do this by adding an Item to the end of tiles.qml:
Item {
id: dragContainer
anchors.fill: parent
}
Add a property to the DragTile component that gives the delegate access to the container:
property Item dragParent
Then, assign the container in tiles.qml:
delegate: DragTile { colorKey: "red"; dragParent: dragContainer }
Next, modify the parent property of the ParentChange in DragTile.qml:
ParentChange { target: tile; parent: dragParent }
The end result:
Note that I intentionally excluded the "back" button, but if you wanted to make it go above that as well for some reason, you can just move dragContainer higher up the hierarchy.
I wanted to create a simple IrrlichtDevice with IrrlichtEngine, but when I start the application, the window just appears on the screen and then instantly disappears.
My code looks like following:
int main()
{
IrrlichtDevice *device =
createDevice( video::EDT_DIRECT3D9, dimension2d<u32>(640, 480), 16,
false, false, false, 0);
}
(code copied from the HelloWorld tutorial of the documentation)
Try
int main()
{
IrrlichtDevice *device =
createDevice( video::EDT_DIRECT3D9, dimension2d<u32>(640, 480), 16,
false, false, false, 0);
while( device->run() )
{ device->getVideoDriver()->beginScene( true, true, video::SColor( 50, 50, 50, 50) );
device->getVideoDriver()->endScene();
}
}
You have no looping system in place. After you create the device the function immediately ends and everything is cleaned up.
bob2 has the correct answer, I would suggest that you practice making simple c++ applications before diving in the deep end.