POST
|
Hi @GISiste , In reaction to your question, backend maintenance was done on Cloud Make. Please try again with your original app (not the workaround copy). If the problem persists, please login to your ArcGIS Online account and review and remove old entries from your "My Apps" folder. Hopefully, with both the backend maintenance and your "My Apps" cleanup, the problem should be resolved. Stephen Quan
... View more
03-13-2023
08:11 PM
|
1
|
1
|
770
|
POST
|
Hi @rob_hewy , the help is accessible if you were to search via Help Contents. If you wish the F1 context-help key to function a patch will be required. Fortunately, you can create the patch yourself using the following AppStudio App. After running it, you will need to restart Qt Creator to see the changes. import QtQuick 2.15
import QtQuick.Controls 2.15
import ArcGIS.AppFramework 1.0
import ArcGIS.AppFramework.Sql 1.0
App {
id: app
width: 800
height: 600
Button {
anchors.centerIn: parent
text: qsTr("Patch")
property SqlDatabase db: SqlDatabase { }
onClicked: {
patchFile("~/Applications/ArcGIS/AppStudio/doc/AppFramework.qch", 'org.qt-project.qtappframework.5156');
patchFile("~/Applications/ArcGIS/AppStudio/doc/ArcGISQtQML.qch", 'org.qt-project.qtarcgisqtqml.5156');
patchFile("~/Applications/ArcGIS/AppStudio/doc/ArcGISQtToolkit.qch", 'org.qt-project.qtarcgisqttoolkit.5156');
text = qsTr("Patched");
enabled = false;
}
function patchFile(fileName, namespace) {
db.databaseName = AppFramework.resolvedPath(fileName);
db.open();
db.query("UPDATE NamespaceTable SET Name = :namespace", { namespace });
db.close();
}
}
} I am interested if this resolves this issue for you. Stephen
... View more
02-21-2023
07:29 PM
|
2
|
1
|
581
|
POST
|
Yes, but, it depends on the QML component you use. Some QML components implement an `icon` which can be used to scale (with resampling) and recolor SVGs, e.g. property url bikingSvg: "https://raw.githubusercontent.com/Esri/calcite-ui-icons/master/icons/biking-32.svg"
ItemDelegate {
icon.source: bikingSvg
icon.color: "blue"
icon.width: 32
icon.height: 32
text: qsTr( "ItemDelegate" )
} You may find it useful to use the icon property in common components such as Button, ToolButton, MenuItem, e.g. Button {
icon.source: bikingSvg
icon.color: "blue"
icon.width: 32
icon.height: 32
text: qsTr( "Button" )
} However, if you use Image, and you will need to take care of resampling and coloring yourself. Resampling is needed with the sourceSize property and coloring needs to be done by the ColorOverlay property. Using the Image object in this way allows you to do layering with different colors for different layers property url bikingSvg: "https://raw.githubusercontent.com/Esri/calcite-ui-icons/master/icons/biking-32.svg"
property url disallowSvg: "https://raw.githubusercontent.com/Esri/calcite-ui-icons/master/icons/circle-disallowed-32.svg"
Item {
width: 32
height: 32
Image {
id: bikingImage
anchors.fill: parent
source: bikingSvg
sourceSize: Qt.size( width, height )
}
ColorOverlay {
color: "blue"
anchors.fill: bikingImage
source: bikingImage
}
Image {
id: disallowImage
anchors.fill: parent
source: disallowSvg
sourceSize: Qt.size( width, height )
}
ColorOverlay {
color: "red"
anchors.fill: disallowImage
source: disallowImage
}
}
... View more
03-25-2020
06:32 PM
|
0
|
0
|
1754
|
POST
|
Hi jose, I noticed that you mention that: > camera sometimes came in black Does this mean that the camera actually works sometimes? Can we clarify how often it does work vs how often it does not work? - Camera works none of the time - Camera works some of the time - Camera works most of the time When the camera does not work, can you describe how you're operating Survey123. Could it be that you're tapping the Camera icon twice? Stephen
... View more
01-02-2020
02:07 PM
|
0
|
1
|
1201
|
POST
|
Hello Eli, Perhaps you can try supply a projection file for that layer. In ArcPad, you can find a copy of NAD_1983_StatePlane_NorthCarolina_FIPS_3200_Feet here: "C:\Program Files (x86)\ArcGIS\ArcPad10.2\Coordinate System\Projected Coordinate Systems\State Plane\NAD 1983\NAD 1983 StatePlane North Carolina FIPS 3200.prj" Take a copy of this file and rename it in a name consistent with your MrSID file and/or JPEG file with the extension set to ".prj". i.e. "NCMECK006017.prj". Then next time you add this MrSID file to ArcPad, ArcPad should notice the PRJ file and it will use that file rather than trying to "guess" it from reading the MrSID. Stephen
... View more
12-03-2019
01:41 PM
|
1
|
1
|
1850
|
POST
|
Hi Jorge, Thank you for your reply. I have rewritten the example as a complete working application so you should no longer have issues running it. In my of my code today, I now use the FileInfo object, with this: You get automatic shortcut to AppFramework.userHomePath via "~" You can get the physical path, i.e. FileInfo.filePath You can get the url (useful to assigning to Image.source), i.e. FileInfo.url You have access to the container folder via FileInfo.folder which is a FileFolder object which you can use to do folder operations, such as fileExists, delete, makeFolder I hope you find this example more useful. Stephen
... View more
10-21-2019
02:48 PM
|
0
|
0
|
957
|
POST
|
Make sure the folder exists. Use FileFolder.makeFolder() to do this. import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
import ArcGIS.AppFramework 1.0
App {
id: app
width: 400
height: 640
property FileInfo target: FileInfo { filePath: "~/ArcGIS/AppStudio/Images/logo.png" }
ColumnLayout {
anchors.fill: parent
anchors.margins: 10
Image {
id: image
}
NetworkRequest {
id: networkRequest
url: "https://appstudio.arcgis.com/images/index/logo-appstudio.png"
responsePath: target.filePath
onReadyStateChanged: {
if ( readyState !== NetworkRequest.ReadyStateComplete ) return;
image.source = target.url
}
}
Button {
text: qsTr("Download Now")
onClicked: {
target.folder.makeFolder();
networkRequest.send();
}
}
Item {
Layout.fillHeight: true
}
}
}
... View more
10-04-2019
11:00 AM
|
0
|
2
|
957
|
POST
|
Hello Ritwik, On ArcPad's Browse toolbar there's the "Measure" tool. See the ArcPad documentation on "The Browser toolbar": help.arcgis.com/en/arcpad/10.0/help/index.html#/The_Browse_toolbar/00s100000045000000/ Here's a video of the "Measure" tool in action. In this video, we measure the width and height of a parcel, then, we measure the permiter of a park. Stephen
... View more
09-22-2019
05:52 PM
|
0
|
0
|
576
|
POST
|
Thanks Paul, I'm using a similar version. In one of the troubleshooting sessions, I modified the header of "moment.js" to the following: //! moment.js
;(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
global.moment = factory()
console.log("typeof(global): ", typeof(global));
global[ "one" ] = 1;
global[ "two" ] = 2;
global[ "three" ] = 3;
global[ "key" + Math.random() ] = 1;
console.log("global keys: ", JSON.stringify( Object.keys( global ) ) );
}(this, (function () { 'use strict';
Then in my app, I imported "moment.js" twice: import "moment.js" as Moment1
import "moment.js" as Moment2 On the first import, this yielded the following output: qml: typeof(global): object
qml: global keys: ["moment","one","two","three","key0.3286030239782699"] On the second import, this yielded the following: qml: typeof(global): object
qml: global keys: ["moment","one","two","three","key0.3286030239782699","key0.7088453144951244"] i.e. what appears to be happening, is the value of "this" which later becomes "global" refers to Javascript object in the global namespace. What the moment.js library is trying to do is install a moment() as a function in the global namespace. For good measure, I wanted to see if I can put some random keys in the global namespace as well, and, yes, that appears to have worked too. This means when you use these objects in your app, you don't need to use the specifier, i.e. I didn't use either Moment1 nor Moment2. I just referred to the moment() function directly. Similarly, for the other items I added in the global namespace, I could access them too, e.g. Text {
text: three // 3
}
If you still cannot get it to work, then, yes, feel free to refer to my workaround, i.e. //! moment.js
var moment
;(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
global.moment = factory()
moment = global.moment
}(this, (function () { 'use strict';
Then, in your app, use the workaround as follows: import QtQuick 2.7
import QtQuick.Controls 2.1
import ArcGIS.AppFramework 1.0
import "moment.js" as Moment
App {
id: app
width: 400
height: 640
property var moment: Moment.moment
Text {
text: moment().format("dddd")
}
}
Stephen
... View more
09-09-2019
08:25 PM
|
0
|
0
|
1834
|
POST
|
Hi Paul, I had numerous attempts at this over the weekend. The first version I did was the workaround which you got a copy from the email. However, I found the workaround ugly. When I retested the "import" as is, I found it to just work with my version of AppStudio. I will do some further troubleshooting, but, can you tell me which version of Qt you are using? Stephen
... View more
09-08-2019
11:07 PM
|
0
|
2
|
1834
|
POST
|
Hi Paul, I did some troubleshooting but found that the "moment.js" works by default, e.g. Import the with the usual syntax: import "moment.js" as Moment Use the moment() function as per documentation Here's an example usage: import QtQuick 2.7
import ArcGIS.AppFramework 1.0
import "moment.js" as Moment
App {
id: app
width: 400 * AppFramework.displayScaleFactor
height: 640 * AppFramework.displayScaleFactor
Column {
anchors.fill: parent
anchors.margins: 10
spacing: 10
Text {
text: JSON.stringify( moment() ) // "2019-09-06T21:46:33.916Z"
}
Text {
text: moment().format('MMMM Do YYYY, h:mm:ss a') // September 7th 2019, 7:47:08 am
}
Text {
text: moment().format("dddd") // Saturday
}
Text {
text: moment().format('YYYY [escaped] YYYY') // 2019 escaped 2019
}
Text {
text: moment().format() // 2019-09-07T07:48:57+10:00
}
}
}
... View more
09-06-2019
02:31 PM
|
1
|
4
|
1834
|
BLOG
|
Hi everyone, with your help, we have received a fix to the Promise chaining bug https://bugreports.qt.io/browse/QTBUG-71329. The fix will be bundled in with the next release of AppStudio 4.0. As soon as AppStudio 4.0 is out, I will update the content of all blogs to reflect Promise chaining as a viable coding pattern for AppStudio.
... View more
08-12-2019
08:45 PM
|
0
|
0
|
872
|
BLOG
|
Hi Paul, I've reported this issue to Qt company and they indicated it will be fixed in a future version of Qt. Refer to https://bugreports.qt.io/browse/QTBUG-77096 for details. Stephen
... View more
07-31-2019
03:15 PM
|
0
|
0
|
657
|
BLOG
|
1. Introduction Many, many, many years ago, in my local bookstore, I had my first experiences with computers. I typed in my first program and always thought what it did was cool: 10 PRINT "HELLO"
20 GOTO 10 Today, I'm going to look into iterators and generators, and, yes, make a connection to how these latest features in ECMAScript 7 update of AppStudio 3.3 and Qt 5.12.1 aren't too dissimilar from old school concepts. 2. For Of Iterators In my previous blog, I talked about "For Of" and showed that it works with Javascript iterators and Javascript generators. function *cities() {
yield { name: "Hawaii", distance: 4221.73 }
yield { name: "Los Angeles", distance: 96.65 }
yield { name: "New York City", distance: 3853.10 }
yield { name: "Redlands", distance: 1.12 }
yield { name: "Seattle", distance: 1566.70 }
}
for ( let city of cities() )
console.log( JSON.stringify( city ) ) View the full ForIter.qml on GitHub. Try it now on AppStudioBlogIterators | WebAssembly. In the above example, we declare a Javascript generator which produces cities with their name and their distance from ESRI. We turn that Javascript generator into a Javascript iterator when we "call" it, i.e. cities(). As a Javascript iterator, we use a "for of" loop to process the results. So far, nothing special. You can glean this sort of thing from any blog about Javascript iterators. If we replace the "for of" syntax with a somewhat traditional looking "while" loop, we get to see some inner workings of the Javascript iterator: function *cities() {
yield { name: "Hawaii", distance: 4221.73 }
yield { name: "Los Angeles", distance: 96.65 }
yield { name: "New York City", distance: 3853.10 }
yield { name: "Redlands", distance: 1.12 }
yield { name: "Seattle", distance: 1566.70 }
}
const iter = cities()
let city = iter.next()
while ( !city.done ) {
console.log( JSON.stringify( city.value ) )
city = iter.next()
} View the full WhileIter.qml on GitHub. Try it now on AppStudioBlogIterators | WebAssembly. The first impression is this "while" version looks a bit hard to swallow. The "for of" shorten version looks cleaner. When you think about it, this version actually gives you more insight into the "yield" keyword. When "yield" is used, a value is returned from the Javascript generator, and the Javascript generator pauses execution until the next time we call "iter.next()". At first, this may seem like an alien concept, but, if you look harder, you'll see this pattern in a lot of places, some quite old. 3. Unix head and tails On Unix, we learn that in shell programming, we can join Unix commands together using Unix pipes. A pipe is a form of redirection that is used in Unix to send output from one program to another for further processing. The first process is not completed before the second is started. They are executed concurrently. $ cat cities.txt $ cat cities.txt | head -3 $ cat cities.txt | head -3 | tail -2 Whilst cat can produce 5 lines of text, in the above pipeline, it doesn't. The 3 Unix commands, cat, head and tail are executing concurrently. The cat command only needs to generate output, if the subsequent head and tail commands require it. In this example, we only require cat to generate 3 lines of text to fullfill the requirements of the subsequent head command. We can mirror the above Unix statement in QML. Button {
text: qsTr("LA and NYC cities")
onClicked: {
function *cities() {
yield { name: "Hawaii", distance: 4221.73 }
yield { name: "Los Angeles", distance: 96.65 }
yield { name: "New York City", distance: 3853.10 }
yield { name: "Redlands", distance: 1.12 }
yield { name: "Seattle", distance: 1566.70 }
}
function* cat( iter ) {
yield* iter
}
function* head( iter, n ) {
if (n <= 0) return
for (let item of iter) {
yield item
if (--n <= 0) break
}
}
function* tail( iter, n ) {
let arr = [ ]
for ( let item of iter ) {
arr.push(item)
if (arr.length > n) arr.shift()
}
yield* arr
}
for ( let city of tail( head( cat( cities() ), 3), 2) )
console.log( JSON.stringify( city ) )
}
} View the full HeadAndTails.qml on GitHub. Try it now on AppStudioBlogIterators | WebAssembly. Similar to the Unix head, tail, cat commands, the above head, tail, cat Javascript generator functions start executing concurrently. The data pipeline occurs when callers consume data and the Javascript generator functions yield data. Because of the requirements of the head generator call, we only require the cat generator and the cities generator to generate 3 lines of output. 4. Infinite loops no longer taboo Traditionally, we consider CPU intensive and/or infinite loops to be taboo in Javascript. function helloWorldTaboo() {
while (true)
console.log( "Hello World" )
} They block the UI, they make the program unresponsive, they may eventually lead to a crash. We consider such code taboo and we have been trained to not write in that style. We require the developer to rewrite their applications to use signals and slots or callback functions that help unblock the UI. Rules changes when it comes to Javascript generators and Javascript iterators. Apparent infinite loops are no longer taboo: function* helloWorldGenerator() {
while (true)
yield "Hello World"
} The following demonstrates the Javascript generator working with a Timer object. The program is not CPU intensive and not quite an infinite loop in that one can stop the invoking the Javascript generator by stopping the Timer. Button {
text: qsTr("Run!")
onClicked: {
function* helloWorldGenerator() {
while (true)
yield "Hello World"
}
helloWorldTimer.iter = helloWorldGenerator()
helloWorldTimer.start()
}
}
Timer {
id: helloWorldTimer
property var iter
running: false
repeat: true
interval: 100
onTriggered: {
let item = iter.next()
if (item.done) {
stop()
return
}
console.log( item.value )
}
} View the full InfiniteHello.qml on GitHub. Try it now on AppStudioBlogIterators | WebAssembly. 5. Generators are bidirectional In Unix, the data flows in Unix pipes in one direction. With Javascript generators, the data flow can be bidirectional. We can take a value from the Javascript generator modify and send the result back to the Javascript generator. The following shows the generator yielding 1, 2 and 3 respectively. For each value, we square the result and send the square back to the generator. function* gen() {
console.log( yield 1 )
console.log( yield 2 )
console.log( yield 3 )
}
let iter = gen()
let v = iter.next()
console.log( JSON.stringify(v) )
v = iter.next( v.value * v.value )
console.log( JSON.stringify(v) )
v = iter.next( v.value * v.value )
console.log( JSON.stringify(v) )
v = iter.next( v.value * v.value )
console.log( JSON.stringify(v) ) View the full Bidirectional.qml on GitHub. Try it now on AppStudioBlogIterators | WebAssembly. 6. Mixing Javascript generators with Promises In a previous blog on Promises, I talked about async / await missing in AppStudio 3.3 and Qt 5.12.1 and that you can vote to have it included it https://bugreports.qt.io/browse/QTBUG-58620. I've read in other blogs that async / await is considered by some as syntactic sugar on Javascript generators yielding Promises. I gave the following example demonstrating what this looks like with _asyncToGenerator() a function that works with Promises inside Javascript generators: Button {
text: qsTr( "Test Promise async/await (Babel)" )
onClicked: {
_asyncToGenerator( function*() {
try {
showTitle( yield download( "GET", "https://appstudio.arcgis.com" ) )
showTitle( yield download( "GET", "https://community.esri.com/groups/appstudio" ) )
showTitle( yield download( "GET",
"https://community.esri.com/groups/survey123" ) )
} catch ( err ) {
console.log( "Caught Error: ", err.message )
throw err
}
} )()
}
} View the full PromiseBabel.qml on GitHub. Try it now on AppStudioBlogIterators | WebAssembly. 6. Using ArcGIS Online Search API The following uses Promises to invoke the ArcGIS Online Search API iteratively to retrieve titles of over 200+ AppStudio samples: Button {
text: qsTr("Search")
onClicked: {
_asyncToGenerator( function*() {
networkRequest.url = "https://www.arcgis.com/sharing/rest/search"
networkRequest.responseType = "json"
try {
let f = "pjson"
let q = "type:Native Application owner:appstudio_samples"
let start = 1
let num = 10
while (start !== -1 ) {
let resp = yield networkRequest.sendWithPromise( { f, q, start, num } )
let { nextStart, total, results } = resp.response
for ( let result of results )
console.log( result.title )
start = nextStart
}
} catch (err) {
console.log("Caught exception: ", err.message)
console.log(err.stack)
throw err
}
} )()
}
} View the full ArcGISOnlineSearch.qml on GitHub. Try it now on AppStudioBlogIterators | WebAssembly. 7. Maze Solving Algorithm The following demonstrates how you can use Promises in a recursive manner to solve a maze: Button {
text: qsTr("Solve Maze")
onClicked: {
_asyncToGenerator( function*() {
function solve(x, y) {
return _asyncToGenerator( function*() {
maze[y][x] = someDude
printDaMaze()
yield sleep(100)
if (x === endingPoint[0] && y === endingPoint[1]) return true
if (x > 0 && maze[y][x - 1] === free && (yield solve(x - 1, y))) return true
if (x < mazeWidth && maze[y][x + 1] === free && (yield solve(x + 1, y))) return true
if (y > 0 && maze[y - 1][x] === free && (yield solve(x, y - 1))) return true
if (y < mazeHeight && maze[y + 1][x] === free && (yield solve(x, y + 1))) return true
maze[y][x] = free
printDaMaze()
yield sleep(100)
return false
} )()
}
if (yield solve( startingPoint[0], startingPoint[1]) ) {
console.log( "Solved!" )
printDaMaze()
} else {
console.log( "Cannot solve. :-(" )
}
} )()
}
} View the full MazeSolver.qml on GitHub. Try it now on AppStudioBlogIterators | WebAssembly. 8. Experimental WebAssembly Qt now offers a WebAssembly target. This means it is possible to deploy QML applications in a web browser. WebAssembly is an experimental technology meaning it's an evolving platform. Expect a lot of things to not work. With the disclaimer out of the way, you can test all of the code snippets in this blog in a Web Assembly application here: https://stephenquan.github.io/AppStudio/Apps/AppStudioBlogIterators/ Send us your feedback to appstudiofeedback@esri.com Become an AppStudio for ArcGIS developer! Watch this video on how to sign up for a free trial. Follow us on Twitter @AppStudioArcGIS to keep up-to-date on the latest information and let us know about your creations built using AppStudio to be featured in the AppStudio Showcase. The AppStudio team periodically hosts workshops and webinars; please click on this link to leave your email if you are interested in information regarding AppStudio events.
... View more
07-31-2019
07:10 AM
|
2
|
2
|
2009
|
POST
|
If you're using AppStudio for ArcGIS, there are several ways for working with images and uploading. 1. NetworkRequest has built in support for multipart/form-data if it detects a file upload in the request (similar to Unix curl command). NetworkRequest {
id: networkRequest
method: "POST"
url: "https://some.end.point"
}
networkRequest.send( {
f: "pjson",
file: networkRequest.uploadPrefix + "myimage.png"
} ) This is the best method, because (a) your application logic is simple, (b) the file is not loaded into memory, but streamed in blocks directly from file to the internet, allow one to upload very large files, (c) you needn't worry about setting your headers correctly for a multipart/form-data upload, this is done for you automatically. 2. We have File and BinaryData object for reading a file and converting it to another format, e.g. base64 encoding
... View more
07-25-2019
06:51 PM
|
0
|
2
|
851
|
Title | Kudos | Posted |
---|---|---|
1 | 03-13-2023 08:11 PM | |
2 | 02-21-2023 07:29 PM | |
1 | 06-19-2019 05:33 PM | |
1 | 06-19-2019 01:00 AM | |
1 | 06-01-2017 04:34 PM |
Online Status |
Offline
|
Date Last Visited |
09-18-2023
02:55 AM
|