Forum Archive

Modules Of Pythonista Displayed With Help

ramvee

Hi,
I wanted a list of modules importable in Pythonista and display corresponding help() text.
As i found help() spewing out text to console for each module cumbersome, i wrote this snippet.
I am sure there must be better ways to doing this. But I am just learning, and
it works :)
Also I learned a little bit about stdout, pkgutil, StringIO in this process.
More Power To Pythonista!

# View help text for all the importable modules
# using StringIO
# Edited based on suggestions by @shtek & @Phuket2 
# Edited to make use of contextlib.redirect_stdout
# coding: utf-8

import ui, pkgutil
from io import StringIO
from contextlib import redirect_stdout

w, h = ui.get_screen_size()
fontsize = 15
if w > 767:
    fontsize = 24
if w > 1500:
    fontsize = 36

modulelist = []
for pkg in pkgutil.iter_modules():
    modulelist.append(pkg[1])


def load_action(sender):
    ttval = (ttableview1.data_source.items[sender.selected_row])
    # redirecting output using contextlib.redirect_stdout
    help_str = StringIO()
    with redirect_stdout(help_str):
        help(ttval)
    helptext = help_str.getvalue()
    ttextview1.text = helptext

ttextview1 = ui.TextView(name='ttextview1', frame=(w * .3, 0, w * .7, h * .9), flex='WH', text='Click Any Module On Left', border_width=1, border_color=0, font=('<system>', fontsize), bg_color = 'lightyellow', text_color = 'red')
ttextview1.editable = False

ttableview1 = ui.TableView(name='ttableview1', frame=(0, 0, w * .3, h * .9), flex='HR', border_width=1, border_color=0, row_height=h / 20, seperator_color = 'red', alpha = .8)

list_source = ui.ListDataSource(sorted(modulelist))
list_source.font = ('Avenir Next Condensed', fontsize)
list_source.text_color = 'red'
list_source.highlight_color = 'yellow'
ttableview1.data_source = ttableview1.delegate = list_source
ttableview1.data_source.action = load_action

vname = str(len(modulelist)) + ' Modules'
view = ui.View(name=vname, bg_color = 'yellow', frame=(0, 0, w, h * .9))

view.add_subview(ttableview1)
view.add_subview(ttextview1)
view.present(title_bar_color = 'yellow')
shtek

better add color, search and remove keyboard

Phuket2

@ramvee, as I am using an external keyboard, I dont see the keyboard. I also think you need a search or maybe a filter is a better word in this case. I think a simple 'is in' filter would work fine. Another think you could think about if you wanted, is a way to scale your fonts. My list font is way to small because I am on a IPad Pro 12.9.
Was interesting for me to see how you captured the stdout. I had thought about how you would go about it before, but never got around to seeing it done

ramvee

@shtek and @Phuket2 ,
Thank you for your suggestions,
i edited the code to suppress the keyboard, added color and tried to use scaleable fonts.
However i need more learning to incorporate search/filter option.

ps. @Phuket2 , was shocked to see that your 12.9 iPad Pro has a screen resolution of 2732 X 2048 pixels! Wow! :)

Phuket2

@ramvee , yes I really love the iPad Pro. But everyone has there own idea. Below, i did some code to illustrate a filter. Sure you can do it. I tried to keep it straight fwd, i hope i did that. But it really is easy. The hardest part is to get the view to behave itself:). As I mentioned before, for this data I would say a simple filter like below, should be enough. It's very simple, case insensitive and no wild cards etc...
Its not a full implementation of what you need, but its pretty close I think.

import ui
from random import choice
from faker import Faker
fake = Faker()

# if you want random data each time this is run, comment out the line below.
fake.seed(2017)


def generate_fake_names(num_names):
    '''
    use faker module to generate a list fake first & last names
    '''
    return ['{} {}'.format(fake.first_name(), fake.last_name())
            for _ in range(num_names)]


class MyClass(ui.View):
    def __init__(self, items=None, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.tf = None
        self.items = items if items else []
        self.table = None
        self.make_view()

    def make_view(self):
        _v_gap = 5

        # make a textfield
        tf = ui.TextField(frame=self.bounds.inset(5, 5), flex='w',
                          placeholder='Filter', clear_button_mode='always',
                          autocapitalization=ui.AUTOCAPITALIZE_NONE,
                          spellchecking_type=False
                          )

        tf.height = 32
        tf.delegate = self
        self.tf = tf
        self.add_subview(tf)

        # make a table
        tbl = ui.TableView(frame=self.bounds.inset(5, 5),
                           corner_radius=3, flex='wh')
        tbl.y = tf.frame.max_y + _v_gap
        tbl.height = self.height - tf.frame.max_y - _v_gap * 2

        # -- Add a ui.ListDataSource
        tbl.data_source = ui.ListDataSource(self.items)
        self.table = tbl
        self.add_subview(tbl)

    def textfield_did_change(self, textfield):
        self.filter_data(textfield.text)

    def filter_data(self, filter_text=''):
        ft = filter_text.lower()
        if not len(ft):
            self.table.data_source.items = self.items
        else:
            self.table.data_source.items =\
            [s for s in self.items if ft in s.lower()]

if __name__ == '__main__':
    style = choice(['sheet', '', 'popover', 'panel', 'sidebar'])
    # style = 'sheet'
    f = (0, 0, 300, 400)
    v = MyClass(frame=f, items=(generate_fake_names(100)),
                bg_color='teal', name=style)
    v.present(style=style, animated=False)
ramvee

@Phuket2
Very nice example, my friend thank you!
I will try and learn it!
Grateful 😁