JavascriptProva

venerdì 23 novembre 2018

Aggiustamenti del foglio Excel dopo avervi incollato la tabella di Word

Una volta incollata la tabella da Word, si fanno due tipi di aggiustamenti:
  1. il testo, che non va più messo a capo
  2. le celle, che vanno espanse a seconda del testo contenuto.
Ecco, divisa in due, la Sub che ne deriva:
Sub aggiustamentipostincollaggio()
'
' aggiustamentipostincollaggio Macro
' Macro registrata il 24/11/2018 da Windows User
'

'
    Columns("D:D").Select
    With Selection
        .WrapText = False
        .Orientation = 0
        .AddIndent = False
        .ShrinkToFit = False
        .ReadingOrder = xlContext
        .MergeCells = False
    End With
    Columns("D:D").EntireColumn.AutoFit
End Sub

Ora bisogna ridurre tutte le celle-firma.
Semplificando il risultato della registrazione della macro, dovrebbe restare questo:
Sub formattacellefirma()
'
' formattacellefirma Macro
' Macro registrata il 24/11/2018 da Windows User
'

'
    Columns("E:AH").Select
    Selection.ColumnWidth = 2.43
End Sub
Ora provo tutte le funzioni da capo.
In fondo, sono solo tre!
Sì, funzionano alla grande!

Impostiamo la pagina per copiare la tabella su Excel

Vado su Excel.
Incollo tutta la tabella di sana pianta.
Sub impostapagina()
'
' impostapagina Macro
' Macro registrata il 24/11/2018 da Windows User
'

'
    With ActiveSheet.PageSetup
        .PrintTitleRows = ""
        .PrintTitleColumns = ""
    End With
    ActiveSheet.PageSetup.PrintArea = ""
    With ActiveSheet.PageSetup
        .LeftHeader = ""
        .CenterHeader = ""
        .RightHeader = ""
        .LeftFooter = ""
        .CenterFooter = ""
        .RightFooter = ""
        .LeftMargin = Application.InchesToPoints(0.787401575)
        .RightMargin = Application.InchesToPoints(0.787401575)
        .TopMargin = Application.InchesToPoints(0.984251969)
        .BottomMargin = Application.InchesToPoints(0.984251969)
        .HeaderMargin = Application.InchesToPoints(0.5)
        .FooterMargin = Application.InchesToPoints(0.5)
        .PrintHeadings = False
        .PrintGridlines = False
        .PrintComments = xlPrintNoComments
        .CenterHorizontally = False
        .CenterVertically = False
        .Orientation = xlLandscape
        .Draft = False
        .PaperSize = xlPaperA4
        .FirstPageNumber = xlAutomatic
        .Order = xlDownThenOver
        .BlackAndWhite = False
        .Zoom = False
        .FitToPagesWide = 1
        .FitToPagesTall = 1
        .PrintErrors = xlPrintErrorsDisplayed
    End With
End Sub
Una quantità enorme di informazioni quando sarebbe sufficiente soltanto la parte marcata in rosso...

giovedì 22 novembre 2018

Codice delle macro per l'inserimento di tabelle in Word (Word 2003)

Ecco: questa la devo fare in tempi rapidi.
Creare una tabella di 35 colonne e 31 righe.
Questo comando dà al foglio un orientamento orizzontale.
ActiveDocument.PageSetup.Orientation = wdOrientLandscape
e ho trovato anche l'inverso, quello che lo riporta verticale:
ActiveDocument.PageSetup.Orientation = wdOrientPortrait.

Comunque il foglio mi serve Landscape.

Ora tracciamo la tabella tutta a celle uguali. Ne inizio la registrazione cercando di ricordare le modalità applicate prima a causa del "mouse invalido" che si crea durante la registrazione delle macro...

Ecco tutta la macro:
Sub tabella()
'
' tabella Macro
' Macro registrata il 23/11/2018 da Windows User
'
    ActiveDocument.Tables.Add Range:=Selection.Range, NumRows:=31, NumColumns _
        :=35, DefaultTableBehavior:=wdWord9TableBehavior, AutoFitBehavior:= _
        wdAutoFitWindow
    With Selection.Tables(1)
        If .Style <> "Griglia tabella" Then
            .Style = "Griglia tabella"
        End If
        .ApplyStyleHeadingRows = True
        .ApplyStyleLastRow = True
        .ApplyStyleFirstColumn = True
        .ApplyStyleLastColumn = True
    End With
End Sub
Cominciamo a spezzettarla...

Ecco, ho individuato un codice minimo che aggiunge una tabella a un foglio:
Sub tabella()
'
' tabella Macro
' Macro registrata il 23/11/2018 da Windows User
'
    ActiveDocument.Tables.Add Range:=Selection.Range, NumRows:=31, NumColumns _
        :=35, DefaultTableBehavior:=wdWord9TableBehavior, AutoFitBehavior:= _
        wdAutoFitWindow

End Sub
Devo vedere cosa significa DefaultTableBehavior...

Rivedo il codice completo:
Sub tabella()
'
' tabella Macro
' Macro registrata il 23/11/2018 da Windows User
'
    ActiveDocument.Tables.Add Range:=Selection.Range, NumRows:=31, NumColumns _
        :=35, DefaultTableBehavior:=wdWord9TableBehavior, AutoFitBehavior:= _
        wdAutoFitWindow
    With Selection.Tables(1)
        If .Style <> "Griglia tabella" Then
            .Style = "Griglia tabella"
        End If
        .ApplyStyleHeadingRows = True
        .ApplyStyleLastRow = True
        .ApplyStyleFirstColumn = True
        .ApplyStyleLastColumn = True
    End With
End Sub
Vediamo il form per la creazione della tabella dove c'è la scelta fra varie opzioni, alle quali sicuramente si riferisce gran parte del codice "non essenziale".



Mi pare che avevo scelto "Adatta alla pagina", ma adesso ci vado in modo sistematico: andiamo con queste impostazioni di default:
    ActiveDocument.Tables.Add Range:=Selection.Range, NumRows:=2, NumColumns:= _
        5, DefaultTableBehavior:=wdWord9TableBehavior, AutoFitBehavior:= _
        wdAutoFitFixed
    With Selection.Tables(1)
        If .Style <> "Griglia tabella" Then
            .Style = "Griglia tabella"
        End If
        .ApplyStyleHeadingRows = True
        .ApplyStyleLastRow = True
        .ApplyStyleFirstColumn = True
        .ApplyStyleLastColumn = True
    End With
Ora modifico "Larghezza fissa colonne" che porto da "Auto" a 1.5:
    ActiveDocument.Tables.Add Range:=Selection.Range, NumRows:=2, NumColumns:= _
        5, DefaultTableBehavior:=wdWord9TableBehavior, AutoFitBehavior:= _
        wdAutoFitFixed
    With Selection.Tables(1)
        .Columns.PreferredWidth = CentimetersToPoints(1.5)
        If .Style <> "Griglia tabella" Then
            .Style = "Griglia tabella"
        End If
        .ApplyStyleHeadingRows = True
        .ApplyStyleLastRow = True
        .ApplyStyleFirstColumn = True
        .ApplyStyleLastColumn = True
    End With


Ora modifico la seconda voce: scelgo "Adatta al contenuto":
    ActiveDocument.Tables.Add Range:=Selection.Range, NumRows:=2, NumColumns:= _
        5, DefaultTableBehavior:=wdWord9TableBehavior, AutoFitBehavior:= _
        wdAutoFitContent
    With Selection.Tables(1)
        If .Style <> "Griglia tabella" Then
            .Style = "Griglia tabella"
        End If
        .ApplyStyleHeadingRows = True
        .ApplyStyleLastRow = True
        .ApplyStyleFirstColumn = True
        .ApplyStyleLastColumn = True
    End With
Mentre in quelle precedenti era:
    ActiveDocument.Tables.Add Range:=Selection.Range, NumRows:=2, NumColumns:= _
        5, DefaultTableBehavior:=wdWord9TableBehavior, AutoFitBehavior:= _
        wdAutoFitFixed
Scegliendo l'opzione "Adatta alla pagina":
    ActiveDocument.Tables.Add Range:=Selection.Range, NumRows:=2, NumColumns:= _
        5, DefaultTableBehavior:=wdWord9TableBehavior, AutoFitBehavior:= _
        wdAutoFitWindow
    With Selection.Tables(1)
        If .Style <> "Griglia tabella" Then
            .Style = "Griglia tabella"
        End If
        .ApplyStyleHeadingRows = True
        .ApplyStyleLastRow = True
        .ApplyStyleFirstColumn = True
        .ApplyStyleLastColumn = True
    End With
Ecco.
Schiacciando "Griglia tabella" si apre questa finestra:



...nella quale sono comprese queste istruzioni:
    ActiveDocument.Tables.Add Range:=Selection.Range, NumRows:=2, NumColumns:= _
        5, DefaultTableBehavior:=wdWord9TableBehavior, AutoFitBehavior:= _
        wdAutoFitFixed
    With Selection.Tables(1)
        If .Style <> "Tabella a colori 1" Then
            .Style = "Tabella a colori 1"
        End If
        .ApplyStyleHeadingRows = True
        .ApplyStyleLastRow = True
        .ApplyStyleFirstColumn = True
        .ApplyStyleLastColumn = True
    End With
Ormai è quasi tutto chiaro.

venerdì 9 novembre 2018

Salvataggio floatingView e removeView (immagini in overlay)

Le strade si dividono: da una parte, l'apprendimento teorico di tutto il sistema delle floatingViews, dall'altra le modifiche funzionali alla mia applicazione N-S.
Salvo la MainActivity, notevole specialmente per il "trucchetto" che evita di dover riavviare l'applicazione dopo i permessi a runtime:
public class MainActivity extends AppCompatActivity {


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Runnable runnable = new Runnable() {
            @RequiresApi(api = Build.VERSION_CODES.M)
            @Override
            public void run() {
                if(Settings.canDrawOverlays(getApplicationContext())){
                    Intent i = new Intent(getApplicationContext(),MainActivity.class);
                    i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
                    startActivity(i);
                }else{
                    new Handler().postDelayed(this,1000);
                }
            }
        };


        if(Build.VERSION.SDK_INT >= 23 ){
            if(!Settings.canDrawOverlays(getApplicationContext())){
                Intent i = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:"+getPackageName()));
                startActivity(i);
                new Handler().postDelayed(runnable,1000);
            }
        }

        Intent intent=new Intent(getApplicationContext(),Servizio.class);
        startService(intent);

    }

}


Salvo il codice dello studio teorico:
public class Servizio extends Service {
    int x_init_coord, y_init_coord, x_margin, y_margin;
    long time_start, time_end;
    private View mFloatingView;
    private View removeView;
    private WindowManager windowManager;
    private WindowManager.LayoutParams wParams;

    public Servizio() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public void onCreate() {
        super.onCreate();


        windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);

        addFloatingWidgetView();
        addRemoveWidgetView();



    }

    private void addFloatingWidgetView() {
        mFloatingView = LayoutInflater.from(this).inflate(R.layout.widget, null);

        wParams = new WindowManager.LayoutParams(
                WindowManager.LayoutParams.WRAP_CONTENT,
                WindowManager.LayoutParams.WRAP_CONTENT,
                WindowManager.LayoutParams.TYPE_PHONE,
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
                PixelFormat.TRANSLUCENT
        );

        wParams.gravity = Gravity.TOP | Gravity.LEFT;
        wParams.x = 0;
        wParams.y = 100;

        windowManager.addView(mFloatingView, wParams);


        mFloatingView.setOnTouchListener(new View.OnTouchListener() {
            int x_init_coord, y_init_coord;
            int x_margin, y_margin;
            @Override
            public boolean onTouch(View view, MotionEvent event) {
                WindowManager.LayoutParams layoutParams = (WindowManager.LayoutParams) mFloatingView.getLayoutParams();
                int x_coord = (int) event.getRawX();
                int y_coord = (int) event.getRawY();
                switch(event.getAction()){
                    case MotionEvent.ACTION_DOWN:

                        time_start = System.currentTimeMillis();

                        x_margin = layoutParams.x;
                        y_margin = layoutParams.y;
                        x_init_coord = x_coord;
                        y_init_coord = y_coord;
                        return true;
                    case MotionEvent.ACTION_MOVE:
                        int x_diff = x_coord - x_init_coord;
                        int y_diff = y_coord - y_init_coord;
                        int x_destination = x_margin + x_diff;
                        int y_destination = y_margin + y_diff;
                        layoutParams.x = x_destination;
                        layoutParams.y = y_destination;
                        windowManager.updateViewLayout(view,layoutParams);
                        return true;
                    case MotionEvent.ACTION_UP:
                        int x_diff_up = x_coord - x_init_coord;
                        int y_diff_up = y_coord - y_init_coord;
                        if(Math.abs(x_diff_up) < 5  && Math.abs(y_diff_up) < 5){
                            time_end = System.currentTimeMillis();
                            if(time_end - time_start < 300){
                                System.out.println("E' UN CLICK");
                            }
                        }
                }
                return false;
            }
        });
    }




    private void addRemoveWidgetView(){

        LayoutInflater inflater = (LayoutInflater)getSystemService(LAYOUT_INFLATER_SERVICE);
        removeView = inflater.inflate(R.layout.removewidget,null);

        WindowManager.LayoutParams removeParams = new WindowManager.LayoutParams(
                WindowManager.LayoutParams.WRAP_CONTENT,
                WindowManager.LayoutParams.WRAP_CONTENT,
                WindowManager.LayoutParams.TYPE_PHONE,
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
                PixelFormat.TRANSLUCENT
        );
        removeParams.gravity = Gravity.TOP | Gravity.LEFT;

        windowManager.addView(removeView,removeParams);



    }


}


Salvo i due xml che vengono "inflatati" per la floatingView e per la removeView:

floatingVIew:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">

    <!--Root container-->
    <RelativeLayout
        android:id="@+id/root_container"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        tools:ignore="UselessParent">

        <ImageView
            android:adjustViewBounds="true"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src = "@drawable/facciadascemo"/>


    </RelativeLayout>

</FrameLayout> 


removeView:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">

    <!--Root container-->
    <RelativeLayout
        android:id="@+id/root_container"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        tools:ignore="UselessParent">

        <ImageView
            android:adjustViewBounds="true"
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:src = "@drawable/removeimage"/>


    </RelativeLayout>

</FrameLayout> 

giovedì 8 novembre 2018

Codice essenziale per una floating image in overlay in android

Ecco ricostruito il codice per muovere una floating image in overlay:
        mFloatingView.setOnTouchListener(new View.OnTouchListener() {
            int x_init_coord, y_init_coord;
            int x_margin, y_margin;
            @Override
            public boolean onTouch(View view, MotionEvent event) {
                WindowManager.LayoutParams layoutParams = (WindowManager.LayoutParams) mFloatingView.getLayoutParams();
                int x_coord = (int) event.getRawX();
                int y_coord = (int) event.getRawY();
                switch(event.getAction()){
                    case MotionEvent.ACTION_DOWN:
                        x_margin = layoutParams.x;
                        y_margin = layoutParams.y;
                        x_init_coord = x_coord;
                        y_init_coord = y_coord;
                        return true;
                    case MotionEvent.ACTION_MOVE:
                        int x_diff = x_coord - x_init_coord;
                        int y_diff = y_coord - y_init_coord;
                        int x_destination = x_margin + x_diff;
                        int y_destination = y_margin + y_diff;
                        layoutParams.x = x_destination;
                        layoutParams.y = y_destination;
                        windowManager.updateViewLayout(view,layoutParams);
                        return true;
                }
                return false;
            }
        });

lunedì 5 novembre 2018

Codice per un service con una floatingView in overlay

Ecco tutto il codice del Service che fa apparire una floating view in overlay:
public class Servizio extends Service {
    int deltaX, deltaY;
    private View mFloatingView;
    private WindowManager windowManager;
    private WindowManager.LayoutParams wParams;
    public Servizio() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public void onCreate() {
        super.onCreate();
        windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
        wParams = new WindowManager.LayoutParams(
                WindowManager.LayoutParams.WRAP_CONTENT,
                WindowManager.LayoutParams.WRAP_CONTENT,
                WindowManager.LayoutParams.TYPE_PHONE,
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
                PixelFormat.TRANSLUCENT
        );

        wParams.gravity = Gravity.TOP | Gravity.LEFT;
        wParams.x=300;
        wParams.y=600;

        mFloatingView=LayoutInflater.from(this).inflate(R.layout.widget,null);

        windowManager.addView(mFloatingView,wParams);


        mFloatingView.setOnTouchListener(new View.OnTouchListener() {


            @Override
            public boolean onTouch(View view, MotionEvent event) {

                switch(event.getAction()){
                    case MotionEvent.ACTION_DOWN:

                        deltaX = (int) event.getRawX() - wParams.x;
                        deltaY = (int) event.getRawY() - wParams.y;

                        return true;

                    case MotionEvent.ACTION_MOVE:
                        wParams.x =(int)event.getRawX()-deltaX;
                        wParams.y = (int)event.getRawY()-deltaY;
                        windowManager.updateViewLayout(mFloatingView,wParams);
                        return true;
                }
                return false;
            }
        });
        


    }
}
Questo codice viene chiamato dall'activity principale in questo modo:
        Intent intent=new Intent(getApplicationContext(),Servizio.class);
        startService(intent);

Per completezza, scrivo per intero il codice della MainActivity in modo da mostrare il codice per i permessi a runtime del disegno in overlay (non valido e quindi ignorato, come già mostrato nel video) in un dispositivo con API < 23.
public class MainActivity extends AppCompatActivity {


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Runnable runnable = new Runnable() {
            @RequiresApi(api = Build.VERSION_CODES.M)
            @Override
            public void run() {
                if(Settings.canDrawOverlays(getApplicationContext())){
                    Intent i = new Intent(getApplicationContext(),MainActivity.class);
                    i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
                    startActivity(i);
                }else{
                    new Handler().postDelayed(this,1000);
                }
            }
        };


        if(Build.VERSION.SDK_INT >= 23 ){
            if(!Settings.canDrawOverlays(getApplicationContext())){
                Intent i = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:"+getPackageName()));
                startActivity(i);
                new Handler().postDelayed(runnable,1000);
            }
        }

        Intent intent=new Intent(getApplicationContext(),Servizio.class);
        startService(intent);

    }

}
Il codice è quello marcato in bianco su blu.

domenica 4 novembre 2018

Ancora sul trucco per non riavviare l'applicazione dopo i permessi a runtime per l'overlay

Views in overlay.
Con l'occasione, ripasso il "trucco" per non dover riavviare il programma una volta concesso il permesso a runtime per gli overlay.

Iniziamo da qui:
public class MainActivity extends AppCompatActivity {
    
    Intent i;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

    }

}
Ora, devo inserire il codice per i permessi.
Innanzitutto non deve mancare mai il classico permesso nel manifest!
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.antonello.studiooverlay">
    <uses-permission android:name="android.permission.ACTION_MANAGE_OVERLAY_PERMISSION" />
    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name" 


Quindi ci sono i permessi a runtime.
Approfitto dell'occasione per ripassare un po' i "meccanismi" della cosa...

Se l'API è uguale o superiore alla 23, se contemporaneamente non è impostato il permesso di Overlay (!Settings.canDrawOverlays(getApplicationContext())), si crea un intent avente per parametri ACTION_MANAGE_OVERLAY_PERMISSION e Uri.parse(...col nome del package...).
Questo dovrebbe bastare a cambiare le carte in tavola dopo aver chiamato startActivity(intent).
Però sempre dopo aver riavviato l'applicazione.

Ecco il codice:
        if(Build.VERSION.SDK_INT >= 23 ){
            if(!Settings.canDrawOverlays(getApplicationContext())){
                Intent i = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:"+getPackageName()));
                startActivity(i);
            }
        }
Ora applichiamo il "trucco".
public class MainActivity extends AppCompatActivity {


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Runnable runnable = new Runnable() {
            @RequiresApi(api = Build.VERSION_CODES.M)
            @Override
            public void run() {
                if(Settings.canDrawOverlays(getApplicationContext())){
                    Intent i = new Intent(getApplicationContext(),MainActivity.class);
                    i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
                    startActivity(i);
                }else{
                    new Handler().postDelayed(this,1000);
                }
            }
        };


        if(Build.VERSION.SDK_INT >= 23 ){
            if(!Settings.canDrawOverlays(getApplicationContext())){
                Intent i = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:"+getPackageName()));
                startActivity(i);
                new Handler().postDelayed(runnable,1000);
            }
        }

    }

}
Ora realizzo un video con due emulatori aventi rispettivamente API 22 e API 23 per metterne in luce le differenze di comportamento.

Rappresentazione dei dati di un database in un DataGridView (parte iniziale)

Dunque riepilogando: mi conviene stare dietro alle tecniche più nuove per la gestione dei database.
Per prima cosa devo creare la coppia String e Connection, in quanto la connection si serve della string per connettersi al database.
Ecco:
        Dim connString As String = "Provider = Microsoft.Jet.OLEDB.4.0; data source = C:\users\antonello\documents\anto.mdb"
        Dim conn As New OleDbConnection(connString)
Questo funziona.
Ora devo vedere bene come si gestiscono i comandi.
Il command accetta due parametri: una query e la connessione, che deve però essere già stata inizializzata con la stringa di connessione.
Quindi, prima creiamo la connessione con la stringa di connessione per parametro, quindi il comando con per parametri la query e la connessione.
        Dim conn As OleDbConnection
        Dim cString As String = "Provider = Microsoft.Jet.OLEDB.4.0; data source=C:\users\antonello\Documents\anto.mdb"
        conn = New OleDbConnection(cString)
        Dim command As New OleDbCommand("select * from pazienti", conn)
O anche:
        Dim conn As OleDbConnection
        Dim cString As String = "Provider = Microsoft.Jet.OLEDB.4.0; data source=C:\users\antonello\Documents\anto.mdb"
        conn = New OleDbConnection(cString)
        Dim query As String = "select * from pazienti"
        Dim command As New OleDbCommand(query, conn)
Per un migliore aiuto mnemonico:
        Dim conn As OleDbConnection
        Dim cString As String = "Provider = Microsoft.Jet.OLEDB.4.0; data source=C:\users\antonello\Documents\anto.mdb"
        conn = New OleDbConnection(cString)


        Dim query As String = "select * from pazienti"
        Dim command As New OleDbCommand(query, conn)

Ora leggiamo i dati che ci sono nel database, mediante la query.
Per fare questo c'è un oggetto OLEDBDataReader.
        Dim reader As OleDbDataReader
        reader = command.ExecuteReader
Per mezzo di questo reader si possono elencare le voci presenti nella tabella.
Però, mentre il command, (e ovviamente la connessione) può essere inizializzato senza che la connessione sia aperta, il reader, che esegue un'azione, richiede sempre la connessione aperta, altrimenti genera un errore:
        conn.Open()

        Dim reader As OleDbDataReader
        reader = command.ExecuteReader


        While (reader.Read)
            MsgBox(reader.GetString(1))
        End While

        conn.Close()

In questo modo ottengo una serie di MessageBoxes contenenti le stringhe del campo n.1.

Ora andiamo avanti e creiamo un DataAdapter, che avrebbe la funzione di prendere tutti i dati del database (forse quelli selezionati dalla query...).

giovedì 1 novembre 2018

Ancora sui form php.

Nel file denominato alla voce "action" si processa l'informazione.

Il form è questo:
<form action=pagina.php method = "POST">
    <input type="text" name = "nome">
    <input type="text" name = "cognome">
    <input type= "submit" value = "Mannalo affanculo">
</form> 
e la pagina è:
<?php
echo $_POST;
?>
No! In questo caso ottengo solo:
Array
Invece la pagina deve essere così, come se $_POST fosse un array i cui elementi siano quelli chiamati con "nome" e "cognome" (come li ho chiamati io nel form, avrei potuto chiamarli pure "Pasquale" e "Cicciobello".
<?php
echo $_POST["nome"];
echo $_POST["cognome"];
?>
Vediamo adesso...
cicciofatticcio
Ecco, mi sono venuti attaccati perché non ho messo nessuno spazio vuoto fra le due risposte.
Vediamo come viene trasmessa l'informazione mediante il metodo GET:
<form action=pagina.php method = "GET">
    
    
    
</form>
La barra degli indirizzi:
https://trigon.xyz/pagina.php?nome=ciccio&cognome=fatticcio
La pagina è vuota perché vengono richieste variabili di tipo POST.
Provo a richiederle GET:
<?php
echo $_GET["nome"];
echo $_GET["cognome"];
?>
La barra degli indirizzi è sempre così:
https://trigon.xyz/pagina.php?nome=ciccio&cognome=bello
E la pagina risultante è:
cicciobello

Ripasso del PHP. Ricominciamo dai form

Per il ripasso del PHP, cominciamo dai forms.
Questo è quel poco che ricordo dei forms:
<form action="POST" >
    

</form> 

Vediamo come si ampliano...

<form action=pagina.php method = "POST">
    
⁢/form>
Ecco, con action si intende la pagina di destinazione.Il Method è, per quanto ricordo, la possibilità di far apparire sulla barra degli indirizzi i dati o no...

Vediamo il contenuto del form...

<form action=pagina.php method = "POST">
    <input type="text" name = "nome">
    <input type="text" name = "cognome">
    <input type= "submit" value = "Mannalo affanculo">
</form> 
Che fa questo form? Assolutamente una minchia, suppongo...

Infatti, ecco il risultato:



Mi sembra evidente che manchi la pagina "pagina.php".
Ora la creo. Vuota. Per vedere l'effetto che fa!

E infatti appare una pagina completamente BIANCA!
Diamo uno sguardo pure alla barra degli indirizzi:
Questa è quella che mi appare adesso:
https://trigon.xyz/pagina.php


Ma proviamo a cambiare il method con "GET":
https://trigon.xyz/pagina.php?nome=Ciccio&cognome=Molliccio
Appare anche il contenuto degli "input".

mercoledì 24 ottobre 2018

Lettura delle tabelle di un database come recordset per mezzo delle librerie DAO da VB.NET

Ecco, anche l'apertura dei recordset e la loro rappresentazione presenta qualche differenza. I codici con il punto esclamativo non mi hanno funzionato.
E' comunque possibile fare ugualmente:
    Private Sub apri()
        Dim engine As DBEngine = New DBEngine
        Dim db As Database
        db = engine.OpenDatabase("C:\users\antonello\documents\anto.mdb")
        Dim rs As Recordset = db.OpenRecordset("pazienti")
        rs.MoveFirst()
        Do While Not rs.EOF

            MsgBox(rs.Fields("nome").Value)
            rs.MoveNext()
        Loop
    End Sub

Creazione di un database a runtime con le librerie DAO

Un bel ripasso dei database gestiti con le librerie DAO in VB.NET.
C'è qualche differenza con la sintassi che conoscevo dall'antico VB6, ma niente di insuperabile, almeno finora:
Imports DAO

Public Class Form1
    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Dim engine = New DBEngine
        Dim db As Database
        db = engine.OpenDatabase("C:\users\antonello\bikongo.mdb")
        Dim tabella As TableDef = db.CreateTableDef("Tabella1")


        tabella.Fields.Append(tabella.CreateField("id", DAO.DataTypeEnum.dbInteger))
        tabella.Fields.Append(tabella.CreateField("nome", DAO.DataTypeEnum.dbText))
        tabella.Fields.Append(tabella.CreateField("cognome", DAO.DataTypeEnum.dbText))
        db.TableDefs.Append(tabella)

    End Sub
End Class
Questa è la creazione di una tabella con tre campi, uno numerico e due di testo.

lunedì 22 ottobre 2018

Creazione di una custom view in Android

Costruzione di una customview in android.
L'avevo vista e capita, e poi ho scordato praticamente tutto.

Ho un file xml che ho chiamato my_view.xml, e un file java che ho chiamato myView.java.
Analizziamo questo file.
public class MyView extends RelativeLayout {
    TextView testo1;
    TextView testo2;
    public MyView(Context context) {
        super(context);
        initView(context);
    }

    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);

        initView(context);
    }

    public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        initView(context);
    }

    private void initView(Context context){
        inflate(context,R.layout.my_view,this);

    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        testo1=(TextView)findViewById(R.id.testo1);
        testo2=(TextView)findViewById(R.id.testo2);
        this.setBackgroundColor(Color.CYAN);
    }

    public void saluta(){
        testo1.setText("BUONGIORNO");
        testo2.setText("BUONASERA");
    }
}
Le variaabili dichiarate a livello di classe sono due variabili oggetto per le due TextView che saranno contenute nella view, ossia testo1 e testo2.
public class MyView extends RelativeLayout {
    TextView testo1;
    TextView testo2;
Quindi vengono chiamati tutti i costruttori della classe genitore RelativeLayout.
    public MyView(Context context) {
        super(context);
        initView(context);
    }

    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);

        initView(context);
    }

    public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        initView(context);
    }
In tutti e tre i costruttori viene chiamato il metodo initView.

Il metodo initView è questo:
    private void initView(Context context){
        inflate(context,R.layout.my_view,this);

    }
in pratice quello che "inflata" il layout specificato in xml entro questo oggetto.
Quindi c'è onFinishInflate, che dovrebbe essere il metodo che entra in azione una volta che l'"inflatazione" sia terminata, in modo che alle variabili oggetto vengano attribuite le views presenti nell'xml.

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        testo1=(TextView)findViewById(R.id.testo1);
        testo2=(TextView)findViewById(R.id.testo2);
        this.setBackgroundColor(Color.CYAN);
    }

Una volta terminata l'inflatazione, si possono creare i metodi, come appunto questo:
    public void saluta(){
        testo1.setText("BUONGIORNO");
        testo2.setText("BUONASERA");
    }
Il file xml è così:
<?xml version="1.0" encoding="utf-8"?>
<merge
    xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/testo1"
        android:layout_alignParentTop="true"
        android:layout_alignParentStart="true"
        android:layout_marginTop="100dp"
        android:layout_marginStart="100dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <TextView
        android:id="@+id/testo2"
        android:layout_alignParentTop="true"
        android:layout_alignParentStart="true"
        android:layout_marginTop="150dp"
        android:layout_marginStart="100dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</merge> 

Il tutto va poi inserito in un'applicazione:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <com.example.antonello.studiocustomview.MyView
        android:id="@+id/myView"
        android:layout_width="200dp"
        android:layout_height="200dp" />



</RelativeLayout> 
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        MyView myView = (MyView)findViewById(R.id.myView);
        myView.saluta();
    }
}
Ecco, adesso credo di aver rinfrescato a sufficienza il modo di creare delle CustomViews.

domenica 22 luglio 2018

Ripasso di OpenFileDialog

Public Class Form1
    Dim stringa As String
    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load


    End Sub

    Private Sub TextBox1_TextChanged(sender As Object, e As EventArgs) Handles TextBox1.TextChanged

    End Sub

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        OpenFileDialog1.Filter = "Files HTML|*.html"
        OpenFileDialog1.FileName = ""
        If OpenFileDialog1.ShowDialog() = DialogResult.OK Then
            TextBox1.Text = OpenFileDialog1.FileName
            Dim stringa As String = OpenFileDialog1.FileName
            TextBox1.Text = splitPath(stringa)
            TextBox2.Text = splitFileName(stringa)
        End If
    End Sub

    Private Function splitFileName(s As String) As String
        Return s.Substring(s.LastIndexOf("\") + 1, s.Length - 1 - s.LastIndexOf("\"))
    End Function

    Private Function splitPath(s As String) As String
        Return s.Substring(0, s.LastIndexOf("\") + 1)
    End Function
End Class

venerdì 20 luglio 2018

Apertura di un file HTML per la lettura del codice in VB.NET

Ripassiamo l'apertura dei files (in VB6 era completamente diversa, e in VB.NET, pur avendola usata, l'ho dimenticata da un pezzo.
Devo aprire un file html per mostrarne il codice.
Presto fatto:
Imports System.IO
Public Class Form1
    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Dim sr As New StreamReader("C:\users\antonello\documents\prova.html")
        Dim line As String = sr.ReadToEnd()
        TextBox1.Text = line
    End Sub
End Class
Ed ecco il codice nella TextBox:
<html>
<body bgcolor=green>
<div style="background-color:red;color:white">Ciao bestia</div>
</body> 

mercoledì 18 luglio 2018

Individuare la posizione di tutti gli spazi di una stringa (ma va bene per qualunque altro carattere o sottostringa)

Una piccola sfida propostami da mio figlio: individuare tutte le parole una per una in una stringa. Per cominciare, ho provato a individuare la posizione degli spazi fra le parole.
Public Class Form1
    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Dim pos As Integer = -1
        Dim start As Integer = 1
        Dim stringa As String = "gongolo mammolo cucciolo eolo dotto pisolo brontolo"
        Dim stringhetta As String = " "

        While pos <> 0
            If pos <> -1 Then Debug.Print(pos)
            pos = InStr(start, stringa, stringhetta)

            start = pos + 1
        End While
    End Sub
End Class
Risultato:
8
16
25
30
36
43
Ma non è ancora raggiunto lo scopo: vuole che la parola venga colorata durante la digitazione se viene riconosciuta come una particolare parola.
Intanto, deve usare una richTextBox, se ricordo bene... e poi... vedremo.

domenica 24 giugno 2018

Immagine spostabile su video

Creare un controllo immagine in vb, come si fa?
C'è la picturebox.
Studiamola...

La proprietà SizeMode impostata su AutoSize mi sembra la scelta migliore.
Ora devo studiare come renderla mobile. Iniziamo quindi a studiarne le coordinate.

Il principio per rendere mobile un'immagine parte dalla conoscenza delle coordinate del puntatore. Come si conoscono in VB?
Ecco: ho realizzato abbastanza agevolmente una PictureBox spostabile con il puntatore del mouse (ed eventualmente con il dito su tablet)
Public Class Form1
    Dim initX, initY As Integer
    Dim deltaX, deltaY As Integer
    Dim pressed As Boolean = False

    Private Sub PictureBox1_MouseDown(sender As Object, e As MouseEventArgs) Handles PictureBox1.MouseDown
        initX = e.X
        initY = e.Y
        pressed = True
    End Sub

    Private Sub PictureBox1_MouseMove(sender As Object, e As MouseEventArgs) Handles PictureBox1.MouseMove
        If pressed Then
            deltaX = e.X - initX
            deltaY = e.Y - initY
            PictureBox1.Left += deltaX
            PictureBox1.Top += deltaY
        End If
    End Sub

    Private Sub PictureBox1_MouseUp(sender As Object, e As MouseEventArgs) Handles PictureBox1.MouseUp
        pressed = False
    End Sub
End Class

domenica 17 giugno 2018

Tabelle hash: risoluzione delle collisioni con linked list in Visual Basic .NET

Ora la gestione con le liste concatenate può presentare lo stesso ordine di problemi.
Salvo il codice precedente:
Public Class Form1
    Dim nodeArray(366) As Nodo

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        createItem("abc")
        createItem("fds")
        createItem("ghr")
        createItem("uyj")
        createItem("jhk")
        createItem("fgh")
        elenca()
    End Sub
    Sub createItem(stringa As String)
        Dim hf As Integer = hashFunction(stringa)
        If nodeArray(hf) Is Nothing Then
            nodeArray(hf) = New Nodo
            nodeArray(hf).valore = stringa
            nodeArray(hf).prossimo = Nothing
        Else
            Do
                hf = hf + 1
                If hf > UBound(nodeArray) Then Exit Do
                If nodeArray(hf) Is Nothing Then
                    nodeArray(hf) = New Nodo
                    nodeArray(hf).valore = stringa
                    nodeArray(hf).prossimo = Nothing
                    Exit Do
                End If
            Loop

        End If

    End Sub

    Sub elenca()
        For n = 0 To UBound(nodeArray)
            If nodeArray(n) Is Nothing Then
                Debug.Print(n & "---")
            Else
                Debug.Print(n & "---" & nodeArray(n).valore)
            End If
        Next
    End Sub
    Function hashFunction(stringa As String) As Integer
        Dim asciValue As Integer = 0
        For n = 1 To Len(stringa)
            asciValue += Asc(Mid(stringa, n, 1))
        Next
        Return asciValue
    End Function
End Class

Class Nodo
    Public valore As String
    Public prossimo As Nodo
End Class



E ora provo...

Ci vuole sempre una funzione Hash, che può essere anche quella che ho già usato:
    Function hashFunction(stringa As String) As Integer
        Dim asciValue As Integer = 0
        For n = 1 To Len(stringa)
            asciValue += Asc(Mid(stringa, n, 1))
        Next
        Return asciValue
    End Function
End Class
In base a questa, ora, devo modificare questa:
    Sub createItem(stringa As String)
        Dim hf As Integer = hashFunction(stringa)
        If nodeArray(hf) Is Nothing Then
            nodeArray(hf) = New Nodo
            nodeArray(hf).valore = stringa
            nodeArray(hf).prossimo = Nothing
        Else
            Do
                hf = hf + 1
                If hf > UBound(nodeArray) Then Exit Do
                If nodeArray(hf) Is Nothing Then
                    nodeArray(hf) = New Nodo
                    nodeArray(hf).valore = stringa
                    nodeArray(hf).prossimo = Nothing
                    Exit Do
                End If
            Loop

        End If

    End Sub
In base alla funzione hashFunction, dovrei piazzare il valore nel nodo adeguato:
    Sub createItem(stringa As String)
        Dim hf As Integer = hashFunction(stringa)
        If nodeArray(hf) Is Nothing Then
            nodeArray(hf) = New Nodo
            nodeArray(hf).valore = stringa
            nodeArray(hf).prossimo = Nothing
        End If

    End Sub
    Sub elenca()
        For n = 0 To UBound(nodeArray)
            If nodeArray(n) Is Nothing Then
                Debug.Print(n & "---")
            Else
                Debug.Print(n & "---" & nodeArray(n).valore)
            End If
        Next
    End Sub
Tutto come prima: vediamo cosa succede.
294---abc
309---fgh
317---fds
321---ghr
344---uyj
Confrontando con il codice:
        createItem("abc")
        createItem("fds")
        createItem("ghr")
        createItem("uyj")
        createItem("jhk")
        createItem("fgh")
Anche qui c'è la stessa collisione: la funzione hashFunction restituisce 317 per "fds" e per "jhk", che però trovando il posto già occupato non viene inserita nella tabella.
Ora dobbiamo gestire però la creazione di liste concatenate per poter piazzare anche questo valore.
Dovrebbe cominciare dalla funzione createItem...
Se il nodo è già occupato va creato un altro nodo e impostato come "prossimo" del primo.

Ecco trovata la soluzione:
Public Class Form1
    Dim nodeArray(366) As Nodo

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        createItem("abc")
        createItem("fds")
        createItem("ghr")
        createItem("uyj")
        createItem("jhk")
        createItem("fgh")
        elenca()
    End Sub
    Sub createItem(stringa As String)
        Dim hf As Integer = hashFunction(stringa)
        Dim tail As Nodo, temp As Nodo

        'se il nodo base non è istanziato, si istanzia e gli si dà il valore.
        If nodeArray(hf) Is Nothing Then
            nodeArray(hf) = New Nodo
            nodeArray(hf).valore = stringa
            nodeArray(hf).prossimo = Nothing
        Else
            'se il nodo base è istanziato, si dà al nodo base istanziato il nome tail (in quanto è la coda della fila)
            tail = nodeArray(hf)
            Do
                'se non esiste un nodo prossimo al nodo tail... si istanzia un nuovo nodo chiamato temp...
                If tail.prossimo Is Nothing Then
                    temp = New Nodo
                    'e gli si danno i valori.
                    temp.valore = stringa
                    temp.prossimo = Nothing

                    'si pone questo nodo temp appena istanziato come prossimo rispetto al nodo tail
                    tail.prossimo = temp

                    'si dà a questo nuovo nodo il nome di tail.
                    tail = temp
                    Exit Do
                Else
                    'se invece esiste il nodo prossimo, allora questo diventerà il nuovo tail.
                    tail.prossimo = tail

                End If

            Loop


        End If

    End Sub

    Sub elenca()
        For n = 0 To UBound(nodeArray)
            'se il nodo non è istanziato si scriverà solo il numero
            If nodeArray(n) Is Nothing Then
                Debug.Print(n & "---")
            Else
                'se il nodo è istanziato, si scriverà il valore
                Debug.Print(n & "---" & nodeArray(n).valore)

                'e bisogna cercare se ci sono altri nodi nella linked list
                Dim tail As Nodo
                tail = nodeArray(n)
                Do
                    If Not tail.prossimo Is Nothing Then
                        tail = tail.prossimo
                        Debug.Print(n & "---" & tail.valore)

                    Else
                        Exit Do
                    End If
                Loop
            End If
        Next
    End Sub
    Function hashFunction(stringa As String) As Integer
        Dim asciValue As Integer = 0
        For n = 1 To Len(stringa)
            asciValue += Asc(Mid(stringa, n, 1))
        Next
        Return asciValue
    End Function
End Class

Class Nodo
    Public valore As String
    Public prossimo As Nodo
End Class
Ed ecco i due valori che erano entrati in collisione, rappresentati su un'unica colonna con gli altri (non ho voluto cimentarmi per una rappresentazione migliore)
294---abc
309---fgh
317---fds
317---jhk
321---ghr
344---uyj
Confronto:
        createItem("abc")
        createItem("fds")
        createItem("ghr")
        createItem("uyj")
        createItem("jhk")
        createItem("fgh")
Perfetto! I due elementi appaiono con lo stesso numero in quanto sono parte di una lista concatenata.
Ora creo intenzionalmente altri elementi il cui risultato della funzione hashFunction sia 317 e vediamo...

        createItem("abc")
        createItem("fds")
        createItem("ghr")
        createItem("uyj")
        createItem("jhk")
        createItem("fgh")
        createItem("ddu")
        createItem("dax")
        createItem("uce")
E scopro che il programma si blocca.
Dopo qualche incazzatura, trovo la fonte dell'errore.
    Sub createItem(stringa As String)
        Dim hf As Integer = hashFunction(stringa)
        Dim tail As Nodo, temp As Nodo

        temp = New Nodo
        temp.valore = stringa
        temp.prossimo = Nothing

        'se il nodo base non è istanziato, si istanzia e gli si dà il valore.
        If nodeArray(hf) Is Nothing Then
            nodeArray(hf) = temp
        Else
            'se il nodo base è istanziato, si dà al nodo base istanziato il nome tail (in quanto è la coda della fila)
            tail = nodeArray(hf)
            Do
                'se non esiste un nodo prossimo al nodo tail... si istanzia un nuovo nodo chiamato temp...
                If tail.prossimo Is Nothing Then
                    tail.prossimo = temp
                    Exit Do
                Else
                    'se invece esiste il nodo prossimo, allora questo diventerà il nuovo tail.
                    tail.prossimo = tail
                End If
            Loop
        End If
    End Sub
Una volta che il programma vede che un nodo è stato già istanziato, deve ripetere il ciclo usando questo come nodo tail, ma tail.prossimo = tail non attribuisce il nome tail al prossimo nodo, ma fa diventare il nodo successivo uguale a quello che attualmente è tail. Correggendo l'errore, il problema si risolve (ho impostato come oggetto iniziale sub Main credendo erroneamente che il pfoblema stesse nel caricamento del form ma va bene lo stesso).
Module Module1
    Dim nodeArray(366) As Nodo
    Sub main()
        createItem("abc")
        createItem("fds")
        createItem("ghr")
        createItem("uyj")
        createItem("jhk")
        createItem("fgh")
        createItem("ddu")
        createItem("dax")
        createItem("uce")
        elenca()
    End Sub

    Sub createItem(stringa As String)
        Dim hf As Integer = hashFunction(stringa)
        Dim tail As Nodo, temp As Nodo

        temp = New Nodo
        temp.valore = stringa
        temp.prossimo = Nothing

        'se il nodo base non è istanziato, si istanzia e gli si dà il valore.
        If nodeArray(hf) Is Nothing Then
            nodeArray(hf) = temp
        Else
            'se il nodo base è istanziato, si dà al nodo base istanziato il nome tail (in quanto è la coda della fila)
            tail = nodeArray(hf)
            Do
                'se non esiste un nodo prossimo al nodo tail... si istanzia un nuovo nodo chiamato temp...
                If tail.prossimo Is Nothing Then
                    tail.prossimo = temp
                    Exit Do
                Else
                    'se invece esiste il nodo prossimo, allora questo diventerà il nuovo tail.
                    tail = tail.prossimo
                End If
            Loop
        End If
    End Sub

    Sub elenca()
        For n = 0 To UBound(nodeArray)
            'se il nodo non è istanziato si scriverà solo il numero
            If nodeArray(n) Is Nothing Then
                Debug.Print(n & "---")
            Else
                'se il nodo è istanziato, si scriverà il valore
                Debug.Print(n & "---" & nodeArray(n).valore)

                'e bisogna cercare se ci sono altri nodi nella linked list
                Dim tail As Nodo
                tail = nodeArray(n)
                Do
                    If Not tail.prossimo Is Nothing Then
                        tail = tail.prossimo
                        Debug.Print(n & "---" & tail.valore)

                    Else
                        Exit Do
                    End If
                Loop
            End If
        Next
    End Sub
    Function hashFunction(stringa As String) As Integer
        Dim asciValue As Integer = 0
        For n = 1 To Len(stringa)
            asciValue += Asc(Mid(stringa, n, 1))
        Next
        Return asciValue
    End Function


    Class Nodo
        Public valore As String
        Public prossimo As Nodo
    End Class

End Module
Vediamo:
294---abc
309---fgh
317---fds
317---jhk
317---ddu
317---dax
317---uce
321---ghr
344---uyj
Mi sembra perfetto! Tutti quelli marcati in rosso fanno parte della linked list creata per risolvere il problema della collisione!

venerdì 15 giugno 2018

Creazione di una tabella hash elementare in VB con gestione della collisione per mezzo della tecnica "Open addressing"

Bene.
Proseguendo nello studio delle tabelle hash e delle liste concatenate, due cose fondamentali mi servono: una funzione che converta una stringa in un numero, ossia la funzione hash, e poi la creazioni di nodi.
Rivediamo la prima.
Mi serve una funzione per trasformare i caratteri nei loro codici ASCII e poi farne la somma.
Questa l'ho già vista prima ma ora la ripasso.
Come parametro, ci sarà una stringa.
    Sub converti(stringa As String)

    End Sub
Quindi predispongo una matrice dinamica:
    Sub converti(stringa As String)
        Dim caratteri() As Char

    End Sub
Ridimensiono la matrice di caratteri alla lunghezza della stringa (-1 perché la matrice comincia con 0):
    Sub converti(stringa As String)
        Dim caratteri() As Char
        ReDim caratteri(Len(stringa) - 1)
    End Sub
Ora ho una matrice di caratteri di lunghezza uguale a quella della stringa.
Ad ogni elemento della matrice devo far corrispondere un carattere della stringa.
    Sub converti(stringa As String)
        Dim caratteri() As Char
        ReDim caratteri(Len(stringa) - 1)
        For n = 0 To Len(stringa) - 1
            caratteri(n) = Mid(stringa, n, 1)
        Next
    End Sub
Come prova, scrivo nella finestra di Debug i singoli valori della matrice caratteri():
Public Class Form1
    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        converti("ciao")
    End Sub
    Sub converti(stringa As String)
        Dim caratteri() As Char
        ReDim caratteri(Len(stringa) - 1)
        For n = 1 To Len(stringa)
            caratteri(n - 1) = Mid(stringa, n, 1)
        Next
        For n = 0 To UBound(caratteri)
            Debug.Print(caratteri(n))
        Next
    End Sub
e vediamo cosa ne viene fuori, per vedere se abbiamo fatto tutta la procedura corretta:
c
i
a
o
Bene.
Adesso devo calcolare il valore ASCII dei caratteri e sommarli insieme:
    Sub converti(stringa As String)
        Dim asciValue As Integer = 0
        Dim caratteri() As Char
        ReDim caratteri(Len(stringa) - 1)
        For n = 1 To Len(stringa)
            caratteri(n - 1) = Mid(stringa, n, 1)
        Next
        For n = 0 To UBound(caratteri)
            asciValue += Asc(caratteri(n))
        Next
        Debug.Print(asciValue)
    End Sub
Ho predisposto un valore asciValue pari a zero che, ripassando tutti i caratteri della matrice convertiti nel loro valore ASCII vi vengono sommati:
c
i
a
o
412

Ma non si può fare a meno della matrice?
Facciamo un calcolo abbreviato.
    Sub converti(stringa As String)
        Dim asciValue As Integer = 0
        For n = 1 To Len(stringa)
            asciValue += Asc(Mid(stringa, n, 1))
            Debug.Print(Mid(stringa, n, 1))
        Next
        Debug.Print(asciValue)
    End Sub
c
i
a
o
412
Considerando che abbiamo come input una stringa e come output vogliamo sapere il valore della somma dei codici ASCII dei suoi caratteri (possiamo evitare di stampare tutti i caratteri, cosa che ci serviva solo per verificare che avevamo fatto tutto bene), trasformiamo la Sub in una Function:
    Function converti(stringa As String) As Integer
        Dim asciValue As Integer = 0
        For n = 1 To Len(stringa)
            asciValue += Asc(Mid(stringa, n, 1))
        Next
        Return asciValue
    End Function
E verifichiamola:
Public Class Form1
    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Debug.Print(converti("abc"))
    End Sub

    Function converti(stringa As String) As Integer
        Dim asciValue As Integer = 0
        For n = 1 To Len(stringa)
            asciValue += Asc(Mid(stringa, n, 1))
        Next
        Return asciValue
    End Function
End Class
294
...che corrisponde alla somma dei codici ASCII di a,b,c.
Vorrei trovare un nome più consono alla funzione:
Public Class Form1
    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Debug.Print(asciiSum("abc"))
    End Sub

    Function asciiSum(stringa As String) As Integer
        Dim asciValue As Integer = 0
        For n = 1 To Len(stringa)
            asciValue += Asc(Mid(stringa, n, 1))
        Next
        Return asciValue
    End Function
End Class




Ora, in previsione dell'uso di liste concatenate come mezzo per superare le collisioni, dovrei inserire questi valori trovati in una serie di "nodi".
Creiamo la classe Nodo.
Class Nodo
    Public valore As Integer
    Public prossimo As Nodo
End Class
La proprietà valore è quella che ospita i valori asciiSum, ossia quelli ottenuti con la funzione hash. La proprietà prossimo dovrebbe indicare il nodo successivamente concatenato nell'eventualità ce ne fosse bisogno.

Come conciliare la funzione che ho trovato prima con i nodi?
Avrei una matrice di nodi, che però non dovrebbero essere istanziati tutti, bensì solo quelli utilizzati.
Ad esempio, per un asciSum di 294 dovrei istanziare l'elemento 294 dell'array di nodi.
Facciamo una lista di 500 nodi, ossia una matrice di 500 variabili nodo (non istanziate), e una variabile che servirà da index ogni volta che si creerà un nuovo valore.
    Dim nodeArray(500) As Integer
    Dim hashFunction As Integer
Mi sta venendo l'idea di chiamare la funzione, anziché asciValue, hashFunction, a sottolineare la funzione più generale di funzione hash.
Ora creo la funzione che crea un nuovo item:
    Sub createItem(stringa As String)

    End Sub


Ecco scritto il tutto:
Public Class Form1
    Dim nodeArray(366) As Nodo

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        createItem("abc")
        createItem("fds")
        createItem("ghr")
        createItem("uyj")
        createItem("jhk")
        createItem("fgh")
        elenca()
    End Sub
    Sub createItem(stringa As String)
        nodeArray(hashFunction(stringa)) = New Nodo
        nodeArray(hashFunction(stringa)).valore = stringa
        nodeArray(hashFunction(stringa)).prossimo = Nothing
    End Sub

    Sub elenca()
        For n = 0 To UBound(nodeArray)
            If nodeArray(n) Is Nothing Then
                Debug.Print(n & "---")
            Else
                Debug.Print(n & "---" & nodeArray(n).valore)
            End If
        Next
    End Sub
    Function hashFunction(stringa As String) As Integer
        Dim asciValue As Integer = 0
        For n = 1 To Len(stringa)
            asciValue += Asc(Mid(stringa, n, 1))
        Next
        Return asciValue
    End Function
End Class

Class Nodo
    Public valore As String
    Public prossimo As Nodo
End Class
Ed ecco l'output:
0---
1---
2---
3---
4---
5---
6---
7---
8---
9---
10---
11---
12---
13---
14---
15---
16---
17---
18---
19---
20---
21---
22---
23---
24---
25---
26---
27---
28---
29---
30---
31---
32---
33---
34---
35---
36---
37---
38---
39---
40---
41---
42---
43---
44---
45---
46---
47---
48---
49---
50---
51---
52---
53---
54---
55---
56---
57---
58---
59---
60---
61---
62---
63---
64---
65---
66---
67---
68---
69---
70---
71---
72---
73---
74---
75---
76---
77---
78---
79---
80---
81---
82---
83---
84---
85---
86---
87---
88---
89---
90---
91---
92---
93---
94---
95---
96---
97---
98---
99---
100---
101---
102---
103---
104---
105---
106---
107---
108---
109---
110---
111---
112---
113---
114---
115---
116---
117---
118---
119---
120---
121---
122---
123---
124---
125---
126---
127---
128---
129---
130---
131---
132---
133---
134---
135---
136---
137---
138---
139---
140---
141---
142---
143---
144---
145---
146---
147---
148---
149---
150---
151---
152---
153---
154---
155---
156---
157---
158---
159---
160---
161---
162---
163---
164---
165---
166---
167---
168---
169---
170---
171---
172---
173---
174---
175---
176---
177---
178---
179---
180---
181---
182---
183---
184---
185---
186---
187---
188---
189---
190---
191---
192---
193---
194---
195---
196---
197---
198---
199---
200---
201---
202---
203---
204---
205---
206---
207---
208---
209---
210---
211---
212---
213---
214---
215---
216---
217---
218---
219---
220---
221---
222---
223---
224---
225---
226---
227---
228---
229---
230---
231---
232---
233---
234---
235---
236---
237---
238---
239---
240---
241---
242---
243---
244---
245---
246---
247---
248---
249---
250---
251---
252---
253---
254---
255---
256---
257---
258---
259---
260---
261---
262---
263---
264---
265---
266---
267---
268---
269---
270---
271---
272---
273---
274---
275---
276---
277---
278---
279---
280---
281---
282---
283---
284---
285---
286---
287---
288---
289---
290---
291---
292---
293---
294---abc
295---
296---
297---
298---
299---
300---
301---
302---
303---
304---
305---
306---
307---
308---
309---fgh
310---
311---
312---
313---
314---
315---
316---
317---jhk
318---
319---
320---
321---ghr
322---
323---
324---
325---
326---
327---
328---
329---
330---
331---
332---
333---
334---
335---
336---
337---
338---
339---
340---
341---
342---
343---
344---uyj
345---
346---
347---
348---
349---
350---
351---
352---
353---
354---
355---
356---
357---
358---
359---
360---
361---
362---
363---
364---
365---
366---

Ecco: le stringhe da porre nella tabella erano queste:
        createItem("abc")
        createItem("fds")
        createItem("ghr")
        createItem("uyj")
        createItem("jhk")
        createItem("fgh")
mentre quelle ritrovate nella tabella sono:
294---abc
309---fgh
317---jhk
321---ghr
344---uyj
c'è una collisione fra le stringhe "fds" e "jhk", la cui somma è sempre 317
Proviamo con il metodo Open Addressing.
Innanzitutto bisogna vedere come individuare una collisione.
Con questo accorgimento, jhk non dovrebbe sostituire fds.
    Sub createItem(stringa As String)
        If nodeArray(hashFunction(stringa)) Is Nothing Then
            nodeArray(hashFunction(stringa)) = New Nodo
            nodeArray(hashFunction(stringa)).valore = stringa
            nodeArray(hashFunction(stringa)).prossimo = Nothing
        End If

    End Sub
Vediamo...

294---abc
309---fgh
317---fds
321---ghr
344---uyj
Confrontando con la lista precedente:
294---abc
309---fgh
317---jhk
321---ghr
344---uyj
Esatto! Come nelle previsioni!
Ora, individuata la collisione, dove andiamo a mettere quel jhk che la funzione hash ha reso uguale al fds?
Dobbiamo scorrere avanti nella lista fino a trovare uno "slot" vuoto.
Innanzitutto è più comodo modificare così la funzione createItem:
    Sub createItem(stringa As String)
        Dim hf As Integer = hashFunction(stringa)
        If nodeArray(hf) Is Nothing Then
            nodeArray(hf) = New Nodo
            nodeArray(hf).valore = stringa
            nodeArray(hf).prossimo = Nothing
        End If

    End Sub
Quindi...
    Sub createItem(stringa As String)
        Dim hf As Integer = hashFunction(stringa)
        If nodeArray(hf) Is Nothing Then
            nodeArray(hf) = New Nodo
            nodeArray(hf).valore = stringa
            nodeArray(hf).prossimo = Nothing
        Else
            Do
                hf = hf + 1
                If nodeArray(hf) Is Nothing Then
                    nodeArray(hf) = New Nodo
                    nodeArray(hf).valore = stringa
                    nodeArray(hf).prossimo = Nothing
                End If
            Loop

        End If

    End Sub
Aumentiamo il numero di indice della variabile nodo facente parte della matrice nodeArray, fin quando troviamo una variabile non inizializzata, che inizializziamo con il valore rimastoci "vacante".
Proviamo.
Ecco una migliore gestione che evita al ciclo Do... Loop di andare avanti superando i limiti della matrice nodeArray:
Public Class Form1
    Dim nodeArray(366) As Nodo

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        createItem("abc")
        createItem("fds")
        createItem("ghr")
        createItem("uyj")
        createItem("jhk")
        createItem("fgh")
        elenca()
    End Sub
    Sub createItem(stringa As String)
        Dim hf As Integer = hashFunction(stringa)
        If nodeArray(hf) Is Nothing Then
            nodeArray(hf) = New Nodo
            nodeArray(hf).valore = stringa
            nodeArray(hf).prossimo = Nothing
        Else
            Do
                hf = hf + 1
                If hf > UBound(nodeArray) Then Exit Do
                If nodeArray(hf) Is Nothing Then
                    nodeArray(hf) = New Nodo
                    nodeArray(hf).valore = stringa
                    nodeArray(hf).prossimo = Nothing
                    Exit Do
                End If
            Loop

        End If

    End Sub

    Sub elenca()
        For n = 0 To UBound(nodeArray)
            If nodeArray(n) Is Nothing Then
                Debug.Print(n & "---")
            Else
                Debug.Print(n & "---" & nodeArray(n).valore)
            End If
        Next
    End Sub
    Function hashFunction(stringa As String) As Integer
        Dim asciValue As Integer = 0
        For n = 1 To Len(stringa)
            asciValue += Asc(Mid(stringa, n, 1))
        Next
        Return asciValue
    End Function
End Class

Class Nodo
    Public valore As String
    Public prossimo As Nodo
End Class
Ed ecco come è stata sistemata la collisione:
294---abc
309---fgh
317---fds
318---jhk
321---ghr
344---uyj
Ecco, ora "jhk" non sparisce più ma viene "incasellato" con l'indice 318, primo "slot" libero dopo quello con cui si contendeva l'indice.
Confronto con le stringhe da "disporre" nel codice:
        createItem("abc")
        createItem("fds")
        createItem("ghr")
        createItem("uyj")
        createItem("jhk")
        createItem("fgh")
Perfetto!
Ora se io voglio ritrovare il punto in cui si trova la stringa "jhk", come faccio?
Tramite la funzione hash (hashFunction) trovo immediatamente la "locazione" 317. A questo punto faccio un confronto che non mi dà la corrispondenza, in quanto in quella "locazione" io ci trovo "fds". Così con un ciclo "while" scorro le locazioni successive fino a quando il confronto darà esito positivo.
Molto più economico che cercare "jhk" su tutta la matrice nodeArray di sana pianta a cominciare dal primo fino a quando avrò trovato la stringa!

giovedì 10 maggio 2018

Pulsante che attiva sul server il download personalizzato a seconda della transazione.

Ecco: ho creato un file dataselector.php, che ha a che fare con il database.
<?php
$servername="localhost";
$username="XXXX";
$password="XXXX";
$database="id3147737_db";

$conn=new mysqli($servername,$username,$password,$database);

if($conn->connect_error){
    die("Connessione non riuscita" . $conn->connect_error);
}

$sql="SELECT * FROM Transazioni WHERE id=" .$_POST['id'];
$result=$conn->query($sql);
if($result->num_rows >0){
    while($row = $result->fetch_assoc()){
        echo "id: " . $row["id"] . " - nome: " . $row["nome"] . "
"; } }else{ echo "Nessun record trovato"; } $conn->close(); ?>
... e nel file index.php (che in futuro sarà la e-mail mandata al cliente) ci metto il form:
<form action="dataselector.php" method="POST">
<input type="hidden" name="id" value=1>
<input type="submit"> 
Nel file dataselector.php ho scritto così l'istruzione sql:
$sql="SELECT * FROM Transazioni WHERE id=" .$_POST['id'];
in modo da selezionare nel database Transazioni il record dove il campo id equivale a quello inviato dal form sotto forma di input type="hidden" (cosa che un giorno farà la e-mail mandata al cliente per lo scaricamento) E così con un input type="hidden" name="id" value=1 ho ottenuto il valore corrispondente a ID = 1:
id: 1 - nome: Downloads/gerbert.png


Ma basta cambiare il valore dell'input di nome "id":
<form action="dataselector.php" method="POST">
<input type="hidden" name="id" value=2>
<input type="submit"> 
e ottengo:
id: 2 - nome: Downloads/abruzzo.png

e invece con il valore 3:
<form action="dataselector.php" method="POST">
<input type="hidden" name="id" value=3>
<input type="submit"> 
...ottengo:
id: 3 - nome: Downloads/bovino.jpg
Perfetto! Adesso devo trovare il modo migliore per fare il download.

Trovato. il link è questo Ho fatto il copia-incolla e sono riuscito a downloadare i files che avevo nella cartella sul server.
Domani mi studio meglio il codice.

mySQLi: SELECT con clausola WHERE.

<?php
$servername="localhost";
$username="XXXX";
$password="XXXX";
$database="id3147737_db";

$conn=new mysqli($servername,$username,$password,$database);

if($conn->connect_error){
    die("Connessione non riuscita" . $conn->connect_error);
}

$sql="SELECT * FROM Transazioni WHERE id=2";
$result=$conn->query($sql);
if($result->num_rows >0){
    while($row = $result->fetch_assoc()){
        echo "id: " . $row["id"] . " - nome: " . $row["nome"] . "
"; } }else{ echo "Nessun record trovato"; } ?>
E funziona:
id: 2 - nome: Downloads/abruzzo.png


lunedì 7 maggio 2018

Connessione e creazione di tabelle database MySqli in php

Ho individuato il codice giusto per connettersi al database:
<?php

$servername = "localhost";
$username = "XXXXXX";
$password = "XXXXXXXX";
$database = "id3147737_questodatabase";

// Create connection
$conn = new mysqli($servername, $username, $password, $database);

// Check connection
if ($conn->connect_error) {
    die("Connection failed: " . $conn->connect_error);
}
echo "Connected successfully";
?>
Ora dovrei inserire il codice per la creazione di una tabella.

La parola chiave fondamentale sarebbe $msqli_query.

$sql="CREATE TABLE MyGuests (
id INT(6) UNSIGNED AUTO_INCREMENT PRIMARY KEY, 
firstname VARCHAR(30) NOT NULL,
lastname VARCHAR(30) NOT NULL,
email VARCHAR(50),
reg_date TIMESTAMP
)";

if ($conn->query($sql) === TRUE) {
    echo "Table MyGuests created successfully";
} else {
    echo "Error creating table: " . $conn->error;
}

$conn->close();
E' tutto copia-incollato, giusto per vedere se funziona. E in effetti funziona!

mercoledì 2 maggio 2018

Ordinamento degli array indicizzati e associativi in php

Per l'ordinamento degli arrays non ho ben capito la differenza che c'è tra sort() e rsort() da una parte, e asort() e arsort() dall'altra.

Ecco: sort() e rsort() sono due funzioni che servono per gli arrays indicizzati.
Ordinano secondo il valore e basta.
Invece asort() e arsort(), ksort() e krsort() servono per gli arrays associativi.

Torno a creare un array associativo...

Intanto lo creo senza ordinamento:
<?php
$dei = array("Zeus"=>"Fulmine","Ares"=>"Guerra","Afrodite"=>"Amore","Dioniso"=>"Vino","Atena"=>"Sapienza");

foreach($dei as $n=>$value){
    echo "$n : $value<br>";
}

?> 
Zeus : Fulmine
Ares : Guerra
Afrodite : Amore
Dioniso : Vino
Atena : Sapienza



Ora ordino secondo il valore con asort().
<?php
$dei = array("Zeus"=>"Fulmine","Ares"=>"Guerra","Afrodite"=>"Amore","Dioniso"=>"Vino","Atena"=>"Sapienza");
asort($dei);

foreach($dei as $n=>$value){
    echo "$n : $value<br>";
}

?> 
Ho usato asort() che ordina secondo il valore:
Afrodite : Amore
Zeus : Fulmine
Ares : Guerra
Atena : Sapienza
Dioniso : Vino


Ora ordino secondo la chiave con ksort().
<?php
$dei = array("Zeus"=>"Fulmine","Ares"=>"Guerra","Afrodite"=>"Amore","Dioniso"=>"Vino","Atena"=>"Sapienza");
ksort($dei);
foreach($dei as $n=>$value){
    echo "$n : $value<br>";
}

?> 
Afrodite : Amore
Ares : Guerra
Atena : Sapienza
Dioniso : Vino
Zeus : Fulmine
Perfetto!

Array indicizzati e associativi, e costrutti iterativi applicabili, in php

Ci sono dunque tre tipi di array in php:
  • indexed
  • associativo
  • multidimensionale

Cominciamo con gli indexed...

Vado a memoria nel creare un array:
<?php
$nani=array("Mammolo","Gongolo","Cucciolo","Pisolo","Brontolo","Dotto","Eolo");

echo "I nani di Biancaneve sono ".count($nani)."<br>";

echo "I loro nomi sono: ";
for($n=0;$n<count($nani);$n++){
    echo "$nani[$n],";
}

?> 
I nani di Biancaneve sono 7
I loro nomi sono: Mammolo,Gongolo,Cucciolo,Pisolo,Brontolo,Dotto,Eolo,

E ho usato la funzione count() e un ciclo for per contare gli elementi dell'array e navigarci in mezzo.


Ora la cosa più difficile: degli array che non hanno un indice numerico ma una variabile.
Per prima cosa, mi rivedrei i cicli foreach, che qui vanno senz'altro applicati.
Applico foreach a un array indicizzato.
<?php
$matrice=array("uno","due","tre","quattro");

foreach($matrice as $value){
    echo "$value<br>";
}


?> 
uno
due
tre
quattro



Se voglio far apparire anche l'indice:
<?php
$matrice=array("uno","due","tre","quattro");

foreach($matrice as $x=>$value){
    echo "$value<br>";
}
?> 
uno
due
tre
quattro
Così l'indice non appare, ma dal momento che ho eguagliato $matrice a $x=>$value posso far apparire anche l'indice:
<?php
$matrice=array("uno","due","tre","quattro");

foreach($matrice as $x=>$value){
    echo "$x $value<br>";
}
?> 
0 uno
1 due
2 tre
3 quattro
Ecco: eguagliando $matrice a $x=>$value ho compreso in essa anche l'indice.
Stessa cosa posso fare per un array associativo.

Creo un array associativo (in questo tipo di array sarebbe il caso di far apparire anche l'indice)
$dei = array("Zeus"=>"Fulmine","Ares"=>"Guerra","Afrodite"=>"Amore","Dioniso"=>"Vino","Atena"=>"Sapienza");
Ora voglio elencarli tutti, facendo apparire anche l'indice che in questo caso è il nome del dio.
<?php
$dei = array("Zeus"=>"Fulmine","Ares"=>"Guerra","Afrodite"=>"Amore","Dioniso"=>"Vino","Atena"=>"Sapienza");

foreach($dei as $nome=>$competenza){
    echo "$nome: $competenza<br>";
}
Zeus: Fulmine
Ares: Guerra
Afrodite: Amore
Dioniso: Vino
Atena: Sapienza

Ecco, appunto, come volevo.
Il for è applicabile solo agli array indicizzati perché hanno come indice un numero, mentre il foreach è applicabile sia agli array indicizzati che a quelli associativi.

Parametri opzionali in php (con una digressione in Java)

A proposito delle funzioni, il primo argomento che mi salta all'occhio è quello dei parametri opzionali con un valore di default.
In Java non mi pare che esista questa funzione, mentre esiste (se ricordo bene) in C.
No, infatti non esiste.
Andiamo in php:
<?php
function funzione($a,$b=3){
    echo "$a<br>";
    echo $b;
}
funzione(3);
?> 
Il default è 3.
3
3

Se invece metto un secondo parametro diverso dal valore di default:
<?php
function funzione($a,$b=3){
    echo "$a<br>";
    echo $b;
}
funzione(3,5);
?> 
3
5
Funziona!
A questo punto faccio una digressione perché non so come si crei un parametro di default in Java.
Ecco: mediante l'overloading del metodo:
    private void funzione(int parametro1, int parametro2){
        System.out.println(parametro1 + " "+parametro2);

    }

    private void funzione(int parametro1){
        int defaultparam=7;
        funzione(parametro1,defaultparam);
    }
...dato che non esiste la possibilità di creare direttamente parametri opzionali.

Cicli while e for in php (sintassi uguale a quella del Java ecc.)

Cicli While e For in php.

<?php
$contatore=0;
while($contatore < 10){
    echo $contatore;
    $contatore++;
}
?>
0123456789

E così anche per:
<?php
$contatore=0;
do{
    echo $contatore;
    echo "<br>";
    $contatore++;
}while($contatore<10);
?>
0
1
2
3
4
5
6
7
8
9


Ciclo for:
<?php
for($n=0;$n<5;$n++){
    echo("Ciao, bello! <br>");
}
?> 
Ciao, bello! 
Ciao, bello! 
Ciao, bello! 
Ciao, bello! 
Ciao, bello! 

Il ciclo for è praticamente uguale a quello degli altri linguaggi OOP, con la differenza che essendo il php poco tipizzato non bisogna inserire alcun tipo della variabile.

C'è poi il ciclo foreach, che lavora sugli array, e così sarà un'occasione per ripassare gli array.
Creo un array in php:
<?php
$nani=array("Gongolo","Mammolo","Pisolo","Brontolo","Eolo","Dotto","Cucciolo");

foreach($nani as $value){
    echo "$value <br>";
}
?> 
Gongolo 
Mammolo 
Pisolo 
Brontolo 
Eolo 
Dotto 
Cucciolo 

Ricordare sempre che le variabili del php possono andare anche tra virgolette!.

Strutture decisionali in php: verifico se la sintassi è uguale a quella del Java.

Proviamo a vedere se le istruzioni condizionali sono simili a quelle del Java...

Ho una variabile: se la variabile contiene il nome "Mario" verrà scritto "Rossi", se contiene il nome "Ciccio" verrà scritto "Molliccio", se contiene un altro nome verrà scritto "Altro cognome".
<?php
$nome="Ciccio";

if($nome=="Mario"){
    echo "Rossi";
}elseif($nome=="Ciccio"){
    echo "Molliccio";
}else{
    echo "Altro cognome";
}
?> 
Funziona perfettamente!
Ora vediamo con lo switch:
<?php
$nome="Ciccio";

switch($nome){
    case "Mario":
        echo "Rossi";
        break;
    case "Ciccio":
        echo "Molliccio";
        break;
    default:
        echo "Altro cognome";
        break;
}
?> 
E funziona perfettamente anche questo!
La sintassi è uguale a quella del Java.

Costanti in php

Le costanti in php...
Vediamole!

La parola chiave è define.
Creo una costante e la visualizzo fuori e dentro una funzione (le costanti dovrebbero essere visibili dappertutto).
<?php
define("PAROLACCIA","Vaffanculo");
echo PAROLACCIA;
echo "<br>";
function funzione(){
    echo PAROLACCIA." dentro la funzione";
}
funzione();
?> 
Vaffanculo
Vaffanculo dentro la funzione

Perfetto!

Ora faccio il confronto con una variabile.
<?php
define("PAROLACCIA","Vaffanculo");
$variabile="Stronzo";
echo PAROLACCIA;
echo "<br>";
echo $variabile;
echo "<br>";
function funzione(){
    echo PAROLACCIA." dentro la funzione";
    echo "<br>";
    echo $variabile;
}
funzione();
?>
Vaffanculo
Stronzo
Vaffanculo dentro la funzione

Notice: Undefined variable: variabile in /storage/ssd3/737/3147737/public_html/index.php on line 11
Per rendere leggibile la variabile (ripasso) ho due possibilità:

parola chiave global:
<?php
define("PAROLACCIA","Vaffanculo");
$variabile="Stronzo";
echo PAROLACCIA;
echo "<br>";
echo $variabile;
echo "<br>";
function funzione(){
    global $variabile;
    echo PAROLACCIA." dentro la funzione";
    echo "<br>";
    echo $variabile;
}
funzione();
?>
Vaffanculo
Stronzo
Vaffanculo dentro la funzione
Stronzo



attingendo all'array $GLOBALS.
<?php
define("PAROLACCIA","Vaffanculo");
$variabile="Stronzo";
echo PAROLACCIA;
echo "<br>";
echo $variabile;
echo "<br>";
function funzione(){
    echo PAROLACCIA." dentro la funzione";
    echo "<br>";
    echo $GLOBALS['variabile'];
}
funzione();
?>
Vaffanculo
Stronzo
Vaffanculo dentro la funzione
Stronzo

Perfetto!
Resta da vedere quel "case insensitive"...

Provo a scrivere in minuscolo il nome della costante:
<?php
define("PAROLACCIA","Vaffanculo");
echo PAROLACCIA;
echo "<br>";
function funzione(){
    echo parolaccia." dentro la funzione";
}
funzione();
?> 
E ottengo:
Vaffanculo

Notice: Use of undefined constant parolaccia - assumed 'parolaccia' in /storage/ssd3/737/3147737/public_html/index.php on line 6
parolaccia dentro la funzione

Ora definisco la costante con un true come terzo parametro, che rende il nome case insensitive:
<?php
define("PAROLACCIA","Vaffanculo",true);
echo PAROLACCIA;
echo "<br>";
function funzione(){
    echo parolaccia." dentro la funzione";
}
funzione();
?>
Vaffanculo
Vaffanculo dentro la funzione

ed ecco che il nome della costante non è più case sensitive ma si può scrivere anche in minuscolo.

martedì 1 maggio 2018

Alcune operazioni sulle stringhe in php.

Devo passare per le stringhe.
Vediamo... se ricordo bene, ci sono delle funzioni per contare il numero di caratteri di una stringa e il numero di parole contenute in essa.
strlen()
str_word_count()
Provo ad applicarle...

<?php
$stringa="Ciao bello vai a quel paese";
var_dump($stringa);
?>
string(27) "Ciao bello vai a quel paese"

Applichiamo le funzioni di cui sopra:
<?php
$stringa="Ciao bello vai a quel paese";
var_dump($stringa);
echo "<br>";
echo "Numero di caratteri della stringa ".strlen($stringa);
echo "<br>";
echo "Numero di parole della stringa ".str_word_count($stringa);
?> 
string(27) "Ciao bello vai a quel paese" 
Numero di caratteri della stringa 27
Numero di parole della stringa 6

Perfetto!
C'è anche una funzione che inverte la stringa, strrev().
Più una curiosità, direi... Proviamo:
<?php
$stringa="Ciao bello vai a quel paese";

echo "<br>";
echo "Numero di caratteri della stringa ".strlen($stringa);
echo "<br>";
echo "Numero di parole della stringa ".str_word_count($stringa);
echo "<br>";
echo "Stringa al contrario ".strrev($stringa);
?> 
Numero di caratteri della stringa 27
Numero di parole della stringa 6
Stringa al contrario eseap leuq a iav olleb oaiC
Sì, una simpatica curiosità!
Più interessanti sono le funzioni che cercano un testo all'interno di un altro testo e sostituiscono testo con altro testo.
Ne ho due, strpos() e str_replace().
Le provo...

<?php
$stringa="Nel mezzo del cammin di nostra vita mi ritrovai per una selva oscura che la diritta via era smarrita";

echo strpos($stringa,"cammin");
?>
14

Sì, corrisponde.
Ora con str_replace voglio sostituire alcune parole della frase.
<?php
$stringa="Nel mezzo del cammin di nostra vita mi ritrovai per una selva oscura che la diritta via era smarrita";

echo str_replace("selva oscura","cazzo di foresta buia che non si vedeva una minchia",$stringa);
?>
Nel mezzo del cammin di nostra vita mi ritrovai per una cazzo di foresta buia che non si vedeva una minchia che la diritta via era smarrita

Perfetto (chiedo perdono al Sommo...)

Overriding di un metodo e uso dell'operatore di scope per eseguire il metodo della classe base in php

Vediamo di fare un overriding di un metodo di una classe, e quindi di accedere al metodo della classe genitrice.

Inventiamo una classe...

Non ho ancora indagato se esistano in php le classi astratte.
Comunque, facciamo che la classe triangolo sia derivata della classe rettangolo, anche se ci starebbe meglio una classe astratta...

<?php

class rettangolo{
    private $area;
    private $base;
    private $altezza;
    
    function __construct($b, $a){
        $this->base=$b;
        $this->altezza=$a;
    }
    
    public function get_area(){
        return $this->base * $this->altezza;
    }
}

class triangolo extends rettangolo{
    function __construct($b,$a){
        $this->base=$b;
        $this->altezza=$a;
    }

    public function get_area(){
        return ($this->base * $this->altezza)/2;
    }
}

$myrect=new rettangolo(4,5);
echo "Area del rettangolo ".$myrect->get_area();
echo "<br>";
$mytrg=new triangolo(4,5);
echo "Area del triangolo ".$mytrg->get_area();
?> 
I risultati sono come mi attendo:
Area del rettangolo 20
Area del triangolo 10

Dunque ho overridato il metodo get_area().
Ora voglio usare l'operatore di scope (non so se anche in php si chiama così) per accedere al metodo della classe base.
<?php

class rettangolo{
    protected $base;
    protected $altezza;
    function __construct($b,$a){
        $this->base=$b;
        $this->altezza=$a;
    }
    
    public function get_area(){
        return $this->base * $this->altezza;;
    }
}

class triangolo extends rettangolo{

    function __construct($b, $a){
        $this->base=$b;
        $this->altezza=$a;
    }

    public function get_area(){
        return rettangolo::get_area();
    }
}

$myrect=new rettangolo(4,5);
echo "Area del rettangolo ".$myrect->get_area();
echo "<br>";
$mytrg=new triangolo(4,5);
echo "Area del triangolo ".$mytrg->get_area()
?> 
Area del rettangolo 20
Area del triangolo 20

Perfetto!

Classi in php: modificatori di accesso ed ereditarietà

Ecco, anche in PHP ci sono i modificatori di accesso public, private e protected.
Ma forse mi conviene vederli in blocco con l'ereditarietà.
Questo è il link di riferimento.

Eseguo pedissequamente o quasi...

Molto semplice e banale: questo è l'esempio che ho trovato sul tutorial
<?php
class person{
    var $name;
    public $height;
    protected $social_insurance;
    private $pinn_number;
    
    function __construct($persons_name){
        $this->name=$persons_name;
    }
    
    function set_name($new_name){
        $this->name=$new_name;
    }
    
    function get_name(){
        return $this->name;
    }
}

$stefan=new person("Stefan Mischook");
echo "Stefan's full name ".$stefan->get_name();
echo "Tell me private stuff: ".$stefan->pinn_number;
?> 
La variabile private non può venir letta da fuori della classe.
Comportamento del tutto analogo a ciò che accade in altri linguaggi OOP.

Ora vediamo l'ereditarietà.
<?php
class person{
    var $name;
    public $height;
    protected $social_insurance;
    private $pinn_number;
    
    function __construct($persons_name){
        $this->name=$persons_name;
    }
    
    public function set_name($new_name){
        $this->name=$new_name;
    }
    
    public function get_name(){
        return $this->name;
    }
}

class employee extends person{
    function __construct($employees_name){
        $this->name=$employees_name;
    }
}

$stefan=new person("Stefan Mischook");
$james=new employee("Johnny Fingers");

echo "----->".$james->get_name();

?>
----->Johnny Fingers

Bene. Abbiamo visto l'ereditarietà e i modificatori di accesso.
L'ultima cosa che devo vedere è l'overriding dei metodi (che comunque è uguale a quello che conosco da altri linguaggi OOP).

Mi introduco alle classi in PHP

Facciamo un po' di prove con il tipo di variabile Object, vale a dire il corrispettivo delle classi in altri linguaggi...

Le classi possono contenere in esse delle funzioni.
Ecco, ho trovato il modo di creare una classe con getter e setter di una proprietà.
Ci riprovo.
<?php
class Persona {
    var $nome;
    function set_name($newName){
        $this->nome=$newName;
    }
    
    function get_name(){
        return $this->nome;
    }
}

$Uomo=new Persona();
$Uomo->set_name("Mario");
echo $Uomo->get_name();

?> 
E funziona:
Mario

Vediamo se è possibile usare pure un costruttore...

<?php
class Persona {
    var $nome;
    function Persona($myName){
        $this->nome=$myName;
    }
    
}

$Uomo=new Persona("Joe Fetecchia");

echo $Uomo->nome;

?> 
Joe Fetecchia

Sì, si può usare anche il costruttore come negli altri linguaggi.
Ma il costruttore si può usare anche con questo costrutto:
<?php
class Persona {
    var $nome;
    function __construct($newName){
    $this->nome=$newName;
    }
}

$Uomo=new Persona("Pippo");

echo $Uomo->nome;

?> 
Pippo

Scope delle variabili in php

Mi sono esercitato su altri blog con le variabili php, ma è meglio che lo faccia qui perché per me il php è una novità, più che una cosa su cui esercitarsi.
L'ho già toccato distrattamente in passato, ma adesso lo scopro come una necessità piuttosto impellente.

Ho visto i tipi di variabili, perlomeno alcuni tipi di variabili, ma adesso è il caso che faccia uno studio generale.

Lo Scope è fondamentale.
Anche qui, come in JavaScript, esistono le funzioni.
Scrivo una funzione la chiamo, banalmente.
<?php

function funzione(){
    echo "Questa è una funzione";
}

funzione();
?> 
Sì, funziona esattamente come il JavaScript.

Detto questo, analizziamo lo scope.
<?php
$variabile=123;
function funzione(){
    echo $variabile;
}

funzione();
?>

Notice: Undefined variable: variabile in /storage/ssd3/737/3147737/public_html/index.php on line 4

La variabile dichiarata fuori dalla funzione non viene letta nella funzione.

variabile letta al di fuori di una funzione:
<?php
$variabile=123;
function funzione(){
    echo "funzione";
}

funzione();
echo $variabile;
?> 
funzione123



Allo stesso modo, la variabile dichiarata nella funzione non dovrebbe poter essere letta al di fuori.
Proviamo:
<?php

function funzione(){
    $variabile=123;
}

funzione();
echo $variabile;
?> 

Notice: Undefined variable: variabile in /storage/ssd3/737/3147737/public_html/index.php on line 8


...e dovrebbe venir letta all'interno della funzione:
<?php

function funzione(){
    $variabile=123;
    echo $variabile;
}

funzione();

?> 
123



Come faccio a leggere una variabile globale all'interno di una funzione?
Credo di aver capito che ci sono due modi:
  • mediante la parola chiave global;
  • mediante l'array $GLOBALS[index] in cui sono immagazzinate le variabili globali (in index va il nome della variabile).
Proviamo...

<?php
$variabile=123;
function funzione(){
    global $variabile;
    echo $variabile;
}

funzione();

?> 
123



Proviamo l'altro metodo:
<?php
$variabile=123;
function funzione(){
    
    echo $GLOBALS['variabile'];
}

funzione();

?> 
123

Perfetto!

giovedì 15 marzo 2018

Primo approccio con php

Ripeschiamolo, questo php.
<?php

echo "vaffanculo Antonello"

?> 
Questo è un codice estremamente rudimentale.
Lo metto nel file studio.php e lo spedisco sul server.

Vediamo...

vaffanculo Antonello


Sì, funziona.

giovedì 8 febbraio 2018

Creare una libreria "interna" in Android

Spinoso problema delle librerie android.
Il punto chiave è generare un .aar.

Iniziamo con: Click File > New > New Module.
In the Create New Module window that appears, click Android Library, then click Next.

Give your library a name and select a minimum SDK version for the code in the library, then click Finish.

L'ho chiamata Mia Libreria e l'SDK minimo è 17.

E' stata fatta la sincronizzazione di Gradle.
Il cambiamento della voce apply plugin sul modulo Gradle della libreria è già stato cambiato da c.antonello.studiojar3 a com.android.library.
Ora dovrei salvare il file.
Come?
Boh... faccio "Save All"...

Ma il passo completo è Save the file and click Tools > Android > Sync Project with Gradle Files. Vado a cliccare quanto dovuto...

Bene.
Ora se io clicco "build APK" dovrebbe generarmi un AAR...
Lo deduco da questo: When you want to build the AAR file, select the library module in the Project window and then click Build > Build APK


Proviamo...

Fatto, e vediamo se si è generato un AAR...

Non funziona.
La procedura che funziona per creare l'aar è invece
View -> Tool Windows -> Gradle
Sulla finestra di gradle che si apre a destra si va su mialibreria -> Tasks -> assemble, e si generano due aar: una versione debug e una versione release.

Ora il problema è come usare queste librerie.
Ora, dato che la libreria fa parte di questo progetto, posso fare direttamente questo:
Make sure the library is listed at the top of your settings.gradle file, as shown here for a library named "my-library-module":
include ':app', ':my-library-module'
include ':app', ':mialibreria'
Confermato!
Open the app module's build.gradle file and add a new line to the dependencies block as shown in the following snippet:
dependencies {
    compile project(":my-library-module")
}
Vediamo:
dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:26.1.0'
    implementation 'com.android.support.constraint:constraint-layout:1.0.2'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.1'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
    compile project(":mialibreria")
}
Vediamo se sincronizza...

Ha sincronizzato.

Ora, nella mia libreria ho messo un codice estremamente fesso per fare la prova:
public class Classe {
    public Classe(){}
    public void Scrivi(){
        System.out.println("Ciao");
    }
}
Vediamo se si riesce a inserire la funzione.
No.


Anziché modificare manualmente il file app Gradle (parte che ho scritto in rosso) si può procedere con File -> Project Structure
Quindi si seleziona la scheda Dependencies e si aggiunge con il + il riferimento alla libreria.
Il file Gradle app si modifica così:
dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    implementation 'com.android.support:appcompat-v7:26.1.0'
    implementation 'com.android.support.constraint:constraint-layout:1.0.2'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.1'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
    implementation project(':mialibreria')
}
compile è deprecato, l'uso più moderno è di implementation.

sabato 13 gennaio 2018

IP statico? Come accidenti si fa?

Sulle reti non mi sono mai interessato, e comincio da sottozero.
DHCP = Dynamic Host Configuration Protocol.
Se ricordo bene, sarebbe quel giochino secondo il quale quando un computer si connette a una rete gli viene assegnato un IP diverso volta per volta.
Sperimentiamolo (GUH! GUH!) con questo computer. Qual è il mio IP attuale?

   Indirizzo IPv4. . . . . . . . . . . . : 192.168.113.2
   Subnet mask . . . . . . . . . . . . . : 255.255.255.0
   Gateway predefinito . . . . . . . . . :
Ora faccio la disconnessione e la riconnessione.
   Indirizzo IPv4. . . . . . . . . . . . : 192.168.200.1
   Subnet mask . . . . . . . . . . . . . : 255.255.255.0
   Gateway predefinito . . . . . . . . . :
Ecco, l'IP è cambiato.
Questa è la prova che l'IP è dinamico.


E questo è quello che si vede entrando nel router:
 PC-Antonello
192.168.1.109
Questo è l'IP assegnato in questo momento dal router al mio computer.
Come accidenti si fa a renderlo statico? Si può?