Header Image - Randall Morgan

Category Archives

8 Articles

Salsa – A Teaching Operating System – Part 3

Last time we discussed the x86 assembly language. We really only scratch the surface of this subject. There is much more to learn. Today we need to address addressing modes (pun intended) and the stack.

First, what is an address mode? When we have an instruction such as “mov” that accesses memory, that address must come from somewhere. The address mode determines how the address for the memory location in the instruction is obtained. For example:

mov ebx, 0x7cff

mov word [ebx], 0x7ce0

In the above example, the first instruction “mov ebx, 0x7cff” load the immediate value “0x7cff” into the EBX register. This is known as immediate mode. In this mode the data to be loaded into the register is part of the instruction and is encoded into the actual machine instruction. You’ll recognize immediate mode by the fact that no memory access (other than fetching the instruction itself) is needed to perform the instruction.

In the second example above, the value 0x7ce0 is moved not into the ebx register but into the memory location pointed to by the value in ebx. So, with the two instructions above 0x7ceo would be placed into memory at address 0x7cff because that is the value in ebx. Anytime you see a register in square braces you can think of this as the register saying “Use the address I contain”. This mode of addressing is called register indirect.The x86 family supports many addressing modes. However, we wont need them all. We’ll introduce others as we move along.

One more thing we should comment on is the use of the “word” value in the second instruction. Many assemblers can infer the proper data size to transfer from memory based on the register used. However, nasm may require the use of a data size to tell it how many bytes to transfer from memory. Here we use the “word” size to transfer 16-bits (two bytes) of data. We can also use byte (8-bits), dword (32-bits), and qword (64-bits). However, for our boot loader we wont be using any 64 bit instructions or registers. This is because our processor will initially be in “real” mode (16-bit mode).

Let’s see another example of storing a byte to memory using an address in the base register bx:

mov byte [ebx], al

As stated, this instruction will move the byte stored in register al (the low order byte of ax) to the memory location pointed to by the value in base register bx.

Now what if we want to transfer the high order byte of ax to memory?

We, we can just use the ah register from the ax pair. Like so:

mov byte [ebx+1], ah

Notice that here we passed a constant offset to the destination by adding 1 to the ebx register. This is very handy when we know ahead of time where the byte should be placed in respect to the base value.

Most x86 assembly instructions (using Intel Syntax) will follow the pattern:

<operation> <destination>, <source>

For example:

mov [ebx], eax

This instruction moves the value in eax the location pointed to by ebx.

Some instructions have an implicit source or destination. For example:

add ebx, 4

This instruction uses ebx as both a source and destination as the instruction takes the value in ebx, adds the immediate value 4 and then places the result back into ebx.

Now let’s look at a simple loop example:

   xor eax, eax ; zeros out eax
   mov ecx, 10 ; load counter with 10
start: 
   inc eax ; increment eax, our loop code goes here
loop start

The ecx register is usually used to store a count when doing repetitive operations. The line “start:” is a label. Labels are special markers in the code that basically names the current code address and must end with a colon. This allows you to refer to points in your code by a human readable label rather than trying to calculate and use the address actual value.

Here we’ve loaded ecx with the number of times we want to repeat our block of code (in this case the inc eax instruction) and marked the start of the block of code with the label “start”. Finally, we use the “loop” operation followed by the label of the block we want to repeat.

The loop operation decrements the value of ecx. Then if ecx is non-zero, it jumps to the address (label) provided. When ecx is zero, execution will continue to the instruction following the “loop” operation.

We’ve seen enough assembly language to get our feet wet. Now let’s return to our operating system.

Displaying Text

One of the first task we want our OS to perform will be to print something to the screen so we know it’s alive. We’ve seen how to use the BIOS’s teletype function to print characters to the screen. But how does the BIOS do this?

When the PC is first powered on the BIOS initializes the video display to a 16 color text mode. It places video memory at location 0xB8000. We can write directly to video memory to display text. However, we need to know a bit more first.

The default video mode stores it’s display data as two byte values. The low order byte determines the character to display and the high order byte determines the foreground and background color of the character.

So how to we know what values correspond to which characters? Well the default is to support the ASCII character set. You can find ASCII character set tables all over the internet. But for completeness check out this one.

Now, what about that color byte? The high order byte of each pair contains two nibbles (4-bit values). The lower order nibble (bits 0-3) contain the four bit code for the foreground color and the high order nibble contains the background color. To see the sixteen possible colors supported check out this link: https://en.wikipedia.org/wiki/BIOS_color_attributes.

Now let’s try displaying some colorful text to the screen in video mode 0. In your favorite text editor, enter the following x86 assembly code:

;
; File: alphabet.asm
; Auth: Randall Morgan <rmorgan62@gmail.com>
; Desc: Program to display the English alphabet on the display in 
; Mode 0, a 25 (row) x 40 (cols) x 16 color display mode.
;

start:
    mov byte al, 'A'     ; Character
    mov byte ah, 0x0F    ; Color: background 0-black, foreground f = white
    mov ecx, 26     ; 26 letters in the alphabet
    mov ebx, 0xB8000 ; Set base register to start of video memory
    
loop_start:
    mov word [ebx], ax  ; Move character and color code into video memory
    inc al              ; Increment al for next character
    inc ah              ; Increment next color pair
    add ebx, 2          ; Move to next character
loop loop_start
end:
    jmp end         ; infinitly jump here
    
 times 510-($-$$) db 0 ; Pad file to 510 bytes
    dw 0xaa55          ; Add the magic number

Save this file in slasa/src as alphabet.asm. Then cd to the salsa directory and execute the following command at the terminal:

nasm src/alphabet.asm -o /bin/alphabet.bin

This will assemble the program into machine code and the BIOS will happily execute it for us as long as we half the magic number properly placed at the end of a 512 byte code block. We wont even need to place it on a 1.44MB disk image for qemu to run it.

Next we need to run the binary file in qemu. We can do this with the following command:

qemu-system-i386 bin/alphabet.bin

This command requires that you are still in the salsa directory. If everything works you should see something like this:

Printing to Bios default Mode 0 screen

Notice the alphabet in the top left of the screen. Notice that we’ve not only printed the alphabet but we have also changed the foreground and background colors of each character. Now we’re getting somewhere…

Ok, next we need to learn a little more about the x86 architecture before we move on to our next step in OS development.

The Stack

A stack is a simple First-In-Last-Out data structure. The hardware stack used by the x86 is a reserved section of RAM and is used to store temporary data such as local variables and intermediate values, and parameters.

Keeping track of the stack and the next open location in memory that we can push data to is the job of the SP (Stack Pointer) register. On the 8086 this register is 16-bits and is named SP. On 32 bit x86 architectures the register has been extended to 32 bits and has been renamed ESP (Extended Stack Pointer).

To initialize a stack we only need to set the stack pointer to a valid location in memory. It should be noted that the x86 stack grows downward. So each time we push a byte onto the stack the stack pointer (sp) is decremented. If our stack is initialized to 0xFFFF and we push the al register onto the stack it will save the contents of al to 0xFFFF and then decrement the stack pointer to 0xFFFE. If we then push the bl register, the contents of bl will be placed in memory at 0xFFFE and then the stack pointer (sp) will be decremented again, leaving SP with a value of 0xFFFD. When we pop (remove) a value off the stack, it is first incremented and then the value at it’s new location is returned.

In our example above, the stack is pointing to 0xFFFD which is the next available memory location we can use. If we pop bl, SP is incremented to point to 0xFFFE and then the value at 0XFFFE is returned. Now, we don’t have to push and pop values to/from the same registers all the time. Though often this is the case. It should also be mentioned that it is convention to refer to the top of the stack as the high address (the address SP was initialized with) and the bottom of the stack as the address currently in SP. All memory address are unsigned. So they never go negative. If we initialized SP to 0xFFFF in a 64KB memory system ans the popped a value without first pushing a value onto the stack, SP would underflow with out error and wrap it’s value around to 0x0000. However, with x86 we need to initialize the stack pointer to a high value so it has space to ground downward.

Branch Instructions

We’ve been using the jmp instruction without really touching on it. The x86 architecture has many branch (sometimes called flow-control) instructions. Some of these instructions are:

  • jmp – unconditional jump, i.e. jump always
  • je – jump when equal
  • jne – jump when not equal
  • jz – jump when last result was zero
  • jg – jump when greater than
  • jge – jump when greater than or equal to
  • jl – jump when less than
  • jle – jump when less than or equal to

These instructions are usually used in combination with a label:

jz start ; jump if the result of the last operation was zero

These instructions are the only instructions that can modify the value of the IP (Instruction Pointer) register. The IP cannot be directly modified.

This brings us to the Flag register. This register contains various bits that are used to indicate various state information. The most common types of flags used in the Flag register are the Error flags which indicate error states such as arithmetic overflow. Status flags such as the interrupt enable flag, and result flags, such as the carry flag which is set if a carry or borrow occurs during an arithmetic operation.

The Flag register cannot be accessed or modified except for the use of “popfd” and “pushfd” instructions.

The most common use of the Flag register is in combination with the “cmp” (Compare) instruction which subtracts one operand from the other, setting the c (carry), z (zero), and sign (negative). Usually a “cmp” instruction is followed by one of the conditional branch operations. For example:

mov eax, [ebx]
cmp eax, 0x0a
jne some_label

The above code load a value pointed to be ebx into the eax register then compares that value with the decimal value 10 (0x0a). If the value in eax is not equal to 10 then the conditional jump, “jne some_label” (jump not equal) is taken. Otherwise execution continues with the next instruction.

We’re gaining ground in our understanding of the x86 architecture and assembly language. To get comfortable with writing and assembling programs take a little time and try to write the following programs:

  • Write an x86 assembly program to fill the BIOS boot screen with ‘Q’ characters.
  • Modify the above program to print each row in a different foreground and background color.
  • Write a program that loads the last byte of the boot sector (byte 512) and subtracts 0x55 from it. If the result is zero, print “zero” to the screen. If the result is not zero, print the resulting value to the screen in decimal. Hint: Checkout the ascii character codes for decimal digits.

OK, that’s enough for today. Next time we’ll look at a few registers we haven’t mentioned before. The x86 debug registers. We’ll also start on a few routines we’ll need for our boot loader and OS.

Until next time, Happy Coding!

Salsa – An Operating System for Teaching -Part 2

Writing x86 Assembler

In part 1 we created a 1.44MB floppy disk image and placed a manually created boot sector on it with a very short machine code program that provided an infinite loop. We wont be able to do much more without a basic understanding of the x86 family of processors and their assembly language. So we’ll take a short detour here and discuss the processor first and then their assembly language.

x86 Processors

The x86 family of processors began life as the Intel 8080 8-bit processor. Intel had produced various processors for cash registers, calculators and other such devices. The 8080 was their first general purpose processor. The 8080 had eight 8-bit registers and two 16-bit registers. Registers A, B, C, D, E, H, L, and Flag were all 8 bits. The SP (Stack Pointer) and PC (Program Counter) registers were both 16 bits. However, these 8-bit registers may be used in pairs as 16-bit registers. These pairs are BC, DE, and HL.

Intel 8080 Registers

The Flag register contains 5 significant bits used to flag special conditions or states. These are the S, Z, AC, P, and C (or CY) flag bits. S is set if the result of an operation is negative. Z is set if the result of an operation is zero. P is set if the number of 1s in the result are even (Even Parity). The C (Carry/Borrow) flag is set if the last addition operation resulted in a carry or if the last subtraction operation required a borrow. The AC (Auxiliary Carry) flag (Sometimes referred to as the H or Half Carry flag) is set when a carry occurs from bit three to bit four during a BCD (Binary Coded Decimal) operation.

These flags are used by conditional instructions such as jump or branch instructions. The flags can be copied to the Accumulator (A register) and the A register and Flag register can be addressed as a single PSW (Program Status Word).

CPU Operation

When power is applied to the processor it waits a short time for it’s clock signal to stabilize. Then places all it’s registers and flags into a known state called the Initial State. Next the processor loads an instruction from instruction memory. The bits in the instruction are then decoded to turn on and off various internal signals. These instructions include encoded information about what the instruction should do, what registers the operation should be performed on, and where the results should be placed. Next, the decoded operation is executed and if required, the results are stored. This set of steps is common to all microprocessors. It is often referred to as the Fetch, Decode, Execute Cycle. Even processors that execute an instruction in a single cycle complete all these steps. They just have to do it by dividing that single cycle into three or four sub-cycle time steps.

Now you may wonder why we are spending time learning about the 8080 when our focus is on x86 machines? Well, the x86 family of processors are just the younger, bigger, more powerful, siblings of the 8080.

Intel followed the 8080 with the 8085 (basically an easier to use 8080 but with slower speed) and then the 8086 & 8088. The 8086 is the first x86 processor and it contains most of the 8080 instructions and registers. However, it also contains so much more! With the 8080 most instructions work only on 8-bit data. Their are a few for manipulating 16 bit data but they are limited in scope. The 8086 however is a true 16-bit machine.

Intel 8086 Registers

In the 8086 the data and accumulator registers became 16-bits wide. However, they still contain the original 8-bit registers of the 8080. The 16-bit registers can address both their high and low order bytes separately using the H or L suffice. Some of the registers where renamed to reduce confusion I’m sure, with the new meaning of H and L.

Also notice that the PC has been renamed IP (Instruction Pointer as compared to Program Counter in the 8080). It still functions exactly the same. It just points to the next instruction in instruction memory to be executed. The Stack Pointer (SP) is just a16 bit version of the SP in the 8080.

The 8086 also gained several new registers. The BP (Base Pointer), SI (Source Index), DI (Destination Index), CS (Code Segment), DS (Data Segment), SS (Stack Segment), ES (Extra Segment). In total the 8086 has 14 16-bit registers. The 16 bit general purpose registers we all given a suffix of ‘X’. So the 8080’s A register’s 16 bit counter part in the 8086 is the AX register.

8086 Memory

The 8086 had a 20-bit address space or 1 megabyte. This was a large jump from the 64k of the 8080. The 20-bit address had to be formed from 16-bit sources. So the memory was partitioned into sixteen 64K segments. The maximum memory addressable by 16-bits. Each segment is identified by a 16-bit segment value ranging from 0x0000 to 0xFFFF. Within each segment, a memory location is selected by a 16-bit offset (the number of bytes from the beginning of the segment). This offset is often referred to as the logical address and it is not a real address but an address into a real segmented address space. This segment/offset combination is often written separating the two parts with a colon, for example: A4FB:4872h. The segment value is first, A4FB then the colon followed by the offset value. So here the we have segment A4FB with offset 4872, Here the h post script means that the values are in Hexadecimal.

To get a real address from this notation the segment value must take the offset value (A4FB) and multiply it by 16 (shift the value 4-bit to the left) and add the offset. So in this example A4FB:4872h becomes a real 20-bit physical address of A9822h.

All of this memory segmentation will be important as we move forward. The 8086 can only actually address 64K of memory at a time. This is why we need the segment registers to allow us to access additional memory beyond 64K.

With this scheme there is a large amount of overlap. Every 16 bytes we start a new segment. So any address ending in 0h is the beginning of a new segment. These 16 byte spaces are called paragraphs. Also note that in the 8086 the segments may overlap, as a result the segment:offset is not unique.

Assembly Language

As we have seen assembly language is the lowest human readable form of program code. But assembly isn’t just a single language. In fact there is a unique assembly language for each microprocessor architecture. Also, each architecture may have multiple dialects of assembly because different Assembler (A tool used to translate Assembly Code to Machine executable code) developers chose to implement their version of assembly language differently. This is the case with the linux “AS” assembler and the “NASM” assembler. “AS” uses the AT&T dialect of x86 assembly language and NASM uses the Intel dialect. These two dialects are very different in terms of syntax:

Intel Syntax: mov eax, 1 (instruction destination, source)

AT&T Syntax: movl $1, %eax (instruction source, destination)

The Intel syntax is pretty self explanatory. In the above example, the amount of data which is to be moved is inferred from the size of the register (32 bits in the case of eax). The addressing mode used is inferred from the operands themselves.

There are some quirks when it comes to the AT&T syntax. First, notice the suffix at the end of the mov instruction. This stands for long and signifies 32 bits of data. Other instruction suffixes include w for a word (16 bits – not to be confused with the word size of your CPU!), q for a quad-word (64 bits) and b for a single byte. While not always required, typically you will see assembly code which uses AT&T syntax explicitly state the amount of data being operated on by the instruction.

We will write our assembly code using the Intel style of x86 assembly code. To translate our assembly code to machine code we will be using the nasm assembler.

Another reason we will need assembly code is that many of the lowest level machine instructions cannot be accessed through high level languages such as C or C++. So under some circumstances assembly or machine code is our only option.

Some Useful Assembly Instructions

Let’s look at a typical x86 assembly instruction:

mov ax, 0x10

This instruction has three parts. The first is the instruction mnemonic “mov”. which in this case represents the move operation. The second part is the destination register “ax”. Which is the 16-bit version of the A register. X is to denote this register as extended from the original 8-bit version. The final part of the instruction above is the value 0f 0x10. A hexadecimal value. This instruction will move the value of 0x10 into the A register.

Now, you might be wondering about that EAX register shown in the AT&T and Intel examples above. We’ll these are 32-bit extended-extended registers that was first used in the 80386 processor. We will discuss 32-bit architectures in a later post. First, I want to get a 16-bit system to boot a real stub OS. There are also 64-bit registers. The 64-bit architecture was first introduced by Intel in their Itanium processor back in 1998. Through 64 bit chips had been produced for specialty computers (i.e. super computers). In 1998 Intel and HP partnered to produce the first Itanium chips. The Itanium was released in 2001. Intel and HP focused this architecture on the server and high end systems market. The original x64 instruction set was designed by AMD (an Intel competitor) and was released in 2000. The instruction set was then implemented by AMD, Intel, and VIA. The 8080 and 8086 registers are still found in the x64 architecture. Their 64-bit counterparts are prefixed with an “R”. I guess it would be confusing to have an extended-extended-extended register.

Here’s a table of Register Naming from 8 to 64 bits

64-Bit32-Bit16-Bit8-Bit
RAXEAXAX (AH/AL)AL
RBXEBXBX (BH/BL)BL
RCXECXCX (CH/CL)CL
RDXEDXDX (DH/DL)DL
RSIESISI (SIH/SIL)SIL
RDIEDIDI (DH/DL)DIL
RSPESPSP (SPH/SPL)SPL
RDPEDPDP (DPH/DPL)DPL
R8R8DR8WR8B
R9R9DR9WR9B
R10R10DR10WR10B
R11R11DR11WR11B
R12R12DR12WR12B
R13R13DR13WR13B
R14R14DR14WR14B
R15R15DR15WR15B

The suffix on the new R8-R15 registers represents ‘B’ for byte, ‘W’ for word (16-bits), ‘D’ for double word (32-bits), and no suffix for the full 64-bits.

OK, One more detour before we write a bit of x86 assembly code.

Interrupt

Interrupts are a mechanism that allow the CPU to be notified of some event. Basically, an interrupt stops the processing of the current task and branches the CPU to a new location in the code called the Interrupt Service Routine or ISR. In the x86 architecture interrupts can be triggered by hardware or software signals. One important use for interrupts is in the handling of I/O devices. Let’s take the keyboard for example. If the CPU had to poll the keyboard for data it would eat up a lot of processor time. This time would be more efficiently used to execute our program instead of continuously asking the keyboard if a key had been pressed. Now if we allow the keypress on the keyboard to trigger an interrupt, then the ISR can simply read the keystroke from the keyboard and pass it on to our program. Once the ISR is finished handling the keypress, it routines to the same code and state before the interrupt occurred. If you think about it, this is just a hardware triggered function call.

Interrupts cal also be turned off and off within the CPU. When they are turned off the CPU will ignore any interrupts that occur. The x86 also include some interrupts that cannot be turned off. These are called “Non-Maskable Interrupts”.

As it turns out the BIOS uses software interrupts to call it’s routines. The BIOS defines many ISRs for typical tasks such as ‘int 0x10’ for for display related routines, and ‘int 0x13’ for disk related routines. The actual method within the BIOS’s ISRs that is called looks at the CPU AX register (in most cases) to determine the actual function to call. So prior to triggering a software interrupt for a BIOS routine, you must first load the function code into one of the CPU registers. This is typically the AX/AH register. However, you need to look up the actual registers used and those that are modified during the ISR. Here’s a document that gives a great list of useful DOS and BIOS routines. Another great source of info on BIOS routines is the Phoenix BIOS User Manual.

Interrupts also have a priority. Higher priority interrupts my interrupt a lower priority interrupts and instructions. Each interrupt is represented by a number. This number is also used as the index into an vector table. When an interrupt occurs, the CPU stops what it is doing, looks up the address of the IRS for the current interrupt in the interrupt vector table and then jumps to that location. The ISR must be placed at the location stored in the vector table.

OK, so we’ve covered the basics of assembly instructions, registers, and interrupts. I guess it’s time to get down to the metals and write some code!

Let’s start by trying to get our boot sector to print something. This will give us a visual indication that the BIOS actually found and executed our code.

;
; File: salsa/src/hello-salsa.asm
; Desc: A simple boot sector that prints "Salsa Booting..." using the BIOS routines.
; Auth: Randall Morgan
; Date: 02/27/2019
;

    mov ah, 0x0e    ; int 10/ah = 0eh -> scrolling teletype BIOS routine
    
    mov al, 'S'
    int 0x10
    mov al, 'a'
    int 0x10
    mov al, 'l'
    int 0x10
    mov al, 's'
    int 0x10
    mov al, 'a'
    int 0x10
    mov al, ' '
    int 0x10
    mov al, 'B'
    int 0x10
    mov al, 'o'
    int 0x10
    mov al, 'o'
    int 0x10
    mov al, 't'
    int 0x10
    mov al, 'i'
    int 0x10
    mov al, 'n'
    int 0x10
    mov al, 'g'
    int 0x10
    mov al, '.'
    int 0x10
    mov al, '.'
    int 0x10
    mov al, '.'
    int 0x10
    
    jmp $       ; Jump to the current address
    
    ;
    ; Padding and magic BIOS number.
    ;
    
    times 510-($-$$) db 0   ; Pad the boot sector with zeros
    
    dw 0xaa55               ; Last two bytes are the magic number.

This code is not the most efficient. I wanted to keep things simple for our first program. The first thing we do here is move the value 0x0E into the the high order 8 bits of the AX register (AH). This value is the function code for the BIOS function we want to execute. We will pass this function code in AH to the BIOS ISR for interrupt 0x10. This function, called the “Scrolling Teletype Character Write” routine simply takes what it finds in the lower 8 bits of the AX register (AL) and tries to print it to the screen at the next character position available. The rest of the program simple passes each character of the string “Salsa Booting…” to the BIOS routine. Once the entire string has been printed, we execute a jump to the currently location. This results in an infinite loop.

The times 510-($-$$) db 0 line may be cryptic at first. So let’s break it down as you’ll need to use it or similar commands often. The ‘db 0’ instructs the assembler to ‘define a byte’ at the current location with the value 0. The single dollar sign ‘$’ is the assembler’s token for the current address and the double dollar sign ‘$$’ is the assembler’s token for the origin address (where our code began). So ‘$-$$’ results in calculating the length of our code. Next this value is subtracted from the value 510 and then assembler macro “times” is called with this value. Times then repeats the “db 0” command for (510-length of our code) which results in padding our code with bytes of zeros out to 510 total bytes. At this point our code is 510 bytes long. We then call “dw 0xaa55”. This defines a word (two bytes) at the current location with the value 0xaa55. Which of course is our magic number that tells the BIOS it has found a boot sector.

One note here. You may have noticed that I used db (define byte) for the 0 padding values. This made the calculation easier. However, I used dw (define word) for the magic number. Using define word here garuantees that the two eight bit bytes of the word will be stored in the correct order i.e. with the correct endianess.

Running Salsa Booting

Now it’s time to run our code. First we need to assemble it into machine code and the make a disk image and place our machine code into it’s boot sector. Run the following command in the salsa/src directory to assemble the file.

$ nasm salsa.asm -o hello-salsa.bin

This will give you a binary file of machine code. Next move the hello-salsa.bin file to your salsa/bin folder.

In the salsa/bin folder run the following command:

$ dd if=/dev/zero of=hello-salsa.img bs=512 count=2880

This will build our blank 1.44MB floppy image. Now we need to copy our binary file to the beginning of this disk. We can do that with the following command:

$ dd if=hello-salsa.bin of=hello-salsa.img

Now that we have our floppy disk image we can run it in qemu with the following command:

$ qemu-system-i386 hello-salsa.img

The result is nothing spectacular. However, we’ve had to cover a lot of ground to get here! So be proud of yourself!

Here’s a screen shot of the program.

I think we’ve covered quite a bit for this post. Go read about x86 registers and interrupts and the BIOS routines. I’ll get to work on the next post where we will implement our boot loader.

Until then, Happy Coding!


Simple Graphics in Python – Part 3

by SysOps 0 Comments

Last time we left off discussing Color in John Zelle’s graphics.py library. If you didn’t catch part 1 and 2 of this series I recommend you read those parts first and then return here. You can find part 1 here: Simple Graphics in Python.

We’re almost done going over the library from a user standpoint. However, in the future I may discuss how the library works if there is enough interest. This time, we’ll be discussing window updating and animations. We’ll develop a few sample apps and have some fun. My intention here isn’t to develop full fledged apps but, rather give you a starting point for your own apps using the graphics.py library. So let’s get started.

Window Updates

The graphics.py library usually handles window updates for you anytime an object that has been drawn to the window changes. Under some circumstances it may be necessary to force a window update. For example, when using the library from some interactive shells. The window may be forced to update using the update() method on the GraphWin object. This will redraw all the items in the window.

The window auto update feature is great for simple graphics. However, as your scenes become more complex you may want to take charge and start updating the window when it bests fits your program’s schedule. This may become necessary when you are drawing many, many items to the window. You can improve efficiency by updating the window only after all items have been drawn. If you want to turn off the auto update window feature you can do so when you create the window by passing autoflush=False as in:

win = GraphWin("Window Title", 400, 400, autoflush=False)

This will disable the auto update feature and you’ll be responsible for calling win.update() when you desire to redraw all the objects in the window. Here’s an example:

"""
Prog:   ex-11_02.py

Auth:   R. Morgan

Desc:   Demonstrate how to use John Zelle's
        graphics.py library turning off the 
        window's auto update feature and 
        calling win.update() yourself.

Lic:    This code is placed in the public domain.

"""

from graphics import *
from random import randint

def main():
    win = GraphWin("Rectangles", 640, 480, autoflush=False)
    
    points = []
    for i in range(1):
        for p in range(0, randint(4, 20)):
            points.append(Point(randint(0,639), randint(0,479)))
        
        r = randint(0,255)
        g = randint(0,255)
        b = randint(0,255)

        poly = Polygon(points)
        poly.setOutline(color_rgb(r, g, b))
        poly.draw(win)

    # First mouse click adds a polygon
    points[0] = win.getMouse()  
    points[-1] = points[0] 
    poly2 = Polygon(points)
    poly2.setOutline('white')
    poly2.draw(win) 

    # Second mouse click should show the new polygon
    win.getMouse()
    update()

    # Thrid mouse click should close the window.
    win.getMouse()
    win.close()

main()

When I read the docs and implemented this program I expected that updates would only occur when the update() method was called. When this didn’t work as expected I re-read the docs and when I still couldn’t understand what was happening, I emailed John. He was kind enough to respond and set me straight about my misunderstandings. After reading his response and once again, re-reading the docs, I realized I had read, but ignored one statement in the docs. This was the cause of misunderstanding. Here’s the line I skimmed over and missed the details:

Now changes to the objects in win will only be shown when the graphics system has some idle time or when the changes are forced by a call to update().

When I read this I walked away with the impression that updates only occur when update() was called if you passed autoflush=False. However, the statement clearly says that auto-updates still occur when the system has idle time. So update is only useful if you have a blocking operation that keeps the auto-update from running.

John pointed out that in the code above, the getMouse() method calls are blocking methods but that he coded them to call update() and force drawing on the window. So my calls to getMouse don’t actually work as commented in the code above. In fact, they force an update to the window.

So with that insight let’s see if we can write a sample that actually demonstrates the use of update. Create the example below:

"""
Prog:   ex-11_03.py

Auth:   R. Morgan

Desc:   Demonstrate how to use John Zelle's
        graphics.py library turning off the 
        window's auto update feature and 
        calling win.update() yourself.

Lic:    This code is placed in the public domain.

"""

from graphics import *
from random import randint

def main():
    win1 = GraphWin("Rectangles", 640, 480, autoflush=False)
    #win2 = GraphWin("Rectangles", 640, 480)
    
    points = []
    for i in range(1):
        # Build up initial polygon
        for p in range(0, randint(4, 20)):
            points.append(Point(randint(0,639), randint(0,479)))
        
        r = randint(80,255)
        g = randint(0,255)
        b = randint(50,255)

        poly = Polygon(points)
        poly.setOutline(color_rgb(r, g, b))
        poly.draw(win1)

        # Loop to keep system busy
        # Since we are using a loop 
        # to delay the system you may need to increa
        max_iters = 999999999
        delay_frac = 33333333
        td = 0
        for j in range(0,max_iters):
            if j % delay_frac == 0:
                poly.undraw()
                poly = None
                r = randint(30,255)
                g = randint(30,255)
                b = randint(30,255)
                points.append(Point(randint(0,639), randint(0,479)))
                poly = Polygon(points)
                poly.setOutline(color_rgb(r, g, b))
                poly.draw(win1)
                print("Updated #", td)
                td += 1
                update() # We manually call update here, then delay again.
    
    
    # mouse click should show the new polygon
    # after busy loop completes
    win.getMouse()
    win.close()

main()

OK, with our new understanding of the autoflush=False option we’ll run the app above. You may need to adjust the value of max_iters and delay_frac as max_iters controls the total run-time of the delay loop and delay_frac controls the delay between updates during the loops.

Our program begins by creating a polygon and displaying it on the screen. Next, we enter a loop and stay in the loop for a very long time. This loop blocks the auto-update feature from updating the display. During the execution of the delay loop we check if we have made delay_frac (delay fraction) iterations since our last update. If so, the modulus expression will return 0 and the if statement will evaluate to true and we enter the if clause. Next, we erase and destroy the original polygon, and generate a new polygon using the points of last polygon with one new point added for good measure. Adding a point allows us to see the shape changed on update. The important thing to understand here is that the object isn’t being drawn to the window until we reach the update line at the bottom of the if clause.

However, if we were to forego the delay loop the auto-update feature would take over and draw the polygon when the system became idle or a method that itself (like the getMouse()) calls update() is called.

One last thing to know about the update() method is that it can take an integer parameter for the desired frame-rate. If you pass a desired frame-rate to update() it as in:

update(30)

It will update the window at this rate.

Animations

While it’s possible to use the graphics library for a GUI (Graphical User Interface), most NooBs will want to do something a bit more entertaining with it. I’m not going to tech game development here but I thought I would toss out a few example apps that I’ll intentionally leave unfinished so you, the reader, can have fun adding features and completing the demo apps.

There are a few ways to accomplish animation on a computer. The most often used is motion animation where an object is moved into it’s new location, then drawn, then erased and moved again. This cycle is known as the animation loop, or if you’re a gamer, the game loop.

Handball

Our first animation is a simple Pong-like game (remember these apps will be unfinished and incomplete) that simply draws a circle and a rectangle on the screen then moves them around the screen.

Screen Shot of the Handball App

We will use an OOP (Object Oriented Programming) approach for the Handball app.

#!/usr/bin/env python3

"""
Prog:   ex-12_01.py

Auth:   R. Morgan

Desc:   Demonstrate how to use John Zelle's
        graphics.py library to do simple
        animation of a pendulum.

Lic:    This code is placed in the public domain.

"""

from graphics import *
from math import *

width = 640
height = 480


class Paddle():
    def __init__(self, x, y, win):
        self.x = x
        self.y = y
        self.w = 10
        self.h = 50
        self.win = win
        self.rect = Rectangle(Point(self.x, self.y), Point(self.x+self.w, self.y+self.h))
        self.rect.setFill('white')
    
    def getX(self):
        return self.rect.p1.getX()

    def getY(self):
        return self.rect.p1.getY()

    def getW(self):
        return self.w

    def getH(self):
        return self.h
        
    def move(self, xspeed, yspeed):
        self.rect.move(xspeed, yspeed)

    def draw(self):
        self.rect.draw(self.win)

    def undraw(self):
        self.rect.undraw()

  
class Ball():
    def __init__(self, x, y, win):
        self.x = x
        self.y = y
        self.xspeed = 3
        self.yspeed = 1
        self.r = 10
        self.win = win
        self.cir = Circle(Point(self.x, self.y), self.r)
        self.cir.setFill('white')

    def set_speed(self, xspeed, yspeed):
        self.xspeed = xspeed
        self.yspeed = yspeed
    
    def move(self):
        self.cir.move(self.xspeed, self.yspeed)
        p1 = self.cir.getP1()
        self.x = p1.getX()
        self.y = p1.getY()
        
    def draw(self):
        self.cir.draw(self.win)

    def undraw(self):
        self.cir.undraw()

    def check_collision(self, pad):
        print("Xspeed: ", self.xspeed, "Yspeed: ", self.yspeed) 
        xbound = self.within_x_bounds(pad)
        ybound = self.within_y_bounds(pad)

        if xbound and ybound:
            if xbound:
                self.xspeed = -self.xspeed
            if ybound:
                self.yspeed = -self.yspeed
            self.move()
            return True

        return False
        

    def within_x_bounds(self, pad):
        if self.xspeed < 0:
            if (self.x < pad.getX() + pad.getW()) and (self.x > pad.getX()):
                return True
            else:
                return False
        else:
            if (self.x + self.r >= pad.getX()) and (self.x <= pad.getX() + pad.getW()):
                return True
            else:
                return False
    
    def within_y_bounds(self, pad):
        if self.y+self.r >= pad.getY() and self.y <= pad.getY()+pad.getH():
            return True
        else:
            return False
    
    def check_edges(self, width, height):
        p1 = self.cir.getP1()
        if p1.getX() < 0 or p1.getX()+self.r > width:
             self.xspeed = -self.xspeed;
        if p1.getY() < 0 or p1.getY()+self.r > height:
            self.yspeed = -self.yspeed;
  
      
def main():
    win = GraphWin("Handball", width, height)
    win.setBackground('black')

    xspeed = 0.1
    yspeed = 0.1

    # initial placement of paddle  
    pad = Paddle(10, (height/2)-25, win) 
    # Draw paddles.
    pad.draw()

    ball = Ball(width/2, height/2, win)
    ball.set_speed(xspeed, yspeed)
    ball.draw()
   
    while 1:
        # get imput if any
        k = win.checkKey()
        if k == 'a':
            if pad.getY() < 0:
                pad.move(0, 0) 
            else:
                pad.move(0, -20)
            print('Pad1: Move Up', pad.getX(), pad.getY())

        if k == 'z':
            if pad.getY() > height - 50:
                pad.move(0, 0)
            else:
                pad.move(0, 20)
            print('Pad1: Move down', pad.getX(), pad.getY())

        ball.check_edges(width, height)
        ball.move()

        if ball.check_collision(pad):
            print("Ball hit paddle")

    win.getMouse()
    win.close()


main()

This may not be the most efficient implementation however, it is only meant to provide you with some inspiration for creating your own apps by demonstrating what can be accomplished using the graphics.py library.

If you scan the code you quickly see that we have a Paddle calls, a Ball class, and a main function. Our Paddle object encapsulates properties (data) and methods (actions) our paddle can take. Our paddle needs to keep track of it’s position (x,y) and size (w,h). When we create a Paddle object from the class (a class is a blueprint for the object we want to create) we pass in these values along with the window we want the paddle to draw itself on. We save the window for future use as we will always draw the paddle to the same window. So saving it here simplifies our code and we no-longer have the need to pass the window each time we call draw() on the paddle.

When a paddle is instantiated (an object is created from the class), Python calls the __init__() method. In this method you place all the code that you need to run to set things up for use. So we create the rectangle that will represent our paddle on the screen. We also set the fill color on the paddle to white. Our paddle is now ready for use.

Often you’ll need to have access to the state of an object. Later, we’ll need to be able to determine if the ball hits our paddle so, we need access to the location and size of our paddle. Do enable this we provide accessor methods getX(), getY(), getW(), getH(). These return the paddles x, y, width, and height respectively.

Our paddle also needs to move up and down so we can hit the ball as it bounces across the court. So, well need a move() method. There may be times we want to move at different speeds. So will pass in the xspeed and yspeed for our paddle. You may be wondering why we need the xspeed. Truly we don’t. We could just hard code the xspeed in our class code. But that would restrict us to moving only in the Y plane. Yes, it’s true that the paddle in Pong moves only in the Y play (up and down). But think how much fun it would be to animate the paddle to shake when the ball hits it. Here, we would need access to the yspeed to accomplish this. Including it also opens the class up for reuse. For example, suppose you want to use the paddle in a falling object game. If we didn’t include the yspeed here, you wouldn’t be able to.

In almost all motion animations each object will need to complete the three tasks of the animation loop, Move, Draw, Erase, Repeat… The graphics library actually takes care of this for use in the move() method of the various shape objects. So we really don’t need to worry about it. But you do need to know it’s happening under the hood.

we’ll add a draw() method to our paddle. Here we only need to call draw on the rectangle that represents our paddle on on the display. We may also need an erase method at some point. So, we’ll include it here and again however, we’ll call it undraw() to stay consistent with the library methods. All we need to do in the undraw() method is to call it’s namesake on the rectangle that represents our paddle.

The Ball class is a bit more complicated. Mostly because we encapsulated the logic of what to do when the ball comes in contact with another object. For example, if the ball hits the edge of the screen or the paddle. The balls move method is a bit different than the paddle’s move method. This is because it is expected that once the ball is moving it will keep moving. Also, we don’t want to have to changed the balls direction ourselves. We want it to include this action when it hits an object so it bounces off on it’s own. So in the Ball class we provide an xspeed and yspeed and set default values for them. Our ball is represented by a circle on the screen. So we have to create a circle and save it. We laos set the fill color in the __init__() method.

We may need to change the ball’s speed so we include a set_speed() method. We will also need to know when the ball has hit the edge of the court. This is handled in the check_edges() method. Here you’ll need to pass in the courts with and height. It is assumed that the upper right corner of the court is (0,0) and all calculations make this assumption.

The check_collision() method is passed the paddle object to test for collision with the ball. The ball object includes two helper methods, within_x_bounds() and within_y_bounds() to check if the ball is within the bounds of the paddle object.

To make this a complete game you need to add scoring and allow the ball to reset and be re-served if it passes the paddle. You can also use this as the basic frame work for Pong by adding another paddle and additional input handling for another player. Just a hint if you try this, google keyboard input methods for python before you attempt this. As, the current approach wont report multiple key presses at once. Their are solutions but I’ll leave that as an exercise for the reader.

Simulations

Games and GUIs aren’t the only things that graphics can help with. Graphics are often used to convey information about some chemical or mathematical process. Let’s take a simple case, that of calculating pi. It is well known that PI can be estimated to surprising accuracy by randomly throwing darts at art board. OK, so it’s a bit more complex than that but, only a little. First, what we really need is a circle inside a square. The circle’s diameter must fit snugly inside the square. More precisely the diameter of the circle must equal the length of one side of the square.

The logic is simple: If the circle’s diameter is equal to the square’s length, than the area of the circle should be equal to: (area of the square / area of circle)*4. To learn more about this you can checkout this link: https://www.youtube.com/watch?v=M34TO71SKGk

We can draw circles, squares, and points (to represent darts) using the graphics.py library. So all we need to do is draw a circle inside a square and throw darts at it, then calculate the ratio of darts that landed in the circle to the total number of darts thrown. We will simply plot random points for our darts and keep track of how many we throw and where they landed. Let’s see how we might do this in python:


#!/usr/bin/env python3
"""
Prog:   ex-13.py

Auth:   R. Morgan

Desc:   Demonstrate how to use John Zelle's
        graphics.py library to visualize the
        process of estimating pi by randomly
        throwing darts.

Lic:    This code is placed in the public domain.

"""
from graphics import *
from random import *
from math import *

width = 400
height = 400
center = width/2
r = width/2

# Find the deststance between two points
def dest(x1,y1, x2,y2):
    return sqrt((x1 - x2)**2 + (y1 - y2)**2)


def main():
    win = GraphWin("Pi Estimation", width, height)
    # Draw a square
    sq = Rectangle(Point(0,0), Point(width-1,height-1))
    sq.setOutline("blue")
    sq.draw(win)
    # Draw a circle fitting the square
    c = Circle(Point(center,center), r)
    c.setOutline("white")
    c.draw(win)

    darts_thrown = 0
    darts_in_circle = 0
    best = 0

    estimate = 0
    best_estimate = 0
    for i in range(1,100000):
        x = randint(0, 400)
        y = randint(0, 400)
        p = Point(x,y)
        darts_thrown += 1
                
        # Is are point in the circle?
        if(dest(center,center, x, y) < r):
            darts_in_circle += 1
            p.setFill(color_rgb(220,200, 120))
            p.draw(win)
        else:
            p.setFill(color_rgb(127, 200, 127))
            p.draw(win)
        
        if i % 3000 == 0:
            estimate = (darts_in_circle/darts_thrown)*4
            if abs(pi - estimate) < abs(pi - best_estimate):
                best_estimate = estimate
            
            print("Iteration: ", i, " Estimated PI: ", best_estimate)

    print("Done!")

    win.getMouse()
    win.close()


main()

If you run this code you should get a printed output of something like this:

Iteration: 3000 Estimated PI: 3.0893333333333333
Iteration: 6000 Estimated PI: 3.0893333333333333
Iteration: 9000 Estimated PI: 3.089777777777778
Iteration: 12000 Estimated PI: 3.0936666666666666
Iteration: 15000 Estimated PI: 3.1018666666666665
Iteration: 18000 Estimated PI: 3.110222222222222
Iteration: 21000 Estimated PI: 3.1125714285714285
Iteration: 24000 Estimated PI: 3.1161666666666665
Iteration: 27000 Estimated PI: 3.1161666666666665
Iteration: 30000 Estimated PI: 3.1161666666666665
Iteration: 33000 Estimated PI: 3.1161666666666665
Iteration: 36000 Estimated PI: 3.1172222222222223
Iteration: 39000 Estimated PI: 3.12174358974359
Iteration: 42000 Estimated PI: 3.12174358974359
Iteration: 45000 Estimated PI: 3.1226666666666665
Iteration: 48000 Estimated PI: 3.12475
Iteration: 51000 Estimated PI: 3.124941176470588
Iteration: 54000 Estimated PI: 3.124941176470588
Iteration: 57000 Estimated PI: 3.124941176470588
Iteration: 60000 Estimated PI: 3.124941176470588
Iteration: 63000 Estimated PI: 3.124941176470588
Iteration: 66000 Estimated PI: 3.1267878787878787
Iteration: 69000 Estimated PI: 3.1267878787878787
Iteration: 72000 Estimated PI: 3.1267878787878787
Iteration: 75000 Estimated PI: 3.1267878787878787
Iteration: 78000 Estimated PI: 3.1267878787878787
Iteration: 81000 Estimated PI: 3.1267878787878787
Iteration: 84000 Estimated PI: 3.1267878787878787
Iteration: 87000 Estimated PI: 3.1267878787878787
Iteration: 90000 Estimated PI: 3.1267878787878787
Iteration: 93000 Estimated PI: 3.1267878787878787
Iteration: 96000 Estimated PI: 3.1267878787878787
Iteration: 99000 Estimated PI: 3.1267878787878787
Done!

I ran this program several times and the best I did was 3.1419. Which is pretty good given the fact that our random number generator is actually a pseudo random number generator. I also believe that the math library in python may be rounding our calculations. Using a more precise math library would improve the estimate. However, this app is only meant to demonstrate the process. So, I’ll leave implementing a more precise version up to the reader.


Screen Shot of Pi Estimator App

Running the application longer with more dart throws will improve the estimation of PI.

Multiple Window

The graphics.py library allows you to have multiple windows. This can be handy for both GUIs and data visualization. You could for example display the plot of darts in a PI estimation program in one window while plotting the error on a graph in another window.

You might wonder why you would ever need more than one window. Well, how often do you use a drop down menu? The drop down menu is actually a small window with a list of items that is placed over the main window. Dialog boxes, popups, etc… are all windows. So being able to create additional windows comes in very handy for GUI applications. However, other types of applications can make use of multiple window. Take our PI estimating application above. We could use an additional window to plot the standard deviation of our a current estimate. Using multiple windows you can show many plots at the same time. This would allow the user to correlate the information in the various plots.

I’m going to show you a simple demo that is once again, an incomplete game. This game is a two player version of Battleship. It has several issues left for you to resolve. however, it does demonstrate the use of two windows being used in a single application. The code here is a bit longer than our other applications and I would say this code is in a pre-alpha state. It is only meant to ignite you imagination and give you a base from which to work to complete the game.

I’m sure I don’t have to explain how Battleship is played. However, if you need and explanation, google “battleship game” and you’ll find a wikipedia article on it. Let’s see some code:


#!/usr/bin/env python3
"""
Prog:   ex-14.py

Auth:   R. Morgan

Desc:   Demonstrate how to use John Zelle's
        graphics.py library and the use of
        multiple windows in a single app.

Lic:    This code is placed in the public domain.

"""
from graphics import *
from random import *
from math import *

width = 400
height = 400



class Board():

    def __init__(self, title, width, height):
        self.xsize = 10
        self.ysize = 10
        self.w = width
        self.h = height
        self.grid = []
        self.win = GraphWin(title, width, height)
        self.vessels = []


    # returns pixels per division
    def xdiv(self):
        xdiv = self.w / self.xsize
        return xdiv


    # returns pixels per division
    def ydiv(self):
        ydiv = self.h / self.ysize 
        return ydiv


    def rowcol_to_xy(self, r, c):
        y = int(r * self.ydiv())
        x = int(c * self.xdiv()) 
        return (x, y)


    def rowcol_to_point(self, r, c):
        p = self.rowcol_to_xy(r, c)
        return Point(p[0], p[1])


    def xy_to_rowcol(self, x, y):
        r = int(y / self.ydiv())
        c = int(x / self.xdiv())
        return (r, c)
    

    # return the coordinates in pixels for
    # the center of the cell at (row, col)
    def center_xy(self, r, c):
        # calc (x,y) position of upper left
        # corner of cell at (r,c)
        xy1 = self.rowcol_to_xy(r, c)
        # Calculate lower right corner
        xy2 = self.rowcol_to_xy(r+1, c+1)
        
        # find the middel of the cell
        dx = (xy2[0] - xy1[0]) - self.xdiv() / 2
        dy = (xy2[1] - xy1[1]) - self.ydiv() / 2
        cx = dx + xy1[0]
        cy = dy + xy1[1]

        return (cx, cy)


    def dist(self, x1, y1, x2, y2):
        return sqrt(((x2-x1)**2) + ((y2 - y1)**2))


    # draws the grid of cells on the board
    def draw(self):
        # Expects (0,0) to be located in the upper left
        xdiv = self.w / self.xsize
        ydiv = self.h / self.ysize
        for i in range(0, self.w, int(xdiv)):
            l = Line(Point(i, 0), Point(i, self.h))
            l.draw(self.win)

        for j in range(0, self.h, int(ydiv)):
            l = Line(Point(0, j), Point(self.w, j))
            l.draw(self.win)


    # place a vessel on the board
    def place(self, vessel):
        plX = vessel.row * self.ydiv()
        plY = vessel.col * self.xdiv()
        if vessel.rect == None:
            rowcol = self.rowcol_to_xy(vessel.row, vessel.col)
            x1 = rowcol[0]
            y1 = rowcol[1]
            if vessel.horz:
                x2 = x1 + (vessel.length * self.xdiv())
                y2 = y1 + self.ydiv()
            else:
                y2 = y1 + (vessel.length * self.ydiv())
                x2 = x1 + self.xdiv()
            vessel.rect = Rectangle(Point(x1, y1), Point(x2, y2))
        vessel.rect.setOutline(color_rgb(127,220,127))
        vessel.rect.draw(self.win)


    # tests to see if the vessels on this board
    # have been hit by the shot taken, and call
    # draw_hit() to mark the shot with a red X 
    # in the cell where it landed.
    def hit(self, loc):
        col = int(loc.getX() / (self.w / self.xsize))
        row = int(loc.getY() / (self.h / self.ysize))
        self.draw_hit(row, col)


    # draws the actual red X, called by hit()
    def draw_hit(self, row, col):
        xy1 = self.rowcol_to_xy(row, col)
        x1 = xy1[0]
        y1 = xy1[1]
        xy2 = self.rowcol_to_xy(row+1, col+1)
        x2 = xy2[0]
        y2 = xy2[1]
        
        p1 = Point(x1,y1)
        p2 = Point(x2, y2)
        p3 = Point(x1,y2)
        p4 = Point(x2, y1)

        l1 = Line(p1, p2)
        l2 = Line(p3, p4)
        l1.setOutline('red')
        l2.setOutline('red')
        l1.draw(self.win)
        l2.draw(self.win)


    # Use to mark the shooter's board
    # for shots taken. So the player may
    # know where they have already shot
    def mark(self, r, c):
        c = self.center_xy(r, c)
        print("Center of mark, x: " + str(c[0]) + ", y: " + str(c[1]))
        pc = Point(c[0], c[1])
        cir = Circle(pc, int(self.xdiv()/2))
        cir.setOutline(color_rgb(50, 50, 200))
        cir.draw(self.win)



# Simple vessel class
class Vessel():

    def __init__(self, name, row, col, length, place_horz):
        self.row = row
        self.col = col
        self.length = length
        self.horz = place_horz
        self.name = name
        self.hit_count = 0
        self.rect = None # created in board.place()

        if self.name == 'Carrier':
            self.makeCarrier()
            print("Row: " + str(self.row))
            print("Col: " + str(self.col))
        elif self.name == 'Battleship':
            self.makeBattleship()
            print("Row: " + str(self.row))
            print("Col: " + str(self.col))
        elif self.name == 'Cruiser':
            self.makeCruiser()
            print("Row: " + str(self.row))
            print("Col: " + str(self.col))
        elif self.name == 'Submarine':
            self.makeSubmarine()
            print("Row: " + str(self.row))
            print("Col: " + str(self.col))
        elif self.name == 'Destroyer':
            self.makeDestroyer()
            print("Row: " + str(self.row))
            print("Col: " + str(self.col))
        else:
            print('Illegal Vessel Type: "'+name+'" not defined')
            return None 


    def makeCarrier(self):
        if self.name != 'Carrier':
            return
        elif self.horz:
            self.col = randint(0, 4)
            self.row = randint(0, 9)
        else:
            self.col = randint(0, 9)
            self.row = randint(0, 4)


    def makeBattleship(self):
        if self.name != 'Battleship':
            return
        elif self.horz:
            self.col = randint(0,5)
            self.row = randint(0, 9)
        else:
            self.col = randint(0, 9)
            self.row = randint(0, 5)


    def makeCruiser(self):
        if self.name != 'Cruiser':
            return
        elif self.horz:
            self.col = randint(0,6)
            self.row = randint(0, 9)
        else:
            self.col = randint(0, 9)
            self.row = randint(0, 6)


    def makeSubmarine(self):
        if self.name != 'Submarine':
            return
        elif self.horz:
            self.col = randint(0,6)
            self.row = randint(0, 9)
        else:
            self.col = randint(0, 9)
            self.row = randint(0, 6)


    def makeDestroyer(self):
        if self.name != 'Destroyer':
            return
        elif self.horz:
            self.col = randint(0,7)
            self.row = randint(0, 9)
        else:
            self.col = randint(0, 9)
            self.row = randint(0, 7)


    def getName(self):
        return self.name


    def move(self, x, y):
        self.rect.move(x, y)


    def draw(self):
        self.rect.draw()   


    # Not Yet Implemented
    # Given a row, col value for
    # a shot, return true if the
    # vessel was hit by shot 
    def hit(self, r, c):
        return False
    


# Simple player class
class Player():

    def __init__(self, name, width, height):
        self.name = name
        self.board = Board(name, width, height)

        # Create fleet
        self.Carrier = Vessel('Carrier', randint(0,4), randint(0, 9), 5, True)
        self.Battleship = Vessel('Battleship', randint(0,5), randint(0,5), 4, False)
        self.Cruiser = Vessel('Cruiser', randint(0,4), randint(0,4), 3, True)
        self.Submarine = Vessel('Submarine', randint(1,4), randint(1,4), 3, True)
        self.Destroyer = Vessel('Destroyer', randint(1,4), randint(1,4), 2, True)


    def getName(self):
        return self.name


    def getMouse(self):
        return self.board.win.getMouse()


    # called when player should take turn
    def turn(self, board):
        loc = self.getMouse()
        board.hit(loc)
        rc = self.board.xy_to_rowcol(loc.getX(), loc.getY())
        self.board.mark(rc[0], rc[1])
        

    # Not Yet Implemented
    # Should test if the player's
    # entire fleet has been sunk,
    # if so, game over!
    def fleetSunk(self):
        return False
        pass


    def close(self):
        self.board.win.close()


    # Initialize fleet
    def draw(self):
        self.board.draw()
        self.board.place(self.Carrier)
        self.board.place(self.Battleship)
        self.board.place(self.Cruiser)
        self.board.place(self.Submarine)
        self.board.place(self.Destroyer)



def main():
    # Open game boards
    player1 = Player("Player 1", 400, 400)
    player2 = Player("Player 2", 400, 400)
    player1.draw()
    player2.draw()

    while ~player1.fleetSunk() and ~player2.fleetSunk():
        player1.turn(player2.board)
        player2.turn(player1.board)

    player1.getMouse()
    player1.close()
    player2.close()


main()
   

Looking over this code we can see it is really rather simple. In main() we create two players and call draw() on them. Next, we enter a while loop. This loop will loop forever as I left the fleetSunk() method unimplemented. It is hard coded to return false. I’ve left implementing this method up to the reader.

Within the loop we call turn on each player passing in the opponent’s game board. Each game board is responsible for calculating it’s own size, and completing all drawing operations on it’s grid.

The player.turn() method takes the opponent’s game board as a parameter and and after getting the mouse click location, passes that location to the opponent’s board.hit() method. Next, we convert the pixel (x,y) values to (row, column) values and pass those to our own board’s mark() method to draw a blue circle to indicate where we’ve taken shots. You could leave this set out or toggle it to make the game more challenging.

The board’s hit method is not implemented in this code and is also left as an exercise for the reader. However, it should take the row, column values passed in and determine if any of the vessels on it’s board have been hit. If so, it should mark that vessel as damaged and increment the vessel.hit_count. This should be done by calling the vessel’s hit() method. The vessel is sunk if the hit_count matches the vessel’s length.

The player’s fleetSunk() method should simply test if all the player’s vessels have been sunk and return true if they have.

I’m leaving the completion of this up to the readers. You’ll most likely want to add some type of scoring. You might even change the X draw for hit’s and the circle drawn as a marker, to an image of an explosion and a slash in the water respectively. You might also make the shooter’s board indicate whether the shot was a hit or a miss. You should have all the tools you need to implement these features. If you take a little time and analyze each class and each of it’s methods, you should have little trouble.

There is one issue that this code has I didn’t have time to correct. That is that the vessels are drawn at random locations and therefor often overlap each other. This isn’t good, as one shot can damage two vessels. This might be allowed if this were Angry Birds (two bird, one shot…). However, it’s Battleship! SO you’ll need to implement some method for ensuring that all vessels are placed in such a manner that they wont overlap. You can find one such solution here: https://stackoverflow.com/questions/3265986. This isn’t the only solution but it’s one that isn’t too hard to implement. Do note that one issue with this method is that everything is placed around a focal point that will never be occupied by a vessel. Effectively ensuring that the center call of the board will always be empty. This could be dealt with by shrinking the field for the purpose of the placement calculation and then randomly shifting it up or down one row.

Good luck! If you have questions of comments I’d enjoy hearing from you.

Simple Graphics in Python – Part 2

by SysOps 0 Comments

We left off the last time discussing how to use the Text and Entry objects in John Zelle’s graphics.py library from his book and course Python Programming an Introduction to Computer Science. If you missed the first installment check it out here: http://www.randallmorgan.me/blog/simple-graphics-in-python/.

This time we will cover a few topics I skipped over such as drawing Rectangles and Ovals. As we did in the first part, we’ll draw these using points or lines first, and then introduce the library methods. This gives you the opportunity to appreciate what the library does for you and gives you the insight needed if you desire to change a method or extend the library by mucking around in it. From what I’ve seen of John on https://www.youtube.com/results?search_query=John+Zelle and at talks, I suspect he would invite you to muck around and make it your own.

Drawing Rectangles

So let’s start with rectangles. A rectangle is simply a four sided object with right angles. A square is a special rectangle in which all sides are of equal length. However, not all rectangles are not squares. Most are not in fact. So we saw in part one how to draw lines using points, our most basic visual object. So we will skip to drawing rectangles using lines and forego the lower level drawing with points.

Notice that in all of computer science, and indeed in all forms of engineering, complex objects are built up from simpler objects. We started with points and turned them into lines and circles. Now we’ll turn lines into rectangles and polygons and circles into ovals.

Our draw_rect function will take five parameters. First, we need the x and y positions of the upper left corner of the rectangle. Next, we need the width and height. Finally, we need the window to draw the rectangle into. Given these parameters we can draw rectangles of all sizes.

Take a look at the code below. Create a finle called ex-08_01.py and enter the following code:

"""
Prog:   ex-08_01.py

Auth:   R. Morgan

Desc:   Demonstrate how to use John Zelle's
        graphics.py library for drawing
        rectangles to the window.

Lic:    This code is placed in the public domain.

"""

from graphics import *
from random import randint

def draw_rect(x, y, w, h, win):
    # Build points for the rect's corners
    p1 = Point(x,y)
    p2 = Point(x+w, y)
    p3 = Point(x+w, y+h)
    p4 = Point(x, y+h)
  
    # draw the lines to connect the points
    lines = []
    lines.append(Line(p1, p2))
    lines.append(Line(p2, p3))
    lines.append(Line(p3, p4))
    lines.append(Line(p4, p1))

    # set the line color
    # and draw
    for ln in lines:
        r = randint(0, 255)
        g = randint(0, 255)
        b = randint(0, 255)
        ln.setFill(color_rgb(r, g, b))
        ln.draw(win)
    
    
    
def main():
    win = GraphWin("Rectangles", 400, 400)

    for i in range(0,50):
        x = randint(0, 400)
        y = randint(0, 400)
        w = randint(0,400)
        h = randint(0,400)
        draw_rect(x, y, w, h, win)

    win.getMouse()
    win.close()

main()

In the main() function we create a loop to draw 50 rectangles using our draw_rect() function. We generate the random position and size and call draw_rect() with them also passing along the window we want them drawn on.

In our draw_rect function() we first generate the points that form the four corners of the rectangle. Then we generate a list of lines passing the points as needed to draw the rectangle, being sure to close the rectangle by connecting the first and last points with the final line. In the loop we set the fill color and draw each line. It’s all very straight forward. Looking back at the line function in the first part of this article, you can see how we might draw rectangles using points if we didn’t have a line function.

Ok, the graphics.py library has it’s own Rectangle() method. I’ll demonstrate using it next.

"""
Prog:   ex-08_02.py

Auth:   R. Morgan

Desc:   Demonstrate how to use John Zelle's
        graphics.py library for drawing
        rectangles to the window.

Lic:    This code is placed in the public domain.

"""

from graphics import *
from random import randint

def main():
    win = GraphWin("Rectangles", 640, 480)
    
    for i in range(1000):
        p1 = Point(randint(0,639), randint(0,479))
        p2 = Point(randint(0,639), randint(0,479))

        r = randint(0,255)
        g = randint(0,255)
        b = randint(0,255)

        rect = Rectangle(p1,p2)
        rect.setOutline(color_rgb(r, g, b))
        rect.draw(win)

    win.getMouse()
    win.close()

main()

Here you can see that the library method takes two points that define the upper left and lower right corners of the rectangle. Our code above then set’s the Outline color to a randomly generated color and draws the rectangle. Nothing special going on here.

There really isn’t a need to include a method for squares unless you need to draw a lot of them. If you do, then it may be worth including a method just for squares. Such a method might look like this:

def square(x, y, side_length, win):
    p1 = Point(x, y)
    p2 = Point(x+side_length, y+side_length)

    r1 = Rectangle(p1, p2)
    r1.draw(win)

If you wanted to keep the same API as the graphics.py library, all of our functions could just return the composed type to the caller so the draw and other methods could be called on it. Then the caller would be in control of calling the draw method where and when it needed to. I changed the API on our functions mainly to differentiate them from the library methods.

Drawing Ovals

OK, next up is Ovals. Just a squares are a special case of rectangles, circles are a special case of ovals. So why didn’t the library call it’s circle method ovals and allow us to simply pass like parameters as we do to get a square from the Rectangle() method? I suspect this inconsistency was mostly for convenience.

Recall how we calculated the (x, y) positions on the circumference of the circle the using cos() and sin() functions? We used a loop that produced a value between 0 and 360 and feed that to the cos and sin functions for the x and y positions. However, because we needed a circle greater than a unit in radius, we had to scale the (x, y) values by the desired radius.

Now ask yourself what happens if the scale factor for x and y are not identical? Would we still get a circle? No, we would get a circle that was squished or expanded in one direction. The would result in an oval being drawn. See the following code in ex-09_01.py:

#!/usr/bin/env python3

"""
Prog:   ex-09_01.py

Auth:   R. Morgan

Desc:   Demonstrate how to use John Zelle's
        graphics.py library to draw ovals 
        using points.

Lic:    This code is placed in the public domain.

"""

from graphics import *
from math import *
from random import randint

# Given the center x, center y
# and radius, draw a circle 
# using points.
def oval(cx, cy, rx, ry, color, win):
    
    for i in range(0, 360):
        x = cos(i)*rx + cx;
        y = sin(i)*ry + cy

        p = Point(x,y)
        p.setFill(color)
        p.draw(win)


def main():
    win = GraphWin("Circles", 400, 400)
    win.setBackground(color_rgb(0,0,0))

    for i in range(0, 20):
        rx = randint(10, 200)
        ry = randint(10, 200)

        r = randint(0,255)
        g = randint(0,255)
        b = randint(0,255) 
        oval(200, 200, rx, ry, color_rgb(r, g, b), win)
   
    win.getMouse()
    win.close()


main()

Run the code above. You should see some attractive ovals… Compare the code here to the circle code we used in the first part of the series.

OK, now let’s see how the library does ovals. Run the code below:

"""
Prog:   ex-09_02.py

Auth:   R. Morgan

Desc:   Demonstrate how to use John Zelle's
        graphics.py library for drawing
        ovals to the window.

Lic:    This code is placed in the public domain.

"""

from graphics import *
from random import randint

def main():
    win = GraphWin("Rectangles", 640, 480)
    
    for i in range(0,50):
        p1 = Point(randint(0,639), randint(0,479))
        p2 = Point(randint(0,639), randint(0,479))

        r = randint(0,255)
        g = randint(0,255)
        b = randint(0,255)

        rect = Oval(p1,p2)
        rect.setOutline(color_rgb(r, g, b))
        rect.draw(win)

    win.getMouse()
    win.close()

main()

Here again we see that the oval method contains the same API calls as the Circle method. The library’s Oval() method takes different parameters than our function. It take a bounding box, a rectangle made of two points that enclose the desired oval. However, it produces an oval none the less.

Polygons

Alright, we’ve covered points, lines, rectangles, squares, circles and ovals, not to mention text. What else can we do with the library? Well, we could build up a collection of points and connect them with lines to build polygons. However, we wont write the code ourselves for this. John’s library already contains a Polygon method and if you’ve made it this far, you can infer how to develop such a function. So I’ll just demonstrate how to use the method from the graphics.py library:


"""
Prog:   ex-10.py

Auth:   R. Morgan

Desc:   Demonstrate how to use John Zelle's
        graphics.py library for drawing
        polygons to the window.

Lic:    This code is placed in the public domain.

"""

from graphics import *
from random import randint

def main():
    win = GraphWin("Rectangles", 640, 480)
    
    for i in range(10):
        points = []
        for p in range(0, randint(4, 20)):
            points.append(Point(randint(0,639), randint(0,479)))
        
        r = randint(0,255)
        g = randint(0,255)
        b = randint(0,255)

        poly = Polygon(points)
        poly.setOutline(color_rgb(r, g, b))
        poly.draw(win)

    win.getMouse()
    win.close()

main()

Loading Images

The graphics.py library contains a method for loading images. However, the formats support depend on your system and it’s setup. Most systems should handle working with PPM and GIF formats. Some may even handle PNG and TIFF formats. Installing PIL (Photo Imaging Library) on your system may get you additional formats. John’s graphics.py library uses the TK or more formally Tkinter library under the hood. graphics.py is really just a lite weight wrapper to make using Tk easier. So if you can get Tk to support a new image format, you can probably get the graphics.py library to support it.

All images support the generic methods of move(), draw(), undraw(), and clone(). In addition there is the Image() method for creating an Image object. You need to pass it an anchor point and a filename. The constructed image will be centered at the given anchor point. You may also call this method passing the width and height along with the filename.

You can call the getAnchor() to get the Point where the image is anchored. Calling getWidth() and getHeight() do exactly as you would expect and return the width and height of the image respectively.

A fun and functional method is the getPixel method that let’s us retrieve the pixel in the image at location (x, y). This method returns the color as a list of (r,g,b) color values. Note that the pixel location is relative to the image it’s self and must be inside the image.

The getPixel method can help us do some pretty cool things when paired with the setPixel() method. This method takes a color_rgb() value.

Lastly, the library can save images to a file. The format saved is determined by the filename extension given. You can perform the save operation by calling the save() method.

Now let us see how simple the graphics.py library make loading an image. Create a new file and enter the program below.


"""
Prog:   ex-10.py

Auth:   R. Morgan

Desc:   Demonstrate how to use John Zelle's
        graphics.py library for images into
        the window.

Lic:    This code is placed in the public domain.

"""

from graphics import *

def main():
    win = GraphWin("Image Loader", 640, 480)
    win.setBackground(color_rgb(0,0,0))

    p1 = Point(320, 240) # Point of center of image
    img = Image(p1, 'images/PixelCar.gif')
    img.draw(win)

    win.getMouse()
    win.close()

main() # Call main()

Now before we can run this program we need and image. You can down load the one I used here: http://www.randallmorgan.me/wp-content/uploads/2018/12/PixelCar.gif

Download this file and save it. I placed my copy in a sub-directory of my program’s directory names images. So the path to the image in the code reflects this. If you place the image in the same directory as your code, you’ll need to remove the ‘image/’ part of the filename and just pass ‘PixelCar.gif’.

Once you have the image and have modified the path in the code to point to your image, it is time to run the application. You should see the car image in the middle of the screen.

Let’s take a short detour and talk about color next.

Color

Throughout this series we’ve been using the color_rgb() method to generate colors. However, the library is based on the TKinter library and it supports named colors. Specifically, X11 named colors. We can easily pass these color names to any method that takes a color_rgb() value. I tend to use the color_rgb() method as it gives more control over the color. However, it can be very convenient to simply pass the color name for things like background colors or well defined colors such as white. You can learn more about X11 Color names here: http://cng.seas.rochester.edu/CNG/docs/x11color.html

Rather than writing an app to demonstrate the use of color names, just take the the image app above (ex-11_01.py) and change the color in the win.setBackground() method to ‘white’. Once you made the change run the program. Go ahead and try some of the other color names you find at the link above.

OK, we’ve covered a lot of material in this issue. In my next installment I’ll cover how to force a window update and animation.

Keep Coding!

Android Native Development with Java Getting to know Android

Mobile platforms such as Android and IPhone are becoming the de facto computing devices.  Many people have left their laptops and even tablets behind as the power of smart-phones has increased. Now applications that once ran on the desktop are running on smart-phones. Software development is changing. Every product must contend with mobile support whether it be a website or service, or a desktop application. Users now expect that their data will travel with them and they will have access to it from all the devices they own. For the software developer, that means supporting multiple devices, often supporting multiple code bases and feature sets. Android has been around now for about a decade and has grown to own most of the mobile market. IOS is a close second. There are tools out their that will help the mobile developer develop products for both platforms using a single code base. I’ve written about my favorite, Dart/Flutter in other articles. However, these tools often lack either the flexibility to accomplish certain tasks, or they simply do not support a critical function of the device. When this occurs it is often necessary to fall back on native development. Either writing code that your main application can call, or writing the complete application in the platforms native code. This means that even a cross platform developer using tools such as Flutter or Xamarin often need to maintain a considerable amount of platform specific knowledge. Simply put, knowing native development for a platform is important even if you use cross platform tool sets.

I recently began taking some Android course (thanks to a Google Scholarship, THANK YOU GOOGLE!!! ) and thought I would share my knowledge and experience here. These are not the first Android courses I have taken. But I still feel I am far from an expert in Android development. Things change so quickly and I often am playing catch up just trying to stay abreast of new technologies. Sharing my knowledge with others and teaching others helps me ground my own understanding of a topic. So here I will share what I know a little at a time.

Tools Needed

Throughout this series I will be using Android Studio. At the moment it is version 3.0.1. The latest API is 27 revision 1. Android Studio is free and can run on Linux, Mac, and Windows.  It includes an emulator for running your compiled code though, other emulators from 3rd parties are available. AndroidStudio can be downloaded from: https://developer.android.com/studio/index.html If you would like to follow along I suggest you download and install AndroidStudio. Read the “User Guide” particularly the sections starting from “Meet Android Studio” to “Build And Run Your App”. These sections will help you get things setup and walk you through setting up the emulator. I will assume a familiarity with the Java programming language throughout this series. If you have never used Java but have C++ or C# experience, you should be able to follow along just fine.

Step One:

Ok, now that you have Android Studio setup and working and have an virtual device working for testing. It’s time to start coding.

The base for most Android Apps is the Activity. An activity typically loads the User Interface (UI) and provides some interaction with the user. An activity goes through a number of stages during it’s life cycle.  For most applications the activity is the heart of the application. Most applications have one or more activities however, it is possible for an application to be written without using an activity. These are rare however and are more advanced than we will cover here. You can think of an activity as a user task.

Fig. 1.1 Android Activity Life Cycle. Courtesy, www.developer.android.com

Fig. 1.1 Android Activity Life Cycle. Courtesy, www.developer.android.com

As stated, activities have a life cycle. The android framework provides 6 core callback methods that allow you to interact with the activity life cycle. These are: onCreate(), onStart(), onResume(), onPause(), onStop(), and onRestart() as shown in figure 1.1.

Android activities get created and destroyed based on the user’s interaction and system resource requirements. If the user navigates aways from your application and opens another app that requires resources your app is using, your application will be destroyed and then rebuilt when the user navigates back to it. The same actions can occur when the user rotates the device your app is installed on. Rather than doing a complicated dance to redraw the layout in the new orientation, android simply destroys your app and then recreates it when the user returns to it. This may seem like a drastic measure to take however, android works hard to limit power consumption and extend battery life, and this approach aids that goal.

The onCreate() method is fired when the activity is first created. Android requires that if we use an activity we must override this method in our code. In the onCreate() method we perform the activity logic that should only be completed once in the activities life cycle. Here we can load the layout, set class variables, and retrieve data that may be needed for the activity.

The onCreate() method receives an parameter of savedInstanceState of type Bundle. A bundle is a collection of state variables and data saved from the last run of the application. If the application has never been ran before the bundle will be null and the android framework will create it for us on the first run.

So let’s try some code! Open Android Studio and create a new project. Call it “ActivityApp” and select “Empty Activity” for the application type. Android Studio will create some boiler plate code for you. It should look like this:

 [cc lang="java" tab_size="4" lines="20"]
package me.randallmorgan.activityapp;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

public class MainActivity extends AppCompatActivity {

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
  }

}
[/cc]

The first line provides our Java package name. This name comes from the concatenation of the app name and the reverse of the domain name you enter when creating the project. This is a typical java standard for package naming. It ensures that no two packages are named exactly the same. So that when these packages are distributed, there is no confusion as to which package is which or who is responsible for the package. Android follows this java standard package naming convention.

Lines 3 and 4 include the needed framework libraries AppCompatActivity and Bundle. Line 6 is the beginning of our application. We declare a public class MainActivity that extends the AppCompatActivity class found in the android framework code. The public, protected, and private prefixes to our class and methods are used to annotate the visibility of the code. We wont worry about these just yet. But we’ll come back to them in a later post.

Next we Override the onCreate() method from the framework’s AppCompatActivity class and provide our own code to initialize our activity. First we call the onCreate() method of our parent calls AppCompatActivity. This ensures that the framework does the work it needs to do to initialize our activity, such as extracting the state information from the saveInstanceState bundle. Finally, we call setContentView() passing in the resource id of our layout. In this case, the default activity_main.xml layout.

The setContentView() method takes the layout file and turns it into an actual view on the screen. We use the resource layout file to store our UI design. We’ll talk about UI design next time. For now, let’s continue with the activity life cycle.

Logging

Android Studio provides a logging system that allows us to see debug messages. These messages can be grouped into various categories such as Error, Warning, Info, Assert, Debug, Verbose, and WTF (What a Terrible Failure). The WTF level is typically reserved for the framework developers and under normal development conditions shouldn’t be used by mobile developers. To use the logging methods we first need to import the “android:util.Log” library. Next we set a tag value. This tag can be any string value but is usually set to the current class name. Then to print out information to the debug output window (Logcat) we only need to call the Log.d() method. The Log.d method takes to strings values and an option Throwable. A throwable is an exception. Exceptions are a type of error handling that allows a program to catch recoverable errors and do some processing on non recoverable errors  (like logging them).

Log.d() is used for debugging messages. There are other Log methods. One for each type of error message we listed earlier. The ones you might use are Log.d(tag, msg), Log.e(tag, msg), log.i(tag, msg), Log.v(tag, msg), Log.a(tag, msg). The Log.wtf(tag, msg) should only be used if you understand what you’re doing. This is for catastrophic errors only!!! Again, it is best left to the Android framework developers.

So let’s see how logging works. Go back to our ActivityApp project. Open the main_activiy.java file. Edit the code as follows:

[cc lang="java" tab_size="2"]
package me.randallmorgan.activityapp;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;

public class MainActivity extends AppCompatActivity {
  final String mTag = "MainActivity";
  
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    
    super.onCreate(savedInstanceState);
    setContentView(R.layout.test_activity);
    
    Log.d(mTag, "onCreate called.");

  }
}
[/cc]

We’ve added line to declare a class wide string variable of “mTag” and set it’s value to “MainActivity” following standard convention. Next we added a line to the end of our onCreate() method to log our message passing Log.d() the variable mTag, and our message “onCreate called.”. If you run the app in your emulator and open the Logcat window you should be able to locate our message: … D/MainActivity: onCreate called. You may need to filter the output using the dropdown  to show only the messages in the Debug category.

Visualizing the Activity Life Cycle

Ok, now that we have debugging working, let’s try out some of the other functions that get called during the lifecycle of our activity. Edit your code to read as follows:

[cc lang="java" tab_size="2"]
package me.randallmorgan.activityapp;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;

public class MainActivity extends AppCompatActivity {
    final String mTag = "MainActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.test_activity);
        Log.d(mTag, "onCreate called.");

    }

    @Override
    protected void onStart() {
        super.onStart();
        Log.d(mTag, "onStart called.");
    }

    @Override
    protected void onResume() {
        super.onResume();
        Log.d(mTag, "onResume called.");
    }

    @Override
    protected void onPause() {
        super.onPause();
        Log.d(mTag, "onPause called.");
    }

    @Override
    protected void onStop() {
        super.onStop();
        Log.d(mTag, "onStop called.");
    }

    @Override
    protected void onRestart() {
        super.onRestart();
        Log.d(mTag, "onRestart called.");
    }
    
}
[/cc]

Now compile and run the app. Open the Logcat window and you should see the follow debug messages:

  • onCreate called.
  • onStart called.
  • onResume called.

Now rotate the view in the emulator. You’ll see a bunch of text scroll through the Logcat window. If you examine it you should see the following message:

  • onPause called.
  • onStop called.
  • onCreate called.
  • onStart called.
  • onResume called.

As you can see, our app was stopped by the Android framework and then recreated in the new orientation.

Ok, that’s it for today. Next time we’ll discuss layouts and the resource xml files. Then we’ll look at interacting with our layout.

Building a Flutter App for Android The Holiday Shopping App.

by SysOps 0 Comments

Here it is a New Years and Iam still working with Flutter and Dart. Back before Christmas I started working with Flutter and built a few simple apps for my android phone. The first “real” app I created was for a friend. She is an artist in Arizona and blogs about art weekly. She’s been doing this for years. I host her site on my webserver, and help maintain her site. Working out issues when ever she wants a new gadget installed or the javascript she copied isn’t working for her.

So to help her out I wrote an android App that allows her followers to read her RSS feed. I plan on adding push notification to the app for her in the future. However, at this point that’s just a little beyond my knowledge of flutter and Android. With flutter I feel I am gaining knowledge quickly. I started my second app which required me to scape some data from an online database. The scrapping was easy using Python, Selenium, and BeautifulSoup. Tools I have plenty of experience with in the past. However, I soon realized that it was going to take some time to create booth a web api for the database and build the app. This app is a simple pet abuse lookup app. It’s allows people who are rehoming pets to check if the perspective new owner is in a criminal database for animal abuse. If they are, a summary of the public court records  can be seen.

So it being near christmas and me still needing an app that would be more complex yet not too complex to build. I settled on building a  Holiday Gift Shopping List App.   Below are a few screen shots. The current working model here still needs the database to be coded. At this point the app is working however it is working with a mock data adapter. A class that presents hard coded data in a maner similar to how I expect the database to work. if you look closely, you will see that if all the gift’s checkboxes are checked, the snowflake icon changes to a checkmark. The phone icon can be used to open the dialpadwith the user’s phone number ready for you to press call. The white + in the upper right open’s the user’s contacts so a Giftee may be added from the contact list. The Circle plus (+), let’s you add a gift idea to a user without opening the user gift list. Great for a quick idea!

All in all, the folks on gitter.im/flutter/flutter helped me out quite a bit. If you’re learning flutter there are quite a few helpful folks there. Icluding some members of the Flutter and Dart development teams.  They have all been very helpful. I’ve manage to help a few folks myself as I have learned Flutter and Dart. Which is nice because it helps drive home what you really know.

In the future I may do a tutorial on building the Holiday Gift App. For now, I’ve got other Flutter projects on the back burner and this one still needing the database connected. So, for now, I’ll complete this and move on.

Hope you all had a great New Years and a Wonderfully productive new year!

 

Newsletter Powered By : XYZScripts.com