Archivio

Archivio per la categoria ‘wxpython’

Python: Copy progress bar

9 Marzo 2012 Commenti chiusi
    import os  
    import wx  
      
      
    class ProgressBar(wx.ProgressDialog):  
        '''''Progress Bar class'''  
        def __init__(self, *args, **kwargs):  
            super(ProgressBar, self).__init__(*args, **kwargs)  
      
      
    def progress_copy(src, dest, buffer_size=1024):  
        '''''copy source file to destination file'''  
        steps = os.stat(src).st_size / buffer_size + 1  
        source = open(src, 'rb')  
        target = open(dest, 'wb')  
        pbar = ProgressBar(title="wait...", message="Time remaining",  
                           maximum=steps, parent=None,  
                           style=wx.PD_AUTO_HIDE | wx.PD_ELAPSED_TIME |  
                           wx.PD_REMAINING_TIME)  
        count = 0  
        while count <= (steps):  
            chunk = source.read(buffer_size)  
            if chunk:  
                count += 1  
                target.write(chunk)  
                pbar.Update(count)  
            else:  
                break  
      
        source.close()  
        target.close()  
        pbar.Destroy()  
        print "...copied"  
      
      
    def main():  
        '''''app starter'''  
        app = wx.PySimpleApp()  
        progress_copy(src=SOURCE_FILE, dest=DEST_FILE)  
        app.MainLoop()  
      
    if __name__ == '__main__':  
        main()
Categorie:python, wxpython Tag:

Python: FantaStat 1.1 Light

15 Dicembre 2011 Commenti chiusi

Nuova versione FantaStat2.0

Si sta avvicinando il mercato di riparazione, quindi ho creato un piccolo tool con Python, per tutti i FantaAllenatori, che non vogliono arrivare impreparati all’asta invernale.

download:
eseguibile win
sorgenti python

Funziona come i fratelli maggiori FantaManager Full e
FantaManager Light, cioè si importano i file MCCxx.txt,
e si consultano le varie medie voto, gol total/bancaldo.wordpress.com/2011/08/05/lista-calciatori-gazzetta-2011-2012/”>file MCCxx.txt,
e si consultano le varie medie voto, gol totali, presenze,
di ogni singolo giocatore.

L’interfaccia è semplicissima (utilizzate le solite wx):


un bottone per importare il file MCC, un bottone per uscire,
una list-control per visualizzare i dati del database ed
un radiobox per visualizzare i calciatori, per ruolo.
Le colonne della list-control, ordinano in modo di, presenze,
di ogni singolo giocatore.

L’interfaccia è semplicissima (utilizzate le solite wx):


un bottone per importare il file MCC, un bottone per uscire,
una list-control per visualizzare i dati del database ed
un radiobox per visualizzare i calciatori, per ruolo.
Le colonne della list-control, ordinano in modo decrescente i
dati, eccetto nome e squadra (che francamente non incidono
sulla qualità della ricerca)

Il database è generato e gestito con Sqlite e SqlAlchemy.
Gli oggetti riferiti al db ed sqla (tabelle, campi ecc) sono definiti
in non-declarative perchè mi piace semplificare il porting su web con
Pylons (ho trovato difficoltà quando ho definito gli oggetti in
declarative)

Come sempre, ho cercato di utilizzare il pattern MVC,
spero riducendo al minimo gli errori di stile.

NOTA:
Ricordarsi di posizionare le dirs /images e /giornate
dove si troverà l’eseguibile o i sorgenti.

Qui i codici (sicuramente migliorabili):

Model:

# -*- coding: utf-8 -*-#
# DataLight.py
'''MODEL Module for FantaStat Application:
Database engine: provided by Sqlite
Database O.R.M: provided by SqlAlchemy (no_declarative)
All data for fantapython manage are contained here.
All the O.R.M. operation on data are defined here as
Model-Class methods.
''' 

from sqlalchemy import create_engine, Column, Integer, Float
from sqlalchemy import ForeignKey, Table
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import aliased
from sqlalchemy.orm import sessionmaker
from sqlalchemy import orm, func, desc, asc
from sqlalchemy import MetaData
from sqlalchemy.types import Unicode

class Giocatore(object):
    '''class for sqla mapper'''
    pass

class Voto(object):
    '''class for sqla mapper'''
    pass

class Model(object): 
    '''Model class with common application data'''
    
    base = declarative_base()
    engine = create_engine('sqlite:///fantastat.db', echo = False)
    metadata = MetaData()
    
    giocatore_table = Table('giocatori', metadata,
        Column('id', Integer(), primary_key=True),
        Column('idgaz', Integer()),
        Column('nome', Unicode(255)),
        Column('squadra', Unicode(255)),
        Column('valore', Integer()),
        Column('ruolo', Unicode(255)),
    )
    
    voto_table = Table('voti', metadata,
        Column('id', Integer(), primary_key=True),
        Column('voto', Float()),
        Column('gg_', Integer()),
        Column('val', Integer()),
        Column('gol_fatti', Integer()),
        Column('pres', Integer()),
        Column('gol_sub', Integer()),
        Column('assist', Integer()),
        Column('ammo', Integer()),
        Column('esp', Integer()),
        Column('rig_tir', Integer()),
        Column('rig_ok', Integer()),
        Column('rig_no', Integer()),
        Column('voto_s', Float()),
    )
    
    # M2M relation
    giocatori_voti_table = Table('giocatori_voti', metadata,
        Column('giocatori_id', Integer, ForeignKey('giocatori.id')),
        Column('voti_id', Integer, ForeignKey('voti.id')),
    )

    # Map SQLAlchemy table definitions to python classes
    orm.mapper(Giocatore, giocatore_table, properties={
        'voti':orm.relation(Voto, secondary = giocatori_voti_table),})
    
    orm.mapper(Voto, voto_table, properties={
        'giocatori':orm.relation(Giocatore, secondary = giocatori_voti_table),})
    
    def __init__(self):
        
        Model.metadata.create_all(Model.engine)
        session = sessionmaker(bind = Model.engine)
        self.session = session()
        self.ruoli = ['portiere', 'difensore', 'ccampista', 'attaccante']
        self.value_state, self.val_right_state = 'asc', 'asc'
        self.name_state, self.name_right_state = 'asc', 'asc'
        self.player_to_change = None
        self.team_a, self.ruolo = '', ''
        self.cols = {0: Giocatore.nome, 1: Giocatore.squadra,
                     2: Voto.voto, 3: Voto.pres,
                     4: (Voto.gol_fatti + Voto.rig_ok),
                     5: Voto.gol_sub, 6: Voto.assist, 7: Voto.ammo,
                     8: Voto.esp, 9: Voto.rig_tir, 10: Voto.rig_no,
                     11: Giocatore.valore}
        
    def get_data_players(self, ruolo, item = Voto.voto):
        '''get the avg of vote and all total data by role'''
        query = self.session.query(Giocatore).filter(
                            Giocatore.ruolo == ruolo).subquery()
        adalias = aliased(Giocatore, query)

        query_p = self.session.query(adalias.nome, adalias.idgaz,
                          func.avg(Voto.voto),
                          func.sum(Voto.pres),
                          func.sum(Voto.gol_fatti+Voto.rig_ok),
                          func.sum(Voto.gol_sub),
                          func.sum(Voto.assist),
                          func.sum(Voto.ammo),
                          func.sum(Voto.esp),
                          func.sum(Voto.rig_tir),
                          func.sum(Voto.rig_no),
                          adalias.valore,
                          adalias.squadra).join( # < JOIN
                            adalias, Voto.giocatori).filter(Voto.pres > 0)
                            .group_by(adalias).order_by( # < GROUP & SORT
                                    asc(func.avg(item))).all()
        return query_p

    def get_players_sort_name(self, ruolo):
        '''Get all available goalkeeper'''
        if self.name_state == 'asc':
            query = self.session.query(Giocatore).filter(
                                        Giocatore.ruolo == '%s' % ruolo
                                        ).order_by(desc(Giocatore.idgaz)).all()
        else:
            query = self.session.query(Giocatore).filter(
                                          Giocatore.ruolo == '%s' % ruolo
                                          ).order_by((Giocatore.idgaz)).all()
        return query

    def get_inserted_days(self): # for checking days
        '''get inserted votes-days for import-votes checking'''
        ggs = [item[0] for item in self.session.query(func.distinct(Voto.gg_)).all()]
        return ggs

    def get_player_by_idgaz(self, idgaz):
        '''Get the player by idgaz'''
        player = self.session.query(Giocatore).filter(
                                    Giocatore.idgaz == idgaz).first()
        return player

    def do_players_exist(self): # check at the first time
        '''Return True if players are already imported'''
        if self.session.query(Giocatore).count() != 0:
            print "Gazzetta players present in database"
            return True
        else:
            print "Gazzetta players must be imported first!"
            return False 
        
    def import_new_player(self, idgaz, nome, squadra, valore, ruolo):
        '''Add new player on "giocatori" table'''
        player = Giocatore()
        player.idgaz = idgaz
        player.nome = nome
        player.squadra = squadra
        player.valore = valore
        player.ruolo = ruolo
        self.session.add(player)
        
    def upload_votes(self, item):
        '''Save the data-tuble of votes file into database'''
        print "UPLOAD", item
        rig_ok = int(item[10]) - int(item[11])
        voto = Voto()
        voto.voto = item[1]
        voto.gg_ = item[2]
        voto.val = item[3]
        voto.gol_fatti = item[4]
        voto.pres = item[5]
        voto.gol_sub = item[6]
        voto.assist = item[7]
        voto.ammo = item[8]
        voto.esp = item[9]
        voto.rig_tir = item[10]
        voto.rig_ok = rig_ok
        voto.rig_no = item[11]
        voto.voto_s = item[12]
        self.session.add(voto)
        #m2m append
        voto.giocatori.append(self.get_player_by_idgaz(item[0])) # pylint: disable=E1101,C0301
        return True

    def total_commit(self):
        '''external commit to use outside loop'''
        self.session.commit()

    def update_player_by_idgaz(self, idgaz, nome, squadra, valore, ruolo):
        '''Get the player by idgaz'''
        player = self.session.query(Giocatore).filter(
                                    Giocatore.idgaz == idgaz).first()
        player.idgaz = idgaz
        player.nome = nome
        player.squadra = squadra
        player.valore = int(valore)
        player.ruolo = ruolo
        
    def get_data_players_by_value(self, ruolo, jolly):
        '''generic query that uses subquery, alias and join function:
        return the sum of a Giocatore.value itering the votes, by idgaz'''
        query = self.session.query(Giocatore).filter(
                            Giocatore.ruolo == ruolo).subquery()
        adalias = aliased(Giocatore, query)
        query_p = self.session.query(adalias.nome, adalias.idgaz,
                          func.avg(Voto.voto),
                          func.sum(Voto.pres),
                          func.sum(Voto.gol_fatti+Voto.rig_ok),
                          func.sum(Voto.gol_sub),
                          func.sum(Voto.assist),
                          func.sum(Voto.ammo),
                          func.sum(Voto.esp),
                          func.sum(Voto.rig_tir),
                          func.sum(Voto.rig_no),
                          adalias.valore,
                          adalias.squadra).join( # < JOIN
                            adalias, Voto.giocatori).filter(Voto.pres > 0)
                            .group_by(adalias).order_by( # < GROUP & SORT
                                    asc(func.sum(jolly))).all()
        return query_p

    def get_data_players_by_valore(self, ruolo):
        '''generic query that uses subquery, alias and join function:
        return all total data player sorted by value'''
        query = self.session.query(Giocatore).filter(
                            Giocatore.ruolo == ruolo).subquery()
        adalias = aliased(Giocatore, query)
        query_p = self.session.query(adalias.nome, adalias.idgaz,
                          func.avg(Voto.voto),
                          func.sum(Voto.pres),
                          func.sum(Voto.gol_fatti+Voto.rig_ok),
                          func.sum(Voto.gol_sub),
                          func.sum(Voto.assist),
                          func.sum(Voto.ammo),
                          func.sum(Voto.esp),
                          func.sum(Voto.rig_tir),
                          func.sum(Voto.rig_no),
                          adalias.valore,
                          adalias.squadra).join( # < JOIN
                            adalias, Voto.giocatori).filter(Voto.pres > 0)
                            .group_by(adalias).order_by( # < GROUP & SORT
                                    asc(adalias.valore)).all()
        return query_p

