스타 델리게이트 예제
별 델리게이트 예제는 스스로 칠할 수 있고 편집을 지원하는 델리게이트를 만드는 방법을 보여줍니다.
QListView, QTableView 또는 QTreeView 에 데이터를 표시할 때 개별 항목은 델리게이트에 의해 그려집니다. 또한 사용자가 항목 편집을 시작하면(예: 항목을 두 번 클릭하여) 델리게이트는 편집이 진행되는 동안 항목 위에 배치되는 편집기 위젯을 제공합니다.
델리게이트는 QAbstractItemDelegate 의 서브 클래스입니다. Qt는 QAbstractItemDelegate 을 상속하고 가장 일반적인 데이터 유형(특히 int
과 QString)을 처리하는 QStyledItemDelegate 을 제공합니다. 사용자 정의 데이터 유형을 지원해야 하거나 기존 데이터 유형에 대한 렌더링 또는 편집을 사용자 정의하려는 경우 QAbstractItemDelegate 또는 QStyledItemDelegate 을 서브클래스로 만들 수 있습니다. 델리게이트에 대한 자세한 내용은 델리게이트 클래스를, Qt의 모델/뷰 아키텍처(델리게이트 포함)에 대한 높은 수준의 소개가 필요한 경우 모델/뷰 프로그래밍을 참조하십시오.
이 예제에서는 "별 5개 중 1개"와 같은 값을 저장할 수 있는 "별점" 데이터 타입을 렌더링하고 편집하는 사용자 정의 델리게이트를 구현하는 방법을 살펴봅니다.
이 예제는 다음 클래스로 구성됩니다:
StarRating
는 사용자 정의 데이터 유형입니다. "별 5개 중 2개" 또는 "별 6개 중 5개"와 같이 별점으로 표현된 평점을 저장합니다.StarDelegate
QStyledItemDelegate 을 상속하고StarRating
( QStyledItemDelegate 에서 이미 처리하는 데이터 유형에 추가하여)을 지원합니다.StarEditor
QWidget 을 상속하고StarDelegate
에서 사용자가 마우스를 사용하여 별점을 편집할 수 있도록 합니다.
StarDelegate
의 동작을 보여주기 위해 QTableWidget 에 일부 데이터를 채우고 그 위에 델리게이트를 설치하겠습니다.
스타 델리게이트 클래스 정의
다음은 StarDelegate
클래스의 정의입니다:
class StarDelegate : public QStyledItemDelegate { Q_OBJECT public: using QStyledItemDelegate::QStyledItemDelegate; void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override; QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override; void setEditorData(QWidget *editor, const QModelIndex &index) const override; void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override; private slots: void commitAndCloseEditor(); };
모든 공용 함수는 사용자 정의 렌더링 및 편집을 제공하기 위해 QStyledItemDelegate 의 가상 함수를 재구현한 것입니다.
스타 델리게이트 클래스 구현
paint() 함수는 QStyledItemDelegate 에서 다시 구현되었으며 뷰에서 항목을 다시 칠해야 할 때마다 호출됩니다:
void StarDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { if (index.data().canConvert<StarRating>()) { StarRating starRating = qvariant_cast<StarRating>(index.data()); if (option.state & QStyle::State_Selected) painter->fillRect(option.rect, option.palette.highlight()); starRating.paint(painter, option.rect, option.palette, StarRating::EditMode::ReadOnly); } else { QStyledItemDelegate::paint(painter, option, index); }
이 함수는 모델에서 QModelIndex 개체로 표시되는 각 항목에 대해 한 번씩 호출됩니다. 항목에 저장된 데이터가 StarRating
인 경우 직접 페인트하고, 그렇지 않은 경우 QStyledItemDelegate 이 대신 페인트하도록 합니다. 이렇게 하면 StarDelegate
에서 가장 일반적인 데이터 유형을 처리할 수 있습니다.
항목이 StarRating
인 경우 항목이 선택되면 배경을 그리고, 나중에 검토할 StarRating::paint()
을 사용하여 항목을 그립니다.
StartRating
starrating.h
에 나타나는 () 매크로 덕분에 에 저장할 수 있습니다. 이에 대해서는 나중에 자세히 설명합니다. Q_DECLARE_METATYPE QVariant
createEditor() 함수는 사용자가 항목 편집을 시작할 때 호출됩니다:
QWidget *StarDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const { if (index.data().canConvert<StarRating>()) { StarEditor *editor = new StarEditor(parent); connect(editor, &StarEditor::editingFinished, this, &StarDelegate::commitAndCloseEditor); return editor; } return QStyledItemDelegate::createEditor(parent, option, index); }
항목이 StarRating
인 경우 StarEditor
을 생성하고 editingFinished()
신호를 commitAndCloseEditor()
슬롯에 연결하여 편집기가 닫힐 때 모델을 업데이트할 수 있습니다.
다음은 commitAndCloseEditor()
의 구현입니다:
void StarDelegate::commitAndCloseEditor() { StarEditor *editor = qobject_cast<StarEditor *>(sender()); emit commitData(editor); emit closeEditor(editor); }
사용자가 편집을 완료하면 commitData() 및 closeEditor()(둘 다 QAbstractItemDelegate)에서 선언됨)를 방출하여 모델에 편집된 데이터가 있음을 알리고 뷰에 편집기가 더 이상 필요하지 않음을 알립니다.
setEditorData() 함수는 편집기를 만들 때 호출되어 모델의 데이터로 초기화합니다:
void StarDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { if (index.data().canConvert<StarRating>()) { StarRating starRating = qvariant_cast<StarRating>(index.data()); StarEditor *starEditor = qobject_cast<StarEditor *>(editor); starEditor->setStarRating(starRating); } else { QStyledItemDelegate::setEditorData(editor, index); } }
에디터에서 setStarRating()
을 호출하면 됩니다.
setModelData() 함수는 편집이 완료되면 편집기의 데이터를 모델에 커밋하기 위해 호출됩니다:
void StarDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { if (index.data().canConvert<StarRating>()) { StarEditor *starEditor = qobject_cast<StarEditor *>(editor); model->setData(index, QVariant::fromValue(starEditor->starRating())); } else { QStyledItemDelegate::setModelData(editor, model, index); } }
sizeHint()
함수는 항목의 기본 크기를 반환합니다:
QSize StarDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const { if (index.data().canConvert<StarRating>()) { StarRating starRating = qvariant_cast<StarRating>(index.data()); return starRating.sizeHint(); } return QStyledItemDelegate::sizeHint(option, index); }
StarRating
로 호출을 전달하기만 하면 됩니다.
스타에디터 클래스 정의
StarDelegate
을 구현할 때 StarEditor
클래스가 사용되었습니다. 다음은 클래스 정의입니다:
class StarEditor : public QWidget { Q_OBJECT public: StarEditor(QWidget *parent = nullptr); QSize sizeHint() const override; void setStarRating(const StarRating &starRating) { myStarRating = starRating; } StarRating starRating() { return myStarRating; } signals: void editingFinished(); protected: void paintEvent(QPaintEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override; private: int starAtPosition(int x) const; StarRating myStarRating; };
이 클래스는 사용자가 편집기 위로 마우스를 이동하여 StarRating
을 편집할 수 있도록 합니다. 사용자가 편집기를 클릭하면 editingFinished()
신호를 보냅니다.
보호된 함수는 마우스 및 페인트 이벤트를 처리하기 위해 QWidget 에서 다시 구현됩니다. 비공개 함수 starAtPosition()
는 마우스 포인터 아래 있는 별의 번호를 반환하는 도우미 함수입니다.
StarEditor 클래스 구현
생성자부터 시작하겠습니다:
StarEditor::StarEditor(QWidget *parent) : QWidget(parent) { setMouseTracking(true); setAutoFillBackground(true); }
사용자가 마우스 버튼을 누르고 있지 않아도 커서를 따라갈 수 있도록 위젯에서 mouse tracking 을 활성화합니다. 또한 QWidget 의 auto-fill background 기능을 켜서 불투명한 배경을 얻습니다. (이 호출이 없으면 뷰의 배경이 편집기를 통해 빛납니다.)
paintEvent() 함수는 QWidget 에서 다시 구현됩니다:
void StarEditor::paintEvent(QPaintEvent *) { QPainter painter(this); myStarRating.paint(&painter, rect(), palette(), StarRating::EditMode::Editable); }
StarDelegate
을 구현할 때와 마찬가지로 StarRating::paint()
을 호출하여 별을 그리기만 하면 됩니다.
void StarEditor::mouseMoveEvent(QMouseEvent *event) { const int star = starAtPosition(event->position().toPoint().x()); if (star != myStarRating.starCount() && star != -1) { myStarRating.setStarCount(star); update(); } QWidget::mouseMoveEvent(event); }
마우스 이벤트 핸들러에서는 개인 데이터 멤버 myStarRating
에서 setStarCount()
를 호출하여 현재 커서 위치를 반영하고 QWidget::update()를 호출하여 강제로 다시 칠합니다.
void StarEditor::mouseReleaseEvent(QMouseEvent *event) { emit editingFinished(); QWidget::mouseReleaseEvent(event); }
사용자가 마우스 버튼을 놓으면 editingFinished()
신호를 전송하기만 하면 됩니다.
int StarEditor::starAtPosition(int x) const { const int star = (x / (myStarRating.sizeHint().width() / myStarRating.maxStarCount())) + 1; if (star <= 0 || star > myStarRating.maxStarCount()) return -1; return star; }
starAtPosition()
함수는 기본 선형 대수를 사용하여 커서 아래에 있는 별을 찾습니다.
별점 등급 클래스 정의
class StarRating { public: enum class EditMode { Editable, ReadOnly }; explicit StarRating(int starCount = 1, int maxStarCount = 5); void paint(QPainter *painter, const QRect &rect, const QPalette &palette, EditMode mode) const; QSize sizeHint() const; int starCount() const { return myStarCount; } int maxStarCount() const { return myMaxStarCount; } void setStarCount(int starCount) { myStarCount = starCount; } void setMaxStarCount(int maxStarCount) { myMaxStarCount = maxStarCount; } private: QPolygonF starPolygon; QPolygonF diamondPolygon; int myStarCount; int myMaxStarCount; }; Q_DECLARE_METATYPE(StarRating)
StarRating
클래스는 별의 개수로 등급을 나타냅니다. 데이터를 보유하는 것 외에도 이 예제에서는 뷰 또는 편집기인 QPaintDevice 에 별을 칠할 수도 있습니다. myStarCount
멤버 변수는 현재 등급을 저장하고 myMaxStarCount
은 가능한 최고 등급(일반적으로 5)을 저장합니다.
Q_DECLARE_METATYPE()
매크로는 StarRating
유형을 QVariant 에 알려 StarRating
값을 QVariant 에 저장할 수 있게 합니다.
StarRating 클래스 구현
생성자는 myStarCount
및 myMaxStarCount
을 초기화하고 별과 다이아몬드를 그리는 데 사용되는 다각형을 설정합니다:
StarRating::StarRating(int starCount, int maxStarCount) : myStarCount(starCount), myMaxStarCount(maxStarCount) { starPolygon << QPointF(1.0, 0.5); for (int i = 1; i < 5; ++i) starPolygon << QPointF(0.5 + 0.5 * std::cos(0.8 * i * 3.14), 0.5 + 0.5 * std::sin(0.8 * i * 3.14)); diamondPolygon << QPointF(0.4, 0.5) << QPointF(0.5, 0.4) << QPointF(0.6, 0.5) << QPointF(0.5, 0.6) << QPointF(0.4, 0.5); }
paint()
함수는 이 StarRating
객체의 별을 페인트 장치에 칠합니다:
void StarRating::paint(QPainter *painter, const QRect &rect, const QPalette &palette, EditMode mode) const { painter->save(); painter->setRenderHint(QPainter::Antialiasing, true); painter->setPen(Qt::NoPen); painter->setBrush(mode == EditMode::Editable ? palette.highlight() : palette.windowText()); const int yOffset = (rect.height() - PaintingScaleFactor) / 2; painter->translate(rect.x(), rect.y() + yOffset); painter->scale(PaintingScaleFactor, PaintingScaleFactor); for (int i = 0; i < myMaxStarCount; ++i) { if (i < myStarCount) painter->drawPolygon(starPolygon, Qt::WindingFill); else if (mode == EditMode::Editable) painter->drawPolygon(diamondPolygon, Qt::WindingFill); painter->translate(1.0, 0.0); } painter->restore(); }
먼저 페인팅에 사용할 펜과 브러시를 설정합니다. mode
매개 변수는 Editable
또는 ReadOnly
일 수 있습니다. mode
이 편집 가능한 경우 WindowText 색상 대신 Highlight 색상을 사용하여 별을 그립니다.
그런 다음 별을 그립니다. Edit
모드인 경우 등급이 최고 등급보다 낮으면 별 대신 다이아몬드를 그립니다.
sizeHint()
함수는 별을 칠할 영역의 기본 크기를 반환합니다:
기본 설정 크기는 최대 별 개수를 칠하기에 충분한 크기입니다. 이 함수는 StarDelegate::sizeHint()
와 StarEditor::sizeHint()
에서 모두 호출됩니다.
main()
함수
다음은 프로그램의 main()
함수입니다:
int main(int argc, char *argv[]) { QApplication app(argc, argv); QTableWidget tableWidget(4, 4); tableWidget.setItemDelegate(new StarDelegate); tableWidget.setEditTriggers(QAbstractItemView::DoubleClicked | QAbstractItemView::SelectedClicked); tableWidget.setSelectionBehavior(QAbstractItemView::SelectRows); tableWidget.setHorizontalHeaderLabels({"Title", "Genre", "Artist", "Rating"}); populateTableWidget(&tableWidget); tableWidget.resizeColumnsToContents(); tableWidget.resize(500, 300); tableWidget.show(); return app.exec(); }
main()
함수는 QTableWidget 을 생성하고 StarDelegate
을 설정합니다. DoubleClicked 과 SelectedClicked 은 edit triggers 으로 설정하여 별점 항목을 선택하면 클릭 한 번으로 편집기가 열리도록 합니다.
populateTableWidget()
함수는 QTableWidget 을 데이터로 채웁니다:
void populateTableWidget(QTableWidget *tableWidget) { static constexpr struct { const char *title; const char *genre; const char *artist; int rating; } staticData[] = { { "Mass in B-Minor", "Baroque", "J.S. Bach", 5 }, ... { nullptr, nullptr, nullptr, 0 } }; for (int row = 0; staticData[row].title != nullptr; ++row) { QTableWidgetItem *item0 = new QTableWidgetItem(staticData[row].title); QTableWidgetItem *item1 = new QTableWidgetItem(staticData[row].genre); QTableWidgetItem *item2 = new QTableWidgetItem(staticData[row].artist); QTableWidgetItem *item3 = new QTableWidgetItem; item3->setData(0, QVariant::fromValue(StarRating(staticData[row].rating))); tableWidget->setItem(row, 0, item0); tableWidget->setItem(row, 1, item1); tableWidget->setItem(row, 2, item2); tableWidget->setItem(row, 3, item3); } }
StarRating
을 QVariant 으로 변환하려면 QVariant::fromValue 을 호출하세요.
가능한 확장 및 제안
Qt의 모델/보기 프레임워크를 사용자 정의하는 방법에는 여러 가지가 있습니다. 이 예제에서 사용된 접근 방식은 대부분의 사용자 정의 대리인 및 편집기에 적합합니다. 스타 델리게이트와 스타 에디터에서 사용하지 않은 가능성의 예는 다음과 같습니다:
- 편집 트리거에 의존하지 않고 QAbstractItemView::edit()를 호출하여 프로그래밍 방식으로 편집기를 열 수 있습니다. 이는 QAbstractItemView::EditTrigger 열거형에서 제공하는 것 이외의 다른 편집 트리거를 지원하는 데 사용할 수 있습니다. 예를 들어 스타 델리게이트 예제에서 마우스로 항목 위로 마우스를 가져가면 편집기를 팝업하는 방법으로 적합할 수 있습니다.
- QAbstractItemDelegate::editorEvent()를 다시 구현하면 별도의 QWidget 서브클래스를 만드는 대신 델리게이트에서 직접 편집기를 구현할 수 있습니다.
© 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.