Add a line feed to a python gimp 2.8.4 script - gimp

I have a Python Gimp script that works well, but have one issue:
When I need to place multiple lines on an image, I have to create two separate text layers.
#Sample of text parameter xml file
<txtLyrs>
<extra_txtLyr>
<extra_txtLyr txtLyrName="lblLayer6" LyrFontColor="WHITE" \
LyrFontName="Interstate-Bold Bold" LyrFontSize="15.0" txtLyrString=\
"Some Text" txtX="460" txtY="331" txtlyr_height="17" txtlyr_width="73" index="71" />
<extra_txtLyr txtLyrName="lblLayer5" LyrFontColor="WHITE" \
LyrFontName="Interstate-Bold Bold" LyrFontSize="8.0" txtLyrString=\
"Some Text [10][13] Really Long Text" txtX="676" txtY="144" txtlyr_height="9"\
txtlyr_width="95" index="70" />
<extra_txtLyr txtLyrName="lblLayer4" LyrFontColor="WHITE" \
LyrFontName="Interstate-Bold Bold" LyrFontSize="8.0" txtLyrString=\
"Some Text" txtX="676" txtY="130" txtlyr_height="9" txtlyr_width="125" index="69" />
<extra_txtLyr txtLyrName="lblLayer3" LyrFontColor="WHITE" \
LyrFontName="Interstate-Bold Bold" LyrFontSize="15.0" txtLyrString=\
"Some Text" txtX="678" txtY="331" txtlyr_height="17" txtlyr_width="72" index="68" />
</extra_txtLyrs>
</txtLyrs>
#Constants removed to make smaller block
#Helper function to read parameters from xml file
def get_extratxt_btn_params( active_name ):
active_dgm = active_name
extraTxtLayers = xmldoc.getElementsByTagName("txtLyrs")[0]
if active_dgm:
siteTxtLayers = xmldoc.getElementsByTagName("extra_txtLyr")[0]
extraButtonTxtLayers = siteTxtLayers.getElementsByTagName("extra_txtLyr")
else:
siteTxtLayers = xmldoc.getElementsByTagName("other_txtLyr")[0]
extraButtonTxtLayers = siteTxtLayers.getElementsByTagName("other_txtLyr")
extraTxtParms = {}
for lyrParm in extraButtonTxtLayers:
lyrParams = [ lyrParm.getAttribute("txtLyrName"), \
lyrParm.getAttribute("LyrFontName"), \
lyrParm.getAttribute("LyrFontSize"), lyrParm.getAttribute("txtLyrString"), \
lyrParm.getAttribute("LyrFontColor"), lyrParm.getAttribute("txtlyr_height"), \
lyrParm.getAttribute("txtlyr_width"), lyrParm.getAttribute("txtX"), \
lyrParm.getAttribute("txtY")]
extraTxtParms [lyrParm.getAttribute("index")] = lyrParams
return extraTxtParms
##**function called by GIMP to create text layers from xml file
def dgm_extratxtlyr_create(image, drawable, title_color, inactive_color, active_color, \
alarm_color, normal_color, active_diagram):
txtlyrdict = get_extratxt_params( active_diagram )
d_sorted_by_value = OrderedDict(sorted(txtlyrdict.items(), key=lambda x: x[1]))
for k, txtlyr in d_sorted_by_value.items():
#Assign Layer Text
txtlayerName = txtlyr[3]
#Assign Layer Font Name
font_name = txtlyr[1]
#Assign Font Size
font_size = txtlyr[2]
#Assign Text color RGB values for colors are predefined constants because
#tuples don't pass correctly from xml
if txtlyr[4] == "RED":
textcolor = alarm_color
elif txtlyr[4] == "WHITE":
textcolor = inactive_color
elif txtlyr[4] == "GREEN":
textcolor = normal_color
elif txtlyr[4] == "BLACK":
textcolor = active_color
#Assign Text X Coordinate
xloc = txtlyr[7]
#Assign Text Y Coordinate
yloc = txtlyr[8]
#Create the new text layer
temp_layer = pdb.gimp_text_layer_new(image, txtlayerName, font_name, font_size, 0)
#Add it to the image
pdb.gimp_image_add_layer(image,temp_layer,-1)
#Change the color of the text
pdb.gimp_text_layer_set_color(temp_layer,textcolor)
#Move the text to the proper location
pdb.gimp_layer_translate(temp_layer, xloc, yloc)
#Set the text justification
pdb.gimp_text_layer_set_justification(temp_layer, 0)
How can I tell GIMP to add line feeds to the text layers, when I know they are too long?