def main():
    '''test starter'''
    mod = Model()


if __name__ == '__main__':
    main()

Qui il codice relativo al View:

# -*- coding: utf-8 -*-#
# ViewLight.py
'''VIEW Module for FantaPython Application:
Graphic library: provided by wx
All the frames and their children widgets,
are defined here.
Binding are defined under CONTROLLER Module.
Every Frame as a panel child and every widgets
have panel as first parent.
''' 


import wx
import os

from wx import html as wxhtml
from wx.lib.buttons import GenBitmapTextButton

class FrameVotiPlayers(wx.Frame):
    '''Frame for votes consulting'''
    def __init__(self, *args, **kwargs):
        ruoli = ['portiere', 'difensore', 'ccampista', 'attaccante']
        wx.Frame.__init__(self, *args, **kwargs)
        self.panel = wx.Panel(self, -1)
        self.panel.SetBackgroundColour('Pink')
        self.btn_import = GenBitmapTextButton(self.panel, wx.ID_NEW,
                                            wx.Bitmap('images\import_tr.png'),
                                            'Import players/votes'.rjust(30),
                                            size = (350, 40), pos = (5, 425))
        self.btn_exit = GenBitmapTextButton(self.panel, wx.ID_ANY,
                                            wx.Bitmap('images\quit.png'),
                                            'Exit'.rjust(30), size = (335, 40),
                                            pos = (355, 425))
        self.rboxruoli = wx.RadioBox(self.panel, wx.ID_ANY, "ruoli", (100, 100),
                                     wx.DefaultSize, ruoli, 4,
                                     wx.RA_SPECIFY_COLS)
        self.rboxruoli.Enable()
        self.listdati = wx.ListCtrl(self.panel, wx.NewId(), size = (690, 350),
                                  style = wx.LC_REPORT | wx.LC_HRULES |
                                  wx.LC_VRULES)
        self.listdati.Show(True)
        self.listdati.InsertColumn(0, "nome", wx.LIST_AUTOSIZE, 140)
        self.listdati.InsertColumn(1, "sq", wx.LIST_FORMAT_CENTER, 70)
        self.listdati.InsertColumn(2, "mv", wx.LIST_FORMAT_CENTER, 70)
        self.listdati.InsertColumn(3, "pr", wx.LIST_FORMAT_CENTER, 40)
        self.listdati.InsertColumn(4, "gf", wx.LIST_FORMAT_CENTER, 40)
        self.listdati.InsertColumn(5, "gs", wx.LIST_FORMAT_CENTER, 40)
        self.listdati.InsertColumn(6, "ass", wx.LIST_FORMAT_CENTER, 40)
        self.listdati.InsertColumn(7, "amm", wx.LIST_FORMAT_CENTER, 50)
        self.listdati.InsertColumn(8, "esp", wx.LIST_FORMAT_CENTER, 40)
        self.listdati.InsertColumn(9, "rt", wx.LIST_FORMAT_CENTER, 40)
        self.listdati.InsertColumn(10, "rs", wx.LIST_FORMAT_CENTER, 40)
        self.listdati.InsertColumn(11, "$", wx.LIST_FORMAT_CENTER, 35)

        # sizerize
        vbox = wx.BoxSizer(wx.VERTICAL)
        vbox.Add(self.rboxruoli, 0, wx.CENTER, 5)
        vbox.Add(wx.StaticLine(self.panel,), 0, wx.ALL|wx.EXPAND, 5)
        vbox.Add(self.listdati, 0, wx.ALL|wx.CENTER, 5)
        vbox.Add(wx.StaticLine(self.panel,), 0, wx.ALL|wx.EXPAND, 5)

        self.panel.SetSizer(vbox)
        self.Centre()
        self.Show()

class InfoMessage(wx.MessageDialog):
    '''Simple message Dialog'''
    def __init__(self, parent, message):
        wx.MessageDialog.__init__(self, parent, message, 'core info', wx.OK |
                                  wx.ICON_EXCLAMATION)
    def get_choice(self):
        '''get the state of the user choice'''
        if self.ShowModal() == wx.ID_OK:
            self.Destroy()

class ChoiceMessage(wx.MessageDialog):
    '''Simple choice message Dialog'''
    def __init__(self, parent, message):
        wx.MessageDialog.__init__(self, parent, message, 'Core question',
                                  wx.YES_NO | wx.ICON_QUESTION)
    def get_yes(self):
        '''get True if YES is clicked'''
        if self.ShowModal() == wx.ID_YES:
            return True
        else:
            self.Destroy()

class EntryDialog(wx.TextEntryDialog):            
    '''Simple Text Entry Dialog'''
    def __init__(self, parent, msg, value):
        wx.TextEntryDialog.__init__(self, parent, msg, 'Core request',
                                    defaultValue = value, style = wx.OK)
    def get_choice(self):
        '''get the state of the user choice'''
        if self.ShowModal() == wx.ID_OK:
            response = self.GetValue()
            self.Destroy()
            return response

class FileBrowser(wx.FileDialog):
    '''Class for file browser'''
    def __init__(self):
        self.fin = None
        wildcard = "File Gazzetta (*.txt)|*.txt|" 
            "Tutti i files (*.*)|*.*"
        wx.FileDialog.__init__(self, None, "scegli il file", os.getcwd(),
                               "", wildcard, wx.OPEN)
        if self.ShowModal() == wx.ID_OK:  
            print(self.GetPath())
            self.file = self.GetPath()
            self.fin = open(self.file, 'r')
        else:
            print "operazione apertura annullata"
            self.file = None
            self.Destroy()
        self.Destroy()
def main():
    '''app starter'''
    app = wx.PySimpleApp()
    FrameVotiPlayers(None, wx.ID_NEW, "FantaStat 1.1 light", size = (700, 500))
    app.MainLoop()

if __name__ == '__main__':
    main()

infine il codice relativo al COntroller:

# -*- coding: utf-8 -*-#
# ControllerLight.py
'''FantaStat v 1.1 Light
Controller Module for FantaStat Application:
Application pattern:
MODEL | VIEW | CONTROLLER
MODEL Module COntains all data for database querying
VIEW Module Contains all GUI for viewing all database data
CONTROLLER Module contains app logic.
All the binding method to frame widget are defined here''' 

import wx

from DataLight import Model
from ProgressBar import BarController
from ViewLight import FrameVotiPlayers, InfoMessage, ChoiceMessage, FileBrowser

class Controller(object):
    '''Controller class for MVC-like pattern'''
    def __init__(self):
        self.model = Model()
        self.list_voti = []
        self.ctrl = FrameVotiPlayers(None, wx.ID_NEW, "FantaStat 1.1 light",
                                     size = (700, 500))
        self.ctrl.btn_exit.Bind(wx.EVT_BUTTON, _frame_exit)
        self.ctrl.btn_import.Bind(wx.EVT_BUTTON, self.import_votes)
        self.query = self.model.get_data_players(u'portiere')
        self.fill_list(self.query)
        self.ctrl.rboxruoli.Bind(wx.EVT_RADIOBOX, self.on_roles)
        self.ctrl.listdati.Bind(wx.EVT_LIST_COL_CLICK, self.on_list_column)

    def upload_players(self, file_in):
        '''upload/upgrade players in db via model'''
        def get_flag_role(idg):
            """get the role, by idgaz"""
            if idg < 200:
                role = 'portiere'
            elif idg > 200 and idg < 500:
                role = 'difensore'
            elif idg > 500 and idg < 800:
                role = 'ccampista'
            else:
                role = 'attaccante'
            return role

        if file_in != None:
            record = len(file_in.readlines()) #progress bar param
            barc = BarController(record)
            file_in.seek(0)
            for line in file_in.readlines():
                val = line.split('|')
                try:
                    idr = int(val[0])
                except ValueError:
                    idr = int(val[0].replace('xefxbbxbf', ''))
                
                role = get_flag_role(idr)
                nomev = val[2].decode('utf-8').replace('"', '')
                squadrav = val[3].replace('"', '')
                play_in = self.model.get_player_by_idgaz(idr)
                if play_in == None:
                    print " <<<< assente giocatore con idgaz = ", idr
                    self.model.import_new_player(idr, nomev, squadrav,
                                                 int(val[27].rstrip()), role)
                else:
                    print " ++++ aggiorno giocatore con idgaz = ", idr
                    self.model.update_player_by_idgaz(idr, nomev,
                                        squadrav, int(val[27]), role)
                barc.update_progress_bar()
            self.model.session.commit()
            barc.destroy_progress_bar()
            print "core> dati inseriti con successo"
        else:
            print "core> Nessun dato da salvare"
    
    def import_votes(self, evt):
        '''Import Votes from Gazzetta.com'''
        parent = evt.GetEventObject().GetParent().GetParent()
        ggs = self.model.get_inserted_days()
        mes = "Inserire i voti nel database?"
        if ChoiceMessage(parent, mes).get_yes() == True:
            fbr = FileBrowser()
            file_in = fbr.fin
            path = fbr.file
            if file_in != None:
                choice = int(path.partition('MCC')[-1].partition('.txt')[0])
                if choice not in ggs:
                    try:
                        self.upload_players(file_in)
                    except ValueError:
                        print "core> Operazione annullata"
                    else:
                        self.list_voti = []
                        file_in.seek(0)
                        for line in file_in:
                            self.list_voti.append((int(line.split("|")[0]),
                                                   float(line.split("|")[7]),
                                                   int(line.split("|")[1]),
                                                   int((line.split("|")[27]
                                                        ).rstrip()),
                                                   int(line.split("|")[11]),
                                                   int(line.split("|")[6]),
                                                   int(line.split("|")[12]),
                                                   int(line.split("|")[15]),
                                                   int(line.split("|")[16]),
                                                   int(line.split("|")[17]),
                                                   int(line.split("|")[18]),
                                                   int(line.split("|")[21]),
                                                   float(line.split("|")[10])))
                        file_in.close()
                        print "core> pre RECORD"
                        record = len(self.list_voti)
                        if record > 0:
                            upbar = BarController(record)
                            for item in self.list_voti: 
                                self.model.upload_votes(item)
                                if True:
                                    upbar.update_progress_bar()
                            self.model.total_commit()
                            print "core> Dati Salvati con successo"
                            upbar.destroy_progress_bar()
                            role = self.ctrl.rboxruoli.GetStringSelection()
                            self.ctrl.listdati.DeleteAllItems()
                            self.fill_list(self.model.get_data_players(role))
                else:
                    mes = "errore file. Giornata gia' inserita!"
                    InfoMessage(parent, mes).get_choice()
        else:
            print "core> nessun dato da salvare"
    
    def fill_list(self, sqla_query):
        '''Fill the ListControl with stats data from db'''
        self.ctrl.listdati.DeleteAllItems()
        for player in sqla_query:
            nome = player[0]
            row = self.ctrl.listdati.InsertStringItem(0, nome)#ROW
            sq = player[12]
            self.ctrl.listdati.SetStringItem(row, 1, str(sq)) #1st C
            m_v = player[2]
            self.ctrl.listdati.SetStringItem(row, 2, str("%.3f" % m_v))
            pres = player[3]
            self.ctrl.listdati.SetStringItem(row, 3, str(pres)) 
            g_f = player[4]
            self.ctrl.listdati.SetStringItem(row, 4, str(g_f))
            g_s = player[5]
            self.ctrl.listdati.SetStringItem(row, 5, str(g_s))
            ass = player[6]
            self.ctrl.listdati.SetStringItem(row, 6, str(ass))
            ammo = player[7]
            self.ctrl.listdati.SetStringItem(row, 7, str(ammo))
            esp = player[8]
            self.ctrl.listdati.SetStringItem(row, 8, str(esp))
            r_t = player[9]
            self.ctrl.listdati.SetStringItem(row, 9, str(r_t))
            r_s = player[10]
            self.ctrl.listdati.SetStringItem(row, 10, str(r_s))
            val = player[11]
            self.ctrl.listdati.SetStringItem(row, 11, str(val))

    def on_list_column(self, evt):
        '''get the column header for sorting database data'''
        header = evt.GetColumn()
        ruolo = self.ctrl.rboxruoli.GetStringSelection()
        if 2 < header < 11:
            jolly = self.model.cols[header]
            self.ctrl.listdati.DeleteAllItems()
            self.fill_list(self.model.get_data_players_by_value(ruolo, jolly))
        elif header == 2:
            jolly = self.model.cols[header]
            self.ctrl.listdati.DeleteAllItems()
            self.fill_list(self.model.get_data_players(ruolo, jolly))
        elif header == 11:
            self.ctrl.listdati.DeleteAllItems()
            self.fill_list(self.model.get_data_players_by_valore(ruolo))
        
    def on_roles(self, evt):
        '''radio box "Ruolo" event handler'''
        role = evt.GetEventObject().GetStringSelection()
        players = self.model.get_data_players(role)
        self.ctrl.listdati.DeleteAllItems()
        self.fill_list(players)

def _frame_exit(evt):
    '''exit from frame'''
    frame = evt.GetEventObject().GetParent().GetParent()
    frame.Close()

def main():
    '''starter'''
    app = wx.PySimpleApp()
    Controller()
    app.MainLoop()
    
if __name__ == '__main__':
    main()

Ultimo oggetto estraneo al FantaStat, è la ControlBar delle importazioni:

# ProgressBar.py
'''Module containing a Progress Bar class'''

import wx

class Data(object):
    '''Data class for M-V-C pattern'''
    def __init__(self):
        self.count = 0
        self.max_v = 0
    def add_count (self):
        '''increase the progress bar count for showing its progress'''
        self.count += 1
    def get_max(self):
        '''Get the max value of the progress bar'''
        return self.max_v
    def get_count(self):
        '''Get the current count value of the progressbar in the loop'''
        return self.count
    def set_max(self, value):
        '''set the max value of the progress bar'''
        self.max_v = int(value)

class ProgressBar(wx.ProgressDialog):
    '''Progress Dialog constructor'''
    def __init__(self, max_v):
        wx.ProgressDialog.__init__(self, "", "Update...", max_v,
                                   style = wx.PD_AUTO_HIDE |
                                   wx.PD_ELAPSED_TIME |
                                   wx.PD_REMAINING_TIME)
    def progress(self, progress):
        '''update the progress bar during the loops'''
        wx.MicroSleep(1) #for short processes use wx.MilliSleep(1)
        self.Update(progress)
    def destroy(self):
        '''Distruttore della progress bar'''
        self.Destroy()

class BarController(object):
    '''Progress Bar Controller Constructor'''
    def __init__(self, value = 3):
        self.data = Data()
        self.data.set_max(value)
        self.max = self.data.get_max()
        self.count = self.data.get_count()
        self.progress_bar = ProgressBar(self.max)
    def update_progress_bar(self):
        '''Aggiorna la progressBar'''
        self.data.add_count()
        self.count = self.data.get_count()
        self.progress_bar.progress(self.count) # update progressbar
    def destroy_progress_bar(self):
        '''Destroy the wx.ProgressDialog instance'''
        self.progress_bar.destroy()

def main():
    '''app starter'''
    app = wx.PySimpleApp()
    bar_c = BarController(600)
    while bar_c.count < bar_c.max:
        bar_c.update_progress_bar()
    bar_c.destroy_progress_bar()
    app.MainLoop()
    
if __name__ == "__main__":
    main()

Nella versione 1.2 implementerò l’ordinamento decrescente per colonna E per media_voto,
in modo da ordinare i giocatori di pari valore (es presenze), mettendo prima quello
con la media voto maggiore.

…continua

ProgressBar riutilizzabile alleggerita

9 Settembre 2011 Commenti chiusi

in riferimento alla vecchia versione, ho snellito il codice, sempre utilizzando un ProgressDialog:

'''
ProgressDialog sample for progress bar generating.
wx.ProgressDialog(title, message, maximum, parent, style)

style type:
wx.PD_APP_MODAL - the progress dialog will block all user events
wx.PD_AUTO_HIDE - The progress dialog will automatically hide itself
                  when it reaches its maximum value.
wx.PD_CAN_ABORT - Puts a Cancel button on box for stop the process.
wx.PD_ELAPSED_TIME - Displays the elapsed time
wx.PD_ESTIMATED_TIME - Displays an estimate of the total time to complete
                       the process.
wx.PD_REMAINING_TIME - Displays the amount of time remaining in a process,
                       estimated time - elapsed time.
'''

import wx

class ProgressBar(wx.ProgressDialog):
    '''Progress Bar class'''
    def __init__(self, *args, **kwargs):
        self.count = 0
        wx.ProgressDialog.__init__(self, *args, **kwargs)
        self.pmax = kwargs['maximum']
    def update_bar(self):
        '''update the progress bar'''
        while self.count < self.pmax:
            self.count += 1
            wx.MilliSleep(1)
            self.Update(self.count)
    
def main():
    '''app starter'''
    app = wx.PySimpleApp()
    app.MainLoop()
    max_item = 50 # range or total numer of lines in a file or....
    pbar = ProgressBar(title = "progress...", message = "Time remaining",
                       maximum = max_item, parent = None, 
                       style = wx.PD_AUTO_HIDE | wx.PD_ELAPSED_TIME |
                       wx.PD_REMAINING_TIME)   
    pbar.update_bar()
    pbar.Destroy()
    
if __name__ == "__main__":
    main()

max_item è ovviamente il parametro che indica la lunghezza massima della progress bar. Se per esempio stessimo parsando un file di testo, max_item sarebbe il numero di righe del file…

Categorie:python, wxpython Tag:

ProgressBar riutilizzabile alleggerita

9 Settembre 2011 Commenti chiusi

in riferimento alla vecchia versione, ho snellito il codice, sempre utilizzando un ProgressDialog:

'''
ProgressDialog sample for progress bar generating.
wx.ProgressDialog(title, message, maximum, parent, style)

style type:
wx.PD_APP_MODAL - the progress dialog will block all user events
wx.PD_AUTO_HIDE - The progress dialog will automatically hide itself
                  when it reaches its maximum value.
wx.PD_CAN_ABORT - Puts a Cancel button on box for stop the process.
wx.PD_ELAPSED_TIME - Displays the elapsed time
wx.PD_ESTIMATED_TIME - Displays an estimate of the total time to complete
                       the process.
wx.PD_REMAINING_TIME - Displays the amount of time remaining in a process,
                       estimated time - elapsed time.
'''

import wx

class ProgressBar(wx.ProgressDialog):
    '''Progress Bar class'''
    def __init__(self, *args, **kwargs):
        self.count = 0
        wx.ProgressDialog.__init__(self, *args, **kwargs)
        self.pmax = kwargs['maximum']
    def update_bar(self):
        '''update the progress bar'''
        while self.count < self.pmax:
            self.count += 1
            wx.MilliSleep(1)
            self.Update(self.count)
    
def main():
    '''app starter'''
    app = wx.PySimpleApp()
    app.MainLoop()
    max_item = 50 # range or total numer of lines in a file or....
    pbar = ProgressBar(title = "progress...", message = "Time remaining",
                       maximum = max_item, parent = None, 
                       style = wx.PD_AUTO_HIDE | wx.PD_ELAPSED_TIME |
                       wx.PD_REMAINING_TIME)   
    pbar.update_bar()
    pbar.Destroy()
    
if __name__ == "__main__":
    main()

max_item è ovviamente il parametro che indica la lunghezza massima della progress bar. Se per esempio stessimo parsando un file di testo, max_item sarebbe il numero di righe del file…

Categorie:python, wxpython, wxPython Tag:

wxPython: Rubrica – Controller

22 Maggio 2011 Commenti chiusi

Dopo le Views ed il model, concludo con il mio controller.
Quasi tutti i Bind ai widgets dei frames (views) sono stati codificati qui.
codice:

#!/usr/bin/python
#Controller.py
'''Rubrica by Bancaldo'''

import wx

from sqlalchemy import and_
from sqlalchemy.orm import sessionmaker

from InfoHTMLFrame import FrameInfo
from CoreFrame import FrameCore
from DelContactFrame import FrameDelContact
from ContactFrame import FrameContact

from Find import FrameFindContact
from Data import Model, Contact

