Einfaches Lernprogramm

Du hast eine Idee für ein Projekt?
Benutzeravatar
kaytec
User
Beiträge: 608
Registriert: Dienstag 13. Februar 2007, 21:57

Hallo !!

Mein Kollege arbeitet als Fortbilder für Menschen mit geistiger Behinderung. Er hat verschieden Lernprogramme, nur die sind nicht so ganz geeignet. Er möchte ein einfaches Programm mit dem er Fragen stellen kann (Bei meinem Beispiel: Für den Telefondienst in userer Telefonzentrale) und die Benutzer auch alleine lernen lassen kann. Es müssen Falscheingabe toleriert werden (Gross/Kleinschreibung oder Leerzeichen --> bei mir noch nicht berücksichtigt).

Das ganze soll später noch eine einfache Oberfläche bekommen und sehr einfach in der Handhabung sein. Die Oberfäche sollte fest sein, damit nicht auf dem Desktop "gespielt" werden kann - geht das mit Tkinter ?

Ist der Ansatz soweit ok ?

Code: Alles auswählen

#! /usr/bin/python
# -*- coding: utf-8 -*-
import random
#
# fragen = ist eine Beispielliste - soll spaeter aus Textdatei geladen werden
#
fragen = [['Verbinden sie mich bitte mit der Schreinerei ?',
           '182', '182', '321', '234', '342'],
          ['Ich wuerde gerne Frau Mueller sprechen',
           '154', '154', '432', '346'],
          ['Ich haette gerne den sozialen Dienst',
           '101', '101', '434',]]

zufalls_index = range(0, len(fragen))
random.shuffle(zufalls_index)
for index in zufalls_index:
    antwort = raw_input(fragen[index][0])
    if antwort.upper() == fragen[index][1].upper():
        print 'Richtig'
    else:
        print 'Falsch'
        hilfe_antworten = range(2, len(fragen[index]))
        random.shuffle(hilfe_antworten)
        hilfe = raw_input('|'.join(fragen[index][i] for i in hilfe_antworten))
        if hilfe.upper() == fragen[index][1].upper():
            print 'Richtig'
        else:
            print 'Falsch'
gruss und dank frank
EnTeQuAk
User
Beiträge: 986
Registriert: Freitag 21. Juli 2006, 15:03
Wohnort: Berlin
Kontaktdaten:

Das ganze soll später noch eine einfache Oberfläche bekommen und sehr einfach in der Handhabung sein. Die Oberfäche sollte fest sein, damit nicht auf dem Desktop "gespielt" werden kann - geht das mit Tkinter ?
Ich denke schon. Wichtig währe vllt. noch eine art Vollbildmodus, damit die entsprechenden Personen wirklich nicht "spielen" können und nicht abgelenkt werden können.


Mein Kollege arbeitet als Fortbilder für Menschen mit geistiger Behinderung. Er hat verschieden Lernprogramme, nur die sind nicht so ganz geeignet. Er möchte ein einfaches Programm mit dem er Fragen stellen kann (Bei meinem Beispiel: Für den Telefondienst in userer Telefonzentrale) und die Benutzer auch alleine lernen lassen kann. Es müssen Falscheingabe toleriert werden (Gross/Kleinschreibung oder Leerzeichen --> bei mir noch nicht berücksichtigt).
Hiermit ist nun aber wirklich das "nicht richtig schreiben" gemeint. Nicht die Lösungen ansich?

Denn aus einer Antwort sich etwas zusammenreimen. Das erfordert dann etwas mehr aufwand / ist kaum möglich.


Grundsätzlich musst du dir erstmal klar machen: Was möchte ich überhaupt.
Wie werden die Daten/Fragen gespeichert (in Textdateien). Werden Punkte vergeben/gespeichert etc.

Dann würde ich nicht mit der Konsole anfangen, wenn du die so oder so nicht benutzt. Okay... vllt. um den Ablauf zu simulieren.
Sondern eventuell gleich die grafische Umsetzung angreifen.
Er hat verschieden Lernprogramme, nur die sind nicht so ganz geeignet.
Hat er sonst so rumgeschaut, ob es entsprechendes wirklich nicht gibt?

