Visualizza post

Questa sezione ti permette di visualizzare tutti i post inviati da questo utente. N.B: puoi vedere solo i post relativi alle aree dove hai l'accesso.


Post - nuzzopippo

Pagine: 1 [2] 3 4 ... 11
16
Tkinter / Re:Tooltip in tkinter
« il: Gennaio 06, 2020, 13:35 »
Ora il codice di test ed esempio, questo test importa "my_tk_object" che altri non è che il codice esposto prima, più "risorse" che è un file python in cui serializzo immagini per icone e testi associati, oltre ad un oggetto che provvede a restituirli, segue il codice relativo, ed inoltre una tabella di riempimento che è stata già postata qui (import tbl_orig) , copiatevela se volete vedere l'esempio funzionale.

il codice di test, 161 roghe
#-*- coding: utf-8 -*-

import tkinter as tk
import risorse as res
import my_tk_object as mtko
from my_tk_object import CreaToolTip as ctt
import tbl_orig as tbl


class GUIAnag(tk.Tk):
    '''Window di test/esempio per utilizzo di tooltip in tkinter'''
    def __init__(self):
        super().__init__()
        self.title('Anagrafiche')
        ids = res.IcoDispencer()
        # --- IMMAGINI PER MENU ---
        self.img_anag = tk.PhotoImage(data=ids.getIco('anagrafiche'))
        self.img_nuova = tk.PhotoImage(data=ids.getIco('nuova'))
        self.img_modifica = tk.PhotoImage(data=ids.getIco('modifica'))
        self.img_salva = tk.PhotoImage(data=ids.getIco('salva'))
        self.img_annulla = tk.PhotoImage(data=ids.getIco('annulla'))
        self.img_cancella = tk.PhotoImage(data=ids.getIco('cancella'))
        self.img_cerca = tk.PhotoImage(data=ids.getIco('cerca'))
        self.img_esci = tk.PhotoImage(data=ids.getIco('chiudi'))
        # --- MENU ---
        barra_menu = tk.Menu(self)
        self.config(menu=barra_menu)
        self.m_anag = mtko.MenuTooltip(barra_menu)
        barra_menu.add_cascade(label='Anagrafiche',
                               menu=self.m_anag,
                               compound='left',
                               image=self.img_anag
        )
        self.m_anag.add_command(label='Nuova',
                                compound='left',
                                image=self.img_nuova,
                                tooltip=ids.getDescr('nuova')
        )
        self.m_anag.add_separator()
        self.m_anag.add_command(label='Modifica',
                                compound='left',
                                image=self.img_modifica,
                                tooltip=ids.getDescr('modifica')
        )
        self.m_anag.add_separator()
        self.m_anag.add_command(label='Salva',
                                compound='left',
                                image=self.img_salva,
                                tooltip=ids.getDescr('salva')
        )
        self.m_anag.add_command(label='Annulla',
                                compound='left',
                                image=self.img_annulla,
                                tooltip=ids.getDescr('annulla')
        )
        self.m_anag.add_separator()
        self.m_anag.add_command(label='Cancella',
                                compound='left',
                                image=self.img_cancella,
                                tooltip=ids.getDescr('cancella')
        )
        self.m_anag.add_command(label='Cerca',
                                compound='left',
                                image=self.img_cerca,
                                tooltip=ids.getDescr('cerca')
        )
        self.m_anag.add_separator()
        self.m_anag.add_command(label='Esci',
                                compound='left',
                                image=self.img_esci,
                                tooltip=ids.getDescr('chiudi'),
                                command=self.destroy
        )
        # --- PANNELLO FOTO ---
        p_foto = tk.Frame(self, padx=4, pady=4)
        p_foto.grid(row=0, column=0, sticky='nsew')
        self.c_foto = tk.Canvas(p_foto,
                                bg='#ffffc0',
                                width=100,
                                height=100,
                                relief='raised',
                                border=2
                                )
        self.c_foto.grid(row=0, column=0)
        p_foto.grid_columnconfigure(0, minsize=10, weight=1)
        p_foto.grid_rowconfigure(0, minsize=10, weight=1)
        # --- PANNELLO DATI ---
        p_data = tk.Frame(self, padx=4, pady=4)
        p_data.grid(row=0, column=1, sticky='ew')
        lbl1 = tk.Label(p_data, text='Codice', padx=2)
        lbl1.grid(row=0, column=0, sticky='ew')
        self.e_ID = tk.Entry(p_data, width=6)
        self.e_ID.grid(row=0, column=1, sticky='ew')
        self.ck_persona = tk.Checkbutton(p_data, text='Persona fisica', padx=2)
        self.ck_persona.grid(row=0, column=2, sticky='ew')
        ttp = 'Spunta definisce definisce Persona fisica, giuridica altrimenti'
        self.ck_persona_ttp = ctt(self.ck_persona, ttp)
        self.ck_obsoleto = tk.Checkbutton(p_data, text='Obsoleto', padx=2)
        self.ck_obsoleto.grid(row=0, column=3, sticky='ew')
        ttp = 'Spunta definisce dato obsoleto e non modificabile'
        self.ck_obsoleto_ttp = ctt(self.ck_obsoleto, ttp)
        lbl2 = tk.Label(p_data,
                        text='Cognome/Rag. Sociale :',
                        anchor='w',
                        padx=2
                        )
        lbl2.grid(row=1, column=0, columnspan=4, sticky='ew')
        self.e_cognome = tk.Entry(p_data)
        self.e_cognome.grid(row=2, column=0, columnspan=4, sticky='ew')
        ttp = 'Cognome o ragione sociale del soggetto'
        self.e_cognome_ttp = ctt(self.e_cognome, ttp)
        lbl3 = tk.Label(p_data, text='Nome :', anchor='w', padx=2)
        lbl3.grid(row=3, column=0, columnspan=2, sticky='ew')
        lbl4 = tk.Label(p_data, text='Nato il :', anchor='w', padx=2)
        lbl4.grid(row=3, column=2, columnspan=2, sticky='ew')
        self.e_nome = tk.Entry(p_data)
        self.e_nome.grid(row=4, column=0, columnspan=2, sticky='ew')
        ttp = 'Nome del soggetto (se persona fisica)'
        self.e_nome_ttp = ctt(self.e_nome, ttp)
        self.e_nato = tk.Entry(p_data)
        self.e_nato.grid(row=4, column=2, columnspan=2, sticky='ew')
        ttp = 'Data di nascita, formato dd/mm/aaaa (se persona fisica)'
        self.e_nato_ttp = ctt(self.e_nato, ttp)
        lbl5 = tk.Label(p_data, text='Luogo di nascita :', anchor='w', padx=2)
        lbl5.grid(row=5, column=0, columnspan=2, sticky='ew')
        lbl6 = tk.Label(p_data, text='Cod. Fisc./P.ta IVA :', anchor='w', padx=2)
        lbl6.grid(row=5, column=2, columnspan=2, sticky='ew')
        self.e_loc = tk.Entry(p_data)
        self.e_loc.grid(row=6, column=0, columnspan=2, sticky='ew')
        ttp = 'Comune o stato estero di nascita'
        self.e_loc_ttp = ctt(self.e_loc, ttp)
        self.e_cf = tk.Entry(p_data)
        self.e_cf.grid(row=6, column=2, columnspan=2, sticky='ew')
        ttp = 'Codice fiscale o partita IVA del soggetto'
        self.e_cf_ttp = ctt(self.e_cf, ttp)       
        p_data.grid_columnconfigure(0, weight=1)
        p_data.grid_columnconfigure(1, weight=1)
        p_data.grid_columnconfigure(2, weight=1)
        p_data.grid_columnconfigure(3, weight=1)
        self.grid_columnconfigure(1, weight=1)
        g = {'minsizes' : [0, 0, 0],
             'weights'  : [1, 1, 1]
             }
        h = ['Cognome', 'Nome', 'data nascita']
        data = [['De Paperoni', 'Paperon', '1901-01-01'],
                ['Paperino', 'Paolino', '1950-11-01'],
                ['Paperone', 'Gastone', '1950-01-06'],
                ['Mouse', 'Miky', '1900-02-29'],
                ['Mouse', 'Minnie', '1910-01-01'],
                ['Mucca', 'Clarabella', '1923-12-13']
                ]
        model = tbl.TableModel(g, h, data)
        tabella = tbl.Table(self, model)
        tabella.grid(row=1, column=0, columnspan=2, sticky='nsew')



