БлогNot. Chart, примеры в Studio

Chart, примеры в Studio

Компонента Chart (диаграмма) в Visual Studio довольно удобна, хотя для начинающих обычно сложно выбрать из множества её возможностей самые актуальные. К тому же, задокументировано-то всё лишь на MSDN.

Приведу несколько примеров, анализ которых поможет Вам быстро освоить основные приёмы работы с графиками и диаграммами в Visual C++. Все коды проверены в сборке Express 2010.

Пример 1. Добавим на форму компоненту Chart и выведем туда данные
//По нажатию кнопки 1 используем заранее добавленную компоненту Chart
using namespace System::Collections::Generic; 
using namespace System::Drawing::Drawing2D;
using namespace System::Drawing;
Dictionary <double, double> f1 = gcnew Dictionary<double, double>();
ArrayList points = gcnew ArrayList();
Pen ^pen = gcnew Pen(System::Drawing::Color::Red); //создаем перо
SolidBrush ^peen = gcnew SolidBrush(System::Drawing::Color::Green); //создаем кисть
double x0=0,xmax=Math::PI,dx=Math::PI/100.; //границы графика по оси x и шаг
f1.Clear();
using namespace System::Windows::Forms::DataVisualization::Charting;
chart1->Series[0]->ChartType = SeriesChartType::Line;
chart1->Series[0]->MarkerStyle = MarkerStyle::Circle;
for (double x = x0; x <= xmax; x += dx) f1.Add((int)(x*100)/100., (Math::Sin(x)));
chart1->Series[0]->Points->DataBindXY(f1.Keys, f1.Values);
chart1->Series[0]->LegendText = L"функция";
chart1->Series[0]->Color = System::Drawing::Color::Green;
chart1->Series[0]->BorderWidth = 2;
Пример 2. Сделаем красивый Chart программно, можно по нажатию новой кнопки на той же форме
using namespace System;
using namespace System::Windows::Forms;
using namespace System::Drawing;
using namespace System::Windows::Forms::DataVisualization::Charting;
//Общие свойства
Chart ^myChart = gcnew Chart();
myChart->Parent = this;
myChart->Left = 10; myChart->Top = 10;
myChart->Width = (this->ClientSize.Width - 10); 
myChart->Height =(this->ClientSize.Height - 20);
myChart->BringToFront(); //или chart1->Visible = false;
ChartArea ^myChartArea = gcnew ChartArea();
//Область в которой будет построен график (их может быть несколько)
myChartArea->Name = "myChartArea";
myChart->ChartAreas->Add(myChartArea);
// График (их может быть несколько)
Series ^mySeries1 = gcnew Series();
mySeries1->ChartType = SeriesChartType::Column;
mySeries1->ChartArea = "myChartArea";
myChart->Series->Add(mySeries1);
// Исходные данные для графика
array <double> ^yval1 = { 10, 6, 4, 6, 3 };
array <System::String ^> ^xval = { "Январь", "Февраль", "Март", "Апрель", "Май" };
mySeries1->Points->DataBindXY(xval, yval1);
//фон градиентом
myChart->BackColor = System::Drawing::Color::MistyRose;
myChart->BackGradientStyle = GradientStyle::DiagonalLeft;
//границы в современном стиле
myChart->BorderSkin->SkinStyle = BorderSkinStyle::Sunken;
myChart->BorderSkin->PageColor = this->BackColor;
//линии сетки покажем разными цветами
myChartArea->AxisX->MajorGrid->LineColor = System::Drawing::SystemColors::ControlDark;
myChartArea->AxisY->MajorGrid->LineColor = System::Drawing::SystemColors::ControlLight;
//добавим второй график
Series ^mySeries2 = gcnew Series();
mySeries2->ChartType = SeriesChartType::Point;
mySeries2->ChartArea = "myChartArea";
myChart->Series->Add(mySeries2);
array <double> ^yval2 = { 4, 7, 3, 5, 5 };
mySeries2->Points->DataBindXY(xval, yval2);

Вот что вышло:

Создаем программно красивый Chart в проекте C++ Windows Forms
Создаем программно красивый Chart в проекте C++ Windows Forms
Пример 3. Вытащим данные из Chart в TextBox
textBox1->Clear();
for each (DataPoint ^p in myChart->Series[0]->Points) { 
 // Пробегаю по всем точкам первой кривой в графике
 textBox1->AppendText("X=" + p->XValue.ToString()+" ");
 for each (double yp in p->YValues) //Y является массивом, поэтому пробегаю по массиву
  textBox1->AppendText("Y=" + yp.ToString());
 textBox1->AppendText(System::Environment::NewLine);
}
Пример 4. Сделаем парсер выражений на основе чужого класса - это можно применить для создания полноценного "графопостроителя"

Более полная версия проекта - по ссылке

Добавим какой-нибудь не очень сложный парсер, например, класс parser.cpp от Chaos Master.

Кинем файлы parser.cpp и parser.h в папку с кодом (Имя_проекта/Имя_проекта, где находится Form1.h). В меню скажем Проект - Существующий элемент и добавим файл .cpp (он д.б. в "Файлы исходного кода" Обозревателя решений)

Код выбранного нами парсера старый, так что нам придётся кое-что переделывать, например, строки char * в String ^ и обратно.

