How to listen to visible changes to the JavaFX SceneGraph for specific node - javafx

We created a small painting application in JavaFX. A new requirement arose, where we have to warn the user, that he made changes, which are not yet persisted and asking him, if the user might like to save first before closing.
Sample Snapshot:
Unfortunately there are a lot of different Nodes, and Nodes can be changed in many ways, like for example a Polygon point can move. The Node itself can be dragged. They can be rotated and many more. So before firing a zillion events for every possible change of a Node object to the canvas I`d like to ask, if anyone might have an idea on how to simplify this approach. I am curious, if there are any listeners, that I can listen to any changes of the canvas object within the scene graph of JavaFX.
Especially since I just want to know if anything has changed and not really need to know the specific change.
Moreover, I also do not want to get every single event, like a simple select, which causes a border to be shown around the selected node (like shown on the image), which does not necessary mean, that the user has to save his application before leaving.
Anyone have an idea? Or do I really need to fire Events for every single change within a Node?

I think you are approaching this problem in the wrong way. The nodes displayed on screen should just be a visual representation of an underlying model. All you really need to know is that the underlying model has changed.
If, for example, you were writing a text editor, the text displayed on the screen would be backed by some sort of model. Let's assume the model is a String. You wouldn't need to check if any of the text nodes displayed on screen had changed you would just need to compare the original string data with the current string data to determine if you need to prompt the user to save.

Benjamin's answer is probably the best one here: you should use an underlying model, and that model can easily check if relevant state has changed. At some point in the development of your application, you will come to the point where you realize this is the correct way to do things. It seems like you have reached that point.
However, if you want to delay the inevitable redesign of your application a little further (and make it a bit more painful when you do get to that point ;) ), here's another approach you might consider.
Obviously, you have some kind of Pane that is holding the objects that are being painted. The user must be creating those objects and you're adding them to the pane at some point. Just create a method that handles that addition, and registers an invalidation listener with the properties of interest when you do. The structure will look something like this:
private final ReadOnlyBooleanWrapper unsavedChanges =
new ReadOnlyBooleanWrapper(this, "unsavedChanged", false);
private final ChangeListener<Object> unsavedChangeListener =
(obs, oldValue, newValue) -> unsavedChanges.set(true);
private Pane drawingPane ;
// ...
Button saveButton = new Button("Save");
saveButton.disableProperty().bind(unsavedChanges.not());
// ...
#SafeVarArgs
private final <T extends Node> void addNodeToDrawingPane(
T node, Function<T, ObservableValue<?>>... properties) {
Stream.of(properties).forEach(
property -> property.apply(node).addListener(unsavedChangeListener));
drawingPane.getChildren().add(node);
}
Now you can do things like
Rectangle rect = new Rectangle();
addNodeToDrawingPane(rect,
Rectangle::xProperty, Rectangle::yProperty,
Rectangle::widthProperty, Rectangle::heightProperty);
and
Text text = new Text();
addNodeToDrawingPane(text,
Text::xProperty, Text::yProperty, Text::textProperty);
I.e. you just specify the properties to observe when you add the new node. You can create a remove method which removes the listener too. The amount of extra code on top of what you already have is pretty minimal, as (probably, I haven't seen your code) is the refactoring.
Again, you should really have a separate view model, etc. I wanted to post this to show that #kleopatra's first comment on the question ("Listen for invalidation of relevant state") doesn't necessarily involve a lot of work if you approach it in the right way. At first, I thought this approach was incompatible with #Tomas Mikula's mention of undo/redo functionality, but you may even be able to use this approach as a basis for that too.

Related

How to automatically run a code from javafx new Scene

I am completely new to Java and I am learning it while developing an app for a school project.
Image Link
I want to code the above program. In it ,
The user will click Ready button in screen 1.
Then screen two will appear and an image of a butterfly will be shown in a order given by me[Preset using a CSV file] Like shown in screen 2 and 3.
Finally, a button set will appear in the grid and user has to select the buttons in the order of the butterfly appearance.
I am stuck in finding a way to start screen 2 and automatically play the butterfly sequence.
I tried putting the image.setimage() on the initialize() block in my screen 2 controller with a delay in-between each setimage() . but it dosent work.
Anyone can suggest me a way to handle this kind of task? Thank a lot in advance.
The issues often seen with this kind of code for beginners are doing sleep or some other long-running operation on the application thread to do some animation. However blocking the javafx application thread results in the scene not being updated resulting in a freeze of the gui.
You either need to move the long-running parts of this animation to a background thread and use Platform.runLater for any GUI updates or use something designed for this exact purpose. There are multiple classes that could be useful in the javafx.animation package, but the most convenient of them seems to be Timeline:
Store the sequence of movements in a suitable data structure and use the Timeline to trigger an event handler in regular intervals to update the gui:
List<FieldIndices> fieldIndices = ...
final Iterator<FieldIndices> iterator = fieldIndices.iterator();
final Timeline timeline = new Timeline();
timeline.getKeyFrames().add(new KeyFrame(Duration.seconds(1), evt -> {
if (iterator.hasNext()) {
moveButterfly(iterator.next());
} else {
removeButterfly();
timeline.stop();
}
}));
timeline.setCycleCount(Animation.INDEFINITE); // repeat until stop is called
timeline.play();
Now all that's left for you to implement is reading the data to a list and implementing the logic of moving the butterfly to a new position.
Note that I do not actually recommend using more than 2 scenes: The user will expect the same position for the buttons and the "fields" showing the butterfly. If you design 2 fxmls any adjustment to one of the scene would require you to do the same adjustments to the other scene. This makes the layout hard to maintain. The alternative requires you to create the scene in java code, but the repetitive nature of the scenes makes this a good idea anyways. (The alternative is injecting 16 fields to the controller and collecting them into a suitable data structure; This is error prone and any change of one of the buttons would probably require 16 changes in the fxml. Use a nested for loop and you need to write the logic for creating a button only once storing the buttons in e.g. a nested array can be done at the same time...)
As I understand, you wanna play butterfly sequence once 2nd stage is shown...To achieve that, you could try something like:
List positions = new ArrayList(); //places to show butterfly (e.g. table cells)
secondStage.setOnShown(windowEvent -> {
// update UI with Pltform.runLater()
// moveButerflyTo() is your method to place butterfly on given place
positions.forEach(position -> Platform.runLater(() -> moveButerflyTo(position)));
});
I didn't try this but it do the job...

QGraphicsScene::clear() clearing scene but not the view

I use a custom class (Configuration) derived from QGraphicsItem and I add its objects to a QGraphicsScene, which is then displayed in a QGraphicsView. Usual stuff. What Im doing exactly is drawing a tree, in multiple steps, one level a step, each node beeing my custom QGraphicsItem.
Here a screenshot. The tree happens to be sequential in the simple case.
I first draw the root node. The signal that triggers that is fired after the user entered a string.
void MainWindow::drawRootSlot(ConfigTreeBuilder & builder)//this is a slot
{
c_scene->clear(); //the clear cause headache. i'll expain
Configuration* conf = new Configuration(builder.getNodesX(), builder.getNodesY(),builder.getNodesConfig());
//code
c_scene->addItem(conf);
//code
}
Each subsequent Configuration is draw inside another slot.
void MainWindow::configTreeSlot(ConfigTreeBuilder & builder) //SLOT!!!
{
while(builder.chooseNextNode()) {
Configuration* conf = new Configuration(builder.getNodesX(), builder.getNodesY(), builder.getNodesConfig());
//code, while loop
QGraphicsLineItem *edge = c_scene->addLine(QLineF(*(parentsPoint), conf->getLeftOrigin()));
edge->setZValue(-1); //below the Configuration item
c_scene->addItem(conf);
}
}
All works fine when done for the first time. When I enter a new string, resetting the tree, dark magic happens. What I expected to it do is: call drawRootSlot(), deleting the whole tree (c_scene->clear()), draw a new root node. And, if I put a debugger breakpoint inside drawRootSlot() this is exactly what happens! But when I run it (without breakpoints), what I get is this:
The previous tree got mangled, but not deleted. The scene gets indeed cleared of its items (printed that) but the view does not reflect that. But again, when I put a breakpoint inside drawRootSlot() thhe view and the scene are in sync.
I tried to delete the scene object, and instaciate a new one instead of calling c_scene->clear(), to guarantee it empty. Then the changes are reflected on the view (the first time drawing always works).
So, I have no idea what to deduce from these symptoms. It works as expected with a breakpoint or with a freshh QGraphicsScene object. It does not when just using c_scene->clear(). One couldsay I just messed up the parent-object/child-object relation, but clear() does remove items from the view... I tried calling it right after c_scene->addItem().
What is this sorrcery? One that makes me believe I'm not actually stupid?
EDIT: Whats interesting and may be a hint to the real problem, is that when c_scene->clear() is called, the edges of the tree, which are normal QGraphicsLineItems, are indeed deleted in all cases (breakpoint or not). Something to do with them not beeing custom?
Ok, calling QGraphicsView::viewport().update() after QGraphicsScene::clear() solved my problems.
But does anyone have an explanaition to the behavior described above?
EDIT: Upon doing doing something else I stumbled upon the actual core of the problem: I messed up the boundingRect() of my GraphicItems, so it was below the visble item, touching only its lower edge (which got deleted, as seen on the screenshot). So now no calls to any update() methods are neccesary.
I think when you call this fitInView() for the graphicsview it cleans up the view from any artifacts that remain from a previous scene.
You can clear both scene and Graphics View
scene->clear();
ui->graphicsView->items().clear();
graphicsView = name of your graphics view
This code will delete both scene and graphics view

Editing A Library Symbol From ActionScript

In the Flash authoring environment I can edit a library symbol and all on-stage instances based upon it reflect the changes. How can I do the same thing in ActionScript? There seems to be no way to address a library symbol.
For example:
Inside Flash CS3, I have created a Square.swf file that has 100 instances of the library symbol Square.
Now, Square.swf is loaded into another file BlueSquare.swf and I want to change the Square symbol into a blue square so that all instances of Square become blue.
How do I do this using Actionscript?
Thanks for the help.
What's in a clip's library symbol is the author-time definition of that object - you can't change it at runtime. Instead the normal approach would be to dynamically change the contents (not definitions) of the clips you want to change, which can be done in various ways, but all the good ways of doing that involve making the dynamically-changing clip understand how to update its appearance. So you need to be able to re-author the changing clips to suit your needs.
If you're loading in an animation that somebody else made, and trying to go through and replace all instances of object A with object B, the only way to achieve that is to traverse through the content's display list looking for A, and when you find one, remove its children and replace them with the the contents of a B. Mind you, for animations that may not really solve your problem, since animations normally add and remove clips frequently, so at any given point you could replace all the "hand" clips with "hand2", but then a frame later new "hand" clips might come into existence. But short of opening up the SWF and changing the binary data inside, there's no other way to dynamically change all of a given object to something else unless the object knows how to change its contents.
If it is only about making sure that the square you are attaching is blue you could use the colorTransform to change its appearance:
var someSquare:Square = new Square();
someSquare.transform.colorTransform = new ColorTransform(0,0,0,1,0x00,0x00,0xff,0x00 );
addChild( someSquare );
Of course this does not change the color of all instances that you have already attached.
If you really wanted to change the actual SWF symbol in Actionscript the only way I see is to parse the swf with as3swf ( https://github.com/claus/as3swf/wiki ), find the shape tag of the symbol, change it and then load the ByteArray that contains the swf via loader.loadBytes() - but that's admittedly quite a complicated way and you can achieve the same result by simply putting some colorizing code into the shape symbol itself and then trigger the color change via an Event that is broadcasted by your main app.
Of course, if you make custom component, when you change it changes will appear on all instances of that component/class. Here's the example: http://livedocs.adobe.com/flex/3/html/intro_3.html
On the other hand, if you use modules whey pretty much do the same as swf-s you used in Flash, when you rebuild-recompile them changes will reflect on your main application which uses them. Here's th eexample for modules: http://blog.flexexamples.com/2007/08/06/building-a-simple-flex-module/
So MXML/AS component/class are your "symbols" which you can create or drop on stage on fly.
Modules are "movies" you can load and they run on their own with possibility to communicate to main movie.
The closest way of achieving this is to use Bitmaps. If you update the bitmapData they display, they will all update automatically.
However this approach is not good at all. You should maintain application state separately in an object model, and have the visualisation update, if the state changes.
What you want to do, is to misuse a feature for changing graphic appearence at design time, to change application state at runtime. In generally, ideas like these can be thought off as bad.
For example if you take the time to separate the state model and the visualisation layer, it will become fairly easy to save the game state on a server or to synchronize it with other clients to achieve multiuser features.
greetz
back2dos
If you are trying to build an Avatar and user can customize your Avatar parts e.g. hands, legs, face etc. and you want all these assets to be kept in separate swf file, that is pretty straightforward. You keep all the assets, in separate swf or one large swf file and load them at runtime. Now, maintain your Avatar object instance and place the child objects, which are chosen by the user.
You can create inside your class a static List with references all the created instances and then apply a change with static methods. For example:
package
{
import flash.display.MovieClip;
import flash.geom.ColorTransform;
public class Square extends MovieClip
{
public static var instances:Array = new Array();
public function Square():void
{
Square.instances.push(this); // This is the trick. Every time a square is created, it's inserted in the static list.
}
// This property gets the color of the current object (that will be the same of all others because the setter defined below).
public function get color():ColorTransform
{
return this.transform.colorTransform;
}
public function set color(arg:ColorTransform):void
{
// Sets the color transform of all Square instances created.
for each(var sqr:Square in Square.instances)
{
sqr.transform.colorTransform = arg;
}
}
}
}

In Qt, create a table with an blank editable row

This is a Qt-specific question.
It's convenient to be able to add new data to a table by typing content into a blank row at the bottom of a table. When the data is committed, a new blank row is added to the table.
Has anyone found a way of implementing this in a generic way, that fits into Qt's model-view programming architecture? My closest attempt involves creating a proxy model, such that the rowCount() returned from the model is always one greater than the source model.
QAbstractTableModel* sourceModel ; // Data is stored here
QBlankRowModel* model ; // Proxy model that adds one to rowCount()
QTableView* view ; // View
view->setModel( model ) ;
model->setSourceModel( sourceModel ) ;
Any suggestions are welcome. Thanks.
From a design-perspective, this should be part of the view, not the model. Therefore, I suggest implementing a view with the functionality and leave the model unchanged. KOffice Kexi does just this with kexitableview (screenshot, documentation). Maybe you want to use some of their code.
BTW, you might still be able to use your hack and combine it with my suggestion by putting it inside a new table view implementation YourTableView:
QBlankRowModel re-implements the
QAbstractTableModel
interface. It returns sourceModel.rowCount()+1 as the QBlankRowModel::rowCount().
It returns a QVariant() if the n+1th row is requested in QBlankRowModel::data().
All the rest within QBlankRowModel is forwarded to the sourceModel (with editing
the n+1th row in QBlankRowModel buffered and replaced with inserting into the
sourceModel when finished).
The new YourTableView inherits from
QTableView and wraps the sourceModel within
YourTableView::setModel(), calling
QTableView::setModel(QBlankRowModel(sourceModel)).
Thereby, your hack is localized at one point.
Your solutions seems a little hackish. Your problem is not only additions, it's also editions. What happens when your user edits a row, the typed data goes directly to your "data layer" even before the user commits his edition?
A better solution would be to restrict the role of your sourceModel. Rather than being a "direct" representation of your data, it should be a "buffered" representation of it. When the sourceModel is created, you make a copy of your data in some kind of Row() instances. The sourceModel, having its own copy of the data can then freely play around, perform editions and additions, and only commit the data to your model layer when the user commits his edits.
If you want a PyQt example of such a table, you can look at the source of a project of mine:
http://hg.hardcoded.net/moneyguru/
You might have to dig around to actually find the "buffering" logic because it's not in the PyQt code itself, but rather the "cross-platform" part of the code:
http://hg.hardcoded.net/moneyguru/src/tip/core/gui/table.py
This logic is then used in my QAbstractItemModel subclass:
http://hg.hardcoded.net/moneyguru/src/tip/qt/controller/table.py
Sounds like a reasonable solution, as it should work for any model that you might want as the actual table model, ie. SqlTableModel or just a plain one. As long as you add the row when the user is done editing and take care not to add the row when the user did not add any data.

Flex - Is there a way to specify what direction a ComboBox will open?

Maybe I should further qualify this - Is there a way to specify which direction a ComboBox will open without copying and pasting the entire ComboBox class and ripping out the code where it determines which direction it will open in...
I'm my specific case - I need it to open upwards - always.
UPDATE: You can't fix this by subclassing it because the function that handles the direction of the opening is:
private function displayDropdown(show:Boolean, trigger:Event = null):void
And that bad boy uses a fair amount of private variables which my subclass wouldn't have access to...
If you build up the Menu object yourself, you can place the menu anywhere you want by simply setting the x,y coordinates of the menu object. You'll need to calculate those coordinates, but you might be able to do this easily without subclassing ComboBox.
I am doing something similar with PopUpButton; you might find it easier to work with PopUpButton. This is based on real code from my current project:
private function initMenu(): void {
var m:Menu = new Menu();
m.dataProvider = theMenuData;
m.addEventListener(MenuEvent.ITEM_CLICK, menuClick);
m.showRoot = false;
// m.x = ... <-- probably don't need to tweak this.
// m.y = ... <-- this is really the interesting one :-)
theMenu.popUp = m;
}
<mx:PopUpButton id="theMenu" creationComplete="initMenu()" ... />
BTW, to get the PopUpButton to act more like I wanted it (always popup, no matter where the click), setting openAlways=true in the MXML works like a charm.
I doubt it - you'd need to subclass the control (which isn't that big a deal.)
Maybe you could mess with the real estate so it's placed in such a fashion (e.g. crowded into the lower right corner) that up is naturally coerced?
I would recommend checking out this post. Yes, you do have to grab the ComboBox code and modify it, but at least now you have an idea where the modifications need to go.
You could set the MaxDropDownHeight, if you set it big enough Windows will automatically set the direction upwards.
This irritated me no end. I have uploaded a solution, its a simple Class that extends the PopUpButton and removes the logic of stage bounds detection as it failed 50% of the time anyway. My code just allows you to simply specify whether you want to open the menu up or down:
http://gist.github.com/505255

Resources