class CoreController(object):
    '''Controller Object'''
    def __init__(self):
        '''Constructor'''
        self.metadata = Model.metadata
        self.metadata.create_all(Model.engine)
        session = sessionmaker(bind = Model.engine)
        self.session = session()
        self.model = Model()
        self.core = FrameCore(None, -1, title  = 'Rubrica', size = (200, 280))
        self.core.btn_add.Bind(wx.EVT_BUTTON, self.on_btn_add)
        self.bind_buttons()
    def bind_buttons(self):
        '''Bind the frame buttons'''
        self.core.Bind(wx.EVT_BUTTON, self.on_btn_info, self.core.btn_info)
        self.core.Bind(wx.EVT_BUTTON, self.on_btn_add, self.core.btn_add)
        self.core.Bind(wx.EVT_BUTTON, self.on_btn_del, self.core.btn_del)
        self.core.Bind(wx.EVT_BUTTON, self.on_btn_find, self.core.btn_find)
        self.core.Bind(wx.EVT_BUTTON, self.on_btn_exit, self.core.btn_exit)
    def fill_find_list(self, char):
        '''Fill the find-frame list-control with elements sorted by letter'''
        self.fr_find.list_ctrl.DeleteAllItems()
        contacts = self.session.query(Contact).filter(Contact.surname.like(
                        ('%s%%') % char)).all()
        i = 0
        for contact in contacts:
            pos = self.fr_find.list_ctrl.InsertStringItem(
                               i, contact.surname)
            self.fr_find.list_ctrl.SetStringItem(pos, 1,
                               contact.name)
            self.fr_find.list_ctrl.SetStringItem(pos, 2,
                               contact.address)
            self.fr_find.list_ctrl.SetStringItem(pos, 3, contact.mail)
            self.fr_find.list_ctrl.SetStringItem(pos, 4, contact.phone)
            self.fr_find.list_ctrl.SetStringItem(pos, 5, contact.mobile)
            i += 1
        return True
    def get_contacts(self):
        '''Get the list of contacts as Surname-Name form'''
        contacts = ['%s %s' % (contact.surname,
                               contact.name) \
                    for contact in self.session.query(Contact).all()]
        contacts.sort()
        return contacts
    def on_add_exit(self, evt):
        '''Button-Exit on_click event handler of Add-contact frame'''
        self.fr_add.Destroy()
    def on_add_save(self, evt):
        '''Button-Save on_click event handler of Add-contact frame'''
        name = self.fr_add.t_name.GetValue()
        surname = self.fr_add.t_surname.GetValue()
        address = self.fr_add.t_address.GetValue()
        phone = self.fr_add.t_phone.GetValue()
        mobile = self.fr_add.t_mobile.GetValue()
        mail = self.fr_add.t_mail.GetValue()
        if surname != '' and name != '' and mobile != '':
            contact = Contact(name = name, surname = surname,
                              address = address, mail = mail,
                              phone = phone, mobile = mobile)
            self.session.add(contact)
            self.session.commit()
            for textctrl in self.fr_add.panel.GetChildren():
                if isinstance(textctrl, wx.TextCtrl):
                    textctrl.Clear()
                    textctrl.Disable()
            self.fr_add.t_surname.Enable()
            self.fr_add.btn_ed_save.Disable()
            print "COMMIT... ", surname, name
        else:
            mess = "Need to complete the (*)fields"
            info = wx.MessageDialog(self.fr_add, mess, 'Error',
                                   wx.OK | wx.ICON_STOP)
            info.ShowModal()
            info.Destroy()
    def on_btn_add(self, evt):
        '''Button-Add on_click event handler'''
        self.fr_add = FrameContact(self.core, -1, title  = 'Save Contact',
                                       size = (330, 300))
        self.fr_add.title.SetLabel('Save new Contact')
        self.fr_add.s_sur.SetLabel('Surname*')
        self.fr_add.s_nam.SetLabel('Name*')
        self.fr_add.s_mob.SetLabel('Mobile*')
        self.fr_add.btn_ed_save.Disable()
        for textctrl in self.fr_add.panel.GetChildren():
            if isinstance(textctrl, wx.TextCtrl):
                textctrl.Disable()
        self.fr_add.t_surname.Enable()
        self.fr_add.t_surname.Bind(wx.EVT_TEXT, self.on_text_entry)
        self.fr_add.Bind(wx.EVT_BUTTON, self.on_add_exit,
                          self.fr_add.btn_ed_exit)
        self.fr_add.Bind(wx.EVT_BUTTON, self.on_add_save,
                          self.fr_add.btn_ed_save)
    def on_btn_chr(self, evt):
        '''Chr-Button on_click event handler of Find-Contact frame '''
        btn_chr = evt.GetEventObject()
        char = btn_chr.GetLabel()
        self.fill_find_list(char)
    def on_btn_del(self, evt):
        '''Button-Delete on_click event handler'''
        contacts = self.get_contacts()
        self.fr_del = FrameDelContact(self.core, -1, 'Delete Contact',
                        size = (325, 200), contacts = contacts)
        self.fr_del.btn_exit_del.Bind(wx.EVT_BUTTON, self.on_del_exit)
        self.fr_del.btn_del.Bind(wx.EVT_BUTTON, self.on_del_del)
        self.fr_del.choice.Bind(wx.EVT_CHOICE, self.on_item_to_del)
        self.fr_del.btn_del.Disable()
    def on_btn_entry(self, evt):
        '''Chr-Button on_entry event handler of Find-Contact frame'''
        btn = evt.GetEventObject()
        btn.SetBackgroundColour('yellow')
        evt.Skip()
    def on_btn_exit(self, evt):
        '''Button-Exit on_click event handler'''
        self.core.Destroy()
    def on_btn_find(self, evt):
        '''Button-Find on_click event handler'''
        self.fr_find = FrameFindContact(self.core, -1, 'Find Contact',
                                        size = (700, 400))
        for child in self.fr_find.panel.GetChildren():
            if isinstance(child, wx.Button):
                child.Bind(wx.EVT_BUTTON, self.on_btn_chr)
                child.Bind(wx.EVT_ENTER_WINDOW, self.on_btn_entry)
                child.Bind(wx.EVT_LEAVE_WINDOW, self.on_btn_leave)
        self.fr_find.btn_find_exit.Bind(wx.EVT_BUTTON, self.on_find_exit)
        self.fr_find.btn_trash.Bind(wx.EVT_BUTTON, self.on_find_trash)
        self.fr_find.list_ctrl.Bind(wx.EVT_LIST_ITEM_RIGHT_CLICK,
                                    self.on_item_right_click)
        self.fr_find.list_ctrl.Bind(wx.EVT_LIST_ITEM_ACTIVATED,
                                    self.on_item_double_click)
        self.fr_find.btn_trash.Disable()
        self.fill_find_list('A')
        self.fr_find.list_ctrl.Focus(0)
        evt.Skip()
    def on_btn_info(self, evt):
        '''Button-Info on_click event handler'''
        self.fr_info = FrameInfo(self.core, -1, title = 'Info',
                                 size = (400, 350))
        self.fr_info.Bind(wx.EVT_BUTTON, self.on_btn_quit,
                          self.fr_info.btn_quit)
    def on_btn_leave(self, evt):
        '''Chr-Button on_leave event handler of Find-Contact frame '''
        btn = evt.GetEventObject()
        btn.SetBackgroundColour('#c2e6f8')
    def on_btn_quit(self, evt):
        '''Button-quit on_click event handler of frame_info'''
        self.fr_info.Destroy()
    def on_del_del(self, evt):
        '''Button-Delete on_click event handler of Del-Contact'''
        cont_to_del = self.fr_del.choice.GetStringSelection()
        self.model.set_name(cont_to_del.split()[1])
        self.model.set_surname(cont_to_del.split()[0])        
        msg = wx.MessageDialog(self.fr_del, 'Are You sure?', 'Delete',
                               wx.YES_NO | wx.ICON_WARNING)
        if msg.ShowModal() == wx.ID_YES:
            print "DELETING CONTACT...%s" % cont_to_del
            to_del = self.session.query(Contact).filter(
                         Contact.surname == '%s' % self.model.get_surname()
                         ).first()
            self.session.delete(to_del)
            self.session.commit()
        self.fr_del.choice.Clear()
        self.fr_del.choice.AppendItems(self.get_contacts())
        self.fr_del.btn_del.Disable()
        msg.Destroy()
    def on_del_exit(self, evt):
        '''Button-Exit on_click event handler of Del-contact frame'''
        self.fr_del.Destroy()
    def on_edit_exit(self, evt):
        '''Button-Exit on_click event handler of Edit-Contact Frame'''
        self.fr_find.Show()
        self.fr_edit.Destroy()
    def on_edit_save(self, evt):
        '''Save new contact data to DataBase'''
        query = self.session.query(Contact).filter(Contact.surname == 
                    self.model.get_surname()).first()
        surname = self.fr_edit.t_surname.GetValue()
        name = self.fr_edit.t_name.GetValue()
        address = self.fr_edit.t_address.GetValue()
        mail = self.fr_edit.t_mail.GetValue()
        phone = self.fr_edit.t_phone.GetValue()
        mobile = self.fr_edit.t_mobile.GetValue()
        query.surname = surname
        query.name = name
        query.address = address
        query.mail = mail
        query.phone = phone
        query.mobile = mobile
        self.session.commit()
        self.fr_find.Show()
        self.fr_edit.Destroy()
        self.fr_find.btn_trash.Disable()
        self.fill_find_list('A')
    def on_find_exit(self, evt):
        '''Button-Exit on_click event handler of Find-contact frame '''
        self.fr_find.Destroy()
    def on_find_trash(self, evt):
        '''Button-Trash on_click event handler of Find-contact frame '''
        surname = self.model.get_surname()
        name = self.model.get_name()
        where = and_(Contact.surname == surname, Contact.name == name)
        to_del = self.session.query(Contact).filter(where).first()
        print "Contact to del ------> ", to_del
        msg = wx.MessageDialog(self.fr_find, 'Are You sure?', 'Delete',
                       wx.YES_NO | wx.ICON_WARNING)
        if msg.ShowModal() == wx.ID_YES:
            self.session.delete(to_del)
            self.session.commit()
        self.fr_find.btn_trash.Disable()
        msg.Destroy()
        self.fill_find_list('A')
    def on_item_double_click(self, evt):
        '''Enable the delete-button of the find-frame'''
        self.fr_find.btn_trash.Enable()
        row = self.fr_find.list_ctrl.GetFocusedItem()
        self.model.set_surname(self.fr_find.list_ctrl.GetItem(row, 0).GetText())
        self.model.set_name(self.fr_find.list_ctrl.GetItem(row, 1).GetText())
        evt.Skip()
    def on_item_right_click(self, evt):
        '''Edit all the selected contact data'''
        self.fr_find.Hide()
        self.set_list_values()
        self.fr_edit = FrameContact(self.fr_find, wx.ID_ANY,
                                        title  = 'Edit Contact',
                                        size = (330, 300))
        self.fr_edit.title.SetLabel('Edit contact %s %s' \
                                    % (self.model.get_surname(),
                                       self.model.get_name()))
        self.fr_edit.t_surname.SetValue(self.model.get_surname())
        self.fr_edit.t_name.SetValue(self.model.get_name())
        self.fr_edit.t_address.SetValue(self.model.get_address())
        self.fr_edit.t_mail.SetValue(self.model.get_mail())
        self.fr_edit.t_phone.SetValue(self.model.get_phone())
        self.fr_edit.t_mobile.SetValue(self.model.get_mobile())
        self.fr_edit.btn_ed_exit.Bind(wx.EVT_BUTTON, self.on_edit_exit)
        self.fr_edit.btn_ed_save.Bind(wx.EVT_BUTTON, self.on_edit_save)
        evt.Skip()
    def on_item_to_del(self, evt):
        '''Enable the delete-button when an item is selected'''
        self.fr_del.btn_del.Enable()
    def on_text_entry(self, evt):
        '''Enable the Save-button of add-contact-frame'''
        self.fr_add.btn_ed_save.Enable()
        for textctrl in self.fr_add.panel.GetChildren():
            if isinstance(textctrl, wx.TextCtrl):
                textctrl.Enable()
    def set_list_values(self):
        '''Set the list-ctrl values to model variables'''
        row = self.fr_find.list_ctrl.GetFocusedItem()
        self.model.set_surname(self.fr_find.list_ctrl.GetItem(row, 0).GetText())
        self.model.set_name(self.fr_find.list_ctrl.GetItem(row, 1).GetText())
        self.model.set_address(self.fr_find.list_ctrl.GetItem(row, 2).GetText())
        self.model.set_mail(self.fr_find.list_ctrl.GetItem(row, 3).GetText())
        self.model.set_phone(self.fr_find.list_ctrl.GetItem(row, 4).GetText())
        self.model.set_mobile(self.fr_find.list_ctrl.GetItem(row, 5).GetText())
        print self.model.get_surname(), self.model.get_name()
        return True 

def main_frame():
    '''Application starter'''
    app = wx.PySimpleApp()
    CoreController()
    app.MainLoop()

if __name__ == '__main__':
    main_frame()

Ora eseguendo il file Controller.py, avremo a disposizione la nostra rubrica.
Il codice e le immagini sono disponibili, impacchettate, qui.

Refactoring:

è possibile ridurre il codice unificando le chiamate alla callback di uscita dal frame. Infatti, invece di codificare una callback da legare ad ogni pulsante “exit” di ogni frame, si crea un’unica callback generica dove, tramite il parametro “evt” passatogli come argomento, si risale al parent del widget bottone cliccato e, una volta raggiunto il livello window, lo si chiude.
es:

    def on_btn_find(self, evt):
        ...
        self.fr_find.btn_find_exit.Bind(wx.EVT_BUTTON, self.on_generic_exit)
        ...
    def on_btn_del(self, evt):
        ...
        self.fr_del.btn_exit_del.Bind(wx.EVT_BUTTON, self.on_generic_exit)
        ...
    def on_generic_exit(self, evt):
        '''Exit from current frame'''
        btn = evt.GetEventObject()
        panel = btn.GetParent()
        frame = panel.GetParent()
        frame.Destroy()
