因曾經寫過 Java 程式, 在當時的 Leader 為了彈性, 而利用眾多 Frameworks 再加上自己的創意打造出一個看起來不錯的程式架構, 不過當時初出社會的我倒是挺感冒的, 因為當時為了要下一道 SQL 的效果, 而必須要更動 10 個程式檔或 XML 檔。
講白一點就是,你今天要在網頁上寫一個新增使用者的頁面,所花的功夫可謂非常的不小。雖然我本人是贊同架構的重要,以及對重覆程式碼特別厭惡,但一個小小的動作需要大費周章,這對一個程式設計師不到10個人的軟體公司而言,個人覺得不是一件好事。
因此如果是自己的專案,總是希望去達到平衡點。
話說之後到了某個公司之後, 居然有個網路/通訊產品的輔助程式(就是utility,在電子公司最不被重視的一塊)要用Java來弄, 公司的其它工具大多是用BCB寫的, 而因為我之前寫過Java, 加上這個程式要用WEB當UI,所以就交給我了。不過這個專案主要是在 sockets上,所以UI方面我不太想用HTML/JSP,甚至是之前用的 frameworks"們"(struts,hibernate,spring,webwork),怕要花太多時間,所以不如學一下 Adobe 的 Flex, 用flash來做前端 UI...
因為怕以後接手的人不好維護, 所以一切從簡, class也不敢弄得太複雜(反正有人就是覺得全部的程式寫在同一個function最好...Orz...), 不過沒被那些 frameworks搞過的人還是覺得 class太多, 主要是公司裏主要是寫 C 程式, 雖然會用 C++編譯器,但還是會用 C 的想法去寫
雖然本人基本上沒什麼特別偏好 Java 或 C/C++, 或那一種 framework, 目前唯一"不太喜歡"的是 MFC ,哈...反正只要能快速及正確的完成任務就是好"工具"(所以現在的需求有了跨 Linux/Win/WinCE/Embedded我才用 QT, 用之前並沒寫過 QT..)。
話說這個案子第一版之後, 中間接了一個 Firmware 維護, 再來是另一個新的 utility, 要用 BCB 重新寫的, 這時我又更精簡的使用 class 數, 當然每個 class 會變得很肥, 因為程式碼多嘛..
這時我又聽到另一種聲音, class太少.........XD
哇咧, 這要我怎麼辦?!
所以"說 class 太多的人, 也會說 class 太少"啊...
其實 class 多少並不是重點, 更不是問題! 而是你想不想去了解程式才是最大的問題啊!
2009年12月19日
BCB 6 安裝後的二件事
1.請到 Tools -> Editor Options 在第一頁(General)Tab把 Undo after save打勾, Undo可是在開發過程中, 所能記憶最短程式片段的保存方式!!(應該沒有人寫幾行就commit到版本管理系統的吧?!)
2.如果你的程式有用到第三方元件, 請先到 ${BCB}/Lib安裝dclusr.bpk
如果在安裝元件時, 編譯時發生 TCustomOutline.. Link Error錯誤訊息, 請把 ${BCB}/Lib/bcbsmp.bpi 加到 requires 裏
註:${BCB}指的是BCB安裝目錄
2.如果你的程式有用到第三方元件, 請先到 ${BCB}/Lib安裝dclusr.bpk
如果在安裝元件時, 編譯時發生 TCustomOutline.. Link Error錯誤訊息, 請把 ${BCB}/Lib/bcbsmp.bpi 加到 requires 裏
註:${BCB}指的是BCB安裝目錄
2009年12月18日
QWT4.5.6 遇到QT 4.6
打算將開發中的專案升級到 QT4.6, 因為有用到 QWT, 所以順便一起升級, 結果在編譯QWT時, 用VC2005 編譯一直出現 qHash()..的錯誤訊息, 結果在網上看到一串滿"激烈"的討論串
QWT原作者與 QT Senior Product Manager的對答
http://www.mail-archive.com/qt4-
preview-feedback@trolltech.com/msg01358.html
最後沒時間研究 WHY 的我, 只好乖乖到 qwt_valuelist.h 把47~49行註解掉
不過以我的"嫩"英文看起來, 這位"Product Manager"還滿強硬與有程式底子的啊!
---話說要將 QWT 以DLL型式使用時, 請在你的專案中加一個定義QWT_DLL
QWT原作者與 QT Senior Product Manager的對答
http://www.mail-archive.com/qt4-
preview-feedback@trolltech.com/msg01358.html
最後沒時間研究 WHY 的我, 只好乖乖到 qwt_valuelist.h 把47~49行註解掉
// MOC_SKIP_BEGIN
//template class QWT_EXPORT QList;
// MOC_SKIP_END
不過以我的"嫩"英文看起來, 這位"Product Manager"還滿強硬與有程式底子的啊!
---話說要將 QWT 以DLL型式使用時, 請在你的專案中加一個定義QWT_DLL
[QT] Print widget && Save widget to image file
1.Print QWidget
2.Save QWidget to image file
QPrinter printer(QPrinter::ScreenResolution);
QPrintDialog *dialog = new QPrintDialog(&printer, this);
dialog->setWindowTitle(tr("Print Document"));
if (dialog->exec() != QDialog::Accepted)
return;
QPainter painter;
QPixmap pix = QPixmap::grabWidget(ui.tabWidget->currentWidget());
painter.begin(&printer);
painter.drawPixmap(0,0,pix);
painter.end();
2.Save QWidget to image file
QPixmap pix;
pix = QPixmap::grabWidget(ui.tabWidget->currentWidget());
QString fileName = QFileDialog::getSaveFileName(this, tr("Save File"),"",tr("Images(*.jpg)"));
if(!fileName.isEmpty()) {
if(!pix.save(fileName,"JPG",100)) {
QMessageBox::warning(this,tr("Error"),tr("Can not save image file!"));
}else {
QMessageBox::information(this,tr("Info"),tr("Save OK."));
}
}
2009年12月16日
拖拖拉拉1
底下是參考 Using Drag and Drop with Item Views 章節
以往在 List, Tree, Table 元件中的拖拉, 除非其 GUI Framework有提供"幫你做到好"的服務, 否則並不是一個簡單的任務
而在 QT 當中, 雖不是很簡單到不用寫程式, 但其用 override function的方式, 讓我們去覆載"拖拉"的事件, 去完成我們的自己拖拉實作函式, 相信這種作法對 OOP 而言不但是直覺而且是富有彈性的方式, 而且通常我們在拖拉的過程中, 多半會去做一些動作, 如存取資料庫等等。
最近在一個專案中, 便繼承了 QTreeWidget 及 QTableWidget , 去實作拖拉函式, 而只要是繼承自 QAbstractItemView的類別都有支援 drag-and-drop, 其實也不用懂得太多, 只要覆載以下這個函式就可以了
QDragEnterEvent 拖拉物件進入可以置放的地方時...
QDragMoveEvent 拖拉物件移動時...
QDropEvent 放下拖拉的物件...
像我就在這些事件中處理一些事情, 以及把使用者拖拉的結果存到資料庫...
另外一些元件的屬性也要設定
如QT說明文件中的範例
QListWidget *listWidget = new QListWidget(this);
listWidget->setSelectionMode(QAbstractItemView::SingleSelection);
listWidget->setDragEnabled(true);
listWidget->viewport()->setAcceptDrops(true);
listWidget->setDropIndicatorShown(true);
listWidget->setDragDropMode(QAbstractItemView::InternalMove);
當然, 如果你不需要捕抓"拖拉事件", 直接在 QT Designer中按一按就可以了!!!!
接下來還有一些更進階的拖拖拉拉...要學習...
以往在 List, Tree, Table 元件中的拖拉, 除非其 GUI Framework有提供"幫你做到好"的服務, 否則並不是一個簡單的任務
而在 QT 當中, 雖不是很簡單到不用寫程式, 但其用 override function的方式, 讓我們去覆載"拖拉"的事件, 去完成我們的自己拖拉實作函式, 相信這種作法對 OOP 而言不但是直覺而且是富有彈性的方式, 而且通常我們在拖拉的過程中, 多半會去做一些動作, 如存取資料庫等等。
最近在一個專案中, 便繼承了 QTreeWidget 及 QTableWidget , 去實作拖拉函式, 而只要是繼承自 QAbstractItemView的類別都有支援 drag-and-drop, 其實也不用懂得太多, 只要覆載以下這個函式就可以了
QDragEnterEvent 拖拉物件進入可以置放的地方時...
QDragMoveEvent 拖拉物件移動時...
QDropEvent 放下拖拉的物件...
像我就在這些事件中處理一些事情, 以及把使用者拖拉的結果存到資料庫...
另外一些元件的屬性也要設定
如QT說明文件中的範例
QListWidget *listWidget = new QListWidget(this);
listWidget->setSelectionMode(QAbstractItemView::SingleSelection);
listWidget->setDragEnabled(true);
listWidget->viewport()->setAcceptDrops(true);
listWidget->setDropIndicatorShown(true);
listWidget->setDragDropMode(QAbstractItemView::InternalMove);
當然, 如果你不需要捕抓"拖拉事件", 直接在 QT Designer中按一按就可以了!!!!
接下來還有一些更進階的拖拖拉拉...要學習...
2009年12月15日
BCB free component
EkszBoxVCL
http://sourceforge.net/projects/ekszbox-vcl/
JVCL
http://jvcl.delphi-jedi.org/
Note:
如果是使用 BCB 6 的話, 想裝 EkzBoxVCL, 請用新增 Package的方式, 把它的 *.pas 檔加進去即可編譯及安裝
http://sourceforge.net/projects/ekszbox-vcl/
JVCL
http://jvcl.delphi-jedi.org/
Note:
如果是使用 BCB 6 的話, 想裝 EkzBoxVCL, 請用新增 Package的方式, 把它的 *.pas 檔加進去即可編譯及安裝
QT 移除 menu
QMenuBar 有 addMenu() 函式(話說其實我是用 designer加的), 卻沒有 removeMenu(), 真是奇怪, 取得 menu obj去 hide() or close()無解, 查詢 google 大神之後, 原來可用 setTitle("") 方式去隱藏某個 menu
話說為什麼加了 menu 之後想移掉~~但又想保留給以後使用, 這個問題很難解~的~啊!?
其實做有GUI程式最難的部分在於..."做決定的人"都有心目中最 User-Friendly 的操作方式, 可是偏偏他們都不用寫程式...
話說為什麼加了 menu 之後想移掉~~但又想保留給以後使用, 這個問題很難解~的~啊!?
其實做有GUI程式最難的部分在於..."做決定的人"都有心目中最 User-Friendly 的操作方式, 可是偏偏他們都不用寫程式...
2009年12月3日
讓 jom 解放編譯 QT!
什麼時候你會喜歡 CPU 使用率 100%?
打報告的時候? NO!
寫程式的時候? NO! NO!
...看迷片的時候? NO! NO! NO!
我知道其中的一個答案便是編譯 QT framework 的時候啊~~
要苦等 2~4 小時真是叫人難受, 而且還有可能發生編譯錯誤, 重頭再來啊~
只要是使用 VC 的 nmake 去編譯, 不管你的PC有幾核心, 統統最多只會用到 1 核心, 所以在 4 核心的 Q6600 上, 在編譯 QT 時, 看到 25% 的CPU使用率, 真是苦笑不得..
幸好有 google 大神, 讓我找到 jom (關鍵字 qt jom)
而最新版的 jom binary都會放在ftp://ftp.qt.nokia.com/jom/
jom 專案首頁http://qt.gitorious.org/qt-labs/jom
一執行下去會出現
jom 0.8.1 - empower your cores
整個感覺只能用"帥啊!老皮!" 來形容, 編譯時間大幅縮短, 不到40分鐘就搞定
哈, 就用 jom 取代 nmake 吧, 底下是我在編譯 QT 4.6 for wince 的畫面(話說QT4.6 在09/12/2 正式 release, 真是超有活力的 Framework, 相較起 BCB, MFC, 以 C++ GUI Framework來說, 真是活力百倍啊..)