Und ganz Wichtig:
Mein Kollege arbeitet als Fortbilder für Menschen mit geistiger Behinderung
Aber Lesen/Schreiben usw. wird vorausgesetzt, ja?

MfG EnTeQuAk
BlackJack

@kaytec: Ich finde diese ganze Indexerei unheimlich undurchsichtig. Selbst nach dem ich sie entfernt habe, verstehe ich die Daten bzw. den Sinn des Programmablaufs noch nicht.

Soweit ich das interpretiere sind in den `fragen`-Listen schonmal zwei *verschiedene* Informationen. Die Frage und mögliche Antworten!? Die sollte man auch trennen. Und dann den Einzelteilen im Programmablauf vernünftige Namen geben. Das ``fragen[1]`` eigentlich die erste *Antwort* ist, ist nicht besonders offensichtlich. Bei ``antworten[0]`` ist das wesentlich klarer. Ungetestet:

Code: Alles auswählen

import random

FRAGEN = (('Verbinden sie mich bitte mit der Schreinerei ?',
           ('182', '182', '321', '234', '342')),
          ('Ich wuerde gerne Frau Mueller sprechen',
           ('154', '154', '432', '346')),
          ('Ich haette gerne den sozialen Dienst',
           ('101', '101', '434')))


def shuffled(iterable):
    result = list(iterable)
    random.shuffle(result)
    return result


def main():
    for frage, antworten in shuffled(FRAGEN):
        antwort = raw_input(frage)
        if antwort.upper() == antworten[0].upper():
            print 'Richtig'
        else:
            print 'Falsch'
            hilfe = raw_input('|'.join(shuffled(antworten)))
            if hilfe.upper() == antworten[0].upper():
                print 'Richtig'
            else:
                print 'Falsch'
Benutzeravatar
kaytec
User
Beiträge: 608
Registriert: Dienstag 13. Februar 2007, 21:57

@EnTeQuAk

Ein geistige Behinderung ist eher eine allgemeine Beschreibung. Das kann eine Lernbehinderung sein oder auch mehrfachschwerstbehindert bedeuten. Bei mir im Wohnheim befindet sich eine Frau über 60, die in ihrer Jugend psychisch erkrankt ist und dadurch eine psychische Behinderung hat. Sie ist in ihrem Verhalten einem Menschen mit geistiger Behinderung sehr nahe. Sie kann lesen, schreiben, rechnen etc.. Es können aber auch Menschen mit einer geistigen Behinderung schreiben, rechnen etc. So eine Werkstatt für Menschen mit einer geistigen Behinderung ist sehr vielfältig. Wir haben ein Schreinere, Metallwerkstatt, Gärtnerei, Wäscherei etc. Die Arbeiten führen die Mitarbeiter mit geistiger Behinderung aus und nicht die Betreuer. DIeses Programm soll Mitarbeiter an der Telefonzentrale schulen. Sie sollen Namen mit Telefonnummern verbinden, damit sie nicht in ihren Unterlagen suchen müssen, sondern schnell auf Anfragen reagieren können. Das sind natürlich Mitarbeiter, die eher eine Lernbehinderung haben und oft auch motorische Einschränkungen besitzen. Die Fehlertoleranz sollte diese Einschränkungen berücksichtigen. Ein Leerzeichen zuviel oder mehrer Grossbuchstaben.

Vollbildmodus habe ich auch gedacht, der sich aber nicht "wegklicken" lässt.

Die Daten wollte ich aus einer Textdatei laden, denn so ist es auch Mitarbeitern m.g.B möglich, diese mit Daten zu "befüllen".

Verbinden sie mich bitte mit der Schreinerei ?
101
101
456
675
#
Verbinden Sie mich mit der Gärtnerei ?
304
304
564
567
766
#
usw.
Mit split(#) die einzelnen Fragen mit zugehörigen Antworten trennen !?

Kennt BlackJack bestimmt von meinem tollen Adressbuch :lol:

Das müsste doch auch mit Bildern bzw. Piktogrammen gehen ? In der Textdatei steht der Pfad zu dem Bild !?

Er hat so ganz tolle Programme auf der Dosebene, die lassen sich auch so mit Daten füttern, doch haben keine Fehlertoleranz und machen keine Vorschläge. Die Benutzer bleiben an der Frage hängen und kommen nur mit der richtigen
Antwort weiter. Sie können nur mit der Hilfe von meinem Kollegen zur
nächsten Frage gelangen. Das ist natürlich frustrierend und behindert sie an der selbständigen Arbeit mit dem Programm.

Oft sind diese Programme für Kinder und unsere Mitarbeiter sind keine Kinder - die Betreuer evt. :roll:

Geld ist natürlich auch immer eine Frage - die Rechner sind z.B. aus unserer Elektroschrottabteilung und nicht auf dem aktuellen Stand der Technik - ist ja auch nicht so wichtig.

@Blackjack
Die Verbindung von Fragen und Antworten ist natürlich nicht so schön, doch das Laden der Daten und die einfache Eingabe der Daten in eine Textdatei zwingen einen doch dazu ?! Den Sinn des Programmablaufes habe ich ja schon oben beschrieben - hoffe du verstehst es. Die ganzen Zufallssachen sollen es ein wenig komplizierter machen, da die Mitarbeiter m.g.B es sehr oft üben und sich nicht die Antworten bei den "Hilfangeboten" einfach durch ihren Platz in der Ausgabe merken. Doof sind sie nicht ! Das ganze Programm soll natürlich nicht so strikt ablaufen, sondern ein Button für "Hilfe holen" oder bei einer Anzahl von falschen Eingaben einfach die Angebote ausgeben, die ich mittels Button wählen kann.

Es soll jetzt nicht der perfekte code werden bzw.das perfekte Programm. Erst mal nur laufen und mein Kollege kann es anwenden. Natürlich kann ich es später noch verbessern oder ganz verwerfen, doch bis ich es besser kann geht mein Kollege evt. in Rente.

gruss und dank frank
Zuletzt geändert von kaytec am Dienstag 3. Juli 2007, 13:31, insgesamt 1-mal geändert.
BlackJack

Die Ablage in der Form in einer Textdatei zwingt einen natürlich nicht, das 1:1 in Listen einlesen zu müssen. Deine Beispieldaten könnte man zum Beispiel so einlesen:

Code: Alles auswählen

from pprint import pprint

def lese_fragen(zeilen):
    zeilen_iterator = iter(zeilen)
    while True:
        frage = zeilen_iterator.next().strip()
        antwort = zeilen_iterator.next().strip()
        fuell_antworten = [zeile.strip()
                           for zeile in iter(zeilen_iterator.next, '#\n')]
        yield (frage, antwort, fuell_antworten)


def main():
    zeilen = open('test.txt', 'r')
    fragen = list(lese_fragen(zeilen))
    zeilen.close()
    pprint(fragen)
Ausgabe:

Code: Alles auswählen

[('Verbinden sie mich bitte mit der Schreinerei?',
  '101',
  ['101', '456', '675']),
 ('Verbinden Sie mich mit der G\xc3\xa4rtnerei ?',
  '304',
  ['304', '564', '567', '766'])]
Die richtige Antwort würde ich allerdings in den "Fülldaten" nicht wiederholen. Das ist unnötige Redundanz und eine mögliche Fehlerquelle.
Benutzeravatar
kaytec
User
Beiträge: 608
Registriert: Dienstag 13. Februar 2007, 21:57

Danke BlackJack !

Das Programm hast du ja schon fertig ! :D

So sollte es aussehen:

Code: Alles auswählen

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


def shuffled(iterable):
	result = list(iterable)
	random.shuffle(result)
	return result


def main():
	fragen_antworten=laden()
	for frage, antworten in shuffled(fragen_antworten):
		antwort = raw_input(frage)
		if antwort.upper() == antworten[0].upper():
			print 'Richtig'
		else:
			print 'Falsch'
			hilfe = raw_input('|'.join(shuffled(antworten)))
			if hilfe.upper() == antworten[0].upper():
				print 'Richtig'
			else:
				print 'Falsch'
				
				
def lese_fragen(zeilen):
	zeilen_iterator = iter(zeilen)
	while True:
		frage = zeilen_iterator.next().strip()
		antworten = [zeile.strip()
			for zeile in iter(zeilen_iterator.next, '#\n')]
		yield (frage, antworten)


def laden():
	zeilen = open('test.txt', 'r')
	fragen_antworten = list(lese_fragen(zeilen))
	zeilen.close()
	return fragen_antworten
	
if __name__ == '__main__':
	main()
danke und gruss frank
Benutzeravatar
kaytec
User
Beiträge: 608
Registriert: Dienstag 13. Februar 2007, 21:57

Habe eine Oberfläche erstellt (erfüllt noch nicht alle Anforderungen) und würde gerne wissen, ob ich da mal wieder auf dem Holzweg bin !? Bei der Löschung von gelernten Fragen, kommt diese Frage, obwohl sie aus der Liste gelöscht ist, wieder ?

Die Datenspeicherung nach BlackJack habe ich nicht so in mein Programm integrieren können.
Die sieht jetzt so aus:

Koennte ich bitte Frau Mueller sprechen !#102#345#564
Koennen Sie mich mit der Schreinerei verbinden !#345#667#123
Koennte Ich bitte Herr Mayer sprechen !#567#345#456
Ich wuerde gerne den Sozialdienst sprechen !#876#534#545

Das Programm ist hier: http://www.ubuntuusers.de/paste/12522/

gruss und dank frank
BlackJack

Du löschst Einträge aus der Liste über die Du gleichzeitig mit der ``for``-Schleife iterierst. Davon bekommt der Iterator, der in der Schleife verwendet wird, nichts mit. Schlimmer noch: Beim Entfernen des ersten Elements werden alle folgenden eine Position nach vorne verschoben. Auch das bekommt der Iterator nicht mit und überspringt deshalb jeden zweiten Eintrag.

Insgesamt sieht's so aus, als wenn Du zu nah an der Realität programmierst, also einen Kartenstapel und dessen Handhabung 1:1 nachbildest. Etwas vorne aus einer Liste zu entfernen ist nicht besonders effizient, weil wie schon gesagt, alle folgenden Elemente eine Position nach vorne kopiert werden. Das Problem löst man üblicherweise, indem man eine zweite Liste anlegt, in die alle "Karten" kommen, die nicht gewusst wurden.
Benutzeravatar
kaytec
User
Beiträge: 608
Registriert: Dienstag 13. Februar 2007, 21:57

Danke BlackJack !

Sowas habe ich gedacht - die Liste wird nicht im Speicher verändert auf den der Iterator zeigt -> Hoffe das stimmt mal so ? Jetzt kommt der Befehl 'update()' oder so - hatte ich mal gehofft ! Du beziehst dein Beispiel auf Karten. Karten in einem Karteikasten, die beim Lernen nach hinten oder nach vorne geschoben werden. Das habe ich auch in meinem Buch gesehen - kennst du das Buch auch :-) ?

gruss und dank frank
Benutzeravatar
kaytec
User
Beiträge: 608
Registriert: Dienstag 13. Februar 2007, 21:57

Warum kann ich so das Fenster nicht schliessen ?

Code: Alles auswählen

	def hilfe(self):	
		hilfe = Tk()
		for antwort in self.fragen_antworten:
			antworten = antwort
		button = (('Antwort 1'  ,0, 1, 1 ), ('Antwort 2', 1, 1, 2), 
						('Antwort 3', 2, 1, 3))
		label = ((antworten[1], 0, 0), (antworten[2], 1, 0), (antworten[3], 2, 0))
		for text, column, row in label:
			hilfe_label = Label(hilfe, text=text, font=('Arial' ,20))
			hilfe_label.grid(column = column, row = row)
		for text, column, row, index in button: 
			hilfe_button = Button(hilfe, text=text, font=('Arial',20), command = lambda 
				antwort = antworten[index] : einfuegen(antwort))
			hilfe_button.grid(column = column, row = row)
			
			
		def einfuegen(antwort):
			self.trainer_entry.insert(60, antwort)
			hilfe.destroy
gruss und dank frank
BlackJack

Du musst die Funktion nicht nur hinschreiben/referenzieren, sondern auch *ausführen* -> ``hilfe.destroy()``.
Benutzeravatar
kaytec
User
Beiträge: 608
Registriert: Dienstag 13. Februar 2007, 21:57

@ BlackJack
Danke - Ich habe es etwas anders gelöst, doch das Löschen aus einer Liste mit der gearbeitet wird ist sehr instabil, denn beim generieren von zufälligen Antworten, die zwar den richtigen Zahlenwert haben, kommt es zu einer 'falschen' Auswertung dieser Antworten. Ist es generell besser eine eigene Klasse für das Laden, Übergeben vom zufälligen Fragen und zufälligen Anworten zu schreiben ?

Hier mal die neuste Version:
http://www.ubuntuusers.de/paste/12552/

gruss und dank frank
BlackJack

An dem Programm ist einiges sehr komisch. Versuch Dir doch mal selber jede Zeile zu erklären und klar zu machen wann welcher Code ausgeführt wird.

Schau Dir mal die innere ``for``-Schleife bei der `laden()`-Funktion an. Was glaubst Du an was `eintrag` gebunden wird.

Oder bei `hilfe()`:

Code: Alles auswählen

    for antwort in self.fragen_antworten:
        antworten = antwort
Was soll dass denn bitte bewirken?

Dann gibt es drei Schleifen über alle Fragen/Antworten, aber das ist eine "Event Driven"-GUI, dass heisst Du kannst nicht einfach in einer Schleife über alle Fragen gehen sondern musst immer auf einen Button-Druck mit einem Schritt reagieren.

Und Du löscht im vorhanden Quelltext immer noch aus einer Liste über die Du iterierst. Und das alles auch noch äusserst umständlich.
Benutzeravatar
kaytec
User
Beiträge: 608
Registriert: Dienstag 13. Februar 2007, 21:57

Danke BlackJack !

Code: Alles auswählen

frage = self.fragen_antworten[0]
self.trainer_label2.config(text=frage[0])
Meinst du es so ? :oops:
laden()
Der eintrag sind die einzelnen Zeichen, die werden durchlaufen und der Zeilenumbruch herausgelöscht !?

gruss und dank frank
BlackJack

Du weist also was die Schleife macht, und was macht die `lstrip()`-Methode?
Benutzeravatar
kaytec
User
Beiträge: 608
Registriert: Dienstag 13. Februar 2007, 21:57

@BlackJack

Meinst du so :oops: ?

Code: Alles auswählen

for zeile in zeilen:
	ohne_zeilen_umbruch = zeile.lstrip()
	fragen_antworten.append(ohne_zeilen_umbruch.split('#'))
Die Methode kann das auch so

gruss und dank frank

edit 20:51 Montag, Juli 09 2007

nein - geht doch nicht so - Der letze Zeilenumbruch wird nicht gelöscht ?

gruss frank
BlackJack

Das `l` in `lstrip()` steht für left/links. Das Zeilenende steht aber 'r'echts.
Benutzeravatar
kaytec
User
Beiträge: 608
Registriert: Dienstag 13. Februar 2007, 21:57

Danke BlackJack !

rstrip() löscht den letzten Zeilenumbruch auch nicht ?
['Koennte Ich bitte Herr Mayer sprechen !', '567', '345', '456\n']

gruss frank
BlackJack

Doch tut es:

Code: Alles auswählen

In [1]: 'spam\n'.rstrip()
Out[1]: 'spam'
Benutzeravatar
kaytec
User
Beiträge: 608
Registriert: Dienstag 13. Februar 2007, 21:57

@BlackJack

So müsste es in meinem Beispiel aussehen ?

Code: Alles auswählen

import random
zeilen = (('Koennte ich bitte Frau Mueller sprechen !#102#345#564\n'), 
				('Koennen Sie mich mit der Schreinerei verbinden!#345#667#123\n'))

def laden(zeilen):
	fragen_antworten = list()
	for zeile in zeilen:
		zeile.rstrip()
		fragen_antworten.append(zeile.split('#'))
	random.shuffle(fragen_antworten)
	return fragen_antworten

fragen_antworten = laden(zeilen)
print fragen_antworten
Da wird es bei mir aber nicht gelöscht ?

gruss und dank frank
Antworten