Just add "new line" characters in your text string, before calling pdb.gimp_text_layer_new, as appropriate.
Btw, the variable you are naming "txtlayerName" (considerations about variable naming style apart) is the actual Text Content of the layer - not just the layer name. It happens that GIMP's usually name text layers by its contents by default - the parameter taken by gimp_text_layer_add_new is the actual text content on the image. You could also consider using the more complete call : "pdb.gimp_text_fontname` instead- which already adds the layer to the image, at the offset you want.
As for inserting the new lines using Python code, it is as simple as using the powerful langage syntax and methods for string manipulation to do that. For example, prior to creating the layer, you could do a lot of things.
This snippet replaces spaces by new lines if the layer is more than 12 characters long, and cut arbitrarily at each 6 chars if there are no spaces. It is naive, but you can get the idea and fashion your code:
if len(txtlayerName) > 12:
if " " in txtlayerName:
txtlayerName = txtlayerName.replace(" ", "\n")
else:
textlayerName = "\n".join(txtlayerName[i:i+6] for i in range(0, len(txtlayerName), 6))
This snippet allows for longer lines, assumiong there are several words in each line -
let's suppose you want a max-width of 80 chars, and there are several white-space separated words in the text:
textwidth = 80
result = []
txtline = []
index = 0
for word in textlayerName.split():
if index + len(word) < textwidth:
txtline.append(word)
index += len(word)
else:
result.append(" ".join(txtline))
txtline = [word]
index = len(word)
textlayerName = "\n".join(result)

This answer is very irrelevant to the question asked but when you try to search for adding font weight or making the text bold, you also come to this page like me.
There's no REAL solution
There's no procedure like gimp_text_layer_set_bold(text_layer))
however there's a workaround i.e., change your font. For Example: Change your font from Sans-serif to Sans-serif Bold. I hope it helps a wandering soul.

Related

Let knitr/kable display latex code for further editing (.Rmd file)

Similar to a previous question, I have to convert the chunk of code I used to create a kableExtra table from a Rmarkdown file into pure LaTeX format in Overleaf. Unfortunately, I still have not understood how I can get the latex code by running the KableExtra table in the Rmd file.
The following is the YAML header:
---
[...]
output:
pdf_document: default
html_document:
---
the following is my code:
{r, results='asis', echo=FALSE}
res_kbl = kableExtra::kbl(merg, longtable=TRUE, booktabs = T, caption = "Table 1", digits = 2) %>%
kableExtra::kable_styling(latex_options = c("striped", "HOLD_position"), font_size = 10) %>%
kableExtra::add_header_above(c(" " = 1, "in Complete sample" = 2, "in Restricted sample" = 2, "MAJ" = 4, "MIN" = 4, "MAJ" = 4, "MIN" = 4)) %>%
kableExtra::add_header_above(c(" " = 1, "No. of funds" = 4, "Fund Size Complete sample (USD million nominal)" = 8, "Fund Size Restricted sample (USD million nominal)" = 8)) %>%
kableExtra::add_header_above(c(" " = 5, "Complete sample" = 8, "Restricted sample" = 8)) %>%
kableExtra::row_spec(row = nrow(merg) - 1, underline = T, extra_css = "border-bottom: 2px solid;")
My question: How can I get the Latex code for the res_kbl table I have created?
Any suggestion would be very much appreciated!
If I understand right, you want to get the LaTex code of the kable res_tbl.
In this case,
Specify format = "latex" in kableExtra::kbl() (or knit::kable())
Next, writeLines(res_kbl)
Instead, you don't need to assign res_kbl variable.
You can use additional pipe operator with writeLines().
The result of writeLines(res_kbl) will give the code you want. But the code can be complex because kableExtra package has so many functionalities.
In addition when using only writeLines(), you don't need asis part in the chunk option. However, comment option can be a problem if you want to use it in the final document not in the console. Set the comment as blank text in that chunk.

