How to set the icon-size in a QTreeWidget? - python

How does one set the icon size for items within a Qtreewidget? So far I have tried
QTreeWidget.setIconSize(QSize(32,32))
But all that does is increase the width, not the height.
However, a
print QTreeWidget.iconSize()
shows the correct result :
PyQt4.QtCore.QSize(32, 32)
Does anyone have a better understanding of how QTreewidget works?

Jason had some of the solution. The height of the treeview's rows needs to be resized as well:
class MyDelegate(QItemDelegate):
def __init__(self):
QItemDelegate.__init__(self)
def sizeHint(self, option, index):
return QSize(32,32)
Then, elsewhere:
delegate = MyDelegate()
tree = QTreeWidget()
tree.setItemDelegate(delegate)
Not ideal as it resizes every row.
[edit] If you want to vary the size of the rows ensure the QTreeWidget/View.uniformRowHeights == False
Then mess around using the index. For me I wanted the 2nd row to bigger than the rest. I'm sure there's a better way but my sizeHint became:
def sizeHint(self,option,index):
parent = index.parent()
if parent.isValid() and not parent.parent().isValid():
return QSize(32,32)
return QSize(24,24)
However, there's another issue with this. Icons are not resizeable. Ah! That has to be done with
QTreeWidget.setIconSize(QSize(width,height))

Looks to me like you need to adjust the row height of the QTreeView to accommodate the larger icons. From what I can see in the pictures, it looks like the icons are being resized properly, but because the rows are not tall enough and the icons are being cropped. See this for how to change the row heights.

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()

How to fixed image when moving item in QGraphicsViewer

I am developing an image viewer using pyqt.
I want the image to be fixed when the box moved.
However, the image is pushed when the box tries to reach the viewer's side like this.
It was implemented using QGraphicsView, QGraphicsScene, and QGraphicsitem.
This is part of main class
self.scene_r = GraphicsScene()
self.scene_r.addPixmap(pix_resized)
self.resizedView.setScene(self.scene_r)
self.resizedView.centerOn(128,128)
This is QGraphicScene Class
class GraphicsScene(QGraphicsScene):
def __init__(self, parent=None):
QGraphicsScene.__init__(self)
rect_item = recItem(QRectF(0, 0, 100, 100))
rect_item.setFlag(QGraphicsItem.ItemIsMovable, True)
rect_item.setZValue(1)
rect_item.setPen(Qt.green)
self.addItem(rect_item)
I tried to override mouseMoveEvent() of QGraphicsRectItem class, but it failed.
That happens for two reasons:
Since you are making the item movable, you can move it freely, anywhere you want.
When the scene rectangle is smaller than the one visible in the view, the view tries to ensure that the whole scene rectangle stays visible, possibly by scrolling its contents.
Note that, unless explicitly set using setSceneRect() (on the scene or on the view, the results might differ), Qt automatically sets the scene rect implicitly to the bigger QRect that contains all visible graphics items.
There at least two possible solutions to your problem, which one to choose depends on what you need, and you can also decide to use both of them.
Explicitly set the sceneRect
You can set the sceneRect to a specific rectangle, an item, or all existing items. Note that, while in your case it won't change much if you set the rectangle for the scene or the view, in more complex cases (for example, multiple views showing the same scene) the result might differ.
# on the view
self.setSceneRect(self.scene_r.itemsBoundingRect())
# alternativaly, on the scene
self.setSceneRect(self.itemsBoundingRect())
Limit the area in which the item can be moved
In this case you can intercept the itemChange ItemPositionChange (note that the ItemSendsGeometryChanges flag must be set) and return an adjusted value before it is actually applied:
class RecItem(QGraphicsRectItem):
def __init__(self, *args):
super().__init__(*args)
self.setFlags(self.ItemSendsGeometryChanges)
def itemChange(self, change, value):
if change == self.ItemPositionChange:
sceneRect = self.scene().sceneRect()
newGeometry = self.boundingRect().translated(value)
# the item pen must be taken into account
halfPen = self.pen().width() / 2
if value.x() < sceneRect.x():
value.setX(sceneRect.x() + halfPen)
if value.y() < sceneRect.y():
value.setY(sceneRect.y() + halfPen)
if newGeometry.right() + halfPen > sceneRect.right():
value.setX(sceneRect.right() - newGeometry.width())
if newGeometry.bottom() + halfPen > sceneRect.bottom():
value.setY(sceneRect.bottom() - newGeometry.height())
return value
return super().itemChange(change, value)

tkinter - resizing empty frame

How do you force a frame to get window_height 0?
the general case where my problem occurs:
import Tkinter as Tk
class App(Tk.Frame):
def __init__(self, master):
Tk.Frame(self, master)
self.place_holder = Tk.Frame(master=self)
self.content = Tk.Frame(master=self)
self.place_holder.pack()
self.content.pack(side=Tk.RIGHT)
Tk.Button(master=self,command=self.add_something).pack(side=Tk.TOP)
self.to_destroy = []
def add_something(self):
foo = Tk.button(master=self.place_holder, command=self.destroy_last)
self.too_destroy.append(foo)
def destroy_last(self):
self.to_destroy[-1].destroy()
the problem:
As I add more elements to the place_holder, it rescales nicely.
When I remove elements from the place_holder, it rescales nicely.
EXCEPT when I remove the last element.
Before i added anything, even when i do place_holder.pack(), it will not show. But after removing the last element, the place_holder will keep the size of this last element. Is there a way to hide the place_holder again untill i add content again?
example image
The empty container at the bottom left does not contain any elements, but still has the size of the last element in it, how can i get this to disappear without removing it (i want it again in the same place)?
What is happening is that when you remove the last widget, pack no longer is managing the frame so it isn't responsible for setting the frame size.
The simplest solution is just to temporarily pack a 1x1 pixel frame, which wil cause the placeholder frame to shrink.
There's no way to make a frame of zero pixels, so this method will always result in a one pixel tall/wide area for the placeholder. If you don't want that one pixel, you can install call pack_forget on the placeholder to completely remove it from the display, and then use pack with suitable options to re-add it when you put something in it.
Example:
def destroy_last(self):
self.to_destroy.pop().destroy()
if len(self.to_destroy) == 0:
tmp = Tk.Frame(self.place_holder, width=1, height=1, borderwidth=0)
tmp.pack()
self.place_holder.update()
tmp.destroy()

Python tkinter main window improper size when .grid() widgets

I have a game board which is rows x columns list.
Min size is 2x2 and max 10x10, with unequal rows:columns being okay (e.g. 2x3, 4x9).
Main window object has no predetermines geometry size setting, and widgets (buttons) are being .grid() in it for each list element in a was that creates a 2D map.
Ideally, given the method used this would lead to a nice, edge=to-edge map inside the main window.
Unfortunately, testing has shown that while this is true for maps with columns count > 3, when columns <= 3 then the window seems to default to a certain X-size, where this ugly free space is present at the right of the window.
This is not the case for Y-axis, which is defined by rows.
Note that buttons placed are fixed 32x32 px (determined by image inside).
def createMap (): #creates rows x columns 2D list - a map
global rowsEntryVar, columnsEntryVar, mapList
mapList = []
for row in range(rowsEntryVar):
tempList = []
for column in range(columnsEntryVar):
tempList.append(Button(root, bd=0, bg=redMagenta, activebackground=redMagenta))
mapList.append(tempList)
and then:
def drawMap ():
global mapList
for row in range(len(mapList)):
for column in range(len(mapList[row])):
mapList[row][column].grid(row=row, column=column)
Image:
Image showing the problem
Please go easy on me, I'm quite new to programming. :)
This appears to be a platform-specific limitation. I can't duplicate the problem on my Mac, but I can on a windows VM. Apparently, Windows won't allow the width of the window to be smaller than the space required for the buttons and icon on the titlebar.
My advice is to give the rows and columns a positive weight so that they will grow to fit the window, and then use the sticky option to cause the buttons to fill the space given to them.
when columns <= 3 then the window seems to default to a certain X-size,
Tkinter defaults to the size of the widgets so you must be setting the geometry for "root" somewhere. The following works fine on my Slackware box (and using a function as a function eliminates the globals). If you are just starting, then it is good to form good habits, like conforming to the Python Style Guide https://www.python.org/dev/peps/pep-0008/ (variables and functions are all lower case with underlines).
from Tkinter import *
def create_map (rowsEntryVar, columnsEntryVar): #creates rows x columns 2D list - a map
mapList = []
for row in range(rowsEntryVar):
tempList = []
for column in range(columnsEntryVar):
tempList.append(Button(root, text="%s-%s" % (row, column),
bd=0, bg="magenta2", activebackground=r"magenta3"))
mapList.append(tempList)
return mapList
def draw_map(mapList):
for row in range(len(mapList)):
for column in range(len(mapList[row])):
mapList[row][column].grid(row=row, column=column)
root = Tk()
map_list=create_map(4, 3)
draw_map(map_list)
root.mainloop()

Categories