zhmiscellany 5.8.0__py3-none-any.whl → 5.8.2__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.
zhmiscellany/macro.py CHANGED
@@ -16,7 +16,7 @@ import time
16
16
  get_mouse_xy = get_mouse_xy
17
17
 
18
18
 
19
- def click_pixel(x=None, y=None, click_duration=None, right_click=False, middle_click=False, shift=False, ctrl=False, act_start=True, act_end=True, click_end_duration=None, double_click=False, animation_time=None, animation_fps=60, animation_easing=True, relative=False, ensure_movement=True, pre_click_duration=None, pre_click_wiggle=True, post_click_duration=None, post_click_wiggle=True, click=True):
19
+ def click_pixel(x=None, y=None, click_duration=None, right_click=False, middle_click=False, shift=False, ctrl=False, act_start=True, act_end=True, click_end_duration=None, double_click=False, animation_time=None, animation_fps=60, animation_easing=True, relative=False, ensure_movement=True, pre_click_duration=None, pre_click_wiggle=True, click=True):
20
20
  if not click:
21
21
  act_start=False;act_end=False
22
22
  if right_click and middle_click:
@@ -169,14 +169,6 @@ def click_pixel(x=None, y=None, click_duration=None, right_click=False, middle_c
169
169
  for key in keys_down:
170
170
  win32api.keybd_event(key, 0, win32con.KEYEVENTF_KEYUP, 0)
171
171
 
172
- if post_click_duration:
173
- if post_click_wiggle:
174
- num_wiggle = round(animation_fps * post_click_duration)
175
- for i in range(num_wiggle):
176
- click_pixel(cx+((random.randint(0, 1)*2)-1), cy+((random.randint(0, 1)*2)-1), act_start=False, act_end=False, click_end_duration=1 / animation_fps)
177
- else:
178
- zhmiscellany.misc.high_precision_sleep(post_click_duration)
179
-
180
172
  if click_end_duration:
181
173
  zhmiscellany.misc.high_precision_sleep(click_end_duration)
182
174
 
@@ -364,6 +356,219 @@ def toggle_function(func, key='f8', blocking=True):
364
356
  return t
365
357
 
366
358
 
359
+ def record_actions_to_code(RECORD_MOUSE_MOVEMENT=False, STOP_KEY='f9'):
360
+ import time
361
+ import pynput
362
+ import pyperclip
363
+
364
+ # --- Configuration ---
365
+ # Set to True to record every single mouse movement.
366
+ # Set to False to only record mouse position on clicks and drags.
367
+
368
+ # --- Global State ---
369
+ events = []
370
+ start_time = None
371
+
372
+ # --- Helper Functions ---
373
+ def format_key(key):
374
+ """
375
+ Formats the pynput key object into a string for the generated script.
376
+ Handles cases where modifier keys (like Ctrl) are held down, which can
377
+ prevent the `key.char` attribute from being populated correctly.
378
+ """
379
+ if isinstance(key, pynput.keyboard.Key):
380
+ # Special keys (e.g., Key.shift, Key.ctrl)
381
+ return f"Key.{key.name}"
382
+
383
+ if isinstance(key, pynput.keyboard.KeyCode):
384
+ # This is the robust way to handle alphanumeric keys, especially with modifiers.
385
+ # key.char can be None or a control character when Ctrl/Alt are held.
386
+ # key.vk is the virtual key code, which is more reliable.
387
+
388
+ # Heuristic for A-Z keys (VK codes on Windows/Linux are often in this range)
389
+ if 65 <= getattr(key, 'vk', 0) <= 90:
390
+ return f"'{chr(key.vk).lower()}'"
391
+
392
+ # For other keys, try to use the char if it exists
393
+ if key.char:
394
+ bs = '\\'
395
+ return f"'{key.char.replace(bs, bs + bs + bs)}'"
396
+
397
+ # As a fallback for other keys without a char, use the vk
398
+ if hasattr(key, 'vk'):
399
+ return f"pynput.keyboard.KeyCode.from_vk({key.vk})"
400
+
401
+ # If it's some other type of key object (less common)
402
+ return str(key)
403
+
404
+ def generate_code():
405
+ """
406
+ Generates the Python script from the recorded events and copies it to the clipboard.
407
+ """
408
+ global events, start_time
409
+
410
+ if not events:
411
+ print("No actions were recorded.")
412
+ return
413
+
414
+ # Code preamble
415
+ code_lines = [
416
+ "import zhmiscellany",
417
+ "",
418
+ "zhmiscellany.misc.die_on_key('f9')",
419
+ "",
420
+ "m = zhmiscellany.macro.click_pixel",
421
+ "k = zhmiscellany.macro.press_key_directinput",
422
+ "s = zhmiscellany.macro.scroll",
423
+ "",
424
+ "click_down_time = 1/30",
425
+ "click_release_time = 1/30",
426
+ "mouse_move_dly = 1/60",
427
+ "key_down_time = 1/30",
428
+ "scroll_dly = 1/30",
429
+ "",
430
+ "pre_click_wiggle = True",
431
+ "",
432
+ "animation_time = 1",
433
+ "",
434
+ 'print("Replaying actions in 3 seconds...")',
435
+ "zhmiscellany.misc.high_precision_sleep(3)",
436
+ ""
437
+ ]
438
+
439
+ last_time = start_time
440
+ for event in events:
441
+ current_time = event['time']
442
+ delay = current_time - last_time
443
+
444
+ action = event['action']
445
+
446
+ if action == 'click':
447
+ x, y, button, pressed = event['x'], event['y'], event['button'], event['pressed']
448
+ button_str = f"Button.{button.name}"
449
+ action_str = "press" if pressed else "release"
450
+
451
+ replacements = {
452
+ 'right': 'right_click=True, ',
453
+ 'middle': 'middle_click=True, ',
454
+ 'left': '',
455
+ }
456
+ for key, value in replacements.items():
457
+ if key in button_str:
458
+ button_str = value
459
+ break
460
+
461
+ replacements = {
462
+ 'press': 'act_end=False, ',
463
+ 'release': 'act_start=False, ',
464
+ }
465
+ for key, value in replacements.items():
466
+ if key in action_str:
467
+ action_str = value
468
+ break
469
+
470
+ code_lines.append(f"m(({x}, {y}), {button_str}{action_str}click_duration=click_down_time, click_end_duration=click_release_time, pre_click_wiggle=pre_click_wiggle, animation_time=animation_time)")
471
+
472
+ elif action == 'move':
473
+ x, y = event['x'], event['y']
474
+ code_lines.append(f"m(({x}, {y}), click_end_duration=mouse_move_dly)")
475
+
476
+ elif action == 'scroll':
477
+ dx, dy = event['dx'], event['dy']
478
+ code_lines.append(f"s({dy}, scroll_dly)")
479
+
480
+ elif action in ('key_press', 'key_release'):
481
+ key = event['key']
482
+ key_str = format_key(key)
483
+ if '.' in key_str:
484
+ key_str = key_str.split('.')[1]
485
+ replacements = {
486
+ 'ctrl': 'ctrl',
487
+ 'shift': 'shift',
488
+ 'alt': 'alt',
489
+ }
490
+ for key, value in replacements.items():
491
+ if key in key_str:
492
+ key_str = value
493
+ break
494
+ action_str = "press" if action == 'key_press' else "release"
495
+
496
+ replacements = {
497
+ 'press': 'act_end=False, ',
498
+ 'release': 'act_start=False, ',
499
+ }
500
+ for key, value in replacements.items():
501
+ if key in action_str:
502
+ action_str = value
503
+ break
504
+
505
+ code_lines.append(f"k('{key_str}', {action_str}key_hold_time=key_down_time)")
506
+
507
+ last_time = current_time
508
+
509
+ code_lines.append("\nprint('Replay finished.')")
510
+
511
+ # Join all lines into a single script
512
+ final_script = "\n".join(code_lines)
513
+
514
+ # Print and copy to clipboard
515
+ print("\n" + "=" * 50)
516
+ print(" RECORDING FINISHED - SCRIPT GENERATED")
517
+ print("=" * 50 + "\n")
518
+ print(final_script)
519
+ print("\n" + "=" * 50)
520
+
521
+ try:
522
+ pyperclip.copy(final_script)
523
+ print("Script has been copied to your clipboard!")
524
+ except pyperclip.PyperclipException:
525
+ print("Could not copy to clipboard. Please install xclip or xsel on Linux.")
526
+ return final_script
527
+
528
+ # --- Pynput Listener Callbacks ---
529
+
530
+ def on_move(x, y):
531
+ if RECORD_MOUSE_MOVEMENT:
532
+ events.append({'action': 'move', 'x': x, 'y': y, 'time': time.time()})
533
+
534
+ def on_click(x, y, button, pressed):
535
+ events.append({'action': 'click', 'x': x, 'y': y, 'button': button, 'pressed': pressed, 'time': time.time()})
536
+
537
+ def on_scroll(x, y, dx, dy):
538
+ # pynput scroll listener reports the mouse position at time of scroll, which we don't need for replay
539
+ events.append({'action': 'scroll', 'dx': dx, 'dy': dy, 'time': time.time()})
540
+
541
+ def on_press(key):
542
+ events.append({'action': 'key_press', 'key': key, 'time': time.time()})
543
+
544
+ def on_release(key):
545
+ if key.name.lower() == STOP_KEY:
546
+ # Stop listeners
547
+ return False
548
+ events.append({'action': 'key_release', 'key': key, 'time': time.time()})
549
+
550
+ print(f"Press '{STOP_KEY}' to stop recording and generate the script.")
551
+ print("...")
552
+
553
+ start_time = time.time()
554
+
555
+ # Create and start listeners
556
+ mouse_listener = pynput.mouse.Listener(on_move=on_move, on_click=on_click, on_scroll=on_scroll)
557
+ keyboard_listener = pynput.keyboard.Listener(on_press=on_press, on_release=on_release)
558
+
559
+ mouse_listener.start()
560
+ keyboard_listener.start()
561
+
562
+ # Wait for the keyboard listener to stop (on F9 press)
563
+ keyboard_listener.join()
564
+
565
+ # Stop the mouse listener explicitly once the keyboard one has finished
566
+ mouse_listener.stop()
567
+
568
+ # Generate the replay script
569
+ return generate_code()
570
+
571
+
367
572
  KEY_CODES = {
368
573
  'None': 0,
369
574
  'LeftMouseButton': 1,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: zhmiscellany
3
- Version: 5.8.0
3
+ Version: 5.8.2
4
4
  Summary: A collection of useful/interesting python libraries made by zh.
5
5
  Home-page: https://discord.gg/ThBBAuueVJ
6
6
  Author: zh
@@ -12,7 +12,7 @@ zhmiscellany/fileio.py,sha256=KmRnWWZJWBc5Or3_mKcRY2ehE_Fuk870iS-imtyERyg,18169
12
12
  zhmiscellany/gui.py,sha256=VgT5j5UIr-wWyL2P1qCHSNL7RrvZWDlm0TX00JGWmKo,7404
13
13
  zhmiscellany/image.py,sha256=qUjxiYpc2VVZp2vwr1vN36O2PVQ7YlMKzhegQ1u4c0M,8198
14
14
  zhmiscellany/list.py,sha256=S8Z85bLJEP9lk2JkGpzUcG6kpRB7a-NWDIHMPiF5bKo,1473
15
- zhmiscellany/macro.py,sha256=yC2coCCHq7lg8bD9QV1ZNeeyhkWI9BNWW0YOsiEWhHA,17331
15
+ zhmiscellany/macro.py,sha256=8Fwp0--03L6MMtgUYTGySTEwlw-DlJo00gKoWIXdREo,24764
16
16
  zhmiscellany/math.py,sha256=btOQTe_GvqP0A7Zz84tmN_c8j1NGe_mKnhmAt40lhLU,2482
17
17
  zhmiscellany/misc.py,sha256=BsTbRWlXI5LZBG7Bl2MgLzHESyCMJnr_KNZAf2wY_H4,29689
18
18
  zhmiscellany/netio.py,sha256=GTamo5cJn4P6u8V_kgZ9kL8qeMUE7OQAmYkmj9Sp_GA,2236
@@ -21,7 +21,7 @@ zhmiscellany/pipes.py,sha256=PxO4aykFzC60xbTQuc66KzZYIxiW0KPebXZbncD2HcU,3795
21
21
  zhmiscellany/processing.py,sha256=srwlV8FZ--svF5e6rZZxhIs_6ZjOAwq2OcQEf6T2Le8,10410
22
22
  zhmiscellany/rust.py,sha256=znN6DYNoa_p-braTuDZKvUnXX8reWLFu_dG4fv2vLR0,442
23
23
  zhmiscellany/string.py,sha256=xyqE6V5YF2nieZDcg5ZrXTIrH2D9oDRbZ5vQGz8rPys,4787
24
- zhmiscellany-5.8.0.dist-info/METADATA,sha256=aFcYkBu7TY9a25dNorkkgbXvYXXjLJrUoGXDW-ieJ70,42153
25
- zhmiscellany-5.8.0.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
26
- zhmiscellany-5.8.0.dist-info/top_level.txt,sha256=ioDtsrevCI52rTxZntMPljRIBsZs73tD0hI00HektiE,13
27
- zhmiscellany-5.8.0.dist-info/RECORD,,
24
+ zhmiscellany-5.8.2.dist-info/METADATA,sha256=qCEULsjGKmFPsHfnyve1eay_nnitpjk7DFxOwZEg2yQ,42153
25
+ zhmiscellany-5.8.2.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
26
+ zhmiscellany-5.8.2.dist-info/top_level.txt,sha256=ioDtsrevCI52rTxZntMPljRIBsZs73tD0hI00HektiE,13
27
+ zhmiscellany-5.8.2.dist-info/RECORD,,