Partage
  • Partager sur Facebook
  • Partager sur Twitter

gtkmm et barre de progression/copie fichier

    2 octobre 2020 à 15:14:05

    Bonjour,

    J'ai fait un programme qui doit afficher une barre de progression pendant la copie d'un fichier sur une clé usb.

    A partir d'un bouton je lance la copie mais ma barre de progression n'avance pas. Elle passe d'un coup à 100% quand la copie est complètement finie.

    Dans le même temps le bouton qui lance la copie reste grisé. C'est comme si tout était bloqué tant que la copie n'est pas terminée.

    Je ne vois pas comment libéré la barre de progression pour qu'elle s'affiche correctement.

    voici le programme

    main.cpp

    #include "Fenetre.h"
    
    
    int main(int argc, char*argv[])
    {
        Gtk::Main app(argc, argv);
        Fenetre usbKey;
        Gtk::Main::run(usbKey);
        return 0;
    }

    Fenetre.h

    #ifndef FENETRE_H
    #define FENETRE_H
    
    #include <gtkmm-3.0/gtkmm/main.h>
    #include <gtkmm-3.0/gtkmm/window.h>
    #include <gtkmm-3.0/gtkmm/box.h>
    #include <gtkmm-3.0/gtkmm/button.h>
    #include <gtkmm-3.0/gtkmm/buttonbox.h>
    #include <gtkmm-3.0/gtkmm/comboboxtext.h>
    #include <gtkmm-3.0/gtkmm/filechooserdialog.h>
    #include <gtkmm-3.0/gtkmm/messagedialog.h>
    #include <gtkmm-3.0/gtkmm/progressbar.h>
    #include <gtkmm-3.0/gtkmm/radiobutton.h>
    #include <gtkmm-3.0/gtkmm/stock.h>
    #include <iostream>
    #include <thread>
    #include <filesystem>
    #include <chrono>
    #include <string>
    #include <cstdlib>
    #include <cstring>
    #include <vector>
    #include <fstream>
    
    class Fenetre : public Gtk::Window
    {
        public:
            Fenetre();
            virtual ~Fenetre();
    
        protected:
            std::string filename, type, type1, se, usb, win, usb1, win1;
            int typeNum;
            void ouvrir_fichier();
            void type_format(Gtk::RadioButton &button); //boutons radios type de formatage
            void os(Gtk::RadioButton &button1); // boutons radios linux ou windows
            void lance_copie(std::string usb, std::string win, Gtk::ProgressBar &bar);
    
        private:
            Gtk::ComboBoxText listeDeroulante;
            Gtk::VBox boiteV;
            Gtk::HBox boiteH;
            Gtk::RadioButton *m_SelectedButton{nullptr}, *m_SelectedButton1{nullptr};
            Gtk::RadioButtonGroup groupe, groupe1;
            Gtk::RadioButton fat32, ntfs, ext4, linux, windows;
            Gtk::HButtonBox boutonBoxH;
            Gtk::Button ouvrirFichier, boutonQ, copiersurUsb;
            Gtk::ProgressBar m_ProgressBar;
    
    };
    
    #endif // FENETRE_H
    

    Fenetre.cpp

    #include "Fenetre.h"
    
    long double f_size(std::string chemin){
        //unsigned long long f_size = 0;
        long double f_size = 0.0;
        for(std::filesystem::recursive_directory_iterator it(chemin); it != std::filesystem::recursive_directory_iterator(); it++){
            if(!std::filesystem::is_directory(*it)){
                f_size += std::filesystem::file_size(*it);
            }
        }
    return f_size;
    }
    
    void remove_file(std::string usb_folder, std::string win_folder){
        if(std::filesystem::exists(usb_folder)){
            system("sudo mountpoint -q $usb && umount $usb");
            std::filesystem::remove(usb_folder);
        }
        if(std::filesystem::exists(win_folder)){
            system("sudo mountpoint -q $win && umount $win");
            std::filesystem::remove(win_folder);
        }
    }
    
    Fenetre::Fenetre():
    boiteV(false, 10), boiteH(false, 10),
    groupe(), groupe1(), fat32(groupe, "fat32"), ntfs(groupe, "ntfs"), ext4(groupe, "ext4"), linux(groupe1, "linux"), windows(groupe1, "win"),
    boutonBoxH(Gtk::BUTTONBOX_END, 10), boutonQ(Gtk::Stock::QUIT), copiersurUsb("Lance la copie"), ouvrirFichier("Fichier iso"),
    usb(), win()
    {
    
        //initialisation
        usb = "/media/duncan/usb",
        win = "/media/duncan/win";
    
    
        if("usb.txt"){
            std::filesystem::remove("usb.txt");
        }
    
        system ("lsblk -dPo tran,label,model,size,path | grep usb | awk '/sd/' | sed 's/\"//g' >> usb.txt");
    
        //ctor
        resize(320,110);
        set_border_width(10);
        set_title("Création clef usb_bootable");
        set_position(Gtk::WIN_POS_CENTER);
    
        //liste déroulante
        if (!std::filesystem::is_empty("usb.txt")){
            listeDeroulante.append("Choisir une clé USB");
    
            std::string usbFile("usb.txt"), ligneTemp;
            std::vector <std::string> cusb;
            int i = 0;
    
            std::ifstream file(usbFile.c_str(), std::ios::in);
            if(file){
    
            while (getline(file, ligneTemp)){
                cusb.push_back(ligneTemp);
                std::cout << i+1 << " " << cusb[i] << std::endl;
                listeDeroulante.append(cusb[i]);
                i++;
            }
            file.clear();
            file.seekg(0, std::ios::beg);
            file.close();
            listeDeroulante.set_active(0);
            }
            else{
                std::cout << "ERREUR: Impossible d'ouvrir le fichier." << std::endl;
            }
        }
        else{
            std::remove("usb.txt");
            listeDeroulante.append("Pas de clé usb");
            listeDeroulante.set_active(1);
        }
    
        //Boutons radios
        fat32.signal_toggled().connect(sigc::bind(sigc::mem_fun(*this, &Fenetre::type_format),sigc::ref(fat32)));
        ntfs.signal_toggled().connect(sigc::bind(sigc::mem_fun(*this, &Fenetre::type_format),sigc::ref(ntfs)));
        ext4.signal_toggled().connect(sigc::bind(sigc::mem_fun(*this, &Fenetre::type_format),sigc::ref(ext4)));
        linux.signal_toggled().connect(sigc::bind(sigc::mem_fun(*this, &Fenetre::os),sigc::ref(linux)));
        windows.signal_toggled().connect(sigc::bind(sigc::mem_fun(*this, &Fenetre::os),sigc::ref(windows)));
    
        //ouvrir fichier iso
        ouvrirFichier.set_can_focus(true);
        ouvrirFichier.signal_clicked().connect(sigc::mem_fun(*this, &Fenetre::ouvrir_fichier));
    
        //lance la copie
        copiersurUsb.signal_clicked().connect(sigc::bind<std::string, std::string>(sigc::mem_fun(*this, &Fenetre::lance_copie), usb, win, sigc::ref(m_ProgressBar)));
        copiersurUsb.set_focus_on_click(false);
    
        //bouton quitter
        boutonQ.signal_clicked().connect(sigc::ptr_fun(&Gtk::Main::quit));
    
        boiteV.pack_start(listeDeroulante);
        boiteV.pack_start(boiteH);
        boiteV.pack_start(boutonBoxH);
        boiteV.pack_start(m_ProgressBar);
        boiteH.pack_start(fat32);
        boiteH.pack_start(ntfs);
        boiteH.pack_start(ext4);
        boiteH.pack_end(linux);
        boiteH.pack_end(windows);
        boutonBoxH.pack_start(ouvrirFichier);
        boutonBoxH.pack_start(copiersurUsb);
        boutonBoxH.pack_start(boutonQ);
        add(boiteV);
        show_all();
    
    }
    
    Fenetre::~Fenetre()
    {
        //dtor
    }
    //type de formatage
    void Fenetre::type_format(Gtk::RadioButton &button)
    {
        if(m_SelectedButton != &button && button.get_active())
          {
             m_SelectedButton = &button;
             std::cout << "Bouton radio "<< m_SelectedButton->get_label() << " sélectionné.\n" << std::flush;
             type = m_SelectedButton->get_label();
          }
        if(type == "vfat"){
            typeNum = 0;
        }
        else if(type == "ntfs"){
            typeNum = 1;
        }
        else{
            typeNum = 2;
        }
    }
    //type d'OS : windows ou linux
    void Fenetre::os(Gtk::RadioButton &button1){
        if(m_SelectedButton1 != &button1 && button1.get_active()){
            m_SelectedButton1 = &button1;
            std::cout << "Bouton radio " << m_SelectedButton1->get_label() << " sélectionné.\n" << std::flush;
            se = m_SelectedButton1->get_label();
        }
    }
    
    //fichier iso
    void Fenetre::ouvrir_fichier()
    {
        Gtk::FileChooserDialog dialog("Choisir un fichier", Gtk::FILE_CHOOSER_ACTION_OPEN);
        dialog.set_transient_for(*this);
    
        //Add response buttons the the dialog:
        dialog.add_button("_Annuler", Gtk::RESPONSE_CANCEL);
        dialog.add_button("_Ouvrir", Gtk::RESPONSE_OK);
    
        //Add filters, so that only certain file types can be selected:
    
        auto filter_iso = Gtk::FileFilter::create();
        filter_iso->set_name("iso files");
        filter_iso->add_mime_type("application/x-cd-image");
        dialog.add_filter(filter_iso);
    
        auto filter_text = Gtk::FileFilter::create();
        filter_text->set_name("Text files");
        filter_text->add_mime_type("text/plain");
        dialog.add_filter(filter_text);
    
        auto filter_cpp = Gtk::FileFilter::create();
        filter_cpp->set_name("C/C++ files");
        filter_cpp->add_mime_type("text/x-c");
        filter_cpp->add_mime_type("text/x-c++");
        filter_cpp->add_mime_type("text/x-c-header");
        dialog.add_filter(filter_cpp);
    
        auto filter_any = Gtk::FileFilter::create();
        filter_any->set_name("Any files");
        filter_any->add_pattern("*");
        dialog.add_filter(filter_any);
    
        //Show the dialog and wait for a user response:
        int result = dialog.run();
    
        //Handle the response:
        switch(result)
        {
            case(Gtk::RESPONSE_OK):
            {
            //Notice that this is a std::string, not a Glib::ustring.
            filename = dialog.get_filename();
            std::cout << "Fichier sélectionné : " << filename << std::endl;
            break;
            }
            case(Gtk::RESPONSE_CANCEL):
            {
            std::cout << "Clic sur annuler" << std::endl;
            break;
            }
            default:
            {
            std::cout << "Mauvais clic" << std::endl;
            break;
            }
        }
    }
    void Fenetre::lance_copie(std::string usb, std::string win, Gtk::ProgressBar &bar){
    
        //FORMATAGE
        //chemin vers la clé usb
        std::string pathUsb(listeDeroulante.get_active_text()), chemin, chemin1;
        std::string::iterator ptr;
        for(ptr = pathUsb.end() - 8; ptr != pathUsb.end(); ptr++){
            chemin += *ptr;
        }
        chemin1 = "pathToUsb=" + chemin + "1";
        char c[chemin1.size() + 1];
        strcpy(c, chemin1.c_str());
        putenv(c); //pathToUsb=/dev/sdf1 par exemple
    
        //formatage
        system("umount $pathToUsb");
        if(type == ""){
            type = "vfat";
            typeNum = 0;
        }
        switch(typeNum){
            case 0:
            system("sudo mkfs.vfat -n USB_BOOT $pathToUsb"); //vfat
            break;
            case 1:
            system("sudo mkfs.ntfs -f -L USB_BOOT $pathToUsb"); //ntfs
            break;
            case 2:
            system("sudo mkfs.ext4 -L usb_boot $pathToUsb"); //ext4
            break;
            default:
            std::cout << "Erreur type de formatage" << std::endl;
            break;
        }
    
        //création des points de montage
        usb1 = "usb=" + usb;
        win1 = "win=" + win;
    
        char u[usb1.size() + 1];
        char w[win1.size() + 1];
        strcpy(u, usb1.c_str());
        strcpy(w, win1.c_str());
        putenv(u);
        putenv(w);
    
        if(std::filesystem::exists(usb)){
            system("sudo mountpoint -q $usb && umount $usb");
            std::filesystem::remove(usb);
        }
        if(std::filesystem::exists(win)){
            system("sudo mountpoint -q $win && umount $win");
            std::filesystem::remove(win);
        }
        std::filesystem::create_directory(usb);
        std::filesystem::create_directory(win);
    
        std::string filename1 = "filename=" + filename;
        char f[filename1.size() + 1];
        strcpy(f, filename1.c_str());
        putenv(f);
    
        //Monte le fichier iso
        if(se == ""){
            se = "linux";
        }
        if(se == "linux"){
            system("sudo mount -t iso9660 -o loop,ro,unhide $filename $win");
        }
        else{
            system("sudo mount -t udf -o loop,ro,unhide $filename $win");
        }
        //monte la clé usb
        system("sudo mount $pathToUsb $usb");
    
        // taille du fichier iso
        //unsigned long long f_size_win = 0;
        long double f_size_win = 0.00;
        f_size_win = f_size(win);
    
        // copie l'iso
        std::thread t1([usb, win](){
            std::cout << "Dans le thread t1" << std::endl;
            auto const copyOption = std::filesystem::copy_options::recursive | std::filesystem::copy_options::skip_symlinks;
            std::filesystem::copy(win, usb, copyOption); //copie fichier
            std::cout << "FIN DE LA COPIE" << std::endl;
    
        });
        // barre de progression
        std::thread t2([usb, f_size_win, &bar](){
    
            Gtk::ProgressBar *progressBar;
            progressBar = &bar;
            progressBar->set_receives_default(true);
            std::cout << "Dans le thread t2" << std::endl;
            //unsigned long long f_size_usb = 0;
            long double f_size_usb = 0.00;
            double valeur = 0.00;
    
            std::cout << usb << std::endl;
            std::cout << "Taille de l'iso = " << f_size_win << std::endl;
    
            do{
                std::this_thread::sleep_for(std::chrono::seconds(1));
                //std::cout << "1 Taille dossier usb = " << f_size_usb << std::endl;
                valeur = f_size_usb / f_size_win;
                std::cout << "Valeur fraction = " << valeur << "\n" << std::flush;
                progressBar->set_fraction(valeur);
                std::cout << "Get fraction = " << progressBar->get_fraction() << std::endl;
                f_size_usb = f_size(usb);
                //std::cout << "2 Taille dossier usb = " << f_size_usb << std::endl;
            }while(valeur < 1);
            //while(f_size_usb < f_size(usb));
        });
    
        if(t1.joinable()){
            t1.join();
            std::cout << "thread 1 join" << std::endl;
        }
        if(t2.joinable()){
            t2.join();
            std::cout << "thread 2 join" << std::endl;
        }
    
        std::thread t3([usb, win](){
    	std::cout << "Dans le thread t3" << std::endl;
    	system("sudo umount -d $win");
    	system("sudo umount $usb");
    	remove_file(usb, win);
        });
    
    	std::thread t4([usb, win](){
        std::cout << "Dans le thread t4" << std::endl;
            while(std::filesystem::exists(usb) && std::filesystem::exists(win)){
                std::cout << "=" << std::flush;
                std::this_thread::sleep_for(std::chrono::seconds(1));
            }
    	});
    	if(t3.joinable()){
            t3.join();
            std::cout << "thread 3 join" << std::endl;
        }
        if(t4.joinable()){
            t4.join();
            std::cout << "thread 4 join" << std::endl;
            std::cout << "Fin de la synchronisation" << std::endl;
        }
    }
    





    • Partager sur Facebook
    • Partager sur Twitter
      2 octobre 2020 à 16:13:47

      Je ne connais pas gtkmm, je sais que sous Windows il existe une fonction de copie de fichier : CopyFileEx qui permet de désigner une fonction CallBack qui renverra l'état de la copie. pour les autres système, je sais pas.

      /!\ Ça ne fonctionne que sous Windows et sur des applications Desktop (avec une bar de progression, c'est mieux).    

      • Partager sur Facebook
      • Partager sur Twitter
        2 octobre 2020 à 23:57:16

        Je ne connais pas d'équivalent sous linux.

        Je pense qu'il faut utiliser une sorte de signal qui rafraîchit la barre de progression, mais je ne trouve rien à ce sujet.

        • Partager sur Facebook
        • Partager sur Twitter
          3 octobre 2020 à 0:39:31

          Tu dois éviter tous ce qui est bloquant dans le thread principal d'une application GUI, comme std::thread::join qui attend l'arrêt des threads concernés. Si le thread principal est bloqué, les mises à jour de l'interface graphique ne se font pas et rien n'est cliquable (la fonction system est également bloquante, il faudrait peut être la déplacer dans le thread).

          De plus, tu ne peux pas manipuler directement les fonctions des widgets en dehors du thread principal, il faut utiliser Glib::Dispatcher envoyer un signal depuis un thread qui va déclencher un slot dans le thread principal. Il y a un exemple sur la façon de gérer un Gtk::ProgressBar avec un std::thread dans la doc de gtkmm.

          • Partager sur Facebook
          • Partager sur Twitter
            3 octobre 2020 à 12:55:52

            D'accord je comprend mieux.

            Je pensais qu'en utilisant 2 threads en parallèle , un pour la copie l'autre pour la barre de progression ça pouvait le faire.

            Je vais étudier l'exemple que tu donnes.

            Aurais-tu quelques lectures pertinentes à propos du multitreading, même en anglais ?

            • Partager sur Facebook
            • Partager sur Twitter

            gtkmm et barre de progression/copie fichier

            × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
            × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
            • Editeur
            • Markdown