JFreeChart is not updating ChartPanel, after reading data from serial port - refresh

I am trying to read some data from serial port and display the same using JFreeChart and ChartPanel.
After I click button 'Aquire' I get new set of data from serial port. For the first time when panel is getting displayed, the values read from serial port is displayed. After I click the button 'Aquire', the values are getting populated in variable 'dataset' but they are not getting refreshed on the graph. Where am I going wrong? please help. Thanks in advance.
import java.awt.BasicStroke;
import java.awt.Button;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Label;
import java.awt.TextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Rectangle2D;
import javax.swing.GroupLayout;
import javax.swing.JApplet;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartMouseEvent;
import org.jfree.chart.ChartMouseListener;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.annotations.XYLineAnnotation;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.event.ChartChangeEvent;
import org.jfree.chart.event.ChartChangeListener;
import org.jfree.chart.panel.CrosshairOverlay;
import org.jfree.chart.plot.Crosshair;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYItemRenderer;
import org.jfree.data.general.DatasetUtilities;
import org.jfree.data.xy.XYDataset;
import org.jfree.data.xy.XYSeriesCollection;
import org.jfree.ui.RectangleEdge;
public class DisplayPanel extends JApplet implements ActionListener
{
/**
*
*/
private static final long serialVersionUID = 1L;
SimplePortComm spc;
JFreeChart xylineChart;
ChartPanel panel;
XYSeriesCollection dataset;
public static String sLLD;
public static String sULD;
Label lblTitle;
Label lblMax, lblMin, lblULD, lblLLD;
TextField txtMax, txtMin, txtULD, txtLLD;
Label lblInrangeCount;
TextField txtInrangeCount;
Button bAquire, bSave, bLoad, bQuit;
int i, nInRangeChannels;
private Crosshair xCrosshair;
private Crosshair yCrosshair;
double dULD,dLLD;
public DisplayPanel() {
dataset = new XYSeriesCollection();
spc = new SimplePortComm();
lblTitle = new Label("Data Aquisition System for MCA");
lblTitle.setPreferredSize(new Dimension(600, 20));
lblMax = new Label("Max:");
lblMin = new Label("Min:");
lblULD = new Label("ULD:");
lblLLD = new Label("LLD:");
lblInrangeCount = new Label("Count in Range:");
lblInrangeCount.setPreferredSize(new Dimension(60, 20));
txtMax = new TextField(10);
txtMax.setEditable(false);
txtMin = new TextField(10);
txtMin.setEditable(false);
txtULD = new TextField(10);
txtULD.setText("240.0");
txtLLD = new TextField(10);
txtLLD.setText("20.0");
txtInrangeCount = new TextField(10);
txtInrangeCount.setPreferredSize(new Dimension(60, 20));
txtInrangeCount.setEditable(false);
dULD = Double.parseDouble(txtULD.getText());
dLLD = Double.parseDouble(txtLLD.getText());
drawChart();
bAquire = new Button("Aquire");
bAquire.setPreferredSize(new Dimension(20, 60));
bAquire.addActionListener(
new ActionListener() {
#Override
public void actionPerformed(ActionEvent aquire) {
//panel.setRefreshBuffer(true);
System.setSecurityManager(null);// with out this statement, set_ports() throws null pointer exception
spc.set_ports(dULD, dLLD);
dataset = SimplePortComm.dataset;
panel.repaint();
panel.updateUI();
System.out.print("Button: Aquire");
}
}
);
bSave = new Button("Save");
bSave.setPreferredSize(null);
bSave.addActionListener(
new ActionListener() {
#Override
public void actionPerformed(ActionEvent save) {
System.out.print("Button: Save");
}
}
);
bLoad = new Button("Load");
bLoad.setPreferredSize(null);
bLoad.addActionListener(
new ActionListener() {
#Override
public void actionPerformed(ActionEvent load) {
System.out.print("Button: Load");
sLLD = txtLLD.getText();
sULD = txtULD.getText();
txtInrangeCount.setText(Integer.toString(spc.getInRangeChannels()));
}
}
);
bQuit = new Button("Quit");
bQuit.setPreferredSize(new Dimension(10, 10));
bQuit.addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent quit) {
System.out.print("Button: Quit");
System.exit(0);
}
});
GroupLayout layout = new GroupLayout(getContentPane());
layout.setAutoCreateGaps(true);
layout.setAutoCreateContainerGaps(true);
layout.setHorizontalGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
.addComponent(lblTitle)
.addComponent(panel)
.addGroup(layout.createSequentialGroup()
.addComponent(bAquire)
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
.addComponent(lblMax)
.addComponent(lblMin))
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
.addComponent(txtMax)
.addComponent(txtMin))
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
.addComponent(lblULD)
.addComponent(lblLLD))
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
.addComponent(txtULD)
.addComponent(txtLLD)))
.addGroup(layout.createSequentialGroup()
.addComponent(lblInrangeCount)
.addComponent(txtInrangeCount))
.addGroup(layout.createSequentialGroup()
.addComponent(bSave)
.addComponent(bLoad)
.addComponent(bQuit))
);
layout.setVerticalGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
.addGroup(layout.createSequentialGroup()
.addComponent(lblTitle)))
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
.addGroup(layout.createSequentialGroup()
.addComponent(panel)))
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
.addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
.addComponent(bAquire))
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
.addComponent(lblMax)
.addComponent(txtMax)
.addComponent(lblULD)
.addComponent(txtULD))
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
.addComponent(lblMin)
.addComponent(txtMin)
.addComponent(lblLLD)
.addComponent(txtLLD))))
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
.addComponent(lblInrangeCount)
.addComponent(txtInrangeCount))
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
.addComponent(bSave)
.addComponent(bLoad)
.addComponent(bQuit))
);
getContentPane().setLayout(layout);
getContentPane().setEnabled(true);
getContentPane().setSize(1500, 1500);
}
public void drawChart()
{
System.setSecurityManager(null);// with out this statement, set_ports() throws null pointer exception
spc.set_ports(dULD, dLLD);
dataset = SimplePortComm.dataset;
xylineChart = ChartFactory.createXYLineChart(
"Data Aquired" ,
"Time" ,
"Amplitude" ,
dataset ,
PlotOrientation.VERTICAL ,
true , true , false);
panel = new ChartPanel(xylineChart, 650, 450, 180, 180, 800, 600, false, true, false, false, false, false);
//panel.setRefreshBuffer(true);
CrosshairOverlay crosshairOverlay = new CrosshairOverlay();
this.xCrosshair = new Crosshair(Double.NaN, Color.BLACK,
new BasicStroke(0f));
this.xCrosshair.setLabelVisible(true);
this.yCrosshair = new Crosshair(Double.NaN, Color.BLACK,
new BasicStroke(0f));
this.yCrosshair.setLabelVisible(true);
crosshairOverlay.addDomainCrosshair(xCrosshair);
crosshairOverlay.addRangeCrosshair(yCrosshair);
panel.addOverlay(crosshairOverlay);
panel.addChartMouseListener(new ChartMouseListener() {
#Override
public void chartMouseMoved(ChartMouseEvent event) {
Rectangle2D dataArea = panel.getScreenDataArea();
JFreeChart chart = event.getChart();
XYPlot plot = (XYPlot) chart.getPlot();
ValueAxis xAxis = plot.getDomainAxis();
double x = xAxis.java2DToValue(event.getTrigger().getX(), dataArea,
RectangleEdge.BOTTOM);
// make the crosshairs disappear if the mouse is out of range
if (!xAxis.getRange().contains(x)) {
x = Double.NaN;
}
double y = DatasetUtilities.findYValue(plot.getDataset(), 0, x);
xCrosshair.setValue(x);
yCrosshair.setValue(y);
}
#Override
public void chartMouseClicked(ChartMouseEvent event) {
Rectangle2D dataArea = panel.getScreenDataArea();
System.out.println("Mouse clicked on the graph");
XYPlot plot = (XYPlot) xylineChart.getPlot();
ValueAxis xAxis = plot.getDomainAxis();
double x = xAxis.java2DToValue(event.getTrigger().getX(), dataArea,
RectangleEdge.BOTTOM);
// make the crosshairs disappear if the mouse is out of range
if (!xAxis.getRange().contains(x)) {
x = Double.NaN;
}
double y = DatasetUtilities.findYValue(plot.getDataset(), 0, x);
System.out.println("Graph click: " + x + ":" + y);
xCrosshair.setValue(x);
yCrosshair.setValue(y);
XYItemRenderer renderer = plot.getRenderer();
renderer.removeAnnotations();
XYLineAnnotation mark = new XYLineAnnotation(x,0,x,250, new BasicStroke(), Color.BLACK);
renderer.addAnnotation(mark);
XYLineAnnotation low = new XYLineAnnotation(0.0f,dULD,400f,dULD, new BasicStroke(), Color.BLUE);
renderer.addAnnotation(low);
low.setToolTipText("low");
XYLineAnnotation high = new XYLineAnnotation(0.0f,dLLD,400f,dLLD, new BasicStroke(), Color.MAGENTA);
renderer.addAnnotation(high);
high.setToolTipText("high");
plot.setRenderer(renderer);
}
});
}
public void init()
{
setBackground(Color.WHITE);
System.setSecurityManager(null);
spc = new SimplePortComm();
}
public static void main(String args[])
{
DisplayPanel ds = new DisplayPanel();
}
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
}
}

