Τετάρτη, 16 Αυγούστου 2017

Αναθεώρηση 28 (Έκδοση 8.9)

Βρέθηκε ένα λάθος που παρουσιάζεται μόνο στα Windows 10. Διορθώθηκε έμμεσα επειδή δεν έχω στο περιβάλλον προγραμματισμού υπολογιστή με Windows 10 (θα φτιάξω όμως σε Virtual Box όταν βρω χρόνο). Εφόσον στα Ελληνικό Ε όταν ήταν πρώτο γράμμα, ως όνομα στοιχείου ελέγχου (control) στη φόρμα χρήστη, έβγαινε λάθος,τότε η λύση είναι εύκολη, τα ονόματα στα στοιχεία θα είναι στα αγγλικά! Το γιατί το κάνει, και που ακριβώς δεν μπόρεσα να το βρω, όμως βρήκα τι έπρεπε να κάνω ώστε να δημιουργήσω το αποτέλεσμα του λάθους από Windows 10 σε Windows 7, κάνοντας βηματική εκτέλεση και προκαλώντας καθαρισμό σε ένα αλφαριθμητικό που κανονικά είχε το όνομα του στοιχείου. Δυο εντολές καθορίζουν αυτό το όνομα, και πιθανολογώ ότι είναι στην πρώτη, μια που επιστρέφει το όνομα του στοιχείου μέσα στο κώδικα της VB6,το UserControl.Ambient.DisplayName, και η δεύτερη η GetStrUntilB() που είναι μια δική μου συνάρτηση που ξέρω ότι δουλεύει καλά, και θα είχε φέρει λάθη και σε άλλα σημεία.

Ο κώδικας σε VB6, στο μοναδικά UserControl της M2000 (και το οποίο χρησιμοποιείται σε όλες τις φόρμες - και στο διορθωτή προγραμμάτων)
Παρατηρήστε ότι η Sub MoveTwips, πρέπει να συνδέσει το Control mo, βάσει του ονόματος που έχει το UserControl στο οποίο καλέσαμε την MoveTwips, διαβάζοντας από το πατρικό (τη φόρμα) τον δείκτη σε αυτό. Δηλαδή μέσα στο κώδικα του UserControl ο μοναδικός τρόπος να πάρουμε ένα δείκτη στο αντικείμενο που ουσιαστικά βρίσκεται στη φόρμα, είναι με χρήση του ονόματος, στη λίστα στοιχείων (controls) στη φόρμα.

Public Sub MoveTwips(ByVal mleft As Long, ByVal mtop As Long, mWidth As Long, mHeight As Long)
      Dim mm$, mo As Control, nm$, cnt$, p As Long
      mm$ = UserControl.Ambient.DisplayName
      nm$ = GetStrUntilB(p, "(", mm$ & "(", True)
      cnt$ = GetStrUntilB(p, ")", mm$, True)
      On Error Resume Next
      If UserControl.Parent Is Nothing Then Exit Sub
      If Err.Number > 0 Then Exit Sub
      If cnt$ <> "" Then
            Set mo = UserControl.Parent.Controls(nm$).item(CInt(cnt$))
      Else
            Set mo = UserControl.Parent.Controls(nm$)
      End If
      If mWidth < 100 Then
            mo.Move mleft, mtop, mo.Width, mo.Height
      ElseIf mHeight < 100 Then
            mo.Move mleft, mtop, mWidth, mo.Height
      Else
            mo.Move mleft, mtop, mWidth, mHeight
      End If
End Sub.

Σάββατο, 5 Αυγούστου 2017

Αναθεώρηση 27 (Έκδοση 8.9)

Μερικές προσθήκες εσωτερικές (διορθώθηκε και η 26 που βγήκε για λίγο)
Μπήκε η δυνατότητα να δώσουμε στο σωρό ένα αντικείμενο και να το φορτώσουμε σε θέση πίνακα. Για ομάδες και λάμδα συναρτήσεις γίνονταν, όπως γίνονταν και με άμεση εκχώρηση (πχ το Α(2)=(1,2,3) είναι άμεση εκχώρηση, ενώ το Στη Β(2)=(1,2,3) είναι έμμεση, επειδή πρώτα υπολογίζεται η δεξιά έκφραση και μετά η αριστερή, ενώ μεσολαβεί η τοποθέτηση του αποτελέσματος στο σωρό τιμών). Η δυνατότητα λοιπόν που μπήκε είναι άμεσα συνδεδεμένη και με το πώς διαβάζουν τμήματα και συναρτήσεις από το σωρό (αφού διαβάζουν με τη Διάβασε, ή READ)

Dim A(10), B(10)
A(2)=(1,2,3)
let B(2)=(1,2,3)
\\ let is equivalent with two commands
\\ Push (1,2,3) : Read B(2)
Print A(2)(2), B(2)(2)

Διάλειμμα για διακοπές!





Εγχειρίδιο της Μ2000 - Τεύχος 35ο

