Mit comboBox eine entsprechende Funktion aufrufen

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

Hallo Leute,

im unten stehenden Quelltext möchte ich über die ComboBox eine entsprechende Funktion aufrufen. In Zeile 7 - 26 habe ich die Funktionen außerhalb der Klasse definiert - auf diesem Weg möchte ich die strikte Trennung zwischen Logik und Gui beibehalten. In Zeile 57 habe ich eine Liste erstellt, die dann in Zeile 66 als Item der Combobox zum Inhalt gemacht wird, das heißt, die Combobox wird gefüllt. In Zeile 67 habe ich die Combobox mit der combo_chosen-Funktion verbunden. Diese Funktion sehen wir außerhalb der Klasse in Zeile 28. Dort habe ich eine Liste angelegt, mit all den Funktionen. Diese Funktion hat das Argument current_combo_index. Darüber soll der eben angeklickte Index ermittelt werden. Wir sehen hier zwei print-Anweisungen. In der ersten print-Anweisung soll nur der eben angeklickte Index ausgegeben werden. In der zweiten Anweisung soll dann durch den Index das entsprechende Element aus der Liste angesprochen werden. Wenn also Index 5 ais der Combobox angeklickt wurde, dann soll auch das 5. Element aus der liste angesprochen werden. Tut das Programm nicht. Er spricht alle Funktionen an. Ich möchte also, dass ein bestimmtes Element aus der Liste angesprochen wird. Und da eine Liste über ein Index verfügt, wollte ich diese darüber ansprechen.

Mein allgemeines Anliegen ist es, dass ich der if-Kaskade aus dem Weg gehen kann. Man könnte natürlich die Indexe einzeln abfragen und dementsprechend eine bestimmte Funktion aufrufen. Aber ich überlege gerade, ob man das irgendwie vermeiden kann. Nur komme ich nicht drauf.

Code: Alles auswählen

#!/usr/bin/env python
#-*- coding:utf-8 -*-

import sys
from PyQt4.QtGui import QWidget, QApplication, QVBoxLayout, QFormLayout, QComboBox

def test_zero():
    print " 0 item Pressed"

def test_one():
    print " 1 item Pressed"

def test_two():
    print " 2 item Pressed"

def test_three():
    print " 3 item Pressed"

def test_four():
    print " 4 item Pressed"

def test_five():
    print " 5 item Pressed"

def test_six():
    print " 6 item Pressed"

def combo_chosen(current_combo_index):

    function_list = [test_zero(),
                  test_one(),
                  test_two(),
                  test_three(),
                  test_four(),
                  test_five(),
                  test_six()]

    print "Index", current_combo_index
    print "Elemen from List:", function_list[current_combo_index]

qt_app = QApplication(sys.argv)

class LayoutExample(QWidget):
 
    def __init__(self):

        QWidget.__init__(self)

        self.setWindowTitle('Example Window')
 
        # Create the QVBoxLayout that lays out the whole form
        self.layout = QVBoxLayout()
 
        # Create the form layout that manages the labeled controls
        self.form_layout = QFormLayout()
 
        self.combo_items = ['Item 1',
                            'Item 2',
                            'Item 3',
                            'Item 4',
                            'Item 5',
                            'Item 6']
 
        # Create and fill the combo box to choose the combo
        self.combo = QComboBox(self)
        self.combo.addItems(self.combo_items)
        self.combo.activated.connect(combo_chosen)
 
        # Cdd the form layout to the main VBox layout
        self.layout.addLayout(self.form_layout)
 
        # Set the VBox layout as the window's main layout
        self.setLayout(self.layout)
 

    def run(self):
        # Show the form
        self.show()
        # Run the qt application
        qt_app.exec_()
 
# Create an instance of the application window and run it
app = LayoutExample()
app.run()
Ausgabe:
0 item Pressed
1 item Pressed
2 item Pressed
3 item Pressed
4 item Pressed
5 item Pressed
6 item Pressed
Index 5
Elemen from List: None
Benutzeravatar
Madmartigan
User
Beiträge: 200
Registriert: Donnerstag 18. Juli 2013, 07:59
Wohnort: Berlin

Okay, mal davon abgesehen, dass ich nicht verstehe wieso eine Auswahl in einer ComboBox eine Funktion auslösen soll (es existieren auch für UI/UX gewisse Konventionen), benutzt du hier das falsche Event und rufst dann noch alle Funktionen bei jedem Wechsel des selektierten Items auf.

Code: Alles auswählen

function_list = [test_zero(),
                  test_one(),
                  test_two(),
                  test_three(),
                  test_four(),
                  test_five(),
                  test_six()]
Das kann nicht funktionieren. Entferne die Klammern in der Liste, die haben hier nichts zu suchen.

Code: Alles auswählen

print "Elemen from List:", function_list[current_combo_index]
Und hier fehlt der Aufruf als Funktion. Ergo ändere oben stehendes wie folgt:

Code: Alles auswählen

print "Elemen from List:", function_list[current_combo_index]()

Code: Alles auswählen

self.combo.activated.connect(combo_chosen)
Liest du eigentlich auch mal in der Doku oder probierst du einfach mal alles durch?
Das Activated Event macht hier keinen Sinn. Was du möchtest, nennt sich currentIndexChanged(int).
Es muss also wie folgt heißen.

Code: Alles auswählen

self.combo.currentIndexChanged.connect(combo_chosen)
EDIT: Hier hatte ich einfach vorausgesetzt, du willst die Funktionen nur aufrufen, wenn der Item Index sich ändert. Wenn das nicht der Fall ist, wäre das Activated Event doch korrekt.


Nur als Anmerkung, auch wenn die Änderungen das Konstrukt lauffähig machen, bleibt der ganze Code dennoch ... "unschön".
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@Madmartigan: Zunächst besten Dank. Eine Frage: Inwiefern ist der Quelltext unschön? Ich habe mich darum bemüht, dass Logik und GUI getrennt bleiben. Und zu deinem Verständnis. Mir geht es darum, dass bei einer Auswahl eines Items eine andere Combobox mit Inhalt befüllen möchte.

Für das bessere Verständnis hier ein Bild:

Bild

Der Anwender sucht sich eine Datenbank aus, in der gesucht werden soll. Zum Beispiel kann er auch in der Buch-Datenbank suchen oder in der Musik-Datenbank etc. Nun klickt der Anwender "Filme" an, und in der Combobox unter "Kriterien" wird die Combobox entsprechend gefüllt. Zum Beispiel Genre, Schauspieler, Medien, Fassung, Herstellungsland, und vieles mehr. Jede Kategorie (Filme, Bücher, Musik) hat bestimmte Such-Kriterien. Und sobald hier also ein Item ausgesucht wird, soll die andere Combobox entsprechend gefüllt werden. Dazu muss also bei der Wahl eines Items eine direkte Funktion aufgerufen werden.
Sirius3
User
Beiträge: 18264
Registriert: Sonntag 21. Oktober 2012, 17:20

@Sophus: die Verknüpfung "Datenbank" -> "Kriterium" ist doch eindeutig etwas variables. Es können jederzeit neue Kriterien oder "Datenbanken" hinzukommen. Wobei hier Datenbank hoffentlich nicht verschiedenes Datenbankschema bedeutet. Denn je nachdem, wie Du Dein Datenbankschema aufbaust, kannst Du die Kriterien daraus auslesen. Das heißt, Du brauchst keine verschiedenen Funktionen hinter jedem Combobox-Eintrag sondern nur eine Funktion, die auf eine Datenbank zugreift.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@Sirius3: Ich habe mir das wie folgt vor gestellt: Für jede Kategorie (Filme, Bücher, Videospiele, Briefmarken, Adressbuch etc.) wird eine Datenbank angelegt. Denn jeder dieser Kategorien hat ganz andere Strukturen. Und all die Kategorien in eine Datenbank unterzubringen erscheint mir sehr unübersichtlich. Denn es kommen noch weitere Kategorien hinzu. Und was die Kriterien anbelangt. Die Funktionen, die hinter jedem Eintrag stehen, sollen die Combobox füllen. Wenn ich also "Buch" aus der Combobox aussuche, dass füllt die Combobox mit den Kriterien wie folgt füllt (ISBN, Verlag, ISSN, Körperschaft, Signatur etc...). Jede Kategorie hat zum Teil andere Kriterien. Und daraus möchte ich am Ende eine Such-Abfrage erstellen: Suche in der Buch-Datenbank nach einer ISBN mit folgender Nummer 123456789.
Sirius3
User
Beiträge: 18264
Registriert: Sonntag 21. Oktober 2012, 17:20

@Sophus: Ich skizzier dann mal kurz, wie das ganze aussehen könnte.
Für jede Kategorie willst Du eine eigene Tabelle haben, deren Felder (Titel, Erscheindatum, Autor, usw.) ja nach Kategorie unterschiedlich sein können. Nun sind die Namen der Felder nicht unbedingt das, was Du in der Combobox anzeigen möchtest, bzw. es wird auch Felder geben, die Du gar nicht anzeigen möchtest. Außerdem mußt Du ja noch irgendwoher wissen, welche Tabellen (Kategorien) es überhaupt gibt.
Du mußt also in Deiner Datenbank zusätzlich noch Verwaltungstabellen anlegen, eine in der die Kategorien (Kat_ID, Name, Tabellenname, Icon, ...) eingetragen werden, und eine, in der die Suchfelder eingetragen werden (Such_ID, Kat_ID, Name, Feldname, ...).

So kannst Du Deine Comboboxen einfach mit den Informationen aus der Datenbank füllen. Dazu reicht aber auch wieder eine Funktion, die beim Ändern der Auswahl der Combobox aufgerufen wird,
Sirius3
User
Beiträge: 18264
Registriert: Sonntag 21. Oktober 2012, 17:20

@Sophus: Ich denke, Du kannst hier gerne Dein Schema auch öffentlich posten. Mir ist folgendes aufgefallen:
Primärschlüssel sind normalerweise Zahlen. Gibt es einen Grund, warum Du Zeichenketten benutzt? Etliche Künstler sind sowohl Schauspieler als auch Sänger. Und viele haben auch noch mehr als einen Künstlernamen.
Bei einem so stark normalisierten Entwurf ist es ziemlich kompliziert, eine allgemeine Suchmaske zu schreiben, da Du irgendwie die vielen JOINs abbilden mußt. Es ist wohl einfacher, dafür ein paar Views zu definieren, dass Du tatsächlich ein View hast, dessen Felder mit den Suchkriterien übereinstimmt. Und dann hast Du ja wieder den von mir beschriebenen Fall, dass Du mit zwei weiteren Tabellen Deine Comboboxen automatisch füllen kannst.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@Sirius3: Ich kann den unvollständigen Entwurf auch gern hier veröffentlichen, nur wird das zum Off Topic, da ich hier nicht wirklich über die Datenbank per se diskutieren wollte. Aber zu deiner Frage: Ich werde mich von den jeweiligen Datenbanksystemen automatisierten IDs verabschieden. Das heißt, ich kreiere eigene IDs, und das mit Hilfe von UUID. Und UUIDs setzen sich aus Buchstaben und Zahlen zusammen. Aus diesem Grund habe ich die ID-Felder Zeichenkette (CHAR) definiert. Das Ein Schauspieler mehre Funktionen ausführen kann, weiß ich. Eine Person kann Drehbuchautor, Schauspieler, Schriftsteller und Sänger sein. Daher gibt es eine gesonderte Tabelle "Funktionen".

Und ja, ich habe meine Tabelle sehr stark normalisiert. Ich möchte es schon ziemlich professionell angehen. Wenn man bedenkt, wie meine aller erste Datenbank gestaltet wurde. Heute würde man mit dem Kopf schütteln. 8) Und das ich dann mit sehr vielen JOINs arbeiten werde, ist mir schon bewusst. Aber einen Schritt nach dem anderen. :)
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

@Sophus:
Als Anregung dazu das EAV-Modell. Ich würde wahrscheinlich eher auf Itemtypebene trennen und normalisieren und nicht versuchen, alles in eine gesamte Tabellenstruktur zu pressen. Letzteres wird zur JOIN-Hölle.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@jerch: Was meinst du mit Itemtypebene trennen? Meinst du zum Beispiel Stammdaten separat in eine andere Datenbank auslagern? Zum Beispiel Personen. Diese können dann später in die Musik-Datenbank oder in die Film-Datenbank aufgenommen werden. So entstehen bestimmte Personen nicht redundant?
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

