Forum Archive

Ui button action parameters

resserone13

If I have a function that is a button action and the button action takes another function that requires parameters. How do I feed the parameters to the function inside of the button action?


def info_page(sender):
    page_info(text, image, dwell_time, desc)

So when I do ....

Btn.action = info_page

How do I feed the parameters to the page_info inside of info_page?

cvp

@resserone13 if I correctly understand, what happens rarely these last times:

import ui

def page_info(text, image, dwell_time, desc):
    print(text, image, dwell_time, desc)

def info_page(sender):
    page_info(sender.prm_text, sender.prm_image, sender.prm_dwell_time, sender.prm_desc)

Btn = ui.Button()
Btn.background_color = 'white'
Btn.prm_text = 'xxx'
Btn.prm_image = ui.Image.named('emj:Airplane')
Btn.prm_dwell_time = 23
Btn.prm_desc = 'abcde'
Btn.title = 'tap me'
Btn.action = info_page
Btn.present('sheet')
mikael

@resserone13, or using standard library functools.partial, which is a really good thing to have in your toolbox for any Python coding.

from functools import partial
import ui


def page_info(text, image, sender):
    print(text, image, sender.title)

btn = ui.Button(
    background_color='white',
    title="Tap me",
)

btn.action = partial(page_info, 'some text', 'some image')

btn.present('sheet')
cvp

@mikael I knew partial but I didn't know we could use it as action. Marvelous language

resserone13

@mikael and @cvp Thanks for the help as always. I ended up using the @cvp suggestion. I’m redoing the personal protective equipment app using the ui module instead of scene. Coming along well.

I hve an issue with displaying hyperlinks in a text view and getting the ui.path.rounde_rect to draw to the screen I a for loop.


import console
import ui
from ppeasy_text import text

app_title = 'PPEasy'
app_title_font = 'Didot'
btn_font = 'Didot'
text_font = '<System>'
text_color = '1D3557'
bg_color = 'CAF0F8'
btn_border_color = '0077B6'
btn_color = 'CAF0F8'
btn_shadow=('gray', 0, 7, 2)
btn_corner_radius = 10

screen_size = ui.get_window_size()

links ='\n\nwww.cdv.gov \n\nwww.fda.gov \n\nwww.coronavirus.gov \n\nwww.covid19.healthdata.org'


v = ui.View()
v.background_color=bg_color    

def make_title(text):
    title = ui.Label()
    title.font= app_title_font, 30
    title.text = text
    title.alignment= ui.ALIGN_CENTER
    title.frame=(screen_size.w * 0.10, screen_size.h * 0.05, screen_size.w * 0.75, screen_size.h * 0.05)
    return  title

def make_button(title, locale, text_param, image_param,dwell_time_param, desc_param, action=None):
    btn= ui.Button()
    btn.background_color='white'
    btn.title=title
    btn.font=(btn_font, 18)
    btn.background_color=bg_color
    btn.tint_color='black'
    btn.border_width=2
    btn.border_color='black'
    btn.frame=(screen_size.w * 0.15, screen_size.h * locale, screen_size.w * 0.7, screen_size.h * 0.07)
    btn.corner_radius= btn_corner_radius
    btn.text_param = text_param
    btn.image_param = image_param
    btn.dwell_time_param = dwell_time_param
    btn.desc_param = desc_param
    btn.action = action
   # v.add_subview(btn_1)
    return  btn

def page_info(text, image, dwell_time, desc):

    v = ui.View()
    v.background_color=bg_color
    title= make_title(text)

    for x, y in (screen_size.w/2, screen_size.h * 0.8), (screen_size.w/2, screen_size.h * 0.4), (screen_size.w/2, screen_size.h * 0.1):
        line = ui.Path()
        line = ui.Path.rounded_rect(x, y, screen_size.w * 0.8, 0, 50)
        line.line_width=3
        #display.add_subview(line)

    img = ui.ImageView()
    img.image = ui.Image.named(image)
    img.frame=(screen_size.w * 0.15, screen_size.h * 0.15, 300, 300)
    img.content_mode = ui.CONTENT_SCALE_ASPECT_FIT

    dwell = ui.Label()
    dwell.font= app_title_font, 25
    dwell.text = dwell_time
    dwell.alignment= ui.ALIGN_CENTER
    dwell.frame=screen_size.w * 0.10, screen_size.h * 0.45, screen_size.w * 0.75, screen_size.h * 0.05

    description = ui.TextView()
    description.editable=False
    description.scroll_enabled=True
    description.auto_content_inset=True
    description.background_color=bg_color
    description.alignment= ui.ALIGN_LEFT
    description.number_of_lines=0
    description.line_break_mode=ui.LB_CHAR_WRAP
    description.font=(text_font, 17)
    description.frame=(screen_size.w * 0.12, screen_size.h * 0.60, screen_size.w * 0.75, screen_size.h * 0.60)
    description.text=desc

    v.add_subview(title)
    v.add_subview(img)
    v.add_subview(dwell)
    v.add_subview(description)
    v.present('sheet', hide_title_bar=True)  

