bestimmt ausgabe im textctrl

Plattformunabhängige GUIs mit wxWidgets.
Bamba
User
Beiträge: 62
Registriert: Dienstag 31. Oktober 2006, 08:48

Freitag 15. Dezember 2006, 14:22

Hallo,

ich habe folgenden code:

Code: Alles auswählen

  def Out(self,*txt):
    for i in txt:
      print "%s" % str(i) ,
    print 
in der Datei textout.py. Ich habe in der Datei myapp.py einen button, der in einem textfeld den text ausgeben soll, den der obenstehende code produziert. Wie muss ich den obenstehenden code ändern?

Danke
Bamba
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

Freitag 15. Dezember 2006, 14:52

Code: Alles auswählen

class App(wx.Frame):
    """
         irgendeine Dummy-Klasse, weil keine Lust irgendwelche alten Posts nach
         Deinem Code zu durchsuchen
    """
    def __init__(self, ...):
         #etwas Code von Dir
         self.TextField = wx.TextCtrl(p,-1,"Nothing selected, yet.\n\n",size=sizetuple, wx.Default,style=wx.TE_MULTILINE)
         #sizetuple musst Du definieren
    def Out(self, txt):
        self.TextField.WriteText(txt)
Sollte im Prinzip genügen, allerdings ist mir nicht ganz klar, warum Du Deinen Code zu umständlich gestaltest. Wenn es einen besonderen Grund gibt, mußt Du das Beispiel halt entsprechend anpassen.
Ansonsten hat eine wx.TextCtrl wahnsinnig viele Methoden. Einfach mal anschauen - das öffnet viele Möglichkeiten.

HTH Christian
Bamba
User
Beiträge: 62
Registriert: Dienstag 31. Oktober 2006, 08:48

Freitag 15. Dezember 2006, 17:21

Danke. Ich habe bloß ein Problem: Das textctrl ist in der einen, die Funktion zum Anzeigen in einer anderen Datei. ich habe mit from Datei import * die Datei, wo sich das textfeld befindet eingebunden, kann es aber nicht ansprechen.

Code: Alles auswählen

class MyFrame(wx.Frame):
    def __init__(self, *args, **kwds):
        wx.Frame.__init__(self, *args, **kwds)
             self.Ausgabe_Text = wx.TextCtrl(self, -1, "", style=wx.TE_MULTILINE)
ich kann Ausgabe_Text einfach nicht ansprechen. Es müsste doch MyFrame.Ausage_Text heißen, oder?
Bamba
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

Freitag 15. Dezember 2006, 17:33

Hoi,

wie sieht denn Deine Importzeile aus?

Tut mir leid, aber ich gehe jetzt ins Wochenende und bin nicht mehr erreichbar - sicher wird jemand anderer hier aushelfen können.

Hinauseilenenden Gruß,
Christian
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Freitag 15. Dezember 2006, 21:13

@Bamba
Am besten poste ein reduziertes Beilspiel beider Files. So, steige ich da nicht durch.

>> Es müsste doch MyFrame.Ausage_Text heißen, oder?
Nein. `MyFrame` müsste im Parent instanziert werden.
Z.b.

Code: Alles auswählen

class App(wx.Frame):
[...]
    self.mf = MyFrame(...)
Danach könntest du `Ausage_Text` so ansprechen: `self.mf.Ausage_Text.

Aber wie gesagt, poste mal eine gekürzte Fassung deiner beiden Scripts. Ich weiß nämlich nicht genau was du meinst.

lg
Bamba
User
Beiträge: 62
Registriert: Dienstag 31. Oktober 2006, 08:48

Samstag 16. Dezember 2006, 13:33

Textausgabe.py

Code: Alles auswählen

import MyFrame

  def Out(self,*txt):
    for i in txt:
       MyFrame.MyFrame.Ausgabe_Text.AppendText(i)
MyFrame.py

Code: Alles auswählen

class MyFrame(wx.Frame):
    def __init__(self, *args, **kwds):
         self.Ausgabe_Text = wx.TextCtrl(self, -1, "", style=wx.TE_MULTILINE)
Reicht das an Code`?[/code]
Bamba
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Samstag 16. Dezember 2006, 18:46

