$30 off During Our Annual Pro Sale. View Details »

COSCUP 2013 presentation - From GTK+ to Qt

PCMan
August 03, 2013

COSCUP 2013 presentation - From GTK+ to Qt

PCMan

August 03, 2013
Tweet

Other Decks in Technology

Transcript

  1. 1
    Qt 正夯, GTK+ 使用者該如何跳槽 ?
    PCMan 的跳槽經驗分享
    ●講者:洪任諭

    Lead developer of LXDE: http://lxde.org/

    PCMan BBS 軟體 : http://pcman.openfoundry.org/

    IE Tab Firefox Plugin: http://code.google.com/p/ietab/
    ●現職:台北榮總過敏免疫風溼科總醫師
    只要有心,人人都可以玩自由軟體 !
    快逃啊 ~

    View Slide

  2. 2
    Gtk+ (GIMP Toolkit)

    Gnome 使用的 GUI toolkit

    以 C 語言開發

    用 C 手工實作物件導向功能

    LGPL 授權

    可跨 Windows, MacOS X ,但支援度差強人意

    主要只含有 GUI 功能,其他功能仰賴其他 libraries

    View Slide

  3. 3
    Qt

    KDE 使用的GUI toolkit

    以 C++ 語言撰寫,搭配自訂關鍵字(signals, slots,...)

    程式碼需經 moc (meta object compiler)預處理

    歷史上曾是QPL/GPL雙授權,商業授權貴

    被Nokia 收購後,有LGPL 授權了 (後被Digia收購)

    跨 Windows, OS X, Android, iOS,...等

    含有database, network, xml 等各種功能模組

    View Slide

  4. 4
    歷史上 ...

    Gtk+ 授權較開放 (LGPL)

    C 語言支援度比較好,較好移植

    Gtk+ 寫的程式相對輕巧 ? Qt 程式比較肥 ?

    C++ 語法比較複雜 ?

    Gtk+ 支持者似乎比較多? Gnome, Adobe,
    Firefox, Google Chrome…

    Gtk+ language bindings 多 (python, perl, ...)

    View Slide

  5. 5
    事實上 ...

    Qt 一樣開放,現在也有 LGPL 授權

    到處都有 C++ , Qt 支援平台還比 gtk+ 多

    Gtk+ 一點都不輕巧, Qt 也不肥 ( 是 KDE 肥,
    不是 Qt)

    C++ 比較簡單,用 C 寫 GObject 才複雜 ... 更容
    易出錯

    有很多商業軟體是 Qt 寫的,例如 Virtualbox

    Qt language bindings 其實一樣多

    View Slide

  6. 6
    為什麼你應該快逃

    Gtk+2 => Gtk+ 3 變化太大,移植絕對不是改
    Makefile 換版本重編譯就會動

    Gtk+ 3 經常改變 API/ABI ,開發者不管使用
    者,經常更新你的程式就壞掉

    Gtk+ 3 就算 API 沒改,同一 API 行為可能不
    同,雖編得過,但是一執行就ㄎㄎ ...

    Gnome 開發者打算讓 Gtk+ 變成 Gnome 專
    用,不顧別人的需求 (google "Gnome rotting")

    View Slide

  7. 7
    Gtk+ 怎麼改成 Qt?
    t
    G k+
    Q

    View Slide

  8. 8
    假設你本來就會 Gtk+

    你只需要學會 C++ ( 這才是最難的部份 )

    但不需要很會 (Qt 只用少部份 C++)

    兩者概念非常相似,剩下的幾乎只是翻譯而已

    當然,翻譯還是很花時間的 ...

    不過,辛苦是值得的!

    View Slide

  9. 9
    GObject vs QObject
    GObject QObject
    方便性 麻煩,需要手寫大量 code ,手動
    模擬 C++ 編譯器行為
    手動填寫 virtual function table
    簡單,是一般 C++ 物件,只需要加
    入 Q_OBJECT macro
    記憶體管理 強制 Reference counting ( 手動計
    算 )
    無 reference counting
    但可指定物件 owner ↔ child
    Owner 解構時會一起解構 child
    可用 smart pointer
    Type safety 差,手動 type casting ,透過 gtk+
    實作的 RTTI 手動型別檢查
    C++ 內建語法,也可用 Qt 提供的
    RTTI
    Property 手動處理,需寫 class init function
    在其中手動註冊新屬性
    g_object_class_install_property()
    用 Q_PROPERTY() macro
    儲存額外資料 g_object_set_data() QObject::setProperty()
    Gtk+ 和 Qt 當中大多數物件的基礎類別

    View Slide

  10. 10
    GObject vs QObject
    GObject QObject
    定義新 signal 麻煩,需要手寫大量 code ,手動
    註冊 signals ,用 automake 規則生
    成 marshaller 函數 ...
    簡單,只需要加入 signals 關鍵
    字,或 Q_SIGNALS macro
    連接 signal g_signal_connect(object,
    "signal_name",
    G_CALLBACK(handler), user_data);
    connect(object,
    SIGNAL(signalName()),
    SLOT(handler()));
    斷開 signal g_signal_handler_disconnect(objec
    t, handler_id);
    disconnect(object,
    SIGNAL(signalName()),
    SLOT(handler()));
    方便性 需手動,物件解構前若忘記手動
    disconnect ,會 crash!!
    物件解構可自動 disconnect
    (sender 或 receiver 都會 )
    Type safety 差,連錯不相容的 signa/slot 會
    crash
    略好,會 runtime 檢查,但
    compile time 不檢查 (Qt5 改善 )
    兩者都有 signal/slot 機制 ( 其實是 Gtk+ 學 Qt 的 ...)

    View Slide

  11. 11
    GObject vs QObject
    GObject QObject
    種類 只能是一般函數
    有 default handler (virtual)
    只能是 QObject 衍生 class 的 methods
    無 default handler
    參數 第一參數為 sender ( 除非 swapped)
    參數無 receiver ,只有任意 user_data ( 通常
    放 receiver data)
    參數無 sender ,用 sender() 取得
    this 是 receiver
    範例 static void handler(SenderObject* sender,
    ParamType param1,...,
    gpointer user_data)
    {
    ReceiverObject* receiver =
    RECEIVER_OBJECT(user_data);
    do_something(receiver);
    }
    void ReceiverObject::handler(ParamType
    param1, ...) {
    QObject* sender_ =sender();
    doSomething();
    }
    Signal handlers 大不同

    View Slide

  12. 12
    用 GObject 定義一個 class (*.h)
    #include
    // type-casting macros (for RTTI)
    #define FM_TYPE_FOLDER (fm_folder_get_type())
    #define FM_FOLDER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),FM_TYPE_FOLDER, FmFolder))
    #define FM_FOLDER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),\
    FM_TYPE_FOLDER, FmFolderClass))
    #define FM_IS_FOLDER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),\
    FM_TYPE_FOLDER))
    #define FM_IS_FOLDER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),\
    FM_TYPE_FOLDER))
    typedef struct _FmFolder FmFolder;
    typedef struct _FmFolderClass FmFolderClass;
    struct _FmFolderClass // virtual function table 及其他 class info
    {
    GObjectClass parent_class;
    void (*files_added)(FmFolder* dir, GSList* files); // virtual function
    };
    struct _FmFolder // class 的記憶體內容
    {
    GObject parent; // parent class
    /**/
    FmPath* dir_path;
    ...
    };
    GType fm_folder_get_type (void); // 向 GObject 物件系統註冊這個 object type
    FmFolder* fm_folder_new(); // 呼叫 constructor
    // 其他 non-virtual methods
    FmFileInfo* fm_folder_get_info(FmFolder* folder);
    FmPath* fm_folder_get_path(FmFolder* folder);

    View Slide

  13. 13
    用 GObject 定義一個 class (*.c)
    G_DEFINE_TYPE(FmFolder, fm_folder, G_TYPE_OBJECT); // 定義物件類別
    static void fm_folder_class_init(FmFolderClass *klass) // 初始化 class info ,填寫 virtual function table
    {
    GObjectClass *g_object_class;
    FmFolderClass* folder_class;
    g_object_class = G_OBJECT_CLASS(klass); // 手動 type casting
    g_object_class->dispose = fm_folder_dispose; // 手動填寫 virtual destructor
    fm_folder_parent_class = (GObjectClass*)g_type_class_peek(G_TYPE_OBJECT);
    folder_class = FM_FOLDER_CLASS(klass);
    folder_class->content_changed = fm_folder_content_changed; // 填寫 virtual function table
    signals[ FILES_ADDED ] = // 註冊新的 signal
    g_signal_new ( "files-added",
    G_TYPE_FROM_CLASS ( klass ),
    G_SIGNAL_RUN_FIRST,
    G_STRUCT_OFFSET ( FmFolderClass, files_added ),
    NULL, NULL,
    g_cclosure_marshal_VOID__POINTER, // marshaller function, 告訴 Gobject 如何傳遞參數,常需從 Makefile 生成
    G_TYPE_NONE, 1, G_TYPE_POINTER);
    }
    static void fm_folder_init(FmFolder *folder) // constructor
    {
    ...
    }
    static void fm_folder_dispose(GObject *object) // destructor
    {
    (* G_OBJECT_CLASS(fm_folder_parent_class)->dispose)(object); // 手動呼叫父類別 destructor
    }

    View Slide

  14. 14
    如果你用 Qt/C++
    #include
    class FmFolder: public QObject { // 唯一缺點,不能多重繼承
    Q_OBJECT // 記得加上 Q_OBJECT macro
    public:
    FmFolder() {
    // constructor
    }
    virtual ~FmFolder() {
    }
    signals: // 註冊新的 signals 就這樣,不用寫 code (MOC 會自動生成所需 code)
    void filesAdded(FileInfoList* files);
    protected:
    virtual void contentChanged() { // 產生 vtable 是 C++ compiler 的工作
    ...
    }
    private: // 有存取權限控管
    FmPath* path;
    };

    View Slide

  15. 15
    多國語言翻譯
    Gtk+ Qt
    工具 使用 GNU gettext Qt 內建 QTranslator
    標記待翻
    譯字串
    _("English string") tr("English String)
    檔案格式 *.po *.ts (xml 格式 )
    初始化 bindtextdomain(GETTEXT_PACKAGE,
    PACKAGE_LOCALE_DIR);
    bind_textdomain_codeset(
    GETTEXT_PACKAGE, "UTF-8" );
    textdomain ( GETTEXT_PACKAGE );
    QTranslator translator;
    translator.load(" 程式名稱 _"
    +Qlocale::system().name(),
    " 檔案位置 ");
    qApp->installTranslator(&translator);
    雖然不同但非常類似

    View Slide

  16. 16
    Qt 跟現有 gnome/C libraries 合用

    幾乎完全沒問題,可完美混用 ( 例如 glib/gio)

    Qt 支援 glib mainloop integration!

    小心 Qt 自訂的關鍵字 signals, slots,... 等等,
    用 -DQT_NO_KEYWORDS compiler flag 關掉 Qt
    自訂保留字,改用 Q_SIGNALS, Q_SLOTS 等代替
    (Glib headers 有用這些字命名變數 )

    不要在 C code 尤其 header ,用到 C++ 關鍵字
    (new, class, …)

    小心 extern "C" 問題 (C++ name mangling)

    View Slide

  17. 17
    廣告時間 -Migrate from GTK+ to Qt

    http://wiki.lxde.org/en/Migrate_from_GTK%2B_to_Qt
    Try it yourself!

    View Slide