Seite 1 von 1

MVC ohne QT-Rahmenwerk

Verfasst: Mittwoch 11. Mai 2016, 21:03
von Sophus
Hallo Leute,

das Thema MVC hatte ich schon mal vor längerer Zeit in einem anderen Thread angesprochen. Viele von euch erinnern sich sicher sehr gut daran. Allerdings habe ich das Thema erst einmal fallen gelassen, weil ich mich nicht fit genug fühlte. Nun dachte ich mir, wage ich mich noch einmal an das Thema heran. Dazu habe ich ein kleines ausführbares Programm geschrieben- siehe unten. In meinem Beispiel werden zwei gleiche Fenster geladen. In einem Fenster soll gerechnet, und das Ergebnis soll dann in beiden Fenstern angezeigt werden.

Mein Augenmerk lag darauf, dass ich ein MVC-Design ohne Qt-Rahmenwerk erstelle. Mein Programm funktioniert, und augenscheinlich macht es auch das, was es tun soll. Vergessen wir mal all die Sicherheitsmechanismen etc. Dies ist nur ein reines Beispiel-Programm. Bevor ich mich noch intensiver in das Thema einarbeiten und euch dann mit weiteren Fragen nerven werde, möchte ich euch zunächst einmal fragen, ob mein Beispielprogramm genau dem MVC-Schema entspricht? Bin ich der Sache recht nahe? Oder gibt es noch Anmerkungen etc?

Ich frage nur deshalb, weil die Controller()-Klasse ganz schön viel über die UI weiß.

Code: Alles auswählen

from PyQt4.QtGui import QPushButton, QWidget, QApplication, \
     QLineEdit, QVBoxLayout

import sys

class CustomModel(object):
    
    def calculate_it(self, a, b):
        return a*b
        
class CustomController(object):
    def __init__(self):
        
        self.view_window = Window(self.calculator)
        self.custom_model = CustomModel()

        self.view_window_1 = Window(None)

        self.view_window.show()
        self.view_window_1.show()

    def calculator(self, a, b):
        #   Work with data
        self.view_window.result_line_edit.setText((unicode(self.custom_model.calculate_it(int(a), int(b)))))
        self.view_window_1.result_line_edit.setText((unicode(self.custom_model.calculate_it(int(a), int(b)))))    
        
class Window(QWidget):
    def __init__(self, callback_calculate):
        QWidget.__init__(self, parent=None)

        self.callback_calculate = callback_calculate

        self.calculate_pushButton = QPushButton()
        self.calculate_pushButton.setText("Calculate")
        self.calculate_pushButton.clicked.connect(self.calc)

        self.close_pushButton = QPushButton()
        self.close_pushButton.setText("Close")
        self.close_pushButton.clicked.connect(self.unload_me)
        
        self.first_line_edit = QLineEdit()
        self.second_line_edit = QLineEdit()
        self.result_line_edit = QLineEdit()
        self.result_line_edit.setReadOnly(True)

        v_layout = QVBoxLayout()
        v_layout.addWidget(self.first_line_edit)
        v_layout.addWidget(self.second_line_edit)
        v_layout.addWidget(self.result_line_edit)
        
        v_layout.addWidget(self.calculate_pushButton)
        v_layout.addWidget(self.close_pushButton)
        self.setLayout(v_layout)

    def calc(self):
        a = unicode(self.first_line_edit.text())
        b = unicode(self.second_line_edit.text())
        self.callback_calculate(a, b)

    def unload_me(self):
        self.close()

def main():
    app = QApplication(sys.argv)
    window = CustomController()
    sys.exit(app.exec_())
    
if __name__ == '__main__':
    main()

Re: MVC ohne QT-Rahmenwerk

Verfasst: Mittwoch 11. Mai 2016, 22:17
von BlackJack
@Sophus: MVC ohne Qt das damit beginnt aus PyQt zu importieren… :-)

