diff --git a/.gitignore b/.gitignore index 110daaa..72b5e14 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,11 @@ py101_2nd_code.wpu .DS_Store -.ipynb_checkpoints \ No newline at end of file +.ipynb_checkpoints +chapter22_type_hints/.mypy_cache/ + +*.pyc +*.zip +*.db +*.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/chapter09_strings/Chapter 9 - Working with Strings.ipynb b/chapter04_strings/Chapter 4 - Working with Strings.ipynb similarity index 99% rename from chapter09_strings/Chapter 9 - Working with Strings.ipynb rename to chapter04_strings/Chapter 4 - Working with Strings.ipynb index c8afbb3..1931c7c 100644 --- a/chapter09_strings/Chapter 9 - Working with Strings.ipynb +++ b/chapter04_strings/Chapter 4 - Working with Strings.ipynb @@ -4,7 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Chapter 9 - Working with Strings\n", + "# Chapter 4 - Working with Strings\n", "\n", "You will be using strings very often when you program. A string is a series of letters surrounded by single, double or triple quotes. Python 3 defines string as a \"Text Sequence Type\". You can cast other types to a string using the built-in `str()` function." ] diff --git a/chapter04_numeric_types/Chapter 3 - Numeric Types.ipynb b/chapter05_numeric_types/Chapter 5 - Numeric Types.ipynb similarity index 99% rename from chapter04_numeric_types/Chapter 3 - Numeric Types.ipynb rename to chapter05_numeric_types/Chapter 5 - Numeric Types.ipynb index dd18be4..13c3fd4 100644 --- a/chapter04_numeric_types/Chapter 3 - Numeric Types.ipynb +++ b/chapter05_numeric_types/Chapter 5 - Numeric Types.ipynb @@ -4,7 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Chapter 4 - Numeric Types" + "# Chapter 5 - Numeric Types" ] }, { diff --git a/chapter05_lists/Chapter 5 - Learning About Lists.ipynb b/chapter06_lists/Chapter 6 - Learning About Lists.ipynb similarity index 99% rename from chapter05_lists/Chapter 5 - Learning About Lists.ipynb rename to chapter06_lists/Chapter 6 - Learning About Lists.ipynb index afae5a6..dbee46c 100644 --- a/chapter05_lists/Chapter 5 - Learning About Lists.ipynb +++ b/chapter06_lists/Chapter 6 - Learning About Lists.ipynb @@ -4,7 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Chapter 5 - Learning About Lists" + "# Chapter 6 - Learning About Lists" ] }, { diff --git a/chapter06_tuples/Chapter 6 - Learning About Tuples.ipynb b/chapter07_tuples/Chapter 7 - Learning About Tuples.ipynb similarity index 99% rename from chapter06_tuples/Chapter 6 - Learning About Tuples.ipynb rename to chapter07_tuples/Chapter 7 - Learning About Tuples.ipynb index ec057af..fb5bcac 100644 --- a/chapter06_tuples/Chapter 6 - Learning About Tuples.ipynb +++ b/chapter07_tuples/Chapter 7 - Learning About Tuples.ipynb @@ -4,7 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Chapter 6 - Learning About Tuples\n", + "# Chapter 7 - Learning About Tuples\n", "\n", "Tuples are another sequence type in Python. Tuples consist of a number of values that are separated by commas. A tuple is immutable whereas a list is not. Immutable means that the tuple has a fixed value and cannot change. You cannot add, delete or modify items in a tuple. Immutable objects are useful when you need a constant hash value. The most popular example is the key to a Python dictionary, which you will learn about in **chapter 7**." ] diff --git a/chapter07_dictionaries/Chapter 7 - Learning About Dictionaries.ipynb b/chapter08_dictionaries/Chapter 8 - Learning About Dictionaries.ipynb similarity index 99% rename from chapter07_dictionaries/Chapter 7 - Learning About Dictionaries.ipynb rename to chapter08_dictionaries/Chapter 8 - Learning About Dictionaries.ipynb index 332e9c0..3a01e9d 100644 --- a/chapter07_dictionaries/Chapter 7 - Learning About Dictionaries.ipynb +++ b/chapter08_dictionaries/Chapter 8 - Learning About Dictionaries.ipynb @@ -4,7 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Chapter 7 - Learning About Dictionaries\n", + "# Chapter 8 - Learning About Dictionaries\n", "\n", "Dictionaries are another fundamental data type in Python. A dictionary is a key, value pair. Some programming languages refer to them as hash tables. They are described as a *mapping* object that maps hashable values to arbitrary objects.\n", "\n", diff --git a/chapter08_sets/Chapter 8 - Learning About Sets.ipynb b/chapter09_sets/Chapter 9 - Learning About Sets.ipynb similarity index 99% rename from chapter08_sets/Chapter 8 - Learning About Sets.ipynb rename to chapter09_sets/Chapter 9 - Learning About Sets.ipynb index 579b497..3ffd92b 100644 --- a/chapter08_sets/Chapter 8 - Learning About Sets.ipynb +++ b/chapter09_sets/Chapter 9 - Learning About Sets.ipynb @@ -4,7 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Chapter 8 - Learning About Sets" + "# Chapter 9 - Learning About Sets" ] }, { diff --git a/chapter11_conditionals/Chapter 11 - Conditional Statements.ipynb b/chapter11_conditionals/Chapter 11 - Conditional Statements.ipynb index 63544db..44fd594 100644 --- a/chapter11_conditionals/Chapter 11 - Conditional Statements.ipynb +++ b/chapter11_conditionals/Chapter 11 - Conditional Statements.ipynb @@ -305,34 +305,34 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "You are a blonde\n" + "You buy American\n" ] } ], "source": [ "age = 21\n", - "hair = 'blonde'\n", + "car = 'Ford'\n", "if age >= 21:\n", - " if hair == 'brown':\n", - " print('You are a brunette')\n", - " elif hair == 'blonde':\n", - " print('You are a blonde')\n", + " if car in ['Honda', 'Toyota']:\n", + " print('You buy foreign cars')\n", + " elif car in ['Ford', 'Chevrolet']:\n", + " print('You buy American')\n", "else:\n", - " print('Not checking hair color')" + " print('You are too young!')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "This code has multiple paths that it can take because it depends on two variables: `age` and `hair`. If the age is greater than a certain value, then it falls into that code block and will execute the nested `if` statement, which checks the `hair` color. If the `age` is less than an arbitrary amount then it will simply print out a message.\n", + "This code has multiple paths that it can take because it depends on two variables: `age` and `car`. If the age is greater than a certain value, then it falls into that code block and will execute the nested `if` statement, which checks the `car` type. If the `age` is less than an arbitrary amount then it will simply print out a message.\n", "\n", "Theoretically, you can nest conditionals any number of times. However, the more nesting you do, the more complicated it is to debug later. You should keep the nesting to only one or two levels deep in most cases.\n", "\n", @@ -353,70 +353,70 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "You are a blonde\n" + "You buy American\n" ] } ], "source": [ "age = 21\n", - "hair = 'blonde'\n", - "if age >= 21 and hair == 'brown':\n", - " print('You are a brunette')\n", - "elif age >= 21 and hair == 'blonde':\n", - " print('You are a blonde')\n", + "car = 'Ford'\n", + "if age >= 21 and car in ['Honda', 'Toyota']:\n", + " print('You buy foreign cars')\n", + "elif age >= 21 and car in ['Ford', 'Chevrolet']:\n", + " print('You buy American')\n", "else:\n", - " print('Not checking hair color')" + " print('You are too young!')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "When you use `and`, both expressions must evaluate to `True` for the code underneath them to execute. So the first conditional checks to see if the age is greater than or equal to 21 AND the hair color is brown. Since it isn't both of those things, you drop down to the `elif` and check those conditions. This time both conditions are `True`, so it prints our your hair color.\n", + "When you use `and`, both expressions must evaluate to `True` for the code underneath them to execute. So the first conditional checks to see if the age is greater than or equal to 21 AND the car is in the list of Japanese cars. Since it isn't both of those things, you drop down to the `elif` and check those conditions. This time both conditions are `True`, so it prints your car preference.\n", "\n", "Let's see what happens if you change the `and` to an `or`:" ] }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "You are a brunette\n" + "You buy foreign cars\n" ] } ], "source": [ "age = 21\n", - "hair = 'blonde'\n", - "if age >= 21 or hair == 'brown':\n", - " print('You are a brunette')\n", - "elif age >= 21 and hair == 'blonde':\n", - " print('You are a blonde')\n", + "car = 'Ford'\n", + "if age >= 21 or car in ['Honda', 'Toyota']:\n", + " print('You buy foreign cars')\n", + "elif age >= 21 and car in ['Ford', 'Chevrolet']:\n", + " print('You buy American')\n", "else:\n", - " print('Not checking hair color')" + " print('You are too young!')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Wait a minute! You just said your hair color was blonde, but this code is saying you're a brunette! What's going on here?\n", + "Wait a minute! You said your car was \"Ford\", but this code is saying you buy foreign cars! What's going on here?\n", "\n", "Well when you use a logical `or`, that means that the code in that code block will execute if either of the statements are `True`. \n", "\n", - "Let's break this down a bit. There are two expressions in `if age >= 21 or hair == 'brown'`. The first one is `age >= 21`. That evaluates to `True`. As soon as Python sees the `or` and that the first statement is `True`, it evaluates the whole thing as `True`. Either your age is greater than or equal to 21 **or** your hair is brown. Either way, it's true and that code gets executed.\n", + "Let's break this down a bit. There are two expressions in `if age >= 21 or car in ['Honda', 'Toyota']`. The first one is `age >= 21`. That evaluates to `True`. As soon as Python sees the `or` and that the first statement is `True`, it evaluates the whole thing as `True`. Either your age is greater than or equal to 21 **or** your car is Ford. Either way, it's true and that code gets executed.\n", "\n", "Using `not` is a bit different. It doesn't really fit with this example at all, so you'll have to write something else. \n", "\n", diff --git a/chapter22_type_hints/bad_type_hinting.py b/chapter22_type_hints/bad_type_hinting.py new file mode 100644 index 0000000..917f127 --- /dev/null +++ b/chapter22_type_hints/bad_type_hinting.py @@ -0,0 +1,4 @@ +# bad_type_hinting.py + +def my_function(a: str, b: str) -> None: + return a.keys() + b.keys() diff --git a/chapter22_type_hints/bad_type_hinting2.py b/chapter22_type_hints/bad_type_hinting2.py new file mode 100644 index 0000000..76caf86 --- /dev/null +++ b/chapter22_type_hints/bad_type_hinting2.py @@ -0,0 +1,4 @@ +# bad_type_hinting2.py + +def my_function(a: str, b: str) -> str: + return a + b diff --git a/chapter22_type_hints/good_type_hinting.py b/chapter22_type_hints/good_type_hinting.py new file mode 100644 index 0000000..81066c7 --- /dev/null +++ b/chapter22_type_hints/good_type_hinting.py @@ -0,0 +1,4 @@ +# good_type_hinting.py + +def my_function(a: str, b: str) -> str: + return a + b diff --git a/chapter23_threading/worker_thread_subclass.py b/chapter23_threading/worker_thread_subclass.py new file mode 100644 index 0000000..0b83bf9 --- /dev/null +++ b/chapter23_threading/worker_thread_subclass.py @@ -0,0 +1,30 @@ +# worker_thread_subclass.py + +import random +import threading +import time + +class WorkerThread(threading.Thread): + + def __init__(self, name): + threading.Thread.__init__(self) + self.name = name + self.id = id(self) + + def run(self): + """ + Run the thread + """ + worker(self.name, self.id) + +def worker(name: str, instance_id: int) -> None: + print(f'Started worker {name} - {instance_id}') + worker_time = random.choice(range(1, 5)) + time.sleep(worker_time) + print(f'{name} - {instance_id} worker finished in ' + f'{worker_time} seconds') + +if __name__ == '__main__': + for i in range(5): + thread = WorkerThread(name=f'computer_{i}') + thread.start() diff --git a/chapter23_threading/worker_threads.py b/chapter23_threading/worker_threads.py new file mode 100644 index 0000000..0091535 --- /dev/null +++ b/chapter23_threading/worker_threads.py @@ -0,0 +1,18 @@ +# worker_threads.py + +import random +import threading +import time + + +def worker(name: str) -> None: + print(f'Started worker {name}') + worker_time = random.choice(range(1, 5)) + time.sleep(worker_time) + print(f'{name} worker finished in {worker_time} seconds') + +if __name__ == '__main__': + for i in range(5): + thread = threading.Thread(target=worker, + args=(f'computer_{i}',)) + thread.start() diff --git a/chapter23_threading/writing_thread.py b/chapter23_threading/writing_thread.py new file mode 100644 index 0000000..57f1ef7 --- /dev/null +++ b/chapter23_threading/writing_thread.py @@ -0,0 +1,36 @@ +# writing_thread.py + +import random +import time +from threading import Thread + + +class WritingThread(Thread): + + def __init__(self, filename: str, number_of_lines: int, + work_time: int = 1) -> None: + Thread.__init__(self) + self.filename = filename + self.number_of_lines = number_of_lines + self.work_time = work_time + + def run(self) -> None: + """ + Run the thread + """ + print(f'Writing {self.number_of_lines} lines of text to ' + f'{self.filename}') + with open(self.filename, 'w') as f: + for line in range(self.number_of_lines): + text = f'This is line {line+1}\n' + f.write(text) + time.sleep(self.work_time) + print(f'Finished writing {self.filename}') + +if __name__ == '__main__': + files = [f'test{x}.txt' for x in range(1, 6)] + for filename in files: + work_time = random.choice(range(1, 3)) + number_of_lines = random.choice(range(5, 20)) + thread = WritingThread(filename, number_of_lines, work_time) + thread.start() \ No newline at end of file diff --git a/chapter24_multiprocessing/process_pool.py b/chapter24_multiprocessing/process_pool.py new file mode 100644 index 0000000..12a32df --- /dev/null +++ b/chapter24_multiprocessing/process_pool.py @@ -0,0 +1,17 @@ +import random +import time + +from multiprocessing import Pool + + +def worker(name: str) -> None: + print(f'Started worker {name}') + worker_time = random.choice(range(1, 5)) + time.sleep(worker_time) + print(f'{name} worker finished in {worker_time} seconds') + +if __name__ == '__main__': + process_names = [f'computer_{i}' for i in range(15)] + pool = Pool(processes=5) + pool.map(worker, process_names) + #pool.terminate() \ No newline at end of file diff --git a/chapter24_multiprocessing/worker_process_subclass.py b/chapter24_multiprocessing/worker_process_subclass.py new file mode 100644 index 0000000..f9ff58e --- /dev/null +++ b/chapter24_multiprocessing/worker_process_subclass.py @@ -0,0 +1,33 @@ +# worker_thread_subclass.py + +import random +import multiprocessing +import time + +class WorkerProcess(multiprocessing.Process): + + def __init__(self, name): + multiprocessing.Process.__init__(self) + self.name = name + + def run(self): + """ + Run the thread + """ + worker(self.name) + +def worker(name: str) -> None: + print(f'Started worker {name}') + worker_time = random.choice(range(1, 5)) + time.sleep(worker_time) + print(f'{name} worker finished in {worker_time} seconds') + +if __name__ == '__main__': + processes = [] + for i in range(5): + process = WorkerProcess(name=f'computer_{i}') + processes.append(process) + process.start() + + for process in processes: + process.join() diff --git a/chapter24_multiprocessing/worker_processes.py b/chapter24_multiprocessing/worker_processes.py new file mode 100644 index 0000000..4920645 --- /dev/null +++ b/chapter24_multiprocessing/worker_processes.py @@ -0,0 +1,21 @@ +import multiprocessing +import random +import time + + +def worker(name: str) -> None: + print(f'Started worker {name}') + worker_time = random.choice(range(1, 5)) + time.sleep(worker_time) + print(f'{name} worker finished in {worker_time} seconds') + +if __name__ == '__main__': + processes = [] + for i in range(5): + process = multiprocessing.Process(target=worker, + args=(f'computer_{i}',)) + processes.append(process) + process.start() + + for proc in processes: + proc.join() \ No newline at end of file diff --git a/chapter26_debugging/debug_code.py b/chapter26_debugging/debug_code.py new file mode 100644 index 0000000..b963634 --- /dev/null +++ b/chapter26_debugging/debug_code.py @@ -0,0 +1,13 @@ +# debug_code.py + +def log(number): + print(f'Processing {number}') + print(f'Adding 2 to number: {number + 2}') + + +def looper(number): + for i in range(number): + log(i) + +if __name__ == '__main__': + looper(5) \ No newline at end of file diff --git a/chapter26_debugging/debug_code_with_breakpoint.py b/chapter26_debugging/debug_code_with_breakpoint.py new file mode 100644 index 0000000..f310fc6 --- /dev/null +++ b/chapter26_debugging/debug_code_with_breakpoint.py @@ -0,0 +1,14 @@ +# debug_code_with_breakpoint.py + +def log(number): + print(f'Processing {number}') + print(f'Adding 2 to number: {number + 2}') + + +def looper(number): + for i in range(number): + breakpoint() + log(i) + +if __name__ == '__main__': + looper(5) \ No newline at end of file diff --git a/chapter26_debugging/debug_code_with_settrace.py b/chapter26_debugging/debug_code_with_settrace.py new file mode 100644 index 0000000..3de0a72 --- /dev/null +++ b/chapter26_debugging/debug_code_with_settrace.py @@ -0,0 +1,14 @@ +# debug_code_with_settrace.py + +def log(number): + print(f'Processing {number}') + print(f'Adding 2 to number: {number + 2}') + + +def looper(number): + for i in range(number): + import pdb; pdb.set_trace() + log(i) + +if __name__ == '__main__': + looper(5) \ No newline at end of file diff --git a/chapter27_decorators/amount_with_properties.py b/chapter27_decorators/amount_with_properties.py new file mode 100644 index 0000000..6fce4ee --- /dev/null +++ b/chapter27_decorators/amount_with_properties.py @@ -0,0 +1,24 @@ +class Amount: + + def __init__(self): + # private attribute + self._amount = None + + def get_amount(self): + return self._amount + + def set_amount(self, value): + if isinstance(value, int) or isinstance(value, float): + self._amount = value + else: + print(f'Value must be an int or float') + + amount = property(get_amount, set_amount) + +if __name__ == '__main__': + amt = Amount() + print(f'The current amount is {amt.amount}') + amt.amount = 'the' + print(f'The current amount is {amt.amount}') + amt.amount = 5.5 + print(f'The current amount is {amt.amount}') diff --git a/chapter27_decorators/amount_with_property_decorator.py b/chapter27_decorators/amount_with_property_decorator.py new file mode 100644 index 0000000..fa6074d --- /dev/null +++ b/chapter27_decorators/amount_with_property_decorator.py @@ -0,0 +1,25 @@ +class Amount: + + def __init__(self): + # private attribute + self._amount = None + + @property + def amount(self): + return self._amount + + @amount.setter + def amount(self, value): + if isinstance(value, int) or isinstance(value, float): + self._amount = value + else: + print(f'Value must be an int or float') + + +if __name__ == '__main__': + amt = Amount() + print(f'The current amount is {amt.amount}') + amt.amount = 'the' + print(f'The current amount is {amt.amount}') + amt.amount = 5.5 + print(f'The current amount is {amt.amount}') diff --git a/chapter27_decorators/amount_without_properties.py b/chapter27_decorators/amount_without_properties.py new file mode 100644 index 0000000..a384f94 --- /dev/null +++ b/chapter27_decorators/amount_without_properties.py @@ -0,0 +1,22 @@ +class Amount: + + def __init__(self): + # private attribute + self._amount = None + + def get_amount(self): + return self._amount + + def set_amount(self, value): + if isinstance(value, int) or isinstance(value, float): + self._amount = value + else: + print(f'Value must be an int or float') + +if __name__ == '__main__': + amt = Amount() + print(f'The current amount is {amt.get_amount()}') + amt.set_amount('the') + print(f'The current amount is {amt.get_amount()}') + amt.set_amount(5.5) + print(f'The current amount is {amt.get_amount()}') 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_class.py b/chapter27_decorators/decorator_class.py new file mode 100644 index 0000000..1ab3964 --- /dev/null +++ b/chapter27_decorators/decorator_class.py @@ -0,0 +1,24 @@ +# decorator_class.py + +class info: + + def __init__(self, arg1, arg2): + print('running __init__') + self.arg1 = arg1 + self.arg2 = arg2 + print('Decorator args: {}, {}'.format(arg1, arg2)) + + def __call__(self, function): + print('in __call__') + + def wrapper(*args, **kwargs): + print('in wrapper()') + return function(*args, **kwargs) + + return wrapper + +@info(3, 'Python') +def treble(number): + return number * 3 + +print(treble(5)) \ No newline at end of file diff --git a/chapter27_decorators/decorator_syntax.py b/chapter27_decorators/decorator_syntax.py new file mode 100644 index 0000000..1c5dd13 --- /dev/null +++ b/chapter27_decorators/decorator_syntax.py @@ -0,0 +1,16 @@ +# decorator_syntax.py + +def func_info(func): + def wrapper(*args): + print('Function name: ' + func.__name__) + print('Function docstring: ' + str(func.__doc__)) + result = func(*args) + return result + return wrapper + +@func_info +def treble(a: int) -> int: + """A function that triples its input""" + return a * 3 + +print(treble(5)) diff --git a/chapter27_decorators/decorator_syntax_with_arguments.py b/chapter27_decorators/decorator_syntax_with_arguments.py new file mode 100644 index 0000000..bfd6ea8 --- /dev/null +++ b/chapter27_decorators/decorator_syntax_with_arguments.py @@ -0,0 +1,24 @@ +# decorator_syntax_with_arguments.py + +def func_info(arg1, arg2): + print('Decorator arg1 = ' + str(arg1)) + print('Decorator arg2 = ' + str(arg2)) + + def the_real_decorator(function): + + def wrapper(*args, **kwargs): + print('Function {} args: {} kwargs: {}' + .format( + function.__name__, + str(args), + str(kwargs))) + return function(*args, **kwargs) + return wrapper + + return the_real_decorator + +@func_info(3, 'Python') +def treble(number): + return number * 3 + +print(treble(5)) diff --git a/chapter27_decorators/first_decorator.py b/chapter27_decorators/first_decorator.py new file mode 100644 index 0000000..57c6293 --- /dev/null +++ b/chapter27_decorators/first_decorator.py @@ -0,0 +1,16 @@ + + +def func_info(func): + def wrapper(): + print('Function name: ' + func.__name__) + print('Function docstring: ' + str(func.__doc__)) + result = func() + return result + return wrapper + + +def treble(): + return 3 * 3 + +decorator = func_info(treble) +print(decorator()) diff --git a/chapter27_decorators/first_decorator_updated.py b/chapter27_decorators/first_decorator_updated.py new file mode 100644 index 0000000..1d61a9c --- /dev/null +++ b/chapter27_decorators/first_decorator_updated.py @@ -0,0 +1,18 @@ +# first_decorator_updated.py + +def func_info(func): + def wrapper(*args): + print('Function name: ' + func.__name__) + print('Function docstring: ' + str(func.__doc__)) + result = func(*args) + return result + return wrapper + + +def treble(a: int) -> int: + """A function that triples its input""" + return a * 3 + + +my_decorator = func_info(treble) +print(my_decorator(5)) diff --git a/chapter27_decorators/logging_decorator.py b/chapter27_decorators/logging_decorator.py new file mode 100644 index 0000000..ca71d71 --- /dev/null +++ b/chapter27_decorators/logging_decorator.py @@ -0,0 +1,37 @@ +import logging + +def logging_formatter(logger, name): + """ + Format logger and add file handler + """ + fh = logging.FileHandler(f"{name}.log") + fmt = '%(asctime)s - %(name)s - %(levelname)s - %(message)s' + formatter = logging.Formatter(fmt) + fh.setFormatter(formatter) + logger.addHandler(fh) + +def log(func): + """ + Log what function is called + """ + def wrapper(*args, **kwargs): + name = func.__name__ + logger = logging.getLogger(name) + logger.setLevel(logging.INFO) + + # add logging formatter and file handler + logging_formatter(logger, name) + + logger.info(f"Running function: {name}") + logger.info(f"{args=}, {kwargs=}") + result = func(*args, **kwargs) + logger.info("Result: %s" % result) + return func + return wrapper + +@log +def treble(a): + return a * 3 + +if __name__ == '__main__': + treble(5) diff --git a/chapter27_decorators/stacked_decorator_tracing.py b/chapter27_decorators/stacked_decorator_tracing.py new file mode 100644 index 0000000..07b25a9 --- /dev/null +++ b/chapter27_decorators/stacked_decorator_tracing.py @@ -0,0 +1,18 @@ +def bold(func): + print(f'You are wrapping {func.__name__} in bold') + def bold_wrapper(): + return "" + func() + "" + return bold_wrapper + +def italic(func): + print(f'You are wrapping {func.__name__} in italic') + def italic_wrapper(): + return "" + func() + "" + return italic_wrapper + +@bold +@italic +def formatted_text(): + return 'Python rocks!' + +print(formatted_text()) \ No newline at end of file diff --git a/chapter27_decorators/stacked_decorators.py b/chapter27_decorators/stacked_decorators.py new file mode 100644 index 0000000..9ebdf05 --- /dev/null +++ b/chapter27_decorators/stacked_decorators.py @@ -0,0 +1,16 @@ +def bold(func): + def wrapper(): + return "" + func() + "" + return wrapper + +def italic(func): + def wrapper(): + return "" + func() + "" + return wrapper + +@bold +@italic +def formatted_text(): + return 'Python rocks!' + +print(formatted_text()) \ No newline at end of file diff --git a/chapter27_decorators/treble.log b/chapter27_decorators/treble.log new file mode 100644 index 0000000..ccf0f36 --- /dev/null +++ b/chapter27_decorators/treble.log @@ -0,0 +1,3 @@ +2020-05-04 16:22:02,781 - treble - INFO - Running function: treble +2020-05-04 16:22:02,781 - treble - INFO - args=(5,), kwargs={} +2020-05-04 16:22:02,781 - treble - INFO - Result: 15 diff --git a/chapter29_profiling/callee_stats.py b/chapter29_profiling/callee_stats.py new file mode 100644 index 0000000..a1a631c --- /dev/null +++ b/chapter29_profiling/callee_stats.py @@ -0,0 +1,12 @@ +import pstats + +def formatted_stats_output(path): + p = pstats.Stats(path) + stripped_dirs = p.strip_dirs() + sorted_stats = stripped_dirs.sort_stats('filename') + sorted_stats.print_callers('\(main') + sorted_stats.print_callees('\(main') + +if __name__ =='__main__': + path = 'profile_output.txt' + formatted_stats_output(path) \ No newline at end of file diff --git a/chapter29_profiling/filtered_stats.py b/chapter29_profiling/filtered_stats.py new file mode 100644 index 0000000..086c800 --- /dev/null +++ b/chapter29_profiling/filtered_stats.py @@ -0,0 +1,11 @@ +import pstats + +def formatted_stats_output(path): + p = pstats.Stats(path) + stripped_dirs = p.strip_dirs() + sorted_stats = stripped_dirs.sort_stats('filename') + sorted_stats.print_stats('\(main') + +if __name__ =='__main__': + path = 'profile_output.txt' + formatted_stats_output(path) \ No newline at end of file diff --git a/chapter29_profiling/formatted_output.py b/chapter29_profiling/formatted_output.py new file mode 100644 index 0000000..d35ca8a --- /dev/null +++ b/chapter29_profiling/formatted_output.py @@ -0,0 +1,11 @@ +import pstats + +def formatted_stats_output(path): + p = pstats.Stats(path) + stripped_dirs = p.strip_dirs() + sorted_stats = stripped_dirs.sort_stats('filename') + sorted_stats.print_stats() + +if __name__ =='__main__': + path = 'profile_output.txt' + formatted_stats_output(path) \ No newline at end of file diff --git a/chapter29_profiling/profile_output.txt b/chapter29_profiling/profile_output.txt new file mode 100644 index 0000000..3b7e13e Binary files /dev/null and b/chapter29_profiling/profile_output.txt differ diff --git a/chapter29_profiling/profile_test.py b/chapter29_profiling/profile_test.py new file mode 100644 index 0000000..6b8641b --- /dev/null +++ b/chapter29_profiling/profile_test.py @@ -0,0 +1,24 @@ +# profile_test.py + +import time + +def quick(): + print('Running quick') + return 1 + 1 + +def average(): + print('Running average') + time.sleep(0.5) + +def super_slow(): + print('Running super slowly') + time.sleep(2) + +def main(): + quick() + super_slow() + quick() + average() + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/chapter30_testing/add_doctest.py b/chapter30_testing/add_doctest.py new file mode 100644 index 0000000..5e0c3cd --- /dev/null +++ b/chapter30_testing/add_doctest.py @@ -0,0 +1,10 @@ +# add_doctest.py + +def add(a: int, b: int) -> int: + """ + >>> add(1, 2) + 3 + >>> add(4, 5) + 9 + """ + a + b \ No newline at end of file diff --git a/chapter30_testing/add_doctest_working.py b/chapter30_testing/add_doctest_working.py new file mode 100644 index 0000000..b8c161b --- /dev/null +++ b/chapter30_testing/add_doctest_working.py @@ -0,0 +1,10 @@ +# add_doctest.py + +def add(a: int, b: int) -> int: + """ + >>> add(1, 2) + 3 + >>> add(4, 5) + 9 + """ + return a + b \ No newline at end of file diff --git a/chapter30_testing/add_test_in_code.py b/chapter30_testing/add_test_in_code.py new file mode 100644 index 0000000..3918d37 --- /dev/null +++ b/chapter30_testing/add_test_in_code.py @@ -0,0 +1,14 @@ +# add_test_in_code.py + +def add(a: int, b: int) -> int: + """ + >>> add(1, 2) + 3 + >>> add(4, 5) + 9 + """ + return a + b + +if __name__ == '__main__': + import doctest + doctest.testmod(verbose=True) \ No newline at end of file diff --git a/chapter30_testing/doctest_external.py b/chapter30_testing/doctest_external.py new file mode 100644 index 0000000..e4861c1 --- /dev/null +++ b/chapter30_testing/doctest_external.py @@ -0,0 +1,4 @@ +# doctest_external.py + +def add(a: int, b: int) -> int: + return a + b \ No newline at end of file diff --git a/chapter30_testing/fizzbuzz_buzz_test/fizzbuzz.py b/chapter30_testing/fizzbuzz_buzz_test/fizzbuzz.py new file mode 100644 index 0000000..5d4c54a --- /dev/null +++ b/chapter30_testing/fizzbuzz_buzz_test/fizzbuzz.py @@ -0,0 +1,3 @@ +def process(number): + if number % 3 == 0: + return 'Fizz' \ No newline at end of file diff --git a/chapter30_testing/fizzbuzz_buzz_test/test_fizzbuzz.py b/chapter30_testing/fizzbuzz_buzz_test/test_fizzbuzz.py new file mode 100644 index 0000000..93d0e2a --- /dev/null +++ b/chapter30_testing/fizzbuzz_buzz_test/test_fizzbuzz.py @@ -0,0 +1,13 @@ +import fizzbuzz +import unittest + +class TestFizzBuzz(unittest.TestCase): + + def test_multiple_of_three(self): + self.assertEqual(fizzbuzz.process(6), 'Fizz') + + def test_multiple_of_five(self): + self.assertEqual(fizzbuzz.process(20), 'Buzz') + +if __name__ == '__main__': + unittest.main() \ No newline at end of file diff --git a/chapter30_testing/fizzbuzz_buzz_test_passing/fizzbuzz.py b/chapter30_testing/fizzbuzz_buzz_test_passing/fizzbuzz.py new file mode 100644 index 0000000..026ca50 --- /dev/null +++ b/chapter30_testing/fizzbuzz_buzz_test_passing/fizzbuzz.py @@ -0,0 +1,5 @@ +def process(number): + if number % 3 == 0: + return 'Fizz' + elif number % 5 == 0: + return 'Buzz' \ No newline at end of file diff --git a/chapter30_testing/fizzbuzz_buzz_test_passing/test_fizzbuzz.py b/chapter30_testing/fizzbuzz_buzz_test_passing/test_fizzbuzz.py new file mode 100644 index 0000000..93d0e2a --- /dev/null +++ b/chapter30_testing/fizzbuzz_buzz_test_passing/test_fizzbuzz.py @@ -0,0 +1,13 @@ +import fizzbuzz +import unittest + +class TestFizzBuzz(unittest.TestCase): + + def test_multiple_of_three(self): + self.assertEqual(fizzbuzz.process(6), 'Fizz') + + def test_multiple_of_five(self): + self.assertEqual(fizzbuzz.process(20), 'Buzz') + +if __name__ == '__main__': + unittest.main() \ No newline at end of file diff --git a/chapter30_testing/fizzbuzz_final_test/fizzbuzz.py b/chapter30_testing/fizzbuzz_final_test/fizzbuzz.py new file mode 100644 index 0000000..7510daa --- /dev/null +++ b/chapter30_testing/fizzbuzz_final_test/fizzbuzz.py @@ -0,0 +1,7 @@ +def process(number): + if number % 3 == 0 and number % 5 == 0: + return 'FizzBuzz' + elif number % 3 == 0: + return 'Fizz' + elif number % 5 == 0: + return 'Buzz' \ No newline at end of file diff --git a/chapter30_testing/fizzbuzz_final_test/test_fizzbuzz.py b/chapter30_testing/fizzbuzz_final_test/test_fizzbuzz.py new file mode 100644 index 0000000..1326335 --- /dev/null +++ b/chapter30_testing/fizzbuzz_final_test/test_fizzbuzz.py @@ -0,0 +1,20 @@ +import fizzbuzz +import unittest + +class TestFizzBuzz(unittest.TestCase): + + def test_multiple_of_three(self): + self.assertEqual(fizzbuzz.process(6), 'Fizz') + + def test_multiple_of_five(self): + self.assertEqual(fizzbuzz.process(20), 'Buzz') + + def test_fizzbuzz(self): + self.assertEqual(fizzbuzz.process(15), 'FizzBuzz') + + def test_regular_numbers(self): + self.assertEqual(fizzbuzz.process(2), 2) + self.assertEqual(fizzbuzz.process(98), 98) + +if __name__ == '__main__': + unittest.main() \ No newline at end of file diff --git a/chapter30_testing/fizzbuzz_final_test_passing/fizzbuzz.py b/chapter30_testing/fizzbuzz_final_test_passing/fizzbuzz.py new file mode 100644 index 0000000..6407376 --- /dev/null +++ b/chapter30_testing/fizzbuzz_final_test_passing/fizzbuzz.py @@ -0,0 +1,9 @@ +def process(number): + if number % 3 == 0 and number % 5 == 0: + return 'FizzBuzz' + elif number % 3 == 0: + return 'Fizz' + elif number % 5 == 0: + return 'Buzz' + else: + return number \ No newline at end of file diff --git a/chapter30_testing/fizzbuzz_final_test_passing/test_fizzbuzz.py b/chapter30_testing/fizzbuzz_final_test_passing/test_fizzbuzz.py new file mode 100644 index 0000000..1326335 --- /dev/null +++ b/chapter30_testing/fizzbuzz_final_test_passing/test_fizzbuzz.py @@ -0,0 +1,20 @@ +import fizzbuzz +import unittest + +class TestFizzBuzz(unittest.TestCase): + + def test_multiple_of_three(self): + self.assertEqual(fizzbuzz.process(6), 'Fizz') + + def test_multiple_of_five(self): + self.assertEqual(fizzbuzz.process(20), 'Buzz') + + def test_fizzbuzz(self): + self.assertEqual(fizzbuzz.process(15), 'FizzBuzz') + + def test_regular_numbers(self): + self.assertEqual(fizzbuzz.process(2), 2) + self.assertEqual(fizzbuzz.process(98), 98) + +if __name__ == '__main__': + unittest.main() \ No newline at end of file diff --git a/chapter30_testing/fizzbuzz_fizz_test/fizzbuzz.py b/chapter30_testing/fizzbuzz_fizz_test/fizzbuzz.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/chapter30_testing/fizzbuzz_fizz_test/fizzbuzz.py @@ -0,0 +1 @@ + diff --git a/chapter30_testing/fizzbuzz_fizz_test/test_fizzbuzz.py b/chapter30_testing/fizzbuzz_fizz_test/test_fizzbuzz.py new file mode 100644 index 0000000..344ed73 --- /dev/null +++ b/chapter30_testing/fizzbuzz_fizz_test/test_fizzbuzz.py @@ -0,0 +1,10 @@ +import fizzbuzz +import unittest + +class TestFizzBuzz(unittest.TestCase): + + def test_multiple_of_three(self): + self.assertEqual(fizzbuzz.process(6), 'Fizz') + +if __name__ == '__main__': + unittest.main() \ No newline at end of file diff --git a/chapter30_testing/fizzbuzz_fizz_test_passing/fizzbuzz.py b/chapter30_testing/fizzbuzz_fizz_test_passing/fizzbuzz.py new file mode 100644 index 0000000..5d4c54a --- /dev/null +++ b/chapter30_testing/fizzbuzz_fizz_test_passing/fizzbuzz.py @@ -0,0 +1,3 @@ +def process(number): + if number % 3 == 0: + return 'Fizz' \ No newline at end of file diff --git a/chapter30_testing/fizzbuzz_fizz_test_passing/test_fizzbuzz.py b/chapter30_testing/fizzbuzz_fizz_test_passing/test_fizzbuzz.py new file mode 100644 index 0000000..344ed73 --- /dev/null +++ b/chapter30_testing/fizzbuzz_fizz_test_passing/test_fizzbuzz.py @@ -0,0 +1,10 @@ +import fizzbuzz +import unittest + +class TestFizzBuzz(unittest.TestCase): + + def test_multiple_of_three(self): + self.assertEqual(fizzbuzz.process(6), 'Fizz') + +if __name__ == '__main__': + unittest.main() \ No newline at end of file diff --git a/chapter30_testing/fizzbuzz_fizzbuzz_test/fizzbuzz.py b/chapter30_testing/fizzbuzz_fizzbuzz_test/fizzbuzz.py new file mode 100644 index 0000000..026ca50 --- /dev/null +++ b/chapter30_testing/fizzbuzz_fizzbuzz_test/fizzbuzz.py @@ -0,0 +1,5 @@ +def process(number): + if number % 3 == 0: + return 'Fizz' + elif number % 5 == 0: + return 'Buzz' \ No newline at end of file diff --git a/chapter30_testing/fizzbuzz_fizzbuzz_test/test_fizzbuzz.py b/chapter30_testing/fizzbuzz_fizzbuzz_test/test_fizzbuzz.py new file mode 100644 index 0000000..d2b08ed --- /dev/null +++ b/chapter30_testing/fizzbuzz_fizzbuzz_test/test_fizzbuzz.py @@ -0,0 +1,16 @@ +import fizzbuzz +import unittest + +class TestFizzBuzz(unittest.TestCase): + + def test_multiple_of_three(self): + self.assertEqual(fizzbuzz.process(6), 'Fizz') + + def test_multiple_of_five(self): + self.assertEqual(fizzbuzz.process(20), 'Buzz') + + def test_fizzbuzz(self): + self.assertEqual(fizzbuzz.process(15), 'FizzBuzz') + +if __name__ == '__main__': + unittest.main() \ No newline at end of file diff --git a/chapter30_testing/fizzbuzz_fizzbuzz_test_passing/fizzbuzz.py b/chapter30_testing/fizzbuzz_fizzbuzz_test_passing/fizzbuzz.py new file mode 100644 index 0000000..7510daa --- /dev/null +++ b/chapter30_testing/fizzbuzz_fizzbuzz_test_passing/fizzbuzz.py @@ -0,0 +1,7 @@ +def process(number): + if number % 3 == 0 and number % 5 == 0: + return 'FizzBuzz' + elif number % 3 == 0: + return 'Fizz' + elif number % 5 == 0: + return 'Buzz' \ No newline at end of file diff --git a/chapter30_testing/fizzbuzz_fizzbuzz_test_passing/test_fizzbuzz.py b/chapter30_testing/fizzbuzz_fizzbuzz_test_passing/test_fizzbuzz.py new file mode 100644 index 0000000..d2b08ed --- /dev/null +++ b/chapter30_testing/fizzbuzz_fizzbuzz_test_passing/test_fizzbuzz.py @@ -0,0 +1,16 @@ +import fizzbuzz +import unittest + +class TestFizzBuzz(unittest.TestCase): + + def test_multiple_of_three(self): + self.assertEqual(fizzbuzz.process(6), 'Fizz') + + def test_multiple_of_five(self): + self.assertEqual(fizzbuzz.process(20), 'Buzz') + + def test_fizzbuzz(self): + self.assertEqual(fizzbuzz.process(15), 'FizzBuzz') + +if __name__ == '__main__': + unittest.main() \ No newline at end of file diff --git a/chapter30_testing/test.txt b/chapter30_testing/test.txt new file mode 100644 index 0000000..5ae2e6b --- /dev/null +++ b/chapter30_testing/test.txt @@ -0,0 +1,7 @@ +The following are tests for doctest_external + +>>> from doctest_external import add +>>> add(1, 2) +3 +>>> add(4, 5) +9 \ No newline at end of file diff --git a/chapter31_jupyter/Hello World.ipynb b/chapter31_jupyter/Hello World.ipynb new file mode 100644 index 0000000..9309eed --- /dev/null +++ b/chapter31_jupyter/Hello World.ipynb @@ -0,0 +1,45 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This is an example of printing in Python: `print('Hello')`\n", + "\n", + "Code block:\n", + "\n", + "```python\n", + "print('hello world')\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.4" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/chapter32_argparse/file_parser.py b/chapter32_argparse/file_parser.py new file mode 100644 index 0000000..9b3db74 --- /dev/null +++ b/chapter32_argparse/file_parser.py @@ -0,0 +1,20 @@ +# file_parser.py + +import argparse + +def file_parser(input_file, output_file=''): + print(f'Processing {input_file}') + print('Finished processing') + if output_file: + print(f'Creating {output_file}') + +def main(): + parser = argparse.ArgumentParser('File parser') + parser.add_argument('--infile', help='Input file') + parser.add_argument('--out', help='Output file') + args = parser.parse_args() + if args.infile: + file_parser(args.infile, args.out) + +if __name__ == '__main__': + main() diff --git a/chapter32_argparse/file_parser_aliases.py b/chapter32_argparse/file_parser_aliases.py new file mode 100644 index 0000000..d56f8b9 --- /dev/null +++ b/chapter32_argparse/file_parser_aliases.py @@ -0,0 +1,23 @@ +# file_parser_aliases.py + +import argparse + +def file_parser(input_file, output_file=''): + print(f'Processing {input_file}') + print('Finished processing') + if output_file: + print(f'Creating {output_file}') + +def main(): + parser = argparse.ArgumentParser('File parser', + description='PyParse - The File Processor', + epilog='Thank you for choosing PyParse!', + add_help=False) + parser.add_argument('-i', '--infile', help='Input file for conversion') + parser.add_argument('-o', '--out', help='Converted output file') + args = parser.parse_args() + if args.infile: + file_parser(args.infile, args.out) + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/chapter32_argparse/file_parser_aliases2.py b/chapter32_argparse/file_parser_aliases2.py new file mode 100644 index 0000000..e64d642 --- /dev/null +++ b/chapter32_argparse/file_parser_aliases2.py @@ -0,0 +1,23 @@ +# file_parser_aliases2.py + +import argparse + +def file_parser(input_file, output_file=''): + print(f'Processing {input_file}') + print('Finished processing') + if output_file: + print(f'Creating {output_file}') + +def main(): + parser = argparse.ArgumentParser('File parser', + description='PyParse - The File Processor', + epilog='Thank you for choosing PyParse!', + add_help=False) + parser.add_argument('-i', '--infile', help='Input file for conversion') + parser.add_argument('-o', '--out', help='Converted output file') + args = parser.parse_args() + if args.infile: + file_parser(args.infile, args.out) + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/chapter32_argparse/file_parser_exclusive.py b/chapter32_argparse/file_parser_exclusive.py new file mode 100644 index 0000000..7eb3b82 --- /dev/null +++ b/chapter32_argparse/file_parser_exclusive.py @@ -0,0 +1,24 @@ +# file_parser_exclusive.py + +import argparse + +def file_parser(input_file, output_file=''): + print(f'Processing {input_file}') + print('Finished processing') + if output_file: + print(f'Creating {output_file}') + +def main(): + parser = argparse.ArgumentParser('File parser', + description='PyParse - The File Processor', + epilog='Thank you for choosing PyParse!', + add_help=False) + group = parser.add_mutually_exclusive_group() + group.add_argument('-i', '--infile', help='Input file for conversion') + group.add_argument('-o', '--out', help='Converted output file') + args = parser.parse_args() + if args.infile: + file_parser(args.infile, args.out) + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/chapter32_argparse/file_parser_no_help.py b/chapter32_argparse/file_parser_no_help.py new file mode 100644 index 0000000..b69614e --- /dev/null +++ b/chapter32_argparse/file_parser_no_help.py @@ -0,0 +1,23 @@ +# file_parser_no_help.py + +import argparse + +def file_parser(input_file, output_file=''): + print(f'Processing {input_file}') + print('Finished processing') + if output_file: + print(f'Creating {output_file}') + +def main(): + parser = argparse.ArgumentParser('File parser', + description='PyParse - The File Processor', + epilog='Thank you for choosing PyParse!', + add_help=False) + parser.add_argument('--infile', help='Input file for conversion') + parser.add_argument('--out', help='Converted output file') + args = parser.parse_args() + if args.infile: + file_parser(args.infile, args.out) + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/chapter32_argparse/file_parser_with_description.py b/chapter32_argparse/file_parser_with_description.py new file mode 100644 index 0000000..c755a64 --- /dev/null +++ b/chapter32_argparse/file_parser_with_description.py @@ -0,0 +1,22 @@ +# file_parser_with_description.py + +import argparse + +def file_parser(input_file, output_file=''): + print(f'Processing {input_file}') + print('Finished processing') + if output_file: + print(f'Creating {output_file}') + +def main(): + parser = argparse.ArgumentParser('File parser', + description='PyParse - The File Processor', + epilog='Thank you for choosing PyParse!') + parser.add_argument('--infile', help='Input file for conversion') + parser.add_argument('--out', help='Converted output file') + args = parser.parse_args() + if args.infile: + file_parser(args.infile, args.out) + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/chapter32_argparse/pysearch.py b/chapter32_argparse/pysearch.py new file mode 100644 index 0000000..9a2e0cc --- /dev/null +++ b/chapter32_argparse/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/chapter32_argparse/sys_args.py b/chapter32_argparse/sys_args.py new file mode 100644 index 0000000..2d1e1a2 --- /dev/null +++ b/chapter32_argparse/sys_args.py @@ -0,0 +1,10 @@ +# sys_args.py + +import sys + +def main(): + print('You passed the following arguments:') + print(sys.argv) + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/chapter33_xml/appts.xml b/chapter33_xml/appts.xml new file mode 100644 index 0000000..5b59f28 --- /dev/null +++ b/chapter33_xml/appts.xml @@ -0,0 +1,21 @@ + + + + 1181251680 + 040000008200E000 + 1181572063 + + + 1800 + Bring pizza home + + + 1181253977 + sdlkjlkadhdakhdfd + 1181588888 + TX + Dallas + 1800 + Bring pizza home + + \ No newline at end of file diff --git a/chapter33_xml/create_xml.py b/chapter33_xml/create_xml.py new file mode 100644 index 0000000..e10ad5f --- /dev/null +++ b/chapter33_xml/create_xml.py @@ -0,0 +1,26 @@ +# create_xml.py + +import xml.etree.ElementTree as ET + + +def create_xml(xml_file): + root_element = ET.Element('note_taker') + note_element = ET.Element('note') + root_element.append(note_element) + + # add note sub-elements + to_element = ET.SubElement(note_element, 'to') + to_element.text = 'Mike' + from_element = ET.SubElement(note_element, 'from') + from_element.text = 'Nick' + heading_element = ET.SubElement(note_element, 'heading') + heading_element.text = 'Appointment' + body_element = ET.SubElement(note_element, 'body') + body_element.text = 'blah blah' + + tree = ET.ElementTree(root_element) + with open(xml_file, "wb") as fh: + tree.write(fh) + +if __name__ == '__main__': + create_xml('test_create.xml') \ No newline at end of file diff --git a/chapter33_xml/edit_xml.py b/chapter33_xml/edit_xml.py new file mode 100644 index 0000000..d2177c3 --- /dev/null +++ b/chapter33_xml/edit_xml.py @@ -0,0 +1,18 @@ +# edit_xml.py + + +import xml.etree.cElementTree as ET + +def edit_xml(xml_file, output_file, from_person): + tree = ET.ElementTree(file=xml_file) + root = tree.getroot() + + for from_element in tree.iter(tag='from'): + from_element.text = from_person + + tree = ET.ElementTree(root) + with open(output_file, "wb") as f: + tree.write(f) + +if __name__ == '__main__': + edit_xml('note.xml', 'output.xml', 'Guido') \ No newline at end of file diff --git a/chapter33_xml/lxml_output.xml b/chapter33_xml/lxml_output.xml new file mode 100644 index 0000000..aa4455c --- /dev/null +++ b/chapter33_xml/lxml_output.xml @@ -0,0 +1,15 @@ + + + Guido + Nadine + Reminder + Don't forget the milk + I'm new! + + + Nicole + Nadine + Appointment + Eye doctor + + diff --git a/chapter33_xml/note.xml b/chapter33_xml/note.xml new file mode 100644 index 0000000..737369c --- /dev/null +++ b/chapter33_xml/note.xml @@ -0,0 +1,15 @@ + + + + Mike + Nadine + Reminder + Don't forget the milk + + + Nicole + Nadine + Appointment + Eye doctor + + \ No newline at end of file diff --git a/chapter33_xml/output.xml b/chapter33_xml/output.xml new file mode 100644 index 0000000..f5539f4 --- /dev/null +++ b/chapter33_xml/output.xml @@ -0,0 +1,14 @@ + + + Mike + Guido + Reminder + Don't forget the milk + + + Nicole + Guido + Appointment + Eye doctor + + \ No newline at end of file diff --git a/chapter33_xml/parse_xml.py b/chapter33_xml/parse_xml.py new file mode 100644 index 0000000..4083e82 --- /dev/null +++ b/chapter33_xml/parse_xml.py @@ -0,0 +1,19 @@ +# parse_xml.py + +from xml.etree.ElementTree import ElementTree + +def parse_xml(xml_file): + tree = ElementTree(file=xml_file) + root_element = tree.getroot() + print(f"The root element's tag is '{root_element.tag}'") + + for child_element in root_element: + print(f'{child_element.tag=}, {child_element.text=}') + if child_element.tag == 'note': + for note_element in child_element: + print(f'{note_element.tag=}, {note_element.text=}') + + +if __name__ == '__main__': + parse_xml('note.xml') + diff --git a/chapter33_xml/parse_xml_with_lxml.py b/chapter33_xml/parse_xml_with_lxml.py new file mode 100644 index 0000000..d81ba59 --- /dev/null +++ b/chapter33_xml/parse_xml_with_lxml.py @@ -0,0 +1,39 @@ +# parse_xml_with_lxml.py + +from lxml import etree, objectify + +def parse_xml(xml_file): + with open(xml_file) as f: + xml = f.read() + + root = objectify.fromstring(xml) + + # Get an element + to = root.note.to + print(f'The {to=}') + + # print out all the note element's tags and text values + for note in root.getchildren(): + for note_element in note.getchildren(): + print(f'{note_element.tag=}, {note_element.text=}') + print() + + # modify a text value + print(f'Original: {root.note.to=}') + root.note.to = 'Guido' + print(f'Modified: {root.note.to=}') + + # add a new element + root.note.new_element = "I'm new!" + + # cleanup the XML before writing to disk + objectify.deannotate(root) + etree.cleanup_namespaces(root) + obj_xml = etree.tostring(root, pretty_print=True) + + # save your xml + with open("lxml_output.xml", "wb") as f: + f.write(obj_xml) + +if __name__ == '__main__': + parse_xml('note.xml') \ No newline at end of file diff --git a/chapter33_xml/test_create.xml b/chapter33_xml/test_create.xml new file mode 100644 index 0000000..ef03836 --- /dev/null +++ b/chapter33_xml/test_create.xml @@ -0,0 +1,2 @@ + +MikeNickAppointmentblah blah \ No newline at end of file diff --git a/chapter33_xml/xml_tree_iterator.py b/chapter33_xml/xml_tree_iterator.py new file mode 100644 index 0000000..c7b0b95 --- /dev/null +++ b/chapter33_xml/xml_tree_iterator.py @@ -0,0 +1,13 @@ +# xml_tree_iterator.py + +from xml.etree.cElementTree import ElementTree + +def parse_xml(xml_file): + tree = ElementTree(file=xml_file) + print("Iterating using a tree iterator") + for elem in tree.iter(): + print(f'{elem.tag=}, {elem.text=}') + + +if __name__ == '__main__': + parse_xml('note.xml') diff --git a/chapter34_json/create_json_file.py b/chapter34_json/create_json_file.py new file mode 100644 index 0000000..6466678 --- /dev/null +++ b/chapter34_json/create_json_file.py @@ -0,0 +1,21 @@ +# create_json_file.py + +import json + +def create_json_file(path, obj): + with open(path, 'w') as fh: + json.dump(obj, fh) + +if __name__ == '__main__': + j = {"menu": { + "id": "file", + "value": "File", + "popup": { + "menuitem": [ + {"value": "New", "onclick": "CreateNewDoc()"}, + {"value": "Open", "onclick": "OpenDoc()"}, + {"value": "Close", "onclick": "CloseDoc()"} + ] + } + }} + create_json_file('test.json', j) \ No newline at end of file diff --git a/chapter34_json/example.json b/chapter34_json/example.json new file mode 100644 index 0000000..1faefce --- /dev/null +++ b/chapter34_json/example.json @@ -0,0 +1 @@ +{"menu": {"id": "file", "value": "File", "popup": {"menuitem": [{"value": "New", "onclick": "CreateNewDoc()"}, {"value": "Open", "onclick": "OpenDoc()"}, {"value": "Close", "onclick": "CloseDoc()"}]}}} \ No newline at end of file diff --git a/chapter34_json/load_json_file.py b/chapter34_json/load_json_file.py new file mode 100644 index 0000000..769a42c --- /dev/null +++ b/chapter34_json/load_json_file.py @@ -0,0 +1,12 @@ +# load_json_file.py + +import json + +def load_json_file(path): + with open(path) as fh: + j_obj = json.load(fh) + print(type(j_obj)) + + +if __name__ == '__main__': + load_json_file('example.json') \ No newline at end of file diff --git a/chapter35_scrape_website/downloading_files.py b/chapter35_scrape_website/downloading_files.py new file mode 100644 index 0000000..ee766f7 --- /dev/null +++ b/chapter35_scrape_website/downloading_files.py @@ -0,0 +1,15 @@ +import urllib.request + +def download_file(url): + urllib.request.urlretrieve(url, "code.zip") + +def alternate_download(url): + with urllib.request.urlopen(url) as response: + data = response.read() + + with open("code2.zip", "wb") as code: + code.write(data) + +if __name__ == '__main__': + url = 'http://www.blog.pythonlibrary.org/wp-content/uploads/2012/06/wxDbViewer.zip' + download_file(url) \ No newline at end of file diff --git a/chapter35_scrape_website/scraper.py b/chapter35_scrape_website/scraper.py new file mode 100644 index 0000000..2da8526 --- /dev/null +++ b/chapter35_scrape_website/scraper.py @@ -0,0 +1,29 @@ +# scraper.py + +import urllib.request + +from bs4 import BeautifulSoup + + +def download_html(url): + with urllib.request.urlopen(url) as response: + html = response.read() + return html + +def scraper(url): + html = download_html(url) + soup = BeautifulSoup(html, 'html.parser') + + title_links = soup.findAll('h1') + articles = {} + for link in title_links: + if link.a: + articles[link.a['href']] = link.text.strip() + + for article in articles: + print(f'{articles[article]} - {article}') + + +if __name__ == '__main__': + url = 'https://www.blog.pythonlibrary.org' + scraper(url) \ No newline at end of file diff --git a/chapter36_csv/books.csv b/chapter36_csv/books.csv new file mode 100644 index 0000000..9833652 --- /dev/null +++ b/chapter36_csv/books.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 \ No newline at end of file diff --git a/chapter36_csv/csv_dict_reader.py b/chapter36_csv/csv_dict_reader.py new file mode 100644 index 0000000..0e0acc4 --- /dev/null +++ b/chapter36_csv/csv_dict_reader.py @@ -0,0 +1,12 @@ +# csv_dict_reader.py + +import csv + +def process_csv_dict_reader(file_obj): + reader = csv.DictReader(file_obj) + for line in reader: + print(f'{line["book_title"]} by {line["author"]}') + +if __name__ == '__main__': + 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 new file mode 100644 index 0000000..964144b --- /dev/null +++ b/chapter36_csv/csv_dict_writer.py @@ -0,0 +1,28 @@ +# csv_dict_writer.py + +import csv + +def csv_dict_writer(path, headers, data): + with open(path, 'w', newline='') as csvfile: + writer = csv.DictWriter(csvfile, delimiter=',', + fieldnames=headers) + writer.writeheader() + for record in data: + writer.writerow(record) + +if __name__ == '__main__': + data = '''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''' + records = [] + for line in data.splitlines(): + records.append(line.strip().split(',')) + headers = records.pop(0) + + list_of_dicts = [] + for row in records: + my_dict = dict(zip(headers, row)) + list_of_dicts.append(my_dict) + + csv_dict_writer('output_dict.csv', headers, list_of_dicts) \ No newline at end of file diff --git a/chapter36_csv/csv_reader.py b/chapter36_csv/csv_reader.py new file mode 100644 index 0000000..ccdc251 --- /dev/null +++ b/chapter36_csv/csv_reader.py @@ -0,0 +1,12 @@ +# csv_reader.py + +import csv + +def process_csv(path): + with open(path, newline='') as csvfile: + reader = csv.reader(csvfile) + for row in reader: + print(row) + +if __name__ == '__main__': + process_csv('books.csv') \ No newline at end of file diff --git a/chapter36_csv/csv_reader_no_header.py b/chapter36_csv/csv_reader_no_header.py new file mode 100644 index 0000000..5030e66 --- /dev/null +++ b/chapter36_csv/csv_reader_no_header.py @@ -0,0 +1,14 @@ +# csv_reader_no_header.py + +import csv + +def process_csv(path): + with open(path, newline='') as csvfile: + reader = csv.reader(csvfile) + # Skip the header + next(reader, None) + for row in reader: + print(row) + +if __name__ == '__main__': + process_csv('books.csv') \ No newline at end of file diff --git a/chapter36_csv/csv_writer.py b/chapter36_csv/csv_writer.py new file mode 100644 index 0000000..0fa594b --- /dev/null +++ b/chapter36_csv/csv_writer.py @@ -0,0 +1,19 @@ +# csv_writer.py + +import csv + +def csv_writer(path, data): + with open(path, 'w', newline='') as csvfile: + writer = csv.writer(csvfile, delimiter=',') + for row in data: + writer.writerow(row) + +if __name__ == '__main__': + data = '''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''' + records = [] + 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 new file mode 100644 index 0000000..0143498 --- /dev/null +++ b/chapter36_csv/csv_writer_rows.py @@ -0,0 +1,18 @@ +# csv_writer_rows.py + +import csv + +def csv_writer(path, data): + with open(path, 'w', newline='') as csvfile: + writer = csv.writer(csvfile, delimiter=',') + writer.writerows(data) + +if __name__ == '__main__': + data = '''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''' + records = [] + 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 new file mode 100644 index 0000000..6d9c4de --- /dev/null +++ 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 new file mode 100644 index 0000000..6d9c4de --- /dev/null +++ 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 new file mode 100644 index 0000000..77c6a82 --- /dev/null +++ b/chapter37_sqlite/add_data.py @@ -0,0 +1,25 @@ +# add_data.py + +import sqlite3 + +conn = sqlite3.connect("books.db") +cursor = conn.cursor() + +# insert a record into the database +cursor.execute("""INSERT INTO books + VALUES ('Python 101', 'Mike Driscoll', '9/01/2020', + 'Mouse Vs Python', 'epub')""" + ) + +# save data to database +conn.commit() + +# insert multiple records using the more secure "?" method +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 new file mode 100644 index 0000000..54ab740 --- /dev/null +++ b/chapter37_sqlite/create_database.py @@ -0,0 +1,13 @@ +# create_database.py + +import sqlite3 + +conn = sqlite3.connect("books.db") + +cursor = conn.cursor() + +# create a table +cursor.execute("""CREATE TABLE books + (title text, author text, release_date text, + publisher text, book_type text) + """) \ No newline at end of file diff --git a/chapter37_sqlite/delete_record.py b/chapter37_sqlite/delete_record.py new file mode 100644 index 0000000..43c0726 --- /dev/null +++ b/chapter37_sqlite/delete_record.py @@ -0,0 +1,17 @@ +# delete_record.py + +import sqlite3 + +def delete_author(author): + conn = sqlite3.connect("books.db") + cursor = conn.cursor() + + sql = f""" + DELETE FROM books + WHERE author = '{author}' + """ + cursor.execute(sql) + conn.commit() + +if __name__ == '__main__': + delete_author(author='Al Sweigart') \ No newline at end of file diff --git a/chapter37_sqlite/queries.py b/chapter37_sqlite/queries.py new file mode 100644 index 0000000..7cb106f --- /dev/null +++ b/chapter37_sqlite/queries.py @@ -0,0 +1,29 @@ +# queries.py + +import sqlite3 + +def get_cursor(): + conn = sqlite3.connect("books.db") + return conn.cursor() + +def select_all_records_by_author(cursor, author): + sql = "SELECT * FROM books WHERE 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"): + print(row) + +def select_using_like(cursor, text): + print("\nLIKE query results:\n") + sql = f""" + SELECT * FROM books + WHERE title LIKE '{text}%'""" + cursor.execute(sql) + print(cursor.fetchall()) + +if __name__ == '__main__': + cursor = get_cursor() + select_all_records_by_author(cursor, + author='Mike Driscoll') + select_using_like(cursor, text='Python') \ No newline at end of file diff --git a/chapter37_sqlite/update_record.py b/chapter37_sqlite/update_record.py new file mode 100644 index 0000000..46ae430 --- /dev/null +++ b/chapter37_sqlite/update_record.py @@ -0,0 +1,19 @@ +# update_record.py + +import sqlite3 + + +def update_author(old_name, new_name): + conn = sqlite3.connect("books.db") + cursor = conn.cursor() + sql = f""" + UPDATE books + SET author = '{new_name}' + WHERE author = '{old_name}' + """ + cursor.execute(sql) + conn.commit() + +if __name__ == '__main__': + update_author(old_name='Mike Driscoll', + new_name='Michael Driscoll') \ No newline at end of file diff --git a/chapter38_excel/books.xlsx b/chapter38_excel/books.xlsx new file mode 100644 index 0000000..f57e615 Binary files /dev/null and b/chapter38_excel/books.xlsx differ diff --git a/chapter38_excel/creating_sheets.py b/chapter38_excel/creating_sheets.py new file mode 100644 index 0000000..d8bdb38 --- /dev/null +++ b/chapter38_excel/creating_sheets.py @@ -0,0 +1,18 @@ +# creating_sheets.py + +import openpyxl + +def create_worksheets(path): + workbook = openpyxl.Workbook() + print(workbook.sheetnames) + # Add a new worksheet + workbook.create_sheet() + print(workbook.sheetnames) + # Insert a worksheet + workbook.create_sheet(index=1, + title='Second sheet') + print(workbook.sheetnames) + workbook.save(path) + +if __name__ == '__main__': + create_worksheets('sheets.xlsx') \ No newline at end of file diff --git a/chapter38_excel/delete_demo.py b/chapter38_excel/delete_demo.py new file mode 100644 index 0000000..58098a3 --- /dev/null +++ b/chapter38_excel/delete_demo.py @@ -0,0 +1,21 @@ +# delete_demo.py + +from openpyxl import Workbook + +def deleting_cols_rows(path): + workbook = Workbook() + sheet = workbook.active + sheet['A1'] = 'Hello' + sheet['B1'] = 'from' + sheet['C1'] = 'OpenPyXL' + sheet['A2'] = 'row 2' + sheet['A3'] = 'row 3' + sheet['A4'] = 'row 4' + # Delete column A + sheet.delete_cols(idx=1) + # delete 2 rows starting on the second row + sheet.delete_rows(idx=2, amount=2) + workbook.save(path) + +if __name__ == '__main__': + deleting_cols_rows('deleting.xlsx') \ No newline at end of file diff --git a/chapter38_excel/delete_sheets.py b/chapter38_excel/delete_sheets.py new file mode 100644 index 0000000..026234e --- /dev/null +++ b/chapter38_excel/delete_sheets.py @@ -0,0 +1,17 @@ +# delete_sheets.py + +import openpyxl + +def create_worksheets(path): + workbook = openpyxl.Workbook() + workbook.create_sheet() + # Insert a worksheet + workbook.create_sheet(index=1, + title='Second sheet') + print(workbook.sheetnames) + del workbook['Second sheet'] + print(workbook.sheetnames) + workbook.save(path) + +if __name__ == '__main__': + create_worksheets('del_sheets.xlsx') \ No newline at end of file diff --git a/chapter38_excel/insert_demo.py b/chapter38_excel/insert_demo.py new file mode 100644 index 0000000..8046b29 --- /dev/null +++ b/chapter38_excel/insert_demo.py @@ -0,0 +1,18 @@ +# insert_demo.py + +from openpyxl import Workbook + +def inserting_cols_rows(path): + workbook = Workbook() + sheet = workbook.active + sheet['A1'] = 'Hello' + sheet['A2'] = 'from' + sheet['A3'] = 'OpenPyXL' + # insert a column before A + sheet.insert_cols(idx=1) + # insert 2 rows starting on the second row + sheet.insert_rows(idx=2, amount=2) + workbook.save(path) + +if __name__ == '__main__': + inserting_cols_rows('inserting.xlsx') \ No newline at end of file diff --git a/chapter38_excel/iterating_over_cell_values.py b/chapter38_excel/iterating_over_cell_values.py new file mode 100644 index 0000000..3f64c5e --- /dev/null +++ b/chapter38_excel/iterating_over_cell_values.py @@ -0,0 +1,14 @@ +# iterating_over_cell_values.py + +from openpyxl import load_workbook + +def iterating_over_values(path): + workbook = load_workbook(filename=path) + sheet = workbook.active + for value in sheet.iter_rows(min_row=1, max_row=3, + min_col=1, max_col=3, + values_only=True): + print(value) + +if __name__ == '__main__': + iterating_over_values('books.xlsx') \ No newline at end of file diff --git a/chapter38_excel/iterating_over_cells.py b/chapter38_excel/iterating_over_cells.py new file mode 100644 index 0000000..0cdd545 --- /dev/null +++ b/chapter38_excel/iterating_over_cells.py @@ -0,0 +1,12 @@ +# iterating_over_cells.py + +from openpyxl import load_workbook + +def iterating_range(path): + workbook = load_workbook(filename=path) + sheet = workbook.active + for cell in sheet['A']: + print(cell) + +if __name__ == '__main__': + iterating_range('books.xlsx') \ No newline at end of file diff --git a/chapter38_excel/open_workbook.py b/chapter38_excel/open_workbook.py new file mode 100644 index 0000000..8e889d7 --- /dev/null +++ b/chapter38_excel/open_workbook.py @@ -0,0 +1,13 @@ +# open_workbook.py + +from openpyxl import load_workbook + +def open_workbook(path): + workbook = load_workbook(filename=path) + print(f'Worksheet names: {workbook.sheetnames}') + sheet = workbook.active + print(sheet) + print(f'The title of the Worksheet is: {sheet.title}') + +if __name__ == '__main__': + open_workbook('books.xlsx') \ No newline at end of file diff --git a/chapter38_excel/remove_sheets.py b/chapter38_excel/remove_sheets.py new file mode 100644 index 0000000..9dbeb4f --- /dev/null +++ b/chapter38_excel/remove_sheets.py @@ -0,0 +1,17 @@ +# remove_sheets.py + +import openpyxl + +def create_worksheets(path): + workbook = openpyxl.Workbook() + sheet1 = workbook.create_sheet() + # Insert a worksheet + workbook.create_sheet(index=1, + title='Second sheet') + print(workbook.sheetnames) + workbook.remove(workbook['Second sheet']) + print(workbook.sheetnames) + workbook.save(path) + +if __name__ == '__main__': + create_worksheets('remove_sheets.xlsx') \ No newline at end of file diff --git a/chapter38_excel/workbook_cells.py b/chapter38_excel/workbook_cells.py new file mode 100644 index 0000000..82d6668 --- /dev/null +++ b/chapter38_excel/workbook_cells.py @@ -0,0 +1,24 @@ +# workbook_cells.py + +from openpyxl import load_workbook + +def get_cell_info(path): + workbook = load_workbook(filename=path) + sheet = workbook.active + print(sheet) + print(f'The title of the Worksheet is: {sheet.title}') + print(f'The value of {sheet["A2"].value=}') + print(f'The value of {sheet["A3"].value=}') + cell = sheet['B3'] + print(f'{cell.value=}') + +def get_info_by_coord(path): + workbook = load_workbook(filename=path) + sheet = workbook.active + cell = sheet['A2'] + print(f'Row {cell.row}, Col {cell.column} = {cell.value}') + print(f'{cell.value=} is at {cell.coordinate=}') + +if __name__ == '__main__': + get_cell_info('books.xlsx') + get_info_by_coord('books.xlsx') \ No newline at end of file diff --git a/chapter38_excel/writing_hello.py b/chapter38_excel/writing_hello.py new file mode 100644 index 0000000..09be8ec --- /dev/null +++ b/chapter38_excel/writing_hello.py @@ -0,0 +1,14 @@ +# writing_hello.py + +from openpyxl import Workbook + +def create_workbook(path): + workbook = Workbook() + sheet = workbook.active + sheet['A1'] = 'Hello' + sheet['A2'] = 'from' + sheet['A3'] = 'OpenPyXL' + workbook.save(path) + +if __name__ == '__main__': + create_workbook('hello.xlsx') \ No newline at end of file diff --git a/chapter39_reportlab/canvas_form.py b/chapter39_reportlab/canvas_form.py new file mode 100644 index 0000000..f7cc75e --- /dev/null +++ b/chapter39_reportlab/canvas_form.py @@ -0,0 +1,23 @@ +# canvas_form.py + +from reportlab.lib.pagesizes import letter +from reportlab.pdfgen import canvas + +def form(path): + my_canvas = canvas.Canvas(path, pagesize=letter) + my_canvas.setLineWidth(.3) + my_canvas.setFont('Helvetica', 12) + my_canvas.drawString(30, 750, 'OFFICIAL COMMUNIQUE') + my_canvas.drawString(30, 735, 'OF ACME INDUSTRIES') + my_canvas.drawString(500, 750, "12/12/2010") + my_canvas.line(480, 747, 580, 747) + my_canvas.drawString(275, 725, 'AMOUNT OWED:') + my_canvas.drawString(500, 725, "$1,000.00") + my_canvas.line(378, 723, 580, 723) + my_canvas.drawString(30, 703, 'RECEIVED BY:') + my_canvas.line(120, 700, 580, 700) + my_canvas.drawString(120, 703, "JOHN DOE") + my_canvas.save() + +if __name__ == '__main__': + form('canvas_form.pdf') \ No newline at end of file diff --git a/chapter39_reportlab/drawing_polygons.py b/chapter39_reportlab/drawing_polygons.py new file mode 100644 index 0000000..8c838e7 --- /dev/null +++ b/chapter39_reportlab/drawing_polygons.py @@ -0,0 +1,16 @@ +# drawing_polygons.py + +from reportlab.lib.pagesizes import letter +from reportlab.pdfgen import canvas + +def draw_shapes(): + my_canvas = canvas.Canvas("drawing_polygons.pdf") + my_canvas.setStrokeColorRGB(0.2, 0.5, 0.3) + my_canvas.rect(10, 740, 100, 80, stroke=1, fill=0) + my_canvas.ellipse(10, 680, 100, 630, stroke=1, fill=1) + my_canvas.wedge(10, 600, 100, 550, 45, 90, stroke=1, fill=0) + my_canvas.circle(300, 600, 50) + my_canvas.save() + +if __name__ == '__main__': + draw_shapes() \ No newline at end of file diff --git a/chapter39_reportlab/hello_platypus.py b/chapter39_reportlab/hello_platypus.py new file mode 100644 index 0000000..7e29ee4 --- /dev/null +++ b/chapter39_reportlab/hello_platypus.py @@ -0,0 +1,25 @@ +# hello_platypus.py + +from reportlab.lib.pagesizes import letter +from reportlab.platypus import SimpleDocTemplate, Paragraph +from reportlab.lib.styles import getSampleStyleSheet + +def hello(): + doc = SimpleDocTemplate("hello_platypus.pdf", + pagesize=letter, + rightMargin=72, + leftMargin=72, + topMargin=72, + bottomMargin=18) + styles = getSampleStyleSheet() + + flowables = [] + + text = "Hello, I'm a Paragraph" + para = Paragraph(text, style=styles["Normal"]) + flowables.append(para) + + doc.build(flowables) + +if __name__ == '__main__': + hello() \ No newline at end of file diff --git a/chapter39_reportlab/hello_reportlab.py b/chapter39_reportlab/hello_reportlab.py new file mode 100644 index 0000000..f1d75f9 --- /dev/null +++ b/chapter39_reportlab/hello_reportlab.py @@ -0,0 +1,7 @@ +# hello_reportlab.py + +from reportlab.pdfgen import canvas + +my_canvas = canvas.Canvas("hello.pdf") +my_canvas.drawString(100, 750, "Welcome to Reportlab!") +my_canvas.save() \ No newline at end of file diff --git a/chapter39_reportlab/image_on_canvas.py b/chapter39_reportlab/image_on_canvas.py new file mode 100644 index 0000000..8be2a08 --- /dev/null +++ b/chapter39_reportlab/image_on_canvas.py @@ -0,0 +1,16 @@ +# image_on_canvas.py + +from reportlab.lib.pagesizes import letter +from reportlab.pdfgen import canvas + + +def add_image(image_path): + my_canvas = canvas.Canvas("canvas_image.pdf", + pagesize=letter) + my_canvas.drawImage(image_path, 30, 600, + width=100, height=100) + my_canvas.save() + +if __name__ == '__main__': + image_path = 'snakehead.jpg' + add_image(image_path) \ No newline at end of file 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/chapter39_reportlab/snakehead.jpg b/chapter39_reportlab/snakehead.jpg new file mode 100644 index 0000000..db89582 Binary files /dev/null and b/chapter39_reportlab/snakehead.jpg differ 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