This doesn't work well:
image_log = gtk.Image()
image_log.set_from_file("test.png")
self.out_button = gtk.Button()
self.out_button.add(image_log)
self.err_button = gtk.Button()
self.err_button.add(image_log)
another_box.pack_start(self.out_button, False)
another_box.pack_start(self.err_button, False)
The problem is, image_log is used twice and GTK doesn't like it. Is there some .copy() method? Or should I just use plain vanilla deepcopy?
EDIT: Looks like there is no default way to clone objects in GTK. Factory will do the trick in this case.
GTK warning:
app/gui.py:248: GtkWarning: gtk_box_pack: assertion `child->parent == NULL' failed
hbox_errlog.pack_start(image_log)
You could use a factory function to reduce code duplication
def make_image_from_file(fname):
im = gtk.Image()
im.set_from_file(fname)
return im
self.out_button.set_image(make_image_from_file(..))
Revisiting
There is a much more natural way. You will like it. In PyGTK 2.12+:
gtk.image_new_from_file(filename)
I had something in the back of my mind telling me this, but I didn't look it up.
http://www.pygtk.org/docs/pygtk/class-gtkimage.html#function-gtk--image-new-from-file
Use
def clone_widget(widget):
widget2=widget.__class__()
for prop in dir(widget):
if prop.startswith("set_") and prop not in ["set_buffer"]:
prop_value=None
try:
prop_value=getattr(widget, prop.replace("set_","get_") )()
except:
try:
prop_value=getattr(widget, prop.replace("set_","") )
except:
continue
if prop_value == None:
continue
try:
getattr(widget2, prop)( prop_value )
except:
pass
return widget2
All this try ... except blocks are there because not all properties could be copied by using
set_prop(get_prop). I haven't tested this for all properties and widgets yet, but it worked well for gtkEntry. Maybe this is slow, but it's nice the use :)
Why not
image_log = gtk.Image()
image_log.set_from_file("test.png")
image_logb = gtk.Image()
image_logb.set_from_file("test.png")
self.out_button = gtk.Button()
self.out_button.add(image_log)
self.err_button = gtk.Button()
self.err_button.add(image_logb)
another_box.pack_start(self.out_button, False)
another_box.pack_start(self.err_button, False)
It is only an extra 2 lines of code, and maybe more efficient than cloning/copying the first image object.
That way you can treat out_button and err_button independently. But it should make sense to use the same gtk.Image() object for both buttons ... it is just an image.
Edit
To avoid duplication (seems like overkill though) you could write a factory for gtk.Image() objects from the same image.
def gtkimage_factory(num_objs, image_file):
i=0
imglist = []
while i<num_objs:
img_ob = gtk.Image()
img_ob.set_from_file(image_file)
imglist.append( img_ob )
i+=1
return imglist
Or something along those lines, you get the idea. But a factory seems like overkill unless you are producing loads of these things and need them independently parented in GTK.
Then...
image_list = gtkimg_factory(2, "test.png")
self.out_button = gtk.Button()
self.out_button.add(image_list[0])
self.err_button = gtk.Button()
self.err_button.add(image_list[1])
another_box.pack_start(self.out_button, False)
another_box.pack_start(self.err_button, False)
Maybe it is something to do with GTK resource management?
all if you want to use the collection of widgets arranged in some fashion, again and again, let's say a box with one entry box and a label(can be complex as hell if you want) and want to use it multiple time in your application like depending upon condition how many similar tabs are needed but obviously working with different data that use composites with glade.
With pygi(gi_composites) python library you can make your own widgets and use them multiple times.
[https://github.com/virtuald/pygi-composite-templates][1]
Related
I have a bunch of screens in my app that all have the same icon that I always want to change together. So I currently have it hard coded and everytime I add a new screen I have to add a new line and it's getting cumbersome:
self.tcs_screen.ids.statusicon.source = "/imgs/..."
self.eclss_screen.ids.statusicon.source = "/imgs/..."
self.gnc_screen.ids.statusicon.source = "/imgs/..."
...
Is it possible to do this from a loop over a list of the screens? I've been trying the following with no success (how do you insert a variable into a property assignment?):
ScreenList = ['tcs_screen', 'eclss_screen', 'gnc_screen']
for x in xrange(len(ScreenList)):
self.ScreenList[x].ids.statusicon.source = "/imgs/..."
Or is there a better way to accomplish this?
Thanks
You have to use getattr() to get the property using the name.
screen_list = ['tcs_screen', 'eclss_screen', 'gnc_screen']
for e in screen_list:
getattr(self, e).ids.statusicon.source = "/imgs/..."
I am beginning to work on a program in which i want multilingual support, but since it is pretty modular (and i want it to be even more in the future), a language change means "destroy what you had of interface and build again with the content which language modules have". (You can see the source as of now on GitHub)
This full-modular approach may give many problems, but i still want it, and so the problem is: Whenever i destroy the widgets i had, until i am alone with the raw Gtk.Window itself, i am not able to assign once again widgets to it. They won't get displayed at all, sometimes silently, sometimes with errors depending on my approach.
Lets suppose the class window, which inherits from a Gtk.Window.
This class is the raw window, and i assign to it a Gtk.Box -
self.interface.
self.interface itself, has two Gtk.Box's, one sidebar and one stack of contents.
To rebuild i tried to:
Change the language variable
Use the method destroy on self.interface, which removes the widget and its child's.
Reuse the function to build the widgets stack on top of self.interface
Re-add self.interface to self (window).
This approach returns:
g_object_unref: assertion 'G_IS_OBJECT (object)' failed
Gtk.main()
Pointing to the .show_all() method in this file.
I've already tried to leave interface without using .destroy on it, applying only on its child's, and then creating them again over it, but didn't worked. The window was left blank with no error at all.
The code i am trying right now is:
#Remember, self is a Gtk.Window()
def __init__(self):
[...]
self.interface = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
self.stack = None
self.add(interface)
self.build_interface()
def build_interface(self):
self.interface.pack_start(
self.create_side_navigation(
self.interface_data["menu"][self.language]["name"])
, False, False, 0
)
self.stack = self.create_content_stack(self.interface_data["menu"][self.language])
self.interface.pack_start(self.stack, True, True, 0)
###Code to rebuild(this is a question dialog):
if response == Gtk.ResponseType.OK:
self.language = self.new_language["Index"]
self.new_language = None
self.stack.destroy()
self.interface.destroy()
self.interface = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
self.build_interface()
self.add(self.interface)
This code will cause the previously told "g_object_unref" error.
What is causing this? Why can't i add anything once deleted?
Feel free to clone the repo and try, the code is well commented(yet i am not sure if poorly written, i am a python newbie) and its quite easy to understand where is the problematic part. Its not too big.
PS: It should need GTK+3.12 because of the popovers.
As a GTK Dev showed to me, GTK has by default all the widgets invisible.
The error was caused in the line which declared the whole interface visibility (windowclass.show_all()), but since the interface changed since when it was applied, it threw that warning.
He pointed me to .remove() instead of .destroy(), and to set .show_all() to the new elements after set up.
The next commit(or the following) on that git, has the solution.
The best way to be multilingual is to keep your widgets the same and merely change the text of labels and titles. This can be done without disturbing the overall setup. For example:
s='Stop'
if lang=='fr': s='Arret'
...
somelabel.set_label(s)
I'm in the process of porting an application from PyGTK to PyGObject. Mostly it's going well because mostly I did conventional things with PyGTK. But there's one somewhat ugly hack I was using to display the value of a SpinButton as currency (with a $ in front of it).
I originally got this solution from the PyGTK mailing list back in the days before Stack Overflow. As you can see, the magic happens on the input and output signals:
import gtk, ctypes
def _currency_input(spinbutton, gpointer):
text = spinbutton.get_text()
if text.startswith('$'):
text = text[1:]
double = ctypes.c_double.from_address(hash(gpointer))
double.value = float(text)
return True
def _currency_output(spinbutton):
text = '$%.*f' % (int(spinbutton.props.digits),
spinbutton.props.adjustment.value)
spinbutton.set_text(text)
return True
def format_spinbutton_currency(spinbutton):
spinbutton.connect('input', _currency_input)
spinbutton.connect('output', _currency_output)
def _test():
s = gtk.SpinButton(gtk.Adjustment(value=1, lower=0, upper=1000,
step_incr=1))
s.props.digits = 2
format_spinbutton_currency(s)
w = gtk.Window()
w.props.border_width = 12
w.add(s)
w.show_all()
w.connect('destroy', gtk.main_quit)
gtk.main()
if __name__ == '__main__':
_test()
Doing my best to translate that into PyGObject, I came up with:
from gi.repository import Gtk
import ctypes
def _currency_input(spinbutton, gpointer):
text = spinbutton.get_text()
if text.startswith('$'):
text = text[1:]
double = ctypes.c_double.from_address(hash(gpointer))
double.value = float(text)
return True
def _currency_output(spinbutton):
text = '$%.*f' % (int(spinbutton.props.digits),
spinbutton.get_value())
spinbutton.set_text(text)
return True
def format_spinbutton_currency(spinbutton):
spinbutton.connect('input', _currency_input)
spinbutton.connect('output', _currency_output)
def _test():
s = Gtk.SpinButton()
s.set_adjustment(Gtk.Adjustment(value=1, lower=0, upper=1000,
step_increment=1))
s.props.digits = 2
format_spinbutton_currency(s)
w = Gtk.Window()
w.props.border_width = 12
w.add(s)
w.show_all()
w.connect('destroy', Gtk.main_quit)
Gtk.main()
if __name__ == '__main__':
_test()
Unfortunately, this doesn't work. It shows up fine initially, but when I click the up or down error, it crashes and I see:
/usr/lib/python2.7/dist-packages/gi/types.py:43: Warning: g_value_get_double: assertion `G_VALUE_HOLDS_DOUBLE (value)' failed
return info.invoke(*args, **kwargs)
Segmentation fault
Any idea what this error message means?
Or what part of my code might not work under PyGObject?
Or, better yet, how to fix this error?
Or, even better still, a more straightforward solution to my original problem (displaying a $ in front of the SpinButton contents)?
From the PyGtk documentation:
http://developer.gnome.org/pygtk/stable/class-gtkspinbutton.html#signal-gtkspinbutton--input
The "input" signal is emitted when the value changes. The value_ptr is a GPointer to the value that cannot be accessed from PyGTK. This signal cannot be handled in PyGTK.
So, I'm atonished to see something like:
double = ctypes.c_double.from_address(hash(gpointer))
This is a real hack, so you got and awful error "Segmentation Fault" which means your are messing in memory you don't have to, and it's quite generic, it happens for example when in C you try to manually access a memory pointer not handled by your application.
This will be a hard one, I tried for one hour and all approaches I tried had problems. I know is not and answer, but as a workaround and if you only need the currency symbol (not grouping) you can experiment adding a currency symbol image to the inherited Gtk.Entry set_icon_from_pixbuf():
(Obviously set the image to a currency image)
Kind regards
Or, even better still, a more straightforward solution to my original problem (displaying a $ in front of the SpinButton contents)?
I got this working by hooking up the GTKSpinButton's output signal to a simple handler:
def onSpinOutput(self, spinbutton):
adjustment = spinbutton.get_adjustment()
spinbutton.set_text(str(int(adjustment.get_value())) + "%")
return True
In my case I'm adding a percentage sign after the number, but you can easily change this to insert something else in front instead. Note that this sets the text and not the value of the entry; this will not work for numerical spin buttons.
When using gtk.AccelGroup any combination with Tab charater is invalid. Now I do understand that UI navigation is done using this key but in some special cases I need to override that behavior. Is there a way to make AccelGroup allow usage of this key?
For example:
group = gtk.AccelGroup()
group.connect(gtk.gdk.keyval_from_name('Tab'), gtk.gdk.CONTROL_MASK, 0, callback)
You can easily get key names and values with this :
#!/usr/bin/env python
import gtk
import gtk
def catch_button(window, event, label):
keyval = event.keyval
name = gtk.gdk.keyval_name(keyval)
mod = gtk.accelerator_get_label(keyval, event.state)
label.set_markup('<span size="xx-large">%s\n%d</span>'% (mod, keyval))
window = gtk.Window()
window.set_size_request(640,480)
label = gtk.Label()
label.set_use_markup(True)
window.connect('key-press-event',catch_button, label)
window.connect('destroy', gtk.main_quit)
window.add(label)
window.show_all()
gtk.main()
But I found that the keynames returned were locale-dependent, of no great use for me. The keyval can probably be used.
Cheers,
Louis
This below is one way to do it. Although if you don't wish for the program to listen for every keypress as you stated above, I should say that I've never run across a way of tying Tab to an AccelGroup. I've tried various things myself, but to no avail.
widget.connect("key-press-event",self.on_key_pressed)
def on_key_pressed(self,widget,event,*args):
if event.keyval == gtk.keysyms.Tab:
do_something()
So I'm playing around with Jython, trying to slap together a generic GUI. Nothing beyond what they have on the Jython Wiki for swing examples. So I declare a JFrame, and then try to add a panel, some text fields, all that good stuff. I get this error when I run it, however. "'NoneType' object has no attribute 'add'"
Here's the basic code I have.
from javax.swing import *
frame = JFrame('E-mail Gathering', defaultCloseOperation = JFrame.EXIT_ON_CLOSE, size =(600,400), locationRelativeTo = None).setVisible(True)
pnl = JPanel()
frame.add(pnl)
self.textfield1 = JTextField('username:',15)
pnl.add(self.textfield1)
self.textfield2 = JTextField('password:', 15)
pnl.add(self.textfield2)
mailButton = JButton('Login',actionPerformed=self.checkmail)
pnl.add(mailButton)
frame.pack()
frame.setVisible(True)
I know the relevant part where it's crashing is at 'frame.add(pnl)' with the aforementioned error. I figured I'd throw the rest up there just in case I'm making some even greater mistakes. I feel like something's wrong where I'm not declaring frame as a JFrame properly, but I know that's not the case because it creates the frame just fine if I don't try to add anything to it.
Thanks for any advice or suggestions you have.
In this line:
frame = JFrame('E-mail Gathering', defaultCloseOperation = JFrame.EXIT_ON_CLOSE, size =(600,400), locationRelativeTo = None).setVisible(True)
you are creating a JFrame, calling its setVisible method, and assigning the return value of setVisible to frame. setVisible doesn't return a value, so frame is None. This causes frame.add to fail.
As you call setVisible at the end anyway, and because you probably don't want to make the frame visible before you have added other components to it and called pack, just remove the setVisible call:
frame = JFrame('E-mail Gathering', defaultCloseOperation = JFrame.EXIT_ON_CLOSE, size =(600,400), locationRelativeTo = None)