Macro ignores parameter change - python-3.x

Given a FreeCAD model that consists of
Spreadsheet "parameters" with a cell aliased as "radius" and value 50
Icosahedron (from the Pyramids-and-Polyhedrons macro) with Radius=parameters.radius
some facebinders which are unimportant for the purpose of this question,
the python script below opens this model, changes the radius cell in the Spreadsheet to 15, call recompute() on the spreadsheet, invokes touch() on the icosahedron, calls recompute() on the document, and finally tessellates the icosahedron. The z coordinate of the vertex at index 11 in the tessellated mesh happens to be equal to the icosahedron's radius. I was expecting it to change to 15, according to the parameter change. But it remains at its original value 50. What am I doing wrong?
To my understanding the macro's execute method should get invoked during the recomputation of the document.
When I trace Document.recompute() with pudb, I only see it executing Facebinder.execute() but not Icosahedron.execute(). The path it takes from Document.recompute() to Facebinder.execute() method is not visible in pudb; it immediately stops in Facebinder.execute().
FREECADPATH = '/usr/local/lib' # path to your FreeCAD.so or FreeCAD.dll POLYHEDRONS_PATH = '/home/user/.FreeCAD/Mod/Pyramids-and-Polyhedrons'
import sys
sys.path.append(FREECADPATH)
sys.path.append(POLYHEDRONS_PATH)
import FreeCAD
filename = 'icosahedron.FCStd'
newRadius = 15
doc = FreeCAD.open(filename)
sheet = doc.Spreadsheet
sheet.set('radius', str(newRadius))
sheet.recompute()
print('radius = ' + str(sheet.get('radius')))
icosahedron = doc.getObjectsByLabel('Icosahedron')[0]
print('icosahedron.Radius = '+str(icosahedron.Radius))
icosahedron.touch()
doc.recompute()
print('icosahedron.Radius = '+str(icosahedron.Radius))
(vertices, faces) = icosahedron.Shape.tessellate(1)
z11 = vertices[11][2]
print('z11 = ' + str(z11))
FreeCAD.closeDocument(doc.Name)

I figured it out. The reason was Pyramids-and-Polyhedrons not being imported after all.
The first problem was that Pyramids-and-Polyhedrons imports FreeCADGui (in order to install its workbench), which depends on certain native libs that need to be added to LD_LIBRARY_PATH before running the script:
export LD_LIBRARY_PATH=$(echo $(find /usr/local/lib/python3.7/site-packages -maxdepth 2 -mindepth 2 -type f -name *.so* | sed -r 's|/[^/]+$||' | sort -u) | sed -E 's/ /:/g')
Now FreeCADGui could be imported, but it was lacking the required addCommand method that Pyramids-and-Polyhedrons invokes. I found out that FreeCADGui will only be fully initialized after FreeCADGui.showMainWindow() has been called. This requires a display, however, and I want to run the script as part of a headless toolchain in a Docker container. Therefore I added Xvfb to the image. I'm launching it in the background before running the updated script with DISPLAY pointing to Xvfb:
FREECADPATH = '/usr/local/lib' # path to your FreeCAD.so or FreeCAD.dll file
POLYHEDRONS_PATH = '/home/user/.FreeCAD/Mod/Pyramids-and-Polyhedrons'
import sys
sys.path.append(FREECADPATH)
sys.path.append(POLYHEDRONS_PATH)
import FreeCAD
import FreeCADGui
FreeCADGui.showMainWindow()
import polyhedrons
filename = 'icosahedron.FCStd'
newRadius = 15
doc = FreeCAD.open(filename)
sheet = doc.Spreadsheet
sheet.set('radius', str(newRadius))
sheet.recompute()
print('radius = ' + str(sheet.get('radius')))
icosahedron = doc.getObjectsByLabel('Icosahedron')[0]
print('icosahedron.Radius = '+str(icosahedron.Radius))
breakpoint()
icosahedron.touch()
doc.recompute()
print('icosahedron.Radius = '+str(icosahedron.Radius))
(vertices, faces) = icosahedron.Shape.tessellate(1)
z11 = vertices[11][2]
print('z11 = ' + str(z11))
FreeCAD.closeDocument(doc.Name)
Now the output is
...
z11 = 15.0
as expected.

