beeware / toga

A Python native, OS native GUI toolkit.
https://toga.readthedocs.io/en/latest/
BSD 3-Clause "New" or "Revised" License
4.37k stars 674 forks source link

[GSoC] Proposal for enhancing Gtk+ backend #418

Closed ArchKudo closed 6 years ago

ArchKudo commented 6 years ago

GSoC 2018 Proposal: Enhancements for Gtk+ backend

Table of Contents

Abstract

This proposal aims to provide new widgets for Gtk+ implementation of Toga, along while improving and testing the current implementation to make it as bug-free as possible. Along the 12 weeks of GSoC I intend to complete the following tasks:

  1. Implement the following new widgets for Gtk:
    1. DateInput
    2. DateTimeInput
    3. TimeInput
    4. Separator
    5. SearchEntry
    6. Activity Indicator or Spinner
    7. ColorInput
  2. Package Toga Gtk application using flatpak
  3. Add styling support using Gtk+ CSS
  4. Provide a test harness for Gtk

Detailed description

New Widgets

Flatpak

Flatpak is a Linux distribution agnostic software packagement. It provides a sandbox for running applications and, also provides stable releases of gi. If Briefcase could target flatpak for deploying Toga apps for Gtk we could guarantee similar installs and, do away with distro-specific bugs and, also allow us to target a specific SDK for running toga apps.

Gtk+ Styling

Gtk from version 3.16+ handles styling of widgets using CSS and applied to the widgets using Gtk.StyleProvider. This also means older methods like override_* are depreciated and need to be styled using CSS.

Using a GUI test harness for Gtk

The only sensible way I have found for testing Gtk+ applications is by emulating user events. Test frameworks like dogtail and ldtp take advantage of Gnome accessibility toolkit to provide this functionality. Also to run GUI tests in ci a headless server of the X windowing system is required. This can be done using Xvfb. Both the testing tools offer similar functionality and have similar dependencies, but, I have found dogtail to have a more pythonic API, is lightweight and works out of the box with Xvfb.

Working Timeline

Week 1

Design interface for these three widgets: DateInput, DateTimeInput, TimeInput. Since an interface doesn’t exist yet, most of the time will be spent designing the interface, adding documentation, and writing tests for toga’s core API.

DateInput interface could include

and for TimeInput interface,

for DateTimeInput we could inherit from previous widgets.

Week 2

Implement above widgets for Gtk. Gtk has a DateInput native widget know as Gtk.Calendar. For TimeInput there exists no native widget however, this stackoverflow post describes using a custom Gtk.SpinButton for TimeInput. This would require setting bounds on the possible value of current time, Switching between 12/24 hour format, Setting current time, etc. DateTimeInput could have positioning logic whether, TimeInput should be placed to the left/right/top/bottom of DateInput.

Week 3

Implement Separator widget. The interface will have an orientation property, to set the Separator either horizontally or vertically. Additionally, a color property could be added to set the color of the separator. Implementation is pretty straight-forward using Gtk.Separator classes. color property could be set using CSS later.

Week 4

Implement SearchEntry. As far as I know, there is no native widget provided. But the basic idea is to inherit from the Gtk.Entry widget. Listen to Key.ENTER presses signal, or provide connecting to a button. Have an initial label like Search. Add a cancel icon to discard query, also a magnifying glass icon at the start would be a nice touch. For reference I could use the example available on gtk-demos.

Week 5

Design and implement ColorInput. The roadmap mentions using Gtk.ColorButton but, to enable any kind of widget to be able to invoke color picker implementation must be an instance of Gtk.ColorSelection. It should be possible to get/set any color of the type color.rgba. Gtk.ColorSelection stores the original value of color, and I could include this property in the interface too.

Week 6

This might mark the last week for implementing all proposed widgets, with the completion of Spinner. Spinner can inherit from Gtk.Spinner, and requires a start and a stop method. This makes it fairly simpler from a Progressbar widget, and from a design perspective Spinner is suitable for tasks running for an indeterminate time, while latter is for when running time is known.

Week 7

Add flatpak support for briefcase. A general flatpak install includes the follwing steps.

  1. Initialize build using flatpak build-init
  2. Build using flatpak build
  3. Finish the build and provide permissions using flatpak build-finish
  4. Supply base64-encoded local GPG key
  5. Provide a .flatpakref file
  6. Users install and run using flatpak run app
  7. There exists good examples on Gnome's gitlab repository for setting up flatpak for python apps like gnome/pitvi, gnome/music

Week 8

Add support for setting properties via Gtk+ CSS. This would require setting CSS identifiers to correctly identify widgets. I am already working on a simple rule-based CSS generator, which when provided by a selector string and a dictionary containing CSS attribute/value pairs should generate valid CSS. Next, the generated CSS could be applied to the widget using the Gtk.StyleContext of that widget. Some of the initial work is available in #397.

Week 9

Use the above utilities for setting properties of widgets. Add unittests to new and existing widgets on the implementation side. Most toga widget properties could be tested using this method: The general idea is to create a Toga widget object. Set some property of the toga widget, and then check whether this produces desirable changes on the native implementation.

A working implementation of set_color for Label widget could be developed as below:

def set_color(self, value):
  if value:
      selector = self.native.get_css_name()
      css = css_rule_factory(selector, {'color', str(value)})
      style_context = self.native.get_style_context()
      gtk_apply_css(style_context, css)

and corresponding test:

def test_can_set_color():
  label = toga.Label('name', style=(Pack(color=RED)))
  assert label._impl.native.get_color(
      label._impl.native.get_state()) == Gdk.RGBA(1,0,0,0)