if __name__ == '__main__':
    root = GUIAnag()
    root.mainloop()

Il file "risorse.py" sono altre 290 righe :( ... separo il post

17
Tkinter / Tooltip in tkinter
« il: Gennaio 06, 2020, 13:26 »
Saluti a Voi :)

Il presente post NON È una richiesta di supporto ma una "offerta" che porgo agli utenti che stanno iniziando con python e che, come constato, si avventurano a programmare GUI con tkinter.

Avendo deciso di fare, solo per sperimentare alcuni pattern (MVC-Observer), quello che sarà il mio ultimo "esperimento" con tkinter, poi passerò ad altro framework, organizzandolo "come piace a me", ossia con tutti gli "accorgimenti" che mi piace realizzare in una finestra grafica, Uno dei detti "accorgimenti" e applicare "tooltip", tanto ai vari widget, quanto ai menu, cosa che in tkinter non è possibile fare a meno di non integrare nel proprio codice l'obsoleta libreria TIX ed utilizzare il controllo aggiuntivo "balloon" ... cosa che non ho voluto fare.

Con l'aiuto di San Google e di un paio di classi, in parte personalizzate, trovare su stackoverflow, citata la fonte nella docs delle calssi che seguono, ho realizzato un sistema di tooltip utilizzabili in tkinter senza aggiungere ulteriori librerie, articolato in tre classi.

La prima "CreaToolTip" è dedicata ai widget "comuni" e si invoca chiamando "tooltip = CreaToolTip(widget, testo)", ovvio che è solo uno schema di chiamata, ove "widget" è il controllo cui deve essere applicato il tootip e "testo" è il testo da applicarsi, va da se che un toltip definito in una classe deve essere memorizzato quale variabile di istanza della classe stessa.

Le altre due classi sono dedicare a far apparire dei tooltip in un menu tkinter, applicati ai soli item "command", ed è ottenuta sub-classando la classe "Menu" di tkinter nella classe "MenuTooltip" che provvede a memorizzare indice e tooltip di un item "command" definito, quindi ad intercettare il passaggio del mouse esponendo il tooltip corrispondente all'item sottostante il puntatore. La definizione del tooltip avviene semplicemente aggiungendo un ulteriore argomento "tooltip=testo_da_mostrare" in una invocazione "menu.add_command()". Poi provvede la classe "MenuTooltip ad invocare la classe "ToolTipWin" che provvede a visalizzare effettivamente il tooltip.
Preciso che dette classi sono limitate a ciò che ritengo mi serva per lo studio per cui sono state create, potrebbero facilmente essere ridotte a due le classi necessarie ed espanse le tipologie di item servite, compito degli eventuali interessati, se vogliono.

In successione a questo post ne farò un altro per fornirvi un esempio funzionale di test, sarebbe troppo lungo per un unico post dato che una interfaccia che stavo preparando per miei scopi e durante la cui realizzazione mi è venuto il pensiero che questo argomento potrebbe interessare altri utenti del Forum.

Intanto, il codice delle classi definenti i tooltip, 202 righe ;
# -*- coding: utf-8 -*-

import tkinter as tk

# *** CLASSI DI UTILITÀ DERIVATE DA ALTRE TROVATE IN GIRO ED ADATTATE ***

class CreaToolTip(object):
    '''
Crea un tooltip per un generico widget.

Esempio originale tratto da stackoverflow, indirizzo:
https://stackoverflow.com/questions/3221956/how-do-i-display-tooltips-in-tkinter

eseguite minime personalizzazioni.
'''
    def __init__(self, controllo, msg=''):
        self.attesa = 500                # Tempo di attesa in millisecondi
        self.lunghezza = 300             # dimensione del messaggio in pixel
        self.controllo = controllo
        self.testo = msg
        # Utilizza gli eventi del controllo chiamante
        self.controllo.bind('<Enter>', self.avvia)
        self.controllo.bind('<Leave>', self.chiudi)
        self.controllo.bind('<ButtonPress>', self.chiudi)
        self.id = None
        self.tw = None

    def avvia(self, event=None):
        self.schedule()

    def chiudi(self, event=None):
        self.unschedule()
        self.nasconditip()

    def schedule(self):
        self.unschedule()
        self.id = self.controllo.after(self.attesa, self.mostratip)

    def unschedule(self):
        iden = self.id
        self.id = None
        if iden:
            self.controllo.after_cancel(iden)

    def mostratip(self, event=None):
        x = y = 0
        x, y, cx, cy = self.controllo.bbox('insert')
        x += self.controllo.winfo_rootx() + 25
        y += self.controllo.winfo_rooty() + 20
        # crea una finestra toplevel con padre il controllo
        self.tw = tk.Toplevel(self.controllo)
        # lascia solo la label e rimuove gli elementi della finestra
        self.tw.wm_overrideredirect(True)
        self.tw.wm_geometry('+%d+%d' % (x, y))
        messaggio = tk.Label(self.tw,
                             text=self.testo,
                             justify='left',
                             background='#f6f6e3',
                             relief='solid',
                             borderwidth=1,
                             wraplength=self.lunghezza
                             )
        messaggio.pack(ipadx=1)

    def nasconditip(self):
        tw = self.tw
        self.tw = None
        if tw:
            tw.destroy()



class MenuTooltip(tk.Menu):
    '''
    da https://stackoverflow.com/questions/55316791/how-can-i-add-a-tooltip-to-menu-item
    permette di aggiungere un tooltip ai menu-items.
    Introdotte variazioni funzionali rispetto all'esempio citato.
    '''
    def __init__(self, parent):
        """
        :parametro parent: il master del Menu, può essere 'root' o 'Menubar'
         .tooltip == Lista di tuple (yposition, text)
         .tooltip_active == Indice (0-based) del Tooltip attivo
         eventi intercettati <Leave>, <Motion>
        """
        super().__init__(parent, tearoff=0)
        self.tooltip = []
        self.tooltip_active = None
        self.tooltip_win = None

        self.bind('<Leave>', self.leave)
        self.bind('<Motion>', self.on_motion)

    def add_command(self, *cnf, **kwargs):
        tooltip = kwargs.get('tooltip')
        if tooltip:
            del kwargs['tooltip']
        super().add_command(*cnf, **kwargs)
        # chiede l'indice dell'ultimo command
        u_command = self.index("end")
        self.add_tooltip(u_command, tooltip)

    def add_tooltip(self, index, tooltip):
        """
        :parametro index  : Indice (0-based) degli item del Menu
        :parametro tooltip: Testo da mostrare quale Tooltip
        :return: None
        """
        self.tooltip.append((self.yposition(index) + 2, tooltip))

    def on_motion(self, event):
        """
        Cicla i .tooltip per trovare il Menu Item
        """
        for idx in range(len(self.tooltip) - 1, -1, -1):
            if event.y >= self.tooltip[idx][0]:
                x = event.x_root
                y = event.y_root
                point = (x, y)
                self.show_tooltip(idx, point)
                break

    def leave(self, event):
        """
        Distrugge il Tooltip corrente e resetta .tooltip_active a None
        """
        if not self.tooltip_active is None:
            # destroy(<tooltip_active>)
            if self.tooltip_win:
                self.tooltip_win.chiudi()
            self.tooltip_active = None

    def show_tooltip(self, idx, point):
        """
        Mostra il Tooltip se non presente, distrugge il Tooltip attivo
        :parametro idx: Indice del Tooltip mostrato
        :point        : coordinate di posizionamento del tooltip
        :return: None
        """
        if self.tooltip_active != idx:
            # destroy(<tooltip_active>)
            if self.tooltip_win:
                self.tooltip_win.chiudi()
            # create new tooltip
            self.tooltip_active = idx
            msg = self.tooltip[idx][1]
            self.tooltip_win = ToolTipWin(self, point, msg)
            self.tooltip_win.avvia()


# *** MIE CLASSI ***

class ToolTipWin(object):
    '''
Ricodifica della classe CreaToolTip mirata alla esposizione di tooltip
in un menu tkinter (gli oggetti items non vengono restituiti quali widget)
'''
    def __init__(self, ctrl, point, msg=''):
        self.attesa = 500
        self.lunghezza = 300
        self.ctrl = ctrl
        self.text = msg
        self.point = point
        self.id = None
        self.tw = None

    def avvia(self):
        self.unschedule()
        self.id = self.ctrl.after(self.attesa, self.mostra_tip)

    def chiudi(self):
        self.unschedule()
        self.nascondi_tip()

    def unschedule(self):
        iden = self.id
        self.id = None
        if iden:
            self.ctrl.after_cancel(iden)

    def mostra_tip(self):
        x, y = self.point
        # crea una finestra toplevel con padre il menu
        self.tw = tk.Toplevel(self.ctrl)
        # lascia solo la label e rimuove gli elementi della finestra
        self.tw.wm_overrideredirect(True)
        self.tw.wm_geometry('+%d+%d' % (x, y))
        messaggio = tk.Label(self.tw,
                            text=self.text,
                            justify='left',
                            background='#f6f6e3',
                            relief='solid',
                            borderwidth=1,
                            wraplength=self.lunghezza
                            )
        messaggio.pack(ipadx=1)

    def nascondi_tip(self):
        tw = self.tw
        self.tw = None
        if tw:
            tw.destroy()

18
Buon giorno , la funziona clear per pulire la entry dopo una selezione tramite snakelist é gia inserita nello script ...
Verrebbe da chiedere "perché non l'hai utilizzata" na comprendo che Tu abbia voluto chiarire l'esempio.

... ho pensato di evitare problematiche derivate dalla comunicazione ed intreccio di db, vi sono 3 db ed ognuno di essi ha le proprie funzioni...
Oiboh! così rinunci alle possibilità offerte dai database relazionali ... a 'sto punto, non dico che equivarrebbe ad utilizzare json o file csv ma poco ci manca ... la potenza dei database relazionali è data, in larga parte, dalle relazioni che possono essere definite tra le varie "tavole" dati, dato che sei alle prime armi, se non hai scadenze, parti bene, con calma e senza fretta di "fare", leggiti qualcosa sui db, cerca di capire come funzionano, come strutturarli ed interrogarli, vedrai che Ti sarà utile poi.

