以WINFORM应用程序为例,在C#应用程序中调用PYTHON程序(Matplotlib绘制图形程序),将调用PYTHON程序生成的窗口程序嵌入在WINFORM窗口中
窗口程序类
using System;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using System.Threading;
using System.Windows.Forms;
namespace MyForm
{
public partial class Form2 : DevComponents.DotNetBar.Office2007Form
{
private static log4net.ILog log = log4net.LogManager.GetLogger(typeof(Form2));
[DllImport("shell32.dll")]
private static extern IntPtr ShellExecute(IntPtr hwnd, string lpOperation, string lpFile, string lpParameters, string lpDirectory, Int32 nShowCmd);
private string pyFileName = @"code.py";
//需要在生成完文件后修改该参数值为生成的文件绝对路径
private string pyParamsfilePath = @"pyParams.dat";
private string args = "-u";
private string dataType = "";
public delegate void UpdateUI();//委托用于更新UI
Process p = null;//接收python程序进程,用于控制进程
Thread startload;//线程用于matplotlib窗体处理
IntPtr figure1;//图像句柄
#region 绘图事件Windows API调用
[DllImport("user32.dll")]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll")]
public static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int MoveWindow(IntPtr hWnd, int x, int y, int nWidth, int nHeight, bool BRePaint);
const int GWL_STYLE = -16;
//const int WS_CAPTION = 0x00C00000;
const int WS_CAPTION = 0x00CC0000;
const int WS_THICKFRAME = 0x00040000;
const int WS_SYSMENU = 0X00080000;
[DllImport("user32")]
private static extern int GetWindowLong(System.IntPtr hwnd, int nIndex);
[DllImport("user32")]
private static extern int SetWindowLong(System.IntPtr hwnd, int index, int newLong);
[DllImport("user32")]
private static extern int InvalidateRect(System.IntPtr hwnd, object rect, bool bErase);
/// <summary>最大化窗口,最小化窗口,正常大小窗口
/// nCmdShow:0隐藏,3最大化,6最小化,5正常显示
/// </summary>
[DllImport("user32.dll", EntryPoint = "ShowWindow")]
public static extern int ShowWindow(IntPtr hwnd, int nCmdShow);
#endregion
/// <summary>
/// 绘制图形按钮点击事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Draw3D(object sender, EventArgs e)
{
Create3DChart("Z");
}
/// <summary>
/// 创建3D图形
/// </summary>
private void Create3DChart(string drawDataType)
{
if (processInfo.process != null)
{//如果python程序进程不为null
if (!processInfo.process.HasExited)
{//如果当前进程没有被终止,未终止时值为false,终止时值为true
processInfo.process.Kill();
}
}
dataType = drawDataType;
//实例化线程,用来初次调用matlab,并把图像窗体放到winform
startload = new Thread(new ThreadStart(RunPythonScript));
//开始线程
startload.Start();
}
/// <summary>
/// 关闭窗口时结束进程
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void Close3DChart(object sender, FormClosedEventArgs e)
{
if (processInfo.process != null)
{//如果python程序进程不为null
if (!processInfo.process.HasExited)
{//如果当前进程没有被终止,未终止时值为false,终止时值为true
processInfo.process.Kill();
}
}
}
/// <summary>
/// 调用python脚本绘制3D图形
/// </summary>
public void RunPythonScript()
{
log.Info($"开始绘图线程---{DateTime.Now.ToString()}");
int count50ms = 0;
//获取python的绝对路径(将文件放在c#程序的debug文件夹中可以这样操作)
string path = System.AppDomain.CurrentDomain.SetupInformation.ApplicationBase + pyFileName;
//创建进程
Process process = new Process();
//没有配置环境变量的话,可以写python.exe的绝对路径,如果配置了,直接写"python.exe"即可
process.StartInfo.FileName = SystemConfigClass.PythonPath;
//设置python文件路径参数
string sArguments = path;
//设置执行方式参数
sArguments += " " + dataType;
sArguments += " " + Application.StartupPath + "\\" + pyParamsfilePath;
sArguments += " " + args;
log.Info($"绘图参数---{DateTime.Now.ToString()}:{sArguments}");
process.StartInfo.Arguments = sArguments;//设置命令行自变量
process.StartInfo.UseShellExecute = true;//设置直接从可执行文件创建进程
//process.StartInfo.RedirectStandardOutput = true;//设置应用程序输出写入进程(程序输出可在c#控制台显示) 调试时使用
//process.StartInfo.RedirectStandardInput = true;//设置应用程序输入读取进程(程序输入可读取进程输入) 调试时使用
//process.StartInfo.RedirectStandardError = true;//设置应用程序错误信息写入进程(程序错误输出可在c#控制台显示) 调试时使用
process.StartInfo.CreateNoWindow = true;//设置新窗口启动进程
process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;//设置窗口隐藏启动
processInfo.process = process;
process.Start();//进程开始
//process.BeginOutputReadLine();//在Process.StandardOutput异步读取数据 调试时使用
//process.OutputDataReceived += new DataReceivedEventHandler(process_OutputDataReceived); //调试时使用
figure1 = IntPtr.Zero;
//循环查找figure1窗体
while (figure1 == IntPtr.Zero)
{
//查找matplotlib的Figure 1窗体,安装PYQT组件后,查找类名Qt652QWindowIcon,未安装查找类名TkTopLevel
figure1 = FindWindow("Qt652QWindowIcon", "Figure 1");
//延时50ms
Thread.Sleep(50);
count50ms++;
//20s超时设置
if (count50ms >= 400)
{
//label1.Text = "matplotlib资源加载时间过长!";
return;
}
}
//跨线程,用委托方式执行
UpdateUI update = delegate
{
ShowWindow(figure1, 0);
//隐藏标签
//label1.Visible = false;
//设置matlab图像窗体的父窗体为panel
SetParent(figure1, PlotPanel.Handle);
//获取窗体原来的风格
var style = GetWindowLong(figure1, GWL_STYLE);
//设置新风格,去掉标题,不能通过边框改变尺寸
SetWindowLong(figure1, GWL_STYLE, style & ~WS_CAPTION & ~WS_THICKFRAME);
//移动到panel里合适的位置并重绘
MoveWindow(figure1, 0, 0, PlotPanel.Width, PlotPanel.Height, true);
//调用显示窗体函数,隐藏再显示相当于刷新一下窗体
ShowWindow(figure1, 5);
};
PlotPanel.Invoke(update);
//再移动一次,防止显示错误
Thread.Sleep(100);
MoveWindow(figure1, 0, 0, PlotPanel.Width, PlotPanel.Height, true);
//process.WaitForExit();//等待退出
}
}
}
进程信息类
/// <summary>
/// 进程信息类
/// </summary>
public static class processInfo {
public static Process process { get; set; } = null;
}
系统设置类
/// <summary>
/// 系统设置类
/// </summary>
public static class SystemConfigClass {
public static string PythonPath { get; set; } = @"D:\Python\python.exe";
/// <summary>
///
/// </summary>
/// <param name="dataFilePath>Application.StartupPath + "\\SystemConfig.DAT"</param>
public static void SetSystemConfig(string dataFilePath) {
if (File.Exists(dataFilePath))
{ //如果文件存在 删除文件后保存文件
string content = "";
using (StreamReader dataFile = new StreamReader(dataFilePath))
{
while ((content = dataFile.ReadLine()) != null)
{
content = content.ToString();
var tempArr = content.Split("^".ToCharArray()).ToList();
if (tempArr != null && tempArr.Count == 2) {
SystemConfigClass.PythonPath = tempArr[1];
}
else {
SystemConfigClass.PythonPath = @"D:\Python\python.exe";
}
}
dataFile.Close();
dataFile.Dispose();
};
}
}
}