Showing posts with label java. Show all posts
Showing posts with label java. Show all posts

Saturday, January 15, 2022

GlucoStatusFX

Aloha,

Two years ago I wrote an iOS app to monitor the diabetes of our son. This app (GlucoTracker) is written in Swift using SwiftUI and I still use it today on my iPhone and my AppleWatch.

After I've created that app I decided it would be nice to also have such an app on my Mac and so I wrote a Macos app using Swift and SwiftUI (GlucoStatus) that I run on all of my Macs.

Last week I thought by myself it might be a nice exercise to port this native Swift Macos app to JavaFX. Well I was really surprised how easy it was to rewrite this app in Java (I'm just more used to Java than to Swift which might be the main reason for this).

So the app gets it's data from a Nightscout server that you have to setup to monitor the blood glucose values. And in addition you somehow need to feed the blood glucose data into the Nightscout server which usually is done by using a specific sensor like the Dexcom G6, the Freestyle Libre, Enlight or others.

Meaning to say without a Nightscout server my app is useless.

But if you have such a server in place you should be able to use the app.

So the main screen looks as follows:


In the upper (colored) part you will see the current value in large letters. Below it you will find the 5 last delta values which can help you to figure out the current trend (this assumes that the values from you sensor will be updated in intervals of 5 minutes.

Then there is the date and time of the last update and the average of the selected range.

On top of the window you can select the range that should be visualized and used for the statistics.

To setup the application you can click on the little button with the gear on the upper right corner and it will show you the following screen:


In the preferences screen you first of all have to set the url of your nightscout server (e.g. https://YOUR-DOMAIN.herokuapp.com).

In addition you can define if you would like to get notifications for different situations (e.g. the value is low, or acceptable low etc.). Except for the "too low" and "too high" situations you can define whether you would like to get a notification. For all situations you can enable/disable an additional sound that will be played with the notification.

Then you can also define intervals for situations like "too low" or "too high". This means that for example if your blood glucose value is too high the app will show you a notification with the given interval (e.g. every 5 minutes or every 20 minutes etc.)

In the lower part of the preferences screen you can then also define the different ranges that you would like to use (e.g. normal values should be within the range of 70-110 mg/dl etc.)

You could also switch to another unit that is more often used in the US which is mmol/l instead of mg/dl.

On the main screen you will also find 3 icons in the colored area. The rounded arrow on the upper right corner will reload the values (this is usually not needed, only in case you would manually update the values). 

The app pings the Nightscout server every minute to check for the latest value.

The icon on the lower right will show you the "time in range" chart which looks as follows:


This chart will simply show you how often your blood glucose values have been in the defined ranges in the given time range (defined by the buttons on top of the main screen e.g. 7 days, 24 hours etc.)

The last icon that you will find on the lower left corner will show you a pattern view of the values from the last week and it looks as follows:


On top you will find the HbAc1 value that is calculated from the last 30 days. Below that you will find a list of patterns that have been identified by the app.

And at the bottom you will see a chart based on the values from the last week that shows the median of all values of a given hour of the day. This makes it possible to identify times where your values are too high or too low. The gray shaded area covers the percentiles between 10 and 90%.

Like I said in the beginning the main reason for doing this was to see how easy it is to port an existing Swift app to JavaFX and give it more or less the same look and feel.

Because I originally created the app for Macos, I did not create a version that looks like a native Windows version yet but it will always look like on the images above.

Because Macos has some special controls (e.g. the Switch), I created a little helper library called AppleFX that you can also find on github and maven central. It does not cover all available controls but only the ones I needed for this app. But if I will find more time I will probably add more Macos controls to the libray.

Be aware that you also need my Toolbox and ToolboxFX libraries to use the AppleFX lib.

As always you can find the source code and also the binaries over at github.

There are no versions for Linux yet because this is again an app that runs in the background and sits in the system tray. Unfortunately this is not really supported on Ubuntu at the moment which is the reason why I did not created installers yet. But I will work on that and will probably add them in the future.

This does not mean that it does not work on Ubuntu, you can run it, create the installers and so on but it does not behave like it should because it should sit in the menu bar and that does not really work yet.

Here are also the links to the latest release:


One last thing...the app is localized and currently I support Germany and English but it would be awesome I could provide more localizations for other languages...so if you are willing to help...you are very welcome...just create a pull request on github or ping me.

And that's it for today...so enjoy your weekend and keep coding...


Wednesday, January 5, 2022

Holiday fun...

 Aloha and a happy new year...

I took the first week of 2022 off and because I love coding I was looking for something that I might add to one of my libraries.

It was not too hard to find something interesting and I decided to give it a try...the Radial Tidy Tree...

For those of you that have no idea what I am talking about...here is a little example from the web...


It is a tree structure that is visualized using a radial layout.

Looks like a fun thing to do but it really gave me some time to get it right. First of all (as nearly always) I had no real use case for it but just wanted to be able to create a chart like that.

So I decided to simply visualize a year. The root node has 4 child nodes, the 4 quarters and each quarter has 3 child nodes, the months of each quarter. Finally each month has it's specific number of days.

That's not real useful data but at least you can use it to create a Radial Tidy Tree. In principle it looks like an easy task but there are some things that are not that easy to solve. 

First of all you have to create the tree structure which is easy using my TreeNode class which I already used for the Sunburst chart. For this one I had to add more properties to it like x, y and angle. Thanks to the java.time package the creation of the tree was easy.

I won't show all the code here but if you like you can simply head over to github and check it out there...

The really tricky part was figuring out the angle step between the items on each level and I tried different approaches before I finally found a way that worked for me. 

Once I was able to place the items in the right place the next thing was to create all the bezier curves between the items to make it look good. And the last step was to put the text in the right position and rotate it correctly.

Well...long story short...here is the result...


And I really like the way the result looks :)

As I already mentioned, I do not have a real use case for it and therefor I cannot guarantee that the tree will work for all use cases. But I did a few other tests and it seems to be ok.

The RadialTidyTree can be found in the latest release of my charts library (17.1.2) which you can either get on github or on maven central.

As with all the charts in my charts library you can find a class that shows how to use it in the test package, for this one just look for RadialTidyTreeTest.java.

And that's it...so keep coding... :)






Friday, December 31, 2021

Harmony...finally

 Aloha,

When you create different libraries and components you find yourself writing the same code in different places over time. In principle that's ok, except you combine those libraries and components. In this case you suddenly have the same classes twice or even more often in your code base. When I started creating Medusa, TilesFX and Charts I did not really think about the possibility to combine those libraries in one project at some point in the future. The main reason for this is that I never plan to create those libraries but they simply grow from components to libraries over time. 

The thing that started me thinking about to re-use more code between those libraries was a project where I needed TilesFX and Charts in the same project. Both of these libraries came with a Country class with different properties and methods. Now in that project I needed both of them and I needed to write some ugly code to convert between them

That was the starting point of the Countries library which you can now find either on github and on maven central.

But then I saw that there are other classes that I more or less use in both libraries and I decided to put those shared classes in a separate project. Because there are projects that use JavaFX and others which don't, I decided to create two projects:

  • eu.hansolo.toolbox
  • eu.hansolo.toolboxfx

Toolbox:

This library contains the code from my Evt project, meaning to say an event system which is similar to the JavaFX events.

Then I also added the code from my Properties project to the Toolbox. The properties are very similar to the JavaFX properties incl. binding. And in the Toolbox they will use the Evt events for property changes.

There are now also tuples in the Toolbox which sometimes can come in handy. They are not that fancy and their getters and setters do look like getA(), get(B) and setA(), setB(). Not so nice but useful.

The last thing I've added is the code from my UnitConverter which contains all kinds of different units and a converter that can convert between them (in the same category e.g. Temperature).

Then there is a Helper method that contains all sorts of methods that I use here and there in my code e.g. clamp() etc.

ToolboxFX:

Then there is the ToolboxFX library which depends on JavaFX but that does not only contain JavaFX related stuff. Here you will find things like my ConicalGradient, FontMetrix, GradientLookup, the Fonts that I do use often and other stuff like Point, Bounds, CornerRadii, Dimension, Location Po, CatmullRom etc.

ToolboxFX depends on Toolbox so you need to add Toolbox too if you use ToolboxFX.

This is stuff that I use a lot in the Charts library but also in TilesFX and Medusa.

But that's not enough, I've also separated the HeatMap from Charts and Countries and put it in a separate project.

So what does that mean for you as a user of one of my libraries?

  • Update your dependencies
    • TilesFX depends on:
      • eu.hansolo:toolbox:17.0.6
      • eu.hansolo:toolboxfx:17.0.15
      • eu.hansolo.fx:heatmap:17.0.3
      • eu.hansolo.fx:countries:17.0.16
    • Medusa depends on:
      • eu.hansolo:toolbox:17.0.6
      • eu.hansolo:toolboxfx:17.0.15
    • Charts depends on:
      • eu.hansolo:toolbox:17.0.6
      • eu.hansolo:toolboxfx:17.0.15
      • eu.hansolo.fx:heatmap:17.0.3
      • eu.hansolo.fx:countries:17.0.16
  • Use the new event system
    • If you make use of things like TileEvent, you should change to TileEvt etc. The best way to see how it works is to take a look at the Demo classes within the library source code.

ATTENTION: The libraries are not backwards compatible due to the new event system !!!


The new versions of TilesFX, Charts and Medusa that will make use of the shared libraries will all start with version 17.1.0. There is still a lot of stuff to streamline (e.g. removing methods from the libraries Helper classes because they are already covered by Helper  in Toolbox and HelperFX in ToolboxFX but for that I need more time.

So here are all libraries that are new or have changed:

I will probably also use the Toolbox and ToolboxFX in future components and libraries.
So that was my holiday project and I'm really happy with it because now I could more easily use combinations of my libraries in projects.

I'm pretty sure there are still some things that do not work correctly, so please, if you stumble upon a problem do not hesitate to file an issue with some example code in the github repo.

I wish all of you a Happy New Year...and hopefully we will get rid of that Covid thing pretty soon...so stay healthy...and keep coding...


Thursday, December 23, 2021

DateRanger...

 Aloha,

Last week I needed some kind of a date picker which I can use to select ranges of dates. So I knew that I once created such a control when I was working for Canoo back in the days. But when I found it I saw that it was realized in JavaFX 2.0 that was based on JDK7 and made use of Skin and Behavior classes which changed in JDK8. 
Because I was not keen on rewriting that stuff I decided to simply create a new control...just for the fun of it :)
And because I used a lot of Canvas recently I made the decision to make use of CSS for this control and not use the Canvas node for it. Using CSS makes the whole thing more usable for standard applications because you can easily style the control to your needs where when using the Canvas node it needs more programming effort to get the same styleability.
So the first step was to figure out a control that I like to have some kind of template.
And I found this one...

It's not really fancy but I really like it's compact look which has all the info that I need. So I've created my version of it which looks as follows:


As you can see I more or less created a copy of the control. So the next step was to add the functionality to select a range of dates.
I've simply added a key listener and if you select a date by clicking somewhere with the mouse you can press the `SHIFT` key with the next click and it will create a range of dates for you.
The range then looks as follows:


Most of the nodes can be styled using CSS and you will find all the available styles in the `date-ranger.css` file.
The plain DateRanger comes without the month and year label and the buttons, so you can also use it for only showing the month. If you would like to use the version above, you can use the DateRangerControl which is also part of the code. This is in principle just a BorderPane that comes with the label and buttons on the top.

It's nothing really fancy but maybe it will be useful for one or the other.
The code is available on github and also on maven central.

Well I guess that's it for 2021...I wish all of you a merry christmas and a happy new year...oh and keep coding... :)

Tuesday, November 23, 2021

TilesFX 17.0.11

Aloha,

when working with dashboard using my TilesFX library I came across some missing things...first of all I needed a Tile that simply has a text in the center of it. Well nothing easier than that...I simply added a CenterTextTileSkin class that offers exactly this functionality. Here is a little screenshot that shows it in action...


As you can see I needed it to visualize the state of a server. If the server goes down the text will change from "ONLINE" to "OFFLINE" and the background color of the tile will change from green to red...very simple but very effective :)

To change the text you have to set the description of the tile.

Here is the code to create the tile above:

Tile serverTile = TileBuilder.create()
.skinType(SkinType.CENTER_TEXT)
.title("Server")
.text("Last check")
.backgroundColor(Dark.GREEN)
.description("ONLINE")
.build();

The other thing I stumbled upon was the fact that handling big numbers in a dashboard can really suck. When you have a dashboard with lots of tiles, there is sometimes simply not enough space to show those big numbers.

So the idea again is simple...just shorten the big numbers to a more readable format. For example 2350 can become 2.3k and 1230401 can become 1.2M. With this you can also show big numbers in a small tile.

The feature to use here is the property shortenNumbers in Tile. I've added it for some tiles where I thought it might come in handy but there might still be places where it is missing...so if you find a place where it could make sense, please file an issue over at github

You could also use the method Helper.shortenNumber(final long value) to shorten the numbers on your own before you set it somewhere. The method will return the formatted number as a String.

The result of using the shortenNumber property in Tile can be seen in the left Tile on image above.

Those new features can be found in the latest version of TilesFX which is 17.0.11 and which is available on github and also on Maven central.

That's it for today...so keep coding... :)


Tuesday, November 16, 2021

Panel Barchart

 Aloha,

I'm currently working on a dashboard where I needed a way to visualize data in a specific way. Let's say we would like to compare the load of 3 servers for each day of a week. And in addition we would like to be able to compare the current week with the last week.

There are probably different ways how you can visualize this and I decided to go with a so called Panel Barchart.

Here is an example of such a chart:


In principle this is some kind of a rotated stacked bar chart where the segments are separated from each other. So my version looks a bit different but you will see it's similar, here you go:


So in the upper chart you see the server load of our 3 servers for this week (of course the numbers don't make sense and are random but you get the idea).

You need to define categories (here it's the days of the week). Now you need to add a series of chart items for each category and each server.

In upper chart eh bars are colored by the categories (workdays = gray, weekend = red). This colors can be defined in the categories. 

If you switch this feature off (colorByCategory = false [default]) it will use the colors of the items in each series.

The chart will show the name of the series on the far left column and the sum of each series on the far right column.

On top of each category column it will show the sum per category (in this case per day). On the upper right corner it will show the overall sum of all values.

Well that's good but sometimes you would like to compare the current values with values from another point in time e.g. last week or last year.

To make this possible you can switch enableComparison to true and as you might already thought you need to define the data of the things you would like to compare.

To be able to compare data you need the exact same number of series in the listOfSeries and the comparisonListOfSeries. In addition the items in the each lisOfSeries need to use the same categories that you use in the comparisonListOfSeries.

When you fullfill these requirements the chart will now show name and the comparisonName in the upper left corner (yes you can set the colors for both of them separately). Now you will also see the items for both series of data and if you like you can also define the colors for the series sums and the category sums separately.

The chart is interactive in the way that you can click on each item and a little popup will show you some text and the value of the item. The text that will be shown in the popup could either be the name of the item or the description of the item. If you set both (name and description) it will take the description.

You can find the code for the example above in the PanelBarChartTest class in the test package of the current jdk17 branch of my charts library.

The latest release is available on github (at the moment it is 17.0.11) and also on maven central.

That's it for today...so keep coding... :)


Monday, September 20, 2021

Mission Timer X

Aloha,

Last week I've watched the live stream of SpaceX Mission "Inspiration4". When following the countdown on the screen I saw a nice control on the bottom of the screen...a mission timer. Well my very first Swing control I've created was the mission timer of the Apollo missions. As you can see I always was fascinated by those things... :)

So here is the screenshot I took:


The thing I really liked is the idea of having a circle where the upcoming events moving around. Very nice design. Of course there are details like colors, dots within the event circles etc. So I just had this screenshot and tried to re-create this control in JavaFX.

First thing to do (as always)...create a good vector drawing of the control.

So here you go:


My version also supports days which is the reason why I have the format in the way you see it on the image above. Another thing which I might improve is the changing colors of the items. In my version the color depends on the angle of the item on the circle but I think fading the item color might be better...we will see, maybe I will add this later on.

Also the font is not really the same as the one used by SpaceX. I've tried to find an appropriate one and decided to use the one in the image above.

The intersting thing about this control is that the width of the control is the only thing I used to do all the calculations. This was needed to make sure the aspect ratio of the control is always the same.

The MissionTimerX control also fires events of the type MissionTimerXEvent.PROCESSED. These events will be fired once an item reaches the center of the control which means it happens :)

Here is a little gif that shows the control in action...


And as always the code is available over at github.

Well...that's it...so keep coding...


Wednesday, July 7, 2021

SpinnerTileSkin

 Aloha,

I finally found some time to continue working on TilesFX. There was an issue in the TilesFX repo over at github that I would like to do for a long time but never really found the time.

So I now added a new skin called SpinnerTileSkin which is based on this issue/request. It does not look exactly like the requested one but I think it's close enough.

In principle the skin shows a numerical value that when changed will spin through the numbers from 0-9 as if they where on a wheel.

Because a screenshot won't really show the effect, here is a little video:


As you can see it is nothing really special but sometimes it might be exactly what you need.

To set it up you simple need the following code:

Tile tile = TileBuilder.create()
.skinType(SkinType.SPINNER)
.prefSize(300, 300)
.title("SpinnerTile")
.minValue(-50)
.maxValue(50)
.value(0)
.decimals(2)
.text("Animated number spinner")
.animated(false)
.build();
tile.currentValueProperty().addListener((o, ov, nv) -> {
if (nv.doubleValue() < 0) {
tile.setValueColor(Tile.RED);
} else {
tile.setValueColor(Tile.FOREGROUND);
}
});


Switching the color from white to red in case the number is negative is done by the listener attached to the currentValueProperty of the tile and is not the standard behavior.

Because I'm preparing my libraries for the upcoming JDK17 LTS release, this skin can only be found in the JDK16 branch of TilesFX. Not sure if I will backport it to the JDK11 master branch.

At the moment the JDK16 branch is not available on Maven Central but I will probably create a release in the coming days...so stay tuned...and keep coding :)


Friday, July 2, 2021

BubbleGridChart

 Aloha,

Last week I was playing around with some data and could not find the right chart to visualize it.

To give you an idea about the data, let's take the harvest of some fruits as an example. You will have ripe and unripe fruits, fruits that have been eaten by birds or caterpillars. Some of them might have been damaged by hail or did not get enough water, others might be rotten or mouldy. For each fruit you have those different numbers and you have the sum of each fruit and the sum of all fruits.

The best way to compare all those numbers would be a matrix style chart. So I've stumbled upon the so called Bubble Grid Chart. So here is an example that I've found on the web:

As you can see it shows the fruit data that I described above.

The value of each crosspoint e.g. 90 Apples that are ripe will be visualized by the size of the bubble. Sizing the bubbles is a bit tricky because you would like to avoid having a few big bubbles and a lot of tiny bubbles. So you need to make sure that the size of the bubbles has no linear relationship to it's value.

In addition the max size of a bubble is given by either the height of the y-category items or the width of the x-category items, depends on which is smaller.

I also would like to have a grid in the background to make it easier to find specific coordinates. So, long story short, here is my version of the BubbleGridChart:


As you can see I've decided to put the x-category items on the bottom and also added the ability to show not only the values on the bubbles but also on the rows and columns of the chart. I really was impressed on how much information you can get out of one chart. At a glance you can see that the number of all ripe fruits is 215 which is 43% of all fruits. Because in this example the number of each fruit was always 100 you cannot really compare by the x-category but this could be different.

