Is this right way to code for JavaFx application? - javafx

I am new to JavaFX. SO i would like to know is the code provided below is the good way to caode in for JavaFX application by declaring .fx files.
or
We should code using.java files.
Help is needed on this regard.
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.Group;
import javafx.scene.text.Text;
import javafx.ext.swing.SwingLabel;
import javafx.ext.swing.SwingTextField;
import javafx.ext.swing.SwingButton;
var login = false;
var userName = "";
var systemUser = "test";
Stage {
title : "Login App"
scene: Scene {
width: 300
height: 300
content: bind if(not login) Group{
content: [
SwingLabel{
text: "User Name:"
},
SwingTextField {
text : bind userName with inverse;
columns : 10;
editable : true;
},
SwingButton{
translateX: 50
translateY: 50
text: "Submit"
action: function(){
if((userName != systemUser)) {
println("Invalid UserName");
}
login = (userName == systemUser);
}
}
]
}
else Group{
content: [
Text {
x: 10 y: 30
content: "You have successfully logged in."
},
SwingButton{
translateX: 10
translateY: 50
text: "Log out"
action: function(){
userName = "";
login = false;
}
}
]
}
}
}

The JavaFX script code (with .fx files) in your question is from the obsolete JavaFX 1.x branch. I strongly advise you not to use the JavaFX 1.x branch. Oracle will drop all support for it this month.
The script code in your question also integrates Swing controls inside JavaFX, which is not supported in current JavaFX 2.x releases.
Instead, use JavaFX 2+ and write your JavaFX code in Java (with .java files) using only JavaFX controls and no Swing controls.
There are some excellent tutorials to get you started:
Oracle's Ensemble Sample
edu.maker.ch JavaFX Tutorial
Oracle's JavaFX HelloWorld App
JavaFX Documentation Home
StackOverflow JavaFX Community Wiki Resource List
Oracle's Java tutorials (if you need to learn the Java language)

Related

How do you get the currently active notebook name in JupyterLab?