17.6 Λίστα.Εισαγωγής (Combo)
Το στοιχείο Λίστα.Εισαγωγής έχει πάνω στη φόρμα ένα στοιχείο Εισαγωγή (textbox) και εμφανίζει με το κάτω βελάκι ή με κλικ με το ποντίκι ένα στοιχειο Λίστα (listbox), σε ξεχωριστό παράθυρο (φαίνεται σαν αναπτυσσόμενη λίστα), λέγεται αναδιπλούμενη λίστα, και εξαφανίζεται όταν αλλάξει η εστίαση σε άλλο στοιχείο στη φόρμα. Η θέση του ξεχωριστού παράθυρου μπορεί να αλλάξει ανάλογα με το που βρίσκεται το στοιχείο σε σχέση με την οθόνη του υπολογιστή. Αν είναι χαμηλά θα βγει πάνω από το στοιχείο Εισαγωγή.  Στο στοιχείο Εισαγωγή θα υπάρχει ένας τίτλος (ιδιότητα "Label") και δίπλα ένα βελάκι σαν τρίγωνο με την βάση από πάνω. Όταν εισάγουμε κείμενο τότε αυτό φαίνεται αμέσως μετά το βελάκι. Στη διαφοροποίηση σε μενού δεν έχουμε κείμενο δεξιά από το βελάκι.


Χρήσεις της Λίστας Εισαγωγής
  • Μενού επιλογών, με διάφορα στοιχεία στη λίστα, χωρίς τη χρήση του πεδίου επιλογής για εμφάνιση επιλογής. Τα στοιχεία της λίστας μπορεί να είναι ομάδες στοιχείων με φερόμενη κουκίδα δεξιά, όπου μια κουκίδα είναι συνέχεια επιλεγμένη, μπορεί να είναι απλά στοιχεία με κουκίδα δεξιά (όπως το στοιχείο Επιλογή ή Checkbox), ή απλά στοιχεία (όπως πλήκτρο ή button), ή μια διαχωριστική γραμμή. Τα στοιχεία μπορεί να είναι ενεργά ή όχι.
  • Αναπτυσσόμενη λίστα για επιλογή στο πεδίο εισαγωγής μόνο από τη λίστα
  • Πεδίο εισαγωγής, με αυτόματη συμπλήρωση βάσει λίστας, με δυνατότητα συμπλήρωσης

Στη λίστα μπορούμε να κάνουμε επιλογές και μπορεί να υπάρχει ανάδραση, ώστε να έχουμε πληροφόριση για τις επιλογές, χωρίς να κάνουμε αλλαγή κατάστασης, δηλαδή χωρίς αλλαγή επιλογής στο στοχείο Εισαγωγή ή χωρίς επιλογή ενέργειας, όταν λειτουργεί ως μενού. Η επιλογή στη λίστα γίνεται με βελάκια, με click σε μη επιλεγμένο, ή με πλήκτρο αρχικού γράμματος (όπου διαδιχικά, με το ίδιο πλήκτρο, μεταφέρεται η επιλογή σε στοιχεία στη λίστα με ίδιο πρώτο γράμμα). Ολοκληρώνουμε την επιλογή μας με Enter, ή με click πάνω στο επιλεγμένο, ή με σύρσιμο δεξιά (πρέπει να αφήσουμε την επιλογή εντός της λίστας για να πιάσει η ενέργεια)

Γεγονότα
  • About
  • MenuChecked item
  • DblClick item
  • Move item
  • Scroll item
  • Click item
  • OpenMenu
  • PickOther item
  • AutoCompleteDone word$
Ιδιότητες
UseOnlyTheList
AutoComplete
NoCaseSensitive
Index
ListText
Label μόνο για να αλλάξουμε τιμή
menuEnabled()  πέρνει στοιχείο στις παρενθέσεις και βάζουμε αληθές ή ψευδές
id(item) δίνει αλφαριθμητικό με αναγνωριστκό για μενού.
List(item) δίνει και παίρνει αλφαριθμητικό
Text δίνει και παίρνει το κείμενο που εμφανίζεται στην Εισαγωγή
locked
ListRadioPrivate() παίρνει μια τιμή Αληθής ή Ψευδής
ListSelected() διαβάζει και δίνει τιμή Αληθής ή Ψευδής
ListChecked() διαβάζει και δίνει τιμή Αληθής ή Ψευδής
ListMenu()
ListRadio()
ListSep()
(συνεχίζεται)
Μέθοδοι
Move
FontAttr
additem

MenuItem
(συνεχίζεται)

Παράδειγμα με αυτόματη συμπλήρωση και πρόσθεση στοιχείων
Σε αυτό το παράδειγμα ότι γράφουμε και βάζουμε στη λίστα θα είναι σε κεφαλαία γράμματα. Όταν επιλέγουμε κάτι στη λίστα με απλό κλικ, ή με βελάκια, ή με ολίσθηση ή με επιλογή πρώτου γράμματος, παράγονται γεγονότα Click(), Scroll(), PickOther() τα οποία τροφοδοτούν ένα στοχείο Εισαγωγή, το Εισ1. Το ίδιο γίνεται όταν γράφουμε τα πρώτα γράμματα στο πεδίο εισαγωγής της Λίστας Εισαγωγής και αυτόματα συμπληρώνεται, καθώς τότε παράγεται το γεγονός AutoCompleteDone().
Η ιδιότητα ListText έχει μια διαφοροποίηση σε σχέση με το τι κάνει όταν εκχωρούμε τιμή και το τι κάνει όταν διαβάζουμε τιμή. Στην εκχώρηση βάζει ένα αλφαριθμητικό με παραγράφους ως την νέα λίστα εισαγωγής. Στην ανάγνωση δίνει ή κενό αλφαριθμητικό αν δεν έχουμε επιλέξει κάτι στη λίστα, ή το επιλεγμένο στη λίστα. Έτσι διαβάζουμε τη λίστα! Θα μπορούσαμε να τη διαβάσουμε ως Λίστα$(Επιλ_λίστας) (επειδή έχουμε ορίσει το Επιλ_λίστας να είναι η ιδιότητα ListIndex, και το Λίστα$() να είναι η ιδιότητα List. Η ιδιότητα list έχει μια παράμετρο και έτσι φτιάχνουμε ένα πίνακα (ουσιαστικά φαίνεται σαν πίνακας, αλλά είναι αντικείμενο με μια παράμετρο που επιστρέφει τιμή, ή δέχεται τιμή, δηλαδή μπορούμε να αλλάξουμε κάποιο στοιχείο). 

Επειδή ανοίγουμε το παράθυρο σαν modal (δηλαδή περιμένει για να κλείσει), και επειδή δεν μπορούμε να πάμε την εστίαση σε ένα στοιχείο πριν η φόρμα γίνει ορατή, εκτελούμε ένα νήμα μιας χρήσης με την Μετά, όπου σε 2/10 του δευτερολέπτου στέλνουμε την εντολή για να κινηθεί η εστίαση στο στοιχείο Εισ1, αφού θα έχει ανοίξει η φόρμα..

Πώς δουλεύει η αυτόματη συμπλήρωση:
Καθώς πληκτρολογούμε γράμματα εμφανίζεται η λέξη που βρίσκει το στοιχείο στην λίστα. Όμως παρόλο που φαίνονται τα γράμματα που δεν έχουμε πληκτρολογήσει, αμέσως μετά το δρομεά, αυτά δεν υπάρχουν σε αυτό που μόλις έχουμε πληκτρολογήσει. Έτσι συνεχίζοντας να πληκτρολογούμε γίνεται νέα εύρεση και αν δεν βρεθεί βλέπουμε το συμπλήρωμα να εξαφανίζεται. Ομοίως αν πάμε στο τέλος της λέξης που έχει βρεθεί και ξεκινήσουμε να σβήνουμε γράμματα, τα γράμματα μετά το δρομέα δεξιά δεν φαίνεται να σβήνονται όσο ακόμα συμπληρώνονται αυτόματα, ή μπορεί να αλλάξουν αν για τα γράμματα που όντως υπάρχουν βρεθεί άλλο συμπλήρωμα. Σε κάθε περίπτωση αναζήτησης παράγεται ένα γεγονός AutoCompleteDone.

Πώς δουλεύει η επιλογή στη λίστα με πλήκτρο:
Όταν έχουμε ανοίξει τη λίστα, με κάτω βελάκι ή με το ποντίκι, μπορούμε να πατήσουμε το πρώτο γράμμα σε αυτό που μας ενδιαφέρει, και αν υπάρχει η επιλογή στη λίστα, που έχει αυτό το γράμμα στη πρώτη θέση, θα μεταφερθεί στο πρώτο που θα βρεθεί, μετγά από το σημείο που βρίσκεται η τωρινή επιλογή, ή απο την αρχή αν είμαστε στο τέλος της λίστας, ή δεν υπάρχει άλλο μέχρι το τέλος αυτής . Αν το ξαναπατήσουμε θα πάει στο επόμενο που θα βρεθεί. Κάθε φορά που κάνουμε αναζήτηση με γράμμα παράγεται ένα γεγονός PickOther. Αν πατήσουμε enter ή κάνουμε click στο επιλεγμένο ή σύρουμε δεξιά, θα παραχθεί το DblClick. Η επιλογή με γράμμα λειτουργεί σε κάθε διαμόρφωση, και όχι ειδικά στην αυτόματη συμπλήρωση.


Δοκιμάστηκε σε Windows 10 64bit (σε Virtual Box). O Edge δεν πέτυχε με την πρώτη να αντιγράψει σωστά το  παρακάτω. Υπάρχει πρόβλημα με το nbsp που κανονικά υπάρχει στα ΔΕΛΤΑ ΓΑΜΜΑ ΒΗΤΑ για να φαίνεται η εσοχή..


Όρισε Φόρμα1 Φόρμα
Όρισε ΛιστΕισ1 Λίστα.Εισαγωγής Φόρμα Φόρμα1
Όρισε Εισ1 Εισαγωγή Φόρμα Φόρμα1
Με Φόρμα1,"Title" ως Τίτλος$
Με ΛιστΕισ1,"Edit", Αληθές,"UseOnlyTheList", Ψευδές,"autocomplete", Αληθές, "ListIndex" ως Επιλ_λίστας, "ShowAlways", Αληθές
Με ΛιστΕισ1,"Label","ΛιστΕισ1", "ListText" ως Στοιχεία$, "list" ως Λίστα$(), "text" ως ΛιστΕισ1$
Στοιχεία$={ΑΛΦΑ
            ΔΕΛΤΑ
            ΓΑΜΜΑ
            ΒΗΤΑ
            }
Με ΛιστΕισ1,"Find" ως Βρες()
Μέθοδος ΛιστΕισ1,"Sort"
ΛιστΕισ1$=Λίστα$(Βρες("ΓΑΜ*"))

Μέθοδος ΛιστΕισ1,"Move", 1000,1000,6000,600
Μέθοδος Εισ1, "Move", 1000,2900,6000,600


Με Εισ1,  "text" ως Εισ1Κείμενο$ , "ShowAlways", Αληθές
Συνάρτηση ΛιστΕισ1.about {
      Περί "Βοήθεια ΛιστΕισ1", "Διάλεξε ή γράψε τα πρώτα γράμματα, μπορείς να προσθέσεις νέο στη λίστα"
}
Συνάρτηση ΛιστΕισ1.AutoCompleteDone {
      Εισ1Κείμενο$=Στοιχεία$
}
Συνάρτηση ΛιστΕισ1.PickOther {
      Εισ1Κείμενο$=Στοιχεία$
}
Συνάρτηση ΛιστΕισ1.dblclick {
      ΛιστΕισ1$=Κεφ$(ΛιστΕισ1$)
      Εισ1Κείμενο$=ΛιστΕισ1$
      Τίτλος$=ΛιστΕισ1$
      Αν Βρες(Τίτλος$)= -1 Τότε {
                  Μέθοδος ΛιστΕισ1,"additem", Τίτλος$
                  Μέθοδος ΛιστΕισ1,"Sort"
                  Επιλ_λίστας=Βρες(Τίτλος$)
      }
}
Συνάρτηση ΛιστΕισ1.click {
      Εισ1Κείμενο$=Στοιχεία$
}
Συνάρτηση ΛιστΕισ1.scroll {
      Εισ1Κείμενο$=Στοιχεία$
}
Μετά 200 {Μέθοδος Εισ1,"GetFocus"}
Μέθοδος Φόρμα1, "show", 1
Περί ""
Όρισε Εισ1 Τίποτα
Όρισε ΛιστΕισ1 Τίποτα
Όρισε Φόρμα1 Τίποτα





Θα μπορούσαμε να μην δεχτούμε αλλαγή τιμής σε τιμή που δεν υπάρχει στη λίστα. Τότε όμως θα πρέπει όταν αφήνουμε το στοιχείο αυτό, για να επιλέξουμε άλλο, να σκεφτούμε έναν τρόπο να καθαρίσουμε τυχόν γράμματα που έχουν γραφτεί σε αυτό. Η άλλη πιο απλή λύση θα είναι να ξεκινήσουμε χωρίς Edit, δηλαδή την ιδιότητα Edit να την έχουμε με τιμή Ψευδές. Σε αυτήν την περίπτωση, επειδή δεν έχουμε δρομέα φανερό (caret) να αναβοσβήνει, το στοιχείο χρωματίζεται, με ένα πράσινο χρώμα και επειδή έχουμε το ShowAlways με τιμή Αληθές, και να χάσει την εστίαση παραμένει αυτό το χρώμα.


Παράδειγμα με επιλογή από τη λίστα




Όρισε Φόρμα1 Φόρμα
Όρισε ΛιστΕισ1 Λίστα.Εισαγωγής Φόρμα Φόρμα1
Όρισε εισ1 Εισαγωγή Φόρμα Φόρμα1
Με Φόρμα1,"Title" ως Τίτλος$
Με ΛιστΕισ1,"UseOnlyTheList", Αληθές, "ListIndex" ως Επιλ_λίστας
Με ΛιστΕισ1,"Label","ΛιστΕισ1", "ListText" ως Στοιχεία$, "list" ως Λίστα$(), "text" ως ΛιστΕισ1$
Στοιχεία$={ΑΛΦΑ
            ΔΕΛΤΑ
            ΓΑΜΜΑ
            ΒΗΤΑ
            }
Με ΛιστΕισ1,"Find" ως Βρες()
Μέθοδος ΛιστΕισ1,"Sort"
ΛιστΕισ1$=Λίστα$(Βρες("ΓΑΜ*"))

Μέθοδος ΛιστΕισ1,"Move", 1000,1000,6000,600
Μέθοδος Εισ1, "Move", 1000,2900,6000,600

Με Εισ1,  "text" ως Εισ1Κείμενο$ , "ShowAlways", Αληθές
Συνάρτηση ΛιστΕισ1.about {
      Περί "Βοήθεια ΛιστΕισ1", "Διάλεξε ή γράψε τα πρώτα γράμματα, μπορείς να προσθέσεις νέο στη λίστα"
}
Συνάρτηση ΛιστΕισ1.PickOther {
      Εισ1Κείμενο$=Στοιχεία$
}
Συνάρτηση ΛιστΕισ1.dblclick {
      Εισ1Κείμενο$=ΛιστΕισ1$
      Τίτλος$=ΛιστΕισ1$
}
Συνάρτηση ΛιστΕισ1.click {
      Εισ1Κείμενο$=Στοιχεία$
}
Συνάρτηση ΛιστΕισ1.scroll {
      Εισ1Κείμενο$=Στοιχεία$
}
Μετά 200 {Μέθοδος Εισ1,"GetFocus"}
Μέθοδος Φόρμα1, "show", 1
Περί ""
Όρισε Εισ1 Τίποτα
Όρισε ΛιστΕισ1 Τίποτα
Όρισε Φόρμα1 Τίποτα
















Παρασκευή, 4 Αυγούστου 2017

Αναθεώρηση 25 (Έκδοση 8.9)

Προσθήκες συναρτήσεων για ιδιότητες αντικειμένων τύπου COM, ιδιότητα(), ιδιότητα$(), property(), property$()

declare form1 form
declare list1(3) listbox form form1
controls=(,)
link controls to controls$()
inventory listindex, titlelist
for i=0 to 2 {
      Method list1(i), "move", 2000,1100+i*1000, 5000,900
      With list1(i), "list" as new list$(), "DisplayLines", 2, "listindex" as new listind, "title" as new mTitle$
      With list1(i), "text", {aaaaaaa
            bbbbbbbbbb
            cccccccc
            }
      Append titlelist, i:=property$(mTitle$)
      Append listindex, i:=property(listind)
      Print listindex(i), type$(listind)
      Append controls, (list$(),)
      Print len(controls$())
      controls$(i)(0)="ok"+str$(i)
      Print list$(0), "ok", controls$(i)(0)
}
     listindex(0)=2
     titlelist$(0)="ok..."
Print len(listindex)
function form1.click {
     listindex(0)=2
}
function list1.dblclick {
      Read New Who, what
      Print listindex(who), who, what
      Refresh
}
method form1, "show",1
declare list1() nothing
declare form1 nothing



Το παρακάτω πρόγραμμα δουλεύει καλά τώρα, και χρησιμοποιεί ένα εξωτερικό αντικείμενο, το vbRichClient 5 από εδώ  http://www.vbrichclient.com/, το οποίο έχει ένα σωρό αντικείμενα και συνάμα έχει δικές του φόρμες, του συστήματος Cairo. Δείτε πως παίρνουμε τα γεγονότα για φόρμες που ανήγουμε προσωρινά με Modal τρόπο:
Κάθε φορά που κάνουμε click στη κίτρινη φόρμα, ανοίγουν έξι "κόκκινες" Modal φόρμες, και κάθε φορά πρέπει να κλείσουμε την πιο μπροστά για να πάμε στην πιο πίσω μέχρι να φτάσουμε στην κίτρινη πάλι. Συνάμα αν κάνουμε κλικ στην κόκκινη φόρμα


Δείτε κάτι ακόμα: Υπήρχε πρόβλημα όταν ξεκίναγα μια "κόκκινη" φόρμα μέσα στην συνάρτηση εξυπηρέτησης γεγονότος, μπορούσε να ανοίξει τέσσερις φόρμες και μετά για άνγωστη αιτία δεν άνοιγε άλλη! Το σκέφθηκα λοιπόν και έβαλα την εντολή After miliseconds {} ή Μετά χιλιοστά_δευτερολέπτου {}, το οποίο είναι νήμα που θα τρέξει αφού το γεγονός θα έχει επισρέψει!

Επίσης από αυτή την αναθεώρηση έβαλα στα γεγονότα που στέλνονται στην Μ2000 (συναρτήσεις με όνομα αντικειμένου μετά μια κάτω παύλα και μετά το όνομα του γεγονότος, να ακολουθεί στο τέλος το αντικείμενο (μια αναφορά του) που στέλνει το γεγονός! Έτσι η Form2_Click δουλεύει με τη σειρά για όλες τις φόρμες που ανοίγουν η μία μετά την άλλη (έξι τον αριθμό)

Επίσης δείτε στο Form2_move ότι διαβάζουμε με αναφορά τα Χ και Υ (σημαίνει ότι μπορούμε να τα αλλάξουμε). Και σε αυτό το γεγονός στο σωρό τιμών (που καθαρίζει μετά την κλήση από το σύστημα των γεγονότων, εντός του διερμηνευτή), ακολουθούν δυο στοιχεία, ένα αλφαριθμητικό με πληροφορίες, και ένα ακόμα, το αντικείμενο που στέλνει το γεγονός (αυτό προστέθηκε σε αυτή την αναθεώρηση).

Επίσης δείτε το  πως δημιουργούμε το Form2. Επειδή θέλουμε να βγει 6 φορές με 6 νήματα, και κάθε νήμα θα μπλοκάρει για όσο η φόρμα Form2 θα είναι ανοιχτή, σημαίνει ότι στο τμήμα θα υπάρχουν 6 Form2, δηλαδή με ίδιο όνομα και σε αναζήτηση θα βρίσκεται το τελευταίο που γράφτηκε.ενώ η διαγραφή δεν θα γίνεται στο νήμα! Το νήμα δηλαδή δεν διαγράφει ότι τυχόν φτιάχνει. Αν ανοίξουμε 600 φόρμες θα υπάρχουν 600*Ν μεταβλητές όπου Ν είναι όσες χρειάστηκε να φτιαχτουν ως νέες στο κάθε νήμα.
Τώρα πώς ξέρουμε ότι θα καθαρίσουν οι μεταβλητές αυτές με την ανάποδη σειρά; Οι φόρμες άνοιξαν σαν Modal δηλαδή πρέπει να κλείσουμε την τελευταία που άνοιξε, και μετά την προτελευταία, και αυτή είναι η σειρά που πρέπει να "καθαρίσουν" και τα αντικείμενα.

Δείτε επίσης πως όταν κρατάμε πατημένο το [X] για κλείσιμο της φόρμας στο RC5 σταματάνε ακόμα και τα νήματα στην Μ2000. (πράγμα που δεν συμβαίνει με τις φόρμες της Μ2000, αφού το τετραγωνάκι στις φόρμες για το κλείσιμο δεν είναι το ίδιο με το κουμπί στις φόρμες της VB6). Ουσιαστικά η Μ2000 έχει ένα μέρος δικό της σε σχέση με τον Window manager, και αυτό ευθύνεται για το πώς χειρίζεται το κλείσιμο και τα modal παράθυρα (πχ η φόρμα ¨Ελεγχος είναι ανεξάρτητη από τα παράθυρα χρήστη που μπορεί να ανοίξουμε με modal τρόπο. Επίσης μπορούμε να ανοίξουμε μη modal παράθυρα από ένα modal παράθυρο, και μετά αν ανοίξουμε ένα Modal θα κλείσει όλα τα προηγούμενα και όταν το κλείσουμε θα ανοίξει μόνο τα αμέσως προηγούμενα που ήταν ανοιχτά, μέχρι να κλείσουμε και το ένα modal, που άνοιξε τα μη modal, οπότε θα ελευθερωθούν τα κάτω από αυτό! Το παράθυρο της βοήθειας είναι και αυτό εκτός των Modal παραθύρων.Τα modal παράθυρα απενεργοποιούν και την κονσόλα. Το κώδικα για αυτό το σύστημα το έχω γράψει εδώ και καιρό, αλλά είναι μια ιδέα με "μαγικούς" αριθμούς, στην ουσία τυχαίους, που διαλέγει το σύστημα, με "νόημα" που δηλώνουν το ποιος έχει το πάνω χέρι. Οπότε όταν ένα modal παράθυρο κλείνει στέλνει στα άλλα το μήνυμά του. Ένα παράθυρο που έχει "απενεργοποιηθεί", ουσιαστικά μπορεί να δουλεύει κανονικά, εκτός από το να δέχεται κλικ και πλήκτρα, δηλαδή μπορεί να μετακινηθεί, ακόμα και να κλείσει! Ακόμα και το messagebox είναι της Μ2000, το Ask() ή Ρώτα(), και βγαίνει πάνω από όλα τα άλλα, ως modal και αυτό. Εδώ έχει μια ιδιαιτερότητα η Μ2000, δεν επιτρέπει δεύτερο messagebox να βγει. Δηλαδή αν το βάλουμε σε ένα νήμα (τα νήματα μπορούν να τρέχουν πίσω από το messagebox) θα βγει λάθος αν ήδη είναι ανοικτό. Ενώ οι φόρμες της Μ2000 είναι GuiM2000 αντικείμενα, οι διάλογοι της Μ2000 όπως το messagebox και για άνοιγμα φακέλων, αρχείων, επιλογής χρώματος και επιλογής γραμματοσειράς είναι ξεχωριτά αντικείμενα (είχαν γραφτεί πριν φτιαχτουν οι φόρμες χρήστη). Αυτά τα αντικείμενα έχουν την ιδιότητα να αλλάζουν μέγεθος με μεγέθυνση των στοιχείων τους και αυτά έχουν το ένα και μοναδικό glis4, ένα user control  γραμμένο στη VB6, με "ζωγραφιές"...δηλαδή δεν έχει κανένα άλλο control πανω στο user control, είναι μια επιφάνεια που σχεδιάζει ότι χρειάζεαι, ακόμα και το scoll bar...είναι ζωγραφιστό! Αποφεύγοντας την χρήση εξωτερικών στοιχείων, ή και βιβλιοθηκών η Μ2000 μπορεί να λειτουργεί το ίδιο από Xp ως και Windows 10, για όσο το βοηθητικό dll της VB6 τρέχει σε αυτά (μέχρι τον Οκτώβριο του 2025, αν και πιστεύω ότι δεν θα πάψει να υποστηρίζεται). Στην ουσία το M2000.exe και το Μ2000.dll (η γλώσσα μπορεί να φορτωθεί σε άλλες γλώσσες και να τρέχει από αυτές), χρειάζονται το msvbvm60.dll γιατί παρέχει συναρτήσεις κοινές αλλά και γιατί έχει το σύστημα των γεγονότων που λειτουργεί ως ενδιάμεσος με τις "πραγματικές" φόρμες των Windows. (στην ουσία κάθε φόρμα της Μ2000 είναι πάνω σε μια άδεια φόρμα της VB6 η οποία είναι ένα πραγματικό παράθυρο των Windows, και τα μηνύματα που λαμβάνει προωθούνται στη φόρμα της VB6, και από εκεί η M2000 τα προωθεί στο πρόγραμμα του χρήστη, τα δυο πρώτα μέρη είναι compiled, ενώ το τρίτο, ο κώδικας σε Μ2000 είναι Interpreted, αλλά ο διερμηνευτής είναι και αυτός compiled, δηλαδή δεν τρέχει ως interpreted Visual Basic 6, αλλά ως γλώσσα μηχανής.


Παρατηρήστε ότι οι συναρτήσεις για γεγονότα σε Com αντικείμενα έχουν _ και όχι τελεία όπως στις φόρμες της Μ2000. Αυτό συμβαίνει γιατί υπάρχει ξεχωριστό σύστημα "εσωτερικό". Ενώ τα γεγονότα για τα Com χρησιμοποιούν το Shink object, για μεταφορά γεγονότων από διαφορετικές διεργασίες


Ακόμα η χρήση του συγκεκριμένιυ εξωτερικού αντικειμένου είναι πειραματική.

clear
' cConstructor
'Title "",0
Declare AppForm Application Form
Declare New_c "{CAECC935-9C70-4176-8BED-C39B2E33B31F}"
Method New_c, "Cairo" as Cairo
'Print Type$(New_c)
'Print Type$(Cairo)
With Cairo, "WidgetForms" as Cairo.WidgetForms
Method Cairo.WidgetForms, "Create", 1,"My Main-Form Caption, Click Me!" withevents as Form1

Print type$(Form1)
vbYellow=65535
vbRed=255
vbFixedDialog=3
vbSizableToolWindow=5

Print Type$(Form1)
With Form1, "WidgetRoot" as Form1.WidgetRoot, "visible" as visible
Print type$(Form1.WidgetRoot)
With Form1, "hWnd" as Form1.hWnd
With Form1.WidgetRoot, "BackColor", vbYellow
          
Method Cairo.WidgetForms, "Create", vbSizableToolWindow, "MyToolWindow - Resize Me!", -1, 800, 600 as Form3
Method Form3,"SetMinMaxDimensions", 200, 400, 300, 600
With Form3, "visible" as Form3.visible
mClicks=0
\\ events
Function Form2_Click {
      drop
      read new form111
      mClicks++
      With form111, "Caption", "Click-Count so far:" +Str$(mClicks)
}
Function Form1_Move {
      Read New &X, &Y
      Print X, Y
      refresh
}
Function Form2_Move {
      Read New &X, &Y
      Print X, Y
      refresh
}
Function Form1_Click {
      Refresh
      \\ After start a new thread after 100 msec, and run once
      for i=1 to 6 {
      After 100 {
            Report "First we show a modal Dialog-Window, and count the Clicks on it"
            Method Cairo.WidgetForms, "Create", vbFixedDialog,"MyDialog ... Click Me!",-1 , 800, 600 withevents as new Form2
            With Form2, "WidgetRoot" as new Form2.WidgetRoot
            With Form2.WidgetRoot, "BackColor", vbRed
            Refresh
            Method Form2 "Show", 1
            Refresh
            Report format$("The Dialog was clicked {0} times.", mClicks)
            Report  {Now we show a non-modal ToolWindow, which remains in the ForeGround of this Form
                              and will close itself automatically, when the MainForm is destroyed
                              }
            Print
            refresh
            Declare Form2.WidgetRoot Nothing
            Declare Form2 Nothing
      }      
      }
     
}
Thread {
try {
      If pos>0 then print
      Print now, visible
      }
      refresh
} as alfa interval 100
Print Visible
Method Form1, "Show", 0, AppForm
Print Visible
\\\  not used here Method Cairo.WidgetForms, "EnterMessageLoop"
Task.Main 100 {
      if Form1.hWnd else exit
}
Threads erase
Declare form1 nothing
Declare form3 nothing
Declare cairo nothing
declare New_c nothing
Show  \\ to get focus to M2000 console




Τετάρτη, 2 Αυγούστου 2017

Αναθεώρηση 24 (έκδοση 8.9) προσθήκες!

Σε αυτήν την αναθεώρηση υπάρχει δυνατότητα να αποθηκεύουμε ιδιότητες σε πίνακες και καταστάσεις (λίστες με κλειδί) από αντικείμενα τύπου com (όπως και οι φόρμες χρήστη, και τα στοιχεία ελέγχου στις φόρμες αυτές), τα οποία δέχονται μια παράμετρο (αριθμό ή αλφαριθμητικό) είτε για να διαβάσουμε είτε για να γράψουμε τιμή.

Στο παράδειγμα που ακολουθεί, έχουμε μια φόρμα και θέλουμε να έχουμε ένα πίνακα από Listbox (λίστες). Έχουμε έναν δείκτη σε πίνακα το controls, τον οποίο συνδέουμε με το control$() (για να έχουμε και αυτό το interface στο αντικείμενο πίνακα). Καλούμε μια μέθοδο move για καθένα listbox για να δώσουμε θέσεις στην φόρμα. Μετά με την With ορίζουμε σε καθένα μια νέα List$(). Όταν ορίζουμε νέα, σημαίνει ότι η υπάρχουσα δεν σβήνει, αλλά ο διερμηνευτής βρίσκει πάντα την τελευταία. Άρα κάπου πρέπει να την κρατήσουμε. Εδώ την βάζουμε σε ένα αυτόματο πίνακα (δείτε τις παρενθέσεις και το κόμμα πριν την δεξιά παρένθεση, που δηλώνει ότι είναι αυτόματος πίνακας, ή δείτε στην αρχή που δίνουμε στην Controls έναν κενό πίνακα με το (,)). Αυτόν τον αυτόματο πίνακα τον βάζουμε στο τέλος του Controls, δηλαδή προσθέτουμε, και εδώ η ελληνική εντολή είναι η Προσθήκη.
Δείτε αμέσως μετά την εκχώρση όπου χρησιμοποιούμε τον πίνακα Control$() (είναι ο ίδιος ο Controls) και προσθέτουμε παρενθέσεις με την τιμή που θέλουμε για την ιδιότητα.

Εδώ χρειάζεται κανείς να καταλάβει μια ιδιαιτερότητα της Μ2000 σε σχέση με τις ιδιότητες αντικειμένων που συνδέουμε σε ονόματα όπως το list$(). Αυτές είναι αντικείμενα τύπου PropReference (property reference), τα οποία συνδέονται με μεταβλητές στο χώρο του τμήματος, έτσι ώστε να μην είναι άμεσα συνδεδεμένες με το αντικείμενο απ΄όπου χειριζόμαστε την ιδιότητα, το com αντικείμενο. Το πλεονέκτημα αυτού του τρόπου είναι ότι το αντικείμενο com είναι μόνο μια φορά συνδεδεμένο με τον διερμηνευτή και θα "Χαλάσει" θα γίνει δηλαδή αποδόμηση (deconstruct)  αν ορίσουμε τίποτα, δηλαδή με την Όρισε ....Τίποτα, ή αυτόματα στο τερματισμό εκτέλεσης του τμήματος. Αν κρατήσουμε τον πίνακα πψ σε μια γενική μεταβλητή, τότε τα αντικείμενα PropReference δεν θα μπορούν να χρησιμοποιηθούν, δεν θα βρίσκουν την σύνδεση (λέγεται hatdlink στον κώδικα της Μ2000), και θα βγεί λάθος.
Αυτός ο τρόπος με έξυπνους δείκτες, υπήρχε εδώ και πολλές εκδόσεις, και όταν φτιάχτηκαν πραγματικοί δείκτες όπως αυτοί για πίνακες, για καταστάσεις, και για σωρούς (τα τρία αντικείμενα με δείκτη της Μ2000), τότε υποχρεωτικά μπήκε λογισμικό για την εκκαθάριση των αντικειμένων, σε περίπτωση που υπάρχουν μεταξύ τους συνδέσεις, δηλαδή το Α σε κάποιο στοιχείο του να έχει δείκτη στο Β και το Β στο Α. Σε αυτά τα αντικείμενα η αποδόμηση μπορεί να γίνει μόλις φύγει και ο τελευταίος δείκτης προς αυτά, δηλαδή υπάρχει μετρητής δεικτών. Το σύστημα συλλογής σκουπιδικών βάζει ακόμα έναν δείκτη και μετά κάνει σε κάθε κρατημένο αντικείμενο, καθαρισμό, και μετά έναν έλεγχο για το αν έχουν μείνει ή όχι συνδέσεις, και καθαρίζει ανάλογα. Με τα αντικείμενα PropReference δεν υπάρχει θέμα να γράψουμε αναφορά σε κάποιο, γιατί δεν θα μας δώσει αναφορά, ενώ θα  φαίνεται σαν να είναι,έτσι το συνδεδεμένο σε αυτό com αντικείμενο θα παραμείνει με έναν δείκτη και μόλις καθαρίσει η μνήμη για μεταβλητές του τμήματος, θα χαθεί άμεσα. Αυτό μπορεί να γίνει αν διακόψουμε το πρόγραμμα, με πλήκτρο Esc, ή με λάθος. Έτσι λοιπόν όταν φτιάχνουμε φόρμες στην Μ2000  και κάνουμε λάθος, βγαίνεουμ στη κονσόλα με καθαρό το σύστημα από αντικείμενα που είχαμε ορίσει (όπως οι φόρμες και τα στοιχεία ελέγχου σε αυτές). Να γιατί και να ξεχάσουμε την Όρισε Φόρμα1 Τίποτα θα καθαρίσει έτσι και αλλιώς.


declare form1 form
declare list1(3) listbox form form1
controls=(,)
link controls to controls$()
for i=0 to 2 {
      Method list1(i), "move", 2000,1200+i*800, 5000,600
      With list1(i), "list" as new list$()
      Append controls, (list$(),)
      Print len(controls$())
      controls$(i)(0)="ok"+str$(i)
      Print list$(0), "ok", controls$(i)(0)
}
method form1, "show",1
declare list1() nothing
declare form1 nothing

Και εδώ το πρόγραμμα με Κατάσταση (Inventory)

declare form1 form
declare list1(3) listbox form form1
Inventory controls
For i=0 to 2 {
      Method list1(i), "move", 2000,1200+i*800, 5000,600
      with list1(i), "list" as new list$()
      Append controls, i:=list$()
      Print len(controls)
      controls$(i)(0)="ok"+str$(i)
      Print list$(0), "ok", controls$(i)(0)
}
Print len(controls)
method form1, "show",1
declare list1() nothing
declare form1 nothing
Title "Your Program"  \\ restore title in taskbar


Και εδώ δυο προχωρημένα (δεν έχει ακόμα βγει το τεύχος για το Combo ή ;Λίστα.Εισαγωγής). Εδώ με πίνακες.

declare form1 form
declare list1(3) combobox form form1
controls=(,)
Enabled=(,)
link controls, Enabled to controls$(), Enabled()
For i=0 to 2 {
      Method list1(i), "move", 2000,1200+i*800, 5000,600
      with list1(i),"MenuStyle", True, "MenuWidth", 3000,  "MenuEnabled" as new list1_enabled()
      with list1(i),"label", "Menu"+str$(i), "list" as new list$(), "MenuGroup","group_a"
      Method list1(i), "MenuItem","Command 1",True
      Method list1(i), "MenuItem","Command 2",false
      Method list1(i), "MenuItem","Command 3",True
      Append controls, (list$(),)
      Append Enabled, (list1_enabled(),)
      Print Type$(list1_enabled())
      controls$(i)(0)="ok"+str$(i)
      Print type$(Enabled(i)()), type$(controls$(i)())
      if i=1 then Enabled(i)(1)=true
      Print list$(0), "ok", controls$(i)(0), Enabled(i)(1)
}
method form1, "show",1
declare list1() nothing
declare form1 nothing


Και εδώ με Κατάσταση Ειδών:

declare form1 form
declare list1(3) combobox form form1
inventory controls, Enabled
For i=0 to 2 {
      Method list1(i), "move", 2000,1200+i*800, 5000,600
      with list1(i),"MenuStyle", True, "MenuWidth", 3000,  "MenuEnabled" as new list1_enabled()
      with list1(i),"label", "Menu"+str$(i), "list" as new list$()
      Method list1(i), "MenuItem","Command 1",True
      Method list1(i), "MenuItem","Command 2",false
      Method list1(i), "MenuItem","Command 3",True
      Append controls, i:=list$()
      Append Enabled, i:=list1_enabled()
      controls$(i)(0)="ok"+str$(i)
      Print type$(Enabled(i)()), type$(controls$(i)())
      if i=1 then Enabled(i)(1)=true
      Print list$(0), "ok", controls$(i)(0), Enabled(i)(1)
}
method form1, "show",1
declare list1() nothing
declare form1 nothing