2

I'm trying to implement a custom Gtk widget, but I can't figure out how to request a specific minimum size and preferred size.

Here you can see that I (try to) request a minimum size of 300x300 and preferred size of 500x500, but Gtk never even calls any of those functions and creates a 200x200 window:

import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk


class MyWidget(Gtk.DrawingArea):
    # None of these are ever called
    def do_get_preferred_width_for_height(self, height):
        print('do_get_preferred_width_for_height')
        return 300, 500
    
    def do_get_preferred_height_for_width(self, width):
        print('do_get_preferred_height_for_width')
        return 300, 500

    def do_get_preferred_size(self):
        print('do_get_preferred_size')
        min_size = Gtk.Requisition()
        min_size.width = min_size.height = 300

        pref_size = Gtk.Requisition()
        pref_size.width = pref_size.height = 500

        return min_size, pref_size
    
    def do_size_request(self, requisition):
        print('do_size_request')
        requisition.width = 500
        requisition.height = 500
    
    # Except for these two
    def do_get_request_mode(self):
        print('do_get_request_mode')
        return Gtk.SizeRequestMode.CONSTANT_SIZE
    
    def do_draw(self, context):
        print('Window size:', window.get_allocated_width(), window.get_allocated_height())
        width = self.get_allocated_width()
        height = self.get_allocated_height()
        
        context.set_source_rgb(0, 1, 0)
        context.rectangle(0, 0, width, height)
        context.fill()

window = Gtk.Window()
window.add(MyWidget())
window.show_all()
window.connect('destroy', Gtk.main_quit)
Gtk.main()

# Output:
# do_get_request_mode
# do_get_request_mode
# Window size: 200 200

What am I doing wrong?

5
  • I've been working on this on and off all day, and I've found a partial solution. If you return Gtk.SizeRequestMode.WIDTH_FOR_HEIGHT in do_get_request_mode(), the width of the window works the way you want it to. If you return Gtk.SizeRequestMode.HEIGHT_FOR_WIDTH, the height works the way you want it to. But I can't for the life of me figure out how to get both to work at the same time! I've tried many different things, including using a tuple, using |, and using CONSTANT_SIZE along with them, all to no avail. Maybe you'd have some ideas for how to get them both to work? Commented Jan 18, 2022 at 21:28
  • @SylvesterKruin That is an interesting observation! I almost went and said I still don't know how to make it work, but when re-examined the docs I noticed I had forgotten to implement the do_get_preferred_width and do_get_preferred_height methods, and adding those made it work! I have no idea why Gtk won't call my do_get_preferred_size method, but hey, I'll take it. I'll let you write that as an answer if you want, since I already have more than enough rep anyway. Commented Jan 18, 2022 at 22:12
  • I can't get it to work, even in the MRE. I wrote the two methods you named above, and they both return 500, and they're getting called, but I've still got the problem. Am I doing something wrong? I tried returning a Gtk.Requisition, but I got an error message. How did you fix it in your MRE? Commented Jan 18, 2022 at 22:22
  • 1
    @SylvesterKruin They have to return two ints, the minimum size and the preferred size. So return 300, 500. Commented Jan 18, 2022 at 22:24
  • The totally confusing Gtk-CRITICAL error: “gtk_widget_get_preferred_width_for_height: assertion 'height >= 0' failed” led me here. – My Gtk.Bin-derived class announces constant size mode and the do_get_preferred_width_for_height() implementation is never called either. I was able to get rid of that nonsense message by implementing do_get_preferred_height() alone. Commented Jan 3, 2023 at 6:03

1 Answer 1

2

The problem here (see this comment by the OP) is that the do_get_preferred_height() and do_get_preferred_width() methods are not implemented. Here is the code you need to add to your class:

def do_get_preferred_height(self):
    print('get_preffered_height') # So you can see when it's called
    return 300, 500

def do_get_preferred_width(self):
    print('get_preffered_width') # So you can see when it's called
    return 300, 500

This should make the window have an initial size of 500x500, and a minimum size of 300x300.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.