Non nascondo che l istruziome globale é qualcosa che non ho approfondito difatti non sono a conoscenza delle criticita che si possano presentare ...
È una questione legata al design progettuale delle applicazioni, complica notevolmente la vita ... anche se espresse per il C, le considerazioni contenute in questa pagina valgono tranquillamente per qualsiasi linguaggio, cerca in rete e fatti un'idea, qualche battuta in merito si è anche data proprio pochi giorni fa in un altro post.


Si tratta di problemi legati all aumentare dei dati nel db su cui lavora o legati al possibile intreccio di dati con altri db?
Cosa? l'index delle tuple in out-for-range prima indicato? Per niente, è design della applicazione : il controllo preventivo che Ti ho indicato dovrebbe eliminarlo, fermo restando la struttura di funzione da Te indicata.
Poi, tutto è possibile ... i misteri di una applicazione che usa TRE db per un obiettivo sconosciuto sono al di la della mia capacità divinatoria ;)

19
Questa funzione, a mio parere, evidenzia numerose criticità :
  • Stai mischiando programmazione funzionale con programmazione ad oggetti, se non calibrata bene la faccenda può causare notevoli problemi;
  • Utilizzi dinamicamente una variabile globale, idea pessima, ancora gestibile se è riferimento per un unico "oggetto", inteso anche come finestra dati, diventa ingestibile appena gli oggetti che vi riferiscono e manipolano aumentano;
  • il modo di utilizzo di SnakeList sembra dimostrare che il suo scope è globale ... ma domando, non fa parte di una GUI? e questa HUI non è definita in una classe? Dove è il suo "self."?

Comprendo i problemi dovuti all'approccio iniziale, li ho avuti e in parte li ho ancora, anche io, pur se "il grosso" lo ho affrontato con altri linguaggi, ma i Tuoi problemi derivano direttamente da carenze concettuali, principalmente in ambito di programmazione ad oggetti,l' apparente stato attuale sembra fatto apposta per crearti dolore.

il metodo "curselection()" delle liste restituisce gli items selezionati, ammesso che vi sia una selezione, un primo controllo da fare è vedere se restituisce qualcosa.

Pur lasciando le criticità su esposte, Ti suggerisco un tentativo di "mettere una pezza", condito da un suggerimento di metodi di classe per svuotare e compilare i controlli, fermo restando quanto detto prima sulle criticità che credo di vedere.

def snakerec(event):            # <-- MALE : snakerec(self, event)
global sd # <-- MALE : GLOBAL == problemi
if SnakeList.curselection():
searksnake = SnakeList.curselection()[0]
sd = SnakeList.get(searchsnake)
else:
return

if sd:
self._compile_ctrl(sd)

# Ti troverai certo più volte a svuotare i controlli, un
# metodo di classe che se ne occupa è più conciso
def _clear_ctrl(self):
self.txtSnakeID.delete(0,END)
    self.txtSnakeSpecies.delete(0,END)
self.txtSnakeAge.delete(0,END)
self.txtSnakeGender.delete(0,END)
self.txtSnakeWeight.delete(0,END)
       
       
# stessa cosa, un unico metodo di classe che riceve i dati da
# mostrare e li rappresenta
def _compile_ctrl(self, sd):
# svuota
self._clear_ctrl()
# e riempie
    self.txtSnakeID.insert(END,sd[1])
    self.txtSnakeSpecies.insert(END,sd[2])
self.txtSnakeAge.insert(END,sd[3])
self.txtSnakeGender.insert(END,sd[4])
self.txtSnakeWeight.insert(END,sd[5])


NON copiare acriticamente ciò che Ti propongo, io non ho presente la Tua applicazione in sviluppo, cerca di comprendere l'idea alla base ed il perché delle modifiche proposte (oltre che del MALE indicato).

Ciao :)

20
...uno dei 2 errori penso sia dovuto al fatto che non vi è un eccezione per la selezione nulla dalla lista,,,

Per quanto riguarda questo aspetto, non occorre una eccezione, è sufficiente verificare che la selezione corrente nella lista abbia o meno dei dati, qualcosa tipo :
var = lista.currselection() # restituisce una lista degli item selezionati
if not var:                                                       # se non è stato restituito niente esce
return

21
Dipende un po' dalla base dati e da come è impostata ... in sostanza se c'è l'autocommit (scrittura immediata dei dati) o meno e altro.

Il rollback serve per annullare una "transazione" andata male e ripristinare ad un iniziale punto precedente la base dati, la "transazione" non riguarda necessariamente una singola operazione sulla base dati ma può riguardare tutta una serie di operazioni (anche molto grossa) riguardante anche dati presenti su più tavole contemporaneamente. Di norma si stabilisce un punto di partenza tramite un "begin-trans".

Personalmente, ho l'abitudine di disattivare l'autocommit e racchiudere tutte le operazioni di inserimento e modifica dei dati in blocchi "begintrans/commit-o-rollback"., anche quando faccio piccole cose.

Per maggiori dettagli sulla "faccenda leggi qua

Per il "tuple index out of range" evidentemente c'è una situazione in cui vai a leggere qualcosa che non c'è, vai ad indovinare come fai ;)

Comunque, si, prevedere i possibili errori e prevenirli/intercettarli è un obbligo per qualsiasi applicazione non sia un "giocattolo", ed anche per quest'ultime non farlo è una pessima abitudine.

Ciao

22
... e perdonatemi se per qualcuno sono cose ovvie...
Ovvie?, forse per qualcuno ma qui vai sul "difficile", almeno per me, stai parlando di processi paralleli (leggasi multithreading e multiprocessing), argomenti che personalmente trovo piuttosto avanzati e difficili, e sui quali il massimo che personalmente posso fare è indicarteli.

....Vorrei che mentre elabora visualizzi con caratteri lampeggianti la scritta "Elaborazione in corso - non toccare".   Ho trovato qualcosa in questo sito : # Gestione per una scritta lampeggiante : https://www.raspberrypi.org/forums/viewtopic.php?t=230465
ma questo quando visualizza la scritta , ferma elaborazione e non va bene. Dovrebbe eseguire una elaborazione che non blocchi il resto della elaborazione....
Se ho capito bene il contesto del discorso in link, vorresti effettuare l'operazione in tkinter? ... se è così, tieni presente che tkinter (come, d'altronde, molti  framework in vari linguaggi, è un processo a thread unico bloccante, non ci scommetterei sulla possibilità di implementarci il multithreading, framework tipo swing (java) mettono a disposizione classi "speciali" (tipo swingworker) per evitare salti mortali ai poveri programmatori ... non credo vi siano strumenti analoghi in tkinter, per lo meno non ne ho, ancora, sentito parlare.