Related

How to properly invoke Python 3 script from SPSS syntax window using SCRIPT command (+ additional problems during runtime)

I would like to run two Python 3 scripts from SPSS syntax window. It is possible to perform it using BEGIN PROGRAM-END PROGRAM. block or SCRIPT command. This time I need to find a solution using second command.
Simplified code:
*** MACROS.
define export_tabs (!positional !tokens (1))
output modify
/select logs headings texts warnings pagetitles outlineheaders notes
/deleteobject delete = yes.
OUTPUT EXPORT
/CONTENTS EXPORT = visible LAYERS = printsetting MODELVIEWS = printsetting
/XLSX DOCUMENTFILE = "doc.xlsx"
OPERATION = createsheet
sheet = !quote(!unquote(!1))
LOCATION = lastcolumn NOTESCAPTIONS = no
!enddefine.
define matrix_tab (!positional !charend('/')
/!positional !charend('/')
/!positional !charend('/')
/!positional !charend('/')
/stat = !tokens (1))
!do !i !in (!3)
ctables
/mrsets countduplicates = no
/vlabels variables = !concat(!1,_,!2,_,!i) display = label
/table !concat(!1,_,!2,_,!i)
[rowpct.responses.count !concat(!unquote(!stat),"40.0"), totals[count f40.0]]
/slabels position = column visible = no
/clabels rowlabels = opposite
/categories variables = !concat(!1,_,!2,_,!i) order = a key = value
empty = include total = yes label = "VALID COUNT" position = after
/titles title = !upcase(!4).
!doend
!enddefine.
*** REPORT.
* Sheet 1.
output close all.
matrix_tab $Q1 / 1 / 1 2 / "QUESTION 1" / stat="pct".
script "C:\path\script 1.py".
script "C:\path\script 2.py".
export_tabs "Q1".
* Sheet 2.
output close all.
matrix_tab $Q2 / 2 / 3 4 / "QUESTION 2" / stat="pct".
script "C:\path\script 1.py".
script "C:\path\script 2.py".
export_tabs "Q2".
When I run a block for the first sheet everything works fine. However, when I run a block for the second sheet SPSS doesn't execute Python scripts and jumps straight to export_tabs macro (problems with synchronization?). I thought a problem had been possibly in a way I executed SCRIPT command. So I tried this:
script "C:\path\script 1.py" pythonversion = 3.
script "C:\path\script 2.py" pythonversion = 3.
but in effect SPSS - even though the syntax window coloured these parts of syntax - returned this error message:
>Error # 3251 in column 152. Text: pythonversion
>The SCRIPT command contains unrecognized text following the the file
>specification. The optional parameter must be a quoted string enclosed in
>parentheses.
>Execution of this command stops.
Has anyone of you had such problem and/or have an idea why this happens?
NOTE: Both Python scripts run smoothly from the Python 3.4.3 shell installed with the version of SPSS I have, thus I don't think the core of the problem is within those codes.
This seems to be a document defect in the way this keyword was implemented. I have been able to replicate it and have logged a defect with IBM SPSS Statistics Development.
In this case, the order matters. Rather than this:
script "C:\path\script 2.py" pythonversion = 3.
Try instead:
script pythonversion = 3 "C:\path\script 2.py".

Run CMD commands in a loop?

I am trying to rename numerous files in a directory (Windows10 laptop) using Python code. I am not finding any error but it is not getting executed. I have recreated the simplified scenario as below.
import pandas as pd
import os
data = {'file_1': ['temp112.pptx', 'temp212.pdf', 'temp312.pdf', 'temp412.pptx'], 'file_2': ['mktg.pptx', 'econ.pdf', 'orgg.pdf', 'qtdm.pptx']}
files=pd.DataFrame.from_dict(data)
for index, row in files.iterrows():
term = "'"+'ren'+' ' + str(row['file_1'] + ' ' + str(row['file_2']+"'"))
os.system(str(term))
print(term)
'ren temp112.pptx mktg.pptx'
'ren temp212.pdf econ.pdf'
'ren temp312.pdf orgg.pdf'
'ren temp412.pptx qtdm.pptx'
However the term 'ren temp112.pptx mktg.pptx' can be executed manually (os.system('ren temp112.pptx mktg.pptx')). Not sure why it is not executing in a loop.

Edit multiple text files in directory [duplicate]

This question already has answers here:
Editing/Replacing content in multiple files in Unix AIX without opening it
(2 answers)
Closed 4 years ago.
I'm working with bash via Ubuntu terminal. I want to make the same text edit to all the files in my directory that have the same extension. My directory contains several versions of 227 numerically counted data files. So for example I have:
tmp0001.ctl
tmp0001.out
tmp0001.trees
tmp0001.txt
tmp0002.ctl
tmp0002.out
tmp0002.trees
tmp002.txt
And so on.
The files I am interested in editing are those with the extension ".ctl". At present, .ctl files look like this (though the numbers vary from tmp0001 through 227 of course):
seqfile = tmp0001.txt
treefile = tmp0001.trees
outfile = tmp0001.out
noisy = 3
seqtype = 2
model = 0
aaRatefile =
Small_Diff = 0.1e-6
getSE = 2
method = 1
I want to edit the .ctl files so that they read:
seqfile = tmp0001.txt
treefile = tmp0001.trees
outfile = tmp0001.out
noisy = 3
seqtype = 2
model = 2
aaRatefile = lg.dat
fix_alpha = 0
alpha = .5
ncatG = 4
Small_Diff = 0.1e-6
getSE = 2
method = 1
I'm not sure how to do this though, I would guess to use an editor like nano or sed, but I'm not sure how to automate this. I thought something along the lines of:
for file in *.ctl
do
nano
???
Hope this isn't too convoluted! Essentially I want to change two lines already in there (model and aaratefile), and add 2 more lines (>ix_alpha = 0 and alpha = .5) in each .ctl file.
Thanks!
You can use sed to perform the task:
sed -e 's/model = 0/model = 2/; s/aaRatefile = /aaRatefile = lg.dat/' \
-e '/Small_Diff/ i fix_alpha = 0\nalpha = .5\nncatG = 4' \
-i~ *.ctl
s/pattern/replacement/ replaces "pattern" by "replacement"
i text inserts the text
if a command is preceded by /pattern/, the command is run only when the current line matches the pattern, i.e. in this case, the lines are inserted before the Small_Diff line.
-i~ tells sed to replace the files "in place", leaving a backup with the ~ appended to the name (so there will be backups named tmp0001.ctl~ etc.)

Python OpenPyxl Remove a Scatter Chart gridlines?

