Qt, Python(一)

http://antkillerfarm.github.io/

Qt主循环

这篇文章有个大概的比较和说明:

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平台相同。

Qt多线程中connect的使用

近日在研究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的解释:

  • If the receiver lives in the thread that emits the signal, Qt::DirectConnection is used. Otherwise, Qt::QueuedConnection is used. The connection type is determined when the signal is emitted.

按照字面上的意思,既然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操作实际上都是在主线程完成的。

Python

教程

http://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000

国人写的一个中文教程。

PyPI

PyPI是一个Python语言的软件库管理软件,它的官网是:

https://pypi.python.org/pypi

用户可以在这里查找自己需要的Python包。

Ubuntu下的安装方法是:

sudo apt-get install python-pip

使用方法是:

pip install <package name>

有的python包因为包含不可移植的二进制文件,使用pip安装的时候会出错。这时,可在ubuntu官网搜索安装相应的python包。

对于可移植的python包,直接使用pip安装即可,在ubuntu官网搜也搜不到的。

iPython

ipython是一个 python 的交互式 shell,比默认的python shell 好用得多,支持变量自动补全,自动缩进,支持 bash shell 命令,内置了许多很有用的功能和函数。

在较新的ipython版本中,添加了ipython notebook的功能,弥补了ipython shell下代码不易保存等缺点,并且在使用 –pylab inline选项后,可以在代码执行后立即显示运行结果(包括图片,数据表格等),因此在数据分析中运用十分广泛。

sudo apt-get install ipython ipython-notebook

软件发布工具

distutils

distutils是python自带的软件发布工具。它的安装脚本一般叫做setup.py,对应的配置文件叫做setup.cfg。

它的文档参见:

https://docs.python.org/2/distutils/index.html

distutils-extra

distutils-extra在distutils的基础上做了扩展,提供诸如i18n之类的支持。如果没有安装,会报如下错误:

error: error in setup.cfg: command 'build' has no such option 'i18n'

安装方法:

sudo apt-get install python-distutils-extra

setuptools

setuptools是一个更强大的distutils扩展,它也是PyPI的基础。它的安装脚本通常以ez_setup.py的形式出现。

它的文档参见:

https://pythonhosted.org/setuptools/setuptools.html

安装方法:

sudo apt-get install python-setuptools

Easy Install

setuptools提供的一个工具,可从网络下载安装外部依赖文件。

它的文档参见:

https://pythonhosted.org/setuptools/easy_install.html

PyGObject

谈起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结尾的包。

Sqlite

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

python调用GTK函数的一般规则

众所周知,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中的括号

Python主要有三种数据类型:字典、列表、元组。其分别由花括号,中括号,小括号表示。如:

字典:dic={‘a’:12,’b’:34}

列表:list=[1,2,3,4]

元组:tup=(1,2,3,4)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值