display result in UI - python

I am building a user interface with pyside and Qt. I have done nice calculations that I want to display the results of in the UI.
The results are 1 float each and they are stored in:
self.dist_total_disp
self.time_total_disp
I have tried displaying them with a label like:
self.layout = QtGui.QVBoxLayout()
self.plot_window =QtGui.QVBoxLayout()
self.dist_time_label = QtGui.QLabel()
self.dist_time_label.setText("total distance = self.dist_total_disp \ntotal survey time = self.time_total_disp ")
self.plot_window.addWidget(self.dist_time_label)
----COMPILE UI----
self.setLayout(self.layout)
self.layout.addLayout(self.plot_window)
But the problem here is that the setText requires a string and self.dist_total_disp and self.time_total_disp cannot be called from within the string.
Also I would like to display the result on the lower right side of the VBox but I don't want to change QVBoxLayout() to QHBoxLayout().
I feel there should be a QtGui tool that is more suitable for this but I could't find one in the documentation.
Edit:
Note that the calculations are done with input from the UI

This should work:
self.dist_time_label.setText("total distance = {0} \ntotal survey time = {1} ".format(self.dist_total_disp, self.time_total_disp))
For adding the label at the lower side of the VBox you should add a spacer above it.

You need to format the string with the values you want to display
self.dist_time_label.setText("total distance = %f\ntotal survey time = %f" % (self.dist_total_disp, self.time_total_disp))
For displaying the label on the lower right side you would use the alignment paramter of addWidget(widget, stretch=0, alignment=0)
self.plot_window.addWidget(self.dist_time_label, alignment=QtCore.Qt.AlignRight|QtCore.Qt.AlignBottom)

Related

QTreeWidget clear empty space at the bottom of widget

