I'm currently developing a PyGObject app and I'm having issues selecting specific children in a Gtk+ FlowBox. Even after selecting the FlowBox selection mode (SINGLE) populating the FlowBox and writing code to select a specific child, the first child is always selected.
#!/usr/bin/python
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gio
class App(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self, title="App")
flowbox = Gtk.FlowBox()
flowbox.set_valign(Gtk.Align.START)
flowbox.set_selection_mode(Gtk.SelectionMode.SINGLE)
# Drawing 3 squares
flowbox.add(self.drawing_area())
flowbox.add(self.drawing_area())
flowbox.add(self.drawing_area())
child = flowbox.get_child_at_index(2)
flowbox.select_child(child)
flowbox.queue_draw()
self.add(flowbox)
def drawing_area(self):
preview = Gtk.DrawingArea()
preview.connect("draw", self.draw_square)
preview.set_size_request(150, 150)
return preview
def draw_square(self, widget, cr):
cr.scale(150, 150)
style_context = widget.get_style_context()
color = style_context.get_color(Gtk.StateFlags.NORMAL)
cr.set_source_rgba(*color)
cr.rectangle(0, 0, 1, 1)
cr.fill()
window = App()
window.connect("delete-event", Gtk.main_quit)
window.show_all()
Gtk.main()
Even though I choose to select the child at index 2, the app only ever shows the first child being selected:
Screenshot of above code running
The strange part is that when I check to see which child is selected using the following code (placed before the "self.add(flowbox)" line), the Terminal displays that the child I specified to be selected (at index 2) is the only selected child, even though the window only shows the first child being selected:
for child in flowbox.get_selected_children():
print child.get_index()
I think you have located a bug in GTK, it seems that something in show_all is messing up. My first guess was that it was caused by the fact that the FlowBox wasn't realized so I changed your code to use the show signal (realize but show is emitted later) and checked whether it still happend. Sadly it was..
So I got the feeling that something else was off so just a quick test added self.show() right after Gtk.Window.__init__ this made the selection work but made the Flowbox wider than needed (probably because of the default width of a empty window). So I added the self.show() in the listener and this actually solved the issue.
The complete code is as follows, but as it is a dirty workaround you should still report this bug.
#!/usr/bin/python
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gio
class App(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self, title="App")
self.flowbox = Gtk.FlowBox()
self.flowbox.set_valign(Gtk.Align.START)
self.flowbox.set_selection_mode(Gtk.SelectionMode.SINGLE)
# Drawing 3 squares
self.flowbox.add(self.drawing_area())
self.flowbox.add(self.drawing_area())
self.flowbox.add(self.drawing_area())
self.flowbox.connect("show", self.on_realize)
self.add(self.flowbox)
def on_realize(self, flowbox):
# The creative workaround/hack
self.show()
child = self.flowbox.get_child_at_index(2)
self.flowbox.select_child(child)
def drawing_area(self):
preview = Gtk.DrawingArea()
preview.connect("draw", self.draw_square)
preview.set_size_request(150, 150)
return preview
def draw_square(self, widget, cr):
cr.scale(150, 150)
style_context = widget.get_style_context()
color = style_context.get_color(Gtk.StateFlags.NORMAL)
cr.set_source_rgba(*color)
cr.rectangle(0, 0, 1, 1)
cr.fill()
window = App()
window.connect("delete-event", Gtk.main_quit)
window.show_all()
Gtk.main()
Related
I'm making an application that can change certain parameters through a Gtk.Menu. I have a Gtk.MenuButton that pops down a Gtk.Menu with other submenus in it. One of those submenus has Gtk.MenuItems with Gtk.SpinButtons in them. I have gotten the Gtk.SpinButtons to receive input by bringing their associated Gdk.Windows to the front of the Z-order with Gdk.Window.show(), but I can't get the Gtk.Entry part of the Gtk.SpinButton to receive keyboard focus.
I have tried to use Gtk.Widget.grab_focus() and other related methods to no avail. It does highlight the Gtk.Entry text, and I can type in new text, but if I click away or press enter, it doesn't actually update/change the Gtk.SpinButton value. I have connected to the "change-value" and "value-changed" signals but typing anything into the Gtk.SpinButton doesn't fire them.
I've found out that a widget can be the "focus widget" but not have the "global input focus" if its toplevel Gtk.Window doesn't also have the global focus. Therefore, I'm stuck. Is there any way around this? Can I make the Gtk.SpinButton entry field have the keyboard focus if it's in a Gtk.Menu?
Here is a minimal example:
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk
class Menu(Gtk.Menu):
def __init__(self):
Gtk.Menu.__init__(self)
menu_item = Gtk.MenuItem(label="Submenu")
menu_item2 = Gtk.MenuItem(label="Item")
self.append(menu_item)
self.append(menu_item2)
submenu = Gtk.Menu()
sub_mi = Gtk.MenuItem()
spin_button = Gtk.SpinButton()
spin_adj = Gtk.Adjustment(value=0,lower=0, upper=10, step_increment=1)
spin_button.set_adjustment(spin_adj)
sub_mi.add(spin_button)
submenu.append(sub_mi)
menu_item.set_submenu(submenu)
spin_button.connect("realize", self.on_realize)
spin_button.connect("map-event", self.on_map_event)
submenu.connect("button-release-event", self.on_button_release)
submenu.connect("enter-notify-event", self.on_enter)
self.show_all()
def on_realize(self, spin_button):
spin_button.add_events(Gdk.EventMask.STRUCTURE_MASK)
def on_map_event(self, spin_button, event):
for win in spin_button.get_window().get_children():
win.show()
def on_button_release(self, menu, event):
return True
def on_enter(self, menu, event):
mouse = event.get_device()
mouse.ungrab(event.time)
win = Gtk.Window()
win.set_default_size(100, 20)
win.connect("destroy", Gtk.main_quit)
mb = Gtk.MenuButton()
win.add(mb)
menu = Menu()
mb.set_popup(menu)
win.show_all()
Gtk.main()
I realize this is convoluted, and there's probably a better solution (like not using a Gtk.Menu for this...), but I've come up with a workaround anyway.
Since I can't activate or fully focus the Gtk.SpinButton I am just forcing it to update the text field whenever there is a leave-notify-event fired from it.
spin_button.connect("leave-notify-event", self.on_leave)
def on_leave(self, spin_button, event):
value = spin_button.get_text()
spin_button.set_value(float(value))
spin_button.update()
I'm working on a gtk3 front end for libvlc written in python using python-vlc. I'm following the gtk3 example from the python-vlc github page, but am experiencing strange behavior. I have a widget that looks like that:
import gi
import sys
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
class PlayerWidget(Gtk.DrawingArea):
__gtype_name__ = 'VLCWidget'
def __init__(self, instance):
Gtk.DrawingArea.__init__(self)
self.player = instance.media_player_new()
def handle_embed(*args):
if sys.platform == 'win32':
self.player.set_hwnd(self.get_window().get_handle())
else:
self.player.set_xwindow(self.get_window().get_xid())
return True
self.connect("realize", handle_embed)
self.set_size_request(320, 200)
I embed it here:
import vlc
import sys
from widgets.player import PlayerWidget
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
class VideoPlayer(Gtk.Window):
CONST_APP_NAME = "video player"
def __init__(self):
Gtk.Window.__init__(self)
if 'linux' in sys.platform:
self.instance = vlc.Instance("--no-xlib")
else:
self.instance = vlc.Instance()
self.set_default_size(800, 600)
header = Gtk.HeaderBar(title=self.CONST_APP_NAME)
header.set_subtitle("Filename.mp4")
header.set_show_close_button(True) # this one is the troublemaker
self.set_titlebar(header)
self.connect("destroy", Gtk.main_quit)
self.player_widget = PlayerWidget(self.instance)
self.add(self.player_widget)
def show_window(self):
self.show_all()
Gtk.main()
def set_media(self, fname):
self.player_widget.player.set_media(self.instance.media_new(fname))
def play(self):
self.player_widget.play()
if not len(sys.argv) > 0:
print('Please provide a filename')
sys.exit(1)
p = VideoPlayer()
p.set_media(sys.argv[1])
p.play()
p.show_window()
p.instance.release()
It works fine if I embed it into an empty Gtk.window. If, however, I add a HeaderBar to that window as well and then add a close button to that HeaderBar using set_show_close_button(True) it stops working as expected. The PlayerWidget will not be shown embedded anymore, but instead a new (second) window will be opened where the video is played. If I do not add the close button to the HeaderBar the widget gets embedded just fine.
A warning is thrown to the console: xcb_window window error: X server failure
I first thought it could be because I use gnome under wayland, but it occurs on X as well as on wayland.
Any help is appreciated.
Update 1: Added full code example. When I ran it today, the first time it actually worked as expected, but after that the same bug as described above occured again. Very weird.
As #mtz and #stovfl correctly pointed out, the problem was that I started the video playback (p.play()) before creating the window (p.show_window()).
As suggested I used GLib.idle_add(p.play) to let the window start the playback once it's ready. The GLib module can be imported using from gi.repository import GLib.
Can anyone tell me how I can have a preference pop-up stay centered on parent window even while moving the parent window? Thank you :)
Here is a very simple example, the preference window is centered and while moving it the parent will also move (but will depend on window manager):
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
class AppWindow(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self, title="Python Example")
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL,spacing=6)
self.add(box)
button1 = Gtk.Button("Information")
button1.connect("clicked", self.on_button_clicked)
box.add(button1)
treeview = Gtk.TreeView()
treeview.set_size_request(640,480)
box.add(treeview)
def on_button_clicked(self, widget):
preferences_window = Gtk.Window (Gtk.WindowType.TOPLEVEL)
preferences_window.set_title ("Preferences Window")
preferences_window.set_modal (True)
preferences_window.set_position(Gtk.WindowPosition.CENTER_ON_PARENT)
preferences_window.set_transient_for(self)
preferences_window.show_all()
preferences_window.connect("delete-event", preferences_window.destroy);
win = AppWindow()
win.connect("delete-event", Gtk.main_quit)
win.show_all()
Gtk.main()
Resulting in:
I am working on a Gtk3 app written in Python. The main window for my app is set up as follows:
#!/bin/python
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk as Gtk
## OTHER IMPORTS
class MainGui(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self, title="APP TITLE")
# Defaults
self.set_default_size(600, 500)
## OTHER CODE
# Setup the Window
self.connect("destroy", self.on_close)
self.show_all()
## OTHER CODE
def on_close(self, widget):
if self.editor.get_document().get_has_changes():
save_dialog = Gtk.MessageDialog(self, 0,
Gtk.MessageType.QUESTION,
Gtk.ButtonsType.YES_NO,
"Save changes?")
response = save_dialog.run()
## REST OF DIALOG HANDELING
The problem I'm having is related to the save dialog. The app displays the dialog just fine, but it hides my main window, which is not the desired effect. I've tried searching around for a solution, but can't seem to figure out what I'm doing wrong. Any help would be greatly appreciated!
Shortly after posting this I realized that the reason things weren't working is because of a bone-headed mistake. I was hooking up my on_close method using this:
self.connect("destroy", self.on_close)
It turns out I should be doing it this way:
self.connect("delete-event", self.on_close)
Now things work great.
How do I get Gtk.scrolledwindow to scroll to a selection in Gtk.Treeview.
I am writing a touch screen kiosk app that has up and down button to move a selection in a treeview.
It doesn't it doesn't scroll down the scrolledwindow when the selection goes off the screen.
My idea to get around this is when the down button is pressed for the selection to move down one (as it already does) and then for the scrolledwindow to scroll to the selection on treeview but I'm unable to work out how.
I'm using Gtk3
Can anyone give me any ideas?
See: http://lazka.github.io/pgi-docs/Gtk-3.0/classes/TreeView.html#Gtk.TreeView.scroll_to_cell
Do not add you treeview to the scrolled window with "add_with_viewport". See http://mailman.daa.com.au/cgi-bin/pipermail/pygtk/2009-January/016440.html
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
class MyWindow(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self, title="Auto Scroll")
self.set_size_request(400, 200)
self.liststore = Gtk.ListStore(str, str)
for n in range(40):
self.liststore.append(["Info", "http://lazka.github.io/pgi-docs/Gtk-3.0/classes/TreeView.html"])
treeview = Gtk.TreeView(model=self.liststore)
for n, name in enumerate(["Name", "Link"]):
renderer_text = Gtk.CellRendererText()
column_text = Gtk.TreeViewColumn("Text", renderer_text, text=n)
treeview.append_column(column_text)
scrolled_window = Gtk.ScrolledWindow()
self.add(scrolled_window)
scrolled_window.add(treeview)
def main(self):
Gtk.main
win = MyWindow()
win.connect("delete-event", Gtk.main_quit)
win.show_all()
Gtk.main()
After you've moved the selection call gtk_tree_view_scroll_to_cell on the selected path.