EasyAlias macro

Need help, or want to share a macro? Post here!
Forum rules
Be nice to others! Respect the FreeCAD code of conduct!
demonlibra
Posts: 79
Joined: Tue Jan 21, 2020 1:11 pm

Re: EasyAlias macro

Post by demonlibra »

Naxia wrote: Wed Oct 05, 2022 8:23 am When I use this macro, it tells me to close all other spreadsheets and doesn't do anything.
It is possible to set or change aliases only in last spreadsheet.
My knowledge of python very low and I dont know how to resolve this problem.
TheMarkster
Veteran
Posts: 5505
Joined: Thu Apr 05, 2018 1:53 am

Re: EasyAlias macro

Post by TheMarkster »

Naxia wrote: Wed Oct 05, 2022 8:23 am When I use this macro, it tells me to close all other spreadsheets and doesn't do anything.
Is there a way around this?
That's a known limitation of the macro. You can only have one spreadsheet open at a time or else the macro can get confused and apply things to the wrong sheet. It has been a while since I worked on the macro. I would need to study the code to refresh my memory. As I recall, the sheet object is being accessed from Qt while the FreeCAD document object is accessed from the FreeCAD Gui Selection API. It was difficult to figure out which Qt object belonged to the selected document object in the combo view, so the workaround was to check if multiple sheets were open and if so then just give the message you saw and exit the macro. You just need to close the other sheet window while you apply the aliases to one of the sheets.
rosta
Posts: 7
Joined: Fri Jul 08, 2022 1:08 pm

Re: EasyAlias macro

Post by rosta »

TheMarkster wrote: Mon Oct 10, 2022 5:28 pm That's a known limitation of the macro. You can only have one spreadsheet open at a time or else the macro can get confused and apply things to the wrong sheet.
This is one of the many things I fixed in my improved version of the macro, see my post here:
https://forum.freecadweb.org/viewtopic. ... 15#p627015

Maybe you want to try out my version?
rslite
Posts: 1
Joined: Sun Jan 29, 2023 12:17 am

Re: EasyAlias macro

Post by rslite »

rosta wrote: Thu Oct 20, 2022 5:08 pm
TheMarkster wrote: Mon Oct 10, 2022 5:28 pm That's a known limitation of the macro. You can only have one spreadsheet open at a time or else the macro can get confused and apply things to the wrong sheet.
This is one of the many things I fixed in my improved version of the macro, see my post here:
https://forum.freecadweb.org/viewtopic. ... 15#p627015

Maybe you want to try out my version?
I also wanted to fix some things in the script and I got a version that automatically selects the spreadsheet when there's only one and also works on multiple columns (and larger than Z). However, your code is better ;)

One other thing that mine does extra is to show the exception text when there's an error setting the alias (so one knows what to fix).

I'll post mine here as well, just in case.

Code: Select all

# -*- coding: utf-8 -*-
import FreeCAD
from PySide import QtGui

"""
EasyAlias.FCMacro.py

This macro can be used to easily create aliases based on the content of selected spreadsheet 
cells in the previous column.  As an example, suppose you wish to have the following:

A1: content = 'radius', B1: content = '5', alias = 'radius'
A2: content = 'height', B1: content = '15', alias = 'height'

The traditional way to set this up would be:
Select A1
Enter radius
Select B1
Enter 5
Right-click B1
Select properties
Select Alias
Enter radius
click OK
Select A2
Enter height
Select B2
Enter 15
Right-click B2
Select Properties
Select Alias
Enter height
Click OK

Using this macro, the work flow becomes:
Select A1
Enter radius
Select B1
Enter 5
Select A2
Enter height
Select B2
Enter 15
Select A1 through A2
Run the EasyAlias macro
Done

"""

__title__ = "EasyAlias"
__author__ = "TheMarkster"
__url__ = "https://wiki.freecadweb.org/Macro_EasyAlias"
__Wiki__ = "https://wiki.freecadweb.org/Macro_EasyAlias"
__date__ = "2022.07.31" #year.month.date
__version__ = __date__


