#!/usr/bin/python # -*- coding: utf-8 -*- # # Pyromaths # Un programme en Python qui permet de créer des fiches d'exercices types de # mathématiques niveau collège ainsi que leur corrigé en LaTeX. # Copyright (C) 2006 -- Jérôme Ortais (jerome.ortais@pyromaths.org) # # 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 2 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, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # #---------------------------------------------------------------------- # Pyromaths : Mode graphique #---------------------------------------------------------------------- import wx import os import string import sys if os.name == "posix": if os.path.basename(__file__)=="pyromaths": sys.path.append(os.path.join( os.path.dirname(__file__), "../lib/pyromaths")) import troisiemes.troisiemes import quatriemes.quatriemes import cinquiemes.cinquiemes import sixiemes.sixiemes import dialog_box import gettext import locale locale.setlocale(locale.LC_ALL, "") locale.textdomain("pyromaths") gettext.textdomain("pyromaths") _=locale.gettext ID_EXIT = 101 ID_ABOUT = 102 ID_SITE = 103 LesFiches = [(_('Sixieme'), sixiemes.sixiemes, ( _("u'Calcul mental'"), # _("u'Ecrire un nombre decimal'"), _("u'Placer une virgule'"), _("u'Ecriture fractionnaire ou decimale'"), _("u'Decomposition de nombres decimaux'"), _("u'Conversions unites'"), _("u'Poser des operations (sauf divisions)'"), _("u'Produits et quotients par 10, 100, 1000'"), _("u'Classer des nombres decimaux'"), _("u'Droites, demi-droites, segments'"), _("u'Droites perpendiculaires et paralleles'"), _("u'Proprietes sur les droites'"), _("u'Multiples de 2, 3, 5, 9, 10'"), _("u'Fractions partage'"), _("u'Fractions et abscisses'"), _("u'Symetrie et quadrillages'"), _("u'Mesurer des angles'"), )), (_('Cinquieme'), cinquiemes.cinquiemes, (_("u'Priorites operatoires'"), _("u'Symetrie centrale'"), _("u'Fractions egales'"), _("u'Sommes de fractions'"), _("u'Produits de fractions'"))), (_('Quatrieme'), quatriemes.quatriemes, ( _("u'Calcul mental'"), _("u'Sommes de fractions'"), _("u'Produits et quotients de fractions'"), _("u'Fractions et priorites'"), _("u'Proprietes sur les puissances'"), _("u'Proprietes sur les puissances de 10'"), _("u'Ecritures scientifiques'"), _("u'Puissances de 10'"), _("u'Distributivite'"), _("u'Double distributivite'"), _("u'Theoreme de Pythagore'"), _("u'Reciproque du theoreme de Pythagore'"), _("u'Cercle et theoreme de Pythagore'"), _("u'Theoreme de Thales'"), _("u'Trigonometrie'"), )), (_('Troisieme'), troisiemes.troisiemes, ( _("u'Fractions'"), _("u'Puissances'"), _("u'PGCD'"), _("u'Developpements'"), _("u'Factorisations'"), _("u'Devt, factorisato, calcul et eqo produit'"), _("u'Equation'"), _("u'Racines carrees'"), _("u'Systeme d\\'equations'"), _("u'Theoreme de Pythagore'"), _("u'Reciproque du theoreme de Pythagore'"), _("u'Cercle et theoreme de Pythagore'"), _("u'Theoreme de Thales'"), _("u'Reciproque du theoreme de Thales'"), _("u'Trigonometrie'"), ))] lst = [0 for i in xrange(max(len(LesFiches[0][2]), len(LesFiches[1][2]), len(LesFiches[2][2]), len(LesFiches[3][2])))] #---------------------------------------------------------------------- def winreg1(cle): import _winreg return _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, 'Software\\Classes\\%s\\' % cle, 0, _winreg.KEY_READ) def winreg2(cle): import _winreg valueobj = _winreg.EnumValue(cle, 0) valuedata = str(valueobj[1]).strip() return 'Software\\Classes\\%s\\shell\\open\\command\\' % valuedata def programme_manquant(self, message): #Ouvre la boite de dialogue informant qu'il manque un programme pour #Pyromaths verif = dialog_box.Verif_progs(self, message, 'Logiciel manquant !') #verif.ShowModal() verif.Destroy() def verifie_programmes(self): # Vérifie que LaTeX et un lecteur de pdf sont bien installés sous Windows, # et cherche quel est le lecteur pdf installé sous les autres OS. #Défini les programmes utilisés pour obtenir les fiches (pdfviewer, latex, dvips, ps2pdf) = ("", "", "", "") if os.name == "nt": #Cas de Windows. erreur_pdf = \ _(u'''Pyromaths ne trouve pas de logiciel installe pour lire les fichiers pdf. Vous pouvez utiliser Sumatra PDF. Vous ne pourrez pas lire les fiches sans ce programme !''') erreur_tex = \ _(u"""ePyromaths ne trouve pas de distribution LaTeX installee pour creer les fiches. Il est conseille d'utiliser MikTeX. Pyromaths ne pourra pas fonctionner sans ce programme !""") try: import _winreg key_name = "SOFTWARE\\Classes\\.pdf" key = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, key_name) (valeur, typevaleur) = _winreg.QueryValueEx(key, "") key = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, _("Software\\Classes\\%s\\shell\\open\\command") % valeur) (valeur, typevaleur) = _winreg.QueryValueEx(key, "") pdfviewer = string.lower(str(valeur)).strip().lstrip('"').split('.exe', 1)[0] + '.exe' key.Close() except WindowsError: programme_manquant(self, erreur_pdf) try: import _winreg key_name = "SOFTWARE\\Classes\\.dvi" key = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, key_name) (valeur, typevaleur) = _winreg.QueryValueEx(key, "") key = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, "Software\\Classes\\%s\\shell\\open\\command" % valeur) (valeur, typevaleur) = _winreg.QueryValueEx(key, "") valuedata = string.lower(str(valeur)).strip() miktex = ('\\').join(str(valuedata).split('\\')[:-1]) + '\\' (latex, dvips, ps2pdf) = (miktex + 'latex.exe', miktex + 'dvips.exe', miktex + 'ps2pdf.exe') key.Close() except WindowsError: programme_manquant(self, erreur_tex) else: fin = 0 for program in ('xpdf', 'evince', 'acroread', 'simpdftex'): if not fin: for path in string.split((os.environ)["PATH"], os.pathsep): if not fin: lefichier = os.path.join(path, program) if os.path.exists(lefichier): pdfviewer = program fin = 1 (latex, dvips, ps2pdf) = ('latex', 'dvips', 'ps2pdf') return (pdfviewer, latex, dvips, ps2pdf) #Défini les programmes utilisés pour obtenir les fiches def spawn(program, *args, **kw): mode = kw.get("mode", os.P_WAIT) try: # check if the os module provides a shortcut return os.spawnvp(mode, program, (program, ) + args) except AttributeError: pass try: spawnv = os.spawnv except AttributeError: # assume it's unix pid = os.fork() if not pid: os.execvp(mode, program, (program, ) + args) return os.wait()[0] else: # got spawnv but no spawnp: go look for an executable for path in string.split((os.environ)["PATH"], os.pathsep): lefichier = os.path.join(path, program) try: return spawnv(mode, lefichier, (lefichier, ) + args) except os.error: pass raise IOError, "cannot find executable" def compilation(fiche_exos, fiche_corrige, pdfviewer, latex, dvips, ps2pdf): lerepertoire = os.getcwd() f0 = os.path.splitext(fiche_exos)[0] dir0 = os.path.dirname(fiche_exos) f1 = fiche_corrige.split(".tex")[0] dir1 = os.path.dirname(fiche_corrige) if latex: #Crée les fichiers au format dvi (2 passes pour la numérotation du #nombre de pages) if os.name == "nt": #Cas de Windows. Met des guillemets autour des #chemins de fichiers pour gérer les espaces. os.chdir(dir0) spawn(latex, '"%s"' % fiche_exos, mode=os.P_WAIT) spawn(latex, '"%s"' % fiche_exos, mode=os.P_WAIT) os.chdir(dir1) spawn(latex, '"%s"' % fiche_corrige, mode=os.P_WAIT) spawn(latex, '"%s"' % fiche_corrige, mode=os.P_WAIT) else: os.chdir(dir0) spawn(latex, fiche_exos, mode=os.P_WAIT) spawn(latex, fiche_exos, mode=os.P_WAIT) os.chdir(dir1) spawn(latex, fiche_corrige, mode=os.P_WAIT) spawn(latex, fiche_corrige, mode=os.P_WAIT) if latex and dvips: #Crée les fichiers au format postscript if os.name == "nt": #Cas de Windows. Met des guillemets autour des #chemins de fichiers pour gérer les espaces. spawn(dvips, '-t a4', '-Ppdf', '-o', '"%s%s"' % (f0, '.ps'), '"%s%s"' % (f0, '.dvi'), mode=os.P_WAIT) spawn(dvips, '-t a4', '-Ppdf', '-o', '"%s%s"' % (f1, '.ps'), '"%s%s"' % (f1, '.dvi'), mode=os.P_WAIT) else: spawn(dvips, '-t a4', '-Ppdf', '-o', '%s%s' % (f0, '.ps'), '%s%s' % (f0, '.dvi'), mode=os.P_WAIT) spawn(dvips, '-t a4', '-Ppdf', '-o', '%s%s' % (f1, '.ps'), '%s%s' % (f1, '.dvi'), mode=os.P_WAIT) if latex and dvips and ps2pdf: #Crée les fichiers au format pdf if os.name == "nt": #Cas de Windows. Met des guillemets autour des #chemins de fichiers pour gérer les espaces. os.chdir(dir0) spawn(ps2pdf, '"%s%s"' % (f0, '.ps'), mode=os.P_WAIT) os.chdir(dir1) spawn(ps2pdf, '"%s%s"' % (f1, '.ps'), mode=os.P_WAIT) else: os.chdir(dir0) spawn(ps2pdf, '%s%s' % (f0, '.ps'), mode=os.P_WAIT) os.chdir(dir1) spawn(ps2pdf, '%s%s' % (f1, '.ps'), mode=os.P_WAIT) #spawn(ps2pdf, '-dPDFSETTINGS=/prepress', #'-dAutoFilterGrayImages=false', '-dAutoFilterColorImages=false', #'-dColorImageFilter=/FlateEncode', '-dGrayImageFilter=/FlateEncode', #'-dAutoRotatePages=/None', f1+'.ps', mode=os.P_WAIT) if latex and dvips and ps2pdf and pdfviewer: #Affiche les deux fiches if os.name == "nt": #Cas de Windows. Met des guillemets autour des #chemins de fichiers pour gérer les espaces. os.chdir(dir0) spawn(pdfviewer, '"%s%s"' % (f0, '.pdf'), mode=os.P_NOWAIT) os.chdir(dir1) spawn(pdfviewer, '"%s%s"' % (f1, '.pdf'), mode=os.P_NOWAIT) else: os.chdir(dir0) spawn(pdfviewer, '%s%s' % (f0, '.pdf'), mode=os.P_NOWAIT) os.chdir(dir1) spawn(pdfviewer, '%s%s' % (f1, '.pdf'), mode=os.P_NOWAIT) #Supprime les fichiers temporaires créés par LaTeX try: for ext in ('.aux', '.dvi', '.log', '.out', '.ps'): os.remove(f0 + ext) os.remove(f1 + ext) except OSError: print u"Le fichier %s ou %s n'a pas ete supprime." % (f0 + ext, f1 + ext) #le fichier é supprimer n'existe pas. os.chdir(lerepertoire) def new_liste_exos(self, exos, debut): grid = wx.FlexGridSizer(0, 2, 0, 0) # rows, cols, hgap, vgap inbexo = len(exos) / 2 + len(exos) % 2 if not inbexo: box = wx.BoxSizer(wx.VERTICAL) if debut: text = wx.StaticText(self, -1, _("Aucun exercice pour ce niveau"), size=(120, 110)) else: text = wx.StaticText(self, -1, "", size=(120, 110)) self.bouton_continuer.Hide() box.Add(text, 0, wx.ALIGN_CENTER | wx.LEFT | wx.BOTTOM | wx.TOP, 30) return box elif debut: self.group_ctrls1 = [] for i in xrange(inbexo): exec 'sc' + str(i) + ' = wx.SpinCtrl( self, ' + str(i) + \ ' , initial=0, max=10, min=0, size=wx.Size(40, 30), \ style=wx.SP_ARROW_KEYS)' exec 'cb' + str(i) + ' = wx.CheckBox( self, -1,' + exos[i] + \ ', size=wx.Size(320, 40),style=wx.BU_EXACTFIT )' exec 'self.group_ctrls1.append((sc' + str(i) + ', cb' + str(i) + \ '))' else: self.group_ctrls2 = [] for i in xrange(len(exos) - inbexo): exec 'sc' + str(i + inbexo) + ' = wx.SpinCtrl( self,' + str(i + inbexo) + \ ' , initial=0, max=10, min=0, size=wx.Size(40, 30), \ style=wx.SP_ARROW_KEYS)' exec 'cb' + str(i + inbexo) + ' = wx.CheckBox( self, -1, ' + \ exos[i + inbexo] + \ ', size=wx.Size(320, 40),style=wx.BU_EXACTFIT )' exec 'self.group_ctrls2.append((sc' + str(i + inbexo) + \ ', cb' + str(i + inbexo) + '))' self.bouton_continuer.Show() if inbexo: if debut: for (sc, cb) in self.group_ctrls1: grid.Add(sc, 0, wx.LEFT | wx.TOP, 5) grid.Add(cb, 0, wx.LEFT | wx.TOP, 5) sc.Enable(False) for (sc, cb) in self.group_ctrls1: self.Bind(wx.EVT_CHECKBOX, self.OnGroupCheckBox, cb) self.Bind(wx.EVT_SPINCTRL, self.OnGroupSpinCtrl, sc) else: for (sc, cb) in self.group_ctrls2: grid.Add(sc, 0, wx.LEFT | wx.TOP, 5) grid.Add(cb, 0, wx.LEFT | wx.TOP, 5) sc.Enable(False) for (sc, cb) in self.group_ctrls2: self.Bind(wx.EVT_CHECKBOX, self.OnGroupCheckBox, cb) self.Bind(wx.EVT_SPINCTRL, self.OnGroupSpinCtrl, sc) return grid class Fenetre_Principale(wx.Frame): def __init__(self): wx.Frame.__init__(self, None, -1, "Pyromaths...") # Crée l'icone de l'application if os.name == "posix": if os.path.basename(__file__) == "pyromaths": self.icon = wx.Icon("/usr/share/pixmaps/pyromaths.png", wx.BITMAP_TYPE_PNG) else: pathname = os.path.dirname((sys.argv)[0]) self.icon = wx.Icon(os.path.join(os.path.abspath(pathname), "pyromaths.ico"), wx.BITMAP_TYPE_ICO) else: pathname = os.path.dirname((sys.argv)[0]) self.icon = wx.Icon(os.path.join(os.path.abspath(pathname), "pyromaths.ico"), wx.BITMAP_TYPE_ICO) self.SetIcon(self.icon) # Crée la barre de status self.CreateStatusBar() self.SetStatusText(number=0, text= _(u'Choisir un niveau, selectionner des exercices, puis cliquer sur "Creer".')) # Paramètre le menu menu_fichier = wx.Menu() menu_fichier.Append(ID_EXIT, _('&Quitter'), _('Quitter l\'application.')) menu_about = wx.Menu() menu_about.Append(ID_SITE, _(u'Acceder au &site'), _(u'Acceder au site de Pyromaths')) menu_about.AppendSeparator() menu_about.Append(ID_ABOUT, _(u'a &propos'), _(u'a propos de Pyromaths.')) # Crée la barre de menu menubar = wx.MenuBar() menubar.Append(menu_fichier, _("&Fichier")) menubar.Append(menu_about, _("&Aide")) self.SetMenuBar(menubar) # Crée le panel self.ligne = wx.BoxSizer(wx.HORIZONTAL) self.colonne_gauche = wx.BoxSizer(wx.VERTICAL) self.colonne_droite = wx.BoxSizer(wx.HORIZONTAL) self.SetBackgroundColour('#FBF5E1') # Crée les RadiosButtons self.niveaux_ctrls = [] radio0 = wx.RadioButton(self, -1, LesFiches[0][0], style=wx.RB_GROUP) radio1 = wx.RadioButton(self, -1, LesFiches[1][0]) radio2 = wx.RadioButton(self, -1, LesFiches[2][0]) radio3 = wx.RadioButton(self, -1, LesFiches[3][0]) radio3.SetValue(True) self.niveaux_ctrls.append(radio0) self.niveaux_ctrls.append(radio1) self.niveaux_ctrls.append(radio2) self.niveaux_ctrls.append(radio3) self.colonne_gauche.Add((10, 30), 0, wx.EXPAND) for radio in self.niveaux_ctrls: self.colonne_gauche.Add(radio, 0, wx.ALIGN_LEFT | wx.LEFT | wx.RIGHT | wx.TOP, 5) # Crée les boutons self.bouton_continuer = wx.Button(self, -1, _(u"Creer"), pos=(50, 50)) self.bouton_continuer.SetDefault() self.bouton_continuer.SetSize(self.bouton_continuer.GetBestSize()) self.bouton_fermer = wx.Button(self, -1, _(u"Quitter"), pos=(50, 50)) self.bouton_fermer.SetSize(self.bouton_fermer.GetBestSize()) # Intègre la liste, les boutons é la colonne gauche self.colonne_gauche.AddMany([((10, 50), 0, wx.EXPAND), (self.bouton_continuer, 0, wx.CENTER), ((10, 10), 0, wx.EXPAND), (self.bouton_fermer, 0, wx.CENTER), ((10, 50), 0, wx.EXPAND)]) # Crée les options self.option1 = wx.CheckBox(self, -1, _(u"Creer un PDF"), style= wx.BU_EXACTFIT) self.option1.SetValue(1) self.colonne_gauche.Add(self.option1, 0, wx.CENTER) # écrit la liste des exercices dans la colonne de droite self.niveau_selectionne = 3 # le niveau par défaut est le niveau 3e exos = LesFiches[3][2] # le niveau par défaut est le niveau 3e self.exos1 = new_liste_exos(self, exos, 1) self.exos2 = new_liste_exos(self, exos, 0) self.colonne_droite.Add(self.exos1, 0, wx.EXPAND) self.colonne_droite.Add(self.exos2, 0, wx.EXPAND) self.ligne.Add(self.colonne_gauche, 0, wx.TOP) self.ligne.Add(self.colonne_droite, 0, wx.TOP) # BINDINGS # Actions de la barre de menu self.Bind(wx.EVT_MENU, self.OnExit, id=ID_EXIT) self.Bind(wx.EVT_MENU, self.site, id=ID_SITE) self.Bind(wx.EVT_MENU, self.About, id=ID_ABOUT) # Action des sélections dans les boutons radio for radio in self.niveaux_ctrls: self.Bind(wx.EVT_RADIOBUTTON, self.Radio_Button, radio) # Actions des boutons self.bouton_fermer.Bind(wx.EVT_BUTTON, self.OnExit, id=self.bouton_fermer.GetId()) self.bouton_continuer.Bind(wx.EVT_BUTTON, self.OnValide, id=self.bouton_continuer.GetId()) # Actions des options # Inutile de créer une action pour ce bouton. # self.Bind(wx.EVT_CHECKBOX, self.OnOption1, # self.option1) # Affiche la boite de dialogue Pyromaths self.SetSizer(self.ligne) self.SetAutoLayout(1) self.SetSize(self.GetBestSize()) self.CenterOnParent(wx.BOTH) self.Show(1) def OnExit(self, event): self.Close(True) def site(self, event): import webbrowser webbrowser.open('http://www.pyromaths.org') def About(self, event): from dialog_box import MyAboutBox about = MyAboutBox(self) about.ShowModal() about.Destroy() def OnSelect(self, event): pos = self.liste_niveaux.GetSelection() self.commentaires.SetValue(LesFiches[pos][2]) def Radio_Button(self, event): radio_selected = event.GetEventObject() cpt = 0 self.colonne_droite.Clear(1) for radio in self.niveaux_ctrls: if radio is radio_selected: self.niveau_selectionne = cpt exos = LesFiches[cpt][2] exos1 = new_liste_exos(self, exos, 1) exos2 = new_liste_exos(self, exos, 0) self.colonne_droite.Add(exos1, 0, wx.EXPAND) self.colonne_droite.Add(exos2, 0, wx.EXPAND) cpt = cpt + 1 self.ligne.Layout() def OnGroupCheckBox(self, event): for groups in (self.group_ctrls1, self.group_ctrls2): for (sc, cb) in groups: if cb.GetValue(): if not sc.GetValue(): sc.Enable(True) sc.SetValue(1) else: sc.Enable(False) sc.SetValue(0) lst[sc.GetId()] = sc.GetValue() def OnGroupSpinCtrl(self, event): for groups in (self.group_ctrls1, self.group_ctrls2): for (sc, cb) in groups: if not sc.GetValue(): cb.SetValue(False) sc.Enable(False) lst[sc.GetId()] = sc.GetValue() def OnValide(self, event): progs = verifie_programmes(self) entete = self.option1.GetValue() listevide = True for i in xrange(len(lst)): if lst[i]: listevide = False break if listevide: dialog_box.Verif_progs(self, _(u"Veuillez selectionner des exercices..."), _("Attention !")) else: wildcard = \ _(u"Documents TeX (*.tex)|*.tex|Tous les fichiers (*.*)|*.*") dossier = os.getcwd() (f0, f1) = ("", "") dlg = wx.FileDialog(self, _(u"Enregistrer les exercices sous ..."), dossier, u"exercices.tex", wildcard, wx.SAVE | wx.OVERWRITE_PROMPT) if dlg.ShowModal() == wx.ID_OK: f0 = dlg.GetPath() dossier = dlg.GetDirectory() nom_exo = dlg.GetFilename() file = os.path.splitext(nom_exo) if file[1].lower() == ".tex": nom_exo = file[0] else: nom_exo = file[0] + file[1] f0 = f0 + ".tex" dlg.Destroy() if f0: dlg = wx.FileDialog(self, _(u"Enregistrer le corrige sous ..."), dossier, u"%s-corrige.tex" % nom_exo, wildcard, wx.SAVE | wx.OVERWRITE_PROMPT) if dlg.ShowModal() == wx.ID_OK: f1 = dlg.GetPath() if f1.lower().rfind(".tex", len(f1) - 4) < 0: f1 = f1 + ".tex" dlg.Destroy() if f0 and f1: LesFiches[self.niveau_selectionne][1].main(lst, f0, f1, entete) if entete: print f0, f1 compilation(f0, f1, progs[0], progs[1], progs[2], progs[3]) class Pyromaths(wx.App): def OnInit(self): wnd = Fenetre_Principale() wnd.Show(True) return True if __name__ == '__main__': app = Pyromaths(redirect=False) app.MainLoop() def latotale(f0, f1, niveau): """ Cr\xc3(C)er des fiches exemples pour tous les niveaux avec tous les exercices dans le dossier /home/jerome/workspace/Pyromaths/src/exemples """ liste = [1 for i in xrange(len(LesFiches[niveau][2]))] LesFiches[niveau][1].main(liste, f0, f1, 1) compilation(f0, f1, 'xpdf', 'latex', 'dvips', 'ps2pdf')