diff --git a/README.md b/README.md index 994facf..b726173 100644 --- a/README.md +++ b/README.md @@ -4,71 +4,61 @@ Guia interactiva con la resolución de los retos propuestos en el libro [Python by Example](https://www.google.cl/books/edition/Python_by_Example/gDGdDwAAQBAJ) de *Nichola Lacey* +![img](./imgs/main_menu.png) + ### Retos Básicos -- [001-011](./basic/basic01.py) -- [012-019](./basic/basic02.py) -- [020-026](./basic/basic03.py) -- [027-034](./basic/basic04.py) -- [035-044](./basic/basic05.py) -- [045-051](./basic/basic06.py) -- [052-059](./basic/basic07.py) +- [001-011](./basic/basic01.py) | [012-019](./basic/basic02.py) | +[020-026](./basic/basic03.py) | [027-034](./basic/basic04.py) | +[035-044](./basic/basic05.py) | [045-051](./basic/basic06.py) | +[052-059](./basic/basic07.py) ![img](./imgs/basics_001-059.png) ### Retos Turtle -- [060](./trtl/turtle01.py) -- [061](./trtl/turtle02.py) -- [062](./trtl/turtle03.py) -- [063](./trtl/turtle04.py) -- [064](./trtl/turtle05.py) -- [065](./trtl/turtle06.py) -- [066](./trtl/turtle07.py) -- [067](./trtl/turtle08.py) -- [068](./trtl/turtle09.py) +- [060](./trtl/turtle01.py) | [061](./trtl/turtle02.py) | +[062](./trtl/turtle03.py) | [063](./trtl/turtle04.py) | +[064](./trtl/turtle05.py) | [065](./trtl/turtle06.py) | +[066](./trtl/turtle07.py) | [067](./trtl/turtle08.py) | +[068](./trtl/turtle09.py) ![img](./imgs/turtle_060-068.png) ### Retos Intermedios -- [069-079](./interm/interm01.py) -- [080-087](./interm/interm02.py) -- [088-095](./interm/interm03.py) -- [096-104](./interm/interm04.py) -- [105-110](./interm/interm05.py) -- [111-117](./interm/interm06.py) -- [118-123](./interm/interm07.py) +- [069-079](./interm/interm01.py) | [080-087](./interm/interm02.py) | +[088-095](./interm/interm03.py) | [096-104](./interm/interm04.py) | +[105-110](./interm/interm05.py) | [111-117](./interm/interm06.py) | +[118-123](./interm/interm07.py) ![img](./imgs/interm_069-123.png) ### Retos Tkinter -- [124](./tkgui/tk01.py) -- [125](./tkgui/tk02.py) -- [126](./tkgui/tk03.py) -- [127](./tkgui/tk04.py) -- [128](./tkgui/tk05.py) -- [129](./tkgui/tk06.py) -- [130](./tkgui/tk07.py) -- [131](./tkgui/tk08.py) -- [132](./tkgui/tk09.py) -- [133](./tkgui/tk10.py) -- [134](./tkgui/tk11.py) -- [135](./tkgui/tk12.py) -- [136](./tkgui/tk13.py) -- [137](./tkgui/tk14.py) -- [138](./tkgui/tk15.py) +- [124](./tkgui/tk01.py) | [125](./tkgui/tk02.py) | [126](./tkgui/tk03.py) | +[127](./tkgui/tk04.py) | [128](./tkgui/tk05.py) | [129](./tkgui/tk06.py) | +[130](./tkgui/tk07.py) | [131](./tkgui/tk08.py) | [132](./tkgui/tk09.py) | +[133](./tkgui/tk10.py) | [134](./tkgui/tk11.py) | [135](./tkgui/tk12.py) | +[136](./tkgui/tk13.py) | [137](./tkgui/tk14.py) | [138](./tkgui/tk15.py) +![img](./imgs/tkinter_124-138.png) ### Retos SQLite3 -- [139](./sqlite/sql01.py) -- [140](./sqlite/sql02.py) -- [141](./sqlite/sql03.py) -- [142](./sqlite/sql04.py) -- [143](./sqlite/sql05.py) -- [144](./sqlite/sql06.py) -- [145](./sqlite/sql07.py) +- [139](./sqlite/sql01.py) | [140](./sqlite/sql02.py) | +[141](./sqlite/sql03.py) | [142](./sqlite/sql04.py) | +[143](./sqlite/sql05.py) | [144](./sqlite/sql06.py) | +[145](./sqlite/sql07.py) + +### Retos Finales + +- [146](./final/fin01.py) | [147](./final/fin02.py) | [148](./final/fin03.py) | +[149](./final/fin04.py) | [150](./final/fin05.py) +![img](./imgs/final_146-150.png) ## Uso +Requiere **Tkinter** para retos [124-128](./tkgui/) + ```sh git clone https://gitea.kickto.net/devfzn/python_by_example.git cd python_by_example python main.py ``` + +Mas [imagenes](./images.md) diff --git a/final/files/files_directory b/final/files/files_directory new file mode 100644 index 0000000..9ae3366 --- /dev/null +++ b/final/files/files_directory @@ -0,0 +1 @@ +directorio para creación/lectura/escritura de archivos y bd. diff --git a/final/fin01.py b/final/fin01.py new file mode 100644 index 0000000..5ab0fed --- /dev/null +++ b/final/fin01.py @@ -0,0 +1,75 @@ +from common.common import clear + +def fin_01(): + """A shift code is where a message can be easily encoded and is one of the + simplest codes to use. Each letter is moved forwards through the alphabet + a set number of letters to be represented by a new letter. For instance, + 'abc' becomes 'bcd' when the code is shifted by one (i.e. each letter in + the alphabet is moved forward one character). You need to create a + program which will display the following menu: + 1) Make a code + 2) Decode a message + 3) Quit + + Enter your selection: + If the user selects 1, they should be able to type in a message + (including spaces) and then enter a number. Python should then display + the encoded message once the shift code has been applied. If the user + selects 2, they should enter an encoded message and the correct number + and it should display the decoded message (i.e. move the correct number + of letters backwards through the alphabet). If they select 3 it should + stop the program from running. After they have encoded or decoded a + message the menu should be displayed to them again until they select quit.""" + abc = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'ñ', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'] + + def code_msg(msg, steps): + coded_message = "" + for letter in msg: + if letter in abc: + ind = abc.index(letter) + ind += steps + while ind >= len(abc): + ind -= len(abc) + coded_message += abc[ind] + else: + coded_message += letter + print(coded_message) + + def decode_msg(msg, steps): + decoded_message = "" + for letter in msg: + if letter in abc: + ind = abc.index(letter) + ind -= steps + while ind < -len(abc): + ind += len(abc) + decoded_message += abc[ind] + else: + decoded_message += letter + print(decoded_message) + + while True: + clear() + print(""" + 1) Codificar mensaje + 2) Decodificar mensaje + s) Salir + """) + sel = input("Ingresa una opción: ") + match sel: + case '1': + msg = input("Ingresa el mensaje a codificar: ") + steps = int(input("Ingresa el nro. de pasos: ")) + code_msg(msg, steps) + input("\nPresiona Enter para continuar\n") + case '2': + msg = input("Ingresa el mensaje a decodificar: ") + steps = int(input("Ingresa el nro. de pasos: ")) + decode_msg(msg, steps) + input("\nPresiona Enter para continuar\n") + case 's': + break + case _: + print("Debes ingresar una opción válida") + input("\nPresiona Enter para continuar\n") diff --git a/final/fin02.py b/final/fin02.py new file mode 100644 index 0000000..6324755 --- /dev/null +++ b/final/fin02.py @@ -0,0 +1,79 @@ +from random import choice +from common.common import clear + +def fin_02(): + """You are going to make an on-screen version of the board game 'Mastermind'. + The computer will automatically generate four colours from a list of + possible colours (it should be possible for the computer to randomly + select the same colour more than once). For instance, the computer may + choose 'red', 'blue', 'red', 'green'. This sequence should not be + displayed to the user. After this is done the user should enter their + choice of four colours from the same list the computer used. For + instance, they may choose 'pink', 'blue', 'yellow' and 'red'. After the + user has made their selection, the program should display how many + colours they got right in the correct position and how many colours they + got right but in the wrong position. In the example above, it should + display the message 'Correct colour in the correct place: 1' and 'Correct + colour but in the wrong place: 1'. The user continues guessing until they + correctly enter the four colours in the order they should be in. At the + end of the game it should display a suitable message and tell them how + many guesses they took.""" + colours = { + 'NEGRO': "\033[0;30;1;47m", + 'PURPURA': "\033[1;35m", + 'CIAN': "\033[0;1;36m", + 'ROJO': "\033[1;31m", + 'VERDE': "\033[1;32m", + 'AMARILLO': "\033[1;33m", + 'AZUL': "\033[1;34m", + 'BLANCO': "\033[1;37m", + 'END': "\033[0m" + } + secrets = [] + guesses = [] + tries = 0 + for _ in range(4): + secrets.append(choice(list(colours.keys())[:-1])) + #print(colours[secrets[i]]+"█████"+secrets[i]+"█████"+colours['END']) + + print("\n COLORES\n") + for color in list(colours.keys())[:-1]: + print(' '+color.capitalize(), end=' ') + print() + + def guess_colours(): + guesses.clear() + print("\nIngresa 4 colores de la lista") + cont = 1 + while len(guesses) < 4: + resp = input(f"Ingresa el color nro. {cont}: ").upper() + if resp in list(colours.keys())[:-1]: + guesses.append(resp) + cont += 1 + print() + + def check_colours(): + ok_guess = 0 + for i, guess in enumerate(guesses): + if guess in secrets: + print(colours[guess]+f"{guess} está en los colores", end='') + if guess == secrets[i]: + print(", en está misma posición"+colours["END"]) + ok_guess +=1 + else: + print(", en otra posición"+colours["END"]) + else: + print(f"{guess} no está entre los colores") + print() + if ok_guess == 4: + return True + else: + return False + + win = False + while not win: + guess_colours() + win = check_colours() + tries += 1 + + print(f"Ganaste en {tries} intentos") diff --git a/final/fin03.py b/final/fin03.py new file mode 100644 index 0000000..be497da --- /dev/null +++ b/final/fin03.py @@ -0,0 +1,137 @@ +from os import getcwd as pwd +from getpass import getpass +from common.common import clear +import csv + +def fin_03(): + """You need to create a program that will store the user ID and passwords + for the users of a system. It should display the following menu: + 1) Create a new User ID + 2) Change a password + 3) Display all User IDs + 4) Quit + + Enter selection: + If the user selects 1, it should ask them to enter a user ID. It should check + if the user ID is already in the list. If it is, the program should display + a suitable message and ask them to select another user ID. Once a suitable + user ID has been entered it should ask for a password. Passwords should be + scored with 1 point for each of the following: + - it should have at least 8 characters. + - it should include uppercase letters. + - it should include lower case letters. + - it should include numbers. + - it should include at least one special character such as + !, £, $, %, &, <, * or @. + If the password scores only 1 or 2 it should be rejected with a message + saying it is a weak password; if it scores 3 or 4 tell them that "This + password could be improved." Ask them if they want to try again. If it + scores 5 tell them they have selected a strong password. Only acceptable + user IDs and passwords should be added to the end of the .csv file. If they + select 2 from the menu they will need to enter a user ID, check to see if + the user ID exists in the list, and if it does, allow the user to change the + password and save the changes to the .csv file. Make sure the program only + alters the existing password and does not create a new record. If the user + selects 3 from the menu, display all the user IDs but not the passwords. If + the user selects 4 from the menu it should stop the program.""" + file_path = f"{pwd()}/final/files/users.csv" + menu = """ + Administrador de Usuarios + + 1) Crear nuevo ID de usuario + 2) Cambiar una contraseña + 3) Ver todos los ID de usuario + s) Salir + """ + def create_pass(): + points = 0 + while points < 3: + points = 0 + password = getpass("Ingresa la contraseña: ") + if len(password) >= 8: + points += 1 + x = len([1 for p in password if p.isupper()]) + points += 1 if x > 0 else 0 + x = len([1 for p in password if p.islower()]) + points += 1 if x > 0 else 0 + x = len([1 for p in password if p.isdigit()]) + points += 1 if x > 0 else 0 + x = len([1 for p in password if p in '!£$%&<*@']) + points += 1 if x > 0 else 0 + if points < 3: + print("La contraseña es demasiado debil") + continue + elif 2 < points < 5: + resp = input("El password podría ser mejor, ¿quieres mejorarlo? (S|n): ").lower() + if resp != 'n': + points = 0 + continue + repass = getpass("Verificar la contraseña:") + if repass != password: + points = 0 + return password + + def create_user(): + user = input("Ingresa el nombre de usuario: ").strip() + temp_users = get_users() + if temp_users is None or user not in temp_users: + password = create_pass() + with open(file_path, '+a') as file: + file.write(f"{user},{password}\n") + input("Usuario creado, presiona Enter para continuar") + else: + input("Usuario ya existe, presiona Enter para continuar") + + def change_pass(): + user = input("Ingresa el nombre del usuario a modificar contraseña: ") + temp_users = get_users() + if temp_users is not None and user in temp_users: + password = create_pass() + temp_data = [] + with open(file_path, 'r') as file: + reader = csv.reader(file) + for line in reader: + if user in line[0]: + temp_data.append([line[0],password]) + else: + temp_data.append([line[0],line[1]]) + with open(file_path, 'w') as file: + writer = csv.writer(file) + writer.writerows(temp_data) + print(f"Password actualizado exitosamente") + else: + print("El usuario ingresado no existe") + input("Presiona Enter para continuar") + + def get_users(): + temp_users = [] + try: + with open(file_path, 'r') as file: + read = csv.reader(file) + for r in read: + temp_users.append(r[0]) + except FileNotFoundError: + pass + return temp_users + + while True: + clear() + print(f"Ruta archivo:\n{file_path}") + print(menu) + sel = input("Ingresa una opción: ") + match sel: + case "1": + create_user() + case "2": + change_pass() + case "3": + users = get_users() + print("\n Usuarios \n----------") + for user in users: + print(" "+user) + input("\nPresiona Enter para continuar") + case "s": + break + case _: + print("Debes ingresar una opción válida") + input("Presiona enter para continuar") diff --git a/final/fin04.py b/final/fin04.py new file mode 100644 index 0000000..f207784 --- /dev/null +++ b/final/fin04.py @@ -0,0 +1,60 @@ +import tkinter as tk + +def fin_04(): + """Create a program that will display the following screen: + +-------------------------------------------------+ + | ___________ ________________ | + | Enter a number: |___________| |View Times Table|| + | ___________ |________________|| + | | | ________________ | + | | | | Clear || + | | | |________________|| + | | | | + | | | | + | |___________| | + |_________________________________________________| + When the user enters a number in the first box and clicks on the "View + Times Table" button it should show the times table in the list area. + For instance, if the user entered 99 they would see the list as shown + in the example on the right. The "Clear" button should clear both boxes.""" + com_bg = "dodger blue" + window = tk.Tk() + window.title("Tablas de Multiplicar") + window.geometry("400x300") + window["bg"] = com_bg + + lbl_num = tk.Label(text="Ingresa un número:", font="Verdana 10") + lbl_num["bg"] = com_bg + lbl_num.place(x=20, y=20, width=130, height=30) + + txt_in = tk.Entry() + txt_in.place(x=150, y=20, width=120, height=30) + + def view(): + num = txt_in.get() + clear() + if num.isdigit(): + num = int(num) + for i in range(1,13): + lst_table.insert('end', f"{i} x {num} = {i*num}") + else: + clear() + + def clear(): + lst_table.delete(0, 'end') + txt_in.delete(0, 'end') + txt_in.focus() + + btn_view = tk.Button(text="Ver tablas", width=100, height=30, command=view) + btn_view.place(x=280, y=20, width=100, height=30) + + lst_table = tk.Listbox() + lst_table["justify"] = "left" + lst_table.place(x=150, y=60, width=120 ,height=230) + + btn_clear = tk.Button(text="Limpiar", width=100, height=30, command=clear) + btn_clear.place(x=280, y=60, width=100, height=30) + + txt_in.focus() + + window.mainloop() diff --git a/final/fin05.py b/final/fin05.py new file mode 100644 index 0000000..327f8a7 --- /dev/null +++ b/final/fin05.py @@ -0,0 +1,387 @@ +from os import getcwd as pwd +import tkinter as tk +import sqlite3 + + +def fin_05(): + """A small art gallery is selling works from different artists and wants to + keep track of the paintings using an SQL database. You need to create a + user-friendly system to keep track of the art. This should include using a + GUI. Below is the current data that needs to be stored in a database. + Artists Contact Details: + + ArtistID Name Address Town Country Postcode + ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + 1 Martin Leighton 5 Park Place Peterborough Cambridgeshire PE32 5LP + 2 Eva Czariecka 77 Warner Close Chelmsford Essex CM22 5FT + 3 Roxy Parkin 90 Hindhead Road London SE12 6WM + 4 Nigel Farnworth 41 Whitby Road Huntly Aberdeenshire AB54 5PN + 5 Teresa Tanner 70 Guild Street London NW7 1SP + + PieceID ArtistID Title Medium Price + ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + 1 5 Woman with black Labrador Oil 220 + 2 5 Bees & thistles Watercolour 85 + 3 2 A stroll to Westminster Ink 190 + 4 1 African giant Oil 800 + 5 3 Water daemon Acrylic 1700 + 6 4 A seagull Watercolour 35 + 7 1 Three friends Oil 1800 + 8 2 Summer breeze 1 Acrylic 1350 + 9 4 Mr Hamster Watercolour 35 + 10 1 Pulpit Rock, Dorset Oil 600 + 11 5 Trawler Dungeness beach Oil 195 + 12 2 Dance in the snow Oil 250 + 13 4 St Tropez port Ink 45 + 14 3 Pirate assassin Acrylic 420 + 15 1 Morning walk Oil 800 + 16 4 A baby barn swallow Watercolour 35 + 17 4 The old working mills Ink 395 + + The art gallery must be able to add new artists and pieces of art. Once a + piece of art has been sold, the data about that art should be removed from + the main SQL database and stored in a separate text file. Users should be + able to search by artist, medium or price.""" + db_path = f"{pwd()}/final/files/artgallery.db" + file_path = f"{pwd()}/final/files/art_sales.txt" + + def exec_query(query, params=None): + with sqlite3.connect(db_path) as db: + cursor = db.cursor() + if params is not None: + result = cursor.execute(query, params) + else: + result = cursor.execute(query) + db.commit() + return result + + def exec_queries(query, params): + with sqlite3.connect(db_path) as db: + cursor = db.cursor() + result = cursor.executemany(query, params) + db.commit() + return result + + def create_tables(): + query="""CREATE TABLE IF NOT EXISTS artists( + id INTEGER PRIMARY KEY, + name TEXT NOT NULL, + address TEXT NOT NULL, + town TEXT, + country TEXT NOT NULL, + postcode TEXT NOT NULL)""" + exec_query(query) + query="""CREATE TABLE IF NOT EXISTS pieces( + id INTEGER PRIMARY KEY, + artistid INTEGER, + title TEXT NOT NULL, + medium TEXT NOT NULL, + price FLOAT, + FOREIGN KEY (artistid) REFERENCES artists(id))""" + exec_query(query) + + def populate_tables(): + query = """INSERT INTO artists(name, address, town, country, postcode) + VALUES(?,?,?,?,?)""" + artists = [ + ('Martin Leighton', '5 Park Place', 'Peterborough', 'Cambridgeshire', 'PE32 5LP'), + ('Eva Czariecka', '77 Warner Close', 'Chelmsford', 'Essex', 'CM22 5FT'), + ('Roxy Parkin', '90 Hindhead Road', None, 'London', 'SE12 6WM'), + ('Nigel Farnworth', '41 Whitby Road', 'Huntly', 'Aberdeenshire', 'AB54 5PN'), + ('Teresa Tanner', '70 Guild Street', None ,'London', 'NW7 1SP') + ] + exec_queries(query, artists) + query = "INSERT INTO pieces(artistid, title, medium, price) VALUES(?,?,?,?)" + pieces = [ + ('5', 'Woman with black Labrador', 'Oil', 220), + ('5', 'Bees & thistles', 'Watercolour', 85), + ('2', 'A stroll to Westminster', 'Ink' , 190), + ('1', 'African giant', 'Oil', 800), + ('3', 'Water daemon', 'Acrylic', 1700), + ('4', 'A seagull', 'Watercolour', 35), + ('1', 'Three friends', 'Oil', 1800), + ('2', 'Summer breeze 1', 'Acrylic', 1350), + ('4', 'Mr Hamster', 'Watercolour', 35), + ('1', 'Pulpit Rock, Dorset', 'Oil', 600), + ('5', 'Trawler Dungeness beach', 'Oil', 195), + ('2', 'Dance in the snow', 'Oil', 250), + ('4', 'St Tropez port', 'Ink', 45), + ('3', 'Pirate assassin', 'Acrylic', 420), + ('1', 'Morning walk', 'Oil', 800), + ('4', 'A baby barn swallow', 'Watercolour', 35), + ('4', 'The old working mills', 'Ink', 395) + ] + exec_queries(query, pieces) + + def drop_tables(): + query = "DROP TABLE IF EXISTS pieces" + exec_query(query) + query = "DROP TABLE IF EXISTS artists" + exec_query(query) + + def add_artist(artist): + query = """INSERT INTO artists (name, address, town, country, postcode) + VALUES(?,?,?,?,?)""" + exec_query(query, artist) + + def add_piece(piece): + query = """INSERT INTO pieces (artistid, title, medium, price) + VALUES(?,?,?,?)""" + exec_query(query, piece) + + def del_piece(piece_id): + query = "DELETE FROM pieces WHERE id=?" + exec_query(query, [piece_id]) + + def sell_piece(): + piece = lst_out.get(tk.ANCHOR) + try: + piece_id = piece.split()[0] + query = "SELECT * FROM pieces WHERE id=?" + result = exec_query(query, [piece_id]) + data = result.fetchone() + with open(file_path, 'a+') as file: + file.write(f"{data[0]}, {data[1]}, {data[2]}, {data[3]}, {data[4]}\n") + del_piece(piece_id) + clean_tk() + except IndexError: + return + + def search_by_id(artistid): + query = f"SELECT * FROM pieces WHERE artistid = ?" + result = exec_query(query, str(artistid)) + data = result.fetchall() + return data + + def search_by_medium(medium): + query = f"SELECT * FROM pieces WHERE medium LIKE ?" + result = exec_query(query, ['%'+medium+'%']) + data = result.fetchall() + return data + + def search_by_price(price): + query = f"SELECT * FROM pieces WHERE price <= ?" + result = exec_query(query, [price]) + data = result.fetchall() + return data + + def restart_data(): + drop_tables() + create_tables() + populate_tables() + with open(file_path, 'w') as file: + file.write('') + #add_artist(("BOB", "Mi casa", None, "Taiwan", "06660")) + #add_piece((6, "Su Casa", "Bic sobre cuaderno", 6666.666)) + #sell_piece(18) + + restart_data() + + com_bg = "dodger blue" + alt_bg = "deep sky blue" + window = tk.Tk() + window.title("Galeria de Arte") + window.geometry("800x600") + window["bg"] = com_bg + + lbl_artist_id = tk.Label(text="Id Artista", font="Verdana 12") + lbl_artist_id.place(x=40, y=230, width=80, height=25) + lbl_artist_id["bg"] = com_bg + txt_artist_id = tk.Entry(font="Verdana 12") + txt_artist_id["justify"] = "center" + txt_artist_id.place(x=120, y=230, width=40, height=25) + + lbl_piece_title = tk.Label(text="Titulo", font="Verdana 12") + lbl_piece_title.place(x=40, y=270, width=60, height=25) + lbl_piece_title['bg'] = com_bg + txt_piece_title = tk.Entry(font="Verdana 12") + txt_piece_title.place(x=100, y=270, width=400, height=25) + + def add_art_piece(): + artist_id = txt_artist_id.get() + if artist_id == '': + txt_artist_id.focus() + return + title = txt_piece_title.get() + if title == '': + txt_piece_title.focus() + return + medium = txt_medium.get() + if medium == '': + txt_medium.focus() + return + price = txt_price.get() + if price == '' or not price.isdigit(): + txt_price.delete(0, 'end') + txt_price.focus() + return + clean_tk() + add_piece((artist_id,title,medium,price)) + + btn_add_piece = tk.Button(text="Agregar Pieza", + font="Verdana 12", + command=add_art_piece) + btn_add_piece['bg'] = alt_bg + btn_add_piece.place(x=510, y=270, width=250, height=25) + + def display_artists(): + query = "SELECT * FROM artists" + data = exec_query(query) + lst_out.delete(0, 'end') + for art in data: + artist_id = (str(art[0]).rjust(3)).ljust(5) + name = (art[1].rjust(15)).ljust(20) + address = art[2].rjust(20) + town = art[3].rjust(20) if art[3] is not None else " " + country = art[4].rjust(20) + postcode = art[5].rjust(20) + line = artist_id+name+address+town+country+postcode + lst_out.insert('end', line) + + def display_pieces(data): + lst_out.delete(0, 'end') + for art in data: + piece_id = (str(art[0]).rjust(5)).ljust(8) + artist_id = (str(art[1]).rjust(5)).ljust(8) + title = art[2].rjust(40) + medium = art[3].rjust(20) + price = str(art[4]).rjust(20) + line = piece_id+artist_id+title+medium+price + lst_out.insert('end', line) + + def search(): + search_by = sel_by.get() + lst_out.delete(0, 'end') + match search_by: + case 'ID Artista': + artistid = txt_artist_id.get() + if artistid.isdigit(): + data = search_by_id(artistid) + display_pieces(data) + case 'Material': + medium = txt_medium.get() + data = search_by_medium(medium) + display_pieces(data) + case 'Precio': + price = txt_price.get() + data = search_by_price(price) + display_pieces(data) + case _: + lst_by.focus() + + btn_find = tk.Button(text="Buscar por", font="Verdana 12", command=search) + btn_find.place(x=510, y=230, width=135, height=25) + btn_find['bg'] = alt_bg + + sel_by = tk.StringVar(window) + sel_by.set("------------") + by_ops = [ 'ID Artista', 'Material', 'Precio' ] + lst_by = tk.OptionMenu(window, sel_by, *by_ops) + lst_by.place(x=645, y=230, width=115, height=25) + lst_by['bg'] = alt_bg + + lbl_medium = tk.Label(text="Material", font="Verdana 12") + lbl_medium['bg'] = com_bg + lbl_medium.place(x=160, y=230, width=80, height=25) + txt_medium = tk.Entry(font="Verdana 12") + txt_medium["justify"] = "center" + txt_medium.place(x=240, y=230, width=120, height=25) + + lbl_price = tk.Label(text="Precio <", font="Verdana 12") + lbl_price['bg'] = com_bg + lbl_price.place(x=370, y=230, width=70, height=25) + txt_price = tk.Entry(font="Verdana 12") + txt_price["justify"] = "center" + txt_price.place(x=440, y=230, width=60, height=25) + + btn_showall = tk.Button(text="Mostrar todos los artistas", + font="Verdana 12", + command=display_artists) + btn_showall['bg'] = alt_bg + btn_showall.place(x=510, y=190, width=250, height=25) + + lst_out = tk.Listbox(font="Verdana 12") + lst_out["justify"] = "left" + lst_out["bg"] = "tomato" + lst_out.place(x=40, y=310, width=720, height=280) + + def new_artist(): + name = txt_in_name.get() + if name == '': + txt_in_name.focus() + return + addrs = txt_in_adrs.get() + if addrs == '': + txt_in_adrs.focus() + return + town = txt_in_town.get() #opt + country = txt_in_country.get() + if country == '': + txt_in_country.focus() + return + postal = txt_in_code.get() + if country == '': + txt_in_code.focus() + return + new_artist = (name, addrs, town, country, postal) + add_artist(new_artist) + clean_tk() + + lbl_title = tk.Label(text="Gestion de Artistas y Obras") + lbl_title['bg'] = com_bg + lbl_title['font'] = "Verdana 30" + lbl_title.place(x=120, y=10, width=550, height=80) + y1 = 100 + y2 = y1+(y1/2) + x1 = 40 + lbl_art_name = tk.Label(text="Nombre", font="Verdana 12") + lbl_art_name.place(x=x1, y=y1, width=70, height=25) + lbl_art_name['bg'] = com_bg + txt_in_name = tk.Entry(font="Verdana 12") + txt_in_name.place(x=x1+70, y=y1, width=220, height=25) + lbl_art_adrs = tk.Label(text="Dirección", font="Verdana 12") + lbl_art_adrs['bg'] = com_bg + lbl_art_adrs.place(x=340, y=y1, width=70, height=25) + txt_in_adrs = tk.Entry(font="Verdana 12") + txt_in_adrs.place(x=420, y=y1, width=340, height=25) + lbl_art_town = tk.Label(text="Ciudad", font="Verdana 12") + lbl_art_town['bg'] = com_bg + lbl_art_town.place(x=x1, y=y2, width=80, height=25) + txt_in_town = tk.Entry(font="Verdana 12") + txt_in_town.place(x=x1+70, y=y2, width=180, height=25) + lbl_art_country = tk.Label(text="Región", font="Verdana 12") + lbl_art_country['bg'] = com_bg + lbl_art_country.place(x=x1+260, y=y2, width=70, height=25) + txt_in_country = tk.Entry(font="Verdana 12") + txt_in_country.place(x=x1+330, y=y2, width=160, height=25) + lbl_art_code = tk.Label(text="Cod. Postal", font="Verdana 12") + lbl_art_code['bg'] = com_bg + lbl_art_code.place(x=540, y=y2, width=90, height=25) + txt_in_code = tk.Entry(font="Verdana 12") + txt_in_code.place(x=630, y=y2, width=130, height=25) + + btn_add_art = tk.Button(text="Agregar artista", + font="Verdana 12", + command=new_artist) + btn_add_art["bg"] = alt_bg + btn_add_art.place(x=x1, y=190, width=250, height=25) + + btn_del_art = tk.Button(text="Vender pieza", + font="Verdana 12", + command=sell_piece) + btn_del_art["bg"] = alt_bg + btn_del_art.place(x=280, y=190, width=250, height=25) + + def clean_tk(): + txt_in_name.delete(0,'end') + txt_in_adrs.delete(0,'end') + txt_in_town.delete(0,'end') + txt_in_country.delete(0,'end') + txt_in_code.delete(0,'end') + lst_out.delete(0, 'end') + txt_artist_id.delete(0,'end') + txt_piece_title.delete(0,'end') + txt_medium.delete(0,'end') + txt_price.delete(0,'end') + + window.mainloop() diff --git a/final/final.py b/final/final.py new file mode 100644 index 0000000..61745ce --- /dev/null +++ b/final/final.py @@ -0,0 +1,47 @@ +from . import ( + fin01 as ex01, + fin02 as ex02, + fin03 as ex03, + fin04 as ex04, + fin05 as ex05 +) + +from common.common import ( + user_input, + run_func, + print_run_func, + opcs_default, + clear +) + +tab = ' ' + +def challenges(): + select_ok = False + while not select_ok: + clear() + print(tab, '1)', "Shift Code #146") + print(tab, '2)', "Mastermind #147") + print(tab, '3)', "Passwords #148") + print(tab, '4)', "Times Tables GUI #149") + print(tab, '5)', "Art Gallery #150") + opcs_default(1) + selection = user_input(9) + match selection: + case 1: + run_func(ex01.fin_01) + case 2: + run_func(ex02.fin_02) + case 3: + print_run_func(ex03.fin_03) + case 4: + print_run_func(ex04.fin_04) + case 5: + print_run_func(ex05.fin_05) + case 'v': + return + case 's': + select_ok = True + exit(0) + case _: + continue diff --git a/images.md b/images.md new file mode 100644 index 0000000..4dabf08 --- /dev/null +++ b/images.md @@ -0,0 +1,13 @@ +![img](./imgs/basics_001-059.png) +![img](./imgs/final_146-150.png) +![img](./imgs/final_150.png) +![img](./imgs/interm_069-123.png) +![img](./imgs/main_menu.png) +![img](./imgs/pattern_067.png) +![img](./imgs/sqlite_145.png) +![img](./imgs/tkinter_124-138.png) +![img](./imgs/tkinter125.png) +![img](./imgs/tkinter_128.png) +![img](./imgs/tkinter_134.png) +![img](./imgs/tkinter_138.png) +![img](./imgs/turtle_060-068.png) diff --git a/imgs/basics_001-059.png b/imgs/basics_001-059.png new file mode 100644 index 0000000..47087ac Binary files /dev/null and b/imgs/basics_001-059.png differ diff --git a/imgs/final_146-150.png b/imgs/final_146-150.png new file mode 100644 index 0000000..ebaf33a Binary files /dev/null and b/imgs/final_146-150.png differ diff --git a/imgs/final_150.png b/imgs/final_150.png new file mode 100644 index 0000000..c1d7720 Binary files /dev/null and b/imgs/final_150.png differ diff --git a/imgs/interm_069-123.png b/imgs/interm_069-123.png new file mode 100644 index 0000000..f3c196a Binary files /dev/null and b/imgs/interm_069-123.png differ diff --git a/imgs/main_menu.png b/imgs/main_menu.png new file mode 100644 index 0000000..86e36f6 Binary files /dev/null and b/imgs/main_menu.png differ diff --git a/imgs/sqlite_145.png b/imgs/sqlite_145.png new file mode 100644 index 0000000..6bf34e6 Binary files /dev/null and b/imgs/sqlite_145.png differ diff --git a/imgs/tkinter125.png b/imgs/tkinter125.png new file mode 100644 index 0000000..cc81d28 Binary files /dev/null and b/imgs/tkinter125.png differ diff --git a/imgs/tkinter_124-138.png b/imgs/tkinter_124-138.png new file mode 100644 index 0000000..9198782 Binary files /dev/null and b/imgs/tkinter_124-138.png differ diff --git a/imgs/tkinter_128.png b/imgs/tkinter_128.png new file mode 100644 index 0000000..2fa36e7 Binary files /dev/null and b/imgs/tkinter_128.png differ diff --git a/imgs/tkinter_134.png b/imgs/tkinter_134.png new file mode 100644 index 0000000..3d21c95 Binary files /dev/null and b/imgs/tkinter_134.png differ diff --git a/imgs/tkinter_138.png b/imgs/tkinter_138.png new file mode 100644 index 0000000..43e5b1e Binary files /dev/null and b/imgs/tkinter_138.png differ diff --git a/imgs/turtle_060-068.png b/imgs/turtle_060-068.png new file mode 100644 index 0000000..46bcf26 Binary files /dev/null and b/imgs/turtle_060-068.png differ diff --git a/interm/interm04.py b/interm/interm04.py index 2d74028..dd8f241 100644 --- a/interm/interm04.py +++ b/interm/interm04.py @@ -2,14 +2,12 @@ class interm004: def two_d(self): """Create the following using a simple 2D list using the standard Python - indexing - - 0 1 2 - ━━━━━━━━━━━━━━━ - 0 2 5 8 - 1 3 7 4 - 2 1 6 9 - 3 4 2 0 - """ + indexing - 0 1 2 + ━━━━━━━━━━━━━━━ + 0 2 5 8 + 1 3 7 4 + 2 1 6 9 + 3 4 2 0""" bidims = [[2,5,8],[3,7,4],[1,6,9],[4,2,0]] print(" | 0 | 1 | 2 |\n━━━━━━━━━━━━━━━") for i in range(len(bidims)): diff --git a/interm/interm07.py b/interm/interm07.py index e815dee..39e2df5 100644 --- a/interm/interm07.py +++ b/interm/interm07.py @@ -1,9 +1,9 @@ class interm007: def sub_118(self): - """Define a subprogram that will ask the user to enter a number and - save it as the variable \'num\'. Define another subprogram that will - use \'num\' and count from 1 to that number.""" + """Define a subprogram that will ask the user to enter a number and save + it as the variable 'num'. Define another subprogram that will use 'num' + and count from 1 to that number.""" def save_num(): return int(input("Ingresa un número: ")) @@ -16,15 +16,13 @@ class interm007: def sub_119(self): """Define a subprogram that will ask the user to pick a low and a high number, and then generate a random number between those two values and - store it in a variable called \'comp_num\'. - Define another subprogram that will give the instruction \'I am thinking - of a number…\' and then ask the user to guess the number they are - thinking of. - Define a third subprogram that will check to see if the comp_num is the - same as the user's guess. If it is, it should display the message - \'Correct, you win\', otherwise it should keep looping, telling the user - if they are too low or too high and asking them to guess again until they - guess correctly.""" + store it in a variable called 'comp_num'. Define another subprogram that + will give the instruction 'I am thinking of a number…' and then ask the + user to guess the number they are thinking of. Define a third subprogram + that will check to see if the comp_num is the same as the user's guess. + If it is, it should display the message 'Correct, you win', otherwise it + should keep looping, telling the user if they are too low or too high + and asking them to guess again until they guess correctly.""" from random import randint def num_gen(): ini = int(input("Ingresa un número inicial: ")) @@ -50,25 +48,21 @@ class interm007: check_nums(secret, guess) def sub_120(self): - """Display the following menu to the user: - 1) Addition - 2) Subtraction - Enter 1 or 2: + """Display the following menu to the user: 1) Addition + 2) Subtraction + Enter 1 or 2: If they enter a 1, it should run a subprogram that will generate two random numbers between 5 and 20, and ask the user to add them together. Work out the correct answer and return both the user's answer and the - correct answer. - If they entered 2 as their selection on the menu, it should run a - subprogram that will generate one number between 25 and 50 and another - number between 1 and 25 and ask them to work out num1 minus num2. This - way they will not have to worry about negative answers. Return both the - user's answer and the correct answer. - Create another subprogram that will check if the user's answer matches - the actual answer. If it does, display \'Correct\', otherwise display a - message that will say \'Incorrect, the answer is\' and display the real - answer. - If they do not select a relevant option on the first menu you should - display a suitable message.""" + correct answer. If they entered 2 as their selection on the menu, it + should run a subprogram that will generate one number between 25 and 50 + and another number between 1 and 25 and ask them to work out num1 minus + num2. This way they will not have to worry about negative answers. Return + both the user's answer and the correct answer. Create another subprogram + that will check if the user's answer matches the actual answer. If it + does, display 'Correct', otherwise display a message that will say + 'Incorrect, the answer is' and display the real answer. If they do not + select a relevant option on the menu you should display a suitable message.""" from random import randint def menu(): print( @@ -170,11 +164,10 @@ class interm007: menu() def sub_122(self): - """Create the following menu: - 1) Add to file - 2) View all records - 3) Quit program - Enter the number of your selection: + """Create the following menu: 1) Add to file + 2) View all records + 3) Quit program + Enter the number of your selection: If the user selects 1, allow them to add to a file called Salaries.csv which will store their name and salary. If they select 2 it should display all records in the Salaries.csv file. If they select 3 it should @@ -229,14 +222,8 @@ class interm007: """In Python, it is not technically possible to directly delete a record from a .csv file. Instead you need to save the file to a temporary list in Python, make the changes to the list and then overwrite the original - file with the temporary list. - Change the previous program to allow you to do this. Your menu should - now look like this: - 1) Add to file - 2) View all records - 3) Delete a record - 4) Quit program - Enter the number of your selection:""" + file with the temporary list. Change the previous program to allow you to + do this. Your menu should have the new option: 3) Delete a record""" from os import getcwd as pwd from os.path import isfile import csv diff --git a/main.py b/main.py index efac10c..1b52b00 100755 --- a/main.py +++ b/main.py @@ -5,6 +5,7 @@ from trtl import trtl from interm import interm from tkgui import tkgui from sqlite import sqlite +from final import final from common.common import clear, user_input, opcs_default def toc(): @@ -147,8 +148,7 @@ def main(): case 5: sqlite.challenges() case 6: - #final_challenges() - pass + final.challenges() case 's': select_ok = True exit(0)