I'm working on creating a server-side extension in JupyterLab and have been searching for quite a while for a way to get the currently active notebook name inside my index.ts file. I found this existing extension that gets the currently active tab name with ILabShell and sets the browser tab to have the same name. In my case I'll be triggering the process with a button on the notebook toolbar so the active tab will always be a notebook. However, when I try to use the code in the activate section of my extension, I get TypeError: labShell.currentChanged is undefined where labShell is an instance of ILabShell. That extension doesn't support JupyterLab 3.0+ so I believe that's part of the problem. However, currentChanged for ILabShell is clearly defined here. What if anything can I change to make it work? Is there another way to accomplish what I'm trying to do? I'm aware of things like this to get the notebook name inside the notebook but that's not quite what I'm trying to do.
Windows 10,
Node v14.17.0,
npm 6.14.13,
jlpm 1.21.1,
jupyter lab 3.0.14
I'm using this example server extension as a template: https://github.com/jupyterlab/extension-examples/tree/master/server-extension
index.ts file from the existing extension to get the current tab name:
import {
ILabShell,
JupyterFrontEnd,
JupyterFrontEndPlugin
} from '#jupyterlab/application';
import { Title, Widget } from '#lumino/widgets';
/**
* Initialization data for the jupyterlab-active-as-tab-name extension.
*/
const extension: JupyterFrontEndPlugin<void> = {
id: 'jupyterlab-active-as-tab-name',
autoStart: true,
requires: [ILabShell],
activate: (app: JupyterFrontEnd, labShell: ILabShell) => {
const onTitleChanged = (title: Title<Widget>) => {
console.log('the JupyterLab main application:', title);
document.title = title.label;
};
// Keep the session object on the status item up-to-date.
labShell.currentChanged.connect((_, change) => {
const { oldValue, newValue } = change;
// Clean up after the old value if it exists,
// listen for changes to the title of the activity
if (oldValue) {
oldValue.title.changed.disconnect(onTitleChanged);
}
if (newValue) {
newValue.title.changed.connect(onTitleChanged);
}
});
}
};
export default extension;
My index.ts file:
import {
ILabShell,
JupyterFrontEnd,
JupyterFrontEndPlugin
} from '#jupyterlab/application';
import { ICommandPalette } from '#jupyterlab/apputils';
import { ILauncher } from '#jupyterlab/launcher';
import { requestAPI } from './handler';
import { ToolbarButton } from '#jupyterlab/apputils';
import { DocumentRegistry } from '#jupyterlab/docregistry';
import { INotebookModel, NotebookPanel } from '#jupyterlab/notebook';
import { IDisposable } from '#lumino/disposable';
import { Title, Widget } from '#lumino/widgets';
export class ButtonExtension implements DocumentRegistry.IWidgetExtension<NotebookPanel, INotebookModel> {
constructor(app: JupyterFrontEnd) {
this.app = app;
}
readonly app: JupyterFrontEnd
createNew(panel: NotebookPanel, context: DocumentRegistry.IContext<INotebookModel>): IDisposable {
// dummy json data to test post requests
const data2 = {"test message" : "message"}
const options = {
method: 'POST',
body: JSON.stringify(data2),
headers: {'Content-Type': 'application/json'
}
};
// Create the toolbar button
let mybutton = new ToolbarButton({
label: 'Measure Energy Usage',
onClick: async () => {
// POST request to Jupyter server
const dataToSend = { file: 'nbtest.ipynb' };
try {
const reply = await requestAPI<any>('hello', {
body: JSON.stringify(dataToSend),
method: 'POST'
});
console.log(reply);
} catch (reason) {
console.error(
`Error on POST /jlab-ext-example/hello ${dataToSend}.\n${reason}`
);
}
// sample POST request to svr.js
fetch('http://localhost:9898/api', options);
}
});
// Add the toolbar button to the notebook toolbar
panel.toolbar.insertItem(10, 'MeasureEnergyUsage', mybutton);
console.log("MeasEnerUsage activated");
// The ToolbarButton class implements `IDisposable`, so the
// button *is* the extension for the purposes of this method.
return mybutton;
}
}
/**
* Initialization data for the server-extension-example extension.
*/
const extension: JupyterFrontEndPlugin<void> = {
id: 'server-extension-example',
autoStart: true,
optional: [ILauncher],
requires: [ICommandPalette, ILabShell],
activate: async (
app: JupyterFrontEnd,
palette: ICommandPalette,
launcher: ILauncher | null,
labShell: ILabShell
) => {
console.log('JupyterLab extension server-extension-example is activated!');
const your_button = new ButtonExtension(app);
app.docRegistry.addWidgetExtension('Notebook', your_button);
// sample GET request to jupyter server
try {
const data = await requestAPI<any>('hello');
console.log(data);
} catch (reason) {
console.error(`Error on GET /jlab-ext-example/hello.\n${reason}`);
}
// get name of active tab
const onTitleChanged = (title: Title<Widget>) => {
console.log('the JupyterLab main application:', title);
document.title = title.label;
};
// Keep the session object on the status item up-to-date.
labShell.currentChanged.connect((_, change) => {
const { oldValue, newValue } = change;
// Clean up after the old value if it exists,
// listen for changes to the title of the activity
if (oldValue) {
oldValue.title.changed.disconnect(onTitleChanged);
}
if (newValue) {
newValue.title.changed.connect(onTitleChanged);
}
});
}
};
export default extension;
There are two ways to fix it and one way to improve it. I recommend using (2) and (3).
Your order of arguments in activate is wrong. There is no magic matching of argument types to signature function; instead arguments are passed in the order given in requires and then optional. This means that you will receive:
...[JupyterFrontEnd, ICommandPalette, ILabShell, ILauncher]
but what you are expecting is:
...[JupyterFrontEnd, ICommandPalette, ILauncher, ILabShell]
In other words, optionals are always at the end. There is no static type check so this is a common source of mistakes - just make sure you double check the order next time (or debug/console.log to see what you are getting).
Actually, don't require ILabShell as a token. Use the ILabShell that comes in app.shell instead. This way your extension will be also compatible with other frontends built using JupyterLab components.
shell = app.shell as ILabShell
(optional improvement) install RetroLab as a development-only requirement and use import type (this way it is not a runtime requirement) to ensure compatibility with RetroLab:
import type { IRetroShell } from '#retrolab/application';
// ... and then in `activate()`:
shell = app.shell as ILabShell | IRetroShell
to be clear: not doing so would not make your extension incompatible; what it does is ensures you do not make it incompatible by depending on lab-specific behaviour of the ILabShell in the future.
So in total it would look like:
import {
ILabShell,
JupyterFrontEnd,
JupyterFrontEndPlugin
} from '#jupyterlab/application';
import type { IRetroShell } from '#retrolab/application';
// ...
const extension: JupyterFrontEndPlugin<void> = {
id: 'server-extension-example',
autoStart: true,
optional: [ILauncher],
requires: [ICommandPalette],
activate: async (
app: JupyterFrontEnd,
palette: ICommandPalette,
launcher: ILauncher | null
) => {
let shell = app.shell as ILabShell | IRetroShell ;
shell.currentChanged.connect((_, change) => {
console.log(change);
// ...
});
}
};
export default extension;

