abstract-windows 0.0.1__tar.gz

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.
@@ -0,0 +1,71 @@
1
+ Metadata-Version: 2.4
2
+ Name: abstract_windows
3
+ Version: 0.0.1
4
+ Home-page: https://github.com/AbstractEndeavors/abstract_windows
5
+ Author: putkoff
6
+ Author-email: partners@abstractendeavors.com
7
+ Classifier: Development Status :: 3 - Alpha
8
+ Classifier: Intended Audience :: Developers
9
+ Classifier: Topic :: Software Development :: Libraries
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.6
13
+ Classifier: Programming Language :: Python :: 3.7
14
+ Classifier: Programming Language :: Python :: 3.8
15
+ Classifier: Programming Language :: Python :: 3.9
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Requires-Python: >=3.6
20
+ Description-Content-Type: text/markdown
21
+ Requires-Dist: abstract_utilities
22
+ Requires-Dist: abstract_webserver
23
+ Dynamic: author
24
+ Dynamic: author-email
25
+ Dynamic: classifier
26
+ Dynamic: description
27
+ Dynamic: description-content-type
28
+ Dynamic: home-page
29
+ Dynamic: requires-dist
30
+ Dynamic: requires-python
31
+
32
+ # Unknown Package (vUnknown Version)
33
+
34
+ No description available
35
+
36
+ ## Installation
37
+
38
+ ```bash
39
+ pip install Unknown Package
40
+ ```
41
+
42
+ ## Dependencies
43
+
44
+ None
45
+
46
+ ## Modules
47
+
48
+ ### src/abstract_react/replace_utils.py
49
+
50
+ Description of script based on prompt: You are analyzing a Python script 'replace_utils.p (mock response)
51
+
52
+ ### src/abstract_react/__init__.py
53
+
54
+ Description of script based on prompt: You are analyzing a Python script '__init__.py' lo (mock response)
55
+
56
+ ### src/abstract_react/import_utils.py
57
+
58
+ Description of script based on prompt: You are analyzing a Python script 'import_utils.py (mock response)
59
+
60
+ ### src/__init__.py
61
+
62
+ Description of script based on prompt: You are analyzing a Python script '__init__.py' lo (mock response)
63
+
64
+ ### src/abstract_react/utils.py
65
+
66
+ Description of script based on prompt: You are analyzing a Python script 'utils.py' locat (mock response)
67
+
68
+ ### src/abstract_react/abstract_react.py
69
+
70
+ Description of script based on prompt: You are analyzing a Python script 'abstract_react. (mock response)
71
+
@@ -0,0 +1,40 @@
1
+ # Unknown Package (vUnknown Version)
2
+
3
+ No description available
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install Unknown Package
9
+ ```
10
+
11
+ ## Dependencies
12
+
13
+ None
14
+
15
+ ## Modules
16
+
17
+ ### src/abstract_react/replace_utils.py
18
+
19
+ Description of script based on prompt: You are analyzing a Python script 'replace_utils.p (mock response)
20
+
21
+ ### src/abstract_react/__init__.py
22
+
23
+ Description of script based on prompt: You are analyzing a Python script '__init__.py' lo (mock response)
24
+
25
+ ### src/abstract_react/import_utils.py
26
+
27
+ Description of script based on prompt: You are analyzing a Python script 'import_utils.py (mock response)
28
+
29
+ ### src/__init__.py
30
+
31
+ Description of script based on prompt: You are analyzing a Python script '__init__.py' lo (mock response)
32
+
33
+ ### src/abstract_react/utils.py
34
+
35
+ Description of script based on prompt: You are analyzing a Python script 'utils.py' locat (mock response)
36
+
37
+ ### src/abstract_react/abstract_react.py
38
+
39
+ Description of script based on prompt: You are analyzing a Python script 'abstract_react. (mock response)
40
+
@@ -0,0 +1,3 @@
1
+ [build-system]
2
+ requires = ["setuptools>=42", "wheel"]
3
+ build-backend = "setuptools.build_meta"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,34 @@
1
+ from time import time
2
+ import setuptools
3
+ with open("README.md", "r", encoding="utf-8") as fh:
4
+ long_description = fh.read()
5
+ setuptools.setup(
6
+ name='abstract_windows',
7
+ version='0.0.1',
8
+ author='putkoff',
9
+ author_email='partners@abstractendeavors.com',
10
+ description="",
11
+ long_description=long_description,
12
+ long_description_content_type='text/markdown',
13
+ url='https://github.com/AbstractEndeavors/abstract_windows',
14
+ classifiers=[
15
+ 'Development Status :: 3 - Alpha',
16
+ 'Intended Audience :: Developers',
17
+ 'Topic :: Software Development :: Libraries',
18
+ 'License :: OSI Approved :: MIT License',
19
+ 'Programming Language :: Python :: 3',
20
+ 'Programming Language :: Python :: 3.6',
21
+ 'Programming Language :: Python :: 3.7',
22
+ 'Programming Language :: Python :: 3.8',
23
+ 'Programming Language :: Python :: 3.9',
24
+ 'Programming Language :: Python :: 3.10',
25
+ 'Programming Language :: Python :: 3.11',
26
+ 'Programming Language :: Python :: 3.12',
27
+ ],
28
+ install_requires=['abstract_utilities','abstract_webserver'],
29
+ package_dir={"": "src"},
30
+ packages=setuptools.find_packages(where="src"),
31
+ python_requires=">=3.6",
32
+ # Add this line to include wheel format in your distribution
33
+ setup_requires=['wheel'],
34
+ )
@@ -0,0 +1,3 @@
1
+ from .browser_utils import *
2
+ from .window_utils import *
3
+ from .string_comp import *
@@ -0,0 +1 @@
1
+ from .browser_utils import *
@@ -0,0 +1,66 @@
1
+ import subprocess
2
+ import time
3
+ from typing import List
4
+ from ..string_comp import *
5
+ from ..window_utils import *
6
+ # tweak if your browser uses Page_Down instead of Tab
7
+ NEXT_TAB_KEY = "ctrl+Tab"
8
+ # how many times to press before giving up
9
+ MAX_TABS = 50
10
+ # time for browser to update title
11
+ DELAY = 0.1
12
+ # Browser title signatures
13
+ BROWSER_PATTERNS = [
14
+ 'Mozilla', 'Firefox', 'Chrome', 'Chromium',
15
+ 'Microsoft Edge', 'Opera', 'Safari'
16
+ ]
17
+ def is_browser(win):
18
+ if get_strings_in_string(win.get('window_title'),BROWSER_PATTERNS):
19
+ return True
20
+ return False
21
+
22
+ def get_tab_titles(win):
23
+ win_id = win["window_id"]
24
+ tab_titles = cycle_tabs(win_id)
25
+ win['tab_titles'] = tab_titles
26
+ return win
27
+
28
+ def get_browsers(parsed_windows=None,active_windows=None):
29
+ parsed_windows = parsed_windows or get_parsed_windows()
30
+ browsers = [get_tab_titles(parsed_window) for parsed_window in parsed_windows if is_browser(parsed_window)]
31
+ return browsers
32
+
33
+ def activate_window(win_id: str):
34
+ subprocess.run(["xdotool", "windowactivate", "--sync", win_id], check=True)
35
+
36
+ def get_window_title(win_id: str) -> str:
37
+ res = subprocess.run(
38
+ ["xdotool", "getwindowname", win_id],
39
+ capture_output=True, text=True, check=True
40
+ )
41
+ return res.stdout.strip()
42
+
43
+ def switch_tab(win_id: str):
44
+ # sends the next‐tab key only to that window
45
+ subprocess.run(
46
+ ["xdotool", "key", "--window", win_id, NEXT_TAB_KEY],
47
+ check=True
48
+ )
49
+
50
+ def cycle_tabs(win_id: str,window_name=None) -> List[str]:
51
+ """Returns the ordered list of tab-titles in the given window."""
52
+ activate_window(win_id)
53
+ first_title = get_window_title(win_id)
54
+ titles = [first_title]
55
+
56
+ for _ in range(MAX_TABS):
57
+ switch_tab(win_id)
58
+ time.sleep(DELAY)
59
+ title = get_window_title(win_id)
60
+ if window_name and title == window_name:
61
+ return
62
+ if title == first_title:
63
+ break
64
+ titles.append(title)
65
+
66
+ return titles
@@ -0,0 +1 @@
1
+ from .string_compare import *
@@ -0,0 +1,103 @@
1
+ from abstract_utilities import make_list
2
+ def get_strings_in_string(string,strings):
3
+ for comp_string in strings:
4
+ if comp_string.lower() in string.lower():
5
+ return True
6
+ return False
7
+ def find_longest_common_substring(string, comp_string):
8
+ # Find the longest common substring
9
+ min_len = min(len(string), len(comp_string))
10
+ for length in range(min_len, 0, -1):
11
+ for i in range(len(string) - length + 1):
12
+ substring = string[i:i + length]
13
+ if substring in comp_string:
14
+ return substring
15
+ return None
16
+
17
+ def get_string_confidence(string,string_remains):
18
+ string_len = len(string)
19
+ remains_str = ''.join(string_remains)
20
+ remains_len = len(remains_str)
21
+ remains_perc = 0
22
+ confidence = 1
23
+ if remains_len:
24
+ remains_perc = remains_len/string_len
25
+ confidence = 1 - remains_perc
26
+ return confidence
27
+ def remove_substring(string, substring):
28
+ # Remove the first occurrence of substring and return remaining parts
29
+ idx = string.find(substring)
30
+ if idx == -1:
31
+ return [string]
32
+ remains = [string[:idx], string[idx + len(substring):]]
33
+ return [r for r in remains if r] # Remove empty strings
34
+
35
+ def compare_strings(string, comp_string):
36
+ results = []
37
+
38
+ # Initialize with the original strings
39
+ current_strings = [(string, comp_string)]
40
+
41
+ while current_strings:
42
+ new_strings = []
43
+ for s, cs in current_strings:
44
+ # Find the longest common substring
45
+ common = find_longest_common_substring(s, cs)
46
+ if not common:
47
+ continue # No common substring, move to next pair
48
+
49
+ # Record the found substring and remaining parts
50
+ string_remains = remove_substring(s, common)
51
+ comp_string_remains = remove_substring(cs, common)
52
+
53
+ # Store the result
54
+ results.append({
55
+ "found": common,
56
+ "string": {"remains": string_remains},
57
+ "comp_string": {"remains": comp_string_remains}
58
+ })
59
+
60
+ # Add remaining parts for further processing
61
+ for sr in string_remains:
62
+ for csr in comp_string_remains:
63
+ new_strings.append((sr, csr))
64
+
65
+ current_strings = new_strings
66
+ string_remains = results[-1].get('string',{}).get('remains')
67
+ string_confidence = get_string_confidence(string,string_remains)
68
+ comp_string_remains = results[-1].get('comp_string',{}).get('remains')
69
+ comp_string_confidence = get_string_confidence(comp_string,comp_string_remains)
70
+ results = {"string":string,
71
+ "comp_string":comp_string,
72
+ "results":results,
73
+ "found":[result['found'] for result in results if result],
74
+ "confidence":{
75
+ "string":string_confidence,
76
+ "comp_string":comp_string_confidence
77
+ }
78
+ }
79
+ return results
80
+
81
+ def return_best_comp_strings(string, comp_strings):
82
+ results = []
83
+ comp_strings = make_list(comp_strings)
84
+ for comp_string in comp_strings:
85
+ result = compare_strings(string, comp_string)
86
+ results.append(result)
87
+
88
+ confidence_results = None
89
+ for i, result in enumerate(results):
90
+ confidence = result.get('confidence', {})
91
+ string_confidence = confidence.get('string', 0)
92
+ comp_string_confidence = confidence.get('comp_string', 0)
93
+ if confidence_results is None:
94
+ #if string_confidence>.70:
95
+ confidence_results = [string_confidence, comp_string_confidence, i]
96
+ elif string_confidence > confidence_results[0]:
97
+ confidence_results = [string_confidence, comp_string_confidence, i]
98
+ elif string_confidence == confidence_results[0]:
99
+ if comp_string_confidence > confidence_results[1]:
100
+ confidence_results = [string_confidence, comp_string_confidence, i]
101
+
102
+ return comp_strings[confidence_results[2]]
103
+
@@ -0,0 +1 @@
1
+ from .window_utils import *
@@ -0,0 +1,183 @@
1
+ from typing import List, Dict, Optional, Union, Callable
2
+ import threading
3
+ import re
4
+ import subprocess
5
+ from Xlib import X, display
6
+ from Xlib.ext import randr
7
+ from difflib import get_close_matches
8
+ XRANDR_CMD = ["xrandr"] # or ["xrandr", "--listmonitors"] if you prefer
9
+ WMCTRL_LIST_CMD = ['wmctrl', '-l', '-p']
10
+ WMCTRL_MOVE_CMD = ['wmctrl', '-i', '-r']
11
+ XDOTOOL_GEOMETRY_CMD = ['xdotool', 'getwindowgeometry']
12
+
13
+ # Regex patterns
14
+
15
+ MONITOR_REGEX = re.compile(
16
+ r"""^
17
+ (?P<name>\S+)\s+ # e.g. "DisplayPort-1-2"
18
+ connected # literal word
19
+ (?:\s+primary)? # optional " primary"
20
+ \s+
21
+ (?P<width>\d+)x(?P<height>\d+) # resolution, e.g. "2560x1440"
22
+ \+(?P<x>\d+)\+(?P<y>\d+) # offsets, e.g. "+0+0"
23
+ """,
24
+ re.VERBOSE
25
+ )
26
+
27
+ POSITION_REGEX = re.compile(r'Position:\s+(\d+),(\d+)')
28
+ GEOMETRY_REGEX = re.compile(r'Geometry:\s+(\d+)x(\d+)')
29
+
30
+
31
+
32
+
33
+
34
+
35
+ def get_monitors():
36
+ monitors = []
37
+ try:
38
+ result = subprocess.run(
39
+ XRANDR_CMD, capture_output=True, text=True, check=True
40
+ )
41
+ for line in result.stdout.splitlines():
42
+ m = MONITOR_REGEX.search(line)
43
+ if not m:
44
+ continue
45
+ gd = m.groupdict()
46
+ monitors.append({
47
+ "name": gd["name"],
48
+ "x": int(gd["x"]),
49
+ "y": int(gd["y"]),
50
+ "width": int(gd["width"]),
51
+ "height": int(gd["height"]),
52
+ })
53
+ except subprocess.SubprocessError as e:
54
+ print(f"Error running xrandr: {e}")
55
+ return monitors
56
+
57
+
58
+
59
+ def get_strings_in_string(string,strings):
60
+ for comp_string in strings:
61
+ if comp_string.lower() in string.lower():
62
+ return True
63
+ return False
64
+ def get_windows_list():
65
+ result = subprocess.run(
66
+ WMCTRL_LIST_CMD, capture_output=True, text=True, check=True
67
+ )
68
+ windows = result.stdout.splitlines()
69
+ return windows
70
+ def get_filters(
71
+ windows: List[str],
72
+ find_window_id: Optional[str] = None,
73
+ find_desktop: Optional[str] = None,
74
+ find_pid: Optional[str] = None,
75
+ find_host: Optional[str] = None,
76
+ find_window_title: Optional[str] = None
77
+ ):
78
+ filters = {k: v for k, v in {
79
+ 'window_id': find_window_id,
80
+ 'desktop': find_desktop,
81
+ 'pid': find_pid,
82
+ 'host': find_host,
83
+ 'window_title': find_window_title
84
+ }.items() if v is not None}
85
+ return filters
86
+
87
+
88
+ def get_monitor_for_window(
89
+ window_id: Optional[str] = None,
90
+ x: Optional[int] = None,
91
+ y: Optional[int] = None
92
+ ) -> Dict[str, Union[str, int]]:
93
+ """Determine which monitor a given window (or coordinate) is on."""
94
+
95
+ if window_id and x is None and y is None:
96
+ geom = get_window_geometry(window_id = window_id)
97
+ if geom is None:
98
+ return {}
99
+ x, y = geom['x'], geom['y']
100
+
101
+ if x is None or y is None:
102
+ return {}
103
+ for mon in get_monitors():
104
+ if mon['x'] <= x < mon['x'] + mon['width'] and \
105
+ mon['y'] <= y < mon['y'] + mon['height']:
106
+ return {
107
+ 'monitor_name': mon['name'],
108
+ 'monitor_details': f"{mon['width']}x{mon['height']}+{mon['x']}+{mon['y']}",
109
+ 'win_x': x,
110
+ 'win_y': y
111
+ }
112
+ return {}
113
+ def parse_window(window):
114
+ parts = window.split(None, 4)
115
+ if len(parts) >= 5:
116
+ win_id, desktop, pid, host, title = parts
117
+ monitor_info = get_monitor_for_window(window_id=win_id)
118
+ window_geometry = get_window_geometry(window_id=win_id)
119
+ info = {
120
+ 'window_id': win_id, 'desktop': desktop, 'pid': pid,
121
+ 'host': host, 'window_title': title,'monitor_info':monitor_info,
122
+ 'window_geometry':window_geometry
123
+ }
124
+
125
+ info.update(monitor_info)
126
+ return info
127
+ def filter_window(info,filters={}):
128
+ if filters and all(parsed_window[k] == v for k, v in filters.items()):
129
+ return info
130
+ def parse_windows(windows,filters={}):
131
+ parsed_windows = []
132
+ for window in windows:
133
+ parsed_window = parse_window(window)
134
+ filtered_window = filter_window(parsed_window,filters=filters)
135
+ if filtered_window:
136
+ return filtered_window
137
+ if parsed_window:
138
+ parsed_windows.append(parsed_window)
139
+ return parsed_windows
140
+ def get_window_items(
141
+ windows: List[str],
142
+ find_window_id: Optional[str] = None,
143
+ find_desktop: Optional[str] = None,
144
+ find_pid: Optional[str] = None,
145
+ find_host: Optional[str] = None,
146
+ find_window_title: Optional[str] = None
147
+ ) -> Union[Dict[str, str], List[Dict[str, str]]]:
148
+ """Parse `wmctrl -l -p` output, optionally filtering."""
149
+ filters = get_filters(
150
+ windows=windows,
151
+ find_window_id= find_window_id,
152
+ find_desktop= find_desktop,
153
+ find_pid= find_pid,
154
+ find_host= find_host,
155
+ find_window_title= find_window_title
156
+ )
157
+ parsed = parse_windows(windows,filters)
158
+ return parsed
159
+ def get_window_geometry(window_id: str) -> Optional[Dict[str, int]]:
160
+ """Get window position (x, y) and size (width, height) using xdotool."""
161
+ try:
162
+ result = subprocess.run(
163
+ XDOTOOL_GEOMETRY_CMD + [window_id],
164
+ capture_output=True, text=True, check=True
165
+ )
166
+ pos_m = POSITION_REGEX.search(result.stdout)
167
+ size_m = GEOMETRY_REGEX.search(result.stdout)
168
+ if pos_m and size_m:
169
+ return {
170
+ 'x': int(pos_m.group(1)),
171
+ 'y': int(pos_m.group(2)),
172
+ 'width': int(size_m.group(1)),
173
+ 'height': int(size_m.group(2))
174
+ }
175
+ return None
176
+ except subprocess.SubprocessError as e:
177
+ print(f"Error getting geometry: {e}")
178
+ return None
179
+ def get_parsed_windows():
180
+ windows = get_windows_list()
181
+ filters = get_filters(windows=windows)
182
+ parsed_windows = parse_windows(windows=windows,filters=filters)
183
+ return parsed_windows
@@ -0,0 +1,71 @@
1
+ Metadata-Version: 2.4
2
+ Name: abstract_windows
3
+ Version: 0.0.1
4
+ Home-page: https://github.com/AbstractEndeavors/abstract_windows
5
+ Author: putkoff
6
+ Author-email: partners@abstractendeavors.com
7
+ Classifier: Development Status :: 3 - Alpha
8
+ Classifier: Intended Audience :: Developers
9
+ Classifier: Topic :: Software Development :: Libraries
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.6
13
+ Classifier: Programming Language :: Python :: 3.7
14
+ Classifier: Programming Language :: Python :: 3.8
15
+ Classifier: Programming Language :: Python :: 3.9
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Requires-Python: >=3.6
20
+ Description-Content-Type: text/markdown
21
+ Requires-Dist: abstract_utilities
22
+ Requires-Dist: abstract_webserver
23
+ Dynamic: author
24
+ Dynamic: author-email
25
+ Dynamic: classifier
26
+ Dynamic: description
27
+ Dynamic: description-content-type
28
+ Dynamic: home-page
29
+ Dynamic: requires-dist
30
+ Dynamic: requires-python
31
+
32
+ # Unknown Package (vUnknown Version)
33
+
34
+ No description available
35
+
36
+ ## Installation
37
+
38
+ ```bash
39
+ pip install Unknown Package
40
+ ```
41
+
42
+ ## Dependencies
43
+
44
+ None
45
+
46
+ ## Modules
47
+
48
+ ### src/abstract_react/replace_utils.py
49
+
50
+ Description of script based on prompt: You are analyzing a Python script 'replace_utils.p (mock response)
51
+
52
+ ### src/abstract_react/__init__.py
53
+
54
+ Description of script based on prompt: You are analyzing a Python script '__init__.py' lo (mock response)
55
+
56
+ ### src/abstract_react/import_utils.py
57
+
58
+ Description of script based on prompt: You are analyzing a Python script 'import_utils.py (mock response)
59
+
60
+ ### src/__init__.py
61
+
62
+ Description of script based on prompt: You are analyzing a Python script '__init__.py' lo (mock response)
63
+
64
+ ### src/abstract_react/utils.py
65
+
66
+ Description of script based on prompt: You are analyzing a Python script 'utils.py' locat (mock response)
67
+
68
+ ### src/abstract_react/abstract_react.py
69
+
70
+ Description of script based on prompt: You are analyzing a Python script 'abstract_react. (mock response)
71
+
@@ -0,0 +1,15 @@
1
+ README.md
2
+ pyproject.toml
3
+ setup.py
4
+ src/abstract_windows/__init__.py
5
+ src/abstract_windows.egg-info/PKG-INFO
6
+ src/abstract_windows.egg-info/SOURCES.txt
7
+ src/abstract_windows.egg-info/dependency_links.txt
8
+ src/abstract_windows.egg-info/requires.txt
9
+ src/abstract_windows.egg-info/top_level.txt
10
+ src/abstract_windows/browser_utils/__init__.py
11
+ src/abstract_windows/browser_utils/browser_utils.py
12
+ src/abstract_windows/string_comp/__init__.py
13
+ src/abstract_windows/string_comp/string_compare.py
14
+ src/abstract_windows/window_utils/__init__.py
15
+ src/abstract_windows/window_utils/window_utils.py
@@ -0,0 +1,2 @@
1
+ abstract_utilities
2
+ abstract_webserver
@@ -0,0 +1 @@
1
+ abstract_windows