GUI的底层实现一般是消息循环:等待消息,等到了就根据消息类型处理消息。然而消息类型和处理实在太复杂了,因此为了便利,采用面向对象的方式,将不同的控件(widget)对应为各种类,相应的有不同方法,比如点击就是onClick。 程序员可以用回调函数(callback)(其实就是函数指针)的方法设定特定的操作。比如Python的tkinter库,Button的构造函数要指定command参数作为每次点击时要调用的回调函数:
1 | button1 = tk.Button(window,text='button1',command=btn1_callback) |
Qt中则使用了信号-槽(signal-slot)机制,一个继承自QObject
的类可以在成员函数里定义信号(signal)和槽(slot)。可以用emit
语句发出一个信号。用QObject::connect
方法可以“连接”对应的信号和槽,每当信号被emit
时它连接的槽就会响应。信号和槽的关系是多对多的,因此可以实现很灵活的代码书写,比如给要为点击button1
增加一种效果,连接button1
的cliked()
信号和对应槽就行了。Qt的这种机制比较灵活,比如只要信号的参数比槽多且能匹配二者就能连接,对于已销毁的槽信号能自动忽略。 C#采用的是事件-委托(event-delegate)模型,委托(delegate)可以看做是函数指针的进阶版,可以把一个函数赋给一个委托实例,然后把这个委托当做函数使用;一个委托实例可以同时持有多个(参数列表和返回类型相同的)函数,还可以添加对象方法;而事件要使用event加委托类型声明,跟一般委托实例的区别在于,它只能在类中作为成员定义,并且只能由定义它的类的对象触发。C#的事件-委托模型对类型要求比较严格。 总的来说,回调函数最不好用,Qt灵活但是受限于C++语法使用有些不太友好(SLOT,SIGNAL宏等),C#是类型严格的,使用时要注意。