py2ls 0.1.10.23__py3-none-any.whl → 0.1.10.25__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
py2ls/batman.py CHANGED
@@ -3,33 +3,75 @@ from email.mime.text import MIMEText
3
3
  from email.mime.multipart import MIMEMultipart
4
4
  from email.mime.base import MIMEBase
5
5
  from email import encoders
6
- import schedule
7
6
  import time
8
7
  from datetime import datetime
9
8
  import os
10
9
  from pprint import pp
11
10
  import json
11
+ import mimetypes
12
+ import sys
12
13
 
13
14
 
14
- # Helper to convert text or list of text to HTML
15
- def convert_to_html(text_list):
15
+ def convert_to_html(text_list, strict=False):
16
16
  if not isinstance(text_list, list):
17
17
  text_list = [text_list]
18
18
 
19
- # Convert each element into HTML and add line breaks
20
- html_content = "" # "<html><body>\n"
21
- html_content += "".join(
22
- text.replace("\n", "<br>") for text in text_list if text.strip()
23
- )
24
- # html_content += "\n</body></html>"
25
-
19
+ # Define a mapping for escape sequences to their HTML representations
20
+ escape_mapping = {
21
+ "\\": "&bsol;", # Backslash
22
+ "'": "&apos;", # Single quote
23
+ '"': "&quot;", # Double quote
24
+ "\n": "<br>", # Newline
25
+ "\r": "", # Carriage return (not represented in HTML)
26
+ "\t": "&nbsp;&nbsp;&nbsp;&nbsp;", # Tab (4 spaces)
27
+ "\b": "", # Backspace (not typically represented)
28
+ "\f": "", # Form feed (not typically represented)
29
+ "\v": "", # Vertical tab (not typically represented)
30
+ "\a": "", # Bell/alert sound (not typically represented)
31
+ "\0": "", # Null character (not typically represented)
32
+ }
33
+ # Convert text by replacing escape sequences with their HTML equivalents
34
+ html_content = ""
35
+ for text in text_list:
36
+ for escape_seq, html_rep in escape_mapping.items():
37
+ text = text.replace(escape_seq, html_rep)
38
+ html_content += text.replace("\n", "<br>") # Add line breaks for newlines
39
+ if strict:
40
+ html_content = "<html><body>\n" + html_content + "\n</body></html>"
26
41
  return html_content
27
42
 
28
43
 
44
+ def get_name(email_str, by="@"):
45
+ if "num" in by:
46
+ for i, char in enumerate(email_str):
47
+ if char.isdigit():
48
+ break
49
+ else:
50
+ return email_str
51
+ return email_str[:i]
52
+
53
+ # Handle the case where '@' is the criteria
54
+ elif "@" in by:
55
+ name = email_str.split("@")[0]
56
+ name = get_name(name, by="num")
57
+ name = " ".join([part.capitalize() for part in name.split(".")])
58
+ return name
59
+
60
+
29
61
  def extract_kv(
30
- dir_data="/Users/macjianfeng/Dropbox/github/python/py2ls/confidential_data/gmail_login.json",
62
+ dir_data=None,
31
63
  idx=0,
32
64
  ): # select the index
65
+ if dir_data is None:
66
+ if "dar" in sys.platform:
67
+ dir_data = "/Users/macjianfeng/Dropbox/github/python/py2ls/confidential_data/gmail_login.json"
68
+ else:
69
+ if "win" in sys.platform:
70
+ dir_data = (
71
+ "Z:\\Jianfeng\\Apps\settings\\confidential_data\\gmail_login.json"
72
+ )
73
+ elif "lin" in sys.platform:
74
+ dir_data = "/Users/macjianfeng/Dropbox/github/python/py2ls/confidential_data/gmail_login.json"
33
75
  with open(dir_data, "r") as file:
34
76
  email_login = json.load(file)
35
77
  for i, (user, pd) in enumerate(email_login.items()):
@@ -38,6 +80,10 @@ def extract_kv(
38
80
 
39
81
 
40
82
  def email_to(**kwargs):
83
+ return email(**kwargs)
84
+
85
+
86
+ def email(**kwargs):
41
87
  """
42
88
  usage:
43
89
  email_to(who="example@gmail.com",
@@ -106,7 +152,14 @@ def email_to(**kwargs):
106
152
  what = ""
107
153
  if isinstance(what, list):
108
154
  what = convert_to_html(what)
109
- if 'class="dataframe"' in str(attachments):
155
+ if 'class="' in str(what):
156
+ # Combine HTML content correctly
157
+ html_content = "<html><body>\n"
158
+ html_content += f"{what}"
159
+ html_content += f"<br><br>{convert_to_html([signature])}"
160
+ html_content += "\n</body></html>"
161
+ message.attach(MIMEText(html_content, "html"))
162
+ elif 'class="' in str(attachments):
110
163
  # Combine HTML content correctly
111
164
  html_content = "<html><body>\n"
112
165
  html_content += f"{convert_to_html(what)}<br>{attachments}"
@@ -124,15 +177,24 @@ def email_to(**kwargs):
124
177
  if isinstance(attachments, str):
125
178
  attachments = [attachments]
126
179
  for file in attachments:
127
- part = MIMEBase("application", "octet-stream")
180
+ # 有时候一些不常用的文件类型, 无法自动识别
181
+ mime_type, _ = mimetypes.guess_type(file)
182
+ if mime_type is None:
183
+ if file.endswith(".xlsx"):
184
+ mime_type = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
185
+ else:
186
+ mime_type = "application/octet-stream"
187
+
188
+ main_type, sub_type = mime_type.split("/", 1)
189
+ # part = MIMEBase("application", "octet-stream") # 旧的版本
190
+ part = MIMEBase(main_type, sub_type)
128
191
  try:
129
192
  with open(file, "rb") as attachment:
130
193
  part.set_payload(attachment.read())
131
194
  encoders.encode_base64(part)
132
-
195
+ filename = os.path.basename(file)
133
196
  part.add_header(
134
- "Content-Disposition",
135
- f'attachment; filename= "{os.path.basename(file)}"',
197
+ "Content-Disposition", f'attachment; filename="{filename}"'
136
198
  )
137
199
  message.attach(part)
138
200
  except FileNotFoundError:
py2ls/ips.py CHANGED
@@ -58,6 +58,18 @@ except NameError:
58
58
  pass
59
59
 
60
60
 
61
+
62
+
63
+ # set 'dir_save'
64
+ if 'dar' in sys.platform:
65
+ dir_save = "/Users/macjianfeng/Dropbox/Downloads/"
66
+ else:
67
+ if 'win' in sys.platform:
68
+ dir_save= "Z:\\Jianfeng\\temp\\"
69
+ elif 'lin' in sys.platform:
70
+ dir_data="/Users/macjianfeng/Dropbox/github/python/py2ls/confidential_data/gmail_login.json"
71
+
72
+
61
73
  def unique(lst, ascending=None):
62
74
  """
63
75
  移除列表中的重复元素,同时可以选择按升序或降序排序。
@@ -260,10 +272,6 @@ def upgrade(module="py2ls"):
260
272
  except subprocess.CalledProcessError as e:
261
273
  print(f"An error occurred while upgrading py2ls: {e}")
262
274
 
263
-
264
- dir_save = "/Users/macjianfeng/Dropbox/Downloads/"
265
-
266
-
267
275
  def get_version(pkg):
268
276
  import importlib.metadata
269
277
 
@@ -749,6 +757,41 @@ def num2str(num, *args, **kwargs):
749
757
  # num2str(12345333.6789, sep=","), type(num2str(12345.6789, ","))
750
758
  # ) # Output: "12,345.6789"
751
759
  # print(num2str(7000.00, sep=","), type(num2str(7000.00, ","))) # Output: "7,000.00"
760
+
761
+
762
+ # Helper to convert text or list of text to HTML
763
+ def str2html(text_list, strict=False):
764
+ if not isinstance(text_list, list):
765
+ text_list = [text_list]
766
+
767
+ # Define a mapping for escape sequences to their HTML representations
768
+ escape_mapping = {
769
+ "\\": "&bsol;", # Backslash
770
+ "'": "&apos;", # Single quote
771
+ '"': "&quot;", # Double quote
772
+ "\n": "<br>", # Newline
773
+ "\r": "", # Carriage return (not represented in HTML)
774
+ "\t": "&nbsp;&nbsp;&nbsp;&nbsp;", # Tab (4 spaces)
775
+ "\b": "", # Backspace (not typically represented)
776
+ "\f": "", # Form feed (not typically represented)
777
+ "\v": "", # Vertical tab (not typically represented)
778
+ "\a": "", # Bell/alert sound (not typically represented)
779
+ "\0": "", # Null character (not typically represented)
780
+ }
781
+
782
+ # Convert text by replacing escape sequences with their HTML equivalents
783
+ html_content = ""
784
+ for text in text_list:
785
+ for escape_seq, html_rep in escape_mapping.items():
786
+ text = text.replace(escape_seq, html_rep)
787
+ html_content += text.replace("\n", "<br>") # Add line breaks for newlines
788
+
789
+ if strict:
790
+ html_content = "<html><body>\n" + html_content + "\n</body></html>"
791
+
792
+ return html_content
793
+
794
+
752
795
  def sreplace(*args, **kwargs):
753
796
  """
754
797
  sreplace(text, by=None, robust=True)
@@ -1443,10 +1486,21 @@ def fappend(fpath, content=None):
1443
1486
  file.write(content)
1444
1487
 
1445
1488
 
1489
+ def filter_kwargs(kws, valid_kwargs):
1490
+ if isinstance(valid_kwargs, dict):
1491
+ kwargs_filtered = {
1492
+ key: value for key, value in kws.items() if key in valid_kwargs.keys()
1493
+ }
1494
+ elif isinstance(valid_kwargs, list):
1495
+ kwargs_filtered = {
1496
+ key: value for key, value in kws.items() if key in valid_kwargs
1497
+ }
1498
+ return kwargs_filtered
1499
+
1500
+
1446
1501
  def fsave(
1447
1502
  fpath,
1448
1503
  content,
1449
- mode="w",
1450
1504
  how="overwrite",
1451
1505
  kind=None,
1452
1506
  font_name="Times",
@@ -1468,7 +1522,7 @@ def fsave(
1468
1522
  None
1469
1523
  """
1470
1524
 
1471
- def save_content(fpath, content, mode=mode, how="overwrite"):
1525
+ def save_content(fpath, content, mode="w", how="overwrite"):
1472
1526
  if "wri" in how.lower():
1473
1527
  with open(fpath, mode, encoding="utf-8") as file:
1474
1528
  file.write(content)
@@ -1515,13 +1569,36 @@ def fsave(
1515
1569
  pdf.output(fpath, "F")
1516
1570
 
1517
1571
  def save_csv(fpath, data, **kwargs):
1572
+ # https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.to_csv.html
1573
+ kwargs_csv = dict(
1574
+ path_or_buf=None,
1575
+ sep=",",
1576
+ na_rep="",
1577
+ float_format=None,
1578
+ columns=None,
1579
+ header=True,
1580
+ index=True,
1581
+ index_label=None,
1582
+ mode="w",
1583
+ encoding=None,
1584
+ compression="infer",
1585
+ quoting=None,
1586
+ quotechar='"',
1587
+ lineterminator=None,
1588
+ chunksize=None,
1589
+ date_format=None,
1590
+ doublequote=True,
1591
+ escapechar=None,
1592
+ decimal=".",
1593
+ errors="strict",
1594
+ storage_options=None,
1595
+ )
1596
+ kwargs_valid = filter_kwargs(kwargs, kwargs_csv)
1518
1597
  df = pd.DataFrame(data)
1519
- df.to_csv(fpath, **kwargs)
1598
+ df.to_csv(fpath, **kwargs_valid)
1520
1599
 
1521
1600
  def save_xlsx(fpath, data, **kwargs):
1522
- format = kwargs.get("format", None)
1523
- if format:
1524
- kwargs.pop("format", None)
1601
+ if any(kwargs):
1525
1602
  format_excel(df=data, filename=fpath, **kwargs)
1526
1603
  else:
1527
1604
  kwargs.pop("format", None)
@@ -1618,7 +1695,7 @@ def fsave(
1618
1695
  print(
1619
1696
  f"Warning:\n{kind} is not in the supported list ['docx', 'txt', 'md', 'html', 'pdf', 'csv', 'xlsx', 'json', 'xml', 'yaml']"
1620
1697
  )
1621
-
1698
+ mode = kwargs.get("mode", "w")
1622
1699
  if kind == "docx" or kind == "doc":
1623
1700
  save_docx(fpath, content, font_name, font_size, spacing)
1624
1701
  elif kind == "txt":
@@ -1735,46 +1812,66 @@ def sort_kind(df, by="name", ascending=True):
1735
1812
  sorted_df = df.iloc[sorted_index].reset_index(drop=True)
1736
1813
  return sorted_df
1737
1814
 
1738
-
1739
- def isa(*args, **kwargs):
1815
+ def isa(content, kind):
1740
1816
  """
1741
- fpath, contains='img'
1742
- containss file paths based on the specified contains.
1817
+ content, kind='img'
1818
+ kinds file paths based on the specified kind.
1743
1819
  Args:
1744
- fpath (str): Path to the file.
1745
- contains (str): contains of file to contains. Default is 'img' for images. Other options include 'doc' for documents,
1820
+ content (str): Path to the file.
1821
+ kind (str): kind of file to kind. Default is 'img' for images. Other options include 'doc' for documents,
1746
1822
  'zip' for ZIP archives, and 'other' for other types of files.
1747
1823
  Returns:
1748
- bool: True if the file matches the contains, False otherwise.
1824
+ bool: True if the file matches the kind, False otherwise.
1749
1825
  """
1750
- for arg in args:
1751
- if isinstance(arg, str):
1752
- if "/" in arg or "\\" in arg:
1753
- fpath = arg
1754
- else:
1755
- contains = arg
1756
- if "img" in contains.lower() or "image" in contains.lower():
1757
- return is_image(fpath)
1758
- elif "doc" in contains.lower():
1759
- return is_document(fpath)
1760
- elif "zip" in contains.lower():
1761
- return is_zip(fpath)
1762
- elif "dir" in contains.lower() or (
1763
- "f" in contains.lower() and "d" in contains.lower()
1764
- ):
1765
- return os.path.isdir(fpath)
1766
- elif "fi" in contains.lower(): # file
1767
- return os.path.isfile(fpath)
1768
- elif "num" in contains.lower(): # file
1769
- return os.path.isfile(fpath)
1770
- elif "text" in contains.lower() or "txt" in contains.lower(): # file
1771
- return is_text(fpath)
1772
- elif "color" in contains.lower(): # file
1773
- return is_str_color(fpath)
1826
+ if "img" in kind.lower() or "image" in kind.lower():
1827
+ return is_image(content)
1828
+ elif "doc" in kind.lower():
1829
+ return is_document(content)
1830
+ elif "zip" in kind.lower():
1831
+ return is_zip(content)
1832
+ elif "dir" in kind.lower() or ("f" in kind.lower() and "d" in kind.lower()):
1833
+ return os.path.isdir(content)
1834
+ elif "fi" in kind.lower(): # file
1835
+ return os.path.isfile(content)
1836
+ elif "num" in kind.lower(): # file
1837
+ return os.path.isfile(content)
1838
+ elif "text" in kind.lower() or "txt" in kind.lower(): # file
1839
+ return is_text(content)
1840
+ elif "color" in kind.lower(): # file
1841
+ return is_str_color(content)
1842
+ elif "html" in kind.lower():
1843
+ # Remove leading and trailing whitespace
1844
+ content = content.strip()
1845
+ # Check for common HTML tags using regex
1846
+ # This pattern matches anything that looks like an HTML tag
1847
+ tag_pattern = r"<[a-zA-Z][^>]*>(.*?)</[a-zA-Z][^>]*>"
1848
+ # Check for any opening and closing tags
1849
+ if re.search(tag_pattern, content):
1850
+ return True
1851
+ # Additional checks for self-closing tags
1852
+ self_closing_tags = ["img", "br", "hr", "input", "meta", "link"]
1853
+ for tag in self_closing_tags:
1854
+ if f"<{tag}" in content:
1855
+ return True
1856
+ return False
1774
1857
  else:
1775
- print(f"{contains} was not set up correctly")
1858
+ print(f"{kind} was not set up correctly")
1776
1859
  return False
1777
1860
 
1861
+ import sys
1862
+ def get_os():
1863
+ os_name = sys.platform
1864
+ if "dar" in os_name:
1865
+ return "macOS"
1866
+ else:
1867
+ if "win" in os_name:
1868
+ return "Windows"
1869
+ elif "linux" in os_name:
1870
+ return "Linux"
1871
+ else:
1872
+ print(f"{os_name}, returned 'None'")
1873
+ return None
1874
+
1778
1875
 
1779
1876
  def listdir(
1780
1877
  rootdir,
@@ -3186,7 +3283,13 @@ def hex2argb(hex_color):
3186
3283
  # Otherwise, assume it's in RRGGBB format and prepend FF for opaque
3187
3284
  if len(hex_color) == 6:
3188
3285
  return f"FF{hex_color}"
3189
-
3286
+ if len(hex_color) == 7:
3287
+ return f"F{hex_color}"
3288
+ else:
3289
+ if len(hex_color) > 8:
3290
+ return hex_color[-9:]
3291
+ else:
3292
+ return "F" * (9 - len(hex_color)) + hex_color
3190
3293
  raise ValueError(
3191
3294
  "Invalid hex color format. Use RRGGBB, #RRGGBB, or aARRGGBB format."
3192
3295
  )
@@ -3540,7 +3643,6 @@ def format_excel(
3540
3643
  number_format=None, # dict: e.g., {1:"0.00", 2:"#,##0",3:"0%",4:"$#,##0.00"}
3541
3644
  data_validation=None, # dict
3542
3645
  conditional_format=None, # dict
3543
- index_default=False,
3544
3646
  **kwargs,
3545
3647
  ):
3546
3648
  if not isinstance(df, pd.DataFrame):
@@ -3711,15 +3813,29 @@ format_excel(
3711
3813
  print(func_xample)
3712
3814
  return None
3713
3815
  kwargs.pop("format", None) # 更好地跟fsave结合使用
3816
+ kwargs.pop("sheet_name", None) # 更好地跟df.to_excel结合使用
3817
+ sheet_name_corr = (
3818
+ sheet_name if isinstance(sheet_name, str) else f"Sheet_{sheet_name}"
3819
+ )
3714
3820
 
3715
- # Save DataFrame to Excel file
3716
- if not index_default:
3717
- df.to_excel(filename, index=False, **kwargs)
3718
- else:
3719
- df.to_excel(filename, **kwargs)
3821
+ # 只有openpyxl才支持 append
3822
+ mode = strcmp(kwargs.get("mode", "w"), ["a", "w"])[0]
3823
+ kwargs.pop("mode", None)
3824
+ engine = strcmp(kwargs.get("engine", "openpyxl"), ["xlsxwriter", "openpyxl"])[0]
3825
+ # 通常是不需要保存index的
3826
+ index = kwargs.get("index", False)
3827
+ kwargs.pop("index", None)
3828
+ # save file
3829
+ with pd.ExcelWriter(filename, mode=mode, engine=engine) as writer:
3830
+ df.to_excel(writer, sheet_name=sheet_name_corr, index=index, **kwargs)
3720
3831
 
3721
3832
  wb = load_workbook(filename)
3722
- ws = wb.worksheets[sheet_name]
3833
+ if isinstance(sheet_name, str):
3834
+ ws = wb[sheet_name]
3835
+ elif isinstance(sheet_name, int):
3836
+ ws = wb.worksheets[sheet_name]
3837
+ else:
3838
+ ws = wb.worksheets[sheet_name] # the index of worksheets
3723
3839
 
3724
3840
  # !Apply cell formatting
3725
3841
  if cell:
@@ -3812,7 +3928,9 @@ format_excel(
3812
3928
  comment = [comment]
3813
3929
  for comment_ in comment:
3814
3930
  for (row, col), comment_str in comment_.items():
3815
- ws.cell(row=row, column=col).comment = Comment(comment_str, "Author")
3931
+ ws.cell(row=row + 1, column=col + 1).comment = Comment(
3932
+ comment_str, "Author"
3933
+ )
3816
3934
 
3817
3935
  # !Add link
3818
3936
  if link:
@@ -3820,7 +3938,7 @@ format_excel(
3820
3938
  link = [link]
3821
3939
  for link_ in link:
3822
3940
  for (row, col), link_str in link_.items():
3823
- ws.cell(row=row, column=col).hyperlink = link_str
3941
+ ws.cell(row=row + 1, column=col + 1).hyperlink = link_str
3824
3942
 
3825
3943
  # !Apply data validation
3826
3944
  if data_validation:
@@ -3870,3 +3988,52 @@ format_excel(
3870
3988
  # Save the workbook
3871
3989
  wb.save(filename)
3872
3990
  print(f"Formatted Excel file saved as:\n{filename}")
3991
+
3992
+
3993
+ from IPython.display import display, HTML, Markdown, Image
3994
+
3995
+ def preview(var):
3996
+ """Master function to preview formatted variables in Jupyter."""
3997
+
3998
+ if isinstance(var, str):
3999
+ if isa(var,'html'):
4000
+ display(HTML(var)) # Render as HTML
4001
+ # Check if it's a valid markdown
4002
+ elif var.startswith("#"):
4003
+ display(Markdown(var))
4004
+ else:
4005
+ # Otherwise, display as plain text
4006
+ print(var)
4007
+
4008
+ elif isinstance(var, pd.DataFrame):
4009
+ # Display pandas DataFrame
4010
+ display(var)
4011
+
4012
+ elif isinstance(var, list) or isinstance(var, dict):
4013
+ # Display JSON
4014
+ json_str = json.dumps(var, indent=4)
4015
+ display(Markdown(f"```json\n{json_str}\n```"))
4016
+
4017
+ elif isinstance(var, bytes):
4018
+ # Display image if it's in bytes format
4019
+ display(Image(data=var))
4020
+
4021
+ elif isinstance(var, str) and (var.endswith(".png") or var.endswith(".jpg")):
4022
+ # Display image from file path
4023
+ display(Image(filename=var))
4024
+
4025
+ elif isinstance(var, dict):
4026
+ # Handle dictionary formatting
4027
+ json_str = json.dumps(var, indent=4)
4028
+ display(Markdown(f"```json\n{json_str}\n```"))
4029
+
4030
+ else:
4031
+ # If the format is not recognized, print a message
4032
+ print("Format not recognized or unsupported.")
4033
+
4034
+
4035
+ # # Example usages:
4036
+ # preview("This is a plain text message.")
4037
+ # preview("# This is a Markdown header")
4038
+ # preview(pd.DataFrame({"Name": ["Alice", "Bob"], "Age": [25, 30]}))
4039
+ # preview({"key": "value", "numbers": [1, 2, 3]})
py2ls/netfinder.py CHANGED
@@ -582,7 +582,7 @@ def find_links(url, driver="request", booster=False):
582
582
  if all(exclusion not in link_href for exclusion in cond_ex):
583
583
  links_href.append(link_href)
584
584
 
585
- unique_links = list(set(links_href)) # Remove duplicates
585
+ unique_links = ips.unique(links_href) # Remove duplicates
586
586
 
587
587
  if booster:
588
588
  for link in unique_links:
@@ -590,7 +590,7 @@ def find_links(url, driver="request", booster=False):
590
590
  sub_links = find_links(link, driver=driver, booster=False)
591
591
  if sub_links:
592
592
  links_href.extend(sub_links)
593
- links_href = list(set(links_href)) # Remove duplicates again
593
+ links_href = ips.unique(links_href) # Remove duplicates again
594
594
 
595
595
  return links_href
596
596
 
py2ls/ocr.py CHANGED
@@ -286,14 +286,16 @@ def add_text_pil(
286
286
  text,
287
287
  position,
288
288
  font_size=12,
289
- color=(255, 0, 0),
290
- bg_color=(173, 216, 230, 120),
289
+ color=(0, 0, 0),
290
+ bg_color=(133, 203, 245, 100),
291
291
  ):
292
292
  # Convert the image to PIL format
293
- pil_image = Image.fromarray(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
294
- # Create a drawing context
295
- draw = ImageDraw.Draw(pil_image)
293
+ pil_image = Image.fromarray(cv2.cvtColor(image, cv2.COLOR_BGR2RGB)).convert("RGBA")
296
294
  # Define the font (make sure to use a font that supports Chinese characters)
295
+ overlay = Image.new("RGBA", pil_image.size, (255, 255, 255, 0))
296
+ # Create a drawing context
297
+ draw = ImageDraw.Draw(overlay)
298
+
297
299
  try:
298
300
  font = ImageFont.truetype(
299
301
  "/System/Library/Fonts/Supplemental/Songti.ttc", font_size
@@ -328,8 +330,14 @@ def add_text_pil(
328
330
  draw.rectangle(background_rect, fill=bg_color)
329
331
  # Add text to the image
330
332
  draw.text(adjusted_position, text, font=font, fill=color)
333
+ # Ensure both images are in RGBA mode for alpha compositing
334
+ if pil_image.mode != "RGBA":
335
+ pil_image = pil_image.convert("RGBA")
336
+ if overlay.mode != "RGBA":
337
+ overlay = overlay.convert("RGBA")
338
+ combined = Image.alpha_composite(pil_image, overlay)
331
339
  # Convert the image back to OpenCV format
332
- image = cv2.cvtColor(np.array(pil_image), cv2.COLOR_RGBA2BGR)
340
+ image = cv2.cvtColor(np.array(combined), cv2.COLOR_RGBA2BGR)
333
341
  return image
334
342
 
335
343
 
@@ -382,12 +390,15 @@ def preprocess_img(
382
390
  image = cv2.imread(image)
383
391
  if not isinstance(image, np.ndarray):
384
392
  image = np.array(image)
385
- if image.shape[1] == 4: # Check if it has an alpha channel
386
- # Drop the alpha channel (if needed), or handle it as required
387
- image = cv2.cvtColor(image, cv2.COLOR_RGBA2RGB)
388
- else:
389
- # Convert RGB to BGR for OpenCV compatibility
390
- image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
393
+ try:
394
+ if image.shape[1] == 4: # Check if it has an alpha channel
395
+ # Drop the alpha channel (if needed), or handle it as required
396
+ image = cv2.cvtColor(image, cv2.COLOR_RGBA2RGB)
397
+ else:
398
+ # Convert RGB to BGR for OpenCV compatibility
399
+ image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
400
+ except:
401
+ pass
391
402
 
392
403
  # Rotate image
393
404
  if rotate == "auto":
@@ -532,7 +543,7 @@ def get_text(
532
543
  thr=0.1,
533
544
  gpu=True,
534
545
  decoder="wordbeamsearch", #'greedy', 'beamsearch' and 'wordbeamsearch'(hightly accurate)
535
- output="all",
546
+ output="txt",
536
547
  preprocess=None,
537
548
  postprocess="not ready",
538
549
  show=True,
@@ -541,9 +552,9 @@ def get_text(
541
552
  font=cv2.FONT_HERSHEY_SIMPLEX,
542
553
  font_scale=0.8,
543
554
  thickness_text=2, # Line thickness of 2 px
544
- color_box=(0, 255, 0), # draw_box
545
- color_text=(203, 44, 57), # draw_box
546
- bg_color=(173, 216, 230, 128),
555
+ box_color=(0, 255, 0), # draw_box
556
+ font_color=(0, 0, 0),
557
+ bg_color=(133, 203, 245, 100),
547
558
  usage=False,
548
559
  **kwargs,
549
560
  ):
@@ -559,8 +570,8 @@ def get_text(
559
570
  show: 是否显示结果图像。
560
571
  ax: 用于显示图像的 Matplotlib 子图。
561
572
  cmap: 用于显示图像的颜色映射。
562
- color_box: 边界框的颜色。
563
- color_text: 文本的颜色。
573
+ box_color: 边界框的颜色。
574
+ font_color: 文本的颜色。
564
575
  kwargs: 传递给 EasyOCR readtext 函数的其他参数。
565
576
 
566
577
  # Uage
@@ -632,19 +643,19 @@ def get_text(
632
643
  if score > thr:
633
644
  top_left = tuple(map(int, bbox[0]))
634
645
  bottom_right = tuple(map(int, bbox[2]))
635
- image = cv2.rectangle(image, top_left, bottom_right, color_box, 2)
646
+ image = cv2.rectangle(image, top_left, bottom_right, box_color, 2)
636
647
  # image = cv2.putText(
637
- # image, text, top_left, font, font_scale, color_text, thickness_text
648
+ # image, text, top_left, font, font_scale, font_color, thickness_text
638
649
  # )
639
650
  image = add_text_pil(
640
651
  image,
641
652
  text,
642
653
  top_left,
643
654
  font_size=font_scale * 32,
644
- color=color_text,
655
+ color=font_color,
645
656
  )
646
- img_cmp = cv2.cvtColor(image, cmap)
647
- ax.imshow(img_cmp)
657
+ # img_cmp = cv2.cvtColor(image, cmap)
658
+ ax.imshow(image)
648
659
  ax.axis("off")
649
660
  # plt.show()
650
661
  # 根据输出类型返回相应的结果
@@ -720,20 +731,20 @@ def get_text(
720
731
  map(int, bbox[1])
721
732
  ) # Bottom-left for more accurate placement
722
733
  bottom_right = tuple(map(int, bbox[2]))
723
- image = cv2.rectangle(image, top_left, bottom_right, color_box, 2)
734
+ image = cv2.rectangle(image, top_left, bottom_right, box_color, 2)
724
735
  # image = cv2.putText(
725
- # image, text, top_left, font, font_scale, color_text, thickness_text
736
+ # image, text, top_left, font, font_scale, font_color, thickness_text
726
737
  # )
727
738
  image = add_text_pil(
728
739
  image,
729
740
  text,
730
741
  top_left,
731
742
  font_size=font_scale * 32,
732
- color=color_text,
743
+ color=font_color,
733
744
  bg_color=bg_color,
734
745
  )
735
746
  img_cmp = cv2.cvtColor(image, cmap)
736
- ax.imshow(img_cmp)
747
+ ax.imshow(image)
737
748
  ax.axis("off")
738
749
  # plt.show()
739
750
  # 根据输出类型返回相应的结果
@@ -797,13 +808,13 @@ def get_text(
797
808
  bottom_right = (right, h - bottom)
798
809
 
799
810
  # Draw the bounding box
800
- image = cv2.rectangle(image, top_left, bottom_right, color_box, 2)
811
+ image = cv2.rectangle(image, top_left, bottom_right, box_color, 2)
801
812
  image = add_text_pil(
802
813
  image,
803
814
  char,
804
815
  left,
805
816
  font_size=font_scale * 32,
806
- color=color_text,
817
+ color=font_color,
807
818
  )
808
819
  img_cmp = cv2.cvtColor(image, cmap)
809
820
  ax.imshow(img_cmp)
@@ -838,8 +849,8 @@ def draw_box(
838
849
  detections=None,
839
850
  thr=0.25,
840
851
  cmap=cv2.COLOR_BGR2RGB,
841
- color_box=(0, 255, 0), # draw_box
842
- color_text=(0, 0, 255), # draw_box
852
+ box_color=(0, 255, 0), # draw_box
853
+ font_color=(0, 0, 255), # draw_box
843
854
  font_scale=0.8,
844
855
  show=True,
845
856
  ax=None,
@@ -857,12 +868,12 @@ def draw_box(
857
868
  if score > thr:
858
869
  top_left = tuple(map(int, bbox[0]))
859
870
  bottom_right = tuple(map(int, bbox[2]))
860
- image = cv2.rectangle(image, top_left, bottom_right, color_box, 2)
871
+ image = cv2.rectangle(image, top_left, bottom_right, box_color, 2)
861
872
  # image = cv2.putText(
862
- # image, text, top_left, font, font_scale, color_text, thickness_text
873
+ # image, text, top_left, font, font_scale, font_color, thickness_text
863
874
  # )
864
875
  image = add_text_pil(
865
- image, text, top_left, font_size=font_scale * 32, color=color_text
876
+ image, text, top_left, font_size=font_scale * 32, color=font_color
866
877
  )
867
878
 
868
879
  img_cmp = cv2.cvtColor(image, cmap)
py2ls/plot.py CHANGED
@@ -2391,3 +2391,49 @@ def style_examples(
2391
2391
  dir_save,
2392
2392
  f"{f.name[i]}.pdf",
2393
2393
  )
2394
+
2395
+
2396
+ import matplotlib.pyplot as plt
2397
+ from PIL import Image
2398
+
2399
+
2400
+ def thumbnail(dir_img_list: list, figsize=(10, 10), dpi=100, show=False, usage=False):
2401
+ """
2402
+ Display a thumbnail figure of all images in the specified directory.
2403
+
2404
+ Args:
2405
+ dir_img_list (list): List of image file paths to display.
2406
+ figsize (tuple): Size of the figure (width, height) in inches.
2407
+ dpi (int): Dots per inch for the figure.
2408
+ """
2409
+ if usage:
2410
+ print(
2411
+ 'thumbnail(listdir("./img-innere-medizin-ii", ["jpeg", "jpg", "png"]).fpath.tolist(),figsize=[5,5],dpi=200)'
2412
+ )
2413
+ num_images = len(dir_img_list)
2414
+ if num_images == 0:
2415
+ print("No images found to display.")
2416
+ return None
2417
+
2418
+ # Calculate the number of rows and columns for the grid
2419
+ cols = int(num_images**0.5)
2420
+ rows = (num_images // cols) + (num_images % cols > 0)
2421
+
2422
+ fig, axs = plt.subplots(rows, cols, figsize=figsize, dpi=dpi)
2423
+ axs = axs.flatten() # Flatten the grid for easy iteration
2424
+
2425
+ for ax, image_file in zip(axs, dir_img_list):
2426
+ try:
2427
+ img = Image.open(image_file)
2428
+ ax.imshow(img)
2429
+ ax.axis("off") # Hide axes
2430
+ except (IOError, FileNotFoundError) as e:
2431
+ ax.axis("off") # Still hide axes if image can't be loaded
2432
+
2433
+ # Hide any remaining unused axes
2434
+ for ax in axs[len(dir_img_list) :]:
2435
+ ax.axis("off")
2436
+
2437
+ plt.tight_layout()
2438
+ if show:
2439
+ plt.show()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: py2ls
3
- Version: 0.1.10.23
3
+ Version: 0.1.10.25
4
4
  Summary: py(thon)2(too)ls
5
5
  Author: Jianfeng
6
6
  Author-email: Jianfeng.Liu0413@gmail.com
@@ -172,7 +172,7 @@ py2ls/.gitignore,sha256=y7GvbD_zZkjPVVIue8AyiuFkDMuUbvMaV65Lgu89To8,2763
172
172
  py2ls/LICENSE,sha256=UOZ1F5fFDe3XXvG4oNnkL1-Ecun7zpHzRxjp-XsMeAo,11324
173
173
  py2ls/README.md,sha256=CwvJWAnSXnCnrVHlnEbrxxi6MbjbE_MT6DH2D53S818,11572
174
174
  py2ls/__init__.py,sha256=Nn8jTIvySX7t7DMJ8VNRVctTStgXGjHldOIdZ35PdW8,165
175
- py2ls/batman.py,sha256=U5FcWkq4kgwvFKur0wsqe0jBdq1hQjwpSUKSRzQWeCM,7509
175
+ py2ls/batman.py,sha256=HsZuB_NspZ8CysEY8lLy78zSqd_mg0LOGBofDwQjfKQ,10101
176
176
  py2ls/brain_atlas.py,sha256=w1o5EelRjq89zuFJUNSz4Da8HnTCwAwDAZ4NU4a-bAY,5486
177
177
  py2ls/chat.py,sha256=Yr22GoIvoWhpV3m4fdwV_I0Mn77La346_ymSinR-ORA,3793
178
178
  py2ls/correlators.py,sha256=RbOaJIPLCHJtUm5SFi_4dCJ7VFUPWR0PErfK3K26ad4,18243
@@ -206,15 +206,15 @@ py2ls/doc.py,sha256=xN3g1OWfoaGUhikbJ0NqbN5eKy1VZVvWwRlhHMgyVEc,4243
206
206
  py2ls/export_requirements.py,sha256=x2WgUF0jYKz9GfA1MVKN-MdsM-oQ8yUeC6Ua8oCymio,2325
207
207
  py2ls/freqanalysis.py,sha256=F4218VSPbgL5tnngh6xNCYuNnfR-F_QjECUUxrPYZss,32594
208
208
  py2ls/ich2ls.py,sha256=3E9R8oVpyYZXH5PiIQgT3CN5NxLe4Dwtm2LwaeacE6I,21381
209
- py2ls/ips.py,sha256=c152TEz1X3huntbr_QySp0rqEZVf3TkFZeOHUr1m7H0,141806
210
- py2ls/netfinder.py,sha256=_stenzqRZsB4m5FDE7YsSkC2jvkmrUOsq48BRQB2mPM,55369
211
- py2ls/ocr.py,sha256=T1C589yPF07lJ6EFpGgKq5Dw0vLIZ_-ffH_WZZVIz5o,31026
212
- py2ls/plot.py,sha256=J8hRKLpQXHQRG_xE_nmT0mQvc1IxCMJ21tJmKsUKFl4,96155
209
+ py2ls/ips.py,sha256=yJHpxn0EQIPk0Xhh_tb7VMWkL9Eh9Hw5XzzY7TfhQOE,147612
210
+ py2ls/netfinder.py,sha256=vgOOMhzwbjRuLWMAPyf_kh3HoOhsJ9dlA-tCkMf7kNU,55371
211
+ py2ls/ocr.py,sha256=5lhUbJufIKRSOL6wAWVLEo8TqMYSjoI_Q-IO-_4u3DE,31419
212
+ py2ls/plot.py,sha256=yj-AfnYNr1ha_Y5EimTsUVSooFc36nE0KCQ8cP9_Trs,97601
213
213
  py2ls/setuptools-70.1.0-py3-none-any.whl,sha256=2bi3cUVal8ip86s0SOvgspteEF8SKLukECi-EWmFomc,882588
214
214
  py2ls/sleep_events_detectors.py,sha256=bQA3HJqv5qnYKJJEIhCyhlDtkXQfIzqksnD0YRXso68,52145
215
215
  py2ls/stats.py,sha256=fJmXQ9Lq460StOn-kfEljE97cySq7876HUPTnpB5hLs,38123
216
216
  py2ls/translator.py,sha256=zBeq4pYZeroqw3DT-5g7uHfVqKd-EQptT6LJ-Adi8JY,34244
217
217
  py2ls/wb_detector.py,sha256=7y6TmBUj9exCZeIgBAJ_9hwuhkDh1x_-yg4dvNY1_GQ,6284
218
- py2ls-0.1.10.23.dist-info/METADATA,sha256=VIi88HkNs4dmmv3pLfKQOq0OGT-x0ktBoBmnIn1yVHY,19791
219
- py2ls-0.1.10.23.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
220
- py2ls-0.1.10.23.dist-info/RECORD,,
218
+ py2ls-0.1.10.25.dist-info/METADATA,sha256=LN1qZZRz26ZuwKH1RSRFFpxTR5TeIElPhveOyOaXTHA,19791
219
+ py2ls-0.1.10.25.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
220
+ py2ls-0.1.10.25.dist-info/RECORD,,