Archive:Development/FAQs/Technical FAQ (zh TW): Difference between revisions

From KDE TechBase
m (Text replace - "<code cppqt>" to "<syntaxhighlight lang="cpp-qt">")
m (Text replace - "</code>" to "</syntaxhighlight>")
Line 186: Line 186:
  static QRect KGlobalSettings::desktopGeometry(const QPoint& point);  
  static QRect KGlobalSettings::desktopGeometry(const QPoint& point);  
  static QRect KGlobalSettings::desktopGeometry(QWidget *w);  
  static QRect KGlobalSettings::desktopGeometry(QWidget *w);  
</code>
</syntaxhighlight>
Use splashScreenDesktopGeometry() to determine the geometry of the desktop when you want to display an application splash screen. Use desktopGeometry() to determine the geometry of the desktop with respect to a given point on the desktop, or with respect to a given widget. Do not use the Qt class QDesktopWidget to determine these values yourself. The KDE functions take the user's settings into account, something the Qt functions cannot do.  
Use splashScreenDesktopGeometry() to determine the geometry of the desktop when you want to display an application splash screen. Use desktopGeometry() to determine the geometry of the desktop with respect to a given point on the desktop, or with respect to a given widget. Do not use the Qt class QDesktopWidget to determine these values yourself. The KDE functions take the user's settings into account, something the Qt functions cannot do.  



Revision as of 20:49, 29 June 2011


Development/FAQs/Technical FAQ

Warning
This section needs improvements: Please help us to

cleanup confusing sections and fix sections which contain a todo


The build system has changed in KDE4.

如何開始新的應用程式?

The easiest way is to use kdesdk/kapptemplate to generate the CMakeLists.txt. Or you can just copy a CMakeLists.txt from another app and install it in a new toplevel directory of existing KDE sources. Or you can start the old way, from scratch.

什麼是Plasma、kpart、kio、kdeinit ...

查看 TechBase,尤其是架構文件。也可以查看 KDE的書籍。

我真的需要使用 kpart 嗎?

不會強迫你使用他,但它能帶來更多好處。KPart 提供強大的程式碼重用。這個技術用起來既簡單,部署的範圍又廣,如果你會用卻不用那可真丟人哪。

如何撰寫 CMakeLists.txt?

請見 CMake 教學

make 有什麼目標可用?

  • all: the default target (the one you get when typing "make").
  • clean: removes all the generated files
  • distclean: also removes the files generated by Makefile.cvs Not very useful within KDE (see dist for the "dist" concept and svn-clean for a better way to make really clean).
  • dist: supposedly for making a tarball out of SVN sources, but not very well supported within KDE. Better use kdesdk/scripts/cvs2pack for that.
  • force-reedit: re-run automake and am_edit on the Makefile.am
  • install: well, install everything :)
  • install-strip: install everything and strips binaries (removes debugging symbols).
  • install-exec: only install the binaries and libraries
  • install-data: only install the data files
  • check: compiles the test programs (i.e. those defined with check_PROGRAMS). It is even possible to actually run some tests during "make check", see kdelibs/arts/tests/Makefile.am

I have a checkout of SVN, there is no configure and no Makefile?

Use make -f Makefile.cvs It will run all the Makefile generation steps

How can I temporarily exclude certain directories from build?

While hacking a program it might be useful to exclude certain directories from build that would otherwise be recompiled, but don't actually need to be. Also, if you checked out source code that didn't compile and you don't have the time or knowledge to fix the error you might want to turn off compilation of the directory alltogether. There are two cases. Toplevel directories, and subdirectories. For toplevel directories you can simply erase them (or not check them out).

If for some reason you don't want to do that, you can also set DO_NOT_COMPILE="someapp" before calling configure, this will make configure skip "someapp". To only compile very few toplevel dirs, instead of using DO_NOT_COMPILE to exclude most of them, you can list in a file named 'inst-apps', at the toplevel, the toplevel subdirs you want to compile. To turn off compilation of any directory, including subdirectories, you have to modify the Makefile or Makefile.am files. Makefile.am is not recommended because that file is in KDE Subversion and you could accidentally commit your changes. So we'll modify the Makefile instead here:

Open the Makefile in the directory immediately above the directory you want to exclude in a text editor and look for a variable SUBDIRS. It will often look somewhat like

SUBDIRS = share core ui . proxy taskmanager taskbar applets extensions data

Just remove the directory that you want to exclude and save your file. A new make will skip the directory you just removed.

Sometimes you'll have to look harder because the SUBDIRS variable consists of a number of other variables:

SUBDIRS = $(COMPILE_FIRST) $(TOPSUBDIRS) $(COMPILE_LAST) 