def mask_page(sender):
    v = ui.View()
    v.background_color=bg_color

    title=make_title('mask')

    level_1_btn = make_button('Level 1', 0.12, 'Level 1', 'Level1Mask.PNG', '', level_1_desc, info_page)
    level_3_btn = make_button('Level 3', 0.22, 'Level 3', 'Level3Mask.PNG', '', level_3_desc, info_page)
    k_n95_btn = make_button('K-N95', 0.32, 'K-N95', 'Kn95Mask.PNG', '', k_n95_desc, info_page)
    n95_btn = make_button('N95', 0.42, 'N95', 'N95Mask.PNG', '', n95_desc, info_page)
    papr_btn = make_button('Papr', 0.52, 'Papr', 'PaprMask.PNG', '', papr_desc, info_page)

    v.add_subview(title)
    v.add_subview(level_1_btn)
    v.add_subview(level_3_btn)
    v.add_subview(k_n95_btn)
    v.add_subview(n95_btn)
    v.add_subview(papr_btn)
    v.present('sheet', hide_title_bar=True)

def info_page(sender):
    page_info(sender.text_param, sender.image_param, sender.dwell_time_param, sender.desc_param)

def gowns_page(sender):
    v = ui.View()
    v.background_color=bg_color
    title= make_title('Gowns')

    cloth_btn = make_button('Cloth', 0.12, 'Cloth', 'ClothGown.PNG', '', cloth_desc, info_page)
    plastic_btn = make_button('Plastic', 0.22, 'Plastic', 'PlasticGown.PNG', '', plastic_desc, info_page)

    v.add_subview(title)
    v.add_subview(cloth_btn)
    v.add_subview(plastic_btn)
    v.present('sheet', hide_title_bar=True)

def wipes_page(sender):
    v = ui.View()
    v.background_color=bg_color
    v.present('sheet', hide_title_bar=True)

def faq_page(sender):
    v = ui.View()
    v.background_color=bg_color

    title = ui.Label()
    title.font= text_font, 25
    title.text = "FAQ"
    title.alignment= ui.ALIGN_CENTER
    title.frame=screen_size.w * 0.1, screen_size.h * 0.02, screen_size.w * 0.75, screen_size.h * 0.05
    tv = ui.TextView()
    tv.editable=False
    tv.scroll_enabled=True
    tv.auto_content_inset=True
    tv.background_color=bg_color
    tv.alignment= ui.ALIGN_LEFT
    tv.number_of_lines= 0
    tv.line_break_mode = ui.LB_CHAR_WRAP
    tv.font= text_font, 17
    tv.frame= screen_size.w * 0.07, screen_size.h * 0.08, screen_size.w * 0.87, screen_size.h * 0.85
    tv.text = "Q. What is the difference between a K-N96 and a N95? \n\nA. Both products are said to filter 95 percent of aerosol particulates. K-N95 respirators differ from N95 respirators because they meet the Chinese standard but are not regulated by U.S. agencies. \n\nQ. Do mask really work? \n\nA. Yes. It stops the transmission of saliva and mucus from person to person "
    v.add_subview(title)
    v.add_subview(tv)
    v.present('sheet', hide_title_bar=True)

