Closed gwingnhbc closed 5 years ago
Do you have a small snippet of code to reproduce the effect in b?
Not exactly a small snippet but I've stripped out all the surperfluous application logic but left the overall gui layout structure and CSS as unchanged as I could, largely because I'm not sure what might be relevant to the behaviour. The line containing "space in this label" is where it all happens.
The behaviour of this snippet is not quite the same as my full code. Here you can see that the pre tags in html preserve the white space in our lable but the text is offset vertically and appears lower than the buttons in the label's hbox container. In the real code the space given to the hbox is less so that the label text lower than the button is truncated i.e. just half of the letters are shown. I'm not sure how to reproduce that full effect in any sensibly small example snippet but I'll continue playing. In this example here the number of rows and layout is known when the rows are generated, but in the real code I dispose of the parent container then redraw the rows from data so perhaps apace/layout can't be optimised in quite the same way?
#! /app/pyvenv/myit/bin/python
# config
from flexx import app, event, ui, flx, config
config.hostname = 'myhost'
config.port = 12345
class Store(flx.PyComponent):
pass
class TaskRunner(flx.PyComponent):
store = flx.ComponentProp()
CSS = """
{
overflow-y: hidden;
}
"""
def init(self):
# Instantiate our Store instance
self._mutate_store(Store())
# Instantiate the javascript UI
self.taskrunnerview = TaskRunnerView(flex=1)
class TaskRunnerView(ui.Widget):
CSS = """
.flx-HBox {
border: 1pxpx solid #9d9;
overflow-x: auto;
min-height: 100%;
}
.flx-VBox {
border: 1px solid #9d9;
overflow-y: hidden;
min-height: 100%;
}
.flx-StackLayout {
border: 1px solid #9d9;
}
.flx-Button {
background: #9d9;
}
"""
def init(self):
with flx.TabLayout(flex=3,style='overflow:scroll;') as self.tablayout:
dfixq = {}
cfixq = {}
with ui.VBox(title='Queues') as self.taskpane:
print(f'taskpane.id is {self.taskpane.id} ')
CSS = """
.flx-VBox {
overflow-y: hidden;
}
.flx-HBox {
overflow-y: hidden;
overflow-x: hidden;
}
"""
with ui.HBox(style='overflow-y:hidden;'):
pass
#ui.Widget(flex=1)
#ui.Label(text='Press to update task queue information -->', flex=0)
#self.refresh = ui.Button(text='Refresh Queues')
with ui.HBox():
ui.Label(text='Datafix Queue',style='font: bold 20px Arial')
ui.Label(text='Codefix Queue',style='font: bold 20px Arial')
with ui.HSplit(flex=1):
with ui.VBox(style='overflow-y:hidden;',flex=1) as self.dfixbx:
pass
with ui.VBox(style='overflow-y:hidden;',flex=1) as self.cfixbx:
pass
with ui.VBox(style='overflow:visible;',flex=1) as self.dfixbx:
pass
with self.cfixbx:
with ui.VBox(style='overflow:visible;',flex=0) as self.cfixq:
# It seems strange to create the cfixbx both hera and above in this example
# In the real code the cfixq vbox is unparented, disposed of, and recreated with each data refresh
# that refresh being part of a reaction to root.store object
CSS = """
.flx-VBox {
overflow: hidden;
}
.flx-HBox {
overflow-y: hidden;
}
.flx-Label {
white-space: pre-wrap;
border-style: solid;
padding: 0px;
margin: 0px;
}
"""
# In the real code this is in a data driven loop with lots of rows.
for i in range(5):
with ui.HBox(style='align-items:flex-start;') as self.cfrow:
ui.Button(text= f"This key is {i}")
ui.Label(html='<pre>A long space in this label!</pre>')
ui.Button(text='Delete')
# static fillers after all the dynamic rows are added.
with self.dfixbx:
self.dfixfiller = ui.Widget(flex=10)
with self.cfixbx:
self.cfixfiller = ui.Widget(flex=10)
if __name__ == '__main__':
app = flx.App(TaskRunner)
app.serve('TaskRunner')
flx.start()
self.dfixbx
is being assigned twice. Flexx does not appear to like that. Removing the second assignment fixes the alignment issue (for me). The whitespace issue already disappeared as you noticed.
Thanks, good spot., I thought I had removed all the dfixbx stuff except the main structure but that line crept in. However I removed the second instantiation and this had no effect on the visuals in my case. Also note that, although the 2nd instantiAtion here was an error in the example, I do need to instantiate the dfixbx and cfixbx multiple times. Here they are only instantiated in the init() but in real life they are dropped and reinstantiated each time there is a reaction to data change. I've put a border around the hbox to make clearer what is happening plus removing all the CSS to simplify it and the example now looks like this. As you can see something odd is there in the layout with buttons being aligned at the top of the box although the html is actually centered. Running the full code the queues are dropped and recreated from data when the reactions happen and they then get 'squeezed' up on layout with the layout apparently being restricted to a box sized as per the buttons with the text below that truncated. SO in real life it looks like this:
Just a wild punt, but I wonder if the pre tags are putting new line chars around the string so that it actually reads "\nA long space in this label\n". That would explain why the box visually appears as three lines in the example. And perhaps why in the real code it truncates if the layout forces it to condense (note I did specify overflow-y hidden to prevent scroll bars appearing on all the supposedly one line label fields. There would remain the question of why a three line field condenses to one line but maybe the dynamic object instantiation outide of the init method is to blame for that?
The double assignment is one of multiple issues with your example. I suggest cleaning things up a bit and go from there. e.g. removing style='align-items:flex-start;'
also seems to help with the alignment issue.
I tried:
#!/usr/bin/env python3
from flexx import app, event, ui, flx
class TaskRunner(flx.PyComponent):
CSS = """
{
overflow-y: hidden;
}
"""
def init(self):
# Instantiate the javascript UI
self.taskrunnerview = TaskRunnerView(flex=1)
class TaskRunnerView(ui.Widget):
CSS = """
.flx-HBox {
border: 1pxpx solid #9d9;
overflow-x: auto;
min-height: 100%;
}
.flx-VBox {
border: 1px solid #9d9;
overflow-y: hidden;
min-height: 100%;
}
.flx-StackLayout {
border: 1px solid #9d9;
}
.flx-Button {
background: #9d9;
}
"""
def init(self):
with flx.TabLayout(flex=3,style='overflow:scroll;') as self.tablayout:
dfixq = {}
cfixq = {}
with ui.VBox(title='Queues') as self.taskpane:
CSS = """
.flx-VBox {
overflow-y: hidden;
}
.flx-HBox {
overflow-y: hidden;
overflow-x: hidden;
}
"""
with ui.HBox(style='overflow-y:hidden;'):
pass
with ui.HBox():
ui.Label(text='Datafix Queue',style='font: bold 20px Arial')
ui.Label(text='Codefix Queue',style='font: bold 20px Arial')
with ui.HSplit(flex=1):
with ui.VBox(style='overflow-y:hidden;',flex=1) as self.dfixbx:
pass
with ui.VBox(style='overflow-y:hidden;',flex=1) as self.cfixbx:
pass
with self.cfixbx:
with ui.VBox(style='overflow:visible;',flex=0) as self.cfixq:
CSS = """
.flx-VBox {
overflow: hidden;
}
.flx-HBox {
overflow-y: hidden;
}
.flx-Label {
white-space: pre-wrap;
border-style: solid;
padding: 0px;
margin: 0px;
}
"""
# In the real code this is in a data driven loop with lots of rows.
for i in range(5):
with ui.HBox() as self.cfrow:
ui.Button(text= f"This key is {i}")
ui.Label(html='<pre>A long space in this label!</pre>')
ui.Button(text='Delete')
# static fillers after all the dynamic rows are added.
with self.dfixbx:
self.dfixfiller = ui.Widget(flex=10)
with self.cfixbx:
self.cfixfiller = ui.Widget(flex=10)
if __name__ == '__main__':
app = flx.App(TaskRunner)
app.serve('TaskRunner')
flx.start()
which looks like this:
No issues as far as I can tell..
Thanks, that's really helpful and good to see it actually formatted correctly . The 'style='align-items:flex-start;' is fortunately just a hangover from one of my many attempts to get the formatting working and can go. I'll do some code cleanup and see if I can get it to still work when I do refreshes of the layout outside the init() rather than the simple case here.
CSS can be quite an adventure ;) Less is more is my experience. GL!
Cleaned up code sample below with all the CSS removed and some test loops included. a)The good news is that multiple assignments/instantiations of the layout are not causing problems and the outer build# loop can be set to demonstrate this. b) The bad news is that layout problems with pre tagged html labels persists and are related to the number of rows displayed, which can be controlled with the inner I# loop. When all rows fit comfortably within the window display is good. As soon as space gets tight then the row horizontal size is reduced, buttons are resized nicely but the label text seems to have space progressively 'stolen' from the bottom which starts eating into the letters. At some point the row has reached a minimum size and no more compression or letter cut off occurs and visually it appears as per the example I posted earlier. You can see this by either setting 'i' to a large value or alternatively just rendering with a small 'i' and then simply resizing your browser window to squeeze the display which shows the row compression/ letter cut off happening in real time on the static display. This happens with both chrome and edge browsers.
#! /app/pyvenv/myit/bin/python
# config
from flexx import app, event, ui, flx, config
config.hostname = 'myserver'
config.port = 12345
class Store(flx.PyComponent):
pass
class TaskRunner(flx.PyComponent):
store = flx.ComponentProp()
def init(self):
# Instantiate our Store instance
self._mutate_store(Store())
# Instantiate the javascript UI
self.taskrunnerview = TaskRunnerView(flex=1)
class TaskRunnerView(ui.Widget):
def init(self):
with flx.TabLayout(flex=3) as self.tablayout:
cfixq = {}
print(f'tablayout instantiated as {self.tablayout.id}')
with ui.VBox(title='Queues') as self.taskpane:
print(f'taskpane.id is {self.taskpane.id} ')
with ui.HBox():
pass
with ui.HBox():
ui.Label(text='Codefix Queue',style='font: bold 20px Arial')
with ui.HSplit(flex=1):
with ui.VBox(flex=1) as self.cfixbx:
pass
for build in range(2):
# layout instantiated multiple times to simulate user refresh actions
if self.cfixq:
print('DEBUG:Getting rid of the old taskQpane.cfixq')
self.cfixfiller.set_parent(None)
self.cfixfiller.dispose()
self.cfixq.set_parent(None)
self.cfixq.dispose()
with self.cfixbx:
with ui.VBox(flex=0) as self.cfixq:
# In the real code this is in a data driven loop with lots of rows.
print(f'instantiating queue, cfixq.id is {self.cfixq.id} ')
for i in range(10):
with ui.HBox(style='1px solid #9d9;') as self.cfrow:
ui.Button(text= f"This key is {i}")
ui.Label(html='<pre>A long space in this label!</pre>')
with self.cfixbx:
self.cfixfiller = ui.Widget(flex=10)
if __name__ == '__main__':
app = flx.App(TaskRunner)
app.serve('TaskRunner')
flx.start()
If you also:
your example will also run without modification for somebody else.
Right click in firefox and select "Inspect Element" and then selecting the "Computed" tab reveals that the pre
element has a margin of 16, which causes "eat up" when the lines come to close together. Something like CSS = ".flx-Label pre { margin: 0; }"
fixes that (for me)
Your current issues seem unrelated to flexx. Without wanting to sound unhelpful, may I suggest you take this discussion to some CSS forum and consider your original question answered?
Thanks for this, the change above didn't alter the label behaviour for me but I got to learn useful stuff about inspecting gui elements which has been very useful. I don't know where that default 16px margin comes from but neither the above CSS, nor setting margin in the 'style parameter' takes effect once the pre tag is there in the html. Ditto for margin-top and bottoms and white-space variants . However one that does get through and take effect is min-height so I offer for anyone else trying to do the same:
ui.Label( style='min-height: 34px;' ,
html='<pre>A long space in this label! </pre>')
For me 34px seems the magic number where, below that, as rows get compresses the text starts to be lost i.e. we just stop the rows getting any thinner than the minimum necessary for the label text. That sort of sucks but it sucks less than formatting a Label string with underscores or dots to preserve internal structure rather than the usual white space which gets stripped out.
I'll close the issue unless anyone wants to contribute anything else/better
For applying the style I did:
class PreLabel(ui.Label):
CSS = ".flx-PreLabel pre { margin: 0; }"
pretext = flx.StringProp('', settable=True)
@flx.reaction
def _pretext_changed(self):
self.set_html('<pre>' + self.pretext + '</pre>')
and added a new property to avoid having to explicitly wrap in pre tags.. then
PreLabel(pretext='A long space in this label!')
works and has no margin.
Thank you, this is so very much better than the hacky fix of min-height I had resorted to. Thanks also for the example of how easy (and how much better) it is to subclass - I continue to learn.
The 'text' attribute of Labels by default condenses white space and it seems difficult to turn this off.
a) I have tried setting 'write-space:pre' both inline via style= when creating the label and via CSS yet this takes no effect (ditto for 'write-space:pre-wrap'). b) Using html instead of text for the label again condenses white space. I wasn't able to turn this off with CSS however it sort of worked when I directly inserted pre tags within the label's html. By sort of I mean that it did preserve the white space however the text then lost its centring within the labels y borders and was rendered with the top half (possibly a little more than half) of the field blank and the lower half just showing the top of the letters and the bottom of the letters cut off. I tried setting CSS padding and margins to 0px but that had no visible effect.
Looking at the source for _widget.py I see a reference to the innerHTML property being rendered as raw html but, knowing little about flexx and javascript, I don't know how I would actually set that property, unless it is set from the html property supplied as per the above attempt (b).