Erstellung eines Windows-Shell-Befehls mit askopenfilename

Fragen zu Tkinter.
colonelkurtz
User
Beiträge: 6
Registriert: Freitag 21. Januar 2011, 15:42

Hallo zusammen,

ich möchte ein Skript schreiben, dass einen Befehl erzeugt, der in der Windows-Shell ein ausführbares Programm (*.exe) mit einer Eingabedatei aufruft.
Dieser Befehl hat dann z.B. die Form: 'V:\Programme\Programm.exe Eingabedatei.dat'

Der Anwender soll diesen Befehl bequem über ein Dialogfenster erzeugen.

Bisher habe ich einen Ansatz, doch stehe ich nun bei Problemen, die ich nicht lösen kann.

Der Ansatz beinhaltet immerhin ein Dialogfenster, dass einen Button enthält, der auf "Knopfdruck" mittels der tkFileDialog.askopenfilename()-Methode ein Dateiauswahlfenster öffnet, indem die Programm.exe-Datei auswählbar ist.

Ansatz:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: iso-8859-1 -*-

import Tkinter
import tkFileDialog


filename=''

root = Tkinter.Tk()

def open_file_dialog():
	global filename
	filename = tkFileDialog.askopenfilename(filetypes=
	[('executable files', '.exe'), ('all files', '.*')], initialdir=['V:\\User\\bin\\Programme\\TXpert']) #Beachte Beispiel-initialdir
	print filename

Tkinter.Button(root, text='Wähle tw0070-Version', command=lambda:
open_file_dialog()).pack()

root.mainloop()


# http://www.velocityreviews.com/forums/t689042-tkinter-get-filename-of-askopenfilename.html
#----> google keyword: tkinter button askopenfilename
Beobachtet man die Python-Shell während das Skript ausgeführt wird, wird dort der Pfad und die Programmdatei ausgegeben, weil in der Funktion 'open_file_dialog():' die Variable 'filename' mit dem Befehl 'prin't ausgegeben wird.

Der 1. Teil des Befehls ist also schon fertig.

Nun soll noch ein zweiter Button entstehen, mit dem auf "Knopfdruck" der Eingabedatei-Name einer Variablen zu gewiesen wird.

Das Problem sehe ich darin, dass die globale Variable Filename außerhalb der Funktion 'open_file_dialog():', nicht den gewünschten Wert (Pfad+Programm-Name) hat, sondern "leer ist". Dadurch kann man außerhalb der Funktion offenbar nicht mit 'filename' arbeiten.
'filename' ist vom Typ unicode und soll dann später um den Eingabedatei-Namen (wäre dann wohl auch unicode) ergänzt werden (etwa: filename = filename + Eingabdateiname).

Zum Schluß würde die Variable 'filename' (= vollständiger Windows-Shell-Befehl) über den Befehl 'subprocess.Popen()' aus der 'subprocess'-Bibliothek ausgeführt werden.

Am Ende nun meine Fragen bzw. ;) :

1. Sind meine Gedanken zu Vervollständigung des Skripts nachvollzieh- und umsetzbar?
2. Fällt jmd. ein, wie dass Skript in Python ergänzt werden kann, damit der Befehl in der Windows-Shell ausgeführt wird?
3. Gibt es andere vielversprechende Ansätze, die den vorgesehenen Zweck ggf. einfacher realisieren lassen?
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

Hm, hab das nicht so richtig verstanden. Die globale Variable nimmt doch einen anderen Wert an, wenn man sie ändert? :-)
Benutzeravatar
Sr4l
User
Beiträge: 1091
Registriert: Donnerstag 28. Dezember 2006, 20:02
Wohnort: Kassel
Kontaktdaten:

Ja global weg, und her mit OOP. Ansonsten haste schon das richtige gesagt.

Code: Alles auswählen

import Tkinter
import tkFileDialog

class Application(Tkinter.Frame):
    def __init__(self, master=None):
        Tkinter.Frame.__init__(self, master)
        self.pack()
        self.filename = None
        Tkinter.Button(self, text='Wähle tw0070-Version', command=self.open_file_dialog).pack()
        Tkinter.Button(self, text='Print filename', command=self.say_hi).pack()
        
    def say_hi(self):
        print self.filename

    def open_file_dialog(self):
        self.filename = tkFileDialog.askopenfilename(filetypes=
            [('executable files', '.exe'), ('all files', '.*')], 
            initialdir=['V:\\User\\bin\\Programme\\TXpert'])