You can see that 90 Apples have been ripe which is 18% of all fruits. This information will be shown in a little info that will popup when you click on the bubble. In addition I've added the ability to sort the chart in x- and y-direction by either their indices or their values.

To be able to sort the items by their index you have to define it upfront.

Let me show you how to set up the x- and y-category items for the chart above.

Y-Category Items:

ChartItem ripe = ChartItemBuilder.create().name("Ripe").index(0).fill(Color.BLUE).build();

ChartItem unripe = ChartItemBuilder.create().name("Unripe").index(0).fill(Color.RED).build();

X-Category Items:

ChartItem peaches = ChartItemBuilder.create().name("Peaches").index(0).fill(Color.ORANGERED).build();

ChartItem apples = ChartItemBuilder.create().name("Apples").index(1).fill(Color.LIMEGREEN).build();

BubbleChart Items:

BubbleGridChartItem peaches1 = BubbleGridChartBuilder.create().categoryXItem(peaches).categoryYItem(ripe).value(60).fill(Color.ORANGERED).build();

BubbleGridChartItem peaches2 = BubbleGridChartBuilder.create().categoryXItem(peaches).categoryYItem(unripe).value(5).fill(Color.ORANGERED).build();

BubbleGridChartItem apples1 = BubbleGridChartBuilder.create().categoryXItem(apples).categoryYItem(ripe).value(90).fill(Color.LIMEGREEN).build();

BubbleGridChartItem apples2 = BubbleGridChartBuilder.create().categoryXItem(apples).categoryYItem(ripe).value(0).fill(Color.LIMEGREEN).build();

The index that you define will later be used to sort the items, just make sure you don't have duplicate indices because I do not check that at the moment (I just needed something that works quickly). So you first create the x- and y-category items and from those you create the actual BubbleGridChart items that will be used to visualize the chart.

The BubbleGridChart itself can be create as follows:

BubbleGridChart bubbleGridChart = 
    BubbleGridChartBuidler.create()
                          .chartBackground(Color.web("#0e0e0e"))
                          .textColor(Color.WHITE)
                          .gridColor(Color.rgb(255, 255, 255, 0.1))
                          .showGrid(true)
                          .showValues(true)
                          .showPercentage(true)
                          .items(bubbleGridChartItems)
                          .sortXCategoryItemsByIndexAscending()
                          .sortYCategoryItemsByIndexDescending()
                          .useXCategoryFill()
                          .useGradientFill(false)
                          .gradient(new LinearGradient(0, 0, 1, 0, true
                                    CycleMethod.NO_CYCLE
                                    new Stop(0, Color.BLUE), 
                                    new Stop(1, Color.RED))
                          .build();

Sorting the categories via the build only works if you also provide the items, otherwise you have to call the sorting methods like sortXCategoryItemsByIndexAscending() after you have added the bubbleGridChartItems. For me the charts now works fine but you might have other requirements, so please do not hesitate to file issues/request over at github.

The BubbleGridChart can be find in the current jdk16 branch which is also available on maven central. The jdk16 branch is more a temporary branch that I use to test stuff before JDK17 will come out in September. Because this will be the next long term stable version I will create a jdk17 branch in the future which will become the new main branch then.

That's it for today, so enjoy the upcoming weekend and...keep coding... :)

Wednesday, June 16, 2021

Fun Selector

 Aloha,

Time flies, it's already mid of June so it's time for another little blogpost about a fun control I've created last week.

It's a selector between two states and the fun is the animation when switching between the two states.

Here is a little demonstration of the control...


So the light green ball defines the selected state. It's not really something special but I like the idea of using animations in a fun way. Always keep in mind that such effects are nice in tools that you use once or twice but you don't want to use these things in a business app every day :)

