I'm trying to create a multiline button with PyGTK. I have a label added to my subclass of gtk.Button, but I'm having trouble sizing the label to the button. If the label makes it's own size, there is no text wrapping even with label.set_line_wrap(True) because the label simply resizes beyond the bounds of the button. I would set the size of the label to that of the button, but unless I explicitly set the size of the button using set_size_request, I haven't been able to find out how big the button is (it's packed in a table).
Any suggestions?
In general, this is not possible with GTK+, because there is no stage where widgets "negotiate" sizes. Instead, widgets report their required size and after that container allocate some areas (normally, equal to or larger than required). In GTK+ 3 there will be width-for-height negotiation, so if your button (rather its label) is anyway going to be allocated several lines, it will be able to request less width and wrap its text.
In 2.x the best you can do is probably use width_chars property of gtk.Label:
import gtk
window = gtk.Window ()
align = gtk.Alignment (0.5, 0.5)
button = gtk.Button ('a very long, possibly multiline text')
label = button.child
label.props.wrap = True
label.props.width_chars = 20
window.set_default_size (500, 500)
window.connect ('destroy', lambda *ignored: gtk.main_quit ())
window.add (align)
align.add (button)
window.show_all ()
gtk.main ()
I found that in Python 3.4 GTK+3 all you need to do is add a \n where you want the break. It doesn't autowrap but you can have multiline labels for button objects. For example,
self.button = Gtk.Button(label="A lotta text \n and a lotta more.")
Related
General idea:
Many items (majority small images) are created on the canvas. The user can click on any item and move it.
I need the user to know which item was last clicked, by showing (drawing) a border/change brightness/any method.. around that item.
Is there any Image/item options to help apply this idea.
You can achieve that by writing a simple modify appearance method for a widget last clicked. Here is the sample code. Below we are performing two actions. First changing the appearance of last widget to normal and then changing the appearance of last clicked widget to highlight it.
def modifyAppearance(self, widget):
global previously_clicked
if 'previously_clicked' in globals():
# rolling back the appearance of previous widget to normal
previously_clicked['bg'] = widget['bg']
previously_clicked['activebackground'] = widget['activebackground']
previously_clicked['relief'] = widget['relief']
# changing the appearance of the last clicked widget
widget['bg'] = 'green'
widget['activebackground'] = '#33B5E5'
widget['relief'] = 'sunken'
previously_clicked = widget
You will need to define global previously_clicked in other methods also, where you will be defining the widgets. You can refer my full code here. It has this functionality
For example this is your button-
B1 = Button(root, text = "Click me", command = clickme)
we can pass more parameters here such as--
highlightcolor=
The color to use for the highlight border when the button has focus. The default is system speciific. (highlightColor/HighlightColor)
and
highlightthickness=
The width of the highlight border. The default is system specific (usually one or two pixels). (highlightThickness/HighlightThickness)
...
OR
...
Whenever the button is clicked you must be specifying some action to do in a function. What you can do is you can tell that function to slight increase the thickness of border by above parameters. :)
The button size should be controlled by packing HBox in VBox but my two buttons still have size depending on text:
first=Gtk.VBox()
second=Gtk.HBox()
third=Gtk.VBox()
fourth=Gtk.HBox()
but1=Gtk.Button(label="any title")
first.pack_start(second,False,False,0)
third.pack_start(fourth,False,False,0)
first.pack_start(but1,False,False,0)
self.data_wp="title of label"
self.label_data=Gtk.Label(label=self.data_wp)
Gtk.Widget.set_size_request(but1,85,15)
but2=Gtk.Button(label=self.data_wp)
Gtk.Container.add(but2,self.label_data)
Gtk.Widget.set_size_request(but2,85,15)
What am I doing wrong? I add button title from label but it shoudn't be the problem, and I tried to set title directly on button - nothing changed. I tried to use only one set of VBox,HBox, but it gave no effect too.
I think the problem here is that you're trying to resize the button using the set_size_request() method of the Gtk.Widget. But set_size_request() only set the minimum size of the widget, so it can still be larger.
I don't know if there is a method to resize the button, but there are the properties width-request and height-request of the Gtk.Widget that you can use.
In your code, it would be:
bt1.set_property("width-request", 85)
bt1.set_property("height-request", 15)
I can make a basic python application like so:
from tkinter import *
block = None
def moveUp(event):
field.move(block,0,-50)
root = Tk()
field = Canvas(root, width = 300, height = 300, bg = 'light blue')
field.pack()
block = field.create_rectangle(100,100,110,110)
field.bind('<Button-1>',moveUp)
mainloop()
and it will behave just like you would expect. It creates a square on a Canvas and moves that square up 50 pixels every time you click in the Canvas.
However, When I replace
field.bind('<Button-1>',moveUp)
to, for example,
field.bind('<Return>',moveUp)
the square does not move, no matter how many times I press the Enter key. This problem persists for any kind of keyboard input (e.g <space>, etc), but any input involving the mouse is fine.
Any input at all is appreciated. Thanks!
The field does not have focus, and therefore does not capture the keypress. One option is simply to make the binding more general:
field.bind('<Return>',moveUp)
to
root.bind('<Return>',moveUp)
Another option is to set the focus to the field:
field.bind('<Return>',moveUp)
field.focus_set()
Not entirely sure what's the reason, but it seems to work if you use bind_all instead of bind.
field.bind_all('<Return>',moveUp)
My guess is that using the keyboard, the canvas does not have focus and so does no register the event. Using bind_all, the event is registered when any widget of the application has focus.
See here for some information on levels of binding.
I'm using a tkinter canvas and trying to make a chat box on the right side of my game. However, I found that when I do...
import turtle
import tkinter as tk
master = tk.Tk()
w = tk.Canvas(master,width=1155,height=600,cursor='cross_reverse', bg='#101010')
shift = 1.000
sc = turtle.TurtleScreen(w)
tu = turtle.RawTurtle(sc)
e = tk.Entry(master, bg = '#000', fg = '#03f', font = 'Courier', justify='right', insertbackground = '#101010',width='115')
lb = tk.Listbox(master,height=3)
#e.grid(row=3,column=3)
sc.bgcolor("#101010")
txt = tk.Text(master,state="disabled")
txt.pack()
lb.pack()
w.pack()
sc.tracer(100)
drawcontinents() #Draws stuff with turtle, works just fine
e.pack()
tk.mainloop()
... a few things go wrong.
1.Text and Entry do not seem to want to coexist. I seem to be only able to have one or the other. My plan was to use entry as a chat entry, and display messages in Text. My backup plan is to append messages to label.
2.Text, entry, and Label box take up the entire window in whatever rows they are in, which blocks out the rest of what I am trying to draw. In other words,it puts the text box in the center, with a big gray stripe from side to side across whatever I've drawn. Is there any way to just display the box, and put it to the right?
3.Whenever I try to use the grid system, my whole computer freezes and I have to restart. Is this because the program is taking up more space than I have available, or is this a known bug or problem with installation?
You cannot use both pack and grid at the same time for the same containing widget (ie: for all widgets inside the same frame, toplevel or root window).
What happens is this: grid lays out all the widgets, potentially changing the size of some widgets based on your options (ie: it may grow a widget to stick to the sides of the cell). pack then notices that some widgets changed size in the containing widget it thinks it is responsible for, so it redoes what it thinks is the proper layout. This may change the size of some widgets based on your options. grid then notices that some widgets it thinks it is responsible for change size so it redoes what it does, potentially changing the size of some widgets. pack notices and re-adjusts, grid notices and re-adjusts, pack notices, ... until the end of time.
The solution is simple: only use grid, or only use pack, for all widgets that have a common parent. In this case, all your widgets share the root window as their parent, so they all need to use grid, or they all need to use pack.
I'm attempting to write a basic Tkinter GUI that has a Text widget at the top, then a Button widget left aligned under it, then another Text widget underneath the button. The problem I'm having is, after packing the Button widget to the left, when I then go to pack the second Text widget, it puts it next to the button on the right, rather than underneath the button. This happens regardless of what I set the side argument to for the second Text widget Here's a simple piece of code that demonstrates this behaviour:
from Tkinter import *
root = Tk()
w = Text(root)
w.pack()
x = Button(root, text="Hi there!")
x.pack(side=LEFT)
y = Text(root)
y.pack(side=BOTTOM)
root.mainloop()
So how would I go about setting up the second Text widget so that it appears below the button, rather than to the right of it?
There are generally two solutions to layout problems:
switch to using grid. It becomes real easy to do layouts like what you are trying to accomplish. Grid can solve probably 95% of all layout issues (it's amazing when you think about it -- Tk does with one manager what most toolkits need half a dozen to accomplish!)
use multiple frames. If some widgets need to be stacked top-to-bottom and some left-to-right you can't always get what you want packing everything in a single frame. Use one frame for the top-to-bottom parts of the layout and additional frames for the left-to-right content.
Also realize that widgets don't have to be children of the widget in which they are packed/gridded. You can use the "in" parameter to put widgets in some other container than their parent.
For example, in your specific example you can create three frames, top, middle, bottom. Pack these top-to-bottom in your toplevel window. Then you can pack the first text widget in the top, the button or buttons horizontally in the middle, and the other text widget in the bottom.
The advantage to such an approach is that it makes it much easier to change the layout in the future (which in my experience always happens at some point). You don't have to re-parent any of your widgets, just pack/place/grid them in some other container.
In your short example it doesn't make much difference, but for complex apps this strategy can be a life saver.
My best advice is this: layout isn't an afterthought. Do a little planning, maybe even spend five minutes drawing on some graph paper. First decide on the major regions of your app and use a frame or some other container for each (paned window, notebook, etc). Once you have those, do the same divide-and-conquer approach for each section. This lets you use different types of layout for different sections of your app. Toolbars get horizontal layout, forms might get vertical layout, etc.
I was initially misunderstanding how packing worked and didn't realise that the entire left side was being "claimed" when i did x.pack(side=LEFT). What I found after reading this and the answer by Alex here is that I was not really after having x packed to the left side at all, but rather having it anchored to the left, using anchor=W (W for West) instead of side=LEFT. My revised code snippet which does what I was after looks like this:
from tkinter import *
root = Tk()
w = Text(root)
w.pack()
x = Button(root, text="Hi there!")
x.pack(anchor=W)
y = Text(root)
y.pack(side=BOTTOM)
root.mainloop()
This way x is not "claiming" the left side anymore, it's just aligned to the left (or West) within its block of space.
Packing happens in the order the .pack methods are called, so once x has "claimed" the left side, that's it -- it will take up the left portion of its parent and everything else within its parent will be to its right. You need a Frame to "mediate", e.g....:
from Tkinter import *
root = Tk()
w = Button(root, text="Mysterious W")
w.pack()
f = Frame(root)
x = Button(f, text="Hi there!")
x.pack()
y = Button(f, text="I be Y")
y.pack(side=BOTTOM)
f.pack(side=LEFT)
root.mainloop()
(changed Texts to Buttons for more immediate visibility of layout only -- the Tkinter on this Mac doesn't show Texts clearly until they have focus, but Buttons are quite clear;-).
Do it the same way that WebView does using the Mosaic Canvas Widget Sets internals(which are very similar to Tk). The trick is that the second identical named Frame Object works as a Block Level Float(inline:block;) for everything placed after it and everything that calls "fr" already will automatically begin over inside of it.
You can have many doing this of TOP aligned widgets and simply add another identical named Frame where you want to break between side=LEFT's. Works after Bottom also.
fr=Frame(root)
fr.pack(fill=X, side=TOP)
block1=Label(fr)
block1.pack(side=LEFT)
block2=Label(fr)
block2.pack(side=LEFT)
block3=Button(fr)
block3.pack(side=LEFT)
# NAME IT THE SAME ID NAME AS THE FIRST MAIN FRAME...
fr=Frame(root)
fr.pack(fill=X, side=TOP)
# These NOW jump into the second Frame breaking the side=LEFT in new Frame
block4=Label(fr)
block4.pack(side=LEFT)
block5=Label(fr)
block5.pack(side=LEFT)
# AND THEY CONTINUE GOING side=LEFT AFTERWARDS.