Seite 1 von 2

Aller Anfang ist schwer

Verfasst: Donnerstag 7. März 2013, 09:18
von TomL
Hallo ich bin Python Anfänger (vorher andere Sprachen) und habe mich in etlichen Tutorials + Buch (Ernesti/Kaiser) eingearbeitet. Ich bin anwendungsorientierter Programmierer, d.h. ich möchte/muss Lösungen für an mich herangetragene Aufgaben erarbeiten. Die Eleganz der Lösung oder sonstige berufsethische Eigenschaften der Lösung interessiert mich erst in zweiter Linie
Zum Beginn habe ich eine kleine Anwendung geschrieben, die einen Wareneingang (mit Stammdaten, Bewegungsdaten- und Lagerbestand) simuliert. Als externe Datenquelle benutze ich dort eine .csv Datei.
Da es in Python selbst keine wirklichen externen Dateizugriffsmethoden gibt (Read-Update-Delete) gibt (oder doch?), benutze ich dazu eine SQLite-Datenbank. Während der Verarbeitung werde Protokollsätze der verarbeiteten Lagerbewegungen sowohl in die Datenbank geschrieben als auch via print auf dem Monitor dargestellt. Nun würde ich dieses Protokoll aber auch gerne auf Papier bringen. Die Recherchen im Netz ergaben dazu eigentlich mehr Fragen als wirkliche Lösungsansätze, zumindest nicht für mich als Anfänger. In meinem dicken Buch steht dazu übrigens kein einziges Wort.
Gibt es wirklich kein Python-internes Standardverfahren zum Einrichten und Benutzen eines Druckers?
Mit dem Thema grafische Benutzeroberflächen habe ich mich noch nicht wirklich ernsthaft befasst, denn dazu fehlt es mir bisher an notwendigem Vorwissen um andere, für mich neue Aspekte dieser Sprache. Deshalb die Frage: Gibt es Werkzeug(e) zum Erstellen grafischer Oberflächen, mit Hilfe deren man intuitiv arbeiten kann und die "nebenbei" importfähigen Python-Code generieren?
Vielen Dank

Re: Aller Anfang ist schwer

Verfasst: Donnerstag 7. März 2013, 10:01
von lackschuh
TomL hat geschrieben:Gibt es wirklich kein Python-internes Standardverfahren zum Einrichten und Benutzen eines Druckers?
Unter Windows:

Code: Alles auswählen

#!/usr/bin/python
# -*- coding: utf-8 -*-

import tempfile
import win32api
import win32print

filename = tempfile.mktemp (".txt")
open (filename, "w").write ("This is a test")
win32api.ShellExecute (
  0,
  "printto",
  filename,
  '"%s"' % win32print.GetDefaultPrinter (),
  ".",
  0
)
Quelle

Re: Aller Anfang ist schwer

Verfasst: Donnerstag 7. März 2013, 11:01
von Sr4l
Gibt es wirklich kein Python-internes Standardverfahren zum Einrichten und Benutzen eines Druckers?
In der Python Std Lib gibt es keine cross platform Möglichkeit zum Drucken, du kannst jedoch über die GUI Toolkits die es für Python Drucken. Mit WxPython habe ich das schon gemacht, QT und GTK können das sicher ähnlich. Dabei kann der Benutzer Drucker und Seite einrichten.

Eine weitere Möglichkeit wäre sich ein Programm zu suchen welches drucken über die Kommandozeile erlaubt. Mit Libreoffice wäre das z.B möglich.

Re: Aller Anfang ist schwer

Verfasst: Donnerstag 7. März 2013, 12:35
von TomL
Diese Lösung kannte ich schon, sie ist aber für mich nicht erklärbar, d.h. ich verstehe nicht wirklich, wie sie parametrisiert ist und demzufolge kracht es, wenn ich sie etwas für meine Zwecke modifiziere.
Traceback (most recent call last):
File "C:/Python32/langer/print.py", line 12, in <module>
0
TypeError: Objects of type '_io.TextIOWrapper' can not be converted to Unicode.

Code: Alles auswählen

# -*- coding: utf-8 -*-
import win32api
import win32print
mypath=r'C:\\python32\\langer\\'
file=open (mypath+"artikneu", "r")
win32api.ShellExecute (
  0,
  "printto",
  file,
  '"%s"' % win32print.GetDefaultPrinter (),
  ".",
  0
)

Re: Aller Anfang ist schwer

