Skip to content

Commit 081363f

Browse files
committed
chapter02 done
1 parent c4624c9 commit 081363f

15 files changed

+950
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ __pycache__/
66
# C extensions
77
*.so
88
*.bat
9+
*.exe
910

1011
# Distribution / packaging
1112
.Python

chapter02/LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2020 Tran Duc Loi
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

chapter02/example0201.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#!/usr/bin/env python3
2+
# -*- coding:utf-8 -*-
3+
###
4+
# Project Name: python4desktop - Created Date: Thursday September 17th 2020
5+
# Author: loitd - Email: loitranduc@gmail.com
6+
# Description: This is a short project/file description
7+
# Copyright (c) 2020 loitd. WWW: https://github.com/loitd
8+
# -----
9+
# Last Modified: Thursday September 17th 2020 9:29:07 am By: loitd
10+
# -----
11+
# HISTORY:
12+
# Date By Comments
13+
# ---------- ------ ----------------------------------------------------------
14+
# 2020-09-17 loitd Initialized
15+
###
16+
17+
from tkinter import *
18+
19+
def hello():
20+
print("Hello")
21+
22+
def makeform(root, fields):
23+
'''Making the form based on the root'''
24+
for field in fields:
25+
# Frame = a container widget to organize other widgets.
26+
row = Frame(root)
27+
lbl = Label(row, width=16, text=field, anchor="w")
28+
# Entry = single-line text field.
29+
ent = Entry(row, width=58)
30+
# pack = organizes widgets in blocks before placing them in the parent widget.
31+
# expand = true => fill space of parent
32+
# fill = NONE | X (horizontally) | Y (vertically) | BOTH
33+
# side = TOP | BOTTOM | LEFT | RIGHT
34+
row.pack(side=TOP, fill=X, padx=5, pady=5)
35+
lbl.pack(side=LEFT)
36+
ent.pack(side=RIGHT, expand=YES, fill=X)
37+
# Insert a sample text
38+
ent.insert(0, "https://github.com/loitd/python4desktop")
39+
# Create a frame to store all buttons
40+
btnFrame = Frame(root)
41+
btnFrame.pack(side=BOTTOM, expand=YES, fill=X)
42+
# Create a button with foreground text color
43+
btn1 = Button(btnFrame, text="Do it", width=10, fg="blue", command=hello)
44+
btn1.pack(side=RIGHT, padx=5, pady=5)
45+
# Create a normal button
46+
btn2 = Button(btnFrame, text="Reset", command=None)
47+
btn2.pack(side=RIGHT, padx=5, pady=5)
48+
# Create a button with image and text on it
49+
imgexit = PhotoImage(file=r"imgexit.gif").subsample(6,7)
50+
btn3 = Button(btnFrame, text="Exit", image=imgexit, compound=LEFT, width=60, command=root.quit)
51+
btn3.image = imgexit # is a must to keep a reference
52+
btn3.pack(side=LEFT, padx=5, pady=5)
53+
54+
if __name__ == "__main__":
55+
root = Tk()
56+
root.iconbitmap("py4de.ico")
57+
root.title("Python4Desktop")
58+
fields = ["Label 1", "Label 2", "Label 3"]
59+
makeform(root, fields)
60+
root.mainloop()