Elm with webcomponents - Is this a bug or am I doing something wrong?

Problem summary
I am using webcomponents in elm 0.19.1.
Goal is for the webcomponent to emit an event if the webcomponent attribute "requeststate" changes.
The event is emitted (I can see in the the logging in the webcomponent constructor), but elm is not firing the 'WebcomponentEvent' correctly.
Problem is on both windows 10 and ubuntu 16.04, firefox and chrome.
Didnot test older elm versions.
Steps to reproduce:
I have the problem reproduced in this minimal repo:
https://github.com/Derryrover/elm_webcom_bug
Compile elm with:
elm make src/Main.elm --output elm.js --debug
use a webserver (for example 'serve') to host locally
click the button 'request'. This will change the attribute 'requeststate' on the webcomponent. That in turn triggers the custom event 'created' on the webcomponent. But elm does not receive this event..
Opening the elm-debugger or clicking the 'request' button again will magically make the event fire in elm. Strange.
Also I have made the branch 'tom-experiment'. In this branch I got the event from webcomponent-click working, but only if I directly click on the webcomponent itself.
The importance of this problem
Why do I want to trigger an event on a webcomponent by changing an attribute?
Goal of this approach is also JavaScript interop.
For example I can now use this webcomponent to create a date-time or uuid or do some other javascript magic. This way I can work around ports completely.
A solution for this problem might settle the entire Javascript interop discussion !
Code
This is my Main.elm:
module Main exposing (..)
import Browser
import Html
import Html.Attributes
import Html.Events
import Json.Decode
main =
Browser.sandbox
{ init = init
, view = view
, update = update
}
type alias Model =
{ request : Bool
, received: Bool
}
init : Model
init = { request = False
, received = False
}
type Msg
= Request
| Reset
| WebcomponentEvent
update : Msg -> Model -> Model
update msg model =
case msg of
WebcomponentEvent ->
{ model | received = True}
Request ->
{ model | request = True }
Reset ->
{ model | request = False , received = False}
view : Model -> Html.Html Msg
view model =
Html.div []
[ Html.button [Html.Events.onClick Request] [Html.text "Request"]
, Html.div
[]
[ Html.text "requested:"
, Html.text (if model.request then "true" else "false")
]
, Html.div
[]
[ Html.text "received:"
, Html.text (if model.received then "true" else "false")
]
, Html.button [Html.Events.onClick Reset] [Html.text "Reset"]
, Html.node "webcomponent-test"
[ Html.Events.on "created" (Json.Decode.succeed WebcomponentEvent)
, Html.Attributes.attribute "requeststate" (if model.request == True then "requested" else "idle")
] []
]
This is the webcomponent.js
class Webcomponent extends HTMLElement {
static get observedAttributes() {
return [
"requeststate"
];
}
attributeChangedCallback(name, oldValue, newValue) {
switch (name) {
case "requeststate":
if (newValue === "requested") {
console.log(`requested in webcomponent triggered`);
const customEvent = new CustomEvent('created', {detail: ""});
this.dispatchEvent(customEvent);
}
}
}
constructor() {
super();
this.addEventListener('created', function (e) {
console.log("event triggered as sensed by javascript: ", e.detail, e);
}, false, true);
}
}
customElements.define('webcomponent-test', Webcomponent);
this is my index.html
<!doctype html>
<html>
<head>
<script type="module" src="./webcomponent.js"></script>
<script src="./elm.js"></script>
</head>
<body>
<div id="elm_element"></div>
<script>
var app = Elm.Main.init({
node: document.getElementById('elm_element')
});
</script>
</body>
</html>
This was discussed on the Elm Slack. The issue ended up being a timing issue. The resolution is to change from
this.dispatchEvent(customEvent)
to
requestAnimationFrame(() => this.dispatchEvent(customEvent))
in the custom element, as can be seen in this ellie https://ellie-app.com/cqGkT6xgwqKa1.
Thanks to #antew for the final solution.
There is another potential cause:
This attributeChangedCallback runs before the element is connected to the DOM
attributeChangedCallback(name, oldValue, newValue) {
switch (name) {
case "requeststate":
if (newValue === "requested") {
console.log(`requested in webcomponent triggered`);
const customEvent = new CustomEvent('created', {detail: ""});
this.dispatchEvent(customEvent);
}
}
}
Add
connectedCallback(){
console.log("Now I am ready to emit Events");
}
to verify your dispatchEvent doesn't run too soon.
requestAnimationFrame (or setTimeout) are workarounds to wait till the Event Loop is empty (and thus connectedCallback ran)
(I don't know your use case) You could also test for oldValue === null or this.isConnected in your attributeChangedCallback
Also note you probably need bubbles:true and composed:true on that CustomEvent when shadowDOM is involved.

Qbs custom module not working

I want to make a module to use the QtRO repc compiler to produce .h files from .rep files.
I coded the module but when I try to load it in an application product it does not load and disable the product.
the modules are in C:\Users\User\qt\qbs
Qbs Module replica.qbs:
import qbs
Module {
property bool source: true
FileTagger {
patterns: "*.rep"
fileTags: ["rep"]
}
Rule {
inputs: ["rep"]
Artifact {
fileTags: ["txt_output"]
}
prepare: {
var cmd = new Command();
cmd.program = "repc.exe";
if source {
cmd.arguments = ["-i", "rep", "-o", "source", input.filePath];
} else {
cmd.arguments = ["-i", "rep", "-o", "replica", input.filePath];
}
console.log("repc on : ", input.filePath);
return [cmd];
}
}
}
product.qbs:
import qbs
Application {
name: "ServiceExposer"
Depends { name: "cpp" }
Depends { name: "Qt.core" }
Depends { name: "Qt.remoteobjects" }
Depends { name: "replica" }
files: [
"main.cpp",
"service_exposer.rep"
]
}
project.qbs:
import qbs
Project {
references: ["ServiceExposer/ServiceExposer.qbs"]
qbsSearchPaths: "C:\Users\User\qt\qbs"
}
I don't see where I made the mistake.
Thank you in advance for your help.
If it's a header file, why do you give it the "cpp" tag? Shouldn't it be "hpp"?
What is the reason you are putting the file into the source directory? Do you plan on adding it to your repository? Normally, build artifacts (no matter whether they are binaries or human-readable files) should be located inside the build directory as not to "pollute" the source tree.
You did not mention in what way the module does not work for you now, so it's hard to diagnose the problem. You should mention what you expected to happen and what happened instead (giving the concrete error message, if there is one).
I managed to make it work after digging a little more in the doc and source code, I share with you the working module.
This module when imported if there are any .rep files (QtRO (remote objects)) module remote object definition) in your project, it will invoke the repc compiler and compile them and put the resulting .h file in your source directory.
Still not complete, I didn't find a way to manipulate the files property of the Product Item to add the .h to it automatically.
import qbs
import qbs.FileInfo
Module {
FileTagger {
patterns: ["*.rep"]
fileTags: ["repc-rep"]
}
Rule {
inputs: ["repc-rep"]
Artifact {
filePath: repc_" + FileInfo.baseName(input.fileName) + "_source.h"
fileTags: ["cpp"]
}
prepare: {
var cmd = new Command();
cmd.description = "repc " + input.fileName;
cmd.program = "repc.exe"
cmd.arguments = ["-i", "rep", "-o", "source", input.filePath, output.filePath];
var cmd2 = new JavaScriptCommand();
cmd2.silent = true;
cmd2.sourceCode = function() {
File.copy(output.filePath, FileInfo.path(input.filePath) + "/" + output.fileName);
}
return [cmd, cmd2];
}
}
}
In order to this module to work, the repc.exe must be in your path.
Any suggestion are welcomed.

