-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathkapitel_04.tex
1526 lines (1288 loc) · 69.6 KB
/
kapitel_04.tex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
\ospchapter{Projektstruktur und Paketerstellung}{Projektstruktur und Paketerstellung}{chap:projektsstruktur}
\index{Anwendung}%
\index{Anwendung!Paketerstellung}%
Ist eine PyQt- oder PySide-Anwendung für den produktiven Einsatz
fertig, möchte man sie natürlich auch Benutzern zur Verfügung stellen.
Die Installation soll möglichst einfach sein, d.\,h. so ablaufen, wie
es der Benutzer von seinem Betriebssystem gewohnt ist. Die
Unabhängigkeit vom Betriebssystem ist schließlich eine der Stärken von
Qt, und dieser Vorteil soll beim Deployment nicht wieder
verlorengehen.
In diesem Kapitel geht es also darum, wie Sie Ihre Anwendung für den
Vertrieb vorbereiten. Zunächst werden wir eine allgemeine
Projektstruktur für PyQt- und PySide-Projekte definieren.
Anschließend sehen Sie, wie Sie eine solche Anwendung in drei
verschiedene Arten von Paketen verpacken können:
\begin{osplist}
\item ein Source-Paket zur Installation, falls schon Python und PyQt
bzw. PySide auf dem System des Benutzers vorhanden sind;
\item Linux-Pakete für Debian-basierte Betriebssysteme sowie für
Distributionen, die RPM nutzen;
\item eine kompilierte Windows-Version samt Installer.
\end{osplist}
Die Paketerstellung für Linux ist die komplexeste und kann hier nicht
im Detail beschrieben werden, auch wenn das gezeigte Vorgehen
installierbare Pakete liefert. Denn um diese in das Repository einer
Linux-Distribution einzupflegen, sind weitere Voraussetzungen zu
erfüllen. In Kapitel \ref{sec:linuxpakete} finden Sie Links mit
weiterführenden Informationen zu diesem Thema.
Als Beispiel nehmen wir die bisher entwickelte Beispielanwendung, wie
wir sie am Ende von Kapitel \ref{chap:qttools} abgeschlossen haben.
Die Anwendung enthält also sowohl Qt-Designer- als auch
Übersetzungsdateien, die in einer Ressourcendatei verwaltet werden.
Der Python-Quellcode des Projekts wird in der Beschreibung der
Projektstruktur noch einmal kurz vorgestellt, so dass Sie dieses
Kapitel auch ohne Rückgriff auf die vorhergehenden lesen können.
\bigskip
\ospsection{Projektstruktur für PyQt- und PySide-Anwendungen}{Projektstruktur für PyQt- und PySide-Anwendungen}{sec:strukturanwendung}
\index{Setup}%
\index{Anwendung!Template}%
Wenn Sie schon intensiv mit Python gearbeitet haben, wissen Sie, dass
Python-Projekte oft eine bestimmte Grundstruktur aufweisen: Im
Hauptordner befindet sich neben \ospfile{README} und \ospfile{LICENSE}
etwa die Datei \ospfile{setup.py} zur Installation der Anwendung oder
Bibliothek in das systemweite Python-Verzeichnis; ein Ordner
\ospfile{src} enthält die Python-Pakete und -Module des Projekts, und
möglicherweise gibt es einen Ordner \ospfile{bin} mit der ausführbaren
Hauptdatei des Projekts.
Schließlich gibt es häufig einen Ordner \ospfile{data} für binäre und
zusätzliche Dateien wie Icons, Konfigurations-XML usw. Ganz ähnlich
sieht auch ein PyQt- bzw. PySide-Projekt aus, nur dass wir für die
Debian-Paketerstellung und die Windows-Installation einige weitere
Dateien benötigen.
Abbildung \ospfigref{fig:projektstrukturfull} zeigt die Struktur eines
typischen PyQt-Projekts, in diesem Fall enthält der Ordner
\ospcmd{helloworld-1.0} alle Dateien unserer Beispielanwendung aus
Kapitel \ref{chap:qttools} sowie die darüber hinaus zur
Paketerstellung nötigen. Im Folgenden betrachten wir die einzelnen
Dateien genauer, und zwar in der Reihenfolge der in der Abbildung
gezeigten Ordner.
\osppagebreak
\ospfigure{0.45}{images/pyqt_projektstruktur_full}{Projektstruktur eines PyQt-Projekts}{fig:projektstrukturfull}
\ospsubsection{Ordner und Dateien des Projekts}{Ordner und Dateien des Projekts}{sec:projektordnerdateien}
Der Ordner \ospfile{bin} enthält die Hauptdatei der PyQt-Anwendung;
hier heißt sie einfach \ospfile{helloworld}. Die Python-Endung
\ospcmd{.py} entfällt, da wir die Anwendung später in das
\ospfile{bin}-Verzeichnis eines Linux-Systems installieren wollen,
also beispielsweise nach \ospfile{/usr/local/bin}. Debian und darauf
basierende Distributionen erlauben keine
programmiersprachenspezifischen Dateiendungen. Die Idee dahinter ist, dass sich
die Programmiersprache ändern kann, der Name der ausführbaren Datei
aber gleich bleiben soll. Schließlich kann es sein, dass andere
Anwendungen bzw. Pakete diese Anwendung aufrufen. Die Datei
\ospfile{helloworld} enthält den Anwendungsstarter; sie lädt unter
anderem die Übersetzungsdateien, erzeugt das
\ospcmd{QApplication}-Objekt und startet die Hauptereignisschleife.
Kapitel \ref{sec:dateiensrcundbin} stellt diese Datei vor.
\index{Ressourcen}%
Der Ordner \ospfile{data} enthält sämtliche Ressourcen, also das in
der Anwendung dargestellte Bild \ospfile{pyqt\_logo.jpg} sowie die
Übersetzungsdateien. Zur besseren Übersicht sind diese Dateien jeweils
in einem eigenen Unterordner abgelegt (\ospfile{images} und
\ospfile{translations}). Eine weitere Datei ist noch hinzugekommen:
\ospfile{hellworld.desktop}, die bei Linux-Paketen einen Menüeintrag
im Anwendungsmenü erzeugt. Diese Datei sehen wir uns in Kapitel
\ref{sec:sourcepakete} näher an.
\index{Debian}%
\index{Debian!Paketerstellung}%
Der Ordner \ospfile{debian} enthält alle Dateien, die wir für die
Debian"=Paketerstellung brauchen. Wir stellen sie in Kapitel
\ref{sec:debpakete} vor.
Der Ordner \ospfile{src} enthält ein Python-Paket \ospcmd{helloworld}
sowie dessen Unterpaket \ospcmd{helloworld.ui}. Sie enthalten im
Moment nur den Code für die Hauptfenster-Klasse. Das Unterpaket
\ospcmd{helloworld.ui} wird später benutzt, um die automatisch
generierten Python-Code-Dateien abzulegen, die die beiden Tools
\ospcmd{rcc} und \ospcmd{uic} aus den Qt-Designer-Dateien und den
Ressourcendateien erzeugen. Die Datei \ospfile{mainwindow.py} enthält
die Definition der Hauptfenster-Klasse, wie sie bisher in unseren
Beispielen deklariert wurde. Beachten Sie, dass wir den Code zum Start
der Anwendung, also die Erzeugung des \ospcmd{QApplication}-Objekts,
per \ospcmd{exec\_()} in \ospcmd{bin/helloworld} ausgelagert haben.
Der Inhalt beider Dateien ist Thema von Kapitel
\ref{sec:dateiensrcundbin}.
\index{GUI}%
Der letzte Unterordner des Projekts ist schließlich \ospfile{ui}, der
alle Qt-Designer-Dateien des Projekts enthält. Es bietet sich an, alle
diese Dateien an einem Ort zu sammeln, um einen einfachen und
schnellen Zugriff darauf zu haben. In Kapitel
\ref{sec:rccuicautomatisierung} zeigen wir, wie sich
die Erzeugung der Python-Code-Dateien aus den \ospcmd{.ui}-Dateien
weitgehend automatisieren lässt.
\index{Projektverwaltung!Projektverwaltungsdatei}%
Der Hauptordner des Projekts enthält neben der gewohnten
Installationsdatei \ospfile{setup.py} eine Reihe weiterer Dateien.
Zunächst liegen hier die beiden Qt-spezifischen Dateien
\ospfile{helloworld.pro} (Projektverwaltungsdatei)\osplinebreak{} und
\ospfile{helloworld.qrc} (Ressourcenverwaltungsdatei). Beide
entsprechen jenen, die in Kapitel \ref{chap:qttools} erstellt wurden.
Die Datei \ospfile{generate\_ui.py} enthält ein Skript zur Erzeugung
von Python-Code aus \ospcmd{.ui}- und \ospcmd{.qrc}-Dateien und wird
in Kapitel \ref{sec:rccuicautomatisierung} vorgestellt.
Die Dateien \ospfile{cxfreeze.bat} und \ospfile{helloworld.nsi} dienen
der Kompilierung sowie Setup-Erstellung unter Windows. Beide Dateien
werden in Kapitel \ref{sec:windowspakete} über die
Windows-Paketerstellung besprochen. Schließlich ist die Datei
\ospfile{MANIFEST.in} notwendig, um ein RPM-Paket zu erstellen; sie
wird Thema in Kapitel \ref{sec:rpmpakete} sein.
\ospsubsection{Die Dateien der Ordner bin und src}{Die Dateien der Ordner bin und src}{sec:dateiensrcundbin}
\index{Anwendung!Template}%
In den bisherigen Beispielanwendungen wurde der gesamte Code in eine
Datei gespeichert, um die Darstellung und Erklärung zu erleichtern.
Außerdem bestanden alle Beispiele aus nur einer oder zwei Klassen,
dazu kam noch der Code zum Starten der Qt-Anwendung in der Funktion
\ospcmd{main}. In \dqo{}richtigen\dqc{} Anwendungen wird man dagegen
die Klassen auf je einzelne Python-Pakete und -Module aufteilen, um
die Wartbarkeit des Codes zu verbessern. Und schließlich wollen wir
den Code zum Start der Anwendung in ein eigenes Skript auslagern, das
der Benutzer dann als Hauptanwendung startet. Dieses Skript lädt
dann das Hauptfenster aus einem der ausgelagerten Module. Genau dies
ist Sinn und Zweck der im vorhergehenden Kapitel vorgestellten
Aufteilung in einen \ospfile{bin}- und einen \ospfile{src}-Ordner.
Der \ospfile{bin}-Ordner enthält ein oder mehrere Dateien, die je
einer Anwendung entsprechen. In unserem Fall besteht das Projekt nur
aus einer Anwendung, in anderen Fällen kann ein Projekt aber durchaus
auch mehrere Anwendungen bereitstellen. Um später einfacher ein
Debian-Paket zu erstellen, sollten diese Anwendungsskripte keine
Endung wie \ospcmd{.py} haben. In unserem Fall heißt das Skript
einfach \ospfile{helloworld}.
Neben dem Start der Anwendung und der Darstellung des Hauptfensters
kümmert sich das Skript zunächst einmal um die Einrichtung der Pfade
zu Daten und zu den eigenen Paketen sowie um das Laden der
Übersetzungsdateien. Ersteres ist wichtig, da die Anwendung nun ja aus
verschiedenen Verzeichnissen gestartet werden könnte: Der Entwickler
startet die Anwendung direkt aus dem Quelltext-Verzeichnis, ein
Linux-Anwender etwa von \ospfile{/usr/bin} und ein Windows-Anwender
wiederum aus einem Unterordner von \ospfile{C:\textbackslash{}Program
Files (x86)\textbackslash{}}. Da wir unter Linux und Windows
anwendungsspezifischen Klassen, Module und Pakete (wie z.\,B. die
Hauptfensterklasse) zentral installieren, muss dazu kein eigener Pfad
angegeben werden. Unsere Anwendung wird diese Klassen später
automatisch finden. Startet der Entwickler das Projekt jedoch aus dem
Quelltextverzeichnis, dann muss der Ordner \ospfile{src} zu den
Python-Pfaden hinzugefügt werden, in denen nach Modulen und Paketen
gesucht wird. Ein Aufruf von \ospcmd{sys.path.append()} mit dem Pfad
zum Ordner \ospfile{src} erledigt dies.
Außerdem wechselt man am besten gleich zu Beginn der Anwendung in den
\ospfile{data}-Ordner. Das ist vor allem dann sinnvoll, wenn nicht
alle Dateien als Ressourcen vorliegen, beispielsweise wenn man ein
Icon oder Bild aus dem Verzeichnis \ospfile{data/images} laden möchte.
Wir werden diese Dateien später unter Linux in ein systemweites
\ospfile{share}-Verzeichnis installieren (beispielsweise nach
\ospfile{/usr/share} unter Ubuntu), unter Windows jedoch in das
Programmverzeichnis unter \ospfile{C:\textbackslash{}Program Files
(x86)\textbackslash{}helloworld}. Als dritte Möglichkeit wurde die
Anwendung direkt aus dem Quelltextverzeichnis gestartet; in diesem
Fall wollen wir den Unterordner \ospfile{data} im Quelltextverzeichnis
verwenden. Wenn man nun gleich beim Start der Anwendung in das
entsprechende \ospfile{data}-Verzeichnis wechselt, kann die Anwendung
später an jeder Stelle im Code die Dateien mit einem relativen Pfad
laden (z.\,B. aus \ospfile{images/pyqt\_logo.jpg}). In unserem Fall
könnten wir uns das im Prinzip sparen, da wir ausschließlich Dateien
aus den in Python-Code umgewandelten Ressourcen laden. Wir greifen
also in unserem Beispiel nie direkt auf das Dateisystem zu, da die
Dateien als Python-Code direkt in die Anwendung geladen werden. Der
Vollständigkeit halber wechseln wir in unserem Beispiel aber trotzdem
nach \ospfile{data}, damit Sie später mit demselben Code auch Dateien
laden können, die Sie nicht in Ressourcen verpacken möchten.
\index{Anwendung!Plattformunabhängigkeit}%
Der erste Teil der Datei \ospfile{helloworld} im Ordner \ospfile{bin}
sieht also folgendermaßen aus:
\begin{osplisting}{Python}{Erster Teil des Hauptanwendungsskripts}{code:mainapp1}
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys, os, re
from PyQt4 import QtCore, QtGui
def main(argv):
helloworld_script = sys.argv[0]
if os.path.islink(helloworld_script):
helloworld_script = os.readlink(helloworld_script)
helloworld_dir = os.path.join(os.path.dirname(helloworld_script), '..')
prefix = os.path.abspath(os.path.normpath(helloworld_dir))
src_dir = os.path.join(prefix, 'src')
data_dir = os.path.join(prefix, 'data')
share_dir = os.path.join(prefix, 'share', 'helloworld')
if re.match(r"(?i)c:\\program", prefix):
# Gestartet unter Windows, installiert
print >>sys.stderr, 'Using resources from', data_dir
elif os.path.exists(src_dir) and os.path.exists(
data_dir) and not prefix.startswith("/usr"):
# Gestartet vom lokalen Ordner, nicht installiert
print >>sys.stderr, 'Using modules from', src_dir
sys.path.insert(0, src_dir)
print >>sys.stderr, 'Using resources from', data_dir
else:
# Gestartet unter *nix, installiert
data_dir = share_dir
print >>sys.stderr, 'Using resources from', data_dir
from helloworld.mainwindow import MyMainWindow
if os.path.exists(data_dir):
os.chdir(data_dir)
\end{osplisting}
Die Logik des Ganzen findet sich in der \ospcmd{if}-Verzweigung: Das
erste \ospcmd{if} prüft auf einen Windows-Pfad, das zweite
\ospcmd{elif} entscheidet, ob es sich um das Quelltext-Verzeichnis
handelt (in diesem Fall existieren \ospcmd{src} und \ospcmd{data},
aber es ist \emph{kein} Unterverzeichnis von \ospfile{/usr}), und für
das abschließende \ospcmd{else} bleibt schließlich nur der Fall, dass
die Anwendung unter einem Unix installiert wurde. Es gibt sicher
exotische Fälle, in denen diese Verzweigung nicht das macht, was sie
soll. Es handelt sich um die einfachste Art einer solchen
Betriebssystem-Weiche, die zumindest für die großen
Linux-Distributionen, Windows und für die meisten Entwickler
funktionieren sollte. Falls Sie in Ihrem Fall eine andere
Konfiguration benötigen, lässt sich die Verzweigung aber auch sicher
leicht an diesen Fall anpassen. Beachten Sie auch, dass die
Hauptfensterklasse erst importiert werden kann, wenn der Pfad auch in
den Python-Suchpfaden eingetragen ist, also nach einem eventuellen
\ospcmd{sys.path.append()}.
Der Rest des Skriptes ist dann das schon Gewohnte und wird so
verwendet, wie bisher implementiert. Es wird ein Objekt der Klasse
\ospcmd{QApplication} erzeugt, die Übersetzungsdateien werden aus den
Ressourcen geladen, und anschließend werden das Hauptfenster angezeigt
und die Hauptereignisschleife gestartet:
\begin{osplisting}{Python}{Zweiter Teil des Hauptanwendungsskripts}{code:mainapp2}
app = QtGui.QApplication(argv)
language = unicode(QtCore.QLocale.system().name())
qtTranslator = QtCore.QTranslator()
qtTranslator.load("qt_{0}".format(language),
QtCore.QLibraryInfo.location(QtCore.QLibraryInfo.TranslationsPath))
app.installTranslator(qtTranslator)
myappTranslator = QtCore.QTranslator()
myappTranslator.load(":/translations/helloworld_{0}".format(language))
app.installTranslator(myappTranslator)
mainwindow = MyMainWindow()
mainwindow.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main(sys.argv)
\end{osplisting}
Somit ist die Anwendung gestartet. Wie aber sieht nun
\ospfile{mainwindow.py} unter \ospfile{src/helloworld} aus? Sie
enthält jetzt ausschließlich den Code der Hauptfensterklasse, also den
Rest der bisherigen Implementierung. Der Vollständigkeit halber sei
der gesamte Code noch einmal aufgelistet:
\begin{osplisting}{Python}{Hauptfensterklasse in mainwindow.py}{code:mainwindowproject}
# -*- coding: utf-8 -*-
from PyQt4 import QtCore, QtGui
from ui.ui_helloworld import Ui_MainWindow
class MyMainWindow(QtGui.QMainWindow):
def __init__(self, *args):
QtGui.QMainWindow.__init__(self, *args)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.createConnects()
scene = QtGui.QGraphicsScene()
item = QtGui.QGraphicsPixmapItem(
QtGui.QPixmap(":/images/pyqt_logo.png"))
scene.addItem(item)
self.ui.graphicsView.setScene(scene)
def createConnects(self):
self.ui.pushButton.clicked.connect(self.textAktualisieren)
self.ui.lineEdit.textChanged.connect(self.ui.label.setText)
@QtCore.pyqtSlot()
def textAktualisieren(self):
self.ui.label.setText(self.ui.lineEdit.text())
\end{osplisting}
Der einzige Unterschied ist der \ospcmd{import} der aus der
Qt-Designer-Datei erzeugten UI-Klasse: Sie liegt später im
Unterverzeichnis \ospfile{ui}, wird also aus dem Paket \ospcmd{ui}
geladen. Im Moment ist diese Datei aber noch nicht vorhanden, die
Anwendung würde also nicht starten. Wie Sie die Datei am besten
erzeugen, zeigt Kapitel \ref{sec:rccuicautomatisierung}.
Die Dateien \ospfile{\_\_init\_\_.py} unter
\ospfile{src/helloworld} und \ospfile{src/hellworld/ui} sind leer. Sie
weisen Python lediglich darauf hin, dass es sich bei den
Verzeichnissen um Python-Pakete handelt.
\ospsection{Automatisierung von rcc und uic}{Automatisierung von rcc und uic}{sec:rccuicautomatisierung}
\index{rcc}%
\index{uic}%
\index{Projektverwaltung}%
\index{Projektverwaltung!Automatisierung}%
In der Projektstruktur finden sich bisher zwar die Dateien
\ospfile{helloworld.ui} und \ospfile{helloworld.qrc} für die GUI und
die Ressourcen, allerdings müssen diese noch mit den Tools
\ospcmd{uic} und \ospcmd{rcc} in Python-Code umgewandelt werden (s.
Kapitel \ref{chap:qttools}). Erst dann kann die Hauptfensterklasse die
GUI und das Bild aus den Ressourcen laden. Nun kann man beispielsweise
weiterhin \ospcmd{pyuic4} und \ospcmd{pyrcc4} für jede einzelne UI-
und Ressourcendatei von Hand aufrufen. Bei nur zwei Dateien in unserem
Beispielprojekt ist das kein Problem. Bei häufigen Änderungen in
diesen Dateien wird aber selbst das auf Dauer mühsam; wenn dann noch
weitere Dateien hinzukommen, wird man sich schnell eine alternative
Lösung wünschen.
Unter C++ übernimmt diese Arbeit weitgehend der Qt Creator, der anhand
der Projektdatei alle GUI- und Ressourcendateien automatisch in C++
wandelt und im Hintergrund die Tools startet. Unter Python kann man
sich eine vergleichbare Lösung per Shell-Skript oder
Windows-Batch-Skript zusammenstellen. Ähnlich arbeitet die hier
präsentierte Lösung, allerdings auf Basis eines Python-Skripts, so
dass die Entwicklung plattformunabhängig bleibt: Man ruft einfach das
Skript auf, und schon ist der Code wieder auf dem aktuellen Stand.
In diesem Fall ist es das Ziel, die so erzeugten Dateien in
\ospfile{src/hellworld/ui} abzulegen. Das folgende Skript
\ospfile{generate\_ui.py} übernimmt das:
\begin{osplisting}{Python}{Erzeugung von Python-Code für GUI und Ressourcen}{code:generateui}
# -*- coding: utf-8 -*-
import sys, os
from subprocess import call
if sys.platform == "win32":
bindir = "c:/Python27/Lib/site-packages/PyQt4/bin"
else:
bindir = "/usr/bin"
uic_path = os.path.join(bindir, "pyuic4")
rcc_path = os.path.join(bindir, "pyrcc4")
ui_path = "ui"
rc_path = ""
out_path = "src/helloworld/ui"
ui_files = { "helloworld.ui": "ui_helloworld.py" }
rc_files = { "helloworld.qrc": "helloworld_rc.py" }
for file in ui_files:
file_path = os.path.join(ui_path, file)
out_file_path = os.path.join(out_path, ui_files[file])
call([uic_path, file_path, "-o", out_file_path])
for file in rc_files:
file_path = os.path.join(rc_path, file)
out_file_path = os.path.join(out_path, rc_files[file])
call([rcc_path, file_path, "-o", out_file_path])
\end{osplisting}
Zunächst definiert das Skript die Pfade zu den beiden Tools
\ospcmd{pyuic4} und \ospcmd{pyrcc4}. Je nach Betriebssystem liegen
diese schon einmal in einem anderen Verzeichnis. Falls Sie eine andere
Python-Version oder PySide statt PyQt verwenden, müssen Sie die Pfade
in der Variablen \ospcmd{bindir} bzw. die aufzurufenden Tools in
\ospcmd{uic\_path} und \ospcmd{rcc\_path} anpassen. Danach erfolgt
die Deklaration der Eingabe- und Ausgabepfade. In diesem Fall liegen
alle GUI-Dateien im Verzeichnis \ospfile{ui}, die Ressourcendatei
liegt im Hauptordner des Projekts. Die beiden Variablen
\ospcmd{ui\_path} und \ospcmd{rc\_path} verweisen auf diese Pfade.
Alle Ausgabedateien sollen schließlich im Verzeichnis
\ospfile{src/helloworld/ui} abgelegt werden, das in der Variablen
\ospcmd{out\_path} definiert wird.
Anschließend folgt die Deklaration zweier \ospcmd{dict()} Variablen:
\ospcmd{ui\_files} und \ospcmd{rc\_files}. Sie enthalten eine Liste
aller Eingabedateien, jeweils mit einem Namen der Ausgabedatei als
Wert. Die Datei \ospfile{helloworld.ui} wird also hier per
\ospcmd{pyuic4} in eine Datei \ospfile{ui\_helloworld.py} umgewandelt
und im Ausgabeordner abgelegt. Die beiden \ospcmd{dict()} werden dann
in den beiden Schleifen durchlaufen und somit jede der definierten
Dateien bearbeitet und umgewandelt. Starten Sie dieses Skript bei
jeder Änderung an den \ospcmd{.ui}- und \ospcmd{.qrc}-Dateien des
Projekts. Wenn Sie neue Dateien hinzufügen, sollten Sie nicht
vergessen, das Skript zu aktualisieren und die neuen Dateien
einzutragen.
Das Skript lässt sich in Sachen Komfort sicher noch verbessern. So
könnten z.\,B. die \ospcmd{.ui}-Dateien automatisch aus dem
Verzeichnis \ospfile{ui} ausgelesen werden, auch die Namen der
Ausgabedateien lassen sich automatisch bestimmen. Gleiches gilt für
Ressourcendateien. Sie müssten dann nicht die Liste mit den Dateinamen
pflegen -- das Skript sucht sie sich automatisch.
\ospsection{Erstellung von Quelltext-Paketen}{Erstellung von Quelltext-Paketen}{sec:sourcepakete}
\index{Anwendung!Paketerstellung}%
\index{Paketerstellung}%
\index{Paketerstellung!Quelltext-Paket}%
\index{Paketerstellung!Paket installieren}%
Aus der bisherigen Beispielanwendung wollen wir nun ein
Quellcode-Paket erstellen, z.\,B. um es als Download im Internet
anzubieten oder zum Python Package
Index\ospfootnote{fn:pypi}{\ospurl{http://pypi.python.org/pypi}}, dem
globalen Repository für Python-Bibliotheken, hinzuzufügen. Dabei wird
der gesamte Quellcode in ein komprimiertes Archiv gepackt. Den
Kompressor bestimmt der Entwickler; standardmäßig wird unter Linux ein
\ospfile{.tar.gz}-Archiv erzeugt, unter Windows hingegen ein
\ospfile{.zip}-Archiv. Ein Anwender, der ein solches Paket
herunterlädt, kann es unter Linux dann mit den folgenden Befehlen auf
seinem Rechner installieren und starten:
\begin{ospsimplelisting}
$ <bf>tar xzf hellworld-1.0.tar.gz</bf>
$ <bf>cd helloworld-1.0</bf>
$ <bf>python setup.py install</bf>
$ <bf>helloworld</bf>
\end{ospsimplelisting}
Hier sollen alle anwendungsspezifischen Module und Pakete in das
systemweite Python-Modul-Verzeichnis installiert werden, die
ausführbare Anwendung wird bei Source-Paketen normalerweise in das
Verzeichnis \ospfile{scripts} der Python-Installation kopiert. Die
Pfade variieren je nach Betriebssystem und Python-Version. Unter
Ubuntu befindet sich das Paket-Verzeichnis von Python 2.6 etwa unter
\ospfile{/usr/local/lib/python2.6/dist-packages/}.\osplinebreak{}
Skripte werden nach \ospfile{/usr/local/bin} installiert. Unter
Windows gibt es meist ein Verzeichnis \ospfile{lib/site-packages} für
Pakete und ein Verzeichnis \ospfile{scripts} für ausführbare Skripte
direkt im Verzeichnis der Python"=Installation (etwa
\ospfile{C:\textbackslash{}Python26}).
\label{setuppy}
\index{Setup}%
Um nun das Source-Paket erzeugen zu können, benötigt man eine Datei
\ospfile{setup.py}. Sie nutzt die sogenannten \emph{Python Distutils}
zur Erzeugung distributionsfähiger Pakete. Alle weiteren Pakete für
Linux und Windows werden später auch auf Basis der Angaben in
\ospfile{setup.py} erstellt, so dass diese Datei zwingend notwendig
ist, auch wenn Sie kein Quelltext-Paket anbieten wollen. Das
vollständige Skript ist nicht besonders lang und enthält neben Namen
und Beschreibung von Anwendung und Autor nur die Angaben zu den zu
installierenden Paketen und Skripten:
\begin{osplisting}{Python}{Die Datei setup.py}{code:setuppyproject}
# -*- coding: utf-8 -*-
from distutils.core import setup
setup(
name = "helloworld",
version = "1.0",
description = "Hello World Software",
author = "Peter Bouda",
author_email = "pbouda@dasskript.com",
url = "http://www.dasskript.com",
package_dir = { "":"src" },
packages = [ "helloworld', "helloworld.ui" ],
scripts = [ "bin/helloworld" ],
long_description = "Hello World is a software that greets the world ...",
)
\end{osplisting}
Die Versionsnummer sollte natürlich nach jeder Veröffentlichung
angepasst werden. Für die Installation der Anwendung sind hier
folgende drei Variablen wichtig:
\begin{ospdeflist}
\ospdefitem{\ospcmd{package\_dir}}{Bei uns liegen die
Python-Pakete \ospcmd{helloworld} und \ospcmd{helloworld.ui} in
einem Unterverzeichnis \ospfile{src}. Die Angabe in
\ospcmd{package\_dir} teilt den distutils mit, dass die Pakete aus
\ospfile{src} direkt in das Python-Pakete-Verzeichnis installiert
werden sollen (darum der leere String als dict-Schlüssel) und
nicht in ein Unterverzeichnis. Ohne diese Angabe würde im
Python-Pakete-Verzeichnis auch ein Unterverzeichnis \ospfile{src}
angelegt.}
\ospdefitem{\ospcmd{packages}}{Diese Variable enthält eine Liste
aller zu installierenden Pakete, in unserem Fall nur die beiden
Pakete \ospcmd{helloworld} und \ospcmd{helloworld.ui}.}
\ospdefitem{\ospcmd{scripts}}{Eine Liste aller ausführbaren Skripte,
also die eigentlichen Anwendungsstarter. In unserem Fall ist das
nur die Datei \ospfile{bin/helloworld}.}
\end{ospdeflist}
Das Quelltext-Paket generieren Sie nun mit einem einfachen Befehl. Sie
müssen sich dazu im Hauptverzeichnis der Anwendung befinden:
\begin{ospsimplelisting}
$ <bf>python setup.py sdist</bf>
\end{ospsimplelisting}
Es folgen eine Reihe von Ausgaben des Prozesses in der Konsole. Das
fertige Paket wird im Verzeichnis \ospfile{dist} abgelegt, je nach
Betriebssystem (also Kompressor) mit einer anderen Dateiendung. In
unserem Fall liegt unter Linux dort die Datei
\ospfile{helloworld-1.0.tar.gz}, die mit den oben genannten Befehlen
installiert werden kann. Die Verwendung alternativer
Kompressionsverfahren und anderer Einstellungen beschreibt die
Dokumentationsseite der Python-Distribution.\ospfootnote{fn:pydoku}{Für Python
2.7 z.\,B.
\ospurl{http://docs.python.org/distutils/sourcedist.html}.}
Beachten Sie, dass das Quelltext-Paket nur die
Python-Quellcode-Dateien enthält, also weder die \ospfile{.ui}-Dateien
des Qt Designer noch die Ressourcendatei. Das Paket eignet sich also
\emph{nicht} zur Weiterentwicklung der Anwendung, sondern erlaubt es
dem Anwender lediglich, die Quelltext-Version auf seinem Rechner zu
installieren und zu benutzen. Natürlich können die ausgelieferten
Quelltext-Dateien beliebig editiert und betrachtet werden, aber gerade
die automatisch erzeugten Dateien des Pakets \ospcmd{helloworld.ui}
eignen sich kaum zur weiteren Code-Pflege. Der Anwender bekommt eben
alles, was er zum Start der Anwendung braucht -- und nicht mehr.
\ospsection{Erstellung von Linux-Paketen}{Erstellung von Linux-Paketen}{sec:linuxpakete}
\index{Paketerstellung!Linux}%
Die wichtigsten Vorbereitungen für die Erstellung eines Linux-Pakets
wurden mit dem Quelltext-Paket schon getroffen. Wenn Sie ein
Quelltext-Paket installieren und Ihre Anwendung starten können, ist
der Weg zu einem \ospcmd{.rpm}- oder \ospcmd{.deb}-Paket für die
gängigen Linux-Distributionen nicht mehr weit. Neben der
Installationsmöglichkeit soll unter Linux aber noch ein Eintrag im
Anwendungsmenü entstehen.
Ein solcher Eintrag erfordert unter den meisten Distributionen bzw.
Fenster-Managern eine sogenannten Desktop-Datei. In der oben
beschriebenen Projektstruktur liegt eine solche als
\ospfile{helloworld.desktop} im Unterverzeichnis \ospfile{data}. Die
Datei beschreibt den Menüeintrag und kann lokalisierte Strings für
verschiedene Oberflächensprachen enthalten. In unserem Beispiel sieht
die Datei folgendermaßen aus:
\begin{ospsimplelisting}
[Desktop Entry]
Name=Hello World
Name[de]=Hallo Welt
Comment=Hello World is a software that greets the ...
Comment[de]=Hallo Welt ist eine Software, die die ...
Exec=helloworld
Terminal=false
Type=Application
Categories=Utility;Qt
Encoding=UTF-8
Version=1.0
\end{ospsimplelisting}
Der Eintrag \ospcmd{Exec} ist die ausführbare Datei, die in unserer
Projektstruktur unter \ospfile{bin/helloworld} liegt. Sie muss im
\ospcmd{PATH} auffindbar sein. Unter Ubuntu installieren wir die
Anwendung später unter \ospcmd{/usr/bin}, so dass die Anwendung mit
dieser Desktop-Datei gestartet werden kann. Als Anwendungstyp
definieren wir \ospcmd{Application}, die Kategorien sind hier etwas
willkürlich als \ospcmd{Utility} und \ospcmd{Qt} gesetzt, da unsere
Anwendung ja im Prinzip nichts tut. Auch bei dieser Datei sollten Sie
später nicht vergessen, die Versionsnummer in Ihrer eigenen Anwendung
immer aktuell zu halten.
Neben den hier aufgeführten Angaben sollte man in den meisten Fällen
noch ein Icon für die Anwendung installieren. Es muss als Grafikdatei
vorliegen und entsprechend zum Paket hinzugefügt werden. Die
Beschreibung des Icons und aller anderen möglichen Angaben in einer
Desktop-Datei finden Sie bei
freedesktop.org.\ospfootnote{fn:freedesktop}{\ospurl{http://standards.freedesktop.org/desktop-entry-spec/desktop-entry"=spec-latest.html}}
Die Existenz einer Desktop-Datei in der Projektstruktur reicht noch
nicht aus; wir müssen den distutils mitteilen, wohin diese Datei zu
installieren ist. Die Free Desktop
Initiative\ospfootnote{fn:freedesktopurl}{\ospurl{http://www.freedesktop.org/wiki/}}
sieht vor, dass die Desktop-Dateien für das Anwendungsmenü unter
\ospfile{/usr[/local]/share/applications} liegen, und die meisten
Distributionen unterstützen mittlerweile diesen Standard. Um unsere
Setup-Datei entsprechend anzupassen, fügen Sie folgenden Code nach den
\ospcmd{import}-Anweisungen und vor dem Aufruf von \ospcmd{setup()}
ein:
\begin{ospsimplelisting}
import os
target_dir = "share"
data_files = []
data_files += [
(os.path.join(target_dir, "applications"), ["data/helloworld.desktop"])]
\end{ospsimplelisting}
Die zusätzlich zu installierenden Dateien sind in einer Liste
\ospcmd{data\_files} hinterlegt. So können wir später weitere Dateien
hinzufügen (z.\,B. das Icon für den Menüeintrag), indem wir die Liste
erweitern. Jeder Eintrag der Liste besteht aus einem Tupel: Der erste
Teil ist das Zielverzeichnis (in unserem Fall
\ospfile{share/applications} -- das \ospcmd{/usr} oder
\ospcmd{/usr/local} wird distributionsspezifisch beim Erstellen des
Pakets ergänzt). Der zweite Teil des Tupels ist eine Liste von
Dateien, die in dieses Zielverzeichnis installiert werden sollen, in
unserem Fall ist das nur die Desktop-Datei. Schließlich müssen Sie dem
Aufruf von \ospcmd{setup()} einen Parameter \ospcmd{data\_files}
hinzufügen, beispielsweise in der Zeile nach
\ospcmd{long\_description}:
\begin{ospsimplelisting}
data_files = data_files
\end{ospsimplelisting}
\index{Lokalisierung!Übersetzungsdateien installieren}%
Alle Pakete werden dann auch die Dateien in \ospcmd{data\_files}
enthalten und bei der Installation in die entsprechenden
Zielverzeichnisse installieren. Über unterschiedliche Inhalte der
Variablen \ospcmd{data\_files} lassen sich sehr elegant auch
unterschiedliche Installationspakete für unterschiedliche
Betriebssysteme verwirklichen. Sie fragen beispielsweise die aktuelle
Plattform ab und setzen dann je nach Ergebnis einen anderen Zielpfad
für die Installation eines Icons. Ein anderes Beispiel ist die
Installation der Übersetzungsdateien, falls Sie diese nicht in die
Ressourcen aufnehmen wollen (weil beispielsweise mehrere Anwendungen
darauf zugreifen sollen und die Dateien sehr groß sind). Sie können in
diesem Fall mit folgenden Befehlen die Übersetzungsdateien in ein
eigenes Share-Verzeichnis, in diesem Fall unter
\ospfile{/usr/share/hellworld/locale}, installieren:
\begin{ospsimplelisting}
for qmfile in glob.glob("data/locale/*.qm"):
qmdir = os.path.dirname(qmfile).replace(
"data", target_dir + "/helloworld")
data_files.append((qmdir, [qmfile]))
\end{ospsimplelisting}
Unser Hauptanwendungsskript ist schon auf diesen Pfad vorbereitet.
Wenn Sie noch einmal das Skript von Seite
\pageref{code:mainapp1} durchgehen, sehen Sie, dass unter Linux
-- falls die Anwendung installiert wurde -- schon ein \ospcmd{chdir()}
in das \ospfile{share}-Verzeichnis erfolgt ist. Sie können also die
Übersetzungsdateien (oder andere Dateien, wie Bilder usw.) bequem aus
dem Dateisystem laden, indem Sie beispielsweise als Pfad
\ospfile{locale/helloworld\_\{0\}} statt dem Ressourcenzugriff
eintragen.
\ospsubsection{RPM-Pakete}{RPM-Pakete}{sec:rpmpakete}
\index{Paketerstellung!RPM}%
Die Erstellung von RPM-Paketen schließt sich nahtlos an die Erstellung
von Quelltext-Paketen an. Es werden immer zwei RPM-Pakete generiert:
Eines entspricht dem Quelltext-Paket in Struktur und Inhalt, das
zweite enthält die Pfade und Dateien, die bei einer Installation des
Quelltext-Pakets angelegt und kopiert würden. Das bedeutet, dass der
Inhalt des Pakets genau die Dateien und Verzeichnisse sind, die man
auf dem aktuellen System bei der Ausführung von \ospcmd{python
setup.py install} in das lokale Betriebssystem installieren würde.
Unter Ubuntu enthält das RPM-Paket demnach Dateien für den Pfad
\ospcmd{/usr/local/}, wie oben beschrieben.
\index{Paketerstellung!MANIFEST}%
Weitere Voraussetzung für die Paketerstellung ist eine Datei
\ospfile{MANIFEST.in}. Sie enthält eine Liste aller Dateien, die neben
dem Python-Code installiert werden sollen. Das entspricht in den
meisten Fällen den Dateien, die schon in \ospfile{setup.py} in der
Variablen \ospcmd{data\_files} eingetragen sind. In unserem Fall
enthält die Datei nur eine einzige Zeile:
\begin{ospsimplelisting}
include data/helloworld.desktop
\end{ospsimplelisting}
Aus der Datei generiert distutils die Datei \ospfile{MANIFEST} zur
Erzeugung der beiden RPM-Pakete, die eine vollständige Dateiliste,
also samt Python-Code-Dateien, enthält. Diese Datei wird jedes Mal neu
generiert. Für die Dateinamen des \ospcmd{include}-Befehls können Sie
auch Wildcards verwenden, um auf mehrere Dateien zu verweisen. Mit
\ospcmd{recursive-include} können Sie das Ganze rekursiv
erweitern.\ospfootnote{fn:sourcedistmanifest}{Siehe wiederum
\ospurl{http://docs.python.org/distutils/sourcedist.html}.}
Falls Sie ein Linux nutzen, das nicht mit RPM-Repositories arbeitet,
muss das entsprechende Paket zur Erstellung und Verwaltung von
\ospfile{.rpm}-Dateien installiert sein. Unter Ubuntu heißt es
schlicht \ospfile{rpm} und kann wie gewohnt über die Paketverwaltung
installiert werden.
Unter jeder Distribution rufen Sie zur Erstellung eines RPM-Pakets das
Skript \ospcmd{setup.py} mit folgenden Parametern auf:
\begin{ospsimplelisting}
$ <bf>python setup.py bdist_rpm</bf>
\end{ospsimplelisting}
Wiederum finden Sie die erzeugten Pakete im Unterverzeichnis
\ospfile{dist}. Für unsere Beispielanwendung enthält dieser Ordner
anschließend zwei Dateien: \ospfile{helloworld-1.0-1.src.rpm} enthält
die Dateien des Quelltext-Pakets,
\ospfile{helloworld-1.0-1.noarch.rpm} die Dateien zur
Installation der Anwendung in das Ziel-Linux-System.
\ospsubsection{Debian-Pakete}{Debian-Pakete}{sec:debpakete}
\index{Paketerstellung!Debian}%
Gegenüber der Erstellung eines RPM-Pakets ist die Erzeugung von
Debian-Paketen zwar deutlich komplizierter, dennoch stellen wir hier
einen ersten Ansatz vor. Die so erzeugten Pakete eignen sich dann
zwar, um für die meisten Benutzer schnell einmal ein Download-Paket
der Anwendung zu erzeugen. Um das Paket später aber in einer der
Distributionen unterzubringen, ist aber noch weitere Arbeit notwendig.
Am Ende dieses Kapitels gehen wir darauf noch einmal ein.
Für die Debian-Paketerstellung benötigt man zunächst einige
zusätzliche Pakete aus dem Repository der eingesetzten
Linux-Distribution, sofern diese nicht schon installiert sind. Für das
hier vorgestellte Beispiel verwenden wir das \emph{Common Debian Build
System}, das, grob gesagt, eine Schnittstelle zwischen den
debhelper-Werkzeugen und den Python distutils zur
Verfügung stellt. Somit basiert die Installation im Endeffekt wieder
auf den Angaben in \ospfile{setup.py}, aus denen der Inhalt des
Debian-Pakets zusammengestellt wird. Darüber hinaus benötigen Sie
die zum Kompilieren von Anwendungen nötigen Tools wie \ospcmd{make},
die meist in einem eigenen Paket zusammengefasst sind. Unter Ubuntu
müssen Sie beispielsweise die folgenden drei Pakete installieren:
\begin{osplist}
\item \ospcmd{devscripts}
\item \ospcmd{build-essential}
\item \ospcmd{cdbs}
\end{osplist}
Alles Weitere befindet sich dann im Unterverzeichnis \ospfile{debian}
unserer Anwendung. Das Verzeichnis enthält vier Dateien -- sie sind
die Mindestanforderung für das Paket. Dazu gehört zunächst
\ospfile{changelog}; darin sind alle Änderungen in den Quelltexten
kurz dokumentiert. Für die Bereitstellung in den gängigen Repositories
ist eine durchgängige Änderungshistorie zwingend erforderlich, sogar
für das erste Einstellen einer Anwendung muss mindestens ein Ticket
geschlossen werden. Im einfachsten Fall und ohne Kompatibilität zu
einem Repository enthält diese Datei nur einen Eintrag für das
\emph{Initial Release}:
\begin{ospsimplelisting}
helloworld (1.0-1) unstable; urgency=low
* Initial release
-- Peter Bouda <pbouda@dasskript.com> Mon, 9 Aug 2011 16:54:26 +0000
\end{ospsimplelisting}
Der Aufbau der Einträge ist immer gleich, wobei die einzelnen Einträge
mit einer Leerzeile voneinander getrennt sind. Die Versionsnummern
müssen laufend fortgeführt werden, und alle Änderungen werden mit
Sternchen aufgelistet. Auch das Datum jedes Eintrags sollte immer das
bei der Erstellung des Pakets aktuelle sein.
Darüber hinaus enthält das \ospfile{debian}-Verzeichnis die Datei
\ospfile{control}, die die Anwendung und ihre Abhängigkeiten näher
beschreiben:
\begin{ospsimplelisting}
Source: helloworld
Maintainer: Peter Bouda <pbouda@dasskript.com>
Section: misc
Priority: optional
Build-Depends: cdbs (>= 0.4.49), debhelper (>= 7), python (>= 2.6),
python-support, python-qt4
XS-Python-Version: >= 2.6
Standards-Version: 3.8.4
Homepage: http://www.dasskript.com
Package: helloworld
Architecture: any
Depends: ${misc:Depends}, python, python-qt4, ${python:Depends}
Description: Hello World is a software
that greets the world in a unique way.
\end{ospsimplelisting}
Für Python, PyQt und PySide sind die Einträge unter
\ospcmd{Build-Depends} und \ospcmd{Depends} wichtig und müssen
gegebenenfalls angepasst werden. Hier stehen alle Debian-Pakete, die
für das Bauen bzw. Starten der Anwendung notwendig sein. Falls es auf
eine Versionsnummer ankommt, steht diese hinter dem Paketnamen in
Klammern. Der Eintrag \ospcmd{XS-Python-Version} existiert nur für
Python-Anwendungen und entspricht der Python-Versionsnummer, die für
das Starten der Anwendung Voraussetzung ist. In unserem Fall benötigt
die Anwendung mindestens die Python-Version 2.6 und das Paket
\ospfile{python-qt4}, um gestartet werden zu können. Für die
Paketerstellung sind außerdem die weiteren in \ospcmd{Build-Depends}
gelisteten Pakete erforderlich.
Als \ospcmd{Architecture} lassen wir es schlicht bei \ospcmd{any}, da
die Anwendung dank Python und PyQt ja plattformunabhängig ist. Der
Eintrag \ospcmd{Section} ist für die Verwalter des Repositorys
wichtig, da er die Anwendung einem Themengebiet zuordnet. Die Liste
der Gebiete ist lang und kann unter dem unten angegebenen Link zur
Python-Debian-Paketerstellung abgerufen werden. Die Angabe
\ospcmd{Standards-Version} beschreibt, nach welcher Version des
Debian-Paket-Standards die Anwendung und die Paketierungsanweisungen
aufgebaut sind. Schauen Sie im Zweifelsfall im Internet nach, welche
die aktuelle Versionsnummer ist. Der Name und die Beschreibung der
Anwendung runden die Angaben ab.
Die Datei \ospfile{copyright} im \ospfile{debian}-Ordner
enthält eine Beschreibung der Lizenz der Anwendung in einem
maschinenlesbaren Format, kann aber auch erst einmal leer bleiben.
Relevant wird der Inhalt dieser Datei erst dann, wenn Sie das Paket in
ein Repository hochladen.
\index{Make-Datei}%
Schließlich enthält das Verzeichnis die Datei \ospfile{rules}, die die
eigentlichen Anweisungen zum Bau des Pakets umfasst. Es entspricht
etwa einer Make\-file-Datei, nur eben für die Paketerstellung. Hier
steht, welche Schritte\osplinebreak{} durchgeführt werden müssen, um
die Anwendung zu kompilieren, zu bereinigen, usw. Für eine
C++-Anwendung auf Basis von Qt würde hier unter anderem der Aufruf von
\ospcmd{qmake} stehen. In unserem Fall nimmt uns das \emph{Common
Debian Build System} die meiste Arbeit ab; die Datei \ospcmd{rules}
beschränkt sich darum mehr oder weniger auf das Einbinden der zur
Verfügung gestellten \ospfile{make}-Skripte:
\begin{ospsimplelisting}
#!/usr/bin/make -f
DEB_PYTHON_SYSTEM=pysupport
include /usr/share/cdbs/1/rules/debhelper.mk
include /usr/share/cdbs/1/class/python-distutils.mk
\end{ospsimplelisting}
\index{python-support}%
Die Variable \ospcmd{DEB\_PYTHON\_SYSTEM} verweist auf
\ospcmd{pysupport}. Das bedeutet, dass das Paket
\ospcmd{python-support} zur Erstellung unseres Pakets verwendet wird.
Als Alternative existiert das Paket \ospcmd{python-central}, das die
gleiche Aufgabe übernimmt. Hier müsste die Variable den Wert
\ospcmd{pycentral} haben.
\index{debuild}%
\index{lintian}%
Ist die Projektstruktur vervollständigt, dient nun \ospcmd{debutil}
der Erstellung des Debian-Pakets. Dazu rufen Sie im Hauptverzeichnis
der Anwendung folgenden Befehl auf, um ein unsigniertes,
binäres Paket zu bauen und gleich vom Tool \ospcmd{lintian} auf
Kompatibilität zum Debian-Repository überprüfen zu lassen:
\begin{ospsimplelisting}
$ <bf>debuild -i -us -uc -b</bf>
\end{ospsimplelisting}
Der Parameter \ospcmd{-b} erzeugt das binäre Paket (das aber dennoch
den Python-Code enthält, analog zum RPM-Installationspaket), die
beiden \ospcmd{-u}"=Parameter verhindern das Signieren von
Quelltext-Paketen und binären Paketen (schließlich haben wir keinen
Schlüssel generiert), der Parameter \ospcmd{-i} ruft schließlich
\ospcmd{lintian} nach erfolgreicher Paketerstellung auf. Bei
\ospcmd{lintian} handelt es sich um ein Tool, das das Paket schon
einmal auf alle formalen Bedingungen für das Hochladen in ein
Repository untersucht. Grundsätzlich werden dabei Verstöße gegen die
Debian Policy gemeldet, so dass der Entwickler diese formalen Fehler
schon vor dem Einreichen des Pakets beheben kann. Wir werden gleich
noch auf die formalen Fehler in unserem Paket zurückkommen.
Nach Aufruf von \ospcmd{debuild} folgt eine Reihe von Ausgaben in der
Konsole; anschließend werden nach erfolgreicher Paketerstellung im
übergeordneten Verzeichnis drei Dateien abgelegt:
\begin{osplist}
\item \ospfile{helloworld\_1.0-1\_amd64.deb}
\item \ospfile{helloworld\_1.0-1\_amd64.changes}
\item \ospfile{helloworld\_1.0-1\_amd64.build}
\end{osplist}
Alle drei Dateien haben in diesem Fall das Suffix \ospfile{\_amd64} im
Dateinamen, da sie auf einem 64-Bit-Linux erzeugt wurden. Die
\ospfile{.build}-Datei ist eine Log-Datei mit der Ausgabe während der
Paketerstellung. \ospfile{helloworld\_1.0-1\_""amd64.changes} enthält
den \ospcmd{changelog} samt einer Checksummme und Angaben zum Paket.
\ospfile{helloworld\_1.0-1\_amd64.deb} ist schließlich das fertige und
installierbare Debian-Paket, das Sie unmittelbar auf Ihrem Rechner
installieren können:
\begin{ospsimplelisting}
$ <bf>dpkg -i helloworld_1.0-1_amd64.deb</bf>
\end{ospsimplelisting}
Sie sollten dann auch gleich einen entsprechenden Eintrag in Ihrem
Anwendungsmenü finden. Zum Deinstallieren reicht ein:
\begin{ospsimplelisting}
$ <bf>dpkg -r helloworld</bf>
\end{ospsimplelisting}
Ein solches Paket kann, wie gesagt, noch nicht direkt in ein
Repository aufgenommen werden; dazu fehlen einige
Mindestvoraussetzungen. Da wir das Tool \ospcmd{debuild} mit der
Option \ospcmd{-i} aufgerufen haben, liefert \ospcmd{lintian} am Ende
der Paketerstellung gleich erste Hinweise auf
Verbesserungsmöglichkeiten:
\begin{ospsimplelisting}
Now running lintian...
W: helloworld: copyright-without-copyright-notice
W: helloworld: new-package-should-close-itp-bug
W: helloworld: binary-without-manpage usr/bin/helloworld
W: helloworld: zero-byte-file-in-doc-directory \
usr/share/doc/helloworld/copyright
Finished running lintian.
\end{ospsimplelisting}
\index{ITP-Bug}%
Es sollten also mindestens die Copyright-Datei gefüllt und eine
Dokumentationsseite (man page) hinzugefügt werden. Auch das Changelog
erfüllt nicht die notwendigen Voraussetzungen, da kein \emph{ITP-Bug}
(Intent To Package) als geschlossen beschrieben ist. Auf all diese
Dinge kann und soll hier nicht näher eingegangen werden. Unter den
beiden folgenden Links finden Sie wichtige Informationen zur
Erstellung von Ubuntu-Paketen sowie die vollständige Spezifikation zur
Erstellung von Debian-Paketen aus Python-Anwendungen:
\begin{ospsimplelisting}
https://wiki.edubuntu.org/PackagingGuide/Python
http://www.debian.org/doc/packaging-manuals/python-policy/index.html
\end{ospsimplelisting}
Mit Hilfe dieser Beschreibungen können Sie Ihre Anwendung vollständig
auf die Veröffentlichung in Debian und darauf basierenden
Linux"=Distributionen vorbereiten. Übrigens: Ubuntus
Launchpad\ospfootnote{fn:launchpad}{\ospurl{https://launchpad.net/}}
stellt eine immer beliebter werdende Plattform für Python-Anwendungen
unter Ubuntu dar. Mit einem eigenen \emph{Personal Package Archive}
(PPA) ist die eigene PyQt- oder PySide-Anwendung auch schnell an den
(Ubuntu-)Benutzer gebracht, der dann automatisch über die gewohnte
Softwareaktualisierung mit Updates versorgt wird.
\ospsubsection{Debian-Paketerstellung mit PySide-Assistant}{Alternative Debian-Paketerstellung mit PySide-Assistant}{sec:pysideassistant}
\index{PyQt vs. PySide}%
\index{PySide Assistant}%
\index{Paketerstellung!Debian}%
Schon den kurzen Ausführungen ist zu entnehmen, dass die