As always the code is available over at github.

And that's it for today...so keep coding...

Friday, April 9, 2021

Friday Fun LXIII - JDKMon

 Aloha,

I've took some days off this week and continued working on a little tool I wrote, JDKMon. Because I have a couple of JDK's on my machine and I usually don't use tools like sdkman or other installers but instead install the JDK's by hand I always wanted to have a tool that keeps track on the latest available versions and inform me about updates.

Well because I've created the Disco API for foojay I'm now able to use this api to create this tool. In principle the tool will try to find all JDK's installed in a folder (that you can define) and checks the Disco API for updates for each of the JDK's found. If an update was found it will tell you the latest available version for the distribution and give you the ability to download the different available versions.

Just to be clear, the tool won't scan your whole harddrive for installed JDK's but only the given folder with all it's subfolders. For me that works fine because I have all JDK's installed in the same folder. 

On MacOS this folder usually can be found at /Library/Java/JavaVirtualMachines where on Windows it can be C:\Program Files\Java and on Linux it might be /usr/lib/jvm. But like mentioned you can choose the folder JDKMon should look for JDK's.

JDKMon will scan for new updates every 3 hours and will show you a notification if there are updates available. The app makes use of FXTrayIcon which makes it possible to have the app running in the SystemTray (if available). On MacOS and Windows that works fine. On Linux it won't work so in this case it is simply an app that comes with a menu.