Cascade QML TabPane - maintaining TabPane through multiple pages

I've been trying to research this all weekend, but can't find a similar example.
I'm trying to keep a TabPane consistent through out multiple pages. As of right now, once you go past 1 page, the TabPane will no longer be there.
For example:
TabbedPane {
id: root
showTabsOnActionBar: true
Tab {
title: qsTr("Search") + Retranslate.onLocaleOrLanguageChanged
Search {
}
}
}
// Search.qml
NavigationPane {
id: navigationPane
Page {
Button {
onClicked: {
navigationPush.push(pageSearchResults.createObject())
}
}
}
attachObjects: [
ComponentDefinition {
id: pageSearchResults
SearchResults {
}
}
]
}
So basically at this point when we're on the Search page, we have the TabPane.
As soon as I push that Button and navigate to the SearchResults page. The TabPane is gone...
// SearchResults.qml
// We're now 2 pages -IN- from the TabPane
Page {
Button {
onClicked: {
navigationPush.push(nextPage.createObject())
}
}
attachObjects: [
ComponentDefinition {
id: nextPage
NextPage {
}
}
]
}
Also once we're on SearchResults - it won't let me push the next page. When I click the Button on SearchResults, you can see the navigationPush(nextPage.createObject()). It gives the following error:
pushPage : mNavigationStack : ("211") NavigationPane:
NavigationPaneOnFwdTransitionDone: emitting push transition ended for
page: 211 client top: 211
What you need to do is add a navigationPane to the tab and push pages onto that.
A sheet should be opened for something seperate to the tab - e.g. a settings page. Sheets also require buttons or code to close them.
It's not clear from your code whether you are using a navigationPane or not, so here is an example of how it should be working:
TabbedPane {
id: root
showTabsOnActionBar: true
Tab {
title: qsTr("Search") + Retranslate.onLocaleOrLanguageChanged
NavigationPane {
id: navSearch
Page {
}
}
}
}
Now when you want to navigate to the next page in search you will add it to the navSearch navigationPane. This will give you the back button as well.
It would be beneficial to read through the documentation on navigation here: http://developer.blackberry.com/native/documentation/cascades/ui/navigation/index.html
It will explain many details of how the navigation works and how different elements such as tabbed panes and sheets interact.

