Dokumentation im PDF-Format

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Nobuddy
User
Beiträge: 1019
Registriert: Montag 30. Januar 2012, 16:38

Hallo zusammen!

Für ein von mir geschriebenes Programm, möchte ich nun auch die dazu gehörige Dokumentation schreiben.
Mein Betriebssystem ist Linux Kubuntu 14.04.
Die Dokumentation schreibe ich OpenOffice Writer und erstelle Hyperlinks vom Inhaltsverzeichnis zum Dokument.
Anschließend, erstelle ich über OpenOffice Writer eine PDF-Datei.
Öffne ich die PDF-Datei, funktioniert das mit den Hyperlinkś dort ohne Probleme.

Nun stelle ich mir die Frage, ob und wie es möglich ist, aus meinem Python-Programm, direkt auf bestimmte Hyperlinkś (Textpassagen) in der PDF-Datei zu kommen?

Ich würde mich freuen, wenn Ihr mir dabei helfen könntet und bin über jeden Input von Euch dankbar!

Grüße Nobuddy
BlackJack

@Nobuddy: Das ist letztendlich eine Frage der Software die zum Anzeigen des PDF-Dokuments verwendet wird und ob die so ein springen zu einem Ziel irgendwie von aussen steuerbar macht. Und ob man die Ziele irgendwie identifizieren und den Programmfunktionen zuordnen kann. Bei Okular kann man per D-Bus steuern welche Seite angezeigt wird.

Letztendlich würde ich mal sagen dass das keine gute Idee ist. Ein Hinweis darauf ist, dass das meines Wissens kein Programm macht.

Welches GUI-Toolkit verwendest Du denn für das Programm? Viele bieten irgendeinen Mechanismus um Online-Hilfe zu integrieren. Eine Textverarbeitung ist auch nicht unbedingt das übliche Werkzeug. Bei Python würde sich beispielsweise Sphinx anbieten. Da kann man PDFs, HTML, ePub, und noch ein paar andere Formate aus dem selben Quelltext erzeugen.
Nobuddy
User
Beiträge: 1019
Registriert: Montag 30. Januar 2012, 16:38

Hallo BlackJack,

das mit der Textverarbeitung und dem umwandeln in ein PDF-Format, war für mich die einfachste Lösung, auch wenn im Nachhinein das nicht zum Ziel führt, wie Du das mir erklärt hast.

Ich verwende als GUI-Toolkit, tkinter.
Das mit Sphinx, werde ich mir anschauen!

Was würdest Du sonst noch, für eine einfache Handhabung vorschlagen?

Grüße Nobuddy
Nobuddy
User
Beiträge: 1019
Registriert: Montag 30. Januar 2012, 16:38

Also das mit Sphinx, ist nicht so einfach, da leider alles in englischer Dokumentation vorliegt und ich .... :?

Gibt es da nichts in deutscher Dokumentation?
Benutzeravatar
noisefloor
User
Beiträge: 4187
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

im Wiki von ubuntuusers.de gibt's (natürlich) einen Artikel zu Sphinx. Da werden auch so die minimalen Grundlagen erklärt.

Sphinx ist im Python-Umfeld ziemlich populär, d.h. die Chancen stehen auch gut, dass du zumindest ein paar deutsche Blogpost dazu findest.

Sphinx hat nebenbei auch noch den Vorteile, dass man mit Sphinx geschriebene Dokus auch (kostenfrei) online bei readthedocs.org online stellen kann.

Gruß, noisefloor
Nobuddy
User
Beiträge: 1019
Registriert: Montag 30. Januar 2012, 16:38

Hallo zusammen,

habe mich jetzt doch zu einer eher 'unorthodoxen' Lösung entschieden.

Wie schon zuvor beschrieben, schreibe ich meine Dokumentation mit OpenOfficeWriter und exportiere dies dann als PDF-Datei.
Bei allen Überschriften, habe ich dahinter einen Tabulator, gefolgt mit dem Feldbefehl Seite, ein Leerzeichen und anschließenden Text '#SEITE' gesetzt.

Beispiel: Das ist eine Überschrift<TAB>3 #Seite

