POST
|
Dear M Ka, As this is an AppStudio question, it's probably asked in the AppStudio community: https://community.esri.com/groups/appstudio Sometime ago, I wrote a blog on LAN, WIFI, Mobile Network Check. See the blog here: https://community.esri.com/groups/appstudio/blog/2018/06/19/network-check Stephen
... View more
03-27-2019
01:53 PM
|
0
|
0
|
1360
|
POST
|
Hi Ronnie, It appears that you have AppStudio 3.2 and Qt5.11.2 installed. Note that there are two copies of Qt Creator, i.e. one we ship with AppStudio and the one you get with Qt5.11.2. From reading your issue, it appears that your are having an issue with the Qt Creator that's shipped with Qt5.11.2. Firstly, let me comment on your Qt5.11.2 installation. It isn't a requirement to install Qt5.11.2 to use AppStudio. This is because we don't ask AppStudio users to install a full development environment on their PC. i.e. If you're seeking to use AppStudio and Cloud Make only to use your apps, then, you don't really need to install Qt5.11.2. You can be fully productive making, managing your apps in AppStudio and leveraging from our Cloud Make infrastructure to have your app built for any platform you need it on. If you wish to make use of Local Make, then, you're intending to install all the development tools on your PC. i.e. Qt5.11.2, Microsoft Visual Studio, Android SDK, Android NDK, Qt Installer Framework, etc. Inside the %USERPROFILE%\Applications\ArcGIS\AppStudio\sde\ideintegration folder there's a post-install.bat script. You'll need to run this script to complete the AppStudio installation with your development tools. This will let the Qt Creator that's shipped with Qt5.11.2 to recognize the components from AppStudio. Stephen
... View more
03-06-2019
06:43 PM
|
2
|
1
|
1097
|
POST
|
Hi Heathcliff, The Barcode Reader is powered by zxing libraries which is defined to support CODE 93. However see what other people have said about their CODE 93 issues Issues · zxing/zxing · GitHub This issue 743 is particularly interesting Issues · zxing/zxing · GitHub as it includes a testing: http://zxing.org/w/decode?u=https%3A%2F%2Fcloud.githubusercontent.com%2Fassets%2F15977592%2F21809695%2Fd59153c6-d76e-11e6-927a-79f4c156a065.png Presumeably you could upload your CODE 93 to gist.github.com (or similar cloud base service) and run it through the http://zxing.org/w/decode?u= tester. Stephen
... View more
02-19-2019
09:29 PM
|
0
|
1
|
800
|
POST
|
Hi José, in the following example, I'm able to use `VACUUM` on a `SqlDatabase`: import QtQuick 2.7
import QtQuick.Controls 2.1
import ArcGIS.AppFramework 1.0
import ArcGIS.AppFramework.Controls 1.0
import ArcGIS.AppFramework.Sql 1.0
App {
id: app
width: 800
height: 640
property string dbFileName: "sample.sqlite"
property string dbFilePath: sqlFolder.filePath(dbFileName)
ListView {
id: listView
property ListModel logListModel: ListModel { }
anchors.fill: parent
anchors.margins: 10
model: logListModel
delegate: Text { text: logText }
function log(logText) { logListModel.append( { logText: logText } ); }
}
FileFolder {
id: sqlFolder
path: "~/ArcGIS/Data/Sql"
}
SqlDatabase {
id: db
databaseName: dbFilePath
}
function db_exec_sql(sql, obj) {
listView.log( "db_exec_sql.sql: %1".arg(sql) );
if (obj != null) {
listView.log( "db_exec_sql.obj: %1".arg(JSON.stringify(obj)) );
}
var query = obj != null ? db.query(sql, obj) : db.query(sql);
if (query.error) {
listView.log( "query.error: %1".arg(query.error) );
return;
}
var ok = query.first();
while (ok) {
listView.log( "query.values: %1".arg(JSON.stringify(query.values)));
ok = query.next();
}
query.finish();
}
Component.onCompleted: {
listView.log( "dbFileName: %1".arg(dbFileName) );
listView.log( "dbFilePath: %1".arg(dbFilePath) );
sqlFolder.makeFolder();
db.open();
db_exec_sql( "DROP TABLE IF EXISTS people" );
db_exec_sql( "CREATE TABLE people (NAME text)" );
db_exec_sql( "INSERT INTO people VALUES (:name) ", { name: 'Bill Gates' } );
db_exec_sql( "INSERT INTO people VALUES (:name) ", { name: 'Jack Dangermond' } );
db_exec_sql( "SELECT * FROM people" );
db_exec_sql( "VACUUM" );
db_exec_sql( "SELECT * FROM people" );
}
} The program completes successfully, and, because I use `~/ArcGIS/Data/Sql`, the folder will exist on all platforms. The output I get when I run the above on Windows is: dbFileName: sample.sqlite
dbFilePath: C:/Users/stephen/ArcGIS/Data/Sql/sample.sqlite
db_exec_sql.sql: DROP TABLE IF EXISTS people
db_exec_sql.sql: CREATE TABLE people (NAME text)
db_exec_sql.sql: INSERT INTO people VALUES (:name)
db_exec_sql.obj: {"name":"Bill Gates"}
db_exec_sql.sql: INSERT INTO people VALUES (:name)
db_exec_sql.obj: {"name":"Jack Dangermond"}
db_exec_sql.sql: SELECT * FROM people
query.values: {"NAME":"Bill Gates"}
query.values: {"NAME":"Jack Dangermond"}
db_exec_sql.sql: VACUUM
db_exec_sql.sql: SELECT * FROM people
query.values: {"NAME":"Bill Gates"}
query.values: {"NAME":"Jack Dangermond"}
... View more
01-29-2019
01:54 PM
|
0
|
1
|
465
|
POST
|
Could it be that you have multiple applets, and, Application.Applets.Item(1) refers to the 'wrong' applet?
... View more
01-14-2019
01:47 PM
|
0
|
0
|
164
|
POST
|
Please validate you have a valid AppStudio JKS keystore by running the keytool as follows: keytool -keystore your_android.keystore -list -v
Keystore type: JKS
Keystore provider: SUN
Your keystore contains 1 entry
Alias name: your_keystore_alias_name
Creation date: ... The fields to check are: - Keystore type: JKS (not PKCS12) - Alias name: whatever shown here matches the one you tell AppStudio - Check date range: (e.g. Valid from: Wed Nov 14 11:22:33 UTC 2018 until: Sun Apr 01 11:22:33 UTC 2046)
... View more
12-10-2018
02:03 PM
|
1
|
1
|
485
|
BLOG
|
Introduction I have some strings in my database. I would like to query them. Also, I want the results quick. Simple, use SQL LIKE and put an index on it. Oh wait, that's weird, it didn't work like how I want... this blog covers the common traps with searching text strings in SQLite. Scenario Let's look at a sample database table. This one is representing property parcels and their very famous owners. CREATE TABLE parcel
(
owner TEXT
);
INSERT INTO parcel (owner) values ('Bill Gates');
INSERT INTO parcel (owner) values ('Steve Jobs');
INSERT INTO parcel (owner) values ('Jack Dangermond');
INSERT INTO parcel (owner) values ('Steve Wozniak');
INSERT INTO parcel (owner) values ('Tim Cook');
INSERT INTO parcel (owner) values ('Mark Zuckerberg'); We will be running queries on the above data, similar to: SELECT * FROM parcel; and, at the same time, we will be studying the query's execution plan with something similar to: EXPLAIN QUERY PLAN SELECT * FROM parcel; Using and optimizing LIKE Once you dabble in a bit of SQL you'll quickly realize that you need to use LIKE for your string searches. For example, if we want to find all owners with names beginning with the letter J we do this: SELECT *
FROM parcel
WHERE owner LIKE 'j%';
-- EXPLAIN QUERY PLAN: SCAN TABLE parcel
-- OUTPUT: {"owner":"Jack Dangermond"}
Here, we observe that it found the right result, i.e. Jack Dangermond, however, the query needed to do a full table scan (i.e. SCAN TABLE parcel) to find that this was the only result. Let's attempt to speed this up with an index and try again: CREATE INDEX ix_parcel_owner ON parcel (owner);
SELECT *
FROM parcel
WHERE owner LIKE 'j%'
-- EXPLAIN QUERY PLAN: SCAN TABLE parcel
-- OUTPUT: {"owner":"Jack Dangermond"}
Well, we got the right result, i.e. Jack Dangermond, but, why is it still doing a full table scan (SCAN TABLE parcel)? Why didn't it use my index ix_parcel_owner? The answer is SQLite, unlike other databases, implements LIKE as a case insensitive search so we got Jack Dangermond which begins with a capital J even though our search pattern was "j%" which begins with a lowercase J. The index that we created was a case sensitive index. LIKE ignored the index because it needed a case insensitive index. To fix this, we try again. We create an index specific for case insensitive searches. CREATE INDEX ix_parcel_owner_collate ON parcel (owner COLLATE NOCASE);
SELECT *
FROM parcel
WHERE owner LIKE 'j%';
-- EXPLAIN QUERY PLAN: SEARCH TABLE parcel USING COVERING INDEX ix_parcel_owner_collate (owner>? AND owner<?)
-- OUTPUT: {"owner":"Jack Dangermond"}
There, we did it. The result is still Jack Dangermond, this time the index was used (SEARCH TABLE parcel USING COVERING INDEX ix_parcel_owner_collate). Take home message, COLLATE NOCASE is your friend. Alternatives to LIKE for string contains searches Now that we've got some success with LIKE, let's use it to find more things. Let's see if we can find all owners with the letter C anywhere in their name: SELECT *
FROM parcel
WHERE owner LIKE '%c%';
-- EXPLAIN QUERY PLAN: SCAN TABLE parcel
-- OUTPUT: {"owner":"Jack Dangermond"}
-- OUTPUT: {"owner":"Tim Cook"}
-- OUTPUT: {"owner":"Mark Zuckerberg"} Okay, we got the results we wanted. Jack Dangermond, Tim Cook and Mark Zuckerberg all have the letter C is their names. However, why are we back at full table scans (SCAN TABLE parcel)? Why isn't the index (ix_parcel_owner_collate) being used any more? That's because the B-Tree index being used works like how you look up names in a phone book. It works great if you have the starting letter(s) (i.e. divide the book in half, choose the half your letter is in, divide the book in half again). However, because we don't have a starting letter, we can no longer use the phone book trick. We're back scanning every record in the table. The index we created is useless for this type of query. So, what can we do? Well, there's a technique you can use but it requires rewriting the query, have a look at the following: SELECT *, INSTR(LOWER(owner), LOWER('c')) idx
FROM parcel;
-- EXPLAIN QUERY PLAN: SCAN TABLE parcel
-- OUTPUT: {"idx":0,"owner":"Bill Gates"}
-- OUTPUT: {"idx":0,"owner":"Steve Jobs"}
-- OUTPUT: {"idx":3,"owner":"Jack Dangermond"}
-- OUTPUT: {"idx":0,"owner":"Steve Wozniak"}
-- OUTPUT: {"idx":5,"owner":"Tim Cook"}
-- OUTPUT: {"idx":8,"owner":"Mark Zuckerberg"}
SELECT *
FROM parcel
WHERE INSTR(LOWER(owner), LOWER('c')) > 0;
-- EXPLAIN QUERY PLAN: SCAN TABLE parcel
-- OUTPUT: {"owner":"Jack Dangermond"}
-- OUTPUT: {"owner":"Tim Cook"}
-- OUTPUT: {"owner":"Mark Zuckerberg"} What is happening is we're using a function INSTR which returns the position of a substring (i.e. the letter C) in a string (i.e. owner). If the substring doesn't exist, you will simply get 0. We use the LOWER function on both the letter C and the owner to make it a case insensitive search. The result is still the same as the previous version involving LIKE, i.e. we are getting all owners with the letter C in their names. However, the query plan is still doing a full table scan (SCAN TABLE parcel). So, what's the point? Well, SQLite allows you to index expressions! Think of it like a pre-calculated column. CREATE INDEX ix_parcel_owner_instr_c ON parcel (INSTR(LOWER(owner), LOWER('c'))); The above statement will create an index. It may take some time. For example, if you had over 200000 records, that index may take several seconds to create. Consider that to be a good thing. Time spent here means the INSTR expression is being calculated for all records in the database once and only once. Every time we add a new record or modify an existing record, it will be the only time when that INSTR expression is calculated / recalculated. That expression will never be recalculated at the time of the query. The query would just reuse the pre-calculated value that was stored in the index: SELECT *
FROM parcel
WHERE INSTR(LOWER(owner), LOWER('c')) > 0;
-- EXPLAIN QUERY PLAN: SEARCH TABLE parcel USING INDEX ix_parcel_owner_instr_c (<expr>>?)
-- OUTPUT: {"owner":"Jack Dangermond"}
-- OUTPUT: {"owner":"Tim Cook"}
-- OUTPUT: {"owner":"Mark Zuckerberg"} The above confirms we are now using the index (SEARCH TABLE parcel USING INDEX ix_parcel_owner_instr_c). Code Sample This AppStudio code sample was used to generate all the SQL content in this blog: import QtQuick 2.7
import QtQuick.Controls 2.1
import ArcGIS.AppFramework 1.0
import ArcGIS.AppFramework.Sql 1.0
App {
id: app
width: 800 * AppFramework.displayScaleFactor
height: 640 * AppFramework.displayScaleFactor
property string logText: ""
Flickable {
id: flickable
anchors.fill: parent
anchors.margins: 10
contentWidth: textArea.width
contentHeight: textArea.height
clip: true
TextArea {
id: textArea
width: flickable.width
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
selectByMouse: true
text: logText
}
}
SqlDatabase {
id: db
databaseName: ":memory:"
}
Component.onCompleted: {
db.open();
exec( [
"CREATE TABLE parcel",
"(",
" owner TEXT",
");"
].join("\n") );
exec("INSERT INTO parcel (owner) values ('Bill Gates');");
exec("INSERT INTO parcel (owner) values ('Steve Jobs');");
exec("INSERT INTO parcel (owner) values ('Jack Dangermond');");
exec("INSERT INTO parcel (owner) values ('Steve Wozniak');");
exec("INSERT INTO parcel (owner) values ('Tim Cook');");
exec("INSERT INTO parcel (owner) values ('Mark Zuckerberg');");
exec( [
"SELECT *",
"FROM parcel",
"WHERE owner LIKE 'j%';"
].join("\n") );
exec("CREATE INDEX ix_parcel_owner ON parcel (owner);");
exec( [
"SELECT *",
"FROM parcel",
"WHERE owner LIKE 'j%';"
].join("\n") );
exec("CREATE INDEX ix_parcel_owner_collate ON parcel (owner COLLATE NOCASE);");
exec( [
"SELECT *",
"FROM parcel",
"WHERE owner LIKE 'j%';"
].join("\n") );
exec( [
"SELECT *",
"FROM parcel",
"WHERE owner LIKE '%c%';"
].join("\n") );
exec( [
"SELECT *, INSTR(LOWER(owner), LOWER('c')) idx",
"FROM parcel;",
].join("\n") );
exec( [
"SELECT *",
"FROM parcel",
"WHERE INSTR(LOWER(owner), LOWER('c')) > 0;"
].join("\n") );
exec("CREATE INDEX ix_parcel_owner_instr_c ON parcel (INSTR(LOWER(owner), LOWER('c')));");
exec( [
"SELECT *",
"FROM parcel",
"WHERE INSTR(LOWER(owner), LOWER('c')) > 0;"
].join("\n") );
}
function exec(sql) {
logText += "\n";
logText += sql + "\n";
var explain = db.query("EXPLAIN QUERY PLAN " + sql);
if (!explain.error && explain.first()) {
logText += "-- EXPLAIN QUERY PLAN: %1\n".arg(explain.values.detail);
explain.finish();
}
var query = db.query(sql);
if (query.error) {
logText += query.error.databaseText;
logText += query.error.driverText;
logText += qsTr("NativeErrorCode: %1").arg(query.error.nativeErrorCode);
logText += qsTr("ErrorType: %1").arg(query.error.type);
return;
}
var ok = query.first();
while (ok) {
logText += "-- OUTPUT: %1\n".arg(JSON.stringify(query.values));
ok = query.next();
}
query.finish();
}
} Summary If speed matters to you, you cannot just create an index and just leave it there. You need to check whether your queries use the index with EXPLAIN QUERY PLAN. If your index isn't being used, look at your query. Look at your WHERE clause. Think of what is happening there. Rewrite your WHERE clause if necessary. Create indexes that matches your WHERE clause. If necessary, be prepared to index on expressions. Don't go overboard, we didn't create an index for every letter of the alphabet. That wasn't in our requirements today. We only wanted to search for the letter C and do that better. Over time, requirements changes. We probably will become disinterested in search for the letter C, then, feel free to drop that index. However, if a new criteria becomes more important, e.g. we want to search for all owners with Jack in the first, middle or last name, then, we will create an index to help with that purpose.
... View more
11-29-2018
01:49 PM
|
2
|
0
|
7844
|
POST
|
Not entirely sure I understand your question. Can you provide more information? What is this string? A stackview can only have pages pushed to it. Is this text string a parameter on the page? If you give an explanation to the problem you're trying to solve we can better help you.
... View more
11-22-2018
12:52 PM
|
1
|
0
|
1131
|
POST
|
Franklin, You misunderstand. AppStudio does not come with a complete installation of Qt. Therefore the compiler and debugger issues you raised are expected since we do not bundle them, nor do we intend you to install them. AppStudio is only licensed to include the Qt Creator IDE. We leverage Qt Creator's ability to edit your app, do syntax highlighting, show offline documentation and test your AppStudio App using the AppStudio Run shortcut as I've illustrated in my previous post in this thread. After you've tested your app using AppStudio Run, you are ready to build the AppStudio apps for a number of platforms. There are 3 main methods: Do it online. Use AppStudio for ArcGIS to create an app from a template (see Install AppStudio for ArcGIS—AppStudio for ArcGIS | ArcGIS ). Apart from your browser, no software installation is required. Install AppStudio for ArcGIS Desktop Edition and use Cloud Make. Apart from AppStudio, no other software installation is required. For more details see Create app installation files using cloud Make—AppStudio for ArcGIS | ArcGIS Install AppStudio for ArcGIS Desktop Edition and use Local Make. To use Local Make, other software installation is required, see Install development tools for local Make—AppStudio for ArcGIS | ArcGIS. As you've already installed AppStudio for ArcGIS Desktop, and that you've indicated that you're "new" to mobile development I would highly recommend you stick with option 2 and follow Cloud Make workflow to build your AppStudio Apps. By using Cloud Make, you can quickly target all platforms: Android, iOS, Linux, Windows, macOS and UWP. The 3rd option is for those who want to build their apps completely offline. You need to understand that on Windows, you will only be able to target Windows and UWP with the installation of Qt5.11.1 and Visual Studio 2017. You can target Android as well, but this requires you to install the Android SDK, Android NDK, Java and Gradle. To support iOS and macOS in option 3, you will need to purchase a mac. Similarly, you will need a Linux machine if you are going to target Linux. See Install development tools for local Make—AppStudio for ArcGIS | ArcGIS Stephen
... View more
11-20-2018
02:04 PM
|
0
|
1
|
1552
|
POST
|
Dear Franklin, Usually AppStudio installs to %USERPROFILE%\Applications\ArcGIS\AppStudio where %USERPROFILE% is, typically, C:\Users\%USERNAME%. The Edit button uses the [Tool-QtCreator] defined %APPDATA%\Esri\AppStudio.ini. If the setting is wrong, then nothing will happen when you click it. Please review, e.g. [Tool-QtCreator]
command=C:\\Users\\Stephen\\Applications\\ArcGIS\\AppStudio/QtCreator/bin/qtcreator.exe To run any AppStudio app from within Qt Creator, you will need to use Tools > External > AppStudio > Run (or Alt + Shift + R): Stephen
... View more
11-19-2018
12:57 PM
|
0
|
3
|
1552
|
BLOG
|
Kale is not food! Kale, apparently has many health benefits such as being high in fiber and water. However, me, personally, I find it on par on eating paper and I refuse to even accept that kale is proper food! Kale rant aside, this blog post is really about coding patterns for writing conditionals in an AppStudio (QML) app. I've come up with a simple app that demonstrate the conditional coding pattern. It has a combo box with the values "apple", "carrot" or "kale" where. Upon a user's selection, the app will respond with the corresponding food type: "fruit", "vegetable" or "unknown" respectively. To implement the above app, there are many methods I've come across, thus, I take a comparative approach covering the most common to the most peculiar. Each approach has a complete working code sample and discussed. Approach 1 - if statement import QtQuick 2.7
import QtQuick.Controls 2.1
import ArcGIS.AppFramework 1.0
App {
id: app
property alias food: foodComboBox.currentText
property string foodType: getFoodType(food)
Column {
anchors.fill: parent
anchors.margins: 10
spacing: 10
ComboBox {
id: foodComboBox
model: [ "apple", "carrot", "kale" ]
}
Text {
text: foodType
}
}
function getFoodType(food) {
if (food === "apple") {
return "fruit";
}
if (food === "carrot") {
return "vegetable";
}
return "unknown";
}
} The above is a complete code sample that determines a type of food given a user's input. We see there are two visual components, ComboBox component for the user to select "apple", "carrot" or "kale" and a Text component to display the corresponding food type "fruit", "vegetable" or "unknown" respectively. The visual component is constant in all the apps shown in the blog. The only thing that differs is the implementation of the foodType property. In this example, it is implemented with the if statement via the getFoodType function. It's very obvious, very easy to read and maintain. I'm usually against the if statement pattern: Tends to invite over application of else and else if Tends to force additional code block indentation Tends to lead to unintended code paths Requires repeated applications of the if statement to describe your business logic I've carefully crafted the answer to not utilize any else statements. When using if statements, I prefer to implement early exit with return statement following the guard pattern removing one level of nesting for flatter code and helps avoids errors. See Guard (computer science) - Wikipedia When there are 3 or more cases if statements can become cumbersome to read and maintain. Terse cases may require a refactor moving code to their own functions. Approach 2 - conditional expressions import QtQuick 2.7
import QtQuick.Controls 2.1
import ArcGIS.AppFramework 1.0
App {
id: app
property alias food: foodComboBox.currentText
property string foodType: food === "apple" ? "fruit" : food === "carrot" ? "vegetable" : "unknown"
Column {
anchors.fill: parent
anchors.margins: 10
spacing: 10
ComboBox {
id: foodComboBox
model: [ "apple", "carrot", "kale" ]
}
Text {
text: foodType
}
}
} The conditional (ternary) operator is very short and deceptively simple. It is often used in 1-liner solutions. I tend to use conditional for very simplest of cases. As your application complexity grows, the conditional expression rapidly becomes hard to read and maintain. They tend to obfuscate what you're trying to achieve. When there are 3 or more cases, it quickly loses its appeal as a 1-liner solution. Approach 3 - switch statements import QtQuick 2.7
import QtQuick.Controls 2.1
import ArcGIS.AppFramework 1.0
App {
id: app
property alias food: foodComboBox.currentText
property string foodType: getFoodType(food)
Column {
anchors.fill: parent
anchors.margins: 10
spacing: 10
ComboBox {
id: foodComboBox
model: [ "apple", "carrot", "kale" ]
}
Text {
text: foodType
}
}
function getFoodType(food) {
switch (food) {
case "apple":
return "fruit";
case "carrot":
return "vegetable";
}
return "unknown";
}
} The switch statement is designed to handle multiple cases. If the values for each case is concise, then, this approach works well. In my apps, I find, however, as my requirement grows, the switch statement becomes longer and longer and they it ends up as code that's longer than one screenful. When that happens, we lose the conciseness of this approach. In order to get back down to something that's manageable we refactor the cases by moving the code for each cases to their own function. Approach 4 - lookup table import QtQuick 2.7
import QtQuick.Controls 2.1
import ArcGIS.AppFramework 1.0
App {
id: app
property alias food: foodComboBox.currentText
property var foodTypes: {
"apple": "fruit",
"carrot": "vegetable"
}
property string foodType: foodTypes[food] || "unknown"
Column {
anchors.fill: parent
anchors.margins: 10
spacing: 10
ComboBox {
id: foodComboBox
model: [ "apple", "carrot", "kale" ]
}
Text {
text: foodType
}
}
} This approach implements a lookup table. We leverage a Javascript object's ability to hold key value pairs. The advantages to this approach is the code is declarative with minimal imperative code. I used this approach when managing large list of keys whose values are simple and defined. We simply hard code the list at the top of the app, and/or deployed the list via JSON object resource that gets loaded when the app starts. This is approach is easy to read and maintain. We have a limitation that the key to the lookup table must be a string. Approach 5 - invoking function by name import QtQuick 2.7
import QtQuick.Controls 2.1
import ArcGIS.AppFramework 1.0
App {
id: app
property alias food: foodComboBox.currentText
property string foodType: getFoodType(food)
Column {
anchors.fill: parent
anchors.margins: 10
spacing: 10
ComboBox {
id: foodComboBox
model: [ "apple", "carrot", "kale" ]
}
Text {
text: foodType
}
}
function getFoodType_apple() {
return "fruit";
}
function getFoodType_carrot() {
return "vegetable";
}
function getFoodType(food) {
var func = "getFoodType_" + food;
return func in app ? app[func]() : "unknown";
}
} This approach uses the fact that we can invoke a Javascript function by name. It also requires us to use the name of the parent object (here it is called app). We have to construct the function name, and, if it exists we invoke that function. I used this approach in app where the requirements were changing. I was building a conversion app. The requirements were changing, i.e. the list of inputs (e.g. the list of foods) will grow over time. This pattern was useful as it allowed me to focus on handling new cases as they occur. property var bool use_merriam_webster: true
function getFoodType_tomato()
{
// A tomato is actually a fruit -- but it's a vegetable at the same time
// https://www.businessinsider.com.au/tomato-fruit-or-vegetable-2018-5?r=US&IR=T
if (use_merriam_webster) {
return "fruit";
}
return "vegetable";
} Despite being the driver function being hard to read, it has advantage that it doesn't require maintenance to add new cases. We simply start coding the new cases and they will automatically be picked up. Summary For the "Kale is not food" app all 5 approaches shown above achieve the task and one may argue that there isn't much differences between them in achieving that task. For more complex applications, some of the more obscure approaches such as lookup table or invocation by function name may become more appealing in long term ability to maintain and scale. Generally, whenever I see code involving either the if or switch statement, I ask myself would it be worthwhile to check to see if another approach works better?. Usually, I find: yes, it is.
... View more
10-17-2018
04:04 PM
|
4
|
0
|
971
|
POST
|
Hi Patrick, All the products that I mentioned will implement your use case. The devil is in the detail. As you indicated your not tech savvy, I'm tending to think that Survey123 may be a good fit for you in terms of ease of use and flexibility. Using Survey123, is um, "as easy as 1-2-3"? Hope that didn't sound too corny. I highly recommend you give it a go and I believe you will achieve success with the shortest time investment. You already have access to all the hardware that's needed. A desktop PC that's capable of editing Microsoft Excel files (runs on Windows, macOS and Linux) to prepare your forms. A mobile device (runs on Android, iOS, Windows Phone, Windows, macOS and Linux) to collect your data in the field. As far as software's concerned, in addition to installing Survey123 and Survey123 Connect, you will need Microsoft Excel and an ArcGIS Online account. There's plentiful material on YouTube and GeoNet and the Survey123 official website itself. However, be aware that when it comes to all software I've recommended it is still a case of choosing your "horses for your courses". There is no one best answer. Each have their unique pros and cons. Your mileage will vary with the different apps, so, please give them all an open mind and see which one is a best for for you. Stephen
... View more
09-30-2018
04:00 PM
|
2
|
1
|
447
|
POST
|
Hi Patrick, I noticed that you posted your question in the ArcPad GeoNet forums? That being the case, let's answer the ArcPad support part. ArcPad runs on both Windows and Windows Mobile devices. Of which your present device is neither. If you wish to run ArcPad on Windows, there are a number of very functional Windows devices out there ranging from inexpensive to most rugged. As to running ArcPad on Windows Mobile support, people tend to go for high precision offerings through Esri Partners such as Esri Partners | Trimble | GeoCollector for ArcPad The other part of your question is what can you run on Android? Esri offers quite a number of solutions: Survey123 for ArcGIS Survey123 for ArcGIS : Smarter Forms, Smarter Decisions QuickCapture for ArcGIS https://community.esri.com/community/esri-labs/blog/2018/07/13/introducing-quickcapture-for-arcgis Collector for ArcGIS Collector for ArcGIS | ArcGIS Some of these run on multiple platforms, i.e. Android, iOS, Windows, macOS, ... If these applications don't quite fit your requirements, you can build a custom application from either templates or from ground up: AppStudio for ArcGIS Configure Quick Report—AppStudio for ArcGIS | ArcGIS Configure Map Viewer—AppStudio for ArcGIS | ArcGIS Configure Map Tour—AppStudio for ArcGIS | ArcGIS https://community.esri.com/groups/survey123/blog/2018/04/29/customizing-survey123-part-1-white-labeling ArcGIS Runtime SDK for Qt (latest) | ArcGIS for Developers ArcGIS Runtime SDK for Android (latest) | ArcGIS for Developers ArcGIS Runtime SDK for iOS (latest) | ArcGIS for Developers Stephen
... View more
09-30-2018
02:44 PM
|
0
|
1
|
447
|
POST
|
You mentioned "ArcGIS Pro desktop 10.2" which is ambiguous. Can you clarify if you were running "ArcGIS Pro" or "ArcGIS Desktop" the answer to that question makes a difference. ArcPad uses Microsoft SQL Server Compact Edition 3.x files for disconnected editing. This format is the native database format present on Windows Mobile devices. It's because of this requirement, the ArcPad Data Manager can only run in 32 bit mode which is means it will only work in ArcGIS Desktop not in ArcGIS Pro.
... View more
09-30-2018
02:25 PM
|
0
|
1
|
308
|
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
|