GTK窗口始终无法重绘? (Thread2意外使用了Thread1更新对象??)

问题描述 投票:1回答:1

[因此,我正在Raspberry Pi 3B +型上使用C进行项目,并且正在使用GTK进行绘制。我在自己的线程上拥有GTK,在自己的线程上拥有程序逻辑。

[基本上,我的程序使用gphoto2从3个数码单反相机拍摄图片并将其保存为jpg,然后GTK加载这些JPG,使用pixbuf将其调整为500x300的大小,并在GUI中更新图片

我所遇到的问题是,GTK窗口将无法重绘,并且在什么时候非常不一致,有时会在拍摄11张照片后发生,有时会在甚至拍摄2或3张照片后发生,没有一致的故障。

可以提供任何帮助,一个线程只是初始化gtk,然后调用GTK main来做GUI,另一个线程执行我的程序逻辑,更新jpg文件,然后更新GTK主对象以重绘。也许这与我的线程在不好的时间更新gtk对象和运行gtk main的循环崩溃和暂停有关。

我的原始GUI如下所示:enter image description here

黑匣子解决了这个问题,这些只是拍摄任何照片之前的默认空白像素缓冲区。问题是它将失败并且图像将仅变为背景颜色(请参见网格“ CAM2前面板”),然后在此调用之后整个窗口出现故障,并且不执行任何操作。我可以拖动它,它会画出一些东西,但我将其拖动到上面。

enter image description here

我知道整个应用程序都无法绘制,因为在移动窗口时得到这样的结果。

enter image description here

非常感谢您的帮助。我已经玩了一段时间了。

这是我的程序逻辑主线程循环调用的代码,用于将图像更新为新的jpg:

void set_image_scaled(GtkWidget* img, const char* path) {
pixbuf = gdk_pixbuf_new_from_file(path, &err);
g_assert_no_error(err);
pxbscaled = gdk_pixbuf_scale_simple(pixbuf, 500, 300, GDK_INTERP_BILINEAR);
gtk_image_set_from_pixbuf(GTK_IMAGE(img), pxbscaled);
g_object_unref(pixbuf);
g_object_unref(pxbscaled);

}

这是更新GtkImage对象的主要逻辑线程(调用set_image_scaled():]

void* cam_main() {
while (running == 1) {
if (digitalRead(4) == 0)
    {
        printf("taking pics of %s item %i\n", (front == 1) ? "front" : "back", pic);
        for (int i = 0; i < count; i++)
        {
            int fd, retval;
            CameraFile *file;
            CameraFilePath cfPath;
            strcpy(cfPath.folder, "/");
            strcpy(cfPath.name, "foo1.jpg");
            printf("Capturing cam%i...\n", i + 1);
            int res = gp_camera_capture(cams[i], GP_CAPTURE_IMAGE, &cfPath, context);
            //printf(gp_port_result_as_string(res));
            printf("capture result: %i\n", res);
            //Camera won't take pic if busy and will continue to program end
            char buf[256];
            snprintf(buf, sizeof(buf), "%s/cam-%i_%i_%s.jpg", workingDir, i + 1, pic, (front == 1) ? "a_front" : "b_back"); //a_ to make front come before back otherwise systems will order incorrectly
            fd = open(buf, O_CREAT | O_WRONLY, 0644);
            retval = gp_file_new_from_fd(&file, fd);
            retval = gp_camera_file_get(cams[i], cfPath.folder, cfPath.name, GP_FILE_TYPE_NORMAL, file, context);
            retval = gp_camera_file_delete(cams[i], cfPath.folder, cfPath.name, context);
            gp_file_free(file);
            //debug
            //if (front == 1 && i == 0)
                //set_image_scaled(front_cams[0], buf);
            if (front == 1)
                set_image_scaled(front_cams[i], buf);
            else
                set_image_scaled(back_cams[i], buf);
        }
        if (front == 1)
            front = 0;
        else
        {
            front = 1;
            pic += 1;
        }
        printf("pics taken...\n");
    }
}

}

我的完整代码供参考:

#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdarg.h>
#include <gphoto2/gphoto2-camera.h>
#include <gphoto2/gphoto2-context.h>
#include <wiringPi.h>
#include <gtk/gtk.h>
#include <pthread.h>

    //program
    static int running = 1;
    //libgphoto2
    static CameraList* list;
    static Camera** cams;
    static GPContext* context;
    static const char *name, *value;
    static int ret, count;
    static int pic = 0;
    static int front = 1;
    static const char* workingDir;
    //GTK
    static GtkWidget *window, *vbox,*hboxDir, *hboxCamLabels, *hboxFrontPics, *hboxBackPics, *hboxStatus, *lblDir, *btnConfigDir, *lblCams, *lblFront, *lblBack, *lblCurrentStatus;
    static GtkWidget *front_cams[3];
    static GtkWidget *back_cams[3];
    static GdkPixbuf *pxbscaled = NULL;
    static GdkPixbuf *pixbuf = NULL;
    static GError* err = NULL;

static void btnConfigDir_ConfigureDirectory(GtkWidget* widget, gpointer data) {
    GtkWidget *dialog;
GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_OPEN;
gint res;

                dialog = gtk_file_chooser_dialog_new ("Open File", window,
                                      action,
                                      "_Cancel",
                                      GTK_RESPONSE_CANCEL,
                                      "_Open",
                                      GTK_RESPONSE_ACCEPT,
                                      NULL);

res = gtk_dialog_run (GTK_DIALOG (dialog));
if (res == GTK_RESPONSE_ACCEPT)
  {
    char *filename;
    GtkFileChooser *chooser = GTK_FILE_CHOOSER (dialog);
    workingDir = gtk_file_chooser_get_filename (chooser);
  }

  //gtk_label_set_text((GTK_LABEL)lblDir, workingDir);

gtk_widget_destroy (dialog);
}

void* main_gtk() {
    //INIT GTK

    //SETUP WINDOW
    window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    //button1=gtk_button_new_with_label("Click me");
    //gtk_button_set_label(GTK_BUTTON(button1), "click me 1");
    g_signal_connect(window,"delete-event", G_CALLBACK(gtk_main_quit), NULL);

    workingDir = "/media/pi/SD CARD";
    lblDir = gtk_label_new("Save to: dir\t");
    btnConfigDir = gtk_button_new_with_label("Configure Directory");
    g_signal_connect(btnConfigDir, "clicked", G_CALLBACK(btnConfigDir_ConfigureDirectory), NULL);
    lblCams = gtk_label_new("\t\t\t\t\t\t\t\tCAM 1\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tCAM2\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tCAM3");
    lblFront = gtk_label_new("Front");
    lblBack = gtk_label_new("Back ");
    lblCurrentStatus = gtk_label_new("Current Status: Idle");

    //SET WINDOW SIZE AND TITLE
    //gtk_widget_set_size_request(window, 600, 400);
    gtk_window_set_title(GTK_WINDOW(window), "CaptureGui");

    //RESIZE IMAGES
    //image
    front_cams[0] = gtk_image_new();
    front_cams[1] = gtk_image_new();
    front_cams[2] = gtk_image_new();
    back_cams[0] = gtk_image_new();
    back_cams[1] = gtk_image_new();
    back_cams[2] = gtk_image_new();

    GdkPixbuf* pxb;
    pxb = gdk_pixbuf_new(0, 0, 8, 500, 300);
    gtk_image_set_from_pixbuf(GTK_IMAGE(front_cams[0]), pxb);
    gtk_image_set_from_pixbuf(GTK_IMAGE(front_cams[1]), pxb);
    gtk_image_set_from_pixbuf(GTK_IMAGE(front_cams[2]), pxb);
    gtk_image_set_from_pixbuf(GTK_IMAGE(back_cams[0]), pxb);
    gtk_image_set_from_pixbuf(GTK_IMAGE(back_cams[1]), pxb);
    gtk_image_set_from_pixbuf(GTK_IMAGE(back_cams[2]), pxb);

    //g_print("1");

    //front_cams[0] = gtk_image_new_from_file("/media/pi/SD CARD/cam-1_0_a_front.jpg");
    //set_image_scaled(front_cams[0], "/media/pi/SD CARD/cam-1_1_a_front.jpg");
    //set_image_scaled(front_cams[1], "/media/pi/SD CARD/cam-1_1_a_front.jpg");
    //set_image_scaled(front_cams[2], "/media/pi/SD CARD/cam-1_1_b_back.jpg");

    //PACK
    hboxDir = gtk_box_new(0, 0);
    hboxCamLabels = gtk_box_new(0, 0);
    hboxFrontPics = gtk_box_new(0, 0);
    hboxBackPics = gtk_box_new(0, 0);
    hboxStatus = gtk_box_new(0, 0);
    vbox = gtk_vbox_new(0, 10);
    gtk_box_pack_end(GTK_BOX(hboxDir), btnConfigDir, 0, 0, 0);
    gtk_box_pack_end(GTK_BOX(hboxDir), lblDir, 0, 0, 0);
    gtk_box_pack_start(GTK_BOX(hboxCamLabels), lblCams, 0, 0, 0);
    gtk_box_pack_start(GTK_BOX(hboxFrontPics), lblFront, 0, 0, 0);
    gtk_box_pack_start(GTK_BOX(hboxFrontPics), front_cams[0], 0, 0, 10);
    gtk_box_pack_start(GTK_BOX(hboxFrontPics), front_cams[1], 0, 0, 10);
    gtk_box_pack_start(GTK_BOX(hboxFrontPics), front_cams[2], 0, 0, 10);
    gtk_box_pack_start(GTK_BOX(hboxBackPics), lblBack, 0, 0, 0);
    gtk_box_pack_start(GTK_BOX(hboxBackPics), back_cams[0], 0, 0, 10);
    gtk_box_pack_start(GTK_BOX(hboxBackPics), back_cams[1], 0, 0, 10);
    gtk_box_pack_start(GTK_BOX(hboxBackPics), back_cams[2], 0, 0, 10);
    gtk_box_pack_start(GTK_BOX(hboxStatus), lblCurrentStatus, 0, 0, 10);
    gtk_box_pack_start(GTK_BOX(vbox), hboxDir, 0, 0, 0);
    gtk_box_pack_start(GTK_BOX(vbox), hboxCamLabels, 0, 0, 0);
    gtk_box_pack_start(GTK_BOX(vbox), hboxFrontPics, 0, 0, 0);
    gtk_box_pack_start(GTK_BOX(vbox), hboxBackPics, 0, 0, 0);
    gtk_box_pack_start(GTK_BOX(vbox), hboxStatus, 0, 0, 0);


    //gtk_box_pack_start(GTK_BOX(hbox), front_cams[0], 0, 0, 0);
    //gtk_box_pack_start(GTK_BOX(hbox), front_cams[1], 0, 0, 0);
    //gtk_box_pack_start(GTK_BOX(hbox), front_cams[2], 0, 0, 0);
    //gtk_box_pack_start(GTK_BOX(hbox), back_cams[0], 0, 0, 0);
    //gtk_box_pack_start(GTK_BOX(hbox), back_cams[1], 0, 0, 0);
    //gtk_box_pack_start(GTK_BOX(hbox), back_cams[2], 0, 0, 0);
    gtk_container_add(GTK_CONTAINER(window), vbox);

    //ADD ELEMENTS TO GUI
    //gtk_container_add(GTK_CONTAINER(window), image1);
    //gtk_container_add(GTK_CONTAINER(window), image2);
    //image2 = gtk_image_new_from_file("/media/pi/SD CARD/cam-1_0_b_back.jpg");
    //gtk_container_add(GTK_CONTAINER(window), image2);

    //SHOW GUI
    gtk_widget_show_all(window);
    gtk_main();
}

void set_image_scaled(GtkWidget* img, const char* path) {
    pixbuf = gdk_pixbuf_new_from_file(path, &err);
    g_assert_no_error(err);
    pxbscaled = gdk_pixbuf_scale_simple(pixbuf, 500, 300, GDK_INTERP_BILINEAR);
    gtk_image_set_from_pixbuf(GTK_IMAGE(img), pxbscaled);
    g_object_unref(pixbuf);
    g_object_unref(pxbscaled);
}

void* cam_main() {
    while (running == 1) {
    if (digitalRead(4) == 0)
        {
            printf("taking pics of %s item %i\n", (front == 1) ? "front" : "back", pic);
            for (int i = 0; i < count; i++)
            {
                int fd, retval;
                CameraFile *file;
                CameraFilePath cfPath;
                strcpy(cfPath.folder, "/");
                strcpy(cfPath.name, "foo1.jpg");
                printf("Capturing cam%i...\n", i + 1);
                int res = gp_camera_capture(cams[i], GP_CAPTURE_IMAGE, &cfPath, context);
                //printf(gp_port_result_as_string(res));
                printf("capture result: %i\n", res);
                //Camera won't take pic if busy and will continue to program end
                char buf[256];
                snprintf(buf, sizeof(buf), "%s/cam-%i_%i_%s.jpg", workingDir, i + 1, pic, (front == 1) ? "a_front" : "b_back"); //a_ to make front come before back otherwise systems will order incorrectly
                fd = open(buf, O_CREAT | O_WRONLY, 0644);
                retval = gp_file_new_from_fd(&file, fd);
                retval = gp_camera_file_get(cams[i], cfPath.folder, cfPath.name, GP_FILE_TYPE_NORMAL, file, context);
                retval = gp_camera_file_delete(cams[i], cfPath.folder, cfPath.name, context);
                gp_file_free(file);
                //debug
                //if (front == 1 && i == 0)
                    //set_image_scaled(front_cams[0], buf);
                if (front == 1)
                    set_image_scaled(front_cams[i], buf);
                else
                    set_image_scaled(back_cams[i], buf);
            }
            if (front == 1)
                front = 0;
            else
            {
                front = 1;
                pic += 1;
            }
            printf("pics taken...\n");
        }
    }
}

int main(int argc, char **argv)
{
    //Kill any processes using cams
    system("pkill -f gphoto2");

    //main_gtk();
    gtk_init(&argc, &argv);

    //Wiring pi init
    wiringPiSetupGpio();

    //Init
    context = gp_context_new();

    detect_cams();

    pthread_t logic_thread_handle, gui_thread_handle;
    pthread_create(&logic_thread_handle, NULL, cam_main, NULL);
    pthread_create(&gui_thread_handle, NULL, main_gtk, NULL);
    pthread_join(gui_thread_handle, 0);
    pthread_join(logic_thread_handle, 0);

    //Deinit
    for (int i = 0; i < count; i++)
    {
        gp_camera_exit(cams[i], context);
        gp_camera_free(cams[i]);
    }
    return 0;
}

void detect_cams() {
    //Detecting all cameras and loading them into mem
        //Detecting all cameras
    ret = gp_list_new(&list);
    if (ret < GP_OK) return 1;
    gp_list_reset(list);
    count = gp_camera_autodetect(list, context);
    if (count < 1)
    {
        printf("No cameras detected.\n");
        return 1;
    }


    //Open all cameras
    printf("Number of cameras: %d\n", count);
    cams = calloc(sizeof (Camera*), count);
    for (int i = 0; i < count; i++)
    {
        gp_list_get_name(list, i, &name);
        gp_list_get_value(list, i, &value);
        ret = open_cam(&cams[i], name, value, context);
        if (ret < GP_OK)
            fprintf(stderr, "Camera %s on port %s failed to open\n", name, value);
    }
}

int open_cam(Camera ** camera, const char *model, const char *port, GPContext *context) {
    GPPortInfoList      *portinfolist = NULL;
    CameraAbilitiesList *abilities = NULL;
    int     ret, m, p;
    CameraAbilities a;
    GPPortInfo  pi;

    ret = gp_camera_new (camera);
    if (ret < GP_OK) return ret;

    if (!abilities) {
        /* Load all the camera drivers we have... */
        ret = gp_abilities_list_new (&abilities);
        if (ret < GP_OK) return ret;
        ret = gp_abilities_list_load (abilities, context);
        if (ret < GP_OK) return ret;
    }

    /* First lookup the model / driver */
        m = gp_abilities_list_lookup_model (abilities, model);
    if (m < GP_OK) return ret;
        ret = gp_abilities_list_get_abilities (abilities, m, &a);
    if (ret < GP_OK) return ret;
        ret = gp_camera_set_abilities (*camera, a);
    if (ret < GP_OK) return ret;

    if (!portinfolist) {
        /* Load all the port drivers we have... */
        ret = gp_port_info_list_new (&portinfolist);
        if (ret < GP_OK) return ret;
        ret = gp_port_info_list_load (portinfolist);
        if (ret < 0) return ret;
        ret = gp_port_info_list_count (portinfolist);
        if (ret < 0) return ret;
    }

    /* Then associate the camera with the specified port */
        p = gp_port_info_list_lookup_path (portinfolist, port);
        switch (p) {
        case GP_ERROR_UNKNOWN_PORT:
                fprintf (stderr, "The port you specified "
                        "('%s') can not be found. Please "
                        "specify one of the ports found by "
                        "'gphoto2 --list-ports' and make "
                        "sure the spelling is correct "
                        "(i.e. with prefix 'serial:' or 'usb:').",
                                port);
                break;
        default:
                break;
        }
        if (p < GP_OK) return p;

        ret = gp_port_info_list_get_info (portinfolist, p, &pi);
        if (ret < GP_OK) return ret;
        ret = gp_camera_set_port_info (*camera, pi);
        if (ret < GP_OK) return ret;
    return GP_OK;
}

[因此,我正在Raspberry Pi 3B +型上使用C进行项目,并且正在使用GTK进行绘制。我在自己的线程上拥有GTK,在自己的线程上拥有程序逻辑。基本上,我的程序会拍照...

c linux multithreading gtk drawing
1个回答
0
投票

You cannot use GTK from different threads;只有调用gtk_init()gtk_main()的线程才能在GTK(和GDK)API上运行。

© www.soinside.com 2019 - 2024. All rights reserved.