Categorie:python, wxpython Tag:

wxPython: Rubrica- Model

22 Maggio 2011 Commenti chiusi

Terminate le view, sono passato al modulo Data, nel quale ho creato una classe Model, dove dichiarare tutte le variabili che mi serviranno come appoggio per la gestione dei dati (Controller).
Poi per poter utilizzare l’ORM di SQLAlchemy ho creato la classe Contact, definendo di fatto la tabella e i campi che utilizzerò per il database (sqlite3 nel mio caso).

il codice:

#!/usr/bin/python
#Data.py
'''Data module for sql alchemy ORM'''

from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base

class Model(object):
    '''Model class with common application data'''
    base = declarative_base()
    engine = create_engine('sqlite:///contacts.db', echo = True)
    metadata = base.metadata
    def __init__(self):
        self.name = None
        self.surname = None
        self.address = None
        self.mail = None
        self.mobile = None
        self.phone = None
    def get_name(self):
        '''get the contact name'''
        return self.name
    def get_surname(self):
        '''get the contact surname'''
        return self.surname
    def get_address(self):
        '''get the contact address'''
        return self.address
    def get_mail(self):
        '''get the contact mail'''
        return self.mail
    def get_phone(self):
        '''get the contact phone number'''
        return self.phone
    def get_mobile(self):
        '''get the contact mobile number'''
        return self.mobile
    def set_name(self, value):
        '''Sets the contact name to the given value'''
        self.name = value
    def set_surname(self, value):
        '''Sets contact surname to the given value'''
        self.surname = value
    def set_address(self, value):
        '''Sets contact address to the given value'''
        self.address = value
    def set_mail(self, value):
        '''Sets contact mail to the given value'''
        self.mail = value
    def set_phone(self, value):
        '''Sets contact phone to the given value'''
        self.phone = value
    def set_mobile(self, value):
        '''Sets contact mobile to the given value'''
        self.mobile = value

class Contact(Model.base):
    '''Contact class for ORM Mapping'''
    __tablename__ = 'contacts'
    id = Column(Integer, primary_key = True)
    name = Column(String)
    surname = Column(String)
    address = Column(String)
    mail = Column(String)
    mobile = Column(String)
    phone = Column(String)
    
    def __init__(self, name, surname, address, mail, mobile, phone):
        self.name = name
        self.surname = surname
        self.address = address
        self.mail = mail
        self.mobile = mobile
        self.phone = phone
    def __repr__(self):
        return "<Contact ('%s %s')>" % (self.surname, self.name)

infine il modulo controller

Categorie:python, wxpython Tag:

wxPython: Rubrica- VIEWs parte 2

21 Maggio 2011 Commenti chiusi

GUI aggiunta contatto
riprendiamo dal frame per l’aggiunta di un contatto.
Lo stesso frame lo utilizzerò, con qualche differenza, anche per la modifica dei contatti esistenti.

Non ho utilizzato sizers per pigrizia, ma il risultato è comunque buono.
codice:

#!/usr/bin/python
#ContactFrame.py
'''Contact Frame for Rubrica App
This frame is used as Add Contact
Frame, and Edit Contact Frame
'''

import wx
from wx.lib.buttons import GenBitmapTextButton

class FrameContact(wx.Frame):
    '''Initial Core Frame'''
    def __init__(self, *args, **kwargs):
        wx.Frame.__init__(self, *args, **kwargs)
        self.panel = wx.Panel(self, wx.ID_ANY)
        self.SetBackgroundColour('#00CCCC')
        font = wx.Font(10, wx.SWISS, wx.NORMAL, wx.BOLD)
        self.title = wx.StaticText(self.panel, wx.ID_ANY, 'Edit Contact', (15, 10))
        self.title.SetFont(font)
        wx.StaticLine(self.panel, wx.ID_ANY, (0, 35), (325, 3))
        wx.StaticLine(self.panel, wx.ID_ANY, (0, 185), (325, 3))
        self.s_sur = wx.StaticText(self.panel, wx.ID_ANY, 'Surname', (5, 55),
                      style = wx.ALIGN_LEFT)
        self.s_nam = wx.StaticText(self.panel, wx.ID_ANY, 'Name', (5, 75),
                      style = wx.ALIGN_LEFT)
        wx.StaticText(self.panel, wx.ID_ANY, 'Address', (5, 95),
                      style = wx.ALIGN_LEFT)
        wx.StaticText(self.panel, wx.ID_ANY, 'Mail', (5, 115),
                      style = wx.ALIGN_LEFT)
        wx.StaticText(self.panel, wx.ID_ANY, 'Phone', (5, 135),
                      style = wx.ALIGN_LEFT)
        self.s_mob = wx.StaticText(self.panel, wx.ID_ANY, 'Mobile', (5, 155),
                      style = wx.ALIGN_LEFT)
        self.t_surname = wx.TextCtrl(self.panel, wx.ID_ANY, pos = (70, 50),
                                  size = (250, 20), style = wx.ALIGN_LEFT)
        self.t_name = wx.TextCtrl(self.panel, wx.ID_ANY, pos = (70, 70),
                                  size = (250, 20), style = wx.ALIGN_LEFT)
        self.t_address = wx.TextCtrl(self.panel, wx.ID_ANY, pos = (70, 90),
                                  size = (250, 20), style = wx.ALIGN_LEFT)
        self.t_mail = wx.TextCtrl(self.panel, wx.ID_ANY, pos = (70, 110),
                                  size = (250, 20), style = wx.ALIGN_LEFT)
        self.t_phone = wx.TextCtrl(self.panel, wx.ID_ANY, pos = (70, 130),
                                  size = (250, 20), style = wx.ALIGN_LEFT)
        self.t_mobile = wx.TextCtrl(self.panel, wx.ID_ANY, pos = (70, 150),
                                  size = (250, 20), style = wx.ALIGN_LEFT)
        self.btn_ed_save = GenBitmapTextButton(self.panel, wx.ID_ANY,
                                            wx.Bitmap('save.png'),
                                            'Save'.rjust(20), (5, 200),
                                            (150, -1))
        self.btn_ed_save.SetBezelWidth(1)
        self.btn_ed_save.SetBackgroundColour('#c2e6f8')
        
        self.btn_ed_exit = GenBitmapTextButton(self.panel, wx.ID_ANY,
                                            wx.Bitmap('cancel.png'),
                                            'Cancel'.rjust(20), (160, 200),
                                            (160, -1))
        self.btn_ed_exit.SetBezelWidth(1)
        self.btn_ed_exit.SetBackgroundColour('#c2e6f8')

        self.btn_ed_save.Bind(wx.EVT_ENTER_WINDOW, self.on_btn_enter)
        self.btn_ed_save.Bind(wx.EVT_LEAVE_WINDOW, self.on_btn_leave)
        self.btn_ed_exit.Bind(wx.EVT_ENTER_WINDOW, self.on_btn_enter)
        self.btn_ed_exit.Bind(wx.EVT_LEAVE_WINDOW, self.on_btn_leave)

        self.Centre()
        self.Show()

    def on_btn_enter(self, event):
        '''Into Button enter-mouse event handler'''
        obj =  event.GetEventObject()
        obj.SetBackgroundColour('#ffdf85')
        obj.Refresh()

    def on_btn_leave(self, event):
        '''From Button leave-mouse event handler'''
        obj =  event.GetEventObject()
        obj.SetBackgroundColour('#c2e6f8')
        obj.Refresh()

def main_core(parent):
    '''Add Contact frame starter'''
    app = wx.PySimpleApp()
    edit = FrameContact(parent, -1, title  = 'Save Contact',
                            size = (330, 300))
    edit.title.SetLabel('Save new Contact')
    edit.s_sur.SetLabel('Surname*')
    edit.s_nam.SetLabel('Name*')
    edit.s_mob.SetLabel('Mobile*')
    app.MainLoop()
    
if __name__ == '__main__':
    main_core(parent = None)

l’esito è:


Nel codice ho inserito anche 2 bind, per cambiare il colore del pulsante quando ci si passa con il puntatore del mouse, e l’altro per rimettere il background iniziale.
Per correttezza avrei dovuto creare i due bind esterni alla View (nel Controller), come tutti gli altri, ma non volendo ingigantire troppo il modulo Controller, ho gestito i due bottoni con eventi e callbacks, all’interno del frame.

GUI gestione contatti

Con questo frame, avrò la vera e propria agenta, con un bottone per ogni lettera, una listctrl per la visualizzazione dei contatti e la possibilità di cancellarli o modificarli.

il codice:

#!/usr/bin/python
# Find.py
'''Find Contact Frame for Rubrica App
Double click on contact, enables the delete
button and you can delete the selected contact.
Right click on contact, enables the edit
frame that modifies the contact values and
stores the data to database
'''

import wx
from wx.lib.buttons import GenBitmapTextButton

