retireSakiの日記

たぶん引退した?ソフトウエアエンジニアのブログ

GTKmm (3.0) と glade によるアプリ (14) - ディレクトリーツリーを表示と処理時間の測定

 今回は、ツリービューを使ったディレクトリーを表示します。
ただし、短時間の処理向けです。理由は後ほどに。
また、ついでにツリーデータを取得する時間の計測も行います。

f:id:retireSaki:20181205152134p:plain

 普通はあまりディレクトリーツリーを表示するようなことはないと思いますが、場合によっては特定のディレクトリー以下のツリー表示をしたい場合もあると思います。

 今回は、右上Entry-Widgetに入力したディレクトリーをルートディレクトリーとし、左側のTreeView-Widgetディレクトリーツリーを表示します。
また、開始ボタンクリックから全ツリーデータ取得完了までの時間を計測し、右下に msec 単位で処理時間を表示します。

ソースコードは最後に公開。(zipファイルをダウンロードできます)

A. glade の編集

f:id:retireSaki:20181205152348j:plain

 今回もメインウィンドウのみの構成です。
詳細はダウンロードして、paned_test.glade を確認してください。
※ GtkTreeView を GtkScrolledWindow 内に設置することで、ツリービューが現在のWidgetサイズを超える時、自動的にスクロールバー&スクロール処理を行うようになります。

B. MainWindow.h の編集


● MainWindow.h

protected:
…
    Gtk::Entry *m_edit_dir = nullptr;
    Gtk::Button *m_btn_start = nullptr;
    Gtk::Label *m_lbl_complete_time = nullptr;

    TreeRecords m_record;
    Glib::RefPtr< Gtk::TreeStore > m_treestore;

private:
…

    void on_m_btn_start_clicked()
    {
        m_treestore->clear();

        m_lbl_complete_time->set_label( "complete time\nstart" );

        const auto startTime = std::chrono::system_clock::now();

        Gtk::TreeModel::Row row;
        getdirs( m_edit_dir->get_text() , 0, row );

        const auto endTime = std::chrono::system_clock::now();
        const auto timeSpan = endTime - startTime;
        m_lbl_complete_time->set_label( "complete time\n"+
                to_string( std::chrono::duration_cast<std::chrono::milliseconds>(timeSpan).count() ) +
                "[ms]" );

        m_treeview->expand_all();
       
    }

    int getdirs(string dir, int ndirpos, Gtk::TreeModel::Row parent_row)
    {
        Gtk::TreeModel::Row row;
        struct stat stat_buf;
        struct dirent **namelist=NULL;
        string search_path;
        int ni, ndirs;

        ndirs = scandir(dir.c_str(), &namelist, NULL, alphasort);

        if (ndirs == -1) {
            cout << "ERROR scandir" <<  endl;
            free(namelist);
            return -1;
        }

        for (ni = 0;ni < ndirs;ni++){

            if ( (string(namelist[ni]->d_name) != ".") &&
                 (string(namelist[ni]->d_name) != "..") ){

                search_path = dir+string(namelist[ni]->d_name);

                if (stat(search_path.c_str(), &stat_buf) == 0){

                    if ((stat_buf.st_mode & S_IFMT) == S_IFDIR){

                        if (ndirpos == 0)
                            row = *( m_treestore->append() );
                        else
                            row = *( m_treestore->append( parent_row.children() ) );

                        row[ m_record.m_col_name ] = namelist[ni]->d_name;

                        getdirs(dir + string(namelist[ni]->d_name) + "/", ndirpos + 1, row);

                    }

                }
                else{
                    cout << "ERROR FILE-STAT" <<  endl << endl;
                }
            }
        }

        free(namelist);

        return 0;
    }
…

1) on_m_btn_start_clicked() 関数

(1.1) TreewView-Widget で表示されている全行をクリア
 void Gtk::TreeStore::clear ( );
 TreeView に関連付けされている TreeAtore の全行をクリアすることで、TreewView-Widgetの全行をクリアします。