Text file reading with dict in python

From bellow text file, read the text file into a python program and group all the words according to their first letter. Represent the groups in form of dictionary. Where the staring alphabet is the "key" and all the words starting with the alphabets are list of "values".
Text file is:
Among other public buildings in a certain town, which for many reason it will be prudent to
refine from mentioning, and to which i will assign no fictitious name, there is one anciently
common to most towns, great or small.
stream = open('file name', 'r')
str = ''
current = ' '
while current != '':
current = stream.read(50)
str += current
words = str.split(' ')
dict = {}
for w in words:
if not w[0] in dict:
dict[w[0]] = [w]
else:
dict[w[0]].append(w)
The dictionary is dict

Creating a checkbox and printing it to pdf file is not working using pdfbox 1.8.9 api

I'm using grails with pdfbox plugin. I'd like to print checkboxes in pdf some are checked and some are not.
To print checkbox I did not a direct way(Even by using PDCheckbox class). So I've used the other way to print the checkbox with tick mark using the below code:
public static writeInputFieldToPDFPage( PDPage pdPage, PDDocument document, Float x, Float y, Boolean ticked) {
PDFont font = PDType1Font.HELVETICA
PDResources res = new PDResources()
String fontName = res.addFont(font)
String da = ticked?"/" + fontName + " 10 Tf 0 0.4 0 rg":""
COSDictionary acroFormDict = new COSDictionary()
acroFormDict.setBoolean(COSName.getPDFName("NeedAppearances"), true)
acroFormDict.setItem(COSName.FIELDS, new COSArray())
acroFormDict.setItem(COSName.DA, new COSString(da))
PDAcroForm acroForm = new PDAcroForm(document, acroFormDict)
acroForm.setDefaultResources(res)
document.getDocumentCatalog().setAcroForm(acroForm)
PDGamma colourBlack = new PDGamma()
PDAppearanceCharacteristicsDictionary fieldAppearance =
new PDAppearanceCharacteristicsDictionary(new COSDictionary())
fieldAppearance.setBorderColour(colourBlack)
if(ticked) {
COSArray arr = new COSArray()
arr.add(new COSFloat(0.89f))
arr.add(new COSFloat(0.937f))
arr.add(new COSFloat(1f))
fieldAppearance.setBackground(new PDGamma(arr))
}
COSDictionary cosDict = new COSDictionary()
COSArray rect = new COSArray()
rect.add(new COSFloat(x))
rect.add(new COSFloat(new Float(y-5)))
rect.add(new COSFloat(new Float(x+10)))
rect.add(new COSFloat(new Float(y+5)))
cosDict.setItem(COSName.RECT, rect)
cosDict.setItem(COSName.FT, COSName.getPDFName("Btn")) // Field Type
cosDict.setItem(COSName.TYPE, COSName.ANNOT)
cosDict.setItem(COSName.SUBTYPE, COSName.getPDFName("Widget"))
if(ticked) {
cosDict.setItem(COSName.TU, new COSString("Checkbox with PDFBox"))
}
cosDict.setItem(COSName.T, new COSString("Chk"))
//Tick mark color and size of the mark
cosDict.setItem(COSName.DA, new COSString(ticked?"/F0 10 Tf 0 0.4 0 rg":"/FF 1 Tf 0 0 g"))
cosDict.setInt(COSName.F, 4)
PDCheckbox checkbox = new PDCheckbox(acroForm, cosDict)
checkbox.setFieldFlags(PDCheckbox.FLAG_READ_ONLY)
checkbox.setValue("Yes")
checkbox.getWidget().setAppearanceCharacteristics(fieldAppearance)
pdPage.getAnnotations().add(checkbox.getWidget())
acroForm.getFields().add(checkbox)
}
This code is working fine in my application, this method is adding checkboxes with tick marks also.
But I can see those rectangle checkboxes or tick marks in only pdf readers, not in all other readers(Like chrome default pdf viewer), and even when I try to print the pdf its not printing the checkboxes, rather its printing some random ASCII numbers.
Please let me know if there is any other way to do this or even if I have to refactor the code.
What is wrong
Your AcroForm checkbox field construction is wrong: You treat it as a text field for which a PDF reader should create an appearance based on the default appearance (DA) value of the field in particular if NeedAppearances is true.
Checkboxes are different, though: you do have to supply an appearance stream at least for the on state, cf. the specification ISO 32000-1:
A check box field represents one or more check boxes that toggle between two states, on and off, when manipulated by the user with the mouse or keyboard. Its field type shall be Btn and its Pushbutton and Radio flags (see Table 226) shall both be clear. Each state can have a separate appearance, which shall be defined by an appearance stream in the appearance dictionary of the field’s widget annotation (see 12.5.5, “Appearance Streams”). The appearance for the off state is optional but, if present, shall be stored in the appearance dictionary under the name Off. Yes should be used as the name for the on state.
(ISO 32000-1 section 12.7.4.2.3 "Check Boxes")
Thus, instead of constructing a DA entry you have to construct an AP ("appearances") entry, itself a dictionary with at least a N ("normal appearances") entry, itself a dictionary with at least an entry for the on state appearance which is recommended to be called Yes.
The specification provides an example which shows a typical check box definition:
1 0 obj
<< /FT /Btn
/T (Urgent)
/V /Yes
/AS /Yes
/AP << /N << /Yes 2 0 R /Off 3 0 R>>
>>
endobj
2 0 obj
<< /Resources 20 0 R
/Length 104
>>
stream
q
0 0 1 rg
BT
/ZaDb 12 Tf
0 0 Td
(4) Tj
ET
Q
endstream
endobj
3 0 obj
<< /Resources 20 0 R
/Length 104
>>
stream
q
0 0 1 rg
BT
/ZaDb 12 Tf
0 0 Td
(8) Tj
ET
Q
endstream
endobj
(The resources in 20 0 obj appear to include a font resource named ZaDb referencing ZapfDingbats.)
By the way, you mention that there is a PDF viewer which actually displays a tick for your document as is. You might want to inform their development that they are doing the wrong thing there.
An example
In a comment you asked for sample code and indicated that it was ok if it were for a current 2.0.x version of PDFBox. So I tried it and came up with this code:
PDDocument document = new PDDocument();
PDPage page = new PDPage();
document.addPage(page);
PDAcroForm acroForm = new PDAcroForm(document);
document.getDocumentCatalog().setAcroForm(acroForm);
COSDictionary normalAppearances = new COSDictionary();
PDAppearanceDictionary pdAppearanceDictionary = new PDAppearanceDictionary();
pdAppearanceDictionary.setNormalAppearance(new PDAppearanceEntry(normalAppearances));
pdAppearanceDictionary.setDownAppearance(new PDAppearanceEntry(normalAppearances));
PDAppearanceStream pdAppearanceStream = new PDAppearanceStream(document);
pdAppearanceStream.setResources(new PDResources());
try (PDPageContentStream pdPageContentStream = new PDPageContentStream(document, pdAppearanceStream))
{
pdPageContentStream.setFont(PDType1Font.ZAPF_DINGBATS, 14.5f);
pdPageContentStream.beginText();
pdPageContentStream.newLineAtOffset(3, 4);
pdPageContentStream.showText("\u2714");
pdPageContentStream.endText();
}
pdAppearanceStream.setBBox(new PDRectangle(18, 18));
normalAppearances.setItem("Yes", pdAppearanceStream);
pdAppearanceStream = new PDAppearanceStream(document);
pdAppearanceStream.setResources(new PDResources());
try (PDPageContentStream pdPageContentStream = new PDPageContentStream(document, pdAppearanceStream))
{
pdPageContentStream.setFont(PDType1Font.ZAPF_DINGBATS, 14.5f);
pdPageContentStream.beginText();
pdPageContentStream.newLineAtOffset(3, 4);
pdPageContentStream.showText("\u2718");
pdPageContentStream.endText();
}
pdAppearanceStream.setBBox(new PDRectangle(18, 18));
normalAppearances.setItem("Off", pdAppearanceStream);
PDCheckBox checkBox = new PDCheckBox(acroForm);
acroForm.getFields().add(checkBox);
checkBox.setPartialName("CheckBoxField");
checkBox.setFieldFlags(4);
List<PDAnnotationWidget> widgets = checkBox.getWidgets();
for (PDAnnotationWidget pdAnnotationWidget : widgets)
{
pdAnnotationWidget.setRectangle(new PDRectangle(50, 750, 18, 18));
pdAnnotationWidget.setPage(page);
page.getAnnotations().add(pdAnnotationWidget);
pdAnnotationWidget.setAppearance(pdAppearanceDictionary);
}
// checkBox.setReadOnly(true);
checkBox.check();
// checkBox.unCheck();
document.save(new File(RESULT_FOLDER, "CheckBox.pdf"));
document.close();
(CreateCheckBox test testCheckboxForSureshGoud)
Be sure to use either
checkBox.check();
or
checkBox.unCheck();
as otherwise the state of the box is undefined.
#mkl has a good answer, but I think it can be simplified a little bit in case you already have a Document and just want to add a PDCheckbox (scala):
def addCheckboxField(
doc: PDDocument,
form: PDAcroForm,
name: String,
pg: Int, // page number
x: Float,
y: Float,
width: Float,
height: Float
) = {
val normalAppearances = new COSDictionary()
normalAppearances.setItem(
"Yes", {
val appearanceStream = new PDAppearanceStream(doc)
appearanceStream.setResources(new PDResources())
appearanceStream
}
)
val appearanceDictionary = new PDAppearanceDictionary()
appearanceDictionary.setNormalAppearance(new PDAppearanceEntry(normalAppearances))
appearanceDictionary.setDownAppearance(new PDAppearanceEntry(normalAppearances))
val field = new PDCheckBox(form)
field.setPartialName(name)
val widget = field.getWidgets.get(0)
widget.setAppearance(appearanceDictionary)
form.getFields.add(field)
val page = doc.getPage(pg)
widget.setRectangle(new PDRectangle(x, y, width, height))
widget.setPage(page)
widget.setPrinted(true)
page.getAnnotations().add(widget)
// do what you want with it
field.unCheck()
}
It's likely there are other simplifications that can be made, but this is what worked for me.
PdfBox version: 2.0.21

