开发者

Resizeable Tk windows done right

There is something wrong with the way I assemble my Tk windows (with R tcltk and tcltk2, under Win XP)

library(tcltk)
library(tcltk2)

开发者_Python百科expandTk <- function() {
  root  <- tktoplevel()
  # textbox with scroll bars
  textbox <- tk2frame(root)
  scr <- tkscrollbar(textbox, repeatinterval=5, command=function(...) tkyview(txt,...))
  txt <- tktext(textbox, bg="white", font="courier", wrap="word", yscrollcommand=function(...)tkset(scr,...))
  tkpack(txt, side="left", fill="both", expand=TRUE)
  tkpack(scr, side="right", fill="y")
  tkmark.set(txt,"insert","0.0")
  tkpack(textbox, fill="both", expand=TRUE)
  # status bar and size grip
  statusText <- tclVar("")
  f <- tk2frame(root, relief="sunken")
  l <- tk2label(f, textvariable=statusText)
  tkpack(l, side="left", pady=2, padx=5, expand=0, fill="x")
  tkpack(f, side="left", expand=1, fill="x", anchor="s")
  sg <- ttksizegrip(root)
  tkpack(sg, side="left", expand=0, anchor="se")
}

The window looks fine, but as soon as I resize it (ie make it smaller), the scrollbar and the statusbar disappears. I am quite sure that this is a user error, I have seen other Tk apps which resize properly, but I can not figure out which option I should use...

Any hint appreciated, Karsten


That's standard behavior for the Tk pack geometry manager. Here's a relevant section of the pack man page:

If the cavity should become too small to meet the needs of a slave then the slave will be given whatever space is left in the cavity. If the cavity shrinks to zero size, then all remaining slaves on the packing list will be unmapped from the screen until the master window becomes large enough to hold them again.

So if you shrink the overall window to be smaller than the space requested for the text widget, you leave no space for the other widgets, and they get unmapped.

The best solution is to use the grid geometry manager, instead of pack. Generally speaking, I find grid to be much easier to use and capable than pack anyway, although your mileage may vary. In particular, it eliminates the need for many superfluous frame widgets, which can simplify your code a lot.

I think you can use something like this:

expandTk <- function() {
  root  <- tktoplevel()
  # textbox with scroll bars
  textbox <- tk2frame(root)
  txt <- tktext(textbox, bg="white", font="courier", wrap="word", yscrollcommand=function(...)tkset(scr,...))
  scr <- tkscrollbar(textbox, repeatinterval=5, command=function(...) tkyview(txt,...))
  tkmark.set(txt,"insert","0.0")

  # Set up the geometry for the stuff inside the "textbox" frame.

  # The text and scrollbar widgets live on the same row.
  tkgrid(txt, scr)

  # The text widget should stick to all four sides of its parcel.
  tkgrid.configure(txt, sticky="nsew")

  # The scrollbar should stick to the top and bottom of its parcel, it need not stick to the
  # left and right.
  tkgrid.configure(scr, sticky="ns")

  # When the window is resized, we want surplus space to be allocated to the text widget,
  # which is in the top left corner of this frame.
  tkgrid.columnconfigure(textbox,0,weight=1)
  tkgrid.rowconfigure(textbox,0,weight=1)

  # status bar and size grip
  statusText <- tclVar("")
  l <- tk2label(root, textvariable=statusText,relief="sunken")
  sg <- ttksizegrip(root)

  # Set up the geometry for the stuff inside the "root" window.

  # First row is just the textbox frame...
  tkgrid(textbox)

  # Second row is the status label and the resize gadget
  tkgrid(l, sg)

  # The textbox widget should span 2 colums, and stick to all four sides of its parcel.
  tkgrid.configure(textbox,columnspan=2,sticky="nsew")

  # The status label should stick to all four sides of its parcel too
  tkgrid.configure(l,sticky="nsew")

  # The resize gadget should only stick to the bottom right of its parcel
  tkgrid.configure(sg,sticky="se")

  # When the window is resized, we want surplus space to go to the textbox frame (and from there
  # to the text widget itself, which it will do thanks to the grid weights we set up above).  The
  # textbox frame is in the top left corner of its parent window.
  tkgrid.columnconfigure(root,0,weight=1)
  tkgrid.rowconfigure(root,0,weight=1)
}

There's some more information about using the grid geometry manager from R here.


If you're insistent on packing the widgets, you should be aware that if there isn't enough space to give all widgets the room they asked for, space is given preferentially to the first widgets packed (within a particular container). Put the statusbar in first, then the scrollbars, and only then the main widget. (You may need to alter which side you're packing particular widgets on to make it all work right.) Also, if it is getting too complicated, remember that you can pack inside frames packed inside frames; that gives you lots of flexibility.

But that's the point when using the grid geometry manager just makes sense. It gives you a lot more fine control when you're going for that last 10% of the look of your app, and needs less nesting of widgets to achieve it.


Definitely grid is best, as Eric points out, but if you really want to use pack then resizing the expanding txt widget, like below, will get you what you are looking for. There are some size heuristics that could be improved.

Add this to the end of your code:

widthOfChar <- ceiling(as.numeric(tclvalue(tcl("font","measure","TkTextFont","0123456789")))/10) + 2
tkbind(root, "<Configure>", function(W) {
  w.width <- as.integer(tkwinfo("width",W))
  txt.width <- w.width - 15L
  tkconfigure(txt, width=floor(txt.width/widthOfChar))
})
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