Python3 and OpenPyxl version 2.3.2
How do you remove a Scatter Chart Gridlines?
from openpyxl.chart.axis import ChartLines
SCchart = ScatterChart()
SCchart.title = "Scatter Chart"
SCchart.style = 13
SCchart.x_axis.majorGridlines = False
gives me the error:
TypeError: expected class 'openpyxl.chart.axis.ChartLines'
And this:
SCchart.x_axis.ChartLines.majorUnit = False
gives the error: AttributeError: 'NumericAxis' object has no attribute 'ChartLines'
I'd like to remove all the chart gridlines.
I have not been able to do this with Openpyxl 2.51, even using some tricks like setting the gridlines to the background color of white (hex 'FFFFFF'). I can get close to accessing the line parameter with openpyxl, but can't quite get it to work. I am including my efforts here, hoping some one else will pick up the ball.
The python gist:
from openpyxl.chart.shapes import GraphicalProperties
# turn majorGridlines off using shapes.GraphicalProperties
sgp = GraphicalProperties(solidFill='FFFFFF') # White
chart.y_axis.majorGridlines.spPr = sgp
This is the XML that openpyxl writes:
<valAx>
<axId val="2" />
<scaling>
<orientation val="minMax" />
<min val="0" />
</scaling>
<axPos val="l" />
<majorGridlines>
<spPr>
<a:solidFill>
<a:srgbClr val="FFFFFF" />
</a:solidFill>
<a:ln>
<a:prstDash val="solid" />
</a:ln>
</spPr>
</majorGridlines>
When I open the spreadsheet that openpyxl just wrote with Excel 2010, the major gridlines are still there with a dark grey color. I click on them and set their color to white and save the file with a new filename. This is how excel writes the new xml code:
<c:valAx>
<c:axId val="83446016"/>
<c:scaling>
<c:orientation val="minMax"/>
<c:min val="0"/>
</c:scaling>
<c:delete val="0"/>
<c:axPos val="r"/>
<c:majorGridlines>
<c:spPr>
<a:ln>
<a:solidFill>
<a:schemeClr val="bg1"/>
</a:solidFill>
<a:prstDash val="solid"/>
</a:ln>
</c:spPr>
</c:majorGridlines>
So logically, you might think that this python would work (spoiler, not for me!):
chart.y_axis.majorGridlines.spPr.ln = sgp
The proper method (the one used by Microsoft) is to set the gridlines to NoFill. Turning off gridlines in excel, saving the file and looking at the chart.xml file yields this:
<c:valAx>
<c:axId val="83031552"/>
<c:scaling>
<c:orientation val="minMax"/>
<c:min val="0"/>
</c:scaling>
<c:delete val="0"/>
<c:axPos val="r"/>
<c:majorGridlines>
<c:spPr>
<a:ln>
<a:noFill/>
<a:prstDash val="solid"/>
</a:ln>
</c:spPr>
</c:majorGridlines>
The python would be something like:
sgp = GraphicalProperties(noFill=True) # bonks, tried None also not effective
chart.y_axis.majorGridlines.spPr.ln = sgp
or maybe
chart.y_axis.majorGridlines.spPr.ln.noFill = True
which also bonks because there is no ln or line in this openpyxl path.
I just found an actual answer that works! You dont need to import ChartLines. The trick was I needed to include another class to get the line thing to work like this:
from openpyxl.drawing.line import LineProperties
# turn majorGridlines off using shapes.GraphicalProperties and drawing.LineProperties
sgp = GraphicalProperties(ln=LineProperties(noFill=True))
chart.x_axis.majorGridlines.spPr = sgp
Try it out!
Not sure if the earlier answers ended this thread, but based on those recommendations and a few tweaks I was able to enable/disable grid Lines for a Line Chart, add data labels.
The following code was used:
from openpyxl.chart.shapes import GraphicalProperties
from openpyxl.drawing.line import LineProperties
from openpyxl.chart.axis import ChartLines
from openpyxl.chart.label import DataLabelList
c1.y_axis.majorGridlines.spPr = GraphicalProperties(noFill = 'True')
c1.y_axis.majorGridlines.spPr.ln = LineProperties(solidFill = '000000')
c1.x_axis.majorGridlines = ChartLines()
c1.x_axis.majorGridlines.spPr = GraphicalProperties(noFill = 'True')
c1.x_axis.majorGridlines.spPr.ln = LineProperties(solidFill = '000000')
c1.dLbls = DataLabelList()
c1.dLbls.showVal = 1
Basically, certain parameters 1st need a class instance to be assigned to them in order to change subsequent sub-parameters.
For ex. When you try to set a value to spPr, the error thrown back will specify the class it expected, which you can then import and assign it's instance to the variable, if I may call it that. Beyond that, the xml can greatly help you figure which variables need to be changed. Hope this helps..
I successfully removed the gridlines from my chart by setting majorGridlines to None.
I included the following two lines in my script.
c1.x_axis.majorGridlines = None
c1.y_axis.majorGridlines = None
In my case, c1 is the instance of openpyxl.chart.LineChart().
I came up with this solution after noticing that majorGridlines accepts None as a value. I noticed this while reading the class definition for _BaseAxis which can be found here:
https://openpyxl.readthedocs.io/en/latest/_modules/openpyxl/chart/axis.html
Below is a full example program. Note: file paths are for windows so you may have to tweak these if you want to run the script on your machine.
# Generate Plots With No Grid Lines
import openpyxl as xl
import numpy as np
from pathlib import Path
# Create Sheet 1 Data
years = np.arange(2000,2023,1)
x1 = np.linspace(0,20,years.size)
x2 = np.linspace(10,2,years.size)
# Create Sheet 2 Data
x3 = np.linspace(10,100,years.size)
x4 = np.linspace(120,50,years.size)
# Open Workbook and Write Sheet 1
wb = xl.Workbook()
sheet = wb[wb.sheetnames[0]]
sheet_one_title = 'Data One'
sheet.title = sheet_one_title
sheet.append(['Year','X1','X2'])
for i in range(0,years.size):
sheet.append([years[i],x1[i],x2[i]])
# Create Sheet 2 and Switch to It
sheet_two_title = "Data Two"
wb.create_sheet(title=sheet_two_title)
sheet = wb[sheet_two_title]
# Write To Sheet 2
sheet.append(['Year','X3','X4'])
for i in range(0,years.size):
sheet.append([years[i],x3[i],x4[i]])
# Now Add The Charts
# Starting With Sheet Two (i.e. "Data Two")
c2 = xl.chart.LineChart()
data = xl.chart.Reference(sheet, min_col=2, min_row=1, max_col=3, max_row=years.size+1)
c2.add_data(data, titles_from_data=True)
dates = xl.chart.Reference(sheet, min_col=1, min_row=2, max_row=years.size+1)
c2.set_categories(dates)
# REMOVE THE GRIDLINES
c2.x_axis.majorGridlines = None
c2.y_axis.majorGridlines = None
# Add Chart to Sheet
sheet.add_chart(c2, 'D5')
# Swtich Back To Sheet 1 (i.e. "Data One")
sheet = wb[sheet_one_title]
# Create Chart
c1 = xl.chart.LineChart()
data = xl.chart.Reference(sheet, min_col=2, min_row=1, max_col=3, max_row=years.size+1)
c1.add_data(data, titles_from_data=True)
dates = xl.chart.Reference(sheet, min_col=1, min_row=2, max_row=years.size+1)
c1.set_categories(dates)
# REMOVE THE GRIDLINES ONCE MORE
c1.x_axis.majorGridlines = None
c1.y_axis.majorGridlines = None
# Add Chart to Sheet
sheet.add_chart(c1, 'D5')
# Get Route to Desktop
desktop = str(Path.home()) + "\Desktop"
# Create Path by Appending File Name
save_path = desktop + "\\PlotExample.xlsx"
# Save Workbook
wb.save(save_path)
print("Workbook Saved To:" + save_path)
p.s. this is my first ever stack overflow answer :)

