TheTruster 的个人资料TheTruster's Box照片日志列表更多 ![]() | 帮助 |
|
|
9月25日 Sezione Modelli 3D - Aggiunti 4 ModelliAggiunti 4 nuovi modelli nella sezione 3D Models. Di seguito le anteprime con i collegamenti per scaricarli. Potete scaricarli liberamente accedendo all'area Download -> Modelli 3D. 11月19日 Automazione 2 - Importare dati da Excel ad Access con ADONel post precedente ho affrontato il problema dell'importazione di dati da un foglio di lavoro di Excel ad un Documento Word. La stessa cosa avrebbe potuto essere realizzata utilizzando come destinazione, in luogo del documento Word, un altro Foglio di Lavoro Excel, un Database Access oppure un programma in Visual Basic 6. Il sistema dell'automazione, però, ci pone nella situazione di dover avere installato sulla macchina l'applicazione del pacchetto Office che intendiamo utilizzare. Nel caso di Excel ad esempio, non potremmo fare ricorso all'apposita DLL se Excel stesso non fosse installato nel sistema. Una soluzione alternativa, anche se non si tratta propriamente di automazione, è quella di trattare Excel come fosse un Database, nel qual caso avremmo bisogno solo del modello ad oggetti ADO e, se il sistema operativo è Windows XP, questo è installato insieme al sistema, per cui sicuramente disponibile. Per il nostro esempio di importazione prenderemo a base un Database Access anche perfettamente vuoto senza alcuna tabella, visto che ci occuperemo di crearla dinamicamente al momento dell'importazione. Avremo anche bisogno di un file di Excel per la nostra prova per cui, se non ne abbiamo uno già disponibile con dei dati intabellati in maniera coerente, creiamone uno, con questa impostazione:
Come si può notare, la cosa importante è utilizzare la prima riga come intestazione dei campi. Passiamo alla creazione del nostro Database - o creiamone uno nuovo - e aggiungiamo un nuovo Modulo, premendo Moduli nella finestra del Database e Nuovo sulla barra superiore della stessa finestra. Si aprirà la finestra dell'Editor di Visual Basic e la prima cosa che faremo, è referenziare la libreria che ci interessa, relativa ad ADO. Lo possiamo fare da Strumenti -> Riferimenti.... Molto probabilmente, dipendentemente dalla versione di Access, si troverà già referenziata la Microsoft ActiveX Data Object 2.1 Library nel qual caso possiamo de-referenziarla andandone a scegliere una analoga, ma più aggiornata, ovvero la Microsoft ActiveX Data Object 2.8 Library oppure la più recente installata nel sistema, se questa non è presente. Il modello ad oggetti ADO, benchè disponga di un numero relativamente basso di oggetti, risulta piuttosto articolato e sarebbe impossibile discuterne le potenzialità e le caratteristiche in poche righe. In generale, comunque, anche ADO come gli oggetti della libreria di Excel si possono riassumere in una certa gerarchia, a capo della quale troviamo l'oggetto Connection. Questo è l'oggetto principale poichè permette di "aprire una porta" sul Database permettendoci l'accesso ai dati. Attraverso la Connection possiamo eseguire delle operazioni direttamente sul DB inviandogli delle frasi SQL, per cui ci consente di inserire dati e riceverne indietro, creare tabelle o cancellarne o ancora modificarne la struttura. Se creare una tabella piuttosto che inserire dati o modificarli non presuppone per forza un ritorno degli stessi verso la nostra applicazione, la semplice lettura dei dati contenuti in una tabella per la loro visualizzazione, ci pone nella situazione di doverli "immagazzinare" da qualche parte. Ci serve un contenitore, insomma. Questo contenitore si chiama Recordset. Come suggerisce la parola questo oggetto è un set di record ovvero una porzione dei dati contenuti genericamente nel nostro DB anche se distribuiti su più tabelle, selezionati secondo dei criteri. Per selezionare dei record si usa una Query ovvero una frase SQL che il motore del DB interpreta per restituirci dei dati coerenti con le nostre condizioni. Mi scuso per questa digressione, forse per qualcuno superflua, ma probabilmente necessaria per permettere a chi si avvicina per la prima volta a questa tecnologia di capire almeno il senso delle righe di codice che andrò a riportare in seguito. In ogni caso consiglio, per una concezione più dettagliata dell'argomento, di dare una scorsa a questo sito (in inglese) dove, oltre alle spiegazioni riferite ai vari oggetti di ADO, sono presenti anche alcuni esempi di utilizzo. Ritornando alla nostra importazione, prepariamo l'oggetto principale di cui ci serviremo per aprire il DB Excel ovvero una Connection. Codice: Dim oConn As ADODB.Connection Di solito, personalmente, preferisco avere un unico oggetto Connection, attraverso il quale aprire più Recordset. L'oggetto Connection viene dichiarato pubblico, aperto all'inizio dell'applicazione e chiuso alla chiusura della stessa oppure quando non risulta più necessario, mentre gli oggetti Recordset vengono aperti e chiusi, spesso nella stessa routine, quando esauriscono la loro utilità, sia essa di inserimento/modifica che di lettura. In questo caso, possiamo accontentarci un solo oggetto Recordset che dichiareremo all'interno della routine di importazione. A proposito, cominciamo a costruirla. Dal punto di vista logico la nostra routine dovrà:
Per aprire la Connessione verso una qualsiasi DB è necessaria la cosiddetta ConnectionString ovvero stringa di connessione. Essa ci permette di scegliere la modalità di apertura del DB, specificando il motore e il file del DB stesso, e passando al Database Username e Password nel caso in cui fossero necessari per accedervi. Per Excel è necessario una particolare ConnectionString e un'ottima fonte per reperirne una adeguata alle nostre necessità è il sito ConnectionStrings.com. Guardando tra quelle disponibili per aprire Excel come DB troveremo anche questa Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\MyExcel.xls;Extended Properties="Excel 8.0;HDR=Yes;IMEX=1"; Per semplicità possiamo anche realizzare una Routine riutilizzabile, appositamente creata per aprire la connessione, liberandoci dalla necessità dover inserire il codice necessario tutte le volte in diverse routines: Codice: Sub ApriConnessione(NomeDB As String) Come si può notare la connessione verso il nostro file Excel si ridurrà ad una semplice riga del tipo: Codice: ApriConnessione "C:\Cartella\FileExcel.xls" Analogamente possiamo creare una seconda routine per chiudere la connessione eventualmente aperta: Codice: Sub ChiudiConnessione() Abbiamo praticamente realizzato i punti 1 e 7 della lista precedente, quindi passiamo, finalmente, alla creazione della routine di importazione. Nella sua costruzione utilizzeremo, oltre alla Connessione verso Excel e al Recordset, la connessione che Access, ovvero la sua Applicazione, apre verso le sue tabelle. Questa Connessione è accessibile attraverso l'oggetto CurrentProject. Sfrutteremo questa Connessione per inviare al DB delle frasi SQL idonee a creare la tabella che ospiterà i dati e le frasi di UPDATE (sempre SQL) che ci permetteranno di inserirvi i dati letti da Excel. Codice: Sub ImportaDatiExcel() Nel codice appena riportato ho richiamato anche la Routine EliminaTabella, appositamente creata per eliminare la tabella, nel caso fosse presente, prima di crearne una nuova. E' ovvio che, se non si cancellasse la tabella preesistente si avrebbe un errore. Utilizzando una routine separata, l'errore viene trascurato, se rilasciato dall'applicazione, evitandoci di inserire ulteriori righe di codice alla routine di importazione mantenendola, tutto sommato, compatta. Ecco la dichiarazione della routine: Codice: Sub EliminaTabella(NomeTabella As String) Abbiamo praticamente terminato e l'unica cosa che rimane da fare è provare il tutto. Se tutto andrà a buon fine, riusciremo ad importare i dati contenuti in un File Excel aperto come fosse un DB in un Database Access, dentro una tabella creata dinamicamente. La creazione della tabella, in questo caso, è stata fatta in maniera un po' "rigida" nel senso che abbiamo già impostato da codice i campi con loro tipo e lunghezza, ma volendo rendere il tutto ancora più flessibile, prima della creazione della tabella, potremmo scorrere con un ciclo i campi del Recordset importato per valutarne numero, nome e tipo di dati inseriti, creando una tabella esattamente corrispondente ai dati da importare. Nel caso vogliate segnalarmi inesattezze o richiedere ulteriori chiarimenti contattatemi pure senza remore 11月14日 Automazione 1 - Importare dati da Excel a WordLe applicazioni del pacchetto Office, possiedono in compendio alle loro già numerose funzioni, anche un ambiente di sviluppo integrato: il Visual Basic for Application o VBA. Ognuna delle applicazioni ovvero Excel, Access, Word, etc., ha delle peculiarità, principalmente dovute alla diversità degli oggetti che in esse vengono trattati. Per fare un esempio, in Word esistono gli oggetti Document, mentre in Excel gli oggetti Worksheet, e così via. La base del linguaggio, quindi, è una sola ed è Visual Basic (seppure in una versione "ridotta" e adattata), ma a seconda dell'applicazione che si sta utilizzando è possibile avere a disposizione degli oggetti ognuno con sue proprietà e metodi utili per automatizzare molte delle operazioni che normalmente andrebbero compiute manualmente. Descrivere seppur brevemente tutte le possibilità offerte dall'automazione sarebbe impossibile per cui mi limiterò a descrivere come è possibile accedere ad un file di Excel dall'esterno, ovvero da un programma Visual Basic 6, o un altro file di Excel o ancora da un documento Word o un Database Access. La metodologia non cambia e data la base comune del linguaggio le operazioni da compiere sono esattamente le stesse. La prima cosa da considerare è che Excel stesso è un oggetto, come pure oggetti sono i suoi vari elementi gerarchicamente dipendenti come Workbook, WorkSheet, Range, Cell, etc. Tutti questi oggetti fanno parte di una cosiddetta Libreria che, se si usa Excel, è già referenziata e fruibile senza fare null'altro, ma che va referenziata nel caso in cui si abbia intenzione di accedere ad un'applicazione Excel da un qualsiasi altro programma VB6 o applicazione Office. Per referenziare la libreria è sufficiente andare nella finestra dell'Editor di Visual Basic attraverso Strumenti -> Macro -> Visual basic Editor - oppure premendo CTRL+F11 - scegliere da questa nuova finestra, dal menu Strumenti, la voce Riferimenti... e cercare la libreria che si chiama Microsoft Excel xx.x Object Library spuntando l'apposita casellina. ![]() Effettuata questa semplice operazione avremo a disposizione tutti gli oggetti che Excel espone normalmente all'interno del proprio VBA. Come accennato in precedenza la struttura degli oggetti di Excel è di tipo gerarchico, nel senso che esiste un oggetto principale ovvero Application, dal quale interdipendono via via altri oggetti. Im maniera molto, molto semplificata, potrebbe riassumersi in questo modo: ![]() Tenendo a mente questa struttura è facile immaginare come può essere possibile gestire più Workbook facenti parte della stessa applicazione o più Worksheet sempre dipendenti dallo stesso Workbook. Alla base ci sono gli oggetti Range che rappresentano una cella o un loro insieme, e gli oggetti Cells attraverso i quali è possibile identificare una singola cella attraverso riga e colonna. Ognuno di questi oggetti possiede numerosi metodi e proprietà, che sarebbe eccessivamente dispersivo trattare in questo piccolo spazio, ma che è possibile esplorare e provare con un minimo di intraprendenza e l'aiuto della Guida in Linea. Fatta questa doverosa presentazione, entriamo nel vivo dell'argomento, ovvero l'accesso ad un file di Excel da VBA. Proveremo, da un Documento Word, ad accedere ad un Workbook Excel salvato sul nostro HardDisk per prelevare il valore da alcune celle, importandole contestualmente sul nostro documento. La prima cosa da fare in Word, dopo aver referenziato la libreria come descritto sopra, è aggiungere un nuovo Modulo VBA. Si fa semplicemente scegliendo la voce Modulo dal menu Inserisci. Nel nuovo Modulo cominciamo con l'istanziare gli oggetti che ci serviranno per accedere al nostro file di Excel, iniziando dall'Applicazione e proseguendo con il WorkBook e il Worksheet. Codice: Dim xlApp As Excel.Application Quindi creiamo una nuova sub che chiameremo Importa. All'interno di questa dovremo occuparci di "dare vita" alla nuova istanza dell'applicazione Excel che intenderemo utilizzare, assegnando di seguito i vari oggetti, "puntandoli" sul Workbook che intendiamo aprire e sul Worksheet sul quale risiedono i dati che vogliamo importare. C'è da precisare che accedendo ad Excel in questo modo, esso rimarrà invisibile (a meno che non si renda visibile esplicitamente), permettendoci così di effettuare le operazioni che ci servono in maniera trasparente per l'utente. Codice: Sub Importa() Possiamo già provare il funzionamento del nostro codice chiudendo la finestra di Visual Basic e, da Word, andando sul menu Strumenti -> Macro -> Macro... per visualizzare una finestra contenente la lista di tutte le macro contenute nel Documento Word, tra le quali dovremmo trovare anche la nostra, denominata Importa. Selezioniamola e premiamo Esegui per avviarne l'esecuzione. Se tutto è andato bene sulla pagina del Documento Word, a partire dal cursore, dovrebbe visualizzarsi la lista di valori contenuta nella prima colonna del nostro file di Excel. Spero che questo semplice esempio sia stato abbastanza chiaro e utile a comprendere almeno i meccanismi che stanno alla base dell'automazione. In caso contrario non esitate a chiedermi ulteriori chiarimenti. Nel prossimo intervento proverò a spiegare come trattare Excel come un DB, accedendovi attraverso il modello ad oggetti ADO e prelevando dei valori da un Foglio di Lavoro, importandoli su una Tabella di un Database Access. 11月3日 Inserire delle ProgressBar in una ListView con Visual Basic 6Spesso capita che un programmatore si trovi a prendere spunto da altre applicazioni per realizzare ciò che gli passa per la testa. A volte è un'esigenza reale, a volte è solo la curiosità di confrontarsi con se stessi, cimentandosi in qualcosa che, probabilmente, sul momento non è utile. Ovviamente non ritengo queste attività "tempo perso", prima di tutto perchè tutto ciò che ci spinge a ragionare vale come bagaglio culturale e secondo perchè c'è la possibilità che, un giorno, si debba realizzare quella determinata cosa, e la si abbia già pronta! La mia "ispirazione" in questo caso, è stata la ListView di e-Mule. Come molti sapranno, in una delle sue colonne è presente una barra di avanzamento che sta ad indicare lo stato di avanzamento del download di un file. Incuriosito, ho deciso di provare a realizzare una cosa del genere con una ListView standard di Visual Basic 6. La barra di avanzamento che ho realizzato non è della stessa complessità di quella di e-Mule, nel senso che non tiene conto di un avanzamento frammentato, ma mi ritengo soddisfatto del risultato e della sua funzionalità. Il principio che sta alla base è quello di realizzare la barra di avanzamento in un'immagine bitmap, assegnandola successivamente al SubItem del controllo ListView. Per prima cosa, cominciamo la realizzazione di questo progetto aggiungendo i Microsoft Windows Common Controls 6.0 ai componenti. Possiamo farlo andando su Progetto -> Componenti e selezionandoli dalla lista dei Controlli. Poi prepariamo il Form che farà da base come in figura, rispettando la nomenclatura dei controlli: ![]() Come accennavo prima, abbiamo bisogno che la nostra barra sia formata da una serie di bitmap, ognua delle quali rappresenterà uno stadio di avanzamento, ma nella ListView facente parte dei Common Controls non è possibile inserire direttamente un'immagine. Essa per essere assegnata ad un Item o SubItem deve risiedere su una ImageList. La prima cosa da fare, quindi, è popolare l'ImageList con le immagini della barra di avanzamento. Ovviamente le immagini della ProgressBar dovranno essere proporzionate alla colonna nella quale dovranno essere inserite, per cui definiamo una variabile pubblica nel form che ci consente di variare con poco sforzo e in qualunque momento, la sua collocazione. Codice: Dim pBarCol As Integer Adesso possiamo creare la routine che disegna materialmente una ProgressBar. Per farlo, abbiamo bisogno del riferimento alla ListView per calcolare altezza e larghezza della barra, del valore di colonna nella quale dovrà essere inserita e, infine, del suo valore. La routine MakeProgressBar, per spiegarla in breve, si occupa di:
Questa è la routine completa. Codice: Sub MakeProgressBar(lw As ListView, colHead As Integer, Value As Integer) Prima ho detto che di ProgressBar ne andranno create una per ogni valore, quindi ci serve una routine che si occupi di generarle, passando alla MakeProgressBar i valori sequenzialmente corretti, da 1 a 100. Codice: Sub CreateProgressBars() Come si può notare, la routine si occupa di eliminare l'assegnazione della ImageList dalla ListView poichè altrimenti non sarebbe possibile popolarla o cancellarla, lancia la generazione delle barre attraverso il ciclo da 1 a 100 e riassegna la ImageList alla ListView. Vedremo in seguito che questa routine tornerà utile anche quando sarà necessario aggiornare la visualizzazione del controllo al variare della dimensione della colonna che ospita la ProgressBar. Arrivati a questo punto abbiamo popolato la nostra ImageList con le immagini rappresentanti tutti i valori di avanzamento necessari. Non rimane che assegnare quella opportuna, in base al valore da rappresentare, al SubItem di nostro interesse. Per rendere le cose più semplici ho predisposto 2 routines: SetValue e GetValue. La SetValue controlla, innanzitutto, se il valore passato rispetta il range di 1 a 100 consentito, quindi assegna al SubItem scelto l'immagine corrispondente al valore da rappresentare, tramite la proprietà ReportIcon. In questa routine, per tenere traccia del valore attualmente rappresentato, viene conservato, nella proprietà Tag del SubItem, anche il valore numerico passato come argomento. Codice: Sub SetValue(itm As ListItem, Value As Integer) Per ottenere il valore attualmente impostato in un determinato SubItem basta usare la Function GetValue, passando l'Item di riferimento come argomento: Codice: Function GetValue(itm As ListItem) As Integer Procediamo, adesso, al monitoraggio della larghezza delle colonne della ListView, poichè in corrispondenza di una variazione della loro larghezza sarà necessario adeguare la dimensione delle ProgressBar. Purtroppo la ListView non fornisce nessun feedback riguardante l'evento di espansione o riduzione delle colonne, per cui possiamo ricorrere ad un semplice Timer, il quale si occuperebbe di controllare ciclicamente che le dimensioni della colonna contenente le ProgressBar non sia variata. Non è un'operazione molto impegnativa, poichè il "lavoro" di ridimensionamento verrebbe eseguito solo nel caso in cui ci sia una variazione di dimensioni. Questa è la routine di evento del Timer nominato tResize: Codice: Private Sub tResize_Timer() Come dicevo, attraverso una variabile Static (WidthMonitor) si controlla che la dimensione rilevata non sia differente rispetto al controllo precedente, nel qual caso le ProgressBar verrebbero rigenerate (sulla base della nuova dimensione) attraverso l'invocazione della CreateProgressBar. Abbiamo adesso tutti gli strumenti per poter utilizzare le ProgressBar nel nostro controllo ListView e quello che dobbiamo fare, per provare il tutto è disporre di un certo numero di Item nella nostra ListView ai quali assegnare le barre. Sfruttiamo l'evento Load del form, per formattare correttamente la ListView, popolandola con degli Item e dei dati fittizi. Codice: Private Sub Form_Load() Si noterà nel codice, che alla ListView viene aggiunta una colonna iniziale che ho definito "Dummy" e che ha larghezza 0. Questo è, più che altro, un escamotage di natura estetica: quando si assegna un'immagine alla proprietà ReportIcon di un SubItem, lo stesso spazio che essa occupa, ma vuoto, viene aggiunto anche accanto all'Item nella prima colonna. Esteticamente è poco gradevole per cui ho preferito nascondere la prima colonna, lasciandola comunque non popolata. Se lanciamo adesso il progetto possiamo già notare che la nostra ListView è popolata e con le barre di avanzamento (tutte settate a valore 0) inserite nella terza colonna. Possiamo anche variare la dimensione di quest'ultima, notando che le ProgressBar si ridimensioneranno di conseguenza. Allo stato attuale il progetto è già funzionante e potrebbe essere usato per rappresentare i valori di avanzamento dei vari "files", poichè basterebbe impostare attraverso la SetValue il corretto valore all'Item interessato, per notare l'avanzamento della barra al valore settato. Per una prova generale, però, abbiamo inserito sul form (vedi immagine iniziale) un secondo Timer e un CommandButton denominati rispettivamente tProgress e Command1. Il Command1 serve semplicemente per avviare/stoppare il Timer, mentre all'interno della routine di evento tProgress_Timer() vengono generati dei valori casuali "decidendo" arbitrariamente l'uno o l'altro Item, incrementando il valore della ProgressBar di un tot, anch'esso casuale. Ecco entrambe le routine di evento: Codice: Private Sub Command1_Click() Codice: Private Sub tProgress_Timer() Questo è il risultato finale con le barre in avanzamento: ![]() I colori delle barre possono essere variati a piacere, basta intervenire sulle variabili BarBorder, BarBack, BarNormal e BarComplete dichiarate nella routine MakeProgressBar. Come al solito, potete scaricare il progetto completo dalla sezione Download -> Software, oppure cliccando qui: ![]() Non esitate a contattarmi per farmi presenti inesattezze o chiedere ulteriori chiarimenti. 10月14日 Array di Controlli in VBA di ExcelIl VBA (Visual Basic for Applications) relativo alle varie applicazioni Office quali Excel, Word, Access, etc. è uno strumento decisamente utile per coloro i quali intendono "espandere" le già ampie possibilità offerte da questi software, implementando da soli caratteristiche non previste dall'applicazione originale o creando, ad esempio, una maschera per il data-entry che faccia da front-end per il proprio documento. Proprio riguardo l'aspetto del data-entry, chi è abituato a lavorare in Visual Basic 6, utilizzando il VBA potrebbe trovarsi ad operare in un ambiente un po' più stretto considerato che in quest'ultimo mancano strumenti importanti che renderebbero agevole la gestione di molti controlli dello stesso genere. Uno di questi è l'array di controlli. Un array è definito come una serie di variabili identificate da uno stesso nome al quale è possibile riferirsi utilizzando un numero ovvero un indice. Analogamente, un array di controlli, è una serie di controlli dello stesso genere che condividono il nome. Anche in questo caso ogni controllo è raggiungibile attraverso un indice numerico. La cosa che rende gli Array di Controlli decisamente utili è l'esposizione degli eventi in maniera comune a tutti i suoi elementi e le Routines di evento sono in grado di restituire un indice che identifica il controllo che ha scatenato l'evento stesso. In Visual Basic 6 è molto semplice creare e gestire un Control Array, poichè questi vengono gestiti direttamente dall'ambiente di sviluppo. Non si può dire alltrettanto per il VBA dove, se vogliamo un array di controlli, dobbiamo crearcelo e gestircelo da soli. In VBA, quando si dispongono i controlli sul Form, essi vengono a far parte della collection Controls che è possibile ciclare per effettuare delle operazioni su più controlli, anche di genere diverso, a prescindere dal loro nome. Questo è un breve esempio per dimostrare come è possibile azzerare il contenuto di tutti i TextBox presenti su un Form utilizzando la collection Controls: Codice: Sub AzzeraTxt(frm As UserForm) Se si inserisce la suddetta routine in un Modulo, sarà possibile richiamarla da qualsiasi UserForm semplicemente scrivendo: Codice: AzzeraTxt Me Purtroppo, se è vero che tramite la collection Controls abbiamo accesso ad una serie di controlli pur se non indicizzati e con nomi differenti è altrettanto vero che non possiamo che ricevere singolarmente gli eventi relativi a ciascuno dei controlli presenti sul Form. Questo ci costringe a dover scrivere in maniera ripetitiva del codice per inserirlo negli eventi relativi a tutti i controlli che ci interessa gestire. Una maniera interessante di far fronte al problema è l'utilizzo delle Classi. Utilizzando una Classe si possono gestire attraverso un solo oggetto molti aspetti relativi a più oggetti facenti parte di uno stesso insieme. All'interno di una Classe si possono definire Proprietà, Metodi ma soprattutto Eventi, il che è proprio ciò che ci interessa, considerato che potremo rilasciare degli eventi comuni a tutti i controlli gestiti dalla Classe stessa. Per fare in modo di avere la maggiore flessibilità possibile ho pensato di realizzare 2 Classi. La prima rappresenta l'oggetto vero e proprio, ovvero l'Item della Array di controlli che andremo a gestire, mentre la seconda si occuperà di fare da "tramite" tra la gestione dei singoli Item disposti in una Collection e la nostra applicazione. La prima delle due Classi è molto semplice e non fa altro che definire gli oggetti relativi alle varie tipologie di controlli che si desiderano gestire (TextBox, CheckBox, Images, etc...) ed il rilascio degli eventi peculiari per ogni controllo. In considerazione del fatto che la classe può essere istanziata più volte per ottenere più Array di controlli, si è manifestato il problema di identificare la Classe "madre" alla quale restituire la chiamata dell'evento. Per ovviare a ciò è bastato utilizzare un ulteriore oggetto "CallerObject" che in pratica permette ad ogni Item di "sapere" a quale Classe appartiene. Ecco il codice della classe cMatrixItem: Codice: Public WithEvents itmTextBox As MSForms.TextBox Come si può notare gli eventi definiti sono davvero pochi come pure i tipi di oggetti gestiti ma, per analogia, è immediata l'aggiunta sia di ulteriori tipi di controlli che di ulteriori eventi ad essi relativi. Andiamo adesso a progettare la Classe che ospiterà la Collection vera e propria. Definiamo prima di tutto gli oggetti che la classe dovrà gestire: gli Item e la Collection. Codice: Dim itm As cMatrixItem L'oggetto itm è dichiarato come cMatrixItem e servirà per tipizzare correttamente l'oggetto da aggiungere alla Collection. Quindi definiamo gli Eventi che la Classe sarà in grado di rilasciare. Come detto prima, gli eventi qui dichiarati a scopo esemplificativo sono pochi, ma è possibile arricchire, semplicemente dichiarandoli, la lista di eventi che la Classe sarà in grado di rilasciare in risposta alle azioni compiute sui controlli gestiti dalla Collection. Codice: Public Event Click(item As Object) Nel nostro caso, al posto di restituire esclusivamente un indice, nelle Routines di Evento forniremo un riferimento all'oggetto che l'evento lo ha generato. Restituire solo l'indice servirebbe a poco visto che i controlli in VBA non sono comunque indicizzabili, rendendo di conseguenza difficile l'identificazione del controllo stesso in relazione all'evento scatenato. I moduli di Classe possiedono un evento che viene scatenato quanto esse vengono istanziate nel codice che le utilizza: l'evento Class_Initialize(). Questo possiamo sfruttarlo per "dare vita" alla nuova Collection di controlli: Codice: Private Sub Class_Initialize() Occupiamoci di definire adesso un metodo Add attraverso il quale, da codice, potremo aggiungere alla Collection i controlli che ci interessa gestire come Array Codice: Public Sub Add(ByVal actItm As Object) Nel metodo appena definito si può notare che il controllo è visto in maniera generica ma viene fatta una distinzione sul suo tipo tramite l'If...Then. Questo avviene perchè non tutti i controlli possiedono le stesse proprietà o i medesimi eventi, anche se molti condividono entrambi. Anche in questo caso gli oggetti considerati sono solo 3, ma la possibilità di espandere il range di tipologie è sempre valida e fattibile in maniera immediata. Continuiamo lo sviluppo della Classe aggiungendo 2 Proprietà in sola lettura: Count e ItemCollection per ottenere rispettivamente il numero di elementi già presenti nell'Array e il riferimento ad uno dei controlli dell'Array attraverso un Indice numerico. Codice: Public Property Get Count() As Single Ricordo che anche nella ItemCollection è possibile aggiungere altri tipi di controlli analogamente a come già fatto per le 3 tipologie in esame. Passiamo adesso a definire le routines che rilasceranno gli eventi veri e propri attraverso la loro invocazione (da parte dell'Item) dei metodi del CallerObject Codice: Friend Sub ItemClick(item As Object) Inutile dire che anche in questo ennesimo caso si possono aggiungere tutti gli eventi necessari relativi ai vari controlli. Ovviamente bisogna badare al fatto che molti controlli possono condividere eventi dello stesso genere, come nel caso dei controlli Image e CheckBox che condividono l'evento Click. In questi casi basterà dichiarare una sola volta la Routine ItemClick. Questo è il codice completo della Classe cCtlMatrix: Codice: Dim itm As cMatrixItem La progettazione delle Classi è terminata. Non rimane che creare un semplice progetto di prova per valutare la correttezza del comportamento. Aggiungiamo un nuovo UserForm e disponiamo su di esso alcuni controlli Image, delle CheckBox, qualche TextBox, un CommandButton e una Label. Lasciamo pure i nomi di default, come in figura. ![]() Usiamo questo codice per il Form: Codice: Dim WithEvents TextBoxArray As cCtlMatrix Per dubbi o chiarimenti non esitate a contattarmi. E' possibile scaricare il file di Excel con il progetto completo da qui: ![]() 9月20日 Esplorare un Database Access con OpenSchema di ADO in Visual Basic 6ADO è uno strumento molto potente per quanto riguarda l'accesso ad un DB, e consente di gestire il trattamento dei dati da/per il DB in maniera piuttosto agevole ed intuitiva con relativamente pochi oggetti e con semplici metodi. Un aspetto meno conosciuto del modello ad oggetti ADO è la possibilità di "esplorare" il DB anche dal punto di vista strutturale con il metodo OpenSchema. Con questo metodo possiamo andare a sbirciare nell'elenco delle tabelle disponibili in un DB e dei relativi campi. Non solo questo, in realtà, poichè il metodo che andrò ad illustrare - seppure in minima parte - permette di esplorare molto più delle semplici tabelle e dei campi che le compongono (oltre ad eventuali indici, tipi di campo, chiavi primarie, etc.) in quanto consente di accedere all'intero catalogo delle query o delle procedure e ad altre peculiari caratteristiche riscontrabili normalmente nella struttura di un database. A questo punto potrebbe anche sollevarsi la questione del "perchè" dovremmo occuparci di andare ad esplorare la struttura di un DB, quando quello che normalmente basta è trattarlo come un cassetto nel quale riporre dati ritirandoli fuori al momento giusto! In realtà (anche se non è una pratica che consiglierei) potrebbe capitare che la struttura di un DB non sia così rigida e che si renda necessario, in corso di utilizzo, l'aggiunta o l'eliminazione dinamica di tabelle o campi. Per non rinunciare alla flessibilità della nostra applicazione non rimane altra strada che far sapere all'applicazione stessa come è fatto il DB. Per semplicità ci rivolgeremo ad un database Access e, per meglio comprendere il funzionamento del metodo in argomento, realizzeremo una semplice applicazione che, volendo, potrà essere migliorata ed ampliata per dotarla di ulteriori caratteristiche, che le permettano di andare ancora più in profondità nell'esplorazione di un DB. Questo piccolo tool sarà formato da un singolo Form all'interno del quale troveranno posto alcuni controlli come un TextBox, due ListView, un controllo CommonDialog, un paio di pulsanti e qualche label. Per usare la ListView e il CommonDialog dovremo referenziare questi controlli nella lista dei Componenti, accessibile dal menu Progetto -> Componenti.
Sarà inoltre necessario referenziare la libreria ADO più recente sul PC. Normalmente dovrebbe essere la Microsoft ActiveX Data Object 2.8 Library
Preparato lo scenario, possiamo procedere alla progettazione del Form.
Iniziamo a scrivere del codice e, nella sezione dichiarazioni del nostro Form, dichiariamo l'oggetto relativo alla connessione che servirà per accedere al DB.
Dim cn As ADODB.Connection Sfruttando l'evento Form_Load() possiamo occuparci di preparare gli oggetti che raccoglieranno i dati, ovvero le ListView. Aggiungeremo delle colonne in relazione ai dati da visualizzare e contestualmente prepareremo anche il CommonDialog per permetterci di specificare il Database .mdb che esploreremo. Private Sub Form_Load()
Passiamo a scrivere il codice relativo alla pressione sul tasto di caricamento del DB.With lwTables .View = lvwReport .ColumnHeaders.Add , , "Nome Tabella" .ColumnHeaders.Add , , "Tipo Tabella" End With With lwFields .View = lvwReport .ColumnHeaders.Add , , "Nome Campo" .ColumnHeaders.Add , , "Tipo Campo" .ColumnHeaders.Add , , "Lunghezza" End With With cDialog .CancelError = True .DefaultExt = "*.mdb" .DialogTitle = "Seleziona un Database Access" .Filter = "Database Access (*.mdb)|*.mdb" .Flags = cdlOFNExplorer Or cdlOFNFileMustExist End With End Sub La sequenza delle operazioni è piuttosto logica:
Private Sub cmdLoadDB_Click()
La Routine RetrieveTables è uno dei tasselli importanti di questa piccola applicazione.On Error GoTo No_File cDialog.ShowOpen Set cn = New ADODB.Connection cn.Open "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & cDialog.FileName txtDBPath.Text = cDialog.FileName lwTables.ListItems.Clear lwFields.ListItems.Clear RetrieveTables No_File: End Sub E' qui che viene utilizzato il metodo OpenSchema per accedere alla struttura delle Tabelle del nostro DB. Come dicevo, attraverso OpenSchema è possibile esplorare diversi aspetti della struttura del database e tutto dipende dalla costante passata come argomento al metodo (Schema As SchemaEnum) e dai criteri imposti (Restrictions), che sono opzionali e variano a seconda della costante utilizzata e in base alla necessità di filtrare, in relazione alla nostra esigenza, la lista degli oggetti che vogliamo ottenere. La sintassi del metodo è la seguente: OpenSchema (Schema As SchemaEnum, [Restrictions], [SchemaID]) Il metodo restituisce un oggetto di Recordset che conterrà tanti record per quante tabelle verranno trovate. Per ottenere la lista di tutte le tabelle comprese quelle nascoste "di sistema" utili alle impostazioni del DB è necessario specificare esclusivamente la costante adSchemaTables. Sub RetrieveTables()
Come si può vedere nella precedente routine sono stati utilizzati esclusivamente 2 campi del recordset contenente la lista delle tabelle, ovvero TABLE_NAME e TABLE_TYPE, ma è possibile sapere molto di più su una tabella, utilizzando il valore contenuto negli altri campi:Dim itmX As MSComctlLib.ListItem Dim rsTables As ADODB.Recordset Set rsTables = cn.OpenSchema(adSchemaTables) If Not (rsTables.BOF And rsTables.EOF) Then While Not rsTables.EOF Set itmX = lwTables.ListItems.Add(, , rsTables.Fields("TABLE_NAME").Value) itmX.ListSubItems.Add , , rsTables.Fields("TABLE_TYPE").Value rsTables.MoveNext Wend End If rsTables.Close Set rsTables = Nothing Set itmX = Nothing End Sub
TABLE_CATALOG
Occupiamoci adesso di gestire la seconda ListView che ospiterà la lista dei campi e alcuni dei dati ad essi relativi.TABLE_SCHEMA TABLE_NAME TABLE_TYPE TABLE_GUID DESCRIPTION TABLE_PROPID DATE_CREATED DATE_MODIFIED Useremo l'evento ItemClick() per ottenere dall'Item cliccato il nome della tabella da passare al metodo OpenSchema come parametro "Restrictions". Private Sub lwTables_ItemClick(ByVal Item As MSComctlLib.ListItem)
Restrictions in realtà può essere un Array contenente diversi parametri dipendenti strettamente dal tipo di oggetto richiesto.lwFields.ListItems.Clear RetrieveFields (Item.Text) End Sub Una lista completa dei parametri relativi ad ogni costante che è possibile passare al metodo OpenSchema la trovate qui: W3Schools - OpenSchema Per quello che ci riguarda, ovvero tirar fuori la lista dei campi relativi ad una tabella ci basta questo:
Ovviamente non è il nostro intento, e considerato che abbiamo a disposizione il nome della tabella della quale intendiamo sapere i campi, è opportuno preparare un Array da passare ad OpenSchema per ottenere quelli presenti nella tabella di nostro interesse. L'array è presente nella routine seguente e si chiama Criteri. Esso viene valorizzato con 3 parametri dei quali i primi 2 come Empty (ovvero l'elenco dei campi non verrà filtrato nè per TABLE_CATALOG nè per TABLE_SCHEMA) ma contiene, nel terzo parametro, il nome della tabella passato attraverso l'argomento FromTable della Routine. Non è necessario indicare il 4° parametro che verrà valutato anch'esso come Empty: Sub RetrieveFields(FromTable As String)
Analogamente a quanto avviene per la ListView delle tabelle anche per i campi abbiamo ottenuto, oltre ai loro nomi, delle informazioni aggiuntive.Dim itmX As MSComctlLib.ListItem Dim rsFields As ADODB.Recordset Dim Criteri As Variant Criteri = Array(Empty, Empty, FromTable) Set rsFields = cn.OpenSchema(adSchemaColumns, Criteri) If Not (rsFields.BOF And rsFields.EOF) Then While Not rsFields.EOF Set itmX = lwFields.ListItems.Add(, , rsFields.Fields("COLUMN_NAME").Value) itmX.ListSubItems.Add , , ConvertTypeCode(rsFields.Fields("DATA_TYPE").Value) itmX.ListSubItems.Add , , rsFields.Fields("CHARACTER_MAXIMUM_LENGTH").Value & "" rsFields.MoveNext Wend End If rsFields.Close Set rsFields = Nothing Set itmX = Nothing End Sub La prima è DATA_TYPE che altro non è che la tipizzazione del campo del DB. Il valore restituito è una costante numerica che effettivamente, a meno di non avere la memoria di Salomone, dice davvero poco sull'effettiva natura del dato presente nel campo. Per ottenere una risposta maggiormente indicativa possiamo realizzare una semplice Function che restituisce una stringa con il nome della costante passata come argomento. Function ConvertTypeCode(c As Integer) As String
Al posto dei nomi delle costanti è ovviamente possibile farsi restituire dalla Function qualsiasi testo ci aiuti a capire meglio il tipo di contenuto del Campo.Select Case c Case adDBTimeStamp ConvertTypeCode = "adDBTimeStamp" Case adArray ConvertTypeCode = "adArray" Case adBigInt ConvertTypeCode = "adBigInt" Case adBinary ConvertTypeCode = "adBinary" Case adSingle ConvertTypeCode = "adSingle" Case adNumeric ConvertTypeCode = "adNumeric" Case adLongVarWChar ConvertTypeCode = "adLongVarWChar" Case adLongVarChar ConvertTypeCode = "adLongVarChar" Case adLongVarBinary ConvertTypeCode = "adLongVarBinary" Case adIUnknown ConvertTypeCode = "adIUnknown" Case adInteger ConvertTypeCode = "adInteger" Case adIDispatch ConvertTypeCode = "adIDispatch" Case adGUID ConvertTypeCode = "adGUID" Case adError ConvertTypeCode = "adError" Case adEmpty ConvertTypeCode = "adEmpty" Case adDouble ConvertTypeCode = "adDouble" Case adDecimal ConvertTypeCode = "adDecimal" Case adDBTimeStamp ConvertTypeCode = "adDBTimeStamp" Case adDBTime ConvertTypeCode = "adDBTime" Case adDBDate ConvertTypeCode = "adDBDate" Case adDate ConvertTypeCode = "adDate" Case adCurrency ConvertTypeCode = "adCurrency" Case adChar ConvertTypeCode = "adChar" Case adBSTR ConvertTypeCode = "adBSTR" Case adByRef ConvertTypeCode = "adByRef" Case adBoolean ConvertTypeCode = "adBoolean" Case adSmallInt ConvertTypeCode = "adSmallInt" Case adTinyInt ConvertTypeCode = "adTinyInt" Case adUnsignedBigInt ConvertTypeCode = "adUnsignedBigInt" Case adUnsignedInt ConvertTypeCode = "adUnsignedInt" Case adUnsignedSmallInt ConvertTypeCode = "adUnsignedSmallInt" Case adUnsignedTinyInt ConvertTypeCode = "adUnsignedTinyInt" Case adBoolean ConvertTypeCode = "adBoolean" Case adUserDefined ConvertTypeCode = "adUserDefined" Case adVarBinary ConvertTypeCode = "adVarBinary" Case adVarChar ConvertTypeCode = "adVarChar" Case adVariant ConvertTypeCode = "adVariant" Case adVector ConvertTypeCode = "adVector" Case adVarWChar ConvertTypeCode = "adVarWChar" Case adWChar ConvertTypeCode = "adWChar" End Select End Function La seconda informazione che intendiamo rilevare dal Campo è la lunghezza massima consentita (valevole per i campi di tipo testo), per cui possiamo analizzare il valore contenuto in CHARACTER_MAXIMUM_LENGTH Anche in questo caso, come per le Tabelle è possibile sapere molte più informazioni sullo stato di un campo, e basta utilizzare altri nomi di campo, tra quelli qui sotto elencati, disponibili nel Recordset restituito da OpenSchema:
TABLE_CATALOG
Siamo giunti alla conclusione e la nostra piccola applicazione dovrebbe essere funzionante, eseguendo le operazioni per le quali l'abbiamo creata. Per scoprire se è vero non ci resta che lanciarla con F5 e caricare un DB premendo sul pulsante con i tre puntini [...]TABLE_SCHEMA TABLE_NAME COLUMN_NAME COLUMN_GUID COLUMN_PROPID ORDINAL_POSITION COLUMN_HASDEFAULT COLUMN_DEFAULT COLUMN_FLAGS IS_NULLABLE DATA_TYPE TYPE_GUID CHARACTER_MAXIMUM_LENGTH CHARACTER_OCTET_LENGTH NUMERIC_PRECISION NUMERIC_SCALE DATETIME_PRECISION CHARACTER_SET_CATALOG CHARACTER_SET_SCHEMA CHARACTER_SET_NAME COLLATION_CATALOG COLLATION_SCHEMA COLLATION_NAME DOMAIN_CATALOG DOMAIN_SCHEMA DOMAIN_NAME DESCRIPTION
Il risultato non è male e abbiamo appena scalfito la superficie del vasto mondo di OpenSchema, ma la metodologia di base qui illustrata è valevole per l'esplorazione degli ulteriori e talvolta più complessi aspetti di un Database. Una delle caratteristiche che si possono implementare in maniera quasi indolore, ad esempio, è la rilevazione delle Query presenti nel DB (delle quali è possibile sapere anche la frase SQL che le definisce) o ancora le chiavi primarie relative ad una determinata Tabella oppure i suoi Indici... le possibilità sono moltissime. In area Download -> Software potete scaricare il progetto di esempio realizzato per illustrare il metodo OpenSchema. 9月13日 Sezione Modelli 3D - Aggiunti 3 ModelliAggiunti 3 nuovi modelli nella sezione 3D Models, di cui uno (la bottiglia di Heineken) completo di Textures già mappate. Potete scaricarli liberamente accedendo all'area Download -> Modelli 3D. 9月6日 Aggiornata sezione Modelli 3DHo inserito 4 nuovi modelli 3D: Potete scaricarli liberamente accedendo all'area Download -> Modelli 3D. 9月5日 RemindMe!Non so voi, ma io ho il bordo del monitor strapieno di piccole etichette adesive che mi stanno a "ricordare che devo ricordarmi" di fare qualcosa, o di chiamare qualcuno, o chissà che altro! Dunque è nato RemindMe! ![]() E' nato un po' di tempo fa, ma considerato che ho deciso di includere questo programmino tra i programmi scaricabili dalla sezione Software, ve lo presento brevemente. Ho deciso di realizzare questo programmino più che altro per passatempo... ce ne sono molti dello stesso tipo in rete, ma ho voluto cimentarmi lo stesso con questa mia realizzazione. La versione del programma è la 0.1beta e mi piacerebbe se qualcuno di voi potesse provarlo comunicandomi cosa ne pensa e segnalandomi gli inevitabili problemi Visto che il programma attualmente è sprovvisto di Help, vi fornisco una panoramica generale delle sue funzioni, invitandovi a chiedermi "lumi" nel caso abbiate qualche dubbio sul funzionamento delle varie opzioni o funzioni. Il programma, una volta avviato, risede nella TrayBar, ed è possibile accedere alle sue funzioni tramite il menu contestuale che appare quando si clicca col pulsante destro sull'icona. ![]() è possibile aggiungere nuove note dall'apposita voce presente nel menu contestuale della TrayBar, oppure attivando la finestra di Gestione delle Note. ![]() Attraverso questa finestra è possibile gestire tutti gli aspetti di ogni singola nota creata, ovvero:
![]() E' possibile, inoltre, variare anche il colore della nota, attraverso una finestra di selezione del colore personalizzata ![]() ed impostare un allarme per ricevere un avviso ad uno specifico giorno ed orario. ![]() Ciascuna delle azioni su descritte è impostabile direttamente da ogni Nota presente sul Desktop, per cui senza la necessità di aprire la finestra di gestione delle note. E' possibile anche variare l'opacità delle note, per renderle semi-trasparenti, facendo in modo che esse siano meno invasive possibile occupando parte del desktop. Ogni Nota è dimensionabile separatamente, ma è possibile decidere una dimensione di Default che varrà applicata a tutte le Note create successivamente. Questo è possibile dalla finestra delle Opzioni: ![]() Attraverso questa finestra si potranno variare le impostazioni di Default che verranno applicate alle note di futura creazione, ovvero:
Spero di non aver tralasciato nulla nell'esposizione di questa panoramica. Nel caso vogliate provare questo piccolo software potete scaricarlo da qui
8月31日 Nuova Sezione - Download Modelli 3DData la mia passione per la modellazione 3D, durante la creazione delle mie scene (alcune delle quali potete vedere nell'album 3D Gallery), mi servono degli oggetti 3D che la quasi totalità delle volte realizzo da me. Data la difficoltà di reperire in rete oggetti free che siano anche dotati di un discreto livello d dettaglio, ho deciso di mettere a disposizione, periodicamente, alcuni dei modelli facenti parte del mio archivio. Per adesso ho già inserito 2 modelli che è possibile scaricare liberamente, ma spero di poter aumentare, nel tempo, la collezione di modelli a disposizione. E' gradito un cenno sul loro utilizzo da parte di quanti li riterranno meritevoli di far parte dei loro progetti. Potete accedere all'area dei modelli cliccando dall'apposito tasto nella sezione Download in basso a destra in Home Page oppure qui: ![]() 8月27日 "Making Of" del progetto "Retrò Rocket"Ultimamente ho partecipato, sul sito di 3DAttack, ad una specie di mini-contest di modellazione grafica che viene chiamato "Sudden Attack". In pratica viene dato un tema, spesso è un oggetto, e bisogna realizzare una scena modellata, texturizzata ed illuminata, approfittando di un lasso di tempo relativamente breve. Più o meno 4 giorni. Per l'ultimo Sudden Attack il tema proposto era un Retrò Rocket, ovvero uno di quei razzi con forme improponibili somiglianti molto più spesso a giocattoli che a razzi veri. Cercando in rete delle references per la modellazione del soggetto principale mi sono imbattuto proprio in un razzo giocattolo, con una forma piuttosto simpatica, da qui è nata l'idea di inserire il Rocket in un contesto che lo facesse assomigliare il più possibile ad un giocattolo. Ecco la reference sulla quale mi sono basato con accanto il modello che ho realizzato: ![]() Il modello è realizzato con la tecnica del Box Modeling, ovvero costruito prendendo come base di partenza un solido semplce (nel mio caso un cilindro), sul quale si effettuano tagli, estrusioni, ridimensionamenti o spostamenti di poligoni, o punti. L'oggetto finale ha un numero relativamente basso di poligoni e verrà inserito in un HyperNurbs (come quasi tutti i miei modelli). L'HyperNurbs ha la caratteristica di "smussare" il modello, aumentandone la suddivisione dei poligoni e questo permette di avere dei modelli "lowpoly", semplici da gestire e veloci da renderizzare per le prove, ma dei quali è possibile aumentarne la definizione prima del render finale. ![]() L'idea che avevo in mente era quella di inserire il "giocattolo" in uno scaffale, condividendo lo spazio con altri oggetti. Il successivo passo, quindi, è stato quello di realizzare la scaffalatura. Ne basta una piccola porzione... giusto la parte che rientrerà nell'inquadratura. ![]() Creato il modello contenitore della scena, ho dimensionato adeguatamente il razzo. La scena, a questo punto, come si potrà immaginare era decisamente spoglia, per cui ho deciso di riempirla con alcuni oggetti che normalmente si potrebbero trovare su uno scaffale della cameretta dei ragazzi. Dato il poco tempo a disposizione, piuttosto che modellare altri giocattoli che avrebbero richiesto più tempo del consentito e "rubato" la scena al soggetto principale, ho optato per qualche scatola e dei dischetti. ![]() Un razzo va nello spazio... si sa. Come fare lo spazio nell'angolino di uno scaffale? Un poster! Va da se, che nello spazio ci sono anche i pianeti (Grazie Francesco Il poster della volta celeste è stato realizzato con un piano e un paio di deformatori per realizzare le pieghe, mentre per i pianeti sono bastate delle semplici sfere. Ho "attaccato" il poster e i "pianeti" allo scaffale con fili realizzati con delle Splines dentro una SweepNUrbs e con dello scotch, realizzato anch'esso con delle Splines, ma questa volta utilizzando una LoftNurbs. Poster e pianeti sono stati texturizzati utilizzando delle texture trovate in rete, opportunamente modificate per lo scopo con Photoshop. La fase di modellazione è terminata, non volevo rischiare che la scena fosse troppo piena, distraendo l'occhio con troppi particolari. Adesso non resta che assemblare tutti gli oggetti nella scena in modo che la disposizione sia armonica ed equilirata, quindi si passa alla fase di illuminazione e inquadratura. ![]() Per le luci ho immaginato un set piuttosto semplice. Un'illuminazione a 2 punti disposti ai lati della camera, con uguale intensità, ma con distanze diverse dal soggetto. Questo tipo di illuminazione garantisce delle ombre più preponderanti dalla parte opposta alla luce più vicina, ammorbidite dalla luce più distante.
Tutte le luci utilizzate sono di tipo Area che fornicono delle ombre più aderenti alla realtà e, oltre alle 2 già menzionate, ho inserito 3 ulteriori luci - sempre area, ma con forma sferica - in corrispondenza di ciascuno dei 3 pianeti. 7月10日 Estrarre Extended Properties da un File in Visual Basic 6Mi piacerebbe condividere con voi una funzione che ho realizzato per rispondere ad una richiesta posta nella Sezione Visual Basic 6 su MasterDrive.it. La richiesta puntava a conoscere una metodologia per rilevare, da un qualsiasi file, in linguaggio VB6, le informazioni contenute nel riepilogo di Esplora Risorse di Windows, ovvero i cosiddetti Metadata o Extended File Properties. In realtà per ottenere le classiche informazioni come Data Ultimo Accesso, Ultima Modifica e Dimensione, esiste l'efficiente e performante API GetFileInformationByHandle oppure il FileSyestemObject, ma la metodologia che sto per descrivere permette di avere indietro molte più informazioni come Proprietario, Autore, Titolo oppure Artista, Titolo album, Anno, Numero brano, Genere, Durata, etc. per i file audio, o ancora Modello fotocamera, Data immagine scattata, Formato per le foto, e molte altre informazioni. Ci si avvarrà di un oggetto Shell creato via codice, utile per stabilire un "contatto" con il sistema, facendoci restituire tutte le proprietà che è possibile riscontrare in un qualsiasi file presente nel nostro sistema. Per comodità ho preferito incapsulare il codice necessario al reperimento delle informazioni in una Function in modo che sia possibile dichiararla in un qualsiasi modulo .bas sfruttandone le potenzialità da qualsiasi punto del codice. Utilizzeremo 3 variabili oggetto "chiave" atte a contenere i riferimenti agli oggetti necessari: Shell, Folder e Item. Servirà, inoltre, un Array per conenere i nomi delle Proprietà che ci interessa ottenere dal File. In realtà non sarebbe necessario, visto che le proprietà (circa 40) sono indicizzabili numericamente, ma considerato che la Function che realizzeremo restituirà una Collection, è opportuno inserire come chiave di ognuna delle proprietà il corrispondente testuale, per richiamarle con maggiore naturalezza all'interno del codice. Ovviamente questo discorso andrebbe a cadere nel caso in cui il nostro programma dovesse girare su un sistema non italiano, poichè la descrizione di queste proprietà è strettamente legata alla lingua del sistema. In questo caso, converrebbe affidarsi esclusivamente all'indice numerico. Il principio di funzionamento è il seguente: vengono dichiarati gli oggetti Shell, Folder e Item, l'array dei nomi delle proprietà e la Collection Dim oShell As Object
Dim oFolder As Object Dim oItem As Object Dim arrProps(41) Dim tmpCol As New Collection A questo punto assegnamo gli oggetti, creando l'oggetto shell e assegnando l'oggetto folder attraverso di esso: Set oShell = CreateObject("Shell.Application")
Set oFolder = oShell.NameSpace(sPath & "") sPath è una delle variabili che la funzione prenderà in ingresso. Popoliamo l'array delle proprietà ciclando, attraverso un indice, le proprietà disponibili: For i = 0 To 40
Questo è il momento in cui possiamo popolare la Collection con i valori delle proprietà precedentemente immagazzinate nell'array, utilizzando un ciclo For Each oItem selezionando solo il nome del File che ci interessa e che avremo passato alla Function attraverso il parametro sFilenamearrProps(i) = oFolder.GetDetailsOf(oFolder.Items, i) Next For Each oItem In oFolder.Items
If LCase(oItem.Name) = LCase(sFileName) Then For i = 0 To 40 If arrProps(i) <> "" Then tmpCol.Add oFolder.GetDetailsOf(oItem, i), arrProps(i) End If Next Exit For End If Next Assegnamo la collection popolata risultante alla Funcion per restituirla al codice chiamante Set FileProperties = tmpCol
e annulliamo tutti gli oggetti utilizzati per restituire le risorse al sistema Set tmpCol = Nothing
Set oFolder = Nothing Set oShell = Nothing Questa è la Function completa: Public Function FileProperties(sPath As String, sFileName As String) As Collection
Dim oShell As Object Dim oFolder As Object Dim oItem As Object Dim arrProps(41) Dim tmpCol As New Collection Set oShell = CreateObject("Shell.Application") Set oFolder = oShell.NameSpace(sPath & "") For i = 0 To 40 arrProps(i) = oFolder.GetDetailsOf(oFolder.Items, i) Next For Each oItem In oFolder.Items If LCase(oItem.Name) = LCase(sFileName) Then For i = 0 To 40 If arrProps(i) <> "" Then tmpCol.Add oFolder.GetDetailsOf(oItem, i), arrProps(i) End If Next Exit For End If Next Set FileProperties = tmpCol Set tmpCol = Nothing Set oFolder = Nothing Set oShell = Nothing End Function Si potrà utilizzare la function in qualsiasi punto del codice con una semplice riga, ma facendo attenzione al fatto che servirà una Collection di appoggio per ricevere il risultato: Dim clProps As Collection
Set clProps = FileProperties("C:\TuaCartella\TuaSottoCartella\", "TuoFile.xyz") Una volta ottenuta la collection è possibile "interrogarla" con la chiave relativa alla proprietà da analizzare: Debug.Print clProps("Autore")
Debug.Print clProps("Data Creazione") oppure, ancora meglio, per evitare problemi con i sistemi in lingua differente, con un indice numerico: Debug.Print clProps(7)
Debug.Print clProps(15) In un sistema Italiano, l'elenco delle chiavi delle proprietà ottenibili è questo: Nome
Dimensione Tipo Data ultima modifica Data creazione Data ultimo accesso Attributi Stato Proprietario Autore Titolo Oggetto Categoria Pagine Commenti Copyright Artista Titolo album Anno Numero brano Genere Durata Velocità in bit Protetto Modello fotocamera Data immagine scattata Formato Titolo puntata Descrizione programma Dimensioni campione audio Velocità campione audio Canali Società Descrizione Versione file Nome prodotto Versione del prodotto Parole chiave 6月6日 DBExplorerMi capita spesso di avere a che fare con la realizzazione di un software che utilizzi dei Database Access. Spesso, durante la programmazione, è necessario analizzare le tabelle del Database, per rendersi conto di quale Campo si trova in quale Tabella, o magari con che tipo di dati è stato dichiarato quel Campo... insomma, ci si trova ad aprire e chiudere la finestra di Access, molte volte! Le cose si complicano, ancora di più, se si utilizzano diversi Database per un unico programma edi è necessario analizzarli contemporaneamente!Ho pensato, quindi, di realizzare un piccolo tool che è in grado di aprire diversi Database Access, anche contemporaneamente, analizzarne la struttura e suddividerla in un diagramma ad albero mostrandone per ogni Database, le Tabelle (con i rispettivi Campi, Chiavi Primarie e Indici), le Query e le Procedure. Il programma consente anche di visualizzare i dati contenuti nelle Tabelle e nelle Query eventualmente già presenti nel Database, inoltre, per le Query, può mostrare anche la stringa SQL utilizzata. Per i Campi invece può mostrare il tipo di formato dei dati contenuti, e l'eventuale lunghezza di caratteri se si tratta di un campo Testo.
Per facilitare la consultazione della struttura dei DB è presente anche una funzione di stampa con anteprima a video, che permette di elencare le Tabelle e, opzionalmente, di stamparne anche tutti i Campi dipendenti.
Per facilitare al programmatore la consultazione del Database e per il fatto che il programma dovrebbe essere utilizzato durante la digitazione del codice, ho ritenuto che, al pari di Access, sarebbe stato scomodo cambiare continuamente finestra per consultare la struttura del DB. Allo scopo è stata introdotta anche un'interfaccia ridotta, una sorta di Side-Bar, in modo che la finestra, decisamente più piccola della di quella principale, possa essere allineata a destra o a sinistra dello schermo, e posizionata opzionalmente "AlwaysOnTop". Con l'interfaccia ridotta, quindi, lo spazio occupato è minore, e risulta più agevole lavorare. Ancora minore è lo spazio occupato se la SideBar viene richiusa come un menù a tendina tramite l'apposito pulsante.
Altra caratteristica saliente del tool è rappresentata dal Query Tester. Questo piccolo tool integrato nell'applicazione consente di comporre una query di selezione utilizzando come riferimento il Database selezionato, usando le funzioni SQL standard catalogate in un diagramma ad albero presente nella stessa finestra. Una volta creata la Query si può vederne il risultato nella griglia presente sul Form.
Un piccolo aiuto per i programmatori VB, consiste nella possibilità di copiare la Query creata, in modo che sia possibile incollarla così com'è nel codice, senza doversi preoccupare di suddividerla in stringhe concatenate... in pratica questa operazione è compiuta dal programma tramite un semplice click sull'apposito pulsante, e questo è il risultato: Query = "SELECT DISTINCTROW [Order Details].OrderID, [Order Details].ProductID, " & _
"Products.ProductName, [Order Details].UnitPrice, [Order " & _ "Details].Quantity, [Order Details].Discount, CCur([Order " & _ "Details].[UnitPrice]*[Quantity]*(1-[Discount])/100)*100 AS " & _ "ExtendedPrice FROM Products INNER JOIN [Order Details] ON " & _ "Products.ProductID = [Order Details].ProductID ORDER BY [Order " &_ "Details].OrderID;" Purtroppo, al momento, il programma è sprovvisto di Help, ma credo che in fin dei conti, l'utilizzo sia abbastanza intuitivo. Conto comunque di realizzarlo, prima o poi. Potete prelevare il programma da qui:
6月5日 MasterDrive.it Meeting 2007Si è concluso da qualche giorno il Si è svolto a Prato, ho partecipato con piacere e, nell'occasione, ho avuto modo di conoscere delle persone simpaticissime che fino a poco tempo fa conoscevo solo per qualche intervento sul Forum, o da qualche chiacchiera in MSN. Eventi come questo "umanizzano" un po' una community web, e permettono di apprezzare anche il lato personale degli utenti oltre a quello prettamente tecnico. Un'occasione per discutere di argomenti frivoli, prendere decisioni sulle migliorie da apportare alla Community e, perchè no, farsi anche una buona mangiata approfittando dell'ottima cucina toscana! L'organizzazione logistica è stata impeccabile, per cui non posso che complimentarmi con Alextyx (Alessandro) e Bottomap (Matteo) che, essendo del luogo, sono riusciti a trovare un posto eccellente per riunirci: Villa Fiorelli. Per chi volesse dare un'occhiata, pubblico in Gallery alcune delle foto scattate al Meeting. 5月27日 TheTruster's Box Starts Here!Eccomi qui. Ho deciso di aprire finalmente il mio spazio. Non credo sia tanto per moda... si creano blog da così tanto tempo, che ormai potrei essere definito anche piuttosto retrogrado, non di certo trendy! In ogni caso, ho deciso di ritagliarmi questo piccolo spazio nel web, senza pretese di alti contenuti, nè aggiornamenti continui; solo un piccolo cassetto dove depositare, ogni tanto, qualche pensiero, se mai sarà meritevole di essere condiviso, o qualche immagine prodotta con il mio software di modellazione 3D preferito (Cinema 4D) oppure qualche pezzetto di codice Visual Basic 6 prodotto dal basso della mia esperienza. La grafica e la programmazione sono due tra le mie passioni quindi, quale miglior modo di condividerle col mondo? Spero apprezzerete ciò che avrò intenzione di condividere con voi, cari i miei navigatori del web... e lasciate un commento, o voi ch'entrate! |
|
|