After the app started it will scan for JDK's and will then show a window with all JDK's found. On MacOS that window will like look like follows:


On the screenshot above you see the alphabetical list of JDK's found in the folder that is shown on top. The distributions where the tool found updates will have additional info like the arrow followed by the latest available version and some colored buttons. The buttons on the right will show the available archive types for each distribution. Once you click on one button it will ask you for a folder to download the package to and after you have selected one it will download the selected package to that folder.

So the tool won't install automatically the downloaded JDK...this is up to you. The tool will also adopt to the selected screen mode (dark/bright) on MacOS and Windows. I've also tried to make the main window and notifications look like the native windows. Here is a screenshot of the Windows version on a bright themed Windows 10 installation:


The supported distributions at the moment are:

- Adoptium (but there are no packages yet)

- AdoptOpenJDK HotSpot

- AdoptOpenJDK OpenJ9

- Corretto

- Dragonwell

- GraalVM CE

- Liberica

- Liberica Native

- Mandrel

- Microsoft Build of OpenJDK*

- OJDK Build

- Oracle JDK (no direct download)

- Oracle OpenJDK

- RedHat (no direct download)

- SAP Machine

- Trava

- Zulu

* The Microsoft Build of OpenJDK is currently not available on the public Disco API but only on my own server which is the reason why you can see it on the Windows screenshot. But it will come with the next deployment.


Like already mentioned this is just a tool that I needed for my own machine and I also used it to test the detection of dark/light mode etc.

Because the Disco API still is under development it might come to situation where you don't find the latest available JDK of a distribution directly but I'm working on that so that it will hopefully be up to date most of the times.

As always the source code is available over at github where you can also find the installers/jars in the releases section.

I'm using github actions to build and upload the artifacts with each build so that you can also find the latest available artifacts for MacOS and Windows in the actions section.

The tool is not finished yet because it needs a bit more love for Linux which will be the next task to do. At least it should look not like a MacOS app on Linux forever. So I will try to adjust it to maybe the Ubuntu UI.

That's it for today, so enjoy the upcoming weekend and...keep coding... 


Saturday, February 6, 2021

"Poor man's" dark mode detection

 Aloha,

I recently read a lot about how to detect the dark mode on MacOS or Windows from Java which is really interesting when you develop desktop applications in Java (yes they still exists).

And there are different ways in figuring this out, one of them is to use jSystemThemeDetector which not only detects the theme but also gives you the ability to listen to changes of the theme. This little library makes use of different other libraries like OSHI, JFA, Jetbrains Annotations and JNA. 

Because I also wrote a little tool that helps me to figure out the system color theme I thought I might share this with you.

So my approach is a bit different in the way that I simply make use of the already existing operating systems tools to get the information I need. If you don't need to listen to theme switches (which is usually the case because users do not change their theme all the time) but only need to know if the operating system is currently using the dark theme or the light theme then you might want to use my approach.

I simply call operating system routines on the command line using Java's Runtime.getRuntime().exec() method and parse the result.

Because I'm on MacOS I've also added the ability to get the current accent color that is used in MacOS which is useful if your application should be as close to the native MacOS apps as possible.

Because the JavaFX stage does not recognize the current MacOS theme means you have to draw the window frame on your own dependent on the current theme but that's fine. I might add another blogpost about the native looking MacOS windows frame I've created.

The thing that I like most about my little tool is that it is only one class that offers some utility methods and everything is plain Java without any dependencies. 

It works on MacOS and Windows 10 and for those of you that are interested in that tool, I've created a little gist.

At the moment this utility class is made for JavaFX but it should be easy to change the Color definitions from JavaFX to Java Swing if you need them :)

That's it for today, enjoy your weekend and keep coding... :)

Sunday, December 27, 2020

JavaFX monitor component

 Aloha,

There was always something I would like to do but never found the time to do it...a control that looks more or less like a heart rate monitor. Well it's not really about monitoring the heart rate but I like the heart rate signal, so it's more about the older tube based oscilloscopes. And the thing that makes me want to create such a control is the fade out of the beam when it moves across the screen. In the tube based devices this was coming from a metallized layer that was applied to the front of the glass tube. When electrons hit this layer, it absorbed a part of the kinetic energy of the electron and released it in the form of light (so called fluorescence). The layer on the glass is metallized so that the electric charge of the electrons can flow off.