chapter02/example0202.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
#!/usr/bin/env python3
2+
# -*- coding:utf-8 -*-
3+
###
4+
# Project Name: python4desktop - Created Date: Thursday September 17th 2020
5+
# Author: loitd - Email: loitranduc@gmail.com
6+
# Description: A downloader without GUI
7+
# Copyright (c) 2020 loitd. WWW: https://github.com/loitd
8+
# -----
9+
# Last Modified: Thursday September 17th 2020 10:14:57 pm By: loitd
10+
# -----
11+
# HISTORY:
12+
# Date By Comments
13+
# ---------- ------ ----------------------------------------------------------
14+
# 2020-09-17 loitd Initialized
15+
###
16+
import requests, shutil, threading, os, time
17+
from lutils.utils import printlog, printwait
18+
19+
url = "https://file-examples-com.github.io/uploads/2017/11/file_example_MP3_1MG.mp3"
20+
filename = "file_example_MP3_1MG.mp3"
21+
lk = threading.Lock()
22+
23+
def download(url, filename):
24+
lk.acquire()
25+
filename = url.split('/')[-1]
26+
with requests.get(url, stream=True) as r:
27+
filesize = r.headers['Content-length'].encode("utf8")
28+
printlog("Start download {0} kilobytes".format(filesize))
29+
with open(filename+".tmp", 'wb') as fh:
30+
fh.write(filesize)
31+
with open(filename, 'wb') as f:
32+
lk.release()
33+
shutil.copyfileobj(r.raw, f)
34+
return filename, filesize
35+
36+
def percentage(filename):
37+
lk.acquire()
38+
with open(filename+".tmp", "rb") as fh:
39+
filesize = fh.read()
40+
filesize = int(filesize)
41+
printlog("Got filesize = {0}".format(filesize))
42+
os.remove(filename+".tmp")
43+
while 1:
44+
thesize = os.stat(filename).st_size
45+
if thesize == filesize:
46+
printlog("Download finished")
47+
lk.release()
48+
break
49+
else:
50+
printwait("{0}/{1} - Percentage: {2:.2f}%".format(thesize, filesize, (thesize * 100 / filesize)), 1)
51+
52+
if __name__ == "__main__":
53+
dld = threading.Thread(name="Dlder", target=download, daemon=True, args=(url, filename))
54+
dld.start()
55+
percentage(filename)
56+
# Join the downloader thread
57+
dld.join()

