retireSakiの日記

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

Qtで開いたファイルパスを履歴保存して、メニューに表示・ファイルを開く

 今回は前回作成した

retiresaki.hatenablog.com

をベースに、開いたファイルの履歴保存とメニューに『最近開いたファイル』として一覧表示の機能を追加します。

 ファイルの履歴機能は、多くのアプリが実装していますが、メニュー処理にちょっとしたコツがあります。
履歴は最大20ファイルで。開いたファイルが履歴に存在していたら、古い方を削除します。
また、表示しているファイルパスは省略していない手抜きです(^^ゞ

 記事後半でソース・リソース等一式のZIPファイルがダウンロードできます。
また、Ubuntu 18.04 LTS で作成しています。

 今回は mainwindow.cpp と mainwindow.h のみ張り付けます。
履歴保存・取得は、appinitdata.cpp で行っていますが、あえて掲載するほどでもないので、ZIPファイルをダウンロードして確認してください。


mainwindow.h

#ifndef APPINITDATA_H
#define APPINITDATA_H

#include <QMainWindow>
#include <QApplication>
#include <QFileInfo>
#include <QSettings>

class appInitData
{
public:
    appInitData();

    void set_appInitDatas(QWidget *p_win, QString m_sid);
    void get_appInitDatas(QWidget *p_win, QString m_sid);

    bool m_bbyteArray;
    QString m_byteArray_name;
    QByteArray m_byteArray;

    QString m_sLastopenPath;

    // history
    QStringList m_listOpenedHistory;

    void set_OpenedHistory(QString strFilepath);
    void get_OpenedHistory();


private:
    QString m_SettingsFile;
    QString m_OrganizationName;

};

#endif // APPINITDATA_H


mainwindow.cpp

<feff>#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{

    ui->setupUi(this);

    // get init
    appInit.m_byteArray_name = "treeWidget_playlist";
    appInit.get_appInitDatas(this->window(), "MAIN");
    appInit.get_OpenedHistory();
    setMenu_Recently_files();

    // setting status bar
    ui->statusBar->addWidget(label_MessageBar_normal);
    label_MessageBar_normal->setText(tr("File not selected"));

    // setting treeWidget_playlist
    QStringList list_label;
    list_label << tr("tag") << tr("value");
    ui->treeWidget_playlist->header()->setSectionResizeMode(QHeaderView::Interactive);
    ui->treeWidget_playlist->setHeaderLabels(list_label);
    if (appInit.m_bbyteArray)
        ui->treeWidget_playlist->header()->restoreState(appInit.m_byteArray);

    // etc init
    editing_Filepath = appInit.m_sLastopenPath;

    setAcceptDrops(true);
}

MainWindow::~MainWindow()
{
    // set init
    appInit.m_byteArray_name = "treeWidget_playlist";
    appInit.m_byteArray = ui->treeWidget_playlist->header()->saveState();
    appInit.m_bbyteArray = true;

    if (editing_Filepath.length() == 0)
        appInit.m_sLastopenPath = "";
    else{
        QFileInfo fi = QFileInfo(editing_Filepath);
        if (fi.isFile())
            appInit.m_sLastopenPath = QFileInfo(editing_Filepath).path();
        else
            appInit.m_sLastopenPath = editing_Filepath;
    }

    appInit.set_appInitDatas(this->window(), "MAIN");

    delete ui;
}

/* from main */
void MainWindow::setParam_readfile(QString param)
{

    if (param.length() > 0){
        QString suffix = QFileInfo(param).suffix();
        if (suffix.toLower() == "xspf"){
            param_readfile = param;
            //open_PlayLlist_file(param);
            QTimer::singleShot(200, this, SLOT(getParamFile()));
        }
        else{
            QMessageBox msgBox;
            msgBox.setText("The file specified in the parameter is not a file with the XSPF extension.");
            msgBox.exec();
        }
    }

}

void MainWindow::getParamFile()
{
    open_PlayLlist_file(param_readfile);
}

/* Menu actions */
void MainWindow::on_action_Quit_triggered()
{
    this->close();
}

void MainWindow::on_action_Open_triggered()
{

    QString init_path;

    if (editing_Filepath.length() > 3){
        QFileInfo fi = QFileInfo(editing_Filepath);
        if (fi.isFile())
            init_path = QFileInfo(editing_Filepath).path();
        else
            init_path = editing_Filepath;
    }
    else
        init_path = QStandardPaths::standardLocations(QStandardPaths::MoviesLocation)[0];

    QString filename = QFileDialog::getOpenFileName(
                this,
                tr("Open Playlist file"),
                init_path,
                tr("Playlist File (*.xspf);;ALL file (*.*)"));
    if (filename.length()==0){
        this->statusBar()->showMessage(tr("select : Canseled"),3000);
        return;
    }

    open_PlayLlist_file(filename);
}

