문서 뷰어
JSON, 텍스트 및 PDF 파일을 표시하고 인쇄하는 위젯 애플리케이션입니다.
문서 뷰어는 정적 및 동적 도구 모음, 메뉴, 동작이 있는 QMainWindow 을 사용하는 방법을 보여줍니다. 또한 위젯 기반 애플리케이션에서 다음 기능을 시연합니다:
- QSettings 을 사용하여 사용자 환경설정을 조회 및 저장하고 이전에 열어본 파일 기록을 관리합니다.
- 위젯 위에 커서를 올려놓을 때 커서 동작 제어하기.
- 동적으로 로드되는 플러그인 만들기.
- UI를 다른 언어로 현지화하기.
예제 실행하기
에서 예제를 실행하려면 Qt Creator에서 Welcome 모드를 열고 Examples 에서 예제를 선택합니다. 자세한 내용은 Qt Creator: 튜토리얼을 참조하세요 : 빌드 및 실행을 참조하세요.
애플리케이션 및 기본 창 만들기
애플리케이션과 메인 창은 main.cpp
에서 구성됩니다. main() 함수는 QCommandLineParser 를 사용하여 명령줄 인수( 도움말, 버전 및 선택적 위치 인수인 파일)를 처리합니다. 사용자가 애플리케이션을 시작할 때 파일 경로를 제공한 경우 메인 창에서 해당 파일이 열립니다:
int main(int argc, char *argv[]) { QApplication app(argc, argv); QCoreApplication::setOrganizationName("QtProject"_L1); QCoreApplication::setApplicationName("DocumentViewer"_L1); QCoreApplication::setApplicationVersion("1.0"_L1); Translator mainTranslator; mainTranslator.setBaseName("docviewer"_L1); mainTranslator.install(); QCommandLineParser parser; parser.setApplicationDescription(Tr::tr("A viewer for JSON, PDF and text files")); parser.addHelpOption(); parser.addVersionOption(); parser.addPositionalArgument("File"_L1, Tr::tr("JSON, PDF or text file to open")); parser.process(app); const QStringList &positionalArguments = parser.positionalArguments(); const QString &fileName = (positionalArguments.count() > 0) ? positionalArguments.at(0) : QString(); MainWindow w(mainTranslator); // Start application only if plugins are available if (!w.hasPlugins()) { QMessageBox::critical(nullptr, Tr::tr("No viewer plugins found"), Tr::tr("Unable to load viewer plugins. Exiting application.")); return 1; } w.show(); if (!fileName.isEmpty()) w.openFile(fileName); return app.exec(); }
MainWindow 클래스
MainWindow
클래스는 메뉴, 작업 및 툴바가 있는 애플리케이션 화면을 제공합니다. 파일을 열 수 있으며 콘텐츠 유형을 자동으로 감지합니다. 또한 QSettings 을 사용하여 이전에 열었던 파일 목록을 유지 관리하고 실행 시 설정을 저장하고 다시 로드합니다. 메인창은 콘텐츠 유형에 따라 열린 파일에 적합한 뷰어를 생성하고 문서 인쇄를 지원합니다.
MainWindow의 생성자는 Qt Designer 에서 생성된 사용자 인터페이스를 초기화합니다. mainwindow.ui
파일은 왼쪽에 QTabWidget 을 제공하여 북마크와 썸네일을 표시합니다. 오른쪽에는 파일 콘텐츠를 볼 수 있는 QScrollArea 이 있습니다.
ViewerFactory 클래스
ViewerFactory
클래스는 알려진 파일 형식에 대한 뷰어를 관리합니다. 이러한 뷰어는 플러그인으로 구현됩니다. ViewerFactory의 인스턴스가 생성되면 보기 영역과 기본 창에 대한 포인터가 생성자에게 전달됩니다:
m_factory.reset(new ViewerFactory(ui->viewArea, this));
뷰어팩토리는 생성 시 사용 가능한 모든 플러그인을 로드합니다. 로드된 플러그인, 이름, 지원되는 MIME 유형을 쿼리할 수 있는 공용 API를 제공합니다:
using ViewerList = QList<AbstractViewer *>; QStringList viewerNames(bool showDefault = false) const; ViewerList viewers() const; AbstractViewer *findViewer(const QString &viewerName) const; AbstractViewer *defaultViewer() const; QStringList supportedMimeTypes() const;
viewer()
함수는 인자로 전달된 QFile 을 열기에 적합한 플러그인에 대한 포인터를 반환합니다:
m_viewer = m_factory->viewer(file);
애플리케이션 설정에 뷰어용 섹션이 포함되어 있으면 뷰어의 가상 restoreState()
함수에 전달됩니다:
void MainWindow::restoreViewerSettings() { if (!m_viewer) return; QSettings settings; settings.beginGroup(settingsViewers); QByteArray viewerSettings = settings.value(m_viewer->viewerName(), QByteArray()).toByteArray(); settings.endGroup(); if (!viewerSettings.isEmpty()) m_viewer->restoreState(viewerSettings); }
그런 다음 표준 UI 에셋이 뷰어에 전달되고 메인 스크롤 영역이 뷰어의 디스플레이 위젯을 표시하도록 설정됩니다:
m_viewer->initViewer(ui->actionBack, ui->actionForward, ui->menuHelp->menuAction(), ui->tabWidget); restoreViewerSettings(); ui->scrollArea->setWidget(m_viewer->widget()); return true; }
AbstractViewer 클래스
AbstractViewer
는 문서를 보고, 저장하고, 인쇄할 수 있는 일반화된 API를 제공합니다. 문서와 뷰어 모두의 속성을 쿼리할 수 있습니다:
- 문서에 콘텐츠가 있는가?
- 문서가 수정되었는가?
- 개요(썸네일 또는 북마크)가 지원되는가?
AbstractViewer는 파생 클래스가 기본 창에 작업과 메뉴를 만들 수 있도록 보호된 메서드를 제공합니다. 이러한 자산을 기본 창에 표시하기 위해 해당 자산은 기본 창에 부모가 됩니다. AbstractViewer는 생성한 UI 에셋을 제거하고 파기할 책임이 있습니다. QObject 에서 상속하여 신호와 슬롯을 구현합니다.
신호
void uiInitialized();
이 신호는 뷰어가 메인 창에서 UI 에셋에 대한 모든 필요한 정보를 수신한 후에 발생합니다.
void printingEnabledChanged(bool enabled);
이 신호는 문서 인쇄가 활성화 또는 비활성화될 때 발생합니다. 예를 들어 새 문서가 성공적으로 로드되었거나 모든 콘텐츠가 제거된 후에 발생합니다.
void showMessage(const QString &message, int timeout = 8000);
이 신호는 사용자에게 상태 메시지를 표시하기 위해 발생합니다.
void documentLoaded(const QString &fileName);
이 신호는 문서가 성공적으로 로드되었음을 애플리케이션에 알립니다.
TxtViewer 클래스
TxtViewer
는 간단한 텍스트 뷰어로, AbstractViewer를 상속합니다. 텍스트 파일 편집, 복사/잘라내기 및 붙여넣기, 인쇄 및 변경 사항 저장을 지원합니다.
ImageViewer 클래스
ImageViewer
는 QLabel 을 사용하여 QImageReader 에서 지원하는 이미지를 표시합니다.
생성자에서는 더 큰 사진을 허용하기 위해 QImageReader 의 할당 제한을 늘립니다:
ImageViewer::ImageViewer() : m_formats(imageFormats()) { connect(this, &AbstractViewer::uiInitialized, this, &ImageViewer::setupImageUi); QImageReader::setAllocationLimit(1024); // MB }
openFile() 함수에서 이미지를 로드하고 크기를 결정합니다. 화면보다 큰 경우 화면 비율을 유지하면서 화면 크기로 축소합니다. 이 계산은 기본 픽셀로 수행해야 하며, 결과 픽셀맵에 디바이스 픽셀 비율을 설정해야 선명하게 표시됩니다:
void ImageViewer::openFile() { #if QT_CONFIG(cursor) QGuiApplication::setOverrideCursor(Qt::WaitCursor); #endif const QString name = m_file->fileName(); QImageReader reader(name); const QImage origImage = reader.read(); if (origImage.isNull()) { statusMessage(tr("Cannot read file %1:\n%2.") .arg(QDir::toNativeSeparators(name), reader.errorString()), tr("open")); disablePrinting(); #if QT_CONFIG(cursor) QGuiApplication::restoreOverrideCursor(); #endif return; } clear(); QImage image = origImage.colorSpace().isValid() ? origImage.convertedToColorSpace(QColorSpace::SRgb) : origImage; const auto devicePixelRatio = m_imageLabel->devicePixelRatioF(); m_imageSize = QSizeF(image.size()) / devicePixelRatio; QPixmap pixmap = QPixmap::fromImage(image); pixmap.setDevicePixelRatio(devicePixelRatio); m_imageLabel->setPixmap(pixmap); const QSizeF targetSize = m_imageLabel->parentWidget()->size(); if (m_imageSize.width() > targetSize.width() || m_imageSize.height() > targetSize.height()) { m_initialScaleFactor = qMin(targetSize.width() / m_imageSize.width(), targetSize.height() / m_imageSize.height()); } m_maxScaleFactor = 3 * m_initialScaleFactor; m_minScaleFactor = m_initialScaleFactor / 3; doSetScaleFactor(m_initialScaleFactor); statusMessage(msgOpen(name, origImage)); #if QT_CONFIG(cursor) QGuiApplication::restoreOverrideCursor(); #endif maybeEnablePrinting(); }
JsonViewer 클래스
JsonViewer
는 QTreeView 에 JSON 파일을 표시합니다. 내부적으로 파일의 내용을 QJsonDocument 에 로드하고 이를 사용하여 JsonItemModel
로 사용자 정의 트리 모델을 채웁니다.
JSON 뷰어 플러그인은 QAbstractItemModel 에서 상속된 사용자 정의 항목 모델을 구현하는 방법을 보여줍니다. JsonTreeItem
클래스는 JSON 데이터를 조작하고 기본 QJsonDocument 로 다시 전파하기 위한 기본 API를 제공합니다.
JsonViewer는 문서의 최상위 객체를 탐색을 위한 북마크로 사용합니다. 다른 노드(키 및 값)를 추가 북마크로 추가하거나 북마크 목록에서 제거할 수 있습니다.
PdfViewer 클래스
PdfViewer
클래스(및 플러그인)는 PDF 뷰어 위젯 예제의 포크입니다. QScroller 을 사용하여 문서를 부드럽게 넘기는 방법을 보여줍니다.
기타 관련 클래스
HoverWatcher 클래스
HoverWatcher
클래스는 위젯 위에 마우스를 올려놓으면 오버라이드 커서를 설정하고, 마우스를 떼면 복원합니다. 동일한 위젯에 대해 여러 개의 HoverWatcher 인스턴스가 생성되는 것을 방지하기 위해 위젯당 싱글톤으로 구현됩니다.
HoverWatcher는 QObject 에서 상속하고 감시하는 QWidget 을 인스턴스의 부모로 삼습니다. 이벤트 필터를 설치하여 호버 이벤트를 소비하지 않고 가로챕니다:
HoverWatcher::HoverWatcher(QWidget *watched) : QObject(watched), m_watched(watched) { Q_ASSERT(watched); m_cursorShapes[Entered].emplace(Qt::OpenHandCursor); m_cursorShapes[MousePress].emplace(Qt::ClosedHandCursor); m_cursorShapes[MouseRelease].emplace(Qt::OpenHandCursor); // no default for Left => restore override cursor m_watched->installEventFilter(this); }
HoverAction
열거형은 HoverWatcher가 반응하는 동작을 나열합니다:
enum HoverAction { Entered, MousePress, MouseRelease, Left, Ignore };
정적 함수는 감시자를 생성하고, 특정 QWidget 에 대해 감시자의 존재를 확인하거나, 감시자를 해제합니다:
static HoverWatcher *watcher(QWidget *watched); static const HoverWatcher *watcher(const QWidget *watched); static bool hasWatcher(QWidget *widget); static void dismiss(QWidget *watched);
커서 모양은 각 HoverAction에 대해 설정하거나 설정 해제할 수 있습니다. 연결된 커서 모양이 없는 경우 동작이 트리거될 때 애플리케이션의 재정의 커서가 복원됩니다.
public slots: void setCursorShape(HoverAction type, Qt::CursorShape shape); void unSetCursorShape(HoverAction type);
mouseButtons
속성은 MousePress
동작에 대해 고려할 마우스 버튼을 보유합니다:
void setMouseButtons(Qt::MouseButtons buttons); void setMouseButton(Qt::MouseButton button, bool enable);
동작을 처리한 후 동작별 신호가 방출됩니다:
signals: void entered(); void mousePressed(); void mouseReleased(); void left();
처리된 액션을 인수로 전달하는 일반 신호가 방출됩니다:
void hoverAction(HoverAction action);
최근 파일 클래스
RecentFiles
는 최근에 연 파일 목록을 관리하는 데 특화된 QStringList 입니다.
RecentFiles에는 단일 파일 또는 여러 파일을 한 번에 추가할 수 있는 슬롯이 있습니다. 경로가 존재하고 열 수 있는 파일을 가리키면 최근 파일 목록에 항목이 추가됩니다. 파일이 이미 목록에 있는 경우에는 원래 위치에서 제거되고 맨 위에 추가됩니다.
public slots: void addFile(const QString &fileName) { addFile(fileName, EmitPolicy::EmitWhenChanged); } void addFiles(const QStringList &fileNames);
파일은 이름 또는 색인을 기준으로 목록에서 제거됩니다:
void removeFile(const QString &fileName) { removeFile(m_files.indexOf(fileName)); } void removeFile(qsizetype index) {removeFile(index, RemoveReason::Other); }
QSettings 에서 저장 및 복원을 구현하는 슬롯:
void saveSettings(QSettings &settings, const QString &key) const; bool restoreFromSettings(QSettings &settings, const QString &key);
설정을 복원할 때 존재하지 않는 파일은 무시됩니다. maxFiles
속성은 저장할 최근 파일의 최대 용량을 보유합니다(기본값은 10개).
qsizetype maxFiles();
void setMaxFiles(qsizetype maxFiles);
RecentFiles
파일을 수락하기 전에 파일을 읽을 수 있는지 확인합니다.
최근 파일 메뉴 클래스
RecentFileMenu
는 QMenu, 최근 파일 객체를 하위 메뉴로 표시하는 데 특화되어 있습니다.
이 클래스의 생성자는 부모 QObject 에 대한 포인터와 시각화할 콘텐츠인 RecentFiles 객체에 대한 포인터를 받습니다. 사용자가 목록에서 최근 파일을 선택하면 트리거되는 fileOpened()
신호는 파일의 절대 경로를 인수로 전달합니다.
참고: RecentFileMenu
는 부모 위젯 또는 생성자에게 전달된 RecentFiles
객체에 의해 소멸됩니다.
class RecentFileMenu : public QMenu { Q_OBJECT public: explicit RecentFileMenu(QWidget *parent, RecentFiles *recent); signals: void fileOpened(const QString &fileName); ... };
번역
애플리케이션의 사용자 인터페이스는 영어와 독일어로 제공됩니다. 기본 언어는 Qt에 의해 자동 선택됩니다: 시스템 언어가 독일어인 경우 독일어, 그렇지 않은 경우 영어입니다. 또한 사용자는 Help > Language 메뉴에서 언어를 전환할 수 있습니다. 각 플러그인과 기본 애플리케이션은 런타임 중에 자체 번역을 독립적으로 로드할 책임이 있습니다.
CMake 통합
최상위 레벨의 CMakeLists.txt는 제공된 언어를 선언합니다.
qt_standard_project_setup(REQUIRES 6.8 I18N_SOURCE_LANGUAGE en I18N_TRANSLATED_LANGUAGES de )
documentviewer
대상은 기본 애플리케이션을 정의합니다. 이 파일은 대상에 대한 현지화된 문자열을 docviewer_de.ts 및 docviewer_en.ts 파일에 저장하고 로드합니다. 또한 Qt에서 제공하는 각 qtbase translations
을 생성된 번역 파일에 병합하여 인쇄 대화 상자와 같은 Qt 대화 상자도 올바르게 번역되도록 합니다:
qt_add_translations(documentviewer SOURCE_TARGETS documentviewer abstractviewer TS_FILE_BASE docviewer MERGE_QT_TRANSLATIONS QT_TRANSLATION_CATALOGS qtbase )
각 플러그인 레벨 CMakeLists.txt
은 해당 플러그인의 소스 파일(SOURCE_TARGETS
)에 대해서만 qt_add_translations
을 호출합니다. 번역 파일의 범위를 플러그인 대상으로 지정하면 기본 애플리케이션과 다른 플러그인의 소스를 다시 스캔하고 재번역하는 것을 방지할 수 있습니다:
qt_add_translations(txtviewer SOURCE_TARGETS txtviewer TS_FILE_BASE txtviewer )
번역기 클래스
Translator
클래스는 메인 애플리케이션과 각 플러그인 모두에 대한 국제화를 관리하는 Qt의 QTranslator 을 감싸는 래퍼입니다. 각 컴포넌트(기본 애플리케이션 및 플러그인)에는 자체 번역기 인스턴스가 있어 전체 애플리케이션에서 조정된 언어 전환이 가능합니다. 시작 시 또는 사용자가 새 언어를 선택하면 Translator::install()
이 호출됩니다. 이 메서드는 QTranslator::load()를 사용하여 QLocale::uiLanguages()와 Qt 리소스 시스템의 기본 이름을 기반으로 번역 파일을 로드합니다. 일치하는 번역을 찾지 못하면 영어로 되돌아갑니다.
void Translator::install() { if (m_baseName.isEmpty()) { qWarning() << "The basename of the translation is not set. Ignoring."; return; } if (!m_translator.isEmpty()) qApp->removeTranslator(&m_translator); if (m_translator.load(m_trLocale, m_baseName, "_"_L1, ":/i18n/"_L1) && qApp->installTranslator(&m_translator)) { qInfo() << "Loaded translation" << m_translator.filePath(); } else { if (m_trLocale.language() != QLocale::영어) { qWarning() << "Failed to load translation" << m_baseName << "for locale"<< m_trLocale.name()<< ". 영어 번역으로 돌아가기"; setLanguage(QLocale::영어); } } }
플러그인 지원
AbstractViewer
베이스 클래스는 각 플러그인이 자체 번역을 관리할 수 있도록 세 가지 메서드를 제공합니다:
AbstractViewer::setTranslationBaseName()
Translator
객체를 초기화하고, 기본 이름을 설정하고, 기본 번역을 로드하기 위해 설치합니다.void AbstractViewer::setTranslationBaseName(const QString &baseName) { m_translator.reset(new Translator); m_translator->setBaseName(baseName); m_translator->install(); }
AbstractViewer::updateTranslation()
기존 번역기에서install()
을 호출하여 새 번역을 설치한 다음retranslate()
을 호출하여 모든 텍스트를 새로 고칩니다.void AbstractViewer::updateTranslation(QLocale::Language lang) { if (m_translator) { m_translator->setLanguage(lang); m_translator->install(); retranslate(); } }
AbstractViewer::retranslate()
각 플러그인이 자체 UI 텍스트를 재번역하기 위해 구현하는 가상 메서드입니다. 예를 들어 이미지 뷰어에서 다시 구현되었습니다:void ImageViewer::retranslate() { m_toolBar->setWindowTitle(tr("Images")); m_zoomInAct->setText(tr("Zoom &In")); m_zoomOutAct->setText(tr("Zoom &Out")); m_resetZoomAct->setText(tr("Reset Zoom")); }
애플리케이션 시작
- 기본 애플리케이션:
main.cpp
에서는 창을 표시하기 전에 애플리케이션의 번역을 로드합니다:Translator mainTranslator; mainTranslator.setBaseName("docviewer"_L1); mainTranslator.install();
- 플러그인: 각 플러그인은
init()
함수에서AbstractViewer::setTranslationBaseName()
을 호출하여 번역 파일 이름으로 번역기를 초기화하고 현재 언어의 번역을 설치합니다.void ImageViewer::init(QFile *file, QWidget *parent, QMainWindow *mainWindow) ... setTranslationBaseName("imgviewer"_L1); ...
런타임 언어 전환
런타임 언어 전환은 두 가지 방법으로 수행할 수 있습니다:
- 메뉴 사용 Help > Language:
QMenu
항목을 클릭하면MainWindow::onActionSwitchLanguage()
이 트리거되어 새 언어가 설치되고 기본 애플리케이션과 플러그인이 다시 번역됩니다:void MainWindow::onActionSwitchLanguage(QLocale::Language lang) { m_translator.setLanguage(lang); m_translator.install(); ui->retranslateUi(this); const auto viewerList = m_factory->viewers(); for (AbstractViewer *viewer : viewerList) viewer->updateTranslation(lang); statusBar()->clearMessage(); }
- 런타임에 전체 시스템의 언어를 전환하는 방법: 애플리케이션은 이벤트를 수신하고
QEvent::LocaleChange
이벤트에서MainWindow::onActionSwitchLanguage()
을 호출하여 이에 반응할 수 있습니다.void MainWindow::changeEvent(QEvent *event) { if (event->type() == QEvent::LocaleChange) onActionSwitchLanguage(QLocale::system().language()); QMainWindow::changeEvent(event); }
소스 파일
모든 Qt 예제를참조하십시오 .
© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.