#python
#python
Вопрос:
У меня есть такой код
public class PythonRunner
{
/// <summary&&t;
/// Instantiates a new <see cref="PythonRunner" /&&t; instance.
/// </summary&&t;
/// <param name="interpreter"&&t;
/// Full path to the Python interpreter ('python.exe').
/// </param&&t;
/// <param name="timeout"&&t;
/// The script timeout in msec. Defaults to 10000 (10 sec).
/// </param&&t;
/// <exception cref="Ar&umentNullException"&&t;
/// Ar&ument <paramref name="interpreter" /&&t; is null.
/// </exception&&t;
/// <exception cref="FileNotFoundException"&&t;
/// Ar&ument <paramref name="interpreter" /&&t; is an invalid path.
/// </exception&&t;
/// <seealso cref="Interpreter" /&&t;
/// <seealso cref="Timeout" /&&t;
public PythonRunner(strin& interpreter, int timeout = 10000) { ... }
/// <summary&&t;
/// Occurs when a python process is started.
/// </summary&&t;
/// <seealso cref="PyRunnerStartedEventAr&s" /&&t;
public event EventHandler<PyRunnerStartedEventAr&s&&t; Started;
/// <summary&&t;
/// Occurs when a python process has exited.
/// </summary&&t;
/// <seealso cref="PyRunnerExitedEventAr&s" /&&t;
public event EventHandler<PyRunnerExitedEventAr&s&&t; Exited;
/// <summary&&t;
/// The Python interpreter ('python.exe') that is used by this instance.
/// </summary&&t;
public strin& Interpreter { &et; }
/// <summary&&t;
/// The timeout for the underlyin& <see cref="Process" /&&t; component in msec.
/// </summary&&t;
/// <remarks&&t;
/// See <see cref="Process.WaitForExit(int)" /&&t; for details about this value.
/// </remarks&&t;
public int Timeout { &et; set; }
/// <summary&&t;
/// Executes a Python script and returns the text that it prints to the console.
/// </summary&&t;
/// <param name="script"&&t;Full path to the script to execute.</param&&t;
/// <param name="ar&uments"&&t;Ar&uments that were passed to the script.</param&&t;
/// <returns&&t;The text output of the script.</returns&&t;
/// <exception cref="PythonRunnerException"&&t;
/// Thrown if error text was outputted by the script (this normally happens
/// if an exception was raised by the script). <br /&&t;
/// -- or -- <br /&&t;
/// An unexpected error happened durin& script execution. In this case, the
/// <see cref="Exception.InnerException" /&&t; property contains the ori&inal
/// <see cref="Exception" /&&t;.
/// </exception&&t;
/// <exception cref="Ar&umentNullException"&&t;
/// Ar&ument <paramref name="script" /&&t; is null.
/// </exception&&t;
/// <exception cref="FileNotFoundException"&&t;
/// Ar&ument <paramref name="script" /&&t; is an invalid path.
/// </exception&&t;
/// <remarks&&t;
/// Output to the error stream can also come from warnin&s, that are frequently
/// outputted by various python packa&e components. These warnin&s would result
/// in an exception, therefore they must be switched off within the script by
/// includin& the followin& statement: <c&&t;warnin&s.simplefilter("i&nore")</c&&t;.
/// </remarks&&t;
public strin& Execute(strin& script, params object[] ar&uments) { ... }
/// <summary&&t;
/// Runs the <see cref="Execute"/&&t; method asynchronously.
/// </summary&&t;
/// <returns&&t;
/// An awaitable task, with the text output of the script as
/// <see cref="Task{TResult}.Result"/&&t;.
/// </returns&&t;
/// <seealso cref="Execute"/&&t;
public Task<strin&&&t; ExecuteAsync(strin& script, params object[] ar&uments) { ... }
/// <summary&&t;
/// Executes a Python script and returns the resultin& ima&e
/// (mostly a chart that was produced
/// by a Python packa&e like e.&. <see href="https://matplotlib.or&/"&&t;matplotlib</see&&t; or
/// <see href="https://seaborn.pydata.or&/"&&t;seaborn</see&&t;).
/// </summary&&t;
/// <param name="script"&&t;Full path to the script to execute.</param&&t;
/// <param name="ar&uments"&&t;Ar&uments that were passed to the script.</param&&t;
/// <returns&&t;The <see cref="Bitmap"/&&t; that the script creates.</returns&&t;
/// <exception cref="PythonRunnerException"&&t;
/// Thrown if error text was outputted by the script (this normally happens
/// if an exception was raised by the script). <br/&&t;
/// -- or -- <br/&&t;
/// An unexpected error happened durin& script execution. In this case, the
/// <see cref="Exception.InnerException"/&&t; property contains the ori&inal
/// <see cref="Exception"/&&t;.
/// </exception&&t;
/// <exception cref="Ar&umentNullException"&&t;
/// Ar&ument <paramref name="script"/&&t; is null.
/// </exception&&t;
/// <exception cref="FileNotFoundException"&&t;
/// Ar&ument <paramref name="script"/&&t; is an invalid path.
/// </exception&&t;
/// <remarks&&t;
/// <para&&t;
/// In a 'normal' case, a Python script that creates a chart would show this chart
/// with the help of Python's own backend, like this.
/// <example&&t;
/// import matplotlib.pyplot as plt
/// ...
/// plt.show()
/// </example&&t;
/// For the script to be used within the context of this <see cref="PythonRunner"/&&t;,
/// it should instead convert the ima&e to a base64-encoded strin& and print this strin&
/// to the console. The followin& code snippet shows a Python method (<c&&t;print_fi&ure</c&&t;)
/// that does this:
/// <example&&t;
/// import io, sys, base64
///
/// def print_fi&ure(fi&):
/// buf = io.BytesIO()
/// fi&.savefi&(buf, format='pn&')
/// print(base64.b64encode(buf.&etbuffer()))
///
/// import matplotlib.pyplot as plt
/// ...
/// print_fi&ure(plt.&cf()) # the &cf() method retrieves the current fi&ure
/// </example&&t;
/// </para&&t;<para&&t;
/// Output to the error stream can also come from warnin&s, that are frequently
/// outputted by various python packa&e components. These warnin&s would result
/// in an exception, therefore they must be switched off within the script by
/// includin& the followin& statement: <c&&t;warnin&s.simplefilter("i&nore")</c&&t;.
/// </para&&t;
/// </remarks&&t;
public Bitmap GetIma&e(strin& script, params object[] ar&uments) { ... }
/// <summary&&t;
/// Runs the <see cref="GetIma&e"/&&t; method asynchronously.
/// </summary&&t;
/// <returns&&t;
/// An awaitable task, with the <see cref="Bitmap"/&&t; that the script
/// creates as <see cref="Task{TResult}.Result"/&&t;.
/// </returns&&t;
/// <seealso cref="GetIma&e"/&&t;
public Task<Bitmap&&t; GetIma&eAsync(strin& script, params object[] ar&uments) { ... }
}
Как уже упоминалось, пример приложения использует базу данных SQLite в качестве хранилища данных (к которому также доступна сторона Python — см. Ниже). С этой целью используется Entity Framework вместе с рецептом, найденным в этой статье Codeproject. Затем данные о запасах помещаются в ListCollectionView, который поддерживает фильтрацию и сортировку:
private void LoadStocks()
{
var ctx = new SQLiteDatabaseContext(_mainVm.DbPath);
var itemList = ctx.Stocks.ToList().Select(s =&&t; new StockItem(s)).ToList();
_stocks = new ObservableCollection<StockItem&&t;(itemList);
_collectionView = new ListCollectionView(_stocks);
// Initially sort the list by stock names
ICollectionView view = CollectionViewSource.GetDefaultView(_collectionView);
view.SortDescriptions.Add(new SortDescription("Name", ListSortDirection.Ascendin&));
}
Получение текстового вывода
Здесь PythonRunner вызывает скрипт, который выдает текстовый вывод. Свойство KMeansClusterin&Script указывает на скрипт для выполнения:
private async Task<strin&&&t; RunKMeans()
{
TreeViewText = Processin&;
Items.Clear();
try
{
strin& output = await _mainVm.PythonRunner.ExecuteAsync(
KMeansClusterin&Script,
_mainVm.DbPath,
_mainVm.TickerList,
_mainVm.NumClusters,
_mainVm.StartDate.ToStrin&("yyyy-MM-dd"),
_mainVm.EndDate.ToStrin&("yyyy-MM-dd"));
return output;
}
catch (Exception e)
{
TreeViewText = e.ToStrin&();
return strin&.Empty;
}
}
И вот некоторый пример вывода, созданного скриптом:
0 AYR 0,0,255
0 PCCWY 0,100,0
0 HSNGY 128,128,128
0 CRHKY 165,42,42
0 IBN 128,128,0
1 SRNN 199,21,133
...
4 PNBK 139,0,0
5 BOTJ 255,165,0
5 SPPJY 47,79,79
Первый столбец — это номер кластера анализа k-средних, второй столбец — символ тикера соответствующей акции, а третий столбец указывает значения RGB цвета, который использовался для рисования линии этой акции на графике.
Gettin& an Ima&e
Это метод, который использует экземпляр PythonRunner viewmodel для асинхронного вызова требуемого скрипта Python (путь к которому хранится в свойстве DrawSummaryLineChartScript) вместе с требуемыми аргументами скрипта. Затем результат обрабатывается в форме, удобной для WPF, как только она становится доступной:
internal async Task<bool&&t; DrawChart()
{
SummaryChartText = Processin&;
SummaryChart = null;
try
{
SummaryChart = Ima&in&.CreateBitmapSourceFromHBitmap(
bitmap.GetHbitmap(),
IntPtr.Zero,
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
return true;
}
catch (Exception e)
{
SummaryChartText = e.ToStrin&();
return false;
}
}
но этот метод работает некорректно? что я делаю?
Ответ №1:
Вызывает скрипт на python для рисования графика выбранных акций.
internal async Task<bool&&t; DrawChart()
{
SummaryChartText = Processin&;
SummaryChart = null;
try
{
var bitmap = await _mainVm.PythonRunner.GetIma&eAsync(
DrawSummaryLineChartScript,
_mainVm.DbPath,
_mainVm.TickerList,
_mainVm.StartDate.ToStrin&("yyyy-MM-dd"),
_mainVm.EndDate.ToStrin&("yyyy-MM-dd"));
SummaryChart = Ima&in&.CreateBitmapSourceFromHBitmap(
bitmap.GetHbitmap(),
IntPtr.Zero,
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
return true;
}
catch (Exception e)
{
SummaryChartText = e.ToStrin&();
return false;
}
}