Hi Bamba.

Wie schon geschrieben muss MyFrame erste instanziert werden, damit du darauf zurückgreifen kannst.

Code: Alles auswählen

import wx

class MyFrame(wx.Frame):
    def __init__(self, prnt, id=-1):
         self.Ausgabe_Text = wx.TextCtrl(parent=prnt, id=id, 
                                         style=wx.TE_MULTILINE,
                                         )

class MyFrame2(wx.Frame):
    def __init__(self, 
                 parent=None, 
                 id=-1, 
                 title = "MyApp"):
        
         wx.Frame.__init__(self, parent, id, title) 
         
         # Instanz von MyFrame
         mf = MyFrame(self)
         # So könnte man nun auf ``Ausgabe_Text`` von ``mf``
         # zurückgreifen.
         text = ["Hello World", "spam", "foo", "bar"]
         for txt in text:
             mf.Ausgabe_Text.AppendText("%s\n" % txt)

def main():
    app = wx.PySimpleApp()
    f = MyFrame2()
    f.Show()
    app.MainLoop()


if __name__ == "__main__":
    main()
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Samstag 16. Dezember 2006, 18:48

Wenn ``MyFrame`` in der Datei ``MyFrame.py`` drin ist, dann müsste man die Zeile

Code: Alles auswählen

mf = MyFrame(self)
zu

Code: Alles auswählen

mf = MyFrame.MyFrame(self) 
ändern.

lg
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Samstag 16. Dezember 2006, 18:51

MyFrame.py

Code: Alles auswählen

import wx

class MyFrame(wx.Frame):
    def __init__(self, prnt, id=-1):
         self.Ausgabe_Text = wx.TextCtrl(parent=prnt, id=id, 
                                         style=wx.TE_MULTILINE,
                                         )
Main.py

Code: Alles auswählen

import wx

import MyFrame


class MyFrame2(wx.Frame):
    def __init__(self, 
                 parent=None, 
                 id=-1, 
                 title = "MyApp"):
        
         wx.Frame.__init__(self, parent, id, title) 
         
         # Instanz von MyFrame
         mf = MyFrame.MyFrame(self)
         # So könnte man nun auf ``Ausgabe_Text`` von ``mf``
         # zurückgreifen.
         text = ["Hello World", "spam", "foo", "bar"]
         for txt in text:
             mf.Ausgabe_Text.AppendText("%s\n" % txt)
# etc
Bamba
User
Beiträge: 62
Registriert: Dienstag 31. Oktober 2006, 08:48

Sonntag 17. Dezember 2006, 13:19

Es tut mir ja leid wegen solch belanglosen Themen stören zu müssen, aber es funktioniert bei mir nicht. Ich habe die obigen Beispiele seperat ausprobiert - funktioniert. Dann habe ich das auf mein Beispiel übertragen - funktoniert nicht. Es kommt immer nur

'module' object has no attribute 'MyFrame'

Obwohl das Modul MyFrame die Klasse MyFrame hat. Das kann eigentlich nicht sein.

hier nochmals mein Code.

Code: Alles auswählen

import MyFrame

mf = MyFrame.MyFrame(self)

class Nachricht:
#  def __init__(self):
  debug_out = False
    
  def Out(self,*txt):
    for i in txt:
      print "%s" % str(i) ,
    print 

  def DOut(self,*txt):    # debug 
    if not self.debug_out: 
      return
    for i in txt:
      mf.Ausgabe_Text("%s"%str(i),)
      #print "%s"%str(i),
    print 
Ich wäre wirklich dankbar, wenn dieser Code jetzt endlich klappen würde.

Danke.
Bamba
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Sonntag 17. Dezember 2006, 20:55

Sorry, aber so funktioniert das nicht.

