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.
Files changed (19) hide show
  1. {keyboardpaster-0.1.17 → keyboardpaster-0.1.18}/KeyboardPaster.egg-info/PKG-INFO +4 -1
  2. {keyboardpaster-0.1.17 → keyboardpaster-0.1.18}/PKG-INFO +4 -1
  3. {keyboardpaster-0.1.17 → keyboardpaster-0.1.18}/README.md +3 -0
  4. {keyboardpaster-0.1.17 → keyboardpaster-0.1.18}/keyboardpaster/app.py +178 -33
  5. {keyboardpaster-0.1.17 → keyboardpaster-0.1.18}/keyboardpaster/keyboardpaster_app.kv +42 -9
  6. keyboardpaster-0.1.18/keyboardpaster/shared_resources.py +1 -0
  7. keyboardpaster-0.1.17/keyboardpaster/shared_resources.py +0 -1
  8. {keyboardpaster-0.1.17 → keyboardpaster-0.1.18}/KeyboardPaster.egg-info/SOURCES.txt +0 -0
  9. {keyboardpaster-0.1.17 → keyboardpaster-0.1.18}/KeyboardPaster.egg-info/dependency_links.txt +0 -0
  10. {keyboardpaster-0.1.17 → keyboardpaster-0.1.18}/KeyboardPaster.egg-info/entry_points.txt +0 -0
  11. {keyboardpaster-0.1.17 → keyboardpaster-0.1.18}/KeyboardPaster.egg-info/requires.txt +0 -0
  12. {keyboardpaster-0.1.17 → keyboardpaster-0.1.18}/KeyboardPaster.egg-info/top_level.txt +0 -0
  13. {keyboardpaster-0.1.17 → keyboardpaster-0.1.18}/LICENSE +0 -0
  14. {keyboardpaster-0.1.17 → keyboardpaster-0.1.18}/keyboardpaster/__init__.py +0 -0
  15. {keyboardpaster-0.1.17 → keyboardpaster-0.1.18}/keyboardpaster/keyboard_layout_detector.py +0 -0
  16. {keyboardpaster-0.1.17 → keyboardpaster-0.1.18}/keyboardpaster/modules/__init__.py +0 -0
  17. {keyboardpaster-0.1.17 → keyboardpaster-0.1.18}/keyboardpaster/modules/autoupdate.py +0 -0
  18. {keyboardpaster-0.1.17 → keyboardpaster-0.1.18}/setup.cfg +0 -0
  19. {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.17
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.17
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 load_inputs(self, dt):
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
- with open("saved_inputs.json", "r") as file:
148
- saved_inputs = json.load(file)
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
- input_field_buttons = sum([x.children for x in self.root.ids['input_fields_container'].children], [])
151
- input_fields = [x for x in input_field_buttons if isinstance(x, TextInput)]
152
- checkboxes = [x for x in input_field_buttons if isinstance(x, MDCheckbox) and getattr(x, 'secret_checkbox', False)]
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
- for name, (text, secret_state) in saved_inputs.items():
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
- # Set state for corresponding checkbox and adjust text visibility
162
- for checkbox in checkboxes:
163
- if checkbox.parent.text_input_id == name:
164
- checkbox.active = secret_state
165
- # Assuming MDTextField is a sibling or accessible as input_field here
166
- # and setting its "password" property based on checkbox state
167
- if secret_state:
168
- input_field.password = True # Hide text if checkbox is checked
169
- else:
170
- input_field.password = False # Show text otherwise
171
- break # Found the matching checkbox, no need to continue the loop
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
- # Save the dictionary to a JSON file.
206
- with open("saved_inputs.json", "w") as file:
207
- json.dump(input_fields, file)
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
- MDLabel:
45
- id: title_bar
46
- text: "Keyboard Paster"
47
- halign: "center"
48
- theme_text_color: "Custom"
49
- text_color: 1, 1, 1, 1
50
- font_size: '30sp'
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