Is this example i have a QTreeWidget with 4 columns. The last column is filled by QFrames.
File ui.py
from PyQt5 import QtCore, QtGui, QtWidgets
import sys
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
app.setStyle("Windows")
treeWidget = QtWidgets.QTreeWidget()
treeWidget.headerItem().setText(0, "Α/Α")
treeWidget.headerItem().setText(1,"Τύπος")
treeWidget.headerItem().setText(2,"Τίτλος")
treeWidget.headerItem().setText(3,"Προεπισκόπιση")
treeWidget.setStyleSheet("QTreeWidget::item{height:60px;}")
l = []
for i in range(0,30):
l.append(QtWidgets.QTreeWidgetItem(["1","1","1","1"]))
treeWidget.addTopLevelItems(l) # add everything to the tree
treeWidget.show()
right_height = treeWidget.header().height()
for el in l:
right_height += treeWidget.visualItemRect(el).height()
print(right_height)
sys.exit(app.exec_())
Output (after scrolling to the bottom of QTreeWidget):
The desired total height of ScrollArea (inside QTreeWidget) is 1823 and it's calculated as the sum of header height and height of each line.
As you can see there is empty space after last row in QTreeWidget. This problem doesn't appear after resizing QDialog manually.
Edit: This may be usefull.
After checking the code for QTreeWidget and inherited/related classes (QTreeView, QAbstractItemView, QAbstractScrollArea and QWidget, but also QAbstractSlider, used for the scroll bars), it seems clear that QTreeView does not respect the behavior shown in QTableView, which automatically scrolls the view to the bottom (without any further margin) whenever the scroll bar reaches the maximum.[1]
Note that this only happens when the (default) verticalScrollMode property is set to ScrollPerItem. For obvious reasons, whenever it is set to ScrollPerPixel, the scroll bar/area will only extend to the visible area of the viewport.
Unfortunately, the laying out of items (and related function results) of QTreeView is based on this aspect, meaning that we cannot try to just paint the tree (by overriding drawTree() and translating the painter), because in that case painting would be only partially consistent, but the behavior will not. For instance, when hovering or using drag&drop.
The above is most probably caused by optimization reasons: there is no way of knowing the whole extent of a tree, and, unless the uniformRowHeights property is True and all items actually have the same heights (which is clearly not your case), the view should always compute again the geometries of each items; while that could be feasible for a table (2d) model, that becomes quite unreasonable for an undefinite tree (3d) model, as it could theoretically block the view updates. At least, based on the default implementation of QTreeView.
There is a possibility, though: completely override the behavior of the scroll bar, and as long as you know that your model has a known and relatively limited extent.
By default, when ScrollPerItem is active, the scroll bar will always have a range that is equal to total_item_count - visible_item_count: if the viewport has x items and it can currently show y items (with y > x) in its viewport, the scroll bar maximum will be y - x (eg: with 10 visible items, if the viewport can only fully show 9, the maximum will be 1).
When the ScrollPerPixel mode is set instead, the extent will always be the maximum pixel height minus the viewport pixel size. Which means that we can know if the top left item is fully shown or not.
Now, the following requires a bit of trickery and ingenuity.
We need to consider the following aspects:
QScrollBar (based on QAbstractSlider) provides an actionTriggered signal that tells us whenever the user tries to manually change the value using the arrow buttons or by clicking on the "sub/add" page areas (the space within the "groove" that is not covered by the slider handle);
QAbstractItemView internally installs an event filter on the scroll bars, and connects to its valueChanged signals;
bonus: any well designed QObject will update its property (and emit its related changed signal) only when the new value is different from the current one, so we can normally be sure that trying to set the scroll bar value to the same one won't trigger anything;
Considering the above, we could implement a few functions in a subclass and connect them (directly or not) to user generated signals and events. The only catch is that we must use the ScrollPerPixel scroll mode for the vertical scroll bar, which will result in a slightly inconsistent display of the scroll bar handle size.
Well, we can live with that.
Here is a possible implementation that considers the above aspects:
class TreeScrollFix(QTreeWidget):
_ignoreScrollBarChange = False
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.verticalScrollBar().actionTriggered.connect(self.vbarTriggered)
self.verticalScrollBar().valueChanged.connect(self.fixVBarValue)
self.setVerticalScrollMode(self.ScrollPerPixel)
def vbarTriggered(self, action):
if action in (
QAbstractSlider.SliderNoAction,
QAbstractSlider.SliderToMinimum,
QAbstractSlider.SliderToMaximum,
QAbstractSlider.SliderMove,
):
# we can safely ignore the above, eventually relying on the
# fixVBarValue function
return
if action in (
QAbstractSlider.SliderSingleStepAdd,
QAbstractSlider.SliderSingleStepSub
):
delta = 1
else:
delta = QApplication.wheelScrollLines()
if not delta:
# this should not happen...
return
if action in (
QAbstractSlider.SliderSingleStepAdd,
QAbstractSlider.SliderPageStepAdd
):
func = self.indexBelow
else:
func = self.indexAbove
if self.verticalScrollBar().value() == self.verticalScrollBar().maximum():
delta -= 1
index = self.indexAt(QPoint(0, 1)) # note the extra pixel
while delta:
newIndex = func(index)
if not newIndex.isValid():
break
index = newIndex
delta -= 1
self.scrollTo(index, self.PositionAtTop)
def fixVBarValue(self, value):
vbar = self.verticalScrollBar()
if not value or vbar.maximum() == value:
return
topLeftIndex = self.indexAt(QPoint(0, 0))
topLeftRect = self.visualRect(topLeftIndex)
# adjust the theoretical value to the actual y of the item (which is
# a negative one)
value += topLeftRect.y()
showTop = topLeftRect.center().y() > 0
if not showTop:
# the item currently shown on the top left is not fully shown, and
# the visible height is less than half of its height;
# let's show the next one instead by adding that item's height
value += topLeftRect.height()
if value != vbar.value():
vbar.setValue(value)
def eventFilter(self, obj, event):
if event.type() == event.Wheel and obj == self.verticalScrollBar():
delta = event.angleDelta().y()
if delta: # delta != 0 -> no vertical scrolling
# "synthesize" the event by explicitly calling the custom
# vbarTriggered function just as it would be normally called;
# note that this is a real workaround that will never work with
# normal implicit or explicit event handling, which means that
# QApplication.postEvent and QApplication.sendEvent might be
# potentially ignored by this if another event filter exists.
self.vbarTriggered(
QAbstractSlider.SliderPageStepSub if delta > 1
else QAbstractSlider.SliderPageStepAdd
)
# the event has been handled, do not let the scroll bar handle it.
return True
return super().eventFilter(obj, event)
def scrollTo(self, index, hint=QAbstractItemView.EnsureVisible):
if hint in (self.PositionAtTop, self.PositionAtTop):
if hint == self.PositionAtBottom:
self._ignoreScrollBarChange = True
super().scrollTo(index, hint)
self._ignoreScrollBarChange = False
return
itemRect = self.visualRect(index)
viewRect = self.viewport().rect()
if hint == self.EnsureVisible and itemRect.y() < viewRect.y():
super().scrollTo(index, self.PositionAtTop)
return
vbar = self.verticalScrollBar()
if not self.indexBelow(index).isValid():
# last item
vbar.setValue(vbar.maximum())
return
self._ignoreScrollBarChange = True
if hint == self.PositionAtCenter:
super().scrollTo(index, self.PositionAtCenter)
elif itemRect.bottom() > viewRect.bottom():
super().scrollTo(index, self.PositionAtBottom)
topLeftIndex = self.indexAt(QPoint(0, 0))
topLeftRect = self.visualRect(topLeftIndex)
if topLeftRect.y() < 0:
delta = topLeftRect.height() + topLeftRect.y()
vbar.setValue(vbar.value() + delta)
self._ignoreScrollBarChange = False
And an example code to test it:
from random import randrange
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
class TreeScrollFix(QTreeWidget):
# as above...
app = QApplication([])
treeWidget = TreeScrollFix()
treeWidget.setColumnCount(2)
for i in range(1, 31):
topLevel = QTreeWidgetItem(treeWidget, ["top item {}".format(i)])
for j in range(randrange(5)):
child = QTreeWidgetItem(topLevel,
['', topLevel.text(0)])
# a random vertical size hint
hint = QSize(100, randrange(30, 80))
child.setSizeHint(1, hint)
child.setText(0, 'height: {}'.format(hint.height()))
treeWidget.header().setSectionResizeMode(QHeaderView.ResizeToContents)
# expand top level indexes randomly
for i in range(randrange(5, treeWidget.topLevelItemCount())):
topIndex = randrange(treeWidget.topLevelItemCount())
treeWidget.setExpanded(treeWidget.model().index(topIndex, 0), True)
treeWidget.setStyleSheet('''
QTreeView::item {
border: 1px solid palette(highlight);
}
QTreeView::item:selected {
border-color: red;
background: palette(highlight);
color: palette(highlighted-text);
}
''')
treeWidget.resize(app.primaryScreen().size() * 2 / 3)
treeWidget.show()
app.exec_()
Note that I added an override for scrollTo(), which is always called when using keyboard navigation. Normally, the item view takes care of the top alignment when ScrollPerItem is active, but in our case the pixel scrolling could create some issues for items that do not have uniform row heights, and when scrolling to the bottom. The override takes care of that depending on the hint argument of that function, so that whenever scrolling won't show the top item in full, it automatically scrolls down to show the next item on top, otherwise it will just scroll to the bottom for the last available, not expaned item. To avoid unnecessary calls, I also used a _ignoreScrollBarChange flag that will make ignore any further and unnecessary computing in fixVBarValue(). This will also work for the internally delayed call to scrollTo() that happens when selecting any item.
Be aware that I've done some testing and it should work as expected. Unfortunately, QAbstractItemView and QTreeView use delayed item layout management, and I cannot completely be sure about these aspects. At least in one case in dozens, I got a UI freeze, but I was not able to reproduce the issue (which might have been caused by external causes). I strongly advice you to take your time to check the code above, the documentation and the Qt sources, and consider using some carefully thought test suite.
Also, for obvious reasons, if you want to use a custom QScrollBar, you'd need to properly disconnect the previous functions and connect them again to the new one.
[1] I am not sure, but it is probably related to a comment in the QTreeView code (near line 3500), which says: optimize (maybe do like QHeaderView by letting items have startposition); see the official sources or the KDAB code browser

How to limit the rightest Gtk.TreeViewColumn's width inside a Gtk.ScrolledWindow?

I cannot prevent the rightest column of a Gtk.TreeView to expand.
As the real Gtk.TreeView may display a greater number of rows, making it usually somewhat greater than the screen's height, it is embedded in a Gtk.ScrolledWindow. This is required. Without it, attaching an empty grid at the right of the treeview, expanding itself horizontally, would fix the problem. Based on this idea, I've tried a workaround that introduces another difficulty (see below).
I have built a minimal working example from the example from https://python-gtk-3-tutorial.readthedocs.io/en/latest/treeview.html#filtering, without filtering nor buttons; and the columns are 80 px wide at least (this works) and their content is horizontally centered. This last detail makes the horizontal expansion of the rightest column visible. In the original example, it does expand too, but as everything is left aligned, this is not really visible. I'd liked to keep the columns' content centered, without seeing the rightest expanded.
This example is minimal, but contains some helping features: you'll find clickable column titles, that will display some information about the clicked column in the console; a remove button (works fine, remove the selected rows) and a paste button that allows to paste new rows from a selection (e.g. from selected lines from a spreadsheet, but there's nothing to check the data are correct, if you paste something that does not convert to int, it will simply crash).
Workaround
A workaround I've tried consist of gathering both the treeview and a horizontally expanding empty right grid at its right inside a grid that would be put inside the Gtk.ScrolledWindow. It works, but causes other subtle problems: in some situations, the treeview does not get refreshed (it happens after a while), yet nothing prevents the main loop to refresh the view (there's no other processing in the background, for instance). To experiment this workaround: comment and uncomment the lines as described in the code below; run the program via python script.py (if you need to install pygobject in a venv, see here), notice the rightest column does not expand to the right any longer, select the 3 first rows and press "remove", then from a spread sheet, select 3 lines of dummy integers as shown below and then press "paste". Scroll down to the last rows: you'll see most of the time that the 3 pasted lines do not show up, even if it is possible to scroll over the last row. Maybe one of them will show up after some time, then another... (or simply select a row, and they'll show up). Strangely, it happens if one has just removed as many lines as one wants to paste after the removal (3 removed, 3 pasted; or 4 removed, 4 pasted etc.).
Example spreadsheet selection:
Question
So, I'd prefer to avoid the workaround (I'm afraid I may find other situations triggering a bad refreshing of the treeview), that I could not fix itself (for instance, setting self.scrollable_treelist.set_propagate_natural_height(True) proved useless, maybe I'm not using it correctly though?) and only attach the treeview itself directly in the Gtk.ScrolledWindow. How to prevent the rightest column to expand, then?
(I've tried to use a fair amount of setters and properties of the cell renderers, the treeview, the treeview columns, the scrolled window, to no avail. Some of them are still in the code below.)
Any solution using and fixing the workaround above would be accepted though.
In any case, the treeview may be scrolled, and lines may be added and removed from it without any refreshing problem.
Source Code
import gi
try:
gi.require_version('Gtk', '3.0')
except ValueError:
raise
else:
from gi.repository import Gtk, Gdk
# ints to feed the store
data_list = [(i, 2 * i, 3 * i, 4 * i, 5 * i) for i in range(40)]
class AppWindow(Gtk.Window):
def __init__(self):
super().__init__(title="Treeview Columns Size Demo")
self.set_border_width(10)
# Setting up the self.grid in which the elements are to be positioned
self.grid = Gtk.Grid()
self.grid.set_column_homogeneous(True)
self.grid.set_row_homogeneous(True)
self.add(self.grid)
# Creating the ListStore model
self.store = Gtk.ListStore(int, int, int, int, int)
for data_ref in data_list:
self.store.append(list(data_ref))
# creating the treeview and adding the columns
self.treeview = Gtk.TreeView(model=self.store)
rend = Gtk.CellRendererText()
rend.set_alignment(0.5, 0.5)
for i, column_title in enumerate([f'n×{p}' for p in [1, 2, 3, 4, 5]]):
column = Gtk.TreeViewColumn(column_title, rend, text=i)
column.set_min_width(80)
# column.set_max_width(80)
# column.set_fixed_width(80)
# column.set_sizing(Gtk.TreeViewColumnSizing(1))
column.set_alignment(0.5)
column.set_clickable(True)
column.connect('clicked', self.on_column_clicked)
self.treeview.append_column(column)
self.treeview.set_hexpand(False)
self.treeview.get_selection().set_mode(Gtk.SelectionMode.MULTIPLE)
# Put the treeview in a scrolled window
self.scrollable_treelist = Gtk.ScrolledWindow()
self.scrollable_treelist.set_vexpand(True)
self.grid.attach(self.scrollable_treelist, 0, 0, 8, 10)
self.scrollable_treelist.add(self.treeview)
# WORKAROUND
# Alternatively, embed the treeview inside a grid containing an
# empty grid to the right of the treeview
# To try it: comment out the previous line; uncomment next lines
# scrolled_grid = Gtk.Grid()
# empty_grid = Gtk.Grid()
# empty_grid.set_hexpand(True)
# scrolled_grid.attach(self.treeview, 0, 0, 8, 10)
# scrolled_grid.attach_next_to(empty_grid, self.treeview,
# Gtk.PositionType.RIGHT, 1, 1)
# self.scrollable_treelist.add(scrolled_grid)
# self.scrollable_treelist.set_propagate_natural_height(True)
# Buttons
self.remove_button = Gtk.Button(label='Remove')
self.remove_button.connect('clicked', self.on_remove_clicked)
self.paste_button = Gtk.Button(label='Paste')
self.paste_button.connect('clicked', self.on_paste_clicked)
self.grid.attach_next_to(self.remove_button, self.scrollable_treelist,
Gtk.PositionType.TOP, 1, 1)
self.grid.attach_next_to(self.paste_button, self.remove_button,
Gtk.PositionType.RIGHT, 1, 1)
self.set_default_size(800, 500)
self.show_all()
# Clipboard (to insert several rows)
self.clip = Gtk.Clipboard.get(Gdk.SELECTION_PRIMARY)
self.clip2 = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
def on_column_clicked(self, col):
print(f'col.get_sizing()={col.get_sizing()}')
print(f'col.get_expand()={col.get_expand()}')
print(f'col.get_width()={col.get_width()}')
print(f'col.get_min_width()={col.get_min_width()}')
print(f'col.get_max_width()={col.get_max_width()}')
print(f'col.get_fixed_width()={col.get_fixed_width()}')
def on_remove_clicked(self, widget):
model, paths = self.treeview.get_selection().get_selected_rows()
refs = []
for path in paths:
refs.append(Gtk.TreeRowReference.new(model, path))
for ref in refs:
path = ref.get_path()
treeiter = model.get_iter(path)
model.remove(treeiter)
# print(f'AFTER REMOVAL, REMAINING ROWS={[str(r[0]) for r in model]}')
def on_paste_clicked(self, widget):
text = self.clip.wait_for_text()
if text is None:
text = self.clip2.wait_for_text()
if text is not None:
lines = text.split('\n') # separate the lines
lines = [tuple(L.split('\t')) for L in lines] # convert to tuples
print(f'PASTE LINES={lines}')
for line in lines:
if len(line) == 5:
line = tuple(int(value) for value in line)
self.store.append(line)
win = AppWindow()
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()

Display ellipsis when Rich Layout scrolls past end of screen/console

Is it possible to configure rich.Layout() to indicate when the number of items in the layout has exceeded the display size of the current layout?
I would like to be able to tell programmatically when the code is attempting to display too many items for the current Table/Layout to display in order to display ellipsis or a message such as "...and 200 further items". This would allow the program to alert the user that some items are not being displayed.
There is a size attribute in Layout, but that value appears to be an input to constrain the layout to a fixed size rather than an indicator of the current Layout size.
In my current application, I would rather not constrain the size of the Layout in order to use the full available layout size, whatever that value may be.
#!/usr/bin/env python3
from rich.console import Console
from rich.layout import Layout
from rich.table import Table
from rich.pretty import Pretty
# Make too many lines for the Layout/Table to display on the current screen size
MAX_LINES = 1000
def fill_table():
table = Table()
for li in range(MAX_LINES):
table.add_row(Pretty(li))
return table
console = Console()
layout = Layout(name="root")
layout["root"].update(fill_table())
console.print(layout)
I'm a bit late to the party, but I've just had to solve a similar issue. The trick is to use Layout's .render() function like so:
#!/usr/bin/env python3
from rich.console import Console
from rich.layout import Layout
from rich.table import Table
from rich.pretty import Pretty
from rich import print as pprint
# Make too many lines for the Layout/Table to display on the current screen size
MAX_LINES = 1000
def fill_table(max_height):
rows = [Pretty(li) for li in range(MAX_LINES)]
# Subtract 4 lines - that's how many the table's header and footer takes
n_rows = max_height - 4
if len(rows) > n_rows:
rows = rows[: n_rows - 1] + [f"...and {len(rows) - n_rows + 1} further items"]
table = Table()
for row in rows:
table.add_row(row)
return table
console = Console()
layout = Layout(name="root")
render_map = layout.render(console, console.options)
pprint("Region of the layout:", render_map[layout].region)
table = fill_table(render_map[layout].region.height)
layout["root"].update(table)
console.print(layout)
I didn't figure out how to measure the Table directly, but measuring Layout does the trick. I still needed to manually input the number of rows that the table's header+footer takes, which might fail if there is a multi-line column name.
EDIT: I've posted an answer to a similar problem which also takes into account multi-line rows.

How to get the QModelIndex associated with the QCheckbox placed in the QTableWIdget

I have the following code to place a CheckBox in the first column of a list of items in a QTableWidget.
checkboxWidget = QWidget()
checkBox = QCheckBox(checkboxWidget)
checkBox.clicked.connect(self._check_changed)
#
# If the variable is in the monitored list
# check the checkbox
#
isMonitored = False
if (self._monitored_variables != None):
if (self._monitored_variables[name]):
isMonitored = True
if (isMonitored):
checkBox.setCheckState(Qt.CheckState.Checked)
else:
checkBox.setCheckState(Qt.CheckState.Unchecked)
layoutCheckbox = QHBoxLayout(checkboxWidget)
layoutCheckbox.addWidget(checkBox)
layoutCheckbox.setAlignment(Qt.AlignCenter)
layoutCheckbox.setContentsMargins(0, 0, 0, 0)
self._variables_view.setCellWidget(row,0, checkboxWidget)
I started with the answer to this question:
How should I connect CheckBox clicked signals in Table Widgets in PyQt5?
The difference that I have is that I want the CheckBox centered in the table cell, hence the extra controls.
The click handler looks like this:
def _check_changed(self):
cb = self.sender()
print(cb.parent())
ix = self._variables_view.indexAt(cb.pos())
print(ix.row(), ix.column(), cb.isChecked())
The problem I am facing is that the row/column is not correct.
How can I recover the row/column of the CheckBox that was clicked?
It seems that my previous answer was not explicit but I am going to take this answer to take it in a generic way that will work for all the classes that inherit from QAbstractItemView and any type of widget placed through the setCellWidget or setIndexWidget methods.
General case:
The key to the solution is to obtain the position of the widget that emits the signal with respect to the viewport of the QAbstractItemView, and then use indexAt to get the QModelIndex. This can be obtained by following the following steps:
Map any relative internal position of the widget to global coordinates.
Map global coordinates to local coordinate relative to the viewport.
use indexAt() with local coordinate.
gl = widget.mapToGlobal(QtCore.QPoint())
lp = view.viewport().mapFromGlobal(gp)
ix = view.indexAt(lp)
Specific case:
In this case, just do the following:
def _check_changed(self):
widget = self.sender()
gl = widget.mapToGlobal(QtCore.QPoint())
lp = self._variables_view.viewport().mapFromGlobal(gp)
ix = self._variables_view.indexAt(lp)
It seems the answer to my issues was rather simple. In the "indexAt" method the "cb.pos()" needs to be changed to "cb.parent().pos()".

Make text in a tkinter Listbox fit inside?

I've looked everywhere for a fix to this. I stumbled across this:
How to fit Tkinter listbox to contents
But this question is asking the reverse of what I want. I want the box to remain the size I've set it to, but the text runs off of the side like in the screenshot from the above linked question. Is there anyway to force a \n to be added to the string once its character count reaches the length of the listbox?
Also, I apologize if something is wrong with the format of my question, I've never posted here before.
class Stars(Tk):
def __init__(self):
Tk.__init__(self)
self.feed = Listbox(self, width = 55 , height = 31, relief = SUNKEN, borderwidth = 3)
self.feed.grid(row = 1, column = 2, columnspan = 2)
def simulate(self):
self.mass = eval(self.massEntry.get())
self.feed.insert(END, 'Star to be created with mass of {} * 10^30 kg; {} solar masses.'.format(1.98855 * self.mass, self.mass))
self.feed.insert(END, '0 years: Protostar formed in an interstellar gas cloud, and begins to compress due to gravity.')
This is all of the relevant code (trying to make a stellar evolution simulation). This is what it looks like when run, with the problem circled in red:
http://imgur.com/dZCYe6s
No, there is no way to have a Listbox wrap the text. If you want to support wrapping, use a Text widget. If you want to select items like in a listbox, you can add some custom bindings to do that.

Categories