Macro Python Assistant Window/de

Generisches Makro-Symbol Erstellen Sie Ihr persönliches Symbol mit demselben Namen des Makros Makro Python-Assistentenfenster

Beschreibung
Dieses Makro bietet einen Arbeitsbereich zum Ausschneiden/Kopieren/Einfügen von Python-Code. Es ist segmentiert, sodass verschiedene Abschnitte ausgewählt werden können, und bleibt zwischen FreeCAD-Sitzungen bestehen.

Versionsmakro : 1.0
Datum der letzten Änderung : 2015-01-21
FreeCAD version : Alle
Herunterladen : Werkzeugleisten-Symbol
Autor: Piffpoof
Autor
Piffpoof
Herunterladen
Werkzeugleisten-Symbol
Links
Macro-Version
1.0
Datum der letzten Änderung
2015-01-21
FreeCAD-Version(s)
Alle
Standardverknüpfung
None
Siehe auch
None

Einer der leistungsstarken Aspekte von Python ist die Python-Konsole, die sowohl als Ausgabegerät als auch als dynamischer Interpreter für Quellcode dient. Das Python-Assistentenfenster (im Folgenden als „PAF” bezeichnet) bietet zusätzliche Funktionen für die Python-Konsole.

Temporary code for external macro link. Do not use this code. This code is used exclusively by Addon Manager. Link for optional manual installation: Macro


# This code is copied instead of the original macro code
# to guide the user to the online download page.
# Use it if the code of the macro is larger than 64 KB and cannot be included in the wiki
# or if the RAW code URL is somewhere else in the wiki.

from PySide import QtGui, QtCore

diag = QtGui.QMessageBox(QtGui.QMessageBox.Information,
    "Information",
    "This macro must be downloaded from this link\n"
    "\n"
    "http://pastebin.com/raw/2m0u94Z1" + "\n"
    "\n"
    "Quit this window to access the download page")

diag.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint)
diag.setWindowModality(QtCore.Qt.ApplicationModal)
diag.exec_()

import webbrowser 
webbrowser.open("http://pastebin.com/raw/2m0u94Z1")
<class="rawcodeurl"><a href="http://pastebin.com/raw/2m0u94Z1">raw code</a>

Beschreibung

Als moderne Entwicklungsumgebung hat Python gegenüber älteren Sprachen und deren Entwicklungsumgebungen viele Vorteile. Ein großer Vorteil ist die Python-Konsole, in der Code interaktiv eingegeben und die Ergebnisse empfangen werden können. (REPL). Diese Ergebnisse können dann kopiert und entweder zum Erstellen von Python-Quellcode (in einem Quellcode-Editor) verwendet oder in geänderter Form wieder in die Python-Konsole eingefügt werden, um weitere Ausgaben zu erhalten. Dies ist eine sehr leistungsfähige Methode zur Entwicklung von Code.

As powerful as it may be, two readily apparent shortcomings with the Python Console are:

  • the console is of limited size and the results of your work from 20 minutes ago may be scrolled far off the screen, they are not lost but they are awkward to retrieve
  • the console is cleared each time you quite your FreeCAD session, the console is empty (aside from the Python startup message) next time you start FreeCAD

The PAW provides the following:

  • it is persistent between FreeCAD sessions, things will not "disappear" from it unless the user removes them
  • it has a contextual menu that allows the following:
    • the standard editing operands: Copy & Paste & Select All
    • Copy a selection to the Python Console
    • Copy the complete contents of the PAW to the Python Console
    • Append the contents of the Python Console to the PAW
    • insert textual markers that facilitate management of the text
    • selection between any two consecutive markers
    • remove the prefix ">>> " which the Python Console uses to denote output
    • reduce multiple blank lines to single blank lines
    • personalise the working environment by managing the PAW (as well as the main FreeCAD window) by a slider-based GUI

Installation

Der gesamte Code für pythonAssistantWindow.FCMacro befindet sich in einem Makro. Die Installation besteht also darin, den Code in das entsprechende Makroverzeichnis zu kopieren und pythonAssistantWindow über das Makro-Menü, die Python-Konsole oder eine Symbolleisten-Schaltfläche (die bevorzugte Methode) aufzurufen.