class FrameFindContact(wx.Frame):
    '''Contact Manager Frame'''
    def __init__(self, *args, **kwargs):
        wx.Frame.__init__(self, *args, **kwargs)
        self.panel = wx.Panel(self, -1)
        self.SetBackgroundColour('#00CCCC')
        letters = [chr(i).upper() for i in xrange(ord('a'), ord('z')+1)]
        self.pos = 5
        for letter in letters:
            button = wx.Button(self.panel, wx.ID_ANY, letter,
                               (self.pos, 3), (26, 30))
            button.SetBackgroundColour('#c2e6f8')
            self.pos += 26
        wx.StaticLine(self.panel, wx.ID_ANY, (0, 35), (700, 3))

        self.list_ctrl = wx.ListCtrl(self.panel, wx.ID_ANY, (0, 50), (690, 200),
                                    wx.LC_REPORT | wx.LC_HRULES | wx.LC_VRULES)
        self.list_ctrl.InsertColumn(0, "Surname", wx.LIST_FORMAT_LEFT, 100)
        self.list_ctrl.InsertColumn(1, "Name", wx.LIST_FORMAT_LEFT, 100)
        self.list_ctrl.InsertColumn(2, "Address", wx.LIST_FORMAT_LEFT, 150)
        self.list_ctrl.InsertColumn(3, "mail", wx.LIST_FORMAT_LEFT, 150)
        self.list_ctrl.InsertColumn(4, "phone", wx.LIST_FORMAT_LEFT, 100)
        self.list_ctrl.InsertColumn(5, "mobile", wx.LIST_FORMAT_LEFT, 100)
        self.list_ctrl.SetFont(wx.Font(8, wx.NORMAL, wx.NORMAL, wx.NORMAL))
        wx.StaticLine(self.panel, wx.ID_ANY, (0, 270), (700, 3))
        self.btn_trash = GenBitmapTextButton(self.panel, wx.ID_ANY,
                                                 wx.Bitmap('trash.png'),
                                                 'Delete'.rjust(20), (10, 300),
                                                 (340, -1))
        self.btn_trash.SetBezelWidth(1)
        self.btn_trash.SetBackgroundColour('#c2e6f8')
        self.btn_find_exit = GenBitmapTextButton(self.panel, wx.ID_ANY,
                                                 wx.Bitmap('cancel.png'),
                                                 'Cancel'.rjust(20), (345, 300),
                                                 (340, -1))
        self.btn_find_exit .SetBezelWidth(1)
        self.btn_find_exit .SetBackgroundColour('#c2e6f8')


        self.Centre()
        self.Show()

def main_core(parent):
    '''Add Contact frame starter'''
    app = wx.PySimpleApp()
    FrameFindContact(parent, -1, title  = 'Find Contact', size = (700, 400))
    app.MainLoop()
   
if __name__ == '__main__':
    main_core(parent = None)

il risultato è:

GUI Cancellazione contatto
Come ultimo frame, per una cancellazione più agile di un contatto,

#!/usr/bin/python
#DelContactFrame.py
'''Delete Contact Frame for Rubrica App'''

import wx
from wx.lib.buttons import GenBitmapTextButton

class FrameDelContact(wx.Frame):
    '''Initial Core Frame'''
    def __init__(self, *args, **kwargs):
        contacts = kwargs.pop('contacts')
        wx.Frame.__init__(self, *args, **kwargs)
        panel = wx.Panel(self, wx.ID_ANY)
        self.SetBackgroundColour('#00CCCC')
        font = wx.Font(10, wx.SWISS, wx.NORMAL, wx.BOLD)
        title = wx.StaticText(panel, wx.ID_ANY, 'Choose Contact to Delete',
                              (70, 10))
        title.SetFont(font)
        self.choice = wx.Choice(panel, wx.ID_ANY, (60, 30), size = (200, 30),
                                choices = contacts)
        wx.StaticLine(panel, wx.ID_ANY, (0, 70), (325, 3))
        text = wx.StaticText(panel, wx.ID_ANY, 'Contact to Delete: ', (10, 80),
                      style = wx.ALIGN_CENTER)
        text.SetFont(font)
        self.name_to_del = wx.StaticText(panel, wx.ID_ANY, '', (140, 82),
                                          style = wx.ALIGN_CENTER)
        wx.StaticLine(panel, wx.ID_ANY, (0, 100), (325, 3))
        self.btn_del = GenBitmapTextButton(panel, wx.ID_ANY,
                                           wx.Bitmap('trash.png'),
                                           'Delete'.rjust(20),
                                           (5, 110), (150, -1))
        self.btn_del.SetBezelWidth(1)
        self.btn_del.SetBackgroundColour('#c2e6f8')
        
        self.btn_exit_del = GenBitmapTextButton(panel, wx.ID_ANY,
                                                wx.Bitmap('cancel.png'),
                                                'Cancel'.rjust(20),
                                                (160, 110), (150, -1))
        self.btn_exit_del.SetBezelWidth(1)
        self.btn_exit_del.SetBackgroundColour('#c2e6f8')

        self.choice.Bind(wx.EVT_CHOICE, self.on_choice)
        self.btn_del.Bind(wx.EVT_ENTER_WINDOW, self.on_btn_enter)
        self.btn_del.Bind(wx.EVT_LEAVE_WINDOW, self.on_btn_leave)
        self.btn_exit_del.Bind(wx.EVT_ENTER_WINDOW, self.on_btn_enter)
        self.btn_exit_del.Bind(wx.EVT_LEAVE_WINDOW, self.on_btn_leave)

        self.Centre()
        self.Show()

    def on_choice(self, evt):
        '''Choice event handler'''
        self.name_to_del.SetLabel(self.choice.GetStringSelection())
        
    def on_btn_enter(self, event):
        '''Into Button enter-mouse event handler'''
        obj =  event.GetEventObject()
        obj.SetBackgroundColour('#ffdf85')
        obj.Refresh()

    def on_btn_leave(self, event):
        '''From Button leave-mouse event handler'''
        obj =  event.GetEventObject()
        obj.SetBackgroundColour('#c2e6f8')
        obj.Refresh()

def main_core(parent):
    '''Add Contact frame starter'''
    app = wx.PySimpleApp()
    list_c = ['Banchelli', 'Bologna', 'Martelli']
    FrameDelContact(parent, -1, title  = 'Delete Contact', size = (325, 200),
                    contacts = list_c)
    app.MainLoop()
    
if __name__ == '__main__':
    main_core(parent = None)

e si presenta così:

ora passiamo al modulo Model.
continua…

Categorie:python, wxpython Tag:

wxPython: Rubrica- VIEWs parte 1

21 Maggio 2011 Commenti chiusi

Ho utilizzato (per lo meno ho tentato di farlo) un simil pattern MVC ( senza Observer), comincerò con la parte grafica.

Ho buttato giù:
un’interfaccia core e le sottointerfacce che utilizzerò per l’aggiunta di un nuovo contatto, per la cancellazione di un contatto, per la gestione di un contatto (modifica) e l’ultima interfaccia per un semplice frame di informazione (il classico about).

GUI Core
partiamo con l’interfaccia core, dove ho utilizzato dei GenBitmapTextButton per ottenere dei pulsanti con immagine e testo:
codice:

#!/usr/bin/python
#CoreFrame.py
'''Core Frame for Rubrica App'''

import wx
from wx.lib.buttons import GenBitmapTextButton

class FrameCore(wx.Frame):
    '''Initial Core Frame'''
    def __init__(self, *args, **kwargs):
        wx.Frame.__init__(self, *args, **kwargs)
        panel = wx.Panel(self, wx.ID_ANY)
        self.SetBackgroundColour('#00CCCC')

        self.btn_info = GenBitmapTextButton(panel, wx.ID_ANY,
                                            wx.Bitmap('info.png'),
                                            'About...'.rjust(15),
                                            (25, 0), (150, -1))
        self.btn_info.SetBezelWidth(1)
        self.btn_info.SetBackgroundColour('#c2e6f8')

        self.btn_add = GenBitmapTextButton(panel, wx.ID_ANY,
                                           wx.Bitmap('add.png'),
                                           'Add Contact'.rjust(15),
                                           (25, 48), (150, -1))
        self.btn_add.SetBezelWidth(1)
        self.btn_add.SetBackgroundColour('#c2e6f8')
        
        self.btn_del = GenBitmapTextButton(panel, wx.ID_ANY,
                                           wx.Bitmap('delete.png'),
                                           'Del Contact'.rjust(15),
                                           (25, 96), (150, -1))
        self.btn_del.SetBezelWidth(1)
        self.btn_del.SetBackgroundColour('#c2e6f8')

        self.btn_find = GenBitmapTextButton(panel, wx.ID_ANY,
                                            wx.Bitmap('search.png'),
                                            'Find Contact'.rjust(15),
                                            (25, 144), (150, -1))
        self.btn_find.SetBezelWidth(1)
        self.btn_find.SetBackgroundColour('#c2e6f8')

        self.btn_exit = GenBitmapTextButton(panel, wx.ID_ANY,
                                            wx.Bitmap('exit.png'),
                                            'Exit'.rjust(15),
                                            (25, 192), (150, -1))
        self.btn_exit.SetBezelWidth(1)
        self.btn_exit.SetBackgroundColour('#c2e6f8')

        self.btn_find.Bind(wx.EVT_ENTER_WINDOW, self.on_btn_enter)
        self.btn_find.Bind(wx.EVT_LEAVE_WINDOW, self.on_btn_leave)
        self.btn_add.Bind(wx.EVT_ENTER_WINDOW, self.on_btn_enter)
        self.btn_add.Bind(wx.EVT_LEAVE_WINDOW, self.on_btn_leave)
        self.btn_del.Bind(wx.EVT_ENTER_WINDOW, self.on_btn_enter)
        self.btn_del.Bind(wx.EVT_LEAVE_WINDOW, self.on_btn_leave)
        self.btn_info.Bind(wx.EVT_ENTER_WINDOW, self.on_btn_enter)
        self.btn_info.Bind(wx.EVT_LEAVE_WINDOW, self.on_btn_leave)
        self.btn_exit.Bind(wx.EVT_ENTER_WINDOW, self.on_btn_enter)
        self.btn_exit.Bind(wx.EVT_LEAVE_WINDOW, self.on_btn_leave)

        self.Centre()
        self.Show()

    def on_btn_enter(self, event):
        '''Into Button enter-mouse event handler'''
        obj =  event.GetEventObject()
        obj.SetBackgroundColour('#ffdf85')
        obj.Refresh()

    def on_btn_leave(self, event):
        '''From Button leave-mouse event handler'''
        obj =  event.GetEventObject()
        obj.SetBackgroundColour('#c2e6f8')
        obj.Refresh()