Now when the electron beam was moving across the screen the electrons left behind a fluorescent line which formed the signal. Ok so that is the thing that I would like to create...not an oscilloscope but this fading effect when visualizing a signal.

So what we need is a line that fades out over time...sounds easy (and in the end the solution is easy) but getting the right idea on how to implement it took me some walks with the dog :)

The first idea is you simple draw a line and apply a horizontal linear gradient to the stroke that fades out to transparency. As long as the signal has a predominantly horizontal orientation this works really fine. But as soon as the signal also has significant vertical values we will run into a problem. Let me try to visualize this in a little graphic:


On the picture above you see two signals that have the same horizontal width but a different signal length. On top I've added two rectangles that shows the horizontal linear gradient that is used to fade out the signal. The effect of fading out should be related to the length of the line and not to the width of the signal. 

To achieve this behavior I need to fade out the signal along the line and not only in horizontal direction. So the fade out should follow the line. My idea was to use a queue with a fixed size, when filled up to the given size it should remove the head element when a new element was added to the end. With such a queue in place I could simply draw a line between each element of the queue and fade out these line segments when I'm iterating over them during the drawing process.

I simply created a FixedSizeQueue that is based on an ArrayBlockingQueue and added the "auto-remove" functionality in the add() method. So the queue looks like this:

public class FixedSizeQueue<E> extends ArrayBlockingQueue<E> {
private int size;

public FixedSizeQueue(final int size) {
super(size);
this.size = size;
}

@Override public boolean add(final E element) {
if (super.size() == this.size) { this.remove(); }
return super.add(element);
}

public E getElementAt(final int index) {
if (index < 0 || index > size() - 1) { throw new IllegalArgumentException("Index out of bounds."); }
return (E) toArray()[index];
}
}

With this queue in place I can now use it as some kind of a signal buffer. I was playing around with different factors to fade out the signal but ended up with a simply linear approach. So I calculate the opacity factor by simply dividing 1/noOfElements. And when iterating through the points in the queue I just multiply the current index with the opacity factor as you can see in the code snippet below:

final Point[] points = queue.toArray(new Point[0]);
double opacityFactor = 1.0 / points.length;
for (int i = 0; i < length - 1; i += 1) {
if (points[i].x < points[i + 1].x) {
lineCtx.setStroke(Color.color(red, green, blue, i * opacityFactor));
lineCtx.strokeLine(points[i].x, points[i].y, points[i + 1].x, points[i + 1].y);
}
}

You could because I draw the lines from left to right the opacity of the elements will in this case fade in the line segments by increasing their opacity from 0 - 1.

So the result looks good enough to me:


But this is only the effect of fading out a line along it's elements...what about the rest of the control?

For the monitor control I use three Canvas nodes and an ImageView that are placed inside a Region. So the whole control uses 5 nodes on the scene graph. 

It's good practice to think about which elements in your control needs to be drawn and when. This is sometimes a bit boring because with the given compute power of today's machines you won't really see a big difference between the optimized drawing and the let's call it brute force drawing. Optimized drawing only draws the elements that are needed where brute force drawing simply draws everything everytime. A good way of testing your controls is to run it on an older Raspberry Pi. This device is fast for it's size but slow compared to your desktop computer and on such a Raspberry Pi you will directly get a visual feedback on how efficient your code is. On an older Pi everything counts...means if you only draw the stuff that is important you will really see the effect on the Pi.

So as I mentioned I use three Canvas nodes

  • background (rect filled with background color and raster with text)
  • line (the line segments)
  • dot (the leading dot with the glow effect)
You might argue I should draw the dot in the same Canvas as the line but this only works for the mode where I fade out the signal line. The monitor control also has another mode where it always leaves the signal on the screen and only removes the part in front of the current position. This mode is common in todays heart rate monitors as you know it from the local hospital. For this mode I don't want to clear the background of the line Canvas on each draw and so I need to draw the dot on a separate Canvas.

Because I separated the background with the raster from the foreground with the line and the dot I only need to draw it when either a parameter has changed (e.g. the background color etc.) or when the control was resized. This is what I mean with optimized drawing, I could also draw the background all the time and put it in the same Canvas as the line but this is just not efficient.

In addition to the Canvas nodes I also use an ImageView to overlay the whole control with what I call a crystal overlay. The main idea is to make the whole UI look more realistic. The main thing here is noise...adding noise to a surface makes it look more natural because in the real world nearly no surface is perfect. For example if you take a look at a liquid crystal display (lcd), you will see some kind of structure on the background. This structure I try to imitate with a an image that contains semi-transparent noise. If you simply put such an image on top of your control it will look more realistic.

