Qt QTableView resize to fit content - python

Hi, I have created MainWindow as shown above. I want to expand the first widget (plots) as much as possible so that the other two widgets fit content (or actually, I want to remove white empty space below tables).
I don't know how to do that.
Currently, both table vertical header size policy is set to FitToContent.
Also, it needs to be dynamic so if I add a new row to the table, a new row should be visible (table will be larger).
I hope I'm clear enough, and also hope there is no need for runnable code.

Ok, I figure it out.
Reimplementing the resizeEvent will do the trick.
def resizeEvent(self, event):
super(Table, self).resizeEvent(event)
height = self.horizontalHeader().height()
for row in range(self.model().rowCount()):
height += self.rowHeight(row)
if self.horizontalScrollBar().isVisible():
height += self.horizontalScrollBar().height()
self.setMaximumHeight(height + 2)
I'm changing the height of QTableView. I'm including height of horizontal header + height of all rows + height of horizontalScrollBar if it is visible.

Related

Tkinter: Issue with height/width scaling in tk.Text widget

I cannot seem to get the scroll on this text to work. The text will scroll to a certain extent, but then not appear afterwards. I believe that the height of the text widget is not what I want. For instance, the image below shows only about a half of what the actual result is for 遺伝子 (which I can find out by attempting to drag from a piece of text in the middle of the frame to the bottom). The width also is not the same size of the frame I would like it to be: the number 23 was just something that appeared to work.
If I do
txt = tk.Text(self.SEARCH_RESULTS_TEXT_FRAME,width=self.SEARCH_RESULTS_FRAME_WIDTH,height=20,background='#d9d9d9',relief=RIDGE)
... the width becomes much too large. I would have thought that it would only take up the size of the frame, which is 240.
Note the SEARCH_RESULTS_TEXT_FRAME is the frame in red.
Relevant code:
#search text frame
self.SEARCH_RESULTS_FRAME_HEIGHT = 240 #240
self.SEARCH_RESULTS_FRAME_WIDTH = self.TITLE_FRAME_WIDTH - 23
self.SEARCH_RESULTS_TEXT_FRAME = Frame(self.SEARCH_RESULTS_FRAME,height=self.SEARCH_RESULTS_FRAME_HEIGHT,width=self.SEARCH_RESULTS_FRAME_WIDTH)
self.SEARCH_RESULTS_TEXT_FRAME.place(x=10,y=10,anchor = NW)
self.SEARCH_RESULTS_TEXT_FRAME.config(background ="#adadad")
def print_dict_to_frame(self,results_list):
txt = tk.Text(self.SEARCH_RESULTS_TEXT_FRAME,width=34,height=20,background='#d9d9d9',relief=RIDGE)
txt.place(x=0,y=0)
txt.tag_configure('header',justify = 'center',font=("Meiryo",12,'bold'))
txt.tag_configure('entry',font=('Meiryo',8))
for r_list in results_list:
header = r_list[0]
entry = r_list[1]
txt.insert(tk.END, "{}\n".format(header),'header')
for single_result in entry:
txt.insert(tk.END,single_result+"\n",'entry')
txt.configure(state=DISABLED)
How can I get the text widget to only take up the width of the frame and the height of the frame, allowing for scrolling?
If I do txt = tk.Text(self.SEARCH_RESULTS_TEXT_FRAME,width=self.SEARCH_RESULTS_FRAME_WIDTH,height=20,background='#d9d9d9',relief=RIDGE) ... the width becomes much too large. I would have thought that it would only take up the size of the frame, which is 240.
The width option specifies a width in the number of characters, not pixels. If you use the value 240, the width will be 240 multiplied times the width of an average character in the font you are using.
How can I get the text widget to only take up the width of the frame and the height of the frame, allowing for scrolling?
Give the text widget a width and height of 1, and then let the geometry manager (pack, place, or grid) be responsible for stretching it to fit the frame.
Since you're using place, you can use the relwidth and relheight options to make it the full size of the frame:
txt.place(x=0,y=0, anchor="nw", relwidth=1.0, relheight=1.0)
Personally, I recommend using pack or grid in almost all cases. It's much easier to create a responsive UI with them than it is with place.

Repeating texture in a Rectangle of a variable size

I have a texture that I want to repeat inside a Rectangle drawn by canvas.before. The problem is that I don't know what is going to be the size of the Rectangle (it's used as a background for its widget).
For example, I have a Rectangle that has 48 px height and width 100 - 500 px. I want to fill its content by horizontally repeating a 48x48 texture.
I know and tried setting texture.wrap = 'repeat' and texture.uvsize and it works correctly but only if I know the widget size beforehand. For example, setting uvsize = (3, 1) for a widget with size 144x48 works fine.
However, this doesn't work when I want to update uvsize before redrawing the widget. I created a canvas callback and updated uvsize there but this has no effect for some reason:
...
with self.canvas.before:
self.cb = Callback(self.on_canvas_redraw)
...
def on_canvas_redraw(self, instr):
self.texture.uvsize = (self.width / 48, 1)
So how can I dynamically update uvsize? Or is there a better way to handle widget resize or a better way to this altogether?

PyQt Pixmap scaling, keep resolution for later