def getSelectedIndexes():
    """ Returns a QModelIndex object or None if none are selected
    use [0] to get at first cell in the selection
    use [0].row() to get first cell's row
    use [0].column() to get first cell's column
    use [-1] to get last cell in the selection
    """
    mw=FreeCADGui.getMainWindow()
    mdiarea=mw.findChild(QtGui.QMdiArea)
    subw=mdiarea.subWindowList()
    widgets = []
    for i in subw:
        if i.widget().metaObject().className() == "SpreadsheetGui::SheetView":
            widgets.append(i.widget())
    if len(widgets) > 1:
        FreeCAD.Console.PrintError("Having more than one spreadsheet view open at a time can confuse the macro.  Close the other sheet views and try again\n")
        return None
    elif len(widgets) == 1:
        return widgets[0].findChild(QtGui.QTableView).selectedIndexes()
    return None

def getSelectedCells():
    """ Returns selected cell addresses in the form of a list of tuples (selectedAddress, nextCellAddress)
    or an empty list if none selected. Works if multiple columns are selected.
    """
    sel = getSelectedIndexes()
    if not sel:
        return []

    cellAddresses = [] #will be list of tuples in form of (selectedAddress, nextCellAddress)
    for c in sel:
        addr = cellIndexToAddress(c.row(), c.column())
        addr_next = cellIndexToAddress(c.row(), c.column()+1)
        cellAddresses.append((addr, addr_next))
    return cellAddresses

def getSpreadsheet():
    """ Return first selected spreadsheet object in document or None if none selected.
    """
    # If there is a single spreadsheet in the active document just return it even if not selected
    cnt = 0
    ss = None
    for o in FreeCADGui.ActiveDocument.Document.Objects:
        if 'Spreadsheet::Sheet' in o.TypeId:
            ss = o
            cnt += 1
    if cnt == 1:
        return ss
    # Throw an exception if there are no spreadsheets in the document
    if cnt == 0:
        raise Exception('No spreadsheet in this document!')

    # If multiple exist, check which one is selected
    selObj = FreeCADGui.Selection.getSelectionEx()
    if not selObj:
        return None
    for obj in selObj:
        if 'Spreadsheet::Sheet' in obj.Object.TypeId:
            return obj.Object
        elif "App::Link" in obj.Object.TypeId:
            if 'Spreadsheet::Sheet' in obj.Object.LinkedObject.TypeId:
                return obj.Object.LinkedObject

def cellIndexToAddress(r, c):
    """ Get the spreadsheet cell address (e.g. A5 or AB10) from a combination 
    of row and column (with starting index 0).
    """
    # Check if the column is larger than 26 ("tens" in base 26 is larger than 0)
    ct = c // 26
    address = '' if ct==0 else chr(64+ct) # ASCII for 'A' is 65
    # Add the rest of the column name ("units" in base 26)
    cu = c % 26
    address += chr(65+cu)
    # Add the row
    address += str(r+1)
    return address

#thanks to Ouriço for this modification to allow parentheses to define the alias
#within the substring
def getAlias(sheet, cellAddr):
    """ Get the alias from the cell contents. If there are parentheses will use 
    the text between the parentheses
    """
    val = sheet.getContents(cellAddr)
    alias = val.replace(' ','_').replace('.','_')
    # extract any text between () and use that as the alias.
    # If brackets not found then use the original alias.
    firstidx  = alias.find('(')
    secondidx = alias.find(')')
    if 0 <= firstidx < secondidx:
        alias = alias[firstidx + 1 : secondidx]
    return alias

FreeCAD.Console.PrintMessage('*** Start EasyAlias ***\n')
s = getSpreadsheet()
if not s:
    raise Exception('No spreadsheet selected.  Please select a spreadsheet in the tree view.')

cellAddresses = getSelectedCells()
if len(cellAddresses) == 0:
    FreeCAD.Console.PrintWarning("Unable to get selected cells. Are any cells selected?\n")