def main_core(parent):
    '''Info frame starter'''
    app = wx.PySimpleApp()
    FrameCore(parent, -1, title  = 'Rubrica', size = (200, 280))
    app.MainLoop()
    
if __name__ == '__main__':
    main_core(parent = None)

l’aspetto è il seguente:

Non dobbiamo preoccuparci di cosa visualizzerà il frame (per quello lavoreranno il controller insieme al Model), ma di come lo visualizzerà.

GUI info:
come in ogni applicazione che si rispetti, mettiamo un bel messaggio di “About”.
Ho utlizzato un HtmlWindow, per ppoter utilizzare il codice html:

#!/usr/bin/python
#InfoHTMLFrame
'''Info Frame for Core-About choice'''
import wx
import wx.html

from wx.lib.buttons import GenBitmapTextButton

class FrameInfo(wx.Frame):
    '''Frame for Info text'''
    def __init__(self, *args, **kwargs):
        wx.Frame.__init__(self, *args, **kwargs)
        self.text = '''
        <html>
        <body bgcolor="#99FFCC">
        <center><table bgcolor="#00CCCC" width="100%" cellspacing="0"
        cellpadding="0" border="1">
        <tr>
        <td align="center"><h1>Rubrica v2.0</h1></td>
        </tr>
        </table>
        </center>
        <p><b>Rubrica</b> is a simple phone-book realized with:<br>
        <b>- wxPython</b> for Graphics<br>
        <b>- Sqlite</b> for database structure<br>
        <b>- SQLAlchemy</b> for Object Ralation Mapping<br>
        I've tried to use a Model-View-COntroller pattern-like.<br>
        web-site: <b>www.bancaldo.wordpress.com</b><br>
        last revision: may 17, 2011</p>
        </body>
        </html>
        '''
        self.SetBackgroundColour('#00CCCC')
        html = wx.html.HtmlWindow(self)
        html.SetPage(self.text)
        self.btn_quit = GenBitmapTextButton(self, wx.ID_ANY,
                                            wx.Bitmap('quit.png'),
                                            ' quit', (25, 150), (150, -1))
        
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(html, 1, wx.EXPAND|wx.ALL, 5)
        sizer.Add(self.btn_quit, 0, wx.ALIGN_CENTER|wx.ALL, 5)
        self.SetSizer(sizer)
        self.Centre()
        self.Show()

def main_info(parent):
    '''Info frame starter'''
    app = wx.PySimpleApp()
    FrameInfo(parent, -1, title = 'Info', size = (400, 350))
    app.MainLoop()
    
if __name__ == '__main__':
    main_info(parent = None)

risulta:

continua….

Categorie:python, wxpython Tag:

wxPython: multipli Bind su bottoni anonimi

15 Maggio 2011 Commenti chiusi

Ho avuto necessità di utilizzare più Bind sullo stesso pulsante, che però, non è associato durante la creazione a nessuna variabile.
Questo perchè era necessario creare n-bottoni.
Nell’esempio i bottoni sono 3, ma potrebbero essere molti di più.
Chiaramente associando ogni bottone ad una differente variabile, tutto sarebbe più facile,
ma il codice diventerebbe ripetitivo e poi mi sono voluto complicare la vita.

Voglio, in sostanza, stampare a video la label del pulsante, quando premuto, ma anche cambiargli lo sfondo, entrando nella propria area con il puntatore del mouse, e ricolorarlo come prima, uscendone.

Il codice funzionante è il seguente

import wx

class TestFrame(wx.Frame):
    def __init__(self, *args, **kwargs):
        wx.Frame.__init__(self, *args, **kwargs)
        self.panel = wx.Panel(self)
        x_pos = 5
        for label in ['a', 'b', 'c']:
            wx.Button(self.panel, wx.ID_ANY, label, (x_pos, 0), (50, 50))
            x_pos += 50
        self.Show()

class Controller(object):
    def __init__(self):
        self.frame = TestFrame(None, -1, 'Test', size = (200, 200))
        for child in self.frame.panel.GetChildren():
            child.SetBackgroundColour('Pink')
            child.Bind(wx.EVT_BUTTON, self.on_click)
            child.Bind(wx.EVT_ENTER_WINDOW, self.on_entry)
            child.Bind(wx.EVT_LEAVE_WINDOW, self.on_exit)
    def on_click(self, event):
        btn = event.GetEventObject()
        print btn.GetLabel()

    def on_entry(self, event):
        btn = event.GetEventObject()
        btn.SetBackgroundColour('Yellow')
        event.Skip()
    def on_exit(self, event):
        btn = event.GetEventObject()
        btn.SetBackgroundColour('Pink')


def starter():
    app = wx.PySimpleApp()
    Controller()
    app.MainLoop()
    
if __name__ == '__main__':
    starter()

ringrazio anche il forum di python.it.org per l’aiuto, in quanto dimenticandomi un skip(), sull’evento ENTRY_WINDOWS, non riuscivo a far funzionare il codice correttamente.
Piccoli programmatori crescono….

link utili: events

Categorie:wxpython Tag:

wx.ProgressDialog: progressBar riutilizzabile

11 Marzo 2011 Commenti chiusi

Questo codice mi è comodo quando voglio visualizzare ua progress
bar, nel momento in cui sto inserendo dati ad esempio, in un
database.
Di seguito il codice:

'''Module containing a Progress Bar class'''

import wx

class Data(object):
    '''Data class for M-V-C pattern'''
    def __init__(self):
        self.count = 0
        self.max_v = 0
    def add_count (self):
        '''increase the progress bar count for showing its progress'''
        self.count += 1
    def get_max(self):
        '''Get the max value of the progress bar'''
        return self.max_v
    def get_count(self):
        '''Get the current count value of the progressbar in the loop'''
        return self.count
    def set_max(self, value):
        '''set the max value of the progress bar'''
        self.max_v = int(value)

class ProgressBar(wx.ProgressDialog):
    '''Progress Dialog constructor'''
    def __init__(self, max_v):
        wx.ProgressDialog.__init__(self, "", "Update...", max_v,
                                   style = wx.PD_AUTO_HIDE |
                                   wx.PD_ELAPSED_TIME |
                                   wx.PD_REMAINING_TIME)
    def progress(self, progress):
        '''update the progress bar during the loops'''
        wx.MicroSleep(1) #for short processes use wx.MilliSleep(1)
        self.Update(progress)
    def destroy(self):
        '''Distruttore della progress bar'''
        self.Destroy()

class BarController(object):
    '''Progress Bar Controller Constructor'''
    def __init__(self, value = 3):
        self.data = Data()
        self.data.set_max(value)
        self.max = self.data.get_max()
        self.count = self.data.get_count()
        self.progress_bar = ProgressBar(self.max)
    def update_progress_bar(self):
        '''Aggiorna la progressBar'''
        self.data.add_count()
        self.count = self.data.get_count()
        self.progress_bar.progress(self.count) # update progressbar
    def destroy_progress_bar(self):
        '''Destroy the wx.ProgressDialog instance'''
        self.progress_bar.destroy()

def main():
    '''app starter'''
    app = wx.PySimpleApp()
    bar_c = BarController(600)
    while bar_c.count < bar_c.max:
        bar_c.update_progress_bar()
    bar_c.destroy_progress_bar()
    app.MainLoop()
    
if __name__ == "__main__":
    main()

in pratica, utilizzo un controller e gli passo come
argomento un numero, che possono essere le righe di un
file che sto parsando, la lunghezza di una lista ecc.
Questo numero, viene settato nella class Data, sulla
variabile ‘max_v’ ed utilizzato come limite per il finecorsa della
progress bar. Ad ogni ciclo (while nell’esempio), la variabile
count aumenta di uno, fino al raggiungimento del valore massimo
(fine corsa della bar).

Messo il tutto in un modulo, è comodo poterlo richiamare, ogni
volta si decida di visualizzare una progress bar.

NOTA:

se l’attesa in secondi tra un ciclo e l’altro (wx.Sleep(int))
snerva troppo, utilizzare il metodo wx.MilliSleep(int)
ed accellerare il tutto.

EDIT:
ho rivisto il codice dopo le “bacchettate” di PyLint

Categorie:wxpython Tag: