Seite 1 von 1

"print" umleiten, aber in echtzeit?

Verfasst: Montag 23. August 2010, 14:47
von p90
Hi,
habe folgendes Problem.
Habe mir für mein Commandozeilenprogramm einen GUI geschrieben.
Dazu habe ich den GUI als einzelnes Programm und importe dann mein Commandozeilenprogramm (ab hier nenne ich es mal CALC)
Jetzt habe ich für CALC aber einige Sachen die ich per "print" rauschreibe und im GUI anzeigen möchte.
Immo mache ich das so:

CALC:

Code: Alles auswählen

from optparse import OptionParser
	
def main(inputfolders, output = None):
	if not output == None:
		import sys
		sys.stdout = output
        #ganz viel anders Zeug...

if __name__ == "__main__":
	inputfolders = []
	inputfolders.append('test')
	inputfolders.append('test2')
	# Comamndline parser Kram
	if options.main:
		main(inputfolders)
GUI:
(es sei erwähnt das das hier mein erstes wxPython Programm ist und dementsprechend hoch ist der Copy und Past Anteil, wem das Programm also bekannt vorkommt, nicht wundern, habe nur einige Zeilen verändert)

Code: Alles auswählen

#!/usr/bin/python

import os
import wx
import sys
from wx.lib.mixins.listctrl import CheckListCtrlMixin, ListCtrlAutoWidthMixin
import CALC as lab

meas = []
inputpath = '/data/auger/amiga/hodoscope/'
for path, dirs, files in os.walk(inputpath):
      if len(dirs) == 0: 
		#a = os.path.split(path) #Messnummer
		#b = os.path.split(a[0]) # Datum
		#c = os.path.split(b[0]) # Comments
		#d = os.path.split(c[0]) # PMT
		#measnumb = str(os.path.join(b[1] ,a[1]))
		#meascomm = str(c[1])
		#measpmt = str(d[1])
		#meas.append((measnumb, meascomm, measpmt))
		meas.append(path)

class CheckListCtrl(wx.ListCtrl, CheckListCtrlMixin, ListCtrlAutoWidthMixin):
    def __init__(self, parent):
        wx.ListCtrl.__init__(self, parent, -1, style=wx.LC_REPORT | wx.SUNKEN_BORDER)
        CheckListCtrlMixin.__init__(self)
        ListCtrlAutoWidthMixin.__init__(self)

