KeyboardPaster 0.1.17__tar.gz → 0.1.18__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.
- {keyboardpaster-0.1.17 → keyboardpaster-0.1.18}/KeyboardPaster.egg-info/PKG-INFO +4 -1
- {keyboardpaster-0.1.17 → keyboardpaster-0.1.18}/PKG-INFO +4 -1
- {keyboardpaster-0.1.17 → keyboardpaster-0.1.18}/README.md +3 -0
- {keyboardpaster-0.1.17 → keyboardpaster-0.1.18}/keyboardpaster/app.py +178 -33
- {keyboardpaster-0.1.17 → keyboardpaster-0.1.18}/keyboardpaster/keyboardpaster_app.kv +42 -9
- keyboardpaster-0.1.18/keyboardpaster/shared_resources.py +1 -0
- keyboardpaster-0.1.17/keyboardpaster/shared_resources.py +0 -1
- {keyboardpaster-0.1.17 → keyboardpaster-0.1.18}/KeyboardPaster.egg-info/SOURCES.txt +0 -0
- {keyboardpaster-0.1.17 → keyboardpaster-0.1.18}/KeyboardPaster.egg-info/dependency_links.txt +0 -0
- {keyboardpaster-0.1.17 → keyboardpaster-0.1.18}/KeyboardPaster.egg-info/entry_points.txt +0 -0
- {keyboardpaster-0.1.17 → keyboardpaster-0.1.18}/KeyboardPaster.egg-info/requires.txt +0 -0
- {keyboardpaster-0.1.17 → keyboardpaster-0.1.18}/KeyboardPaster.egg-info/top_level.txt +0 -0
- {keyboardpaster-0.1.17 → keyboardpaster-0.1.18}/LICENSE +0 -0
- {keyboardpaster-0.1.17 → keyboardpaster-0.1.18}/keyboardpaster/__init__.py +0 -0
- {keyboardpaster-0.1.17 → keyboardpaster-0.1.18}/keyboardpaster/keyboard_layout_detector.py +0 -0
- {keyboardpaster-0.1.17 → keyboardpaster-0.1.18}/keyboardpaster/modules/__init__.py +0 -0
- {keyboardpaster-0.1.17 → keyboardpaster-0.1.18}/keyboardpaster/modules/autoupdate.py +0 -0
- {keyboardpaster-0.1.17 → keyboardpaster-0.1.18}/setup.cfg +0 -0
- {keyboardpaster-0.1.17 → keyboardpaster-0.1.18}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: KeyboardPaster
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.18
|
|
4
4
|
Summary: A Python package to type text using the keyboard module
|
|
5
5
|
Home-page: https://github.com/KnutssonDevelopment/KeyboardPaster
|
|
6
6
|
Author: Brian Knutsson
|
|
@@ -39,6 +39,9 @@ A Python package to type text using the keyboard module. This is useful in situa
|
|
|
39
39
|
- Paste images as base64 code
|
|
40
40
|
|
|
41
41
|
## Changes
|
|
42
|
+
### v0.1.18
|
|
43
|
+
- FEATURE: Added a possibility to have multiple profiles.
|
|
44
|
+
|
|
42
45
|
### v0.1.17
|
|
43
46
|
- FEATURE: Added a button to put text into clipboard
|
|
44
47
|
- UPDATE: Changed the keyboard paste option into an icon
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: KeyboardPaster
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.18
|
|
4
4
|
Summary: A Python package to type text using the keyboard module
|
|
5
5
|
Home-page: https://github.com/KnutssonDevelopment/KeyboardPaster
|
|
6
6
|
Author: Brian Knutsson
|
|
@@ -39,6 +39,9 @@ A Python package to type text using the keyboard module. This is useful in situa
|
|
|
39
39
|
- Paste images as base64 code
|
|
40
40
|
|
|
41
41
|
## Changes
|
|
42
|
+
### v0.1.18
|
|
43
|
+
- FEATURE: Added a possibility to have multiple profiles.
|
|
44
|
+
|
|
42
45
|
### v0.1.17
|
|
43
46
|
- FEATURE: Added a button to put text into clipboard
|
|
44
47
|
- UPDATE: Changed the keyboard paste option into an icon
|
|
@@ -10,6 +10,9 @@ A Python package to type text using the keyboard module. This is useful in situa
|
|
|
10
10
|
- Paste images as base64 code
|
|
11
11
|
|
|
12
12
|
## Changes
|
|
13
|
+
### v0.1.18
|
|
14
|
+
- FEATURE: Added a possibility to have multiple profiles.
|
|
15
|
+
|
|
13
16
|
### v0.1.17
|
|
14
17
|
- FEATURE: Added a button to put text into clipboard
|
|
15
18
|
- UPDATE: Changed the keyboard paste option into an icon
|
|
@@ -3,6 +3,7 @@ import re
|
|
|
3
3
|
import json
|
|
4
4
|
import pkg_resources
|
|
5
5
|
import subprocess
|
|
6
|
+
|
|
6
7
|
from pynput.keyboard import Controller, Key
|
|
7
8
|
|
|
8
9
|
from kivymd.app import MDApp
|
|
@@ -12,6 +13,9 @@ from kivy.lang import Builder
|
|
|
12
13
|
from kivy.clock import Clock
|
|
13
14
|
from kivy.core.window import Window
|
|
14
15
|
from kivymd.uix.selectioncontrol.selectioncontrol import MDCheckbox
|
|
16
|
+
from kivymd.uix.button.button import MDRaisedButton
|
|
17
|
+
from kivymd.uix.dialog import MDDialog
|
|
18
|
+
from kivymd.uix.textfield import MDTextField
|
|
15
19
|
|
|
16
20
|
from keyboardpaster.keyboard_layout_detector import get_keyboard_layout
|
|
17
21
|
from keyboardpaster.modules.autoupdate import autoupdate
|
|
@@ -125,13 +129,21 @@ class KeyboardPasterApp(MDApp):
|
|
|
125
129
|
start_delay = ObjectProperty(None)
|
|
126
130
|
layout = 'EN_US'
|
|
127
131
|
|
|
132
|
+
def __init__(self):
|
|
133
|
+
super().__init__()
|
|
134
|
+
self.config = None
|
|
135
|
+
self.json_config = 'saved_inputs.json'
|
|
136
|
+
|
|
128
137
|
def build(self):
|
|
129
138
|
self.theme_cls.primary_palette = "DeepPurple" # Change to your desired primary color
|
|
130
139
|
self.theme_cls.accent_palette = "Amber" # Change to your desired accent color
|
|
131
140
|
self.theme_cls.theme_style = "Light" # Set the theme to either "Light" or "Dark"
|
|
132
141
|
|
|
142
|
+
self.read_config()
|
|
143
|
+
|
|
133
144
|
self.detect_keyboard_layout()
|
|
134
|
-
Clock.schedule_once(self.load_inputs, 1)
|
|
145
|
+
#Clock.schedule_once(self.load_inputs, 1)
|
|
146
|
+
Clock.schedule_once(self.set_profile, 1)
|
|
135
147
|
self.title = f"Keyboard Paster v{app_version}"
|
|
136
148
|
Window.size = (1000, 700)
|
|
137
149
|
|
|
@@ -139,49 +151,182 @@ class KeyboardPasterApp(MDApp):
|
|
|
139
151
|
return Builder.load_file(kv_file_path)
|
|
140
152
|
|
|
141
153
|
def on_stop(self):
|
|
142
|
-
# self.save_inputs()
|
|
143
154
|
pass
|
|
144
155
|
|
|
145
|
-
def
|
|
156
|
+
def read_config(self):
|
|
157
|
+
try:
|
|
158
|
+
with open(self.json_config, "r", encoding='utf-8') as file:
|
|
159
|
+
self.config = json.load(file)
|
|
160
|
+
except FileNotFoundError:
|
|
161
|
+
pass
|
|
162
|
+
except AttributeError:
|
|
163
|
+
pass
|
|
164
|
+
except json.JSONDecodeError:
|
|
165
|
+
pass
|
|
166
|
+
except ValueError:
|
|
167
|
+
pass
|
|
168
|
+
except KeyError:
|
|
169
|
+
pass
|
|
170
|
+
|
|
171
|
+
if 'profiles' in self.config:
|
|
172
|
+
pass
|
|
173
|
+
elif 'input_text_0' in self.config:
|
|
174
|
+
values = self.config
|
|
175
|
+
self.config = dict()
|
|
176
|
+
self.config['profiles'] = dict()
|
|
177
|
+
self.config['profiles']['DEFAULT'] = values
|
|
178
|
+
else:
|
|
179
|
+
self.config = dict()
|
|
180
|
+
self.config['profiles'] = dict()
|
|
181
|
+
self.config['profiles']['DEFAULT'] = dict()
|
|
182
|
+
|
|
183
|
+
def write_config(self):
|
|
184
|
+
with open(self.json_config, "w", encoding='utf-8') as file:
|
|
185
|
+
json.dump(self.config, file)
|
|
186
|
+
|
|
187
|
+
@staticmethod
|
|
188
|
+
def close_dialog(button_instance):
|
|
189
|
+
# Navigate up the widget tree until we find an MDDialog instance
|
|
190
|
+
current_widget = button_instance
|
|
191
|
+
while current_widget and not isinstance(current_widget, MDDialog):
|
|
192
|
+
current_widget = current_widget.parent
|
|
193
|
+
|
|
194
|
+
# If we found an MDDialog instance, dismiss it
|
|
195
|
+
if isinstance(current_widget, MDDialog):
|
|
196
|
+
current_widget.dismiss()
|
|
197
|
+
|
|
198
|
+
def add_profile(self, profile_name, button_instance):
|
|
199
|
+
# Access the text from the input box
|
|
200
|
+
print(f"New profile name: {profile_name}")
|
|
201
|
+
self.config['profiles'][profile_name] = dict()
|
|
202
|
+
self.set_profile(None, profile_name)
|
|
203
|
+
|
|
204
|
+
# Close the dialog using the button instance
|
|
205
|
+
self.close_dialog(button_instance)
|
|
206
|
+
|
|
207
|
+
def create_new_profile(self):
|
|
208
|
+
self.close_dropdown()
|
|
209
|
+
|
|
210
|
+
profile_input = MDTextField(
|
|
211
|
+
hint_text="Enter profile name",
|
|
212
|
+
size_hint_x=0.8,
|
|
213
|
+
pos_hint={"center_x": 0.5}
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
dialog = MDDialog(
|
|
217
|
+
title="New Profile",
|
|
218
|
+
type="custom",
|
|
219
|
+
content_cls=profile_input,
|
|
220
|
+
buttons=[
|
|
221
|
+
MDRaisedButton(
|
|
222
|
+
text="CANCEL",
|
|
223
|
+
on_release=self.close_dialog
|
|
224
|
+
),
|
|
225
|
+
MDRaisedButton(
|
|
226
|
+
text="OK",
|
|
227
|
+
on_release=lambda x: self.add_profile(profile_input.text, x)
|
|
228
|
+
)
|
|
229
|
+
],
|
|
230
|
+
)
|
|
231
|
+
dialog.open()
|
|
232
|
+
|
|
233
|
+
def close_dropdown(self):
|
|
234
|
+
profile_dropdown = self.root.ids['profile_dropdown']
|
|
235
|
+
profile_dropdown.dismiss()
|
|
236
|
+
|
|
237
|
+
def handle_dropdown(self, profile):
|
|
238
|
+
self.set_profile(None, profile)
|
|
239
|
+
self.close_dropdown()
|
|
240
|
+
|
|
241
|
+
def generate_profile_selector(self):
|
|
242
|
+
profile_dropdown = self.root.ids['profile_dropdown']
|
|
243
|
+
profile_selector = self.root.ids['profile_selector']
|
|
146
244
|
try:
|
|
147
|
-
|
|
148
|
-
|
|
245
|
+
profiles = list(self.config['profiles'].keys())
|
|
246
|
+
except KeyError:
|
|
247
|
+
profiles = ['DEFAULT']
|
|
248
|
+
max_length = 7
|
|
249
|
+
|
|
250
|
+
# Removing existing widgets
|
|
251
|
+
for child in profile_dropdown.children:
|
|
252
|
+
for widget in reversed(child.children):
|
|
253
|
+
child.remove_widget(widget)
|
|
254
|
+
|
|
255
|
+
btn = MDRaisedButton()
|
|
256
|
+
btn.text = '- New -'
|
|
257
|
+
btn.size_hint_y = None
|
|
258
|
+
btn.bind(on_release=lambda x: self.create_new_profile())
|
|
259
|
+
profile_dropdown.add_widget(btn)
|
|
260
|
+
|
|
261
|
+
for profile in profiles:
|
|
262
|
+
if len(profile) > max_length:
|
|
263
|
+
max_length = len(profile)
|
|
264
|
+
|
|
265
|
+
if profile != profile_selector.text.strip():
|
|
266
|
+
btn = MDRaisedButton()
|
|
267
|
+
btn.text = profile
|
|
268
|
+
btn.size_hint_y = None
|
|
269
|
+
btn.bind(on_release=lambda x, p=profile: self.handle_dropdown(p))
|
|
270
|
+
profile_dropdown.add_widget(btn)
|
|
271
|
+
|
|
272
|
+
number_of_whitespaces = max_length - len(profile_selector.text.strip())
|
|
273
|
+
profile_selector.text = profile_selector.text + (number_of_whitespaces + 1) * " "
|
|
274
|
+
|
|
275
|
+
def set_profile(self, dt=None, profile=None):
|
|
276
|
+
profile_selector = self.root.ids['profile_selector']
|
|
277
|
+
profile_name = "DEFAULT"
|
|
278
|
+
saved_inputs = list()
|
|
279
|
+
|
|
280
|
+
if profile is None:
|
|
281
|
+
profile_name = list(self.config['profiles'].keys())[0]
|
|
282
|
+
else:
|
|
283
|
+
profile_name = profile
|
|
284
|
+
|
|
285
|
+
profile_selector.text = profile_name
|
|
286
|
+
self.load_inputs()
|
|
287
|
+
self.generate_profile_selector()
|
|
149
288
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
289
|
+
def load_inputs(self):
|
|
290
|
+
profile_selector = self.root.ids['profile_selector']
|
|
291
|
+
profile_name = profile_selector.text.strip()
|
|
292
|
+
|
|
293
|
+
if profile_name in self.config['profiles']:
|
|
294
|
+
profile_values = self.config['profiles'][profile_name]
|
|
295
|
+
else:
|
|
296
|
+
profile_values = dict()
|
|
153
297
|
|
|
154
|
-
|
|
298
|
+
input_field_buttons = sum([x.children for x in self.root.ids['input_fields_container'].children], [])
|
|
299
|
+
input_fields = [x for x in input_field_buttons if isinstance(x, TextInput)]
|
|
300
|
+
checkboxes = [x for x in input_field_buttons if isinstance(x, MDCheckbox) and getattr(x, 'secret_checkbox', False)]
|
|
301
|
+
|
|
302
|
+
if len(profile_values.items()) > 1:
|
|
303
|
+
for name, (text, secret_state) in profile_values.items():
|
|
155
304
|
# Set text for TextInput
|
|
156
305
|
for input_field in input_fields:
|
|
157
306
|
if input_field.parent.text_input_id == name:
|
|
158
307
|
input_field.text = text
|
|
159
308
|
break # Found the matching input field, no need to continue the loop
|
|
160
309
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
310
|
+
# Set state for corresponding checkbox and adjust text visibility
|
|
311
|
+
for checkbox in checkboxes:
|
|
312
|
+
if checkbox.parent.text_input_id == name:
|
|
313
|
+
checkbox.active = secret_state
|
|
314
|
+
# Assuming MDTextField is a sibling or accessible as input_field here
|
|
315
|
+
# and setting its "password" property based on checkbox state
|
|
316
|
+
if secret_state:
|
|
317
|
+
input_field.password = True # Hide text if checkbox is checked
|
|
318
|
+
else:
|
|
319
|
+
input_field.password = False # Show text otherwise
|
|
320
|
+
break # Found the matching checkbox, no need to continue the loop
|
|
321
|
+
else:
|
|
322
|
+
for input_field in input_fields:
|
|
323
|
+
input_field.text = text = ""
|
|
172
324
|
|
|
173
|
-
except FileNotFoundError:
|
|
174
|
-
pass
|
|
175
|
-
except AttributeError:
|
|
176
|
-
pass
|
|
177
|
-
except json.JSONDecodeError:
|
|
178
|
-
# Handle cases where the JSON file is empty or corrupted
|
|
179
|
-
pass
|
|
180
|
-
except ValueError:
|
|
181
|
-
# Handle cases where the JSON file is empty or corrupted
|
|
182
|
-
pass
|
|
183
325
|
|
|
184
326
|
def save_inputs(self):
|
|
327
|
+
profile_selector = self.root.ids['profile_selector']
|
|
328
|
+
profile_name = profile_selector.text.strip()
|
|
329
|
+
|
|
185
330
|
# Assuming `input_field_buttons` contains all relevant child widgets,
|
|
186
331
|
# including both TextInput and MDCheckbox widgets.
|
|
187
332
|
input_field_buttons = sum([x.children for x in self.root.ids['input_fields_container'].children], [])
|
|
@@ -202,9 +347,9 @@ class KeyboardPasterApp(MDApp):
|
|
|
202
347
|
# Store the tuple of text and secret_state in the dictionary.
|
|
203
348
|
input_fields[child.parent.text_input_id] = (child.text, secret_state)
|
|
204
349
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
350
|
+
|
|
351
|
+
self.config['profiles'][profile_name] = input_fields
|
|
352
|
+
self.write_config()
|
|
208
353
|
|
|
209
354
|
def type_text(self, input_text, _checkbox):
|
|
210
355
|
if not input_text:
|
|
@@ -36,18 +36,50 @@
|
|
|
36
36
|
icon: "content-copy"
|
|
37
37
|
on_press: app.copy_text(_text_input.text, _cb_execute)
|
|
38
38
|
|
|
39
|
+
<ProfileButton@MDFlatButton>:
|
|
40
|
+
text: "DEFAULT"
|
|
41
|
+
size_hint_y: None
|
|
42
|
+
height: 44
|
|
43
|
+
on_release:
|
|
44
|
+
root.select(self.text)
|
|
45
|
+
|
|
46
|
+
<CustomDropDown@DropDown>:
|
|
39
47
|
|
|
40
48
|
MDBoxLayout:
|
|
41
49
|
orientation: "vertical"
|
|
42
50
|
|
|
43
51
|
MDTopAppBar:
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
52
|
+
MDBoxLayout:
|
|
53
|
+
orientation: "horizontal"
|
|
54
|
+
|
|
55
|
+
MDLabel:
|
|
56
|
+
id: title_bar
|
|
57
|
+
text: "Keyboard Paster"
|
|
58
|
+
halign: "center"
|
|
59
|
+
theme_text_color: "Custom"
|
|
60
|
+
text_color: 1, 1, 1, 1
|
|
61
|
+
font_size: '30sp'
|
|
62
|
+
|
|
63
|
+
MDBoxLayout:
|
|
64
|
+
orientation: "horizontal"
|
|
65
|
+
size_hint_x: .3
|
|
66
|
+
|
|
67
|
+
MDLabel:
|
|
68
|
+
text: "Profile:"
|
|
69
|
+
halign: 'right'
|
|
70
|
+
theme_text_color: "Custom"
|
|
71
|
+
text_color: 1, 1, 1, 1
|
|
72
|
+
font_size: '22sp'
|
|
73
|
+
|
|
74
|
+
MDFlatButton:
|
|
75
|
+
id: profile_selector
|
|
76
|
+
text: "DEFAULT"
|
|
77
|
+
pos_hint: {'center_y': 0.5}
|
|
78
|
+
theme_text_color: "Custom"
|
|
79
|
+
text_color: 1, 1, 1, 1
|
|
80
|
+
font_size: '22sp'
|
|
81
|
+
on_parent: profile_dropdown.dismiss()
|
|
82
|
+
on_release: profile_dropdown.open(self)
|
|
51
83
|
|
|
52
84
|
MDBoxLayout:
|
|
53
85
|
orientation: "vertical"
|
|
@@ -133,8 +165,9 @@ MDBoxLayout:
|
|
|
133
165
|
text: str(round(mod_delay.value, 1)) + " seconds"
|
|
134
166
|
theme_text_color: "Secondary"
|
|
135
167
|
|
|
136
|
-
|
|
137
|
-
|
|
138
168
|
MDRaisedButton:
|
|
139
169
|
text: "Save Inputs"
|
|
140
170
|
on_release: app.save_inputs()
|
|
171
|
+
|
|
172
|
+
CustomDropDown:
|
|
173
|
+
id: profile_dropdown
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
app_version = "0.1.18"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
app_version = "0.1.17"
|
|
File without changes
|
{keyboardpaster-0.1.17 → keyboardpaster-0.1.18}/KeyboardPaster.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|