#!/usr/bin/python
#
#-----------------------------------------------------------------------
# Ma_Sys.ma EinMalEins 1.0.0.2, Copyright (c) 2012 Ma_Sys.ma.
# For further info send an e-mail to Ma_Sys.ma@web.de.
# Or visit http://masysma.ohost.de/ alternatively.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#-----------------------------------------------------------------------
#
import gtk
import gtk.gdk
import pango
import random
import sys
import threading
import time
STATUS_NORMAL = 1
STATUS_ERROR = 2
STATUS_INCORRECT = 3
class MaSysMaEinMalEinsBackgroundMode(threading.Thread):
SLEEP_TIME = 60 * 2 # Seconds between questions
CHECK_INTERVAL = 10 # Seconds between terminate checks (this does not seem to be a good solution)
def __init__(self,main):
threading.Thread.__init__(self)
self.main = main
self.terminated = False
def term(self):
self.terminated = True
def run(self):
time_passed = 0
while not self.terminated:
if time_passed >= self.SLEEP_TIME:
gtk.gdk.threads_enter()
self.main.show()
gtk.gdk.threads_leave()
time_passed = 0
time.sleep(self.CHECK_INTERVAL)
time_passed += self.CHECK_INTERVAL
class MaSysMaEinMalEinsExercise:
LIMIT_LOWER = 3
LIMIT_UPPER = 10 # Should be enlarged to 20
PROBABILITY_OF_DIVISION = 0.5
def __init__(self):
self.initialize()
def initialize(self, reinitialize_list = True):
if reinitialize_list:
self.exercises = []
for i in range(self.LIMIT_LOWER, self.LIMIT_UPPER):
for j in range(self.LIMIT_LOWER, self.LIMIT_UPPER):
self.exercises.append((i, j, i * j))
def exercise(self, gui):
if gui.is_background_mode:
gui.hide()
factor_1, factor_2, result = self.select_random_element()
if self.do_division():
self.calculate_formula(result, "/", self.divide_by(factor_1, factor_2))
else:
self.calculate_formula(factor_1, "*", factor_2)
gui.set_text(self.formula + "?")
def select_random_element(self):
exercise = self.exercises.pop(random.randint(0, len(self.exercises) - 1))
if len(self.exercises) == 0:
self.initialize(False)
return exercise
def do_division(self):
if random.random() < self.PROBABILITY_OF_DIVISION:
return True
else:
return False
def calculate_formula(self, number_1, symbol, number_2):
self.calculate_result(number_1, symbol, number_2)
self.formula = str(number_1) + " " + symbol + " " + str(number_2) + " = "
def calculate_result(self, number_1, symbol, number_2):
if symbol == "*":
self.result = number_1 * number_2
elif symbol == "/":
self.result = number_1 / number_2
else:
self.result = -1
def divide_by(self, factor_1, factor_2):
if random.random() < 0.5:
return factor_1
else:
return factor_2
def check(self, gui):
"""Returns: True: Next exercise; False: Keep exercise."""
if not gui.get_text() == "":
try:
parsed_input = int(gui.get_text())
if parsed_input == self.result:
return True
except:
gui.set_status(STATUS_ERROR)
gui.set_text("Invalid Input!")
return False
gui.set_status(STATUS_INCORRECT)
gui.set_text(self.formula + str(self.result))
return False
class MaSysMaEinMalEins(gtk.Window):
def __init__(self):
gtk.gdk.threads_init()
self.layout()
self.initialize_data()
self.background_mode_check()
self.exercise.exercise(self)
self.initialize_frame_data_post()
gtk.gdk.threads_enter()
gtk.main()
gtk.gdk.threads_leave()
def layout(self):
gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)
self.set_title("Ma_Sys.ma EinMalEins")
self.connect("destroy", self.destroy)
self.set_border_width(5)
self.description = gtk.Label("No exercise yet.")
# http://faq.pygtk.org/index.py?req=show&file=faq04.001.htp
self.description.modify_font(pango.FontDescription("sans 22"))
self.result_input = gtk.Entry()
self.result_input.connect("key_press_event", self.key_event)
layout = gtk.VBox()
layout.add(self.description)
layout.add(self.result_input)
self.add(layout)
def initialize_data(self):
self.status = STATUS_NORMAL
self.exercise = MaSysMaEinMalEinsExercise()
def background_mode_check(self):
if len(sys.argv) == 2 and sys.argv[1] == "-b":
self.is_background_mode = True
self.background_mode = MaSysMaEinMalEinsBackgroundMode(self)
self.background_mode.start()
else:
self.is_background_mode = False
def initialize_frame_data_post(self):
self.resize(300, 200)
self.set_gravity(gtk.gdk.GRAVITY_CENTER)
self.show_all()
def key_event(self, widget, event):
key = gtk.gdk.keyval_name(event.keyval)
if key == "Return":
self.user_accepted()
self.result_input.set_text("")
elif key == "q":
self.destroy()
else:
return (not event.string.isdigit()) and event.string.isalnum()
return False
def user_accepted(self):
if self.status == STATUS_NORMAL:
if self.exercise.check(self):
self.exercise.exercise(self)
return # Important: We do not want to reset status as it might have been changed while checking
elif self.status == STATUS_ERROR:
self.description.set_text(self.exercise.formula)
elif self.status == STATUS_INCORRECT:
self.exercise.exercise(self)
self.status = STATUS_NORMAL
def destroy(self, widget = None, data = None):
if self.is_background_mode:
self.background_mode.term()
gtk.main_quit()
def set_text(self, text):
self.description.set_text(text)
def get_text(self):
return self.result_input.get_text()
def set_status(self, status):
self.status = status
def get_status(self):
return self.status
if __name__ == '__main__':
MaSysMaEinMalEins()