History of previously opened m-files in MATLAB

Is anyway to find history of previously opened m-files in MATLAB R2014b from 2 or 3 months ago? (a list of name of files and paths)
Matlab R2014b stores its recent files in:
%APPDATA%\MathWorks\MATLAB\R2014b\MATLAB_Editor_State.xml
It's a .xml file so it's easy to load and parse with xmlread. I'm not very familiar with xml parsing syntax, but here is how to get information about files (to be adapted to your needs of course):
function [recentFiles] = GetRecentFiles()
%[
% Opens editor's state file
filepart = sprintf('MathWorks\\MATLAB\\R%s\\%s', version('-release'), 'MATLAB_Editor_State.xml');
filename = fullfile(getenv('APPDATA'), filepart);
document = xmlread(filename);
% Get information about 'File' nodes
recentFiles = struct([]);
fileNodes = document.getElementsByTagName('File');
for fni = 1:(fileNodes.getLength())
attributes = fileNodes.item(fni-1).getAttributes(); % Careful, zero based indexing !
for ai = 1:(attributes.getLength())
% Get node attribute
name = char(attributes.item(ai-1).getName()); % Zero based + need marshaling COM 'string' type
value = char(attributes.item(ai-1).getValue()); % Zero based + need marshaling COM 'string' type
% Save in structure
name(1) = upper(name(1)); % Just because I prefer capital letter for field names ...
recentFiles(fni).(name) = value;
end
end
%]
end
This returns a structure like this:
recentFiles =
1x43 struct array with fields:
AbsPath
LastWrittenTime
Name
NB: I've tried to type in matlab command window matlab.desktop.editor.*, but seems there's nothing regarding recent files (anyway there are a lot of interesting things to manipulate the editor from the command line)
Last answer waIs really helpful. I've just modified it to read and open the recent tab files. This works on Matlab R2013a:
function [recentFiles] = recover_tabs()
%[
% Opens editor's state file
filepart = sprintf('MathWorks\\MATLAB\\R%s\\%s', version('-release'), 'MATLAB_Editor_State.xml');
filename = fullfile(getenv('APPDATA'), filepart);
document = xmlread(filename);
% Get information about 'File' nodes
recentFiles = struct([]);
fileNodes = document.getElementsByTagName('File');
for fni = 1:(fileNodes.getLength())
attributes = fileNodes.item(fni-1).getAttributes(); % Careful, zero based indexing !
for ai = 1:(attributes.getLength())
% Get node attribute
name = char(attributes.item(ai-1).getName()); % Zero based + need marshaling COM 'string' type
value = char(attributes.item(ai-1).getValue()); % Zero based + need marshaling COM 'string' type
% Save in structure
name(1) = upper(name(1)); % Just because I prefer capital letter for field names ...
recentFiles(fni).(name) = value;
end
end
% loop to access files in the tab history
for j=1:length(recentFiles)
arquivo = [recentFiles(j).AbsPath '\' recentFiles(j).Name];
% if exists, then open
if exist(arquivo, 'file') == 2
open(arquivo);
end
end
%]
end
Base in the answer by CitizenInsane, but for any Matlab version.
To find the .xml file in any Matlab version, use prefdir:
>> prefdir
ans = '/Users/user/Library/Application Support/MathWorks/MATLAB/R2018a'
MATLAB_Editor_State.xml will be stored there. Therefore the fuction would be:
function [recentFiles] = GetRecentFiles()
% Opens editor's state file
filepart = sprintf([ prefdir '/MATLAB_Editor_State.xml']);
filename = fullfile(getenv('APPDATA'), filepart);
document = xmlread(filename);
% Get information about 'File' nodes
recentFiles = struct([]);
fileNodes = document.getElementsByTagName('File');
for fni = 1:(fileNodes.getLength())
attributes = fileNodes.item(fni-1).getAttributes(); % Careful, zero based indexing !
for ai = 1:(attributes.getLength())
% Get node attribute
name = char(attributes.item(ai-1).getName()); % Zero based + need marshaling COM 'string' type
value = char(attributes.item(ai-1).getValue()); % Zero based + need marshaling COM 'string' type
% Save in structure
name(1) = upper(name(1)); % Just because I prefer capital letter for field names ...
recentFiles(fni).(name) = value;
end
end
In R2018b you can increase the Most recently used file list in Preferences > Editor/Debugger. I like the methods above, but they do not work if you're working across machines (e.g., using Github). I coded a solution that uses the modified file date from the machine, instead of relying on MATLAB itself.

Resources