Reputation: 3218
I am very new in gtk3, and possibly messed up my python as well.
In my menu.py
, I have defined my menubar in xml:
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, Gdk, Pango
UI_INFO = """
<ui>
<menubar name='MenuBar'>
<menu action='FileNew'>
<menuitem action='FileNewStandard' />
<menuitem action='FileOpenStandard' />
<menuitem action='FileQuit' />
</menu>
<menu action='EditMenu'>
<menuitem action='EditCopy' />
<menuitem action='EditPaste' />
</menu>
<menu action='ChoicesMenu'>
<menuitem action='Book'/>
<menuitem action='Wine'/>
</menu>
</menubar>
<popup name='PopupMenu'>
<menuitem action='EditCopy' />
<menuitem action='EditPaste' />
</popup>
</ui>
"""
class MenuWindow(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self, title="Menu Example")
action_group = Gtk.ActionGroup("my_actions")
self.add_file_menu_actions(action_group)
self.add_edit_menu_actions(action_group)
self.add_choices_menu_actions(action_group)
uimanager = self.create_ui_manager()
uimanager.insert_action_group(action_group)
menubar = uimanager.get_widget("/MenuBar")
# button = Gtk.Button("Open") # Submit button to write to
# button.connect("clicked", self.on_button_clicked)
def add_file_menu_actions(self, action_group):
action_filenewmenu = Gtk.Action("FileNew", None, None, Gtk.STOCK_NEW)
action_group.add_action(action_filenewmenu)
action_new = Gtk.Action("FileNewStandard", "_New",
"Create a new file", Gtk.STOCK_NEW)
# action_new.connect("activate", self.on_menu_file_new_generic)
action_group.add_action_with_accel(action_new, None)
action_fileopen = Gtk.Action("FileOpen", None, None, Gtk.STOCK_OPEN)
action_group.add_action(action_fileopen)
action_open = Gtk.Action("FileOpenStandard", "_Open",
"Open an existing file", Gtk.STOCK_OPEN)
# action_open.connect("activate", self.file_open_clicked)
action_group.add_action_with_accel(action_open, None)
action_filequit = Gtk.Action("FileQuit", None, None, Gtk.STOCK_QUIT)
# action_filequit.connect("activate", self.on_menu_file_quit)
action_group.add_action(action_filequit)
def add_edit_menu_actions(self, action_group):
action_group.add_actions([
("EditMenu", None, "Edit"),
("EditCopy", Gtk.STOCK_COPY, None, None, None,
self.on_menu_others),
("EditPaste", Gtk.STOCK_PASTE, None, None, None,
self.on_menu_others),
])
def add_choices_menu_actions(self, action_group):
action_group.add_action(Gtk.Action("ChoicesMenu", "Choices", None,
None))
action_group.add_radio_actions([
("Book", None, "Book", None, None, 1),
("Wine", None, "Wine", None, None, 2)
], 1, self.on_menu_choices_changed)
def create_ui_manager(self):
uimanager = Gtk.UIManager()
# Throws exception if something went wrong
uimanager.add_ui_from_string(UI_INFO)
# Add the accelerator group to the toplevel window
accelgroup = uimanager.get_accel_group()
self.add_accel_group(accelgroup)
return uimanager
And I have called this in my main
function as:
#!/usr/bin/python3
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, Gdk
import menu
class MainWindow(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self, title="Collection Manager")
self.set_default_size(1000, 20)
self.set_border_width(10)
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
MenuElem = menu.MenuWindow()
box.pack_start(MenuElem, False, False, 0)
self.add(box)
window = MainWindow()
window.connect("delete-event", Gtk.main_quit)
window.show_all()
Gtk.main()
I must be dowing something very wrong, as I am getting error:
python3 main2.py
(main2.py:15800): Gtk-WARNING **: Can't set a parent on a toplevel widget
(main2.py:15800): Gtk-CRITICAL **: gtk_widget_destroy: assertion 'GTK_IS_WIDGET (widget)' failed
And instead of menubar, I am getting two window as:
Kindly help
EDIT: From Cilyan's reply I am getting error:
python3 main_mini.py
Traceback (most recent call last):
File "main_mini.py", line 24, in <module>
window = MainWindow()
File "main_mini.py", line 15, in __init__
MenuElem = menu.MenuManager()
File "/home/rudra/Devel/Cmanage/menu.py", line 32, in __init__
super.__init__()
TypeError: descriptor '__init__' of 'super' object needs an argument
Upvotes: 1
Views: 1984
Reputation: 8471
One important point, is that when write something like class SubClass(BaseClass)
, you extend BaseClass
, which means that SubClass
is a BaseClass
with additions. In your code, this means that MenuWindow
is a Gtk.Window
(a detached rectangular area of the UI with a title bar and possibly some widgets in it). And actually, you don't want to pack a window into another window. You want to pack a menubar into your main window.
My understanding is that you wanted to encapsulate the code related to the menu in its own class, but you do not need this class to be a widget by itself. To do this, you usually do not need to inherit anything(*). However, as your class will probably manipulate GObject signals, you should at least derive from GObject.GObject.
I followed another approach, however. What your MainWindow
needs is just a UIManager. But a UIManager that would be specially sized for the application's purpose so that the MainWindow
code is not cluttered with menu related tasks. I choose to directly inherit from Gtk.UIManager
and extend it to include everything the application needs and create some kind of specialized UIManager for the application. Here is what I changed from MenuWindow
:
# New base class, new name to better define what it really is (optional)
# Now, our object will be UIManager by itself, and we can extend it
class MenuManager(Gtk.UIManager):
def __init__(self):
# Use super, it's much easier and flexible
super().__init__()
action_group = Gtk.ActionGroup("my_actions")
self.add_file_menu_actions(action_group)
self.add_edit_menu_actions(action_group)
self.add_choices_menu_actions(action_group)
# This code comes from the create_ui_manager function.
# No need to instanciate a Gtk.UIManager, self is the UIManager already
self.add_ui_from_string(UI_INFO)
self.insert_action_group(action_group)
Then, I changed nothing apart from removing create_ui_manager
. Now, the integration into the MainWindow
is slightly different:
class MainWindow(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self, title="Collection Manager")
self.set_default_size(1000, 20)
self.set_border_width(10)
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
# Don't forget to change the class's name
MenuElem = menu.MenuManager()
# Code moved from MenuWindow's __init__ function
menubar = MenuElem.get_widget("/MenuBar")
# Now you really pack the menubar widget, not a window containing a menu
box.pack_start(menubar, False, False, 0)
self.add(box)
# Come from create_ui_manager. The AccelGroup must be added to the main window, not to the UIManager itself
self.add_accel_group(MenuElem.get_accel_group())
There is one last thing you may need to do, which I can't review because you did not provide code for it: the on_menu_choices_changed
and on_menu_others
functions. These functions will probably have an impact on the program as its whole, so you need them to access some kind of back-reference to your application, and possibly to other UI elements, i.e. other widgets that will be packed into the MainWindow
.
One possible solution would be to pass a reference to the MainWindow
object when instanciating the menu and save it so that the callback can use it when it's signaled:
class MenuManager(Gtk.UIManager):
def __init__(self, mainwindow):
super().__init__()
self.mainwindow = mainwindow
# ...
def on_menu_choices_changed(self, widget, current):
# For example
if current.get_name() == "Wine":
self.mainwindow.set_icon_to_glass_of_wine()
else:
self.mainwindow.set_icon_to_book()
# ...
class MainWindow(Gtk.Window):
def __init__(self):
# ...
MenuElem = menu.MenuManager(self)
# ...
That said, when you start dealing with bigger applications, it's easier and cleaner to switch to a stronger software design, like a Model-View-Controller architecture.
Upvotes: 3