Reputation: 55
i'm fooling around with this Python Library:
There is an object named PopUpDialog which base class is of Frame type.
One of the fields of the Frame object is can_scroll=True.
I would like this field to be False when I instantiate the PopUpDialog, so there will be no scoll bar.
I tried to make a new class named so
class PopUpDialogNoScroll(PopUpDialog):
super(PopUpdDialogNoScroll, self).__init__(screen,...
and
class PopUpDialogNoScroll(Frame):
super(PopUpdDialogNoScroll, self).__init__(screen,...
However, all my attempts resulted in an error like "7 arguments are given 2 are expected"
Could someone point me out how to derive a class and set the fields of a parent and/or grandparent class?
Thanks and be well
EDIT Thanks to awarrier 99 i got this far, however the scrollbar is still visible. Here I have got some demo code.
from asciimatics.widgets import Frame, ListBox, Layout, Divider, Text, TextBox, Widget, Button, Label, DropdownList, PopUpDialog, CheckBox, RadioButtons, PopupMenu
from asciimatics.scene import Scene
from asciimatics.screen import Screen
from asciimatics.exceptions import ResizeScreenError, NextScene, StopApplication, InvalidFields
class PopUpDialogNoScroll(PopUpDialog):
def __init__(self, *args, **kwargs):
super(PopUpDialogNoScroll, self).__init__(*args, **kwargs)
self.can_scroll = False
self.has_border = False
class DemoView(Frame):
def __init__(self, screen):
super(DemoView, self).__init__(screen,
11,
48,
data=None,
on_load=None,
has_border=True,
hover_focus=True,
name=None,
title="Welcome",
x=None,
y=None,
has_shadow=False,
reduce_cpu=False,
is_modal=False,
can_scroll=False)
body = Layout([1], fill_frame=True)
self.add_layout(body)
button = Button("Click me", self.clicked, label=None)
body.add_widget(button, 0)
def clicked(self):
self._scene.add_effect(
PopUpDialogNoScroll(self.screen, "1..\n2..\n3..\n4..\n5.. ", ["OK"], on_close=self.done, has_shadow=False, theme='info'))
def done(self):
pass
last_scene = None
def demo(screen, scene):
scenes = [
Scene([DemoView(screen)], duration=-1, clear=True, name="demo"),
]
screen.play(scenes, stop_on_resize=False, unhandled_input=None, start_scene=scene, repeat=True, allow_int=True)
Screen.wrapper(demo, catch_interrupt=True, arguments=[last_scene])
Upvotes: 0
Views: 130
Reputation: 3855
First of all, any calls to a super should be made in the first line of the class's own __init__
function. So your class should have its own __init__
function. Then, to pass all the parameters you want to the original class, you can provide two arguments to incorporate all of the arguments that the inherited class would have: *args
and **kwargs
. Then, you can explicitly set the can_scroll field to False in the __init__
function, like so:
class PopUpDialogNoScroll(PopUpDialog):
def __init__(self, *args, **kwargs):
super(PopUpDialogNoScroll, self).__init__(*args, **kwargs)
self.can_scroll = False
By having *args
and **kwargs
, you can pass in any parameters that the original PopUpDialog
class would expect without having to explicitly rewrite them and you can provide them to the super
class
Edit:
When you migrate your code over to Python 3, you can change the super
call to the following which is new syntax in Python 3: super().__init(*args, **kwargs)
. Note that the only difference is that you don't have to explicitly specify the class name and the self
reference
Edit 2:
Looking at the source provided here, I was able to make a PopUpDialogNoScroll
class by copying whatever code they had used for their PopUpDialog
class, and then changing this line from their source:
super(PopUpDialog, self).__init__(screen, height, width, self._data, has_shadow=has_shadow, is_modal=True)
to:
super(PopUpDialogNoScroll, self).__init__(screen, height, width, self._data, has_shadow=has_shadow, is_modal=True, can_scroll=False)
Not the most elegant of solutions, but it works
from asciimatics.widgets import Frame, ListBox, Layout, Divider, Text, TextBox, Widget, Button, Label, DropdownList, PopUpDialog, CheckBox, RadioButtons, PopupMenu
from asciimatics.scene import Scene
from asciimatics.screen import Screen
from asciimatics.exceptions import ResizeScreenError, NextScene, StopApplication, InvalidFields
from wcwidth import wcswidth
from functools import partial
def _enforce_width(text, width, unicode_aware=True):
"""
Enforce a displayed piece of text to be a certain number of cells wide. This takes into
account double-width characters used in CJK languages.
:param text: The text to be truncated
:param width: The screen cell width to enforce
:return: The resulting truncated text
"""
# Double-width strings cannot be more than twice the string length, so no need to try
# expensive truncation if this upper bound isn't an issue.
if (2 * len(text) < width) or (len(text) < width and not unicode_aware):
return text
# Can still optimize performance if we are not handling unicode characters.
if unicode_aware:
size = 0
for i, c in enumerate(str(text)):
w = wcwidth(c) if ord(c) >= 256 else 1
if size + w > width:
return text[0:i]
size += w
elif len(text) + 1 > width:
return text[0:width]
return text
def _split_text(text, width, height, unicode_aware=True):
"""
Split text to required dimensions.
This will first try to split the text into multiple lines, then put a "..." on the last
3 characters of the last line if this still doesn't fit.
:param text: The text to split.
:param width: The maximum width for any line.
:param height: The maximum height for the resulting text.
:return: A list of strings of the broken up text.
"""
# At a high level, just try to split on whitespace for the best results.
tokens = text.split(" ")
result = []
current_line = ""
string_len = wcswidth if unicode_aware else len
for token in tokens:
for i, line_token in enumerate(token.split("\n")):
if string_len(current_line + line_token) > width or i > 0:
# Don't bother inserting completely blank lines - which should only happen on the very first
# line (as the rest will inject whitespace/newlines)
if len(current_line) > 0:
result.append(current_line.rstrip())
current_line = line_token + " "
else:
current_line += line_token + " "
# At this point we've either split nicely or have a hugely long unbroken string (e.g. because the
# language doesn't use whitespace. Either way, break this last line up as best we can.
current_line = current_line.rstrip()
while string_len(current_line) > 0:
new_line = _enforce_width(current_line, width, unicode_aware)
result.append(new_line)
current_line = current_line[len(new_line):]
# Check for a height overrun and truncate.
if len(result) > height:
result = result[:height]
result[height - 1] = result[height - 1][:width - 3] + "..."
# Very small columns could be shorter than individual words - truncate
# each line if necessary.
for i, line in enumerate(result):
if len(line) > width:
result[i] = line[:width - 3] + "..."
return result
class PopUpDialogNoScroll(Frame):
"""
A fixed implementation Frame that provides a standard message box dialog.
"""
def __init__(self, screen, text, buttons, on_close=None, has_shadow=False, theme="warning"):
"""
:param screen: The Screen that owns this dialog.
:param text: The message text to display.
:param buttons: A list of button names to display. This may be an empty list.
:param on_close: Optional function to invoke on exit.
:param has_shadow: optional flag to specify if dialog should have a shadow when drawn.
:param theme: optional colour theme for this pop-up. Defaults to the warning colours.
The `on_close` method (if specified) will be called with one integer parameter that
corresponds to the index of the button passed in the array of available `buttons`.
Note that `on_close` must be a static method to work across screen resizing. Either it
is static (and so the dialog will be cloned) or it is not (and the dialog will disappear
when the screen is resized).
"""
# Remember parameters for cloning.
self._text = text
self._buttons = buttons
self._on_close = on_close
# Decide on optimum width of the dialog. Limit to 2/3 the screen width.
string_len = wcswidth if screen.unicode_aware else len
width = max([string_len(x) for x in text.split("\n")])
width = max(width + 2,
sum([string_len(x) + 4 for x in buttons]) + len(buttons) + 5)
width = min(width, screen.width * 2 // 3)
# Figure out the necessary message and allow for buttons and borders
# when deciding on height.
dh = 4 if len(buttons) > 0 else 2
self._message = _split_text(text, width - 2, screen.height - dh, screen.unicode_aware)
height = len(self._message) + dh
# Construct the Frame
self._data = {"message": self._message}
super(PopUpDialogNoScroll, self).__init__(
screen, height, width, self._data, has_shadow=has_shadow, is_modal=True, can_scroll=False)
# Build up the message box
layout = Layout([width - 2], fill_frame=True)
self.add_layout(layout)
text_box = TextBox(len(self._message), name="message")
text_box.disabled = True
layout.add_widget(text_box)
layout2 = Layout([1 for _ in buttons])
self.add_layout(layout2)
for i, button in enumerate(buttons):
func = partial(self._destroy, i)
layout2.add_widget(Button(button, func), i)
self.fix()
# Ensure that we have the right palette in place
self.set_theme(theme)
def _destroy(self, selected):
self._scene.remove_effect(self)
if self._on_close:
self._on_close(selected)
def clone(self, screen, scene):
"""
Create a clone of this Dialog into a new Screen.
:param screen: The new Screen object to clone into.
:param scene: The new Scene object to clone into.
"""
# Only clone the object if the function is safe to do so.
if self._on_close is None or isfunction(self._on_close):
scene.add_effect(PopUpDialog(screen, self._text, self._buttons, self._on_close))
class DemoView(Frame):
def __init__(self, screen):
super(DemoView, self).__init__(screen,
11,
48,
data=None,
on_load=None,
has_border=True,
hover_focus=True,
name=None,
title="Welcome",
x=None,
y=None,
has_shadow=False,
reduce_cpu=False,
is_modal=False,
can_scroll=False)
body = Layout([1], fill_frame=True)
self.add_layout(body)
button = Button("Click me", self.clicked, label=None)
body.add_widget(button, 0)
def clicked(self):
self._scene.add_effect(
PopUpDialogNoScroll(self.screen, "1..\n2..\n3..\n4..\n5.. ", ["OK"], on_close=self.done, has_shadow=False, theme='info'))
def done(self):
pass
last_scene = None
def demo(screen, scene):
scenes = [
Scene([DemoView(screen)], duration=-1, clear=True, name="demo"),
]
screen.play(scenes, stop_on_resize=False, unhandled_input=None, start_scene=scene, repeat=True, allow_int=True)
Screen.wrapper(demo, catch_interrupt=True, arguments=[last_scene])
Upvotes: 1