Here you have to find the COMPILE_FIRST, TOPSUBDIRS and COMPILE_LAST variables. One of those contains the dir you want to exclude. Remove the directory where you find it and save the Makefile again. To undo your changes you can either regenerate the Makefile from scratch or revert to an older backup (you did make one, did you? :-). To regenerate a Makefile, just make force-reedit.

You can also copy the original line in the file when editing and make it a comment by prefixing a '#' in front of it. Then undoing the change is as easy as making the modified line a comment and removing the comment in the original line.

What are the various compilation options?

--enable-debug

Add debug symbols, disable optimisations and turns logging of kdDebug() on.

--disable-debug

The opposite of the previous one: enable optimisations and turns kdDebug() logging off.

--enable-final

Concatenates all .cpp files into one big .all_cpp.cpp file, and compiles it in one go, instead of compiling each .cpp file on its own. This makes the whole compilation much faster, and often leads to better optimised code, but it also requires much more memory. And it often results in compilation errors when headers included by different source files clash one with the other, or when using c static functions with the same name in different source files. This is a good thing to do at packaging time, but of course not for developers, since a change in one file means recompiling everything.

--disable-fast-perl

By default KDE uses perl instead of sh and sed to convert Makefile.in into Makefile. This is an addition to autoconf done by Michael Matz. You can use this option to disable this but it is a lot slower.

Which compile option do you recommend?

If you are a developer, you should definitely compile Qt and KDE with --enable-debug. You will then be able to debug your program even inside Qt and KDE function calls. If you are just a user, you can still use --enable-debug. KDE will occupy more space on your hard disk but it won't slow down your desktop. The advantage is that you get stack trace when an application crash. If you can reproduce a crashing behaviour, go to bugs.kde.org, check that your bug doesn't exist yet and submit it. It will help us improve kde. For Qt, the compilation options are explained in qt-copy/README.qt-copy.

Tips to increase compilation speed?

See --enable-final above :) . "make final" uses the all-in-one-file trick in the current directory even if --enable-final wasn't used, and "make no-final" does a normal compilation in the current directory even if --enable-final was used. Include your moc files! Header files declaring a QObject descendant have to be run through moc to produce a .moc file. This .moc file has to be compiled, for which two possibilities exists: compile it separately, or #include it in the C++ file implementing that above mentioned class. The latter is more efficient in term of compilation speed. BTW, kdesdk/scripts/includemocs does this automatically. Buy more ram, a faster machine and another processor. On a bi-PIII 866 MHz with 1GB of RAM, kde compiles at a decent speed :-)))

There is a STRIP variable set in the Makefile but it doesn't seem to be used?

The strip is done at install. To use it, use "make install-strip" instead of "make install".

應該採用什麼縮排格式?

如果你在編寫已有的應用程式,請尊重作者的縮排格式。否則,你可以依照自己喜好選用。

i18n 和 I18N_NOOP 的區別?

If you do

QString translatedStuff = i18n("foobar");

translatedStuff will contain the translation of "foobar", while for

const char *markedStuff = I18N_NOOP("foobar");

markedStuff will still contain literal "foobar", but translators will know you want "foobar" translated so you can later on do QString translatedStuff = i18n(markedStuff); and get the translation of "foobar", which wouldn't work without that I18N_NOOP. So, normally you want to just use i18n(), but in cases where you absolutely need to pass something untranslated, but still need to translate it later or in the case that you have something to be translated before the KInstance exists, use I18N_NOOP().

I get "virtual tables error"?

This often comes from the moc files not being in sync with the sources, or not linked at all. Check that you are running the right moc. 'which moc' will tell it. Regenerate your moc files (make force-reedit; make clean; make).

我在 myClassHeader.h 中加入了 Q_OBJECT,但是沒有產生 moc 檔?

You need am_edit to reparse your Makefile.am to generate the correct Makefile. If it's the first Q_OBJECT you're using in this directory, you'll need to re-run Makefile.cvs or create_makefile from kdesdk/scripts. Otherwise, you can simply run "make force-reedit".

為了讓程式跑的更快,我把整個類別都放到cpp檔裡,我該如何得到生成的moc檔呢?

如果類別使用了 Q_OBJECT 巨集,那你最好別那樣作。Maybe METASOURCES=file.cpp might work for moc files though.

我開發出一個 kpart(或外掛)。我不想安裝它,因為還沒有完成。但我需要 KDE 找到他,當我使用 KTrader或 KLibLoader 時需要他。我怎麼做呢?

KDE searches its libraries in $KDEDIR/lib and in the lib directory of all the components of $KDEDIRS (note the additional 'S', this different from $KDEDIR). So, while you are still developing your library and don't want to install it, you can use this trick: cd to your development directory, the one where your library is built. Set up KDEDIRS so that it include your development directory: export KDEDIRS=`pwd`:$KDEDIR Create a pseudo lib directory where KDE should find your component: ln -s .libs lib (all the objects and libraries are built in the .libs directory). Run kbuildsycoca to inform KDE that it KDEDIRS has changed.