I don't know how SimplePortComm works, but your ActionListener is replacing the dataset that was used to create the chart. It works the first time because the original dataset is the one that xylineChart is listening to; the chart doesn't know about the new dataset. You can tell the chart about the change by using plot.setDataset(dataset);
Your ActionListener is also blocking the event dispatch thread, so you might want to use a SwingWorker like they show here.

Related

Handling Infinity in JavaFX numerical TextField

I have a JavaFX TextField specialized to accept numbers, including scientific notation. It does pretty much everything I want. But, because it accepts scientific notation, it is easy for a user to enter a number beyond the range that can be represented by a double. When they do, the TextField displays "Infinity" (or "-Infinity"). When that happens the field can no longer be edited to correct the problem. The contents cannot be selected and deleted either. Tapping the "Escape" key does not return to the previous contents.
Here is an SSCCE, based closely on the answer by James_D to this question a few years ago.
import java.text.DecimalFormatSymbols;
import java.util.function.UnaryOperator;
import java.util.regex.Pattern;
import javafx.application.Application;
import javafx.beans.value.ObservableValue;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.control.TextFormatter;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
import javafx.util.StringConverter;
public class NumericTextFieldDemo extends Application {
char sep = new DecimalFormatSymbols().getDecimalSeparator();
String negStarter = new StringBuilder("-").append(sep).toString();
String posStarter = new StringBuilder("+").append(sep).toString();
String patternStr = new StringBuilder()
.append("[+|-]?(([1-9][0-9]*)|0)?(\\")
.append(sep)
.append("[0-9]*)?(([e|E][+|-]?[0-9]*)?)")
.toString();
Pattern validEditingState = Pattern.compile(patternStr);
class NumericTextField extends TextField {
UnaryOperator<TextFormatter.Change> filter = c -> {
String text = c.getControlNewText();
if (validEditingState.matcher(text).matches()) {
return c;
} else {
return null;
}
};
StringConverter<Double> converter = new StringConverter<Double>() {
#Override
public Double fromString(String s) {
if (s.isEmpty() || "-".equals(s) || "+".equals(s)
|| negStarter.equals(s) || posStarter.equals(s)) {
return 0.0;
} else {
return Double.valueOf(s);
}
}
#Override
public String toString(Double d) {
return d.toString();
}
};
NumericTextField(double initValue) {
TextFormatter<Double> textFormatter = new TextFormatter<>(converter, initValue, filter);
textFormatter.valueProperty().addListener((ObservableValue<? extends Double> obs, Double oldValue, Double newValue) -> {
System.out.println("User entered value: " + newValue);
});
setTextFormatter(textFormatter);
}
NumericTextField() {
this(0.0);
}
}
#Override
public void start(Stage primaryStage) throws Exception {
NumericTextField ntf = new NumericTextField();
// Setting the font seems to be required on macOS.
ntf.setFont(new Font("Arial", 14));
VBox root = new VBox(5, ntf);
root.setAlignment(Pos.CENTER);
primaryStage.setScene(new Scene(root, 250, 150));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Is there any way to catch the infinities and leave the TextField in a usable state? Is there some change that could be made to the class to prevent entering such numbers in the first place?
Just use the built-in string converter for doubles:
TextFormatter<Double> tf = new TextFormatter<>(new DoubleStringConverter());
TextField ntf = new TextField();
ntf.setTextFormatter(tf);

Web Service doesn’t work when I try to update the android widget if the app is killed

We are developing android widget for Xamarin.Forms application. The widget updates and gets data from the Web Service when the app is in Background, but stops working when the app is killed/closed. I have followed this article for developing this widget -
Xamarin: Android Widget with timer, stops when app killed
I want to Update the widget when the user clicks on Refresh button. If I add hardcoded data for textboxes and click Refresh it updates the time but doesn’t work if I assign web service result data for the textboxes. I have added internet permission in AndroidManifest.xml. Is there a way I can get the data from web service even when the app is closed? Or Probably I am missing some permission?
AppWidget.cs -
public static class WidgetConsts
{
public const string DebugTag = "com.myapp.WIDGET";
public const string ActionWakeup = "com.myapp.WIDGET_WAKEUP";
public const string ActionWidgetUpdate = "android.appwidget.action.APPWIDGET_UPDATE";
public const string ActionWidgetDisabled = "android.appwidget.action.APPWIDGET_DISABLED";
}
[BroadcastReceiver]
[IntentFilter(new string[] { WidgetConsts.ActionWakeup })]
public class AlarmReceiver : BroadcastReceiver
{
public override void OnReceive(Context context, Intent intent)
{
if (intent.Action.Equals(WidgetConsts.ActionWakeup))
{
Log.Debug(WidgetConsts.DebugTag, "Wakeup alarm called");
if (AppWidget.widgetTimer == null)
{
Log.Debug(WidgetConsts.DebugTag, "Widget updating does not run, enforcing update...");
AppWidget.UpdateAppWidget(context);
}
else
{
Log.Debug(WidgetConsts.DebugTag, "Widget updating runs, no action needed");
}
}
}
}
[BroadcastReceiver]
[IntentFilter(new string[] { WidgetConsts.ActionWidgetUpdate})]
[MetaData("android.appwidget.provider", Resource = "#xml/appwidget_provider")]
public class AppWidget : AppWidgetProvider
{
public static System.Timers.Timer widgetTimer = null;
public override void OnUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds)
{
RemoteViews views = BuildRemoteViews(context, appWidgetIds);
(AppWidgetManager.GetInstance(Android.App.Application.Context)).UpdateAppWidget(new ComponentName(Android.App.Application.Context, Java.Lang.Class.FromType(typeof(AppWidget))), views);
// appWidgetManager.UpdateAppWidget(appWidgetIds[0], views);
// set timer for updating the widget views each 5 sec
if (widgetTimer == null)
{
widgetTimer = new System.Timers.Timer();
widgetTimer.Interval = 5000;
widgetTimer.Elapsed += OnTimedEvent;
}
widgetTimer.Enabled = true;
// set alarm to wake up the app when killed, each 60 sec
// needs a fresh BroadcastReceiver because AppWidgetProvider.OnReceive is
// not virtual and overriden method in this class would not be called
AlarmManager am = (AlarmManager)context.GetSystemService(Context.AlarmService);
Intent ai = new Intent(context, typeof(AlarmReceiver));
ai.SetAction(WidgetConsts.ActionWakeup);
PendingIntent pi = PendingIntent.GetBroadcast(context, 0, ai, PendingIntentFlags.UpdateCurrent);
am.SetRepeating(AlarmType.ElapsedRealtime, 100, 1000 * 60, pi);
}
public override void OnEnabled(Context context)
{
AlarmManager am = (AlarmManager)context.GetSystemService(Context.AlarmService);
Intent ai = new Intent(context, typeof(AlarmReceiver));
ai.SetAction(WidgetConsts.ActionWakeup);
PendingIntent pi = PendingIntent.GetBroadcast(context, 0, ai, PendingIntentFlags.UpdateCurrent);
am.SetRepeating(AlarmType.ElapsedRealtime, 100, 1000 * 60, pi);
base.OnEnabled(context);
}
public override void OnDisabled(Context context)
{
Log.Debug(WidgetConsts.DebugTag, "Disabling the widget");
if (widgetTimer != null)
{
Log.Debug(WidgetConsts.DebugTag, "Stopping timer");
widgetTimer.Enabled = false;
}
else
Log.Debug(WidgetConsts.DebugTag, "Timer is null");
base.OnDisabled(context);
}
private void OnTimedEvent(object sender, ElapsedEventArgs e)
{
Log.Debug(WidgetConsts.DebugTag, "Updating status...");
new Handler(Looper.MainLooper).Post(() =>
{
//Run my code to periodically update the widget
RemoteViews views = new RemoteViews(Android.App.Application.Context.PackageName, Resource.Layout.SnapVertWidget);
AppWidgetManager manager = AppWidgetManager.GetInstance(Android.App.Application.Context);
ComponentName thisWidget = new ComponentName(Android.App.Application.Context, Java.Lang.Class.FromType(typeof(AppWidget)));
int[] appWidgetIds = manager.GetAppWidgetIds(thisWidget);
(AppWidgetManager.GetInstance(Android.App.Application.Context)).UpdateAppWidget(new ComponentName(Android.App.Application.Context, Java.Lang.Class.FromType(typeof(AppWidget))), views);
// manager.UpdateAppWidget(appWidgetIds[0], views);
});
}
static public void UpdateAppWidget(Context context)
{
Intent intent = new Intent(context, typeof(AppWidget));
intent.SetAction(WidgetConsts.ActionWidgetUpdate);
int[] ids = AppWidgetManager.GetInstance(context).GetAppWidgetIds(new ComponentName(context, Java.Lang.Class.FromType(typeof(AppWidget))));
intent.PutExtra(AppWidgetManager.ExtraAppwidgetIds, ids);
context.SendBroadcast(intent);
}
public RemoteViews BuildRemoteViews(Context context, int[] appWidgetIds)
{
xxx.Droid.Services.MyWidget myWidget = new xxx.Droid.Services.MyWidget();
var entry = myWidget.GetData();
// Build an update that holds the updated widget contents
var updateViews = new RemoteViews(context.PackageName, Resource.Layout.SnapVertWidget);
updateViews.SetTextViewText(Resource.Id.txtvwUpdate, Convert.ToString(DateTime.Now));
updateViews.SetTextViewText(Resource.Id.txtvwCityName, entry.Result.CityName);
updateViews.SetTextViewText(Resource.Id.txtvwTemp, entry.Result.TempValue);
//SetTextViewText(widgetView);
RegisterClicks(context, appWidgetIds, updateViews);
return updateViews;
}
private void RegisterClicks(Context context, int[] appWidgetIds, RemoteViews widgetView)
{
Intent intentUpdate = new Intent(context, typeof(AppWidget));
intentUpdate.SetAction(AppWidgetManager.ActionAppwidgetUpdate);
//Update the current widget instance only, by creating an array that contains the widget’s unique ID//
int[] idArray = new int[] { appWidgetIds[0] };
intentUpdate.PutExtra(AppWidgetManager.ExtraAppwidgetIds, idArray);
PendingIntent pendingUpdate = PendingIntent.GetBroadcast(
context, appWidgetIds[0], intentUpdate,
PendingIntentFlags.UpdateCurrent);
widgetView.SetOnClickPendingIntent(Resource.Id.btnRefresh, pendingUpdate);
Intent launchAppIntent = new Intent(context, typeof(MainActivity));
PendingIntent launchAppPendingIntent = PendingIntent.GetActivity(context, 0, launchAppIntent, PendingIntentFlags.UpdateCurrent);
widgetView.SetOnClickPendingIntent(Resource.Id.pnlWeather, launchAppPendingIntent);
}
}

Java FX: Getting and editing an object from Observable List

I know about the method list.getSelectionModel().getSelectedItem(); and its index version, but heres my problem:
I have setup a List in my GUI which holds Objects of a class Person. In my GUI theres also Text Fields with the attributes of that class (Name,Street,Age etc).
What I did so far is implement a method clickList() which will fill the attribute fields with the data from the selected object in the listview. So the user can edit them from here and press another button which should then update those attributes.
I also setup so you can create a new object from the text form by doing this inside my "OK" Button inside my Controller:
ObservableList<Person> items = FXCollections.observableArrayList();
items.add(new Person(tf_vn.getText(), tf_nn.getText(),tf_strasse.getText(), tf_plz.getText(), tf_ort.getText(),genderChoice, sliderAge.getValue());
list.setItems(items);
However what im struggling with is the editing of the already existing Person. Can someone give me some pointers? I know I can get the selected object index but how do I work with it? Basicly I just need to find a way to do something like selectedObject.setAge(),selectedObject.setName() etc
I looked through all the getSelectionModel() methods but didnt find a solution, im sure there is a easy one....
Thanks in advance !
This way you can reach selectedObject's methods
ListView<Person> l = new ListView<>();
...
l.getSelectionModel().getSelectedItem().setFirstName("new name");
l.refresh();
a full example may look like;
package so;
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
public class TableViewSample extends Application {
private TableView<Person> table = new TableView<Person>();
private final ObservableList<Person> data = FXCollections.observableArrayList(
new Person("Jacob", "Smith", "jacob.smith#example.com"),
new Person("Isabella", "Johnson", "isabella.johnson#example.com"),
new Person("Ethan", "Williams", "ethan.williams#example.com"),
new Person("Emma", "Jones", "emma.jones#example.com"),
new Person("Michael", "Brown", "michael.brown#example.com"));
final HBox hb = new HBox();
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) {
Scene scene = new Scene(new Group());
stage.setTitle("Table View Sample");
stage.setWidth(450);
stage.setHeight(550);
final Label label = new Label("Address Book");
label.setFont(new Font("Arial", 20));
// table.setEditable(true);
TableColumn<Person, String> firstNameCol = new TableColumn<>("First Name");
firstNameCol.setMinWidth(100);
firstNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("firstName"));
TableColumn<Person, String> lastNameCol = new TableColumn<>("Last Name");
lastNameCol.setMinWidth(100);
lastNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("lastName"));
TableColumn<Person, String> emailCol = new TableColumn<>("Email");
emailCol.setMinWidth(200);
emailCol.setCellValueFactory(new PropertyValueFactory<Person, String>("email"));
table.setItems(data);
table.getColumns().addAll(firstNameCol, lastNameCol, emailCol);
final TextField addFirstName = new TextField();
addFirstName.setPromptText("First Name");
addFirstName.setMaxWidth(firstNameCol.getPrefWidth());
final TextField addLastName = new TextField();
addLastName.setMaxWidth(lastNameCol.getPrefWidth());
addLastName.setPromptText("Last Name");
final TextField addEmail = new TextField();
addEmail.setMaxWidth(emailCol.getPrefWidth());
addEmail.setPromptText("Email");
final Button addButton = new Button("Add");
addButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent e) {
if (table.getSelectionModel().getSelectedItem() != null) {
table.getSelectionModel().getSelectedItem().setFirstName(addFirstName.getText());
table.getSelectionModel().getSelectedItem().setLastName(addLastName.getText());
table.getSelectionModel().getSelectedItem().setEmail(addEmail.getText());
table.refresh();
} else {
data.add(new Person(addFirstName.getText(), addLastName.getText(), addEmail.getText()));
addFirstName.clear();
addLastName.clear();
addEmail.clear();
}
}
});
table.getSelectionModel().selectedItemProperty().addListener((e, o, n) -> {
// addFirstName.clear();
// addLastName.clear();
// addEmail.clear();
if (n != null) {
addFirstName.setText(n.getFirstName());
addLastName.setText(n.getLastName());
addEmail.setText(n.getEmail());
addButton.setText("Edit");
}
else {
addButton.setText("Add");
}
});
hb.getChildren().addAll(addFirstName, addLastName, addEmail, addButton);
hb.setSpacing(3);
final VBox vbox = new VBox();
vbox.setSpacing(5);
vbox.setPadding(new Insets(10, 0, 0, 10));
vbox.getChildren().addAll(label, table, hb);
((Group) scene.getRoot()).getChildren().addAll(vbox);
stage.setScene(scene);
stage.show();
}
public static class Person {
private final SimpleStringProperty firstName;
private final SimpleStringProperty lastName;
private final SimpleStringProperty email;
private Person(String fName, String lName, String email) {
this.firstName = new SimpleStringProperty(fName);
this.lastName = new SimpleStringProperty(lName);
this.email = new SimpleStringProperty(email);
}
public String getFirstName() {
return firstName.get();
}
public void setFirstName(String fName) {
firstName.set(fName);
}
public String getLastName() {
return lastName.get();
}
public void setLastName(String fName) {
lastName.set(fName);
}
public String getEmail() {
return email.get();
}
public void setEmail(String fName) {
email.set(fName);
}
}
}