(1.2) ツリーデータを取得し、TreeModel にセット
 int getdirs (string, int, Gtk::TreeModel::Row);
   string : 検索するディレクトリー
   int : ルートからの階層の深さ
   Row : TreeModelの行クラス

 この関数は下位のディレクトリーがなくなるまで、再帰的に自身を呼び出します。

(1.3) 処理時間の計測
 system_clock はシステムのリアルタイムのクロックが扱えます。

 std::chrono::duration_cast(時間差).count();
 とすることで、開始時刻と終了時刻の時間差をミリ秒で取得できます。


2) getdirs(string, int, Gtk::TreeModel::Row) 関数

(2.1) scandir() で指定したディレクトリー走査をし、全エントリーを取得
 int scandir ( const char *dirp,
  struct dirent ***namelist,
   int (*filter)(const struct dirent *),
   int (*compar)(const struct dirent **, const struct dirent **) );
   dir : 指定ディレクトリー
   namelist : 取得結果が保存される構造体ポインター
     ※ 使用後は free でメモリーを開放する必要があります。
   filter : フィルター比較関数
   compair : ソート比較関数
     alphasort : アルファベット順 ( strcoll )
     versionsort : 自然な数字順 ( strverscmp )

   戻り値 : 取得したエントリー数
      (-1) の時はエラー (メモリ不足)

(2.2) ディレクトリーかどうか判断

 stat() 関数で、エントリーの状態を取得し、ディレクトリーか判断する。

 S_IFMT (0170000) ファイル種別を示すビット領域を表すビットマスクを使い、ファイルの種別を判断します。

   S_IFSOCK  0140000 ソケット
   S_IFLNK 0120000 シンボリックリンク
   S_IFREG 0100000 通常のファイル
   S_IFBLK 0060000 ブロックデバイス
   S_IFDIR 0040000 ディレクト
   S_IFCHR 0020000 キャラクターデバイス
   S_IFIFO 0010000 FIFO
 if (( stat_buf.st_mode & S_IFMT ) == S_IFDIR ) を、POSIXのマクロを使って
 if ( S_ISREG( stat_buf.st_mode ) ) としても結果は同じです。
   S_ISREG(m)   通常のファイルか? 
   S_ISDIR(m) ディレクトリか?
   S_ISCHR(m) キャラクターデバイスか?
   S_ISBLK(m) ブロックデバイスか?
   S_ISFIFO(m) FIFO (名前付きパイプ) か?
   S_ISLNK(m) シンボリックリンクか? (POSIX.1-1996 にはない)
   S_ISSOCK(m) ソケットか? (POSIX.1-1996 にはない)

C. MainWindow.cc の編集

 これは、今までやってきたことと同じですので、ダウンロード後ソースファイルをチェックしてください。


※今回は、ボタンクリックのイベントのまま、ディレクトリーの走査やツリーの更新を行っているため、タスクを握ったままになってしまいます。処理中は表示も何もできないので、長時間処理を要するようなディレクトリーには向きません。

このような処理はサンプルが少ないので、今回のサンプルアプリは貴重だと思います。


最後に、全てのファイルを圧縮したファイル(zip)を載せます。

●ソース一式

www.mediafire.com

GTKプログラミングを学習するなら!

Gtk+3入門

Gtk+3入門

GTK+・GDKによるLinuxアプリケーション開発 (New riders)

GTK+・GDKによるLinuxアプリケーション開発 (New riders)

Learn GTK# Programming

Learn GTK# Programming

注目記事

「Amazon.co.jpアソシエイト」または「[乙の名称を挿入]は、Amazon.co.jpを宣伝しリンクすることによってサイトが紹介料を獲得できる手段を提供することを目的に設定されたアフィリエイトプログラムである、Amazonアソシエイト・プログラムの参加者です。