Macro to find paragraphs in text selection in Open/Libre/Neo Office

I am trying to enumerate the paragraphs selected by the user in (Neo|Libre|Open)Office.
When I use the code below, modified version from here,
Sub CheckForSelection
Dim oDoc as Object
Dim oText
oDoc = ThisComponent
oText = oDoc.Text
if not IsAnythingSelected(oDoc) then
msgbox("No text selected!")
Exit Sub
end if
oSelections = oDoc.getCurrentSelection()
oSel = oSelections.getByIndex(0)
' Info box
'MsgBox oSel.getString(), 64, "Your Selection"
oPE = oSel.Text.createEnumeration()
nPars = 0
Do While oPE.hasMoreElements()
oPar = oPE.nextElement()
REM The returned paragraph will be a paragraph or a text table
If oPar.supportsService("com.sun.star.text.Paragraph") Then
nPars = nPars + 1
ElseIf oPar.supportsService("com.sun.star.text.TextTable") Then
nTables = nTables + 1
end if
Loop
' Info box
MsgBox "You selection has " & nPars & " paragraphs.", 64
end Sub
it finds ALL the paragraphs in the document, not just in the selection. Google has failed me. Any thoughts on how to find individual paragraphs in the selection?
The oSel.Text is a shortcut for oSel.getText() which "Returns the text interface in which the text position is contained." https://www.openoffice.org/api/docs/common/ref/com/sun/star/text/XTextRange.html#getText
So to get a ParagraphEnumeration only from the Selection, you should use oPE = oSel.createEnumeration() instead of oPE = oSel.Text.createEnumeration().