Verfasst: Donnerstag 7. März 2013, 12:37
von BlackJack
@TomL: Wie Drucker angesprochen und vor allem (Grafik)daten dafür erzeugt werden ist sehr systemabhängig, darum gibt es nichts in der Standardbibliothek. Wie Sr4l schon geschrieben hat, bieten die meisten GUI-Toolkits Unterstützung für Drucker. Eine weitere Alternative ist das erzeugen von PDF was man ja auf den gängigsten Plattformen anzeigen und drucken kann.

Was meinst Du mit externen Dateizugriffsmethoden/Read-Update-Delete? Man kann mit Python aus Dateien lesen, in Dateien schreiben (auch Daten überschreiben) und die Schreib-/Leseposition innerhalb der Datei abfragen und setzen. Daten mitten aus einer Datei löschen geht nicht, aber das liegt nicht an Python, sondern an den Dateisystemen die auf PCs eingesetzt werden. Das kann man auch mit keiner anderen Programmiersprache.

Quelltext generieren ist eigentlich nicht mehr zeitgemäss. Die grafischen Designer für moderne GUIs speichern alle die GUI-Struktur als Daten und die dazugehörigen GUI-Bibliotheken können aus diesen Datendateien dann dynamisch die GUI-Elemente erstellen. Damit ist man beim Entwurf der GUI unabhängig von der Programmiersprache. Sowohl Glade (Gtk) als auch der Qt-Designer (Qt) beherrschen das.

Falls Du das umfassende Handbuch von Ernesti/Kaiser hast, dann vergiss am besten alles was da über OOP drin steht. Das ist kein idiomatisches Python. Es sei denn sie haben es in aktuellen Druckversionen überarbeitet.

Das `file` sollte eher `filename` heissen. Das Argument ist der Pfad/Name einer Datei die gedruckt werden soll und kein Dateiobjekt.

Re: Aller Anfang ist schwer

Verfasst: Donnerstag 7. März 2013, 13:02
von TomL
Danke, das war natürlich der Fehler. Nach Korrektur kam dann aber das:

pywintypes.error: (31, 'ShellExecute', 'Ein an das System angeschlossenes Gerät funktioniert nicht.')

Mein Standard-Drucker funktioniert eigentlich :D
Ich komme von der IBM-Welt, da ist man natürlich (fast immer) sowieso in einer Datenbankumgebung.
Was das Buch betrifft, so ist es Python3/3.Auflage, zu OOP bin ich noch nicht durchgedrungen. Das sind für mich als Oldie sowieso Felsbrocken von Verständnisgebirgen. :roll:
Versuche ja begleitend etwas "Echtes" zu schreiben, damit ich gleich merke, worin meine Missverständnisse liegen.

Re: Aller Anfang ist schwer

Verfasst: Donnerstag 7. März 2013, 13:16
von BlackJack
@TomL: Also wenn Du schon mal so etwas wie ``struct`` in C oder ``RECORD`` in Pascal verwendet hast und zu diesen Strukturen Funktionen/Prozeduren geschrieben hast, welche die Datenstrukturen verändern oder benutzen, dann ist der erste Schritt in OOP schon fast geschafft, denn das zusammenfassen von Daten zu einem Verbundtyp macht man in Python mit Klassen. Dann muss man die Funktionen nur noch als Methoden in die Klasse verschieben. Dann noch ein wenig mit den „magischen Methoden” mit den vielen Unterstrichen beschäftigen und Vererbung anschauen.

Re: Aller Anfang ist schwer

Verfasst: Donnerstag 7. März 2013, 16:04
von TomL
So ging es dann doch :D
Wenn ich jetzt noch die Schriftart/Größe sowie den Seitenkopf bestimmen könnte... was wäre ich wieder etwas klüger.

Code: Alles auswählen

import tempfile
import win32api
import win32print
filename = tempfile.mktemp (".txt")
mypath=r'C:\\python32\\langer\\'
file=mypath+"artikneu"
fin=open(file, "r")
fout=open (filename, "w")
for line in fin:           #Aus-Lesen der Input-Datei, d.h. alle Datensaetze werden eingelesen
    fout.write(line)
fin.close()
fout.close()
win32api.ShellExecute (
  0,
  "print",
  filename,
  '"%s"' % win32print.GetDefaultPrinter (),
  ".",
  0
)

Re: Aller Anfang ist schwer