Chissà, forse col multiprocessing potresti realizzare qualcosa del genere o, anche qui forse, con framework più evoluti tipo le QT o le WX ma la mia al momento è solo una ipotesi ... lascio la parola ad eventuali utenti che conoscano la problematica.

23
Videogame / Re:campo minato con python URGENTE
« il: Gennaio 03, 2020, 16:59 »
No però "global" no, eh?... che ce ne sono già abbastanza di problemi nel mondo.
Non posso che concordare, personalmente evito proprio di fare variabili globali (e neanche le costanti mi piacciono), già mi incasino di mio ...

I casi sono due...
1) l'OP impara a usare le classi,
2) oppure ....
Considerato che "campo minato" è una applicazione grafica, penso che sia la 1) e basta, anche se imparare ogni aspetto è comunque "bene"

... l'urgenza posta nel titolo, però, mi ha fatto pensare di limitarmi al solo aspetto richiesto ed incrociare le dita augurando fortuna :)

24
È il terzo post sullo stesso argomento, non credo che il cross-posting sia molto gradito.

Comunque, già risposto in merito nel Tuo precedente post nella sezione video-games.

Ciao

25
Videogame / Re:campo minato con python URGENTE
« il: Gennaio 03, 2020, 16:08 »
Il codice da Te postato è incompleto, oltre a riferire a funzioni non riportate, comunque una assegnazione di questo tipo
celle = emptyMatrix(righe,colonne)

fatta all'interno di una funzione, definisce una variabile locale e non riferisce ad una variabile globale, esempio
Python 3.6.9 (default, Nov  7 2019, 10:44:02) 
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license()" for more information.
>>> stato = []
>>> celle = []
>>> def init_matrix(r, c, v):
m = [[v]*c for i in range(r)]
return m

>>> def empty_matrix(r, c):
return init_matrix(r, c, 0)

>>> def inizializza():
righe = 5
colonne = 5
celle = empty_matrix(righe, colonne)
stato = empty_matrix(righe, colonne)


>>> inizializza()
>>> print(celle)
[]
>>> print(stato)
[]
>>> def inizializza():
righe = 5
colonne = 5
global celle
global stato
celle = empty_matrix(righe, colonne)
stato = empty_matrix(righe, colonne)


