Есть часть кода на Delphi:
chart:=sheet.chartobjects.add(270,205,397,220);
chart.chart.charttype:=4;
chart.Chart.SetSourceData(Sheet.Range['A3:B34']);
который выводит в Excel график (диаграмму):
Мне нужен другой тип графика, через макрос (excel-vba) он выглядит так:
ActiveSheet.Shapes.AddChart2(332, xlLineMarkersStacked).Select
Вопрос: какой код на Delphi должен выполняться вместо этой строчки, чтоб он вывел
мне вот такой тип графика:
Ответы
Ответ 1
Вам достаточно заменить
chart.chart.charttype:=4;
на
chart.chart.charttype:=66; // xlLineMarkersStacked = 66
чтобы сменить тип диаграммы на нужный.
Если часто приходится использовать построение диаграмм в Excel внутри Delphi-программ,
стоит завести модуль, где будут перечислены основные константы. Вот все константы энумерации
для диаграмм Excel, скопируйте их в новый юнит, оформите как const и вам проще будет
транслировать макросы Excel в код Delphi.
Я передаю табличные данные из html в excel с помощью tableExport.
Выглядит это так:
doExport = function () {
_this.$el.tableExport($.extend({}, _this.options.exportOptions, {
type: type, // выбранный тип экспорта
}));
};
Получается файл с дефолтным именем tableExport.xls, который создаётся в дефолтной
папке для загрузок, записывается, сохраняется и закрывается.
Понадобилось добавить после таблицы график. tableExport для этого не предназначен.
Итого вопрос - как после выполнения tableExport мне обратиться к этому файлу? Его имя
остаётся дефолтным - но может добавляться порядковый номер. То есть имя я точно не
буду знать. Понимаю, что есть способ как-то запомнить путь и имя файла, но куда копать?
Ответы
Ответ 1
Вы не можете с помощью JS обратиться к файлу из файловой системы пользователя. Вам
нужно как то добавить ваши данные до сохранения файла. Вероятно для этого придется
разбираться в коде библиотеки и возможно модифицировать ее. Думаю лучшим решением будет
сохранить график в отдельном файле.
Делаю отчет в Excel-е с диаграммой на delphi.
У него фиксированная позиция:
chart := sheet.chartobjects.add(270,205,397,220);
Как сделать, чтоб диаграмма началась с нужной мне колонки?
Ответы
Ответ 1
Амирхон, чтобы вам проще было работать в Delphi (да и в других программах) с офисными
продуктами, постарайтесь разобраться в иерархии объектов. Как только вы разберетесь,
многие вещи сразу станут понятны. Я не могу посоветовать вам какую-то конкретную книгу,
поскольку сам когда-то разбирался методом научного тыка, но, возможно, кто-то другой
посоветует.
Хороший тон - использовать инструменты разработчика, они очень сильно помогают. В
офисе 2003 и ранее - это запись макросов с последующим их анализом, позже - то же самое
+ использование вкладки Разработчик (её необходимо предварительно включить в настройках).
Итак, Chart входит в семейство Shapes, которое принадлежит Sheet (принадлежащий,
в свою очередь, Book). Фишка в том, что к одному и тому же объекту можно обратиться
по-разному, OLE это позволяет. И тот факт, что у каждого программиста есть свои любимые
подходы, порождает массу непонятного. Но стоит прочувствовать иерархию, всё становится
на свои места.
У меня подход несколько иной, чем у вас. Я вначале определяю объект, а уже затем
задаю его свойства. На мой взгляд, так удобнее. Допустим, мы создали лист в Excel и
заполнили несколько ячеек для диаграммы данными:
Excel:=CreateOleObject('Excel.Application');
Excel.Visible:=True;
Book:=Excel.Workbooks.Add; // мне не жалко переменных для каждого объекта
Sheet:=Book.Worksheets[1]; // поскольку дальше проще работать
for i := 1 to 5 do
begin
Sheet.Cells[i, 1].FormulaR1C1:=i;
Sheet.Cells[i, 2].FormulaR1C1:=cos(i); // пусть будет косинус
end;
Все переменные здесь - OleVariant. Теперь создаем диаграмму:
ch:=Sheet.Shapes.AddChart;
Я специально выбрал для диаграммы переменную ch, а не chart, поскольку у диаграммы
несколько уровней свойств, в том числе и свойство Chart, многие начинающие путаются
в этом. Далее:
Ch.Name := 'График 1'; // обзовём диаграмму так
Ch.Chart.ChartType := xlLineMarkers; // тип диаграммы
Типы диаграмм мы обсуждали в Как правильно задать тип chart-а c помощью delphi. Либо
вы поступаете так, как я порекомендовал, либо можно использовать один из встроенных
модулей Delphi, например, ExcelXP или Excel2010, в которых эти константы определены.
Отмечу, что, чтобы изменить некоторые свойства диаграммы, например, название, я обращаюсь
непосредственно к переменной ch, а вот для работы с графиком я должен уже обращаться
к свойству Chart.
Ну, а теперь вернемся к нашему основному вопросу. Изменим координаты. Координаты
- свойства непосредственно диаграммы, это Left и Top. Задаются они в пикселях, причём
только для левого верхнего угла, а правый нижний уже определяется Width и Height (шириной
и высотой, причём, как правило, в долях экрана, а не в пикселях). Так как вам нужно
поместить диаграмму не в координатах пикселей, а в координатах ячеек, то нам нужно
вначале их рассчитать:
Ch.Left := Sheet.Range['A1:D1', EmptyParam].Width + 2;
Ch.Top := Sheet.Range['E1:E3', EmptyParam].Width + 2;
Я поместил, таким образом, свою диаграмму в ячейку E4, сместив её край на 2 пикселя.
Кстати, тот же самый результат можно получить по-другому:
Ch.Left := Sheet.Range['E4', EmptyParam].Left + 2;
Ch.Top := Sheet.Range['E4', EmptyParam].Top + 2;
Что ещё раз демонстрирует разные подходы.
Как создать график в WPF с координатами x,y и возможностью создания разных кривых?
Я пытался использовать Chart, но я не знаю как создать элемент Chart в коде.
Ответы
Ответ 1
Существует множество библиотек которые позволяют строить различные типы графиков
в WPF - проектах.
Несколько из них:
OxyPlot
LiveCharts
Выбор языка программирования (F#) для небольших примеров по использованию оставил
на свое усмотрение, так как не было указано явно какой предпочтительнее.
1.
OxyPlot поддерживает различные типы графиков, среди которых LineSeries, PieSeries,
BarSeries,...Из недостатков - слабая документация. Проблема существует уже давно, но,
к сожалению, работа в этом направлении не ведется. Таким образом найти ответ на интересующий
вопрос довольно сложно. Но так как библиотека уже завоевала определенную популярность,
то по ней имеется не мало вопросов (с ответами :) ) на английском SO, которые должны
помочь.
Чтобы ответ был более полным рассмотрим простой пример - построение графиков двух
функций sin и cos с возможностью отмечать галочками отображать обе линии, любую из
них или не показывать вовсе.
OxyPlot:
xmlns:oxy="clr-namespace:OxyPlot.Wpf;assembly=OxyPlot.Wpf"
XAML:
MainViewModel:
open OxyPlot
...
type MainViewModel() =
let x = -5.0
let y = 5.0
let range = [x..0.1..y]
let sindata =
range
|> List.map(fun value -> value, sin value)
|> List.map(DataPoint)
let cosdata =
range
|> List.map(fun value -> value, cos value)
|> List.map(DataPoint)
member __.SPoints = sindata
member __.CPoints = cosdata
2.
LiveCharts стремительно набирающая популярность библиотека. Из основных преимуществ
- документация. Недостатки - баги, которые, впрочем, довольно успешно исправляются.
Помимо стандартного набора типов графиков поддерживает 180° Gauge, Bubble series. Кроме
этого планируется добавить поддержку некоторых других типов - Countour Maps. Если документации
будет недостаточно, чтобы найти ответ на вопрос можно написать в чате (gitter).
Пример практически аналогичный с предыдущим, только без возможности влиять на отображаемые
графики. Связано с тем, что подобным привязка к элементам управления на данный момент
не работает. Поэтому нужно создавать свойство в VM и связывать с ним.
XAML:
xmlns:lvc="clr-namespace:LiveCharts.Wpf;assembly=LiveCharts.Wpf"
VM:
open LiveCharts
open LiveCharts.Configurations
type LiveChartsVM() =
let x = -5.0
let y = 5.0
let range = [x..0.1..y]
let sindata =
range
|> List.map(fun value -> value, sin value)
|> ChartValues
let cosdata =
range
|> List.map(fun value -> value, cos value)
|> ChartValues
do
let mapper = Mappers.Xy().X(fst).Y(snd)
LiveCharts.Charting.For(mapper, SeriesOrientation.Horizontal)
member __.SPoints = sindata
member __.CPoints = cosdata
Хочу нарисовать график, нашел бесплатную библиотеку AChartEngine. Однако демо который
там имеется реализован на java. Пытался импортировать этот проект в Android Studio,
но ругается на отсутствие gradle в проекте.
В примере встречается создание объектов интерфейса(что нельзя сделать в c#) и не
получается разобраться как происходит отрисовка самого графика. Понимаю, что рисуется
это в LinearLayout, но как это делается...
Написал Main.axml по этому примеру
Main.axml
Вроде создал объект класса необходимого мне графика и добавил туда серию с точками,
но как это нарисовать?
MainActivity.cs
using Android.App;
using Android.Widget;
using Android.OS;
using AChartEngine.Charts;
using AChartEngine.Models;
using AChartEngine.Renderers;
using AChartEngine;
namespace ChartsAChartEngine
{
[Activity(Label = "ChartsAChartEngine", MainLauncher = true)]
public class MainActivity : Activity
{
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.Main);
TextView topText = FindViewById(Resource.Id.topText);
EditText editTextX = FindViewById(Resource.Id.xValue);
EditText editTextY = FindViewById(Resource.Id.yValue);
Button translateButton = FindViewById
Ответы
Ответ 1
Давно пользовался этой библиотекой, насколько помню, следующий шаг должен быть:
var view = new GraphicalView(context, lineChart)
Т.е. создать view на базе графика и как обычно вставить его в разметку.
Добавлено
А это не этот же компонент, уже адаптированный под Xamarin?
https://components.xamarin.com/view/achartengine
Как это сделать?
Мне нужен мульти-сектор в пончике с закругленными концами и добавить тень самому
пончику.
Хочу использовать chart.js, но можно и другие варианты без jquery.
Ответы
Ответ 1
Есть вариант сделать с помощью SVG. На последней секции(#part4) в качестве примера
заливка градиентом.
UPD: Добавил еще вариант без CSS
.circle {
height: 200px;
width: 200px;
fill: none;
stroke-width: 5;
stroke-linecap: round;
position: absolute;
left:0;
top:0;
}
#part1 {
stroke: #172bc3;
z-index: 3;
transform: rotate(20deg);
}
#part2 {
stroke: #f21d92;
z-index: 2;
transform: rotate(-70deg);
}
#part3 {
stroke: #9631eb;
z-index: 5;
transform: rotate(-160deg);
}
#part4 {
stroke: url(#gradient);
z-index: 4;
transform: rotate(-250deg);
}
#shadow {
stroke: white;
z-index:1;
stroke-width: 2;
}
Ответ 2
Решается вопрос с помощью SVG. Применил circles, вместо paths( почему бы и нет?:D ):
D3.js вариация, похожий ответ.
Обратите внимание на заморочку, что бы перекрытие было не как в макете дизайнера,
а правильно, в одну сторону.
Похожие перекрытия в ответе господина @Pavel с решением на Chart.js
let overlap = 0.2,
duration = 1000,
count = 2 + Math.floor(Math.random() * 5),
data = Array(count).fill(0).map((d, i) => ({value: 1 + 5*Math.random()}));
let svg = d3.select('#donut g');
let pie = d3.pie().sort(null); //
let arc = d3.arc().innerRadius(70).outerRadius(85);
let d = pie(data.map(d => d.value));
d.forEach(d => d.endAngle += overlap);
drawArcs("right");
drawArcs("left");
function drawArcs(className) {
let right = className === "right";
svg.selectAll("path." + className).data(d).enter()
.append("path").classed(className, true)
.attr("fill", (d, i) => `hsl(${i*77},75%,45%)`)
.transition().duration(duration).attrTween('d', tween)
function tween(d) {
let center = (d.startAngle+d.endAngle)/2;
let interpolator = d3.interpolate(center, right ? d.endAngle : d.startAngle)
return t => {
let v = interpolator(t);
d.startAngle = right ? center - overlap*t : v;
d.endAngle = right ? v : center + overlap*t;
return arc.cornerRadius(10*t)(d);
}
}
}
UPD: по просьбам трудящихся добавил сделал вариант с анимацией заполнения в одну
сторону.
let overlap = 0.2,
duration = 1000,
count = 2 + Math.floor(Math.random() * 5),
data = Array(count).fill(0).map((d, i) => ({value: 1 + 5*Math.random()}));
let svg = d3.select('#donut g');
let pie = d3.pie().sort(null); //
let arc = d3.arc().innerRadius(70).outerRadius(85);
let d = pie(data.map(d => d.value));
d.forEach(d => d.endAngle += overlap);
drawArcs("right");
drawArcs("left");
function drawArcs(className) {
let right = className === "right";
svg.selectAll("path." + className).data(d).enter()
.append("path").classed(className, true)
.attr("fill", (d, i) => `hsl(${i*77},75%,45%)`)
.transition().duration(duration).attrTween('d', tween)
function tween(d) {
let center = d.startAngle;
let interpolator = d3.interpolate(center, right ? d.endAngle : d.startAngle)
return t => {
let v = interpolator(t);
d.startAngle = right ? center : v;
d.endAngle = right ? v : center + overlap*t;
return arc.cornerRadius(10*t)(d);
}
}
}
Здравствуйте, необходимо передать массив с сервера(nodejs) на клиент, чтобы потом по нему строить график. Я передаю массив, но на клиенте пишет, что это не массив. Где ошибка в коде? Сервер: app.post('/averageTimeInQueue', function(req, res, next) {
var data = [
[2012, 1000, 400, 232],
[2005, 1170, 460, 421],
[2006, 660, 1120, 4324]
];
res.send(data);
});
Клиент: google.charts.load('current', {'packages':['line']});
google.charts.setOnLoadCallback(drawChart); function drawChart() {
var data = new google.visualization.DataTable();
data.addColumn('number', 'Day');
data.addColumn('number', 'Guardians of the Galaxy');
data.addColumn('number', 'The Avengers');
data.addColumn('number', 'Transformers: Age of Extinction'); data.addRows(averageTimeInQueue);
var options = {
chart: {
title: 'Box Office Earnings in First Two Weeks of Opening',
subtitle: 'in millions of dollars (USD)'
},
width: 600,
height: 350,
axes: {
x: {
0: {side: 'top'}
}
}
}; var chart = new google.charts.Line(document.getElementById('chart_div2')); chart.draw(data, options);
} function averageTimeInQueue(){
var jsonData = $.ajax({
url: "/averageTimeInQueue",
async: false,
type: 'POST'
}).responseText;
return jsonData;
}
Ответ
Конечно не массив, вы же его как текст отправляете. Сериализируйте его в json на сервере res.send(JSON.stringify(data)); на клиенте data.addRows(averageTimeInQueue()) вместо data.addRows(averageTimeInQueue) и var jsonData = $.ajax({
url: "/averageTimeInQueue",
dataType: "json",
async: false,
type: 'POST'
}).responseText;
return JSON.parse(jsonData);
Проблема следующего характера: есть Chart с большим набором точек, необходимо изобразить график. Высота Chart может быть аж 2-3 тысячи единиц. После загрузки точек в Chart добавляется по оси OY дополнительное поле. Я выставила в ChartArea свойство position как Height = 100 и Widht = 100, т.е. тем самым, график должен был растянуться на весь Chart, но поле почему-то все-равно есть. Из Chart мне необходимо было убрать видимость линеек осей и начало координат должно начинаться с левого верхнего угла (сделала AxisY.IsReversed = true). Так вот как убрать эти "сопли"?
Ответ
Методом научного тыка нашёл свойство у Axis'ов которое отвечает за отображение черточек к которым стыкуются цифры... В общем если выставить его в false то черточки пропадут: area.AxisY.MajorTickMark.Enabled = false;
area.AxisX.MajorTickMark.Enabled = false;
А чтобы убрать отступы по краям необходимо сделать следующее (по аналогии с Position):
area.InnerPlotPosition= new ElementPosition(0, 0, 100, 100);
Здравствуйте, Поставил в форме график синуса и так же пытаюсь показать разрешенную площадь значений.
Надо вывести на графике что-то похожее на серый прямоугольник (см. рис. 1) . Он не обязательно начинается с Y=0
Точки синуса приходят во время работы програмы одна за одной.
Пытался использовать StripLine - очень похоже на то, что мне надо (см. рис. 2, желтоватая и голубоватая зоны), но у меня не получается определить его начальное значение как точку, в которой две координаты не равны 0. Так же его продолжительность должна быть не бесконечной. (В отличие от рис. 2) рис. 1
рис. 2
Ответ
Благодаря одному из участников английского Stackoverflow, вот он ответ (точнее, работающий код) : void drawAllowedArea(Point startPoint, Point endPoint, PaintEventArgs e)
{
var l = (float)chart1.ChartAreas[0].AxisX.ValueToPixelPosition(startPoint.X);
var t = (float)chart1.ChartAreas[0].AxisY.ValueToPixelPosition(endPoint.Y);
var r = (float)chart1.ChartAreas[0].AxisX.ValueToPixelPosition(endPoint.X);
var b = (float)chart1.ChartAreas[0].AxisY.ValueToPixelPosition(startPoint.Y);
var rect = RectangleF.FromLTRB(l, t, r, b);
using (var br = new SolidBrush(Color.FromArgb(100, Color.Blue)))
{
e.Graphics.FillRectangle(br, rect.X, rect.Y, rect.Width, rect.Height);
}
e.Graphics.DrawRectangle(Pens.Red, rect.X, rect.Y, rect.Width, rect.Height);
} //and use this function in chart1_Paint event:
void chart1_Paint(object sender, PaintEventArgs e)
{
drawAllowedArea(new Point(20, 50000), new Point(40, 100000), e);
}
Ссылка на мой вопрос: https://stackoverflow.com/questions/41263864/winforms-chart-draw-a-allowed-area-on-line-chart Спасибо всем, кто думал как мне помочь! :)
Есть часть кода на Delphi: chart:=sheet.chartobjects.add(270,205,397,220);
chart.chart.charttype:=4;
chart.Chart.SetSourceData(Sheet.Range['A3:B34']);
который выводит в Excel график (диаграмму):
Мне нужен другой тип графика, через макрос (excel-vba) он выглядит так: ActiveSheet.Shapes.AddChart2(332, xlLineMarkersStacked).Select
Вопрос: какой код на Delphi должен выполняться вместо этой строчки, чтоб он вывел мне вот такой тип графика:
Ответ
Вам достаточно заменить chart.chart.charttype:=4;
на chart.chart.charttype:=66; // xlLineMarkersStacked = 66
чтобы сменить тип диаграммы на нужный. Если часто приходится использовать построение диаграмм в Excel внутри Delphi-программ, стоит завести модуль, где будут перечислены основные константы. Вот все константы энумерации для диаграмм Excel, скопируйте их в новый юнит, оформите как const и вам проще будет транслировать макросы Excel в код Delphi.
Я передаю табличные данные из html в excel с помощью tableExport.
Выглядит это так: doExport = function () {
_this.$el.tableExport($.extend({}, _this.options.exportOptions, {
type: type, // выбранный тип экспорта
}));
};
Получается файл с дефолтным именем tableExport.xls, который создаётся в дефолтной папке для загрузок, записывается, сохраняется и закрывается. Понадобилось добавить после таблицы график. tableExport для этого не предназначен. Итого вопрос - как после выполнения tableExport мне обратиться к этому файлу? Его имя остаётся дефолтным - но может добавляться порядковый номер. То есть имя я точно не буду знать. Понимаю, что есть способ как-то запомнить путь и имя файла, но куда копать?
Ответ
Вы не можете с помощью JS обратиться к файлу из файловой системы пользователя. Вам нужно как то добавить ваши данные до сохранения файла. Вероятно для этого придется разбираться в коде библиотеки и возможно модифицировать ее. Думаю лучшим решением будет сохранить график в отдельном файле.
Делаю отчет в Excel-е с диаграммой на delphi.
У него фиксированная позиция: chart := sheet.chartobjects.add(270,205,397,220);
Как сделать, чтоб диаграмма началась с нужной мне колонки?
Ответ
Амирхон, чтобы вам проще было работать в Delphi (да и в других программах) с офисными продуктами, постарайтесь разобраться в иерархии объектов. Как только вы разберетесь, многие вещи сразу станут понятны. Я не могу посоветовать вам какую-то конкретную книгу, поскольку сам когда-то разбирался методом научного тыка, но, возможно, кто-то другой посоветует. Хороший тон - использовать инструменты разработчика, они очень сильно помогают. В офисе 2003 и ранее - это запись макросов с последующим их анализом, позже - то же самое + использование вкладки Разработчик (её необходимо предварительно включить в настройках). Итак, Chart входит в семейство Shapes, которое принадлежит Sheet (принадлежащий, в свою очередь, Book). Фишка в том, что к одному и тому же объекту можно обратиться по-разному, OLE это позволяет. И тот факт, что у каждого программиста есть свои любимые подходы, порождает массу непонятного. Но стоит прочувствовать иерархию, всё становится на свои места. У меня подход несколько иной, чем у вас. Я вначале определяю объект, а уже затем задаю его свойства. На мой взгляд, так удобнее. Допустим, мы создали лист в Excel и заполнили несколько ячеек для диаграммы данными: Excel:=CreateOleObject('Excel.Application');
Excel.Visible:=True;
Book:=Excel.Workbooks.Add; // мне не жалко переменных для каждого объекта
Sheet:=Book.Worksheets[1]; // поскольку дальше проще работать
for i := 1 to 5 do
begin
Sheet.Cells[i, 1].FormulaR1C1:=i;
Sheet.Cells[i, 2].FormulaR1C1:=cos(i); // пусть будет косинус
end;
Все переменные здесь - OleVariant. Теперь создаем диаграмму: ch:=Sheet.Shapes.AddChart;
Я специально выбрал для диаграммы переменную ch, а не chart, поскольку у диаграммы несколько уровней свойств, в том числе и свойство Chart, многие начинающие путаются в этом. Далее: Ch.Name := 'График 1'; // обзовём диаграмму так
Ch.Chart.ChartType := xlLineMarkers; // тип диаграммы
Типы диаграмм мы обсуждали в Как правильно задать тип chart-а c помощью delphi. Либо вы поступаете так, как я порекомендовал, либо можно использовать один из встроенных модулей Delphi, например, ExcelXP или Excel2010, в которых эти константы определены. Отмечу, что, чтобы изменить некоторые свойства диаграммы, например, название, я обращаюсь непосредственно к переменной ch, а вот для работы с графиком я должен уже обращаться к свойству Chart Ну, а теперь вернемся к нашему основному вопросу. Изменим координаты. Координаты - свойства непосредственно диаграммы, это Left и Top. Задаются они в пикселях, причём только для левого верхнего угла, а правый нижний уже определяется Width и Height (шириной и высотой, причём, как правило, в долях экрана, а не в пикселях). Так как вам нужно поместить диаграмму не в координатах пикселей, а в координатах ячеек, то нам нужно вначале их рассчитать: Ch.Left := Sheet.Range['A1:D1', EmptyParam].Width + 2;
Ch.Top := Sheet.Range['E1:E3', EmptyParam].Width + 2;
Я поместил, таким образом, свою диаграмму в ячейку E4, сместив её край на 2 пикселя. Кстати, тот же самый результат можно получить по-другому: Ch.Left := Sheet.Range['E4', EmptyParam].Left + 2;
Ch.Top := Sheet.Range['E4', EmptyParam].Top + 2;
Что ещё раз демонстрирует разные подходы.