Verfasst: Donnerstag 7. März 2013, 17:39
von BlackJack
@TomL: Eigentlich ist das umkopieren in eine neue Datei hier ja nicht wirklich nötig, oder? Falls doch, geht das mit `shutil.copy()` einfacher als selber eine Kopierschleife zu schreiben.

Pfadnamen sollte man mit `os.path.join()` zusammen setzen.

`file` ist auch hier wieder unpassend, das es kein Dateiobjekt ist. Ausserdem verdeckt man damit den einbauten Datentyp mit dem gleichen Namen.

Wenn man Dateien öffnet, hilft die ``with``-Anweisung beim schliessen der Datei, egal aus welchen Gründen der ``with``-Block verlassen wurde. Sonst müsste man mit ``try``/``finally`` arbeiten um das sicherzustellen.

Du bist ein wenig inkonsequent bei der Leerzeichensetzung. Laut Style Guide for Python Code gehört vor die Klammer bei Aufrufen keines.

Ungetestet:

Code: Alles auswählen

#!/usr/bin/env python
import os
import shutil
import tempfile
import win32api
import win32print


def main():
    temp_filename = tempfile.mktemp('.txt')
    source_path = r'C:\\python32\langer'
    filename = 'artikneu'
    shutil.copy(os.path.join(source_path, filename), temp_filename)
    win32api.ShellExecute(
        0,
        'print',
        temp_filename,
        '"{0}"'.format(win32print.GetDefaultPrinter()),
        '.',
        0
    )


if __name__ == '__main__':
    main()
Eventuell sollte man die temporäre Datei nach dem Drucken auch wieder löschen. Ich weiss jetzt nicht ob `ShellExecute()` blockiert oder asynchron läuft.

Re: Aller Anfang ist schwer

Verfasst: Donnerstag 7. März 2013, 20:55
von TomL
Vielen Dank für die vielen Hinweise...war ja auch nur ein erster Versuch mit dem Beispiel-Code aber solche Tipps findet man in Tutorials eher schwer oder gar nicht.
Deshalb finde ich dieses Forum sehr gut, auch wenn es sicher manchmal nerven wird, immer ähnliche Anfängerprobleme präsentiert zu bekommen. Maybe in 2 Jahren kann ich evtl. selbst solche Fragen beantworten.
Wenn mir jemand noch sagen könnte, wo ich eine (möglichst deutsche) Beschreibung von win32api... etc. finden kann, wäre mir toll geholfen.

Re: Aller Anfang ist schwer

Verfasst: Donnerstag 7. März 2013, 21:22
von TomL
@BlackJack: Leider komme ich aus den (chronologisch) Algol, Basic, Fortran, PL/1, Assembler, C (1988), RPG und MS-VBA-Welten, also aus fossilen Zeiten. Aber was eine Datenstruktur ist weiß ich schon. Der Syntax und die Verwendung in OOP werde ich sicher auch noch packen, Versuch macht immer klug und zur Not gibt es hier ja viele kluge und geduldige Helfer :)

Re: Aller Anfang ist schwer

Verfasst: Donnerstag 7. März 2013, 22:25
von /me
TomL hat geschrieben:Leider komme ich aus den (chronologisch) Algol, Basic, Fortran, PL/1, Assembler, C (1988), RPG und MS-VBA-Welten, also aus fossilen Zeiten.
PL/1 und RPG habe ich auch hinter mir. Das hindert nicht beim Erlernen neuer Konzepte. Ich habe allerdings OOP mit Turbo Pascal begonnen.

Re: Aller Anfang ist schwer

Verfasst: Freitag 8. März 2013, 00:01
von kbr
/me hat geschrieben:Ich habe allerdings OOP mit Turbo Pascal begonnen.
Wirth hat OOP doch erst mit Oberon eingeführt, wenn ich mich recht erinnere. In Turbo Pascal gab es noch keine Klassen - und ich meine die Doku damals gut gelesen zu haben. Aber mittels Records (bzw. Strukturen in C) ließ sich OOP in diesen Sprachen sicherlich schon ansatzweise realisieren.

Re: Aller Anfang ist schwer