Getting an error in in JavaFX

I am trying to test this code but I am encountering problems. I have netbeans with default JavaFX platform.
This example is from this web site: http://onjava.com/pub/a/onjava/2007/07/27/introduction-to-javafx-script.html?page=4
package captureexample1;
import java.io.*;
import javafx.ui.*;
import javafx.ui.canvas.*;
import javafx.ui.filter.*;
import java.awt.Robot;
import java.awt.Rectangle;
import java.awt.image.RenderedImage;
import javax.imageio.ImageIO;
import java.lang.System;
class CaptureExample extends CompositeNode{
attribute lx: Integer;
attribute ly: Integer;
operation CaptureExample();
}
attribute CaptureExample.lx = 0;
attribute CaptureExample.ly = 0;
operation saveCapture(lx_copy:Integer, ly_copy:Integer) {
var robot = new Robot();
var rect = new Rectangle (lx_copy, ly_copy, 50, 50);
var BI=robot.createScreenCapture(rect);
var file = new File(".//capture.jpg");
ImageIO.write((RenderedImage)BI, "jpg", file);
}
function CaptureExample.composeNode() =
Group{
transform: []
content:[ImageView {
transform: []
image: Image { url: ".//app//Sunset.gif" }
cursor: DEFAULT
onMouseClicked: operation(e:CanvasMouseEvent) {
saveCapture(e.source.XOnScreen,e.source.YOnScreen);
}
onMouseMoved: operation(e:CanvasMouseEvent) {
lx = e.x;
ly = e.y;
}
},
Rect{
x: bind lx
y: bind ly
width: 50
height:50
strokeWidth: 1
stroke: black
}]
};
Frame {
centerOnScreen: true
visible: true
height: 230
width: 300
title: "Capture the screen..."
onClose: operation() {System.exit(0);}
content: ScrollPane {
background: white
view: Canvas {
background: black
cursor: DEFAULT
content: CaptureExample
}
}
}
It seems you are mixing JavaFX script and JavaFX2.
JavaFX script (aka JavaFX 1.3) is obsolete, not supported and old.
modern JavaFX 2 uses Java as language instead of FX script. You can see examples here: http://docs.oracle.com/javafx/2/get_started/jfxpub-get_started.htm
If you really want to use JavaFX 1.3:
get older NetBeans, like 6.1
rename you .java file to .fx
But if you just trying to learn JavaFX I really advise you to try JavaFX 2.

Resources