Macro Python Assistant Window/it

Uno dei potenti aspetti di Python è la sua Console che serve sia come dispositivo di output che da interprete dinamico del codice sorgente. La macro Python Assistant Window (in seguito denominata 'PAW') fornisce ulteriori funzionalità alla console Python.



Descrizione
Essendo un ambiente di sviluppo moderno, Python presenta diversi vantaggi rispetto alle lingue più vecchie e dei loro ambienti di sviluppo. Di grande utilità è la console Python in cui si può inserire codice e ricevere i risultati in modo interattivo. I risultati possono poi essere copiati e utilizzati per costruire il codice sorgente Python (in un editor di sorgente) o incollati nella console Python in forma alterata per ottenere ulteriori output. Questo è un metodo molto potente per sviluppare un codice.

Nonostante la sua potenza, appare subito evidente che la console Python ha due difetti:
 * la console ha delle dimensioni limitate e per vedere i risultati del lavoro fatto poco tempo prima può essere necessario scorrere di parecchio la schermata, i risultati non sono andati persi, ma sono scomodi da recuperare
 * la console si azzera ogni volta che si chiude una sessione di FreeCAD, e ad ogni avvio di FreeCAD la console è vuota (a parte il messaggio di avvio di Python)

La PAW fornisce quanto segue:
 * è persistente tra le sessioni FreeCAD, da essa le cose non "scompaiono", a meno che l'utente le rimuova
 * ha un menù contestuale che permette di:
 * eseguire le operazioni standard di editazione: Copia & Incolla & Seleziona tutto
 * copiare una selezione nella Console Python
 * copiare l'intero contenuto della PAW nella Console Python
 * aggiungere il contenuto della Console Python alla PAW
 * inserire dei marcatori testuali che facilitano la gestione del testo
 * selezione tra qualsiasi due marcatori consecutivi
 * rimuovere il prefisso ">>> " usato dalla Console Python per indicare l'output
 * ridurre più righe vuote a una singola riga vuota
 * si può personalizzare l'ambiente di lavoro, per gestire la PAW tramite un pulsante GUI (nello stesso modo della finestra principale di FreeCAD)

Installazione
Tutto il codice di pythonAssistantWindow.FCMacro è contenuto in una macro, quindi per installarla basta copiare il suo codice nella appropriata directory delle Macro. Dopo, si può invocare pythonAssistantWindow dal menu Macro, dalla console Python o da un pulsante della barra degli strumenti (il metodo preferito).
 * per informazioni su come installare il codice delle macro vedere la pagina Come installare le macro
 * per informazioni su come installarla abbinata a un pulsante in una barra degli strumenti vedere la pagina Personalizzare la barra degli strumenti

Nota: Per coordinare la memoria persistente all'interno di FreeCAD viene utilizzata una variabile globale.

Nota: Nella directory "UserAppData" viene usato un file di testo per memorizzare il contenuto testuale della PAW tra le sessioni di FreeCAD.

Uso
La PAW si utilizza meglio se ha un pulsante su una barra degli strumenti. Può anche essere eseguita dal menu Macro o incollando il codice nella Console Python, ma queste ultime due opzioni riducono notevolmente la sua facilità d'uso.

Quando si avvia FreeCAD l'unica traccia della PAW è un pulsante sulla barra degli strumenti. Cliccando sul pulsante causerà:
 * si apre la PAW nell'angolo in basso a destra
 * secondo le impostazioni predefinite occupa circa 1/3 della larghezza e dell'altezza dello schermo, il resto può essere utilizzato dalla finestra principale FreeCAD
 * nella finestra appare l'ultimo contenuto visualizzato nella PAW - non ci dovrebbe essere alcuna differenza dall'ultima volta in cui è stato utilizzata
 * alla prima esecuzione la PAW è vuota
 * quando la PAW è già aperta, ma è nascosta da altre finestre, viene portata in primo piano per renderla visibile
 * quando si chiude la PAW il suo contenuto viene scritto nel file e la finestra scompare - non vi è alcun dialogo di conferma
 * viceversa, se si esce da FreeCAD (Menu -> Esci) appare una finestra di dialogo che chiede se si vuole salvare le modifiche non salvate nella PAW

