Câu trả lời:
Các tài liệu Qt có thể giải thích nó tốt nhất:
Trong Qt, các sự kiện là các đối tượng, bắt nguồn từ
QEvent
lớp trừu tượng , đại diện cho những thứ đã xảy ra bên trong một ứng dụng hoặc là kết quả của hoạt động bên ngoài mà ứng dụng cần biết. Các sự kiện có thể được nhận và xử lý bởi bất kỳ phiên bản nào củaQObject
lớp con, nhưng chúng đặc biệt liên quan đến các widget. Tài liệu này mô tả cách các sự kiện được phân phối và xử lý trong một ứng dụng điển hình.
Vì vậy, sự kiện và tín hiệu / khe cắm là hai cơ chế song song thực hiện những việc giống nhau. Nói chung, một sự kiện sẽ được tạo bởi một thực thể bên ngoài (ví dụ: bàn phím hoặc con lăn chuột) và sẽ được phân phối thông qua vòng lặp sự kiện trong QApplication
. Nói chung, trừ khi bạn thiết lập mã, bạn sẽ không tạo sự kiện. Bạn có thể lọc chúng qua QObject::installEventFilter()
hoặc xử lý các sự kiện trong đối tượng phân lớp bằng cách ghi đè các hàm thích hợp.
Tín hiệu và Khe cắm dễ dàng tạo và nhận hơn nhiều và bạn có thể kết nối hai QObject
lớp con bất kỳ . Chúng được xử lý thông qua Metaclass (hãy xem tệp moc_classname.cpp của bạn để biết thêm), nhưng hầu hết giao tiếp giữa các lớp mà bạn sẽ tạo ra có thể sẽ sử dụng tín hiệu và khe cắm. Tín hiệu có thể được gửi ngay lập tức hoặc hoãn lại qua hàng đợi (nếu bạn đang sử dụng luồng).
Một tín hiệu có thể được tạo ra.
Trong Qt, cả tín hiệu và sự kiện đều là cách triển khai của mẫu Observer . Chúng được sử dụng trong các tình huống khác nhau vì chúng có những điểm mạnh và điểm yếu khác nhau.
Trước hết, chúng ta hãy xác định chính xác ý nghĩa của chúng tôi về 'sự kiện Qt': một hàm ảo trong lớp Qt, mà bạn dự kiến sẽ thực hiện lại trong lớp cơ sở của mình nếu bạn muốn xử lý sự kiện. Nó liên quan đến mẫu Phương pháp Mẫu .
Lưu ý cách tôi đã sử dụng từ " xử lý ". Thật vậy, đây là sự khác biệt cơ bản giữa mục đích của tín hiệu và sự kiện:
Sự khác biệt là khi bạn "xử lý" sự kiện, bạn nhận trách nhiệm "đáp trả" bằng một hành vi có ích bên ngoài lớp học. Ví dụ: hãy xem xét một ứng dụng có một nút với một số trên đó. Ứng dụng cần cho phép người dùng tập trung vào nút và thay đổi số bằng cách nhấn các phím bàn phím "lên" và "xuống". Nếu không, nút sẽ hoạt động như bình thường QPushButton
(nó có thể được nhấp vào, v.v.). Trong Qt, điều này được thực hiện bằng cách tạo một "thành phần" nhỏ có thể tái sử dụng của riêng bạn (lớp con của QPushButton
), sẽ thực hiện lại QWidget::keyPressEvent
. Mã giả:
class NumericButton extends QPushButton
private void addToNumber(int value):
// ...
reimplement base.keyPressEvent(QKeyEvent event):
if(event.key == up)
this.addToNumber(1)
else if(event.key == down)
this.addToNumber(-1)
else
base.keyPressEvent(event)
Xem? Đoạn mã này trình bày một sự trừu tượng mới: một widget hoạt động giống như một nút, nhưng có thêm một số chức năng. Chúng tôi đã thêm chức năng này rất tiện lợi:
keyPressEvent
một tín hiệu, chúng tôi sẽ cần quyết định xem sẽ kế thừa QPushButton
hay chỉ kết nối bên ngoài với tín hiệu. Nhưng điều đó sẽ thật ngu ngốc, vì trong Qt, bạn luôn phải kế thừa khi viết một widget với hành vi tùy chỉnh (vì lý do chính đáng - khả năng tái sử dụng / mô đun). Vì vậy, bằng cách tạo ra keyPressEvent
một sự kiện, họ truyền đạt ý định của mình mà keyPressEvent
chỉ là một khối chức năng cơ bản. Nếu đó là một tín hiệu, nó sẽ trông giống như một thứ mà người dùng phải đối mặt, khi nó không được dự định.keyPressEvent
là một tín hiệu.Thiết kế của Qt đã được suy nghĩ kỹ lưỡng - chúng đã khiến chúng ta rơi vào hố sâu thành công bằng cách giúp bạn dễ dàng làm điều đúng và khó làm điều sai (bằng cách biến keyPressEvent thành một sự kiện).
Mặt khác, hãy xem xét cách sử dụng đơn giản nhất QPushButton
- chỉ cần khởi tạo nó và nhận thông báo khi nó được nhấp vào :
button = new QPushButton(this)
connect(button, SIGNAL(clicked()), SLOT(sayHello())
Điều này rõ ràng được thực hiện bởi người dùng của lớp:
QPushButton
mỗi khi chúng ta muốn nút nào đó thông báo cho chúng ta về một cú nhấp chuột, điều đó sẽ đòi hỏi rất nhiều lớp con mà không có lý do chính đáng! Một tiện ích luôn hiển thị "Xin chào thế giới" messagebox
khi được nhấp vào chỉ hữu ích trong một trường hợp duy nhất - vì vậy nó hoàn toàn không thể sử dụng lại được. Một lần nữa, chúng ta không có lựa chọn nào khác ngoài việc làm điều đúng đắn - bằng cách kết nối với nó từ bên ngoài.clicked()
- hoặc kết nối nhiều tín hiệu với sayHello()
. Với tín hiệu không có ồn ào. Với phân lớp, bạn sẽ phải ngồi xuống và suy ngẫm về một số sơ đồ lớp cho đến khi bạn quyết định một thiết kế phù hợp.Lưu ý rằng một trong những nơi QPushButton
phát ra clicked()
đang được mousePressEvent()
triển khai. Điều đó không có nghĩa là clicked()
và mousePressEvent()
có thể thay thế cho nhau - chỉ là chúng có liên quan với nhau.
Vì vậy, các tín hiệu và sự kiện có những mục đích khác nhau (nhưng có liên quan ở chỗ cả hai đều cho phép bạn "đăng ký" nhận thông báo về điều gì đó đang xảy ra).
Tôi không thích câu trả lời cho đến nay. - Hãy để tôi tập trung vào phần này của câu hỏi:
Các sự kiện có phải là sự trừu tượng của tín hiệu / khe cắm không?
Câu trả lời ngắn gọn: không. Câu trả lời dài đặt ra một câu hỏi “tốt hơn”: Các tín hiệu và sự kiện liên quan với nhau như thế nào?
Một vòng lặp chính không hoạt động (ví dụ như Qt) thường bị "kẹt" trong lệnh gọi select () của hệ điều hành. Lệnh gọi đó làm cho ứng dụng "ngủ", trong khi nó chuyển một loạt các ổ cắm hoặc tệp hoặc bất cứ thứ gì đến hạt nhân yêu cầu: nếu có điều gì đó thay đổi trên những thứ này, hãy để lệnh gọi select () quay trở lại. - Và hạt nhân, với tư cách là chủ nhân của thế giới, biết khi nào điều đó xảy ra.
Kết quả của lệnh gọi select () đó có thể là: dữ liệu mới trên ổ cắm kết nối với X11, một gói tới cổng UDP mà chúng tôi lắng nghe được gửi đến, v.v. - Nội dung đó không phải là tín hiệu Qt, cũng không phải là sự kiện Qt và Vòng lặp chính Qt tự quyết định nếu nó biến dữ liệu mới thành cái này, cái kia hoặc bỏ qua nó.
Qt có thể gọi một (hoặc một số) phương thức như keyPressEvent (), biến nó thành một sự kiện Qt một cách hiệu quả. Hoặc Qt phát ra một tín hiệu, có tác dụng tra cứu tất cả các chức năng đã đăng ký cho tín hiệu đó và gọi chúng lần lượt.
Một sự khác biệt của hai khái niệm đó có thể nhìn thấy ở đây: một vị trí không có phiếu bầu về việc các vị trí khác đã đăng ký tín hiệu đó sẽ được gọi hay không. - Sự kiện giống như một chuỗi và trình xử lý sự kiện quyết định xem nó có làm gián đoạn chuỗi đó hay không. Về mặt này, các tín hiệu trông giống như một ngôi sao hoặc một cái cây.
Một sự kiện có thể kích hoạt hoặc hoàn toàn được biến thành một tín hiệu (chỉ phát ra một tín hiệu và không gọi “super ()”). Một tín hiệu có thể được chuyển thành một sự kiện (gọi một trình xử lý sự kiện).
Điều gì sẽ tóm tắt những gì phụ thuộc vào trường hợp: tín hiệu được nhấp () - tóm tắt các sự kiện của chuột (một nút đi xuống và lên lại mà không cần di chuyển quá nhiều). Các sự kiện bàn phím là sự trừu tượng từ các cấp thấp hơn (những thứ như 果 hoặc é là một số phím bấm trên hệ thống của tôi).
Có thể focusInEvent () là một ví dụ ngược lại: nó có thể sử dụng (và do đó trừu tượng) tín hiệu được click (), nhưng tôi không biết liệu nó có thực sự làm không.
Sự kiện được gửi đi bởi vòng lặp sự kiện. Mỗi chương trình GUI cần một vòng lặp sự kiện, bất kể bạn viết nó là Windows hay Linux, sử dụng Qt, Win32 hoặc bất kỳ thư viện GUI nào khác. Mỗi luồng có vòng lặp sự kiện riêng. Trong Qt, "Vòng lặp sự kiện GUI" (là vòng lặp chính của tất cả các ứng dụng Qt) bị ẩn, nhưng bạn bắt đầu nó gọi:
QApplication a(argc, argv);
return a.exec();
Tin nhắn HĐH và các ứng dụng khác gửi đến chương trình của bạn được gửi đi dưới dạng sự kiện.
Tín hiệu và khe là cơ chế Qt. Trong quá trình biên dịch bằng moc (trình biên dịch siêu đối tượng), chúng được thay đổi thành các hàm gọi lại.
Sự kiện nên có một người nhận, điều này sẽ gửi nó. Không ai khác sẽ nhận được sự kiện đó.
Tất cả các khe kết nối với tín hiệu phát ra sẽ được thực thi.
Bạn không nên nghĩ về Tín hiệu là sự kiện, vì như bạn có thể đọc trong tài liệu Qt:
Khi một tín hiệu được phát ra, các khe kết nối với nó thường được thực thi ngay lập tức, giống như một lệnh gọi hàm thông thường. Khi điều này xảy ra, cơ chế tín hiệu và vị trí hoàn toàn độc lập với bất kỳ vòng lặp sự kiện GUI nào.
Khi bạn gửi một sự kiện, nó phải đợi một thời gian cho đến khi vòng lặp sự kiện gửi tất cả các sự kiện đến trước đó. Do đó, việc thực thi mã sau khi gửi sự kiện hoặc tín hiệu là khác nhau. Mã sau khi gửi một sự kiện sẽ được chạy ngay lập tức. Với các cơ chế tín hiệu và khe cắm, nó phụ thuộc vào loại kết nối. Thông thường, nó sẽ được thực thi sau tất cả các vị trí. Sử dụng Qt :: QueuedConnection, nó sẽ được thực thi ngay lập tức, giống như các sự kiện. Kiểm tra tất cả các loại kết nối trong tài liệu Qt .
When you send an event, it must wait for time when event loop dispatch all events that came earlier. Because of this, execution of the cod after sending event or signal is different
Có một bài báo thảo luận chi tiết về xử lý sự kiện: http://www.packtpub.com/article/events-and-signals
Nó thảo luận về sự khác biệt giữa các sự kiện và tín hiệu ở đây:
Sự kiện và tín hiệu là hai cơ chế song song được sử dụng để thực hiện cùng một việc. Như một sự khác biệt chung, các tín hiệu hữu ích khi sử dụng tiện ích con, trong khi các sự kiện hữu ích khi triển khai tiện ích con. Ví dụ: khi chúng tôi đang sử dụng một tiện ích con như QPushButton, chúng tôi quan tâm đến tín hiệu được nhấp () của nó hơn là các sự kiện nhấn chuột hoặc nhấn phím ở mức độ thấp khiến tín hiệu được phát ra. Nhưng nếu chúng ta đang triển khai lớp QPushButton, chúng ta quan tâm nhiều hơn đến việc triển khai mã cho các sự kiện chuột và phím. Ngoài ra, chúng tôi thường xử lý các sự kiện nhưng nhận được thông báo bằng cách phát tín hiệu.
Đây dường như là một cách nói phổ biến về nó, vì câu trả lời được chấp nhận sử dụng một số cụm từ giống nhau.
Lưu ý, vui lòng xem các bình luận hữu ích bên dưới về câu trả lời này của Kuba Ober, điều này khiến tôi tự hỏi liệu nó có thể hơi đơn giản không.
event
. Cụ thể, các tín hiệu và khe cắm là các phương thức, trong khi cơ chế kết nối là một cấu trúc dữ liệu cho phép tín hiệu gọi một hoặc nhiều khe cắm được liệt kê là kết nối với nó. Tôi hy vọng bạn thấy rằng việc nói về tín hiệu / khe cắm như một số "tập hợp con" hoặc "biến thể" của các sự kiện là vô nghĩa và ngược lại. Họ thực sự là những thứ khác nhau mà xảy ra được sử dụng để kết thúc tương tự trong bối cảnh của một số vật dụng. Đó là nó. Bạn càng khái quát hóa, bạn càng trở thành IMHO ít hữu ích hơn.
TL; DR: Tín hiệu và khe cắm là các cuộc gọi phương thức gián tiếp. Sự kiện là cấu trúc dữ liệu. Vì vậy, chúng là những động vật khá khác biệt.
Thời điểm duy nhất khi chúng kết hợp với nhau là khi các cuộc gọi vị trí được thực hiện qua các ranh giới luồng. Các đối số lệnh gọi vị trí được đóng gói trong một cấu trúc dữ liệu và được gửi dưới dạng một sự kiện đến hàng đợi sự kiện của luồng nhận. Trong luồng nhận, QObject::event
phương thức giải nén các đối số, thực hiện lệnh gọi và có thể trả về kết quả nếu đó là một kết nối chặn.
Nếu chúng ta sẵn sàng khái quát hóa thành sự lãng quên, người ta có thể coi các sự kiện như một cách gọi event
phương thức của đối tượng đích . Đây là một cách gọi phương pháp gián tiếp, theo mốt - nhưng tôi không nghĩ đó là một cách nghĩ hữu ích về nó, ngay cả khi đó là một câu nói đúng.
Các sự kiện (theo nghĩa chung là tương tác giữa người dùng / mạng) thường được xử lý trong Qt với các tín hiệu / khe cắm, nhưng tín hiệu / khe cắm có thể thực hiện nhiều việc khác.
QEvent và các lớp con của nó về cơ bản chỉ là các gói dữ liệu được chuẩn hóa nhỏ để khung giao tiếp với mã của bạn. Nếu bạn muốn chú ý đến con chuột theo một cách nào đó, bạn chỉ cần nhìn vào API QMouseEvent và các nhà thiết kế thư viện không phải phát minh lại bánh xe mỗi khi bạn cần tìm ra con chuột đã làm gì ở một số góc của API Qt.
Đúng là nếu bạn đang chờ đợi các sự kiện (một lần nữa trong trường hợp chung) thuộc một loại nào đó, thì vị trí của bạn gần như chắc chắn sẽ chấp nhận một lớp con QEvent làm đối số.
Như đã nói, các tín hiệu và khe cắm chắc chắn có thể được sử dụng mà không cần QEvents, mặc dù bạn sẽ thấy rằng động lực ban đầu để kích hoạt tín hiệu thường sẽ là một số loại tương tác của người dùng hoặc hoạt động không đồng bộ khác. Tuy nhiên, đôi khi, mã của bạn sẽ đạt đến điểm mà việc kích hoạt một tín hiệu nhất định sẽ là điều đúng đắn cần làm. Ví dụ: kích hoạt tín hiệu được kết nối với thanh tiến trình trong một quá trình dài không liên quan đến QEvent cho đến thời điểm đó.
Tôi tìm thấy câu hỏi này khi đọc 'Xử lý sự kiện' của Leow Wee Kheng. Nó cũng nói:
Jasmine Blanchette nói:
Lý do chính khiến bạn sử dụng các sự kiện thay vì các lệnh gọi hàm tiêu chuẩn, hoặc các tín hiệu và vị trí, là các sự kiện có thể được sử dụng đồng bộ và không đồng bộ (tùy thuộc vào việc bạn gọi sendEvent () hay postEvents ()), trong khi gọi một hàm hoặc gọi một khe luôn đồng bộ. Một ưu điểm khác của các sự kiện là chúng có thể được lọc.
Một xem xét thực dụng nhỏ khác: phát hoặc nhận tín hiệu yêu cầu kế thừa QObject
trong khi một đối tượng của bất kỳ kế thừa nào có thể đăng hoặc gửi một sự kiện (vì bạn gọi QCoreApplication.sendEvent()
hoặc postEvent()
) Đây thường không phải là vấn đề nhưng: để sử dụng tín hiệu, PyQt yêu cầu QObject
phải là siêu lớp đầu tiên, và bạn có thể không muốn sắp xếp lại thứ tự kế thừa của mình chỉ để có thể gửi tín hiệu.)
Theo tôi, các sự kiện là hoàn toàn dư thừa và có thể bị loại bỏ. Không có lý do gì không thể thay thế tín hiệu bằng sự kiện hoặc sự kiện bằng tín hiệu, ngoại trừ Qt đã được thiết lập như nó vốn có. Các tín hiệu đã xếp hàng được bao bọc bởi các sự kiện và các sự kiện có thể được bao bọc bởi các tín hiệu, ví dụ:
connect(this, &MyItem::mouseMove, [this](QMouseEvent*){});
Sẽ thay thế mouseMoveEvent()
chức năng tiện lợi được tìm thấy trong QWidget
(nhưng không QQuickItem
còn trong nữa) và sẽ xử lý các mouseMove
tín hiệu mà trình quản lý cảnh sẽ phát ra cho mục đó. Thực tế là tín hiệu được phát ra thay mặt cho vật phẩm bởi một số thực thể bên ngoài là không quan trọng và xảy ra khá thường xuyên trong thế giới của các thành phần Qt, mặc dù nó được cho là không được phép (các thành phần Qt thường phá vỡ quy tắc này). Nhưng Qt là một tập đoàn có nhiều quyết định thiết kế khác nhau và bị vùi dập khá nhiều vì sợ phá vỡ mã cũ (dù sao điều này cũng thường xuyên xảy ra).
QObject
phân cấp cha mẹ-con, khi cần thiết. Kết nối tín hiệu / khe cắm chỉ là một lời hứa để gọi một chức năng, trực tiếp hoặc gián tiếp, khi một điều kiện nhất định được đáp ứng. Không có hệ thống phân cấp xử lý liên quan với các tín hiệu và vị trí.
accepted
tham số tham chiếu vào một tín hiệu và các vị trí xử lý tín hiệu đó hoặc bạn có thể có một cấu trúc làm tham số tham chiếu với một accepted
trường. Nhưng Qt đã thực hiện các lựa chọn thiết kế mà nó đã đưa ra và chúng hiện đã được hoàn thiện.