@Sophus:
Typen mit gleichem Attributset kommen in eine Tabelle (ergo andere Attributsets in andere Tabellen). Damit ist das DB-Schema zwar weniger stark normalisiert, es lässt sich aber einfacher damit aus dem Code heraus hantieren. An der Stelle stellt sich die Frage, wie Du überhaupt die DB einbindest. Nimmst Du qSql oder einen Python-ORM?
Und nein, ich meine nicht irgendwas in eine separate DB auslagern, das macht man nicht (damit würdest Du fast alle Vorteile einer DB verspielen, z.B. beim Suchen).
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@jerch: Ich verwende die Bibliothek MySQLdb. Und das mit den Attributset habe ich vermutlich immer noch nicht verstanden. Aber mal davon abgesehen klingt es eher danach, dass die Redundant dadurch besonders gefordert werden könnte. Ich weiß auch, dass man mit der Normalisierung nicht übertreiben sollte. Ich versuche mich ja auch schon im Zaum zu halten.
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

MySQL? Soll das lokal laufen oder wird die Anwendung sich an eine zentrale Instanz wenden? Lokal würde ich MySQL nur mit ausliefern, wenn SQLite partout nicht reicht, was ich mir hier nicht vorstellen kann. Generell rate ich Dir, eine höhere Abstraktion zu nutzen. Sowohl qSql als auch die Python-ORMs setzen nicht low-level auf einem bestimmten DBMS auf - so kann man das dann auch relativ unkompliziert tauschen.
Zu den Attributsets: Filme haben wahrscheinlich andere Attribute als Bücher, dass wären dann nach meinem Modell verschiedene Tabellen. Zeig doch mal Dein ER-Diagramm, ansonsten bleibt das alles graue Theorie.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@jerch: Ist die Bibliothek MySQLdb zu niedrig? Von QSql möchte ich mich fern halten, weil ich mich nicht zu stark an das QT-Rahmenwerk binden möchte. Ich ging davon aus, dass MySQLdb über ORM verfügt. Ansonsten müsste ich mich entweder für Storm, Autumn oder SQLAlchemy entscheiden. Bis jetzt (ohne die JOINs) abrietet es sich gut mit der Bibliothek. Zu deiner Frage: Es wird eine Anwendung mit mehreren Datenbank-Schnittstellen. Der Anwender soll sich selbst entscheiden. Ihm wird die Möglichkeit gegeben, ob er mit SQlite, MS Access, MySqL, MS SQL oder PostgreSQL arbeiten möchte. Für jeder dieser Datenbanken gibt es Bibliotheken. Ich lege mich also nicht fest. Sollte der Anwender sich für eine Server-seitige Datenbank entscheiden muss der Anwender schon selbst einen Server aufsetzen. Meine Software erstellt die Datenbanken, Tabellen und Felder selbst.

Und zu den Attributen. Ich glaube ich habe mich falsch ausgedrückt. Das Bild was ich euch gezeigt habe, ist nur eine Datenbank, also eine Film-Datenbank. Es gibt aber auch eine Bücher-Datenbank und so weiter. Eben weil ich weiß, dass es unübersichtlich wird, wenn man all die unterschiedlichen (ich nenne sie) Kategorien in eine Tabelle pressen würde. Und ein ER-Diagramm kann ich zur Zeit noch nicht zeigen. Es ist noch in Arbeit. Ich wollte euch nur anhand des Bildes nur aufzeigen, dass ich mit meinem Ausgangsproblem etwas erreichen wollte, und zwar, dass die ComboBox mit bestimmten Kriterien gefüllt werden.
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

