I need to present a chart with a Y Axis in MS Chart so that instead of showing 1000, 1500, 2000, it shows as 1K, 1.5K and 2K etc.
Can this be done, and can anyone point me in the right direction?
You could implement the customize event of mschart.
private void chart1_Customize(object sender, EventArgs e)
{
foreach (var yAxisLabel in chart1.ChartAreas[0].AxisY.CustomLabels)
{
label.Text = double.Parse(label.Text)/1000 + "K";
}
}
This event is fired just before the chart image is drawn. This event should be used to customize the chart picture.
Related
I am using the chartjs-plugin-datalabels library to help configure my datalabels in a chartjs.
The problem is the data labels keep overlapping on a simple line chart where the values between the two data sets for a given X axis are close.
Is there anyway to use the align function to dynamically change whether the data label is on the top or bottom?
Psedocode
if datasetOne of a given X axis item is > datasetTwo of a given X axis item then make the datalabel for dataSetOne[X] === Top else make it bottom
if datasetTwo of a given X axis item is > datasetOne of a given X axis item then make the datalabel for dataSetTwo[X] === Bottom else make it top
Currently I was doing something like this. But the overlapping issue happens again if my second dataset index has lower values than the first data set
align: function ( context: Context) {
if (context.datasetIndex === 0) {
return 'top';
} else {
return 'bottom';
}
}
I have a long array with data that I slice with Javascript in order to display data of different date ranges in my chart. This way the backend only needs to get the data once, and I can the just slice it on the client side.
// All data
var allLabels = [
// data here
];
var allData = [
// data here
];
Then I do:
var labelsCount = allLabels.length;
var dataCount = allData.length;
var updatedLabels;
var updatedData;
if($date_range === 'last_7_days')
{
updatedLabels = allLabels.slice(labelsCount - 7);
updatedData = allData.slice(labelsCount - 7);
}
if($date_range === 'last_30_days')
{
updatedLabels = allLabels.slice(labelsCount - 30);
updatedData = allData.slice(labelsCount - 30);
}
scoreChart.data.labels = updatedLabels;
scoreChart.data.datasets[0].data = updatedData;
scoreChart.update({
duration: 1000,
easing: 'easeInOutExpo'
});
This all works as expected. When switching from 30 to 7 days the points on the right of the 7 days disappear, and the graph scales and grows nicely to the new 7 days x-axis.
The other way around, when you have the graph of 7 days and then switch to 30, produces an ugly visual effect where the first point of the graph sticks to the side, overlaps the new data points and then animates.
After the animation the graph looks as expected, it's just the animation that's ugly. It's a little tricky to explain so hopefully the screenshots help. Green arrows indicate the animation direction. I've set the animation duration to 10s so I can take this screenshot, the red circle highlights the point that starts on the right of the graph and then animates to the left.
I've also tried adding this:
scoreChart.data.labels.pop();
scoreChart.data.datasets[0].data.pop();
scoreChart.update();
and this:
scoreChart.data.labels = [];
scoreChart.data.datasets[0].data = [];
scoreChart.update();
Before the line scoreChart.data.labels = updatedLabels; but that gives the same result.
Another thing I can do is only update the labels. The result is that the chart just zooms on the timeline when changing date ranges, without the nice animation as they have in the example.
You could try to first remove all labels and the data when switching to 'last_30_days'.
if($date_range === 'last_30_days')
{
scoreChart.data.labels = [];
scoreChart.data.datasets[0].data = [];
scoreChart.update({
duration: 500,
easing: 'easeInOutExpo'
});
updatedLabels = allLabels.slice(labelsCount - 30);
updatedData = allData.slice(labelsCount - 30);
}
I wanted to have an online monitoring system that could tell where the shape is currently, but am getting very weird coordinates of the item, also the dimensions of it get higher by 1 each time I create new one and drag it.
Initial position (map size is 751 by 751, checked by outputting to qDebug(), scene bound to yellow space) :
Dragging it to the left top corner.
As you can see in the beginning it was on (200;200), but after dragging it is on (-201;-196). After deleting it and creating new shape on the same position with the same properties, new shape can't be seen because it is outside of the map, which suggests that edits don't show correct data.
Here is the code of updating the edits:
void CallableGraphicsRectItem::mouseReleaseEvent(QGraphicsSceneMouseEvent* event)
{
QGraphicsRectItem::mouseReleaseEvent(event);
ptr->updateEdits(this);
}
Here is what I managed to cut down into updateEdits():
void MainWindow::updateEdits(QAbstractGraphicsShapeItem* item)
{
//stuff not related to scene
auto posReal = item->scenePos();
auto pos = posReal.toPoint();
//create QString from coordinates
QString coordinate;
coordinate.setNum(pos.x());
ui->leftXEdit->setText(coordinate);
coordinate.setNum(pos.y());
ui->upperYEdit->setText(coordinate);
//get width and height for rect, radius for circle
auto boundingRectReal = item->sceneBoundingRect();
auto boundingRect = boundingRectReal.toRect();
ui->widthEdit->setText(QString::number(boundingRect.width()));
//disables height edit for circles, not really relevant
if (!items[currentShapeIndex].isRect)
{
ui->heightEdit->setDisabled(true);
}
else
{
ui->heightEdit->setDisabled(false);
ui->heightEdit->setText(QString::number(boundingRect.height()));
}
}
Here is how I anchor the QGraphicsScene to the left top corner of the yellow area:
scene->setSceneRect(0, 0, mapSize.width() - 20, mapSize.height() - 20);
ui->graphicsView->setScene(scene);
How can I report the right data to the edits?
You're better off overriding the itemChange method and using the ItemPositionHasChanged notification. You have to set the ItemSendsGeometryChanges flag on the item so that it receives these notifications.
I'm not sure that your item's final position has been set when you're still in the mouseReleaseEvent method. Tracking it in itemChange will ensure that the data is valid, and this kind of thing is what it's for.
Also, note that "pos" is in the item's parent coordinates, and "boundingRect" is in the item's coordinate space. You should use "scenePos" and "sceneBoundingRect" if you want to be sure you're using scene coordinates. If the item doesn't have a parent, then "pos" and "scenePos" will return the same values, but "boundingRect" and "sceneBoundingRect" will generally differ.
I have a problem in my app. I draw lines and points with the help of mouse and I need to detect wether I intersect the line or get the point. And with points it works well, but with lines it doesn't work. I add lines to the AnchorPane. Then I spotted, when cursor is situated inside of 3 lines (Triange) - it's always intersect. Here is my example:
ArrayList<Line> lines = new ArrayList<Line>();
Line l1 = new Line(10, 150, 50, 10);
Line l2 = new Line(10, 150, 100, 150);
Line l3 = new Line(100, 150, 50, 10);
lines.add(l1);
lines.add(l2);
lines.add(l3);
for (int i=0; i<lines.size();++i) {
if (lines.get(i).intersects(48, 48, 4, 4)) {
System.out.println("found a bug!" + " line #"+i);
}
}
If someone knew the answer - it would be great!
I'll imagine that your question is: how to pick a line by clicking anywhere within two pixels of the line?
In the sample output below, the user has just clicked very close to the right line of the triangle, causing that line to be highlighted.
The code below works by testing each shape in the pane and seeing if the shape intersects a rectangular box 2 pixels on either side of a mouse press location. The solution is very similar to that used in solving: Checking Collision of Shapes with JavaFX.
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.*;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
import javafx.scene.shape.*;
import javafx.stage.Stage;
public class LinePicker extends Application {
public static void main(String[] args) { Application.launch(LinePicker.class); }
#Override public void start(final Stage stage) throws Exception {
final Pane pane = new Pane();
pane.getChildren().setAll(
new Line( 10, 150, 50, 10),
new Line( 10, 150, 100, 150),
new Line(100, 150, 50, 10)
);
pane.setPrefSize(
200, 200
);
Scene scene = new Scene(addPickHandler(pane));
stage.setScene(scene);
stage.show();
}
private Pane addPickHandler(final Pane pane) {
pane.setOnMousePressed(new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent event) {
final Rectangle hotspot = new Rectangle(
event.getX() - 2,
event.getY() - 2,
4,
4
);
for (Node child : pane.getChildren()) {
if (child instanceof Shape) {
final Shape shape = (Shape) child;
Shape intersect = Shape.intersect(shape, hotspot);
if (intersect.getBoundsInLocal().getWidth() != -1) {
shape.setStyle("-fx-stroke: red;");
} else {
shape.setStyle("-fx-stroke: black;");
}
}
}
}
});
return pane;
}
}
Comment
It is a little confusing to me why shved90 was unable to get the above solution to function for him in his application.
Shev comments that "In Line, as I understand, it checks all the bounds and if the line would be with some angle its bound would be like rectangle and as a result I check whether I spotted on it (not only on line and its nearest points). But I needed geometry operation."
However the solution presented here is a geometry based solution in that the click hotspot must intersect the actual line and not the rectangular bounds encompassing the line. This is because the Shape.intersect method used in the solution works on the intersection of the actual shapes involved and not the bounds of the shapes involved.
Note however that in the shved90's original question, he uses the Node intersects method rather than the Shape.intersect method. The documentation of Node intersects states "The default behavior of this function is simply to check if the given coordinates intersect with the local bounds.", which, from his comment, is obviously not what shved90 wanted.
Shev does note that "I've found Line2d class in javaFx, as like in Java2D, I convert to this line and use it's intersection". I would not recommend using the Line2D class bundled with JavaFX as it is a private com.sun api that is not guaranteed to be present or have a binary backward compatible API in future JavaFX releases.
I am using CSCrollView window for our Application in which i have table drawn in View.
I have derived the CMYclass from CSCrollView, But whenevr i am scrolling the window up and down whatever i have drwan is getting erased. How i can acheive it this... i need to perform same actvity like a Word Pad is doing with images and Text. I want to keep scroll the View Vertically. Till the page ends.
Here is the code snippet:-
void CMyView::OnInitialUpdate()
{
CSize sizeTotal;
// TODO: calculate the total size of this view
sizeTotal.cx = 450;
sizeTotal.cy = 700;
SetScrollSizes(MM_TEXT, sizeTotal);
}
void CMyView::OnDraw(CDC* pDC)
{
for(int i = 1;i<50;++i)
{
AddRow(pDC);
TopPos = Height+TopPos;// ![Outpu Window Image][1]
nCountRow++;
}
}
it is only drawing 18 rows, but when iam scrolling down above drawn content is no more and also there is nothing coming up in scrolled area.
Is there anything more need to add?
Thanking for help
Regards,
Mukesh
Long time I have used CScrollView. Here is my hint: Try mapping modes other than MM_TEXT. Also look up other functions in CScrollView. I suggest to draw simple stuff first than some complicated rows.