void MainWindow::open_PlayLlist_file(QString filename)
{
    editing_Filepath = filename;
    //label_MessageBar_normal->setText(editing_Filepath);
    this->statusBar()->showMessage(tr("select : ")+filename,3000);

    XML_tree_open();

    label_MessageBar_normal->setText(editing_Filepath);
}

void MainWindow::on_action_Save_triggered()
{

}

void MainWindow::on_action_SaveAS_triggered()
{

}

void MainWindow::on_action_New_triggered()
{
    set_NewData();

    label_MessageBar_normal->setText("( new file )");
}

void MainWindow::on_action_About_triggered()
{
    aboutDialog *pdialog = new aboutDialog(this);
    pdialog->exec();
    delete pdialog;
}

// set menu from opened history
void MainWindow::setMenu_Recently_files()
{

    // check on first creation
    if (action_Recently_file_Group == nullptr){
        // first creation
        action_Recently_file_Group = new QActionGroup(this);

        // create 20 actions
        for (int ni = 0;ni < 20;ni++){
            QAction* paction = new QAction("filepath", action_Recently_file_Group);
            paction->setVisible(false);
            ui->menu_Recently_files->addAction(paction);
        }

        connect(action_Recently_file_Group, SIGNAL(triggered(QAction*)),
                 this, SLOT(onaction_Recently_file_Group_triggered(QAction*)) );
    }
    else{
        // hide all actions
        QList<QAction *> list_action = action_Recently_file_Group->actions();
        foreach(QAction *paction, list_action){
            paction->setVisible(false);
        }
    }

    // set filepath in m_listOpenedHistory to display
    QList<QAction *> list_action = action_Recently_file_Group->actions();
    int nlist = 0;
    foreach (QString filepath, appInit.m_listOpenedHistory){
        list_action[nlist]->setText(filepath);
        list_action[nlist]->setVisible(true);
        nlist++;
    }

}

// triggered sub menu of Recently files
void MainWindow::onaction_Recently_file_Group_triggered(QAction *paction)
{
    // opens
    open_PlayLlist_file(paction->text());
}


/************************************************************
    new settings for TreeWidget
 ************************************************************/
void MainWindow::set_NewData()
{
    ui->treeWidget_playlist->clear();

    QStringList liststr;

    liststr << "playlist version" << "1";
    QTreeWidgetItem *ptree_top = new QTreeWidgetItem(liststr, 0);
    ptree_top->setToolTip(0,tr("attibute in playlist"));
    ui->treeWidget_playlist->addTopLevelItem(ptree_top);

    liststr.clear();
    liststr << "title" << "";
    QTreeWidgetItem *ptree_item_1 = new QTreeWidgetItem(liststr, 0);
    ptree_item_1->setFlags(Qt::ItemIsEditable|Qt::ItemIsEnabled);
    ptree_top->addChild(ptree_item_1);

    liststr.clear();
    liststr << "<trackList>" << "";
    QTreeWidgetItem *ptree_item_tracklist = new QTreeWidgetItem(liststr, 0);
    ptree_top->addChild(ptree_item_tracklist);

    liststr.clear();
    liststr << "<track>" << "";
    QTreeWidgetItem *ptree_item_track = new QTreeWidgetItem(liststr, 0);
    ptree_item_tracklist->addChild(ptree_item_track);

    liststr.clear();
    liststr << "title" << "";
    QTreeWidgetItem *ptree_item_detail_title = new QTreeWidgetItem(liststr, 0);
    ptree_item_detail_title->setFlags(Qt::ItemIsEditable|Qt::ItemIsEnabled);
    ptree_item_track->addChild(ptree_item_detail_title);

    liststr.clear();
    liststr << "location" << "";
    QTreeWidgetItem *ptree_item_detail_location = new QTreeWidgetItem(liststr, 0);
    ptree_item_detail_location->setFlags(Qt::ItemIsEditable|Qt::ItemIsEnabled);
    ptree_item_track->addChild(ptree_item_detail_location);

    ui->treeWidget_playlist->expandAll();
}

/************************************************************
    Get Playlist and Set TreeWidget
 ************************************************************/