>>> inizializza()
>>> print(celle)
[[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]
>>> print(stato)
[[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]
>>>


Come puoi vedere, perché una variabile possa essere utilizzata quale globale e ricevere una assegnazione all'interno di una funzione, deve essere prima dichiarata come "global", non vi è necessità di ciò se bisogna solo leggerne i valori.
Per maggiori dettagli, cerca la documentazione relativa alla visibilità delle variabili ed alle variabili globali.

Ciao :)

26
Database / Re:sqlite3.OperationalError: no such table: snake
« il: Gennaio 02, 2020, 13:16 »
Comprensibile intenzione, comunque, rischi di girare un po' a vuoto (esperienza diretta ;) )
...Per il problema in questione ho intenzioni di provare con l inserimento di un percorso assoluto per il file.db nella stessa directoryin cui si trovano gli script...

Personalmente, ritengo i "percorsi assoluti" non grandiosi, inizialmente Ti ho indicato di utilizzare le possibilità del modulo "os", tra le altre cose, se li combini con le capacità del modulo "sys", puoi facilmente determinare il percorso in cui viene eseguita l'applicazione, così :
appdir = os.path.abspath(os.path.dirname(sys.argv[0]))

Magari ci sono anche modi migliori, in questa pagina ho scritto come ci sono "arrivato" a quella istruzione (appunto di quando ancora non mi rendevo conto di quanto poco sapessi)

Giusto per toccare con mano, metti questo script in una direttrice e lancialo daterminale quando sei in  un'altra directory
import os
import sqlite3
import sys

appdir = os.path.abspath(os.path.dirname(sys.argv[0]))

def snakedata():
    db_name = os.path.join(appdir, 'data', 'snake.db')
    print(db_name)
    conn = sqlite3.connect(db_name)
    cur = conn.cursor()
    query = '''
            CREATE TABLE IF NOT EXISTS snake (id INTEGER PRIMARY KEY,
                                              snakeID TEXT UNIQUE NOT NULL,
                                              snakeSpecies text,
                                              snakeAge text,
                                              snakeGender text,
                                              snakeWeight text)
            '''
    cur.execute(query)
    conn.commit()
    conn.close()

if __name__ == '__main__':
    conn = sqlite3.connect('snake.db')
    cur = conn.cursor()
    query = '''
            CREATE TABLE IF NOT EXISTS spider (id INTEGER PRIMARY KEY,
                                               spiderID TEXT UNIQUE NOT NULL,
                                               spiderSpecies text,
                                               spiderAge text,
                                               spiderGender text,
                                               spiderWeight text)
            '''
    cur.execute(query)
    conn.commit()
    conn.close()
    snakedata()


Vedrai che verranno creati due database snake, uno nella directory da dove lanci, l'antro in una sotto-directory "data" di quella contenente la Tua applicazione (ritengo improprio mischiare i dati con i files applicativi)

Questa è una sessione di esempio sul mio sistema
NzP:~$ pwd
/home/nuzzopippo/my_tmp/tmp
NzP:~$ tree
.
└── bambam
    ├── data
    └── esempio.py

2 directories, 1 file
NzP:~$ python3 bambam/esempio.py
/home/nuzzopippo/my_tmp/tmp/bambam/data/snake.db
NzP:~$ tree
.
├── bambam
│   ├── data
│   │   └── snake.db
│   └── esempio.py
└── snake.db

2 directories, 3 files
NzP:~$


Ciao

27
Potrei dirti, sbrigativamente, che Ti basterebbe eseguire una query di ricerca tipo "SELECT * FROM snake WHERE snakeID = 'id_da_inserire'" e se restituisce dati annullare il processo ... ma, invece, Ti faccio un esempio :

Supponiamo di creare una tavola snake "leggermente" diversa da quella indicata in Tuo precedente post

>>> def snakedata():
conn = sqlite3.connect('snake.db')
cur = conn.cursor()
query = '''
CREATE TABLE IF NOT EXISTS snake (id INTEGER PRIMARY KEY,
                                  snakeID TEXT UNIQUE NOT NULL,
                                  snakeSpecies text,
                                  snakeAge text,
                                  snakeGender text,
                                  snakeWeight text)
'''
cur.execute(query)
conn.commit()
conn.close()


>>> snakedata()


Ora, popoliamola un po'
>>> data = [(1, "crota", "Crotalo", "20", "Serpente a sonagli", "5"),
    (2, "viper", "Vipera", "18", "Vipera comune", "1.5"),
    (3, "boa", "Boa", "21", "Boa constrictor", "17")
    ]
>>> conn = sqlite3.connect('snake.db')
>>> query = 'INSERT INTO snake VALUES (?, ?, ?, ?, ?, ?)'
>>> conn.executemany(query, data)
<sqlite3.Cursor object at 0x7ff87f187a40>
>>> conn.commit()


Diciamo, ora, che abbiamo acquisito uno splendido anaconda nel nostro rettilario, vogliamo inserirlo
>>> serpe = (4, "anac", "Anaconda", "22", "Anaconda", "82")
>>> conn.execute(query, serpe)
<sqlite3.Cursor object at 0x7ff87f187b90>
>>> conn.commit()
>>> result = conn.execute('SELECT * FROM snake')
>>> for biscia in result:
print(biscia)


(1, 'crota', 'Crotalo', '20', 'Serpente a sonagli', '5')
(2, 'viper', 'Vipera', '18', 'Vipera comune', '1.5')
(3, 'boa', 'Boa', '21', 'Boa constrictor', '17')
(4, 'anac', 'Anaconda', '22', 'Anaconda', '82')

Allora ... abbiamo felicemente inserito il nostro anaconda nel rettilario e visto che striscia tra i suoi simi .... però i facenderos brasiliani hanno salvato un anaconda verde dalla foresta che avevano incendiato ed hanno pensato bene, non sapendo che farsene, di mandarlo a tener compagnia a quello in nostro possesso, lo inseriamo:

>>> serpe = (5, "anac", "Anaconda", "19", "Anaconda verde", "65")
>>> conn.execute(query, serpe)
Traceback (most recent call last):
  File "<pyshell#24>", line 1, in <module>
    conn.execute(query, serpe)
sqlite3.IntegrityError: UNIQUE constraint failed: snake.snakeID
>>>

... Non ce lo ha inserito, malgrado fosse un anaconda! ...ciò per questa particolare istruzione data per creare la tavola :  snakeID TEXT UNIQUE NOT NULL,
detta istruzione definisce che uno "snakeID" deve essere unico valore nella tavola "snake£ e deve essere, per forza, valorizzato in ogni inserimento.

Ovviamente, in un programma, tale violazione della integrità dati deve essere intercetta, proseguendo nell'esempio :
>>> try:
conn.execute(query, serpe)
except sqlite3.IntegrityError as e:
print('Errore di inserimento :', e)
conn.rollback()


Errore di inserimento : UNIQUE constraint failed: snake.snakeID
>>> serpe = (5, "anacv", "Anaconda", "19", "Anaconda verde", "65")
>>> try:
conn.execute(query, serpe)
except sqlite3.IntegrityError as e:
print('Errore di inserimento :', e)
conn.rollback()


<sqlite3.Cursor object at 0x7ff87f187c70>
>>> result = conn.execute('SELECT * FROM snake')
>>> for biscia in result:
print(biscia)


(1, 'crota', 'Crotalo', '20', 'Serpente a sonagli', '5')
(2, 'viper', 'Vipera', '18', 'Vipera comune', '1.5')
(3, 'boa', 'Boa', '21', 'Boa constrictor', '17')
(4, 'anac', 'Anaconda', '22', 'Anaconda', '82')
(5, 'anacv', 'Anaconda', '19', 'Anaconda verde', '65')
>>>

... in un colpo solo Ti mostrato come intercettare tale "Violazione" ed il possibile "rimedio" (cambiare lo snakeID) ... Tutta 'sta tirata per renderTi evidente una cosa significativa : non è sufficiente solo apprendere un linguaggio di programmazione ma è bene apprendere anche altre cose, nello specifico se intendi adoperare database relazionali, guardati il loro funzionamento ed il loro linguaggio di definizione/interrogazione.

Se farlo o meno à una Tua scelta, anche se te lo consiglio vivamente, dato l'ordine delle Tue richieste.

Detto questo, ti suggerirei di modificare la tavola come Ti ho mostrato, poi di variare la funzione addsnakedata() in questo modo :
def addsnakedata():
if(len (snakeID.get())!=0) :
try:
snakedb_backend.addsnakerec(snakeID.get(),
                                  snakeSpecies.get(),
                                  snakeAge.get(),
                                  snakeGender.get(),
                                  snakeWeight.get()
                                  )
      SnakeList.delete(0,END)
        SnakeList.insert(END,
                       snakeID.get(),
                       snakeSpecies.get(),
                       snakeAge.get(),
                       snakeGender.get(),
                       snakeWeight.get()
                       )
except sqlite3.IntegrityError as e:
# qui intercetti l'errore ed annulli l'operazione


C'è da dire, che l'intercettazione andrebbe effettuata in "addsnakerec", per eseguire il rollback ... ma lascio a Te le decisioni "operative" che vorrai applicare, nel caso di occorra, vedi la docs di Raise ;)

28
Ciao @paolodecaro

La problematica da Te espressa è tutt'altro che "strana" ha varie "cause", la principale è la strutturazione stessa della applicazione proposta ... ho l'impressione Tu non abbia ancora inquadrato adeguatamente le "classi" python, il codice postato ha evidenti segni di tale situazione.

Or bene, una applicazione ad interfacce grafiche leggermente più che esemplificativa richiede necessariamente concetti di programmazione ad oggetti.

