winforms C#多线程动态创建图片框和组合框

qmelpv7a  于 2023-04-21  发布在  C#
关注(0)|答案(1)|浏览(228)

如下图所示,我有一个简单的WinForms应用程序,它的右侧有一个主组合框,根据我选择的屏幕数量,我可以获得相同数量的动态创建的组合框和图片框。在这个例子中,我选择了4。

如下面的代码所示,我试图创建一个多线程进程,其中根据我在动态创建的组合框中选择的选项(可用相机),然后我想在同一个索引图片框中显示图像流。例如,如果我在组合框1中选择一个相机,那么我想在图片框1中显示它。

private void comboBoxCameraSelection_SelectedIndexChanged(object sender, EventArgs e)
        {
            panelComboboxes.Controls.Clear();
            tableLayoutPanel1.Controls.Clear();
            string selectedValue = comboBoxCameraSelection.SelectedIndex.ToString();
            int numNewPictureBoxes = Int32.Parse(selectedValue) + 1;
            // Set cell border style with a margin of 1 pixel
            tableLayoutPanel1.CellBorderStyle = TableLayoutPanelCellBorderStyle.Inset;

            for (int i = 0; i < numNewPictureBoxes; i++)
            {
                numComboBoxes++;
                ComboBox newComboBox = new ComboBox();
                newComboBox.Name = "comboBox" + numComboBoxes.ToString();
                newComboBox.Size = new System.Drawing.Size(100, 21);
                newComboBox.Location = new System.Drawing.Point(10, 50 + numComboBoxes * 25);
                newComboBox.DropDownStyle = ComboBoxStyle.DropDownList;
                newComboBox.Dock = DockStyle.Top;
                LoadCameraList(newComboBox);
                newComboBox.SelectedIndexChanged += new EventHandler(comboBox_SelectedIndexChanged);
                panelComboboxes.Controls.Add(newComboBox);

                PictureBox pb = new PictureBox();
                pb.SizeMode = PictureBoxSizeMode.StretchImage;
                pb.Dock = DockStyle.Fill;
                pb.Anchor = AnchorStyles.None;
                pb.Margin = new Padding(1); // Add a margin of 1 to the picture box
                tableLayoutPanel1.Controls.Add(pb);
            }
            numComboBoxes = 0;
        }

        private void comboBox_SelectedIndexChanged(object sender, EventArgs e)
        {
            ComboBox? comboBox = sender as ComboBox;
            if (comboBox != null)
            {
                int cameraIndex = int.Parse(comboBox.Name.Replace("comboBox", ""));
                int selectedItemIndex = comboBox.SelectedIndex;
                Debug.WriteLine("Selected camera index: " + cameraIndex + ", Selected item index: " + selectedItemIndex);
                PictureBox pb = (PictureBox)tableLayoutPanel1.Controls[cameraIndex-1];
                camera = new Thread(() => CaptureCameraCallback(selectedItemIndex-1, pb));
                camera.Start();
            }

        }

        private void CaptureCameraCallback(int camInd,PictureBox pictureBox)
        {
            frame = new Mat();

            capture = new VideoCapture(camInd);

            HttpClient client = new HttpClient();
            client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("image/jpeg"));

            var apiUrl = "http://192.168.0.136:5000/object_detection";
            //capture.Open(2);
            while (isCameraRunning == 1)
            {
                capture.Read(frame);

                // Convert the frame to a JPEG image
                var buffer = new byte[frame.Width * frame.Height * frame.ElemSize()];
                Cv2.ImEncode(".jpg", frame, out buffer);

                // Send the JPEG image to the Flask API
                var content = new ByteArrayContent(buffer);
                content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("image/jpeg");
                var response = client.PostAsync(apiUrl, content).Result;
                var imageData = response.Content.ReadAsByteArrayAsync().Result;
                Image img;
                using (var stream = new MemoryStream(imageData))
                {
                    img = Image.FromStream(stream);
                }
                pictureBox.Image = img;
            }
        }

在运行我的脚本后,我没有得到任何错误或任何类型的异常。但没有任何东西显示到图片框。
我相信我在comboBox_SelectedIndexChanged方法中传递图片框时犯了一个错误。也许我引用错了?
我希望我采取的一般方法是正确的,希望有人能帮我解决...
谢谢!

xsuvu9jc

xsuvu9jc1#

我假设CaptureCameraCallback在后台线程上运行。您只能从UI线程更新UI:

pictureBox.BeginInvoke((Action)(() =>
{
    pictureBox.Image = img;
}));

在某些情况下,你可能需要调用Control.Invalidate()来强制更新。但我认为在这种情况下应该自动完成。
请注意,如果您想要高帧率的大图像,winforms可能还不够。您需要尽可能减少复制和分配的数量,其他较低级别的API可能更适合此目的。
我还建议从图像源创建一个抽象层,这样您就可以分别测试图像源和UI。
您还应确保正确处置所有一次性物品。

相关问题