Now, KDE should find your library when using KTrader or KLibLoader.

如何不使用root,安裝額外的 KDE 東西?

If want to install your application privately, configure it with another prefix: for $HOME/kdeprefix, use configure --prefix=$HOME/kdeprefix. Then let KDE know about this prefix: set KDEDIRS to $HOME/kdeprefix:$KDEDIR. To make KDE aware of new prefixes, one can also edit /etc/kderc and add

[Directories]
prefixes=/the/new/prefix

but this doesn't answer this specific question ;-) Make sure to run "kbuildsycoca" after setting the new KDEDIRS.

當我使用 KTrader 時,我的 kpart lib 未列出

The mimetype database must be rebuilt when you install new services (such as applications or parts). In theory this happens by itself (kded is watching those directories), but in doubt, run "kbuildsycoca". The best way to debug trader-related problems is to use ktradertest: cd kdelibs/kio/tests; make ktradertest, then run ./ktradertest to see how to use it.

我修改了kdelibs中的一些東西,安裝了新的函式庫,但新的 KDE 應用程式似乎沒有使用它?

The solution is simple: start new apps from a command line, then they will use the new lib.

The reason is that applications started by other KDE applications (kicker, minicli, konqueror, etc.) are started via kdeinit, which loads the libs when KDE starts. So the "old" version of the libs keep being used. But if you want kdeinit to start using the new libs, simply restart it. This is done by typing kdeinit in a terminal.

This is necessary if you can't start things from the command line - e.g. for a kioslave. If you change something in kio, you need to restart kdeinit and kill the running kioslave, so that a new one is started.

I'm developing both a KPart and a standalone application, how do I avoid duplicated code?

Apps are often tempted to link to their part because they of course have much functionality in common. However this is wrong for the reasons below. A lib is something you link to, a module is something you dlopen. You can't dlopen a lib ; you can't link to a module. A lib has a version number and is installed in $libdir (e.g. $KDEDIR/lib) a module doesn't have a version number (in its name), it's more like a binary (we don't have konqueror-1.14.23 either :), and is installed into kde_moduledir (e.g. $KDEDIR/lib/kde3) (which means it's not in the search path for ld.so, so this breaks on systems without -rpath). If you didn't understand the above, don't worry. The point is: you should NOT make your application link to your (or any other) KPart, nor any other kind of dlopened module. The solutions let the app dlopen the part. This is what KOffice does. However this limits the app to the very limited ReadOnlyPart/ReadWritePart API. Keep in mind that you can't call a non-virtual method whose implementation you don't link to. The solution is to define a ReadWritePart-derived class (like we have in koffice: KoDocument), with new virtual methods. Either this derived class has code (and you need a lib shared by the app and the part, see point 2 below), or an abstract interface (header file only) is enough. You can also use known interfaces to child objects of the part instead of changing the base class of the part itself - this is the solution used by e.g. KParts::BrowserExtension. define a common library with the common classes and let both the part and the app use it. That library can be noinst_ or lib_, both work. In the first case the compiled object code is duplicated, in the second case a real versioned lib will be installed. The idea here is that the part itself is not available to the app, but instead the part is a very thin wrapper around the same classes as the app uses. Only KParts-specific stuff remains in the part.

啟動另一個應用程式最好的方式?

In KDE there are several ways to start other programs from within your application. 這裡總結了一些你應該和不應該使用的方法和理由。

fork + exec

不要使用他們。除非你有很好的理由,是不能使用 KProcess 的。

KProcess

只有在你要啟動子行程(process)的時候,才使用KProcess。例如,你要取得 stdout/stderr或需要透過 stdin 發送資料。您不應該用它來啟動其他 KDE 應用程式,除非您的應用程式叫kgdb :-)

startServiceByDesktopPath

是啟動桌面(KDE/Gnome/X)應用程式或 KDE 服務的最佳方式。應用程式或服務必須有 .desktop 檔。可以利用 KDEinit 來提高啟動性能和降低記憶體使用量。這只適合應用程式可用於 KDEinit loadable module(KLM)時。

KRun

Generic way to open documents/applications/shell commands. Uses startServiceBy.... where applicable. Offers the additional benefit of startup-notification.
KRun可以從二進位檔或 desktop 檔啟動任何應用程式。他會判斷檔案的 MIME 類型,再執行最佳的處理方式,這同樣可以啟動 Shell 指令。這就是我們推薦使用 KRun 執行其他程式的原因。

KToolInvocation::invokeBrowser

KToolInvocation::invokeBrowser launches a web browser. The difference with using the more generic KRun on the webpage URL is that KRun has to determine the mimetype of the URL first (which, for HTTP, involves starting a download to read the headers), so if you know that the URL is an HTML webpage, use invokeBrowser, it will be faster.