chapter02/example0203.py

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
#!/usr/bin/env python3
2+
# -*- coding:utf-8 -*-
3+
###
4+
# Project Name: python4desktop - Created Date: Thursday September 17th 2020
5+
# Author: loitd - Email: loitranduc@gmail.com
6+
# Description: A downloader without GUI
7+
# Copyright (c) 2020 loitd. WWW: https://github.com/loitd
8+
# -----
9+
# Last Modified: Thursday September 17th 2020 10:14:57 pm By: loitd
10+
# -----
11+
# HISTORY:
12+
# Date By Comments
13+
# ---------- ------ ----------------------------------------------------------
14+
# 2020-09-17 loitd Initialized
15+
###
16+
import os, time
17+
from lutils import utils
18+
from tkinter import Tk, messagebox, TOP, BOTTOM, RIGHT, LEFT, YES, NO, X, Y, filedialog, ttk, Frame, Label, Entry, Button, HORIZONTAL, PhotoImage
19+
from queue import Queue
20+
from functools import partial
21+
from requests import get, exceptions
22+
from shutil import copyfileobj
23+
from threading import Thread, Lock
24+
25+
def maingui():
26+
root = Tk()
27+
root.iconbitmap("py4de.ico")
28+
root.title("Python4Desktop Downloader")
29+
# ----url
30+
row = Frame(root)
31+
row.pack(side=TOP, fill=X, padx=5, pady=5)
32+
lbl = Label(row, width=10, text="URL:")
33+
lbl.pack(side=LEFT)
34+
ent = Entry(row, width=88)
35+
ent.pack(side=RIGHT, expand=YES, fill=X)
36+
ent.insert(0, testurl)
37+
# save file name
38+
row = Frame(root)
39+
row.pack(side=TOP, fill=X, padx=5, pady=5)
40+
lbl2 = Label(row, width=10, text="Save as:")
41+
lbl2.pack(side=LEFT)
42+
btnSave = Button(row, text="...", command=saveFileAs)
43+
btnSave.pack(side=RIGHT, expand=NO)
44+
lblSave = Label(row, width=88, text="Please select destination file name/path")
45+
lblSave.pack(side=RIGHT, expand=YES, fill=X)
46+
btnSave['command'] = partial(saveFileAs, lblSave)
47+
# ProgressBar
48+
rowProgress = Frame(root)
49+
rowProgress.pack(side=TOP, fill=X, padx=5, pady=5)
50+
lbl = Label(rowProgress, width=10, text="PROGRESS")
51+
lbl.pack(side=LEFT)
52+
progress = ttk.Progressbar(rowProgress, orient=HORIZONTAL, length=100, mode="determinate")
53+
progress.pack(side=RIGHT, expand=YES, fill=X)
54+
# Buttons
55+
btnFrame = Frame(root)
56+
btnFrame.pack(side=BOTTOM, expand=YES, fill=X)
57+
# Exit
58+
imgexit = PhotoImage(file=r"imgexit.gif").subsample(3)
59+
btnExit = Button(btnFrame, text="Exit", image=imgexit, compound=LEFT, command=(lambda root=root: exit(root)))
60+
btnExit.image = imgexit # is a must to keep a reference
61+
btnExit.pack(side=LEFT, padx=5, pady=5)
62+
# Download
63+
imgdl = PhotoImage(file=r"imgdld.gif").subsample(3)
64+
btnDld = Button(btnFrame, text="DOWNLOAD", compound=LEFT)
65+
btnDld['image'] = imgdl # is a must to keep a reference
66+
btnDld['command'] = partial(start_download, btnDld, ent, progress, lblSave)
67+
btnDld.pack(side=RIGHT, padx=5, pady=5)
68+
# ---loops
69+
cmdloop(root, cmdq)
70+
root.mainloop()
71+
72+
def saveFileAs(lblSave):
73+
files = [('All Files', '*.*'), ('Python Files', '*.py'), ('Text Document', '*.txt')]
74+
filename = filedialog.asksaveasfile(filetypes = files, defaultextension = files)
75+
# print(filename, filename.name)
76+
cmdq.put(( updateFilename, (lblSave, filename.name), {} ))
77+
return filename
78+
79+
def cmdloop(root, cmdq):
80+
try:
81+
if not cmdq.empty():
82+
f,a,k = cmdq.get_nowait()
83+
f(*a, **k)
84+
except Exception as e:
85+
raise(e)
86+
# pass
87+
root.after(200, cmdloop, root, cmdq)
88+
89+
def start_download(btnDld, ent, progress, lblSave):
90+
try:
91+
# cmdq.put((messagebox.showinfo, ('title', 'message'), {}))
92+
cmdq.put(( tonggle_button, (btnDld, ), {} ))
93+
# get some inputs
94+
url = ent.get()
95+
filename = lblSave['text']
96+
if url and url != "": mainq.put_nowait(url)
97+
utils.printlog("Queue size: {0}".format(mainq.qsize()))
98+
lk1 = Lock()
99+
# start DOWNLOAD thread
100+
dld = Thread(name="DOWNLOAD", target=download, daemon=True, args=(filename, lk1, btnDld))
101+
dld.start()
102+
# start STATUS thread
103+
stt = Thread(name="STATUS", target=status, daemon=True, args=(filename, lk1, progress))
104+
stt.start()
105+
# Additional
106+
# JOINING - Blocking
107+
# dld.join()
108+
# stt.join()
109+
# mainq.join() ## block until all tasks are done -> q.task_done()
110+
except Exception as e:
111+
raise(e)
112+
finally:
113+
pass
114+
115+
def exit(root):
116+
EXITFLAG = 1
117+
root.quit()
118+
119+
def download(filename, lk1, btnDld):
120+
lk1.acquire() #acquire the lock before status can
121+
try:
122+
if mainq.empty(): return 0
123+
url = mainq.get(1, 1) #block=True, timeout=None
124+
# filename = url.split('/')[-1]
125+
utils.printlog("Start download: {0}".format(url))
126+
with get(url, stream=True, timeout=3) as r:
127+
filesize = r.headers['Content-length'].encode("utf8")
128+
utils.printlog("Download size: {0} kilobytes".format(filesize))
129+
with open(filename+".tmp", 'wb') as fh:
130+
fh.write(filesize)
131+
with open(filename, 'wb') as f:
132+
lk1.release()
133+
copyfileobj(r.raw, f)
134+
return filename, filesize
135+
except exceptions.ConnectionError as e:
136+
utils.printlog("Error during connect to URL: {0}".format(e))
137+
# messagebox.showerror("Error", "Connection to {0} timeout. Please check your internet connection.".format(url))
138+
return 0,0
139+
except exceptions.MissingSchema as e:
140+
utils.printlog("Invalid URL: {0}".format(e))
141+
except Exception as e:
142+
raise(e)
143+
finally:
144+
utils.printlog("Finalizing with qsize: {0}".format(mainq.qsize()))
145+
if lk1.locked(): lk1.release()
146+
cmdq.put(( tonggle_button, (btnDld, ), {} )) #toggle the button
147+
148+
def status(filename, lk1, progress):
149+
lk1.acquire()
150+
try:
151+
with open(filename+".tmp", "rb") as fh:
152+
filesize = fh.read()
153+
filesize = int(filesize)
154+
utils.printlog("Got filesize = {0}".format(filesize))
155+
os.remove(filename+".tmp")
156+
while 1: #Return true if the lock is acquired
157+
thesize = os.stat(filename).st_size
158+
if thesize == filesize:
159+
utils.printlog("Download finished")
160+
if lk1.locked(): lk1.release()
161+
cmdq.put(( setProgress, (progress, 100), {} )) #set progress
162+
break
163+
else:
164+
percent = (thesize * 100 / filesize)
165+
utils.printwait("{0}/{1} - Percentage: {2:.2f}% complete".format(thesize, filesize, percent), 1)
166+
cmdq.put(( setProgress, (progress, percent), {} )) #set progress
167+
except FileNotFoundError as e:
168+
utils.printlog("File not found")
169+
return 0
170+
except Exception as e:
171+
raise(e)
172+
finally:
173+
if lk1.locked(): lk1.release()
174+
175+
def tonggle_button(btn):
176+
utils.printlog("Current BTN state is: {0}".format(btn['state']))
177+
if btn['state'] != 'disabled':
178+
btn['state'] = 'disabled'
179+
else:
180+
btn['state'] = 'normal'
181+
182+
def setProgress(progressBar, percent):
183+
progressBar['value'] = percent
184+
185+
def updateFilename(lbl, filename):
186+
lbl['text'] = filename
187+
188+
if __name__ == "__main__":
189+
testurl = "https://file-examples-com.github.io/uploads/2017/11/file_example_MP3_5MG.mp3"
190+
testurl = "https://file-examples-com.github.io/uploads/2017/11/file_example_MP3_1MG.mp3"
191+
testurl = "https://file-examples-com.github.io/uploads/2017/11/file_example_MP3_700KB.mp3"
192+
filename = "file_example_MP3_1MG.mp3"
193+
filename = "file_example_MP3_700KB.mp3"
194+
EXITFLAG = 0
195+
cmdq = Queue()
196+
mainq = Queue(1)
197+
# Start main gui thread
198+
gui = Thread(name="GUI", target=maingui, daemon=True, args=())
199+
gui.start()
200+
# Join the main thread
201+
gui.join()

chapter02/file_example_MP3_1MG.mp3

746 KB
Binary file not shown.

chapter02/file_example_MP3_700KB.mp3

Whitespace-only changes.

chapter02/imgdld.gif

4.86 KB
Loading

chapter02/imgexit.gif

3.45 KB
Loading

chapter02/imgexit.jpg

2.3 KB
Loading

0 commit comments

Comments
 (0)