Σάββατο, 3 Φεβρουαρίου 2018

Παράδειγμα με φόρμα και νήματα - Example with user form and threads

Εδώ θα δούμε ένα παράδειγμα με χρήση μιας φόρμας και δυο νημάτων. Υπάρχουν δυο κώδικες, ένας με αγγλικές εντολές και ένας άλλος με ελληνικές. Τα γεγονότα από τη φόρμα είναι πάντα στα αγγλικά, εδώ χρησιμοποιούμε τα Resize και Click γεγονότα.
Οι συναρτήσεις που εξυπηρετούν γεγονότα καλούνται "τοπικά". Αυτό σημαίνει ότι η θέαση αναγνωριστικών μέσα στις συναρτήσεις είναι ίδια με την θέαση του τμήματος που ανήκουν. Έτσι η μεταβλητή Περιστοφή που ορίστηκε στο τμήμα είναι σε ίδια θέαση στη συνάρτηση Φόρμα1.Resize(). Οτιδήποτε νέο φτιάχνουμε στην συνάρτηση θα διαγραφεί στην επιστροφή από την  κλήση αυτής της συνάρτησης. Μπορούμε να χρησιμοποιήσουμε την Τοπική ή Τοπικές για να φτιάξουμε μεταβλητές που ήδη υπάρχουν στο τμήμα, και έτσι σκιάζουμε τις υπάρχουσες, μέχρι την επιστροφή, αν το θέλουμε αυτό.
Τα νήματα έχουν και αυτά θέαση μεταβλητών στο τμήμα που ανήκουν, αλλά δεν σβήνουν ότι νέο φτιάχνουν. Για το λόγο αυτό δεν φτιάχνουμε κάτι νέο! Μπορούμε να χρησιμοποιούμε στατικές μεταβλητές, ιδιωτικές για το νήμα, αλλά εδώ δεν τις χρησιμοποιούμε.
Τα νήματα τρέχουν στη σειρά, το ένα μετά το άλλο, βάσει χρόνου εκτέλεσης, αλλά μπορούμε να αλλάξουμε σχέδιο, σε ταυτόχρονο (το άλλο, αυτό που εξ ορισμού χρησιμοποιούμε, λέγεται διαδοχικό). Θέλει προσοχή και εδώ δεν θα αναλυθεί ο τρόπος χρήσης του ταυτόχρουν σχεδίου. Σχέδιο δεν μπορούμε να αλλάξουμε αν έχουμε φτιάξει ήδη ένα νήμα. Τα γεγονότα εκτελούνται ανεξάρτητα από το τι κάνουν τα νήματα, αλλά κάθε φορά εκτελούνται μονομιάς, χωρίς να δημιουργούν πρόβλημα στο κώδικα των νημάτων. Μπορούμε να αλλάζουμε μέγεθος στην φόρμα, και ταυτόχρονα να εκτελούνται νήματα, όπως επίσης μπορούμε να μετακινήσουμε τη φόρμα και πάλι τα νήματα ταυτόχρονα θα εκτελούνται.

Τα νήματα όταν φτιάχνονται είναι δεμένα με το τωρινό επίπεδο όπου δρουν εντολές εξόδου, όπως οι εντολές γραφικών. Μπορούμε να επιλέξουμε ένα επίπεδο με την Επίπεδο αντικείμενο {} όπου αντικείμενο είναι μια φόρμα. Χωρίς αντικείμενο έχουμε το επίπεδο της κονσόλας, δηλαδή η Επίπεδο {} θα στείλει όλες τις εντολές εξόδου στην κονσόλα. Οι συναρτήσεις για τα γεγονότα φορμών δεν έχουν σύνδεση με την φόρμα, πρέπει να το δηλώσουμε με την εντολή Επίπεδο.
Χρησιμοποιούμε ένα αυτόματο νήμα, που φτιάχνουμε με την Μετά χιλιοστά_δευτερολέπτου {}. Αυτό το νήμα θα εκτελεστεί μετά το χρόνο που δίνουμε και θα διαγραφεί. Εδώ καλούμε την συνάρτηση εξυπηρέτησης Φόρμα1.Resize() με κλήση με την Κάλεσε Τοπικά, ώστε να δώσουμε την ίδια θέαση μεταβλητών με αυτήν του τμήματος. Δεν είναι ίδια η κλήση με την κλήση του γεγονότος, γιατί το γεγονός πάντα στέλνει και άλλα δεδομένα, που εμείς δεν τα χρησιμοποιούμε, και αυτά διαγράφονται μετά τη κλήση, επειδή διαγράφεται ο σωρός τιμών που τα περιέχει. Στη Κάλεσε Τοπικά δεν έχουμε διαγραφή σωρού. Εδώ δεν έχουμε θέμα επειδή δεν γίνεται χρήση των επιπλέον δεδομένων. Γενικά στις κλήσεις τμημάτων και συναρτήσεων ο διερμηνευτής δεν ελέγχει τι στέλνουμε, και ο έλεγχος είναι θέμα αυτού που καλούμε, να δει τι θα πάρει και τι θα αφήσει (οι συναρτήσεις αν κληθούν με την κάλεσε αφήνουν στο σωρό, ενώ αν κληθούν σε παραστάσεις τότε έχουν νέο σωρό και τον καθαρίζουν στην επιστροφή).

Για να τρέξετε το κώδικα ξεκινήστε το M2000.exe μετά γράψτε Σ Α πατήστε Entet αντιγράψτε το κώδικα, πατήστε Esc, γράψτε Α και πατήστε Enter. Μπορούμε να το τρέξουμε και με Δοκιμή Α για να ανοίξει μια φόρμα ελέγχου.

Διαφυγή Όχι
Ανανέωση 50
Όρισε Φόρμα1 Φόρμα
Σταθερές Το_Κείμενο$="Hello World", Η_Γραμματοσειρά$="Arial Black", Το_Μέγεθος=22
Σταθερή Z1=Μέγεθος.Χ(Το_Κείμενο$, Η_Γραμματοσειρά$, Το_Μέγεθος, 0) δια 2
Περιστροφή=Αληθές
Η_Γωνία=Πι/5
Επίπεδο Φόρμα1 {
      Παράθυρο 32, 8000,8000;
      Οθόνη #2277BB, 0
      Σημ : Κίνηση.Π ;  ' άλλος τρόπος για να κεντράρουμε τη φόρμα.
      \\ φτιάχνουμε το νήμα να έχει έξοδο στην Φόρμα1
      Νήμα {
            Ανανέωση 5000
            Θέση πλάτος_φόρμας/2, ύψος_φόρμας/2
            Κύκλος Γέμισμα 5, Z1
            Επιγραφή Το_Κείμενο$, Η_Γραμματοσειρά$, Το_Μέγεθος, Η_Γωνία, 2
            Ανανέωση 20
            Η_Γωνία-=Πι/90
      } ως ΠερΕπιγραφή
}  
Με Φόρμα1, "Title", "M2000 Παράδειγμα Φόρμας", "Sizable", Αληθές, "Width" ως πλάτος_φόρμας, "Height" ως ύψος_φόρμας
Συνάρτηση Φόρμα1.Click {
      Περιστροφή~
      Αν Περιστροφή Τότε {Νήμα ΠερΕπιγραφή Ξεκίνα} Αλλιώς Νήμα ΠερΕπιγραφή Κράτα
}
Συνάρτηση Φόρμα1.Resize {
      Επίπεδο Φόρμα1 {
            Ανανέωση 5000
            Οθόνη #2277BB
            Θέση Πλάτος.Σημείου*3, Ύψος.Σημείου*3
            Βάψε πλάτος_φόρμας-Πλάτος.Σημείου*7, ύψος_φόρμας-Ύψος.Σημείου*7, 1, 5
            Θέση πλάτος_φόρμας/2, ύψος_φόρμας/2
            Κύκλος Γέμισμα 5, Z1
            Επιγραφή Το_Κείμενο$, Η_Γραμματοσειρά$, Το_Μέγεθος, Η_Γωνία, 2
            Αν Περιστροφή Τότε Η_Γωνία-=Πι/90
            Θέση 0,0 : Pen 5 {Draw 0,0}
            Ανανέωση 0
            Ανανέωση 20
      }
}
Μετά 10 {Κάλεσε Τοπικά Φόρμα1.Resize() : Ανανέωση 10}
Νήμα {
      Τύπωσε Τικ ' ρολόι νημάτων
      Ανανέωση
} ως αβγ κάθε 20
Νήμα ΠερΕπιγραφή κάθε 20
Αναμονή 50 ' δίνει χρόνο να τρέξουν τα νήματα
Μέθοδος Φόρμα1, "Show", 1 ' με 1 ανοίγει ως "modal"
Νήματα Σβήσε
Όρισε Φόρμα1 Τίποτα
Ανανέωση 50




This is an example using a form and two threads.There are two scripts, one with english statements, and one with greek statements. Events from form are always in english, here we use the Resize and the Click events.
Functions that are events services, are called as "local". This means that scope inside functions are module's scope. So State variable, defined in module are in same scope in my_form1.resize() function. If anything is new in a function then that erased at return from calling this function. We can use Local var_name to make new variables to shadow same scope and same name variables, if we wish that.
Threads also have same scope as module's name, but they didn't erase anything new. So never make a new variable in a thread (we can make static variables private to threads, but here we don't use them).
Threads run with sequential plan. We can use concurrent plan, using Thread.Plan Concurrent prior the first definition of thread (after that plan can't change). Concurrent plan need some attention to write code so here we don't discuss how this can be done. Sequantial means that every thread execute code, then pass execution to other thread. Events always run based in a different plan, hard to explain here. We can resize form and as form resize threads can be run. The same hold if we move form.
Threads are bound to the current layer, when defined. So we want a thread to direct the console statements to form's layer, so we define this thread in a Layer block as Layer Form_object {}.
We can use Layer { } do send commands to console, inside a thread which is bound a layer else.
Functions as event services havn't bound to form, they bound to the console, so we need to use Layer objectName {} to direct statements to form's layer.
There is an auto thread, which run once after some milliseconds, declared with After milliseconds {}. This thread erased after executed once. We use it to call resize not by event, but by thread, using Call Local way). This is not the same call, because in an event call there are more parameters in stack of values, one of them is the form as object, and they erased after call, erasing the stack of values by default. In Call Local we have no erasing of stack of values. We can use as same here because we didn't use those arguments. In M2000 functions and modules have to manage the arguments, not the interpreter, in a general view. Functions if called using Call then get stack of values from parent, and return it, but if called in an expression then always have new stack and erase it at return

Open M2000.exe, then write Edit A press enter then copy this, press Esc and write A and press enter to run it. You can run this with Test A to open control form.

Escape Off
Refresh 50
Declare my_form1 Form
Const myText$="Hello World", myFont$="Arial Black", mySize=22
Const Z1=Size.X(myText$, myFont$, mySize, 0) div 2
State=True
myAngle=pi/5
Layer my_form1 {
      window 32, 8000,8000;
      cls #2277BB, 0
      Rem : motion.w ;  ' another way to center form
      \\ we make thread to run using export to layer  my_form1
      Thread {
            Refresh 5000
            Move form.width/2, form.height/2
            Circle Fill 5, Z1
            Legend myText$, myFont$, mySize, myAngle, 2
            Refresh 20
            myAngle-=pi/90
      } as rotLegend
}  
With my_form1, "Title", "M2000 Form Example", "Sizable", True, "Width" as form.width, "Height" as form.height
Function my_form1.Click {
      State~
      If State Then {Thread rotLegend Restart} Else Thread rotLegend Hold
}
Function my_form1.Resize {
      layer my_form1 {
            Refresh 5000
            Cls #2277BB
            Move twipsX*3, twipsY*3
            Fill form.width-twipsX*7, form.height-twipsY*7, 1, 5
            Move form.width/2, form.height/2
            Circle Fill 5, Z1
            Legend myText$, myFont$, mySize, myAngle, 2
            If state then myAngle-=pi/90
            Move 0,0 : Pen 5 {Draw 0,0}
            Refresh 0
            Refresh 20
      }
}
After 10 {Call Local my_form1.Resize() : refresh 10}
Thread {
      Print Tick ' thread clock
      Refresh
} As abc interval 20
Thread rotLegend Interval 20
Wait 50 ' give time to threads to execute once
Method my_form1, "Show", 1 ' use 1 for modal show
Threads Erase
Declare my_form1 Nothing
Refresh 50