`MyFrame(self)` ist sozusagen ein Neues Widget das du geschrieben hast. Es erwartet bei der Erzeugung (Instanzierung -> ``mf = MyFrame.MyFrame(self)``) ein Parent-Fenster (Elternfenster. Also das darüberliegende Fenster in dem es erzeugt werden soll.) was zu `wx` gehört!

Du kannst ein wx Widget nicht einfach inerhalb einer Klasse instanzieren das nicht von einem anderen wx Objekt (Widget) abgeleitet ist.

Du solltest dich wirklich ein wenig mit den Basics von wxPython beschäftigen, damit du weißt wie das abläuft.

In meinen Beispiel:

Code: Alles auswählen

class MyFrame2(wx.Frame):
    def __init__(self,
                 parent=None,
                 id=-1,
                 title = "MyApp"):
        wx.Frame.__init__(self, parent, id, title)

[...]
`MyFrame2` ist die Spezialisierung (oder abgeleitete Klasse) `von wx.Frame`. Dieses Fenster wird später das Hauptfenster. Mit `wx.Frame.__init__(self, parent, id, title)` wird der Konstruktor von `wx.Frame`aufgerufen um `MyFrame2` zu initialisieren.

So, self ist nun `MyFrame2` (sry, ist nicht 100%ig richtig aber weiß momentan nicht wie ich das besser auszudrücken soll ;)). Das heißt, wenn Du jetzt ein wx Objekt (Widget) in `MyFrame2` erzeugen willst (bzw. wenn es nun darauf draufliegen soll), muss du an das objekt bei `parent` self angeben. Das ganze muss im Konstruktor stattfinden!
Bei deinem Widget sehe das so aus:

Code: Alles auswählen

mf = MyFrame.MyFrame(self)
Das `self` "sagt" nun das `MyFrame.MyFrame` in oder auf `MyFrame2` erzeugt werden soll.

So, mal konkreter ohne das ganz wirre Zeug:

Das grundgerüsst für ein wx Programm sieht immer in etwa so aus:

Code: Alles auswählen

import wx

# Das ist die Spezialisierung von `wx.Frame` (Abgeleitet Klasse). 
# Diese Spezialisierung wird später das Hauptfenster (Parent-Window)!
# Wenn man ein Hauptfenster erzeugen will, erbt man für gewöhnlich von 
# `wx.Frame`
class HauptFenster(wx.Frame):
    def __init__(self, parent=None, id=-1, title = "MyApp"):
         # den Konstruktor von `wx.Frame` aufrufen. Danach hat deine
         # Spezialisierung (abgeleitet Klasse) alle Eigenschaften von `wx.Frame`
         # + denen die du hinzufügst. 
         wx.Frame.__init__(self, parent, id, title)

def main():
    # Eine Instanz von `wx._core.PySimpleApp` erzeugen. 
    app = wx.PySimpleApp()
    # Jetzt wird das Hauptfenster, hier instanziert! 
    f = HauptFenster()
    # Hiermit wird "gesagt" das dass Hauptfenster angezeigt werden soll.
    f.Show()
    # Da nun, das Hauptfenster bestimmt worden ist (f.Show()), sagen wir jetzt
    # dass die Applikation gestartet werden soll und dabei das gerade bestimmte
    # Fenster `f` zum Haupotfenster macht!
    app.MainLoop()

if __name__ == "__main__":
    main()
So, nun hast du ein Huaptfenster. Alle widgents müssen jetzt im Konstruktor des Hauptfensters oder _eines_ anderen Widgets erzeugt werden und nicht außerhalb! In deinem Beispiel hast du den Fehler gemacht, das `mf = MyFrame.MyFrame(self)` außerhalb eines Hauptfensters oder Widgets erzeugt wurden ist ;)

Ein Beispiel wie man jetzt ein Widget im Hauptfenster erzeugt:

Code: Alles auswählen

import wx

