Skip to content

Commit

Permalink
Add custom logic and screenshots.
Browse files Browse the repository at this point in the history
  • Loading branch information
freakboy3742 committed Dec 21, 2019
1 parent a0b5d39 commit 4d07401
Show file tree
Hide file tree
Showing 8 changed files with 240 additions and 46 deletions.
Binary file added docs/tutorial/images/iOS/tutorial-5.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/tutorial/images/linux/tutorial-2.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/tutorial/images/macOS/tutorial-2.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/tutorial/images/windows/tutorial-2.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 3 additions & 3 deletions docs/tutorial/tutorial-1.rst
Original file line number Diff line number Diff line change
Expand Up @@ -177,17 +177,17 @@ This should open a GUI window:
.. group-tab:: macOS

.. image:: images/macOS/tutorial-1.png
:alt: Hello World Tutorial 1 window, on macOS
:alt: Hello World Tutorial 1 window, on macOS

.. group-tab:: Linux

.. image:: images/linux/tutorial-1.png
:alt: Hello World Tutorial 1 window, on Linux
:alt: Hello World Tutorial 1 window, on Linux

.. group-tab:: Windows

.. image:: images/windows/tutorial-1.png
:alt: Hello World Tutorial 1 window, on Windows
:alt: Hello World Tutorial 1 window, on Windows

