5
\$\begingroup\$

I am using the code below to pass filter selections from table A to table B in Spotfire. I am doing this to create a filtering relationship between the tables, but skipping the creation of a traditional Spotfire table relationship, as this is not advisable in this setting.

The code is associated with a document property which itself is driven by a simple data function, following the instructions found here.

After it is triggered, the code finds a specific filter in table A. It reads the content and creates a list. It checks the length of the list, and if greater than zero, it takes the filter object and passes it to the matching filter in table B. If nothing is selected (len = 0), then the matching filter in table B is reset. The process is repeated for multiple filters.

As far as I can tell it works on my machine. But even as a complete beginner, I can tell it is poorly written. I would appreciate suggestions and feedback on how I can make it better.

from Spotfire.Dxp.Application.Filters import *
from Spotfire.Dxp.Application.Visuals import VisualContent
from System import Guid     

#Get the active page and filterPanel
page = Application.Document.ActivePageReference
filterPanel = page.FilterPanel

##########################reasons#############################################
#get information for the first filter
theFilter = filterPanel.TableGroups[0].GetFilter("Reason")
lbFilter = theFilter.FilterReference.As[ListBoxFilter]()
#get information for the second filter
theFilter2 = filterPanel.TableGroups[1].GetFilter("Reason")
lb2Filter = theFilter2.FilterReference.As[ListBoxFilter]()

#check if any selections are made in the first filter
n = []
for value in lbFilter.SelectedValues:
    n.append(value)
if len(n)>0:
    lb2Filter.IncludeAllValues = False
    lb2Filter.SetSelection(lbFilter.SelectedValues)
#if no selections are made, reset the second filter
else:
    lb2Filter.Reset()

#########################Type#####################################################
theFilterPT = filterPanel.TableGroups[0].GetFilter("Type")
lbFilterPT = theFilterPT.FilterReference.As[ListBoxFilter]()

theFilter2PT = filterPanel.TableGroups[1].GetFilter("Type")
lb2FilterPT = theFilter2PT.FilterReference.As[ListBoxFilter]()

n = []
for value in lbFilterPT.SelectedValues:
    n.append(value)
if len(n)>0:
    lb2FilterPT.IncludeAllValues = False
    lb2FilterPT.SetSelection(lbFilterPT.SelectedValues)
else:
    lb2FilterPT.Reset()
\$\endgroup\$
2
  • 3
    \$\begingroup\$ Hey! Thanks for the ping, I've edited the title to bring it in line with our rules. Since most people need help the title you had wouldn't be any different than other questions. I have also made some minor edits to your description so that it's clear that the question is on-topic. Thank you for the added description and pinging me. If you think I've drastically changed your question feel free to roll back my edit :) \$\endgroup\$ – Peilonrayz Feb 11 '20 at 13:10
  • \$\begingroup\$ @Peilonrayz. I appreciate the help, and thanks for creating the tags. \$\endgroup\$ – K R Feb 11 '20 at 14:03
2
\$\begingroup\$
  • from ... import * is discouraged by a large amount of people.

    Currently it's easy to see what comes from Filters, as you only have one. But if you have two or more than it just becomes impossible to understand.

  • Your code doesn't look Pythonic, unfortunately this is because Spotfire has violated PEP 8 making anything that uses it look, to my eyes, ugly.

    Whilst I'm not a fan of the style you are using, I would like to congratulate you on sticking to one style. Well done.

  • If the comments help you then keep them. However if you wrote them to help us then I would recommend that you remove them.

  • You have lots of un-needed variables; page, theFilter, theFilterPT.
  • Functions are the bread and butter of programming. And your code is in some dire need of functions.

In your code we see that you have the following definitions:

#get information for the first filter
theFilter = filterPanel.TableGroups[0].GetFilter("Reason")
lbFilter = theFilter.FilterReference.As[ListBoxFilter]()
#get information for the second filter
theFilter2 = filterPanel.TableGroups[1].GetFilter("Reason")
lb2Filter = theFilter2.FilterReference.As[ListBoxFilter]()
theFilterPT = filterPanel.TableGroups[0].GetFilter("Type")
lbFilterPT = theFilterPT.FilterReference.As[ListBoxFilter]()

theFilter2PT = filterPanel.TableGroups[1].GetFilter("Type")
lb2FilterPT = theFilter2PT.FilterReference.As[ListBoxFilter]()

We can see clearly that only two things change between them; [0] and [1], and "Reason" and "Type". I personally think filterPanel.TableGroups[0] shouldn't be in the function and we can just pass the filter in the function. This can result in:

def getGroup(tableGroup, filter):
    return (
        tableGroup
            .getFilter(filter)
            .FilterReference
            .As[Filters.ListBoxFilter]()
    )

After this we can see you've written something else twice too.

n = []
for value in lbFilter.SelectedValues:
    n.append(value)
if len(n)>0:
    lb2Filter.IncludeAllValues = False
    lb2Filter.SetSelection(lbFilter.SelectedValues)
#if no selections are made, reset the second filter
else:
    lb2Filter.Reset()

Firstly creating n can be simplified simply as list(lbFilter.SelectedValues). You don't need to use len(n)>0 as just if n: does that check, with a list, for you. Finally I find it easier to have the smaller code block first, and so flipped the if.

def filterGroups(tableGroups, filter):
    group1 = getGroup(tableGroups[0], filter)
    group2 = getGroup(tableGroups[1], filter)

    if not list(group1.SelectedValues):
        group2.Reset()
    else:
        group2.IncludeAllValues = False
        group2.SetSelection(group1.SelectedValues)

Finally I would wrap everything in a main function and you'd get the following:

import System
from Spotfire.Dxp.Application import Filters, Visuals


def getGroup(tableGroup, filter):
    return (
        tableGroup
            .getFilter(filter)
            .FilterReference
            .As[Filters.ListBoxFilter]()
    )


def filterGroups(tableGroups, filter):
    group1 = getGroup(tableGroups[0], filter)
    group2 = getGroup(tableGroups[1], filter)

    if not list(group1.SelectedValues):
        group2.Reset()
    else:
        group2.IncludeAllValues = False
        group2.SetSelection(group1.SelectedValues)


def main():
    tableGroups = (
        Filters
            .Application
            .Document
            .ActivePageReference
            .FilterPanel
            .TableGroups
    )
    filterGroups(tableGroups, "Reason")
    filterGroups(tableGroups, "Type")


if __name__ == '__main__':
    main()
\$\endgroup\$
2
  • \$\begingroup\$ Thank you so much for this. Now that I wrapped up another project, I can get back to this code. One question: What is the benefit of wrapping it in a main function? \$\endgroup\$ – K R Apr 25 '20 at 17:16
  • \$\begingroup\$ @KR In short main() stops the variables from being in the global scope. They're not needed to be in global scope and for some this causes an unhealthy dependence on global variables. It also causes there to be less name collisions and so you can safely call different but similar variables in two separate functions the same name. This allows you to use shorter and easier to read variable names everywhere. \$\endgroup\$ – Peilonrayz Apr 25 '20 at 17:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.