Damit es in dem Dokument nicht komisch aussieht, habe ich die nachfolgende Information nach der Überschrift , unsichtbar gemacht.
Um nun diese Information auswerten zu können, erstelle ich eine Kopie Dokumentation.txt aus der Dokumentation.odt.
Diese Dokumentation.txt durchsuche ich nun nach der Information '#SEITE' und erstelle daraus eine Datei, mit der Seitenangabe und der Überschrift.

In meinem Programm, gibt es Master- und Sub-Buttons, deren Namen identisch mit den Überschriften aus der Dokumentation sind.
Mit einem Dictionary, wird die Position der Buttons ermittelt und die dazu gehörige Dokumentation bereitgestellt, die über die Hilfe oder F1 bei Bedarf abgerufen werden kann.

Grüße Nobuddy
Benutzeravatar
noisefloor
User
Beiträge: 4187
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

verstehe ich nicht ganz... Wie und wo wird die Doku denn jetzt bereitgestellt, wenn man so einen Sub-Button drückt?

Gruß, noisefloor
Nobuddy
User
Beiträge: 1019
Registriert: Montag 30. Januar 2012, 16:38

Hallo noisefloor,

das drücken eines Master- oder Sub-Button, ruft nicht automatisch die Doku auf.
Vielmehr wird der Status der Master- oder Sub-Button in einer Variablen hinterlegt.

Beispiel:

Code: Alles auswählen

self.help_position = self.masterbutton, self.subbutton
Die aus der 'Dokumentation.txt' erstellten Datei 'HELP_LISTING.txt', mit der Seitenangabe und der Überschrift, dient dazu die richtige Position in der 'Firmware-Dokumentation.pdf' zu finden.

Beispiel 'HELP_LISTING.txt':
  • 2 Kunden
    3 Lieferanten
    4 Kunden - Startseite - Einführung
    5 Kunden - Neuanlage
    7 Kunden - Kunden - Stammdaten
    8 Kunden - Kunden - Kommunikation
Spalte 0 = Seitenzahl in 'Firmware-Dokumentation.pdf'
Spalte 1 = Überschrift

Gibt es eine zusätzliche Info in der 'HELP_LISTING.txt', wie z.B.:
  • 4 Kunden - Kunden - Stammdaten
,dann wird in der aktuellen Funktion, self.help_position aktualisiert.

Code: Alles auswählen

self.help_position = self.masterbutton, self.subbutton, 'Stammdaten'
Erstellen Dictionary aus 'HELP_LISTING.txt':

Code: Alles auswählen

page = 0
content = 1
mydict = dict()
for x in mylist:
    line = list()
    for y in x[content].split(' - '):
        line.append(y)
    mydict.update({tuple(line) : x[page]})
mylist, bezieht sich auf die geladene Liste aus 'HELP_LISTING.txt'.

Das Ausgabeergebnis bei Aufruf Menü Hilfe oder <F1>, ist wie folgt:

Code: Alles auswählen

page = self.mydict.get(tuple(self.help_position))
if not page:
    for key in sorted(self.mydict):
        check = ''.join(key)
        if check.startswith(''.join(self.content)):
            page = self.mydict[key]
            break
if page:
    # HELP_LISTING.txt, help_listing
    filelist = 'help'
    output = '{} {}'.format(Path(filelist), page)
    subprocess.Popen(['xpdf', Path(filelist), page])
'Firmware-Dokumentation.pdf' wird mit xpdf in Position page geöffnet.

Grüße Nobuddy
Zuletzt geändert von Nobuddy am Freitag 28. November 2014, 10:37, insgesamt 1-mal geändert.
Benutzeravatar
noisefloor
User
Beiträge: 4187
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

ok, Danke. Aus dem vorherigen Post ging nämlich nicht hervor, dass die explizit auf xpdf als PDF-Viewer setzt. Auch wenn ich es mir fast gedacht habe ;-)

Gruß, noisefloor
Nobuddy
User
Beiträge: 1019
Registriert: Montag 30. Januar 2012, 16:38

Dachte mir, daß dem noch ein bischen mehr Information bedarf. :wink:

xpdf ist der einzige PDF-Reader, so wie ich es bis jetzt festgestellt habe, bei dem eine Seiten-Position mit übergeben werden kann.
Mit dem Standard okular bei KDE, wäre es mir zwar lieber gewesen, habe da aber keine Möglichkeit gefunden, dies so umzusetzen.