class Repository(wx.Frame):
    def __init__(self, parent, id, title):
        wx.Frame.__init__(self, parent, id, title, size=(900, 400))

        panel = wx.Panel(self, -1)

        vbox = wx.BoxSizer(wx.VERTICAL)
        hbox = wx.BoxSizer(wx.HORIZONTAL)

        leftPanel = wx.Panel(panel, -1)
        rightPanel = wx.Panel(panel, -1)

        self.log = wx.TextCtrl(rightPanel, -1, style=wx.TE_MULTILINE)
        self.list = CheckListCtrl(rightPanel)
	self.list.InsertColumn(0, 'Path')
        #self.list.InsertColumn(0, 'Date/Number', width=140)
	#self.list.InsertColumn(1, 'Comment')
        #self.list.InsertColumn(2, 'PMT')

        for i in meas:
            index = self.list.InsertStringItem(sys.maxint, i)
            #self.list.SetStringItem(index, 1, i[1])
            #self.list.SetStringItem(index, 2, i[2])

        vbox2 = wx.BoxSizer(wx.VERTICAL)

        sel = wx.Button(leftPanel, -1, 'Select All', size=(100, -1))
        des = wx.Button(leftPanel, -1, 'Deselect All', size=(100, -1))
        apply = wx.Button(leftPanel, -1, 'Apply', size=(100, -1))


        self.Bind(wx.EVT_BUTTON, self.OnSelectAll, id=sel.GetId())
        self.Bind(wx.EVT_BUTTON, self.OnDeselectAll, id=des.GetId())
        self.Bind(wx.EVT_BUTTON, self.OnApply, id=apply.GetId())

        vbox2.Add(sel, 0, wx.TOP, 5)
        vbox2.Add(des)
        vbox2.Add(apply)

        leftPanel.SetSizer(vbox2)

        vbox.Add(self.list, 1, wx.EXPAND | wx.TOP, 3)
        vbox.Add((-1, 10))
        vbox.Add(self.log, 0.5, wx.EXPAND)
        vbox.Add((-1, 10))

        rightPanel.SetSizer(vbox)

        hbox.Add(leftPanel, 0, wx.EXPAND | wx.RIGHT, 5)
        hbox.Add(rightPanel, 1, wx.EXPAND)
        hbox.Add((3, -1))

        panel.SetSizer(hbox)

        self.Centre()
        self.Show(True)

    def OnSelectAll(self, event):
        num = self.list.GetItemCount()
        for i in range(num):
            self.list.CheckItem(i)

    def OnDeselectAll(self, event):
        num = self.list.GetItemCount()
        for i in range(num):
            self.list.CheckItem(i, False)

    def OnApply(self, event):
        num = self.list.GetItemCount()
        inputfolders = []
	for i in range(num):
            if i == 0: self.log.Clear()
            if self.list.IsChecked(i):
		#path = os.path.join(inputpath, )
		#measnumb = self.list.GetItemText(i)
		#meascomm = self.list.GetItemText(i)
		#measpmt = self.list.GetItemText(i)
		#inputfolders.append(path)
		inputfolders.append(self.list.GetItemText(i))
                #self.log.AppendText(self.list.GetItemText(i) + '\n')
	
	lab.main(inputfolders, self.log)
	self.log.AppendText("Finished" + '\n')
	#self.log.AppendText("%s" + '\n')


app = wx.App()
Repository(None, -1, 'Measurements')
app.MainLoop()
Sorry, das ich euch beim GUI das Programm nicht vereinfachen konnte aber wie gesagt, das ist mein erster wxPython Versuch und ich weis noch nicht was vlt hier Probleme geben kann.

Das Problem ist nun, so wie es ist geht es.
Es zeigt alle "prints" an, aber erst NACHDEM CALC wieder beendet wurde.
Also sagen wir ich mache alle 100s "print 'Hallo'" und das 10 mal.
Dann passiert erst 1000s nichts um dann auf einen Schlag 10 mal Hallo anzuzeigen.
Da ich das ganze aber dazu benutzen möchte dem Benutzer zu zeigen wie weit das Programm bereits ist, ist dies etwas suboptimal.

Hoffe jemand kann mir einen Tipp geben.
bis später


p90

Re: "print" umleiten, aber in echtzeit?

Verfasst: Montag 23. August 2010, 15:52
von ms4py
Wenn du mit einer GUI arbeitest, solltest du Ausgaben auch auf der Oberfläche darstellen und nicht in der Konsole (höchstens Logging-Informationen, aber um das scheint es ja nicht zu gehen). Dann besteht das Problem erst gar nicht...

Dazu musst du eben das Design von "CALC" entsprechend anpassen, dass es nicht mehr mit `print` arbeitet. Stattdessen solltest du Rückgabe-Werte verwenden.

Edit. Was mir noch auf die Schnelle aufgefallen ist: Auf `None` überprüft man nicht mit `==` sondern mit `is`.

Edit2. Mit `wx.App(None)` müsste das Problem außerdem behoben sein. So solltest du allerdings nicht vorgehen (s. mein restlicher Post).

Re: "print" umleiten, aber in echtzeit?

Verfasst: Montag 23. August 2010, 21:55
von p90
Hi,

am Ende will ich ja zwei Programme haben, eins für die Kommandozeile und halt einen GUI um es zu vereinfachen + wird einem doch immer gesagt "Trennt den Code in logische Segmente! Besonders Berechnung von GUI!"

Konnte deine Tipps leider noch nicht ausprobieren, habe meinen Laptop mit dem Code liegen gelassen aber werde mich sofort zurück melden wenn ich es ausprobiert habe.

Bis später