/* functions */
void MainWindow::XML_tree_open()
{
    clearFileinfo();
    ui->treeWidget_playlist->clear();

    QFile file(editing_Filepath);
    if (!file.open(QFile::ReadOnly | QFile::Text)) {
        QMessageBox::warning(this, tr("PLeditor"),
                             tr("Cannot read file %1:\n%2.")
                             .arg(editing_Filepath)
                             .arg(file.errorString()));
        this->statusBar()->showMessage(tr("Cannot open file : ")+editing_Filepath, 3000);
        return;
    }

    XML_tree_read(&file);

    // set open file history
    appInit.set_OpenedHistory(editing_Filepath);
    setMenu_Recently_files();
}

bool MainWindow::XML_tree_read(QIODevice *device)
{
    QString errorStr;
    int errorLine;
    int errorColumn;

    if (!domDocument.setContent(
                device, true, &errorStr, &errorLine,&errorColumn)) {
        QMessageBox::information(window(), tr("PLeditor"),
                                 tr("Parse error at line %1, column %2:\n%3")
                                 .arg(errorLine)
                                 .arg(errorColumn)
                                 .arg(errorStr));
        this->statusBar()->showMessage(
                    tr("Parse error in file : ")+editing_Filepath, 3000);
        return false;
    }

    QDomElement root = domDocument.documentElement();
    if (root.tagName() != "playlist") {
        QMessageBox::information(window(), tr("PLeditor"),
                                 tr("The file is not an playlist file."));
        this->statusBar()->showMessage(
                    tr("not an playlist : ")+editing_Filepath, 3000);
        return false;
    } else if (root.hasAttribute("version")
               && root.attribute("version") != "1") {
        QMessageBox::information(window(), tr("PLeditor"),
                                 tr("The file is not an playlist version 1 "
                                    "file."));
        this->statusBar()->showMessage(
                    tr("not an playlist version: ")+editing_Filepath, 3000);
        return false;
    }

    QStringList liststr;

    QDomNamedNodeMap attrMap = root.attributes();

    liststr << "playlist "+attrMap.item(0).toAttr().name() << attrMap.item(0).toAttr().value();
    QTreeWidgetItem *ptree_item = new QTreeWidgetItem(liststr, 0);
    ptree_item->setToolTip(0,tr("attibute in playlist"));
    ui->treeWidget_playlist->addTopLevelItem(ptree_item);

    // playlist attribute
    int attrCount = attrMap.length();
    for(int i = 0; i < attrCount; ++i){
        QDomAttr attr = attrMap.item(i).toAttr();
    }

    // in playlist-tag
    parsePlaylistElement(ptree_item,root);

    // in tracklist
    m_nTrack_cnt = 0;

    // Loops in a track list
    QDomElement child = root.firstChildElement("trackList");
    while (!child.isNull()) {
        liststr.clear();
        liststr << "<tracklist>" << "";
        QTreeWidgetItem *ptree_item_tracklist = new QTreeWidgetItem(liststr, 0);
        ptree_item_tracklist->setToolTip(0,tr("tracklist"));
        ptree_item->addChild(ptree_item_tracklist);

        // Loops in a track
        QDomElement child2 = child.firstChildElement("track");
        while (!child2.isNull()) {
            liststr.clear();
            liststr << "<track>" << "";
            QTreeWidgetItem *ptree_item_track = new QTreeWidgetItem(liststr, 0);
            ptree_item_track->setToolTip(0,tr("track"));
            ptree_item_tracklist->addChild(ptree_item_track);

            // in track
            parseTrackElement(ptree_item_track,child2);

            m_nTrack_cnt++;

            child2 = child2.nextSiblingElement("track");
        }

        child = child.nextSiblingElement("tracklist");
    }

    ui->treeWidget_playlist->expandAll();

    if (m_nTrack_cnt == 0){
        this->statusBar()->showMessage(
                    tr("Track was not found : ")+editing_Filepath, 10000);
    }
    else {
        this->statusBar()->showMessage(
                    QString::number(m_nTrack_cnt)+tr(" tracks found : ")+editing_Filepath, 10000);
    }

    setFileinfo();
    return true;
}

// Elements other than track list
void MainWindow::parsePlaylistElement(QTreeWidgetItem *ptree_playlist,const QDomElement &root)
{
    QStringList liststr;

    QDomNode node = root.firstChild();
    while (!node.isNull()) {
        if (node.isElement()) {
            QDomElement element = node.toElement();
            if (element.tagName() != "trackList"){
                liststr.clear();
                liststr << element.tagName() << element.text();
                QTreeWidgetItem *ptree_playlist_element = new QTreeWidgetItem(liststr, 0);
                ptree_playlist_element->setFlags(Qt::ItemIsEditable|Qt::ItemIsEnabled);
                ptree_playlist->addChild(ptree_playlist_element);
            }
        }
        node = node.nextSibling();
    }

}