class HauptFenster(wx.Frame):
    def __init__(self, parent=None, id=-1, title = "MyApp"):
         wx.Frame.__init__(self, parent, id, title)
         
         # Wir wollen nun ein Textfeld im Hauptfenster erzeugen.
         # So, das Keyword ``parent`` erwartet eine Referenz eines
         # wx Hauptfensters oder eines anderen wx Objekts (Widget) _in_ dem es
         # erzeugt werden soll. Da wir wollen das es im Hauptfenster erzeugt
         # wird, übergeben wir hier `self`
         self.Ausgabe_Text = wx.TextCtrl(parent=self, # erwartet eine Referenz...
                                         id=-1,
                                         style=wx.TE_MULTILINE,
                                         )
         
def main():
    app = wx.PySimpleApp()
    f = HauptFenster()
    f.Show()
    app.MainLoop()

if __name__ == "__main__":
    main()

So, und nun sag mal was genau du machen willst. Dann setze ich mich ran und baue dir ein Beispiel.

lg
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Montag 18. Dezember 2006, 07:31

Wenn du ein Debug-Fesnter machen willst, und dann stdout oder stderr Ausgaben ins Debug-Fenster umleiten willst, geht das so.

Code: Alles auswählen

import wx
import sys

class DebugWindow(wx.Frame):
    def __init__(self, parent):
        self.debug_window = wx.TextCtrl(parent=parent,
                                        id=-1,
                                        style=wx.TE_MULTILINE,
                                       )
        self.write = self.debug_window.AppendText
        
class HauptFenster(wx.Frame):
    def __init__(self, parent=None, id=-1, title = "MyApp"):
         wx.Frame.__init__(self, parent, id, title)
         
         self.debug_window = DebugWindow(parent=self)
         
         sys.stdout = self.debug_window
         # Folgendes wird in `self.debug_window.debug_window`ausgegeben und nciht in der Konsole.
         print >>sys.stdout, "foo"
         print >>sys.stdout, "bar"
         print >>sys.stdout, "spam"
         print "Hello World"
         
         for i in xrange(100):
             print i,

def main():
    app = wx.PySimpleApp()
    f = HauptFenster()
    f.Show()
    app.MainLoop()

if __name__ == "__main__":
    main()
Folgendes erscheint im TextCtrl nach dem Programmstart:

Code: Alles auswählen

foo
bar
spam
Hello World!
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
Natürlich kannst du auch stderr umleiten -> `sys.stderr = self.debug_window`

Wie funktioniert das?
Ganz einfach, die klasse muss ein Attribut ``write``haben das auf ein callable zeigt, das Argumente empfangen kann. Z.B. -> `self.write = self.debug_window.AppendText`. Alles was im obigen Beispiel an `sys.stdout` mit `print`"übergeben" wurde, wird an `self.debug_window.AppendText` gereicht :)

Ich hoffe ich konnte dir damit ein wenig helfen. Wenn ich das Falsch verstanden habe, dann nochmal konkreter beschrieben was du genau willst.

lg
sape
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

Montag 18. Dezember 2006, 14:23

Hoi Bamba

so, Wochenende vorbei und ich sehe, daß - soweit ich das verstanden habe - Dein Problem von "sape" gelöst wurde. Dein letztes Beispiel schaut relativ wirre aus. Der springende Punkt ist tatsächlich, daß Du den Ausgabepuffer ersetzen mußt, wenn Du mit print in Dein TextCtrl schreiben willst. Das würde ich persönlich aber bleiben lassen, sondern mir irgendeine write-Methode schaffen, die direkt in die TextCtrl schreibt, wie ich das möchte (z. B. könnte man in einen Puffer schreiben, um eine zweite Ausgabe zu nutzen) oder ebend die Methoden benutzten, die TextCtrl ohnehin zur Verfügung stellt (wie "AppendText").

Der entschreidende konzeptuelle Fehler, den ich bei Dir aber sehe ist der, daß Du versuchst Dein GUI irgendwo zu importieren - ohne das es gestartet ist - und dann etwas damit zu machen. Das wird nicht funktionieren, egal welches Programmiersprache und welches Framework Du benutzt (etwas Vergleichbares gibt es allerdings durchaus). Was Du machen mußt ist folgendes: Eine Applikation schreiben, die das GUI zur Verfügung stellt. Diese darf auch x-beliebige andere Module importieren, muß diese aber selber verwalten. Wenn Dein Frame nun einen Out-Methode hat (oder Write oder sonstwie), diese Methode sonstwo stehen - der Event, der aber die Methode aufruft muß aus Deinem GUI kommen. So kann beispielsweise Out im unten stehenden Beispiel ausgelagert werden und mit "from filename import Out" wieder eingebunden werden. So etwas findet man oft, um Code übersichtlicher zu machen.

Gruß,
Christian

Und noch ein Beispiel (aber eigentlich ist sape's Beispiel schöner - nur manchmal hilft es halt, wenn verschiedene Leute mit verschiedenen Beispielen etwas erklären):

Code: Alles auswählen

import wx

class MyFrame(wx.Frame):
    def __init__(self, parent, id, titel):
        wx.Frame.__init__(self, None, -1, "title")
        p = wx.Panel(self, -1)

        sizer = wx.BoxSizer(wx.VERTICAL)

        self.TextField = wx.TextCtrl(p, -1, "Nothing selected, yet.\n\n", size=(400,400), style=wx.TE_MULTILINE)
        sizer.Add(self.TextField)


        sizer2 = wx.BoxSizer(wx.HORIZONTAL)

        closebutton = wx.Button(p, -1 ,"Close Me Here")
        sizer2.Add(closebutton)
        self.Bind(wx.EVT_BUTTON, self.OnClose, closebutton)

        writebutton = wx.Button(p, -1 ,"write some text")
        sizer2.Add(writebutton)
        self.Bind(wx.EVT_BUTTON, self.OnWrite, writebutton)

        sizer.Add(sizer2)

        p.SetSizerAndFit(sizer)
        self.SetClientSize(p.GetSize())

    def OnClose(self, event = None):
        self.Destroy()
        #und andere Methoden zum Aufraeumen

    def Out(self, txt):
        self.TextField.WriteText(txt)

    def OnWrite(self, event = None):
        self.Out("etwas Text\n")

class SimpleApp(wx.App):
    def OnInit(self):
        frame = MyFrame(None, -1, "")
        frame.Show(True)
        self.SetTopWindow(frame)
        return True
        
app = SimpleApp(0)
app.MainLoop()
Bamba
User
Beiträge: 62
Registriert: Dienstag 31. Oktober 2006, 08:48

Montag 18. Dezember 2006, 14:53

Jo. Vielen Dank erstmal. Aber ihr habt was übersehen:

Euer Code ist in einer Datei. Das was ich meinte ist in zwei Dateien s. oben. Jetzt meine Frage: Dann muss ich ja eigentlich importieren. Kann ich das auch mit Polymorphismus schaffen? Ich will in der lib (Nachricht.py) keine import Anweisungen der Gui (die lib soll alleine auskommen). Aber wie geht es das notwendige import zu umgehen?

Nochmals danke.

Bamba
Bamba
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

Montag 18. Dezember 2006, 15:17

Bamba hat geschrieben: Ich will in der lib (Nachricht.py) keine import Anweisungen der Gui (die lib soll alleine auskommen). Aber wie geht es das notwendige import zu umgehen?
Es gelten die gleichen Importregeln wie sonst auch. Du kannst das Import nicht "umgehen". Was soll "die lib soll alleine auskommen" heißen? Deine Nachricht-Klasse ist kein lauffähiges Programm. Deine GUI kann mit Deiner Nachricht-Klasse arbeiten. Wenn die Nachricht-Klasse das GUI als parent hat, kann sie auch Informationen zurückgeben, aber ansonsten: Zeige doch mal den relevanten Code Deines GUIs, sage was Du machen willst und wir gehen das mal durch.

Gruß,
Christian
Antworten