Tutorial OpenOffice.org Basic - #1 Χρήση μακροεντολών στο Writer

Ανώνυμος (χωρίς επαλήθευση) | Τρί, 04/01/2008 - 14:31 | 23' | 3

ΜΕΡΟΣ 1ο Το OpenOffice.org έχει τα πάντα, ακόμα και μακροεντολές. Σε αυτή τη σειρά tutorials μαθαίνουμε όλα τα μυστικά για τη σωστή χρήση τους. Κάθε μέρα που περνάει, το OpenOffice.org (OO.org) κερδίζει ακόμα ένα χρήστη. Αυτό μας χαροποιεί όλους γιατί απλούστατα το OO.org είναι ένα από τα πιο άξια προϊόντα του ανοικτού-κώδικα και όσο περισσότεροι άνθρωποι το χρησιμοποιούν τόσο περισσότεροι αναγνωρίζουν τα πλεονεκτήματα του ελεύθερου λογισμικού. Αυτό που οι πιο πολλοί αρχάριοι χρήστες του δεν γνωρίζουν, όμως, είναι ότι κάτω από την επιφάνεια το OO.org κρύβει μια πανίσχυρη γλώσσα προγραμματισμού, την OpenOffice.org Basic. Σε αυτή τη σειρά οδηγών θα μάθουμε πως να την χρησιμοποιούμε ξεκινώντας από το επεξεργαστή κειμένου, το Writer. Τα βασικά της ΟΟ.org Basic Το αναπόφευκτο ερώτημα τώρα είναι γιατί ένας χρήστης του Writer να κάτσει να μάθει προγραμματισμό. Η απάντηση είναι μονολεκτική: αυτοματοποίηση. Έστω ότι πρέπει να φτιάχνουμε μια αναφορά κάθε μέρα στην οποία να περιλαμβάνουμε και την χρήση του χώρου του δίσκου ή μια λίστα με όλους τους συνδεδεμένους χρήστες. Η δουλειά αυτή δεν είναι δύσκολη (αρκεί να χρησιμοποιήσουμε τις εντολές df και who και μετά να αντιγράψουμε τα αποτελέσματα στο έγγραφο του Writer), αλλά είναι βαρετή μέχρι αηδίας. Δεν θα ήταν καλύτερα να βάζαμε το πρόγραμμα να κάνει τη δουλειά για εμάς; Αυτή δεν είναι η δουλειά του υπολογιστή, άλλωστε; Σε αυτό το tutorial θα δούμε ότι με την Basic του OO.org μπορούμε να γράψουμε μακροεντολές που αυτοματοποιούν οτιδήποτε θέλουμε, από το άνοιγμα εγγράφων και την εισαγωγή εξωτερικών δεδομένων σε αυτά μέχρι τη δημιουργία γραφικών διαλόγων, την εργασία με δυναμικά δεδομένα κτλ. Η έκδοση του OO.org που χρησιμοποιήσαμε ήταν η 2.0 (1.9.79) του Linux ενώ για αντιπαραβολή κάναμε τα ίδια και στην 1.1.4 των Windows. Φυσικά, υπάρχουν αρκετές ομοιότητες ανάμεσα στην Basic του OO.org και οποιαδήποτε άλλη Basic του κόσμου (Visual Basic, Gambas, κτλ). Όλες οι Basic έχουν την ίδια δομή στις εντολές τους, και διαφέρουν μόνο ως προς τις αναμενόμενες ιδιαιτερότητες. Από εδώ και πέρα, λέγοντας Basic θα εννοούμε πάντα την γλώσσα του OO.org και, εννοείται, ότι δεν μπορούμε να πάρουμε τον κώδικα από τα παρακάτω παραδείγματα και να τον χρησιμοποιήσουμε σε κάποια άλλη Basic (είπαμε, μοιάζουν, αλλά όχι τόσο). ΟΚ, αφού το ξεκαθαρίσαμε ας ξεκινήσουμε. Εδώ δεν πρόκειται να τα πούμε όλα από το μηδέν, αλλά θα μάθουμε μέσω παραδείγματος. Να λοιπόν ένα μικρό κομματάκι κώδικα που ανοίγει ένα νέο κενό έγγραφο του Writer. Πρώτα, ανοίγουμε τη Διαχείριση Μακροεντολών (δείτε το σχετικό πλαίσιο), δημιουργούμε ένα νέο module και στο νέο παράθυρο που θα εμφανιστεί γράφουμε τα εξής:

Sub Main
  loadNewFile
End Sub
Sub LoadNewFile
  dim doc as object
  dim desk as object
  dim url as string
  dim args()
  desk = CreateUnoService("com.sun.star.frame.Desktop")
  url = "private:factory/swriter"
  doc = desk.loadComponentFromUrl(url, "_blank", 0, args())
End Sub

Τώρα πατάμε το κουμπί Run BASIC από την εργαλειοθήκη για να τρέξουμε τον κώδικα. Θα πρέπει να ανοίξει ένα νέο έγγραφο του Writer. Από τον κώδικα αυτό καταλαβαίνουμε πως φορτώνουμε όποιο αρχείο θέλουμε. Το μυστικό είναι η μεταβλητή url:

 url = "private:factory/swriter"

Εναλλακτικά, μπορούμε να φτιάξουμε μια ρουτίνα που να ανοίγει ένα δεδομένα αρχείο ή, αν αποτύχει, να δημιουργεί ένα νέο:

Sub LoadNewFile (optional myFile as string)
  dim doc as object
  dim desk as object
  dim url as string
  dim Dummy()
  if isMissing(myFile) then
      myFile = "private:factory/swriter"
  end if
  desk = CreateUnoService("com.sun.star.frame.Desktop")
  url = myFile
  doc = desk.loadComponentfromurl(url,"_blank",0,Dummy())
End Sub

Όσοι έχουν χρησιμοποιήσει τη Basic στο παρελθόν δεν θα δυσκολευτούν. Εδώ φτιάξαμε δύο υπορουτίνες. Η πρώτη (Main) χρησιμοποιείται για τον έλεγχο της μακροεντολής. Η δεύτερη (loadNewFile) κάνει την κυρίως δουλειά. Καθορίζει μερικές μεταβλητές (doc, desk, url και args) και μετά παράγει ένα UNO (Universal Network Object), ένα αντικείμενο που μας δίνει πρόσβαση στις μεθόδους και τις ιδιότητες των αντικειμένων του Writer. Γράψιμο σε ένα έγγραφο Η δημιουργία ενός κενού αρχείου δεν είναι καμιά καινοτομία, αφού θα μπορούσαμε να το κάνουμε απλώς με το Ctrl+n. Ας προσθέσουμε λοιπόν μια υπορουτίνα που θα γράφει στο έγγραφο:

 Sub Insert_words
   dim doc as object
   dim cursor as object
   doc=thisComponent
   cursor=doc.text.createTextCursor
   cursor.string=”Hello World”
 End Sub

Για να δουλέψει θα πρέπει επίσης να ξαναγράψουμε τη Main:

 Sub Main
   loadNewFile
   insert_words
 End Sub

Αυτό το παράδειγμα δείχνει πόσο εύκολο είναι να γράψουμε κώδικα που να ελέγχει το Writer. Για μεγαλύτερη ευχρηστία, ας γράψουμε μια ρουτίνα που να “δέχεται” κάτι από το χρήστη και να το γράφει σε ένα νέο έγγραφο ως νέα παράγραφο:

Sub Add_paragraph (myText as String)
  dim doc as object
  dim cursor as object
  doc=thisComponent
  cursor=doc.text.createTextCursor
  cursor.gotoEnd(False)
  doc.text.insertControlCharacter(cursor, _
  com.sun.star.text.ControlCharacter.PARAGRAPH_BREAK, False)
  doc.text.insertControlCharacter(cursor, _
  com.sun.star.text.ControlCharacter.PARAGRAPH_BREAK, False)
  cursor.string = myText
End Sub

Αυτή η ρουτίνα μετακινεί τον κέρσορα στο τέλος του εγγράφου, φτιάχνει μια νέα παράγραφο και εισάγει το κείμενο του χρήστη. Να και η Main:

 Sub Main
  loadNewFile
  add_paragraph("This is my first paragraph.")
  add_paragraph("This is my second paragraph.")
End Sub

Φυσικά, σε αυτό το παράδειγμα θα ήταν πολύ πιο γρήγορο να γράφαμε με το χέρι ότι θέλαμε σε ένα έγγραφο του Writer. Ωστόσο, πρόκειται απλά για ένα παράδειγμα του οποίου οι χρήσεις φαίνονται καλύτερα αν το συνδυάσουμε με εξωτερικά αρχεία. Και πάλι, όσοι έχουν χρησιμοποιήσει λίγο Basic δεν θα δυσκολευτούν με τον επόμενο κώδικα:

Sub Load_report_file(myFile as String)
  dim filenumber As Integer
  dim iLine As String
  dim pText As String
  filenumber = Freefile
  open myFile For Input As filenumber
  while not EOF(filenumber)
      Line Input #filenumber, iLine
      if (iLine <> “”) then
          pText = PText & iLine
      else
          add_paragraph(pText)
          pText=””
      end if
  wend
  if (PText<>””) then
      add_paragraph(pText)
  end if
  close #filenumber
End Sub

Αυτή η υπορουτίνα παίρνει ως παράμετρο το όνομα αρχείου, το οποίο σκανάρει ψάχνοντας για ολοκληρωμένες παραγράφους. Εάν βρει μια παράγραφο (στην ουσία ψάχνει για κενή γραμμή) θα τη στείλει σε ένα νέο έγγραφο. Εάν δεν βρει θα συνεχίσει μέχρι το τέλος της παραγράφου. Εδώ πρέπει να διευκρινίσουμε ορισμένα πραγματάκια. Το πρώτο είναι η χρήση της Freefile. H δήλωση open αναμένει από εμάς να καθορίσουμε ένα μοναδικό ακέραιο ως αριθμό αναφοράς του αρχείου. Θα μπορούσαμε να δώσουμε ότι αριθμό θέλαμε, αλλά τότε θα έπρεπε να θυμόμαστε ότι αριθμούς έχουμε ήδη χρησιμοποιήσει (πράγμα σημαντικό όταν έχουμε περισσότερα από ένα αρχεία ανοικτά). Γι' αυτό υπάρχει η Freefile, που αναθέτει αυτόματα έναν αριθμό από μια σειρά αριθμών. Το δεύτερο πράγμα που πρέπει να πούμε είναι το γιατί υπάρχει μια δεύτερη δήλωση add_paragraph έξω από το βρόχο while...wend. Ο λόγος είναι για να “πιάσουμε” την τυχούσα παράγραφο στο τέλος του αρχείου που δεν τερματίζεται από μια κενή γραμμή. Τώρα λοιπόν μπορούμε να χρησιμοποιήσουμε την παραπάνω ρουτίνα για να διαβάσουμε δεδομένα από ότι αρχεία θέλουμε, π.χ.:

Sub Load_report_simple
  dim rep_dir as String
  rep_dir = "~/articles/lxf75_ooobasic1/demo/"
  load_report_file(rep_dir & "manager_header.txt")
  load_report_file(rep_dir & "body.txt")
End Sub

Φυσικά, ακόμα δεν φαίνονται τα πλεονεκτήματα του να γράφουμε ένα έγγραφο με αυτόν τον τρόπο, αφού θα ήταν ευκολότερο να γράψουμε απευθείας τα δεδομένα σε ένα έγγραφο και να το αποθηκεύσουμε ή να το τυπώσουμε. Για να γίνει λοιπόν πιο χρήσιμος ο κώδικάς μας, μπορούμε να δώσουμε τη δυνατότητα επιλογής:

Sub Load_report (optional reportType as integer)
  const rep_dir as string = "~/articles/lxf_ooobasic1/demo/"
  if isMissing(reportType) then
      reportType = 1
  end if
  select case reportType
  case 1
      load_report_file(rep_dir & "manager_header.txt")
  case 2
  load_report_file(rep_dir & "contractor_header.txt")
  end select
  load_report_file(rep_dir & "body.txt")
End Sub

Αυτή τη φορά υπάρχουν δύο αρχεία που μπορούμε να φορτώσουμε (manager_header.txt ή contractor_header.txt) ανάλογα με την τιμή της reportType μεταβλητής που “περνάμε” στην ρουτίνα. Στη συνέχεια όμως εισάγουμε το ίδιο αρχείο (body.txt). H Main θα πρέπει να γίνει κάπως έτσι:

Sub Main
  loadNewFile
  load_report(1)
End Sub

ή

Sub Main
  loadNewFile
  load_report(2)
End Sub

Εδώ το έμπειρο μάτι θα δει αμέσως ένα μειονέκτημα, ότι πρέπει να τροποποιούμε τον κώδικα κάθε φορά που πρέπει να φτιάξουμε διαφορετικές αναφορές. Χρειαζόμαστε, επομένως, ένα κομψό τρόπο για φορτώνουμε τα συγκεκριμένα αρχεία που θέλουμε κάθε φορά. Γι' αυτό θα φτιάξουμε ένα μικρό κουτί διαλόγου (dialog box) από το οποίο θα ελέγχουμε το τελικό αποτέλεσμα. Αλληλεπίδραση Οι διάλογοι του OO.org δημιουργούνται και αυτοί από τη Διαχείριση μακροεντολών. Αυτή τη φορά, όμως, πηγαίνουμε στην καρτέλα 'Διάλογοι' και μετά πατάμε Δημιουργία. Αν ονομάσουμε το νέο διάλογο 'dlgReport', μπορούμε να τον εμφανίσουμε με τον παρακάτω κώδικα:

dim dlgReport as object
Sub DlgReport_show
  basicLibraries.loadLibrary("Tools")
  dlgReport = loadDialog("Standard","dlgReport")
  dlgReport.execute()
End Sub

Θα χρειαστεί βέβαια να αλλάξουμε πάλι τη Main:

 Sub Main
   dlgReport_show
 End Sub

Προς το παρόν ο διάλογος που φτιάξαμε είναι κενός, απλώς εμφανίζεται και βγαίνουμε από αυτόν με ESC. Μπορούμε όμως να του προσθέσουμε κουμπιά και λίστες και με λίγο κώδικα να τους βάλουμε ότι λειτουργίες θέλουμε. Για παράδειγμα, θα φτιάξουμε τον διάλογο έτσι ώστε να μπορούμε από αυτόν να επιλέξουμε το είδος του αρχείου που θέλουμε να ανοίξει το Writer. Γι' αυτό, θα χρειαστούμε ένα κουτί λίστας και ένα κουμπί. Επιλέγουμε το αντίστοιχο αντικείμενο από την εργαλειοθήκη και το σχεδιάζουμε πάνω στη φόρμα του διαλόγου. Κατόπιν με διπλό κλικ πάνω στο καθένα, δίνουμε στη λίστα και στο κουμπί τα ονόματα lstReport και btnReport αντίστοιχα ενώ βάζουμε και ένα πιο κατανοητό τίτλο στο κουμπί (π.χ. “Δημιουργία αναφοράς”). Το επόμενο λογικό βήμα θα ήταν να φορτώσουμε τη λίστα με λεπτομέρειες χρησιμοποιώντας τις μεθόδους του OO.org, δηλαδη το addItem:

Sub DlgReport_show
  basicLibraries.loadLibrary(“Tools”)
  dlgReport = loadDialog(“Standard”,”dlgReport”)
  dlgReport.lstReport.AddItem(“Managers”,0)
  dlgReport.lstReport.AddItem(“Contractors”,1)
  dlgReport.execute()
End Sub

Δυστυχώς το OO.org δεν δουλεύει έτσι. Αντίθετα πρέπει να δώσουμε τα εξής:

dim lstReport as object
sub DlgReport_show
  basicLibraries.loadLibrary("Tools")
  dlgReport = loadDialog("Standard","dlgReport")
  lstReport = dlgReport.getControl("lstReport")
  lstReport.AddItem("Managers",0)
  lstReport.AddItem("Contractors",1)
  dlgReport.execute()
End Sub

Η διαφορά έγκειται στο ότι ορίσαμε τη λίστα ως ξεχωριστό αντικείμενο και μετά αποκτήσαμε πρόσβαση σε αυτή μέσω της μεθόδου getControl του διαλόγου. Αφού ορίσαμε το lstReport ως καθολική παράμετρο και την αρχικοποιήσαμε, μπορούμε να την χρησιμοποιούμε σε κάθε υπορουτίνα που γράφουμε. Ενεργοποίηση κουμπιού Τώρα πρέπει να προσθέσουμε στο διάλογο λίγο κώδικα για το χειρισμό του κουμπιού όταν κάνουμε σε αυτό κλικ:

Sub BtnReport_Click
  loadNewFile
  load_report(lstReport.selectedItemPos)
End Sub

Τώρα το κουμπί του διαλόγου λειτουργεί (δηλαδή πατιέται) αλλά δυστυχώς δεν κάνει τίποτε άλλο. Λογικό είναι αυτό αφού δεν έχουμε γράψει κώδικα γι' αυτό. Με διπλό κλικ στο κουμπί εμφανίζονται οι ιδιότητες του. Εκεί πηγαίνουμε στην καρτέλα Συμβάντα και επιλέγουμε την ρουτίνα που θέλουμε να εκτελείται όταν κάνουμε κλικ στο κουμπί (με ESC κλείνουμε τις ιδιότητες). Κάπως έτσι αποκτάμε μια λειτουργική φόρμα διαλόγου με την οποία ελέγχουμε την δημιουργία ενός εκ των δύο διαφορετικών ειδών αρχείων. Λίγη μαγεία ακόμα Μέχρι τώρα φτιάξαμε αρκετά πραγματάκια, αλλά πάντοτε έπρεπε να τα αποθηκεύουμε χειροκίνητα. Επειδή όμως μας αρέσει η αυτοματοποίηση μπορούμε να προσθέσουμε λίγο κώδικα που να κάνει αυτόματη αποθήκευση:

Sub SaveMyFile (fileUrl as string)
  dim params()
  doc.saveAsUrl("file:/" & fileUrl, params())
  doc.close(true)
End Sub

Φυσικά, μπορεί να θέλουμε να τυπώνουμε αντί να αποθηκεύουμε τα έγγραφά μας. Κι αυτό είναι απλό με την εξής ρουτίνα:

 Sub PrintMyFile
   dim params()
   doc.print(params())
   doc.close(true)
 End Sub

Μέχρι τώρα μάθαμε πως να χειριζόμαστε έγγραφα του Writer και να εξάγουμε δεδομένα από εξωτερικά αρχεία. Ασχοληθήκαμε όμως μόνο με στατικά αρχεία. Το ενδιαφέρον είναι ότι μπορούμε άνετα να παράγουμε δυναμικά δεδομένα κάνοντας χρήση της μεθόδου SystemShellExecute του OO.org.

 Sub RunCommand (command as string)
   dim svc as object
   svc = createUnoService("com.sun.star.system.SystemShellExecute")
   svc.execute(command, "", 0)
 End Sub
 Sub BtnReport_Click
   const tmpfile as string = "/tmp/myfile.tmp"
   loadNewFile
   load_report(lstReport.getselectedItemPos())
   runCommand("df > " & tmpfile)
   load_report_file(tmpfile)
 End Sub

Εδώ η εντολή df εκτελείται στο κέλυφος του Linux και το αποτέλεσμά της αποθηκεύεται σε ένα αρχείο, το /tmp/myfile.tmp. Το περιεχόμενο αυτού του αρχείου φορτώνεται μετά σε ένα νέο έγγραφο του Writer που θα δείχνει σαν κι αυτό:

    ‘Filesystem 1K-blocks Used Available Use% Mounted on
     /dev/hda3 3470204 3089264 201816 94% /
     /dev/hda4 1510060 1064572 368780 75% /opt
     /dev/hda1 4593600 3732708 860892 82% /
     WINDOWS’.

Αυτό είναι χρήσιμο, αλλά δυστυχώς δεν είναι ιδιαίτερα ελκυστικό. Θα ήταν καλύτερα αν το αποτέλεσμα εμφανιζόταν σε κάποιο πίνακα. Στο φάκελο magazine/oobasic του DVD του τεύχους 10 έχουμε τον πλήρη κώδικα που χρειάζεται για να φορτώσουμε τα περιεχόμενα ενός αρχείο σε ένα πίνακα. Δυστυχώς, για λόγους χώρου δεν μπορούμε να το δούμε εδώ, αλλά ο κώδικας αυτός περιέχει χρήσιμες λειτουργίες της Basic όπως την Chr που επιστρέφει τον ASCII κωδικό ενός ακεραίου, την Array που φτιάχνει ένα array από strings (συμβολοσειρές) και την ubound που επιστρέφει το μέγιστο δείκτη σε ένα array. Μέχρι την επόμενη φορά, saludos! MΑCRO ORGANIZER Η Διαχείριση μακροεντολών (Macro Organizer) του ΟΟ.org είναι ο κατάλληλος τρόπος δημιουργίας και συντήρησης μακροεντολών, διαλόγων, αλλά και ολόκληρων βιβλιοθηκών. Από αυτό μπορούμε να δημιουργήσουμε νέα αντικείμενα, να τα επεξεργαστούμε ή να τα διαγράψουμε. Οι πολύ θαρραλέοι (ή όσοι θέλουν να μπλέξουν) μπορούν να πειράξουν ακόμα και τις ενσωματωμένες μακροεντολές του ίδιου του ΟΟ.org. Ο τρόπος πρόσβασης στο Organizer εξαρτάται από την έκδοση του ΟΟ.org που χρησιμοποιούμε. Στην έκδοση 1.1.4 από το μενού 'Εργαλεία' πρέπει να πάμε στο Μακροεντολές > Macro ... > Organizer, ενώ στην έκδοση 2.0 πηγαίνουμε από το μενού 'Εργαλεία' στο Μακροεντολές > Διαχείριση μακροεντολών > OpenOffice.org Basic > Διαχείριση. ΣΥΝΔΕΣΗ ΚΩΔΙΚΑ ΜΕ ΑΝΤΙΚΕΙΜΕΝΑ Μερικές γλώσσες προγραμματισμού, όπως οι Delphi, Kylix και Gambas κάνουν αυτόματα σύνδεση κώδικα με τα αντικείμενα. Στην ΟΟ.org Basic, όμως, πρέπει να φτιάχνουμε τον κώδικα και τα αντικείμενα ξεχωριστά και μετά να τα συνδέουμε μεταξύ τους. Η σύνδεση γίνεται από την καρτέλα Συμβάντα στις ιδιότητες του κάθε αντικειμένου (με δεξί κλικ στο αντικείμενο επιλέγουμε Ιδιότητες). ΓΡΗΓΟΡΕΣ ΣΥΜΒΟΥΛΕΣ

  • Η OO.org Basic είναι case-insensitive, δεν ξεχωρίζει κεφαλαία από μικρά, δηλαδή θα αναγνωρίσει την MyVariable ως ίδια με την MYVARIABLE ή την myvariable. Γι' αυτό δεν έχει σημασία πως θα γράφουμε μια μεταβλητή. Για τη δική μας ευκολία καλό είναι να επιλέξουμε ένα τρόπο και να παραμείνουμε σε αυτόν.
  • Για να κλείσουμε ένα κουτί διαλόγου πρέπει να πατήσουμε το κουμπί Esc.
  • Όταν δημιουργούμε ένα κουμπί, και γράφουμε τον κώδικα γι' αυτό, δεν ξεχνάμε να να συνδέσουμε τον κώδικα με το κουμπί από τις ιδιότητές του.

Αναδημοσίευση από το τεύχος 10

Δώσε αστέρια!

MO: (ψήφοι: 0)

Σχόλια

Πολύ καλό Δημήτρη.
Περιμένουμε και ένα αντίστοιχο για το calc αν και τώρα που μας έβαλες στο κλίμα όλα δείχνουν πιο εύκολα :-)

Πολύ καλά όλα αυτά, αλλά πως μπορώ να χρεισιμοποιήσω και εγώ τις μακροεντολές που είμαι άσχετος. Υπάρχει κάτι όπου μπορώ να απευθινθώ και να διαβάσω. Μάλιστα με ενδιαφέρει ιδιέτερα για το Bace, ευχαριστώ.

καλημερα

υπαρχει tab control οπως στην access στο open office base ??

μπορω να δημιουργησω ribon με buttons με icon ??

ευχαριστω