Here is an example screenshot of the monitor control with both variants:


As you can see the upper image has the crystal overlay effect switched off where the lower image has it switched on. In the monitor control you can decide if you would like to use it or not.

So in principle we simply stack all the different layers over each other and so get the final result.

I have also added some themes that contain common used color combinations for oscilloscopes but you can of course also set all the different colors separately.

There is a Demo class in the code that gives you the ability to play around with the parameters and if you would like to see it in action here is a little video:

As always the code/binary is available in the following places:

github

bintray

maven central

I guess that's it for today...so keep coding...

Friday, December 4, 2020

Worldmap connections...

Aloha,

Everytime I'm sitting on an airplane I'm taking out the magazine where it shows the flight connections of the airline on a worldmap. Somehow I really like that kind of visualization.

For those that don't know what I'm talking about, it's something like this:


When I've created the last update of my charts library I stumbled upon my worldmap and thought by myself...why not creating such a visualization by using the worldmap I already have.

You can choose this kind of visualization for different kind of data, e.g. simple connections, weighted connections to visualize a flow, incoming/outgoing connections and more.

Long story short...I've added the ability to cover some use cases I came up with. The first one is just a visualization of simple connections without showing any direction.



This really only shows the connections between points on the map. Nice but what if we would like to use colors and also show the direction? No problem, in this case it could look as follows...

Not bad but what if we would like to indicate the points with the highest volume of outgoing connections? No problem, here you go...


Hmm...so far so good but what if we would like to visualize a data flow from one point to another? Therefor the connection itself need to have a value which defines the thickness of the connection. Well...here it is...

Ok that's nice...so what about gradients...would it not be nice to have the connections change their color on their way from their startpoint to their endpoint? Yes it would be...so...


Allright...with this I covered all things I've implemented so far and as always I have no idea if someone has a use for that because I simply created it for the fun of it.

But if you can use it and you need more features...just let me know and I will try to make it happen.

If you are interested in how the above charts have been done you can find the code for it in the WorldmapConnectionsTest.java file in the test package.

In principle you can add MapPoint objects with a name, a color and latitude/longitude coordinates to show the points. The connections are of type MapConnection which takes 2 MapPoints, a value, a color or gradient and a tooltip text.

Attention: The connections are not clickable yet...so there is no tooltip and no user interaction implemented right now but it might follow.

The code can be found over at github at: https://github.com/HanSolo/charts

You will find the changes in the dev branch.

That's it for today, so enjoy your upcoming weekend...and keep coding...


Saturday, November 21, 2020

Arc charts...

Aloha,

I really need to post more often, it's already November...

When I was looking for some interesting charts that I might add to my JavaFX charts library I stumbled upon an arc diagram. It's a chart that is very specific and cannot be used to visualize all kinds of data but it's great to visualize interactions or flows between items. More info about this diagram can be found at wikipedia.

As always this chart was just created for the fun of it and not because I really need it, so I always try to make it as useful as possible but I don't know the special needs in the industry. So whenever you find something that can improve the handling of these charts, please let me know and I will try to make it better.

The hardest part is always to find some data to play with and this time I've found an interesting dataset about the interactions between the characters in the movie Star Wars Episode I.

So I gave it a try and here are some results by using my new arcchart:





As you can see there are different options one can enable/disable in this chart.

Each connection between two items can have a value which can be used to weight the connection (wider strokes mean bigger values => stronger connection). One could also weight the dots of each item by the sum of their connection values. If you click on an item it will highlight all it's connections.

In the last image you see that you can also make use of the full circle for connections. In this case you read the chart clockwise, meaning to say outgoing connections to the right will be in the upper part and outgoing connections to the left will be in the lower part.

The items are sorted by the sum of their connection values from left to right. As you can see in the image above, Anakin has the most interactions in Episode I, followed by Jar Jar and Qui Gon.

The most interesting part is that creating the whole new chart took not longer than 3-4h last night which again shows how productive you can be by using JavaFX...love it :)

In addition one could also create a cluster to group items. For this I've simply created another chart without any meaning just to show the clustering. So in this case the clusters are europe and asia. The items in each cluster are sorted by the sum of their connection values from left to right.


If you have clustered items all connections from the cluster will get the color of the cluster. A cluster will be created as follows:

Cluster europe = new Cluster("Europe", Color.BLUE, germany, france, italy, spain);

Where germany, france, italy and spain are PlotItems that can be created as follows:

PlotItem germany = new PlotItem("GERMANY", 1_250_000, Color.RED);

The connections between the items can be created as follows:

germany.addToOutgoing(india, 150_000);

If you would like to highlight a specific connection you can get it as follows:

arcChart.getConnection(germany, india).setFill(Color.GREEN);

Oh and the new ArcChart is part of the last release of my charts library which you can find at:

So that's it for today...enjoy the upcoming weekend and...keep coding...