using HalconDotNet; using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; using static VisionEditTest.ToolInfo; namespace VisionEditTest { [Serializable] public class Job { /// /// 工具对象集合 /// public List L_toolList = new List(); public TreeView tvw_job = null; /// /// 当前流程树是否处于折叠状态 /// private static bool jobTreeFold = true; /// /// 当前流程此次运行结果 /// private JobRunStatu jobRunStatu = JobRunStatu.Fail; /// /// 工具输入项个数 /// private int inputItemNum = 0; /// /// 工具输出项个数 /// private int outputItemNum = 0; /// /// 指示图像窗口是否为第一次显示窗体,第一次显示时要初始化 /// internal bool firstDisplayImage = true; /// /// 需要连线的节点对,不停的画连线,注意键值对中第一个为连线的结束节点,第二个为起始节点,一个输出可能连接多个输入,而键值对中的键不能重复,所以把源作为值,输入作为键 /// internal Dictionary D_itemAndSource = new Dictionary(); /// /// 本流程所绑定的图像窗口的句柄 /// internal HTuple imageWindow = new HTuple(); /// /// 本流程所绑定的生产窗口的名称 /// internal string imageWindowName = "无"; /// /// 流程结果图像所绑定的窗体 /// internal string debugImageWindow = "图像"; /// /// 流程运行结果图像 /// internal HObject jobResultImage; /// /// 编辑节点前节点文本,用于修改工具名称 /// private string nodeTextBeforeEdit = string.Empty; /// /// 流程编辑时的右击菜单 /// private static ContextMenuStrip rightClickMenu = new ContextMenuStrip(); /// /// 在空白除右击菜单 /// private static ContextMenuStrip rightClickMenuAtBlank = new ContextMenuStrip(); /// /// 流程名 /// internal string jobName = string.Empty; /// /// 流程树中节点的最大长度 /// private int maxLength = 130; /// /// 正在绘制输入输出指向线 /// internal static bool isDrawing = false; /// /// 记录本工具执行完的耗时,用于计算各工具耗时 /// private double recordElapseTime = 0; /// /// 标准图像字典,用于存储标准图像路径和图像对象 /// internal static Dictionary D_standardImage = new Dictionary(); /// /// 工具图标列表 /// internal static ImageList imageList = new ImageList(); /// /// 记录起始节点和此节点的列坐标值 /// private static Dictionary startNodeAndColor = new Dictionary(); /// /// 记录前面的划线所跨越的列段, /// private static Dictionary> list = new Dictionary>(); /// /// 每一个列坐标值对应一种颜色 /// private Dictionary colValueAndColor = new Dictionary(); /// /// 输入输出指向线的颜色数组 /// private static Color[] color = new Color[] { Color.Blue, Color.Orange, Color.Black, Color.Red, Color.Green, Color.Brown, Color.Blue, Color.Black, Color.Red, Color.Green, Color.Orange, Color.Brown, Color.Blue, Color.Black, Color.Red, Color.Green, Color.Orange, Color.Brown, Color.Blue, Color.Black, Color.Red, Color.Green, Color.Orange, Color.Brown, Color.Blue, Color.Black, Color.Red, Color.Green, Color.Orange, Color.Brown }; public Job() { } /// /// 拖动工具节点 /// /// /// internal void tvw_job_ItemDrag(object sender, ItemDragEventArgs e)//左键拖动 { try { if (((TreeView)sender).SelectedNode != null) { if (((TreeView)sender).SelectedNode.Level == 1) //输入输出不允许拖动 { tvw_job.DoDragDrop(e.Item, DragDropEffects.Move); } else if (e.Button == MouseButtons.Left) { tvw_job.DoDragDrop(e.Item, DragDropEffects.Move); } } } catch (Exception ex) { } } /// /// 节点拖动 /// /// /// internal void tvw_job_DragEnter(object sender, DragEventArgs e) { try { if (e.Data.GetDataPresent("System.Windows.Forms.TreeNode")) { e.Effect = DragDropEffects.Move; } else { e.Effect = DragDropEffects.None; } } catch (Exception ex) { } } /// /// 释放被拖动的节点 /// /// /// internal void tvw_job_DragDrop(object sender, DragEventArgs e)//拖动 { try { //获得拖放中的节点 TreeNode moveNode = (TreeNode)e.Data.GetData("System.Windows.Forms.TreeNode"); //根据鼠标坐标确定要移动到的目标节点 System.Drawing.Point pt; TreeNode targeNode; pt = ((TreeView)(sender)).PointToClient(new System.Drawing.Point(e.X, e.Y)); targeNode = tvw_job.GetNodeAt(pt); //如果目标节点无子节点则添加为同级节点,反之添加到下级节点的未端 if (moveNode == targeNode) //若是把自己拖放到自己,不可,返回 return; if (targeNode == null) //目标节点为null,就是把节点拖到了空白区域,不可,直接返回 return; if (moveNode.Level == 1 && targeNode.Level == 1 && moveNode.Parent == targeNode.Parent) //都是输入输出节点,内部拖动排序 { moveNode.Remove(); targeNode.Parent.Nodes.Insert(targeNode.Index, moveNode); return; } if (moveNode.Level == 0) //被拖动的是子节点,也就是工具节点 { if (targeNode.Level == 0) { moveNode.Remove(); tvw_job.Nodes.Insert(targeNode.Index, moveNode); ToolInfo temp = new ToolInfo(); for (int i = 0; i < L_toolList.Count; i++) { if (L_toolList[i].toolName == moveNode.Text) { temp = L_toolList[i]; L_toolList.RemoveAt(i); L_toolList.Insert(targeNode.Index - 2, temp); break; } } } else { moveNode.Remove(); tvw_job.Nodes.Insert(targeNode.Parent.Index + 1, moveNode); ToolInfo temp = new ToolInfo(); for (int i = 0; i < L_toolList.Count; i++) { if (L_toolList[i].toolName == moveNode.Text) { temp = L_toolList[i]; L_toolList.RemoveAt(i); L_toolList.Insert(targeNode.Parent.Index, temp); break; } } } } else //被拖动的是输入输出节点 { if (targeNode.Level == 0 && GetToolInfoByToolName(jobName, targeNode.Text).toolType == ToolType.Output) { string result = moveNode.Parent.Text + " . -->" + moveNode.Text.Substring(3); GetToolInfoByToolName(jobName, targeNode.Text).input.Add(new ToolIO("<--" + result, "", DataType.String)); TreeNode node = targeNode.Nodes.Add("", "<--" + result, 26, 26); node.ForeColor = Color.DarkMagenta; D_itemAndSource.Add(node, moveNode); targeNode.Expand(); DrawLine(); return; } else if (targeNode.Level == 0) return; //连线前首先要判断被拖动节点是否为输出项,目标节点是否为输入项 if (moveNode.Text.Substring(0, 3) != "-->" || targeNode.Text.Substring(0, 3) != "<--") { return; } //连线前要判断被拖动节点和目标节点的数据类型是否一致 if ((DataType)moveNode.Tag != (DataType)targeNode.Tag) { // Frm_Main.Instance.OutputMsg("被拖动节点和目标节点数据类型不一致,不可关联", Color.Red); return; } string input = targeNode.Text; if (input.Contains("《")) //表示已经连接了源 input = Regex.Split(input, "《")[0]; else //第一次连接源就需要添加到输入输出集合 D_itemAndSource.Add(targeNode, moveNode); GetToolInfoByToolName(jobName, targeNode.Parent.Text).GetInput(input.Substring(3)).value = "《- " + moveNode.Parent.Text + " . " + moveNode.Text.Substring(3); targeNode.Text = input + "《- " + moveNode.Parent.Text + " . " + moveNode.Text.Substring(3); DrawLine(); //移除拖放的节点 if (moveNode.Level == 0) moveNode.Remove(); } //更新当前拖动的节点选择 tvw_job.SelectedNode = moveNode; //展开目标节点,便于显示拖放效果 targeNode.Expand(); } catch (Exception ex) { } } /// /// 流程树的双击事件 /// internal void TVW_DoubleClick(object sender, MouseEventArgs e) { //判断是否在节点上双击 TreeViewHitTestInfo test = tvw_job.HitTest(e.X, e.Y); if (test.Node == null || test.Location != TreeViewHitTestLocations.Label) //双击节点 { if (jobTreeFold) { tvw_job.ExpandAll(); jobTreeFold = false; } else { tvw_job.CollapseAll(); jobTreeFold = true; } return; } string toolName = tvw_job.SelectedNode.Text; // TestFrmIn myForm = new TestFrmIn() for (int i = 0; i < L_toolList.Count; i++) { if (L_toolList[i].toolName == toolName) { switch(toolName) { case "可输入工具": ShapeMatchTool shapeMatchTool = (ShapeMatchTool)(L_toolList[i].tool); TestFrmIn myTestFrmIn = new TestFrmIn(); myTestFrmIn.shapeMatchTool = shapeMatchTool; myTestFrmIn.inputText = shapeMatchTool.text; myTestFrmIn.Show(); break; } } } } #region 绘制输入输出指向线 internal void tvw_job_AfterSelect(object sender, TreeViewEventArgs e) { nodeTextBeforeEdit = tvw_job.SelectedNode.Text; } internal void Draw_Line(object sender, TreeViewEventArgs e) { tvw_job.Refresh(); DrawLine(); } internal void tbc_jobs_SelectedIndexChanged(object sender, EventArgs e) { tvw_job.Refresh(); DrawLine(); } public void DrawLineWithoutRefresh(object sender, MouseEventArgs e) { tvw_job.Update(); DrawLine(); } #endregion /// /// 通过工具名获取工具信息 /// /// 工具名 /// 工具信息 internal static ToolInfo GetToolInfoByToolName(string jobName, string toolName) { try { Job job = new Job(); //for (int i = 0; i < Project.Instance.L_jobList.Count; i++) //{ // if (Project.Instance.L_jobList[i].jobName == jobName) // { // job = Project.Instance.L_jobList[i]; // break; // } //} job = TestForm.GetInstance().myJob; for (int i = 0; i < job.L_toolList.Count; i++) { if (job.L_toolList[i].toolName == toolName) { return job.L_toolList[i]; } } return new ToolInfo(); } catch (Exception ex) { return new ToolInfo(); } } private static Graphics g; /// /// 绘制输入输出指向线 /// /// public void DrawLine() { try { if (!isDrawing) { isDrawing = true; Thread th = new Thread(() => { tvw_job.MouseWheel += new MouseEventHandler(numericUpDown1_MouseWheel); //划线的时候不能滚动,否则画好了线,结果已经滚到其它地方了 maxLength = 150; colValueAndColor.Clear(); startNodeAndColor.Clear(); list.Clear(); TreeView tree = tvw_job; g = tree.CreateGraphics(); tree.CreateGraphics().Dispose(); foreach (KeyValuePair item in D_itemAndSource) { CreateLine(tree, item.Key, item.Value); } Application.DoEvents(); tvw_job.MouseWheel -= new MouseEventHandler(numericUpDown1_MouseWheel); isDrawing = false; }); th.IsBackground = true; th.ApartmentState = ApartmentState.STA; //此处要加一行,否则画线时会报错 th.Start(); } } catch (Exception ex) { } } /// /// 画Treeview控件两个节点之间的连线 /// /// 要画连线的Treeview /// 结束节点 /// 开始节点 private void CreateLine(TreeView treeview, TreeNode endNode, TreeNode startNode) { try { //得到起始与结束节点之间所有节点的最大长度 ,保证画线不穿过节点 int startNodeParantIndex = startNode.Parent.Index; int endNodeParantIndex = endNode.Parent.Index; int startNodeIndex = startNode.Index; int endNodeIndex = endNode.Index; int max = 0; if (!startNode.Parent.IsExpanded) { max = startNode.Parent.Bounds.X + startNode.Parent.Bounds.Width; } else { for (int i = startNodeIndex; i < startNode.Parent.Nodes.Count - 1; i++) { if (max < treeview.Nodes[startNodeParantIndex].Nodes[i].Bounds.X + treeview.Nodes[startNodeParantIndex].Nodes[i].Bounds.Width) max = treeview.Nodes[startNodeParantIndex].Nodes[i].Bounds.X + treeview.Nodes[startNodeParantIndex].Nodes[i].Bounds.Width; } } for (int i = startNodeParantIndex + 1; i < endNodeParantIndex; i++) { if (!treeview.Nodes[i].IsExpanded) { if (max < treeview.Nodes[i].Bounds.X + treeview.Nodes[i].Bounds.Width) max = treeview.Nodes[i].Bounds.X + treeview.Nodes[i].Bounds.Width; } else { for (int j = 0; j < treeview.Nodes[i].Nodes.Count; j++) { if (max < treeview.Nodes[i].Nodes[j].Bounds.X + treeview.Nodes[i].Nodes[j].Bounds.Width) max = treeview.Nodes[i].Nodes[j].Bounds.X + treeview.Nodes[i].Nodes[j].Bounds.Width; } } } if (!endNode.Parent.IsExpanded) { if (max < endNode.Parent.Bounds.X + endNode.Parent.Bounds.Width) max = endNode.Parent.Bounds.X + endNode.Parent.Bounds.Width; } else { for (int i = 0; i < endNode.Index; i++) { if (max < treeview.Nodes[endNodeParantIndex].Nodes[i].Bounds.X + treeview.Nodes[endNodeParantIndex].Nodes[i].Bounds.Width) max = treeview.Nodes[endNodeParantIndex].Nodes[i].Bounds.X + treeview.Nodes[endNodeParantIndex].Nodes[i].Bounds.Width; } } max += 10; //箭头不能连着 节点, if (!startNode.Parent.IsExpanded) startNode = startNode.Parent; if (!endNode.Parent.IsExpanded) endNode = endNode.Parent; if (endNode.Bounds.X + endNode.Bounds.Width + 20 > max) max = endNode.Bounds.X + endNode.Bounds.Width + 20; if (startNode.Bounds.X + startNode.Bounds.Width + 20 > max) max = startNode.Bounds.X + startNode.Bounds.Width + 20; //判断是否可以在当前处划线 foreach (KeyValuePair> item in list) { if (Math.Abs(max - item.Key) < 15) { foreach (KeyValuePair item1 in item.Value) { if (startNode != item1.Value) { if ((item1.Value.Bounds.X < maxLength && item1.Key.Bounds.X < maxLength) || (item1.Value.Bounds.X < maxLength && item1.Key.Bounds.X < maxLength)) { max += (15 - Math.Abs(max - item.Key)); } } } } } Dictionary temp = new Dictionary(); temp.Add(endNode, startNode); if (!list.ContainsKey(max)) list.Add(max, temp); else list[max].Add(endNode, startNode); if (!startNodeAndColor.ContainsKey(startNode)) startNodeAndColor.Add(startNode, color[startNodeAndColor.Count]); Pen pen = new Pen(startNodeAndColor[startNode], 1); Brush brush = new SolidBrush(startNodeAndColor[startNode]); g.DrawLine(pen, startNode.Bounds.X + startNode.Bounds.Width, startNode.Bounds.Y + startNode.Bounds.Height / 2, max, startNode.Bounds.Y + startNode.Bounds.Height / 2); g.DrawLine(pen, max, startNode.Bounds.Y + startNode.Bounds.Height / 2, max, endNode.Bounds.Y + endNode.Bounds.Height / 2); g.DrawLine(pen, max, endNode.Bounds.Y + endNode.Bounds.Height / 2, endNode.Bounds.X + endNode.Bounds.Width, endNode.Bounds.Y + endNode.Bounds.Height / 2); g.DrawString("<", new Font("微软雅黑", 12F), brush, endNode.Bounds.X + endNode.Bounds.Width - 5, endNode.Bounds.Y + endNode.Bounds.Height / 2 - 12); Application.DoEvents(); } catch { } } //取消滚轮事件 void numericUpDown1_MouseWheel(object sender, MouseEventArgs e) { HandledMouseEventArgs h = e as HandledMouseEventArgs; if (h != null) { h.Handled = true; } } /// /// 添加输入 /// /// /// private void Add_input(object sender, EventArgs e) { try { string result = sender.ToString(); //首先检查是否已经有此输入项,若已添加,则返回 foreach (var item in tvw_job.SelectedNode.Nodes) { string text; if (((TreeNode)item).Text.Contains("《")) { text = Regex.Split(((TreeNode)item).Text, "《")[0]; } else { text = ((TreeNode)item).Text; } if (text == "<--" + result) { //Frm_Main.Instance.OutputMsg(Configuration.language == Language.English ? "This input or output item already exists and cannot be added repeatedly" : "已存在此输入或输出项,不可重复添加", Color.Green); return; } } int insertPos = GetInputItemNum(tvw_job.SelectedNode); //获取插入位置,要保证输入项在前,输出项在后 TreeNode node = tvw_job.SelectedNode.Nodes.Insert(insertPos, "", "<--" + result, 26, 26); node.ForeColor = Color.DarkMagenta; tvw_job.SelectedNode.Expand(); DataType ioType = (DataType)((ToolStripItem)sender).Tag; //指定输入变量的类型 //if (result == (Configuration.language == Language.English ? "InputImage" : "输入图像")) // node.Tag = DataType.Image; //else if (result == "BlobResult") // node.Tag = "BlobResult"; //else node.Tag = ioType; node.Name = "<--" + result; GetToolInfoByToolName(jobName, tvw_job.SelectedNode.Text).input.Add(new ToolIO(result, "", ioType)); //如果是给输出工具添加输入,则需要连线 if (GetToolInfoByToolName(jobName, tvw_job.SelectedNode.Text).toolType == ToolType.Output) { string toolNodeText = Regex.Split(sender.ToString(), " . ")[0]; string toolIONodeText = Regex.Split(sender.ToString(), " . ")[1]; D_itemAndSource.Add(GetToolIONodeByNodeText(tvw_job.SelectedNode.Text, "<--" + sender.ToString()), GetToolIONodeByNodeText(toolNodeText, toolIONodeText)); Draw_Line(null, null); } } catch (Exception ex) { } } /// /// 通过TreeNode节点文本获取输入输出节点 /// /// 工具名称 /// IO名称 internal TreeNode GetToolIONodeByNodeText(string toolName, string toolIOName) { try { foreach (TreeNode toolNode in tvw_job.Nodes) { if (toolNode.Text == toolName) { foreach (TreeNode itemNode in ((TreeNode)toolNode).Nodes) { if (((TreeNode)itemNode).Text == toolIOName) { return itemNode; } } } } return null; } catch (Exception ex) { return null; } } /// /// 添加输出 /// /// /// private void Add_output(object sender, EventArgs e) { try { string result = sender.ToString(); foreach (var item in tvw_job.SelectedNode.Nodes) { if (((TreeNode)item).Text == "-->" + result) { return; } } TreeNode node = tvw_job.SelectedNode.Nodes.Add("", "-->" + result, 26, 26); node.ForeColor = Color.Blue; tvw_job.SelectedNode.Expand(); DataType ioType = (DataType)((ToolStripItem)sender).Tag; //指定输出变量的类型 if (result == "输出图像") { // node.Tag = DataType.Image; node.ToolTipText = "图形变量不支持显示"; } //else if (result == "BlobResult") // node.Tag = "BlobResult"; //else node.Tag = ioType; node.Name = "-->" + result; GetToolInfoByToolName(jobName, tvw_job.SelectedNode.Text).output.Add(new ToolIO(result, "", ioType)); node.ToolTipText = "未运行"; tvw_job.ShowNodeToolTips = true; } catch (Exception ex) { } } /// /// 获取工具输入项的个数 /// private int GetInputItemNum(TreeNode toolNode) { try { int num = 0; foreach (TreeNode item in toolNode.Nodes) { if (item.Text.Substring(0, 3) == "<--") { num++; } } return num; } catch (Exception ex) { return 0; } } } public enum JobRunStatu { Succeed, Fail, } }