I am creating a label with a pixmap in a cell of a QTableWidget, and I want to be able to "zoom" in and out of the table. I accomplish this by scaling the pixmap with .scaled(width, height) which works totally fine, however once the pixmap is scaled down I can not scale it back up again and maintain the original resolution. In other words once it has drawn to a smaller size I no longer have those pixels for later use, so when I scale it up I'm re-sampling the image.
How can I make it so that I am able to "zoom" in and then back out again of a pixmap while efficiently maintaining the image resolution? Is scaled() the wrong way to go altogether? I could theoretically just reference the original file each time I zoom in and out and create a fresh pixmap at the desired scale, but that assumes that the files are always accessible (which they may not be in my case).
EDIT:
Okay so based on comments I need to create a new pixmap copy for each scale cycle, now the question is how cleanest to do that and how do I make a new instance of the pixmap without editing the original? Here's a snippet of basically what I have now:
global pixArray
pixArray = []
# pix array
label = QtGui.QLabel()
pic = QtGui.QPixmap(imageFile)
label.setPixmap(pic)
#toss this label into the master array for later
pixArray.append(label)
# apply scaled image
label = pixArray[len(pixArray)-1]
picScaled = label.pixmap()
label.setPixmap(picScaled.scaled(rowWidth, rowHeight))
# so now the user wants to scale everything, rowWidth and rowHeight have changed:
for column in range(self.table.columnCount()):
# resize the cells
self.table.setColumnWidth(column, rowWidth)
self.table.setRowHeight(0, rowHeight)
# resize the pixmap label
item = self.table.cellWidget(0, column)
label = pixArray[column]
pic = label.pixmap()
# at this point I am actually scaling the original pixmap, how to create a copy?
item.setPixmap(pic.scaled(rowWidth, rowHeight))

How to ensure that the width of a widget doesnt exceed the width of the window [duplicate]

How to I prevent the automatic widening of Tkinter widgets (specifically labels)? I have a label in my code to which I pass strings of varying length. In the case that the strings are wider than the column width (using the grid layout manager), I would prefer them to be moved to a new line rather than stretching the column. Below is some code that illustrates the problem.
import Tkinter
class window(Tkinter.Tk):
def __init__(self,parent):
Tkinter.Tk.__init__(self,parent)
self.parent = parent
self.initialize()
def initialize(self):
self.grid()
self.columnconfigure(0, minsize=50)
self.columnconfigure(0, minsize=150)
self.rowconfigure(0,minsize=20)
self.rowconfigure(1,minsize=20)
self.rowconfigure(2,minsize=20)
self.labvar = Tkinter.StringVar()
self.lab = Tkinter.Label(self,bg='white',relief='groove',
textvariable=self.labvar)
self.lab.grid(row=0,column=0,rowspan=2,sticky='NSEW')
self.labvar.set("I don't want this to resize (Y dimension) ...")
self.but = Tkinter.Button(self, text='Click me!',command=self.onbut)
self.but.grid(row=2,column=0, sticky='NSEW')
def onbut(self):
self.labvar.set("I don't want this to resize (Y dimension) ...I'd rather this on a new line!")
if __name__ == "__main__":
app = window(None)
app.title('Window')
app.mainloop()
As a quick side note: what is the correct way to avoid the self.labvar.set("I dont...") line stretching over the 80 character limit? I tried using """ and breaking it over two lines but the string was then put in to the label with two lines as well.
Use wraplength option:
self.lab = Tkinter.Label(self,bg='white', relief='groove',
textvariable=self.labvar, wraplength=250)
According to The Tkinter Label Widget documentation:
Labels can display multiple lines of text. You can use newlines or use
the wraplength option to make the label wrap text by itself. When
wrapping text, you might wish to use the anchor and justify options to
make things look exactly as you wish.
...
wraplength=
Determines when a label’s text should be wrapped into multiple lines. This is given in screen units. Default is 0 (no wrapping).
If you give a label a width, it will try its best to honor that width even if the content changes. If you add text that is larger than the width, the text will be truncated. So, for example, you could create your label like this:
self.lab = Tkinter.Label(self,..., width=40)
If you instead want the text to wrap, you can define the wraplength argument, and text longer than the value will wrap to the next line. Unfortunately, the wraplength requires a screen unit (pixels) rather than character width, so you might have to do some math based on the font that you're using. Or, wait until the original widget is rendered, get it's width, and use that for the wraplength.

change size of Frame even if there widget python

hi is there any way to change width and height of widget even if there's widget?
i have code like this
form = Tk()
form.geometry("500x500")
def click():
global frame
frame.config(height = 0 ,width = 0)
frame = LabelFrame(form , text = "vaaja")
frame.place(x = 20 , y = 30)
Label(frame, text ="1").grid(row = 0,column = 0 )
Label(frame, text = "2").grid(row = 1 ,column = 0 )
Button(form , text="Click", command = click).place(x = 200 , y = 200)
form.mainloop()
and when I click the button the size of the frame is the same ( I'cant use grid_forget() for labels and then change the size of frame)
Because you are using place, you have two solutions: you can use place to set the width and height to zero, or you can turn geometry propagation off.
Using place to set the width and height
place allows you to define the width and the height of the placed widget, so in your click function you can do this:
def click():
frame.place_configure(width=0, height=0)
Turning geometry propagation off
A frame is resized to fit its contents by something called "geometry propagation". If you turn this off, you can control the size of the frame with the width and height options of the frame itself. Usually it's better to let Tkinter decide the size for you, but sometimes there's a need to have an explicit size, which is why it's possible to turn geometry propagation off.
Since you are using grid to manage the widgets internal to the frame, you need to use grid_propagate(False) to turn geometry propagation off for that frame:
frame.grid_propagate(False)
By doing so, you're responsible for setting the initial width and height of the widget, though you could leave propagation on to get the initial size, then turn it off with the button click in order to work around that issue.
There's an interesting bug (or feature...) in that if you set the width and height to zero, Tkinter won't redraw the window. At least, it doesn't on the Mac. I don't recall the workaround for that because I never, ever need to set a widget to a zero size, but setting it to 1x1 pixel makes it nearly invisible.

Categories

Resources