Sophus hat geschrieben:Von QSql möchte ich mich fern halten, weil ich mich nicht zu stark an das QT-Rahmenwerk binden möchte.
Verständlich, das spricht dann für einen Python-ORM. Genannt hast du ja welche, ich würde da auf SQLAlchemy gehen.
Sophus hat geschrieben:Ich ging davon aus, dass MySQLdb über ORM verfügt.
Nein hat es nicht. Es ist der low level Treiber für MySQL in Python.
Sophus hat geschrieben:Bis jetzt (ohne die JOINs) abrietet es sich gut mit der Bibliothek.
Aber halt nur mit MySQL. Die ORMs unterstützen eine ganze Reihe von DB-Backends und machen deutlich mehr - idR kommen da fertige Pythonobjekte raus, wo z.B. Spaltenwerte als Objektattribute vorgehalten werden oder man sehr einfach die Relationen abfragen kann. Ganz zu schweigen von möglichen SQL-injection Angriffszenarien, die Du wahrscheinlich mit MySQLdb gebaut hast. Bitte lies Dich zum Thema ORM ein, dass suchst Du nämlich.
Sophus hat geschrieben:Es wird eine Anwendung mit mehreren Datenbank-Schnittstellen. Der Anwender soll sich selbst entscheiden. Ihm wird die Möglichkeit gegeben, ob er mit SQlite, MS Access, MySqL, MS SQL oder PostgreSQL arbeiten möchte.
--> ORMs können das mit ihren connection strings.
Sophus hat geschrieben:Und zu den Attributen. Ich glaube ich habe mich falsch ausgedrückt. Das Bild was ich euch gezeigt habe, ist nur eine Datenbank, also eine Film-Datenbank. Es gibt aber auch eine Bücher-Datenbank und so weiter. Eben weil ich weiß, dass es unübersichtlich wird, wenn man all die unterschiedlichen (ich nenne sie) Kategorien in eine Tabelle pressen würde. Und ein ER-Diagramm kann ich zur Zeit noch nicht zeigen. Es ist noch in Arbeit. Ich wollte euch nur anhand des Bildes nur aufzeigen, dass ich mit meinem Ausgangsproblem etwas erreichen wollte, und zwar, dass die ComboBox mit bestimmten Kriterien gefüllt werden.
Also willst Du doch die Daten in unterschiedlichen Datenbanken vorhalten? Wenn ja, warum?
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@jerch: Jetzt verstehe ich was du mit ORM meinst bzw. was ORM eigentlich tut. Du meinst, damit erspare ich mir sämtliche Bibliotheken für SQLite (wobei diese Bibliothek in meinem Python 2.7.6 schon dabei ist), für MySQL, MS SQL, PostgreSQL etc? Ich wäre jetzt dazu übergegangen und hätte für jedes Datenbanksystem eine bestimmte Bibliothek geladen und dementsprechend gearbeitet. Ich werde mich dazu einlesen müssen.

Und wieso ich für jede Kategorie eine Datenbank erstelle? Eben weil die Bücher ganz andere Angaben haben als zum Beispiel Filme oder anders als ein Adressbuch. Worüber ich jedenfalls nachdenke, ist, dass ich eine weitere Datenbank anlege, in der sozusagen Stammdaten reinkommen. Zum Beispiel Personen. Und dass dann von dort aus "Beziehungen" zu anderen Datenbanken hergestellt werden und die Personen nicht redundant vorkommen. Sonst habe ich nachher die gleichen Personen einmal in der Bücher-Datenbank, Musik-Datenbank und Film-Datenbank. Aber ich glaube, davon hast du mir ja abgeraten. Und das mit der Itemtyp-Ebene erscheint für mich nicht befriedigend, da man dabei nicht so stark normalisieren kann.
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

Sophus hat geschrieben:Und wieso ich für jede Kategorie eine Datenbank erstelle? Eben weil die Bücher ganz andere Angaben haben als zum Beispiel Filme oder anders als ein Adressbuch.
Das ist doch kein Grund für eine andere Datenbank. Das ist eher ein Grund für separate Tabellen, wie ich vorgeschlagen hatte, wenn man nicht übermässig normalisieren möchte. Eine Datenbank kommt sehr gut mit vielen verschiedenen Tabellen klar.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@jerch: Ich bin leider noch nicht so sehr erfahren mit Datenbanken. Aber wie es bis jetzt ist mag ich es irgendwie. Es wirkt "aufgeräumter". Mit dem Verfahren bestimmte Attributsets in eine Tabelle zu pressen wirkt bestimmt sehr unaufgeräumt, und die Redundanz ist bestimmt sehr groß. Wenn ich dich also richtig verstehe, dann soll ich die Bücher und all die anderen Kategorien auch in die selbe Datenbank stecken, wo auch die Film-Kategorie ist?
Sirius3
User
Beiträge: 18264
Registriert: Sonntag 21. Oktober 2012, 17:20

@Sophus: genau, selbe Datenbank, unterschiedliche Tabellen. Aber das hatte ich ja schon ganz zu anfang geschrieben.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@Sirius3: Stimmt, hattest du erwähnt. Aber nur so aus Neugier. Welche Nachteile erkaufe ich mir, wenn ich für jede Kategorie eine Datenbank anlege? Sagen wir mal eine Datenbank für Filme, Musik, Bücher und dann eine Datenbank wo alle Stammdaten vorhanden sind. Zum Beispiel Länder, Personen, Orte, Genre etc. Die Redundanz wäre damit aufgehoben, richtig?
Antworten