Hinweis: Eine globale Variable innerhalb von FreeCAD wird verwendet, um den persistenten Speicher zu koordinieren.

Hinweis: Eine Textdatei im Verzeichnis „UserAppData” wird verwendet, um den Textinhalt des PAF zwischen FreeCAD-Sitzungen zu speichern.

Anwendung

Das PAF lässt sich am besten als Schaltfläche in einer Symbolleiste verwenden. Es kann über das Makro-Menü oder durch Einfügen des Codes in die Python-Konsole ausgeführt werden, aber die beiden letztgenannten Optionen beeinträchtigen die Benutzerfreundlichkeit erheblich.

When FreeCAD is started there will be no sign of the PAW, other than a button on a toolbar. Clicking the button will cause:

  • the PAW to open in the lower right corner
    • the default settings are for the about 1/3 of the screen width to be dedicated to the PAW with the remainder being used by the main FreeCAD window, the height of the PAW will be about 1/3 of the window height
  • the contents of the PAW when it last ran will appear in the window - there should be no difference from the last time it was used
    • if the PAW has not run before then the contents will be empty
  • if the PAW is already open but hidden by other windows then it will be raised to the top so that it becomes visible
  • closing the PAW will cause the contents to be written to file and the window will close - there is no Dialog asking whether to save or not
  • however there is a Dialog asking whether to save if FreeCAD is quit (Menu->Quit FreeCAD) with unsaved changes in thePAW

Most of the functionality for the PAW is provided by the contextual menu, the options are:

  • Copy
    - provide the standard Copy function
  • Copy selection to console
    - the current selection is copied to the end of the Python Console
  • Copy contents to console
    - the complete contents of the PAW is copied to the end of the Python Console
  • Paste
    - provide the standard Paste function
  • Append contents of Python Console to PAW
    - the copies of the Python Console are placed at the end of the PAW - note that the contents of the Python Console may be a mixture of Python Code, output from Python Code, error message text, output from any part of FreeCAD
  • Select between Markers
    - markers are used to divide up the text of the PAW into sections, once the contents are in sections then a section can be selected individually and worked with (e.g. Copy, Copy to the Python Console, Delete). The intention of Markers is to allow separate and unrelated sequences of Python statement to exist in the PAW, and then be managed and worked with individually.
  • Select all
    - provide the standard Select All function
  • Clearbr
    - provide the standard Clear function where all the text in the PAW is deleted
  • Insert marker
    - insert a textual Marker at the current cursor location
  • Remove ">>> "
    - after Python Console output is copied to the PAW, any lines which were output from executed Python commands will be prefixed with ">>> ", this option removed those prefixes so the output can be used as context free data
  • Reduce multiple blank lines to single blank lines
    - compacts the text by removing multiple blank lines
  • Alter GUI settings
    - brings up a modal window with three controls:
    • a slider to set the percentage of the screen width dedicated to the PAW (remembering that there is a certain width which the FreeCAD main window will not go below)
    • a slider to set the percentage of the height of the screen dedicated to the PAW
    • a pair of radio buttons to indicate whether the PAW should be placed at the top or bottom of the left hand side of the screen
  • Save as file
    - the contents of the PAW window are written out to a user selected file - the contents of the PAW are not altered
  • Close window
    - the PAW window is closed and the contents written out to the persistent storage file
    Note: there is no Dialog asking about saving, it is done automatically

Benutzeroberfläche

Die Benutzeroberfläche ist ein einfaches Textbearbeitungsfenster mit einer Schaltfläche zum Starten von PAF und einem Kontextmenü mit Optionen für die Bearbeitung des Textes im Textbearbeitungsfenster. Die Optionen werden im Abschnitt Verwendung beschrieben.

Beispiele

Ein erstes Beispiel

Im Januar benötigt Ihr Kollege Hilfe beim Python-Code, um eine Datei zu aktualisieren. Sie schreiben und debuggen den Code auf Ihrem Computer und senden ihm den Quellcode. Sie haben drei verschiedene Möglichkeiten gefunden, um die Aufgabe zu lösen, und sind sich nicht sicher, welche davon die beste ist. Sie kopieren alle drei fertigen Versionen auf den PAF und trennen sie durch Markierungen.

Zu Beginn des Monats Mai arbeiten Sie fröhlich mit FreeCAD an Ihrem Flaschenprojekt. Es gibt einige Probleme, den genauen Python-Code zu finden, um das Gewünschte zu generieren, also modellieren Sie dies grafisch und der entsprechende Python-Code wird in der Python-Konsole generiert. Mit PAF kopieren Sie den Inhalt der Python-Konsole in PAF. Sie formen ihn, indem Sie ihn zurückverschieben und ausführen, die Ergebnisse kopieren, sie in PAF modifizieren und zurückkopieren, bis Sie zufrieden sind.

Am nächsten Montag kommt Ihr Chef zu Ihnen und sagt, dass im Werk dringend ein CAD-Operator benötigt wird, da es Probleme mit der Faltsequenz für den neuen Verpackungsstrom gibt. Sie fliegen noch am selben Tag dorthin und bleiben zwei Wochen lang vor Ort. Sie erledigen Ihren Auftrag und kehren dann an Ihren normalen Arbeitsplatz zurück.

Als Sie zurückkommen, ist offensichtlich, dass andere Ihren Bereich als Mittagspausenraum genutzt haben, sodass Sie ein wenig aufräumen müssen. Wenn Sie FreeCAD starten und auf die Schaltfläche PAF in der Symbolleiste klicken, finden Sie Ihre Arbeit von gestern vor, als wäre sie erst gestern entstanden. Sie erkennen, dass die Lösung für Ihr Flaschendesign in dem Dateicode liegt, den Sie im Januar geschrieben haben, zusammen mit dem, was Sie zwei Wochen zuvor hinterlassen haben. Schnell können Sie die Code-Segmente auswählen und in die Python-Konsole kopieren, um sie auszuführen und zu optimieren.

Sobald der Code stabil ist, können man ihn entweder als Python-Datei oder als FreeCAD-Makrodatei speichern.

Ein weiteres Beispiel

Sie versuchen herauszufinden, was mit den Rotationswerten verschiedener Objekte in einer Baugruppe nicht stimmt. Von den 27 Objekten können Sie kein Muster erkennen, welche Objekte betroffen sind. Also schreiben Sie ein paar Zeilen Python-Code, um die fehlerhaften Objekte zu isolieren, und fügen diesen Code in die PAF ein. Dann schreiben Sie ein paar Zeilen Python-Code, um die Label- und Rotationswerte zu erhalten, und fügen diesen Code ebenfalls in die PAF ein. Kopieren Sie den Code, um alle Objekte aufzulisten, die Sie sehen möchten, und wiederholen Sie dies für jedes Objekt – alles im PAF. Die Python-Konsole enthält alle diese Ergebnisse (einschließlich Tippfehlern und Fehlermeldungen), aber sie ist längst aus dem sichtbaren Bereich des Fensters herausgescrollt. Jetzt haben Sie also einen übersichtlichen Satz von Python-Zeilen, den Sie aus dem PAF kopieren, in die Python-Konsole einfügen und schon haben Sie die genaue Liste der Objekte, die debuggt werden müssen – zusammen mit ihren spezifischen Werten. Bei Bedarf können Sie die Ergebnisse aus der Python-Konsole dann wieder in den PAF einfügen – setzen Sie ihnen das Python-Kommentarzeichen („#“) vor, wenn Sie sie im Python-Code speichern und ausführbar halten möchten. Schließlich können Sie diese Zusammenstellung aus dem PAF in einer Datei speichern, damit sie sicher auf der Festplatte liegt. So sind Sie bereit für das nächste Mal, wenn Sie die Logik im Code übernehmen oder anpassen müssen.

Optionen

Die einzige Option für das PAW ist die Möglichkeit, seine anfängliche Anzeigegröße und -form entsprechend der Größe und Form des FreeCAD-Hauptfensters zu ändern. Es gibt drei Konstanten im Python-Code, die die anfängliche Größe und Platzierung des PAW festlegen.

Anmerkungen

Dieser Code enthält einen sehr einfachen Konzeptnachweis für einen persistenten Speicher. Er kann für alle nützlich sein, die eine solche Kapazität benötigen.

Verweise

keine (bisher)

Skript

Wenn die Skriptliste nicht mit den FreeCAD-Versionsinformationen und einer letzten Zeile „thus ends the macro...“ endet, hat das Wiki das Skript erneut verschluckt und es muss es erneut heruntergeladen oder kopiert werden von [ungekürzte Skript auf pastebin.com]

ToolBar Icon

Macro_Python_Assistant_Window.FCMacro

#
#
#				Python Assistant Window
#				v 0.1 initial release
#
#
#***********************************************************************************
#
# provide a text editing window with functions to aid in coding Python within FreeCAD
#
############ To Do ############
# - contextual window doesn't fire if cursor is on last position in file
# - executing "from PySide import QtGui, QtCore" on console seems to close window
# - is it possible to copy code to console and then select and execute it?
##############################
#***********************************************************************************
# The next three variables define the width and height and vertical positioning
# of the Python Assistant Window
# 'pawWidthPercent' specifies the percentage of the screen width to be assigned to the Python Assistant Window
# 'pawHeightPercent' specifies the percentage of the screen height to be assigned to the Python Assistant Window
# 'pawAtBottomFlag' specifies if the Python Assistant Window is at the top or the bottom
# The Python Assistant Window is automatically placed at the left,
# so pawWidthPercent = 26, pawHeightPercent = 41, pawAtBottomFlag = False will cause the
# following:
# 1) the main FreeCAD window will be placed in the upper left corner of the screen,
#	 it's height will be 100% of the screen height,
#	 it's width will be 74% (=100%-26%) of the screen
# 2) the Python Assistant Window will be placed in the left side of the screen,
#	 it's height will be 41% of the screen height,
#	 it's width will be 26% of the screen
#	 it will be at the top (leaving empty space below it)
# The empty space (either above or below the Python Assistant Window),
# is left for the text editor (for editing the Macros) to be placed in.
#
pawWidthPercentInitial		= 37.5 # percent of the screen width
pawHeightPercentInitial		= 32.0 # percent of the screen height
pawAtBottomFlagInitial		= True
#***********************************************************************************
# import statements
import sys, operator, os
from os.path import expanduser
from PySide import QtGui, QtCore

# UI Class definitions

class PythonAssistantWindow(QtGui.QMainWindow):
	""""""
	def __init__(self, pythonTextToEdit):
		self.textIn = pythonTextToEdit
		super(PythonAssistantWindow, self).__init__()
		self.initUI(pythonTextToEdit)
	def initUI(self, pythonTextToEdit):
		"""Constructor"""
		# set default return value and pointer to subsequent child window
		self.result			= userCancelled
		self.childWindow	= None
		self.alertWindow	= None
		# set window dimensions for Python Advisor Window from the constants at the top of Macro file
		self.pawWinWidth	= pawWidthPercentInitial/100.0 * availableWidth
		self.pawWinHeight	= pawHeightPercentInitial/100.0 * availableHeight
		self.left			= screenWidth - self.pawWinWidth
		if pawAtBottomFlagInitial:
			self.top		= screenHeight - self.pawWinHeight
		else:
			self.top		= 0		
		self.editorHeight	= self.pawWinHeight
		# set dimensions for main FreeCAD window
		self.mainWinWidth	= availableWidth - (self.pawWinWidth+interWindowGap)
		self.mainWinHeight	= availableHeight
		# define main window
		FreeCADGui.getMainWindow().setGeometry(0, 0, self.mainWinWidth, self.mainWinHeight)
		# now set up this window
		self.setGeometry(self.left, self.top, self.pawWinWidth, self.pawWinHeight)
		self.setWindowTitle("Python Assistant Window")
		#
		centralWidget 		=  QtGui.QWidget(self)
		layout 				=  QtGui.QGridLayout()
		centralWidget.setLayout(layout)
		# set up text editing widget
		self.text_editor = QtGui.QPlainTextEdit(self)
		self.text_editor.move(0,0)
		self.text_editor.resize(self.pawWinWidth,self.editorHeight)
		self.text_editor.appendPlainText(self.textIn)
		self.text_editor.setSizePolicy(QtGui.QSizePolicy.Expanding,QtGui.QSizePolicy.Expanding)
		self.text_editor.textChanged.connect(self.onTextChanged)
		# set up a monospace font for the text editor to match the Python console
		font = QtGui.QFont()
		font.setFamily("Courier")
		font.setStyleHint(QtGui.QFont.Monospace)
		font.setFixedPitch(True)
		font.setPointSize(12)
		self.text_editor.setFont(font)
		#self.text_editor.cursorPositionChanged.connect(self.onCursorPosition)
		self.cursor = self.text_editor.textCursor()
		# populate layout
		layout.addWidget(self.text_editor,0,0)
		self.setCentralWidget(centralWidget)
		# set contextual menu options for text editing widget
		# menu dividers
		mnuDivider1 = QtGui.QAction(self)
		mnuDivider1.setText(menuDividerText)
		mnuDivider1.triggered.connect(self.onMenuDivider)
		mnuDivider2 = QtGui.QAction(self)
		mnuDivider2.setText(menuDividerText)
		mnuDivider2.triggered.connect(self.onMenuDivider)
		mnuDivider3 = QtGui.QAction(self)
		mnuDivider3.setText(menuDividerText)
		mnuDivider3.triggered.connect(self.onMenuDivider)
		mnuDivider4 = QtGui.QAction(self)
		mnuDivider4.setText(menuDividerText)
		mnuDivider4.triggered.connect(self.onMenuDivider)
		# clear text
		mnuClear = QtGui.QAction(self)
		mnuClear.setText("Clear")
		mnuClear.triggered.connect(self.onClear)
		# paste copy/paste buffer
		mnuPaste = QtGui.QAction(self)
		mnuPaste.setText("Paste")
		mnuPaste.triggered.connect(self.onPaste)
		# paste contents of console
		mnuAppendFromConsole = QtGui.QAction(self)
		mnuAppendFromConsole.setText("Append contents of console")
		mnuAppendFromConsole.triggered.connect(self.onAppendFromConsole)
		# select between markers
		mnuSelectMarkers = QtGui.QAction(self)
		mnuSelectMarkers.setText("Select between markers")
		mnuSelectMarkers.triggered.connect(self.onSelectMarkers)
		# select all
		mnuSelectAll = QtGui.QAction(self)
		mnuSelectAll.setText("Select all")
		mnuSelectAll.triggered.connect(self.onSelectAll)
		# insert marker
		mnuInsertMarker = QtGui.QAction(self)
		mnuInsertMarker.setText("Insert marker")
		mnuInsertMarker.triggered.connect(self.onInsertMarker)
		# remove console generated ">>> " character strings
		mnuStripPrefix = QtGui.QAction(self)
		mnuStripPrefix.setText("Remove '>>> '")
		mnuStripPrefix.triggered.connect(self.onStripPrefix)
		# remove blank lines
		mnuReduceBlankLines = QtGui.QAction(self)
		mnuReduceBlankLines.setText("Delete multiple blank lines")
		mnuReduceBlankLines.triggered.connect(self.onReduceBlankLines)
		# copy selection
		mnuCopy = QtGui.QAction(self)
		mnuCopy.setText("Copy")
		mnuCopy.triggered.connect(self.onCopy)
		# copy selection to console
		mnuCopySelectionToConsole = QtGui.QAction(self)
		mnuCopySelectionToConsole.setText("Copy selection to console")
		mnuCopySelectionToConsole.triggered.connect(self.onCopySelectionToConsole)
		# copy to console
		mnuCopyToConsole = QtGui.QAction(self)
		mnuCopyToConsole.setText("Copy contents to console")
		mnuCopyToConsole.triggered.connect(self.onCopyToConsole)
		# save as file
		mnuSaveAsFile = QtGui.QAction(self)
		mnuSaveAsFile.setText("Save contents to file")
		mnuSaveAsFile.triggered.connect(self.onSaveAsFile)
		# close window
		mnuCloseWindow = QtGui.QAction(self)
		mnuCloseWindow.setText("Close window")
		mnuCloseWindow.triggered.connect(self.onCloseWindow)
		# alter GUI settings
		mnuSettings = QtGui.QAction(self)
		mnuSettings.setText("=Alter GUI settings=")
		mnuSettings.triggered.connect(self.onSettings)
		# define menu and add options
		self.text_editor.setContextMenuPolicy(QtCore.Qt.ActionsContextMenu)
		self.text_editor.addAction(mnuCopy)
		self.text_editor.addAction(mnuCopySelectionToConsole)
		self.text_editor.addAction(mnuCopyToConsole)
		self.text_editor.addAction(mnuDivider1)
		self.text_editor.addAction(mnuPaste)
		self.text_editor.addAction(mnuAppendFromConsole)
		self.text_editor.addAction(mnuSelectMarkers)
		self.text_editor.addAction(mnuSelectAll)
		self.text_editor.addAction(mnuClear)
		self.text_editor.addAction(mnuDivider2)
		self.text_editor.addAction(mnuInsertMarker)
		self.text_editor.addAction(mnuStripPrefix)
		self.text_editor.addAction(mnuReduceBlankLines)
		self.text_editor.addAction(mnuDivider3)
		self.text_editor.addAction(mnuSaveAsFile)
		self.text_editor.addAction(mnuSettings)
		self.text_editor.addAction(mnuCloseWindow)
		#
		self.show()
	#----------------------------------------------------------------------
	def onMenuDivider(self):
		# just a divider in the menu so we don't do anything
		pass
	def onClear(self):
		# clear editing field
		self.text_editor.clear()
	def onPaste(self):
		# paste contents of system copy/paste buffer into QPlainTextEdit field
		self.text_editor.paste()
	def onAppendFromConsole(self):
		# copy text from "Python console"
		mainWindow	= FreeCADGui.getMainWindow()
		pcDW		= mainWindow.findChild(QtGui.QDockWidget, "Python console")
		pcPTE		= pcDW.findChild(QtGui.QPlainTextEdit, "Python console")
		consoleStr	= pcPTE.document().toPlainText()
		self.text_editor.appendPlainText(copyFromConsoleText)
		self.text_editor.appendPlainText("")
		self.text_editor.appendPlainText(consoleStr)
	def onCopy(self):
		# copy selected text to system copy/paste buffer
		self.text_editor.copy()
	def onCopySelectionToConsole(self):
		# copy selected text to "Python console"
		mainWindow	= FreeCADGui.getMainWindow()
		pcDW		= mainWindow.findChild(QtGui.QDockWidget, "Python console")
		pcPTE		= pcDW.findChild(QtGui.QPlainTextEdit, "Python console")
		#
		cursor		= self.text_editor.textCursor()
		cursorText	= self.text_editor.toPlainText()
		textToCopy = cursorText[cursor.selectionStart():cursor.selectionEnd()]
		if len(textToCopy)>0:
			pcPTE.appendPlainText(textToCopy)
	def onCopyToConsole(self):
		# copy text to "Python console"
		mainWindow	= FreeCADGui.getMainWindow()
		pcDW		= mainWindow.findChild(QtGui.QDockWidget, "Python console")
		pcPTE		= pcDW.findChild(QtGui.QPlainTextEdit, "Python console")
		pcPTE.appendPlainText(copyToConsoleText)
		pcPTE.appendPlainText()
	def onInsertMarker(self):
		# insert marker
		self.text_editor.insertPlainText(markerText)
	def onStripPrefix(self):
		# strip out ">>> " from text edit window
		self.text_editor.selectAll()
		if len(self.text_editor.toPlainText())>0:
			self.text_editor.selectAll()
			tmp = self.text_editor.toPlainText()
			self.text_editor.clear()
			self.text_editor.appendPlainText(tmp.replace(">>> ",""))
	def onReduceBlankLines(self):
		# reduce multiple blank lines to single blank lines
		contents = self.text_editor.toPlainText()
		self.text_editor.clear()
		self.text_editor.appendPlainText(os.linesep.join([s for s in contents.splitlines() if s]))
	def onSelectMarkers(self):
		cursor		= self.text_editor.textCursor()
		cursorText	= self.text_editor.toPlainText()
		bNum = cursor.blockNumber(); cNum = cursor.columnNumber()
		pos = cursor.position(); cursorTextLength = len(cursorText)
		occurrences = [i for i in range(len(cursorText)) if cursorText.startswith(markerText, i)]
		if len(occurrences)==0:
			self.alertWindow = QtGui.QMessageBox()
			self.alertWindow.setText("There are no markers...")
			self.alertWindow.show()
		elif len(occurrences)==1:
			hdrStart = occurrences[0]
			hdrEnd = hdrStart + markerTextLength
			if pos<hdrStart:
				selectStart = 0; selectEnd = hdrStart
				self.cursor.setPosition(selectStart)
				self.cursor.setPosition(selectEnd, QtGui.QTextCursor.KeepAnchor)
				self.text_editor.setTextCursor(self.cursor)
			if pos>hdrEnd:
				selectStart = hdrEnd; selectEnd = cursorTextLength
				self.cursor.setPosition(selectStart)
				self.cursor.setPosition(selectEnd, QtGui.QTextCursor.KeepAnchor)
				self.text_editor.setTextCursor(self.cursor)
		else:
			startOccurrences = list(); endOccurrences = list(occurrences)
			for i in range(len(occurrences)):
				startOccurrences.append(occurrences[i] + markerTextLength + 1)
			startOccurrences.insert( 0, 0)
			endOccurrences.insert( len(occurrences), cursorTextLength)
			for i in range(len(occurrences)+1):
				if startOccurrences[i]<pos<endOccurrences[i]:
					if i==0:
						selectStart = startOccurrences[i]
					else:
						selectStart = startOccurrences[i]-1
					selectEnd = endOccurrences[i]
					self.cursor.setPosition(selectStart)
					self.cursor.setPosition(selectEnd, QtGui.QTextCursor.KeepAnchor)
					self.text_editor.setTextCursor(self.cursor)
					break
	def onSelectAll(self):
		self.text_editor.selectAll()
	def onCloseWindow(self):
		self.close()
	def onSettings(self):
		# get new width (as %), height (as %), vertical flag
		self.childWindow = GetGuiConfigParams(self)
		pass
	def onTextChanged(self):
		FreeCAD.PythonAssistantWindowStatus[1] = True
	def onCursorPosition(self):
		#print ("Line: {} | 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()

#----------------------------------------------------------------------

# Class definitions
		
# 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 | QtGui.QMessageBox.No, QtGui.QMessageBox.No)
		if reply == QtGui.QMessageBox.Yes:
			# write out contents for next session
			file = open(persistenceFile,"w")
			file.write(FreeCAD.PythonAssistantWindowStatus[0].text_editor.toPlainText())
			file.close()
	del FreeCAD.PythonAssistantWindowStatus

# Constant definitions

userCancelled		= "Cancelled"
markerText			= "#=========== marker ===========\n"
markerTextLength	= len(markerText)
copyFromConsoleText	= "#=========== copy from console ==========="
copyToConsoleText	= "#============ copy to console ============"
menuDividerText		= "--------"
interWindowGap		= 3 # space between 2 windows for appearance sake
persistenceFile		= App.ConfigGet("UserAppData")+"PythonAssistantWindow.txt"
# code ***********************************************************************************
# put down user data saving routine for when FreeCAD exits
mw=Gui.getMainWindow()
mw.mainWindowClosed.connect(onFreeCADShutdown)
# get screen dimensions
screenWidth			= QtGui.QDesktopWidget().screenGeometry().width()
screenHeight		= QtGui.QDesktopWidget().screenGeometry().height()
# get dimensions for available space on screen
availableWidth		= QtGui.QDesktopWidget().availableGeometry().width()
availableHeight		= QtGui.QDesktopWidget().availableGeometry().height()

if not hasattr(FreeCAD,"PythonAssistantWindowStatus"):
	previousContents = ""
	if os.path.isfile(persistenceFile):
		# read contents of last session
		file = open(persistenceFile,"r")
		previousContents = file.read()
		file.close()
	# open window with contents from last session
	form = PythonAssistantWindow(previousContents)
	# save pointer to window so it can be located again and Raised when it becomes obscured
	FreeCAD.PythonAssistantWindowStatus = [None, False]
	FreeCAD.PythonAssistantWindowStatus[0] = form
else:
	# window is open so Raise it so it is visible
	FreeCAD.PythonAssistantWindowStatus[0].raise_()
	pass
#
#OS: Mac OS X
#Word size: 64-bit
#Version: 0.14.3703 (Git)
#Branch: releases/FreeCAD-0-14
#Hash: c6edd47334a3e6f209e493773093db2b9b4f0e40
#Python version: 2.7.5
#Qt version: 4.8.6
#Coin version: 3.1.3
#SoQt version: 1.5.0
#OCC version: 6.7.0
#
#thus ends the macro...