Avendo disponibili abbondanti frammenti di tempo mentre la consorte si prepara per uscire, mi son divertito a rendere, senza stravolgerlo troppo, un attimino più funzionale il Tuo codice, che Ti propongo per Tua comparazione, fermo restando l'invito, nel caso non sia sbagliata la mia interpretazione, ad approfondire le Tue conoscenze, principalmente sulla programmazione ad oggetti in python, oltre che utilizzarne la metodica nel programmare intrfacce grafiche (gli esempi trovati in rete di solito sono inadeguati)

il Tuo "programma padre", trasformato sub-classando tkinter.Tk, lo ho chiamato "main.py"
import tkinter as tk
import seriale_suoni as ss


class App(tk.Tk):
    '''Finestra principale dell'applicazione.'''
    def __init__(self):
        super().__init__()
        mioContenitore1 = tk.Frame(self)
        mioContenitore1.pack()
        # Assegno le dimensioni alla finestra : larghezza, altezza,
        # posizione iniziale schermo (X) (pixel)
        self.geometry('300x70+400+200')
        # Ora assegno un titolo alla mia finestra
        self.title('La mia prima Finestra con Tkinter')
        bsuoni = tk.Button(self, command = self.chiamasuoni)
        bsuoni.configure(text = "Gestione Suoni Device", background = "#00FF00")
        bsuoni.pack()
        btempi = tk.Button(self, command = self.chiamatempi)
        btempi.configure(text = "Gestione Tempi Device", background = "#00FFFF")
        btempi.pack()

    def chiamasuoni(self):
        sound_win = ss.GestioneSuoni(self)
        sound_win.grab_set()

    def chiamatempi(self):
        pass
   

if __name__ == '__main__':
    w = App()
    w.mainloop()


Ho eliminato gli import ritenuti inadeguati, il Tuo "arduino_python_seriale_suoni_07p" lo ho ri-denominato "seriale_suoni.py" e realizzatolo sub-classando una tkinter.Toplevel ... ovviamente togliendo tutti gli import "personalizzati" e quelli ritenuti inadeguati, il codice di seriale_suoni.py :
import tkinter as tk
from tkinter import ttk
from tkinter import messagebox


class GestioneSuoni(tk.Toplevel):
    def __init__(self, Genitore):
        super().__init__(Genitore)
        label1 = tk.Label(text="Laboratorio Paolo--Figlio")
        self.minsize(600, 400)
        self.title("Laboratorio Arduino-Python : Gestione Suoni")
        #window.wm_iconbitmap('prova.ico')
        self.geometry("%dx%d+%d+%d" % (800, 600, 150, 120))  # anche posizione
        #---------------------------------------------------------------------#
        # creo i pannelli che conterrano widget
        #---------------------------------------------------------------------#
        #mioGenitore2 = window
        ### Il quadro principale si chiama 'mioContenitore1'
        mioContenitore3 = tk.Frame(self)
        mioContenitore3.pack()
        #---------------------------------------------------------------------#
        # quadro alto - Pannello di Intestazione - Scheletro-Paolo
        #---------------------------------------------------------------------#
        quadro_intestazione_suoni = tk.Frame(mioContenitore3,
                                             borderwidth = 10,
                                             relief = 'ridge',
                                             width= 500,
                                             height = 4,
                                             background = "#1589ff",
                                             )
        quadro_intestazione_suoni.pack(side='top', fill='both', expand='yes')
        #---------------------------------------------------------------------#
        #  Inserisco Widget - Pannello di Intestazione
        #---------------------------------------------------------------------#
        labelint1 = tk.Label(quadro_intestazione_suoni,
                             text="Gestione Suoni da assegnare ai Device")
        labelint1.pack()
        #---------------------------------------------------------------------#
        # quadro alto - Combobox Selezione per tutti i device
        #---------------------------------------------------------------------#
        quadro_centrale_suoni = tk.Frame(mioContenitore3,
                                         borderwidth = 10,
                                         relief = 'ridge',
                                         width= 500,
                                         height = 4,
                                         background = "#7FFFD4"
                                         )
        quadro_centrale_suoni.pack(side='top', fill='both', expand='yes')
        #---------------------------------------------------------------------#
        # quadro alto - Selezione Device di attribuzione
        #---------------------------------------------------------------------#
        quadro_centrale_device_suoni = tk.Frame(mioContenitore3,
                                                borderwidth = 10,
                                                relief = 'ridge',
                                                width= 500,
                                                height = 4,
                                                background = "#1589ff",
                                                )
        quadro_centrale_device_suoni.pack(side='top', fill='both', expand='yes')
        #-----------------------------------------------------------------------#
        # Quadro comandi-basso piede comandi
        #-----------------------------------------------------------------------#
        quadro_piedecomandi_suoni = tk.Frame(mioContenitore3,
                                             borderwidth = 5,
                                             relief = 'ridge',
                                             width= 500,
                                             height = 20,
                                             background = "#e6e6ff",
                                             )
        quadro_piedecomandi_suoni.pack(side='top', fill='both', expand='yes')
        #------------------------------------------------------------------------#
        # Creo area piede - configuro tasti operativi
        #------------------------------------------------------------------------#
        gruppo9_suoni = tk.LabelFrame(quadro_piedecomandi_suoni,
                                      text="Comandi Operativi",
                                      padx=5,
                                      pady=5,
                                      width=200
                                      )
        gruppo9_suoni.grid(row=1,
                           sticky='W',
                           padx=5,
                           pady=5,
                           ipadx=200,
                           ipady=20
                           )
        bexit = tk.Button(gruppo9_suoni, command=self.bexit_Premuto)
        bexit.configure(text="Esci senza Salvare", background="#ffd700")
        bexit.grid(row=2, column=1)
        # Carico Skiera iniziale che contiene il percorso per ogni suono.
        #sksuoni = (os.listdir('G:\\Paolo_Python\\paolo_python_programmi\python_suoni'))
        sksuoni = []
        for i in range(10):
            a_menga = 'suono_%02d' % i
            sksuoni.append(a_menga)
        max = len(sksuoni)
        self.SUONOLINK  = tk.StringVar()
        self.SUONOLINKSEL  = tk.StringVar()
        self.SUONOMIO  = tk.StringVar()
        #---------------------------------------------------------------------#
        #  Inserisco Widget - Pannello di Intestazione
        #---------------------------------------------------------------------#
        # Ricerca Brano Musicale
        suono_lbl = tk.Label(quadro_centrale_suoni,
                             text="Ricerca Suono",
                             bg= "#7FFFD4",
                             font=('arial', 8),
                             bd=10
                             )
        suono_lbl.grid(row=0, column=0, sticky='w')
        self.combo = ttk.Combobox(quadro_centrale_suoni,
                                  textvariable=self.SUONOLINK,
                                  values=sksuoni,
                                  width=50
                                  )
        #binding of user selection with a custom callback
        self.combo.bind('<<ComboboxSelected>>',
                       self.PrendiSuonoSelezionato)
        self.combo.current(1)    #set primo elemento della Tabella
        self.combo.grid(row=0, column=1,  sticky='w')
        suonosel_lbl = tk.Label(quadro_centrale_suoni,
                                text="Suono Selezionato",
                                bg= "#7FFFD4",
                                font=('arial', 8),
                                bd=10
                                )
        suonosel_lbl.grid(row=4, column=0,  sticky='w')
        self.SUONOLINKW = tk.Entry(quadro_centrale_suoni,
                                   textvariable=self.SUONOMIO,
                                   width=50,
                                   disabledbackground="#FFA500",
                                   state="disabled",
                                   font=('arial', 10,),
                                   disabledforeground='white',
                                   bd=4
                                   )
        self.SUONOLINKW.grid(row=4, column=1, sticky="W")

    def gestionesuoni(self):
        pass
   
    def bexit_Premuto(self):
        self.destroy()

    def PrendiSuonoSelezionato(self, evt):
        self.SUONOLINKSEL = str(self.combo.get())
        self.SUONOMIO.set(self.SUONOLINKSEL)
        self.SUONOLINKW.update()     
     
     
     
 
      #def main():               
        #window = Tk()
        #gestionesuoni = GestioneSuoni(window)
        #window.mainloop()       
           
   
   
   