Добавим на форму компоненты textBox1 и label1, напишем демо-код для применения парсера (вызывается по нажатию кнопки):

TParser *parser = new TParser();
try {
 String ^Str=textBox1->Text;
 double x=0; //а можно ведь в цикле взять очередную переменную откуда-то!
 Str=Str->Replace("x",x.ToString());
 using namespace Runtime::InteropServices;
 char *p = (char*) (Marshal::StringToHGlobalAnsi (Str)).ToPointer ();
 parser->Compile(p);
 parser->Evaluate();
 label1->Text= parser->GetResult().ToString();
}
catch(TError error) {
 System::String ^ str1 = gcnew System::String (error.error);
 System::String ^ str2 = gcnew System::String (error.pos.ToString());
 label1->Text= "Error " + str1 +  " at position " + str2;
 return;
}

Возможно, понадобится изменить какие-то ещё мелочи, например, я в файле parser.cpp раскомментарил строку

//#include "stdafx.h"

и изменил в коде exp(1) на exp(1.) - иначе не компилировалось в Studio.

Также ясно, что в начале файла form1.h добавлена строка

#include "parser.h"

Теперь в поле ввода можно писать любые допустимые парсером выражения с переменной x, например, cos(x)+1, текущее значение переменной x из программы подставится в выражение и его результат динамически подсчитается.

На основе показанных кодов легко сделать, например, программу построения графиков с интерпретацией введённой пользователем функции. Скажем, если выражение для функции с аргументом, обозначенным x, вводится в текстовое поле textBox1, код построения графика функции в пределах от 0 до 3.14 с шагом, равным 0.1, будет таким:

TParser *parser = new TParser();
try {
 using namespace Runtime::InteropServices;
 using namespace System::Collections::Generic; 
 using namespace System::Drawing::Drawing2D;
 using namespace System::Drawing;
 using namespace System::Windows::Forms::DataVisualization::Charting;
 Dictionary <double, double> f1 = gcnew Dictionary<double, double>();
 chart1->Series[0]->ChartType = SeriesChartType::Line;
 chart1->Series[0]->MarkerStyle = MarkerStyle::Circle;
 for (double x=0; x<3.14; x+=0.1) {
   String ^Str=textBox1->Text;
   Str=Str->Replace("x",x.ToString());
   Str=Str->Replace(",","."); //Если не та локаль
   //А лучше извлечь через Single::TryParse независимо от локали
   char *p = (char*) (Marshal::StringToHGlobalAnsi (Str)).ToPointer ();
   parser->Compile(p);
   parser->Evaluate();
   f1.Add(x, parser->GetResult());
 }
 chart1->Series[0]->Points->DataBindXY(f1.Keys, f1.Values);
 chart1->Series[0]->LegendText = textBox1->Text;
 chart1->Series[0]->Color = System::Drawing::Color::Green;
 chart1->Series[0]->BorderWidth = 2;
}
catch (TError error) {
 System::String ^ str1 = gcnew System::String (error.error);
 System::String ^ str2 = gcnew System::String (error.pos.ToString());
 MessageBox::Show( "Error " + str1 +  " at position " + str2);
 return;
}

P.S. В более новой Studio 2019 пришлось сделать ^f1 вместо f1 и, соответственно, везде f1-> вместо f1.

Заменить фиксированные пределы и шаг на вводимые из формы - элементарно. Вот что вышло для теста:

Пример построения графика на основе парсера функций
Пример построения графика на основе парсера функций
Пример 5. Применение разрывов

На форму проекта помещена chart1 со свойством Dock = Fill, построение происходит по загрузке формы.

 private: System::Void MyForm_Load(System::Object^ sender, System::EventArgs^ e) {
  using namespace System::Collections::Generic;
  using namespace System::Drawing::Drawing2D;
  using namespace System::Windows::Forms::DataVisualization::Charting;
  Dictionary <double, double> ^f1 = gcnew Dictionary<double, double>();
  chart1->Series[0]->ChartType = SeriesChartType::Line;
  chart1->Series[0]->MarkerStyle = MarkerStyle::Circle;
  
  chart1->ChartAreas[0]->AxisY->ScaleBreakStyle->Enabled = true;
  chart1->ChartAreas[0]->AxisY->ScaleBreakStyle->CollapsibleSpaceThreshold = 25;
  chart1->ChartAreas[0]->AxisY->ScaleBreakStyle->LineWidth = 2;
  chart1->ChartAreas[0]->AxisY->ScaleBreakStyle->LineColor = Color::Red;
  chart1->ChartAreas[0]->AxisY->ScaleBreakStyle->StartFromZero = StartFromZero::Auto;
  chart1->ChartAreas[0]->AxisY->ScaleBreakStyle->Spacing = 2;
  
  for (double x = 0; x < 3.14; x += 0.1) {
   double f = tan(x); //#include <cmath>
   f1->Add(x, f);
  }
  chart1->Series[0]->Points->DataBindXY(f1->Keys, f1->Values);
  chart1->Series[0]->LegendText = L"tan(x)";
  chart1->Series[0]->Color = System::Drawing::Color::Green;
  chart1->Series[0]->BorderWidth = 2;
 }
разрывы по оси y на графике
разрывы по оси y на графике

04.02.2014, 17:03 [52384 просмотра]


теги: программирование графика c++/cli

К этой статье пока нет комментариев, Ваш будет первым