root = Tkinter.Tk()
app = Application(root)
app.mainloop()
root.destroy()
Dazu noch subporcess und ein Button für das Argument und fertig.

PS: es gibt Python Code Tags
colonelkurtz
User
Beiträge: 6
Registriert: Freitag 21. Januar 2011, 15:42

Servus Sr4l,

enstchuldigt für die späte Antwort & besten Dank für die Hilfestellung. Nachdem ich es entsprechend angepasst habe, funktioniert das Skript so, wie ich es erfragt habe. Ursprünglich hatte ich gehofft das ohne OOP hinzukriegen, da dies (noch) nicht mein Ding ist ;). Jedoch habe ich schon häufiger gelesen, dass globale Variablen nicht das 1. Mittel sein sollten.
Wie auch immer im folgenden das aktuelle Skript, wo nun der Print-Befehl durch die Ausführung des Windows-Shell-Befehls ersetzt worden ist.

Code: Alles auswählen

import Tkinter, os, subprocess
import tkFileDialog

class Application(Tkinter.Frame):
		def __init__(self, master=None):
			Tkinter.Frame.__init__(self, master)
			self.pack()
			self.filename = None
			self.filename_A = None
			Tkinter.Button(self, text='Wähle tw0070-Version', command=self.open_file_dialog).pack()
			Tkinter.Button(self, text='Wähle tw0070-Eingabedatei', command=self.open_file_dialog_A).pack()
			Tkinter.Button(self, text='Starte Rechnung', command=self.say_hi).pack()
			
		def say_hi(self):
			self.filename = os.path.normpath(self.filename)
			self.filename_A = os.path.normpath(self.filename_A)
			self.filename = self.filename,  self.filename_A
			subprocess.Popen(self.filename, shell=True)
			#print self.filename
			
		def open_file_dialog(self):
			self.filename = tkFileDialog.askopenfilename(filetypes=[('executable files', '.exe'), ('all files', '.*')], initialdir=['V:\\User\\bin\Programme\\TXpert'])
			
		def open_file_dialog_A(self):
			self.filename_A = tkFileDialog.askopenfilename(filetypes=[('data', '.dat'), ('all files', '.*')], initialdir=['H:\\Scripting-Unterlagen\\Python\\Skripte\Beispiel_Drosselpunkt\\T5422_Buchmann_Rinnthal\Test-Rechnungen'])

root = Tkinter.Tk()
app = Application(root)
app.mainloop()
root.destroy()
MfG,

Kurtz
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Dafür braucht es ja auch nun nicht wirklich OOP, das geht auch ganz gut ohne, zu dem dein Beispiel (entschuldige bitte) ist nun ja, ein absolut mieses Beispiel für OOP.

Hier das ganze mal ohne OOP und etwas Übersichtlicher.
EDIT: Nicht nutzen, weißt mehrere Fehler auf, bitte weiter unten nachlesen.

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: cp1252 -*-
import os
import subprocess
import Tkinter
import tkFileDialog

def say_hi(filename, filename_a):
    subprocess.Popen(os.path.normpath(filename), os.path.normpath(filename_a),
                     shell=True)

def open_file_dialog():
    options = {}
    options['filetypes'] = ('executable files', '.exe'), ('all files', '.*')
    options['initialdir'] = 'V:\\User\\bin\Programme\\TXpert'
    return tkFileDialog.askopenfilename(**options)
                       
def open_file_dialog_a():
    options = {}
    options['filetypes'] = ('data', '.dat'), ('all files', '.*')
    options['initialdir'] = 'H:\\Scripting-Unterlagen\\Python\\Skripte\\' \
                            'Beispiel_Drosselpunkt\\T5422_Buchmann_Rinnthal\\' \
                            'Test-Rechnungen'
    return tkFileDialog.askopenfilename(**options)

if __name__ == "__main__":
    pack_style = dict(expand=True, fill="x")
    root = Tkinter.Tk()
    root.title('Say Hi!')
    Tkinter.Button(root, text='Wähle tw0070-Version',
                   command=open_file_dialog).pack(pack_style)
    Tkinter.Button(root, text='Wähle tw0070-Eingabedatei',
                   command=open_file_dialog_a).pack(pack_style)
    Tkinter.Button(root, text='Starte Rechnung', command=say_hi).pack(pack_style)
    root.mainloop()
