Всем привет.
Возникла необходимость постройки графиков в консольном приложении (fpWeb), для чего была добавлена невидимая форма с чартами, некоторые модули в uses, и все прекрасно работает под Win.
Однако после сборки под Linux возникла проблема с тулкитом визуализации. При запуске требует GUI-библиотеку, которой на сервере нет. И даже если установить либы GTK или QT, иксов-то все рано нет, и все равно не запускается.
Можно, конечно, установить какой-нибудь Xvfb, который создаст мне фейковый X-сервер, но это уже ИМХО перебор.
Есть какой-то способ заставить работать невидимую форму в Linux без X-сервера?
Пробовал LCLWidgetType = nogui, но он сильно недоделанный, валит ошибки, а в конце еще и не умеет создавать виртуальный дескриптор канвы, что убивает всякую возможность отрисовки чего-либо в битмап. Задница...
Форма с TChart в консольном приложении
Модератор: Модераторы
- Alexander
- энтузиаст
- Сообщения: 864
- Зарегистрирован: 18.12.2005 18:10:00
- Откуда: оттуда
- Контактная информация:
В вопросе есть противоречие. Консольное приложение и есть не графическое по определению.
Что можно попытаться сделать? Например, использовать GNU/Plot:
Что можно попытаться сделать? Например, использовать GNU/Plot:
Код: Выделить всё
unit GnuPlotUtils;
{$MODE OBJFPC}{$H+}
{
Gnu Plot visualizator.
Version: 1.
Written on FreePascal (https://freepascal.org/).
Copyright (C) 2024-2025 Artyomov Alexander
http://self-made-free.ru/
aralni@mail.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
}
interface
uses
SysUtils, Classes, Process, DataTypes;
procedure SaveDataForPlot(const FileName: String;
const Data: TDoubleMatrix;
const Labels: TIntegerArray);
procedure PlotKMeansClusters(const DataFile, CentroidFile: String;
K: Integer);
procedure PlotGBPredictions(const DataFile, PredictionsFile: String);
implementation
procedure SaveDataForPlot(const FileName: String;
const Data: TDoubleMatrix;
const Labels: TIntegerArray);
var
f: TextFile;
i: Integer;
begin
AssignFile(f, FileName);
Rewrite(f);
try
for i := 0 to High(Data) do
begin
if (Length(Labels) > 0) and (i <= High(Labels)) then
WriteLn(f, Format('%g %g %d', [Data[i][0], Data[i][1], Labels[i]]))
else
WriteLn(f, Format('%g %g', [Data[i][0], Data[i][1]]));
end;
finally
CloseFile(f);
end;
end;
procedure PlotKMeansClusters(const DataFile, CentroidFile: String;
K: Integer);
var
gnuplot: TProcess;
script: TStringList;
scriptFile: String;
begin
script := TStringList.Create;
try
script.Add('set size square');
script.Add('set grid');
script.Add('set key outside');
script.Add('set title "KMeans Clustering (K=' + IntToStr(K) + ')"');
script.Add('set xlabel "Feature 1"');
script.Add('set ylabel "Feature 2"');
script.Add('set palette defined (0 "red", 1 "blue", 2 "green", 3 "purple")');
script.Add('plot "' + DataFile + '" using 1:2:3 with points pt 7 ps 1 palette title "Data", \');
script.Add(' "' + CentroidFile + '" with points pt 2 ps 3 lc black title "Centroids"');
script.Add('pause -1 "Press Enter to continue"');
scriptFile := ChangeFileExt(DataFile, '.plt');
script.SaveToFile(scriptFile);
gnuplot := TProcess.Create(nil);
try
gnuplot.Executable := 'gnuplot';
gnuplot.Parameters.Add(scriptFile);
gnuplot.Execute;
finally
gnuplot.Free;
end;
finally
script.Free;
end;
end;
procedure PlotGBPredictions(const DataFile, PredictionsFile: String);
var
gnuplot: TProcess;
script: TStringList;
scriptFile: String;
begin
script := TStringList.Create;
try
script.Add('set title "Gradient Boosting Predictions"');
script.Add('set xlabel "Feature"');
script.Add('set ylabel "Target"');
script.Add('plot "' + DataFile + '" using 1:2 with points pt 7 title "Actual", \');
script.Add(' "' + PredictionsFile + '" using 1:2 with lines lw 2 title "Predicted"');
script.Add('pause -1 "Press Enter to continue"');
scriptFile := ChangeFileExt(DataFile, '_gb.plt');
script.SaveToFile(scriptFile);
gnuplot := TProcess.Create(nil);
try
gnuplot.Executable := 'gnuplot';
gnuplot.Parameters.Add(scriptFile);
gnuplot.Execute;
finally
gnuplot.Free;
end;
finally
script.Free;
end;
end;
end.
Код: Выделить всё
procedure VisualizeKMeans;
var
dataFile, centroidFile: String;
emptyLabels: TIntegerArray;
begin
if not isDataLoaded or (Length(kmeansModel.centroids) = 0) then
begin
ShowError('Данные не загружены или модель не обучена');
Exit;
end;
if Length(x[0]) <> 2 then
begin
ShowError('Визуализация доступна только для 2D данных');
Exit;
end;
try
dataFile := 'kmeans_data.dat';
centroidFile := 'kmeans_centroids.dat';
// Создаем пустой массив меток для центроидов
SetLength(emptyLabels, 0);
// Сохраняем данные с метками кластеров
GnuPlotUtils.SaveDataForPlot(dataFile, x, kmeansModel.labels);
// Сохраняем центроиды (без меток)
GnuPlotUtils.SaveDataForPlot(centroidFile, kmeansModel.centroids, emptyLabels);
// Визуализируем
GnuPlotUtils.PlotKMeansClusters(dataFile, centroidFile, Length(kmeansModel.centroids));
ShowSuccess('Визуализация завершена. Проверьте окно GNU Plot.');
except
on E: Exception do
ShowError('Ошибка визуализации: ' + E.Message);
end;
end;
Последний раз редактировалось Alexander 20.08.2025 14:58:41, всего редактировалось 1 раз.
Да, ситуация в точности та же, и предлагаемое решение работает.sts писал(а):чтото похожее
https://forum.lazarus.freepascal.org/in ... ic=56262.0
Но, блин, настраивать чарт в рантайме -- это же адская боль! Один из самых сложных компонентов в истории...
Я для того и использовал форму, чтобы не приходилось этой трахолюдией заниматься.
Придется еще и парсер lfm прикручивать...
mike а в каком виде нужно получать графики и что с ними нужно делать дальше? Может быть действительно можно сделать проще?
наоборот это возможностиmike писал(а):о, блин, настраивать чарт в рантайме -- это же адская боль!
делаете гуевое приложение которое позволяет настраивать чарт (смутно припоминаю что его можно настраивать в рантайме, там соответствующие формы есть ) и сохранять настройку в lfm.
в лазарусе все для этого есть, тока скомпоновать надо.
Добавлено спустя 4 минуты 36 секунд:
там деловто
TWriter.WriteComponent
TReader.ReadComponent
В виде растровых картинок и отдавать по HTTP в ответ на <img src=WAYFARER писал(а):mike а в каком виде нужно получать графики и что с ними нужно делать дальше?
Добавлено спустя 11 минут 18 секунд:
Я поступил проще. Бросил в папку проекта форму с нужными чартами, никак не подключенную к самому проекту. Открываю ее когда нужно Лазарусом, меняю что надо, а программа читает сохраненный lfm.sts писал(а):делаете гуевое приложение которое позволяет настраивать чарт (смутно припоминаю что его можно настраивать в рантайме, там соответствующие формы есть ) и сохранять настройку в lfm.
С обработчиками событий это все дружит почти никак. Т.к. создавать нужно не форму, а только чарт, вырезаю из полного lfm описание этого чарта, и скармливаю его в ReadComponent. Но при этом обработчики событий чарта (принадлежащие форме, а не чарту) не присваиваются соответствующим полям, причем с ошибкой (Invalid value for property). Это ломает всю стройную архитектуру.там деловто
TWriter.WriteComponent
TReader.ReadComponent
Я бы тупо отдавал в html шаблон с каким нибудь chart.js json с даннымиmike писал(а):В виде растровых картинок и отдавать по HTTP в ответ на <img src=
