http://antkillerfarm.github.io/
这篇文章有个大概的比较和说明:
http://blog.chinaunix.net/uid-20754930-id-1877623.html
以下是我针对Qt5代码(2015.4)的一个研究。
QApplication::exec
QGuiApplication::exec
QCoreApplication::exec
QEventLoop::exec
QEventLoop::processEvents
QEventDispatcher::processEvents
再以下就和具体的平台相关了。
Windows平台:
QEventDispatcherWin32::processEvents
它的实现主要是GetMessage+WaitForMultipleObjects,如上文中对MiniGUI的主循环所述。
Unix平台:
QEventDispatcherUNIX::processEvents
QEventDispatcherUNIXPrivate::doSelect
这个函数主要使用select系统调用来监听事件源。
Android平台:
QAndroidEventDispatcher::processEvents
QUnixEventDispatcherQPA::processEvents
QEventDispatcherUNIX::processEvents
以下和Unix平台相同。
近日在研究OpenGL多线程处理时,偶然看到了如下代码:
http://download.csdn.net/detail/zhoukuanbin/7669187
没有CSDN账号的读者,也可以在这里下载:
https://github.com/antkillerfarm/antkillerfarm_crazy/tree/master/Qt/MyGlTest
粗看代码,发现并无特殊的OpenGL处理,理论上应该无法达到一个线程处理渲染,另一个线程处理贴图的目标。但实际编译运行代码,又确实可用。因此这里面一定有什么原因。
void ThreadGl::run()
{
connect(this, SIGNAL(SigCopy()), this, SLOT(Copy()));
printf("ID 0:%x\n", currentThreadId());
while(1)
{
if(!m_bEndFlag)
{
break;
}
usleep(16000);
emit SigCopy();
}
qDebug("Copy End\n");
}
void ThreadGl::Copy()
{
m_glWidgets -> OnTimeOut(m_pImage, m_Formal, m_pthId);
}
这段代码引起了我的注意。粗一看,connect函数连接的SIGNAL和SLOT都属于同一个对象。而connect函数的第5个参数(这是个有默认值的可省略参数)的默认值为Qt::AutoConnection。
这是Qt官方对Qt::AutoConnection的解释:
按照字面上的意思,既然SIGNAL和SLOT都属于同一个对象,那么这里的Qt::AutoConnection就等同于Qt::DirectConnection。而Qt::DirectConnection与直接调用SLOT函数是一个效果。
于是这里将emit SigCopy();
替换为Copy();
,结果程序运行出错。
再试一下,将connect函数的第5个参数设为Qt::DirectConnection,同样有问题。
而如果将之设为Qt::QueuedConnection的话,程序运行一切正常。
综上,Qt在这里的处理,显然认为SIGNAL和SLOT属于不同的线程。
那么判断的依据是什么呢?
这里有必要对emit SigCopy();
中emit
背后的戏法做一个剖析。
众所周知,emit并非C++关键字,而属于Qt扩展的一部分。Qt为了处理这些扩展,引入了moc工具作为预处理工具。
而moc处理之后,emit SigCopy();
就变成了QMetaObject::activate
QMetaObject::activate
中使用以下代码,判定信号接收者的线程是否和信号发送者的一致。
const bool receiverInSameThread = currentThreadId == receiver->d_func()->threadData->threadId;
于是问题转化为receiver->d_func()->threadData
是怎么来的呢?
答案在QObject::QObject
中:
d->threadData = (parent && !parent->thread()) ? parent->d_func()->threadData : QThreadData::current();
从这里可以看出所谓信号接收者的线程,实际上是接收者对象创建时所处的线程。
回到原来的问题。ThreadGl虽然是Qt的线程对象,但它本身却是在主线程中创建的,所以以它为接收者,实际上就是以主线程为接收者。
显然这个例子代码并没有真正实现OpenGL多线程处理,所有的OpenGL操作实际上都是在主线程完成的。
http://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000
国人写的一个中文教程。
PyPI是一个Python语言的软件库管理软件,它的官网是:
https://pypi.python.org/pypi
用户可以在这里查找自己需要的Python包。
Ubuntu下的安装方法是:
sudo apt-get install python-pip
使用方法是:
pip install
有的python包因为包含不可移植的二进制文件,使用pip安装的时候会出错。这时,可在ubuntu官网搜索安装相应的python包。
对于可移植的python包,直接使用pip安装即可,在ubuntu官网搜也搜不到的。
ipython是一个 python 的交互式 shell,比默认的python shell 好用得多,支持变量自动补全,自动缩进,支持 bash shell 命令,内置了许多很有用的功能和函数。
在较新的ipython版本中,添加了ipython notebook的功能,弥补了ipython shell下代码不易保存等缺点,并且在使用 –pylab inline选项后,可以在代码执行后立即显示运行结果(包括图片,数据表格等),因此在数据分析中运用十分广泛。
sudo apt-get install ipython ipython-notebook
distutils是python自带的软件发布工具。它的安装脚本一般叫做setup.py,对应的配置文件叫做setup.cfg。
它的文档参见:
https://docs.python.org/2/distutils/index.html
distutils-extra在distutils的基础上做了扩展,提供诸如i18n之类的支持。如果没有安装,会报如下错误:
error: error in setup.cfg: command 'build' has no such option 'i18n'
安装方法:
sudo apt-get install python-distutils-extra
setuptools是一个更强大的distutils扩展,它也是PyPI的基础。它的安装脚本通常以ez_setup.py的形式出现。
它的文档参见:
https://pythonhosted.org/setuptools/setuptools.html
安装方法:
sudo apt-get install python-setuptools
setuptools提供的一个工具,可从网络下载安装外部依赖文件。
它的文档参见:
https://pythonhosted.org/setuptools/easy_install.html
谈起Python调用GTK+的话题,在GTK+ 2的时代可以使用PyGTK库。它的官网是:
http://www.pygtk.org/
但由于PyGTK库本质上来说,使用了和一般的Python调用C库一样的方法,因此每导入一个库,都需要编写大量的接口文件。GNOME工程有那么多项目,采用这种办法显然是不太可行的。
于是,GNOME工程发起了GObject Introspection项目。众所周知,GNOME工程诸多项目的基石是GObject。这是一个很成功的类型系统,使用该系统可以获得远比普通C库更强的支持。GObject Introspection项目的目标就是创建一个用于跨语言绑定的中间层。这个项目的官网是:
https://wiki.gnome.org/Projects/GObjectIntrospection
随着GObject Introspection项目的进展,越来越多的语言绑定开始使用这套系统,python也不例外。PyGObject项目就是这种方法的成果,目前它已经取代了PyGTK库,成为GTK+ 3时代Python调用GTK+的官方方案。它的官网是:
https://wiki.gnome.org/Projects/PyGObject
相关的API参考文档可参见:
http://lazka.github.io/pgi-docs/
指南文档可参见:
http://python-gtk-3-tutorial.readthedocs.org/en/latest/
安装方法:
Ubuntu下,可以执行:
sudo apt-get install python-gi python3-gi
Windows下,有个PyGObject for Windows项目,该项目的官网是:
http://sourceforge.net/projects/pygobjectwin32/files/
PyGObject的helloworld程序可参见:
https://github.com/antkillerfarm/antkillerfarm_crazy/blob/master/python/pygtk-helloworld.py
这里必须指出的是,PyGObject依赖的接口文件只有在dev包中才有。安装相关Glib项目时,一定要选择名称以-dev结尾的包。
Ubuntu下的安装方法是:
sudo apt-get install sqlite
Python自带了pysqlite包,用于调用sqlite。
pysqlite的helloworld程序可参见:
https://github.com/antkillerfarm/antkillerfarm_crazy/blob/master/python/pysqlite-helloworld.py
可以使用sqlite_bro软件包,查看sqlite数据库。安装方法:
sudo pip install sqlite_bro
众所周知,GTK库是用C语言编写的,其相关的文档提供的也是C函数的接口,python接口的文档相对较少,获得也不太容易。因此有必要掌握一下这方面基本的命名规则。(这里只讲方法,对深层次的调用原理不做探究。)
1)类初始化函数
C:GtkWidget * gtk_button_new (void);
python:button = Gtk.Button()
这里除了把C函数式的写法,替换成python的类的写法之外,并无其他差异。
2)普通类成员函数
C:void gtk_container_add (GtkContainer *container, GtkWidget *widget);
python:button.add(image)
可以看出,C函数的第一个表示self类指针的参数被省略掉了。
3)回调函数
这里以按钮的click事件回调为例:
C:void user_function (GtkButton *button, gpointer user_data)
python:
button.connect("clicked", self.playToggled)
def playToggled(self, w):
这里的情况要复杂的多。首先在事件回调注册的时候,由于我们并没有给user_data赋值,因此照理说playToggled函数,只有button这一个参数。但实际传递给playToggled函数的有两个参数,self是一个用于占位的参数,w对应button,没有东西对应user_data。
4)用于输出的参数
C:void gst_message_parse_error (GstMessage *message, GError **gerror, gchar **debug);
python:err, debug = message.parse_error()
这个例子中的gerror和debug都是用于输出的参数。如果只想得到其一,还可用以下方法:
debug = message.parse_error()[2]
5)枚举类型
C:
enum GstSeekFlags
{
GST_SEEK_FLAG_FLUSH,
...
}
python:Gst.SeekFlags.FLUSH
6)宏常量
C:#define GST_CLOCK_TIME_NONE ((GstClockTime) -1)
python:Gst.CLOCK_TIME_NONE
7)类型转换
大多数情况下,类型转换自动完成,并无什么问题。这里只讨论特殊的情况。
C:
gst_element_seek (pipeline, 1.0, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH,
GST_SEEK_TYPE_SET, time_nanoseconds,
GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE))
python:
self.player.seek(1.0, Gst.Format.TIME, Gst.SeekFlags.FLUSH, Gst.SeekType.SET,\
time_nanoseconds, Gst.SeekType.NONE, Gst.CLOCK_TIME_NONE)
这里首先按照一般的方法,将C代码转化成python代码。但是运行之后,产生如下错误:
OverflowError: long too big to convert
究其原因,gst_element_seek的最后一个参数是uint64类型的。但python原生并不支持该类型,而是将之转换成int类型(实际上是int64类型),这样的话,GST_CLOCK_TIME_NONE的值显然超出了int64所能表示的范围。
解决的办法是使用ctypes库,将
Gst.CLOCK_TIME_NONE
改为
c_long(Gst.CLOCK_TIME_NONE).value
Python主要有三种数据类型:字典、列表、元组。其分别由花括号,中括号,小括号表示。如:
字典:dic={‘a’:12,’b’:34}
列表:list=[1,2,3,4]
元组:tup=(1,2,3,4)