def dd_page(sender):
    v = ui.View()
    v.background_color=bg_color

    title = ui.Label()
    title.font= text_font, 25
    title.text = "Dos and Don'ts"
    title.alignment= ui.ALIGN_CENTER
    title.frame=screen_size.w * 0.1, screen_size.h * 0.02, screen_size.w * 0.75, screen_size.h * 0.05

    tv = ui.TextView()
    tv.editable=False
    tv.scroll_enabled=True
    tv.auto_content_inset=True
    tv.background_color=bg_color
    tv.alignment= ui.ALIGN_LEFT
    tv.number_of_lines= 0
    tv.line_break_mode = ui.LB_CHAR_WRAP
    tv.font= text_font, 17
    tv.frame= screen_size.w * 0.07, screen_size.h* 0.08, screen_size.w * 0.85, screen_size.h * 0.85
    tv.text = "DO wear your cask correctly. Cover you mouth and nose. \nDON'T improperly wear your mask. Make sure you mouth and nose is covered.  \n\nDO keep you mask clean. \nDON'T touch mask with your hands. \n\nDO change your mask regulary. \nDON'T wear a dirty mask' \n\nDO wear your mask while talking. \nDON'T remove your mask to talk. \n\nDO wear masks that fit well and trap your breath. \nDON'T ware masks with vents. They allow your breath to escape and spread to others. \n\nDo keep 6 feet apart when out in public even while wearing a mask. \nDON'T beark social distancing even if your wearing a mask. \n\nDO"
    v.add_subview(title)
    v.add_subview(tv)
    v.present(hide_title_bar=True)

def covid_page(sender):
    v= ui.View()
    v.background_color=bg_color

    title = ui.Label()
    title.font= app_title_font, 25
    title.text = 'Covid Information'
    title.alignment= ui.ALIGN_CENTER
    title.frame=screen_size.w * 0.1, screen_size.h * 0.02, screen_size.w * 0.75, screen_size.h * 0.05

    tv = ui.TextView()
    tv.editable=False
    tv.scroll_enabled=True
    tv.background_color=bg_color
    tv.alignment= ui.ALIGN_LEFT
    tv.number_of_lines= 0
    tv.line_break_mode = ui.LB_CHAR_WRAP
    tv.font= app_title_font, 17
    tv.frame= screen_size.w * 0.07, screen_size.h * 0.08, screen_size.w * 0.87, screen_size.h * 0.85
    tv.text = 'COVID-19 is caused by a coronavirus called SARS-CoV-2. Older adults and people who have severe underlying medical conditions like heart or lung disease or diabetes seem to be at higher risk for developing more serious complications from COVID-19 illness. \n\nSymptoms may appear 2-14 days after exposure to the virus. People with these symptoms may have COVID-19:\n\n•Fever or chills\n•Cough\n•Shortness of breath or difficulty breathing\n•Fatigue\n•Muscle or body aches\n•Headache\n•New loss of taste or smell\n•Sore throat\n•Congestion or runny nose\n•Nausea or vomiting\n•Diarrhea \n\nEmergency warning signs. If someone is showing any of these signs, seek emergency medical care immediately:\n\n•Trouble breathing\n•Persistent pain or pressure in the chest\n•New confusion\n•Inability to wake or stay awake\n•Bluish lips or face \n\nCOVID-19 most commonly spreads during close contact. People who are physically near (within 6 feet) a person with COVID-19 or have direct contact with that person are at greatest risk of infection. \n\nWhen people with COVID-19 cough, sneeze, sing, talk, or breathe they produce respiratory droplets. These droplets can range in size from larger droplets (some of which are visible) to smaller droplets. Small droplets can also form particles when they dry very quickly in the airstream. \n\nInfections occur mainly through exposure to respiratory droplets when a person is in close contact with someone who has COVID-19. Respiratory droplets cause infection when they are inhaled or deposited on mucous membranes, such as those that line the inside of the nose and mouth. \n\nAs the respiratory droplets travel further from the person with COVID-19, the concentration of these droplets decreases. Larger droplets fall out of the air due to gravity. Smaller droplets and particles spread apart in the air.With passing time, the amount of infectious virus in respiratory droplets also decreases.'
    v.add_subview(title)
    v.add_subview(tv)
    v.present(hide_title_bar=True)

