Possibly a dupe, but couldn't find a match. Feels like a blind spot in my mind because of its (seemingly) simplicity, but can't figure it out.
from gi.repository import Gtk
win = Gtk.Window()
maingrid = Gtk.Grid()
# the attach test
label = Gtk.Label("Test123 ")
maingrid.attach(label, 0, 0, 1, 1)
entry = Gtk.Entry()
maingrid.attach(entry, 1, 0, 1, 1)
# ok, then the box test
label2 = Gtk.Label("Test456 ")
entry2 = Gtk.Entry()
box = Gtk.Box()
maingrid.attach(box, 0, 1, 2, 1)
box.pack_start(label2, False, False, 0)
box.pack_start(entry2, False, False, 0)
The way Gtk calculates the real size of every widget is complex... at least to me.
In this case, you are setting only the "size request" of the entry with set_width_chars().
Changes the size request of the entry to be about the right size for n_chars characters. Note that it changes the size request, the size can still be affected by how you pack the widget into containers. If n_chars is -1, the size reverts to the default entry size.
When the window is shown or resized, the entry requests a size of at least n_chars width. Indeed when you reduce the window size, your entries will be reduced, but no more than n_chars characters. Let's try with 20 characters and this will be clear. With 5 characters, the entry will be reduced no more than 9 characters, at least on my machine... I think because even the main window has a size request that is bigger than only the label and the 5-chars entry.
If you want you can use set_max_width_chars(). In this case, the entry will not be assigned a size greater than n_chars. Maybe this is what you want.
If you set the same number in set_max_width_chars() and set_width_chars(), you will always have an entry with the same size.