p90

Re: "print" umleiten, aber in echtzeit?

Verfasst: Montag 23. August 2010, 22:34
von cofi
p90 hat geschrieben:wird einem doch immer gesagt "Trennt den Code in logische Segmente! Besonders Berechnung von GUI!"
Statt GUI wuerde ich von UI sprechen. Auch die Darstellung im Terminal hat nichts mit der eigentlichen Logik zu tun, darauf wollte auch ms4py raus, wenn ich seinen Post richtig lese.

Re: "print" umleiten, aber in echtzeit?

Verfasst: Dienstag 24. August 2010, 00:24
von p90
Hi,

okay, das musst du mir erklären.
Wie gesagt, ich schreibe per Print immer nur das momentan bearbeitete Verzeichnis raus damit man sieht wo das Programm gerade ist.
Vlt hab ich ja auch einfach ein grundlegendes Prinzip nicht verstanden?

bis später und danke für die Antworten


p90

Re: "print" umleiten, aber in echtzeit?

Verfasst: Dienstag 24. August 2010, 03:34
von BlackJack
@p90: Auch die Ein- und Ausgabe in der Konsole ist UI die man von der Logik trennen kann/sollte. Die Logik sollte also auch davon unabhängig sein und ``print``/`input()`/`raw_input()` haben dort nichts zu suchen. Statt in der Logik ein ``print`` einzubauen, könntest Du zum Beispiel eine Rückruffunktion entgegennehmen, die von der Logik mit dem aktuell verarbeiteten Verzeichnis aufgerufen wird. Im Konsolenprogramm kannst Du dort eine Funktion übergeben, die den Verzeichnisnamen mit ``print`` ausgibt, und im GUI-Programm eine Funktion, die ihn irgendwo in der GUI anzeigt.

Wenn der Logik an der Stelle, wo der aktuell verarbeitete Verzeichnisname ausgegeben werden soll, auch bekannt ist, wie viele es insgesamt sind, könnte man die Infos Gesamtanzahl und aktueller Index ebenfalls der Rückruffunktion mitgeben. Dann kann man sie auch benutzen um den Fortschritt in Form von Prozenten in der Konsole oder eines Fortschrittsbalkens in der GUI darzustellen. In wxPython wäre da `wx.ProgressDialog` einen Blick wert.

Bei der GUI musst Du übrigens bei langlaufenden Berechnungen noch beachten, dass die GUI-Darstellung "einfriert" wenn die Hauptschleife des GUI-Toolkits nicht zum Zuge kommt. Wenn die Abstände zwischen einzelnen Rückmeldungen von der Logik kurz sind, reicht es die `wx.App.Yield()`-Methode aufzurufen. Ansonsten müsste die Berechnung in einem eigenen Thread laufen. Im Modul `wx.lib.delayedresult` gibt es da ein wenig Hilfestellung das mit der GUI zu verbinden.

Um einen Abbruchwunsch vom Benutzer in die Logik zu tragen, könnte die Rückruffunktion einen Wahrheitswert zurückgeben, der von der Logik dann entsprechend interpretiert wird.

Re: "print" umleiten, aber in echtzeit?

Verfasst: Dienstag 24. August 2010, 21:56
von p90
Hi,

hm, verstehe es einfach nicht.

Wenn ich doch z.B. das hier mache:

Code: Alles auswählen

import wx
class Test(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None, -1, "Test")
        print "Halloe"

app = wx.App()
Test()
app.MainLoop()

PS:
Und noch viel schlimmer, wie bekomme ich das Fenster wieder weg?
Dachte es läge daran das ich mein Panel entfernt hatte, aber es geht trotzdem nicht mehr weg.

Bekomme ich ein schönes zweites Fenster in dem dann alle Prints auftauchen.
Sowas bräuchte ich ja nur.
Ich möchte halt nur wissen ob in einem Verzeichnis ein Fehler auftrat oder nicht.
Da ich nicht weiß wie viele Dateien in einem Verzeichnis sind kann ic auch keine Progressbar verwenden.