def links_page(sender):
    v = ui.View()
    v.background_color=bg_color

    v= ui.View()
    v.background_color=bg_color
    title = ui.Label()
    title.font= text_font, 25
    title.text = "Links"
    title.alignment= ui.ALIGN_CENTER
    title.frame=screen_size.w * 0.1, screen_size.h * 0.02, screen_size.w * 0.75, screen_size.h * 0.05

    tv = ui.TextView()
    tv.editable=False
    tv.scroll_enabled=True
    tv.auto_content_inset=True
    tv.background_color=bg_color
    tv.alignment= ui.ALIGN_CENTER
    tv.number_of_lines= 0
    tv.line_break_mode = ui.LB_CHAR_WRAP
    tv.font= text_font, 22
    tv.frame= screen_size.w * 0.07, screen_size.h * 0.08, screen_size.w * 0.87, screen_size.h * 0.85
    tv.text ='https://www.who.int/'
    v.add_subview(title)
    v.add_subview(tv)
    v.present('sheet', hide_title_bar=True)

title = make_title(app_title)

mask_btn = make_button('Mask', 0.12, '', '', '', '', mask_page)
shield_btn = make_button('Shield', 0.22, 'Face Shield', 'FaceShield.JPG', '',fs_desc, info_page)
gowns_btn = make_button('Gowns', 0.32,'', '', '', '', gowns_page)
wipes_btn = make_button('Wipes', 0.42, '', '', '', '', wipes_page)
faq_btn = make_button('FAQ', 0.52, '','','','',faq_page)
dd_btn = make_button('Dos/Donts', 0.62, '', '', '', '', dd_page)
covid_btn = make_button('Covid Info', 0.72, '', '', '', '', covid_page)
links_btn = make_button('Links', 0.82, '', '', '', '', links_page)

v.add_subview(title)
v.add_subview(mask_btn)
v.add_subview(shield_btn)
v.add_subview(gowns_btn)
v.add_subview(wipes_btn)
v.add_subview(faq_btn)
v.add_subview(dd_btn)
v.add_subview(covid_btn)
v.add_subview(links_btn)

v.present('full_screen', orientations='portrait', hide_title_bar=True)

I also have Trouble importing text from another file. I tried to set it up like the brick breaker levels and colors example but it’s not working.

cvp

@resserone13 first of all, to avoid repetitive code, you could create your own class with, as parameters, varying infos, here l_txt and tv_txt

class my_View(ui.View):
  def __init__(self,l_txt,tv_txt):
    self.background_color=bg_color
    title = ui.Label()
    title.font= text_font, 25
    title.text = l_txt
    title.alignment= ui.ALIGN_CENTER
    title.frame=screen_size.w * 0.1, screen_size.h * 0.02, screen_size.w * 0.75, screen_size.h * 0.05

    tv = ui.TextView()
    tv.editable=False
    tv.scroll_enabled=True
    tv.auto_content_inset=True
    tv.background_color=bg_color
    tv.alignment= ui.ALIGN_CENTER
    tv.number_of_lines= 0
    tv.line_break_mode = ui.LB_CHAR_WRAP
    tv.font= text_font, 22
    tv.frame= screen_size.w * 0.07, screen_size.h * 0.08, screen_size.w * 0.87, screen_size.h * 0.85
    tv.text = tv_txt
    self.add_subview(title)
    self.add_subview(tv)
.
.
.
def links_page(sender):
    v= my_View('Links', 'https://www.who.int/')
    v.present('sheet', hide_title_bar=True)
resserone13

@cvp ok be been trying to do more of that. That why I have the page_info(). I need to make a few more classes to cut down on the code.

How can I make the text on the links page be able to receive touches and take the user to the websites.

cvp

@resserone13 I was busy to understand your loop/path in page_info. What is its goal?

cvp

@resserone13 said:

take the user to the websites

How do you want to "take the user to a website"?
Open Safari or have a web view in your ui?

Easiest way is to have, instead of a textview, a button and it's action to open the url

resserone13

@cvp page_info() is like any_scene() class I used when I made the app with scene. I modeled page_info() after any_scene(). Page_info() is a generic function to display the page. Kinda like your myview().

I’ve also created make_btn() and make_title() for making buttons and creating titles.

resserone13

@resserone13 I would like them to be directed to the website in Safari.

cvp

@resserone13 written just before your answer about safari, this code opens a web view in your app.
If you open safari, you will leave your app. Sure of this?

