Document mit Standardprogramm öffnen und wieder beenden

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: 994
Registriert: Montag 30. Januar 2012, 16:38

Hallo zusammen,

das Öffnen eines Dokumente mit dem Standardprogramm des Betriebsystems Linux Kubuntu, funktioniert bei mir ohne Probleme.

Nun möchte ich mit python, von Rechnungen Positionen in einer Tabelle ausgeben lassen, gleichzeitig wird das dazugehörige PDF-Rechnungsdokument geöffnet.
Soweit funktioniert das schon mal bei mir.

Nun komme ich zu dem Punkt, wo es hakt.
Wenn ich die Tabelle mit den Rechnugspositionen schließe, möchte ich das PDF-Dokument auch über python-Code schließen, um mit der nächsten Rechnung weiterzumachen. Sonst wird für jedes neue PDF-Dokument ein neues Fenster geöffnet und das kann sehr schnell unübersichtlich werden.

Zum Öffnen von Dokumenten, verwende ich dies:

Code: Alles auswählen

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

import os
import platform

class System(object):
    def __init__(self):
        """    
        Ermittle das Betriebssystem.
        """
        
        self.system = platform.system()
        self.platform = platform.platform()

    def filestarter(self):
        """    
        Ermittlung und Ausgabe des Programmstarters.
        """
       
        try:
            return {'Windows': 'start',
                    'Linux': 'xdg-open',
                    'Darwin': 'open'
                }[platform.system()]
        except KeyError:
            raise RuntimeError('Für Ihr Betriebssystem\n%s\n \
konnte kein passender Starter ermittelt werden!' % platform.platform())

    def system(self):
        """    
        Ausgabe des Betriebssystem.
        """
        
        return self.system

    def system_platform(self):
        """    
        Ausgabe der Betriebssystem-Platform.
        """
        
        return self.platform
   
    
class OpenFile(object):
        """
        Überprüfung des Betriebssystem und Ausgabe von Dokumenten mittels Programmstarters..
        """
    
    def __init__(self, files):

        if type(files) == str:
            files = [files]
        try:
            default_program = System().filestarter()
            self.proc_start_pid = os.getpid()
        except RuntimeError as e:
            print(e)
            print('\nÖffnen Sie zum Editieren der Datei einen Texteditor.')
            return
        for file in files:
            print('Datei %s wird mit dem Standardprogramm\n \
Ihres Betriebsystem zum Editieren geöffnet!' % file)
            self.doc_process = subprocess.Popen([default_program, file])
            self.process_pid = self.doc_process.pid
            self.doc_process.wait()
            