Und die Frage ob das „genau dem MVC-Schema entspricht” kann eigentlich nur zur Gegenfrage haben: *welches* MVC-Schema‽ Und wenn Du Dir eine konkrete Spielart ausgesucht hast, dann solltest Du doch nach deren Beschreibung selber überprüfen können, ob Du es richtig umgesetzt hast.

Der Controller ist in der Tat zu eng mit dem bzw. den Views verbunden. Und mit dem Model auch. Der erstellt die ja sogar alle selbst. Wie soll man die dann austauschen können?

Irgendwie fehlt Dir auch ein Model. Es reicht nicht die Klasse so zu nennen, da müsste auch irgendein Zustand sein, den man Darstellen und Verändern kann. Sonst macht MVC keinen Sinn.

Re: MVC ohne QT-Rahmenwerk

Verfasst: Mittwoch 11. Mai 2016, 22:36
von Sophus
@BlackJack: Die Python-Codebox klappt wieder? Wunderbar ;-)

Ich habe mir schon gedacht, dass ich mit meinem Ansatz noch sehr weit entfernt bin. Und zum Model. Ich dachte, Ein Modell entspricht der Geschäftslogik? Dort werden irgendwelche Aufgaben verrichtet.

Meine Spielart geht in die Richtung, dass ich MVC gerne im MDI-Konzept anwenden möchte.

Re: MVC ohne QT-Rahmenwerk

Verfasst: Mittwoch 11. Mai 2016, 23:32
von BlackJack
@Sophus: Das Model ist irgendein Objekt das dem Benutzer durch den View präsentiert wird und via View und Controller manipuliert werden kann. Eine simple Funktion die etwas ausrechnet ist in dem Sinne kein Model. Die Klasse ist ja auch semantisch keine Klasse. Da hättest Du einfach eine Funktion `calculate()` schreiben können, ohne das da irgendwas verloren geht.

Re: MVC ohne QT-Rahmenwerk

Verfasst: Donnerstag 12. Mai 2016, 00:08
von Sophus
@BlackJack: Gehen wir mal einen Schritt nach dem anderen vor. Du hast meine Befürchtung bestätigt, und zwar, dass der Controller zu viel weißt. Wie sähe die ideale Variante aus? Nach meinem Verständnis gilt der Controller als eine Art Schnittstelle zwischen View und Model. View und Model wissen nichts voneinander. Daher habe ich mich dazu hinreißen lassen, dass ich dem Controller vieles bekannt gab. In der Tat erscheint mir das zu viel des Guten. Allerdinga muss ich gestehen, dass ich keine Ahnung habe wie ich es hätte anders machen können.

Re: MVC ohne QT-Rahmenwerk

Verfasst: Donnerstag 12. Mai 2016, 08:21
von BlackJack
@Sophus: Sag doch erst einmal was MVC hier ganz konkret bedeuten soll.

Das X und Y nichts voneinander wissen sollen ist übrigens auch keine klare Formulierung, denn es gibt einmal das Wissen darüber wie etwas aufgebaut ist, und zum anderen das Wissen über konkrete Exemplare. Der View sollte das Model nicht ”statisch” kennen, aber er muss natürlich genug über das Model wissen um es dem Benutzer irgendwie darstellen zu können. Es sei denn Du gehst wirklich komplett über den Controller. Dieses Extrem verstehen einige wohl auch unter MVC. Es gibt halt nicht *das* MVC. Und es macht IMHO auch nicht wirklich so viel Sinn sich mit MVC ohne Bezug auf eine konkrete Interpretation zu beschäftigen. Selbst wenn man sich aus reiner Neugier an dem (schwammigen) Muster damit beschäftigt, sollte man sich nicht ohne Bezug zu „known uses“ damit beschäftigen, dann sogar mit möglichst vielen Implementierungen oder zumindest möglichst unterschiedlichen.

Welches Problem versuchst Du hier mit MVC eigentlich zu lösen? Denn ein Entwurfsmuster macht nur Sinn wenn man es auf ein Problem anwendet und es dieses Problem dann auch tatsächlich löst, und zwar so das man es nicht einfacher lösen könnte, denn dann passt das Muster nicht zum Problem. Wenn man ein Problem löst, kann man auch einfacher überprüfen ob man es richtig umgesetzt hat, denn man kann überprüfen ob das Problem gelöst wurde.

Re: MVC ohne QT-Rahmenwerk

Verfasst: Freitag 13. Mai 2016, 12:06
von BlackJack
@Sophus: Mal ausgehend von Deinem Beispielcode habe ich versucht das dort bestehende Problem zu lösen, ohne jetzt explizit MVC im Hinterkopf gehabt zu haben. Soweit ich die Problemstellung identifizieren konnte, gibt es verschiedene binäre Operationen die Austauschbar sein sollen, und für die es mehr als eine Darstellungsart gibt, also auch austauschbar, oder mehr als eine gleichzeitig. Das würde ich wie folgt lösen:

Code: Alles auswählen

#!/usr/bin/env python
# coding: utf-8
from __future__ import absolute_import, division, print_function
import sys
from operator import div

from PyQt4.QtCore import pyqtSignal, QObject
from PyQt4.QtGui import (
    QApplication, QColor, QLabel, QLCDNumber, QLineEdit, QMessageBox,
    QPushButton, QVBoxLayout, QWidget,
)


class BinaryOperation(QObject):

    result = pyqtSignal(object)
    error = pyqtSignal(object)

    def __init__(self, operation, parent=None):
        QObject.__init__(self, parent)
        self.operation = operation

    def __call__(self, operand_a, operand_b):
        try:
            result = self.operation(operand_a, operand_b)
        except Exception as error:
            self.error.emit(error)
        else:
            self.result.emit(result)


class MainUI(QWidget):

    def __init__(self, binary_operation, parent=None):
        QWidget.__init__(self, parent)
        self.binary_operation = binary_operation
        
        self.first_argument_edit = QLineEdit()
        self.second_argument_edit = QLineEdit()
        self.result_label = QLabel()

        layout = QVBoxLayout(self)
        layout.addWidget(self.first_argument_edit)
        layout.addWidget(self.second_argument_edit)
        layout.addWidget(self.result_label)

        button = QPushButton('Calculate')
        layout.addWidget(button)

        self.binary_operation.result.connect(self.on_result)
        self.binary_operation.error.connect(self.on_error)
        button.clicked.connect(self.do_calculate)

    def do_calculate(self):
        try:
            first_argument = float(self.first_argument_edit.text())
            second_argument = float(self.second_argument_edit.text())
        except Exception as error:
            self.on_error(error)
        else:
            self.binary_operation(first_argument, second_argument)

    def on_result(self, result):
        self.result_label.setText(str(result))

    def on_error(self, error):
        QMessageBox.critical(self, 'Error', str(error))


class LCDWindow(QWidget):

    def __init__(self, binary_operation, parent=None):
        QWidget.__init__(self, parent)

        palette = self.palette()
        palette.setColor(palette.Window, QColor(0, 0, 0))
        self.setPalette(palette)

        self.lcd_number = QLCDNumber()
        self.lcd_number.setSegmentStyle(QLCDNumber.Flat)
        self.lcd_palette = self.lcd_number.palette()
        self.on_result(0)

        layout = QVBoxLayout(self)
        layout.addWidget(self.lcd_number)

        binary_operation.result.connect(self.on_result)
        binary_operation.error.connect(self.on_error)

    def set_digit_color(self, color):
        self.lcd_palette.setColor(self.lcd_palette.WindowText, color)
        self.lcd_number.setPalette(self.lcd_palette)

    def on_result(self, result):
        self.set_digit_color(QColor(0, 255, 0))
        self.lcd_number.display(result)

    def on_error(self, _error):
        self.set_digit_color(QColor(255, 0, 0))


def main():
    app = QApplication(sys.argv)
    executor = BinaryOperation(div)
    lcd_window = LCDWindow(executor)
    lcd_window.show()
    window = MainUI(executor)
    window.show()
    sys.exit(app.exec_())


if __name__ == '__main__':
    main()
Anstelle von der Division könne man auch beliebige andere binäre Operationen mit zwei Gleitkomma-Operanden verwenden, aber bei der Division kann man leicht eine Ausnahme provozieren, indem man eine Division durch Null versucht.

Wenn man ein bisschen die Augen zukneift und Fünfe gerade sein lässt, könnte man die `div()`-Funktion als Model sehen, die `BinaryOperation` als Controller, und die `QWidget`\s als Views. Ich glaube ich würde es trotzdem nicht als MVC bezeichnen. Ist halt einfach das was notwendig ist um Logik (binäre Funktionen) und GUI sauber voneinander zu trennen, und mehr als eine GUI-Komponente zu bedienen. Wäre das nicht als Anforderung vorhanden, hätte ich die `BinaryOperation` weggelassen, weil's KISS wiedersprochen hätte und YAGNI bedeutet hätte. (Ich liebe diese Akronyme :-))

Re: MVC ohne QT-Rahmenwerk

Verfasst: Dienstag 31. Mai 2016, 08:43
von Sophus
@BlackJack: Ich wurde gar nicht per E-Mai benachrichtigt, dass du nochmal auf meinen Beitrag geantwortet hast. Daher konnte ich gar nicht entsprechend reagieren. An dieser Stelle erst einmal Danke.

Nun, zwei Dinge: Das mit den Rechen-Operatoren galt nur als ein Beispiel. Das mein Model in diesem Fall nicht korrekt ist, war mir einleuchtend. Was mir erst einmal wichtig ist, dass ich in den Genuss von MVC komme. Wie du weißt, arbeite ich in meinem MIni-Projekt mit einem MDI-Fenster. Oder die Aktualität des MDI's können wir bis ins Unendliche diskutieren - ist reine Geschmacksfrage. Ganz grundlegend denke ich, dass bei einem MDI-Programm ein MVC angemessen erscheint. Denn ich kann ja das ein und das selbe Fenster - zum Beispiel Filme anzeigen lassen oder Filme hinzufügen - mehrmals anzeigen lassen. Das ist ja auch der Sinn von MDI. Und ich möchte MVC kennenlernen, ohne aber gleich mächtige Geschütze auffahren zu müssen. Ich will mich beim Lernen nicht gleich wieder überfordern.

Und da mich das MVC-Thema interessiert, habe ich mich mal im Netz rumgeschaut und was gefunden. Diese Seite: http://stackoverflow.com/questions/2669 ... 2#26699122

Nun eine Frage an euch: Ist dieses Prinzip, was dort vorgestellt wird, wirklich MVC, oder eher nicht? Was mich bei einem MVC-Schema wirklich verwirrt, ist die Tatsache, dass ich mittlerweile viele UI-Fenster kreiert habe. Das heißt, für jedes View bräuchte ich ein Controller, richtig? Der Model ist und bleibt ja die Datenbank.

Und dann fällt mir gleich eine dritte und letzte Frage ein.

Da ich ja, wie oben bereits angesprochen, mit dem MDI arbeite, könnte man dich das MDI als Controller betrachten oder? Denn im MDI werden die Fenster alle zentral kreiert und geöffnet. Insofern ist das MDI ja auch ein Controller, oder?

Re: MVC ohne QT-Rahmenwerk

Verfasst: Dienstag 31. Mai 2016, 09:24
von Sirius3
@Sophus: ich weiß jetzt nicht, was Du mit *dem* MDI meinst? Das ist doch auch nur ein Konzept. Das Problem, das ich bei MVC sehe, ist, dass man das C eigentlich nicht braucht; in Deinem Verlinkten Beispiel macht der Controller eigentlich nichts. Wenn Du für jedes View einen Controller machst, dann kannst Du die Controller-Logik auch gleich in das View packen, weil die dann so eng miteinander verzahnt sind, dass ein künstliches Trennen nur Aufwand aber keinen Nutzen generiert.

Auf der anderen Seite, wenn Du Logik brauchst, die quasi fast alle Views auch brauchen, dann wäre es sinnvoll, diese von den Views zu abstrahieren und ins Model zu stecken.

Ein Controller ist also nur dann sinnvoll, wenn das Model schon extern vorgegeben und unveränderlich ist. Dann würde ich es aber DLA (Datenhaltung-Logik-Anzeige) nennen und der Anzeige überhaupt keinen Zugriff auf die Daten erlauben.

Re: MVC ohne QT-Rahmenwerk

Verfasst: Dienstag 31. Mai 2016, 09:37
von Sophus
@Sirius3: Mit MDI meinte ich die MDI_Area, die man im QT-Designer verwenden kann.Oder habe ich deine Frage nicht ganz verstanden? Meistens werden die Programme in SDI verwirklicht, was für mich eher unübersichtlich ist. Aber wie gesagt, ober Sinn und Unsinn lässt sich streiten. Von daher möchte ich an dieser Stelle keine Diskussion vom Zaun brechen.

Meine Überlegung war eher: Da alle Fenster in meinem Programm durch das MDI als Sub_Window geöffnet werden, und die Fenster zentral kreiert und geöffnet werden, würde nach meinem Verständnis das MDI-Verfahren ja als eine Art Controller dienen? Denn das MDI-Programm erbt ja vom QMainWindow(). Ist zusamen das Hauptprogramm.

Re: MVC ohne QT-Rahmenwerk

Verfasst: Dienstag 31. Mai 2016, 09:51
von Sirius3
@Sophus: die MDI-Area ist ein View. Und mit gutem Willen ein Controller, der die Anweisung »erzeuge ein Unterfenster« entgegennimmt und sie ausführt. Auf keinen Fall ist die MDI-Area ein Controller für alle Views. Da fehlt ja auch irgendwie die Verwendung des Models.

Re: MVC ohne QT-Rahmenwerk

Verfasst: Mittwoch 1. Juni 2016, 22:30
von Sophus
@Blackjack,

in meiner ersten Version meintest du, dass der Controller zu viel vom View weißt.

Ich habe mich an diesem Beispiel orientiert.
http://www.hsg-kl.de/faecher/inf/python ... /index.php

Scrolle bis zum Template 2 - MVC oder bis zum Template 3 - Timer, Canvas, sauberes Beenden. Dort findest du die Beispiele, woran ich mich orientierte. Ich weiß allerdings nicht, ob dieses Beispiel "falsch" ist. Wie du ja bereits sagtest, weißt der Controller leider zu viel.

Re: MVC ohne QT-Rahmenwerk

Verfasst: Mittwoch 1. Juni 2016, 23:17
von BlackJack
Um $GOTTES Willen ist das alles gruselig. Einbuchstabige Typpräfixe für einbuchstabige Namen, `eval()` auf Benutzereingaben die eigentlich nur in Zahlen umgewandelt werden sollen. Ab und zu auch mal längere Namen in verschiedenen Schreibweisen, GUIs mit `place()`, Aktionen an `Button` mit `bind()` statt als `command`, also effektives umgehen der vom Benutzer erwarteten Verhaltensweisen von Schaltflächen. Und dann eine `__del__()`-Methode mit der Begründung „Der Destruktor __del__ ist für diesen Zweck gedacht.“

Falls die Webseite aus Papier wäre, würde ich ja verbrennen vorschlagen. :evil: Auf jeden Fall nicht lesen, da steht sehr viel Unsinn.

Es gibt dort ja auch eine weitere spezielle MVC-Seite: http://www.hsg-kl.de/faecher/inf/python ... /index.php mit weiteren Beispielen von deren Idee von MVC und auch dort kennt der Controller sowohl Model als auch View sehr intim, beide werden auch *im* Controller erzeugt. Beides ist also nicht austauschbar. An der Stelle würde mich dann wirklich mal interessieren was der Vorteil von dieser Aufteilung auf drei Klassen sein soll. Das sieht mir einfach nach „MVC ist so ein tolles Ding das macht alles viel schöner, UML-Diagramme viel umfangreicher, das muss man jetzt überall drauf pflastern“ aus. Eine Trennung von Geschäftslogik und GUI hätte bei den Beispielen ausgereicht und mindestens bei dem `ggt`-Beispiel hätte man sich die Modelklasse auch sparen können weil es semantisch gar keine Klasse ist. Was auch hässlich ist, sind die Namen `Model`, `View`, und `Controller` in jedem Beispiel. Ein Name sollte dem Leser verraten was der Wert im *Kontext des Programms* bedeutet und nicht (nur) was er in einem abstrakten Entwurfsmuster für eine Rolle einnimmt.