Verfasst: Freitag 8. März 2013, 00:33
von BlackJack
@kbr: Turbo/Borland Pascal war auch mein erster Kontakt mit OOP. Ich glaube die Anfänge waren schon ab TP5 drin. Und das hat nicht zwingend etwas mit Wirth zu tun was Borland alles in ihren Pascal-Dialekt gesteckt hat. :-) Mein Kontakt mit OOP war mit Borland Pascal 6 und dann 7. Ich konnte dem allerdings am Anfang überhaupt nichts abgewinnen. Wir hatten eine Adressverwaltung im Informatikunterricht traditionell prozedural geschrieben. Und dann hat der Lehrer vorgeführt wie man es auf OOP umstellt. Allerdings so, dass es einfach nur leicht anders hingeschrieben war, ohne irgendwelche Vorteile zu demonstrieren.

Re: Aller Anfang ist schwer

Verfasst: Freitag 8. März 2013, 00:34
von TomL
@User: wer ist Wirth, meiner schreib sich Wirt, heißt Heinz und will immer meinen Deckel abkassieren

Re: Aller Anfang ist schwer

Verfasst: Freitag 8. März 2013, 08:53
von kbr
@BlackJack: Ich meine bezüglich Pascal TP3 genutzt zu haben, bin mir da aber nicht mehr ganz sicher; und klar - Wirth ist nicht Borland.
@TomL: Das ist aber lässtig ...

:)

Re: Aller Anfang ist schwer

Verfasst: Freitag 8. März 2013, 12:03
von Leonidas
Ich hab in TP7 auch immer nur prozedural programmiert, in Visual Basic 6 sowieso und OOP kam erst in Python dazu. Allerdings ist das ja auch schon einige Jahre her.

Re: Aller Anfang ist schwer

Verfasst: Freitag 8. März 2013, 13:12
von TomL
Ich glaube ich hab's jetzt erstmal grundsätzlich gelöst (ohne Feinheiten).
Die zu druckende Datei muss ein Attribut haben (z.B. .txt) ohne gehts offenbar nicht.

Code: Alles auswählen

import win32api
import win32print
import os.path
#Drucken einer Textdatei
def prtf(*FN):
    if len(FN) == 0:
        filename=input("Bitte kompletten Pfad/Dateinamen eingeben: ")
    else:
        filename=os.path.join(FN[0])
    if os.path.isfile(filename):
        win32api.ShellExecute (
          0,
          "printto",
          filename,
          '"%s"' % win32print.GetDefaultPrinter (),
          ".",
          0
        )
        return(0)
    else:
        print("Datei ",filename," nicht gefunden")
        return(1)
if __name__ == '__main__':
    prtf()

Re: Aller Anfang ist schwer

Verfasst: Freitag 8. März 2013, 13:38
von BlackJack
@TomL: Die Parameterübergabe an die furchtbar kryptisch benannte Funktion ist eigenartig bis sinnlos. Wenn Du eine Liste übergeben bekommen willst, dann übergib eine Liste und benutze nicht die ``*``-Magie in der Funktionssignatur. Damit verbaut man sich Änderungen an der API und wenn die Benutzer eine Liste übergeben wollen, müssen sie auch beim Aufruf wieder ein ``*`` benutzen.

Allerdings benutzt Du dann immer nur das erste Element der Liste, da wird es dann sinnlos, es sei denn es muss eine Liste mit einer Liste als einzigem Argument übergeben werden. Jetzt bin ich verwirrt. Kann es sein, dass Du eigentlich so etwas hier schreiben wolltest:

Code: Alles auswählen

def print_file(path_components=None):
    if path_components is None:
        filename = input('...')
    else:
        filename = os.path.join(path_components)
    # ...
Wobei die Vermischung zwischen Programmlogik und Benutzerinteraktion nicht schön ist. Ebenso wie das unflexible behandeln von Fehlern und der Rückgabewert. Um solche speziellen Fehlercodes zu vermeiden wurden extra Ausnahmen erfunden. Falls `ShellExecute()` Ausnahmen auslösen sollte, würde ich auch den `isfile()`-Test rauswerfen.

``return`` ist keine Funktion, also sollte man sie auch nicht so schreiben als wäre es eine.

Eine Dateiendung braucht Windows damit es weiss welche Anwendung für das Drucken zuständig ist.

Re: Aller Anfang ist schwer

Verfasst: Sonntag 17. März 2013, 03:43
von TomL
Eigentlich wollte ich nur zeilenweise ein Protokoll drucken, wenn das nur über eine .txt Datei geht, auch gut aber etwas enttäuschend.
Das andere habe ich mir zu Herzen genommen, sprich methodische Hinweise. Inzwischen erlebe ich bei tkinter meine Stalingrade, die muss man wohl haben. wenn man eine neue Programmierwelt betritt.