相關鏈結:
http://www.ffuts.org/blog/compile-qt-45-for-visual-studio-using-your-multi-core-processor/
我用的編譯組態:
configure -no-qt3support -no-opengl -platform win32-msvc2005 -no-libtiff -no-dbus -no-phonon -no-phonon-backend -no-webkit
打報告的時候? NO!
寫程式的時候? NO! NO!
...看迷片的時候? NO! NO! NO!
我知道其中的一個答案便是編譯 QT framework 的時候啊~~
要苦等 2~4 小時真是叫人難受, 而且還有可能發生編譯錯誤, 重頭再來啊~
只要是使用 VC 的 nmake 去編譯, 不管你的PC有幾核心, 統統最多只會用到 1 核心, 所以在 4 核心的 Q6600 上, 在編譯 QT 時, 看到 25% 的CPU使用率, 真是苦笑不得..
幸好有 google 大神, 讓我找到 jom (關鍵字 qt jom)
而最新版的 jom binary都會放在ftp://ftp.qt.nokia.com/jom/
jom 專案首頁http://qt.gitorious.org/qt-labs/jom
一執行下去會出現
jom 0.8.1 - empower your cores
整個感覺只能用"帥啊!老皮!" 來形容, 編譯時間大幅縮短, 不到40分鐘就搞定
哈, 就用 jom 取代 nmake 吧, 底下是我在編譯 QT 4.6 for wince 的畫面(話說QT4.6 在09/12/2 正式 release, 真是超有活力的 Framework, 相較起 BCB, MFC, 以 C++ GUI Framework來說, 真是活力百倍啊..)

