Inspecting Python objects with custom inspectors
Glamorous Toolkit works with other runtimes. For example, we can work with Python through PythonBridge. But what might be less obvious is that we can also extend the inspector using Python.
To exemplify how this works, let's consider exploring a movie collection defined in this CSV:
csvFile := (FileLocator gtResource / 'feenkcom' / 'gtoolkit-demos' / 'data' / 'imdb' / 'Movies.csv') fullName.
We load it with pandas, and to do that we first set up the Python runtime by installing the pandas module. (NB: You might need to install pipenv first.)
PBApplication isRunning ifFalse: [ PBApplication start ]. PBApplication uniqueInstance installModule: 'pandas'.
Ok, now that's done. Next, we define views as extensions to the movie collection entities:
import pandas from gtoolkit_bridge import gtView class Movie: def __init__(self, series): self.series = series @gtView def gtViewDescription(self, builder): text = builder.textEditor() text.title("Description") text.priority(30) text.setString(str(self.series)) return text @gtView def gtViewDetails(self, builder): clist = builder.columnedList() clist.title("Details") clist.priority(20) clist.items(lambda: list(self.series.index)) clist.column('Key', lambda each: each) clist.column('Value', lambda each: str(self.series[each])) clist.set_accessor(lambda each: self.series[each]) return clist class MovieCollection: def __init__(self, dataFrame): self.df = dataFrame def size(self): return len(self.df.index) def movieAtPosition(self, index): return Movie(self.df.loc[index]) def directors(self): values = self.df["Directors"].astype(str).unique() values.sort() return map(lambda each: [each, self.moviesWithDirector(each)], values) def years(self): values = self.df["Year"].unique().tolist() values.sort() return map(lambda each: [each, self.moviesReleasedInYear(each)], values) def moviesReleasedInYear(self, year): return MovieCollection(self.df[self.df["Year"] == year].reset_index(drop=True)) def moviesWithDirector(self, director): return MovieCollection(self.df[self.df["Directors"] == director].reset_index(drop=True)) @gtView def gtViewYears(self, builder): clist = builder.columnedList() clist.title("Years") clist.priority(30) clist.items(lambda: self.years()) clist.column("Year", lambda each: str(each[0])) clist.column("Count", lambda each: str(each[1].size())) clist.set_accessor(lambda each: [*self.years()][each][1]) return clist @gtView def gtViewDirectors(self, builder): clist = builder.columnedList() clist.title("Directors") clist.priority(20) clist.items(lambda: self.directors()) clist.column("Director", lambda each: str(each[0])) clist.column("Count", lambda each: str(each[1].size())) clist.set_accessor(lambda each: [*self.directors()][each][1]) return clist @gtView def gtViewMovies(self, aBuilder): table = aBuilder.columnedList() table.title("Movies") table.priority(50) table.items(lambda: list(self.df.index)) table.column("Title", lambda index: str(self.df.at[index, "Title"])) table.column("Release date", lambda index: str(self.df.at[index, "Release Date"])) table.column("Directors", lambda index: str(self.df.at[index, "Directors"])) table.column("Genres", lambda index: str(self.df.at[index, "Genres"])) table.set_accessor(lambda each: Movie(self.df.loc[list(self.df.index)[each]])) return table def gtViewMoviesDetails(self, aBuilder): table = aBuilder.columnedList() table.title("Movies with details") table.priority(50) table.items(lambda: list(self.df.index)) for each in self.df.columns: (lambda col: table.column(col, lambda index: str(self.df.at[index, col])))(each) table.set_accessor(lambda each: Movie(self.df.loc[list(self.df.index)[each]])) return table
Now we are set, so let's inspect the movie collection:
MovieCollection(pandas.read_csv(csvFile))
Alternative approach
Instead of putting our code inside a Python snippet, we can also use a traditional Python source file.
First we copy a predefined Python source file movie.py
into the working directory of the PythonBridge so that it can be found in Python's default search path.
sourceFile := FileLocator gtResource / 'feenkcom' / 'gt4python' / 'data' / 'python' / 'movie.py'. PBPlatform current workingDirectory ensureCreateDirectory. sourceFile copyTo: PBPlatform current workingDirectory / sourceFile basename. PBPlatform current workingDirectory
We can open an IDE view on local Python source files in the PythonBridge's working directory to verify that all is OK and to view the file's content. Press the + icon top left to expand the directory view and the - icon to get back.
GtLSPPythonModel onDirectory: PBPlatform current workingDirectory
Now we can use our local source file from a Python snippet.
import movie import pandas movie.MovieCollection(pandas.read_csv(csvFile))