diff --git a/.gitignore b/.gitignore index 9b41e9d..72b5e14 100644 --- a/.gitignore +++ b/.gitignore @@ -15,4 +15,5 @@ chapter22_type_hints/.mypy_cache/ *.pyc *.zip *.db -*.pdf \ No newline at end of file +*.pdf +*.mypy_cache \ No newline at end of file diff --git a/appendix_b_git/arithmetic.py b/appendix_b_git/arithmetic.py new file mode 100644 index 0000000..f49c8db --- /dev/null +++ b/appendix_b_git/arithmetic.py @@ -0,0 +1,13 @@ +# arithmetic.py + +def add(x, y): + return x + y + +def divide(x, y): + return x / y + +def multiply(x, y): + return x * y + +def subtract(x, y): + return x - y diff --git a/chapter27_decorators/classmethod_example.py b/chapter27_decorators/classmethod_example.py new file mode 100644 index 0000000..e35b726 --- /dev/null +++ b/chapter27_decorators/classmethod_example.py @@ -0,0 +1,27 @@ +class Time: + """an example 24-hour time class""" + + def __init__(self, hour, minute): + self.hour = hour + self.minute = minute + + def __repr__(self): + return "Time(%d, %d)" % (self.hour, self.minute) + + @classmethod + def from_float(cls, moment): + """2.5 == 2 hours, 30 minutes, 0 seconds, 0 microseconds""" + hours = int(moment) + if hours: + moment = moment % hours + minutes = int(moment * 60) + return cls(hours, minutes) + + def to_float(self): + """return self as a floating point number""" + return self.hour + self.minute / 60 + +Time(7, 30) +Time.from_float(5.75) +t = Time(10, 15) +t.to_float() \ No newline at end of file diff --git a/chapter27_decorators/decorator_syntax_with_arguments.py b/chapter27_decorators/decorator_syntax_with_arguments.py index ee861f2..bfd6ea8 100644 --- a/chapter27_decorators/decorator_syntax_with_arguments.py +++ b/chapter27_decorators/decorator_syntax_with_arguments.py @@ -7,8 +7,11 @@ def func_info(arg1, arg2): def the_real_decorator(function): def wrapper(*args, **kwargs): - print('Function {} args: {} kwargs: {}'.format( - function.__name__, str(args), str(kwargs))) + print('Function {} args: {} kwargs: {}' + .format( + function.__name__, + str(args), + str(kwargs))) return function(*args, **kwargs) return wrapper diff --git a/chapter32_argparse/file_parser.py b/chapter32_argparse/file_parser.py index 5dc6f85..9b3db74 100644 --- a/chapter32_argparse/file_parser.py +++ b/chapter32_argparse/file_parser.py @@ -2,7 +2,7 @@ import argparse -def file_parser(input_file, output_file = ''): +def file_parser(input_file, output_file=''): print(f'Processing {input_file}') print('Finished processing') if output_file: diff --git a/chapter32_argparse/file_parser_aliases.py b/chapter32_argparse/file_parser_aliases.py index 97e7fa7..d56f8b9 100644 --- a/chapter32_argparse/file_parser_aliases.py +++ b/chapter32_argparse/file_parser_aliases.py @@ -2,7 +2,7 @@ import argparse -def file_parser(input_file, output_file = ''): +def file_parser(input_file, output_file=''): print(f'Processing {input_file}') print('Finished processing') if output_file: diff --git a/chapter32_argparse/file_parser_aliases2.py b/chapter32_argparse/file_parser_aliases2.py index 43b80a6..e64d642 100644 --- a/chapter32_argparse/file_parser_aliases2.py +++ b/chapter32_argparse/file_parser_aliases2.py @@ -2,7 +2,7 @@ import argparse -def file_parser(input_file, output_file = ''): +def file_parser(input_file, output_file=''): print(f'Processing {input_file}') print('Finished processing') if output_file: diff --git a/chapter32_argparse/file_parser_exclusive.py b/chapter32_argparse/file_parser_exclusive.py index 4f03b89..7eb3b82 100644 --- a/chapter32_argparse/file_parser_exclusive.py +++ b/chapter32_argparse/file_parser_exclusive.py @@ -2,7 +2,7 @@ import argparse -def file_parser(input_file, output_file = ''): +def file_parser(input_file, output_file=''): print(f'Processing {input_file}') print('Finished processing') if output_file: diff --git a/chapter32_argparse/file_parser_no_help.py b/chapter32_argparse/file_parser_no_help.py index 9c89971..b69614e 100644 --- a/chapter32_argparse/file_parser_no_help.py +++ b/chapter32_argparse/file_parser_no_help.py @@ -2,7 +2,7 @@ import argparse -def file_parser(input_file, output_file = ''): +def file_parser(input_file, output_file=''): print(f'Processing {input_file}') print('Finished processing') if output_file: diff --git a/chapter32_argparse/file_parser_with_description.py b/chapter32_argparse/file_parser_with_description.py index f0c1b81..c755a64 100644 --- a/chapter32_argparse/file_parser_with_description.py +++ b/chapter32_argparse/file_parser_with_description.py @@ -2,7 +2,7 @@ import argparse -def file_parser(input_file, output_file = ''): +def file_parser(input_file, output_file=''): print(f'Processing {input_file}') print('Finished processing') if output_file: diff --git a/chapter35_scrape_website/downloading_files.py b/chapter35_scrape_website/downloading_files.py index b80f20d..ee766f7 100644 --- a/chapter35_scrape_website/downloading_files.py +++ b/chapter35_scrape_website/downloading_files.py @@ -4,8 +4,9 @@ def download_file(url): urllib.request.urlretrieve(url, "code.zip") def alternate_download(url): - f = urllib.request.urlopen(url) - data = f.read() + with urllib.request.urlopen(url) as response: + data = response.read() + with open("code2.zip", "wb") as code: code.write(data) diff --git a/chapter36_csv/csv_dict_reader.py b/chapter36_csv/csv_dict_reader.py index bf57378..0e0acc4 100644 --- a/chapter36_csv/csv_dict_reader.py +++ b/chapter36_csv/csv_dict_reader.py @@ -8,5 +8,5 @@ def process_csv_dict_reader(file_obj): print(f'{line["book_title"]} by {line["author"]}') if __name__ == '__main__': - with open('books.csv') as csvfile: + with open('books.csv', newline='') as csvfile: process_csv_dict_reader(csvfile) \ No newline at end of file diff --git a/chapter36_csv/csv_dict_writer.py b/chapter36_csv/csv_dict_writer.py index 70a97f8..964144b 100644 --- a/chapter36_csv/csv_dict_writer.py +++ b/chapter36_csv/csv_dict_writer.py @@ -3,7 +3,7 @@ import csv def csv_dict_writer(path, headers, data): - with open(path, 'w') as csvfile: + with open(path, 'w', newline='') as csvfile: writer = csv.DictWriter(csvfile, delimiter=',', fieldnames=headers) writer.writeheader() @@ -16,12 +16,12 @@ def csv_dict_writer(path, headers, data): wxPython Recipes,Mike Driscoll,Apress,2018,978-1-4842-3237-8 Python Interviews,Mike Driscoll,Packt Publishing,2018,9781788399081''' records = [] - for line in data.split('\n'): + for line in data.splitlines(): records.append(line.strip().split(',')) - headers = records[0] + headers = records.pop(0) list_of_dicts = [] - for row in records[1:]: + for row in records: my_dict = dict(zip(headers, row)) list_of_dicts.append(my_dict) diff --git a/chapter36_csv/csv_reader.py b/chapter36_csv/csv_reader.py index 6354ed0..ccdc251 100644 --- a/chapter36_csv/csv_reader.py +++ b/chapter36_csv/csv_reader.py @@ -3,7 +3,7 @@ import csv def process_csv(path): - with open(path) as csvfile: + with open(path, newline='') as csvfile: reader = csv.reader(csvfile) for row in reader: print(row) diff --git a/chapter36_csv/csv_reader_no_header.py b/chapter36_csv/csv_reader_no_header.py index 8a54d8a..5030e66 100644 --- a/chapter36_csv/csv_reader_no_header.py +++ b/chapter36_csv/csv_reader_no_header.py @@ -3,7 +3,7 @@ import csv def process_csv(path): - with open(path) as csvfile: + with open(path, newline='') as csvfile: reader = csv.reader(csvfile) # Skip the header next(reader, None) diff --git a/chapter36_csv/csv_writer.py b/chapter36_csv/csv_writer.py index 9e30b45..0fa594b 100644 --- a/chapter36_csv/csv_writer.py +++ b/chapter36_csv/csv_writer.py @@ -3,7 +3,7 @@ import csv def csv_writer(path, data): - with open(path, 'w') as csvfile: + with open(path, 'w', newline='') as csvfile: writer = csv.writer(csvfile, delimiter=',') for row in data: writer.writerow(row) @@ -14,6 +14,6 @@ def csv_writer(path, data): wxPython Recipes,Mike Driscoll,Apress,2018,978-1-4842-3237-8 Python Interviews,Mike Driscoll,Packt Publishing,2018,9781788399081''' records = [] - for line in data.split('\n'): + for line in data.splitlines(): records.append(line.strip().split(',')) csv_writer('output.csv', records) \ No newline at end of file diff --git a/chapter36_csv/csv_writer_rows.py b/chapter36_csv/csv_writer_rows.py index 47b8d75..0143498 100644 --- a/chapter36_csv/csv_writer_rows.py +++ b/chapter36_csv/csv_writer_rows.py @@ -3,7 +3,7 @@ import csv def csv_writer(path, data): - with open(path, 'w') as csvfile: + with open(path, 'w', newline='') as csvfile: writer = csv.writer(csvfile, delimiter=',') writer.writerows(data) @@ -13,6 +13,6 @@ def csv_writer(path, data): wxPython Recipes,Mike Driscoll,Apress,2018,978-1-4842-3237-8 Python Interviews,Mike Driscoll,Packt Publishing,2018,9781788399081''' records = [] - for line in data.split('\n'): + for line in data.splitlines(): records.append(line.strip().split(',')) csv_writer('output2.csv', records) \ No newline at end of file diff --git a/chapter36_csv/output.csv b/chapter36_csv/output.csv index e69de29..6d9c4de 100644 --- a/chapter36_csv/output.csv +++ b/chapter36_csv/output.csv @@ -0,0 +1,4 @@ +book_title,author,publisher,pub_date,isbn +Python 101,Mike Driscoll, Mike Driscoll,2020,123456789 +wxPython Recipes,Mike Driscoll,Apress,2018,978-1-4842-3237-8 +Python Interviews,Mike Driscoll,Packt Publishing,2018,9781788399081 diff --git a/chapter36_csv/output_dict.csv b/chapter36_csv/output_dict.csv index e69de29..6d9c4de 100644 --- a/chapter36_csv/output_dict.csv +++ b/chapter36_csv/output_dict.csv @@ -0,0 +1,4 @@ +book_title,author,publisher,pub_date,isbn +Python 101,Mike Driscoll, Mike Driscoll,2020,123456789 +wxPython Recipes,Mike Driscoll,Apress,2018,978-1-4842-3237-8 +Python Interviews,Mike Driscoll,Packt Publishing,2018,9781788399081 diff --git a/chapter37_sqlite/add_data.py b/chapter37_sqlite/add_data.py index 9e3a0c0..77c6a82 100644 --- a/chapter37_sqlite/add_data.py +++ b/chapter37_sqlite/add_data.py @@ -15,11 +15,11 @@ conn.commit() # insert multiple records using the more secure "?" method -albums = [('Python Interviews', 'Mike Driscoll', - '2/1/2018', 'Packt Publishing', 'softcover'), - ('Automate the Boring Stuff with Python', - 'Al Sweigart', '', 'No Starch Press', 'PDF'), - ('The Well-Grounded Python Developer', - 'Doug Farrell', '2020', 'Manning', 'Kindle')] -cursor.executemany("INSERT INTO books VALUES (?,?,?,?,?)", albums) +books = [('Python Interviews', 'Mike Driscoll', + '2/1/2018', 'Packt Publishing', 'softcover'), + ('Automate the Boring Stuff with Python', + 'Al Sweigart', '', 'No Starch Press', 'PDF'), + ('The Well-Grounded Python Developer', + 'Doug Farrell', '2020', 'Manning', 'Kindle')] +cursor.executemany("INSERT INTO books VALUES (?,?,?,?,?)", books) conn.commit() \ No newline at end of file diff --git a/chapter37_sqlite/create_database.py b/chapter37_sqlite/create_database.py index 54634c1..54ab740 100644 --- a/chapter37_sqlite/create_database.py +++ b/chapter37_sqlite/create_database.py @@ -2,7 +2,6 @@ import sqlite3 -# use :memory: to put it in RAM conn = sqlite3.connect("books.db") cursor = conn.cursor() diff --git a/chapter37_sqlite/queries.py b/chapter37_sqlite/queries.py index fcccd1d..7cb106f 100644 --- a/chapter37_sqlite/queries.py +++ b/chapter37_sqlite/queries.py @@ -8,7 +8,7 @@ def get_cursor(): def select_all_records_by_author(cursor, author): sql = "SELECT * FROM books WHERE author=?" - cursor.execute(sql, [(author)]) + cursor.execute(sql, [author]) print(cursor.fetchall()) # or use fetchone() print("\nHere is a listing of the rows in the table\n") for row in cursor.execute("SELECT rowid, * FROM books ORDER BY author"): diff --git a/chapter39_reportlab/platypus_multipage.py b/chapter39_reportlab/platypus_multipage.py new file mode 100644 index 0000000..9a0fe76 --- /dev/null +++ b/chapter39_reportlab/platypus_multipage.py @@ -0,0 +1,26 @@ +# platypus_multipage.py + +from reportlab.lib.pagesizes import letter +from reportlab.lib.styles import getSampleStyleSheet +from reportlab.lib.units import inch +from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer + + +def create_document(): + doc = SimpleDocTemplate("platypus_multipage.pdf", + pagesize=letter) + styles = getSampleStyleSheet() + flowables = [] + spacer = Spacer(1, 0.25*inch) + + # Create a lot of content to make a multipage PDF + for i in range(50): + text = 'Paragraph #{}'.format(i) + para = Paragraph(text, styles["Normal"]) + flowables.append(para) + flowables.append(spacer) + + doc.build(flowables) + +if __name__ == '__main__': + create_document() \ No newline at end of file diff --git a/chapter39_reportlab/simple_table.py b/chapter39_reportlab/simple_table.py new file mode 100644 index 0000000..19c201d --- /dev/null +++ b/chapter39_reportlab/simple_table.py @@ -0,0 +1,21 @@ +# simple_table.py + +from reportlab.lib.pagesizes import letter +from reportlab.platypus import SimpleDocTemplate, Table + +def simple_table(): + doc = SimpleDocTemplate("simple_table.pdf", pagesize=letter) + flowables = [] + + data = [['col_{}'.format(x) for x in range(1, 6)], + [str(x) for x in range(1, 6)], + ['a', 'b', 'c', 'd', 'e'] + ] + + tbl = Table(data) + flowables.append(tbl) + + doc.build(flowables) + +if __name__ == '__main__': + simple_table() \ No newline at end of file diff --git a/chapter39_reportlab/simple_table_with_style.py b/chapter39_reportlab/simple_table_with_style.py new file mode 100644 index 0000000..db47c7e --- /dev/null +++ b/chapter39_reportlab/simple_table_with_style.py @@ -0,0 +1,29 @@ +# simple_table_with_style.py + +from reportlab.lib import colors +from reportlab.lib.pagesizes import letter +from reportlab.platypus import SimpleDocTemplate, Table, TableStyle + +def simple_table_with_style(): + doc = SimpleDocTemplate("simple_table_with_style.pdf", + pagesize=letter) + flowables = [] + + data = [['col_{}'.format(x) for x in range(1, 6)], + [str(x) for x in range(1, 6)], + ['a', 'b', 'c', 'd', 'e'] + ] + + tblstyle = TableStyle( + [('BACKGROUND', (0, 0), (-1, 0), colors.red), + ('TEXTCOLOR', (0, 1), (-1, 1), colors.blue) + ]) + + tbl = Table(data) + tbl.setStyle(tblstyle) + flowables.append(tbl) + + doc.build(flowables) + +if __name__ == '__main__': + simple_table_with_style() \ No newline at end of file diff --git a/chapter40_graphs/bar_chart.py b/chapter40_graphs/bar_chart.py new file mode 100644 index 0000000..11808f3 --- /dev/null +++ b/chapter40_graphs/bar_chart.py @@ -0,0 +1,14 @@ +# bar_chart.py + +import matplotlib.pyplot as plt + +def bar_chart(numbers, labels, pos): + plt.bar(pos, numbers, color='blue') + plt.xticks(ticks=pos, labels=labels) + plt.show() + +if __name__ == '__main__': + numbers = [2, 1, 4, 6] + labels = ['Electric', 'Solar', 'Diesel', 'Unleaded'] + pos = list(range(4)) + bar_chart(numbers, labels, pos) \ No newline at end of file diff --git a/chapter40_graphs/bar_chart.title.py b/chapter40_graphs/bar_chart.title.py new file mode 100644 index 0000000..a68a7b9 --- /dev/null +++ b/chapter40_graphs/bar_chart.title.py @@ -0,0 +1,18 @@ +# bar_chart_title.py + +import matplotlib.pyplot as plt + +def bar_chart(numbers, labels, pos): + plt.bar(pos, [4, 5, 6, 3], color='green') + plt.bar(pos, numbers, color='blue') + plt.xticks(ticks=pos, labels=labels) + plt.title('Gas Used in Various Vehicles') + plt.xlabel('Vehicle Types') + plt.ylabel('Number of Vehicles') + plt.show() + +if __name__ == '__main__': + numbers = [2, 1, 4, 6] + labels = ['Electric', 'Solar', 'Diesel', 'Unleaded'] + pos = list(range(4)) + bar_chart(numbers, labels, pos) \ No newline at end of file diff --git a/chapter40_graphs/bar_chart_labels.py b/chapter40_graphs/bar_chart_labels.py new file mode 100644 index 0000000..2625f0f --- /dev/null +++ b/chapter40_graphs/bar_chart_labels.py @@ -0,0 +1,16 @@ +# bar_chart_labels.py + +import matplotlib.pyplot as plt + +def bar_chart(numbers, labels, pos): + plt.bar(pos, numbers, color='blue') + plt.xticks(ticks=pos, labels=labels) + plt.xlabel('Vehicle Types') + plt.ylabel('Number of Vehicles') + plt.show() + +if __name__ == '__main__': + numbers = [2, 1, 4, 6] + labels = ['Electric', 'Solar', 'Diesel', 'Unleaded'] + pos = list(range(4)) + bar_chart(numbers, labels, pos) \ No newline at end of file diff --git a/chapter40_graphs/bar_chart_legend.py b/chapter40_graphs/bar_chart_legend.py new file mode 100644 index 0000000..049be31 --- /dev/null +++ b/chapter40_graphs/bar_chart_legend.py @@ -0,0 +1,20 @@ +# bar_chart_legend.py + +import matplotlib.pyplot as plt + +def bar_chart(numbers, labels, pos): + plt.bar(pos, [4, 5, 6, 3], color='green') + plt.bar(pos, numbers, color='blue') + plt.xticks(ticks=pos, labels=labels) + plt.xlabel('Vehicle Types') + plt.ylabel('Number of Vehicles') + plt.title('Gas Used in Various Vehicles') + plt.legend(['First Label', 'Second Label'], + loc='upper left') + plt.show() + +if __name__ == '__main__': + numbers = [2, 1, 4, 6] + labels = ['Electric', 'Solar', 'Diesel', 'Unleaded'] + pos = list(range(4)) + bar_chart(numbers, labels, pos) \ No newline at end of file diff --git a/chapter40_graphs/bar_charth.py b/chapter40_graphs/bar_charth.py new file mode 100644 index 0000000..cdd2763 --- /dev/null +++ b/chapter40_graphs/bar_charth.py @@ -0,0 +1,14 @@ +# bar_charth.py + +import matplotlib.pyplot as plt + +def bar_charth(numbers, labels, pos): + plt.barh(pos, numbers, color='blue') + plt.yticks(ticks=pos, labels=labels) + plt.show() + +if __name__ == '__main__': + numbers = [2, 1, 4, 6] + labels = ['Electric', 'Solar', 'Diesel', 'Unleaded'] + pos = list(range(4)) + bar_charth(numbers, labels, pos) \ No newline at end of file diff --git a/chapter40_graphs/line_plot.py b/chapter40_graphs/line_plot.py new file mode 100644 index 0000000..03b7dd5 --- /dev/null +++ b/chapter40_graphs/line_plot.py @@ -0,0 +1,12 @@ +# line_plot.py + +import matplotlib.pyplot as plt + +def line_plot(numbers): + plt.plot(numbers) + plt.ylabel('Random numbers') + plt.show() + +if __name__ == '__main__': + numbers = [2, 4, 1, 6] + line_plot(numbers) \ No newline at end of file diff --git a/chapter40_graphs/multiple_figures.py b/chapter40_graphs/multiple_figures.py new file mode 100644 index 0000000..ea4bf17 --- /dev/null +++ b/chapter40_graphs/multiple_figures.py @@ -0,0 +1,16 @@ +# multiple_figures.py + +import matplotlib.pyplot as plt + +def line_plot(numbers, numbers2): + first_plot = plt.figure(1) + plt.plot(numbers) + + second_plot = plt.figure(2) + plt.plot(numbers2) + plt.show() + +if __name__ == '__main__': + numbers = [2, 4, 1, 6] + more_numbers = [5, 1, 10, 3] + line_plot(numbers, more_numbers) \ No newline at end of file diff --git a/chapter40_graphs/multiple_plots.py b/chapter40_graphs/multiple_plots.py new file mode 100644 index 0000000..1b9b9af --- /dev/null +++ b/chapter40_graphs/multiple_plots.py @@ -0,0 +1,18 @@ +# multiple_plots.py + +import matplotlib.pyplot as plt +import numpy as np + +def multiple_plots(): + # Some example data to display + x = np.linspace(0, 2 * np.pi, 400) + y = np.sin(x ** 2) + + fig, axs = plt.subplots(2) + fig.suptitle('Vertically stacked subplots') + axs[0].plot(x, y) + axs[1].plot(x, -y) + plt.show() + +if __name__ == '__main__': + multiple_plots() \ No newline at end of file diff --git a/chapter40_graphs/multiple_plots2.py b/chapter40_graphs/multiple_plots2.py new file mode 100644 index 0000000..f0487cf --- /dev/null +++ b/chapter40_graphs/multiple_plots2.py @@ -0,0 +1,15 @@ +# multiple_plots2.py + +import matplotlib.pyplot as plt + +def multiple_plots(): + numbers = [2, 4, 1, 6] + more_numbers = [5, 1, 10, 3] + fig, axs = plt.subplots(2) + fig.suptitle('Vertically stacked subplots') + axs[0].plot(numbers) + axs[1].plot(more_numbers) + plt.show() + +if __name__ == '__main__': + multiple_plots() \ No newline at end of file diff --git a/chapter40_graphs/pie_chart_fancy.py b/chapter40_graphs/pie_chart_fancy.py new file mode 100644 index 0000000..fef5f86 --- /dev/null +++ b/chapter40_graphs/pie_chart_fancy.py @@ -0,0 +1,19 @@ +# pie_chart.py + +import matplotlib.pyplot as plt + +def pie_chart(): + numbers = [40, 35, 15, 10] + labels = ['Python', 'Ruby', 'C++', 'PHP'] + # Explode the first slice (Python) + explode = (0.1, 0, 0, 0) + + fig1, ax1 = plt.subplots() + ax1.pie(numbers, explode=explode, labels=labels, + shadow=True, startangle=90, + autopct='%1.1f%%') + ax1.axis('equal') + plt.show() + +if __name__ == '__main__': + pie_chart() \ No newline at end of file diff --git a/chapter40_graphs/pie_chart_plain.py b/chapter40_graphs/pie_chart_plain.py new file mode 100644 index 0000000..d28980c --- /dev/null +++ b/chapter40_graphs/pie_chart_plain.py @@ -0,0 +1,14 @@ +# pie_chart_plain.py + +import matplotlib.pyplot as plt + +def pie_chart(): + numbers = [40, 35, 15, 10] + labels = ['Python', 'Ruby', 'C++', 'PHP'] + + fig1, ax1 = plt.subplots() + ax1.pie(numbers, labels=labels) + plt.show() + +if __name__ == '__main__': + pie_chart() \ No newline at end of file diff --git a/chapter41_images/blur.py b/chapter41_images/blur.py new file mode 100644 index 0000000..4dff2cf --- /dev/null +++ b/chapter41_images/blur.py @@ -0,0 +1,13 @@ +# blur.py + +from PIL import Image +from PIL import ImageFilter + + +def blur(path, modified_photo): + image = Image.open(path) + blurred_image = image.filter(ImageFilter.BLUR) + blurred_image.save(modified_photo) + +if __name__ == '__main__': + blur('butterfly.jpg', 'butterfly_blurred.jpg') \ No newline at end of file diff --git a/chapter41_images/border.py b/chapter41_images/border.py new file mode 100644 index 0000000..675d3ff --- /dev/null +++ b/chapter41_images/border.py @@ -0,0 +1,20 @@ +# border.py + +from PIL import Image, ImageOps + + +def add_border(input_image, output_image, border): + img = Image.open(input_image) + + if isinstance(border, int) or isinstance(border, tuple): + bimg = ImageOps.expand(img, border=border) + else: + raise RuntimeError('Border is not an integer or tuple!') + + bimg.save(output_image) + +if __name__ == '__main__': + in_img = 'butterfly_grey.jpg' + + add_border(in_img, output_image='butterfly_border.jpg', + border=100) diff --git a/chapter41_images/border2.py b/chapter41_images/border2.py new file mode 100644 index 0000000..671cbd1 --- /dev/null +++ b/chapter41_images/border2.py @@ -0,0 +1,20 @@ +# border2.py + +from PIL import Image, ImageOps + + +def add_border(input_image, output_image, border): + img = Image.open(input_image) + + if isinstance(border, int) or isinstance(border, tuple): + bimg = ImageOps.expand(img, border=border) + else: + raise RuntimeError('Border is not an integer or tuple!') + + bimg.save(output_image) + +if __name__ == '__main__': + in_img = 'butterfly_grey.jpg' + + add_border(in_img, output_image='butterfly_border2.jpg', + border=(10, 50)) diff --git a/chapter41_images/butterfly.jpg b/chapter41_images/butterfly.jpg new file mode 100644 index 0000000..9750c31 Binary files /dev/null and b/chapter41_images/butterfly.jpg differ diff --git a/chapter41_images/butterfly_grey.jpg b/chapter41_images/butterfly_grey.jpg new file mode 100644 index 0000000..f416524 Binary files /dev/null and b/chapter41_images/butterfly_grey.jpg differ diff --git a/chapter41_images/colored_border.py b/chapter41_images/colored_border.py new file mode 100644 index 0000000..2c2d37a --- /dev/null +++ b/chapter41_images/colored_border.py @@ -0,0 +1,25 @@ +# colored_border.py + +from PIL import Image, ImageOps + +def add_border(input_image, output_image, border, color=0): + img = Image.open(input_image) + + if isinstance(border, int) or isinstance( + border, tuple): + bimg = ImageOps.expand(img, + border=border, + fill=color) + else: + msg = 'Border is not an integer or tuple!' + raise RuntimeError(msg) + + bimg.save(output_image) + +if __name__ == '__main__': + in_img = 'butterfly_grey.jpg' + + add_border(in_img, + output_image='butterfly_border_red.jpg', + border=100, + color='indianred') \ No newline at end of file diff --git a/chapter41_images/cropping.py b/chapter41_images/cropping.py new file mode 100644 index 0000000..7eb5883 --- /dev/null +++ b/chapter41_images/cropping.py @@ -0,0 +1,12 @@ +# cropping.py + +from PIL import Image + + +def crop_image(path, cropped_path): + image = Image.open(path) + cropped = image.crop((40, 590, 979, 1500)) + cropped.save(cropped_path) + +if __name__ == '__main__': + crop_image('ducks.jpg', 'ducks_cropped.jpg') \ No newline at end of file diff --git a/chapter41_images/ducks.jpg b/chapter41_images/ducks.jpg new file mode 100644 index 0000000..86837a1 Binary files /dev/null and b/chapter41_images/ducks.jpg differ diff --git a/chapter41_images/get_histogram.py b/chapter41_images/get_histogram.py new file mode 100644 index 0000000..32964ab --- /dev/null +++ b/chapter41_images/get_histogram.py @@ -0,0 +1,16 @@ +# get_histrogram.py + +import matplotlib.pyplot as plt + +from PIL import Image + + +def get_image_histrogram(path): + image = Image.open(path) + histogram = image.histogram() + plt.hist(histogram, bins=len(histogram)) + plt.xlabel('Histogram') + plt.show() + +if __name__ == '__main__': + get_image_histrogram('butterfly.jpg') \ No newline at end of file diff --git a/chapter41_images/get_image_info.py b/chapter41_images/get_image_info.py new file mode 100644 index 0000000..1b6736f --- /dev/null +++ b/chapter41_images/get_image_info.py @@ -0,0 +1,12 @@ +# get_image_info.py + +from PIL import Image + +def get_image_info(path): + image = Image.open(path) + print(f'This image is {image.width} x {image.height}') + exif = image._getexif() + print(exif) + +if __name__ == '__main__': + get_image_info('ducks.jpg') \ No newline at end of file diff --git a/chapter41_images/jellyfish.jpg b/chapter41_images/jellyfish.jpg new file mode 100644 index 0000000..3cee85e Binary files /dev/null and b/chapter41_images/jellyfish.jpg differ diff --git a/chapter41_images/lizard.jpg b/chapter41_images/lizard.jpg new file mode 100644 index 0000000..535baf8 Binary files /dev/null and b/chapter41_images/lizard.jpg differ diff --git a/chapter41_images/open_image.py b/chapter41_images/open_image.py new file mode 100644 index 0000000..3459f7c --- /dev/null +++ b/chapter41_images/open_image.py @@ -0,0 +1,6 @@ +# open_image.py + +from PIL import Image + +image = Image.open('jellyfish.jpg') +image.show() \ No newline at end of file diff --git a/chapter41_images/resize_image.py b/chapter41_images/resize_image.py new file mode 100644 index 0000000..bec9953 --- /dev/null +++ b/chapter41_images/resize_image.py @@ -0,0 +1,23 @@ +# resize_image.py + +from PIL import Image + +def resize_image(input_image_path, + output_image_path, + size): + original_image = Image.open(input_image_path) + width, height = original_image.size + print(f'The original image size is {width} wide x {height} ' + f'high') + + resized_image = original_image.resize(size) + width, height = resized_image.size + print(f'The resized image size is {width} wide x {height} ' + f'high') + resized_image.show() + resized_image.save(output_image_path) + +if __name__ == '__main__': + resize_image(input_image_path='lizard.jpg', + output_image_path='lizard_small.jpg', + size=(800, 400)) \ No newline at end of file diff --git a/chapter41_images/scale_image.py b/chapter41_images/scale_image.py new file mode 100644 index 0000000..aa2febc --- /dev/null +++ b/chapter41_images/scale_image.py @@ -0,0 +1,37 @@ +# scale_image.py + +from PIL import Image + +def scale_image(input_image_path, + output_image_path, + width=None, + height=None + ): + original_image = Image.open(input_image_path) + w, h = original_image.size + print(f'The original image size is {w} wide x {h} ' + 'high') + + if width and height: + max_size = (width, height) + elif width: + max_size = (width, h) + elif height: + max_size = (w, height) + else: + # No width or height specified + raise ValueError('Width or height required!') + + original_image.thumbnail(max_size, Image.ANTIALIAS) + original_image.save(output_image_path) + + scaled_image = Image.open(output_image_path) + width, height = scaled_image.size + print(f'The scaled image size is {width} wide x {height} ' + 'high') + + +if __name__ == '__main__': + scale_image(input_image_path='lizard.jpg', + output_image_path='lizard_scaled.jpg', + width=800) \ No newline at end of file diff --git a/chapter41_images/sharpen.py b/chapter41_images/sharpen.py new file mode 100644 index 0000000..494b0fa --- /dev/null +++ b/chapter41_images/sharpen.py @@ -0,0 +1,13 @@ +# sharpen.py + +from PIL import Image +from PIL import ImageFilter + + +def sharpen(path, modified_photo): + image = Image.open(path) + sharpened_image = image.filter(ImageFilter.SHARPEN) + sharpened_image.save(modified_photo) + +if __name__ == '__main__': + sharpen('butterfly.jpg', 'butterfly_sharper.jpg') \ No newline at end of file diff --git a/chapter42_gui/button_events.py b/chapter42_gui/button_events.py new file mode 100644 index 0000000..b282fab --- /dev/null +++ b/chapter42_gui/button_events.py @@ -0,0 +1,41 @@ +# button_events.py + +import wx + + +class MyPanel(wx.Panel): + + def __init__(self, parent): + super().__init__(parent) + + button = wx.Button(self, label='Press Me') + button.Bind(wx.EVT_BUTTON, self.on_button1) + button2 = wx.Button(self, label='Second button') + button2.Bind(wx.EVT_BUTTON, self.on_button2) + + main_sizer = wx.BoxSizer(wx.HORIZONTAL) + main_sizer.Add(button, proportion=1, + flag=wx.ALL | wx.CENTER | wx.EXPAND, + border=5) + main_sizer.Add(button2, 0, wx.ALL, 5) + self.SetSizer(main_sizer) + + def on_button1(self, event): + print('You clicked the first button') + + def on_button2(self, event): + print('You clicked the second button') + + +class MyFrame(wx.Frame): + + def __init__(self): + super().__init__(None, title='Hello World') + panel = MyPanel(self) + self.Show() + + +if __name__ == '__main__': + app = wx.App(redirect=False) + frame = MyFrame() + app.MainLoop() \ No newline at end of file diff --git a/chapter42_gui/hello_with_panel.py b/chapter42_gui/hello_with_panel.py new file mode 100644 index 0000000..f6bb3e8 --- /dev/null +++ b/chapter42_gui/hello_with_panel.py @@ -0,0 +1,23 @@ +# hello_with_panel.py + +import wx + + +class MyPanel(wx.Panel): + + def __init__(self, parent): + super().__init__(parent) + button = wx.Button(self, label='Press Me') + +class MyFrame(wx.Frame): + + def __init__(self): + super().__init__(None, title='Hello World') + panel = MyPanel(self) + self.Show() + + +if __name__ == '__main__': + app = wx.App(redirect=False) + frame = MyFrame() + app.MainLoop() \ No newline at end of file diff --git a/chapter42_gui/hello_wx.py b/chapter42_gui/hello_wx.py new file mode 100644 index 0000000..7dfe858 --- /dev/null +++ b/chapter42_gui/hello_wx.py @@ -0,0 +1,8 @@ +# hello_wx.py + +import wx + +app = wx.App(False) +frame = wx.Frame(parent=None, title='Hello World') +frame.Show() +app.MainLoop() diff --git a/chapter42_gui/hello_wx_class.py b/chapter42_gui/hello_wx_class.py new file mode 100644 index 0000000..7a23617 --- /dev/null +++ b/chapter42_gui/hello_wx_class.py @@ -0,0 +1,15 @@ +# hello_wx_class.py + +import wx + +class MyFrame(wx.Frame): + + def __init__(self): + super().__init__(None, title='Hello World') + self.Show() + +if __name__ == '__main__': + app = wx.App(False) + frame = MyFrame() + frame.Show() + app.MainLoop() diff --git a/chapter42_gui/image_viewer.py b/chapter42_gui/image_viewer.py new file mode 100644 index 0000000..38dd872 --- /dev/null +++ b/chapter42_gui/image_viewer.py @@ -0,0 +1,34 @@ +# image_viewer.py + +import wx + +class ImagePanel(wx.Panel): + + def __init__(self, parent, image_size): + super().__init__(parent) + + img = wx.Image(*image_size) + self.image_ctrl = wx.StaticBitmap(self, + bitmap=wx.Bitmap(img)) + browse_btn = wx.Button(self, label='Browse') + + main_sizer = wx.BoxSizer(wx.VERTICAL) + main_sizer.Add(self.image_ctrl, 0, wx.ALL, 5) + main_sizer.Add(browse_btn) + self.SetSizer(main_sizer) + main_sizer.Fit(parent) + self.Layout() + + +class MainFrame(wx.Frame): + + def __init__(self): + super().__init__(None, title='Image Viewer') + panel = ImagePanel(self, image_size=(240,240)) + self.Show() + + +if __name__ == '__main__': + app = wx.App(redirect=False) + frame = MainFrame() + app.MainLoop() \ No newline at end of file diff --git a/chapter42_gui/image_viewer_working.py b/chapter42_gui/image_viewer_working.py new file mode 100644 index 0000000..3abd01f --- /dev/null +++ b/chapter42_gui/image_viewer_working.py @@ -0,0 +1,78 @@ +# image_viewer_working.py + +import wx + +class ImagePanel(wx.Panel): + + def __init__(self, parent, image_size): + super().__init__(parent) + self.max_size = 240 + + img = wx.Image(*image_size) + self.image_ctrl = wx.StaticBitmap(self, + bitmap=wx.Bitmap(img)) + + browse_btn = wx.Button(self, label='Browse') + browse_btn.Bind(wx.EVT_BUTTON, self.on_browse) + + self.photo_txt = wx.TextCtrl(self, size=(200, -1)) + + main_sizer = wx.BoxSizer(wx.VERTICAL) + hsizer = wx.BoxSizer(wx.HORIZONTAL) + + main_sizer.Add(self.image_ctrl, 0, wx.ALL, 5) + hsizer.Add(browse_btn, 0, wx.ALL, 5) + hsizer.Add(self.photo_txt, 0, wx.ALL, 5) + main_sizer.Add(hsizer, 0, wx.ALL, 5) + + self.SetSizer(main_sizer) + main_sizer.Fit(parent) + self.Layout() + + def on_browse(self, event): + """ + Browse for an image file + @param event: The event object + """ + wildcard = "JPEG files (*.jpg)|*.jpg" + with wx.FileDialog(None, "Choose a file", + wildcard=wildcard, + style=wx.ID_OPEN) as dialog: + if dialog.ShowModal() == wx.ID_OK: + self.photo_txt.SetValue(dialog.GetPaths()[0]) + self.load_image() + + def load_image(self): + """ + Load the image and display it to the user + """ + filepath = self.photo_txt.GetValue() + img = wx.Image(filepath, wx.BITMAP_TYPE_ANY) + + # scale the image, preserving the aspect ratio + W = img.GetWidth() + H = img.GetHeight() + if W > H: + NewW = self.max_size + NewH = self.max_size * H / W + else: + NewH = self.max_size + NewW = self.max_size * W / H + img = img.Scale(NewW,NewH) + + self.image_ctrl.SetBitmap(wx.Bitmap(img)) + self.Refresh() + + +class MainFrame(wx.Frame): + + def __init__(self): + super().__init__(None, title='Image Viewer') + panel = ImagePanel(self, image_size=(240,240)) + self.Show() + + +if __name__ == '__main__': + app = wx.App(redirect=False) + frame = MainFrame() + app.MainLoop() \ No newline at end of file diff --git a/chapter42_gui/sizer_with_two_widgets.py b/chapter42_gui/sizer_with_two_widgets.py new file mode 100644 index 0000000..f69c706 --- /dev/null +++ b/chapter42_gui/sizer_with_two_widgets.py @@ -0,0 +1,33 @@ +# sizer_with_two_widgets.py + +import wx + + +class MyPanel(wx.Panel): + + def __init__(self, parent): + super().__init__(parent) + + button = wx.Button(self, label='Press Me') + button2 = wx.Button(self, label='Second button') + + main_sizer = wx.BoxSizer(wx.HORIZONTAL) + main_sizer.Add(button, proportion=1, + flag=wx.ALL | wx.CENTER | wx.EXPAND, + border=5) + main_sizer.Add(button2, 0, wx.ALL, 5) + self.SetSizer(main_sizer) + + +class MyFrame(wx.Frame): + + def __init__(self): + super().__init__(None, title='Hello World') + panel = MyPanel(self) + self.Show() + + +if __name__ == '__main__': + app = wx.App(redirect=False) + frame = MyFrame() + app.MainLoop() \ No newline at end of file diff --git a/chapter42_gui/stacked_buttons.py b/chapter42_gui/stacked_buttons.py new file mode 100644 index 0000000..f8ce7de --- /dev/null +++ b/chapter42_gui/stacked_buttons.py @@ -0,0 +1,25 @@ +# stacked_buttons.py + +import wx + + +class MyPanel(wx.Panel): + + def __init__(self, parent): + super().__init__(parent) + button = wx.Button(self, label='Press Me') + button2 = wx.Button(self, label='Press Me too') + button3 = wx.Button(self, label='Another button') + +class MyFrame(wx.Frame): + + def __init__(self): + super().__init__(None, title='Hello World') + panel = MyPanel(self) + self.Show() + + +if __name__ == '__main__': + app = wx.App(redirect=False) + frame = MyFrame() + app.MainLoop() \ No newline at end of file diff --git a/chapter43_packages/arithmetic/__init__.py b/chapter43_packages/arithmetic/__init__.py new file mode 100644 index 0000000..be3c5fc --- /dev/null +++ b/chapter43_packages/arithmetic/__init__.py @@ -0,0 +1,2 @@ +# __init__.py +from . import add \ No newline at end of file diff --git a/chapter43_packages/arithmetic/add.py b/chapter43_packages/arithmetic/add.py new file mode 100644 index 0000000..07fb1d6 --- /dev/null +++ b/chapter43_packages/arithmetic/add.py @@ -0,0 +1,4 @@ +# add.py + +def add(x, y): + return x + y \ No newline at end of file diff --git a/chapter43_packages/arithmetic/divide.py b/chapter43_packages/arithmetic/divide.py new file mode 100644 index 0000000..1f6707b --- /dev/null +++ b/chapter43_packages/arithmetic/divide.py @@ -0,0 +1,4 @@ +# divide.py + +def divide(x, y): + return x / y \ No newline at end of file diff --git a/chapter43_packages/arithmetic/multiply.py b/chapter43_packages/arithmetic/multiply.py new file mode 100644 index 0000000..0a4dcbc --- /dev/null +++ b/chapter43_packages/arithmetic/multiply.py @@ -0,0 +1,4 @@ +# multiply.py + +def multiply(x, y): + return x * y \ No newline at end of file diff --git a/chapter43_packages/arithmetic/subtract.py b/chapter43_packages/arithmetic/subtract.py new file mode 100644 index 0000000..f9340ac --- /dev/null +++ b/chapter43_packages/arithmetic/subtract.py @@ -0,0 +1,4 @@ +# subtract.py + +def subtract(x, y): + return x - y \ No newline at end of file diff --git a/chapter43_packages/module/arithmetic.py b/chapter43_packages/module/arithmetic.py new file mode 100644 index 0000000..f49c8db --- /dev/null +++ b/chapter43_packages/module/arithmetic.py @@ -0,0 +1,13 @@ +# arithmetic.py + +def add(x, y): + return x + y + +def divide(x, y): + return x / y + +def multiply(x, y): + return x * y + +def subtract(x, y): + return x - y diff --git a/chapter43_packages/module/test_arithmetic.py b/chapter43_packages/module/test_arithmetic.py new file mode 100644 index 0000000..f259dcf --- /dev/null +++ b/chapter43_packages/module/test_arithmetic.py @@ -0,0 +1,22 @@ +# test_arithmetic.py + +import arithmetic +import unittest + +class TestArithmetic(unittest.TestCase): + + def test_addition(self): + self.assertEqual(arithmetic.add(1, 2), 3) + + def test_subtraction(self): + self.assertEqual(arithmetic.subtract(2, 1), 1) + + def test_multiplication(self): + self.assertEqual(arithmetic.multiply(5, 5), 25) + + def test_division(self): + self.assertEqual(arithmetic.divide(8, 2), 4) + + +if __name__ == '__main__': + unittest.main() \ No newline at end of file diff --git a/chapter43_packages/my_package/LICENSE b/chapter43_packages/my_package/LICENSE new file mode 100644 index 0000000..153d416 --- /dev/null +++ b/chapter43_packages/my_package/LICENSE @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. \ No newline at end of file diff --git a/chapter43_packages/my_package/README.md b/chapter43_packages/my_package/README.md new file mode 100644 index 0000000..e69de29 diff --git a/chapter43_packages/my_package/arithmetic/__init__.py b/chapter43_packages/my_package/arithmetic/__init__.py new file mode 100644 index 0000000..be3c5fc --- /dev/null +++ b/chapter43_packages/my_package/arithmetic/__init__.py @@ -0,0 +1,2 @@ +# __init__.py +from . import add \ No newline at end of file diff --git a/chapter43_packages/my_package/arithmetic/add.py b/chapter43_packages/my_package/arithmetic/add.py new file mode 100644 index 0000000..07fb1d6 --- /dev/null +++ b/chapter43_packages/my_package/arithmetic/add.py @@ -0,0 +1,4 @@ +# add.py + +def add(x, y): + return x + y \ No newline at end of file diff --git a/chapter43_packages/my_package/arithmetic/divide.py b/chapter43_packages/my_package/arithmetic/divide.py new file mode 100644 index 0000000..1f6707b --- /dev/null +++ b/chapter43_packages/my_package/arithmetic/divide.py @@ -0,0 +1,4 @@ +# divide.py + +def divide(x, y): + return x / y \ No newline at end of file diff --git a/chapter43_packages/my_package/arithmetic/multiply.py b/chapter43_packages/my_package/arithmetic/multiply.py new file mode 100644 index 0000000..0a4dcbc --- /dev/null +++ b/chapter43_packages/my_package/arithmetic/multiply.py @@ -0,0 +1,4 @@ +# multiply.py + +def multiply(x, y): + return x * y \ No newline at end of file diff --git a/chapter43_packages/my_package/arithmetic/subtract.py b/chapter43_packages/my_package/arithmetic/subtract.py new file mode 100644 index 0000000..f9340ac --- /dev/null +++ b/chapter43_packages/my_package/arithmetic/subtract.py @@ -0,0 +1,4 @@ +# subtract.py + +def subtract(x, y): + return x - y \ No newline at end of file diff --git a/chapter43_packages/my_package/setup.py b/chapter43_packages/my_package/setup.py new file mode 100644 index 0000000..4684c74 --- /dev/null +++ b/chapter43_packages/my_package/setup.py @@ -0,0 +1,22 @@ +import setuptools + +with open("README.md", "r") as fh: + long_description = fh.read() + +setuptools.setup( + name="arithmetic-YOUR-USERNAME-HERE", # Replace with your own username + version="0.0.1", + author="Mike Driscoll", + author_email="driscoll@example.com", + description="A simple arithmetic package", + long_description=long_description, + long_description_content_type="text/markdown", + url="https://github.com/driscollis/arithmetic", + packages=setuptools.find_packages(), + classifiers=[ + "Programming Language :: Python :: 3", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", + ], + python_requires='>=3.6', +) \ No newline at end of file diff --git a/chapter44_pyinstaller/image_viewer.py b/chapter44_pyinstaller/image_viewer.py new file mode 100644 index 0000000..8e4dec4 --- /dev/null +++ b/chapter44_pyinstaller/image_viewer.py @@ -0,0 +1,78 @@ +# image_viewer_working.py + +import wx + +class ImagePanel(wx.Panel): + + def __init__(self, parent, image_size): + super().__init__(parent) + self.max_size = 240 + + img = wx.Image(*image_size) + self.image_ctrl = wx.StaticBitmap(self, + bitmap=wx.Bitmap(img)) + + browse_btn = wx.Button(self, label='Browse') + browse_btn.Bind(wx.EVT_BUTTON, self.on_browse) + + self.photo_txt = wx.TextCtrl(self, size=(200, -1)) + + main_sizer = wx.BoxSizer(wx.VERTICAL) + hsizer = wx.BoxSizer(wx.HORIZONTAL) + + main_sizer.Add(self.image_ctrl, 0, wx.ALL, 5) + hsizer.Add(browse_btn, 0, wx.ALL, 5) + hsizer.Add(self.photo_txt, 0, wx.ALL, 5) + main_sizer.Add(hsizer, 0, wx.ALL, 5) + + self.SetSizer(main_sizer) + main_sizer.Fit(parent) + self.Layout() + + def on_browse(self, event): + """ + Browse for an image file + @param event: The event object + """ + wildcard = "JPEG files (*.jpg)|*.jpg" + with wx.FileDialog(None, "Choose a file", + wildcard=wildcard, + style=wx.ID_OPEN) as dialog: + if dialog.ShowModal() == wx.ID_OK: + self.photo_txt.SetValue(dialog.GetPath()) + self.load_image() + + def load_image(self): + """ + Load the image and display it to the user + """ + filepath = self.photo_txt.GetValue() + img = wx.Image(filepath, wx.BITMAP_TYPE_ANY) + + # scale the image, preserving the aspect ratio + W = img.GetWidth() + H = img.GetHeight() + if W > H: + NewW = self.max_size + NewH = self.max_size * H / W + else: + NewH = self.max_size + NewW = self.max_size * W / H + img = img.Scale(NewW,NewH) + + self.image_ctrl.SetBitmap(wx.Bitmap(img)) + self.Refresh() + + +class MainFrame(wx.Frame): + + def __init__(self): + super().__init__(None, title='Image Viewer') + panel = ImagePanel(self, image_size=(240,240)) + self.Show() + + +if __name__ == '__main__': + app = wx.App(redirect=False) + frame = MainFrame() + app.MainLoop() \ No newline at end of file diff --git a/chapter44_pyinstaller/pysearch.py b/chapter44_pyinstaller/pysearch.py new file mode 100644 index 0000000..9a2e0cc --- /dev/null +++ b/chapter44_pyinstaller/pysearch.py @@ -0,0 +1,49 @@ +# pysearch.py + +import argparse +import pathlib + + +def search_folder(path, extension, file_size=None): + """ + Search folder for files + """ + folder = pathlib.Path(path) + files = list(folder.rglob(f'*.{extension}')) + + if not files: + print(f'No files found with {extension=}') + return + + if file_size is not None: + files = [f for f in files + if f.stat().st_size > file_size] + + print(f'{len(files)} *.{extension} files found:') + for file_path in files: + print(file_path) + + +def main(): + parser = argparse.ArgumentParser( + 'PySearch', + description='PySearch - The Python Powered File Searcher') + parser.add_argument('-p', '--path', + help='The path to search for files', + required=True, + dest='path') + parser.add_argument('-e', '--ext', + help='The extension to search for', + required=True, + dest='extension') + parser.add_argument('-s', '--size', + help='The file size to filter on in bytes', + type=int, + dest='size', + default=None) + + args = parser.parse_args() + search_folder(args.path, args.extension, args.size) + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/chapter46_mac/image_viewer.py b/chapter46_mac/image_viewer.py new file mode 100644 index 0000000..8e4dec4 --- /dev/null +++ b/chapter46_mac/image_viewer.py @@ -0,0 +1,78 @@ +# image_viewer_working.py + +import wx + +class ImagePanel(wx.Panel): + + def __init__(self, parent, image_size): + super().__init__(parent) + self.max_size = 240 + + img = wx.Image(*image_size) + self.image_ctrl = wx.StaticBitmap(self, + bitmap=wx.Bitmap(img)) + + browse_btn = wx.Button(self, label='Browse') + browse_btn.Bind(wx.EVT_BUTTON, self.on_browse) + + self.photo_txt = wx.TextCtrl(self, size=(200, -1)) + + main_sizer = wx.BoxSizer(wx.VERTICAL) + hsizer = wx.BoxSizer(wx.HORIZONTAL) + + main_sizer.Add(self.image_ctrl, 0, wx.ALL, 5) + hsizer.Add(browse_btn, 0, wx.ALL, 5) + hsizer.Add(self.photo_txt, 0, wx.ALL, 5) + main_sizer.Add(hsizer, 0, wx.ALL, 5) + + self.SetSizer(main_sizer) + main_sizer.Fit(parent) + self.Layout() + + def on_browse(self, event): + """ + Browse for an image file + @param event: The event object + """ + wildcard = "JPEG files (*.jpg)|*.jpg" + with wx.FileDialog(None, "Choose a file", + wildcard=wildcard, + style=wx.ID_OPEN) as dialog: + if dialog.ShowModal() == wx.ID_OK: + self.photo_txt.SetValue(dialog.GetPath()) + self.load_image() + + def load_image(self): + """ + Load the image and display it to the user + """ + filepath = self.photo_txt.GetValue() + img = wx.Image(filepath, wx.BITMAP_TYPE_ANY) + + # scale the image, preserving the aspect ratio + W = img.GetWidth() + H = img.GetHeight() + if W > H: + NewW = self.max_size + NewH = self.max_size * H / W + else: + NewH = self.max_size + NewW = self.max_size * W / H + img = img.Scale(NewW,NewH) + + self.image_ctrl.SetBitmap(wx.Bitmap(img)) + self.Refresh() + + +class MainFrame(wx.Frame): + + def __init__(self): + super().__init__(None, title='Image Viewer') + panel = ImagePanel(self, image_size=(240,240)) + self.Show() + + +if __name__ == '__main__': + app = wx.App(redirect=False) + frame = MainFrame() + app.MainLoop() \ No newline at end of file