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)
總結
以上是生活随笔為你收集整理的Qt, Python(一)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: NoSQL, Clojure
- 下一篇: Machine Learning之Pyt