Repository: psychopy/psychopy

Issue:
cannot use an Excel Macro-Enabled Workbook (.xslm) for “Conditions” in the Builder

I tried to load a sheet from an Excel Macro-Enabled Workbook (.xslm) for “Conditions” in the Builder. But It is mentioned the following: “No parameters set (conditionsFile not found)”. It only works when I have the same sheet (without any macros).

My question: is it possible to use a sheet from .xslm (an excel sheet with macros)?


Discussion:
From a quick google It sounds like openpyxl can read xlsm files but I think it will just treat it as a regular xlsx file. The macro won't be usable from within Python. I guess the question is what your macro is doing and whether it's needed for the conditions file to operate 
Thanks Jon, how could I use openpyxl to read the xlsm file so that it can be treated as an xlsx file and be read by Psychopy to set the conditions in the builder?
This is already happening underneath the sheets (no pun intended!) What Jon meant to say is, depending on what your macros do, it may or may not lead to usable results in PsychoPy. Would it not be possible for you to first convert the files to xlsx, i.e. get rid of the macros?
Hi, and thanks for responding. The macro does a "complicated" randomization (I understand that I could use some code in Python to do the same but I am more familiar with macros). My goal is as follows: a macro does the randomization so that the sheet can be used by Psychopy. I really would like to explore this possibility. 
So I think the solution, which @dvbridges is working on, will get the file to load, but I'd be amazed if it _runs_ the macro. 

The macro will have been written in something like Visual Basic (or does it use some other syntax these days?) and I don't think the xlread or pyopenxl libraries will be providing Visual Basic interpreters. So if you're expecting the macro to re-do the randomization on each load this won't work and you'll need to do the same thing in Python instead. If the macro is something that you manually run (from within excel) but want it kept with the file for convenience then I think that will be fine.

Resolving patch:
diff --git a/psychopy/data/utils.py b/psychopy/data/utils.py
--- a/psychopy/data/utils.py
+++ b/psychopy/data/utils.py
@@ -270,7 +270,7 @@ def pandasToDictList(dataframe):
             trialList.append(thisTrial)
         return trialList, fieldNames
 
-    if fileName.endswith('.csv') or (fileName.endswith(('.xlsx','.xls'))
+    if fileName.endswith('.csv') or (fileName.endswith(('.xlsx','.xls','.xlsm'))
                                      and haveXlrd):
         if fileName.endswith('.csv'):
             trialsArr = pd.read_csv(fileName, encoding='utf-8-sig')
@@ -284,7 +284,7 @@ def pandasToDictList(dataframe):
         logging.debug(u"Clearing unnamed columns from {}".format(fileName))
         trialList, fieldNames = pandasToDictList(trialsArr)
 
-    elif fileName.endswith('.xlsx'):
+    elif fileName.endswith(('.xlsx','.xlsm')):
         if not haveOpenpyxl:
             raise ImportError('openpyxl or xlrd is required for loading excel '
                               'files, but neither was found.')
@@ -309,7 +309,11 @@ def pandasToDictList(dataframe):
         # get parameter names from the first row header
         fieldNames = []
         for colN in range(nCols):
-            fieldName = ws.cell(_getExcelCellName(col=colN, row=0)).value
+            if parse_version(openpyxl.__version__) < parse_version('2.0'):
+                fieldName = ws.cell(_getExcelCellName(col=colN, row=0)).value
+            else:
+                # From 2.0, cells are referenced with 1-indexing: A1 == cell(row=1, column=1)
+                fieldName = ws.cell(row=1, column=colN + 1).value
             fieldNames.append(fieldName)
         _assertValidVarNames(fieldNames, fileName)
 
@@ -318,7 +322,11 @@ def pandasToDictList(dataframe):
         for rowN in range(1, nRows):  # skip header first row
             thisTrial = {}
             for colN in range(nCols):
-                val = ws.cell(_getExcelCellName(col=colN, row=rowN)).value
+                if parse_version(openpyxl.__version__) < parse_version('2.0'):
+                    val = ws.cell(_getExcelCellName(col=colN, row=0)).value
+                else:
+                    # From 2.0, cells are referenced with 1-indexing: A1 == cell(row=1, column=1)
+                    val = ws.cell(row=rowN + 1, column=colN + 1).value
                 # if it looks like a list or tuple, convert it
                 if (isinstance(val, basestring) and
                         (val.startswith('[') and val.endswith(']') or


Test patch: