本篇文章主要是对Vpro帮助文档中创建一个QuickBuild App的翻译和VB转C#
加载QuickBuild应用程序
添加GUI和使用线程
增强GUI和显示结果
显示图像
2022.5.19更新模块
创建应用程序
打开Visual Studio并创建一个新的Visual Basic Windows应用程序。注意:本例使用Visual Basic,但您也可以使用c#编写应用程序。对于本主题,应用程序的名称为UsingQB
设置环境
为了在你的应用中使用VisionPro类,你必须引用定义你将使用的类的程序集。对于所有使用QuickBuild应用程序作为基础的VisionPro项目,您将需要引用以下程序集:
导入命名空间
为了在你的应用程序中更容易使用VisionPro类,你需要导入你将要使用的命名空间。这样做可以使代码更容易阅读和更快地输入。
打开Form1.cs 添加代码:
using Cognex.VisionPro;
using Cognex.VisionPro.QuickBuild;
public class Form1
{
}
声明对象变量
您需要声明一些变量来维护对已加载的QuickBuild应用程序及其包含的作业的引用。
在Form1类中声明以下三个变量:
public class Form1
{
private CogJobManager myJobManager;
private CogJob myJob;
private CogJobIndependent myIndependentJob;
}
(一些基本的引用我就不在代码中体现了)
加载快速构建应用程序
当您保存一个快速构建应用程序时,它被存储为一个VisionPro持久性(.vpp)文件。此文件包含重新创建QuickBuild应用程序所需的所有信息,与在QuickBuild中保存该应用程序时完全相同。在大多数情况下,加载已保存的应用程序最方便的时间是在表单的load方法中。
选择Form1.vb [Design]选项卡,双击表单,创建在加载表单时运行的Form1_Load方法。
当你保存一个QuickBuild应用程序时,VisionPro将其保存为一个CogJobManager对象。LoadObjectFromFile方法从文件加载一个已保存的VisionPro对象,但它将其作为一个通用对象返回。使用CType将对象转换为适当的类型。
注意:如果你在默认位置安装了VisionPro, QuickBuild示例应用程序的位置是准确的。如果您在其他地方安装了它,则需要更改路径名。
private void Form1_Load(System.Object sender, System.EventArgs e)
{
myJobManager = (CogJobManager)CogSerializer.LoadObjectFromFile(@"C:\Program Files\Cognex\VisionPro\Samples\Programming\QuickBuild\advancedAppOne.vpp");
myJob = myJobManager.Job(0);
myIndependentJob = myJob.OwnedIndependent;
//清空队列
myJobManager.UserQueueFlush();
myJobManager.FailureQueueFlush();
myJob.ImageQueueFlush();
myIndependentJob.RealTimeQueueFlush();
}
QuickBuild应用程序可以有多个作业,但在本例中,应用程序只使用一个作业。通过调用myJobManager.Job(0),您将获得这个快速构建应用程序中第一个(也是唯一一个)作业的引用。下一行是我的工作。OwnedIndependent获取对CogJobIndependent对象的引用,CogJobIndependent对象提供了对该作业的更多信息的访问。
加载已保存的QuickBuild应用程序的最后一步是确保所有队列都为空。请记住,加载一个VisionPro持久性文件将重新创建与保存时完全相同的已保存对象。清除所有队列是一个好主意,以防其中一个队列中存储了数据。
优雅地关闭
表示QuickBuild应用程序的CogJobManager对象在运行时创建了几个线程。关闭CogJobManager很重要。
Form1_FormClosing方法在表单关闭时被调用。这就是您应该调用Shutdown的地方。
private void Form1_FormClosing(object sender, System.Windows.Forms.FormClosingEventArgs e)
{
myJobManager.Shutdown();
}
运行程序
此时,您的基于QuickBuild的Visual Basic应用程序什么也不做,只是加载QuickBuild应用程序并等待您关闭窗口退出。
在下一步中,您将向程序添加用户界面元素。
创建RunOnce按钮
您将添加的第一个用户界面元素是一个运行已保存的QuickBuild应用程序的按钮。从Visual Studio工具箱中,从“通用控件”部分拖动一个按钮到Form1。命名此按钮为RunOnceButton,并将其文本设置为RunOnce。
命名处 (意为按钮的名字,我这里显而易见的设为button1,官方文档设置的是RunOnceButton,后面的代码中出现的RunOnceButton即为这里所描述的意思)
文本设置 (在界面中按钮上显示的文字)
设置方法在设计界面点击并直接输入就可以
或者在按钮属性中找到Text进行设置。
双击按钮来创建它的Click处理程序,并输入以下代码:
private void RunOnceButton_Click(System.Object sender, System.EventArgs e)
{
try
{
myJobManager.Run();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
Run方法运行myJobManager中的所有作业,其中包含已保存的QuickBuild应用程序。按钮的Click处理程序调用Try/Catch块中的Run来处理QuickBuild应用程序开始运行时可能发生的任何错误。
现在运行您的程序,并单击RunOnce按钮使应用程序运行。(因为此时没有显示任何输出,所以不会看到任何东西。)尝试以尽可能快的速度重复点击RunOnce按钮。您应该会得到一条错误消息。
处理作业管理器事件
当您快速单击Run Once按钮时出现错误的原因是Run是一个立即返回的异步方法。如果在作业运行时再次调用。你会得到一个CogNotStoppedException错误。
为了避免这个问题,您可以在作业运行时禁用Run Once按钮,并在作业停止时重新启用它。在作业开始时禁用该按钮非常简单。你需要做的就是在Click处理程序中禁用按钮:
private void RunOnceButton_Click(System.Object sender, System.EventArgs e)
{
try
{
RunOnceButton.Enabled = false;
myJobManager.Run();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
当作业停止运行时重新启用按钮稍微复杂一些。 当作业管理器中的所有作业都完成运行时,CogJobManager将触发一个Stopped事件。 您将在作业管理器的Stopped事件处理程序中放入重新启用按钮的代码。
在Form1类的末尾,为作业管理器编写stop处理程序,如下所示:
private void myJobManager_Stopped(object sender, CogJobManagerActionEventArgs e)
{
RunOnceButton.Enabled = true;
}
定义了Stopped事件处理程序之后,需要确保作业管理器知道它的位置。 使用AddHandler为事件注册处理程序。 AddHandler的第一个参数是事件; 第二个参数是处理程序的地址。 在Form1_Load方法的末尾添加以下代码:
private void Form1_Load(System.Object sender, System.EventArgs e)
{
myJobManager = (CogJobManager)CogSerializer.LoadObjectFromFile(@"C:\Program Files\Cognex\VisionPro\Samples\Programming\QuickBuild\advancedAppOne.vpp");
myJob = myJobManager.Job(0);
myIndependentJob = myJob.OwnedIndependent;
myJobManager.UserQueueFlush();
myJobManager.FailureQueueFlush();
myJob.ImageQueueFlush();
myIndependentJob.RealTimeQueueFlush();
myJobManager.Stopped += myJobManager_Stopped;
}
无论何时注册一个事件处理程序,最好在关闭程序之前删除它。 在本例中,执行此操作的最佳位置是在关闭作业管理器之前的表单的FormClosing处理程序中。 FormClosing处理程序现在应该是这样的:
private void Form1_FormClosing(object sender, System.Windows.Forms.FormClosingEventArgs e)
{
myJobManager.Stopped -= myJobManager_Stopped;
myJobManager.Shutdown();
}
如果您在此时运行您的程序,您将得到一个错误:跨线程操作无效。
处理跨线程函数调用
请记住,作业管理器创建了几个线程。其中一个线程触发stop事件,该事件运行已停止的事件处理程序,并最终尝试重新启用Run Once按钮。这个事件序列是非法的,因为一个按钮——或任何其他Microsoft Windows窗体控件——只能由创建该控件的线程访问。该按钮不是由任何作业管理器线程创建的。Microsoft提供了一种对控件进行线程安全调用的方法。注意:您可以在这篇MDSN文章中了解更多关于进行线程安全调用的内容。每个控件都有一个InvokeRequired属性,如果它被一个没有创建该控件的线程调用,该属性将返回True。当为True时,必须创建一个指向将访问该控件的函数的指针,切换到创建该控件的线程,并使用指针再次调用该函数。在。net中,指向函数的指针是Delegate。您将声明一个与要处理的事件具有相同函数签名(参数)的委托。在这种情况下,您的委托具有与已停止事件相同的签名。在已停止事件处理程序之前添加这行代码
delegate void myJobManagerDelegate(object sender, CogJobManagerActionEventArgs e);
现在,在Stopped事件处理程序中,您将检查处理程序是否从错误的线程被调用。如果是,您将使用您定义的委托来创建一个指向处理程序的指针,然后使用Invoke方法递归地调用它。stop事件处理程序应该是这样的:
private void myJobManager_Stopped(object sender, CogJobManagerActionEventArgs e)
{
if (InvokeRequired)
{
myJobManagerDelegate myDel = new myJobManagerDelegate(myJobManager_Stopped);
object[] eventArgs = new[] { sender, e };
Invoke(myDel, eventArgs);
return;
}
RunOnceButton.Enabled = true;
}
Invoke方法接受一个指针(已停止事件处理程序)和一个对象(合并该处理程序的参数)。
这种技术是处理用户界面项可能被另一个线程访问的情况的标准方法。学习这个习语是个好主意。
如果此时运行程序,将不会得到任何错误。下一步是添加一个显示控件,这样您就可以看到您的程序正在做什么。
添加连续运行按钮
您将添加的下一个用户界面元素是Run Continuous按钮。当您单击此按钮时,它将保持按下状态,应用程序将运行,直到您再次单击该按钮。为了获得这种行为,本例使用一个复选框,并将其外观更改为按钮。
确保Form1.vb [Design]选项卡处于激活状态。从Visual Studio工具箱的“公共控件”部分中,将复选框控件拖动到窗体中。将其文本更改为连续运行。将其名称更改为RunContCheckBox。最后,将其外观更改为Button。
用户界面应该是这样的:
双击新的Run Continuous按钮来创建它的Check_Changed处理程序,并添加以下代码:
private void RunContCheckBox_CheckedChanged(System.Object sender, System.EventArgs e)
{
if ((RunContCheckBox.Checked))
{
try
{
RunOnceButton.Enabled = false;
myJobManager.RunContinuous();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
else
try
{
RunContCheckBox.Enabled = false;
myJobManager.Stop();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
RunContinuous方法反复运行myJobManager中的所有作业,其中包含已保存的QuickBuild应用程序,直到Stop停止它们。CheckedChanged处理程序负责在作业启动时禁用Run Once按钮,以及在等待作业停止时禁用Run Continuous按钮。
向Stopped处理程序添加以下代码行,以便在所有作业都已停止时重新启用Run Continuous按钮。
private void myJobManager_Stopped(object sender, CogJobManagerActionEventArgs e)
{
if (InvokeRequired)
{
myJobManagerDelegate myDel = new myJobManagerDelegate(myJobManager_Stopped);
object[] eventArgs = new[] { sender, e };
Invoke(myDel, eventArgs);
return;
}
RunOnceButton.Enabled = true;
RunContCheckBox.Enabled = true;
}
并向Run Once按钮的Click处理程序添加以下代码行,以在作业运行一次时禁用Run Continuous按钮
private void RunOnceButton_Click(System.Object sender, System.EventArgs e)
{
try
{
RunOnceButton.Enabled = false;
RunContCheckBox.Enabled = false;
myJobManager.Run();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
如果此时运行程序,您将看到按钮在适当的时候变为启用和禁用。您也可以单击窗口的关闭框来退出应用程序,即使它正在运行。
显示应用程序的结果
到目前为止添加的用户界面启动和停止保存的QuickBuild应用程序,但没有显示任何结果。在本部分示例中,您将从作业中提取数据并将其显示在文本框中。
下图显示了如何将作业管理器包含的每个作业的结果发布到各种队列。您将在这里使用的队列是“张贴项目”列表。在VisionPro API中,这个队列被称为用户队列。
如图所示,当用户队列中有一个可用的新结果时,作业管理器触发一个UserResultAvailable事件。您将从作业中提取数据并将其显示在表单中。
确保Form1.vb [Design]选项卡处于激活状态。从Visual Studio工具箱的“公共控件”部分拖动文本框到窗体。将其名称更改为RunStatusTextBox。这里将显示应用程序的运行状态。
在Form1类的末尾,添加以下UserResultAvailable事件处理程序。注意,因为它修改用户界面项(文本框),所以需要使用前面学习的Invoke习语。
private void myJobManager_UserResultAvailable(object sender, CogJobManagerActionEventArgs e)
{
if (InvokeRequired)
{
// Create a pointer to this function
myJobManagerDelegate myDel = new myJobManagerDelegate(myJobManager_UserResultAvailable);
// Call this same function on the correct thread
object[] eventArgs = new[] { sender, e };
Invoke(myDel, eventArgs);
return;
}
Cognex.VisionPro.ICogRecord topRecord = myJobManager.UserResult as ICogRecord;
RunStatusTextBox.Text = topRecord.SubRecords["UserResultTag"].Content + ": "
+ topRecord.SubRecords["JobName"].Content + " --> "
+ topRecord.SubRecords["RunStatus"].Content.ToString();
}
UserResult方法从作业管理器的User Queue中删除并返回最早的结果记录。这个记录包含了张贴项目列表中每个项目的子记录。(请参见配置张贴项列表。)它还包含这些额外的子记录:
·UserResultTag
这次作业的序列号
·JobName
返回结果的作业的名称
·RunStatus
任务的运行状态
在本示例的稍后部分,您将在单个字符串中显示所有这些值。
在运行程序之前,需要在表单的Load处理程序中注册UserResultsAvailable处理程序,并在表单的FormClosing处理程序中注销它。Load处理程序现在应该是这样的:
private void Form1_Load(System.Object sender, System.EventArgs e)
{
myJobManager = (CogJobManager)CogSerializer.LoadObjectFromFile(@"C:\Program Files\Cognex\VisionPro\Samples\Programming\QuickBuild\advancedAppOne.vpp");
myJob = myJobManager.Job(0);
myIndependentJob = myJob.OwnedIndependent;
myJobManager.UserQueueFlush();
myJobManager.FailureQueueFlush();
myJob.ImageQueueFlush();
myIndependentJob.RealTimeQueueFlush();
myJobManager.Stopped += myJobManager_Stopped;
myJobManager.UserResultAvailable += myJobManager_UserResultAvailable;
}
FormClosing处理程序看起来像这样:
private void Form1_FormClosing(object sender, System.Windows.Forms.FormClosingEventArgs e)
{
myJobManager.Stopped -= myJobManager_Stopped;
myJobManager.UserResultAvailable -= myJobManager_UserResultAvailable;
Application.DoEvents();
myJobManager.Shutdown();
}
除了删除UserResultAvailable处理程序,FormClosing方法还调用Application.DoEvents()来清除Windows消息队列。如果在表单的关闭框被单击之后,在UserResultAvailable处理程序未注册之前,新用户结果变为可用,则可以防止出现死锁。
运行应用程序
当您运行应用程序时,您将在文本框中看到结果。在下一步中,您将向应用程序添加一个显示。
显示控件概述
VisionPro提供四种显示控件,您可以使用它们来显示图像和图形。
Table 1. VisionPro Display Controls
下面是表格中内容的翻译 请自行对照理解。
CogToolDisplay
这是你在快速构建和视觉工具中看到的显示控件。您可以将一个VisionPro工具连接到显示控件,并直接从工具提取记录。它使用一个下拉列表来选择要显示的图像记录。
CogRecordsDisplay
该控件类似于CogToolDisplay控件。它允许用户从下拉列表中选择一个图像记录,并显示该图像的任何图形子记录。不像CogToolDisplay,这个控件不知道关于VisionPro工具的任何事情;您必须提供要显示的记录。
CogRecordDisplay
此控件显示单个图像记录及其图形子记录。它没有下拉列表框,也不允许用户选择图像。
CogDisplay
这是最低级别的显示控件,作为所有其他显示控件的基础。此控件显示必须直接提供给该控件的单个图像和一组图形。
对于这个例子,您将使用CogRecordDisplay控件来显示LastRun.CogFixtureTool1。OutputImage记录,其中包含括号的图像和显示孔之间距离的图形。
添加对以下程序集的引用:
Cognex.VisionPro.Controls
Cognex.VisionPro.Display.Controls
确保Form1.vb [Design]选项卡处于激活状态。从visualstudio工具箱的VisionPro显示控件部分拖动一个CogRecordDisplay控件到你的表单。
注意:如果在工具箱中没有看到CogRecordDisplay,右键单击工具箱并选择choose Items。在出现的“选择工具箱项”对话框中,向下滚动,直到找到CogRecordDisplay,并确保复选框被选中。
在CogRecordDisplay下面拖动一个CogDisplayStatusBar。你的表单应该看起来像这样:
在FormClosing方法的末尾添加以下代码,在应用程序关闭时释放这些控件:
private void Form1_FormClosing(object sender, System.Windows.Forms.FormClosingEventArgs e)
{
myJobManager.Stopped -= myJobManager_Stopped;
myJobManager.UserResultAvailable -= myJobManager_UserResultAvailable;
Application.DoEvents();
myJobManager.Shutdown();
CogDisplayStatusBar1.Dispose();
CogRecordDisplay1.Dispose();
}
在Load方法的末尾添加以下代码,以将显示状态栏与显示器链接起来。
private void Form1_Load(System.Object sender, System.EventArgs e)
{
myJobManager = (CogJobManager)CogSerializer.LoadObjectFromFile(@"C:\Program Files\Cognex\VisionPro\Samples\Programming\QuickBuild\advancedAppOne.vpp");
myJob = myJobManager.Job(0);
myIndependentJob = myJob.OwnedIndependent;
myJobManager.UserQueueFlush();
myJobManager.FailureQueueFlush();
myJob.ImageQueueFlush();
myIndependentJob.RealTimeQueueFlush();
myJobManager.Stopped += myJobManager_Stopped;
myJobManager.UserResultAvailable += myJobManager_UserResultAvailable;
CogDisplayStatusBar1.Display = CogRecordDisplay1;
}
获取要在CogRecordDisplay控件中显示的记录的代码位于UserResultAvailable处理程序的末尾:
private void myJobManager_UserResultAvailable(object sender, CogJobManagerActionEventArgs e)
{
if (InvokeRequired)
{
myJobManagerDelegate myDel = new myJobManagerDelegate(myJobManager_UserResultAvailable);
object[] eventArgs = new[] { sender, e };
Invoke(myDel, eventArgs);
return;
}
Cognex.VisionPro.ICogRecord topRecord = myJobManager.UserResult;
RunStatusTextBox.Text = topRecord.SubRecords["UserResultTag"].Content + ": "
+ topRecord.SubRecords["JobName"].Content + " --> "
+ topRecord.SubRecords["RunStatus"].Content.ToString();
Cognex.VisionPro.ICogRecord tmpRecord;
tmpRecord = topRecord.SubRecords["ShowLastRunRecordForUserQueue"];
tmpRecord = tmpRecord.SubRecords["LastRun"];
tmpRecord = tmpRecord.SubRecords["CogFixtureTool1.OutputImage"];
CogRecordDisplay1.Record = tmpRecord;
CogRecordDisplay1.Fit(true);
}
对SubRecords的连续调用遵循子记录路径,以获得QuickBuild应用程序的Fixture工具的输出图像。Record属性指定要显示的记录。Fit方法缩放图像及其图形以适应显示控件。
如果现在运行该程序,您将看到每个图像显示在CogRecordDisplay控件中。
以上都是我在学习过程中自己通过有道词典翻译过来的,内容的表达可能会有些问题,欢迎大家指出,并且我也会在后学的学习过程中及时更正。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Cognex.VisionPro;
using Cognex.VisionPro.QuickBuild;
namespace Load
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
//增加的内容 这里是我个人在增加按钮控件时自己写的button1_Click方法 不是双击控件自己添加的所以事件并没有绑定
//this.button1.Click += new System.EventHandler(this.button1_Click);
}
private CogJobManager mJobManager;
private CogJob mJob;
private CogJobIndependent mJobIndependent;
private void Form1_Load(object sender, EventArgs e)
{
mJobManager = CogSerializer.LoadObjectFromFile(@"..\QuickBuild1.vpp") as CogJobManager;
mJob = mJobManager.Job(0);
mJobIndependent = mJob.OwnedIndependent;
//清空队列
mJobManager.UserQueueFlush();
mJobManager.FailureQueueFlush();
mJob.ImageQueueFlush();
mJobIndependent.RealTimeQueueFlush();
mJobManager.Stopped += new CogJobManager.CogJobManagerStoppedEventHandler(mJobManager_Stopped);//
mJobManager.UserResultAvailable += myJobManager_UserResultAvailable;
CogDisplayStatusBar1.Display = CogRecordDisplay1;
}
//运行按钮
private void button1_Click(object sender, EventArgs e)
{
try
{
button1.Enabled = false;//运行时禁用按钮
checkBox1.Enabled = false;//运行 禁用
mJobManager.Run();//调用Run方法运行mJobManager中的作业
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private void mJobManager_Stopped(object sender, CogJobManagerActionEventArgs e)
{
if (InvokeRequired)
{
mJobManagerDelegate myDel = new mJobManagerDelegate(mJobManager_Stopped);
object[] eventArgs = new object[2] { sender, e };
Invoke(myDel, eventArgs);
return;
}
button1.Enabled = true;
checkBox1.Enabled = true;
}
delegate void mJobManagerDelegate(object sender, CogJobManagerActionEventArgs e);
private void Form1_FormClosing(object sender, System.Windows.Forms.FormClosingEventArgs e)
{
mJobManager.Stopped -= mJobManager_Stopped;
mJobManager.Shutdown();
Application.DoEvents();
mJobManager.Shutdown();
CogDisplayStatusBar1.Dispose();
CogRecordDisplay1.Dispose();
}
private void checkBox1_CheckedChanged(object sender, EventArgs e)
{
if (checkBox1.Checked)
{
try
{
checkBox1.Enabled = false;
mJobManager.RunContinuous();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
else
try
{
checkBox1.Enabled = false;
mJobManager.Stop();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private void myJobManager_UserResultAvailable(object sender, CogJobManagerActionEventArgs e)
{
if (InvokeRequired)
{
// Create a pointer to this function
mJobManagerDelegate myDel = new mJobManagerDelegate(myJobManager_UserResultAvailable);
// Call this same function on the correct thread
object[] eventArgs = new object[2] { sender, e };
Invoke(myDel, eventArgs);
return;
}
//下面两句话的作用是清除控件图像记录 不加现在也体现不出什么问题 最好还是加上。如果有大佬知道这块怎么理解请指点。
CogRecordDisplay1.InteractiveGraphics.Clear();
CogRecordDisplay1.StaticGraphics.Clear();
Cognex.VisionPro.ICogRecord topRecord = mJobManager.UserResult() as ICogRecord;
RunStatusTextBox.Text = topRecord.SubRecords["UserResultTag"].Content.ToString() + ": "
+ topRecord.SubRecords["JobName"].Content.ToString() + " --> "
+ topRecord.SubRecords["RunStatus"].Content.ToString();
Cognex.VisionPro.ICogRecord tmpRecord;
tmpRecord = topRecord.SubRecords["ShowLastRunRecordForUserQueue"];
tmpRecord = tmpRecord.SubRecords["LastRun"];
tmpRecord = tmpRecord.SubRecords[0];
//上面这句原本是下面的代码可是运行时就是不显示结果,使用上面这句就可以运行了,同样 这里我不是很明白是为什么,版本问题?
//tmpRecord = tmpRecord.SubRecords["CogFixtureTool1.OutputImage"];
CogRecordDisplay1.Record = tmpRecord;
CogRecordDisplay1.Fit(true);
}
}
}
这下运行过程中就不会出现什么问题了,点击两个按钮都可正常运行,但是点击窗口关闭,会出现下面的情况希望有懂的大佬指点指点。