// Elements in a track
void MainWindow::parseTrackElement(QTreeWidgetItem *ptree_track,const QDomElement &element)
{
    QStringList liststr;

    QDomElement child = element.firstChildElement();
    while (!child.isNull()) {
        QString tag_name = child.tagName();
        QString value = child.text();
        if (QUrl(value).isLocalFile()){
            value = QUrl(value).toLocalFile();
        }

        QString str= tag_name+","+value;

        liststr.clear();
        liststr << tag_name << value;

        QTreeWidgetItem *ptree_track_item = new QTreeWidgetItem(liststr, 0);
        ptree_track_item->setFlags(Qt::ItemIsEditable|Qt::ItemIsEnabled);
        ptree_track->addChild(ptree_track_item);

        child = child.nextSiblingElement();
    }

}

// file info
void MainWindow::clearFileinfo()
{
    ui->label_track_cnt->setText("");
    ui->label_update_date->setText("");
}

void MainWindow::setFileinfo()
{
    ui->label_track_cnt->setText(QString::number(m_nTrack_cnt)+ " tracks");

    QFileInfo fi = QFileInfo(editing_Filepath);
    ui->label_update_date->setText("(last-modified) "+fi.lastModified().toString("yyyy/MM/dd HH:mm:ss"));
}

/************************************************************
    drop processing from an external file
 ************************************************************/
void MainWindow::dragEnterEvent(QDragEnterEvent *e)
{
    if (e->mimeData()->hasUrls()) {
        e->acceptProposedAction();
    }
}
void MainWindow::dropEvent(QDropEvent *e)
{
    if (e->mimeData()->hasUrls()) {
        QString suffix = QFileInfo(e->mimeData()->text().trimmed()).suffix();
        if (suffix.toLower() == "xspf"){
            open_PlayLlist_file(QUrl(e->mimeData()->text().trimmed()).toLocalFile());
        }
    }
}

A.『最近開いたファイル』の中に、ファイルパス(Action)を設定

起動時およびXSPFファイルの読み込み完了時に
 void setMenu_Recently_files();
を呼び出します。

この関数内で、取得済のファイルパスの履歴リスト (QStringList)を使って、『最近開いたファイル』の中にその履歴をaction登録します。

(1) 最初の呼び出し時に
 QActionGroup
を作成し、
20個の Action を作成します。
20個は保存する最大のオープン履歴数です。

QActionGroup (各20個のaction) のトリガーは
 void onaction_Recently_file_Group_triggered(QAction*)
に接続するように設定します。

(2) 次回以降は、履歴actionを一旦すべて非表示設定します。

(3) 履歴リストをループ処理で、ファイルパスを設定、表示状態に変更します。

B.ファイルパスの Action トリガーを処理

 void onaction_Recently_file_Group_triggered(QAction*)
でクリックされた履歴の QAtion* を受け取ります。

actionに設定されているテキストをそのまま、オープンするファイルとして処理します。

ちょいテク

今回、QActionGroupをトリガーとしました。
 triggered(QAction*)
トリガーの送信先を1つにすることで、今回のようにまとめて処理できます。
action のテキストをデータとして扱いましたが、
dataメンバーを使えば、より現実的な処理ができます。

また、QAction 個別にトリガーを設定した場合、
 triggered()
となり、個別の処理がやりやすくなります

処理の方法によって、どの種類の triggered を使うか決めると良いでしょう。


他のクラスファイルやリソースファイルなどはZIPファイルをダウンロードして確認してください。

ZIP-file (73KB) download from Google DRIVE

 空いた時間を見つけながら作成しているので、バグ等があってもご愛嬌でお願いします。(簡単なデバックと動作テストしかしていません)
 今回は表示だけですが、最終的にはエディターとなる予定です (^^♪
また、区切りの良いタイミングで公開しますので、気長にお待ちくださいw

「Qt Quick」アプリケーション開発 (I・O BOOKS)

「Qt Quick」アプリケーション開発 (I・O BOOKS)

Qtプログラミング入門―使いやすいフレームワークを基礎から解説 (I・O BOOKS)

Qtプログラミング入門―使いやすいフレームワークを基礎から解説 (I・O BOOKS)

24時間集中講座 Qtプログラミング

24時間集中講座 Qtプログラミング

  • 作者: ダニエルソーリン,Daniel Solin,アットコム
  • 出版社/メーカー: ピアソンエデュケーション
  • 発売日: 2001/05
  • メディア: 単行本
  • 購入: 1人 クリック: 9回
  • この商品を含むブログ (2件) を見る

注目記事

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