C GTK окно не удается перерисовать? (Объект обновления Thread1 неожиданно используется объектом Thread2 ??) - PullRequest
1 голос
/ 21 марта 2020

Итак, я работаю над проектом в C для Raspberry Pi Model 3B + и использую GTK для рисования. У меня GTK в собственном потоке, а моя программа logi c в своем собственном потоке.

По сути, моя программа делает снимки с 3-х зеркал с помощью gphoto2 и сохраняет их в jpgs, затем GTK загружает эти JPG и изменяет их размеры до 500x300 с pixbuf и обновляет изображение в GUI

Проблема, с которой я столкнулся, заключается в том, что окно GTK не сможет перерисоваться, и это очень противоречиво, когда иногда это происходит после того, как 11 изображений В некоторых случаях это может произойти даже после того, как были сделаны 2 или 3 снимка.

Любая помощь приветствуется: один поток просто инициализирует gtk, а затем вызывает GTK main для выполнения GUI, а другой выполняет мою программу logi c, обновляет файлы jpg и затем обновляет объекты GTK. для gtk main перерисовать. Может быть, это как-то связано с тем, что мой поток обновляет объекты gtk в неподходящее время, и l oop работает gtk main, аварийно завершает работу и останавливается ??

Мой исходный GUI выглядит так: enter image description here

Черные ящики не являются проблемой, это просто пустые пиксельные буферы по умолчанию до того, как были сделаны какие-либо снимки. Проблема заключается в том, что он потерпит неудачу, и изображение просто превратится в цвет фона (см. На сетке «CAM2 Front»), а затем после этого вызова все окно горит и ничего не делает. Я могу перетащить его, и он будет рисовать вещи, которые я перетаскиваю, но это все.

enter image description here

Я знаю, что это целое приложение, которое не в состоянии рисовать, потому что я получаю такие результаты, когда я перемещаю окно.

enter image description here

Любая помощь очень важна. Я уже давно с этим играюсь.

Этот код вызывается моей программой logi c main thread l oop, чтобы обновить изображение до нового принятого 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);

}

Это основной поток logi c, который обновляет объект 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");
    }
}

}

My весь код для справки:

#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;
}

1 Ответ

0 голосов
/ 22 марта 2020

Вы не можете использовать GTK из разных потоков ; только API-интерфейс GTK (и GDK) может работать только с потоками, вызывающими gtk_init() и gtk_main().

Надлежащий способ использования потоков в приложении GTK - это использование рабочего потока для своей длительной работы, код блокировки и всякий раз, когда вам нужно обновить GUI, используйте g_main_context_invoke() с основным контекстом NULL.

В качестве альтернативы вы можете использовать GTask, чтобы создать свою собственную асинхронную задачу, выполняющуюся в потоке, и обновить GUI после завершения задачи.

В вашем примере кода вы должны когда-либо вызывать только set_image_scaled() - g_main_context_invoke(), и вы не следует помещать GUI в собственную нить.

...