Say I have a program that contains some data. Some integer values, e.g. {1, 5, 10, 3}. The program has a GUI that displays the data. Each integer is converted to string and displayed as a label. There are several different parts of the program where the data can be changed, e.g. a button that increments a value when clicked. Each time a value changes the corresponding label must be updated to display the new value.
Perhaps a more common senario: say we have a control that both changes data and displays data, e.g. a slider bar. Each time the slider is moved we want the data to change, and be displayed in a label (or maybe many different labels). But there are also other ways the data can change, and when it does the slider bar should move.
For Java Swing, what is the current best practise for doing this automatically so we don't ever forget to update the labels?
Same question for Objective C Cocoa.
I would first look at the observer design pattern.
You can observe changes in data and update the UI accordingly. Each language has a different way of implementing it.
I think in Swing the most common approach is to use PropertyChangeListeners.
This problem is solved in any of the Swing UI widgets. They all have a view side and a model side (e.g. a JTable is the view side of a table, where the TableModel is the model side).
If you want to make changes to the data, and make sure your UI is updated, you make the changes on the model side and Swing takes care of the rest. You can take the same approach. Have a model to back your view, and make sure all changes happen on the model. The view elements should then register listeners to that model, and update themself whenever it is needed (basically, the Observer pattern as suggested by tjg184)
There is the delegate pattern in Objective c
VariableChanger.h
#protocol VariableChangedProtocol
-(void) variableDidChange;
#end
#interface VariableChangerClass : NSObject
{
id<VariableChangedProtocol> delegate;
int exampleVariable;
}
#property int exampleVariable;
-(id) initWithDelegate:(id<VariableChangedProtocol>) target;
#end
VariableChanger.m
#implementation
#synthesize exampleVariable;
-(id) initWithDelegate:(id<VariableChangedProtocol>) target
{
self = [super init];
if (self)
{
delegate = target;
}
}
-(void) setExampleVariable:(int) newValue
{
if(newValue != exampleVariable)
exampleVariable = newValue;
[delegate variableDidChange];
}
#end
VariableChangedListener.m
#implementation VariableChangedListener
-(id) init
{
self = [super init];
if (self)
{
VariableChangerClass *v = [[VariableChangerClass alloc] initWithDelegate:self];
}
return self;
}
-(void) variableDidChange
{
NSLog(#"The variable changed");
}
#end
Related
Pardon my question if it may seem stupid but I'm curious. I am making a program in Java of which will have a GUI, and am curious about the whole idea of properties. Why use them when we can just add data to a class? For example:
class myButton extends Button {
private boolean booleanProperty = false;
myButton(args...) {
// Do something with the property
}
public void setProperty(boolean value) {
this.booleanProperty = value;
}
public boolean getProperty() {
return this.booleanProperty;
}
}
Seems to work just fine for storing additional information on the custom implementation of the button. But what about:
class myButton extends Button {
private SimpleBooleanProperty booleanProperty = new SimpleBooleanProperty(false);
myButton(args...) {
// Do something with the property
}
public void setProperty(boolean value) {
this.booleanProperty.set(value);
}
public boolean getProperty() {
return this.booleanProperty.get();
}
}
The only real difference, I am seeing (correct me if I'm wrong) is that that you can attach listeners to the property values, but I feel as if there has to be more than just that. Ideas?
The power of JavaFX's properties is that they can be bound in ways that will automatically update the UI when a change occurs.
As an example consider an element you want to hide if a textField contains no value:
TextField tf = ...
Node container = ...
container.visibleProperty.bind(tf.textProperty.isNotEmpty());
Now as you change the text in tf, you will see container switching whether its visible based on the presence of text.
They really are useful in a lot of ways I even started them using in non UI related stuff. But look at this example: You habe an undomanager class
public class UndoManager {
BooleanProperty canUndo = ...;
BooleanProperty canRedo = ...;
...
}
And you have 3 places from where you can invoke undo/redo.
MenuButton menuUndo;
Button toolbarUndo;
MenuButton contextMenuUndo;
You basically only beed to do this:
menuUndo.disabledProperty().bind(undoManager.undoProperty()):
toolbarUndo.disabledProperty().bind(undoManager.undoProperty());
contextMenuUndo.disabledProperty().bind(undoManager.undoProperty());
and you dont ever have to worry about it again. If you add a new place where an undo can happen you just have to bind it too.
In this case you don't have a benefit, since you do not allow access to the property object itself. Usually this is done.
This allows you to add listeners to the property and be notified, when it changes.
Bindings are using this possibility to keep values the same and properties as well as the Bindings class provide methods for simple conversions of properties.
BooleanProperty booleanProperty = new SimpleBooleanProperty();
booleanProperty.addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
System.out.println("property changed form "+oldValue +" to "+newValue);
}
});
booleanProperty.set(true);
booleanProperty.set(true);
booleanProperty.set(false);
booleanProperty.set(false);
booleanProperty.set(false);
booleanProperty.set(true);
booleanProperty.set(false);
booleanProperty.set(true);
booleanProperty.set(false);
Furthermore it allows you to pass an object representing the property. E.g. code that has to write/read booleanProperty does not need information about the myButton to write/read the property; you can just pass the property.
TableView is an example of a class that makes use of propertys. The columns used with TableView get a property from the items in the TableView and TableView registers a listener to that property. This allows it to change the values displayed in the cells even if the changes are triggered somewhere else in the code. Also for editable cells the properties of the items can be automatically modified.
The use of a javafx property for a pre-made object to hold an arbitrary value for later use. So you can set values to a text field or some other object that doesn't directly effect the shown value
Imagine programming a robot that would take care of resturant customers. How would it respond to customers or any other tasks that would have to be taken care of without using something like property listeners.
The benefit of using property listeners is that you can make your program become Concurrent. If there are no customers coming the next 1 hour your otherwise Sequentially made program would stand and do nothing for the next hour. Maybe switching the word Concurent with flexible in this example would be better, but you should look up Concurent programming and Sequence programming. Those properties allow you to customly make your program concurent.
You should also know that the gui you are using are already use built-in (event listening) features which build on the same principle.
Now what if you made that robot - instead of handing 1 customer at a time - respond depending upon what had to be done instead. Ask customers what the food tasted like (if customers have eaten), take new order(if called upon - by ANYBODY), take dishes(When any customer has paid and there is dish on a table), handle payment(When called upon by anybody). And ofcours handling a new customer arriving at the resturant.
The concurrently made program will handle any task needed by any customer. The sequencly made robot would only handle one customer at a time. Maybe it only then also has to be limited to greeting customers and placing them on seats to be anyhow useful. (You cannot have 1 robot for each customer).
Some people think it is easier to program sequencially can also be said. This is beacuse it can be difficult to keep track of subtasks that has to be done in a particular order. For instance that robot should not look for dishes when there haven´t arrived any customers. And what happens if it receives a payment call - while carrying dish? So it´s hard to prioritize and sort out the different tasks. However when succesfully doing it you program becomes so much more effective. It will be able to mutli task vs just solo-tasking.
And yes the sole purpose of properties is indeed that you can add listeners to them.
Mathematica comes with a simple java program that allows to inspect the communication between front end and the kernel. It's called LinkSnooper and in general it works quite nice. It looks like this
I think I can improve the behavior and usability of the program to some extend, but to do this, I need to reimplement some parts. One fundamental piece that I need is a text pane, which has the following properties:
it can receive a lot of data and it probably should use a fast ring-buffer so that the very first log-lines are removed when the data grows too much. Another possibility is that it automatically starts to write data to disk and possibly reloads it when the user scrolls up to see the first entries
it should be able to handle colored text. I plan to use a simple highlighter (the log-data is actually real Mathematica syntax) on each arriving line to make reading more easy
it doesn't need to be writable. It's OK if the text pane is read-only.
Question: Does something like this already exist? Currently, LinkSnooper uses a JTextArea underneath and before I start do write my own version, I wanted to ask whether someone has already done this.
Edit:
What I planned to do was to use some Logger framework because it seems natural to me that those libraries should be able to handle a lot of data. Additionally, they often provide interfaces to format the messages and you can define different handlers that can take care of different messages. What I was hoping for was that someone already has combined this with a neatly working text window that can handle large output.
As Simon has pointed out I would suggest using JavaFX for this task.
If you "just" need to display large amounts of log data without advanced highlighting (sub-string range highlighting), ListView is the component for you.
It uses a virtualized layout container, so only the cells that are in the visible area of the viewport are actually rendered. This allows for lazy loading, cell recycling etc.
The ListView uses an ObservableList as its DataStructure. Similar to EMF EList, the ObservableListautomatically notifies the ListView on changes in its contained data.
There are several factory methods to create an ObservableList via FXCollections even allowing to wrap an existing List (e.g. RingBuffer).
If you need the advanced highlighting, RichTextFX is probably the solution to go for as it allows detailed styling of its contained text. RichTextFX uses a virtualized layout, too.
Edit #2
Tom has written about this in his blog: http://tomsondev.bestsolution.at/2014/12/27/displaying-and-editing-large-styled-texts/
Edit #1 ListView example
JavaFX does a very good job at separating the model from the view, so we try not to mix this up and need to create two things:
A data class (model)
A Cell renderer for that data class (view).
First the data class:
public class LogData {
private final String logMessage;
private List<String> highlightedFragments = null;
public LogData(String pLogMessage) {
logMessage = pLogMessage;
}
public String getLogMessage() {
return logMessage;
}
public List<String> getHighlightedFragments() {
if (highlightedFragments == null) {
doHighlight();
}
return highlightedFragments;
}
private void doHighlight() {
List<String> highlightedParts = Collections.emptyList(); // TODO lexer
highlightedFragments = highlightedParts;
}
}
The interesting part is, that the highlighting is done on demand not on initialization. Or in other words: The lexer only performs its work, when the cell renderer requests the data.
Now the Cell renderer:
ListView<LogData> listView = new ListView<>();
listView.setCellFactory(cb -> new LogDataCell(){});
public class LogDataCell extends ListCell<LogData>
{
#Override
protected void updateItem(LogData item, boolean empty) {
super.updateItem(item, empty);
if(empty || item == null) {
setText(null);
setGraphic(null);
}
else {
List<String> fragments = item.getHighlightedFragments();
if(fragments == null || fragments.isEmpty()) {
setText(item.getLogMessage());
setGraphic(null);
}
else {
TextFlow textFlow = null; //TODO
setText(null);
setGraphic(textFlow);
}
}
}
}
This is not a fully working example, there are several TODOs left, but hopefully you get the idea.
If you want to add search highlighting, I described a similar approach for the TableView control element here: JavaFX Table with highlighted text (Labels) with poor performance
I am developing a GWT app (I'm fairly new to GWT so this is a request for best practices; I haven't found any relevant answer on SO or elsewhere) where a timeline is required. This timeline (with descriptions, labels, interaction handles, and plots) resides in its own container (a Panel).
[<] [now] [>] // Interaction (navigation)
2007 2008 2009 2010 // Labels
| | | |
+ Group 1 // Collapsible groups
- Group 2
Item 2a ===== == // Item with plots (plots are wrapped in container per Item)
Item 2b ===== === =
-Group 3
Item 3a ===
Item 3b ===
Now, when user's navigate the timeline (using the button to move forward or backwards), I need to recalculate some elements of the layout:
Labels need recalculating / repositioning
Plots need recalculating / repositioning. Plots are based on a set of Timeslot elements (extends Widget, attributes dateStart and dateEnd) which are already related to Items which are related to Groups.
The collapsible panels are DisclosurePanels.
As far as I can tell, I now have two options for handling navigation:
I can clear() the container panel and do a complete redraw. For this, I need to preserve the state (collapsed/expanded) for all groups. (Groups and items are static for the entire period, by the way!) This will give one big redraw.
I can let the plot containers (each Item has its own TimeslotContainer which is a FlowPanel) hold a reference to all its Timeslots and then let every TimeslotContainer redraw itself (i.e., filter and position relevant Timeslots) based on the current timespan. This will give several minor redraws (one per Item per expanded Group), the advantage being that the DisclosurePanels will be preserved, thus maintaining their own state.
I'm inclined to go with the second solution. But are there any best practices on this one? Am I missing some common gotchas?
If groups and items are static I would also recommend the second approach.
DOM operations (constructing, etc) are properly the most expensive functions in a GWT application (performance wise) so these DOM operations should be kept at a minimum.
However I don't think performance may be a big issue here because the amount of DOM elements is relatively low.
Nevertheless I still think the second approach is better. You don't have to store the state for groups and items and as they are static it doesn't really make sense to redraw them.
I can only think of one advantage of the first approach:
There will be only one relatively easy draw function. In the second approach all TimesSlotContainer have to implement a function in order to redraw themselves and also take into account position and context of the Timespan. That function might be more complicated then one big re-draw function.
I went ahead and implemented a version of the second solution under the original intention to try the first if the second did not perform sufficiently. (This was never realized, though, as the second solution performed highly satisfactorily.)
What I asked in the question was primarily references to the GWT way to do this. I still haven't found anything written regarding this issue and so suspect that nobody has missed such guidelines before :) To conclude my search, I will self-answer the question outlining the way I ended up implementing together with the (assumed) pros and cons. Thanks to #Ümit for supporting my instincts. The following is in part intended to address the last paragraph in #Ümit's answer regarding the complexity of the draw methods.
I ended up letting TimeslotContainers (the Panels containing TimeSlots), LabelPanel (Panel with generated HTML elements), and NavigationPanel realize a simple interface:
public interface IReceivesPeriodChangedEvents {
public void periodChanged();
}
A simple EventBus handled the notification process on these IReceivesPeriodChangedEvents instances:
public class EventBus {
private static EventBus instance;
private Set<IReceivesPeriodChangedEvents> receivers;
private EventBus() {
this.receivers = new HashSet<IReceivesPeriodChangedEvents>();
}
public static EventBus getInstance() {
if (instance == null) {
instance = new EventBus();
}
return instance;
}
public void addReceiver(IReceivesPeriodChangedEvents receiver) {
this.receivers.add(receiver);
}
public void notifyPeriodChanged() {
for (IReceivesPeriodChangedEvents receiver : this.receivers) {
receiver.periodChanged();
}
}
}
Whenever one of TimeslotContainer, LabelPanel or NavigationPanel were instantiated (once per page load, these objects are reused throughout the lifetime of the page) they made sure to subscribe to the EventBus:
public class TimeslotContainer extends FlowPanel implements IReceivesPeriodChangedEvents {
public TimeslotContainer(/* ... */) {
// ...
EventBus.getInstance().addReceiver(this);
}
// ...
}
In order to handle the "complex" draw method, I simply created a method (buildFromStore()) to add relevant Widgets (e.g., newly created Timeslot objects on the TimeslotContainers). This resulted in great reuse of the draw method:
public class NavigationPanel extends FlowPanel implements IReceivesPeriodChangedEvents {
public NavigationPanel() {
// ...
buildFromStore();
EventBus.getInstance().addReceiver(this);
}
private void buildFromStore() {
// Add lots of HTML elements here
}
#Override
public void periodChanged() {
clear();
buildFromStore();
}
}
Simple as that!
In conclusion:
No gotchas encounted. Lots of minor redraws did not seem to be a problem (I never implemented solution one, though). The draw methods could be reused (in this case, at least). The collapsible DisclosurePanels never lost their state as they were never replaced (only their contents were).
could someone help me. I'm new to gwt and maybe this is so simple. but I can't seem to figure this out...
I create 2 button for a row in a celltable, each with this method:
protected void addButtonColumn(String header, final IHasValue<Row, Button> hasVal){
Column<Row, String> column = new Column<Row, String>(new TextButtonCell()) {
#Override
public String getValue(Row object) {
return ((Button)hasVal.getValue(object)).getText();
}
};
column.setFieldUpdater(new FieldUpdater<Row, String>() {
#Override
public void update(int index, Row object, String value) {
((Button) hasVal.getValue(object)).click();
}
});
table.addColumn(column, header);
}
I want each button to something different when clicked, but it doesn't work. i know that i should do something in setfieldupdater but i don't know what.
Your use of the TextButtonCell to contain a Button (i.e. a widget) doesn't really make a lot of sense - wouldn't it be easier to let the cell have access to the data, and use the ValueUpdater to trigger some kind of behavior based on that data directly?
Cells are not widgets - they are much simpler than widgets. This chiefly means two things: they are faster to draw, and stupider. Both of these things are as a result of a single cell instance being used to draw many pieces of data in slightly different ways. If you are going the effort of building a button per element, it doesn't make sense to use a cell-based widget then - you are nearly drawing everything twice, and getting the worst of both worlds (slow code, that is hard to work with).
Don't use a Button with ClickHandlers attached, but some other abstraction to deal with clicks, like a Command instance for each row, or even better, some kind of handler that accepts the row instance clicked. It might even make sense to have a FieldUpdater instance passed in as a parameter for your method (and maybe make the IHasValue generic on String instead of Button, so your models don't need to wrap widgets).
(This may not be answering your question directly, but is instead hopefully helping shed some light on why we use cells at all, and how to best write code that takes advantage of cells.)
I am trying to implement a ListSelectionListener for some of my JTables. Simply (at the moment) the ListSelectionListener is supposed to simply return the text of the cell that was selected.
My program design has several JTables and I would like to have one ListSelectionListener work for them all. In the valueChanged event of the ListSelectionListener I thought it was possible to do something like:
private class SelectionHandler implements ListSelectionListener {
public void valueChanged(ListSelectionEvent e)
{
JTable table = (JTable)e.getSource();
String data = (String) table.getValueAt(table.getSelectedRow(), 0);
// Print data
}
}
Behind the scenes I have used the following code to get the SelectionHandler working with the table in question:
fbTable.setCellSelectionEnabled(true);
ListSelectionModel cellSM = fbTable.getSelectionModel();
cellSM.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
cellSelectionModel.addListSelectionListener(selectionHandler);
When I run the program I get a ClassCastException error:
Exception in thread "AWT-EventQueue-0" java.lang.ClassCastException: javax.swing.DefaultListSelectionModel cannot be cast to javax.swing.JTable
at cardboardfantasy.CardboardFantasyView$SelectionHandler.valueChanged(CardboardFantasyView.java:360)
// This is the line in question: JTable table = (JTable)e.getSource();
Is there a way to do something like this? One solution I thought of was to compare the source of the event (e.getSource()) to all my JTables to see if they were equivalent (big if block) and then just calling .getValueAt inside that block but that would making the code in the future difficult if tables were to be added or removed.
Either debug your code in your IDE, set a breakpoint and see what the type of e.getTarget() is:
Object source = e.getSource();
JTable table = (JTable)source; // breakpoint on this line and inspect the variable 'source'
String data = (String) table.getValueAt(table.getSelectedRow(), 0);
Or if debugging is not possible for whatever reason do this:
Object source = e.getSource();
System.out.println(source.getClass());
But: debugging using System.out.println is evil. your debugger is your friend.
As the error implies, the source object in question is a DefaultListSelectionModel not a JTable. This makes sense since the source of the event (that is, the object which fired the event) was the selection model object, not the table. Also, models in themselves make no assumptions about what type of object is using them as a model so there is no way to get a reference to the table via the selection model.
Pass the JTable instance to your selection handler. As long as the handler listens on one table, you'll be able to use that instance instead of relying on the information from the event.
I think there are two main solutions:
Use a JList and register the listener not with model but directly with the list. Then, if the list is contained by a table you could just ask for the list's (Component) parent to find the responsible table
Override DefaultListSelectionModel to (for example) take an additional argument in the constructor, which would be a JTable instance (every table will need a new instance of that model). You would save that instance in an instance variable and could then operate directly on the table when an event occurrs
I do not think that either of these solutions is ideal. I have the feeling that you could make your life easier by using some pattern or idiom to get around having to know which table the source was. But to give you any clues there we'd have to see a lot more of your code.