CacheBase 0.5-dev release

Stellt hier eure Projekte vor.
Internetseiten, Skripte, und alles andere bzgl. Python.
Antworten
solar22
User
Beiträge: 27
Registriert: Donnerstag 14. Oktober 2010, 20:31

Hallo liebe Pythonfreunde :)

CacheBase ist gestern in der Version 0.5-dev erschienen.

CacheBase ist ein kleines aber feines Tool zum Verwalten von (nicht) gefundenen Geocaches auf der Datenbank http://www.opencaching.de

Ich stelle ein .deb Paket bereit für Ubuntu, inwiefern das auf anderen Debiansystem läuft muss probiert werden.
CacheBase ist ein Schulprojekt, die späte Veröffentlichung liegt daran, das ich erst die Note bekommen musste bevor ich das veröffentlichen darf nach Schulordnung...ist doof, ist aber leider so.

Vielleicht finden sich ja ein paar die das Programm auch nutzen wollen, ich bin grad an der Brücke zu gpsbabel dran und dann geht auch der Export direkt aufs GPS Gerät.

Ich freue mich natürlich über jedes Feedback
Wenn die Resonanz groß ist installier ich noch ein kleines Forum auf der Projektseite, ich denke bis dahin reicht E-Mail und der Linux/PHP Bereich auf http://www.geoclub.de und hier.

Hier die Projektseite: http://www.cache-base.de

Gruß.

solar22
deets

Wenn du Feedback zum Source wollen wuerdest, muesstest du ihn zur Verfuegung stellen ;)
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

@deets:

Er will offenbar nur Werbung machen. Feedback zu PHP-Code in einem Python-Forum zu wollen, ist doch eher ungewöhnlich.
solar22
User
Beiträge: 27
Registriert: Donnerstag 14. Oktober 2010, 20:31

CacheBase ist überwiegend in Python geschrieben...
Hab wohl vergessen den Source an den Beitrag anzuhängen 8) passiert.
PHP übernimmt nur die Aufgaben zur Beschaffung ein paar Dateien aus dem Internet.

Source: http://cache-base.de/dload/cachebase.zip

Ich arbeite mich grad noch tiefer in Python ein, sodass PHP auf lange Zeit verschwindet aus dem Projekt ;)
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Verzeihung, ich hatte beim Durchstöbern des Debian-Pakets von deiner Website nicht geahnt, dass der ganze Python-Code im Verzeichnis `UI` steckt.
BlackJack

@snafu: Der Quelltext ist ja im Debian-Paket. Da ist auch Python dabei. Ich habe mal reingeschaut -- und mir sofort gewünscht ich hätte es nicht getan. :twisted:

Da sind ein paar gruselige Sachen drin. Zum Beispiel sowas nach dem Muster um "Textknoten" per `minidom` aus XML-Daten zu extrahieren:

Code: Alles auswählen

for node in dom.getElementsByTagName('foo'):
    value = node.toxml().replace('<foo>', '').replace('</foo>')
Das steckt in einer Funktion, die einziger Bestandteil eines Moduls ist, das in einem Paket mit dem Namen `classes` steckt. In dem Paket steckt noch ein Modul `map`, das ebenfalls nur eine einzige Funktion enthält, und das in einem Modul `classes` (ja das heisst so wie das Paket) importiert wird. Diese ``import``-Anweisung ist das einzige was in dem Modul steht. Neben einer leeren `__init__.py` war das alles im `classes`-Paket. WTF!?
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

BlackJack hat geschrieben:@snafu: Der Quelltext ist ja im Debian-Paket. Da ist auch Python dabei. Ich habe mal reingeschaut -- und mir sofort gewünscht ich hätte es nicht getan. :twisted:
Wie war das unlängst mit dem "Arschigkeitsfaktor" :mrgreen: ?

Im Gegnsatz zu euch war ich auch so mutig und habe es mal gestartet ;-)

Daher mal ein paar konstruktive Sachen (@solar22):

Erst mal Daumen hoch, dass Du ein Projekt auf die Beine stellst. Auch wenn es da noch viel Verbesserungspotenzial gibt; inhaltliches dabei mal ausgeklammert, da ich zum einen kein GeoCacher bin und zum anderen ich keine Caches im Programm angezeigt bekomme.

Als erstes fällt auf, dass die GUI ziemlich breit beim Start daher kommt. Dürften so knappe 1280 Pixel sein; ok mein Monitor ist auch noch ein alter 19"-4:3-Modell. Aber Du nutzt keinen Layoutmanager - wieso nicht? Speziell mit dem QtDesigner ist das doch ein Kinderspiel, ein schickes Layout zu basteln.

Du benutzt ein QMainWindow aber nicht dessen Funktionen! Wieso nutzt Du die Toolleiste (heißt das so?) nicht? Damit erspart man sich diese unschönen Buttons! Ich habe mal ein wenig gebastelt und am Layout geschraubt. Einiges würde ich generell anders lösen (z.B. den Webview mit dem sonderbaren Schließen-Knopf). Link

Auf technischer Seite fiel mir sofort auf, dass Du das itembasierte QTableWidget nutzt. Auch wenn's am Anfang hart ist, aber ich würde das weghauen und auf das modelbasierte QListView / QTableView setzen. (Imho willst Du ein ListView). So schwer ist es gar nicht, sein eigenes Model zu erstellen; zumal Du ja keine großen Sperenzien drin hast und daher einfach auf das QStandardItemModel zurückgreifen kannst.

Allerdings bietet Qt Dir bereits spezielle Models für Datenbankmodelle an! Dazu verpackst Du das dann noch in ein QSortFilterProxyModel und Du bist alle Sorgen des manuellen Filterns los. Mich hatte lunar seinerzeit auf die richtige Linie gebracht. Das hat sich echt gelohnt damals. Ich finde den Code grad nicht mehr, aber bei Interesse suche ich den gerne noch mal raus.

(Ha, beim Suchen im Board gefunden: Wurde Dir schon einmal empfohlen!)

Bei einer ComboBox gibt es doch auch die Möglichkeit, indexbasiert auszuwerten. So etwas geht imho gar nicht:

Code: Alles auswählen

	def loadTableItems(self,auswahl):
		if auswahl == "alle Caches":
			sql = "SELECT * FROM caches"
                ... # gekürzt
		if auswahl == "meine Caches":
			sql = ("SELECT * FROM caches WHERE `owner` = '%s' OR `id` = 'DUMMY'" % self.config[1][1])
		if auswahl == "Favoriten":
			sql = "SELECT * FROM caches WHERE `favo` = '1'"
		if auswahl == "Suche":
			sql = ("SELECT * FROM caches WHERE name LIKE '%"+str(self.searchQuery.text())+"%' OR wp LIKE '%"+str(self.searchQuery.text())+"%' OR owner LIKE '%"+str(self.searchQuery.text())+"%'")
Ich würde mir die Kommandos wohl in einer Liste merken und dann per Index das richtige auswählen. Da einige dynamisch sind, muss man ggf. auch eine lambda- oder eine echte Funktion in der Liste speichern. (Da Du aber ja hoffentlich auf das SQL- und das ProxyFilter-Model umsteigen wirst, erübrigt sich das ja dann eh :P )

Die String-Konkatenation ist übrigens inkonsistent. Mal mittels "%", mal per "+". Beides würde ich zugunsten von "".format() verwerfen. Davon abgesehen sollte man SQL-Queries nicht manuell zusammenbasteln, sondern die dafür vorgesehenen Techniken verwenden. Stichwort SQL-Injection. (Ok, bei der lokalen App nicht so dramatisch, aber fürs "Leben" ist es doch besser, es immer und überall sauber zu implementieren):

Code: Alles auswählen

# Im Falle von SQLite ist das iirc so korrekt
sql = r"SELECT * FROM caches WHERE owner=? OR id='DUMMY'"
# später dann
cur.execute(sql, (self.config[1][1],))
# man beachte das *Tupel* als zweiten Parameter, speziell wenn man
# nur einen Parameter ersetzen will ist das eine böse Falle
Je nach DB variiert die genaue Syntax. Näheres dazu findest Du in der Doku.

Generell mag ich übrigens keine "*"-SELECTs. Ich würde da explizit immer alle Attributsnamen hinschreiben.

Das hier sieht auch strange aus:

Code: Alles auswählen

	def loadConfig(self):	
		self.cur.execute("SELECT * FROM config")
		self.cur.fetchone()
		self.config = {}
		n = 1
		for row in self.cur:
			self.config[n] = row
			n = n +1
Du baust Dir ein Dict, welches als Key einen Integerwert enthält, der aber ein aufsteigender Index ist. Dazu braucht es kein Dictionary! Das kannst Du auch einfach in einer Liste speichern. Der Zugriff bei der SQL-Query oben zeigt ja, wie umständlich das ist. Zumal Deine Struktur in der DB-Tablle "config" ja aus den Attributen "name" und "value" besteht :shock: Was liegt da näher, als den "namen" als Key und den "value"-Eintrag als value-Wert des Dicts zu nehmen? ;-)

Ob man diese Config wirklich in der DB halten muss und dazu kein simples INI- oder JSON-File verwendet ist sicher Geschmackssache.

Dieses loadConfig() bzw. den Code daraus habe ich auch noch woanders im Code gesehen - vermeide solche Redundanzen.

Deine __init__()-Methode aus Cachebase sieht auch falsch eingerückt aus.

Ich würde wohl auch auf die neue connect-Syntax zurückgreifen und das mal umstellen. Speziell falls Du mal auf PySide migrieren willst bietet sich das an.

Generell ist das ganze Projekt ein ziemliches Datei-Durcheinander! Das solltest Du dringend mal aufräumen. Ordne die Dateien einfach mal sinnvoller, lösche nicht mehr benötigte Sachen. Source-Code würde ich in einen "/src"-Ordner packen, Beispiel-Dateien in "/examples" o.ä.

Als letztes: Wozu brauchst Du in Gootes Namen PHP? :shock: Das musst Du uns hier wirklich mal erklären :mrgreen:

So, nach dem langen (unvollständigen) Review brauche ich erst mal nen starken Tee :-D
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Nachtrag:

So, nachdem ich ziemlich umständlich die DB manuelle eingespielt bekommen habe (Der Dump ist mit der Tabellenstruktur der mitgelieferten DB nicht kompatibel; das Attribut "favo" in der Tabelle "caches" muss man temporär löschen), wollte ich das ganze mal starten... und dachte das Programm ist abgeschmiert. Oha... das dauert ja Minuten, die 13000 Caches einzulesen :shock:

Da liegt einiges im argen...

Letztlich ist mir das Programm dann noch abgeschmiert, nachdem ich es gewagt habe, einen Cache im TableWidget anzuklicken. K.A. was da für "Magie" im Hintergrund abläuft ;-)

Also mein Rechner ist zwar nicht mehr der beste, aber bei der Performance wirst Du mit dem Tool keine User begeistern können.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

So, ich konnt's nicht lassen und hab mal einen minimalen Prototypen gebastelt, der im Prinzip den ganzen Model-Kram, den ich oben beschrieb, mit Deiner DB demonstriert:
Bild

Main
CacheWidget

Und damit man es zur Not weiter bearbeiten kann noch das ui-File:
Ui-Datei

Selbst beim sofortigen Laden aller Datensätze startet das Teil ohne merkenswerte Verzögerung bei mir. Man sieht also, dass im "CacheBase"-Code einiges im Argen liegt.

Zum Betrieb braucht man noch eine DB; ich habe den Dump vom Projekt des OP verwendet.

So weit ich das gesehen habe, kann man leider dem QSortFilterProxyModel nicht mehrere Columns zuweisen, anhand derer es filtern soll, sondern nur eine oder alle. Da müßte man eben noch mal gucken, wie man das realisiert (Subclassing?).

Sind die Datensätze zu groß, um alles auf einmal zu laden, so muss man sich auch noch mal genauer schlau machen, was der SQL-Layer von Qt so für Optionen bietet. Ggf. muss man eben auch je nach Auswahl das Model mit einer neuen Query versehen. Da habe ich mich bisher noch nicht mit befasst.

@solar22: Ich hoffe Du erkennst den Mehrwert des MVC-Ansatzes sowie die Notwendigkeit, sich mit der Qt-Doku mal genauer zu befassen. Dort gibt es nämlich allerlei schöne Tutorials, die einem den Umgang mit den div. Techniken gut erklären und die wichtigen Komponenten mit typischen Use-Cases erläutern.

Notiz an mich: Diese Weisheit sollte ich mir auch merken! :mrgreen:
Notiz2: Ich muss mir auch endlich mal ein Snippet-Projekt auf bitbucket oder github anlegen, ähnlich wie lunar das handhabt :-)
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
solar22
User
Beiträge: 27
Registriert: Donnerstag 14. Oktober 2010, 20:31

Hallo Hyperion!

Erstmal vielen vielen Dank für deinen sehr ausführlichen Review :)
Er bietet viel Spaß beim lesen - immer diese kleinen Witze im Text.

Um kurz zum Ursprung was zu sagen: Ich selbst programmiere seit Jahren in PHP und hab da bereits eine Klasse zum Downloaden von Caches.
Somit war es erstmal für mich ok dies mit Python über os.system aufzurufen.
Bei dem Schulprojekt ging es darum eine neue Programmiersprache zu lernen, da ich Python eh mal lernen wollte fing ich an zu tüfteln 8)
Okay, der Code ist sehr widerlich...das muss ich zugeben, aber jetzt mal zum eigentlichen.

Layoutmanager? Sagt mir auf Anhieb zwar nix - aber okay, werd ich mich mit beschäftigen :) Ich wollt mir eh nächte Woche mal ein Buch über python und qt4 ausleisen in der Bibliothek.
Ich hab mir auch gleich deine Beispiel .ui Datei angesehen, danke dafür. Du hast recht es ist wesentlich besser so, damit kann man gut arbeiten.
Aber ich denke ich werd mich erstmal auf den Code konzentrieren und dann auf das Layout.

Ja stimmt, das modelbasierente QListView/QTableView wurde mir schonmal vorgeschlagen, aber damals war mir das noch etwas zu schwer. Heute würd ich mich da schon eher dran wagen.

Bei den SELECT * FROM brauche ich aber auch immer alles 8)
Ho
Hoffe der Tee war gut :)

Mich wunderts dass das Programm abschmiert.
Die Ladezeit beträgt auf 2 Rechnern die ich hab bei 16000 Datensätzen ca. 7 sekunden.
Ja der DUMP war genaudazu da die favo Spalte zuzufügen 8) Hab ich wohl vergessen zu löschen, danke für den Hinweis!

Komisch, wenn du alle Abhänigkeiten installiert hast sollte es eigentlich nicht abschmieren.
Deinem Screenshot entnehme ich du benutzt Windows, da hab ich es nie getestet.

Ich werd mir den Prototypen sehr genau anschauen und sicher auch verwenden, was sicher kein Problem darstellt, oder?

So, ich mach mich dann sofort an die Arbeit!
Und noch ein großes Dankeschön an dich :)

Gruß!

Alex
derdon
User
Beiträge: 1316
Registriert: Freitag 24. Oktober 2008, 14:32

solar22 hat geschrieben:Ja stimmt, das modelbasierente QListView/QTableView wurde mir schonmal vorgeschlagen, aber damals war mir das noch etwas zu schwer. Heute würd ich mich da schon eher dran wagen.
Ich habe da mal zwei Beispiele zu geschrieben, einmal für Listen und einmal für Tabellen: https://bitbucket.org/derdon/hodgepodge/src/tip/qt4/

HTH, derdon
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

solar22 hat geschrieben: Layoutmanager? Sagt mir auf Anhieb zwar nix - aber okay, werd ich mich mit beschäftigen :) Ich wollt mir eh nächte Woche mal ein Buch über python und qt4 ausleisen in der Bibliothek.
Naja, lies doch mal hier nach :-) Die Qt-Doku Seiten sind wirklich immer zu empfehlen! Ein (gutes) Buch als Zusatz schadet natürlich nicht ;-)
solar22 hat geschrieben: Aber ich denke ich werd mich erstmal auf den Code konzentrieren und dann auf das Layout.
Naja, das schöne am Arbeiten mit dem Designer ist ja, dass man leicht immer wieder etwas ändern kann. Und ein wenig mit Layouts spielen kann man da schon zu Beginn - motiviert ja auch mehr, wenn's gleich nach etwas aussieht und sich "gut" anfühlt.
solar22 hat geschrieben: Ja stimmt, das modelbasierente QListView/QTableView wurde mir schonmal vorgeschlagen, aber damals war mir das noch etwas zu schwer. Heute würd ich mich da schon eher dran wagen.
Du kannst ja schön an Deinem Code (als Negativbeispiel) sehen, wohin die itembasierten Sachen einen führen, sobald man dahinter implizit ein komplexeres Modell hat. Und wenn man nicht from Scratch sein komplett eigenes Modell mit diversen komplizierten Anforderungen schreiben muss, dann ist das alles nicht so schwer. derdon hat ja auch mal zwei Beispiele gepostet, lunar hat auf seiner Seite iirc auch einige Sachen aus dem Bereich (mit Delegates uvm). Aber zu Beginn reichen ja wohl auch erst einmal die Standardmodels wie gezeigt bzw. benutzt.
solar22 hat geschrieben: Bei den SELECT * FROM brauche ich aber auch immer alles 8)
Ich mags dennoch nicht :-P Zudem sieht man ja bei einer expliziten Nennung im Quellcode auf den ersten Blick noch einmal die Namen der Attribute.
solar22 hat geschrieben: Mich wunderts dass das Programm abschmiert.
Die Ladezeit beträgt auf 2 Rechnern die ich hab bei 16000 Datensätzen ca. 7 sekunden.
Komisch, wenn du alle Abhänigkeiten installiert hast sollte es eigentlich nicht abschmieren.
Deinem Screenshot entnehme ich du benutzt Windows, da hab ich es nie getestet.
Tja, k.A. was da passiert. Und bei mir dauert es wirklich Minuten lang. Und ich habe keinen Pentium drin, sondern schon eine 4 Jahre alte CPU und restliche Komponenten. Das Abschmieren passierte ja dann beim Anklicken eines Caches im TablkeWidget. Da muss ja im Hintergrund irgend etwas passieren nehme ich mal an? (Kommt da evtl. dieser PHP-Aufruf zum Tragen?)
solar22 hat geschrieben: Ich werd mir den Prototypen sehr genau anschauen und sicher auch verwenden, was sicher kein Problem darstellt, oder?
Nee, mach ruhig! Da ist ja nix besonderes dran - hab ja auch letztlich nur simple Sachen aus der Doku zusammengeschrieben ;-)

Ich habe übrigens PySide dafür verwendet; das sollte aber so 1:1 auch mit PyQt4 tun. Musst halt nur die Imports anpassen. Daher hab ich die Ui auch generiert, anstelle sie dynamisch mittels uic-Modul zu laden. Das Pendant dazu ist in PySide nämlich noch buggy.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
solar22
User
Beiträge: 27
Registriert: Donnerstag 14. Oktober 2010, 20:31

Mit dem Beispiel von derdon kann ich sehr gut einarbeiten, danke dafür! :)

Aber wo wir auch mal bei Layout sind.
Man kennt das ja aus vielen Programmen. Man klickt auf zB "Einstellungen" und es öffnet sich ein neues Fenster, aber ohne das alte zu schließen.
Wie kann ich das mit Python machen?
Momentan lös ich das ja so, dass das Einstellungsmenu ein extra Programm ist :K

Gruß!

Alex
BlackJack

@solar22: Du entwirfst einen Dialog, lädst den, und zeigst ihn an wenn der Button gedrückt wurde. So etwas sollte in Tutorials eigentlich behandelt werden.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

@soalr22: Ich habe mich mal mit der Auswahl von verschiedenen Spalten fürs Filtern befasst; siehe dazu diesen Thread hier.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
solar22
User
Beiträge: 27
Registriert: Donnerstag 14. Oktober 2010, 20:31

Ich hol den Thread mal wieder hoch.
Endlich hatte und hab ich wieder Zeit an dem Projekt zu arbeiten.

Ich hab nun etlichen PHP Code in Python geschrieben (mit pyCurl, zB).
Nurnoch der GPX Interpreter fehlt noch, da bin ich dran.

@Hyperion:
Danke für das Beispiel mit QTableView. Ich hab es nun geschafft das zu implementieren.
Auch das Auslesen von Tabellenitems geht wunderbar :)

Aber eine Frage hätt ich noch zu der SQL Einbindung.
Wie kann ich einzelne Abfragen wie INSERTS oder UPDATES loslassen?

Gruß.
solar22
Antworten