相關鏈結:
http://www.ffuts.org/blog/compile-qt-45-for-visual-studio-using-your-multi-core-processor/
我用的編譯組態:
configure -no-qt3support -no-opengl -platform win32-msvc2005 -no-libtiff -no-dbus -no-phonon -no-phonon-backend -no-webkit
2009年12月1日
[QT] 使用到 plugin , 要散佈程式時注意事項
在 QT 中有些看似簡單的事, 還是需要一些步驟才能順利完成
初學QT, 就幫忙別部門開發程式, 到收尾階段進行補上 icon 圖示, 結果安裝在別台電腦, 發現 icon 都跑不出來, 即使確定這些 icon 都有被當成 resource 包在執行檔裏, 後來查了 QT Reference 才發現 icon 這種圖形格式, 也是屬於一種plugin, 所以當使用 dynamic link QT時, 這些 dll 也要部署過去
在 qt\plugins下就有依目錄當作分類的各式 plugin, 如果僅是 icon, 便屬於 imageformats\qico4.dll, 安裝到主程式同目錄下的 plugins\imageformats\qicon4.dll 就可以了
如果是使用 static link QT, 需要用巨集 Q_IMPORT_PLUGIN 讓 QT 知道在編譯時, 要將這些 plugin link進主程式,
不過事情有那麼簡單, 就不需要"師程工"啦我的開發環境是 VS2005 + QT VS plugin 來開發 QT application, 至於為什麼用 VS2005, 一切只是習慣問題(看來得改?), 在這個 app 安裝在使用者電腦上之後, 所有圖示依然是出不來, 當下覺得奇怪, 不是都依照文件把該搬的 dll 都搬過去了嗎? 包含 VS2005的 C/C++ runtime library, 然後我再次查詢文件中的 Deploying an Application on Windows 章節, 嘗試了使用 QApplication::addLibraryPath()去設定路徑, 然後用libraryPaths()印出所有 QT搜尋 DLL 的路徑, 圖示還是出不來, 在浪費了1小時之後, 最後沒有用 Shared QT Libary , 還是用 static QT Library.....(難怪 Google Earth也是這樣子用), 這時第一次感覺 QT 有點難搞.然後時間經過了一個月, 在開發另一個 app 之後還是遇到相同問題, 不過這個 app 不能用 static QT library, 因為雖然是免費使用, 但掛上公司的名字,還是要遵守 QT 的licence-使用LGPL的話, 不能用 static QT, 所以時間依舊又浪費了1個小時...只好再求助 Google 大神, 發現了以下的討論串, 還滿精采的
http://lists.trolltech.com/qt-interest/2007-02/thread00449-0.html
裏面提到有人跑去問 QT 人員, 說是如果使用 VS2005 去編譯 QT 及 QT 程式的話, plugin都載入不了, 這是和 QT plugin 去搜尋 VC C/C++ Runtime Library DLL(CRT)有關係, QT plugin用到的 CRT 只會從 local 目錄去找 (windows/system32), 如果找不到就會標示此 plugin 載入失敗, 下次也不會重新當試載入, 而解決辦法是不要將 CRT 放到跟執行檔同目錄的方式去包裝安裝程式, 而是用 CRT的安裝程式 vcredist_x86.exe(可以從網站上下載, 或是在 VS2005目錄$\Program Files\Microsoft Visual Studio 8\SDK\v2.0\BootStrapper\Packages\vcredist_x86\
所以情況變成, 你的安裝程式先要執行這個安裝檔, 讓 user 去安裝, 再安裝自己的程式, 這樣子 plugin就會順利載入然後有人問, 他的程式是放在 USB 隨身碟裏的, 而且使用者不一定有權限安裝(也就是綠色軟體啦), 那該怎麼辦?!
後來又有人提到 CRT 放到 某 plugin 目錄下就能解決了, 但這樣子不是每個plugin的目錄都要放那些 dll 嗎?
而且QT plugin dll一定要放在 /plugins類別/ 底下..
就快放棄的時候...
想說如果把 CRT 的 Microsoft.VC80.CRT.manifest copy到 plugin目錄中, 然後把裏面的 file tag
< file name="msvcr80.dll"..
改成相對路徑
< file name="../../msvcr80.dll"
再使用 QApplication::addLibraryPath() 把 plugin 目錄設定好
Yeah! 可以動也, copy一個文字檔總比copy 3個dll好...暫時先這樣子, 會不會有其它故事就不知道了...
在2009/12/13-------果然此法在Windows Vista/7上失效--------哈!
還好這些 DLL 不算大, 不過一想到只不是要在應用程式裏使用到ICON / JPG / GIF就要搞得大費周章, 真的滿鳥的...
初學QT, 就幫忙別部門開發程式, 到收尾階段進行補上 icon 圖示, 結果安裝在別台電腦, 發現 icon 都跑不出來, 即使確定這些 icon 都有被當成 resource 包在執行檔裏, 後來查了 QT Reference 才發現 icon 這種圖形格式, 也是屬於一種plugin, 所以當使用 dynamic link QT時, 這些 dll 也要部署過去
在 qt\plugins下就有依目錄當作分類的各式 plugin, 如果僅是 icon, 便屬於 imageformats\qico4.dll, 安裝到主程式同目錄下的 plugins\imageformats\qicon4.dll 就可以了
如果是使用 static link QT, 需要用巨集 Q_IMPORT_PLUGIN 讓 QT 知道在編譯時, 要將這些 plugin link進主程式,
#include < QtPlugin >
Q_IMPORT_PLUGIN(qico)
int main(int argc, char *argv[]) {
.........
}
不過事情有那麼簡單, 就不需要"師程工"啦我的開發環境是 VS2005 + QT VS plugin 來開發 QT application, 至於為什麼用 VS2005, 一切只是習慣問題(看來得改?), 在這個 app 安裝在使用者電腦上之後, 所有圖示依然是出不來, 當下覺得奇怪, 不是都依照文件把該搬的 dll 都搬過去了嗎? 包含 VS2005的 C/C++ runtime library, 然後我再次查詢文件中的 Deploying an Application on Windows 章節, 嘗試了使用 QApplication::addLibraryPath()去設定路徑, 然後用libraryPaths()印出所有 QT搜尋 DLL 的路徑, 圖示還是出不來, 在浪費了1小時之後, 最後沒有用 Shared QT Libary , 還是用 static QT Library.....(難怪 Google Earth也是這樣子用), 這時第一次感覺 QT 有點難搞.然後時間經過了一個月, 在開發另一個 app 之後還是遇到相同問題, 不過這個 app 不能用 static QT library, 因為雖然是免費使用, 但掛上公司的名字,還是要遵守 QT 的licence-使用LGPL的話, 不能用 static QT, 所以時間依舊又浪費了1個小時...只好再求助 Google 大神, 發現了以下的討論串, 還滿精采的
http://lists.trolltech.com/qt-interest/2007-02/thread00449-0.html
裏面提到有人跑去問 QT 人員, 說是如果使用 VS2005 去編譯 QT 及 QT 程式的話, plugin都載入不了, 這是和 QT plugin 去搜尋 VC C/C++ Runtime Library DLL(CRT)有關係, QT plugin用到的 CRT 只會從 local 目錄去找 (windows/system32), 如果找不到就會標示此 plugin 載入失敗, 下次也不會重新當試載入, 而解決辦法是不要將 CRT 放到跟執行檔同目錄的方式去包裝安裝程式, 而是用 CRT的安裝程式 vcredist_x86.exe(可以從網站上下載, 或是在 VS2005目錄$\Program Files\Microsoft Visual Studio 8\SDK\v2.0\BootStrapper\Packages\vcredist_x86\
所以情況變成, 你的安裝程式先要執行這個安裝檔, 讓 user 去安裝, 再安裝自己的程式, 這樣子 plugin就會順利載入然後有人問, 他的程式是放在 USB 隨身碟裏的, 而且使用者不一定有權限安裝(也就是綠色軟體啦), 那該怎麼辦?!
後來又有人提到 CRT 放到 某 plugin 目錄下就能解決了, 但這樣子不是每個plugin的目錄都要放那些 dll 嗎?
而且QT plugin dll一定要放在 /plugins類別/ 底下..
就快放棄的時候...
想說如果把 CRT 的 Microsoft.VC80.CRT.manifest copy到 plugin目錄中, 然後把裏面的 file tag
< file name="msvcr80.dll"..
改成相對路徑
< file name="../../msvcr80.dll"
再使用 QApplication::addLibraryPath() 把 plugin 目錄設定好
Yeah! 可以動也, copy一個文字檔總比copy 3個dll好...暫時先這樣子, 會不會有其它故事就不知道了...
在2009/12/13-------果然此法在Windows Vista/7上失效--------哈!
還好這些 DLL 不算大, 不過一想到只不是要在應用程式裏使用到ICON / JPG / GIF就要搞得大費周章, 真的滿鳥的...
2009年11月25日
[QT] 背景圖片
在QT中指定widget 背景圖片的一個方法是使用 stylesheet,如下
而當背景圖比 widget 長寬還要小時, 會自動"並排顯示"(tile), 如果背景圖是由重覆性的小圖像組成這倒無所謂, 但如果不是的話, 就會造成困擾, 例如背景是一張東京鐵塔的圖, 而當視窗放到最大時, 就會看到2個東京鐵塔以上....

因此我們應該使用的css樣式背景屬性不是 background-imag, 而是 border-image
3 10 3 10的意思請參考底下這段文字
A border image is an image that is composed of nine parts (top left, top center, top right, center left, center, center right, bottom left, bottom center, and bottom right). When a border of a certain size is required, the corner parts are used as is, and the top, right, bottom, and left parts are stretched or repeated to produce a border with the desired size.
但這樣又有一個問題, 當這個stylesheet指定到一個 UI container上時, 這個屬性也會apply到children上...(如以下視窗中的PushButtn widget..)

這時要用指定obj-name的方式, #frame中的frame即是指定widget的obj-name, 格式如下
#obj-name {...css property...}
效果如下, 不管視窗縮小或放大, 都只會有一隻兔子..XD

而底下的link中的範例為
最後感謝我家兔子"老皮"的配合...
reference:http://doc.trolltech.com/4.5/stylesheet-reference.html#border-image
http://doc.trolltech.com/4.5/stylesheet-customizing.html#the-box-model
http://pepper.troll.no/s60prereleases/doc/stylesheet-examples.html
QString imageFile = QApplication::applicationDirPath() + "/background/background.bmp";
this->setStyleSheet(QString("background-image: url(%1);").arg(imageFile));
而當背景圖比 widget 長寬還要小時, 會自動"並排顯示"(tile), 如果背景圖是由重覆性的小圖像組成這倒無所謂, 但如果不是的話, 就會造成困擾, 例如背景是一張東京鐵塔的圖, 而當視窗放到最大時, 就會看到2個東京鐵塔以上....

因此我們應該使用的css樣式背景屬性不是 background-imag, 而是 border-image
QString imageFile = QApplication::applicationDirPath() + "/background/b.bmp";
ui.frame->setStyleSheet(QString(" border-image: url(%1) 3 10 3 10; ").arg(imageFile));
3 10 3 10的意思請參考底下這段文字
A border image is an image that is composed of nine parts (top left, top center, top right, center left, center, center right, bottom left, bottom center, and bottom right). When a border of a certain size is required, the corner parts are used as is, and the top, right, bottom, and left parts are stretched or repeated to produce a border with the desired size.
但這樣又有一個問題, 當這個stylesheet指定到一個 UI container上時, 這個屬性也會apply到children上...(如以下視窗中的PushButtn widget..)

這時要用指定obj-name的方式, #frame中的frame即是指定widget的obj-name, 格式如下
#obj-name {...css property...}
QString imageFile = QApplication::applicationDirPath() + "/background/b.bmp";
ui.frame->setStyleSheet(QString("#frame{ border-image: url(%1) 3 10 3 10;border-width: 4px;} ").arg(imageFile));
效果如下, 不管視窗縮小或放大, 都只會有一隻兔子..XD

而底下的link中的範例為
QPushButton {
color: grey;
border-image: url(/home/kamlie/code/button.png) 3 10 3 10;
border-top: 3px transparent;
border-bottom: 3px transparent;
border-right: 10px transparent;
border-left: 10px transparent;
}
最後感謝我家兔子"老皮"的配合...
reference:http://doc.trolltech.com/4.5/stylesheet-reference.html#border-image
http://doc.trolltech.com/4.5/stylesheet-customizing.html#the-box-model
http://pepper.troll.no/s60prereleases/doc/stylesheet-examples.html
2009年11月24日
[BCB]在 TThread 裏存取 UI 元件
[BCB]在 TThread 裏存取 UI 元件
因為接手了一支 BCB 寫的應用程式, 在重構的過程中, 有了這篇文章
///////
使用 BCB 的時候我們都知道, 要在 thread 中存取 UI 的話, 必需要用 Synchronize 這個函式, 原因是因為 VCL 不是 thread-safe的, 使用方式是先實作一個 member function, 裏面再去存取 UI, 如底下是每次新增一個 Thread Object, 在 .cpp 上面便會有的註解.
然而如果在 thread 中會去頻繁存取 UI 的話, 那這種方式會很麻煩, 因為你會在 thread class 中新增 n 個function, 只為了要存取 n 個 UI 物件屬性...
//////
在我接手的程式中, 裏面並沒有用到 thread 或 timer, 不過卻用到一些會 blocking function, 雖然每次 block 的時間不長, 但會很頻繁的寫入及讀取資料, 因此會了不要凍結 UI, 在程式中會發現底下這個 function
第一眼看起來還不錯吧, 我要 delay 500 millseconds, 只要呼叫 Delay(500)就好了, 因此程式的寫法如下
然而這種寫法會造成 CPU load 非常高,甚至100%(因為迴圈的關係), 而且別忘了, Delay 函式在處理 message event 時, 是會中斷整個程式運行的, 所以假設你在送出資料的時候, 有 UI 事件發生時, 程式會中斷, 在處理完 UI 事件之後, 才會離開 Delay 函式。
所以如果使用者在你的視窗上按下滑鼠左鍵, 那你的程式就暫時中止了, 而如果你正在做很重要的事, 那這種寫法就很困擾了, 除了 UI 事件會中斷程式運行之外, 另外造成 CPU load太高也是一個重大問題...
而客戶用了你的程式, 除了整個系統造成非常忙碌之外, 剛好我接手的這支程式又是用來做 Update Firmware的程式, 在更新過程中,尤於是send函式和read函式都有 timeout, 如果 timeout到了, Firmware Update 也跟著掛了..(Flash燒錄到一半)
到最後還是要用 thread 來解決..
然而這支程式有一定的歷史, 我不打算整個改寫, 不過要達到的目標就是把 blocking 的函式全搬到 thread 中, 由 thread 來更新 UI, 還有 thread 也可以呼叫視窗類別的函式..(因為原程式中global變數太多, 造成不好重構)
因此就有了底下這些 function, 其實是很簡單的解決辦法...(轉來轉去)
要呼叫 Form1(視窗類別)的 ShowConnectR() 函式就可以這麼用, ShowConnectR()裏會存取到 UI
ShowMessage
將 Form1 中的 UI Disable掉
以上這些 function 還可以繼續擴充...希望有簡單的方式可以從 TThread 中去存取 UI, 話說最近在用 QT 就沒有這樣子的困擾, 要存取 UI 就丟個 Signal給那個 UI object....
因為接手了一支 BCB 寫的應用程式, 在重構的過程中, 有了這篇文章
///////
使用 BCB 的時候我們都知道, 要在 thread 中存取 UI 的話, 必需要用 Synchronize 這個函式, 原因是因為 VCL 不是 thread-safe的, 使用方式是先實作一個 member function, 裏面再去存取 UI, 如底下是每次新增一個 Thread Object, 在 .cpp 上面便會有的註解.
void __fastcall MyThread::UpdateCaption()
{
Form1->Caption = "Updated in a thread";
}
然而如果在 thread 中會去頻繁存取 UI 的話, 那這種方式會很麻煩, 因為你會在 thread class 中新增 n 個function, 只為了要存取 n 個 UI 物件屬性...
//////
在我接手的程式中, 裏面並沒有用到 thread 或 timer, 不過卻用到一些會 blocking function, 雖然每次 block 的時間不長, 但會很頻繁的寫入及讀取資料, 因此會了不要凍結 UI, 在程式中會發現底下這個 function
void Delay(DWORD DT)
{
long tt = GetTickCount();
while (GetTickCount() - tt < DT) {
Application->ProcessMessages();
if ((GetTickCount() - tt) <= 0)
tt = GetTickCount();
}
}
第一眼看起來還不錯吧, 我要 delay 500 millseconds, 只要呼叫 Delay(500)就好了, 因此程式的寫法如下
SendData(...)...
Delay(500);
ReadData(...)...
Delay(500);
///////////////////////////////////
for(int i=0;i<128;i++) {
SendData(...)...
Delay(50);
ReadData(...)...
Delay(50);
}
//////////////////////////////////
RESET();
Delay(6000);
//////////////////////////////////
然而這種寫法會造成 CPU load 非常高,甚至100%(因為迴圈的關係), 而且別忘了, Delay 函式在處理 message event 時, 是會中斷整個程式運行的, 所以假設你在送出資料的時候, 有 UI 事件發生時, 程式會中斷, 在處理完 UI 事件之後, 才會離開 Delay 函式。
所以如果使用者在你的視窗上按下滑鼠左鍵, 那你的程式就暫時中止了, 而如果你正在做很重要的事, 那這種寫法就很困擾了, 除了 UI 事件會中斷程式運行之外, 另外造成 CPU load太高也是一個重大問題...
而客戶用了你的程式, 除了整個系統造成非常忙碌之外, 剛好我接手的這支程式又是用來做 Update Firmware的程式, 在更新過程中,尤於是send函式和read函式都有 timeout, 如果 timeout到了, Firmware Update 也跟著掛了..(Flash燒錄到一半)
到最後還是要用 thread 來解決..
然而這支程式有一定的歷史, 我不打算整個改寫, 不過要達到的目標就是把 blocking 的函式全搬到 thread 中, 由 thread 來更新 UI, 還有 thread 也可以呼叫視窗類別的函式..(因為原程式中global變數太多, 造成不好重構)
因此就有了底下這些 function, 其實是很簡單的解決辦法...(轉來轉去)
//
// ////////////////////////////call function of Form1
void (__closure *pfun)(void);
void __fastcall CallFunction();
void __fastcall SyncFunction(void (__closure *p)(void));
void __fastcall TOnUpdateAll::SyncFunction(void (__closure *p)(void))
{
pfun = p;
Synchronize(CallFunction);
}
void __fastcall TOnUpdateAll::CallFunction()
{
if (pfun)
this->pfun();
pfun = NULL;
}
///////////////Enable Disable variable of Form1
TControl *pControl;
bool obj_enable;
void __fastcall SyncObject(TControl *obj, bool enable);
void __fastcall UIObjectActionEnable();
void __fastcall TOnUpdateAll::SyncObject(TControl *obj, bool enable)
{
pControl = obj;
obj_enable = enable;
Synchronize(UIObjectActionEnable);
}
void __fastcall TOnUpdateAll::UIObjectActionEnable()
{
if (pControl) {
if (obj_enable)
pControl->Enabled = true;
else
pControl->Enabled = false;
}
}
///Call ShowMessage in Thread (蝦米..沒錯 ShowMessage 也是存取UI)
String msg;
bool popup;
void __fastcall MsgShowWrapper(String str, bool pop = true);
void __fastcall MsgShow();
void __fastcall TOnUpdateAll::MsgShow()
{
Form1->Memo3->Lines->Add(msg);
if (popup)
ShowMessage(msg);
}
void __fastcall TOnUpdateAll::MsgShowWrapper(String str, bool pop )
{
msg = str;
popup = pop;
Synchronize(MsgShow);
}
要呼叫 Form1(視窗類別)的 ShowConnectR() 函式就可以這麼用, ShowConnectR()裏會存取到 UI
SyncFunction(Form1->ShowConnectR);
ShowMessage
MsgShowWrapper("Hello, World");
將 Form1 中的 UI Disable掉
SyncObject(Form1->UpdateBtn, true);
以上這些 function 還可以繼續擴充...希望有簡單的方式可以從 TThread 中去存取 UI, 話說最近在用 QT 就沒有這樣子的困擾, 要存取 UI 就丟個 Signal給那個 UI object....
[QT]用 QTableView 解決資料過多, 顯示速度太慢的問題
在QT中, 有一個很方便的 class 可以使用來呈現 Table 表格資料: QTableWidget,但是如果在資料很多的情況, 如有幾萬筆的資料時, 使用QTableWidget 會造成 CPU 負載過高,整個應用程式好像 crash 一樣..
如果只有幾千筆的資料, 可以在 insert data前後使用底下的方式, 讓QTableWidget 先暫停 redraw
但是當資料超過1萬筆之後, 只能使用 QTableView 來解決此問題, 使用 QTableView 的方式可以看QT Help裏的 Model/View programming 章節, 底下簡單列出繼承 QTableView 之後, 最基本必須要覆載的函式, 讓 QTableView 能呈現資料
1.準備 model
1.1繼承 QAbstractTableModel
1.2覆載
2.將此 model 給 QTableView (QLogData為我們繼承自 QAbstractTableModel 的class)
2.1每當 model 有變化時, QTableView 自然會知道, 因為 QTableView的資料來源就是 Model
2.2在 setModel() 前後再加上 setUpdatesEnabled()會更快
3.可以想像成我們是一次塞資料給 QTableWidget, 而 QTableView 則只會去抓現在元件呈現的區域所需的資料, 如使用者拉捲軸時, QTableView 便會去跟 Model 要目前所需的 row/column 的資料
3.1 另外resizeColumnsToContents() 這函式你也許會用到
如果只有幾千筆的資料, 可以在 insert data前後使用底下的方式, 讓QTableWidget 先暫停 redraw
ui.tableWidget->setUpdatesEnabled(false);
ui.tableWidget->setRowCount( .... );
...
QTableWidgetItem *item = new QTableWidgetItem;
item->setText(ret.value(sql_header[i]));
item->setFlags(Qt::ItemIsEnabled);
ui.tableWidget->setItem(row,col++,item);
...
ui.tableWidget->setUpdatesEnabled(true);
但是當資料超過1萬筆之後, 只能使用 QTableView 來解決此問題, 使用 QTableView 的方式可以看QT Help裏的 Model/View programming 章節, 底下簡單列出繼承 QTableView 之後, 最基本必須要覆載的函式, 讓 QTableView 能呈現資料
1.準備 model
1.1繼承 QAbstractTableModel
1.2覆載
int rowCount(const QModelIndex & parent = QModelIndex() ) const;
int columnCount(const QModelIndex & parent = QModelIndex() ) const;
QVariant data(const QModelIndex & index, int role = Qt::DisplayRole ) const;
QVariant headerData ( int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const ;
2.將此 model 給 QTableView (QLogData為我們繼承自 QAbstractTableModel 的class)
QLogData *sqlDataModel = new QLogData;
ui.tableView->setModel(sqlDataModel);
2.1每當 model 有變化時, QTableView 自然會知道, 因為 QTableView的資料來源就是 Model
2.2在 setModel() 前後再加上 setUpdatesEnabled()會更快
3.可以想像成我們是一次塞資料給 QTableWidget, 而 QTableView 則只會去抓現在元件呈現的區域所需的資料, 如使用者拉捲軸時, QTableView 便會去跟 Model 要目前所需的 row/column 的資料
3.1 另外resizeColumnsToContents() 這函式你也許會用到
[QT]Signal/Slots auto-connect 自動連結
在QT中,
如果要讓signals/slots之間產生連結, 必須透過connect()這個函式,
除此之外還可以在Qt Designer 裏以拖拉的方式設定,
如果是自訂的signals/slots, 只要隨後在class定義/實作中補上即可
另外針對一些很短的function body, 也可以偷懶一點用auto connect
從以下link
http://doc.trolltech.com/4.5/qmetaobject.html#connectSlotsByName
可以知道底下的命名慣例, 會是該signal的slots
如果要讓signals/slots之間產生連結, 必須透過connect()這個函式,
除此之外還可以在Qt Designer 裏以拖拉的方式設定,
如果是自訂的signals/slots, 只要隨後在class定義/實作中補上即可
另外針對一些很短的function body, 也可以偷懶一點用auto connect
從以下link
http://doc.trolltech.com/4.5/qmetaobject.html#connectSlotsByName
可以知道底下的命名慣例, 會是該signal的slots
void on_objectName_signal(parameter)
訂閱:
文章 (Atom)