La maggior parte della funzionalità della PAW sono fornite dal menu contestuale, le opzioni sono: - fornisce la funzione standard di Copia - la selezione corrente viene copiata alla fine della Console Python - tutto il contenuto della PAW viene copiato alla fine della Console Python - fornisce la funzione standard di Incolla - il contenuto della Console Python viene copiato alla fine della PAW - notare che i contenuti della Console Python possono essere una miscela di codice Python, output del codice Python, messaggi di errore, output di qualsiasi parte di FreeCAD - seleziona tra marcatori - i marcatori sono utilizzati per suddividere il testo della PAW in sezioni, e quando i contenuti sono sezionati, ogni sezione può essere selezionata individualmente e lavorata con (ad esempio copia, copia nella Console Python, Elimina). I marcatori consentono a sequenze separate e indipendenti di dichiarazioni Python di esistere nella PAW, e di essere poi gestite e lavorate singolarmente. - fornisce la funzione standard di Seleziona tutto - fornisce la funzione standard di Elimina che cancella tutti itest della PAW - inserisce un marcatore testuale nella posizione corrente del cursore - dopo aver copiato l'output della Console Python nella PAW, le linee che prodotte come output dei comandi Python eseguiti hanno il prefisso ">>>", questa opzione rimuove tali prefissi in modo che l'output può essere utilizzato come contesto libero da dati - compatta il testo, eliminando le righe vuote multiple - porta in primo piano una finestra modale con tre controlli: - il contenuto della finestra PAW viene scritto in un file selezionato dall'utente - il contenuto della PAW non viene alterato - la finestra PAW viene chiusa e il contenuto scritto in un file a memoria persistente Nota: Non appare alcun dialogo, il salvataggio viene fatto automaticamente
 * Copy
 * Copy selection to console
 * Copy contents to console
 * Paste
 * Append contents of Python Console to PAW
 * Select between Markers
 * Select all
 * Clear
 * Insert marker
 * Remove ">>> "
 * Reduce multiple blank lines to single blank lines
 * Alter GUI settings
 * PythonAssistantWindowGui2.jpg
 * un cursore per impostare la percentuale della larghezza dello schermo dedicata alla PAW (ricordando che c'è una larghezza limite al di sotto della quale la finestra principale di FreeCAD non scende)
 * un cursore per impostare la percentuale dell'altezza dello schermo dedicata alla PAW
 * una coppia di pulsanti di opzione per indicare se la PAW deve essere posizionata nella parte superiore o inferiore del lato sinistro dello schermo
 * Save as file
 * Close window

User Interface
The user interface is a simple text editing window, there is one button to start the PAW and a contextual menu of options to perform on the text in the text editing window. The options are described in the Usage section.

Example
In January your co-worker needs some help with the Python code to update a file. You write and debug it on your computer and send him the source code. You have come up with 3 different ways to do the job and are not sure which is the best to keep. You copy all 3 finished versions to the PAW and separate them by Markers.

As the month of May starts, you are happily working on the bottle project using FreeCAD. There is some problem getting the exact Python code to generate what is required so you model this graphically and the equivalent Python code is generated on the Python Console. Using PAW you copy the contents of the Python Console to the PAW. You shape it by moving it back and running, copying the results, modifying them in PAW and copying them back until you are happy.

The next Monday your boss rushed over to say that there is a big requirement for a CAD operator at the plant where they are having problems with the folding sequence for the new packaging stream. You will fly there the same day and be there for 2 weeks. You complete your assignment and return to your normal office space.

When you get back it is obvious that people have been using your area for a lunch area so you have to clean up a bit. Upon starting FreeCAD and clicking the PAW button on the toolbar, your work from before is sitting there as if it is from the day before. You realise that the solution to your bottle design lies in the file code you wrote back in January along with what you left 2 weeks earlier. Quickly you can select and copy the code segments to the Python Console to execute and fine tune them.

Once the code is stable then you can save it to a file for either e Python file or a FreeCAD mAcro file.

Options
About the only option for the PAW is the ability to alter it's initial display size and shape in accordance with the size and shape fo the main FreeCAD window. There are 3 constant values in the Python code which initial size and placement of the PAW.

Remarks
There is a very simple proof of concept for a persistent storage work in this code. It may be of use to anyone else requiring such a capacity.

Links
none (so far)

Script
Column: {}".format(		#	self.text_editor.textCursor.blockNumber,		#	self.text_editor.textCursor.columnNumber))		#print self.text_editor.textCursor.position+self.text_editor.textCursor.columnNumber		pass	def onSaveAsFile(self):		filePath = QtGui.QFileDialog.getSaveFileName(parent=None,caption="Save contents as",dir=expanduser("~"),filter="*.txt")		file = open(filePath[0],"w")		file.write(self.text_editor.toPlainText)		file.close	def closeEvent(self,event):		# write out contents for next session		file = open(persistenceFile,"w")		file.write(self.text_editor.toPlainText)		file.close		# clear global flag		del FreeCAD.PythonAssistantWindowStatus		self.close

class GetGuiConfigParams(QtGui.QMainWindow): """"""	def __init__(self, parentWindow): self.parentWindow = parentWindow super(GetGuiConfigParams, self).__init__ self.initUI(parentWindow) def initUI(self, parentWindow): """Constructor""" self.result							= userCancelled # grab geometry from our parent so we can tell if user has changed values self.initialParentWindowX			= self.parentWindow.geometry.x		self.initialParentWindowY			= self.parentWindow.geometry.y		self.initialParentWindowH			= self.parentWindow.geometry.height self.initialParentWindowW			= self.parentWindow.geometry.width self.initialHeightSliderSetting		= self.initialParentWindowH/float(availableHeight)*100 self.initialWidthSliderSetting		= self.initialParentWindowW/float(availableWidth-interWindowGap)*100 # set some fixed GUI attributes width								= 450 height								= 40 buttonWidth							= 80 sliderWidth							= 100 self.setWindowTitle("GUI Configuration") self.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint) self.resize(width, height) self.widthSlider					= self.initialWidthSliderSetting self.heightSlider					= self.initialHeightSliderSetting #		centralWidget 						= QtGui.QWidget(self) layout 								= QtGui.QGridLayout centralWidget.setLayout(layout) verticalLine 						= QtGui.QFrame # sliders widthSlider = QtGui.QSlider(QtCore.Qt.Horizontal, self) widthSlider.setFocusPolicy(QtCore.Qt.NoFocus) widthSlider.valueChanged[int].connect(self.widthSliderChangeValue) widthSlider.setFixedWidth(sliderWidth) widthSlider.setValue(self.initialWidthSliderSetting) heightSlider = QtGui.QSlider(QtCore.Qt.Horizontal, self) heightSlider.setFocusPolicy(QtCore.Qt.NoFocus) heightSlider.valueChanged[int].connect(self.heightSliderChangeValue) heightSlider.setFixedWidth(sliderWidth) heightSlider.setValue(self.initialHeightSliderSetting) # labels pawWidthLbl		= QtGui.QLabel("Python Assistant Window width", self) pawHeightLbl	= QtGui.QLabel("Python Assistant Window height", self) # radio buttons - window top or bottom self.rb1 = QtGui.QRadioButton("Window at Top",self) self.rb1.clicked.connect(self.onRb1) self.rb2 = QtGui.QRadioButton("Window at Bottom",self) self.rb2.toggle # set default value self.rb2.clicked.connect(self.onRb2) if self.parentWindow.geometry.y==0: self.rb1.toggle # cancel button cancelButton = QtGui.QPushButton('Cancel', self) cancelButton.clicked.connect(self.onCancel) cancelButton.setFixedWidth(buttonWidth) # OK button okButton = QtGui.QPushButton('OK', self) okButton.clicked.connect(self.onOk) okButton.setFixedWidth(buttonWidth) #		verticalLine.setFrameStyle(QtGui.QFrame.VLine) verticalLine.setSizePolicy(QtGui.QSizePolicy.Minimum,QtGui.QSizePolicy.Expanding) #		pawWidthLbl.setSizePolicy(QtGui.QSizePolicy.Expanding,QtGui.QSizePolicy.Expanding) pawHeightLbl.setSizePolicy(QtGui.QSizePolicy.Expanding,QtGui.QSizePolicy.Expanding) self.rb1.setSizePolicy(QtGui.QSizePolicy.Expanding,QtGui.QSizePolicy.Expanding) self.rb2.setSizePolicy(QtGui.QSizePolicy.Expanding,QtGui.QSizePolicy.Expanding) # populate layout layout.addWidget(widthSlider,0,0) layout.addWidget(pawWidthLbl,0,1) layout.addWidget(heightSlider,2,0) layout.addWidget(pawHeightLbl,2,1) layout.addWidget(verticalLine,0,2,3,1) layout.addWidget(self.rb1,0,3) layout.addWidget(self.rb2,2,3) layout.addWidget(cancelButton,3,1,QtCore.Qt.AlignRight) layout.addWidget(okButton,3,3) #		self.setCentralWidget(centralWidget) #		self.show def widthSliderChangeValue(self, value): self.widthSliderValue = value def heightSliderChangeValue(self, value): self.heightSliderValue = value def onRb1(self): pass def onRb2(self): pass def onCancel(self): self.result = userCancelled self.close def onOk(self): self.result = "OK" # the two slider values are the width and height of the Python Assistant Window # resize main FreeCAD window freeCadMainWidth	= ((1-(self.widthSliderValue/100.0)) * availableWidth)-(3*interWindowGap) FreeCADGui.getMainWindow.setGeometry(0, 0, freeCadMainWidth, availableHeight) # resize the PAW window newPawWidth			= availableWidth-freeCadMainWidth newPawHeight		= (self.heightSliderValue/100.0) * availableHeight if self.rb1.isChecked: newPawTop		= 0 else: newPawTop		= availableHeight - newPawHeight self.parentWindow.setGeometry(freeCadMainWidth+interWindowGap, newPawTop, newPawWidth-interWindowGap, newPawHeight) self.close




 * 1) Class definitions
 * 1) Function definitions

def onFreeCADShutdown: # this will be invoked when FreeCAD is told to shut down #QtGui.QMessageBox.information(None,"","FreeCAD shutting down") if FreeCAD.PythonAssistantWindowStatus[1]: reply = QtGui.QMessageBox.question(None, "",			"The Python Assistant Window has changes, do you want to save them?",			QtGui.QMessageBox.Yes