Widgets to be tested:

Weeks 10

I aim to complete three tasks during this week. First is to integrate dogtail with toga's source. Since dogtail isn’t available updated on pypi, it needs to be bundled along with toga_gtk. Also dogtail depends on pyatspi, at-spi-2's python wrapper which comes as a part of gnome install, but isn’t available for install using pip too. So it requires being linked to our usage. Second is adding xvfb-run and its dependency xinit to ci, which are also required by dogtail and provide a non-graphical “headless” mode for running Gtk apps. At last, I did like to create a simple test which launches application and logs signals emitted by the app to check whether everything works correctly on the ci. The tests/ directory can contain be later divided into unittests and scenarios subdirs.

Week 11

I could implement simple position tests during this week. For example, currently in toga’s tutorial0 many users including me can’t click the button present on the top of the screen(because it's shifted up and not visible). Using dogtail API it is possible to move the mouse to the position where it is located, and provide a mouse click action using dogtail.rawinput.click(x,y) next, assert whether the button releases an onclick event. Also, it is possible to check whether a certain child node exists and, then interact with them using key presses or clicks.

Week 12

In this week some additionally advanced scenarios or integration tests can be implemented. Most of the inspiration can be derived from the examples/{widget} directory. This generally expands from the work done in a previous week to various widgets starting with containers. I have written a simple example derived from examples/button in a user-story fashion:

1. Can create main window.
2. Can create box inside window.
3. Dimensions of box are contained inside the main window.
4. Can set box direction to `column`.
5. Can add button widgets.
6. Button widgets show up horizontally adjacent to each other.
7. All button widgets are contained inside the box, etc.

Key Deliverables

Involved Risks

  1. It is currently impossible to get some CSS/Font properties using pygobject (See: #119) and other such bugs in pygobject itself.
  2. Most Linux distributions have moved to Wayland as their default display server, hence Xvfb's usefulness is questionable.
  3. pygobject exposes a C API, so it is very easy to mess memory management and can cause segfaults and core dumps when some of [g]objects require being free’d using native API.
  4. flatpak requires installing, and running python apps might require additional build steps or arguments while running apps.
  5. As mentioned before, testing using pyatspi requires linking system libraries to the virtualenv, which may require adding separate logic for each platform.

Other Commitments

I have my semester examinations even after commencement of GSoC coding period. Luckily(?) my university has really long semesters, and it is generally a mini-holiday between examinations, so it won’t generally hurt my commitments to toga, but as soon as the timetable comes out I could provide with dates when I won't be available.

freakboy3742 commented 6 years ago

The bulk of this looks really good; however, I have three concerns:

  1. The timings seem really arbitrary (and not at all granular). We're looking for granularity of no more than a week; having "weeks 2-4" in your schedule is an immediate red flag. You've also allocated 1 week do design and implement 3 widgets in week 1, but 3 weeks to implement the SplitPane widget (which, I'll note, already exists).

  2. Testing GUI applications isn't a trivial task, yet you've essentially covered it as a four week "wave hands, I'll test" entry in your project plan. We need a lot more detail here about what you've got in mind, how it will work, how it will interact with unit testing, and so on.

  3. Including Flatpak is a nice touch; however, I get the impression you've added this without really digging into how complex it is. Do you have any reason for believing it's a 2 week task?

ArchKudo commented 6 years ago

You've also allocated 1 week do design and implement 3 widgets in week 1

I was thinking of just the implementation only, as if I were to create a Gtk+ app :cry: , Fixed!

but 3 weeks to implement the SplitPane widget (which, I'll note, already exists).

I was following docs/projects/roadmap.rst hence the confusion, fixed too!

Including Flatpak is a nice touch; however, I get the impression you've added this without really digging into how complex it is. Do you have any reason for believing it's a 2 week task?

It earlier included time required for me to study about it, but I took some time to read flatpak's documentation and updated my schedule accordingly.

freakboy3742 commented 6 years ago

@ArchKudo The timings for widget design and implementation make a bit more sense now; however, the later work seems a bit off.

ArchKudo commented 6 years ago

You've been working on CSS styling (on and off) for a while in #397 - I don't get the impression that this work is about a week away from being finished; am I missing something?

I have been stashing changes locally, as to not clutter my pr, since I already have a failing test(due to upstream though...).

They validate that "yes, calling set label causes the label to be set"

Maybe I chose a wrong example but the aim is to test whether setting something on the core interface translates to valid Gtk+ action but, I get your point.

Basic label setting is the part least likely to break, because it's a direct and obvious API, compared to positioning, styling, and other features.

For positoning you're right it is not possible to unittest easily, however Gtk+ has methods to get styling attributes of objects

About the third point you mentioned, I could only try to convince you to the fact dogtail is a rather small library and more of wrapper. And I can only think of certain usecases which are common to a subset of widgets. Like if I have tests for positioning for Toga.Box, it would be more or less identical when testing positioning in other containers but, apart from that every widget would require completely different tests.

freakboy3742 commented 6 years ago

@ArchKudo I'm still a little hazy on exactly what dogtail does/doesn't do, and the role it plays in the testing process (and even whether it's the best option for GTK testing).

However, the rest of this proposal is looking pretty good, and the deadline is rapidly approaching. If you can find (or write) a good summary of the state of GTK app testing and why dogtail is the best choice, that would make this proposal even stronger; otherwise, it's probably OK as is.

freakboy3742 commented 6 years ago

Unfortunately, this project was not selected for the 2018 GSoC. Thanks for applying!