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:

fileName := (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

	def gtViewDescription(self, builder):
		text = builder.textEditor()
		return text

	def gtViewDetails(self, builder):
		clist = builder.columnedList()
		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()
		return map(lambda each: [each, self.moviesWithDirector(each)], values)
	def years(self):
		values = self.df["Year"].unique().tolist()
		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))

	def gtViewYears(self, builder):
		clist = builder.columnedList()
		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

	def gtViewDirectors(self, builder):
		clist = builder.columnedList()
		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

	def gtViewMovies(self, aBuilder):
		table = aBuilder.columnedList()
		table.items(lambda: list(self.df.index))
		table.column("Title", lambda index: str([index, "Title"]))
		table.column("Release date", lambda index: str([index, "Release Date"]))
		table.column("Directors", lambda index: str([index, "Directors"]))
		table.column("Genres", lambda index: str([index, "Genres"]))
		return table
	def gtViewMoviesDetails(self, aBuilder):
		table = aBuilder.columnedList()
		table.title("Movies with details")
		table.items(lambda: list(self.df.index))
		for each in self.df.columns:
			(lambda col: table.column(col, lambda index: str([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: