Skip to content

Commit 54ac139

Browse files
authored
Merge pull request #14 from InnoFang/dev
♻️ 🏗️ ✨ 🎨 Decoupling the CodeCounter and Visualization, and using searching progress bar to make the output more user-friendly
2 parents 05adaa9 + 4dc84ea commit 54ac139

File tree

3 files changed

+132
-76
lines changed

3 files changed

+132
-76
lines changed

code_counter/core/counter.py

Lines changed: 19 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@
55
import asyncio
66
from collections import defaultdict
77
from code_counter.conf.config import Config
8+
from code_counter.core.visualization import GraphVisualization
89
from code_counter.core.countable.iterator import CountableIterator, RemoteCountableIterator
10+
from code_counter.tools.progress import SearchingProgressBar
911

1012

1113
class CodeCounter:
12-
1314
def __init__(self):
1415
self.config = Config()
1516

@@ -18,19 +19,9 @@ def __init__(self):
1819
self.total_blank_lines = 0
1920
self.total_comment_lines = 0
2021
self.files_of_language = defaultdict(int)
22+
self.lines_of_language = defaultdict(int)
2123

2224
self.args = None
23-
self.lines_of_language = {}
24-
25-
self.result = {
26-
'total': {
27-
'code': 0,
28-
'comment': 0,
29-
'blank': 0,
30-
},
31-
'code': {},
32-
'file': {}
33-
}
3425

3526
def setArgs(self, args):
3627
self.args = args
@@ -41,8 +32,6 @@ def setArgs(self, args):
4132
if args.ignore:
4233
self.config.ignore = set(args.ignore)
4334

44-
self.lines_of_language = {suffix: 0 for suffix in self.config.suffix}
45-
4635
def search(self):
4736
if self.args is None:
4837
raise Exception('search_args is None, please invoke the `setArgs` function first.')
@@ -58,7 +47,11 @@ def search(self):
5847
if self.args.verbose:
5948
self.__print_searching_verbose_title(output_file)
6049

50+
progress_bar = SearchingProgressBar()
51+
progress_bar.start()
6152
asyncio.run(self.__search(input_path, output_file))
53+
progress_bar.stop()
54+
progress_bar.join()
6255

6356
self.__print_result_info(output_file)
6457

@@ -118,73 +111,23 @@ def __print_result_info(self, output_file=None):
118111
file=output_file)
119112
print(file=output_file)
120113

121-
self.result['total']['code'] = self.total_code_lines
122-
self.result['total']['blank'] = self.total_blank_lines
123-
self.result['total']['comment'] = self.total_comment_lines
124-
125-
total_files = 0
126-
127-
for _, cnt in self.files_of_language.items():
128-
total_files += cnt
114+
total_files = sum(self.files_of_language.values())
129115

130116
print("\t{:>10} |{:>10} |{:>10} |{:>10} |{:>10}"
131117
.format("Type", "Files", 'Ratio', 'Lines', 'Ratio'), file=output_file)
132118
print("\t{}".format('-' * 65), file=output_file)
133119

134-
for tp, cnt in self.files_of_language.items():
135-
code_line = self.lines_of_language[tp]
136-
self.result['code'][tp] = code_line
137-
self.result['file'][tp] = cnt
120+
for tp, file_count in self.files_of_language.items():
121+
count_count = self.lines_of_language[tp]
138122
print("\t{:>10} |{:>10} |{:>10} |{:>10} |{:>10}".format(
139-
tp, cnt, '%.2f%%' % (cnt / total_files * 100),
140-
code_line, '%.2f%%' % (code_line / self.total_code_lines * 100)), file=output_file)
123+
tp, file_count, '%.2f%%' % (file_count / total_files * 100),
124+
count_count, '%.2f%%' % (count_count / self.total_code_lines * 100)), file=output_file)
141125

142126
def visualize(self):
143-
from matplotlib import pyplot as plt
144-
from matplotlib import font_manager as fm
145-
from matplotlib import cm
146-
import numpy as np
147-
148-
plt.figure('Visualization of Statistical Results', figsize=(15, 6))
149-
150-
size = 0.3
151-
wedgeprops = dict(width=0.3, edgecolor='w')
152-
proptease = fm.FontProperties()
153-
154-
plt.subplot(121)
155-
total_values = list(self.result['total'].values())
156-
total_keys = list(self.result['total'].keys())
157-
explode = np.array([0., 0., 0.])
158-
explode[total_keys.index('code')] = 0.05
159-
patches, l_text, p_text = plt.pie(total_values, labels=total_keys, autopct='%2.1f%%',
160-
explode=explode, startangle=90)
161-
proptease.set_size('x-large')
162-
plt.setp(l_text, fontproperties=proptease)
163-
plt.setp(p_text, fontproperties=proptease)
164-
plt.axis('equal')
165-
plt.title("Total Statistics")
166-
plt.legend(title="Index", loc='best', bbox_to_anchor=(0, 1))
167-
168-
plt.subplot(122)
169-
length = len(self.result['code'].values())
170-
colors = cm.rainbow(np.arange(length) / length)
171-
patches1, l_text1, p_text1 = plt.pie(list(self.result['code'].values()),
172-
labels=list(self.result['code'].keys()), autopct='%2.1f%%', radius=1,
173-
wedgeprops=wedgeprops, colors=colors, pctdistance=0.85, labeldistance=1.1)
174-
patches2, l_text2, p_text2 = plt.pie(list(self.result['file'].values()),
175-
labels=list(self.result['file'].keys()), autopct='%2.1f%%',
176-
radius=1 - size,
177-
wedgeprops=wedgeprops, colors=colors, pctdistance=0.8, labeldistance=0.4)
178-
# font size include: ‘xx-small’,x-small’,'small’,'medium’,‘large’,‘x-large’,‘xx-large’ or number, e.g. '12'
179-
proptease.set_size('x-large')
180-
plt.setp(l_text1, fontproperties=proptease)
181-
proptease.set_size('large')
182-
plt.setp(p_text1, fontproperties=proptease)
183-
proptease.set_size('medium')
184-
plt.setp(p_text2, fontproperties=proptease)
185-
proptease.set_size('small')
186-
plt.setp(l_text2, fontproperties=proptease)
187-
plt.axis('equal')
188-
plt.title("Inner Pie: Code Files, Outer Pie: Code Lines")
189-
plt.legend(list(self.result['code'].keys()), title="Abbreviation", loc='best', bbox_to_anchor=(1.05, 1))
190-
plt.show()
127+
gv = GraphVisualization(
128+
total_code_lines=self.total_code_lines,
129+
total_blank_lines=self.total_blank_lines,
130+
total_comment_lines=self.total_comment_lines,
131+
files_of_language=self.files_of_language,
132+
lines_of_language=self.lines_of_language)
133+
gv.visualize()

code_counter/core/visualization.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
#!/usr/bin/env python3
2+
# -*- coding: utf-8 -*-
3+
4+
from matplotlib import pyplot as plt
5+
from matplotlib import font_manager as fm
6+
from matplotlib import cm
7+
import numpy as np
8+
from collections import defaultdict
9+
10+
11+
class GraphVisualization:
12+
def __init__(self, total_code_lines=0, total_blank_lines=0, total_comment_lines=0,
13+
files_of_language=defaultdict(int), lines_of_language=defaultdict(int)):
14+
self.total_code_lines = total_code_lines
15+
self.total_blank_lines = total_blank_lines
16+
self.total_comment_lines = total_comment_lines
17+
self.code_type_count = defaultdict(int)
18+
self.file_type_count = defaultdict(int)
19+
20+
for tp, file_count in files_of_language.items():
21+
count_count = lines_of_language[tp]
22+
self.code_type_count[tp] = count_count
23+
self.file_type_count[tp] = file_count
24+
25+
def visualize(self):
26+
plt.figure('Visualization of Statistical Results', figsize=(15, 6))
27+
28+
size = 0.3
29+
wedgeprops = dict(width=0.3, edgecolor='w')
30+
proptease = fm.FontProperties()
31+
32+
plt.subplot(121)
33+
total_keys = ['Code', 'Blank', 'Comment']
34+
total_values = [self.total_code_lines, self.total_blank_lines, self.total_comment_lines]
35+
explode = np.array([0., 0., 0.])
36+
explode[0] = 0.05 # Offset the label `Code` outward a little
37+
patches, l_text, p_text = plt.pie(total_values, labels=total_keys, autopct='%2.1f%%',
38+
explode=explode, startangle=90)
39+
proptease.set_size('x-large')
40+
plt.setp(l_text, fontproperties=proptease)
41+
plt.setp(p_text, fontproperties=proptease)
42+
plt.axis('equal')
43+
plt.title("Total Statistics")
44+
plt.legend(title="Index", loc='best', bbox_to_anchor=(0, 1))
45+
46+
plt.subplot(122)
47+
length = len(self.code_type_count)
48+
colors = cm.rainbow(np.arange(length) / length)
49+
patches1, l_text1, p_text1 = plt.pie(list(self.code_type_count.values()),
50+
labels=list(self.code_type_count.keys()), autopct='%2.1f%%', radius=1,
51+
wedgeprops=wedgeprops, colors=colors, pctdistance=0.85, labeldistance=1.1)
52+
patches2, l_text2, p_text2 = plt.pie(list(self.file_type_count.values()),
53+
labels=list(self.file_type_count.keys()), autopct='%2.1f%%',
54+
radius=1 - size,
55+
wedgeprops=wedgeprops, colors=colors, pctdistance=0.8, labeldistance=0.4)
56+
# font size include: ‘xx-small’,x-small’,'small’,'medium’,‘large’,‘x-large’,‘xx-large’ or number, e.g. '12'
57+
proptease.set_size('x-large')
58+
plt.setp(l_text1, fontproperties=proptease)
59+
proptease.set_size('large')
60+
plt.setp(p_text1, fontproperties=proptease)
61+
proptease.set_size('medium')
62+
plt.setp(p_text2, fontproperties=proptease)
63+
proptease.set_size('small')
64+
plt.setp(l_text2, fontproperties=proptease)
65+
plt.axis('equal')
66+
plt.title("Inner Pie: Code Files, Outer Pie: Code Lines")
67+
plt.legend(list(self.code_type_count.keys()), title="Abbreviation", loc='best', bbox_to_anchor=(1.05, 1))
68+
plt.show()

code_counter/tools/progress.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#!/usr/bin/env python3
2+
# -*- coding: utf-8 -*-
3+
4+
import threading
5+
import sys
6+
import time
7+
8+
9+
class SearchingProgressBar(threading.Thread):
10+
__LEN__ = 10
11+
12+
def __init__(self):
13+
super(SearchingProgressBar, self).__init__()
14+
self.daemon = True
15+
self._stop_event = threading.Event()
16+
17+
def stop(self):
18+
self._stop_event.set()
19+
20+
def stopped(self):
21+
return self._stop_event.is_set()
22+
23+
def __clear(self):
24+
sys.stdout.write("\r")
25+
sys.stdout.write(" " * self.__LEN__)
26+
sys.stdout.write("\r")
27+
sys.stdout.flush()
28+
29+
def run(self):
30+
while True:
31+
progress = "searching "
32+
for i in range(self.__LEN__):
33+
if self.stopped():
34+
self.__clear()
35+
return
36+
time.sleep(0.1)
37+
sys.stdout.write("\r")
38+
progress += " ."
39+
sys.stdout.write(progress)
40+
sys.stdout.flush()
41+
sys.stdout.write("\r")
42+
sys.stdout.write("\r")
43+
sys.stdout.write("searching " + " " * self.__LEN__)
44+
sys.stdout.write("\r")
45+
sys.stdout.flush()

0 commit comments

Comments
 (0)