Press the close button (or select Quit from the application's menu), and you're
done! Congratulations - you've just written a standalone, native application
Expand Down
129 changes: 121 additions & 8 deletions docs/tutorial/tutorial-2.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ In the ``src/helloworld`` directory, you should see 3 files: ``__init__.py``,
``__main__.py`` and ``app.py``.

``__init__.py`` marks the ``helloworld`` directory as an importable Python
package. It is an empty file; the very fact it exists tells the Python
interpreter that ``helloworld`` is a package.
module. It is an empty file; the very fact it exists tells the Python
interpreter that the ``helloworld`` directory defines a module.

``__main__.py`` marks the ``helloworld`` package as a special kind of package -
an executable module. If you try to run the ``helloworld`` module, using
``__main__.py`` marks the ``helloworld`` module as a special kind of module -
an executable module. If you try to run the ``helloworld`` module using
``python -m helloworld``, the ``__main__.py`` file is where Python will start
executing. The contents of ``__main__.py`` is relatively simple::

Expand Down Expand Up @@ -107,10 +107,120 @@ This ``main()`` method is the one that is imported and invoked by
``__main__.py``. It creates and returns an instance of our ``HelloWorld``
application.

That's the simplest possible Toga application. Let's put some of our own
content into the application, and make the app do something interesting.

Adding some content of our own
==============================

**TODO**
Modify your ``HelloWorld`` class so it looks like this::

class HelloWorld(toga.App):
def startup(self):
main_box = toga.Box(style=Pack(direction=COLUMN))

name_label = toga.Label(
'Your name: ',
style=Pack(padding=(0, 5))
)
self.name_input = toga.TextInput(style=Pack(flex=1))

name_box = toga.Box(style=Pack(direction=ROW, padding=5))
name_box.add(name_label)
name_box.add(self.name_input)

button = toga.Button(
'Say Hello!',
on_press=self.say_hello,
style=Pack(padding=5)
)

main_box.add(name_box)
main_box.add(button)

self.main_window = toga.MainWindow(title=self.name)
self.main_window.content = main_box
self.main_window.show()

def say_hello(self, widget):
print("Hello", self.name_input.value)

Let's look in detail at what has changed.

We're still creating a main box; however, we are now applying a style::

main_box = toga.Box(style=Pack(direction=COLUMN))

Toga's builtin layout system is called "Pack". It behaves a lot like CSS. You
define objects in a heirarchy - in HTML, the objects are ``<div>``, ``<span>``,
and other DOM elements; in Toga, they're widgets and boxes. You can then assign
styles to the individual elements. In this case, we're indicating that this is
a ``COLUMN`` box - that is, it is a box that will consume all the available
width, and will expand it's height as content is added, but it will try to be
as short as possible.

Next, we define a couple of widgets::

name_label = toga.Label(
'Your name: ',
style=Pack(padding=(0, 5))
)
self.name_input = toga.TextInput(style=Pack(flex=1))

Here, we define a Label and a TextInput. Both widgets have styles associated
with them; the label will have 5px of padding on it's left and right, and no
padding on the top and bottom. The TextInput is marked as being flexible - that
is, it will absorb all available space in it's layout axis.

The TextInput is assigned as an instance variable of class. This gives us
easy access to the widget instance - something that we'll use in a moment.

Next, we define a box to hold these two widgets::

name_box = toga.Box(style=Pack(direction=ROW, padding=5))
name_box.add(name_label)
name_box.add(self.name_input)

The ``name_box`` is a box just like the main box; however, this time, it's a
``ROW`` box. That means content will be added horizontally, and it will try
to make it's width as narrow as possible. The box also has some padding - 5px
on all sides.

Now we define a button::

button = toga.Button(
'Say Hello!',
on_press=self.say_hello,
style=Pack(padding=5)
)

The button also has 5px of padding on all sides. We also define a *handler* -
a method to invoke when the button is pressed.

Then, we add the name box and the button to the main box::

main_box.add(name_box)
main_box.add(button)

This completes our layout; the rest of the startup method is as it was
previously - defining a MainWindow, and assigning the main box as the window's
content::

self.main_window = toga.MainWindow(title=self.name)
self.main_window.content = main_box
self.main_window.show()

The last thing we need to do is define the handler for the button. A handler
can be any method, generator or asynchronous co-routine; it accepts the widget
that generated the event as an argument, and will be invoked whenever the
button is pressed::

def say_hello(self, widget):
print("Hello, ", self.name_input.value)

The body of the method is a simple print statement - however, it will
interrogate the current value of the name input, and use that content as the
text that is printed.

Now that we've made these changes we can see what they look like by starting
the application again. As before, we'll use Developer mode:
Expand Down Expand Up @@ -154,17 +264,20 @@ This should open a GUI window:
.. group-tab:: macOS

.. image:: images/macOS/tutorial-2.png
:alt: Hello World Tutorial 2 window, on macOS
:alt: Hello World Tutorial 2 window, on macOS

.. group-tab:: Linux

.. image:: images/linux/tutorial-2.png
:alt: Hello World Tutorial 2 window, on Linux
:alt: Hello World Tutorial 2 window, on Linux

.. group-tab:: Windows

.. image:: images/windows/tutorial-2.png
:alt: Hello World Tutorial 2 window, on Windows
:alt: Hello World Tutorial 2 window, on Windows

If you enter a name in the text box, and press the GUI button, you should see
output appear in the console where you started the application.

Next steps
==========
Expand Down
74 changes: 58 additions & 16 deletions docs/tutorial/tutorial-4.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,35 @@ So - how do you update your installed app when you make code changes?
Updating application code
=========================

**TODO** Make some visible code change...
Our application current prints to the console when you press the button.
However, GUI applications shouldn't really use the console for output. They
need to use dialogs to communicate with users.

Then, we can tell Briefcase to update the code in our application:
Let's add a dialog box to say hello, instead of writing to the console.
Modify the `say_hello` callback so it looks like this::

def say_hello(self, widget):
self.main_window.info_dialog(
'Hi there!',
"Hello, {}".format(self.name_input.value)
)

This directs Toga to open a modal dialog box when the button is pressed.

If you run ``briefcase dev`` and press the button, you'll see the new dialog
box. However, if you run ``briefcase run``, the dialog box won't appear.

Why is this? Well, ``briefcase dev`` operates by running your code in place -
it tries to produce as realistic runtime environment for your code as possible,
but it doesn't provide or use any of the platform infrastructure for wrapping
your code as an application. Part of the process of packaging your app involves
copying your code *into* the application bundle - and at the moment, your
application still has the old code in it.

So - we need to tell briefcase to copy over the new version of the code. We
*could* do this by deleting the old platform directory and starting from
scratch. However, Briefcase provides an easier way - you can update the code
for your existing bundled application:

.. tabs::

Expand Down Expand Up @@ -55,33 +81,48 @@ Then, we can tell Briefcase to update the code in our application:
If Briefcase can't find the scaffolded template, it will automatically invoke
`create` to generate a fresh scaffold.

Now that we've updated the installer code, We can then run `briefcase build`
to re-compiled app, and `briefcase run` to run the updated app.
Now that we've updated the installer code, We can then run ``briefcase build``
to re-compiled app, and ``briefcase run`` to run the updated app.

Updating dependencies and icons
===============================

This only updates the application code, though - what if your dependencies have
changed, or you have new application resources, like a new splash screen or
application icon)? Briefcase

* ``briefcase update -d`` (or ``briefcase update --update_dependencies``)
will re-install your the application dependecies.
application icon)? In this situation, the ``update`` command has some options
you can use:

* ``briefcase update -r`` (or ``briefcase update --update_resources``)
will re-install your application resources.
* ``briefcase update -d`` (or ``briefcase update --update_dependencies``)
will re-install your the application dependecies.

Lets try this now

**TODO** Add a custom icon, and update resources.
* ``briefcase update -r`` (or ``briefcase update --update_resources``)
will re-install your application resources.

Update and run in one step
==========================

If you're rapidly iterating code changes, you'll likely want to make a code
change, update the application, and immediately re-run your application.
Briefcase has a shortcut to support this usage pattern - the ``-u`` (or
``--update``) option on the ``run`` command:
``--update``) option on the ``run`` command.

Let's try making another change. You may have noticed that if you don't type
a name in the text input box, the dialog will say "Hello, ". Let's modify the
``say_hello`` function again to handle this edge case::

def say_hello(self, widget):
if self.name_input.value:
name = self.name_input.value
else:
name = 'stranger'

self.main_window.info_dialog(
'Hi there!',
"Hello, {}".format(name)
)

Run your app in development mode (with ``briefcase dev``) to confirm that the
new logic works; then update, build and run the app with one command:

.. tabs::

Expand Down Expand Up @@ -137,8 +178,9 @@ Briefcase has a shortcut to support this usage pattern - the ``-u`` (or
[helloworld] Starting app...
This should only be required if you're testing the *packaging* of your
application, or a bug that only manifests when your application is in packaged
form. For most day-to-day development, ``briefcase dev`` will be a lot faster.
application, or hunting a bug that only manifests when your application is in
packaged form. For most day-to-day development, ``briefcase dev`` will be a lot
faster.

Next steps
==========
Expand Down
77 changes: 58 additions & 19 deletions docs/tutorial/tutorial-5/iOS.rst
Original file line number Diff line number Diff line change
Expand Up @@ -30,32 +30,71 @@ This directory will contain a ``Hello World`` folder, which will contain
an Xcode project, as well as the support libraries and the application code
needed for the application.

You can then use briefcase to compile the application compile an installer,
using the ``build`` command:
You can then use Briefcase to compile the application compile an installer,
using the ``build`` command. You'll be prompted to select a device to compile
for; if you've got simulators for multiple iOS versions installed, you may also
be asked which iOS version you want to target. The options you are shown may
differ from the options show in this output; for our purposes, it doesn't
matter which simulator you pick.

.. code-block:: bash
(beeware-venv) $ briefcase build iOS
[hello-world] Generating application template...
Using app template: https://github.com/beeware/briefcase-iOS-Xcode-template.git
Select iOS version:
[helloworld] Building DMG...
1) 10.3
2) 13.3
> 2
Select simulator device:
1) iPad (7th generation)
2) iPad Air (3rd generation)
3) iPad Pro (11-inch)
4) iPad Pro (12.9-inch) (3rd generation)
5) iPad Pro (9.7-inch)
6) iPhone 11
7) iPhone 11 Pro
8) iPhone 11 Pro Max
9) iPhone 8
10) iPhone 8 Plus
> 6
Targeting an iPhone 11 running iOS 13.3 (device UDID 4768AA69-497B-4B37-BD0C-3961756C38AC)
[hello-world] Building XCode project...
...
[helloworld] Created Hello World-0.0.1.dmg.
Once this step completes, the ``macOS`` folder will contain an ``Hello
World.app``. This file is a self contained macOS executable. If you open
the Finder, you can double click on the icon to start the application. If
you send ``Hello World.app`` to a friend, they will be able to do the same
- double click on the app, and see your app running.

The ``macOS`` folder will contain a file named ``Hello World-0.0.1.dmg``.
If you locate this file in the Finder, and double click on it's icon,
you'll mount the DMG, giving you a copy of the Hello World app, and a
link to your Applications folder for easy installation. Drag the app file
into Application, and you've installed your application. Send the DMG file
to a friend, and they should be able to do the same.
Build succeeded.
[hello-world] Created Hello World.app.
We're now ready to run our application. You could do this by running
``briefcase run iOS``. If you run briefcase in that way, you'll be asked again
for the device you want to target. If you already know the devices that are
available, you can tell briefcase to use that simulator by providing a ``-d``
(or ``--device``) option. Using the name of the device you selected when
you built your application, run::

$ briefcase run iOS -d "iPhone 11"

If you have multiple iPhone 11 simulators, briefcase will pick the highest
iOS version; if you want to pick a particular iOS version, you tell it to use
that specific version::

$ briefcase run iOS -d "iPhone 11::13.3"

Or, you can name a specific device UDID::

$ briefcase run iOS -d 4768AA69-497B-4B37-BD0C-3961756C38AC

This will start the iOS simulator, install your app, and start it. You should
see the simulator start, and eventually open your iOS application:

.. image:: ../images/iOS/tutorial-5.png
:alt: Hello World Tutorial 5 window, on iOS

Next steps
==========
Expand Down

0 comments on commit 4d07401

Please sign in to comment.