s.Document.openTransaction("EasyAlias")
for ca in cellAddresses:
    addr, addr_nextcol = ca          
    # credit to red6rick for this bit of code to allow for skipped rows
    alias = getAlias(s, addr)
    if alias:
        try:
            #FreeCAD.Console.PrintMessage(f"Setting alias: {s.Label}[{addr_nextcol}] ---> {alias}\n")
            s.setAlias(addr_nextcol, alias)  #use e.g. content of A5 as alias for B5
        except Exception as e:
            FreeCAD.Console.PrintError(f"*** Unable to set alias '{alias}' for '{s.Label}[{addr_nextcol}]'\n")
            FreeCAD.Console.PrintError(f"Error: {e}\n")
            FreeCAD.Console.PrintError("Remember, aliases cannot begin with a numeral or an underscore or contain any invalid characters.\n")
    else:
        FreeCAD.Console.PrintWarning(f"Skip empty cell {addr}\n")
s.Document.commitTransaction()

App.ActiveDocument.recompute()
rosta
Posts: 7
Joined: Fri Jul 08, 2022 1:08 pm

Re: EasyAlias macro

Post by rosta »

rslite wrote: Sun Jan 29, 2023 12:38 am I also wanted to fix some things in the script and I got a version that automatically selects the spreadsheet when there's only one and also works on multiple columns (and larger than Z). However, your code is better ;)
Thank you for the compliment! :D

By the way, the EasyAlias Macro wiki page still has the old code with all the known limitations. How is the usual way to provide new versions?
@TheMarkster : You are the current author, could you update the wiki page?
TheMarkster
Veteran
Posts: 5505
Joined: Thu Apr 05, 2018 1:53 am

Re: EasyAlias macro

Post by TheMarkster »

rosta wrote: Tue Apr 25, 2023 6:52 pm
rslite wrote: Sun Jan 29, 2023 12:38 am I also wanted to fix some things in the script and I got a version that automatically selects the spreadsheet when there's only one and also works on multiple columns (and larger than Z). However, your code is better ;)
Thank you for the compliment! :D

By the way, the EasyAlias Macro wiki page still has the old code with all the known limitations. How is the usual way to provide new versions?
@TheMarkster : You are the current author, could you update the wiki page?
I'll take a look at your code this afternoon if I can or maybe tomorrow.
TheMarkster
Veteran
Posts: 5505
Joined: Thu Apr 05, 2018 1:53 am

Re: EasyAlias macro

Post by TheMarkster »

@rosta
I have updated the wiki. Thanks for the updated code. I tested only minimally, but seems to work as advertised. New version: 2023.04.27
watchdog_timer
Posts: 2
Joined: Sat Apr 29, 2023 10:11 am

Re: EasyAlias macro

Post by watchdog_timer »

I just installed the latest version of this macro (2023-04-27) , but it's not working for me. I'm getting the following error:

04:58:15 Traceback (most recent call last):
File "/home/me/.FreeCAD/Macro/EasyAlias.FCMacro", line 175, in <module>
main()
File "/home/me/.FreeCAD/Macro/EasyAlias.FCMacro", line 158, in main
for selectedCell in spreadsheet.ViewObject.getView().selectedCells():
<class 'AttributeError'>: 'Gui.ViewProviderDocumentObject' object has no attribute 'getView'

I'm using FreeCAD V0.19 on Linux
chrisb
Veteran
Posts: 53938
Joined: Tue Mar 17, 2015 9:14 am

Re: EasyAlias macro

Post by chrisb »

watchdog_timer wrote: Sat Apr 29, 2023 10:32 am I'm using FreeCAD V0.19 on Linux
That's rather old, and fixes will most probably not be made to/for this ancient version. Can you upgrade to 0.20 or even 0.21?
A Sketcher Lecture with in-depth information is available in English, auf Deutsch, en français, en español.
TheMarkster
Veteran
Posts: 5505
Joined: Thu Apr 05, 2018 1:53 am

Re: EasyAlias macro

Post by TheMarkster »

The latest version uses an API that was not available before. I updated the wiki to show it requires 0.20.
Post Reply