TabularAdapter Editor issue

I've run into an issue with the TabularAdapter in the TraitsUI package...
I've been trying to figure this out on my own for much too long now, so I wanted to ask the experts here for some friendly advise :)
I'm going to add a piece of my program that illustrates my problem(s), and I'm hoping someone can look it over and say 'Ah Ha!...Here's your problem' (my fingers are crossed).
Basically, I can use the TabularAdapter to produce a table editor into an array of dtypes, and it works just fine except:
1) whenever I change the # of elements (identified as 'Number of fractures:'), the array gets resized, but the table doesn't reflect the change until after I click on one of the elements. What I'd like to happen is that the # of rows (fractures) changes after I release the # of fractures slider. Is this doable?
2) The second issue I have is that if the array gets resized before it's displayed by .configure_traits() (by the notifier when Number_of_fractures gets modified), I can shrink the size of the array, but I can't increase it over the new size.
2b) I thought I'd found a way to have the table editor display the full array even when it's increased over the 5 set in the code (just before calling .trait_configure()), but I was fooled :( I tried adding another Group() in front of the vertical_fracture_group so the table wasn't the first thing to display. This more closely emulates my entire program. When I did this, I was locked into the new smaller size of the array, and I could no longer increase its size to my maximum of 15. I'm modifying the code to reflect this issue.
Here's my sample code:
# -*- coding: utf-8 -*-
"""
This is a first shot at developing a ****** User Interface using Canopy by
Enthought. Canopy is a distribution of the Python language which has a lot of
scientific and engineering features 'built-in'.
"""
#-- Imports --------------------------------------------------------------------
from traitsui.api import TabularEditor
from traitsui.tabular_adapter import TabularAdapter
from numpy import zeros, dtype
from traits.api import HasTraits, Range
from traitsui.api import View, Group, Item
#-- FileDialogDemo Class -------------------------------------------------------
max_cracks = 15 #maximum number of Fracs/cracks to allow
class VertFractureAdapter(TabularAdapter):
columns = [('Frac #',0), ('X Cen',1), ('Y Cen',2), ('Z Cen',3),
('Horiz',4), ('Vert',5), ('Angle',6)]
class SetupDialog ( HasTraits ):
Number_Of_Fractures = Range(1, max_cracks) # line 277
vertical_frac_dtype = dtype([('Fracture', 'int'), ('x', 'float'), ('y', 'float'),
('z', 'float'), ('Horiz Length', 'float'), ('Vert Length', 'float')
, ('z-axis Rotation, degrees', 'float')])
vertical_frac_array = zeros((max_cracks), dtype=vertical_frac_dtype)
vertical_fracture_group = Group(
Item(name = 'vertical_frac_array',
show_label = False,
editor = TabularEditor(adapter = VertFractureAdapter()),
width = 0.5,
height = 0.5,
)
)
#-- THIS is the actual 'View' that gets put on the screen
view = View(
#Note: When as this group 'displays' before the one with the Table, I'm 'locked' into my new maximum table display size of 8 (not my original/desired maximum of 15)
Group(
Item( name = 'Number_Of_Fractures'),
),
#Note: If I place this Group() first, my table is free to grow to it's maximum of 15
Group(
Item( name = 'Number_Of_Fractures'),
vertical_fracture_group,
),
width = 0.60,
height = 0.50,
title = '****** Setup',
resizable=True,
)
#-- Traits Event Handlers --------------------------------------------------
def _Number_Of_Fractures_changed(self):
""" Handles resizing arrays if/when the number of Fractures is changed"""
print "I've changed the # of Fractures to " + repr(self.Number_Of_Fractures)
#if not self.user_StartingUp:
self.vertical_frac_array.resize(self.Number_Of_Fractures, refcheck=False)
for crk in range(self.Number_Of_Fractures):
self.vertical_frac_array[crk]['Fracture'] = crk+1
self.vertical_frac_array[crk]['x'] = crk
self.vertical_frac_array[crk]['y'] = crk
self.vertical_frac_array[crk]['z'] = crk
# Run the program (if invoked from the command line):
if __name__ == '__main__':
# Create the dialog:
fileDialog = SetupDialog()
fileDialog.configure_traits()
fileDialog.Number_Of_Fractures = 8
In my discussion with Chris below, he made some suggestions that so far haven't worked for me :( Following is my 'current' version of this test code so Chris (or anyone else who wishes to chime in) can see if I'm making some glaring error.
# -*- coding: utf-8 -*-
"""
This is a first shot at developing a ****** User Interface using Canopy by
Enthought. Canopy is a distribution of the Python language which has a lot of
scientific and engineering features 'built-in'.
"""
#-- Imports --------------------------------------------------------------------
from traitsui.api import TabularEditor
from traitsui.tabular_adapter import TabularAdapter
from numpy import zeros, dtype
from traits.api import HasTraits, Range, Array, List
from traitsui.api import View, Group, Item
#-- FileDialogDemo Class -------------------------------------------------------
max_cracks = 15 #maximum number of Fracs/cracks to allow
class VertFractureAdapter(TabularAdapter):
columns = [('Frac #',0), ('X Cen',1), ('Y Cen',2), ('Z Cen',3),
('Horiz',4), ('Vert',5), ('Angle',6)]
even_bg_color = 0xf4f4f4 # very light gray
class SetupDialog ( HasTraits ):
Number_Of_Fractures = Range(1, max_cracks) # line 277
dummy = Range(1, max_cracks)
vertical_frac_dtype = dtype([('Fracture', 'int'), ('x', 'float'), ('y', 'float'),
('z', 'float'), ('Horiz Length', 'float'), ('Vert Length', 'float')
, ('z-axis Rotation, degrees', 'float')])
vertical_frac_array = Array(dtype=vertical_frac_dtype)
vertical_fracture_group = Group(
Item(name = 'vertical_frac_array',
show_label = False,
editor = TabularEditor(adapter = VertFractureAdapter()),
width = 0.5,
height = 0.5,
)
)
#-- THIS is the actual 'View' that gets put on the screen
view = View(
Group(
Item( name = 'dummy'),
),
Group(
Item( name = 'Number_Of_Fractures'),
vertical_fracture_group,
),
width = 0.60,
height = 0.50,
title = '****** Setup',
resizable=True,
)
#-- Traits Event Handlers --------------------------------------------------
def _Number_Of_Fractures_changed(self, old, new):
""" Handles resizing arrays if/when the number of Fractures is changed"""
print "I've changed the # of Fractures to " + repr(self.Number_Of_Fractures)
vfa = self.vertical_frac_array
vfa.resize(self.Number_Of_Fractures, refcheck=False)
for crk in range(self.Number_Of_Fractures):
vfa[crk]['Fracture'] = crk+1
vfa[crk]['x'] = crk
vfa[crk]['y'] = crk
vfa[crk]['z'] = crk
self.vertical_frac_array = vfa
# Run the program (if invoked from the command line):
if __name__ == '__main__':
# Create the dialog:
fileDialog = SetupDialog()
# put the actual dialog up...if I put it up 'first' and then resize the array, I seem to get my full range back :)
fileDialog.configure_traits()
#fileDialog.Number_Of_Fractures = 8
There are two details of the code that are causing the problems you describe. First, vertical_frac_array is not a trait, so the tabular editor cannot monitor it for changes. Hence, the table only refreshes when you manually interact with it. Second, traits does not monitor the contents of an array for changes, but rather the identity of the array. So, resizing and assigning values into the array will not be detected.
One way to fix this is to first make vertical_frac_array and Array trait. E.g. vertical_frac_array = Array(dtype=vertical_frac_dtype). Then, inside of _Number_Of_Fractures_changed, do not resize the vertical_frac_array and modify it in-place. Instead, copy vertical_frac_array, resize it, modify the contents, and then reassign the manipulated copy back to vertical_frac_array. This way the table will see that the identity of the array has changed and will refresh the view.
Another option is to make vertical_frac_array a List instead of an Array. This avoids the copy-and-reassign trick above because traits does monitor the content of lists.
Edit
My solution is below. Instead of resizing the vertical_frac_array whenever Number_Of_Fractures changes, I instead recreate the array. I also provide a default value for vertical_frac_array via the _vertical_frac_array_default method. (I removed from unnecessary code in the view as well.)
# -*- coding: utf-8 -*-
"""
This is a first shot at developing a ****** User Interface using Canopy by
Enthought. Canopy is a distribution of the Python language which has a lot of
scientific and engineering features 'built-in'.
"""
#-- Imports --------------------------------------------------------------------
from traitsui.api import TabularEditor
from traitsui.tabular_adapter import TabularAdapter
from numpy import dtype, zeros
from traits.api import HasTraits, Range, Array
from traitsui.api import View, Item
#-- FileDialogDemo Class -------------------------------------------------------
max_cracks = 15 #maximum number of Fracs/cracks to allow
vertical_frac_dtype = dtype([('Fracture', 'int'), ('x', 'float'), ('y', 'float'),
('z', 'float'), ('Horiz Length', 'float'), ('Vert Length', 'float')
, ('z-axis Rotation, degrees', 'float')])
class VertFractureAdapter(TabularAdapter):
columns = [('Frac #',0), ('X Cen',1), ('Y Cen',2), ('Z Cen',3),
('Horiz',4), ('Vert',5), ('Angle',6)]
class SetupDialog ( HasTraits ):
Number_Of_Fractures = Range(1, max_cracks) # line 277
vertical_frac_array = Array(dtype=vertical_frac_dtype)
view = View(
Item('Number_Of_Fractures'),
Item(
'vertical_frac_array',
show_label=False,
editor=TabularEditor(
adapter=VertFractureAdapter(),
),
width=0.5,
height=0.5,
),
width=0.60,
height=0.50,
title='****** Setup',
resizable=True,
)
#-- Traits Defaults -------------------------------------------------------
def _vertical_frac_array_default(self):
""" Creates the default value of the `vertical_frac_array`. """
return self._calculate_frac_array()
#-- Traits Event Handlers -------------------------------------------------
def _Number_Of_Fractures_changed(self):
""" Update `vertical_frac_array` when `Number_Of_Fractures` changes """
print "I've changed the # of Fractures to " + repr(self.Number_Of_Fractures)
#if not self.user_StartingUp:
self.vertical_frac_array = self._calculate_frac_array()
#-- Private Interface -----------------------------------------------------
def _calculate_frac_array(self):
arr = zeros(self.Number_Of_Fractures, dtype=vertical_frac_dtype)
for crk in range(self.Number_Of_Fractures):
arr[crk]['Fracture'] = crk+1
arr[crk]['x'] = crk
arr[crk]['y'] = crk
arr[crk]['z'] = crk
return arr
# Run the program (if invoked from the command line):
if __name__ == '__main__':
# Create the dialog:
fileDialog = SetupDialog()
fileDialog.configure_traits()

Resources