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

	@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"]))
		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(fileName))