Qt 로 만들자: 디렉토리 보기
파일 관리자의 가장 기본이 되는 기능은 단연 디렉토리 보기일 것이다. 이번에는 Qt 에서 제공하는 기능을 가지고 <디렉토리 보기> 를 만들어 보자.
1. 요구 사항
2. 코드 작성
2.1 프로젝트 작성
2.2 헤더 분석(dirlister.h)
22 번째 줄: Qt 는 모델/뷰 아케텍쳐를 사용한다. 모델을 통하여 데이터를 조직화하고, 뷰를 통하여 데이터를 표현한다. QFileSystemModel 은 파일 시스템에 대한 모델이다. 이를 위해 <QFileSystemModel> 을 포함할 필요가 있다. 사실 <QtWidgets> 를 포함한다면 <QFileSystemModel> 을 따로 포함할 필요는 없다.
23 번째 줄: QTreeView 는 모델을 트리 형태로 표현하는 뷰이다.
31 번째 줄: QModelIndex 은 모델에 있는 특정 데이터를 가리킨다.
2.3 소스 분석(dirlister.cpp)
2.3.1 생성자와 소멸자
메뉴와 위젯을 초기화한다.
2.3.2 initMenus()
'파일' 메뉴를 생성하고, '끝내기' 항목을 추가한다.
2.3.3 initWidgets()
'위치' 레이블과 한줄 편집기를 가로로 배치한다.
14 번째 줄: QLabel::setBuddy() 는 QLabel 에 지정된 단축키를 누를 때 촛점을 받을 위젯을 설정한다. 지금의 경우 Alt-L 을 누르면, 한줄 편집기로 촛점이 옮겨진다.
16~19 번째 줄: 파일 시스템 모델을 생성하고, 드라이브와 디렉토리를 검색한다. 단 '.' 과 '..' 은 제외한다. QFileSystemModel::setFilter() 는 검색하고자 하는 조건을 설정한다. 주의할 점은 QDir::AllDirs 는 반드시 포함해야 한다. 그렇지 않으면 디렉토리 구조가 올바르게 검색되지 않는다.
현재 디렉토리의 구조를 따라 모두 수동으로 검색한다. 이 작업은 현재 디렉토리를 화면에 올바르게 표시하기 위해 필요하다. QFileSystemModel 은 QFileSystemModel::setRootPath() 를 호출하면 검색을 시작한다. 하지만, 이렇게 하면 현재 디렉토리가 화면 밖으로 밀리는 경우가 생긴다. 이를 방지하기 위해 부모 디렉토리를 차례로 검색하는 것이다.
예를 들어, QFileSystemModel::setRootPath("x:/path/to/dir") 을 호출하면, 다음 순서로 검색한다.
이후에 다른 항목들이 추가될 때, 예를 들어, x:/path/to/above, 현재 디렉토리보다 앞쪽에 추가되면, 현재 디렉토리는 뒤로 밀리면서, 뷰의 밖에 위치할 수 있다.
따라서 애초에 루트부터 부모디렉토리까지 디렉토리를 검색해서 추가해주면, 이 문제를 피할 수 있다.
2 번째 줄: QFileInfo 는 파일 관련 정보를 담고 있다. QDir 클래스는 디렉토리 관련 기능을 제공하는 클래스이다. QDir::drives() 는 드라이브의 목록을 돌려준다. 드라이브가 지원되지 않는 OS 의 경우에는 '/' 를 돌려준다.
4 번째 줄: QEventLoop 는 이벤트 루프를 지역적으로 실행할 수 있도록 한다.
5 번째 줄: 일반적으로 connect() 는 SIGNAL() 과 SLOT() 매크로를 이용하지만, Qt5 에서는 이것외에도 멤버 함수에 대한 함수 포인터도 지원한다. 이 방법의 장점은 시그널과 슬롯을 컴파일 타임에 확인할 수 있다는 것이다. 기존의 SIGNAL/SLOT 방법은 컴파일 타임에 확인하지 못하고, 런타임에 디버깅 메세지로 오류가 출력이 되기 때문에 종종 문제가 된다. 그리고 기나긴 인자 목록을 나열하지 않아도 되는 것도 좋은 점이다.
8 번째 줄: QFileSystemModel::setRootPath() 를 호출하여 주어진 디렉토리를 검색하도록 한다. 모두 검색하였으면 directoryLoaded() 시그널이 발생한다.
9 번째 줄: QEventLoop::exec() 는 이벤트 루프로 진입하여 QEventLoop::quit() 슬롯이 호출되면 루프를 빠져 나온다.
12 번째 줄: QDir::currentPath() 는 현재 디렉토리를 경로를 알려주는 정적 멤버 함수이다.
16 번째 줄: QString::indexOf() 는 주어진 문자열은 주어진 위치에서부터 찾아서, 그 위치를 돌려준다.
17 번째 줄: QString::mid() 는 주어진 위치에서부터 주어진 길이만큼의 문자열을 돌려준다.
19 번째 줄: QFileSystemModel::rootPath() 는 현재 설정된 root path 를 돌려준다. QFileSystemModel::setRootPath()는 같은 디렉토리를 설정하면 directoryLoaded() 시그널이 발생하지 않으므로, 현재 root path 를 반드시 확인해야 한다. 그렇지 않으면 이벤트 루프에서 무한 대기한다.
디렉토리 구조용 모델과 트리뷰, 디렉토리 내용용 모델과 트리뷰를 생성한다.
4 번째 줄: QTreeView::setModel() 는 트리뷰로 나타낼 모델을 설정한다.
6 번째 줄: QTreeView::setHeaderHidden() 은 Name, Size, Type 같은 헤더를 숨긴다.
8 번째 줄: QTreeView::columnCount() 은 컬럼의 갯수를 돌려준다.
9 번째 줄: QTreeView::setColumnHidden() 은 주어진 위치의컬럼을 숨긴다.
10~11 번째 줄: QTreeView::activate() 시그널은 항목을 더블 클릭하거나 항목에서 엔터를 누르는 경우에 발생한다. QTreeView::clicked() 는 항목을 클릭했을 때 발생한다.
24 번째 줄: QTreeView::setRootIsDecorated() 는 펼치기/접기 아이콘을 나타낼지를 결정한다.
26 번째 줄: QTreeView::setExpandsOnDoubleClick() 은 더블 클릭했을 때, 가지를 펼칠지를 결정한다.
2 번째 줄: QSplitter 는 위젯을 사이에 크기 조절 막대를 삽입하여 관리하는 위젯이다.
3 번째 줄: QSplitter::addWidget() 은 위젯을 추가한다.QSplitter::setOrientation() 으로 설정하지 않으면, 가로 방향이 추가된다.
4 번째 줄: QSplitter::setStrectchFactor()는 주어진 위젯의 크기 인자를 설정한다. 0 은 최소 크기이다. 예를 들어 세 개의 위젯이 각각 1, 2, 3 의 크기 인자를 가진다면, 각각 1/6, 2/6, 3/6 의 크기를 나누어 가진다.
시작했을 때 디렉토리 구조가 촛점을 갖고, 현재 디렉토리가 선택되도록 한다.
4 번째 줄: QAbstractItemView::setCurrentIndex() 는 현재 인덱스를 설정한다. QFileSystemModel::index() 는 주어진 경로에 대한 인덱스를 돌려준다.
6 번째 줄: QAbstractItemView::currentIndex() 은 현재 인덱스를 돌려준다.
2.3.4 dirActivated()
디렉토리가 활성화(activated)되거나 디렉토리를 클릭(clicked) 했을 때 호출된다. 선택된 인덱스에 따라 위치 한줄 편집기에 현재 절대 경로를 설정하고, 디렉토리 내용이 보이게 한다.
8 번째 줄: QFileSystemModel::filePath() 은 주어진 인덱스에 해당하는 파일 경로를 돌려준다.
11 번째 줄: QDir::toNativeSeparators() 는 주어진 경로의 디렉토리 구분자를 OS 에 맞게 바꾼다. Qt 는 내부적으로 디렉토리 구분자로 '/' 를 쓴다. 하지만 Windows 나 OS/2 같은 경우에는 '\' 를 쓰므로, OS 에 맞게 바꾼다.
13 번째 줄: QTreeView::setRootIndex() 는 루트 인덱스를 설정한다. 루트 인덱스를 설정하면 해당 인덱스의 자식 인덱스만을 표현한다. 참고로 디렉토리 내용은 QListView 가 아니라 QTreeView 로 나타낸다는 것을 기억하자.
3. 마무리하면서...
파일 관리자의 가장 기본적인 요소인 <디렉토리 보기> 를 구현해보았다. Qt 자체에 관련 기능이 충실히 구현되어 있기 때문에 어렵지 않게 구현할 수 있었다.
이를 조금만 더 활용한다면 간단한 파일 관리자 정도는 금방 만들 수 있을 것이다.
다음은 <디렉토리 보기> 의 실행 화면이다.
전체 소스는 여기에서 확인하자.
1. 요구 사항
- 현재 보고 있는 디렉토리의 절대 경로를나타내기
- 디렉토리를 트리구조로 나타내기
- 디렉토리를 구성하고 있는 서브-디렉토리와 파일들을 나타내기
2. 코드 작성
2.1 프로젝트 작성
- 프로젝트 이름: DirLister
- 메인 클래스 이름: DirLister
- 메인 클래스 유형: QMainWindow
2.2 헤더 분석(dirlister.h)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | #ifndef DIRLISTER_H #define DIRLISTER_H #include <QMainWindow> #include <QtWidgets> #include <QFileSystemModel> /** * @brief 디렉토리 구조와 내용을 보여준다 */ class DirLister : public QMainWindow { Q_OBJECT public: DirLister(QWidget *parent = 0); ~DirLister(); private: QLineEdit *_locationLine; ///< 위치 QFileSystemModel *_dirModel; ///< 디렉토리 구조용 파일 시스템 모델 QTreeView *_dirView; ///< 디렉토리 구조용 트리뷰 QFileSystemModel *_entryModel; ///< 디렉토리 내용용 파일 시스템 모델 QTreeView *_entryView; ///< 디렉토리 내용용 트리뷰 void initMenus(); void initWidgets(); private slots: void dirActivated(const QModelIndex ¤t); }; #endif // DIRLISTER_H |
22 번째 줄: Qt 는 모델/뷰 아케텍쳐를 사용한다. 모델을 통하여 데이터를 조직화하고, 뷰를 통하여 데이터를 표현한다. QFileSystemModel 은 파일 시스템에 대한 모델이다. 이를 위해 <QFileSystemModel> 을 포함할 필요가 있다. 사실 <QtWidgets> 를 포함한다면 <QFileSystemModel> 을 따로 포함할 필요는 없다.
23 번째 줄: QTreeView 는 모델을 트리 형태로 표현하는 뷰이다.
출처: Qt 도움말(QTreeView) |
31 번째 줄: QModelIndex 은 모델에 있는 특정 데이터를 가리킨다.
2.3 소스 분석(dirlister.cpp)
2.3.1 생성자와 소멸자
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | /** * @brief DirLister 생성자 * @param parent 부모 위젯 */ DirLister::DirLister(QWidget *parent) : QMainWindow(parent) { initMenus(); // 메뉴 초기화 initWidgets(); // 위젯 초기화 } /** * @brief DirLister 소멸자 */ DirLister::~DirLister() { } |
메뉴와 위젯을 초기화한다.
2.3.2 initMenus()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | /** * @brief 메뉴를 초기화한다 */ void DirLister::initMenus() { // '파일' 메뉴 생성 QMenu *fileMenu = new QMenu(tr("파일(&F)")); // '끝내기' 항목 추가 fileMenu->addAction(tr("끝내기(&x)"), this, SLOT(close()), QKeySequence::Quit); // '파일' 메뉴 추가 menuBar()->addMenu(fileMenu); } |
'파일' 메뉴를 생성하고, '끝내기' 항목을 추가한다.
2.3.3 initWidgets()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | /** * @brief 위젯을 초기화한다 */ void DirLister::initWidgets() { // '위치' 한줄편집기 생성 _locationLine = new QLineEdit; // 읽기 전용으로 _locationLine->setReadOnly(true); // '위치' 레이블 생성 QLabel *locationText = new QLabel(tr("위치(&L)")); // 단축키를 '위치' 한줄편집기와 연결 locationText->setBuddy(_locationLine); // 디렉토리 구조용 파일 시스템 모델 생성 _dirModel = new QFileSystemModel; // 디렉토리, 모든 드라이브. '.' 과 '..' 는 제외 _dirModel->setFilter(QDir::AllDirs | QDir::Drives | QDir::NoDotAndDotDot); // 위치 위젯 가로로 배치 QHBoxLayout *hboxLayout = new QHBoxLayout; hboxLayout->addWidget(locationText); hboxLayout->addWidget(_locationLine); |
'위치' 레이블과 한줄 편집기를 가로로 배치한다.
14 번째 줄: QLabel::setBuddy() 는 QLabel 에 지정된 단축키를 누를 때 촛점을 받을 위젯을 설정한다. 지금의 경우 Alt-L 을 누르면, 한줄 편집기로 촛점이 옮겨진다.
16~19 번째 줄: 파일 시스템 모델을 생성하고, 드라이브와 디렉토리를 검색한다. 단 '.' 과 '..' 은 제외한다. QFileSystemModel::setFilter() 는 검색하고자 하는 조건을 설정한다. 주의할 점은 QDir::AllDirs 는 반드시 포함해야 한다. 그렇지 않으면 디렉토리 구조가 올바르게 검색되지 않는다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | // 드라이브 또는 '/' 디렉토리 내용을 읽음 foreach (QFileInfo info, QDir::drives()) { QEventLoop loop; connect(_dirModel, &_dirModel->directoryLoaded, &loop, &loop.quit); // 읽을 디렉토리 설정 _dirModel->setRootPath(info.filePath()); loop.exec(); } QString currentDir = QDir::currentPath(); // 현재 디렉토리 구성 요소를 따라 디렉토리 내용을 읽음 for (int i = 0; i != -1;) { i = currentDir.indexOf("/", i + 1); QString dir = currentDir.mid(0, i); if (_dirModel->rootPath() != dir) { QEventLoop loop; connect(_dirModel, &_dirModel->directoryLoaded, &loop, &loop.quit); _dirModel->setRootPath(dir); loop.exec(); } } |
현재 디렉토리의 구조를 따라 모두 수동으로 검색한다. 이 작업은 현재 디렉토리를 화면에 올바르게 표시하기 위해 필요하다. QFileSystemModel 은 QFileSystemModel::setRootPath() 를 호출하면 검색을 시작한다. 하지만, 이렇게 하면 현재 디렉토리가 화면 밖으로 밀리는 경우가 생긴다. 이를 방지하기 위해 부모 디렉토리를 차례로 검색하는 것이다.
예를 들어, QFileSystemModel::setRootPath("x:/path/to/dir") 을 호출하면, 다음 순서로 검색한다.
- x:/path/to/dir
- x:/
- x:/path
- x:/path/to
이후에 다른 항목들이 추가될 때, 예를 들어, x:/path/to/above, 현재 디렉토리보다 앞쪽에 추가되면, 현재 디렉토리는 뒤로 밀리면서, 뷰의 밖에 위치할 수 있다.
따라서 애초에 루트부터 부모디렉토리까지 디렉토리를 검색해서 추가해주면, 이 문제를 피할 수 있다.
2 번째 줄: QFileInfo 는 파일 관련 정보를 담고 있다. QDir 클래스는 디렉토리 관련 기능을 제공하는 클래스이다. QDir::drives() 는 드라이브의 목록을 돌려준다. 드라이브가 지원되지 않는 OS 의 경우에는 '/' 를 돌려준다.
4 번째 줄: QEventLoop 는 이벤트 루프를 지역적으로 실행할 수 있도록 한다.
5 번째 줄: 일반적으로 connect() 는 SIGNAL() 과 SLOT() 매크로를 이용하지만, Qt5 에서는 이것외에도 멤버 함수에 대한 함수 포인터도 지원한다. 이 방법의 장점은 시그널과 슬롯을 컴파일 타임에 확인할 수 있다는 것이다. 기존의 SIGNAL/SLOT 방법은 컴파일 타임에 확인하지 못하고, 런타임에 디버깅 메세지로 오류가 출력이 되기 때문에 종종 문제가 된다. 그리고 기나긴 인자 목록을 나열하지 않아도 되는 것도 좋은 점이다.
8 번째 줄: QFileSystemModel::setRootPath() 를 호출하여 주어진 디렉토리를 검색하도록 한다. 모두 검색하였으면 directoryLoaded() 시그널이 발생한다.
9 번째 줄: QEventLoop::exec() 는 이벤트 루프로 진입하여 QEventLoop::quit() 슬롯이 호출되면 루프를 빠져 나온다.
12 번째 줄: QDir::currentPath() 는 현재 디렉토리를 경로를 알려주는 정적 멤버 함수이다.
16 번째 줄: QString::indexOf() 는 주어진 문자열은 주어진 위치에서부터 찾아서, 그 위치를 돌려준다.
17 번째 줄: QString::mid() 는 주어진 위치에서부터 주어진 길이만큼의 문자열을 돌려준다.
19 번째 줄: QFileSystemModel::rootPath() 는 현재 설정된 root path 를 돌려준다. QFileSystemModel::setRootPath()는 같은 디렉토리를 설정하면 directoryLoaded() 시그널이 발생하지 않으므로, 현재 root path 를 반드시 확인해야 한다. 그렇지 않으면 이벤트 루프에서 무한 대기한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | // 디렉토리 구조용 트리뷰 생성 _dirView = new QTreeView; // 모델 설정 _dirView->setModel(_dirModel); // 헤더 숨김 _dirView->setHeaderHidden(true); // 두번째 이후 모든 컬럼 숨김 for (int i = _dirModel->columnCount() - 1; i > 0; --i) _dirView->setColumnHidden(i, true); connect(_dirView, &_dirView->activated, this, &this->dirActivated); connect(_dirView, &_dirView->clicked, this, &this->dirActivated); // 디렉토리 내용용 파일 시스템 모델 생성 _entryModel = new QFileSystemModel; // 모든 디렉토리, 파일. '.' 과 '..' 은 제외 _entryModel->setFilter(QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot); _entryModel->setRootPath(""); // 디렉토리 내용용 트리뷰 생성 _entryView = new QTreeView; // 모델 설정 _entryView->setModel(_entryModel); // 펼치기/감추기 아이콘 숨김 _entryView->setRootIsDecorated(false); // 더블 클릭 무시 _entryView->setExpandsOnDoubleClick(false); |
디렉토리 구조용 모델과 트리뷰, 디렉토리 내용용 모델과 트리뷰를 생성한다.
4 번째 줄: QTreeView::setModel() 는 트리뷰로 나타낼 모델을 설정한다.
6 번째 줄: QTreeView::setHeaderHidden() 은 Name, Size, Type 같은 헤더를 숨긴다.
8 번째 줄: QTreeView::columnCount() 은 컬럼의 갯수를 돌려준다.
9 번째 줄: QTreeView::setColumnHidden() 은 주어진 위치의컬럼을 숨긴다.
10~11 번째 줄: QTreeView::activate() 시그널은 항목을 더블 클릭하거나 항목에서 엔터를 누르는 경우에 발생한다. QTreeView::clicked() 는 항목을 클릭했을 때 발생한다.
24 번째 줄: QTreeView::setRootIsDecorated() 는 펼치기/접기 아이콘을 나타낼지를 결정한다.
26 번째 줄: QTreeView::setExpandsOnDoubleClick() 은 더블 클릭했을 때, 가지를 펼칠지를 결정한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | // 스플리터 생성 QSplitter *splitter = new QSplitter; splitter->addWidget(_dirView); splitter->setStretchFactor(0, 0); splitter->addWidget(_entryView); splitter->setStretchFactor(1, 1); // 위치와 스플리터 세로 배치 QVBoxLayout *vboxLayout = new QVBoxLayout; vboxLayout->addLayout(hboxLayout); vboxLayout->addWidget(splitter); // 중앙 위젯 생성 QWidget *w = new QWidget; w->setLayout(vboxLayout); // 중앙 위젯 설정 setCentralWidget(w); |
2 번째 줄: QSplitter 는 위젯을 사이에 크기 조절 막대를 삽입하여 관리하는 위젯이다.
3 번째 줄: QSplitter::addWidget() 은 위젯을 추가한다.QSplitter::setOrientation() 으로 설정하지 않으면, 가로 방향이 추가된다.
4 번째 줄: QSplitter::setStrectchFactor()는 주어진 위젯의 크기 인자를 설정한다. 0 은 최소 크기이다. 예를 들어 세 개의 위젯이 각각 1, 2, 3 의 크기 인자를 가진다면, 각각 1/6, 2/6, 3/6 의 크기를 나누어 가진다.
1 2 3 4 5 6 7 | // 디렉토리 구조용 트리뷰에 촛점 설정 _dirView->setFocus(); // 현재 디렉토리를 현재 인덱스로 설정 _dirView->setCurrentIndex(_dirModel->index(QDir::currentPath())); // 현재 인덱스 활성화 dirActivated(_dirView->currentIndex()); } |
시작했을 때 디렉토리 구조가 촛점을 갖고, 현재 디렉토리가 선택되도록 한다.
4 번째 줄: QAbstractItemView::setCurrentIndex() 는 현재 인덱스를 설정한다. QFileSystemModel::index() 는 주어진 경로에 대한 인덱스를 돌려준다.
6 번째 줄: QAbstractItemView::currentIndex() 은 현재 인덱스를 돌려준다.
2.3.4 dirActivated()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | /** * @brief 디렉토리가 활성화되었을 때 호출됨 * @param index 활성화된 인덱스 */ void DirLister::dirActivated(const QModelIndex &index) { // 인덱스로부터 파일 경로를 얻음 QString path(_dirModel->filePath(index)); // 위치 텍스트를 경로로 설정 _locationLine->setText(QDir::toNativeSeparators(path)); // 디렉토리 내용용 트리뷰에 디렉토리 내용 표시 _entryView->setRootIndex(_entryModel->index(path)); } |
디렉토리가 활성화(activated)되거나 디렉토리를 클릭(clicked) 했을 때 호출된다. 선택된 인덱스에 따라 위치 한줄 편집기에 현재 절대 경로를 설정하고, 디렉토리 내용이 보이게 한다.
8 번째 줄: QFileSystemModel::filePath() 은 주어진 인덱스에 해당하는 파일 경로를 돌려준다.
11 번째 줄: QDir::toNativeSeparators() 는 주어진 경로의 디렉토리 구분자를 OS 에 맞게 바꾼다. Qt 는 내부적으로 디렉토리 구분자로 '/' 를 쓴다. 하지만 Windows 나 OS/2 같은 경우에는 '\' 를 쓰므로, OS 에 맞게 바꾼다.
13 번째 줄: QTreeView::setRootIndex() 는 루트 인덱스를 설정한다. 루트 인덱스를 설정하면 해당 인덱스의 자식 인덱스만을 표현한다. 참고로 디렉토리 내용은 QListView 가 아니라 QTreeView 로 나타낸다는 것을 기억하자.
3. 마무리하면서...
파일 관리자의 가장 기본적인 요소인 <디렉토리 보기> 를 구현해보았다. Qt 자체에 관련 기능이 충실히 구현되어 있기 때문에 어렵지 않게 구현할 수 있었다.
이를 조금만 더 활용한다면 간단한 파일 관리자 정도는 금방 만들 수 있을 것이다.
다음은 <디렉토리 보기> 의 실행 화면이다.
전체 소스는 여기에서 확인하자.
글 잘 봤습니다. 특정 디렉토리 밑으로 보여주고 싶은데 어떻게 해야 합니까?
답글삭제예를 들어 D:/TEST/Source경로가 있다면 Source폴더가 제일 상위이고 그 밑으로 폴더만 보여주고 싶습니다.
답이 늦었네요. 2.3.4 dirActivated() 절을 보시면
삭제_entryView->setRootIndex(_entryModel->index(path));
가 있습니다. 이 부분이 말씀하시는 역할을 할 수 있습니다. 실제로 이 부분이 오른쪽 칸에 해당 디렉토리 내용만 보여줍니다.
만약 왼쪽 디렉토리 구조에서 특정한 폴더 이하만 보여주고자 한다면, 2.3.3 initWidgets() 절에서
_dirView->setModel(_dirModel);
다음에
_dirView->setRootIndex(_dirModel->index("D:/TEST/Sources"));
를 추가하시면 됩니다. 다만, 이럴 경우 D:/TEST/Sources 폴더 자체는 보이지 않고, 하위 디렉토리만 보입니다. 부모 디렉토리(D:/TEST/Sources)를 나타나게 하려면, 다소 복잡합니다. QFileSystemModel 을 상속해서 재구현(reimplement)하시든지, QSortFilterProxyModel 등을 활용하셔야 합니다.
connect 부분에서 포인터 말고 signal과 slot으로 표현하려면 어떻게 해야되는지 여쭤볼 수 있을까요?
답글삭제C2276 '&': 바인딩된 멤버 함수 식의 연산이 잘못되었습니다.
삭제C2661 'QObject::connect': 오버로드된 함수에서 2개의 매개변수를 사용하지 않습니다. 이렇게 오류가 뜨는데 잘 모르겠습니다...ㅠ
안녕하세요? 말씀하신 것처럼 문법적으로 오류가 있는 것이 맞습니다. 이 코드를 작성할 당시에는 허용이 되었는데, 이후에는 허용이 되지 않더라고요. 포인터 변수 이름 대신에 클래스 이름을 쓰셔야 합니다. 예를 들어,
삭제connect(_dirModel, &_dirModel->directoryLoaded, &loop, &loop.quit);
을
connect(_dirModel, &QFileSystemModel::directoryLoaded, &loop, &QEventLoop::quit);
이렇게 바꾸시면 됩니다. 또
connect(_dirModel, &_dirModel->directoryLoaded, &loop, &loop.quit);
은
connect(_dirModel, &QFileSystemModel::directoryLoaded, &loop, &QEventLoop::quit);
으로, 그리고
connect(_dirView, &_dirView->activated, this, &this->dirActivated);
connect(_dirView, &_dirView->clicked, this, &this->dirActivated);
은
connect(_dirView, &QTreeView::activated, this, &DirLister::dirActivated);
connect(_dirView, &QTreeView::clicked, this, &DirLister::dirActivated);
로 바꾸세요.
SIGNAL/SLOT의 경우 시그널과 슬롯 인자들의 자료형을 같이 적어주어야 합니다.
예를 들어,
connect(_dirModel, &_dirModel->directoryLoaded, &loop, &loop.quit);
은
connect(_dirModel, SIGNAL(directoryLoaded(const QString)), &loop, SLOT(quit()));
이렇게요. 보시는 것처럼 SIGNAL/SLOT 방법은 인자들의 자료형을 모두 나열해야 해서 은근히 불편합니다. 멤버 함수 포인터를 사용하는 것을 추천합니다.
답변 감사드립니다...ㅠㅠ
삭제더 빨리 확인했어야됬는데 바빠서 이제야 확인했네요...ㅠ
삭제안녕하세요 저번에 connect관련되어 질문 드렸던 사람입니다. 혹시 단위가 KB가 아닌 KiB로 표시되어 KB로 변환시키려고 하는데 어떻게 해야되는지 여쭤볼 수 있을까요?
답글삭제QFileSystemModel의 data() 를 상속받아 다시 구현해야 합니다. 일단 다음 코드를 dirlister.h 에 추가하세요.
삭제class EntryModel : public QFileSystemModel
{
Q_OBJECT
public:
EntryModel(QObject *parent = nullptr) : QFileSystemModel(parent) {};
virtual ~EntryModel() {};
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override
{
QVariant v = QFileSystemModel::data(index, role);
if (index.column() == 1 && role == Qt::DisplayRole)
{
QString size(v.toString());
size.replace(QString("KiB"), QString("KB"));
v = size;
}
return v;
}
};
그리고 initWidgets()
_entryModel = new QFileSystemModel;
를
_entryModel = new EntryModel;
로 바꾸시면 됩니다. 나머지 단위에 대해서도 적당히 바꾸시면 됩니다.
앗, 들여 쓰기가 모두 깨졌네요ㅠㅠ
삭제키비바이트 때문에 몇일간 고생하고 있었는데 감사합니다...ㅠㅠㅠㅠ
삭제매번 답장을 늦게 드려서 죄송합니다...ㅠㅠ
삭제코드 적용하고 나서 kib하고 mib는 고쳤는데 byte가 한글로 바이트로 해석되서 나오는 부분은 수정을 어떻게 해야되는지 여쭤볼 수 있을까요? 계속 귀찮게 해드려서 죄송합니다...ㅠㅠ
삭제개인적인 생각으로는 데이터 자체가 KiB형태로 저장되어있어서 그런 것 같다고 생각하고 있습니다 그래서 용량을 1024로 나눠서 바꾸는 예제를 찾아봤는데 적용을 못 시키다가 도움 요청하게 된거였습니다.
삭제제 프로그램에서는 9.15KiB 이런 식으로 표기가 됩니다....
삭제Qt 가 없는 환경에서 있다보니 답이 늦었습니다. 바이트의 경우 번역어의 context를 찾으면 될 줄 알았는데, 생각대로 되지 않네요.
삭제차라리 파일 크기를 직접 얻어 표시하는 게 더 적합하고 효율적으로 보이네요. EntryModel::data() 를 다음처럼 바꿔 보세요.
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override
{
if (index.column() == 1 && role == Qt::DisplayRole )
{
auto model = dynamic_cast(index.model());
auto size = model->size(index);
if (size >= 1024)
return QVariant::fromValue(tr("%1 KB").arg(size / 1024));
return QVariant::fromValue(tr("%1 bytes").arg(size));
}
return QFileSystemModel::data(index, role);
}
늦게 답변드려서 죄송합니다...ㅠㅠ 그리고 답장해주셔서 감사합니다... 지금 확인해보고 적용중인데 dynamic_cast 다음에 <>가 없어서 오류가 뜨고 있습니다...ㅠㅠ
삭제찾아보면서 해결하고 있는 중이긴 하나 말씀드리는게 나을 것 같아서 메세지 남기게 되었습니다.
파일탐색기를 좀 더 발전 시키려고 하니 계속 이렇게 질문이 발생되게 되네요
바로 확인하고 메세지 드리려고 했는데 엇갈려버렸네요....ㅠㅠ
삭제아마도 <>이 태그 처리 되어 빠졌나 보네요. <> 사이에 const QFileSystemModel* 를 넣으시면 됩니다.
삭제정말 감사드립니다...ㅠㅠ 덕분에 정상적으로 출력 되는거 확인 했습니다. 혹시 MB의 경우에는 return QVariant::fromValue(tr("%1 MB").arg(size / 1024 / 1024)); 이런식으로 한줄 더 추가해주면 될까요?
삭제아시겠지만, KB 부분하고 MB 부분을 나누는 if 문도 함께 추가하세요. 원하는 결과를 얻으시기를 바랄게요.
삭제if(size >= (1024*1024))
삭제return QVariant::fromValue(tr("%1 MB").arg(size / (1024*1024)));
말씀대로 이렇게 추가해봤는데 제가 뭘 계속 놓치는것 같은데 아직 포인트를 못찾고 있습니다...ㅠㅠ
삭제귀찮게 해드려서 죄송합니다...
아! 원인 찾았습니다! 감사합니다 덕분에 진도 나갈 수 있었습니다...ㅠㅠㅠ
삭제일단 상속받은 클래스의 생성자는 부모 클래스의 생성자를 초기화 리스트에서 호출해야 합니다. 그리고 QFileIconProvider 생성자는 인자를 받지 않습니다. 그러니 IconProvider 생성자의 인자도 필요 없습니다.
답글삭제C++ 에서 가상 함수를 오버라이드할 때는 소멸자도 반드시 가상 함수로 선언해야 합니다.
fn은 언제 어떻게 값이 정해지나요?
부모 클래스의 함수를 이용할 때는 부모 클래스의 함수임을 분명히 적어야 합니다. 아니면 함수가 인자로 구분돼야 합니다. 그래서 마지막
return icon(type);
은 무한 재귀에 빠질 것으로 보입니다.
return QFileIconProvider::icon(type);
으로 바꾸세요. 끝으로 원하시는 함수는
QIcon QFileIconProvider::icon(QFileIconProvider::IconType type) const
보다는
QIcon QFileIconProvider::icon(const QFileInfo &info) const
일 것 같습니다.
좀딱님이 조언해주신 덕분에 저 부분만 빼고 제가 원하는 방향의 파일탐색기를 만들 수 있었습니다... 매번 번거로우셨을텐데 감사드립니다...ㅠㅠ
답글삭제저 부분은 좀딱님이 말씀해주신 조언대로 수정하여 시도해보겠습니다!
ICON_SIZE = QSize(64,64)
답글삭제accepted_types = (".jpg",".tiff",".png",".exr",".psd")
# this depends on the plugins you have installed,
# PSD and EXR requires external ones).
class IconProvider(QFileIconProvider):
def __init__(self) -> None:
super().__init__()
def icon(self, type: 'QFileIconProvider.IconType'):
fn = type.filePath()
if fn.endswith(accepted_types):
a = QPixmap(ICON_SIZE)
a.load(fn)
return QIcon(a)
else:
return super().icon(type)
이 코드가 제가 참고한 파이썬 코드인데 c++에 맞게 변형하다보니까 오류가 생긴것 같습니다...ㅠㅠ
부연 설명을 추가하면 ListView에서 QFileModelSystem을 이용해서 DirModel인 TreeView에서 데이터를 받아오고 나열 되있는 데이터를 IconMode를 통해 아이콘 화 시키고 거기서 JPG,PNG 등 이미지 형식을 윈도우 내에서 기본적으로 제공하는 이미지 아이콘이 아닌 Thumbnail로 표현하려고 한 것이었습니다.
답글삭제따라서 위 파이썬 코드를 이용해서 구현해보려고 했는데 저도 코드를 구성하면서 fileinfo를 불러올 수 있는 방법이 없다고 생각하여 여러방면으로 생각해보았지만 cpp파일에 선언되어있는 DirModel을 불러올 방법을 생각하지 못하고 있었습니다.
그래서 저 상태에서 멈춰있었고 죄송스럽지만 좀딱님에게 질문 드린 것이었습니다.
좀 더 자세히 설명드렸어야됬는데 죄송합니다.
좀딱님 마지막으로... 제가 몇달째 해결하지 못하던 구현이었는데 좌측 파일시스템 모델에 윈도우 파일탐색기처럼 드라이브 이외에도 다운로드, 동영상, 문서, 바탕화면 등의 파일이 나올 수 있게 구현하는 것이었습니다... 인터넷에서 여러 문서들을 서칭해봤으나 결국 이거는 해결하지 못하고 계속 멈춰있는 상태입니다. 제가 많은 민폐인것은 알고 있으나 이렇게 질문 드리게 되었습니다... 지난 몇달간 질문에 답해주셔서 너무 감사했습니다.
답글삭제답변이 늦었습니다. 연말이라 그런지 많이 바쁘네요. 실제로 코드를 작성해 보지는 않았지만, 생각해 볼 수 있는 방법은 2 가지 정도입니다.
삭제첫째는 QFileSystemModel 을 직접 이용하는 것이 아니라 proxy model 을 이용해서 원하시는 파일 모델과 QFileSystemModel 을 중개하는 것입니다.
두번째는 QFileSystemModel의 myComputer() 를 이용하는 것입니다. 다만, myComputer() 를 실제로 사용해 본 경험이 없어서 구체적인 사용법을 말씀드리기는 어렵네요.
부디 문제를 해결하셔서 좋은 결과를 얻으시기 바랍니다. 메리 크리스마스하시고, 새해 복도 많이 받으세요~
앗 답변 주셔서 감사합니다....ㅠㅠ 연말에 회사 휴일 때문에 이제 답글 달게 되었네요. 좀딱님께서 말씀해주신 두 가지 방법 모두 탐색해봤던 방법이었는데 역시 QFileSystemModel로 구현할 수 있는 방법이 그 2가지가 맞았군요... 바쁘신데 알려주셔서 다시 한번 감사드립니다. 좀딱님도 새해 복 많이 받으세요!
삭제