Seite 1 von 1
Zugriff per Modul auf Grafikobjekte
Verfasst: Mittwoch 18. März 2009, 20:30
von Jan42
Hallo,
Ich schreibe für Info ein rss-reader und habe alles bis auf die Ausgabe.
Der Grafikcode:
Code: Alles auswählen
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'gui.ui'
#
# Created: Wed Mar 18 18:12:28 2009
# by: PyQt4 UI code generator 4.4.3
#
# WARNING! All changes made in this file will be lost!
from PyQt4 import QtCore, QtGui
class Ui_pthreader(object):
def setupUi(self, pthreader):
pthreader.setObjectName("pthreader")
pthreader.resize(702, 545)
self.TextEditAusgabe = QtGui.QPlainTextEdit(pthreader)
self.TextEditAusgabe.setGeometry(QtCore.QRect(10, 50, 681, 441))
self.TextEditAusgabe.setObjectName("TextEditAusgabe")
self.widget = QtGui.QWidget(pthreader)
self.widget.setGeometry(QtCore.QRect(10, 10, 681, 45))
self.widget.setObjectName("widget")
self.gridLayout = QtGui.QGridLayout(self.widget)
self.gridLayout.setObjectName("gridLayout")
self.label = QtGui.QLabel(self.widget)
self.label.setObjectName("label")
self.gridLayout.addWidget(self.label, 0, 0, 1, 1)
self.rss_link = QtGui.QLineEdit(self.widget)
self.rss_link.setObjectName("rss_link")
self.gridLayout.addWidget(self.rss_link, 0, 1, 1, 1)
self.buttonSpeichern = QtGui.QPushButton(self.widget)
self.buttonSpeichern.setObjectName("buttonSpeichern")
self.gridLayout.addWidget(self.buttonSpeichern, 0, 2, 1, 1)
self.widget1 = QtGui.QWidget(pthreader)
self.widget1.setGeometry(QtCore.QRect(10, 500, 681, 45))
self.widget1.setObjectName("widget1")
self.gridLayout_2 = QtGui.QGridLayout(self.widget1)
self.gridLayout_2.setObjectName("gridLayout_2")
self.buttonAbrufen = QtGui.QPushButton(self.widget1)
self.buttonAbrufen.setObjectName("buttonAbrufen")
self.gridLayout_2.addWidget(self.buttonAbrufen, 0, 0, 1, 1)
self.buttonBeenden = QtGui.QPushButton(self.widget1)
self.buttonBeenden.setObjectName("buttonBeenden")
self.gridLayout_2.addWidget(self.buttonBeenden, 0, 1, 1, 1)
self.retranslateUi(pthreader)
QtCore.QMetaObject.connectSlotsByName(pthreader)
def retranslateUi(self, pthreader):
pthreader.setWindowTitle(QtGui.QApplication.translate("pthreader", "Dialog", None, QtGui.QApplication.UnicodeUTF8))
self.label.setText(QtGui.QApplication.translate("pthreader", "RSS-Link", None, QtGui.QApplication.UnicodeUTF8))
self.buttonSpeichern.setText(QtGui.QApplication.translate("pthreader", "Speichern", None, QtGui.QApplication.UnicodeUTF8))
self.buttonAbrufen.setText(QtGui.QApplication.translate("pthreader", "Abrufen", None, QtGui.QApplication.UnicodeUTF8))
self.buttonBeenden.setText(QtGui.QApplication.translate("pthreader", "Beenden", None, QtGui.QApplication.UnicodeUTF8))
das Hauptprogramm:
Code: Alles auswählen
# -*- coding: cp1252 -*-
import sys
import rss_save
import urlcon
import search
import output
from PyQt4 import QtGui, QtCore
from gui import Ui_pthreader as Uipt
class MeinDialog(QtGui.QDialog, Uipt):
def __init__(self):
QtGui.QDialog.__init__(self)
self.setupUi(self)
self.connect(self.buttonSpeichern,
QtCore.SIGNAL("clicked()"), self.onSpeichern)
self.connect(self.buttonBeenden,
QtCore.SIGNAL("clicked()"), self.onBeenden)
self.connect(self.buttonAbrufen,
QtCore.SIGNAL("clicked()"), self.onAbrufen)
self.TextEditAusgabe.setReadOnly(True)
def onSpeichern(self):
d = self.rss_link.text()
rss_save.save(d)
a = self.rss_link.setText('')
def onBeenden(self):
self.close()
def onAbrufen(self):
urlcon.connect()
search.rssparse()
output.display()
app = QtGui.QApplication(sys.argv)
dialog = MeinDialog()
dialog.show()
sys.exit(app.exec_())
und das Modul output, welches mir Sorgen bereitet:
Code: Alles auswählen
def display():
from PyQt4 import QtGui, QtCore
from pthreader import MeinDialog
datei = "C:\Python25\geparsed.txt"
in_file = open(datei, "r")
weiter = True
while weiter:
line = in_file.readline()
if line.find("abcde12345") !=-1:
weiter = False
else:
MeinDialog.self.TextEditAusgabe.setPlainText(line)
Das letzte Modul macht folgendes. Die Module connect und search haben die wichtigen Sachen heruntergeladen und gefiltert.
Dieser fertige Text aus Nachrichten ist in der Datei geparsed.txt gespeichert.
Dieses Modul soll jetzt diese Datei öffnen, Zeile für Zeile auslesen und in das PlainTextEdit-Feld Zeile für Zeile eingefügen. Ich bekomm es aber nicht hin, dass das Modul auf die Grafikobjekte zugreift. Kann mir einer helfen? Es geht nur noch um die Ausgabe, wenn das Auszugebende schon in einer Datei existiert.
Verfasst: Mittwoch 18. März 2009, 21:54
von jerch
Hui, da sind aber einige Ungereimtheiten drin (habs nur mal schnell überflogen). Wenn ich Dich richtig verstanden habe (bzw. Deinen Code), willst Du von display() aus das Ganze starten. Hierzu meine Anmerkungen:
- Imports sollten in der Regel nicht in Funktionen gemacht werden.
- Du importierst zwar MeinDialog, instanziierst aber nicht. MeinDialog.self.Text..... ist ziemlicher Tobak, schau doch bitte mal in einschlägigen Python-Tutorials, wie das mit den Klassen in Python gemacht wird und was self bedeutet.
- Deine GUI wird angezeigt. Jepp, weil in pthreader.py Code steht, der während des Imports ausgeführt wird. Siehe hierzu Python-Tutorials, Stichwort import.
- gui.ui brauchst Du nicht mit pyuic zu übersetzen, besser ist es, das zur Laufzeit in Deiner MeinDialog-Klasse via uic-Loader zu machen. Da gibts hier ein paar Threads zu.
Der zweite und dritte Punkt sind der Grund für das vermeintliche Fehlverhalten.
Grüße, Jerch
Verfasst: Mittwoch 18. März 2009, 22:24
von Jan42
Gibt es eine Möglichkeit, das auch ohne Modul zu machen? Den Code direkt in die def onAbrufen schreiben?
Was ist instanzieren? In der PyQt4 Doku die ich nutze wird nicht wirklich erklärt, wie und warum die class MeinModul so geschrieben werden muss.
Es nutzt mir auch nicht, was self oder import genauer ist. Das weiß ich ja. Was ich nicht weiß, ist was ich jetzt importieren muss. Prinzipiell ja eigentlich die gesamte Grafik.
So nen Beispiel wär gut. Das versteht man leichter als sich irgendwo allgemeine Sachen durchzulesen ohne das man weiß, wie das auf eigene Programm angewendet wird.
Verfasst: Mittwoch 18. März 2009, 22:36
von jerch
Vergiß bitte Deine display()-Funktion, da stecken noch viel mehr Fehler drin (Dateihandling mit try-finally, Applikationsdaten ins Python verz?)
Ich kuck mal über Dein MeinDialog drüber, die Funktionalität ist hier am besten aufgehoben.
Verfasst: Mittwoch 18. März 2009, 22:41
von jerch
Ein paar Fragen noch:
Wie soll sich das Programm denn verhalten? Soll die Datei bei Aufruf einmal geparst werden oder in regelmäßigen Abständen? Manipuliert an der Datei ein anderes Programm rum, während es von der Gui geparst werden soll?
Verfasst: Mittwoch 18. März 2009, 23:19
von jerch
Ok, hab jetzt erst gesehen, das Du display() ja in onAbrufen aufrufst. Das einfachste ist, hier den Aufruf mit folgendem Code zu ersetzen:
Code: Alles auswählen
datei = "C:\Python25\geparsed.txt"
with open(datei, "r") as file:
for i in file:
if i.find("abcde12345") !=-1:
break
self.TextEditAusgabe.setPlainText(i)
(Keine Ahnung, was Du mit dem find('abc123') und dem setPlainText() verfolgst)
Um das
with in Python 2.5 nutzen zu können, mußt Du es noch importieren (ab 2.6 Standard):
Um zu verhindern, das der Code der oberen Ebene bei Modulimport ausgeführt wird, solltest Du den Code mit
maskieren.
Verfasst: Mittwoch 18. März 2009, 23:24
von Jan42
Also die def onSpeichern speichert rss-links untereinander in einer textdatei.
Wenn man auf den Button Abrufen geht, greift ein Modul auf diese Datei zu und läd zu jedem link in der Datei den Inhalt als xml Dokument herunter.
Direkt nach dem runterladen, noch bevor der nächste Link kommt, müsste der parser einschreiten und das wichtigste rausfiltern und in eine seperate Datei schreiben. Direkt danach müsste das ausgegeben werden, noch bevor, die connect funktion den zweiten link geladen hat. also der parser müsste eigentlich in der function connect aufgerufen werden. Das hatte ich schon mal. Hat funktioniert. Doch hier müsste dann auch der Code zum Ausgeben aufgerufen werden. Daher wollte ich das per Modul machen, leider gabs da dann Probleme.
Edit: das abcde12345 ist einfach um zu erkennen, dass das das Ende der Datei ist. Nach Leerzeilen kann ich nicht suchen, da sie zwischendrin vorhanden sind. Anders weiß ich nicht wie man noch das ende der Datei erkenntlich machen kann. Sonst würde er ja unendlich leere Zeilen ausgeben. setPlain...soll laut meinem Pythonbuch der Ausgabebefehl für son großes Textfeld sein.
Edit2: Das Problem bei deiner Lösung wäre auch, dass ich nur ein einzigen Link nehmen darf. Weil bei mehreren einfach der letzte die ersten datein überschreibt. Daher müsste diese Art von Befehlen in die connectfunktion eingefügt werden.
Verfasst: Mittwoch 18. März 2009, 23:40
von Jan42
Vielen vielen dank. Statt der Abfrage mit readline hab ich einfach
Code: Alles auswählen
datei = "C:\Python25\geparsed.txt"
with open(datei, "r") as file:
text = file.read()
self.TextEditAusgabe.setPlainText(text)
genommen. Aber jetzt bräuchte ich noch Hilfe dabei, wie ich diesen Code als Modul in der funktion connect ausführe. Das Problem ist die Sache, mit dem Import. Ich weiß nicht, was genau ich importieren soll, da ich bei dem Grafikmodul auch schon nicht genau weiß, was die einzelnen Befehle genau machen. So wie es jetzt ist, würde es nur für einen einzelnen Link gehen.
Gruß Jan.
Danke

Verfasst: Donnerstag 19. März 2009, 00:03
von jerch
Was ist instanzieren? In der PyQt4 Doku die ich nutze wird nicht wirklich erklärt, wie und warum die class MeinModul so geschrieben werden muss.
Es nutzt mir auch nicht, was self oder import genauer ist. Das weiß ich ja. Was ich nicht weiß, ist was ich jetzt importieren muss.
Hoppla, wie mir scheint, fehlen Dir ein paar grundsätzliche Dinge in Sachen OOP (Objektorientierte Programmierung) und dessen Paradigmen.
Quellen zum Erlesen:
http://de.wikipedia.org/wiki/Objektorie ... rammierung
http://de.wikipedia.org/wiki/Objektorientierung
http://docs.python.org/tutorial/classes.html <-- das Ganze dann für Python
Auch bin ich mir nicht sicher, ob für Dich die "volle Breitseite" mit PyQt hier der richtige Weg ist, da Dir offensichtlich grundlegenderes Wissen zu OOP fehlt und PyQt nicht die besten Docs hat (viele C++ Bsp.)
zu Deinem connect-Problem:
Deine "__main__"-Kontext ist in pthreader.py und Deine Gui-MainThread-Klasse Dein MeinDialog. Versuche bitte nicht in urlcon.py irgendwas von MeinDialog zu importieren. Umgedreht wird ein Schuh draus. (Irgendwie habe ich das Gefühl, das Du die Kirche ums Dorf trägst.) Erklär bitte nochmal genau, was Du hier willst.
zum File-Ende:
Mit "for i in file" interiert Python automatisch bis zum Ende.
Verfasst: Donnerstag 19. März 2009, 00:32
von jerch
Noch eine Frage:
Warum der Umweg über eine Datei, wenn Du eh alles direkt hintereinander weg ausführst und die Datei komplett wieder einliest? (und bitte nicht ins Pythonverzeichnis mit sowas)
Verfasst: Donnerstag 19. März 2009, 00:41
von Jan42
Bis jetzt habe ich eine Liste von rss-links, die durch einzelnes speichern verschiedener Links mit dem Button Speichern und der Funktion
Code: Alles auswählen
def save(a):
datei = "C:/Python25/rss_database.txt"
in_file = open(datei, "a")
in_file.write(a+"\n")
in_file.close()
erstellt wird. Drückt man auf den Button Abrufen so wird zuerst die Funktion connect inistialisiert.
Code: Alles auswählen
def connect():
import urllib
datei = "C:/Python25/rss_database.txt"
in_file = open(datei, "r")
while True:
url = in_file.readline()
if len(url) == 0:
break
rss = urllib.urlretrieve(url, "rss.xml")
in_file.close()
Wie man sieht, arbeitet er nacheinander jede Zeile ab, und überschreibt so immer wieder die vorherige xml Datei mit dem nächsten Link.
Direkt nach dem runterladen müsste der Parser
Code: Alles auswählen
def rssparse():
file = open("c:/Python25/rss.xml")
weiter = True
while weiter:
line = file.readline()
if len(line) == 0:
weiter = False
else:
if line.find("<item>") != -1:
line = file.readline()
zeile = line
for i in range(1,7):
if zeile.find("<title>") != -1:
a = zeile.find(">")
b = zeile.rfind("<")
Titel = zeile[a+1:b]
if zeile.find("<link>") != -1:
a = zeile.find(">")
b = zeile.rfind("<")
Link = zeile[a+1:b]
if zeile.find("<description>") != -1:
a = zeile.find(">")
b = zeile.rfind("<")
Description = zeile[a+1:b]
if zeile.find("<pubDate>") != -1:
a = zeile.find(">")
b = zeile.rfind("<")
Date = zeile[a+1:b-5]
if zeile.find("</item>") != -1:
break
zeile = file.readline()
datei = "C:/Python25/geparsed.txt"
in_file = open(datei, "a")
in_file.write(Titel+"\n")
in_file.write(Description+"\n")
in_file.write(Date+"\n")
in_file.write(Link+"\n\n\n")
if line.find("<entry>") != -1:
line = file.readline()
zeile = line
for i in range(1,7):
if zeile.find("<title>") != -1:
a = zeile.find(">")
b = zeile.rfind("<")
Titel = zeile[a+1:b]
if zeile.find("<link") != -1:
a = zeile.find("=")
b = zeile.find("/>")
Link = zeile[a+2:b-2]
if zeile.find("<updated>") != -1:
a = zeile.find(">")
b = zeile.find("<")
Date1 = zeile[a+1:b-10]
Date2 = zeile[a+12:b-1]
if zeile.find("<summary>") != -1:
a = zeile.find(">")
b = zeile.rfind("<")
Summe = zeile[a+1:b]
if zeile.find("</entry>") != -1:
break
zeile = file.readline()
datei = "C:/Python25/geparsed.txt"
in_file = open(datei, "a")
in_file.write(Titel+"\n")
in_file.write(Summe+"\n")
in_file.write(Date1+" "+Date2+"\n")
in_file.write(Link+"\n\n\n")
file.close()
in_file.close()
initialisert werden, damit für jeden Link die entsprechende xml Datei gleich geparsed und das Ergebnis in der Datei geparsed.txt gespeichert wird. Leider überschreibt diese funkction die Datei auch mit jedem neuen Parsvorgang. Also müsste direkt nach dem Parser das ganz ausgegeben werden. Daher hier das Modul für die Ausgabe. Doch dabei gab es ja Probleme mit dem importieren.
Ich bin sicher das ist alles ganz umständlich gemacht. Man bedenke aber, das das mein erstes Projekt in Python ist. Ich habe mir die Sprache wegen diesem Projekt zu gemüte geführt. Also ist mein Wissen auch dementsprechen klein. Warscheinlich muss ich das Schleifenproblem bei der Abfrage einzelner rss-links nochmal überdenken. Vielleicht hast du Anregungen, inwiefern man das mittels objektorientierter Programmierung verbessern kann. Ich kann mir darunter nix konkretes vorstellen. Deine Links les ich mir durch.
Danke dafür. Ich hoffe du hast dem ganzen entnehmen können, worum es mir geht. Ich hatte auch probiert, für jeden Link einzelne Datein anzulegen, also extra xml und geparsed Datein. Doch damit kam ich nicht klar, bzw. hat das nicht so gefunzt.
Gruß Jan.
Dank für eure tolle Hilfe. Hat mir bereits viel gebracht!
Edit: An das hintereinander ohne Datei hab ich nicht gedacht. Wüsste auch nicht wie genau ich das realisiere. Wenn ich das alles hintereinander machen würde müsste ich mir ein neuen Parser basteln, da ich ja nicht in einem String Zeile für Zeile durchgehen kann, oder?
Verfasst: Donnerstag 19. März 2009, 02:01
von jerch
Also ich bewundere ja Deinen Elan, mit dem Du Dich an so ein Projekt in einer neuen Sprache ranmachst (ein eigener XML-Parser

). Leider erstreckt sich meine Begeisterung nicht auf Deinen Code, der lehrt mir eher das Fürchten (bitte nicht persönlich nehmen).
Dein Parser dürfte mit einem Großteil valider RSS-Feeds nicht klarkommen, da Du das XML falsch angehst. Ich möchte das auch nicht weiter ausführen, nur soviel dazu: Du kannst eine gültige XML-Datei komplett in eine Zeile packen, da nicht die Zeilenumbrüche sondern die Tags die Delimiter sind. Hier würde Dein Parser versagen. Von XML (lxml) bis hin zu fertigen RSS-Parsern gibt es eine Menge guter Bibliotheken für Python, die Dir das Leben einfacher machen können.
Ok, wie würde ich sowas angehen?
--Stichwort: Softwaredesign
Ich bin ein großer Fan der agilen Entwicklung, würde aber in einer so konkreten Fragestellung mich einiger Hilfestellungen aus der klass. Entwicklung bedienen:
- Zielstellung formulieren --> use cases entwerfen (Inspiration aus anderen ähnlich gelagerten Projekten)
- hieraus OOP-Entwurf stricken
- benutzbare Dritt-Libaries suchen --> Entwurf revidieren
- Implementation (inkl. unittests)
Das ist jetzt nur ein Grobschema F und variiert je nach Fragestellung. Das wirklich tolle an Python ist, das meist nicht viel zum Selberschreiben übrig bleibt.
Ich bin mir nicht sicher, ob Du mir noch folgen kannst. Wenn nicht, würde ich Dir dringend raten, mit einfachen Beispielimplementationen und kleinen Problemen unter Zuhilfenahme eines Tutorials in Python anzufangen.
Warum erzähle ich Dir das alles?
Leider sind in Deinem Code von race conditions (file handling) bis Designschwächen (Modularisierung mit import-Problemen, Überschreiben von Dateien) alles vertreten. Ich bin gerne bereit, Dir einzelne Fehler genauer aufzuzeigen, falls Du daran interessiert bist. Und wenn Du obige Überlegungen mit einbeziehst und "fertige" RSS-Parser einsetzt, dann sollte das Programm in Deiner Gui-Klasse mit ein paar Methoden zu stemmen sein.
Ich hoffe, das waren nicht zu viele negative Schwingungen, wollte Dich nicht abschrecken.

