I have 2 frames inside root which called "header_frame" and "activity_frame" both are in the same column which is "column=0". I want both frames to be resizeable matching its root parent filling all empty space like this :
I have tried all grid config possibilities including setting 'rowconfigure' on 'root', set activity_frame to stick on "North" which is header_frame, last but not least I've also tried to stick header_frame to south which is the result I do not want because those frame share same size (I hope header_frame has 'maxsize' attribute but sadly it didn't have). so this is the code that I've tried :
self.root = root
self.column = ""
self.search = ""
self.root.minsize(500,480)
self.comboboxValue = None
self.root.title("CUCI (CSV Unique Column Identifier)")
self.root.configure(background="powder blue")
self.root.grid_columnconfigure(0,weight=1)
self.root.grid_rowconfigure(0,weight=1)
self.root.grid_rowconfigure(1,weight=1)
#header frame
self.header_frame = tk.Frame(self.root)
self.header_frame.grid(row=0, column=0,sticky="NEW")
self.header_frame.grid_columnconfigure(0,weight=1)
self.header_frame.grid_rowconfigure(0,weight=1)
self.header_frame.configure(background="grey")
#activity Frame
self.activity_frame = tk.Frame(self.root)
self.activity_frame.grid(row=1, column=0,sticky="NEWS")
self.activity_frame.grid_columnconfigure(0,weight=1)
self.activity_frame.grid_rowconfigure(0,weight=1)
self.activity_frame.configure(background="grey",pady=1)
Here's the layout result from my code which I do not expect:
The point is that I want to fill those empty spaces with activity_frame to be stick-ed on 'header_frame'.Please I do not wish to use pack(self.activity_frame.pack(fill=tk.X)). I just want to use grid because it's easy to use
The reason for the gap is because you don't have the header frame stick to the bottom of the space it was given. If you change the sticky attribute for the header to be "nsew" you'll see that the header fills the extra space.
self.header_frame.grid(row=0, column=0,sticky="nesw")
I'm guessing you don't want the header frame to be so tall. If that is the case, give row 0 a weight of 0 instead of 1. That way all extra unallocated space will be given to row 1.
self.root.grid_rowconfigure(0,weight=0)
After doing so, and after adding a couple of other widgets to simulate your screen, this is what it looks like:
Related
I got a problem with changing the height of the Treeview.heading. I have found some answers about the dimensions of Treeview.column, but when I access Treeview.heading in the documentation, there is not a single word about changing the height of the heading dynamically when the text doesn't fit (and wrapping it) or even just hard-coding height of the heading in pixels.
I don't have to split the text to two rows, but when I just keep it that long the whole table (as it has many entries) takes up the whole screen. I want to keep it smaller, therefore I need to split longer entries.
Here is how it looks like:
I can't find any documentation to verify this but it looks like the height of the heading is determined by the heading in the first column.
Reproducing the problem
col_list = ('Name', 'Three\nLine\nHeader', 'Two\nline')
tree = Treeview(parent, columns=col_list[1:])
ix = -1
for col in col_list:
ix += 1
tree.heading(f'#{ix}', text=col)
The fix
col_list = ('Name\n\n', 'Three\nLine\nHeader', 'Two\nline')
or, if you want to make it look prettier
col_list = ('\nName\n', 'Three\nLine\nHeader', 'Two\nline')
The only problem is I haven't figured out how to centre the heading on a two line header
Edit
The newlines work if it is the top level window but not if it is a dialog. Another way of doing this is to set the style. I've got no idea why this works.
style = ttk.Style()
style.configure('Treeview.Heading', foreground='black')
you can use font size to increase the header height for sometimes;
style = ttk.Style()
style.configure('Treeview.Heading', foreground='black', background='white', font=('Arial',25),)
I have a list of pictures:
pictures = {im1,im2,im3,im4,im5,im6}
Where
im1:
im2:
im3:
im4:
im5:
im6:
I want to assign the pictures to labels (1,2,3,4 etc.)
For instance, here pictures 1 to 3 belong to label 1, picture 4 belongs to label 2, picture 5 to label 3, and picture 6 to label 4.
-> label = {1,1,1,2,3,4}
Since I need to see the images when I label them, I need a method to do that while labeling them. I was thinking of creating an array of images:
And then I define the ranges by clicking on the first and last picture belonging to the same labels, so for example:
What do you think ? Is this somehow possible ?
I would like to assign different labels to different ranges of pictures.
For instance: When one has finished selecting the first label one could indicate it by a Double-click and then do the selection of the second label range, then Double-click, then do the selection of the third label range, then Double-click, then do the selection of the fourth label range, etc.
It does not have to be Double-clicking to change the selection of the labels, it could also just be a buttom or any other idea that you might have.
In the end one should have the list of labels.
Essentially, most of the interaction you are looking for boils down to being able to display images, and detect clicks on them in real time. As that is the case, you can use the jupyter widgets (aka ipywidgets) module to achieve most (if not all) of what you are looking for.
Take a look at the button widget which is described here with explanation on how to register to its click event. The problem - we can't display an image on a button, and I didn't find any way to do this within the ipywidgets documentation. There is an image widget, but it does not provide an on_click event. So construct a custom layout, with a button underneath each image:
COLS = 4
ROWS = 2
IMAGES = ...
IMG_WIDTH = 200
IMG_HEIGHT = 200
def on_click(index):
print('Image %d clicked' % index)
import ipywidgets as widgets
import functools
rows = []
for row in range(ROWS):
cols = []
for col in range(COLS):
index = row * COLS + col
image = widgets.Image(
value=IMAGES[index], width=IMG_WIDTH, height=IMG_HEIGHT
)
button = widgets.Button(description='Image %d' % index)
# Bind the click event to the on_click function, with our index as argument
button.on_click(functools.partial(on_click, index))
# Create a vertical layout box, image above the button
box = widgets.VBox([image, button])
cols.append(box)
# Create a horizontal layout box, grouping all the columns together
rows.append(widgets.HBox(cols))
# Create a vertical layout box, grouping all the rows together
result = widgets.VBox(rows)
You can technically also write a custom widget to display an image and listen for a click, but I simply don't believe it's worth your time and effort.
Good luck!
The qsl package provides widgets that do this. For your case, the following code would allow you to label images in batches. Full disclosure, qsl is a project I started because I, like you, wanted to label images from inside Jupyter notebooks.
import qsl
from IPython.display import display
labeler = qsl.MediaLabeler(
items=[
{"target": "https://i.stack.imgur.com/cML6z.jpg"},
{"target": "https://i.stack.imgur.com/6EVAP.jpg"},
{"target": "https://i.stack.imgur.com/CAxUw.jpg"},
{"target": "https://i.stack.imgur.com/8fhan.jpg"},
{"target": "https://i.stack.imgur.com/eMXn5.jpg"},
{"target": "https://i.stack.imgur.com/YFBfM.jpg"}
],
# Optional, you can also configure the labeler from
# the UI.
config={
"image": [
{
"name": "Type",
"options": [
{"name": "Foo"},
{"name": "Bar"}
]
}
]
},
# Optional, set to 1 if you want to label
# one image at a time.
batch_size=4,
# Optionally, save labels to JSON. You
# can also get the labels using `labeler.items`.
jsonpath="labels.json"
)
display(labeler)
This generates a UI that looks like this.
Here is a Google Colab notebook that shows how to do this in Google Colab.
So column width is done using cell width on all cells in one column ike this:
from docx import Document
from docx.shared import Cm
file = /path/to/file/
doc = Document(file)
table = doc.add_table(4,2)
for cell in table.columns[0].cells:
cell.width = Cm(1.85)
however, the row height is done using rows, but I can't remember how I did it last week.
Now I managed to find a way to reference the rows in a table, but can't seem to get back to that way. It is possible to change the height by using the add_row method, but you can't create a table with no rows, so the the top row will always be the default height, which about 1.6cms.
There is a way to access paragraphs without using add_paragraph, does anyone know how to access the rows without using the add_row method because it was that that I used to set row height in a table as a default.
I have tried this but it doesn't work:
row = table.rows
row.height = Cm(0.7)
but although this does not give an error, it also has no effect on the height.
table.rows is a collection, in particular a sequence, so you need to access each row separately:
for row in table.rows:
row.height = Cm(0.7)
Also check out row.height_rule for some related behaviors you have access to:
https://python-docx.readthedocs.io/en/latest/api/table.html#row-objects
When you assign to table.rows.height, it just adds a .height instance attribute that does nothing. It's one of the side-effects of a dynamic language like Python that you encounter a mysterious behavior like this. It goes away as you gain more experience though, at least it has for me :)
Some additional information:
The answer here is correct, but this will give a minimum row height. Using WD_ROW_HEIGHT_RULE.EXACTLY will fix the cell height to the set row height ignoring the contents of the cell, this can result in cropping of the text in an undesirable way.
para = table.cell(0,0).add_paragrph('some text')
SOLUTION:
add_paragraph actually adds a blank line above the text.
Use the following instead to avoid using add_paragraph:
table.cell(0,0).paragraphs[0].text = 'some text'
or using add_run can make it easier to also work with the text:
run = table.cell(0,0).paragraphs[0].add_run('some text')
run.bold = True
I need to add an IF statement, FOR and WHILE loop in my form, can someone add some kind of IF statment, FOR and WHILE loop in my code?
from tkinter import * # Ingress all components from Tkinter
mGui = Tk()
mGui.geometry('400x400') # The size of the form window
mGui.title('Registration Form',)
def response():
label3 = Label(text='Thank You!',fg='White', bg='Purple',font='none 16 bold').place(x=140,y=300) # The colours and font style and size used for the response
mlabel = Label(text='Registration Form',fg='White', bg='Purple',font='none 18 bold underline') # The colours and font style and size used for the form title
mlabel.pack()
mlabel2 = Label(text='Forename',fg='White', bg='Purple',font='times 14 bold').place(x=0,y=100) # The colours and font style and size used for the label
mlabel3 = Label(text='Surname',fg='White', bg='Purple',font='times 14 bold').place(x=0,y=150) # The colours and font style and size used for the label
mbutton = Button(text = 'Submit',command = response).place(x=150,y=250) # Location of the the button 'submit' using the x and y axis
mGui.configure(background='Green') # Background colour of the form
mEntry = Entry(bg='White').place(x=100,y=100)
mEntry = Entry(bg='White').place(x=100,y=150)
mGui.mainloop() # The code iterates
Tkinter programs are event-driven, like all GUIs that I know of.
The only place where you can execute your own code is in callbacks or timeouts.
If you want to check the contents of the entry windows, you could do that in at least two ways:
Write a callback function for the Submit button that retrieves the contents of the entry windows and validates them. You could e.g. use a messagebox telling the user that the entries are not valid.
You could also set up the entry windows to validate themselves when a key is pressed. I've used this to e.g. make the text or background of an entry window red if it contains invalid text. You could also put a tip in a Label below the entry windows, like "Both Forename and Surname must at least contain one character."
I'm working with the data grid with SQLite database using a feature called callbacks as described in this lesson: displaying-large-amounts-of-data
I'd like to make some changes to the sample stack included in that lesson (you can download the stack from the link on the top of that page).
I'd like to display the 'plot' text in the DG instead of the 'Title' of the movie and the plot text should have variable line heights as described in this lesson: how-do-i-create-a-form-with-variable-line-heights
In the sample stack I made these changes:
in the Row Template:
renamed field "Title" to "plot", set the dontWrap to false and changed fixedLineHeight to false
renamed field "ReleaseDate" to "nr"
added:
set the text of field "nr" of me to pDataArray["id"]
in the Row Behavior:
## changed the layoutControl to make space for wrapping of field "plot"
on LayoutControl pControlRect
local theFieldRect
put the rect of me into theFieldRect
set the right of button "Genre" of me to item 3 of theFieldRect
set the right of field "LblGenre" of me to the left of button "Genre" of me
set the right of field "nr" of me to item 3 of theFieldRect
## Expand field "plot"
put the rect of field "plot" of me into theFieldRect
put item 3 of pControlRect - 180 into item 3 of theFieldRect
set the rect of field "plot" of me to theFieldRect
##Now resize field to fit content
put item 2 of theFieldRect \
+ the formattedheight of field "plot" of me - \
the bottommargin of field "plot" of me \
into item 4 of theFieldRect
set the rect of field "plot" of me to theFieldRect
## Now update the bounding rect to match total height you
## want this row to have
put item 4 of theFieldRect into item 4 of pControlRect
set the rect of graphic "Background" of me to pControlRect
end LayoutControl
In the lesson on setting variable line heights it says to turn off the "fixed control height" of the data grid. However when I do that nothing gets displayed and I'm getting a scrip error.
The stack with my changes is here: Databases-callbacks-variable-line-height.zip
(just replace the original stack from the lesson with it; the SQLite database is the same and should be placed in the same folder as the stack).
How to fix this so that the variable line height will work?
I located the problem with fixed control height turned off. The data grid behavior isn't setting the GetDataForLine pLine parameter properly when caching the height for each row. I'll provide a modification you can make to the data grid behavior until a fix is included in LiveCode, but you should probably reconsider your approach. When fixed line height is turned off the data grid has to compute the height of every line prior to displaying the data grid. This takes a loooooong time for the 50,000 record example you are working with.
First, edit the script of the data grid behavior:
edit script of btn "data grid" of stack "revdatagridlibrary"
Next, go to line 3097. Below 3097 add the following line of code:
add 1 to theSequence
The code should now look like this:
repeat for each key theIndex in sDataArray
add 1 to theSequence
## Get height
if sDataArray[theIndex] is NULL then
Save the script. This will save the change in your current version of LiveCode. Be aware that the next time you update LiveCode the change will be lost. I submitted the fix to RunRev, however, so it should be fixed in the next release.
Also, your code that sets the rect of graphic "Background" needs a small change. Right now it won't work if the bottom of the field is higher than the bottom of the "Genre" button. The code should probably be something like this:
put max(item 4 of theFieldRect, the bottom of button "Genre" of me + 4) into item 4 of pControlRect