How can I create a GtkImage from a GIcon witha a fallback? - gtk

I have a Gio.Icon (or GIcon in C, I'm using pygobject). Right now I'm using the following code to create a Gtk.Image from the Gio.Icon:
image = icon and Gtk.Image(gicon=icon, icon_size=Gtk.IconSize.DIALOG, pixel_size=48, use_fallback=True)
The problem is, that the Gio.Icon isn't garanteed to have a valid icon name/path and when it doesn't it shows a broken image icon. I would like to fall back to using a different icon I know exists if the supplied Gio.Icon is invalid. Is there some way to know if the Gio.Icon is invalid, or if the Gtk.Image would show as a broken image?
EDIT
A minimal example:
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gio
win = Gtk.Window()
win.connect('destroy', Gtk.main_quit)
icon = Gio.Icon.new_for_string('gnome-garbage')
fallback_icon = Gio.Icon.new_for_string('folder')
image = Gtk.Image(gicon=icon, icon_size=Gtk.IconSize.DIALOG, pixel_size=48)
win.add(image)
win.show_all()
Gtk.main()

I found an answer in the GtkImage documentation:
If the file isn’t loaded successfully, the image will contain a “broken image” icon similar to that used in many web browsers. If you want to handle errors in loading the file yourself, for example by displaying an error message, then load the image with gdk_pixbuf_new_from_file(), then create the GtkImage with gtk_image_new_from_pixbuf().
Although, in my case I actually need to use a GtkIconTheme to get a PixBuf instead of gdk_pixbuf_new_from_file:
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gio
win = Gtk.Window()
win.connect('destroy', Gtk.main_quit)
def load_icon(icon):
info = Gtk.IconTheme.get_default().lookup_by_gicon(icon, 48, Gtk.IconLookupFlags.FORCE_SIZE)
if info:
return info.load_icon()
icon = Gio.Icon.new_for_string('gnome-garbage')
fallback_icon = Gio.Icon.new_for_string('folder')
pixbuf = load_icon(icon) or load_icon(fallback_icon)
image = Gtk.Image.new_from_pixbuf(pixbuf)
win.add(image)
win.show_all()
Gtk.main()

Related

Editable label in Python GTK+ 3?

I'm new to GTK programming. I want to have a Label widget whose text can be edited, kind of like this: https://docs.gtk.org/gtk4/class.EditableLabel.html.
The problem is I have no idea how to implement this. I understand that Gtk.Button has a set_label() function, though I don't know how to use it to make an editable label.
You can do that with Gtk.Entry which you can set_editable based on "something". Example with check button would look something like this:
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk
def on_editable_toggled(button, entry):
value = button.get_active()
entry.set_editable(value)
entry.set_sensitive(value)
win = Gtk.Window()
win.connect("destroy", Gtk.main_quit)
vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
win.add(vbox)
entry = Gtk.Entry()
entry.set_text("Hello World")
vbox.pack_start(entry, True, True, 0)
check_editable = Gtk.CheckButton(label="Editable")
check_editable.connect("toggled", on_editable_toggled, entry)
check_editable.set_active(True)
vbox.pack_start(check_editable, True, True, 0)
win.show_all()
Gtk.main()
It doesn't really look like a label, but you can use the Gtk CSS styling to change the background and border colors to make it look like one when it is set to non-editable.

is there a way to take numerical inputs in gtk+, pygobject?

I need to implement a input dialog box that takes in numerical values. How is it done in Pygobject?. It is similar to excel taking numerical inputs.
If you're looking for simple numerical values, you can use Gtk.SpinButton.
The more general way getting input from the user in the UI is Gtk.Entry. That widget does not support built-in validation, but it can be trivially implemented, similarly as shown in this stackoverflow answer (by overriding the insert_text method).
If you want to show validation errors to your user, you can use either CSS styling, or for example set the secondary icon (with e.g. the secondary_icon_name property) to a warning sign, with an accompanying label explaining what is wrong.
With some help from Ricardo Silva Veloso, this is basically the code I was suggesting to write in my first comment (also considering #nielsdg secondary icon suggestion):
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk
class MyEntry(Gtk.Entry, Gtk.Editable):
__gtype_name__ = 'MyEntry'
def __init__(self, **kwargs):
super(MyEntry, self).__init__(**kwargs)
def do_insert_text(self, text, length, position):
if text.isnumeric():
self.props.secondary_icon_name = None
self.get_buffer().insert_text(position, text, length)
return length + position
else:
self.props.secondary_icon_name = "dialog-error"
self.props.secondary_icon_tooltip_text = "Only numbers are allowed"
return position
win = Gtk.Window()
myentry = MyEntry()
win.add(myentry)
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()

Deploy Bokeh Document to Server

I am following the example code I've attached below. It currently displays the plot and associated slider embedded in the doc returned by the "modify_doc" function in the notebook. However, I'd like to deploy it into it's own server to make a GUI, while still maintaining it's ability to run the callback when the slider is changed and update the plot. However when I try to use Panel Pyviz to deploy it, it just displays the message "< bokeh.document.document.Document object at 0x00000193EC5FFE80 >" on the server that pops up. How do I deploy the doc in a way that will display the image?
import numpy as np
import holoviews as hv
from bokeh.io import show, curdoc
from bokeh.layouts import layout
from bokeh.models import Slider, Button
renderer = hv.renderer('bokeh').instance(mode='server')
# Create the holoviews app again
def sine(phase):
xs = np.linspace(0, np.pi*4)
return hv.Curve((xs, np.sin(xs+phase))).opts(width=800)
stream = hv.streams.Stream.define('Phase', phase=0.)()
dmap = hv.DynamicMap(sine, streams=[stream])
# Define valid function for FunctionHandler
# when deploying as script, simply attach to curdoc
def modify_doc(doc):
# Create HoloViews plot and attach the document
hvplot = renderer.get_plot(dmap, doc)
# Create a slider
def slider_update(attrname, old, new):
# Notify the HoloViews stream of the slider update
stream.event(phase=new)
start, end = 0, np.pi*2
slider = Slider(start=start, end=end, value=start, step=0.2, title="Phase")
slider.on_change('value', slider_update)
# Combine the holoviews plot and widgets in a layout
plot = layout([
[hvplot.state],
[slider]], sizing_mode='fixed')
doc.add_root(plot)
return doc
# To display in the notebook
show(modify_doc, notebook_url='localhost:8888')
# To display in a script
doc = modify_doc(curdoc())
# To deploy to separate server using Panel (attempt, doesn't work. Just displays #"<bokeh.document.document.Document object at 0x00000193EC5FFE80>":
graph = pn.Row (doc)
graph.show()
There is some mixing of very different APIs going on in your example. Panel is meant to simplify some of Bokeh's APIs so a lot of what you're doing here isn't necessary. That said I'll provide a few versions which are either using only Panel components or combining Panel and bokeh components.
To start with this is how I might write this example using just Panel:
import numpy as np
import panel as pn
import holoviews as hv
pn.extension()
start, end = 0, np.pi*2
slider = pn.widgets.FloatSlider(start=start, end=end, value=start, step=0.2, name="Phase")
#pn.depends(phase=slider.param.value)
def sine(phase):
xs = np.linspace(0, np.pi*4)
return hv.Curve((xs, np.sin(xs+phase))).opts(width=800)
dmap = hv.DynamicMap(sine)
row = pn.Row(dmap, slider)
# Show in notebook
row.app('localhost:8888')
# Open a server
row.show()
# To deploy this using `panel serve` or `bokeh serve`
row.servable()
In your example you instead use various bokeh components, this is also possible and might be desirable if you've already got bokeh code.
import numpy as np
import holoviews as hv
import panel as pn
from bokeh.models import Slider, Button
# Create the holoviews app again
def sine(phase):
xs = np.linspace(0, np.pi*4)
return hv.Curve((xs, np.sin(xs+phase))).opts(width=800)
stream = hv.streams.Stream.define('Phase', phase=0.)()
dmap = hv.DynamicMap(sine, streams=[stream])
def slider_update(attrname, old, new):
# Notify the HoloViews stream of the slider update
stream.event(phase=new)
start, end = 0, np.pi*2
slider = Slider(start=start, end=end, value=start, step=0.2, title="Phase")
slider.on_change('value', slider_update)
graph = pn.Row(dmap, slider)
# Show in notebook
row.app('localhost:8888')
# Open a server
row.show()
# To deploy this using `panel serve` or `bokeh serve`
row.servable()
If you want to serve these apps to multiple people I'd definitely recommend using panel serve but if you really want to make a script you can run with python script.py you should do this:
def app():
start, end = 0, np.pi*2
slider = pn.widgets.FloatSlider(start=start, end=end, value=start, step=0.2, name="Phase")
#pn.depends(phase=slider.param.value)
def sine(phase):
xs = np.linspace(0, np.pi*4)
return hv.Curve((xs, np.sin(xs+phase))).opts(width=800)
dmap = hv.DynamicMap(sine)
return pn.Row(dmap, slider)
pn.serve({'/': app})
This requires latest Panel, but ensures that even if you run the app as a script that each user gets a new instance of the app which does not share state with all others.

How to align widget buttons in IPython notebook

I have the following peace of code
from ipywidgets import widgets
from IPython.display import display
import numpy as np
class Test(object):
def __init__(self, arraylen):
self.a = np.random.randn(arraylen)
self.button = widgets.Button(description = 'Show')
self.button.on_click(self.show)
display(self.button)
self.button1 = widgets.Button(description = 'Show1')
self.button1.on_click(self.show)
display(self.button1)
def show(self, ev = None):
np.savetxt('test',self.a)
self.button.disabled = True
test = Test(10)
The output is two buttons in a column as shown here:
Would it also be possible, to embed the buttons in a HTML table (where I could choose them to be in a row), or any other organization scheme? Maybe it could look like this:
You could use (a very basic example):
from IPython.display import display
from ipywidgets import widgets
button1 = widgets.Button(description = 'Button1')
button2 = widgets.Button(description = 'Button2')
display(widgets.HBox((button1, button2)))
Here you can find basic and more complete examples about the use of the widgets. If you are using Jupyter you should adapt some info (from ipywidgets import widgets instead from IPython.html import widgets,...).

pygtk menubar with fixed

I'm new to pyGTK, and now I'm trying to create a menubar with a fixed layout, but I only get a background on the items, not on the entire bar. My code:
import gtk
class App(gtk.Window):
def __init__(self):
super(App,self).__init__()
self.set_size_request(640,480)
self.set_position(gtk.WIN_POS_CENTER)
menubar = gtk.MenuBar()
menu_file= gtk.Menu()
menuitem_file = gtk.MenuItem("File")
menuitem_file.set_submenu(menu_file)
menuitem_exit = gtk.MenuItem("Exit")
menuitem_exit.connect("activate",gtk.main_quit)
menu_file.append(menuitem_exit)
menubar.append(menuitem_file)
fixed = gtk.Fixed()
vbox = gtk.VBox(False, 2)
vbox.pack_start(menubar, False, False, 0)
fixed.add(vbox)
self.add(fixed)
self.connect("destroy",gtk.main_quit)
self.show_all()
App ()
gtk.main ()
You need to make vbox request size, e.g. add vbox.set_size_request (300,50) and see the difference. It is not correct size, but then I don't know why you use gtk.Fixed at all. In 99.95% of case you don't need gtk.Fixed. And especially if you are new to GTK+ you might think you need it while you actually don't.