class my_View(ui.View):
  def __init__(self,l_txt,tv_txt):
    self.background_color=bg_color
    self.frame = (0,0,700,700)
    title = ui.Label()
    title.font= text_font, 25
    title.text = l_txt
    title.alignment= ui.ALIGN_CENTER
    title.frame=screen_size.w * 0.1, screen_size.h * 0.02, screen_size.w * 0.75, screen_size.h * 0.05

    if tv_txt.startswith('http'):
      tv = ui.Button()
      tv.title = tv_txt
      def open_url(sender):
        wv = ui.WebView()
        wv.load_url(sender.title)
        wv.frame = sender.superview.frame
        wv.present('sheet')
        wv.wait_modal()
      tv.action = open_url
    else:
      tv = ui.TextView()
      tv.editable=False
      tv.scroll_enabled=True
      tv.auto_content_inset=True
      tv.number_of_lines= 0
      tv.line_break_mode = ui.LB_CHAR_WRAP
      tv.text = tv_txt
    tv.background_color=bg_color
    tv.alignment= ui.ALIGN_CENTER
    tv.font= text_font, 22
    tv.frame= screen_size.w * 0.07, screen_size.h * 0.08, screen_size.w * 0.87, screen_size.h * 0.85
    self.add_subview(title)
    self.add_subview(tv)
cvp

@resserone13 i'm no more available during one hour...sorry

cvp

@resserone13 said:

from ppeasy_text import text

Where can I find this?

cvp

@resserone13 said:

I also have Trouble importing text from another file. I tried to set it up like the brick breaker levels and colors example but it’s not working.

Where is your code for this?

resserone13

@cvp the imported file ppeasy_text.py is in the same folder as the main.py file.

from ppeasy_text import text

cvp

@resserone13 I understand but it is not in your GitHub, for testing your code 😀

cvp

@resserone13 did you try my code to open a web view?

cvp

@resserone13 said:

line = ui.Path.rounded_rect(x, y, screen_size.w * 0.8, 0, 50)

Your rectangle has a width of 0, thus not displayed...

cvp

@resserone13 and path can be drawn in the draw method of the ui.View, but, it seems, only if you sub-class it.

class View_with_Path(ui.View):
  def draw(self):
    for x, y in (screen_size.w/2, screen_size.h * 0.8), (screen_size.w/2, screen_size.h * 0.4), (screen_size.w/2, screen_size.h * 0.1):
        path = ui.Path.rounded_rect(x, y, screen_size.w * 0.8, 10, 50)
        path.line_width=3
        ui.set_color('yellow') # only to see where the path is drawn
        path.fill()

def page_info(text, image, dwell_time, desc):

    v = View_with_Path()
    #v = ui.View()
    v.frame = (0,0,screen_size.w,screen_size.h)
    v.background_color=bg_color
    title= make_title(text)

    #for x, y in (screen_size.w/2, screen_size.h * 0.8), (screen_size.w/2, screen_size.h * 0.4), (screen_size.w/2, screen_size.h * 0.1):
        #path = ui.Path.rounded_rect(x, y, screen_size.w * 0.8, 0, 50)
        #line.line_width=3
        #ui.set_color('yellow')
        #path.fill()
        #display.add_subview(line)
.
.
.
resserone13

@cvp said:

@resserone13 did you try my code to open a web view?
Yes I got the web view working. Thanks.

cvp

@resserone13 said:

Yes I got the web view working

If you prefer to open really Safari:

    if tv_txt.startswith('http'):
      tv = ui.Button()
      tv.title = tv_txt
      def open_url(sender):
        import webbrowser
        webbrowser.open('safari-'+sender.title)
mikael

@resserone13, things are moving so fast here that I am probably hopelessly late, but I can think of two options if you want to have clickable links in the middle of a body of text.

First is to use a webview, which works but has the downside that it wants to open the link in place. You can get around this by using a delegate to capture the attempt and open the link in Safari instead.

Second is to use objc and attributed text with a textview or label. This is not difficult but can be made even easier with an addition to the RichLabel that is part of the ui3 package, let me know if that seems relevant to you.

resserone13

@mikael I just have my iPhone 11 with Pythonista. I don’t know if I have ui3. I’ve had trouble using stash to import and pip install modules. The web view is working OK for me. I just need to add some navigational buttons at the top. But it does look nice. I will probably have questions on objc In the near future. Once I figure out UI I will dive into objective c.

resserone13

@cvp. Thanks for all your input and advice. I am reviewing everything and re-writing the script. Hopefully everything will be functions. I am going to try classes as well but I might have mainly functions. I’m going to work on it and I will keep you updated. Thanks for all your help it’s really allowing me to get a lot done and learn a lot. Hopefully when I keep showing you these updates you’ll see that I’m learning from what you’re showing me.

cvp

@resserone13 said:

I just need to add some navigational buttons at the top.

import ui

class MyWebView(ui.View):
    def __init__(self, url):
        self.width, self.height = ui.get_window_size()
        self.wv = ui.WebView(frame=self.bounds)
        self.wv.load_url(url)
        self.add_subview(self.wv)
        bi_back = ui.ButtonItem(image=ui.Image.named('iob:ios7_arrow_back_32'))
        bi_forward = ui.ButtonItem(image=ui.Image.named('iob:ios7_arrow_forward_32'))
        bi_back.action = self.go_back
        bi_forward.action = self.go_forward
        self.right_button_items = [bi_forward, bi_back]
        self.present('fullscreen')

    def go_back(self, sender):
        self.wv.go_back()

    def go_forward(self, sender):
        self.wv.go_forward()

wv = MyWebView('https://omz-software.com/pythonista/docs/')
resserone13

I have a for loop that loops over btn names and creates a button for each name. How do I make it loop over a list of btns and a list of actions so that the each button gets a name and an action?
I’m using enumerate to help space the btns out. I tired switching to zip but then I loose the i variable for the enumerate function?


    def add_btn(self, btn_names, actions_list):
        for i, name, a in enumerate((btn_names)):
            btn= ui.Button()
            btn.background_color=btn_clr
            btn.title=name
            btn.font=(btn_fnt, btn_txt_sz)
            btn.tint_color=txt_clr
            btn.border_width=2
            btn.border_color=txt_clr
            btn.frame=(width * 0.15, height * (0.12 + i * 0.1), width * 0.7, height * 0.05)
            btn.corner_radius= btn_corner_radius
            btn.action='?????'
            self.add_subview(btn)


cvp

@resserone13 something like (not tested):

def add_btn(self, btn_names, actions_list):
    for i in range(len(btn_names)):
        btn = ui.Button()
        btn.name = btn_names[i]
        btn.action = actions_list[i]
ccc

range(len())is almost always a sign to use enumerate()instead.

import ui


def show_sender(sender):
    print(sender.title)


class MyView(ui.View):
    def __init__(self):
        self.add_buttons("abc", (show_sender, show_sender, show_sender))

    def add_buttons(self, titles, actions):
        width = self.width
        height = self.height
        for i, (title, action) in enumerate(zip(titles, actions)):
            button = ui.Button(
                title=title,
                action=action,
                background_color="pink",
                border_color="dark green",
                border_width=2,
                corner_radius=5,
                font=("system", 12),
                tint_color="grey",
            )
            button.frame = (width * 0.1, height * (0.1 + i), width * 0.8, height * 0.8)
            self.add_subview(button)

MyView().present()
mikael

@resserone13, maybe clearer and less error-prone is to pair the titles and actions when calling the method:

class MyView(ui.View):
    def __init__(self):
        self.add_buttons(
            ("A", action_a),
            ("B", action_b),
        )

    def add_buttons(self, *args):
        width = self.width
        height = self.height
        for i, (title, action) in enumerate(args):
            button = ui.Button(
                title=title,
                action=action,
                ...
resserone13

Thanks @cvp @ccc and @mikael for the help. I was able to get past the hang up and move forward on the app development. I ended up using @ccc suggestion because i was using code very similar to what I already had. I’m finishing up a few more things then I will post on GitHub.


    def add_btn(self, btn_names, actions_list):
        for i, (n, a) in enumerate(zip(btn_names, actions_list)):
            btn= ui.Button()
            btn.background_color=btn_clr
            btn.title=n
            btn.font=(btn_fnt, btn_txt_sz)
            btn.tint_color=txt_clr
            btn.border_width=2
            btn.border_color=txt_clr
            btn.frame=(width * 0.15, height * (0.12 + i * 0.1), width * 0.7, height * 0.05)
            btn.corner_radius= btn_corner_radius
            btn.action=a
            self.add_subview(btn)

resserone13

@cvp @ccc @mikael Here is the UI version of the personal protective equipment app. It looks kind of plain. Any suggestions to spruce up how it looks?

https://github.com/resserone13/PPEasy-ui-only