if __name__ == '__main__':
    S = System()
    system = S.system
    platform = S.platform
    output = 'Betriebssytem: {}\nPlatform: {}'.format(system, platform)
    print(output)
    file = '/pfad/zu/datei.pdf'
    p = OpenFile(file)
    print('Programm_PID', p.proc_start_pid)
    print(Prozess_PID', p.process_pid)
Ich hoffe, dass Ihr mir dabei ein wenig helfen könnt, wie ich das Problem lösen kann!

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

Hallo,

Instanzen von Popen kennten die Methoden ˋterminate und ˋkillˋ. Damit kannst du Prozesse beenden.

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

Hallo noisefloor,

das habe ich schon versucht, ohne Erfolg.

Ich habe jetzt mal direkt in OpenFile getestet.

Code: Alles auswählen

        for file in files:
            print('Datei %s wird mit dem Standardprogramm\n \
Ihres Betriebsystem zum Editieren geöffnet!' % file)
            self.doc_process = subprocess.Popen([default_program, file])
            self.process_pid = self.doc_process.pid
            print('process_pid: ', self.process_pid)
            #sleep(1)
            #self.doc_process.wait()
            #self.doc_process.terminate()
            self.doc_process.kill()
Mit terminate und kill, öffnet sich kein Fenster, bzw. wird geöffnet und gleich wieder geschlossen.
Setze ich einen Timer (sleep(1)), öffnet sich das Dokumentenfenster, schließt sich aber nach Ablauf des Timers nicht.

Evtl. eine Idee?

Grüße Nobuddy
Benutzeravatar
__blackjack__
User
Beiträge: 13103
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Nobuddy: Der Code enthält zwei Syntaxfehler und die beiden Klassen sind auch nicht wirklich Klassen.

`subprocess` fehlt bei den Importen.

Der Code unter dem ``if __name__ …`` sollte in einer Funktion stehen.

`S` und `p` sind keine guten Namen. `S` müsste `system` heissen. Dann wird deutlich das die Klasse komisch ist, denn `system.system` klingt nicht sinnvoll.

`System` hat zwei Methoden die nicht verwendet werden: `system()` und `system_platform()`. Das sind aber auch nur triviale Getter die man nicht braucht. `system()` *kann* man zudem gar nicht nutzen weil in der `__init__()` die Methode mit einer Zeichenkette verdeckt wird.

Dann bleibt neben der `__init__()` nur noch die ”Methode” `filestarter()` übrig, die aber gar keine Methode ist, sondern nur eine Funktion die in die Klasse gesteckt wurde, aber gar keinen Bezug zu dem Objekt hat. Wenn man diese Funktion also aus der Klasse heraus zieht ist die ziemlich nackt und macht so gar keinen Sinn mehr.

`OpenFile` ist auch nur eine Funktion die unnötig kompliziert als Klasse geschrieben ist. Die hat nur eine `__init__()` und in der läuft alles ab was die ”Klasse” macht. Der Name der Klasse entspricht inhaltlich ja auch eher einer Funktion.

Wonach entscheidest Du ob Du `format()` oder ``%`` verwendest? ``%`` würde ich in neuem Code nicht mehr verwenden ohne guten Grund.

`files` sind keine Dateien sondern Datei*namen*.

Man verwendet `type()` nicht für Typprüfungen. Dafür gibt es `isinstance()`. Typprüfungen sind auch immer ein bisschen blöd weil man aufpassen muss alles zu erwischen was Sinn macht. `os.PathLike` würde man hier beispielsweise doch wohl auch gerne erlauben.

`proc_start_pid` zu ermitteln macht keinen Sinn. Wenn man diese Information irgendwo haben will, dann sollte man sie dort ermitteln, aber nicht sinnloserweise jedes mal wenn man ein Dokument über diese Funktion öffnen will.

Auch die Prozess-ID vom betriebssystemspezifischen Starter zu ermitteln macht absolut gar keinen Sinn da die Funktion ja erst zurück kehrt wenn der beendet ist. Zu dem Zeitpunkt ist die PID zu nichts mehr zu gebrauchen.

Anstelle von `Popen` + `wait()` kann man da auch einfach `run()` verwenden.

Wo in den Docstrings ”Ausgabe” steht ist tatsächlich fast immer ”Rückgabe” gemeint. Ausnahme ist der Docstrings von `OpenFile`, da ist mit ”Ausgabe” tatsächlich ”öffnen” gemeint.

Was letztlich übrig bleibt (ungetestet):

Code: Alles auswählen

#!/usr/bin/env python3
import os
import subprocess
from platform import platform, system


def get_file_opener():
    """    
    Ermittelt den Programmstarter für das Betriebssystem.
    """
    try:
        return {"Darwin": "open", "Linux": "xdg-open", "Windows": "start"}[
            system()
        ]
    except KeyError:
        raise RuntimeError(
            f"Für Ihr Betriebssystem\n"
            f"{platform()}\n"
            f"konnte kein passender Starter ermittelt werden!"
        )


def open_files(file_paths):
    if isinstance(file_paths, (os.PathLike, str)):
        file_paths = [file_paths]

    try:
        file_opener = get_file_opener()
    except RuntimeError as error:
        print(error)
        print("\nÖffnen Sie zum Editieren der Datei einen Texteditor.")
    else:
        for file_path in file_paths:
            print(
                f"Datei {file_path!r} wird mit dem Standardprogramm\n"
                f"Ihres Betriebsystem zum Editieren geöffnet!"
            )
            subprocess.run([file_opener, str(file_path)], check=True)


def main():
    print(f"Betriebssytem: {system()}\nPlatform: {platform()}")
    open_files("/pfad/zu/datei.pdf")


if __name__ == "__main__":
    main()
Zur Frage nach dem schliessen: Das geht nicht. Die Starterprogramme kehren in der Regel sofort zurück nach dem sie das Standardprogramm für die Datei(en) geöffnet haben und man hat keinen Weg da robust und plattformübergreifend an die PID(s) der gestarteten Programme heran zu kommen. Zudem gibt es Programme die selber auch wieder, ähnlich wie die Starter, nicht zwingend einen neuen Prozess starten, sondern nur einem eventuell schon vorhandenen Prozess signalisieren er soll noch eine weitere Datei öffnen, und sich selbst dann sofort wieder beenden.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Nobuddy
User
Beiträge: 994
Registriert: Montag 30. Januar 2012, 16:38

Hallo __blackjack__,

Unwissentheit, schütz vor Fehlern nicht ...., dafür Danke für den Input! :wink:
Werde mir Deinen Input durcharbeiten.
Schade, dass das nicht umsetzbar ist, wäre eine tolle Vereinfachung gewesen.
Gibt es evtl. eine andere Lösung, das mehrfach öffnen von Programmen zu verhindern, wenn es schon geöffnet ist?

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

Hallo,

grundsätzlich halte ich es für fragwürdig, wenn sich Programme automagisch schließen. Das gibt dem Nutzer im,er das Gefühl, dass er gerade nicht die Kontrolle über seinen Rechner hat, sondern eine „fremde Macht“ gerade das Sagen hat.

Grundsätzlich gibt es wahrscheinlich schon Möglichkeiten, einem Programm zu sagen, dass es eine neue Instanz starten soll statt einen neuen Tab oder so. Nur unterscheidet sich das ziemlich sicher von Programm zu Programm. Bei PDFs zum Beispiel gibt es den Adobe Reader, Chrome, neuerdings auch Firefox und X andere Programme, die sich als Default PDF Betrachter anbiedern. Wenn du das für alle möglichen / gängigen Fälle unterscheiden willst, dann hast du einen riesen Haufen Code nur für alle möglichen Fallunterscheidungen.

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

Hallo __blackjack__,
Dein Code funktioniert mit ein paar kleinen Änderungen bei mir.
Habe noch kein python 3.8, daher musste ich 'os.PathLike' durch 'type()' ersetzen.
Das 'f' for den Strings musste ich auch entfernen, gehört wohl auch zu python 3.8.

Danke nochmals, für die Info zu meinem geposteten Code.

Hallo noisefloor,
einen rießen Haufen Code, wie eine eierlegende Wollmilchsau ..., das muss nicht sein.
Dachte, wenn ein Programmfenster geöffnet wird, gibt es eine System-Adressierung, die es ermöglicht darauf zuzugreifen und beenden zu können.

Grüße Nobuddy
Benutzeravatar
__blackjack__
User
Beiträge: 13103
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Nobuddy: Sowohl `os.PathLike` als auch f-Zeichenketten gibt es bereits in Python 3.6 und das ist bereits recht ”abgehangen”. Und `os.PathLike` nicht zu haben ist ist kein Grund für `type()`. Auch nur `str` würde man mit `isinstance()` testen.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Nobuddy
User
Beiträge: 994
Registriert: Montag 30. Januar 2012, 16:38

@__blackjack__, habe python 3.4.
Das mit `isinstance()` funktioniert, danke für den Hinweis nochmals, werde dies zukünftig verwenden und alte Zöpfe ändern.
Das mit f-Zeichenketten, ist interessant .... muss unbedingt python 3.6 oder höher installieren.
Antworten