Continuously update TextArea

I'm writing a program that creates a process using youtube-dl. That process has two InputStreams (inputStream and errorStream) of which I want to reroute each into a text area.
I've been trying to get the TextAreas to update without locking the JavaFX thread. It's working but I feel like it's terribly inefficient as it creates a large number of Task objects that only append a line. I've recreated the code I've been using below, using List<String> instead of BufferedReader to simplify the problem a bit.
When I press the button it will create two threads, one for each list, with an UpdateTask. The UpdateTask then creates a WriteTask gives it to Platform.runLater() which places it on the JavaFX thread again.
Surely there must be a better way to do this?
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import javafx.application.Platform;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;
public class ConcurrentTest extends VBox{
TextArea output;
TextArea error;
Button start;
TextField writable;
public ConcurrentTest(){
// Init components
output = new TextArea();
output.setEditable(false);
error = new TextArea();
error.setEditable(false);
// Init button
start = new Button("Print stuff");
start.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent arg0) {
List<String> outputLines = Arrays.asList("A", "B", "C", "D");
List<String> errorLines = Arrays.asList("A", "B", "C", "D");;
Thread outputThread = new Thread(new UpdateTask<String>(output, outputLines));
outputThread.setDaemon(true);
outputThread.start();
Thread errorThread = new Thread(new UpdateTask<String>(error, errorLines));
errorThread.setDaemon(true);
errorThread.start();
}
});
writable = new TextField();
writable.setPromptText("Write while some text areas are getting updated.");
// Add components
this.getChildren().addAll(output, error, start, writable);
}
// UPDATE TASK CLASS
public class UpdateTask<V> extends Task<V>{
TextArea target;
Iterator<String> it;
public UpdateTask(TextArea target, List<String> lines){
this.target = target;
it = lines.iterator();
}
#Override
protected V call() throws Exception {
while(it.hasNext()){
Thread.sleep(1500); // Time to type something to test
Platform.runLater(new WriteTask<String>(target, it.next()));
}
return null;
}
}
// WRITE TASK CLASS
public class WriteTask<V> extends Task<V>{
TextArea target;
String line;
public WriteTask(TextArea target, String line) {
this.target = target;
this.line = line;
}
#Override
protected V call() throws Exception {
target.appendText(line + "\n");
return null;
}
}
}
For the entire program, the launcher with main:
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Parent;
import javafx.scene.Scene;
public class ConcurrentTestLauncher extends Application {
#Override
public void start(Stage primaryStage) {
try {
Parent root = new ConcurrentTest();
Scene scene = new Scene(root);
primaryStage.setTitle("Concurrent FX Test");
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}

display a list of blob

i have some blobs in my database ,actually i can retrie just one and display it in ImageView and i would like to retire several and give the choice to the user like if he just click at the same button
the image of ImageView change
here is my main class:
there is my mainclass:
package com.example.autretest;
import java.util.List;
import android.os.Bundle;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.Toast;
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ImageButton imagebutton=(ImageButton)findViewById(R.id.imageButton1);
addListenerOnButton();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
public void addListenerOnButton() {
final ImageView imageview=(ImageView)findViewById(R.id.imageView1);
final Button button = (Button) findViewById(R.id.button1);
button.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0) {
Bitmap bm = null;
List<Bitmap> listbitmap=null;
SQLiteAdapter mySQLiteAdapter =new SQLiteAdapter(getApplicationContext());
mySQLiteAdapter.openToRead();
listbitmap=mySQLiteAdapter.queueAllphoto();
int location=2;
while (location<listbitmap.size()){
imageview.setImageBitmap(listbitmap.get(location));
}
location++;
mySQLiteAdapter.close();
//Bitmap bmp = BitmapFactory.decodeByteArray(content,0,content.length);
//image = new BitmapDrawable(BitmapFactory.decodeByteArray(content, 0, content.length));
Toast.makeText(MainActivity.this,
"ImageButton is clicked!", Toast.LENGTH_SHORT).show();
}
});
}
}
then you've got the otherclass SqliteAdapter:
package com.example.autretest;
import java.sql.Blob;
import java.util.List;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
public class SQLiteAdapter {
public static final String MYDATABASE_NAME = "mydatabase";
public static final String MYDATABASE_TABLE_APP = "ma_table";
public static final String MYDATABASE_TABLE_photo = "pictures";
public static final String MYDATABASE_TABLE_plan = "plan";
public static final int MYDATABASE_VERSION = 1;
public static final String KEY_CONTENT = "Content";
public static final String KEY_CONTENT_ID = "Content_PK";
public static final String KEY_CONTENT_ID_photo = "Content_PK_photo";
public static final String KEY_CONTENT_ID_plan = "Content_PK_plan";
public static final String KEY_CONTENT_photo = "Content_photo";
public static final String KEY_CONTENT_plan = "Content_plan";
//create table MY_DATABASE (ID integer primary key, Content text not null);
private static final String SCRIPT_CREATE_DATABASE =
"create table " + MYDATABASE_TABLE_APP + " ("
+ KEY_CONTENT + " text not null)"+
"create table " + MYDATABASE_TABLE_photo + " ("+ KEY_CONTENT_photo + " blob not null)"+" " +
"("+ KEY_CONTENT_ID_photo + " INTEGER not null);" +" ("+ KEY_CONTENT_ID + " INTEGER not null) "+
"create table " + MYDATABASE_TABLE_plan + " ("+ KEY_CONTENT_plan + " INTEGER not null)";
private SQLiteHelper sqLiteHelper;
private SQLiteDatabase sqLiteDatabase;
private Context context;
int s=0;
byte[]app_image=null;
public SQLiteAdapter(Context c){
context = c;
}
#SuppressWarnings("null")
public void DropDB() {
SQLiteDatabase db = null;
//On peut faire ce qu'on veut ici moi j'ai décidé de supprimer la table et de la recréer
//comme ça lorsque je change la version les id repartent de 0
db.execSQL("DROP DATABASE " + MYDATABASE_NAME + ";");
}
public SQLiteAdapter openToRead() throws android.database.SQLException {
sqLiteHelper = new SQLiteHelper(context, MYDATABASE_NAME, null, MYDATABASE_VERSION);
sqLiteDatabase = sqLiteHelper.getReadableDatabase();
return this;
}
public Bitmap getIcone(){
String[] columns = new String[]{KEY_CONTENT_photo};
sqLiteHelper = new SQLiteHelper(context, MYDATABASE_NAME, null, MYDATABASE_VERSION);
sqLiteDatabase = sqLiteHelper.getReadableDatabase();
byte[]image_bytes=null;
Cursor rslt=null;
rslt=sqLiteDatabase.query(MYDATABASE_TABLE_photo,columns, null, null, null, null, null);
if(rslt.getCount()!=0){
rslt.moveToFirst();
image_bytes=rslt.getBlob(rslt.getColumnIndex(KEY_CONTENT_photo));
Bitmap bmp=BitmapFactory.decodeByteArray(image_bytes, 0, image_bytes.length);
return bmp;
}
else {
return null;
}
}
public String queueAll(){
String[] columns = new String[]{KEY_CONTENT};
Cursor cursor = sqLiteDatabase.query(MYDATABASE_TABLE_APP, columns,
null, null, null, null, null);
String result = "";
int index_CONTENT = cursor.getColumnIndex(KEY_CONTENT);
for(cursor.moveToFirst(); !(cursor.isAfterLast()); cursor.moveToNext()){
result = result + cursor.getString(index_CONTENT) + "\n";
}
return result;
}
public SQLiteAdapter openToWrite() throws android.database.SQLException {
sqLiteHelper = new SQLiteHelper(context, MYDATABASE_NAME, null, MYDATABASE_VERSION);
sqLiteDatabase = sqLiteHelper.getWritableDatabase();
return this;
}
public void close(){
sqLiteHelper.close();
}
public long insert(String content){
ContentValues contentValues = new ContentValues();
contentValues.put(KEY_CONTENT, content);
return sqLiteDatabase.insert(MYDATABASE_TABLE_APP, null, contentValues);
}
public long insertphoto(byte[] iconebyte){
// "create table " + MYDATABASE_TABLE_APP + " ("+ KEY_CONTENT + " text not null)" +
//" ("+ KEY_CONTENT_ID + " INTEGER not null);"+" ("+ KEY_CONTENT_plan + " INTEGER not null)"+
// String sql="Insert into MYDATABASE_TABLE_APP_photo(KEY_CONTENT_ID_photo,KEY_CONTENT_ID,KEY_CONTENT_photo) values(?,?,?)";
//executeSql
ContentValues contentValues = new ContentValues();
contentValues.put(KEY_CONTENT_photo, iconebyte);
return sqLiteDatabase.insert(MYDATABASE_TABLE_photo, null, contentValues);
}
public int deleteAll(){
return sqLiteDatabase.delete(MYDATABASE_TABLE_APP, null, null);
}
public int deleteAllphoto(){
return sqLiteDatabase.delete(MYDATABASE_TABLE_photo, null, null);
}
#SuppressWarnings("null")
public List<Bitmap> queueAllphoto(){
Bitmap bitmap;
String[] columns = new String[]{KEY_CONTENT_photo};
Cursor cursor = sqLiteDatabase.query(MYDATABASE_TABLE_photo, columns,
null, null, null, null, null);
byte[] result = null;
List<Bitmap> listbitmap=null;
int index_CONTENT = cursor.getColumnIndex(KEY_CONTENT_photo);
for(cursor.moveToFirst(); !(cursor.isAfterLast()); cursor.moveToNext()){
result = cursor.getBlob(index_CONTENT) ;
}
bitmap=BitmapFactory.decodeByteArray(result , 0, result.length);
listbitmap.add(bitmap);
return listbitmap;
}
public class SQLiteHelper extends SQLiteOpenHelper {
public SQLiteHelper(Context context, String name,
CursorFactory factory, int version) {
super(context, name, factory, version);
}
#Override
public void onCreate(SQLiteDatabase db) {
//db.execSQL(SCRIPT_CREATE_DATABASE);
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
}
the first thing to know is the number of blobs that need to be shown then after that i created the same number of buttons where i just "setBackgroundRessource"!
here is an example but the images are not blobs but drawables but the idea is the same
except that you need to convert the blob in a bytearray
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
Button btn = new Button(getBaseContext());
btn.setId(k1);
final int id_ = btn.getId();
LinearLayout linear = (LinearLayout) findViewById(R.id.linearscroll);
linear.addView(btn, params);
final Button btn1 = ((Button) findViewById(id_));
btn1.setBackgroundResource(matable[k1]);
matable is just an array of all my drawable