Closed zeluspudding closed 7 years ago
Is it possible to update the text in a label or title? I have a "single page" (form) terminal application I'm making that basically "allows me to go left or right" (via buttons) through different "slides". Really it's just redrawing the same form with different data. It would be nice to know which "slide" I'm on by dynamically updating the title or a label. How would I do that?
Also, I don't know how I feel being the 666th person staring your repo :/ :) just kidding
Here's some example code:
from asciimatics.widgets import Frame, TextBox, Layout, Label, Divider, Text, \
RadioButtons, Button, PopUpDialog
from asciimatics.scene import Scene
from asciimatics.screen import Screen
from asciimatics.exceptions import ResizeScreenError, NextScene, StopApplication, \
InvalidFields
import sys
import pandas
import os
import time
class Model(object):
def __init__(self, db_path = None):
def load_database(self):
if self._db_path == None:
print('need path to db')
else:
return pandas.read_pickle(self._db_path)
self._db_path = db_path
self._db = load_database(self) # object created by the class
self.length = len(self._db)
def __str__(self):
return str(self._db.tail(1))
def update_contract(self, row):
self._db.update(row)
def get_contract(self, index):
def stringify_values(dict_):
return {k: str(v) if not isinstance(v, str)
else v for k, v in dict_.items()}
# Create wrap around index
if index < 0:
self.index = self.length + index
elif index >= self.length:
self.index = index - self.length
else:
self.index = index
return stringify_values(dict(self._db.loc[self.index]))
def get_index_range(self):
return len(self._db)
class DemoFrame(Frame):
def __init__(self, screen, model):
super(DemoFrame, self).__init__(screen,
int(screen.height),
int(screen.width * 2 // 3),
data=model.get_contract(-1),
title="My Form",
reduce_cpu=True)
# Save off the model that accesses the contacts database.
self._model = model
layout = Layout([10, 30, 1])
self.add_layout(layout)
layout.add_widget(Label("File %s / 9441" %(model.index+1)), 0)
layout.add_widget(
Text(label="principal:",
name="principal",
on_change=self._on_change), 1)
layout.add_widget(
Text(label="lender:",
name="lender",
on_change=self._on_change), 1)
layout.add_widget(
Text(label="lender_address1:",
name="lender_address1",
on_change=self._on_change), 1)
layout.add_widget(RadioButtons([("Yes: ", 1),
("No: ", 2)],
label="Is this a valid contract?",
name="Valid Mortgage",
on_change=self._on_change), 1)
layout.add_widget(Divider(height=3), 1)
layout2 = Layout([1, 1, 1])
self.add_layout(layout2)
layout2.add_widget(Button("Next", self._next), 0)
layout2.add_widget(Button("Previous", self._previous), 1)
layout2.add_widget(Button("Quit", self._quit), 2)
self.fix()
def _on_change(self):
self.save()
def _next(self):
# Save table
# Build new scene?
self.data = self._model.get_contract(self._model.index + 1)
def _previous(self):
# Save table
self.data = self._model.get_contract(self._model.index - 1)
def _quit(self):
self._scene.add_effect(
PopUpDialog(self._screen,
"Quit?",
["No", "Yes"],
on_close=self._quit_on_yes))
@staticmethod
def _quit_on_yes(selected):
# Yes is the second button
if selected == 1:
raise StopApplication("User requested exit")
def demo(screen, scene):
screen.play([Scene([DemoFrame(screen, db)], -1)], stop_on_resize=True, start_scene=scene)
base_path = r'path\to\pandas_data'
mortgage_db = os.path.join(base_path, r'code\post_process_pdfs\data_entry_db.pkl')
db = Model(mortgage_db)
last_scene = None
while True:
try:
Screen.wrapper(demo, catch_interrupt=False, arguments=[last_scene]) # Catch interrupt turns off quiting via CTRL+C
sys.exit(0)
except ResizeScreenError as e:
last_scene = e.scene
Thanks for the vote... Hopefully it'll move off 666 soon! ;-)
In answer to your question, there is no API to change the title or a label at the moment. While you can update the internal properties and force an update, it is not "the done thing". I'd be happy for them to be exposed in future releases, though.
In the meantime, I suggest you use a disabled Text or TextBox widget instead of the Label. See the _header
field in https://github.com/peterbrittain/asciimatics/blob/master/samples/top.py for an example of how to do it.
Hmmm. I've tried your suggestion and while a TextBox appears to get placed in my form (and is even usable if I enable it) I can't seem to get it to render pre-assigned text via .value
. What am I missing?
The _header string I'm trying to render is "*****This doesn't show up****"
below.
from asciimatics.widgets import Frame, TextBox, Layout, Label, Divider, Text, \
RadioButtons, Button, PopUpDialog
from asciimatics.scene import Scene
from asciimatics.screen import Screen
from asciimatics.exceptions import ResizeScreenError, NextScene, StopApplication, \
InvalidFields
import sys
import pandas
import os
import time
class Model(object):
def __init__(self, db_path = None):
def load_database(self):
if self._db_path == None:
print('need path to db')
else:
return pandas.read_pickle(self._db_path)
self._db_path = db_path
self._db = load_database(self) # object created by the class
self.length = len(self._db)
def __str__(self):
return str(self._db.tail(1))
def update_contract(self, row):
self._db.update(row)
def get_contract(self, index):
def stringify_values(dict_):
return {k: str(v) if not isinstance(v, str)
else v for k, v in dict_.items()}
# Create wrap around index
if index < 0:
self.index = self.length + index
elif index >= self.length:
self.index = index - self.length
else:
self.index = index
return stringify_values(dict(self._db.loc[self.index]))
def get_index_range(self):
return len(self._db)
class DemoFrame(Frame):
def __init__(self, screen, model):
super(DemoFrame, self).__init__(screen,
int(screen.height),
int(screen.width * 2 // 3),
data=model.get_contract(0),
reduce_cpu=True,
name="My Form")
# Save off the model that accesses the contacts database.
self._model = model
layout = Layout([1, 30, 1])
self.add_layout(layout)
self._header = TextBox(1, as_string=True)
self._header.disabled = True
self._header.custom_colour = "label"
# self._header.value = "File %s / 9441" % self._model.index
self._header.value = "*****This doesn't show up****"
layout.add_widget(self._header, 1)
layout.add_widget(
Text(label="Principal:",
name="principal",
on_change=self._on_change), 1)
layout.add_widget(
Text(label="Lender:",
name="lender",
on_change=self._on_change), 1)
layout.add_widget(
Text(label="Lender Address:",
name="lender_address1",
on_change=self._on_change), 1)
layout.add_widget(
Text(label="Maturity Date:",
name="maturity_date",
on_change=self._on_change), 1)
layout.add_widget(
Text(label="Interest Rate [%]:",
name="interest_rate",
on_change=self._on_change), 1)
layout.add_widget(RadioButtons([("No: ", 1),
("Yes: ", 2)],
label="Remove this record?",
name="Valid Mortgage",
on_change=self._on_change), 1)
layout.add_widget(Divider(height=3), 1)
layout2 = Layout([1, 1, 1])
self.add_layout(layout2)
layout2.add_widget(Button("Next", self._next), 0)
layout2.add_widget(Button("Previous", self._previous), 1)
layout2.add_widget(Button("Quit", self._quit), 2)
self.fix()
def _on_change(self):
# changed = False
self.save()
# for key, value in self.data.items():
# if key not in form_data or form_data[key] != value:
# changed = True
# break
def _next(self):
# Save table
self._header.value = "Form %s / 9441" %(self._model.index + 1)
self.data = self._model.get_contract(self._model.index + 1)
def _previous(self):
# Save table
self.data = self._model.get_contract(self._model.index - 1)
# try:
# self.save(validate=True)
# message = "Values entered are:\n\n"
# for key, value in self.data.items():
# message += "- {}: {}\n".format(key, value)
# except InvalidFields as exc:
# message = "The following fields are invalid:\n\n"
# for field in exc.fields:
# message += "- {}\n".format(field)
# self._scene.add_effect(
# PopUpDialog(self._screen, message, ["OK"]))
def _quit(self):
self._scene.add_effect(
PopUpDialog(self._screen,
"Quit?",
["No", "Yes"],
on_close=self._quit_on_yes))
@staticmethod
def _quit_on_yes(selected):
# Yes is the second button
if selected == 1:
raise StopApplication("User requested exit")
def demo(screen, scene):
screen.play([Scene([DemoFrame(screen, db)], -1)], stop_on_resize=True, start_scene=scene)
base_path = r'path_to_db'
mortgage_db = os.path.join(base_path, r'code\post_process_pdfs\data_entry_db.pkl')
db = Model(mortgage_db)
last_scene = None
while True:
try:
Screen.wrapper(demo, catch_interrupt=False, arguments=[last_scene]) # Catch interrupt turns off quiting via CTRL+C
sys.exit(0)
except ResizeScreenError as e:
last_scene = e.scene
What version are you using?
I suspect that you're running the v1.8.0 or earlier. There is a patch to the latest version of the code that allows you to override the value of a Widget during construction. When I run your code with the latest version on master, you see the "*This doesn't show up" Label.
Ahh yes. I am running v1.8.0. I'll update. Thank you so much again!
... I'd just like to compliment that I think asciimatics, in particular your contacts.py example, is an excellent introduction to Separated Presentation. Thank you so much for taking the time to whip up useful examples and documentation!
OK - I've now exposed these in the latest build.
Shoot. I accidentally posted too soon. then tried to delete the issue. Just learned "ISSUE DELETE" doesn't exist. Bare with me as I flesh out the details