Grüße, Jerch
Verfasst: Donnerstag 19. März 2009, 08:38
von Jan42
Danke für deine Tipps. Ich werd gucken ob ich das umgesetzt bekomme. Doch erstma nicht in dem Umfang. Ich hab noch bis Dienstag Zeit. Bis dahin will ich es nur mit Hilfe von Klassen schaffen, mehrere Link anzeigen zu können. Sobald das Projekt abgegeben ist, dafür muss es nicht soo gut sein. Hauptsache es macht was es soll^^, werde ich es überarbeiten und noch Dinge hinzufügen.
So jetzt erstma zur Schule^^ Nachmittag dann OOP angucken
Gruß Jan
Verfasst: Donnerstag 19. März 2009, 13:08
von jerch
Hier mal eine kurze Anregung von mir.
Datei "rssgui.ui":
Code: Alles auswählen
<ui version="4.0" >
<class>RssGui</class>
<widget class="QWidget" name="RssGui" >
<property name="geometry" >
<rect>
<x>0</x>
<y>0</y>
<width>519</width>
<height>379</height>
</rect>
</property>
<property name="windowTitle" >
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout" >
<item row="0" column="0" >
<widget class="QLineEdit" name="lineEdit" />
</item>
<item row="1" column="0" >
<widget class="QTextBrowser" name="textBrowser" />
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
und die Python-Datei dazu:
Code: Alles auswählen
# -*- coding: utf-8 -*-
import sys
import feedparser
from PyQt4 import QtGui, QtCore, uic
class SimpleFeedReader(QtGui.QWidget):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
uic.loadUi('rssgui.ui', self)
self.connect(self.lineEdit,
QtCore.SIGNAL("returnPressed()"), self.getFeed)
# Bsp.-Feed
self.lineEdit.setText('http://newsfeed.zeit.de/index')
def getFeed(self):
d = feedparser.parse(unicode(self.lineEdit.text()))
if d.bozo == 1:
self.textBrowser.setHtml('<h3>Keine gültigen \
RSS-Daten.</h3>')
return
self.textBrowser.setHtml(''.join('<h3>' + i.title +
'</h3>' + i.summary + '<hr>' for i in d.entries))
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
widget = SimpleFeedReader()
widget.show()
app.exec_()
Zum Parsen der RSS-Daten nehme ich feedparser, die API ist sehr einfach zu bedienen, da alles in Dictionaries und Listen gepackt wird. Das Bsp. zeigt auch nur den Titel und die Zusammenfassung eines Feedeintrages. Man könnte das noch um Erstellungsdatum, Link zum Artikel, Author etc. erweitern. Die Daten hierfür bietet feedparser schön aufbereitet an.
Für einen "richtigen" Feedreader würde ich allerdings die GUI anders aufbauen und modelbasiert die Daten zur Anzeige bringen. Ich denke aber, daß das zuviel ist für Dich bis Dienstag und Du schon gut beschäftigt bist mit OOP & Konsorten.
Grüße, Jerch