series.slices.template.events.on("over", function(ev) {
series.slices.template.tooltipHTML=showHtml(ev, series.name);
}, this);
Tooltip issue in amchart4: not reload on pie chart slice.
I am using custom tooltip like generating custom html on mouse hover. Everything is working but when I move cursor from one slice to another, the popup html is not refreshing. When I mouse out and then again mouse over the same slice, the popup html shows correct data.
I think the previous html data is cached somewhere. Please help me.
It could possibly help to see what showHtml is doing. But we'll make do without that.
There are a few problems with this code.
Code-wise, the most obvious is this line:
series.slices.template.tooltipHTML=showHtml(ev, series.name);
You're resetting the template's tooltipHTML instead of the actual slice's tooltipHTML.The template has applyOnClones as true by default so it will propagate to all the other slices, so if this worked it was by accident. If there's any slice-specific settings in showHtml that don't rely on data placeholders this will break tooltipHTML for the next slice that's hovered over.
The real issue, however, is the approach.
So long as tooltipHTML or tooltipText are set, a tooltip will appear on hover.
It's better to use an adapter for tooltipHTML in this case.
As a quick test to see which wins the race condition, the hover event or the tooltipText/HTML adapter, make a handler for each, and hover over a slice:
series.slices.template.events.on("over", function(){
console.log("hover");
});
// override tooltipText so tooltipHTML is actually used
series.slices.template.tooltipHTML = "something...";
series.slices.template.adapter.add("tooltipHTML", function(tooltipHTML) {
console.log("adapter");
return tooltipHTML;
});
// console:
// "adapter"
// "hover"
You'll find that the adapter triggers first, so by the time you hover, the tooltip is already on its way with its HTML and all.
So use an adapter, just be sure to adjust your showHtml function so its first argument takes target itself instead of event (it may not be necessary to have another argument with the tooltipHTML that's being modified because it will always be sent the original tooltipHTML unformatted string, so that could be referred to manually whether as a string or variable).
The adapter can look like this:
var originalTooltipHTML = "<strong>{country}: </strong>"
series.slices.template.tooltipHTML = originalTooltipHTML;
series.slices.template.adapter.add("tooltipHTML", function(tooltipHTML, target) {
// #2: If we had used an event, here you would work on the target itself, event.target, not the template.
// The showHtml might not even be needed, perhaps whatever it does can go in here instead.
return showHtml(target);
});
Here's a demo with all that thrown together:
https://codepen.io/team/amcharts/pen/a0122e572d27cf513a78384345cad3a6
Related
I am designing a GUI using C, Glade, and Gtk.
I have some signals configured in glade to update the labels of various widgets, mainly GtkButton and GtkLabel. The overall functionality is that when a certain radio button is clicked, all button and labels change in response (language selection).
I am using the function gtk_label_set_label(...) in the widgets _draw() function and it works as expected (text changes, g_print occurs (once)).
gboolean on_lblMyLabel_draw(GtkLabel *label, gpointer *user_data) {
gtk_label_set_label(label, "custom text");
g_print("%s\n", "custom text");
return FALSE;
}
However, when I attempt the same from a button,
gboolean on_btnMyButton_draw(GtkButton *button, gpointer *user_data) {
gtk_button_set_label(button, "custom text");
g_print("%s\n", "custom text");
return FALSE;
}
The text does not update, but dissappears, and the g_print() statement prints forever (as if the draw is recursively calling itself).
Funnily, if I move the button code from _draw to _click, it works as expected, however, I need the GUI to redraw itself, so updating on click is impractical.
Is there a way, using _draw() to prevent this?
Is there a better way to do this?
thx!
Is there a way, using _draw() to prevent this?
No, and you shouldn’t be using the draw signal for this either. It has an entirely different purpose, and will be called each time a widgets redraws itself. That’s also the reason why your button is going into an infinite recursion: you changed its label so it figures it needs to be redrawn; that redraw leads to your callback being called, which again changes the label, etc etc
Is there a better way to do this?
Yes, and you mention it yourself already: make sure you do the logic of changing the widgets in the appropriate place (for example, on a click event), and let the GTK widgets take care of redrawing themselves.
Unless you’re doing something very exotic (like not running an event loop, which you automatically get with GtkApplication), this will all work fine.
Last time I was doing optimizations (maybe a year ago), I was able to see all possible function calls in the js flamechart.
Now, however, it doesn't seem to go all the way.
Here's a long running function:
I'm expecting way more sub-calls so that I may understand why is it running so long.
Here's how that function looks like:
function updateIfNeeded() {
switch (state) {
case 'NO_REQUEST':
throw new Error('Unexpected draw callback.\n' + 'Please report this to <https://github.com/elm-lang/virtual-dom/issues>.');
case 'PENDING_REQUEST':
rAF(updateIfNeeded);
state = 'EXTRA_REQUEST';
var nextNode = view(nextModel);
var patches = diff(currNode, nextNode);
domNode = applyPatches(domNode, currNode, patches, eventNode);
currNode = nextNode;
return;
case 'EXTRA_REQUEST':
state = 'NO_REQUEST';
return;
}
}
It's part of the elm-runtime.
Now, while it is possible that this function might not call any other functions, it would not explain why it's running for so long.
Where is the button for my complete flamechart :}
The Performance panel has a Disable JavaScript Samples checkbox in the capture settings menu.
When this checkbox is enabled, the timeline only shows the yellow placeholders to differentiate between script execution time and, layout, paint and composite activity.
Notice how the Cog/Settings Icon is red when the checkbox is enabled.
When the checkbox is not checked, the timeline shows the flame chart. When all the capture options are in their default state, the Cog/Settings Icon is blue while the menu is open and grey when the menu is closed/collaped.
Unfortunately it is not possible to be certain that this was the exact issue that you encountered, as the shared screenshot doesn't depict the capture settings.
Hopefully this knowledge proves valuable should you encounter the same behaviour in the future.
I am trying to figure out how to have custom brush handles highlight data in a range equal to a single value, which seems to result in a null selection.
Here the Plunkr I recreated on one of Mike Bostock's examples. It's in Angular 2/ionic, so if that's an issue, please go here to view his example's plain javascript. The primary thing I edited was commenting out where he hides the custom handles with css when there is a null selection.
if (s == null) {
//handle.attr("display", "none");
//circle.classed("active", false);
}
http://plnkr.co/edit/tRyhlJ
https://bl.ocks.org/mbostock/4349545
If you look in the plunkr example, you can see that if you click and let go the brush is still a perfect sphere, with the line where you clicked. Instead if disappearing, I want the brush handles to stay... and not cause errors when you click to expand the handles.
The error I am currently getting, and can't seem to find a workaround, is this:
How do I gracefully ignore this internal d3 error and continue on letting my selection expand?
I've tried all sorts of things like turning pointer-events off when it's in this state, and manually unhiding/resizing the default handles, to no avail. Every time, when I click the handles I get this error.
As far as I can tell, there's no "clean" way to deal with it. The reason is that a single click defines a range whose size is 0, which the brush considers an empty range (see source) and so it purposely nullifies the selection (see source).
That all means that unless you create your own version of d3-brush to do what you want, there's no way to have an empty selection that's not null nor a way to render a brush for an empty selection.
There's one workaround I can think of: when you detect an empty selection (where s == null) use brush.move to set the selection to something. That something would have to be a range whose size is not 0 (because if you make it 0 then d3, again, would consider that an empty selection and nullify it). To make a non-zero selection that looks like a zero-sized selection (which it has to be, because it's being defined by a single click event) you'd have to make it a tiny selection, eg [123, 123.0001]. Instead of 123, you need to know the mouse position of the click, which you can get using d3.mouse. Putting it all together, it looks like this:
if (s == null) {
var mousex = d3.mouse(this)[0]
gBrush.call(brush.move, [mousex, mousex+.001]);
}
I'm new to D3 JS.
I spent some time to learn D3 js and build a sort of time table (I'm not sure this is the term) and to animate it.
Now I found a strange behavior on exit transition, when I try to remove the last element in a row the animation goes perfectly but when the element is not the last one, D3 removes it without animation.
It's hard to understand my problem, it's really easier to watch it! :)
I created a working fiddle here: http://jsfiddle.net/fLBq4/5/
Click 'Draw' to build the graph (data are loaded from an external js that create the var demodata)
Then click the second button and watch the last element of Sunday. The transition works correctly.
Then click the last button. You'll see that the first element of Friday is removed without the transition!
Now... I'm really surprised because the code for removing elements it's the same for both executions:
frames.exit()
.transition()
.duration(500)
.attr('width',0)
.remove();
Moreover, I implemented the listeners for 'start' and 'end' transition events (you will not find them in the fiddle). These events are fired correctly, i.e. the 'end' event is timed correctly 500 msec after start.
Why won't D3 animate all the elements in the same way, in my case?
What's happening is that you're getting caught out by D3's data matching. The bar that disappears suddenly isn't in the exit selection, it's in the update selection. The second bar is in the exit selection and disappears gradually, but the first bar is moved to its position instantaneously, so you can't actually see that. I've added a transition to the update selection so you can see what's happening here.
The reason that this is happening is that D3 matches data and DOM elements by index by default. That is, the first data element corresponds to the first DOM element in the selection and so on. In your particular case, you're removing an element from the array, so the last DOM element ends up being not matched and becomes part of the exit selection. The other elements however change their position (as new data is matched to them).
To fix, simply provide a function that tells D3 how to match data and DOM elements, e.g.
var frames = groups.selectAll('.frame')
.data(function(d){return d;}, function(d) { return d.start; });
Complete demo here.
I've have found some weirdness with Maps V3 in jQuery UI tabs.
A common issue, when a map is initialized in a hidden tab, it gets confused and doesn't know how big to make the map.
The solution for this is:
$('#tabs').tabs({show: function(e, ui) {if (ui.index == 5) {google.maps.event.trigger(map, "resize"); } }});
That works fine. That maps resizes visually in the tab.
The next issue, perhaps less commonly, is that I want to load markers based on the latLng of my site's users. When I try to load them via the init function, the getBounds appear to be the same... I assume it's the same issue. The map is confused about the load size.
Like the trigger for resize, is there a way to trigger a function and pass the map DOM info after it has been initialized? I am so new to this!