@Sr4l
Deine Vorgabe war auch nicht gerade Beispielhaft, warscheinlich weil du es ziemlich schnell geschrieben hast, aber denk mal über folgende Punkte nach.
1. Eine Application-Klasse wird, so wie ich das sehe ziemlich häufig laut den Tkinter Tutorials vorgeschlagen, aber IMHO (fast) nie Notwendig.
2. Deine App braucht keine "mainloop", sondern root, eigentlich braucht immer nur das "Tk"-Widget eine "mainloop".
3. Im Konstruktor verwendest du den "pack"-Geometrie-Manager auf die App selbst, damit nimmst du einem späteren Nutzer der App die Wahl welch Manager er nutzen möchte.
4. Es ist eigentlich nicht beonders Klever die Pfade in der Klasse zu definieren, denn ändern sich mal diese, muss man ja die Klasse ändern. Zugegeben, der einfachheit halber habe ich die Pfade jetzt auch in den Funktionen gelassen, das sollte man noch ändern.
5. Das "master=None" in der Parameterliste des Konstrucktors ist unötig, weil du eigentlich immer ein "master" erwartest. Es gibt eigentlich nur selten Fälle wo das nicht der Fall ist und dort kann man dann ja selbst None übergeben.
6. Du kannst an deinen Frame bei der Initialisierung keine Optionen übergeben, was aber in Tkinter häufig gemacht wird. Hier wäre es also Sinnvoll noch eine Dictionary in die Parameterliste zu übernehmen und diese beim Initialisieren der Basisklasse mit zu übergeben.
7. Und root.destroy() ist absolut nicht notwendig, da root nie in einer "mainloop" lief und beim beenden der "app.mainloop" automatisch zerstört wird oder besser gesagt einfach nicht fortgeführt wird.
Zuletzt geändert von Xynon1 am Mittwoch 26. Januar 2011, 11:27, insgesamt 1-mal geändert.
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
Barabbas
User
Beiträge: 349
Registriert: Dienstag 4. März 2008, 14:47

Hallo Xypnon1,

dein Beispiel funktioniert nicht. Ganz so einfach scheint dir der Verzicht auf OOP auch nicht zu fallen :mrgreen: .

*scnr*

brb
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

War ungetestet sry, hatte kein Win laufen muss das gleich mal nachholen.
Edit: :oops:
Ok hier wird man um ein Klassenobjekt oder eine Modul weite Variable nicht herum kommen, dennoch braucht es keine App und die anderen Punkte gelten dennoch.
Zuletzt geändert von Xynon1 am Dienstag 25. Januar 2011, 11:39, insgesamt 1-mal geändert.
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

So hier nochmal:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: cp1252 -*-
import os
import subprocess
import Tkinter
import tkFileDialog

_filename = ""
_filename_a = ""

def open_file_dialog(initialdir="/"):
    options = {}
    options['filetypes'] = ('executable files', '.exe'), ('all files', '.*')
    options['initialdir'] = initialdir
    _filename = tkFileDialog.askopenfilename(**options)

def open_file_dialog_a(initialdir="./"):
    options = {}
    options['filetypes'] = ('data', '.dat'), ('all files', '.*')
    options['initialdir'] = initialdir
    _filename_a = tkFileDialog.askopenfilename(**options)


def say_hi():
    if all((_filename, _filename_a)):
        subprocess.Popen(os.path.normpath(filename),
                         os.path.normpath(filename_a), shell=True)
    
if __name__ == "__main__":
    path_to_exe = 'D:\\User\\bin\Programme\\TXpert'
    path_to_dat = 'D:\\Scripting-Unterlagen\\Python\\Skripte\\Beispiel_' \
                  'Drosselpunkt\\T5422_Buchmann_Rinnthal\\Test-Rechnungen'
    
    pack_style = dict(expand=True, fill="x")
    root = Tkinter.Tk()
    root.title('Say Hi!')
    exe = Tkinter.Button(root, text='Wähle tw0070-Version',
                         command=lambda: open_file_dialog(path_to_exe))
    exe.pack(pack_style)
    dat = Tkinter.Button(root, text='Wähle tw0070-Eingabedatei',
                         command=lambda: open_file_dialog_a(path_to_dat))
    dat.pack(pack_style)
    
    bt = Tkinter.Button(root, text='Starte Rechnung')
    bt.config(command=say_hi)
    bt.pack(pack_style)
    root.mainloop()
Zuletzt geändert von Xynon1 am Mittwoch 26. Januar 2011, 11:03, insgesamt 1-mal geändert.
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

Was Xynon1 da mit den Dictionaries in Keyword-Argumenten macht bitte nicht angewöhnen.

Code: Alles auswählen