More details: the problem with KRun for webpages is that it delays the appearance of the browser window, and if the user's preferred browser is a non-kde application like firefox then it has to start a second download while konqueror which can reuse the kioslave started by KRun. On the other hand if the URL might be an image or anything else than html, then KRun is the right solution, so that the right application is started.

如何建立並提交補丁給KDE?

You have spotted a bug and you want to write the code to fix it. Or you want to code a specific feature. Sending a patch is very appreciated by developers. A tutorial is available but here is a description of how you should proceed: Get the latest KDE using SVN to check that the code you want to write has not been added yet. Check the bug database to see if your bug is not worked on. Get in contact with the author. His/her name is in the about box or in the source header. If the project has a mailing-list, browse the archives to see if your bug/feature has not been the subject of any discussion. If you can't find any mailing lists or author, simply write to kde-devel. Post a message explaining your intentions. It is important to inform the author(s) about what you are planning because somebody might already be working on your feature, or a better design could be proposed by the author, or he could give you some good advice. Next step is to code your feature. It is usually a good idea to keep an original at hand and to work on a copy. This allow to check the behaviours of both versions of the code. Respect the author's indentation and naming scheme, code carefully, think about side-effects and test everything many times. Using the latest KDE code, make a diff using either svn diff or a diff -uNp original-dir new-dir. Don't send reversed patch. The first argument of diff should be the old directory and the second the new directory. Send a mail to the author/mailing-list with your patch as attachment (don't forget to attach it :-) ). People usually have some remarks on your work and you must work further on your patch to improve it. It is common to see three or four submission before acceptation.

Ok, you have done it, your code has been included in KDE. You are now fully part of the KDE project. Thanx a lot.

How do I make my application Xinerama and multi-head safe?

Never make assumptions about the geometry of the "desktop" or the arrangement of the screens. Make use of the following functions from kglobalsettings.h:

 static QRect KGlobalSettings::splashScreenDesktopGeometry(); 
 static QRect KGlobalSettings::desktopGeometry(const QPoint& point); 
 static QRect KGlobalSettings::desktopGeometry(QWidget *w);

Use splashScreenDesktopGeometry() to determine the geometry of the desktop when you want to display an application splash screen. Use desktopGeometry() to determine the geometry of the desktop with respect to a given point on the desktop, or with respect to a given widget. Do not use the Qt class QDesktopWidget to determine these values yourself. The KDE functions take the user's settings into account, something the Qt functions cannot do.

It is ideal to try to avoid using the desktop geometry altogether. Your application will be much more standards compliant if you let the window manager place your windows for you. When this is not possible, you have the aforementioned functions available. Please beware that the geometry that is returned from these functions may not start at (0,0)! Do your math correctly!

One other caution: Both KWin and the NETWM specification have severe difficulties handling struts with Xinerama or "merged" displays. This can result in dead areas on the screen, for instance if kicker does not span a whole edge. There is not much that can be done about this, and you should try to avoid hacks to circumvent this at this time. We hope to find a proper solution for this soon.

I get an error about KDE not finding UIC plugins, but I know they're installed. What's wrong?

This is almost certainly an installation problem, not a KDE code problem. A number of problems can lead to this, but most likely you have more than one version of Qt laying around and the configure script is calling a different one than KDE is using.

Another thing that may help is to rebuild and reinstall your kdewidgets.so file, which is located in the kdelibs/kdewidgets directory. Note that if you *do* have multiple versions of Qt, this may compile against the wrong one. This problem creeps up on various mailing lists occasionally, so looking at the archives on lists.kde.org may be helpful.

I put some functions in anonymous namespace and someone reverted it and made those functions static, why?

Symbols defined in a C++ anonymous namespace do NOT have internal linkage. Anonymous namespaces only give an unique name for that translation unit and that is it; they don't change the linkage of the symbol at all. Linkage isn't changed on those because the second phase of two-phase name lookup ignores functions with internal linkages. Also, entities with internal linkage cannot be used as template arguments.

可以刪除空指標嗎?

可以。在C++刪除(delete)空指標是不會作業的。「if (ptr) delete ptr;」是多餘的。刪除後,使用「ptr = 0;」是一個好主意。特別是刪除可以從不同的程式碼路徑被呼叫。

My locally installed application doesn't run, but KDEDIRS is set correctly, what's going on?

If you're running a 64 bits system, your libraries might have been compiled with the -DLIB_SUFFIX=64 option given to cmake. If your application wasn't compiled with that option, it'll get its modules installed into $prefix/lib/kde4, not $prefix/lib64/kde4 -- and then it will not be found. Easy solutions: a symlink to lib64 or compile your code with -DLIB_SUFFIX=64, too.