if __name__ == '__main__':

    root = tk.Tk() # the app window
    window = GestioneSuoni(root)
    root.mainloop()


Vedrai che dalla finestra "padre" potrai richiamare la finestra "figlia" e che a selezione nel combo-box la label viene aggiornata, anche se non ho curato molto quanto fatto (tempi non larghi) la problematica posta "funziona" e te lo pongo quale esempio comparativo.

Cuao e buon anno :)

29
Purtroppo, non conosco per nulla le librerie qt, e molto poco in genere le GUI in python, ma mi ha intrigato la "espressione regolare", materia da sempre per me ostica che cerco di apprendere.
Con un po' di sbattimento ho definito una regex che sembra valutare correttamente l'input numerico, accettando virgole e punti quale separatore, te la propongo nel caso possa esserTi utile :
disc = re.compile(r'^-?\d+(?:[,.]\d*)?$')


mi son divertito a fare un po' di test con "sub" delle re e pare funzionare :

>>> str_in = '-12,345'
>>> str_out = re.sub(',', '.', str_in) if disc.match(str_in) else 'NONE'
>>> print(str_out)
-12.345
>>> str_in = '-12,.345'
>>> str_out = re.sub(',', '.', str_in) if disc.match(str_in) else 'NONE'
>>> print(str_out)
NONE
>>> str_in = '-12.345'
>>> str_out = re.sub(',', '.', str_in) if disc.match(str_in) else 'NONE'
>>> print(str_out)
-12.345
>>> str_in = '-12345'
>>> str_out = re.sub(',', '.', str_in) if disc.match(str_in) else 'NONE'
>>> print(str_out)
-12345
>>> str_in = '12345'
>>> str_out = re.sub(',', '.', str_in) if disc.match(str_in) else 'NONE'
>>> print(str_out)
12345
>>> str_in = '12a345'
>>> str_out = re.sub(',', '.', str_in) if disc.match(str_in) else 'NONE'
>>> print(str_out)
NONE
>>>


Suppongo che, forse, che QRegExpValidator possa essere sub-classato, ovvero possa essere utilizzato un generico QValidator con eventuali metodi associati per "ridefinire" l'input del controllo associato ... segnalo quanto sopra nel caso possa esserTi utile.

Ciao

30
Database / Re:sqlite3.OperationalError: no such table: snake
« il: Dicembre 30, 2019, 12:02 »
Oddio!

 ... al momento non ho il tempo necessario a guardarmi tutto il codice, comunque dovresti provvedere, in primo luogo, a verificare se il database esiste (p.e. con i metodi di "os", e "os.path") e, se esiste, verificare che contenga le tavole necessarie, ciò potresti farlo, posto quanto fatto prima, in questo modo :

>>> conn =  sqlite3.connect('snake.db')
>>> cur = conn.cursor()
>>> query = "SELECT name FROM sqlite_master WHERE type='table'"
>>> cur.execute(query)
<sqlite3.Cursor object at 0x7f60c95c3960>
>>> result = cur.fetchall()
>>> for elem in result:
print(elem)


('snake',)
>>>


Quindi provvedersi, invece del ciclo di stampa, alla verifica delle tavole esistenti, con creazione di quelle necessarie ed assenti.
La cosa potresti farla inserendo una specifica funzione nel modulo di backend della base dati da richiamarsi o prima del mainloop di Tk (processo bloccante) ovvero nello "__init__" di "wild".

Poi, riferendosi al backend dati, vedo continui avvii e chiusure della connessione ... per funzionare, funzionano (credo) ma la faccenda è onerosa per le risorse richieste ... valuta Tu ma personalmente stabilirei e memorizzerei una volta per tutte la connessione all'avvio della applicazione e la passerei quale parametro alle funzioni di gestione dati ... magari, per eventuali fenomeni di time-out, userei accorgimenti come il decoratore indicato in questa pagina ... poi regolati Tu.

In ogni caso, fermarsi un attimo e tornare all'esame della logica dettata dalle esigenze del programma, pianificandole a priori, è sempre bene, principalmente quando ci si inceppa un po', fa risparmiare tempo.

Ciao :)

P.S. - @RicPol ha inserito delle indicazioni sulle quali non posso che concordare, lascio, comunque, inalterato ciò che stavo per inviare.

Pagine: 1 [2] 3 4 ... 11