def open_file_dialog(initialdir="/"):
    # PFUI PFUI PFUI
    options = {}
    options['filetypes'] = ('executable files', '.exe'), ('all files', '.*')
    options['initialdir'] = initialdir
    _filename = tkFileDialog.askopenfilename(**options)
So sollte das aussehen (mal abgesehen davon, dass das eh überhaupt nicht funktioniert):

Code: Alles auswählen

def open_file_dialog(initial_dir="/"):
    _filename = tkFileDialog.askopenfilename(
        filetypes=(...),
        initial_dir=initial_dir
     )
Das Selbe gilt für das ``dict(...)`` im Main-Bereich.

Was soll eigentlich der Name "say_hi"?! Das war doch ein Beispielname, vielleicht könnte man den ja mal so umändern, dass er nicht lügt... ich sehe nämlich nicht, dass die Methode "hi" sagt?
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

1. Sollte die Übersichtlichkeit waren, warum sollte das "pfui pfui pfui" sein? Zugegeben solange man nur zwei oder drei Argumente für den "tkFileDialog" hat, ist das überflüssig.
Was an deinem ist anders? Wenn du die Daten so übergibst wird auch eine dict erstellt, was in bestimmten Ableitungen von Tkinter sehr unschön gemacht ist. Denn die Grundklassen bieten immer noch die Wahl ob man eine Dictionary übergeben oder eine in der Parameterliste erstellen möchte. Später dazu gekommene bieten diesen Luxus nicht mehr und genau dieser ist mir hier genommen und man kann nur eine in der Parameterliste erstellen, was an sich schon mies ist.

2. Was ist an dict im Mainbereich falsch? Das macht genau das selbe was du oben noch gut hiest.

3. say_hi, ist wie der Rest alles aus dem Beispiel entnommen, darüber habe ich mir nun keine Gedanken gemacht.
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

Xynon1 hat geschrieben:1. Sollte die Übersichtlichkeit waren, warum sollte das "pfui pfui pfui" sein? Zugegeben solange man nur zwei oder drei Argumente für den "tkFileDialog" hat, ist das überflüssig.
Was an deinem ist anders? Wenn du die Daten so übergibst wird auch eine dict erstellt, was in bestimmten Ableitungen von Tkinter sehr unschön gemacht ist. Denn die Grundklassen bieten immer noch die Wahl ob man eine Dictionary übergeben oder eine in der Parameterliste erstellen möchte. Später dazu gekommene bieten diesen Luxus nicht mehr und genau dieser ist mir hier genommen und man kann nur eine in der Parameterliste erstellen, was an sich schon mies ist.

2. Was ist an dict im Mainbereich falsch? Das macht genau das selbe was du oben noch gut hiest.
Falsch ist daran (zumindest an den angesprochenden Teilen) gar nichts. Es geht mir nur darum, einem Anfänger von Anfang an idiomatisches Python zu vermitteln. Und idiomatisch ist das mit den Dictionaries überhaupt nicht. Deswegen das Pfui, damit es auch ankommt.

Überleg doch mal, wenn du Keyword-Argument-Übergeben via Dictionary zeigst, muss der Leser bereits Vorkenntnis darüber haben, dass Keyword-Argumente intern(!) als Dictionaries übergeben werden und dass ``**foo`` quasi Dict-Unpacking ist.

In Anbetracht deiner Ausführungen oben scheinst du das mit den Keyword-Argumenten und dem Dictionary auch noch nicht so richtig verstanden zu haben. Kurz zur Erklärung: In CPython können Positional Arguments (Deutsche Übersetzung anyone?) auch als Keyword Arguments übergeben werden. Ich nehme mal an, dass du mit "später dazugekommenen" Klassen solche meinst, die nicht in einem stdlib-Modul definiert wurden. Sollte das der Fall sein, stimmt deine Aussage nicht wirklich, weil wie eben erwähnt alle Positional Arguments als Keyword Arguments übergeben werden können. Die einzige Ausnahme hierfür sind in C (und Konsorten) implementierte Callables, also auch manche builtins.

Hoffe das hilft. :-)
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Was ich meinte war, das die meisten Objekte in Tkinter für ihre Konstrucktoren oder Methoden einem die Wahl lassen, ob man die Referenz einer Dictionary übergibt oder eine erstellt. Dies ist bei späteren Modulen nicht mehr der Fall, leider gibt es solche Wracks auch in der stdlib von python-tk.

Der Standard sollte in Tkinter so aussehen "def configure(self, cnf=None, **kw):", sprich du hast die Wahl ob du das dict, cnf als Positionales oder kw einzeln als Schlüssel Argumentw übergibst.
In einigen anderen Anbindungen, wie zB ttk(ein Beispiel für später dazu gekommene) ist es generell nur als Schlüssel Argumente möglich. Was zB ab Python 3.x mit in das python-tk Paket als Standard gepackt wurde. Aber auch schon vorher gab es solche Leichen wie halt "tkFileDialog" dort geht gut alles drei, vier mal durch die magische **-Operation. zB. Tix ist dagegen wesentlich besser und bietet immer die Wahl.

Wenn du also idiomatisches Python vermitteln willst, darfst du kein Tkinter nutzen.
Und ja ich weiß das "Dict-Unpacking" unschön ist, aber wird in Tkinter leider ziemlich häufig benötigt, ansonsten blickt man nicht mehr durch.
Aber wie steht das jetzt im Zusammenhang mit 2. ?
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

Xynon1 hat geschrieben:Was ich meinte war, das die meisten Objekte in Tkinter für ihre Konstrucktoren oder Methoden einem die Wahl lassen, ob man die Referenz einer Dictionary übergibt oder eine erstellt. Dies ist bei späteren Modulen nicht mehr der Fall, leider gibt es solche Wracks auch in der stdlib von python-tk.
Okay, das hab ich nicht verstanden. Könntest du vielleicht mal ein Beispiel anführen? Ich weiß einfach nicht, worauf du hinaus willst.
vier mal durch die magische **-Operation. zB. Tix ist dagegen wesentlich besser und bietet immer die Wahl.
Du weißt aber schon, dass ``foo(bar=blah)`` das Selbe ist wie ``foo(**{'bar' : blah})``?
Wenn du also idiomatisches Python vermitteln willst, darfst du kein Tkinter nutzen.
Willst du dann dem Anfänger unidiomatisches Python beibringen, nur weil er vielleicht eine Library nutzt, die nicht regelkonform ist? So quasi nach dem Motto "nutzt ja eh schon ekliges Zeug, dann ist ja wurscht was wir dem sonst so beibringen"?
Und ja ich weiß das "Dict-Unpacking" unschön ist, aber wird in Tkinter leider ziemlich häufig benötigt, ansonsten blickt man nicht mehr durch.
Also ich persönlich finde es übersichtlicher, Keyword-Argumente auf mehrere Zeilen innerhalb der "Aufruf-Klammer" zu verteilen anstatt erst mal das Dictionary zu erstellen und dann mit **-Magie zu übergeben...

Aber wie steht das jetzt im Zusammenhang mit 2. ?
Ich wollte eigentlich darauf raus, dass man üblicherweise statt

Code: Alles auswählen

dict(foo=bar, blah=blubb)
lieber die Literalform schreibt:

Code: Alles auswählen

{'foo' : bar, 'blah' : blubb}
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Dauerbaustelle hat geschrieben:Du weißt aber schon, dass ``foo(bar=blah)`` das Selbe ist wie ``foo(**{'bar' : blah})``?
Ja, weiß ich, es diente wirklich nur der Übersichtlichkeit, ich hätte es mit dazu schreiben sollen.
Dauerbaustelle hat geschrieben:Willst du dann dem Anfänger unidiomatisches Python beibringen, nur weil er vielleicht eine Library nutzt, die nicht regelkonform ist? So quasi nach dem Motto "nutzt ja eh schon ekliges Zeug, dann ist ja wurscht was wir dem sonst so beibringen"?
Nein, es war nur mein Ziel kurz und Übersichtlich aufzuführen, das es ohne OOP und ohne global umsetztbar ist. Wenn man Tkinter aber länger Zeit benutzt wird man leider dennoch hinunwieder auf **-Operationen zugreifen müssen, zB bei ttk um selber saubere Klassen zu haben. Was wie ich schon zugegeben habe nicht schön ist, aber nicht anders geht.
Dauerbaustelle hat geschrieben:Also ich persönlich finde es übersichtlicher, Keyword-Argumente auf mehrere Zeilen innerhalb der "Aufruf-Klammer" zu verteilen anstatt erst mal das Dictionary zu erstellen und dann mit **-Magie zu übergeben..
Wie ich schon gesagt habe war es hier nicht wirklich Notwendig und man hätte es sicher auch so wie du vorgeschlagen hast machen können, wenn du aber größere Optionale Parameter hast bekommt man mit deiner Methode auch Schwierigkeiten. Zudem sollte man solche Funktionen/Methoden überhaupt nicht schreiben, die sowas zulassen, denn dafür brauchst du auch die **-Magie. Wenn du das also in deinen Beispielen so umsetzt könnte ich jetzt auch behaupten das du Anfängern nicht idiomatische (unidiomatiosches :) ) Python beibringst, da dies auch nicht die Regel sein sollte. (Tkinter ist echt verkorkst :D )
Dauerbaustelle hat geschrieben:Ich wollte eigentlich darauf raus, dass man üblicherweise statt
Ich hatte eigentlich in Erinnerung das das egal wäre, zugegeben dict() ist etwas langsamer und akzeptiert auch nur 255 Argumente und nimmt auch keine Zahlen als Key, aber solange man nicht eine diese Parameter überschreitet ist es eigentlich egal.
Dauerbaustelle hat geschrieben:Okay, das hab ich nicht verstanden. Könntest du vielleicht mal ein Beispiel anführen? Ich weiß einfach nicht, worauf du hinaus willst.
Zu guter letzt ein paar Beispiele, was ich hier meine:
Toplevel, Button, Canvas, Frame, Text, ... Konstrucktor:

Code: Alles auswählen

def __init__(self, master=None, cnf={}, **kw):
Fast alle lassen die Wahl ob man cnf die Referenz eines Wörterbuches übergeben möchte, oder ob man sie als Argument Parameter übergibt. Das gilt auch für die meisten der Methoden. Aber Zusatzt Module neben der Tkinter.py die zum Teil halt später in das python-tk Paket übernommen wurden bieten dies nicht mehr, weil aus puren bequemlichkeits Gründen weggelassen wurden.
Hier eine Liste der Tkinter Module, folgende bieten die Wahl (Python 2.6.6):
Tkinter.py - gibt ein paar Ausnahmen
Dialog.py
Tix.py

Bietet die Wahl nicht an:
Canvas.py
ScrolledText.py
tkColorChooser.py
tkCommonDialog.py
tkFileDialog.py
tkFont.py
tkMessageBox.py
tkSimpleDialog.py
turtle.py - geht zwar anders vor, hat aber bei einigen Methoden das selbe Problem.
ttk.py - Erst ab Python 3.x im Standard enthalten

Gerade bei den "tk" voran gestellten Modulen, sieht man das sie recht hastig geschrieben wurden und nur auf einfache Nutzung und nicht auf Sauberkeit programmiert wurden. Wohin gegen ttk.py sehr Sauber ist, aber auch aus (vermutlicher) Bequemlichkeit nur die Argument Parameter für die Optionen Akzeptiert.
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
colonelkurtz
User
Beiträge: 6
Registriert: Freitag 21. Januar 2011, 15:42

Hallo zusammen,

der Ansatz ohne OOP ist interessant und ich habe den Entwurf von Xynon1 getestet. Allerdings scheint dieser nicht zu laufen ('Rechnung' startet nicht bzw. Shell-Befehl wird nicht ausgeführt) und ich vermute der Grund liegt in der

Code: Alles auswählen

if all((filename, filename_a)):
aus der

Code: Alles auswählen

def say_hi
Funktion (oder Prozedur?), die mittlerweile in 'Programmstart' umbenannt wurde ;).

Die Vermutung ergibt sich daraus, dass ich ein print- unmittelbar vor dem subprocess-befehl eingefügt habe. Der print-Befehl wird nach dem Anklicken des 'Starte Rechnung' - Buttons nicht in der Konsole ausgegeben. Insofern gehe ich davon aus, dass die o.g. if-Bedingung nicht erfüllt ist.

Ich poste hier nochmal den ganzen Code:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: cp1252 -*-
import os
import subprocess
import Tkinter
import tkFileDialog

_filename = ""
_filename_a = ""

def open_file_dialog(initialdir="/"):
    options = {}
    options['filetypes'] = ('executable files', '.exe'), ('all files', '.*')
    options['initialdir'] = initialdir
    _filename = tkFileDialog.askopenfilename(**options)

def open_file_dialog_a(initialdir="./"):
    options = {}
    options['filetypes'] = ('data', '.dat'), ('all files', '.*')
    options['initialdir'] = initialdir
    _filename_a = tkFileDialog.askopenfilename(**options)


def Programmstart(filename, filename_a):
    if all((filename, filename_a)):
        print test
        subprocess.Popen(os.path.normpath(filename),
                         os.path.normpath(filename_a), shell=True)
    
if __name__ == "__main__":
    path_to_exe = 'V:\\User\\bin\Programme\\TXpert'
    path_to_dat = 'H:\\Scripting-Unterlagen\\Python\\Skripte\\Beispiel_' \
                  'Drosselpunkt\\T5422_Buchmann_Rinnthal\\Test-Rechnungen'
    
    pack_style = dict(expand=True, fill="x")
    root = Tkinter.Tk()
    root.title('Programmstart')
    exe = Tkinter.Button(root, text='Wähle tw0070-Version',
                         command=lambda: open_file_dialog(path_to_exe))
    exe.pack(pack_style)
    dat = Tkinter.Button(root, text='Wähle tw0070-Eingabedatei',
                         command=lambda: open_file_dialog_a(path_to_dat))
    dat.pack(pack_style)
    
    bt = Tkinter.Button(root, text='Starte Rechnung')
    bt.config(command=lambda: Programmstart(_filename, _filename_a))
    bt.pack(pack_style)
    root.mainloop()
@ Dauerbaustelle:

Deinen Vorschlag zur Modifikation hinsichtlich den Dictionaries in Keyword-Argumenten bin ich erstmal nicht nachgekommen, da mehrere Konsolen-Meldungen bei mir Verwirrung stiften.
Falls es von Interesse ist, poste ich die hier im Anschluss, da sonst meine Nachricht hier sehr lang wird ;).

Am Schluss nochmal eine 'Off-Topic' - Frage:

Das Tkinter-Dialogfenster, so wie es jetzt ist soll um einige Label- und Entry-Felder ergänzt werden und diese dann mit voraussichtlich 2 LabelFreame Widgets sortiert (gruppiert) werden. Könnte ich dazu eine kurze Hilfestellung erhalten?

Für einen von beiden Entwürfen (von Xynon1 ohne OOP (wenn er dann läuft ;)) oder Sr4l mit OOP) würde mich das weiterbringen.

Besten Dank!

MfG,

Kurtz
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Ich kann dir auch nochmal ein OOP script geben, allerdings halte ich das hier für unötig, da du die Form weder irgendwo einbinden noch sonst wie nutzen willst, sondern nur in deinem "Hauptscript".

Das "all((filename, filename_a))", wird erfüllt, wenn die String darin nicht leer sind, hier habe ich aber die "_" Unterstriche vor beiden vergessen, also sind sie immer leer. Werde da oben gleich mal korrigieren.
Ich möchte auch nochmal betonen, das Dauerbaustelle recht hat, die **-Magie, sollte nie oder halt nur bedingt, wenn man keine andere Wahl hat genutzt werden.

Zu deiner Form die du haben willst könntest du da mal eine knappe Skizze machen, oder dies etwas detailierter Beschreiben ?
Aber Prinzipiell, sollte es nicht allzu schwer sein. Wenn du aber hier mehrere von den hier erwähnten Widgets brauchst bietet sich OOP wieder an.

Das Sortieren, musst du in der Logik machen die GUI sollte nur der Anzeige der Daten dienen, hier sollte nichts sortiert werden.
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

Xynon1 hat geschrieben:
Dauerbaustelle hat geschrieben:Also ich persönlich finde es übersichtlicher, Keyword-Argumente auf mehrere Zeilen innerhalb der "Aufruf-Klammer" zu verteilen anstatt erst mal das Dictionary zu erstellen und dann mit **-Magie zu übergeben..
Wie ich schon gesagt habe war es hier nicht wirklich Notwendig und man hätte es sicher auch so wie du vorgeschlagen hast machen können, wenn du aber größere Optionale Parameter hast bekommt man mit deiner Methode auch Schwierigkeiten. Zudem sollte man solche Funktionen/Methoden überhaupt nicht schreiben, die sowas zulassen, denn dafür brauchst du auch die **-Magie. Wenn du das also in deinen Beispielen so umsetzt könnte ich jetzt auch behaupten das du Anfängern nicht idiomatische (unidiomatiosches :) ) Python beibringst, da dies auch nicht die Regel sein sollte. (Tkinter ist echt verkorkst :D )
Ich komm nicht mit. Wann bekommt man denn damit Schwierigkeiten, wenn man Keyword-Argumente in der vorgesehenen Weise, nämlich in den "Aufruf-Klammern", übergibt?
Dauerbaustelle hat geschrieben:Ich wollte eigentlich darauf raus, dass man üblicherweise statt
Ich hatte eigentlich in Erinnerung das das egal wäre, zugegeben dict() ist etwas langsamer und akzeptiert auch nur 255 Argumente und nimmt auch keine Zahlen als Key, aber solange man nicht eine diese Parameter überschreitet ist es eigentlich egal.
Deswegen spreche ich ja auch von "üblicherweise" und "idiomatischem" Python. Der üblicherweise verwendete -- und damit ein Standard, auch wenn nicht formal erzwungen -- Weg ist eben die Literalform.
Zu guter letzt ein paar Beispiele, was ich hier meine:
Toplevel, Button, Canvas, Frame, Text, ... Konstrucktor:

Code: Alles auswählen

def __init__(self, master=None, cnf={}, **kw):
Fast alle lassen die Wahl ob man cnf die Referenz eines Wörterbuches übergeben möchte, oder ob man sie als Argument Parameter übergibt.
Man hat eigentlich *immer* die Wahl, es sei denn, es gibt ein ``*args`` hinter Argumenten mit Default-Wert, etwa so:

Code: Alles auswählen

def foo(x, y=z, *args)
Dann kann man zusätzliche positionale Argumente nur dann angebene, wenn man ``y`` nicht als Keyword-Argument angibt. Meintest du das? Das hat dann auch nichts mit der Funktionssignatur zu tun, sondern das ist einfach eine Einschränkung von CPython.
colonelkurtz
User
Beiträge: 6
Registriert: Freitag 21. Januar 2011, 15:42

Hallo zusammen,

im folgenden die Skizze, so wie ich mir die GUI des Skripts vorstelle. Das Skript existiert schon fast vollständig ohne GUI. Es besteht nur aus ein paar Schleifen und Funktionsaufrufen. Ich hoffe die große Auflösung der Skizze stört nicht.

Bild

MfG,

Kurtz
Zuletzt geändert von colonelkurtz am Mittwoch 26. Januar 2011, 13:28, insgesamt 1-mal geändert.
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Dauerbaustelle hat geschrieben:Ich komm nicht mit. Wann bekommt man denn damit Schwierigkeiten, wenn man Keyword-Argumente in der vorgesehenen Weise, nämlich in den "Aufruf-Klammern", übergibt?
Nur bei der Übersichtlichkeit, wie ich schon mehrfach geschrieben hatte. Ansonst ist nur das Problem, das man keinen Zugriff mehr auf die übergebene Dictionary hat. Was aber in Tkinter mit ".configure"/".config" umgangen wurde und somit auf indirekte Art wieder möglich ist.
Dauerbaustelle hat geschrieben:Deswegen spreche ich ja auch von "üblicherweise" und "idiomatischem" Python. Der üblicherweise verwendete -- und damit ein Standard, auch wenn nicht formal erzwungen -- Weg ist eben die Literalform.
Ok, sehe ich ein, ich hatte dict nur immer gerne bei Einzeilern verwendet, lässt sich aber ändern.
Dauerbaustelle hat geschrieben:Man hat eigentlich *immer* die Wahl, es sei denn, es gibt ein ``*args`` hinter Argumenten mit Default-Wert, ...
Mir gehts hier nicht um Positionale Parameter im Allgemeinen, sondern um nur den "cnf", oder "options"-Parameter in den Tkinter-Klassen und Funktionen, denn dieser erwartet ein Wörterbuch, welches man auch als Schlüssel Argumente an "**kw" übergeben kann.
Über die "Tkinter._cnfmerge"-Funktion, werden diese immer zusammen gepackt und die Optionen ausgelesen und verwertet. Und die Wahl hat man halt nicht immer, denn diese muss von der API vorgegeben sein, weil die Optionen daraus ausgelesen werden müssen.
*args könnte dann ja hinter der Konfigvariable(cnf) kommen, was ja dennoch möglich wäre.
Dauerbaustelle hat geschrieben:Dann kann man zusätzliche positionale Argumente nur dann angebene, wenn man ``y`` nicht als Keyword-Argument angibt. Meintest du das? Das hat dann auch nichts mit der Funktionssignatur zu tun, sondern das ist einfach eine Einschränkung von CPython.
Das hat damit halt nicht viel zu tun, denn es geht hier weder um CPython, noch um eine unbegrenzte Zahl positionaler Argumente, sondern lediglich um die interne Strucktur von Tkinter.

@colonelkurtz
Man hätte die Skizze auch kurz runter skalieren können und unten den freien wegschneiden können.
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
colonelkurtz
User
Beiträge: 6
Registriert: Freitag 21. Januar 2011, 15:42

Servus,

nur kurz zur Info: Das Bild der Skizze wurde etwas handlicher skaliert.

MfG,

Kurtz
Zuletzt geändert von colonelkurtz am Mittwoch 26. Januar 2011, 13:57, insgesamt 2-mal geändert.
Antworten