Re: MVC ohne QT-Rahmenwerk

Verfasst: Freitag 3. Juni 2016, 13:25
von Sophus
@BlackJack: Deinen Link kenne ich auch. Sie ist mir beim Recherchieren in die Hände gefallen. Ich habe noch einen weiteren Link gefunden. Diesmal bewegt sich das Beispiel im Bereich wxPython.

http://wiki.wxpython.org/ModelViewController

Wie du siehst, sind diese extremen Varianten ziemlich stark verbreitet. Beruht es auf die Fehlinterpretation des MVC-Konzeptes? Soweit ich das MVC-Konzept verstehe, ist, dass es darauf beruht, dass man Logik und View voneinander trennen kann, und austauschbar bleiben soll. Wobei man nicht immer und überall ein MVC-Konzept braucht, wenn man View und Logik voneinander trennt.

Aber ich denke, bei meinem MDI-Konzept wäre ein MVC-Konzept durchaus anzuwenden, mhmh?

Re: MVC ohne QT-Rahmenwerk

Verfasst: Freitag 3. Juni 2016, 13:46
von Sirius3
@Sophus: interessant ist ja bei Deinem neusten Link, dass im Theorieteil View/Controller immer zusammenstehen und nie einzeln erklärt werden. Der Autor hatte wohl selbst Schwierigkeiten, zwischen Controller und View sauber zu trennen. Wenn man die Beispiele anschaut, dann ist sein Controller kein Controller sondern einfach das Hauptfenster.

Re: MVC ohne QT-Rahmenwerk

Verfasst: Freitag 3. Juni 2016, 13:47
von DasIch
Sophus hat geschrieben:Wobei man nicht immer und überall ein MVC-Konzept braucht, wenn man View und Logik voneinander trennt.
Man braucht MVC nie, es hat einige gewaltige Probleme und es wird in einigen Bereichen auch nicht verwendet.

Überhaupt ist MVC auch nur ein Pattern, ein Muster. Wenn man ein Problem hat (und nur dann!) verwendet man häufig Lösungen die in dieses Muster fallen oder ähnlich sind.

Re: MVC ohne QT-Rahmenwerk

Verfasst: Sonntag 5. Juni 2016, 16:02
von Sophus
@DasIch: Wobei in machen Problemkonstellationen das MCV-konzept automatisch zur Stande kommt. Ich zum Beispiel schreibe eine Filmverwaltungs-Software. Ich bin ein Film-Liebhaber. Und wer weiß, vielleicht habe ich irgendwann im Leben eine eigene Filmbibliothek :-)

Zurück zum Thema. Ich löse mein Problem wie folgt. Ich habe eine View, einen Thread und einen Model der für die Datenbank verantwortlich ist. In dieser Konstellation ist der/das Thread ein Controller, oder? Natürlich dient ein Thread dazu, damit die View bei intensiven Arbeiten nicht kurzfristig einfriert Aber da der Trhead in dem Moment zwischen der View und dem Model steht, betrachte ich diesen Thread als Controller. Demzufolge ist unweigerlich ein MVC-Konzept entstanden.

Re: MVC ohne QT-Rahmenwerk

Verfasst: Sonntag 5. Juni 2016, 16:12
von Sirius3
@Sophus: Oh Sophus, ein Thread ist auf keinen Fall ein Controller, weil ein Thread daneben steht, nebenläufige Programmierung eben. Und dass ein Controller zwischen View und Modell steht, ist nur eine Variante von MVC, die meist aber keinen wirklichen Vorteil bietet.