2015-11-30 20 views
6

Phiên bản ngắnCơ chế của đại biểu mặc định cho chế độ xem mục trong Qt là gì?

Đại biểu mặc định được sử dụng bởi QTreeView là gì? Đặc biệt tôi đang cố gắng tìm phương pháp paint() của nó?

dài hơn phiên bản

Tôi là một người sử dụng Python (Pyside/PyQt), và đang sử dụng một đại biểu tùy chỉnh để tái tạo một số các chức năng của QTreeView. Do đó, tôi đang cố gắng tìm đại biểu mặc định và phương thức vẽ, được sử dụng trong một QTreeView. Thậm chí tốt hơn sẽ là một lời giải thích về cách nó hoạt động.

bài Chữ thập

tôi đăng các câu hỏi tương tự tại Trung tâm Qt (http://www.qtcentre.org/threads/64458-Finding-default-delegate-for-QTreeView?).

Trả lời

8

tl; dr

Các đại biểu mặc định cho tất cả các quan điểm mục là QStyledItemDelegate. Phương thức paint() gọi drawControl(), được định nghĩa trong qcommonstyle.cpp, để vẽ từng mục. Do đó, hãy nghiên cứu chi tiết qcommonstyle.cpp để biết chi tiết về các chi tiết nitty-gritty về cách thức mỗi mục được vẽ.


dạng dài câu trả lời

Những người thích ngắn gọn nên đọc tl; dr trên và documentation on Styles in Item Views. Nếu bạn vẫn còn bị mắc kẹt (và nhiều người dùng Python có thể sẽ có), phần còn lại của câu trả lời này sẽ giúp ích.

I. Các đại biểu mặc định là QStyledItemDelegate

Các QStyledItemDelegate là các đại biểu mặc định cho các mục trong quan điểm. Điều này được nêu rõ trong Qt's Model/View Programming overview:

Kể từ khi Qt 4.4, thực hiện đại diện mặc định được cung cấp bởi QStyledItemDelegate, và điều này được sử dụng như các đại biểu mặc định bằng cách quan điểm tiêu chuẩn Qt của.

Các docs for QStyledItemDelegate cung cấp một ít chi tiết hơn:

Khi hiển thị dữ liệu từ các mô hình trong quan điểm mục Qt, ví dụ, một QTableView, các mục riêng lẻ được vẽ bởi một đại biểu. Ngoài ra, khi một mục được chỉnh sửa là , nó cung cấp tiện ích trình chỉnh sửa được đặt ở đầu chế độ xem mục trong khi chỉnh sửa diễn ra. QStyledItemDelegate là ủy nhiệm mặc định cho tất cả các chế độ xem mục Qt và được cài đặt trên chúng khi chúng được tạo.

Tóm lại, nếu bạn muốn hiểu cơ chế cơ bản của bất kỳ chế độ xem mục nào (không chỉ là chế độ xem dạng cây, mà còn cả bảng và danh sách), hãy nghiên cứu QStyledItemDelegate.

Phương pháp paint() trong qstyleditemdleegate.cpp được xác định on line 419 of the doxygenated code base. Hãy nhìn vào hai dòng cuối cùng:

QStyle *style = widget ? widget->style() : QApplication::style(); 
    style->drawControl(QStyle::CE_ItemViewItem, &opt, painter, widget); 

Hai điều đang diễn ra ở đây. Đầu tiên, kiểu được đặt - thường là QApplication.style(). Thứ hai, phương thức drawControl() của kiểu đó được gọi để vẽ mục đang được vẽ. Và đó là nó. Đó là nghĩa đen là dòng cuối cùng của QStyledItemDelegate.paint()!

Do đó, nếu bạn thực sự muốn tìm hiểu cách đại biểu mặc định vẽ tranh, chúng tôi thực sự phải nghiên cứu phong cách, đang thực hiện tất cả công việc thực tế. Đó là những gì chúng tôi sẽ làm trong phần còn lại của tài liệu này.

II. QStyle: Điều gì mang lại cho các đại biểu phong cách của nó?

Khi bất cứ thứ gì được hiển thị bằng Qt, nó được vẽ theo một số kiểu được chọn theo cách hệ thống cụ thể, khi bạn khởi tạo QApplication. Từ the docs for QStyle:

Lớp QStyle là lớp cơ sở trừu tượng đóng gói giao diện và cảm giác của GUI. Qt chứa một tập hợp các lớp con QStyle mô phỏng kiểu của các nền tảng khác nhau được Qt hỗ trợ (QWindowsStyle, QMacStyle, QMotifStyle, v.v.). Theo mặc định, các kiểu này được tích hợp vào thư viện QtGui.

Trong Qt, bạn sẽ tìm thấy mã nguồn cho kiểu N trong src/gui/styles/N.cpp.

Mỗi kiểu có chứa việc thực hiện các thao tác cơ bản được sử dụng để vẽ mọi thứ trong GUI, từ chế độ xem dạng cây đến menu thả xuống. Các kiểu chuẩn, chẳng hạn như QWindowsStyle, kế thừa hầu hết các phương thức của chúng từ QCommonStyle. Mỗi phong cách cụ thể thường chỉ bao gồm các độ lệch nhỏ từ cơ sở chung đó. Do đó, một nghiên cứu gần gũi của qcommonstyle.cpp sẽ tiết lộ chức năng cơ bản mà các nhà phát triển Qt thấy hữu ích khi vẽ tất cả các phần của GUI. Tầm quan trọng của nó là khó để vượt qua.

Trong phần tiếp theo, chúng tôi sẽ kiểm tra các phần có liên quan để vẽ các mục xem.

III. QStyle.drawControl(): thực hiện nội soi trên các đại biểu

Như đã đề cập ở trên, tìm hiểu các cơ chế cơ bản của bản vẽ một cái nhìn đòi hỏi kiểm tra việc thực hiện các drawControl() trong qcommonstyle.cpp, thực hiện điều đó bắt đầu từ ngày dòng 1197. Lưu ý trong những gì sau, khi tôi đề cập đến một số dòng mà không đề cập đến tên tệp, theo quy ước, tôi đang đề cập đến qcommonstyle.cpp in the doxygenated code base.

Các documentation for QStyle.drawControl() là bài học:

QStyle.drawControl (yếu tố, tùy chọn, họa sĩ)

Tham số:

  • yếu tố - QStyle.ControlElement

  • tùy chọn - QtGui.QStyleOption

  • họa sĩ - PySide.QtGui.QPainter

Vẽ yếu tố được đưa ra với các họa sĩ cung cấp cùng với tùy chọn phong cách theo quy định của tùy chọn .... Thông số tùy chọn là con trỏ đến đối tượng QStyleOption và chứa tất cả thông tin bắt buộc để vẽ phần tử mong muốn.

Người gọi cho biết drawControl() loại yếu tố mà nó đang cố vẽ bằng cách chuyển cờ QStyle.ControlElement. Các phần tử điều khiển là các thành phần cấp cao hơn của một cửa sổ hiển thị thông tin cho người dùng: những thứ như hộp kiểm, nút bấm và các mục menu. Tất cả các yếu tố kiểm soát được liệt kê ở đây:

http://pyqt.sourceforge.net/Docs/PyQt4/qstyle.html#ControlElement-enum

Nhớ lại các yếu tố kiểm soát được gửi trong lời kêu gọi QStyledItemDelegate.paint()CE_ItemViewItem, mà chỉ đơn giản là một món hàng sẽ được hiển thị bên trong một cái nhìn mục. Trong QCommonStyle.drawControl(), trường hợp CE_ItemViewItem bắt đầu từ ngày dòng 2153. Chúng ta hãy đào ở

A. subElementRect():. Kích thước quan trọng

Nó là chìa khóa để có được kích thước và bố cục của từng hạng mục đúng. Đây là điều đầu tiên drawControl() tính toán. Để có được thông tin này, nó gọi subElementRect() (được xác định trên dòng 2313 và được gọi đầu tiên trên dòng 2158). Ví dụ, ta có:

QRect textRect = subElementRect(SE_ItemViewItemText, vopt, widget); 

Đối số đầu tiên là một lá cờ QStyle.SubElement, trong trường hợp này SE_ItemViewItemText. Các phần con kiểu dáng đại diện cho các bộ phận cấu thành của các phần tử điều khiển. Mỗi mục trong một chế độ xem có ba phần tử con có thể có: hộp kiểm, biểu tượng và văn bản; hiển nhiên, phần con của SE_ItemViewItemText đại diện cho văn bản. Tất cả các bổ sung có thể được liệt kê ở đây:

http://pyqt.sourceforge.net/Docs/PyQt4/qstyle.html#SubElement-enum

Phương pháp subElementRect() chứa tất cả các trường hợp trong liệt kê subelement: trường hợp SE_ItemViewItemText của chúng tôi bắt đầu từ ngày dòng 3015.

Lưu ý rằng subElementRect() trả về một QRect, trong đó xác định một hình chữ nhật trong mặt phẳng sử dụng độ chính xác nguyên, và có thể được xây dựng với bốn số nguyên (trái, trên, chiều rộng, chiều cao). Ví dụ: r1 = QRect(100, 200, 11, 16). Điều này xác định, đối với một phần con, kích thước của nó cũng như vị trí x, y, nơi nó sẽ được vẽ trong khung nhìn.

subElementRect() thực sự gọi viewItemLayout() (được xác định trên dòng 999) để thực hiện công việc thực, đó là quy trình gồm hai bước. Đầu tiên, viewItemLayout() tính chiều cao và chiều rộng của các phụ con bằng cách sử dụng viewItemSize(). Thứ hai, nó tính toán vị trí x và y của subelement. Hãy xem xét từng hoạt động này lần lượt.

1.viewItemLayout(): tính toán chiều rộng và chiều cao của phần tử con

Bắt đầu từ ngày dòng 1003, viewItemLayout() gọi viewItemSize() (được định nghĩa trên dòng 838), mà tính chiều cao và chiều rộng cần thiết cho một subelement.

Nơi nào viewItemSize() nhận các số mặc định cho những thứ như chiều cao của thanh tiêu đề? Đây là tỉnh của chỉ số pixel. Chỉ số pixel là kích thước phụ thuộc vào kiểu được biểu thị bằng một giá trị pixel duy nhất. Ví dụ: Style.PM_IndicatorWidth trả về chiều rộng của chỉ báo hộp kiểm và QStyle.PM_TitleBarHeight trả về chiều cao thanh tiêu đề cho kiểu ứng dụng của bạn. Tất cả sự khác nhau QStyle.PixelMetric s được liệt kê ở đây:

http://pyqt.sourceforge.net/Docs/PyQt4/qstyle.html#PixelMetric-enum

Bạn có thể lấy giá trị của một điểm ảnh được sử dụng số liệu QStyle.pixelMetric(), được sử dụng rất nhiều trong viewItemSize(). Dữ liệu đầu vào đầu tiên của pixelMetric() là một trong số QStyle.PixelMetric s trong điều tra. Trong qcommonstyle.cpp thi hành bắt đầu từ ngày pixelMetric() dòng 4367.

Ví dụ, viewItemSize() tính toán chiều rộng và chiều cao của hộp kiểm (nếu cần thiết) bằng cách sử dụng sau đây:

return QSize(proxyStyle->pixelMetric(QStyle::PM_IndicatorWidth, option, widget), 
    proxyStyle->pixelMetric(QStyle::PM_IndicatorHeight, option, widget)); 

Lưu ý rằng pixelMetric() không chỉ được sử dụng trong viewItemSize(), nhưng có mặt khắp mọi nơi. Nó được sử dụng để tính toán các thuộc tính số liệu của nhiều phần tử GUI, từ đường viền cửa sổ đến các biểu tượng, và được xếp chồng lên nhau trong suốt qcommonstyle.cpp. Về cơ bản, bất cứ khi nào bạn cần biết số lượng pixel mà phong cách của bạn sử dụng cho một số yếu tố đồ họa có kích thước không thay đổi (như hộp kiểm), kiểu sẽ gọi trên số liệu pixel.

2. viewItemLayout(): Rubik Cube các phần tử con để có được x/y vị trí

Phần thứ hai của viewItemLayout() được dành cho việc tổ chức bố trí của các phần tử con có chiều rộng và chiều cao đều chỉ tính. Nghĩa là, nó cần phải tìm vị trí x và y của chúng để hoàn thành việc điền các giá trị vào QRect s chẳng hạn như textRect. Các phần phụ sẽ được sắp xếp khác nhau tùy thuộc vào thiết lập chung của chế độ xem (ví dụ: liệu nó có phải là hướng trái hoặc phải). Do đó, viewItemLayout() tính toán vị trí nghỉ cuối cùng của mỗi phụ thuộc phụ thuộc vào các yếu tố đó.

Nếu bạn cần manh mối về cách thực hiện lại sizeHint() trong đại biểu tùy chỉnh, subElementRect() có thể là nguồn mẹo và thủ thuật hữu ích. Cụ thể, viewItemSize() có thể chứa các mẩu tin hữu ích và các chỉ số pixel có liên quan mà bạn có thể muốn truy vấn khi bạn muốn chế độ xem tùy chỉnh đối sánh chặt chẽ với mặc định.

Khi QRect s được tính cho văn bản, biểu tượng và hộp kiểm con, drawControl() di chuyển và cuối cùng bắt đầu vẽ.

B.Màu nền: nhận nguyên thủy

Đầu nền được lấp đầy trong cho mục (dòng 2163):

drawPrimitive(PE_PanelItemViewItem, option, painter); 

này là rất nhiều như các cuộc gọi đến QStyle.drawControl(), nhưng đối số đầu tiên không phải là một điều khiển phần tử, nhưng một phần tử nguyên thủy (PE). Giống như drawControl() là một phương pháp rộng lớn để điều khiển bản vẽ của tất cả các phần tử điều khiển cấp cao hơn, QStyle.drawPrimitive() vẽ hầu hết các phần tử đồ họa nguyên thủy cấp thấp hơn trong GUI (chẳng hạn như nền hình chữ nhật trong chế độ xem mục). Các đơn vị cấp dưới, các yếu tố nguyên thủy, được liệt kê ở đây:

http://pyqt.sourceforge.net/Docs/PyQt4/qstyle.html#PrimitiveElement-enum

Các tài liệu nói, "Một yếu tố nguyên thủy là một yếu tố GUI thông thường, chẳng hạn như một chỉ số hộp kiểm hoặc nút bevel." Ví dụ: phần tử nguyên thủy PE_PanelItemViewItem là "[T] anh ấy nền cho một mục trong chế độ xem mục".

Mỗi kiểu phải có triển khai drawPrimitive() và bắt đầu trên dòng 140. Trong đó, bạn có thể khám phá chi tiết cách thức thực hiện các hoạt động sơn nguyên thủy của nó. Đây là một gợi ý hữu ích về cách sử dụng lệnh paint() trong thực tế trong các đại biểu tùy chỉnh của bạn.

Việc triển khai lại QStyle.drawPrimitive() cho trường hợp PE_PanelItemViewItem bắt đầu trên dòng 773. Đầu tiên, chọn màu nền thích hợp dựa trên trạng thái của mục. Nó thực hiện điều này bằng cách truy vấn mục QStyle.StateFlag của mục. option.state chứa các cờ trạng thái mô tả trạng thái của mục tại thời điểm đó (được bật, đã chọn, đang chỉnh sửa, v.v.) không. Các trạng thái này không chỉ được sử dụng ở phía sau, nhưng bạn có thể sẽ cần sử dụng nó khi triển khai lại QStyledItemDelegate.paint() trong các đại biểu tùy chỉnh của bạn. Bạn có thể tìm thấy một điều tra của QStyle.StateFlag s ở đây:

http://pyqt.sourceforge.net/Docs/PyQt4/qstyle.html#StateFlag-enum

Sau khi chọn đúng màu, drawPrimitive() sau đó sử dụng QPainter.fillRect() để lấp đầy khu vực phù hợp với màu sắc đó (dòng 786):

p->fillRect(vopt->rect, vopt->backgroundBrush); 

QPainter.fillRect() là một phương pháp rất hữu ích khi triển khai các đại biểu tùy chỉnh cho chính bạn.

Sau khi chăm sóc nền, drawControl() sau đó tiến hành vẽ xong mục, bắt đầu bằng hộp kiểm (dòng 2165), sau đó là biểu tượng (dòng 2185) và cuối cùng là văn bản (dòng 2194). Chúng tôi sẽ không xem xét chi tiết vấn đề này, nhưng tôi sẽ kết thúc bằng cách thảo luận ngắn gọn về cách rút ra văn bản.

C. Vẽ đoạn Phúc Âm: sẽ phản bội

Thứ nhất, nhà nước của mặt hàng đó được sử dụng để xác định màu sắc của văn bản. Điều này sử dụng QStyle.StateFlags vừa được thảo luận. Sau đó, trách nhiệm drawControl() đá vẽ văn bản ra một phương pháp tùy chỉnh viewItemDrawText() (được định nghĩa tại dòng 921):

viewItemDrawText(painter, vopt, textRect); 

phương pháp này có trong họa sĩ, tùy chọn, và hình chữ nhật văn bản mô tả ở trên (phần A) như thông số.Lưu ý tham số tùy chọn là rất quan trọng: nó là một lớp học QStyleOption mà nội dung được truyền xung quanh trong một phong cách (nó bao gồm biểu tượng, checkstate, và các thuộc tính văn bản).

Sau khi văn bản được kéo từ tùy chọn, nó được kết hợp thành một QtGui.QTextLine được thêm vào QtGui.QTextLayout. Văn bản cuối cùng, elided (tức là, với hình elip, tùy thuộc vào cài đặt quấn từ) được vẽ bởi QPainter.drawText() (xem dòng 983), đây là một trong các hoạt động vẽ nguyên thủy của Qt.

Thẳng thắn một thỏa thuận tốt là viewItemDrawText() được sử dụng để xử lý việc gói từ. Đây là nơi chúng tôi bắt đầu thâm nhập vào một số can đảm của Qt mà không có người dùng Python nào từng có ý định xem, ít tinker hơn nhiều. Ví dụ, nó sử dụng lớp QStackTextEngine. Tôi khuyến khích bạn 'qstacktextengine pyqt' của Google để xem mức độ thường xuyên của điều này đối với người dùng Python. Nếu điều này bạn quan tâm, có nó!

IV. Tóm tắt

Nếu bạn muốn truy cập cơ chế sơn bên dưới cho đại biểu mặc định, bạn sẽ kết thúc nghiên cứu triển khai QStyle.drawControl() trong qcommonstyle.cpp, một con thú 6000 dòng của một tệp. Bài tập này có thể rất hữu ích cho việc tìm ra các quy trình chính xác được sử dụng để tính toán kích thước và vẽ các phần tử đồ họa nguyên thủy mà các mục chứa. Đôi khi, tuy nhiên, con thú này có thể là hết sức đáng sợ và vô ích, như khi nói đến đối phó với bọc từ. Trong những trường hợp đó, bạn có thể chỉ cần tìm ra một triển khai tùy chỉnh của chức năng mong muốn cho đại biểu của bạn.

Cuối cùng, bây giờ chúng ta đã thấy một bức tranh lớn về cách mọi thứ hoạt động, chúng tôi có thể đánh giá cao hơn cách tài liệu hữu ích cho QStyle là, cụ thể là phần Styles in Item Views. Ở đó, chúng tôi tìm thấy tình yêu mặc khải sau đây:

Tranh của các mục trong chế độ xem được thực hiện bởi một đại biểu. đại biểu mặc định Qt của, QStyledItemDelegate, cũng được sử dụng để tính hình chữ nhật giới hạn của mục (và các phần tử con của họ) ... Khi QStyledItemDelegate vẽ các mục của nó, nó thu hút CE_ItemViewItems ... Khi thực hiện một phong cách để tùy chỉnh bản vẽ các quan điểm mục, bạn cần để kiểm tra việc triển khai QCommonStyle (và bất kỳ lớp con nào khác mà từ đó phong cách của bạn được kế thừa). Bằng cách này, bạn tìm hiểu xem cách thức và cách thức các yếu tố phong cách khác đã được vẽ và sau đó bạn có thể triển khai lại bức tranh về các yếu tố cần được vẽ khác nhau.

Về cơ bản, câu trả lời cho bài đăng gốc là "Những gì họ nói".

Các vấn đề liên quan