PS: Habe noch kurz vor Deinem Post, noch in Bezug auf self.help_position, dies etwas erweitert!

Grüße Nobuddy
BlackJack

@Nobuddy: Das ist aber mal wieder teilweise umständlich gruseliger Code.

Was soll denn zum Beispiel das hier bewirken…

Code: Alles auswählen

    line = list()
    for y in x[content].split(' - '):
        line.append(y)
…was ``line = x[content].split(' - ')`` nicht schon liefert!?

Und `update()` ist die falsche Methode wenn man da grundsätzlich ein neues Wörterbuchobjekt erzeugt das immer nur *ein* Schlüssel/Wert-Paar enthält. Das ist doch deutlich komplizierter als einfach dem Schlüssel einen Wert zuzuweisen.

Magische Indizes an Namen zu binden ist ja schon mal ganz nett, aber besser wäre es da richtige Namen draus zu machen, also zum Beispiel Attributnamen auf einem `collections.namedtuple`-Typ oder man bindet die einzelnen Elemente direkt in der Schleife an passende Namen.

`x`, `y`, `mylist` und `mydict`? Die Namen sind ja total nichtssagend.

Da würde letztlich so etwas übrig bleiben:

Code: Alles auswählen

path2pagenumber = dict()
for page_number, path in rows:
    path2pagenumber[tuple(path.split(' - '))] = page_number
Oder direkt als ein Ausdruck:

Code: Alles auswählen

path2pagenumber = dict(
    (tuple(path.split(' - ')), page_number) for page_number, path in rows
)
Benutzeravatar
snafu
User
Beiträge: 6862
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

BlackJack hat geschrieben:Oder direkt als ein Ausdruck:

Code: Alles auswählen

path2pagenumber = dict(
    (tuple(path.split(' - ')), page_number) for page_number, path in rows
)
Oder als Dictionary Comprehension (ungetestet):

Code: Alles auswählen

path2pagenumber = {
    tuple(path.split('-')): page_number
    for page_number, path in rows
}
Zuletzt geändert von snafu am Freitag 28. November 2014, 12:37, insgesamt 1-mal geändert.
Benutzeravatar
noisefloor
User
Beiträge: 4187
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

wenn's kein Nachteil ist, dass es "nur" mit xpdf läuft, ist das vorgehen prinzipiell ok. Nur funktioniert der Code halt auf keinem System, wo xpdf nicht installiert ist.

IMHO bietet es sich an, die Doku als HTML Dokument zu liefern. Das öffnest du dann mit dem webbrowser-Modul, was cross-platform läuft.

Und wir wären dann wieder bei Sphinx, weil das gleichermaßen die Doku nach HTML wie auch PDF rendern kann. HTML via Sphinx hat noch den Vorteil, dass du ein schickes Navi-Menü dazu bekommt. Und die Anker auch direkt gesetzt werden.

Gruß, noisefloor
Nobuddy
User
Beiträge: 1019
Registriert: Montag 30. Januar 2012, 16:38

Hallo zusammen und Danke für Euren Input! :wink:

@BlackJack, mit gruselig meinst Du wohl etwas umständlich.
Daß 'x[content].split(' - ')' das Gleiche liefert wie:

Code: Alles auswählen

        line = list()
        for y in x[content].split(' - '):
            line.append(y)
, da war ich mir nicht sicher ... deshalb habe ich den umständlichen Code gepostet.

Mit 'update()', hast Du völlig recht! Habe mich da vielleicht schon zu arg an dies gewöhnt ... :wink:
Das mit dem
...... aber besser wäre es da richtige Namen draus zu machen, also zum Beispiel Attributnamen auf einem `collections.namedtuple`-Typ oder man bindet die einzelnen Elemente direkt in der Schleife an passende Namen.
Vielleicht könntest Du mir zu 'Attributnamen' und `collections.namedtuple` kurz etwas posten, daß mir das klar wird?

Zu '`x`, `y`, `mylist` und `mydict`'!
x und y, sind für mich einfach kürzer im Code, als row usw.
mylist und mydict habe ich der Einfachheit verwendet, in meinem Code haben die schon bestimmte Namen, wie self.help_position und self.help_listingdict. Kann mir vorstellen, daß Du mit diesen Namen auch nicht einverstanden bist ... :wink:

Anhand Deiner zwei Beispiele in Deinem ersten Post und dem Post von snafu, verstehe ich Deine Rüge, was Du mir zuvor versucht hast mitzuteilen.
Danke für Euren Input, werde mir das aneignen! :wink:

Werde auch mal von snafu, dies 'Dictionary Comprehension (ungetestet)' testen!

@noisefloor, das mit xpdf ist momentan meine Lösung, mit Kubuntu 14.04 war dies kein Problem, xpdf nach zu installieren.
Das mit Sphinx ist noch nicht vom Tisch, brauche da aber mehr Zeit dazu, als die von mir aktuell umgesetzte Lösung. :wink:

Grüße an Alle
Nobuddy
BlackJack

@Nobuddy: Man sollte bei Namen nicht daran denken das die möglichst kurz sind und damit schneller geschrieben werden können, sondern ob die beim Lesen etwas taugen. Denn Quelltext wird deutlich öfter gelesen als geschrieben und *dann* ist es wichtig den möglichst leicht verstehen zu können und nicht möglichst wenige Zeichen zu haben. Und das ist einfacher wenn die Namen die Bedeutung widergeben die man direkt lesen kann und nicht kryptische Buchstabenkürzel die man immer im Kopf erst ”übersetzen” muss.
Nobuddy
User
Beiträge: 1019
Registriert: Montag 30. Januar 2012, 16:38

Hallo BlackJack,

habe die Bedeutung, von leicht verständlichem Code verstanden.
Werde dies bei meinen zukünftigen Posts beachten!

Habe mich ein wenig in Bezug auf < Attributnamen auf einem `collections.namedtuple` > umgeschaut.
Ist eine prima Sache, wenn man es richtig verstanden hat, aber dafür brauche ich noch ein wenig ... :wink:

Ich habe einfach etwas herumprobiert, um zu verstehen.
Habe dies mal probiert, ohne Class ... usw.

Code: Alles auswählen

from collections import namedtuple

Lieferant = namedtuple('lieferant','lieferant namen')
print(Lieferant)
Printausgabe 0: <class '__main__.lieferant'>
Wenn ich das richtige verstehe, habe ich hier Attributnamen für 'Lieferant' erstellt.
Ist das so richtig?

Code: Alles auswählen

L04711 = Lieferant('04711', 'Test GmbH')
print(L04711)
print(L04711.namen)
Hier weise ich 'Lieferant' Inhalte zu und binde die Inhalte zu 'L04711'.
Printausgabe 1: lieferant(lieferant='04711', namen='Test GmbH')
Printausgabe 2: Test GmbH

Ist das ein kleiner Anfang ind die richtige Richtung?

Grüße Nobuddy
Benutzeravatar
snafu
User
Beiträge: 6862
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Die Funktionsweise von Namedtuples hast du richtig erfasst. Das sind im Grunde eine Art Datenspeicher mit sprechenden Namen. Hilfreich sind sie, wenn man eine reine Datenstruktur benötigt, die ohne eigene Funktionalität auskommt. Wenn man dann aus einer solchen Struktur etwas abruft a la ``print(info.name)``, dann ist das halt lesbarer als ein ``print(info[2])`` oder als das umständlichere ``id, date, name = info; print(name)`` (jetzt nur als Beispiel gemeint).
Nobuddy
User
Beiträge: 1019
Registriert: Montag 30. Januar 2012, 16:38

Hallo zusammen,

wünsche Euch ein gutes neues Jahr! :wink:

Gleichzeitig möchte ich mich im Nachhinein, für Euren Input bedanken und habe mir diesen angeeignet.
Zur Lesbarkeit von Code, auf Bezug meiner Kürzel x .. y und Co, habe ich Abstand genommen und weise jetzt statt dessen Namen zu, die aussagekräftig sind.
Ich habe dies bemerkt, als ich älteren Code von mir wieder angefasst habe und es schwierig war, sich einzufinden.
Dies war auch ein weiterer Grund, die Empfehlung von BlackJack umzusetzen.

Das mit den namestuple, funktioniert inzwischen echt super und vereinfacht das Leben ungemein.
Was mir hier zugute kam, war daß ich schon lang zuvor, die Steuerung von Listen, Listennamen und Spaltennamen zentralisiert hatte und die Spaltennamen in den Listen vereinheitlicht hatte.

Grüße Nobuddy
Antworten