pyloid 0.20.2__py3-none-any.whl → 0.20.2.dev0__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.
- pyloid/__init__.py +7 -7
- pyloid/api.py +104 -104
- pyloid/autostart.py +101 -101
- pyloid/browser_window.py +1956 -1915
- pyloid/builder/__init__.py +80 -80
- pyloid/builder/build_config.schema.json +72 -72
- pyloid/builder/spec.py +246 -246
- pyloid/custom/titlebar.py +116 -116
- pyloid/filewatcher.py +163 -163
- pyloid/js_api/event_api.py +24 -24
- pyloid/js_api/window_api.py +255 -255
- pyloid/monitor.py +921 -921
- pyloid/pyloid.py +1404 -1404
- pyloid/thread_pool.py +500 -500
- pyloid/timer.py +307 -307
- pyloid/tray.py +48 -48
- pyloid/utils.py +122 -122
- {pyloid-0.20.2.dist-info → pyloid-0.20.2.dev0.dist-info}/LICENSE +201 -201
- {pyloid-0.20.2.dist-info → pyloid-0.20.2.dev0.dist-info}/METADATA +1 -1
- pyloid-0.20.2.dev0.dist-info/RECORD +21 -0
- pyloid-0.20.2.dist-info/RECORD +0 -21
- {pyloid-0.20.2.dist-info → pyloid-0.20.2.dev0.dist-info}/WHEEL +0 -0
pyloid/browser_window.py
CHANGED
@@ -1,1915 +1,1956 @@
|
|
1
|
-
import sys
|
2
|
-
import os
|
3
|
-
from PySide6.QtWidgets import (
|
4
|
-
QMainWindow,
|
5
|
-
)
|
6
|
-
from PySide6.QtWebEngineWidgets import QWebEngineView
|
7
|
-
from PySide6.QtWebChannel import QWebChannel
|
8
|
-
from PySide6.QtGui import (
|
9
|
-
QKeySequence,
|
10
|
-
QShortcut,
|
11
|
-
QCursor,
|
12
|
-
)
|
13
|
-
from PySide6.QtCore import Qt, QPoint, QUrl, QEvent, QFile
|
14
|
-
from PySide6.QtWebEngineCore import
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
from
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
from .
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
from
|
28
|
-
from
|
29
|
-
from PySide6.
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
def
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
self.
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
if self.resize_direction:
|
137
|
-
self.
|
138
|
-
self.
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
if
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
if
|
186
|
-
self.
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
if
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
self.
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
self.
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
self
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
)
|
328
|
-
|
329
|
-
|
330
|
-
)
|
331
|
-
|
332
|
-
|
333
|
-
)
|
334
|
-
|
335
|
-
|
336
|
-
)
|
337
|
-
|
338
|
-
|
339
|
-
)
|
340
|
-
|
341
|
-
|
342
|
-
)
|
343
|
-
|
344
|
-
|
345
|
-
)
|
346
|
-
|
347
|
-
|
348
|
-
)
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
self.
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
self.web_view.
|
385
|
-
|
386
|
-
|
387
|
-
self.web_view.
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
def
|
538
|
-
"""
|
539
|
-
Loads HTML
|
540
|
-
|
541
|
-
Parameters
|
542
|
-
----------
|
543
|
-
|
544
|
-
The HTML
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
>>>
|
551
|
-
>>> window
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
self.
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
>>>
|
595
|
-
>>> window
|
596
|
-
|
597
|
-
|
598
|
-
self.
|
599
|
-
self.
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
The
|
612
|
-
|
613
|
-
Examples
|
614
|
-
--------
|
615
|
-
>>> app = Pyloid(app_name="Pyloid-App")
|
616
|
-
>>> window = app.create_window("pyloid-window")
|
617
|
-
>>> window.
|
618
|
-
"""
|
619
|
-
self.
|
620
|
-
self.
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
>>> window.
|
637
|
-
>>> window.
|
638
|
-
"""
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
|
666
|
-
|
667
|
-
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
|
672
|
-
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
|
688
|
-
|
689
|
-
""
|
690
|
-
|
691
|
-
|
692
|
-
|
693
|
-
|
694
|
-
|
695
|
-
|
696
|
-
|
697
|
-
|
698
|
-
|
699
|
-
|
700
|
-
|
701
|
-
|
702
|
-
|
703
|
-
|
704
|
-
|
705
|
-
|
706
|
-
|
707
|
-
|
708
|
-
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
|
718
|
-
|
719
|
-
|
720
|
-
|
721
|
-
|
722
|
-
|
723
|
-
|
724
|
-
|
725
|
-
|
726
|
-
|
727
|
-
|
728
|
-
|
729
|
-
|
730
|
-
|
731
|
-
|
732
|
-
|
733
|
-
|
734
|
-
|
735
|
-
|
736
|
-
|
737
|
-
|
738
|
-
|
739
|
-
|
740
|
-
|
741
|
-
|
742
|
-
|
743
|
-
|
744
|
-
|
745
|
-
|
746
|
-
|
747
|
-
|
748
|
-
|
749
|
-
Examples
|
750
|
-
--------
|
751
|
-
>>> app = Pyloid(app_name="Pyloid-App")
|
752
|
-
>>> window = app.create_window("pyloid-window")
|
753
|
-
>>> window.
|
754
|
-
|
755
|
-
|
756
|
-
self.
|
757
|
-
|
758
|
-
|
759
|
-
|
760
|
-
|
761
|
-
|
762
|
-
|
763
|
-
|
764
|
-
|
765
|
-
|
766
|
-
|
767
|
-
|
768
|
-
|
769
|
-
|
770
|
-
|
771
|
-
|
772
|
-
|
773
|
-
|
774
|
-
|
775
|
-
|
776
|
-
|
777
|
-
|
778
|
-
|
779
|
-
|
780
|
-
|
781
|
-
|
782
|
-
|
783
|
-
|
784
|
-
self.
|
785
|
-
|
786
|
-
|
787
|
-
|
788
|
-
|
789
|
-
|
790
|
-
|
791
|
-
|
792
|
-
""
|
793
|
-
|
794
|
-
|
795
|
-
|
796
|
-
|
797
|
-
|
798
|
-
|
799
|
-
|
800
|
-
|
801
|
-
self.
|
802
|
-
|
803
|
-
|
804
|
-
|
805
|
-
|
806
|
-
|
807
|
-
|
808
|
-
|
809
|
-
|
810
|
-
|
811
|
-
|
812
|
-
""
|
813
|
-
|
814
|
-
|
815
|
-
|
816
|
-
|
817
|
-
|
818
|
-
|
819
|
-
|
820
|
-
|
821
|
-
|
822
|
-
|
823
|
-
|
824
|
-
|
825
|
-
|
826
|
-
self.
|
827
|
-
|
828
|
-
|
829
|
-
|
830
|
-
|
831
|
-
|
832
|
-
|
833
|
-
|
834
|
-
|
835
|
-
|
836
|
-
|
837
|
-
|
838
|
-
>>>
|
839
|
-
>>> window.
|
840
|
-
|
841
|
-
|
842
|
-
self._window.
|
843
|
-
|
844
|
-
|
845
|
-
|
846
|
-
|
847
|
-
|
848
|
-
|
849
|
-
|
850
|
-
|
851
|
-
|
852
|
-
|
853
|
-
|
854
|
-
|
855
|
-
|
856
|
-
|
857
|
-
"""
|
858
|
-
|
859
|
-
|
860
|
-
|
861
|
-
|
862
|
-
|
863
|
-
|
864
|
-
|
865
|
-
|
866
|
-
|
867
|
-
|
868
|
-
|
869
|
-
|
870
|
-
|
871
|
-
|
872
|
-
def
|
873
|
-
"""
|
874
|
-
|
875
|
-
|
876
|
-
Examples
|
877
|
-
--------
|
878
|
-
>>> app = Pyloid(app_name="Pyloid-App")
|
879
|
-
>>> window = app.create_window("pyloid-window")
|
880
|
-
>>> window.
|
881
|
-
"""
|
882
|
-
|
883
|
-
|
884
|
-
|
885
|
-
|
886
|
-
|
887
|
-
|
888
|
-
|
889
|
-
|
890
|
-
|
891
|
-
|
892
|
-
|
893
|
-
|
894
|
-
|
895
|
-
>>>
|
896
|
-
""
|
897
|
-
|
898
|
-
|
899
|
-
|
900
|
-
|
901
|
-
|
902
|
-
|
903
|
-
|
904
|
-
|
905
|
-
|
906
|
-
|
907
|
-
>>>
|
908
|
-
""
|
909
|
-
|
910
|
-
|
911
|
-
|
912
|
-
|
913
|
-
|
914
|
-
|
915
|
-
|
916
|
-
|
917
|
-
|
918
|
-
|
919
|
-
>>>
|
920
|
-
""
|
921
|
-
|
922
|
-
|
923
|
-
|
924
|
-
|
925
|
-
|
926
|
-
|
927
|
-
|
928
|
-
|
929
|
-
|
930
|
-
|
931
|
-
|
932
|
-
|
933
|
-
|
934
|
-
|
935
|
-
|
936
|
-
|
937
|
-
|
938
|
-
|
939
|
-
|
940
|
-
|
941
|
-
|
942
|
-
|
943
|
-
|
944
|
-
|
945
|
-
|
946
|
-
>>>
|
947
|
-
""
|
948
|
-
|
949
|
-
|
950
|
-
|
951
|
-
|
952
|
-
|
953
|
-
|
954
|
-
|
955
|
-
|
956
|
-
|
957
|
-
|
958
|
-
>>>
|
959
|
-
""
|
960
|
-
|
961
|
-
|
962
|
-
|
963
|
-
|
964
|
-
|
965
|
-
|
966
|
-
|
967
|
-
|
968
|
-
|
969
|
-
|
970
|
-
|
971
|
-
|
972
|
-
|
973
|
-
|
974
|
-
|
975
|
-
|
976
|
-
|
977
|
-
|
978
|
-
|
979
|
-
|
980
|
-
|
981
|
-
|
982
|
-
|
983
|
-
|
984
|
-
|
985
|
-
|
986
|
-
|
987
|
-
|
988
|
-
|
989
|
-
|
990
|
-
|
991
|
-
|
992
|
-
|
993
|
-
|
994
|
-
|
995
|
-
|
996
|
-
|
997
|
-
|
998
|
-
|
999
|
-
|
1000
|
-
|
1001
|
-
|
1002
|
-
|
1003
|
-
|
1004
|
-
|
1005
|
-
|
1006
|
-
|
1007
|
-
|
1008
|
-
|
1009
|
-
|
1010
|
-
|
1011
|
-
|
1012
|
-
|
1013
|
-
|
1014
|
-
|
1015
|
-
|
1016
|
-
|
1017
|
-
|
1018
|
-
|
1019
|
-
|
1020
|
-
|
1021
|
-
|
1022
|
-
|
1023
|
-
|
1024
|
-
|
1025
|
-
|
1026
|
-
|
1027
|
-
|
1028
|
-
|
1029
|
-
#
|
1030
|
-
|
1031
|
-
|
1032
|
-
|
1033
|
-
|
1034
|
-
|
1035
|
-
|
1036
|
-
|
1037
|
-
|
1038
|
-
|
1039
|
-
|
1040
|
-
|
1041
|
-
|
1042
|
-
|
1043
|
-
|
1044
|
-
|
1045
|
-
|
1046
|
-
|
1047
|
-
|
1048
|
-
|
1049
|
-
|
1050
|
-
|
1051
|
-
|
1052
|
-
|
1053
|
-
|
1054
|
-
|
1055
|
-
|
1056
|
-
|
1057
|
-
|
1058
|
-
|
1059
|
-
|
1060
|
-
|
1061
|
-
|
1062
|
-
|
1063
|
-
|
1064
|
-
|
1065
|
-
|
1066
|
-
|
1067
|
-
|
1068
|
-
|
1069
|
-
|
1070
|
-
|
1071
|
-
|
1072
|
-
|
1073
|
-
|
1074
|
-
|
1075
|
-
|
1076
|
-
|
1077
|
-
|
1078
|
-
|
1079
|
-
|
1080
|
-
|
1081
|
-
|
1082
|
-
|
1083
|
-
|
1084
|
-
|
1085
|
-
|
1086
|
-
|
1087
|
-
|
1088
|
-
|
1089
|
-
|
1090
|
-
|
1091
|
-
|
1092
|
-
|
1093
|
-
|
1094
|
-
|
1095
|
-
|
1096
|
-
|
1097
|
-
|
1098
|
-
|
1099
|
-
|
1100
|
-
|
1101
|
-
|
1102
|
-
|
1103
|
-
|
1104
|
-
|
1105
|
-
|
1106
|
-
|
1107
|
-
|
1108
|
-
|
1109
|
-
|
1110
|
-
|
1111
|
-
|
1112
|
-
|
1113
|
-
|
1114
|
-
|
1115
|
-
|
1116
|
-
|
1117
|
-
|
1118
|
-
|
1119
|
-
|
1120
|
-
|
1121
|
-
|
1122
|
-
|
1123
|
-
|
1124
|
-
|
1125
|
-
|
1126
|
-
|
1127
|
-
|
1128
|
-
###########################################################################################
|
1129
|
-
|
1130
|
-
|
1131
|
-
|
1132
|
-
|
1133
|
-
|
1134
|
-
|
1135
|
-
|
1136
|
-
|
1137
|
-
dict
|
1138
|
-
|
1139
|
-
|
1140
|
-
Examples
|
1141
|
-
--------
|
1142
|
-
|
1143
|
-
|
1144
|
-
|
1145
|
-
|
1146
|
-
|
1147
|
-
|
1148
|
-
|
1149
|
-
app.run()
|
1150
|
-
```
|
1151
|
-
|
1152
|
-
|
1153
|
-
|
1154
|
-
|
1155
|
-
|
1156
|
-
|
1157
|
-
|
1158
|
-
|
1159
|
-
|
1160
|
-
|
1161
|
-
|
1162
|
-
|
1163
|
-
|
1164
|
-
|
1165
|
-
|
1166
|
-
|
1167
|
-
|
1168
|
-
|
1169
|
-
|
1170
|
-
|
1171
|
-
|
1172
|
-
|
1173
|
-
|
1174
|
-
|
1175
|
-
|
1176
|
-
|
1177
|
-
|
1178
|
-
|
1179
|
-
|
1180
|
-
|
1181
|
-
|
1182
|
-
|
1183
|
-
```
|
1184
|
-
""
|
1185
|
-
|
1186
|
-
|
1187
|
-
|
1188
|
-
|
1189
|
-
|
1190
|
-
|
1191
|
-
|
1192
|
-
|
1193
|
-
|
1194
|
-
|
1195
|
-
|
1196
|
-
|
1197
|
-
|
1198
|
-
|
1199
|
-
|
1200
|
-
|
1201
|
-
|
1202
|
-
|
1203
|
-
|
1204
|
-
|
1205
|
-
|
1206
|
-
|
1207
|
-
|
1208
|
-
|
1209
|
-
|
1210
|
-
|
1211
|
-
|
1212
|
-
|
1213
|
-
|
1214
|
-
|
1215
|
-
|
1216
|
-
|
1217
|
-
|
1218
|
-
|
1219
|
-
|
1220
|
-
|
1221
|
-
|
1222
|
-
|
1223
|
-
|
1224
|
-
|
1225
|
-
|
1226
|
-
|
1227
|
-
|
1228
|
-
|
1229
|
-
|
1230
|
-
|
1231
|
-
|
1232
|
-
|
1233
|
-
|
1234
|
-
|
1235
|
-
|
1236
|
-
|
1237
|
-
|
1238
|
-
|
1239
|
-
|
1240
|
-
|
1241
|
-
|
1242
|
-
|
1243
|
-
|
1244
|
-
|
1245
|
-
|
1246
|
-
|
1247
|
-
|
1248
|
-
|
1249
|
-
|
1250
|
-
|
1251
|
-
|
1252
|
-
|
1253
|
-
|
1254
|
-
|
1255
|
-
|
1256
|
-
|
1257
|
-
|
1258
|
-
|
1259
|
-
|
1260
|
-
|
1261
|
-
|
1262
|
-
|
1263
|
-
|
1264
|
-
|
1265
|
-
|
1266
|
-
|
1267
|
-
|
1268
|
-
|
1269
|
-
|
1270
|
-
|
1271
|
-
|
1272
|
-
|
1273
|
-
|
1274
|
-
|
1275
|
-
|
1276
|
-
|
1277
|
-
|
1278
|
-
|
1279
|
-
|
1280
|
-
|
1281
|
-
|
1282
|
-
|
1283
|
-
|
1284
|
-
|
1285
|
-
|
1286
|
-
|
1287
|
-
|
1288
|
-
|
1289
|
-
|
1290
|
-
|
1291
|
-
|
1292
|
-
|
1293
|
-
|
1294
|
-
|
1295
|
-
|
1296
|
-
|
1297
|
-
|
1298
|
-
|
1299
|
-
|
1300
|
-
|
1301
|
-
|
1302
|
-
|
1303
|
-
|
1304
|
-
|
1305
|
-
|
1306
|
-
|
1307
|
-
|
1308
|
-
|
1309
|
-
|
1310
|
-
|
1311
|
-
|
1312
|
-
|
1313
|
-
|
1314
|
-
|
1315
|
-
|
1316
|
-
|
1317
|
-
|
1318
|
-
|
1319
|
-
|
1320
|
-
|
1321
|
-
|
1322
|
-
|
1323
|
-
|
1324
|
-
|
1325
|
-
|
1326
|
-
|
1327
|
-
|
1328
|
-
|
1329
|
-
|
1330
|
-
|
1331
|
-
|
1332
|
-
|
1333
|
-
|
1334
|
-
|
1335
|
-
|
1336
|
-
|
1337
|
-
|
1338
|
-
|
1339
|
-
```
|
1340
|
-
|
1341
|
-
|
1342
|
-
|
1343
|
-
|
1344
|
-
|
1345
|
-
|
1346
|
-
|
1347
|
-
|
1348
|
-
|
1349
|
-
|
1350
|
-
|
1351
|
-
|
1352
|
-
|
1353
|
-
|
1354
|
-
|
1355
|
-
|
1356
|
-
|
1357
|
-
|
1358
|
-
|
1359
|
-
|
1360
|
-
|
1361
|
-
|
1362
|
-
|
1363
|
-
"""
|
1364
|
-
|
1365
|
-
|
1366
|
-
|
1367
|
-
|
1368
|
-
|
1369
|
-
|
1370
|
-
|
1371
|
-
|
1372
|
-
|
1373
|
-
|
1374
|
-
|
1375
|
-
|
1376
|
-
|
1377
|
-
|
1378
|
-
|
1379
|
-
|
1380
|
-
|
1381
|
-
app
|
1382
|
-
|
1383
|
-
""
|
1384
|
-
|
1385
|
-
|
1386
|
-
|
1387
|
-
|
1388
|
-
|
1389
|
-
|
1390
|
-
|
1391
|
-
|
1392
|
-
|
1393
|
-
|
1394
|
-
|
1395
|
-
|
1396
|
-
|
1397
|
-
|
1398
|
-
|
1399
|
-
|
1400
|
-
|
1401
|
-
|
1402
|
-
|
1403
|
-
|
1404
|
-
|
1405
|
-
|
1406
|
-
|
1407
|
-
|
1408
|
-
|
1409
|
-
|
1410
|
-
|
1411
|
-
|
1412
|
-
|
1413
|
-
|
1414
|
-
|
1415
|
-
|
1416
|
-
|
1417
|
-
|
1418
|
-
|
1419
|
-
|
1420
|
-
|
1421
|
-
|
1422
|
-
app
|
1423
|
-
|
1424
|
-
|
1425
|
-
|
1426
|
-
|
1427
|
-
|
1428
|
-
|
1429
|
-
|
1430
|
-
|
1431
|
-
|
1432
|
-
|
1433
|
-
|
1434
|
-
|
1435
|
-
|
1436
|
-
|
1437
|
-
|
1438
|
-
|
1439
|
-
|
1440
|
-
|
1441
|
-
|
1442
|
-
|
1443
|
-
|
1444
|
-
|
1445
|
-
|
1446
|
-
|
1447
|
-
```
|
1448
|
-
|
1449
|
-
|
1450
|
-
|
1451
|
-
|
1452
|
-
|
1453
|
-
|
1454
|
-
|
1455
|
-
|
1456
|
-
|
1457
|
-
|
1458
|
-
|
1459
|
-
|
1460
|
-
|
1461
|
-
|
1462
|
-
|
1463
|
-
""
|
1464
|
-
|
1465
|
-
|
1466
|
-
|
1467
|
-
|
1468
|
-
|
1469
|
-
|
1470
|
-
|
1471
|
-
|
1472
|
-
|
1473
|
-
|
1474
|
-
|
1475
|
-
|
1476
|
-
|
1477
|
-
|
1478
|
-
|
1479
|
-
|
1480
|
-
|
1481
|
-
|
1482
|
-
|
1483
|
-
|
1484
|
-
|
1485
|
-
|
1486
|
-
|
1487
|
-
|
1488
|
-
|
1489
|
-
""
|
1490
|
-
|
1491
|
-
|
1492
|
-
|
1493
|
-
|
1494
|
-
|
1495
|
-
|
1496
|
-
|
1497
|
-
|
1498
|
-
|
1499
|
-
|
1500
|
-
|
1501
|
-
|
1502
|
-
|
1503
|
-
|
1504
|
-
|
1505
|
-
|
1506
|
-
|
1507
|
-
|
1508
|
-
|
1509
|
-
|
1510
|
-
|
1511
|
-
|
1512
|
-
|
1513
|
-
|
1514
|
-
|
1515
|
-
|
1516
|
-
|
1517
|
-
|
1518
|
-
|
1519
|
-
|
1520
|
-
|
1521
|
-
|
1522
|
-
|
1523
|
-
|
1524
|
-
|
1525
|
-
|
1526
|
-
|
1527
|
-
|
1528
|
-
|
1529
|
-
|
1530
|
-
|
1531
|
-
|
1532
|
-
|
1533
|
-
|
1534
|
-
|
1535
|
-
|
1536
|
-
|
1537
|
-
|
1538
|
-
|
1539
|
-
|
1540
|
-
|
1541
|
-
|
1542
|
-
|
1543
|
-
|
1544
|
-
|
1545
|
-
|
1546
|
-
|
1547
|
-
|
1548
|
-
|
1549
|
-
|
1550
|
-
|
1551
|
-
|
1552
|
-
```
|
1553
|
-
"""
|
1554
|
-
|
1555
|
-
|
1556
|
-
|
1557
|
-
|
1558
|
-
|
1559
|
-
|
1560
|
-
|
1561
|
-
|
1562
|
-
|
1563
|
-
|
1564
|
-
|
1565
|
-
|
1566
|
-
|
1567
|
-
|
1568
|
-
|
1569
|
-
|
1570
|
-
|
1571
|
-
|
1572
|
-
|
1573
|
-
|
1574
|
-
|
1575
|
-
|
1576
|
-
|
1577
|
-
|
1578
|
-
|
1579
|
-
|
1580
|
-
|
1581
|
-
window.
|
1582
|
-
|
1583
|
-
|
1584
|
-
|
1585
|
-
|
1586
|
-
|
1587
|
-
|
1588
|
-
|
1589
|
-
|
1590
|
-
|
1591
|
-
|
1592
|
-
|
1593
|
-
|
1594
|
-
|
1595
|
-
self._window.
|
1596
|
-
|
1597
|
-
|
1598
|
-
|
1599
|
-
|
1600
|
-
|
1601
|
-
|
1602
|
-
|
1603
|
-
|
1604
|
-
|
1605
|
-
|
1606
|
-
|
1607
|
-
|
1608
|
-
|
1609
|
-
|
1610
|
-
):
|
1611
|
-
"""
|
1612
|
-
Sets the
|
1613
|
-
|
1614
|
-
Parameters
|
1615
|
-
----------
|
1616
|
-
|
1617
|
-
|
1618
|
-
|
1619
|
-
|
1620
|
-
|
1621
|
-
|
1622
|
-
|
1623
|
-
|
1624
|
-
|
1625
|
-
|
1626
|
-
|
1627
|
-
|
1628
|
-
|
1629
|
-
|
1630
|
-
|
1631
|
-
|
1632
|
-
|
1633
|
-
|
1634
|
-
|
1635
|
-
|
1636
|
-
|
1637
|
-
|
1638
|
-
|
1639
|
-
|
1640
|
-
|
1641
|
-
|
1642
|
-
|
1643
|
-
|
1644
|
-
|
1645
|
-
|
1646
|
-
|
1647
|
-
|
1648
|
-
|
1649
|
-
|
1650
|
-
|
1651
|
-
|
1652
|
-
|
1653
|
-
|
1654
|
-
|
1655
|
-
|
1656
|
-
|
1657
|
-
|
1658
|
-
|
1659
|
-
|
1660
|
-
|
1661
|
-
|
1662
|
-
|
1663
|
-
|
1664
|
-
|
1665
|
-
|
1666
|
-
|
1667
|
-
|
1668
|
-
|
1669
|
-
|
1670
|
-
|
1671
|
-
|
1672
|
-
|
1673
|
-
|
1674
|
-
|
1675
|
-
|
1676
|
-
|
1677
|
-
|
1678
|
-
|
1679
|
-
|
1680
|
-
|
1681
|
-
|
1682
|
-
|
1683
|
-
|
1684
|
-
|
1685
|
-
|
1686
|
-
|
1687
|
-
|
1688
|
-
|
1689
|
-
|
1690
|
-
|
1691
|
-
|
1692
|
-
|
1693
|
-
|
1694
|
-
|
1695
|
-
|
1696
|
-
|
1697
|
-
|
1698
|
-
|
1699
|
-
|
1700
|
-
|
1701
|
-
|
1702
|
-
|
1703
|
-
|
1704
|
-
|
1705
|
-
|
1706
|
-
|
1707
|
-
|
1708
|
-
|
1709
|
-
|
1710
|
-
|
1711
|
-
|
1712
|
-
|
1713
|
-
|
1714
|
-
|
1715
|
-
|
1716
|
-
|
1717
|
-
|
1718
|
-
|
1719
|
-
|
1720
|
-
|
1721
|
-
|
1722
|
-
|
1723
|
-
|
1724
|
-
|
1725
|
-
|
1726
|
-
|
1727
|
-
|
1728
|
-
|
1729
|
-
|
1730
|
-
|
1731
|
-
|
1732
|
-
|
1733
|
-
|
1734
|
-
|
1735
|
-
|
1736
|
-
|
1737
|
-
|
1738
|
-
|
1739
|
-
|
1740
|
-
|
1741
|
-
|
1742
|
-
|
1743
|
-
)
|
1744
|
-
|
1745
|
-
|
1746
|
-
|
1747
|
-
|
1748
|
-
|
1749
|
-
|
1750
|
-
|
1751
|
-
|
1752
|
-
|
1753
|
-
|
1754
|
-
|
1755
|
-
|
1756
|
-
|
1757
|
-
|
1758
|
-
|
1759
|
-
|
1760
|
-
|
1761
|
-
|
1762
|
-
|
1763
|
-
|
1764
|
-
|
1765
|
-
|
1766
|
-
|
1767
|
-
|
1768
|
-
|
1769
|
-
|
1770
|
-
|
1771
|
-
|
1772
|
-
|
1773
|
-
|
1774
|
-
|
1775
|
-
|
1776
|
-
|
1777
|
-
|
1778
|
-
|
1779
|
-
|
1780
|
-
|
1781
|
-
|
1782
|
-
|
1783
|
-
|
1784
|
-
|
1785
|
-
|
1786
|
-
|
1787
|
-
|
1788
|
-
|
1789
|
-
|
1790
|
-
|
1791
|
-
|
1792
|
-
|
1793
|
-
|
1794
|
-
|
1795
|
-
|
1796
|
-
|
1797
|
-
|
1798
|
-
|
1799
|
-
|
1800
|
-
|
1801
|
-
|
1802
|
-
|
1803
|
-
|
1804
|
-
|
1805
|
-
|
1806
|
-
|
1807
|
-
|
1808
|
-
|
1809
|
-
|
1810
|
-
|
1811
|
-
|
1812
|
-
|
1813
|
-
|
1814
|
-
|
1815
|
-
|
1816
|
-
|
1817
|
-
|
1818
|
-
|
1819
|
-
|
1820
|
-
|
1821
|
-
|
1822
|
-
|
1823
|
-
|
1824
|
-
|
1825
|
-
```
|
1826
|
-
|
1827
|
-
|
1828
|
-
|
1829
|
-
|
1830
|
-
|
1831
|
-
|
1832
|
-
|
1833
|
-
|
1834
|
-
|
1835
|
-
|
1836
|
-
|
1837
|
-
|
1838
|
-
|
1839
|
-
|
1840
|
-
|
1841
|
-
|
1842
|
-
|
1843
|
-
|
1844
|
-
|
1845
|
-
|
1846
|
-
|
1847
|
-
|
1848
|
-
|
1849
|
-
|
1850
|
-
|
1851
|
-
|
1852
|
-
|
1853
|
-
|
1854
|
-
|
1855
|
-
|
1856
|
-
|
1857
|
-
|
1858
|
-
|
1859
|
-
|
1860
|
-
|
1861
|
-
|
1862
|
-
|
1863
|
-
|
1864
|
-
|
1865
|
-
|
1866
|
-
|
1867
|
-
|
1868
|
-
|
1869
|
-
|
1870
|
-
|
1871
|
-
|
1872
|
-
|
1873
|
-
|
1874
|
-
|
1875
|
-
|
1876
|
-
|
1877
|
-
|
1878
|
-
|
1879
|
-
|
1880
|
-
|
1881
|
-
|
1882
|
-
|
1883
|
-
|
1884
|
-
|
1885
|
-
|
1886
|
-
|
1887
|
-
|
1888
|
-
|
1889
|
-
|
1890
|
-
|
1891
|
-
|
1892
|
-
|
1893
|
-
|
1894
|
-
|
1895
|
-
|
1896
|
-
|
1897
|
-
|
1898
|
-
|
1899
|
-
|
1900
|
-
|
1901
|
-
|
1902
|
-
|
1903
|
-
|
1904
|
-
|
1905
|
-
|
1906
|
-
|
1907
|
-
|
1908
|
-
|
1909
|
-
|
1910
|
-
|
1911
|
-
|
1912
|
-
|
1913
|
-
|
1914
|
-
|
1915
|
-
|
1
|
+
import sys
|
2
|
+
import os
|
3
|
+
from PySide6.QtWidgets import (
|
4
|
+
QMainWindow,
|
5
|
+
)
|
6
|
+
from PySide6.QtWebEngineWidgets import QWebEngineView
|
7
|
+
from PySide6.QtWebChannel import QWebChannel
|
8
|
+
from PySide6.QtGui import (
|
9
|
+
QKeySequence,
|
10
|
+
QShortcut,
|
11
|
+
QCursor,
|
12
|
+
)
|
13
|
+
from PySide6.QtCore import Qt, QPoint, QUrl, QEvent, QFile
|
14
|
+
from PySide6.QtWebEngineCore import (
|
15
|
+
QWebEnginePage,
|
16
|
+
QWebEngineSettings,
|
17
|
+
QWebEngineUrlRequestInterceptor,
|
18
|
+
)
|
19
|
+
from .api import PyloidAPI
|
20
|
+
import uuid
|
21
|
+
from typing import List, Optional, Dict, Callable
|
22
|
+
import json
|
23
|
+
from PySide6.QtWidgets import (
|
24
|
+
QWidget,
|
25
|
+
QVBoxLayout,
|
26
|
+
)
|
27
|
+
from .custom.titlebar import CustomTitleBar
|
28
|
+
from .js_api.window_api import WindowAPI
|
29
|
+
from PySide6.QtGui import QPixmap, QMovie
|
30
|
+
from PySide6.QtWidgets import QSplashScreen, QLabel
|
31
|
+
from PySide6.QtCore import QSize
|
32
|
+
from typing import TYPE_CHECKING
|
33
|
+
from PySide6.QtWebEngineCore import QWebEngineSettings
|
34
|
+
|
35
|
+
if TYPE_CHECKING:
|
36
|
+
from ..pyloid import Pyloid
|
37
|
+
|
38
|
+
|
39
|
+
# 어차피 load 부분에만 쓰이니까 나중에 분리해서 load 위에서 선언하자.
|
40
|
+
class CustomWebPage(QWebEnginePage):
|
41
|
+
def __init__(self, profile=None):
|
42
|
+
super().__init__(profile)
|
43
|
+
self.featurePermissionRequested.connect(self._handlePermissionRequest)
|
44
|
+
self.desktopMediaRequested.connect(self._handleDesktopMediaRequest)
|
45
|
+
self._permission_handlers = {}
|
46
|
+
self._desktop_media_handler = None
|
47
|
+
self._url_handlers = {} # URL 핸들러 저장을 위한 딕셔너리 추가
|
48
|
+
|
49
|
+
# interceptor ( all url request )
|
50
|
+
self.interceptor = CustomUrlInterceptor()
|
51
|
+
self.profile().setUrlRequestInterceptor(self.interceptor)
|
52
|
+
|
53
|
+
def _handlePermissionRequest(self, origin: QUrl, feature: QWebEnginePage.Feature):
|
54
|
+
print(origin, feature)
|
55
|
+
|
56
|
+
"""Default permission request handler"""
|
57
|
+
if feature in self._permission_handlers:
|
58
|
+
# Execute if a handler is registered
|
59
|
+
handler = self._permission_handlers[feature]
|
60
|
+
handler(origin, feature)
|
61
|
+
else:
|
62
|
+
# Allow all permissions by default
|
63
|
+
self.setFeaturePermission(
|
64
|
+
origin, feature, QWebEnginePage.PermissionPolicy.PermissionGrantedByUser
|
65
|
+
)
|
66
|
+
|
67
|
+
def setPermissionHandler(self, feature: QWebEnginePage.Feature, handler):
|
68
|
+
"""Register a handler for a specific permission"""
|
69
|
+
self._permission_handlers[feature] = handler
|
70
|
+
|
71
|
+
def _handleDesktopMediaRequest(self, *args, **kwargs):
|
72
|
+
print("Desktop media request received:", args, kwargs)
|
73
|
+
|
74
|
+
# interceptor ( navigation request )
|
75
|
+
def acceptNavigationRequest(self, url, navigation_type, is_main_frame):
|
76
|
+
"""네비게이션 요청을 처리하는 메서드"""
|
77
|
+
print(f"Navigation Request - URL: {url.toString()}")
|
78
|
+
print(f"Navigation Type: {navigation_type}")
|
79
|
+
print(f"Is Main Frame: {is_main_frame}")
|
80
|
+
|
81
|
+
# # URL이 구글이 아닐 경우에만 구글로 리다이렉트
|
82
|
+
# if "example.com" not in url.toString():
|
83
|
+
# self.setUrl(QUrl("https://www.example.com"))
|
84
|
+
# return False
|
85
|
+
|
86
|
+
return True
|
87
|
+
|
88
|
+
def add_url_handler(self, pattern: str, handler):
|
89
|
+
"""URL 패턴에 대한 핸들러 등록
|
90
|
+
|
91
|
+
Parameters:
|
92
|
+
-----------
|
93
|
+
pattern : str
|
94
|
+
정규표현식 패턴
|
95
|
+
handler : callable
|
96
|
+
URL을 인자로 받고 bool을 반환해야 함
|
97
|
+
True를 반환하면 네비게이션을 허용, False를 반환하면 차단
|
98
|
+
"""
|
99
|
+
import re
|
100
|
+
|
101
|
+
self._url_handlers[re.compile(pattern)] = handler
|
102
|
+
|
103
|
+
|
104
|
+
# interceptor ( all url request )
|
105
|
+
class CustomUrlInterceptor(QWebEngineUrlRequestInterceptor):
|
106
|
+
def interceptRequest(self, info):
|
107
|
+
url = info.requestUrl().toString()
|
108
|
+
print(url)
|
109
|
+
|
110
|
+
|
111
|
+
class CustomWebEngineView(QWebEngineView):
|
112
|
+
def __init__(self, parent: "BrowserWindow" = None):
|
113
|
+
super().__init__(parent._window)
|
114
|
+
self.parent: "BrowserWindow" = parent
|
115
|
+
|
116
|
+
# Custom Web Page
|
117
|
+
self.custom_page = CustomWebPage()
|
118
|
+
self.setPage(self.custom_page)
|
119
|
+
|
120
|
+
self.is_resizing = False
|
121
|
+
self.resize_start_pos = None
|
122
|
+
self.resize_direction = None
|
123
|
+
self.screen_geometry = self.screen().virtualGeometry()
|
124
|
+
self.is_resizing_enabled = True
|
125
|
+
|
126
|
+
self.setAttribute(Qt.WA_SetCursor, False)
|
127
|
+
|
128
|
+
self.is_in_resize_area = False
|
129
|
+
|
130
|
+
def mouse_press_event(self, event):
|
131
|
+
if self.parent.frame or not self.is_resizing_enabled:
|
132
|
+
return
|
133
|
+
|
134
|
+
if event.button() == Qt.LeftButton:
|
135
|
+
self.resize_direction = self.get_resize_direction(event.pos())
|
136
|
+
if self.resize_direction:
|
137
|
+
self.is_resizing = True
|
138
|
+
self.resize_start_pos = event.globalPos()
|
139
|
+
|
140
|
+
def start_system_drag(self):
|
141
|
+
"""Start system window move"""
|
142
|
+
if self.parent._window.windowHandle():
|
143
|
+
self.parent._window.windowHandle().startSystemMove()
|
144
|
+
|
145
|
+
def mouse_move_event(self, event):
|
146
|
+
if self.parent.frame or not self.is_resizing_enabled:
|
147
|
+
return
|
148
|
+
|
149
|
+
# Check resize direction
|
150
|
+
was_in_resize_area = self.is_in_resize_area
|
151
|
+
resize_direction = self.get_resize_direction(event.pos())
|
152
|
+
self.is_in_resize_area = bool(resize_direction)
|
153
|
+
|
154
|
+
if resize_direction and not self.is_resizing:
|
155
|
+
self.set_cursor_for_resize_direction(resize_direction)
|
156
|
+
|
157
|
+
if self.is_resizing:
|
158
|
+
self.resize_window(event.globalPos())
|
159
|
+
return
|
160
|
+
|
161
|
+
# Change cursor when entering/leaving resize area
|
162
|
+
if self.is_in_resize_area != was_in_resize_area:
|
163
|
+
if self.is_in_resize_area:
|
164
|
+
# self.setAttribute(Qt.WA_SetCursor, True)
|
165
|
+
pass
|
166
|
+
else:
|
167
|
+
self.unsetCursor()
|
168
|
+
# self.setAttribute(Qt.WA_SetCursor, False)
|
169
|
+
|
170
|
+
def mouse_release_event(self, event):
|
171
|
+
if self.parent.frame or not self.is_resizing_enabled:
|
172
|
+
return
|
173
|
+
|
174
|
+
if event.button() == Qt.LeftButton:
|
175
|
+
self.is_resizing = False
|
176
|
+
|
177
|
+
if self.resize_direction:
|
178
|
+
self.unsetCursor()
|
179
|
+
self.resize_direction = None
|
180
|
+
|
181
|
+
self.setAttribute(Qt.WA_SetCursor, False)
|
182
|
+
|
183
|
+
def eventFilter(self, source, event):
|
184
|
+
if self.focusProxy() is source:
|
185
|
+
if event.type() == QEvent.MouseButtonPress:
|
186
|
+
self.mouse_press_event(event)
|
187
|
+
elif event.type() == QEvent.MouseMove:
|
188
|
+
self.mouse_move_event(event)
|
189
|
+
elif event.type() == QEvent.MouseButtonRelease:
|
190
|
+
self.mouse_release_event(event)
|
191
|
+
return super().eventFilter(source, event)
|
192
|
+
|
193
|
+
def get_resize_direction(self, pos):
|
194
|
+
if (
|
195
|
+
not self.parent.frame and self.is_resizing_enabled
|
196
|
+
): # Check if frame is not present and resizing is enabled
|
197
|
+
margin = 8 # Margin in pixels to detect edge
|
198
|
+
rect = self.rect()
|
199
|
+
direction = None
|
200
|
+
|
201
|
+
if pos.x() <= margin:
|
202
|
+
direction = "left"
|
203
|
+
elif pos.x() >= rect.width() - margin:
|
204
|
+
direction = "right"
|
205
|
+
|
206
|
+
if pos.y() <= margin:
|
207
|
+
direction = "top" if direction is None else direction + "-top"
|
208
|
+
elif pos.y() >= rect.height() - margin:
|
209
|
+
direction = "bottom" if direction is None else direction + "-bottom"
|
210
|
+
|
211
|
+
return direction
|
212
|
+
return None
|
213
|
+
|
214
|
+
def set_cursor_for_resize_direction(self, direction):
|
215
|
+
if not self.parent.frame and direction and self.is_resizing_enabled:
|
216
|
+
cursor = None
|
217
|
+
if direction in ["left", "right"]:
|
218
|
+
cursor = Qt.SizeHorCursor
|
219
|
+
elif direction in ["top", "bottom"]:
|
220
|
+
cursor = Qt.SizeVerCursor
|
221
|
+
elif direction in ["left-top", "right-bottom"]:
|
222
|
+
cursor = Qt.SizeFDiagCursor
|
223
|
+
elif direction in ["right-top", "left-bottom"]:
|
224
|
+
cursor = Qt.SizeBDiagCursor
|
225
|
+
|
226
|
+
if cursor:
|
227
|
+
self.setCursor(cursor)
|
228
|
+
self.setAttribute(Qt.WA_SetCursor, True)
|
229
|
+
|
230
|
+
def resize_window(self, global_pos):
|
231
|
+
if (
|
232
|
+
not self.parent.frame
|
233
|
+
and self.resize_start_pos
|
234
|
+
and self.resize_direction
|
235
|
+
and self.is_resizing_enabled
|
236
|
+
): # Check if frame is not present and resizing is enabled
|
237
|
+
delta = global_pos - self.resize_start_pos
|
238
|
+
new_geometry = self.parent._window.geometry()
|
239
|
+
|
240
|
+
if "left" in self.resize_direction:
|
241
|
+
new_geometry.setLeft(new_geometry.left() + delta.x())
|
242
|
+
if "right" in self.resize_direction:
|
243
|
+
new_geometry.setRight(new_geometry.right() + delta.x())
|
244
|
+
if "top" in self.resize_direction:
|
245
|
+
new_geometry.setTop(new_geometry.top() + delta.y())
|
246
|
+
if "bottom" in self.resize_direction:
|
247
|
+
new_geometry.setBottom(new_geometry.bottom() + delta.y())
|
248
|
+
|
249
|
+
self.parent._window.setGeometry(new_geometry)
|
250
|
+
self.resize_start_pos = global_pos
|
251
|
+
|
252
|
+
|
253
|
+
class BrowserWindow:
|
254
|
+
def __init__(
|
255
|
+
self,
|
256
|
+
app: "Pyloid",
|
257
|
+
title: str = "pyloid app",
|
258
|
+
width: int = 800,
|
259
|
+
height: int = 600,
|
260
|
+
x: int = 200,
|
261
|
+
y: int = 200,
|
262
|
+
frame: bool = True,
|
263
|
+
context_menu: bool = False,
|
264
|
+
dev_tools: bool = False,
|
265
|
+
js_apis: List[PyloidAPI] = [],
|
266
|
+
):
|
267
|
+
###########################################################################################
|
268
|
+
self.id = str(uuid.uuid4()) # Generate unique ID
|
269
|
+
self._window = QMainWindow()
|
270
|
+
self.web_view = CustomWebEngineView(self)
|
271
|
+
|
272
|
+
self._window.closeEvent = self.closeEvent # Override closeEvent method
|
273
|
+
###########################################################################################
|
274
|
+
self.app = app
|
275
|
+
self.title = title
|
276
|
+
self.width = width
|
277
|
+
self.height = height
|
278
|
+
self.x = x
|
279
|
+
self.y = y
|
280
|
+
self.frame = frame
|
281
|
+
self.context_menu = context_menu
|
282
|
+
self.dev_tools = dev_tools
|
283
|
+
self.js_apis = [WindowAPI()]
|
284
|
+
for js_api in js_apis:
|
285
|
+
self.js_apis.append(js_api)
|
286
|
+
self.shortcuts = {}
|
287
|
+
self.close_on_load = True
|
288
|
+
self.splash_screen = None
|
289
|
+
###########################################################################################
|
290
|
+
|
291
|
+
def _set_custom_frame(
|
292
|
+
self,
|
293
|
+
use_custom: bool,
|
294
|
+
title: str = "Custom Title",
|
295
|
+
bg_color: str = "darkblue",
|
296
|
+
text_color: str = "white",
|
297
|
+
icon_path: str = None,
|
298
|
+
):
|
299
|
+
"""Sets or removes the custom frame."""
|
300
|
+
if use_custom:
|
301
|
+
self._window.setWindowFlags(Qt.FramelessWindowHint)
|
302
|
+
self.custom_title_bar = CustomTitleBar(self._window)
|
303
|
+
self.custom_title_bar.set_style(bg_color, text_color)
|
304
|
+
self.custom_title_bar.set_title(title)
|
305
|
+
|
306
|
+
if icon_path:
|
307
|
+
self.custom_title_bar.set_icon(icon_path)
|
308
|
+
|
309
|
+
layout = QVBoxLayout()
|
310
|
+
layout.setContentsMargins(0, 0, 0, 0)
|
311
|
+
layout.setSpacing(0)
|
312
|
+
layout.addWidget(self.custom_title_bar)
|
313
|
+
layout.addWidget(self.web_view)
|
314
|
+
|
315
|
+
central_widget = QWidget()
|
316
|
+
central_widget.setLayout(layout)
|
317
|
+
self._window.setCentralWidget(central_widget)
|
318
|
+
|
319
|
+
# Add properties for window movement
|
320
|
+
self._window.moving = False
|
321
|
+
self._window.offset = QPoint()
|
322
|
+
else:
|
323
|
+
self._window.setWindowFlags(Qt.Window)
|
324
|
+
self._window.setCentralWidget(self.web_view)
|
325
|
+
self.custom_title_bar = None
|
326
|
+
|
327
|
+
self._window.show()
|
328
|
+
|
329
|
+
def _load(self):
|
330
|
+
self.set_title(self.title)
|
331
|
+
|
332
|
+
self.set_size(self.width, self.height)
|
333
|
+
self.set_position(self.x, self.y)
|
334
|
+
|
335
|
+
# allow local file access to remote urls and screen capture
|
336
|
+
self.web_view.settings().setAttribute(
|
337
|
+
QWebEngineSettings.WebAttribute.LocalContentCanAccessRemoteUrls, True
|
338
|
+
)
|
339
|
+
self.web_view.settings().setAttribute(
|
340
|
+
QWebEngineSettings.WebAttribute.ScreenCaptureEnabled, True
|
341
|
+
)
|
342
|
+
self.web_view.settings().setAttribute(
|
343
|
+
QWebEngineSettings.WebAttribute.AutoLoadImages, True
|
344
|
+
)
|
345
|
+
self.web_view.settings().setAttribute(
|
346
|
+
QWebEngineSettings.WebAttribute.JavascriptEnabled, True
|
347
|
+
)
|
348
|
+
self.web_view.settings().setAttribute(
|
349
|
+
QWebEngineSettings.WebAttribute.LocalStorageEnabled, True
|
350
|
+
)
|
351
|
+
self.web_view.settings().setAttribute(
|
352
|
+
QWebEngineSettings.WebAttribute.ErrorPageEnabled, True
|
353
|
+
)
|
354
|
+
self.web_view.settings().setAttribute(
|
355
|
+
QWebEngineSettings.WebAttribute.AutoLoadIconsForPage, True
|
356
|
+
)
|
357
|
+
self.web_view.settings().setAttribute(
|
358
|
+
QWebEngineSettings.WebAttribute.ShowScrollBars, True
|
359
|
+
)
|
360
|
+
self.web_view.settings().setAttribute(
|
361
|
+
QWebEngineSettings.WebAttribute.DnsPrefetchEnabled, True
|
362
|
+
)
|
363
|
+
self.web_view.settings().setAttribute(
|
364
|
+
QWebEngineSettings.WebAttribute.PdfViewerEnabled, True
|
365
|
+
)
|
366
|
+
self.web_view.settings().setAttribute(
|
367
|
+
QWebEngineSettings.WebAttribute.FullScreenSupportEnabled, True
|
368
|
+
)
|
369
|
+
self.web_view.settings().setAttribute(
|
370
|
+
QWebEngineSettings.WebAttribute.JavascriptCanAccessClipboard, True
|
371
|
+
)
|
372
|
+
self.web_view.settings().setUnknownUrlSchemePolicy(
|
373
|
+
QWebEngineSettings.UnknownUrlSchemePolicy.AllowAllUnknownUrlSchemes
|
374
|
+
)
|
375
|
+
self.web_view.settings().setAttribute(
|
376
|
+
QWebEngineSettings.WebAttribute.AllowRunningInsecureContent, True
|
377
|
+
)
|
378
|
+
self.web_view.settings().setAttribute(
|
379
|
+
QWebEngineSettings.WebAttribute.AllowGeolocationOnInsecureOrigins, True
|
380
|
+
)
|
381
|
+
self.web_view.settings().setAttribute(
|
382
|
+
QWebEngineSettings.WebAttribute.AllowWindowActivationFromJavaScript, True
|
383
|
+
)
|
384
|
+
self.web_view.settings().setAttribute(
|
385
|
+
QWebEngineSettings.WebAttribute.JavascriptCanPaste, True
|
386
|
+
)
|
387
|
+
self.web_view.settings().setAttribute(
|
388
|
+
QWebEngineSettings.WebAttribute.WebRTCPublicInterfacesOnly, False
|
389
|
+
)
|
390
|
+
|
391
|
+
# Set icon
|
392
|
+
if self.app.icon:
|
393
|
+
self._window.setWindowIcon(self.app.icon)
|
394
|
+
else:
|
395
|
+
print("Icon is not set.")
|
396
|
+
|
397
|
+
# Set Windows taskbar icon
|
398
|
+
if sys.platform == "win32":
|
399
|
+
import ctypes
|
400
|
+
|
401
|
+
myappid = "mycompany.myproduct.subproduct.version"
|
402
|
+
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(myappid)
|
403
|
+
|
404
|
+
# Remove title bar and borders (if needed)
|
405
|
+
if not self.frame:
|
406
|
+
self._window.setWindowFlags(Qt.FramelessWindowHint)
|
407
|
+
|
408
|
+
# Disable default context menu
|
409
|
+
if not self.context_menu:
|
410
|
+
self.web_view.setContextMenuPolicy(Qt.NoContextMenu)
|
411
|
+
|
412
|
+
# Set up QWebChannel
|
413
|
+
self.channel = QWebChannel()
|
414
|
+
|
415
|
+
# Register additional JS APIs
|
416
|
+
if self.js_apis:
|
417
|
+
for js_api in self.js_apis:
|
418
|
+
# Define window_id, window, and app for each JS API
|
419
|
+
js_api.window_id = self.id
|
420
|
+
js_api.window = self
|
421
|
+
js_api.app = self.app
|
422
|
+
|
423
|
+
self.channel.registerObject(js_api.__class__.__name__, js_api)
|
424
|
+
|
425
|
+
self.web_view.page().setWebChannel(self.channel)
|
426
|
+
|
427
|
+
# Connect pylonjs bridge
|
428
|
+
self.web_view.loadFinished.connect(self._on_load_finished)
|
429
|
+
|
430
|
+
# Add QWebEngineView to main window
|
431
|
+
self._window.setCentralWidget(self.web_view)
|
432
|
+
|
433
|
+
# Set F12 shortcut
|
434
|
+
self.set_dev_tools(self.dev_tools)
|
435
|
+
|
436
|
+
def _on_load_finished(self, ok):
|
437
|
+
"""Handles the event when the web page finishes loading."""
|
438
|
+
if ok and self.js_apis:
|
439
|
+
# Load qwebchannel.js
|
440
|
+
qwebchannel_js = QFile("://qtwebchannel/qwebchannel.js")
|
441
|
+
if qwebchannel_js.open(QFile.ReadOnly):
|
442
|
+
source = bytes(qwebchannel_js.readAll()).decode("utf-8")
|
443
|
+
self.web_view.page().runJavaScript(source)
|
444
|
+
qwebchannel_js.close()
|
445
|
+
|
446
|
+
js_code = """
|
447
|
+
if (typeof QWebChannel !== 'undefined') {
|
448
|
+
new QWebChannel(qt.webChannelTransport, function (channel) {
|
449
|
+
window.pyloid = {
|
450
|
+
EventAPI: {
|
451
|
+
_listeners: {}, // 콜백 함수들을 저장할 객체
|
452
|
+
|
453
|
+
listen: function(eventName, callback) {
|
454
|
+
// 이벤트에 대한 콜백 배열이 없다면 생성
|
455
|
+
if (!this._listeners[eventName]) {
|
456
|
+
this._listeners[eventName] = [];
|
457
|
+
}
|
458
|
+
|
459
|
+
// 콜백 함수 저장
|
460
|
+
this._listeners[eventName].push(callback);
|
461
|
+
|
462
|
+
document.addEventListener(eventName, function(event) {
|
463
|
+
let eventData;
|
464
|
+
try {
|
465
|
+
eventData = JSON.parse(event.detail);
|
466
|
+
} catch (e) {
|
467
|
+
eventData = event.detail;
|
468
|
+
}
|
469
|
+
callback(eventData);
|
470
|
+
});
|
471
|
+
},
|
472
|
+
|
473
|
+
unlisten: function(eventName) {
|
474
|
+
// 해당 이벤트의 모든 리스너 제거
|
475
|
+
if (this._listeners[eventName]) {
|
476
|
+
this._listeners[eventName].forEach(callback => {
|
477
|
+
document.removeEventListener(eventName, callback);
|
478
|
+
});
|
479
|
+
// 저장된 콜백 제거
|
480
|
+
delete this._listeners[eventName];
|
481
|
+
}
|
482
|
+
}
|
483
|
+
}
|
484
|
+
};
|
485
|
+
console.log('pyloid.EventAPI object initialized:', window.pyloid.EventAPI);
|
486
|
+
|
487
|
+
%s
|
488
|
+
|
489
|
+
document.addEventListener('mousedown', function (e) {
|
490
|
+
if (e.target.hasAttribute('data-pyloid-drag-region')) {
|
491
|
+
window.pyloid.WindowAPI.startSystemDrag();
|
492
|
+
}
|
493
|
+
});
|
494
|
+
|
495
|
+
function updateTheme(theme) {
|
496
|
+
document.documentElement.setAttribute(
|
497
|
+
'data-pyloid-theme',
|
498
|
+
theme
|
499
|
+
);
|
500
|
+
}
|
501
|
+
|
502
|
+
// 테마 변경 이벤트 리스너
|
503
|
+
document.addEventListener('themeChange', (e) => {
|
504
|
+
console.log('themeChange event received:', e);
|
505
|
+
updateTheme(e.detail.theme);
|
506
|
+
});
|
507
|
+
|
508
|
+
updateTheme('%s');
|
509
|
+
|
510
|
+
|
511
|
+
// Dispatch a custom event to signal that the initialization is ready
|
512
|
+
const event = new CustomEvent('pyloidReady');
|
513
|
+
document.dispatchEvent(event);
|
514
|
+
});
|
515
|
+
} else {
|
516
|
+
console.error('QWebChannel is not defined.');
|
517
|
+
}
|
518
|
+
"""
|
519
|
+
js_api_init = "\n".join(
|
520
|
+
[
|
521
|
+
f"window.pyloid['{js_api.__class__.__name__}'] = channel.objects['{js_api.__class__.__name__}'];\n"
|
522
|
+
f"console.log('pyloid.{js_api.__class__.__name__} object initialized:', window.pyloid['{js_api.__class__.__name__}']);"
|
523
|
+
for js_api in self.js_apis
|
524
|
+
]
|
525
|
+
)
|
526
|
+
self.web_view.page().runJavaScript(js_code % (js_api_init, self.app.theme))
|
527
|
+
|
528
|
+
# if splash screen is set, close it when the page is loaded
|
529
|
+
if self.close_on_load and self.splash_screen:
|
530
|
+
self.close_splash_screen()
|
531
|
+
else:
|
532
|
+
pass
|
533
|
+
|
534
|
+
###########################################################################################
|
535
|
+
# Load
|
536
|
+
###########################################################################################
|
537
|
+
def load_file(self, file_path: str):
|
538
|
+
"""
|
539
|
+
Loads a local HTML file into the web view.
|
540
|
+
|
541
|
+
Parameters
|
542
|
+
----------
|
543
|
+
file_path : str
|
544
|
+
The path to the local HTML file to be loaded.
|
545
|
+
|
546
|
+
Examples
|
547
|
+
--------
|
548
|
+
>>> app = Pyloid(app_name="Pyloid-App")
|
549
|
+
>>> window = app.create_window("pyloid-window")
|
550
|
+
>>> window.load_file('/path/to/local/file.html')
|
551
|
+
>>> window.show()
|
552
|
+
"""
|
553
|
+
self._load()
|
554
|
+
file_path = os.path.abspath(file_path) # absolute path
|
555
|
+
self.web_view.setUrl(QUrl.fromLocalFile(file_path))
|
556
|
+
self.web_view.focusProxy().installEventFilter(self.web_view)
|
557
|
+
|
558
|
+
def load_url(self, url: str):
|
559
|
+
"""
|
560
|
+
Sets the URL of the window.
|
561
|
+
|
562
|
+
Parameters
|
563
|
+
----------
|
564
|
+
url : str
|
565
|
+
The URL to be loaded in the web view.
|
566
|
+
|
567
|
+
Examples
|
568
|
+
--------
|
569
|
+
>>> app = Pyloid(app_name="Pyloid-App")
|
570
|
+
>>> window = app.create_window("pyloid-window")
|
571
|
+
>>> window.load_url('https://www.example.com')
|
572
|
+
>>> window.show()
|
573
|
+
"""
|
574
|
+
self._load()
|
575
|
+
self.web_view.setUrl(QUrl(url))
|
576
|
+
self.web_view.focusProxy().installEventFilter(self.web_view)
|
577
|
+
|
578
|
+
def load_html(self, html_content: str, base_url: str = ""):
|
579
|
+
"""
|
580
|
+
Loads HTML content directly into the web view.
|
581
|
+
|
582
|
+
Parameters
|
583
|
+
----------
|
584
|
+
html_content : str
|
585
|
+
The HTML content to be loaded.
|
586
|
+
base_url : str, optional
|
587
|
+
The base URL to use for resolving relative URLs (default is "").
|
588
|
+
|
589
|
+
Examples
|
590
|
+
--------
|
591
|
+
>>> app = Pyloid(app_name="Pyloid-App")
|
592
|
+
>>> window = app.create_window("pyloid-window")
|
593
|
+
>>> html_content = "<html><body><h1>Hello, Pyloid!</h1></body></html>"
|
594
|
+
>>> window.load_html(html_content)
|
595
|
+
>>> window.show()
|
596
|
+
"""
|
597
|
+
self._load()
|
598
|
+
self.web_view.setHtml(html_content, QUrl(base_url))
|
599
|
+
self.web_view.focusProxy().installEventFilter(self.web_view)
|
600
|
+
|
601
|
+
###########################################################################################
|
602
|
+
# Set Parameters
|
603
|
+
###########################################################################################
|
604
|
+
def set_title(self, title: str):
|
605
|
+
"""
|
606
|
+
Sets the title of the window.
|
607
|
+
|
608
|
+
Parameters
|
609
|
+
----------
|
610
|
+
title : str
|
611
|
+
The title to be set for the window.
|
612
|
+
|
613
|
+
Examples
|
614
|
+
--------
|
615
|
+
>>> app = Pyloid(app_name="Pyloid-App")
|
616
|
+
>>> window = app.create_window("pyloid-window")
|
617
|
+
>>> window.set_title('My Window Title')
|
618
|
+
"""
|
619
|
+
self.title = title
|
620
|
+
self._window.setWindowTitle(self.title)
|
621
|
+
|
622
|
+
def set_size(self, width: int, height: int):
|
623
|
+
"""
|
624
|
+
Sets the size of the window.
|
625
|
+
|
626
|
+
Parameters
|
627
|
+
----------
|
628
|
+
width : int
|
629
|
+
The width of the window.
|
630
|
+
height : int
|
631
|
+
The height of the window.
|
632
|
+
|
633
|
+
Examples
|
634
|
+
--------
|
635
|
+
>>> app = Pyloid(app_name="Pyloid-App")
|
636
|
+
>>> window = app.create_window("pyloid-window")
|
637
|
+
>>> window.set_size(800, 600)
|
638
|
+
"""
|
639
|
+
self.width = width
|
640
|
+
self.height = height
|
641
|
+
self._window.setGeometry(self.x, self.y, self.width, self.height)
|
642
|
+
|
643
|
+
def set_position(self, x: int, y: int):
|
644
|
+
"""
|
645
|
+
Sets the position of the window.
|
646
|
+
|
647
|
+
Parameters
|
648
|
+
----------
|
649
|
+
x : int
|
650
|
+
The x-coordinate of the window's position.
|
651
|
+
y : int
|
652
|
+
The y-coordinate of the window's position.
|
653
|
+
|
654
|
+
Examples
|
655
|
+
--------
|
656
|
+
>>> app = Pyloid(app_name="Pyloid-App")
|
657
|
+
>>> window = app.create_window("pyloid-window")
|
658
|
+
>>> window.set_position(100, 100)
|
659
|
+
"""
|
660
|
+
self.x = x
|
661
|
+
self.y = y
|
662
|
+
self._window.setGeometry(self.x, self.y, self.width, self.height)
|
663
|
+
|
664
|
+
def set_position_by_anchor(self, anchor: str):
|
665
|
+
"""
|
666
|
+
Positions the window at a specific location on the screen.
|
667
|
+
|
668
|
+
Parameters
|
669
|
+
----------
|
670
|
+
anchor : str
|
671
|
+
The anchor point indicating where to position the window.
|
672
|
+
Possible values: 'center', 'top', 'bottom', 'left', 'right',
|
673
|
+
'top-left', 'top-right', 'bottom-left', 'bottom-right'
|
674
|
+
|
675
|
+
Examples
|
676
|
+
--------
|
677
|
+
>>> window.set_position_by_anchor('center')
|
678
|
+
>>> window.set_position_by_anchor('top-right')
|
679
|
+
"""
|
680
|
+
screen = self.app.primaryScreen().availableGeometry()
|
681
|
+
window_size = self.get_size()
|
682
|
+
|
683
|
+
if anchor == "center":
|
684
|
+
x = (screen.width() - window_size["width"]) // 2
|
685
|
+
y = (screen.height() - window_size["height"]) // 2
|
686
|
+
elif anchor == "top":
|
687
|
+
x = (screen.width() - window_size["width"]) // 2
|
688
|
+
y = screen.top()
|
689
|
+
elif anchor == "bottom":
|
690
|
+
x = (screen.width() - window_size["width"]) // 2
|
691
|
+
y = screen.bottom() - window_size["height"]
|
692
|
+
elif anchor == "left":
|
693
|
+
x = screen.left()
|
694
|
+
y = (screen.height() - window_size["height"]) // 2
|
695
|
+
elif anchor == "right":
|
696
|
+
x = screen.right() - window_size["width"]
|
697
|
+
y = (screen.height() - window_size["height"]) // 2
|
698
|
+
elif anchor == "top-left":
|
699
|
+
x = screen.left()
|
700
|
+
y = screen.top()
|
701
|
+
elif anchor == "top-right":
|
702
|
+
x = screen.right() - window_size["width"]
|
703
|
+
y = screen.top()
|
704
|
+
elif anchor == "bottom-left":
|
705
|
+
x = screen.left()
|
706
|
+
y = screen.bottom() - window_size["height"]
|
707
|
+
elif anchor == "bottom-right":
|
708
|
+
x = screen.right() - window_size["width"]
|
709
|
+
y = screen.bottom() - window_size["height"]
|
710
|
+
else:
|
711
|
+
raise ValueError("Invalid anchor point.")
|
712
|
+
|
713
|
+
self.set_position(x, y)
|
714
|
+
|
715
|
+
def set_frame(self, frame: bool):
|
716
|
+
"""
|
717
|
+
Sets the frame of the window.
|
718
|
+
|
719
|
+
Parameters
|
720
|
+
----------
|
721
|
+
frame : bool
|
722
|
+
If True, the window will have a frame. If False, the window will be frameless.
|
723
|
+
|
724
|
+
Examples
|
725
|
+
--------
|
726
|
+
>>> app = Pyloid(app_name="Pyloid-App")
|
727
|
+
>>> window = app.create_window("pyloid-window")
|
728
|
+
>>> window.set_frame(True)
|
729
|
+
>>> window.set_frame(False)
|
730
|
+
"""
|
731
|
+
self.frame = frame
|
732
|
+
was_visible = self._window.isVisible()
|
733
|
+
if self.frame:
|
734
|
+
self._window.setWindowFlags(Qt.Window)
|
735
|
+
else:
|
736
|
+
self._window.setWindowFlags(Qt.FramelessWindowHint)
|
737
|
+
if was_visible:
|
738
|
+
self._window.show()
|
739
|
+
|
740
|
+
def set_context_menu(self, context_menu: bool):
|
741
|
+
"""
|
742
|
+
Sets the context menu of the window.
|
743
|
+
|
744
|
+
Parameters
|
745
|
+
----------
|
746
|
+
context_menu : bool
|
747
|
+
If True, the context menu will be disabled. If False, the default context menu will be enabled.
|
748
|
+
|
749
|
+
Examples
|
750
|
+
--------
|
751
|
+
>>> app = Pyloid(app_name="Pyloid-App")
|
752
|
+
>>> window = app.create_window("pyloid-window")
|
753
|
+
>>> window.set_context_menu(True)
|
754
|
+
>>> window.set_context_menu(False)
|
755
|
+
"""
|
756
|
+
self.context_menu = context_menu
|
757
|
+
if self.context_menu:
|
758
|
+
self.web_view.setContextMenuPolicy(Qt.NoContextMenu)
|
759
|
+
else:
|
760
|
+
self.web_view.setContextMenuPolicy(Qt.DefaultContextMenu)
|
761
|
+
|
762
|
+
def set_dev_tools(self, enable: bool):
|
763
|
+
"""
|
764
|
+
Sets the developer tools of the window.
|
765
|
+
|
766
|
+
If enabled, the developer tools can be opened using the F12 key.
|
767
|
+
|
768
|
+
Parameters
|
769
|
+
----------
|
770
|
+
enable : bool
|
771
|
+
If True, the developer tools will be enabled. If False, the developer tools will be disabled.
|
772
|
+
|
773
|
+
Examples
|
774
|
+
--------
|
775
|
+
>>> app = Pyloid(app_name="Pyloid-App")
|
776
|
+
>>> window = app.create_window("pyloid-window")
|
777
|
+
>>> window.set_dev_tools(True)
|
778
|
+
>>> window.set_dev_tools(False)
|
779
|
+
"""
|
780
|
+
self.dev_tools = enable
|
781
|
+
if self.dev_tools:
|
782
|
+
self.add_shortcut("F12", self.open_dev_tools)
|
783
|
+
else:
|
784
|
+
self.remove_shortcut("F12")
|
785
|
+
|
786
|
+
def open_dev_tools(self):
|
787
|
+
"""
|
788
|
+
Opens the developer tools window.
|
789
|
+
|
790
|
+
Examples
|
791
|
+
--------
|
792
|
+
>>> app = Pyloid(app_name="Pyloid-App")
|
793
|
+
>>> window = app.create_window("pyloid-window")
|
794
|
+
>>> window.open_dev_tools()
|
795
|
+
"""
|
796
|
+
self.web_view.page().setDevToolsPage(QWebEnginePage(self.web_view.page()))
|
797
|
+
self.dev_tools_window = QMainWindow(self._window)
|
798
|
+
dev_tools_view = QWebEngineView(self.dev_tools_window)
|
799
|
+
dev_tools_view.setPage(self.web_view.page().devToolsPage())
|
800
|
+
self.dev_tools_window.setCentralWidget(dev_tools_view)
|
801
|
+
self.dev_tools_window.resize(800, 600)
|
802
|
+
self.dev_tools_window.show()
|
803
|
+
|
804
|
+
# Add this line to handle dev tools window closure
|
805
|
+
self.dev_tools_window.closeEvent = lambda event: setattr(
|
806
|
+
self, "dev_tools_window", None
|
807
|
+
)
|
808
|
+
|
809
|
+
def closeEvent(self, event):
|
810
|
+
"""Handles the event when the window is closed."""
|
811
|
+
# Close developer tools if open
|
812
|
+
if hasattr(self, "dev_tools_window") and self.dev_tools_window:
|
813
|
+
self.dev_tools_window.close()
|
814
|
+
self.dev_tools_window = None
|
815
|
+
|
816
|
+
# Solve memory leak issue with web view engine
|
817
|
+
self.web_view.page().deleteLater()
|
818
|
+
self.web_view.deleteLater()
|
819
|
+
self._remove_from_app_windows()
|
820
|
+
event.accept() # Accept the event (allow the window to close)
|
821
|
+
|
822
|
+
def _remove_from_app_windows(self):
|
823
|
+
"""Removes the window from the app's window list."""
|
824
|
+
if self in self.app.windows:
|
825
|
+
self.app.windows.remove(self)
|
826
|
+
if not self.app.windows:
|
827
|
+
self.app.quit() # Quit the app if all windows are closed
|
828
|
+
|
829
|
+
###########################################################################################
|
830
|
+
# Window management (no ID required)
|
831
|
+
###########################################################################################
|
832
|
+
def hide(self):
|
833
|
+
"""
|
834
|
+
Hides the window.
|
835
|
+
|
836
|
+
Examples
|
837
|
+
--------
|
838
|
+
>>> app = Pyloid(app_name="Pyloid-App")
|
839
|
+
>>> window = app.create_window("pyloid-window")
|
840
|
+
>>> window.hide()
|
841
|
+
"""
|
842
|
+
self._window.hide()
|
843
|
+
|
844
|
+
def show(self):
|
845
|
+
"""
|
846
|
+
Shows the window.
|
847
|
+
|
848
|
+
Examples
|
849
|
+
--------
|
850
|
+
>>> app = Pyloid(app_name="Pyloid-App")
|
851
|
+
>>> window = app.create_window("pyloid-window")
|
852
|
+
>>> window.show()
|
853
|
+
"""
|
854
|
+
self._window.show()
|
855
|
+
|
856
|
+
def focus(self):
|
857
|
+
"""
|
858
|
+
Focuses the window.
|
859
|
+
|
860
|
+
Examples
|
861
|
+
--------
|
862
|
+
>>> app = Pyloid(app_name="Pyloid-App")
|
863
|
+
>>> window = app.create_window("pyloid-window")
|
864
|
+
>>> window.focus()
|
865
|
+
"""
|
866
|
+
self._window.activateWindow()
|
867
|
+
self._window.raise_()
|
868
|
+
self._window.setWindowState(
|
869
|
+
self._window.windowState() & ~Qt.WindowMinimized | Qt.WindowActive
|
870
|
+
)
|
871
|
+
|
872
|
+
def show_and_focus(self):
|
873
|
+
"""
|
874
|
+
Shows and focuses the window.
|
875
|
+
|
876
|
+
Examples
|
877
|
+
--------
|
878
|
+
>>> app = Pyloid(app_name="Pyloid-App")
|
879
|
+
>>> window = app.create_window("pyloid-window")
|
880
|
+
>>> window.show_and_focus()
|
881
|
+
"""
|
882
|
+
self._window.show()
|
883
|
+
self._window.activateWindow()
|
884
|
+
self._window.raise_()
|
885
|
+
self._window.setWindowState(
|
886
|
+
self._window.windowState() & ~Qt.WindowMinimized | Qt.WindowActive
|
887
|
+
)
|
888
|
+
|
889
|
+
def close(self):
|
890
|
+
"""
|
891
|
+
Closes the window.
|
892
|
+
|
893
|
+
Examples
|
894
|
+
--------
|
895
|
+
>>> app = Pyloid(app_name="Pyloid-App")
|
896
|
+
>>> window = app.create_window("pyloid-window")
|
897
|
+
>>> window.close()
|
898
|
+
"""
|
899
|
+
self._window.close()
|
900
|
+
|
901
|
+
def fullscreen(self):
|
902
|
+
"""
|
903
|
+
Enters fullscreen mode.
|
904
|
+
|
905
|
+
Examples
|
906
|
+
--------
|
907
|
+
>>> app = Pyloid(app_name="Pyloid-App")
|
908
|
+
>>> window = app.create_window("pyloid-window")
|
909
|
+
>>> window.fullscreen()
|
910
|
+
"""
|
911
|
+
self._window.showFullScreen()
|
912
|
+
|
913
|
+
def toggle_fullscreen(self):
|
914
|
+
"""
|
915
|
+
Toggles the fullscreen mode of the window.
|
916
|
+
|
917
|
+
Examples
|
918
|
+
--------
|
919
|
+
>>> app = Pyloid(app_name="Pyloid-App")
|
920
|
+
>>> window = app.create_window("pyloid-window")
|
921
|
+
>>> window.toggle_fullscreen()
|
922
|
+
"""
|
923
|
+
if self._window.isFullScreen():
|
924
|
+
self._window.showNormal()
|
925
|
+
else:
|
926
|
+
self._window.showFullScreen()
|
927
|
+
|
928
|
+
def minimize(self):
|
929
|
+
"""
|
930
|
+
Minimizes the window.
|
931
|
+
|
932
|
+
Examples
|
933
|
+
--------
|
934
|
+
>>> app = Pyloid(app_name="Pyloid-App")
|
935
|
+
>>> window = app.create_window("pyloid-window")
|
936
|
+
>>> window.minimize()
|
937
|
+
"""
|
938
|
+
self._window.showMinimized()
|
939
|
+
|
940
|
+
def maximize(self):
|
941
|
+
"""
|
942
|
+
Maximizes the window.
|
943
|
+
|
944
|
+
Examples
|
945
|
+
--------
|
946
|
+
>>> app = Pyloid(app_name="Pyloid-App")
|
947
|
+
>>> window = app.create_window("pyloid-window")
|
948
|
+
>>> window.maximize()
|
949
|
+
"""
|
950
|
+
self._window.showMaximized()
|
951
|
+
|
952
|
+
def unmaximize(self):
|
953
|
+
"""
|
954
|
+
Restores the window from maximized state.
|
955
|
+
|
956
|
+
Examples
|
957
|
+
--------
|
958
|
+
>>> app = Pyloid(app_name="Pyloid-App")
|
959
|
+
>>> window = app.create_window("pyloid-window")
|
960
|
+
>>> window.unmaximize()
|
961
|
+
"""
|
962
|
+
self._window.showNormal()
|
963
|
+
|
964
|
+
def toggle_maximize(self):
|
965
|
+
"""
|
966
|
+
Toggles the maximized state of the window.
|
967
|
+
|
968
|
+
Examples
|
969
|
+
--------
|
970
|
+
>>> app = Pyloid(app_name="Pyloid-App")
|
971
|
+
>>> window = app.create_window("pyloid-window")
|
972
|
+
>>> window.toggle_maximize()
|
973
|
+
"""
|
974
|
+
if self._window.isMaximized():
|
975
|
+
self._window.showNormal()
|
976
|
+
else:
|
977
|
+
self._window.showMaximized()
|
978
|
+
|
979
|
+
def is_fullscreen(self) -> bool:
|
980
|
+
"""
|
981
|
+
Returns True if the window is fullscreen.
|
982
|
+
|
983
|
+
Examples
|
984
|
+
--------
|
985
|
+
>>> app = Pyloid(app_name="Pyloid-App")
|
986
|
+
>>> window = app.create_window("pyloid-window")
|
987
|
+
>>> window.is_fullscreen()
|
988
|
+
"""
|
989
|
+
return self._window.isFullScreen()
|
990
|
+
|
991
|
+
def is_maximized(self) -> bool:
|
992
|
+
"""
|
993
|
+
Returns True if the window is maximized.
|
994
|
+
|
995
|
+
Examples
|
996
|
+
--------
|
997
|
+
>>> app = Pyloid(app_name="Pyloid-App")
|
998
|
+
>>> window = app.create_window("pyloid-window")
|
999
|
+
>>> window.is_maximized()
|
1000
|
+
"""
|
1001
|
+
return self._window.isMaximized()
|
1002
|
+
|
1003
|
+
def capture(self, save_path: str) -> Optional[str]:
|
1004
|
+
"""
|
1005
|
+
Captures the current window.
|
1006
|
+
|
1007
|
+
Parameters
|
1008
|
+
----------
|
1009
|
+
save_path : str
|
1010
|
+
Path to save the captured image. If not specified, it will be saved in the current directory.
|
1011
|
+
|
1012
|
+
Returns
|
1013
|
+
-------
|
1014
|
+
Optional[str]
|
1015
|
+
Returns the path of the saved image.
|
1016
|
+
|
1017
|
+
Examples
|
1018
|
+
--------
|
1019
|
+
>>> app = Pyloid(app_name="Pyloid-App")
|
1020
|
+
>>> window = app.create_window("pyloid-window")
|
1021
|
+
>>> save_path = window.capture("screenshot.png")
|
1022
|
+
>>> print(f"Image saved at: {save_path}")
|
1023
|
+
Image saved at: screenshot.png
|
1024
|
+
"""
|
1025
|
+
try:
|
1026
|
+
# Capture the window
|
1027
|
+
screenshot = self._window.grab()
|
1028
|
+
|
1029
|
+
# Save the image
|
1030
|
+
screenshot.save(save_path)
|
1031
|
+
return save_path
|
1032
|
+
except Exception as e:
|
1033
|
+
print(f"An error occurred while capturing the window: {e}")
|
1034
|
+
return None
|
1035
|
+
|
1036
|
+
###########################################################################################
|
1037
|
+
# Shortcut
|
1038
|
+
###########################################################################################
|
1039
|
+
def add_shortcut(self, key_sequence: str, callback: Callable):
|
1040
|
+
"""
|
1041
|
+
Adds a keyboard shortcut to the window if it does not already exist.
|
1042
|
+
|
1043
|
+
Parameters
|
1044
|
+
----------
|
1045
|
+
key_sequence : str
|
1046
|
+
Shortcut sequence (e.g., "Ctrl+C")
|
1047
|
+
callback : Callable
|
1048
|
+
Function to be executed when the shortcut is pressed
|
1049
|
+
|
1050
|
+
Returns
|
1051
|
+
-------
|
1052
|
+
QShortcut or None
|
1053
|
+
Created QShortcut object or None if the shortcut already exists
|
1054
|
+
|
1055
|
+
Examples
|
1056
|
+
--------
|
1057
|
+
```python
|
1058
|
+
app = Pyloid(app_name="Pyloid-App")
|
1059
|
+
|
1060
|
+
window = app.create_window("pyloid-window")
|
1061
|
+
|
1062
|
+
def on_shortcut():
|
1063
|
+
print("Shortcut activated!")
|
1064
|
+
window.add_shortcut("Ctrl+C", on_shortcut)
|
1065
|
+
|
1066
|
+
app.run()
|
1067
|
+
```
|
1068
|
+
"""
|
1069
|
+
if key_sequence in self.shortcuts:
|
1070
|
+
# print(f"Shortcut {key_sequence} already exists.")
|
1071
|
+
return None
|
1072
|
+
|
1073
|
+
shortcut = QShortcut(QKeySequence(key_sequence), self._window)
|
1074
|
+
shortcut.activated.connect(callback)
|
1075
|
+
self.shortcuts[key_sequence] = shortcut
|
1076
|
+
return shortcut
|
1077
|
+
|
1078
|
+
def remove_shortcut(self, key_sequence: str):
|
1079
|
+
"""
|
1080
|
+
Removes a keyboard shortcut from the window.
|
1081
|
+
|
1082
|
+
Parameters
|
1083
|
+
----------
|
1084
|
+
key_sequence : str
|
1085
|
+
Shortcut sequence to be removed
|
1086
|
+
|
1087
|
+
Examples
|
1088
|
+
--------
|
1089
|
+
```python
|
1090
|
+
app = Pyloid(app_name="Pyloid-App")
|
1091
|
+
|
1092
|
+
window = app.create_window("pyloid-window")
|
1093
|
+
window.remove_shortcut("Ctrl+C")
|
1094
|
+
|
1095
|
+
app.run()
|
1096
|
+
```
|
1097
|
+
"""
|
1098
|
+
if key_sequence in self.shortcuts:
|
1099
|
+
shortcut = self.shortcuts.pop(key_sequence)
|
1100
|
+
shortcut.setEnabled(False)
|
1101
|
+
shortcut.deleteLater()
|
1102
|
+
|
1103
|
+
def get_all_shortcuts(self):
|
1104
|
+
"""
|
1105
|
+
Returns all registered shortcuts in the window.
|
1106
|
+
|
1107
|
+
Returns
|
1108
|
+
-------
|
1109
|
+
dict
|
1110
|
+
Dictionary of shortcut sequences and QShortcut objects
|
1111
|
+
|
1112
|
+
Examples
|
1113
|
+
--------
|
1114
|
+
```python
|
1115
|
+
app = Pyloid(app_name="Pyloid-App")
|
1116
|
+
|
1117
|
+
window = app.create_window("pyloid-window")
|
1118
|
+
shortcuts = window.get_all_shortcuts()
|
1119
|
+
print(shortcuts)
|
1120
|
+
|
1121
|
+
app.run()
|
1122
|
+
```
|
1123
|
+
"""
|
1124
|
+
return self.shortcuts
|
1125
|
+
|
1126
|
+
###########################################################################################
|
1127
|
+
# Event (Calling the JS from Python)
|
1128
|
+
###########################################################################################
|
1129
|
+
def emit(self, event_name, data: Optional[Dict] = None):
|
1130
|
+
"""
|
1131
|
+
Emits an event to the JavaScript side.
|
1132
|
+
|
1133
|
+
Parameters
|
1134
|
+
----------
|
1135
|
+
event_name : str
|
1136
|
+
Name of the event
|
1137
|
+
data : dict, optional
|
1138
|
+
Data to be sent with the event (default is None)
|
1139
|
+
|
1140
|
+
Examples
|
1141
|
+
--------
|
1142
|
+
(Python)
|
1143
|
+
```python
|
1144
|
+
app = Pyloid(app_name="Pyloid-App")
|
1145
|
+
|
1146
|
+
window = app.create_window("pyloid-window")
|
1147
|
+
window.emit("customEvent", {"message": "Hello, Pyloid!"})
|
1148
|
+
|
1149
|
+
app.run()
|
1150
|
+
```
|
1151
|
+
---
|
1152
|
+
|
1153
|
+
(JavaScript)
|
1154
|
+
```javascript
|
1155
|
+
document.addEventListener('customEvent', (data) => {
|
1156
|
+
console.log(data.message);
|
1157
|
+
});
|
1158
|
+
```
|
1159
|
+
"""
|
1160
|
+
script = f"""
|
1161
|
+
(function() {{
|
1162
|
+
const eventData = {json.dumps(data)};
|
1163
|
+
const customEvent = new CustomEvent('{event_name}', {{ detail: eventData }});
|
1164
|
+
document.dispatchEvent(customEvent);
|
1165
|
+
}})();
|
1166
|
+
"""
|
1167
|
+
self.web_view.page().runJavaScript(script)
|
1168
|
+
|
1169
|
+
###########################################################################################
|
1170
|
+
# Get Properties
|
1171
|
+
###########################################################################################
|
1172
|
+
def get_window_properties(self):
|
1173
|
+
"""
|
1174
|
+
Returns the properties of the window.
|
1175
|
+
|
1176
|
+
Returns
|
1177
|
+
-------
|
1178
|
+
dict
|
1179
|
+
Dictionary containing the properties of the window
|
1180
|
+
|
1181
|
+
Examples
|
1182
|
+
--------
|
1183
|
+
```python
|
1184
|
+
app = Pyloid(app_name="Pyloid-App")
|
1185
|
+
|
1186
|
+
window = app.create_window("pyloid-window")
|
1187
|
+
properties = window.get_window_properties()
|
1188
|
+
print(properties)
|
1189
|
+
|
1190
|
+
app.run()
|
1191
|
+
```
|
1192
|
+
"""
|
1193
|
+
return {
|
1194
|
+
"id": self.id,
|
1195
|
+
"title": self.title,
|
1196
|
+
"width": self.width,
|
1197
|
+
"height": self.height,
|
1198
|
+
"x": self.x,
|
1199
|
+
"y": self.y,
|
1200
|
+
"frame": self.frame,
|
1201
|
+
"context_menu": self.context_menu,
|
1202
|
+
"dev_tools": self.dev_tools,
|
1203
|
+
}
|
1204
|
+
|
1205
|
+
def get_id(self):
|
1206
|
+
"""
|
1207
|
+
Returns the ID of the window.
|
1208
|
+
|
1209
|
+
Returns
|
1210
|
+
-------
|
1211
|
+
str
|
1212
|
+
ID of the window
|
1213
|
+
|
1214
|
+
Examples
|
1215
|
+
--------
|
1216
|
+
```python
|
1217
|
+
app = Pyloid(app_name="Pyloid-App")
|
1218
|
+
|
1219
|
+
window = app.create_window("pyloid-window")
|
1220
|
+
window_id = window.get_id()
|
1221
|
+
print(window_id)
|
1222
|
+
|
1223
|
+
app.run()
|
1224
|
+
```
|
1225
|
+
"""
|
1226
|
+
return self.id
|
1227
|
+
|
1228
|
+
def get_size(self) -> Dict[str, int]:
|
1229
|
+
"""
|
1230
|
+
Returns the size of the window.
|
1231
|
+
|
1232
|
+
Returns
|
1233
|
+
-------
|
1234
|
+
dict
|
1235
|
+
Dictionary containing the width and height of the window
|
1236
|
+
|
1237
|
+
Examples
|
1238
|
+
--------
|
1239
|
+
```python
|
1240
|
+
app = Pyloid(app_name="Pyloid-App")
|
1241
|
+
|
1242
|
+
window = app.create_window("pyloid-window")
|
1243
|
+
size = window.get_size()
|
1244
|
+
print(size)
|
1245
|
+
|
1246
|
+
app.run()
|
1247
|
+
```
|
1248
|
+
"""
|
1249
|
+
return {"width": self.width, "height": self.height}
|
1250
|
+
|
1251
|
+
def get_position(self) -> Dict[str, int]:
|
1252
|
+
"""
|
1253
|
+
Returns the position of the window.
|
1254
|
+
|
1255
|
+
Returns
|
1256
|
+
-------
|
1257
|
+
dict
|
1258
|
+
Dictionary containing the x and y coordinates of the window
|
1259
|
+
|
1260
|
+
Examples
|
1261
|
+
--------
|
1262
|
+
```python
|
1263
|
+
app = Pyloid(app_name="Pyloid-App")
|
1264
|
+
|
1265
|
+
window = app.create_window("pyloid-window")
|
1266
|
+
position = window.get_position()
|
1267
|
+
print(position)
|
1268
|
+
|
1269
|
+
app.run()
|
1270
|
+
```
|
1271
|
+
"""
|
1272
|
+
return {"x": self.x, "y": self.y}
|
1273
|
+
|
1274
|
+
def get_title(self) -> str:
|
1275
|
+
"""
|
1276
|
+
Returns the title of the window.
|
1277
|
+
|
1278
|
+
Returns
|
1279
|
+
-------
|
1280
|
+
str
|
1281
|
+
Title of the window
|
1282
|
+
|
1283
|
+
Examples
|
1284
|
+
--------
|
1285
|
+
```python
|
1286
|
+
app = Pyloid(app_name="Pyloid-App")
|
1287
|
+
|
1288
|
+
window = app.create_window("pyloid-window")
|
1289
|
+
title = window.get_title()
|
1290
|
+
print(title)
|
1291
|
+
|
1292
|
+
app.run()
|
1293
|
+
```
|
1294
|
+
"""
|
1295
|
+
return self.title
|
1296
|
+
|
1297
|
+
def get_url(self) -> str:
|
1298
|
+
"""
|
1299
|
+
Returns the URL of the window.
|
1300
|
+
|
1301
|
+
Returns
|
1302
|
+
-------
|
1303
|
+
str
|
1304
|
+
URL of the window
|
1305
|
+
|
1306
|
+
Examples
|
1307
|
+
--------
|
1308
|
+
```python
|
1309
|
+
app = Pyloid(app_name="Pyloid-App")
|
1310
|
+
|
1311
|
+
window = app.create_window("pyloid-window")
|
1312
|
+
url = window.get_url()
|
1313
|
+
print(url)
|
1314
|
+
|
1315
|
+
app.run()
|
1316
|
+
```
|
1317
|
+
"""
|
1318
|
+
return self.web_view.url().toString()
|
1319
|
+
|
1320
|
+
def get_visible(self) -> bool:
|
1321
|
+
"""
|
1322
|
+
Returns the visibility of the window.
|
1323
|
+
|
1324
|
+
Returns
|
1325
|
+
-------
|
1326
|
+
bool
|
1327
|
+
True if the window is visible, False otherwise
|
1328
|
+
|
1329
|
+
Examples
|
1330
|
+
--------
|
1331
|
+
```python
|
1332
|
+
app = Pyloid(app_name="Pyloid-App")
|
1333
|
+
|
1334
|
+
window = app.create_window("pyloid-window")
|
1335
|
+
visible = window.get_visible()
|
1336
|
+
print(visible)
|
1337
|
+
|
1338
|
+
app.run()
|
1339
|
+
```
|
1340
|
+
"""
|
1341
|
+
return self._window.isVisible()
|
1342
|
+
|
1343
|
+
def get_frame(self) -> bool:
|
1344
|
+
"""
|
1345
|
+
Returns the frame enabled state of the window.
|
1346
|
+
|
1347
|
+
Returns
|
1348
|
+
-------
|
1349
|
+
bool
|
1350
|
+
True if the frame is enabled, False otherwise
|
1351
|
+
|
1352
|
+
Examples
|
1353
|
+
--------
|
1354
|
+
```python
|
1355
|
+
app = Pyloid(app_name="Pyloid-App")
|
1356
|
+
|
1357
|
+
window = app.create_window("pyloid-window")
|
1358
|
+
frame = window.get_frame()
|
1359
|
+
print(frame)
|
1360
|
+
|
1361
|
+
app.run()
|
1362
|
+
```
|
1363
|
+
"""
|
1364
|
+
return self.frame
|
1365
|
+
|
1366
|
+
###########################################################################################
|
1367
|
+
# Resize
|
1368
|
+
###########################################################################################
|
1369
|
+
def set_resizable(self, resizable: bool):
|
1370
|
+
"""
|
1371
|
+
Sets the resizability of the window.
|
1372
|
+
|
1373
|
+
Parameters
|
1374
|
+
----------
|
1375
|
+
resizable : bool
|
1376
|
+
True to make the window resizable, False to make it fixed size
|
1377
|
+
|
1378
|
+
Examples
|
1379
|
+
--------
|
1380
|
+
```python
|
1381
|
+
app = Pyloid(app_name="Pyloid-App")
|
1382
|
+
|
1383
|
+
window = app.create_window("pyloid-window")
|
1384
|
+
window.set_resizable(True)
|
1385
|
+
|
1386
|
+
app.run()
|
1387
|
+
```
|
1388
|
+
"""
|
1389
|
+
self.resizable = resizable
|
1390
|
+
if self.frame:
|
1391
|
+
flags = self._window.windowFlags() | Qt.WindowCloseButtonHint
|
1392
|
+
if resizable:
|
1393
|
+
pass
|
1394
|
+
else:
|
1395
|
+
flags |= Qt.MSWindowsFixedSizeDialogHint
|
1396
|
+
self._window.setWindowFlags(flags)
|
1397
|
+
else:
|
1398
|
+
# 프레임이 없는 경우 커스텀 리사이징 로직을 설정합니다.
|
1399
|
+
self.web_view.is_resizing_enabled = resizable
|
1400
|
+
|
1401
|
+
self._window.show() # 변경사항을 적용하기 위해 창을 다시 표시합니다.
|
1402
|
+
|
1403
|
+
def set_minimum_size(self, min_width: int, min_height: int):
|
1404
|
+
"""
|
1405
|
+
Sets the minimum size of the window.
|
1406
|
+
|
1407
|
+
Parameters
|
1408
|
+
----------
|
1409
|
+
min_width : int
|
1410
|
+
Minimum width of the window
|
1411
|
+
min_height : int
|
1412
|
+
Minimum height of the window
|
1413
|
+
|
1414
|
+
Examples
|
1415
|
+
--------
|
1416
|
+
```python
|
1417
|
+
app = Pyloid(app_name="Pyloid-App")
|
1418
|
+
|
1419
|
+
window = app.create_window("pyloid-window")
|
1420
|
+
window.set_minimum_size(400, 300)
|
1421
|
+
|
1422
|
+
app.run()
|
1423
|
+
```
|
1424
|
+
"""
|
1425
|
+
self._window.setMinimumSize(min_width, min_height)
|
1426
|
+
|
1427
|
+
def set_maximum_size(self, max_width: int, max_height: int):
|
1428
|
+
"""
|
1429
|
+
Sets the maximum size of the window.
|
1430
|
+
|
1431
|
+
Parameters
|
1432
|
+
----------
|
1433
|
+
max_width : int
|
1434
|
+
Maximum width of the window
|
1435
|
+
max_height : int
|
1436
|
+
Maximum height of the window
|
1437
|
+
|
1438
|
+
Examples
|
1439
|
+
--------
|
1440
|
+
```python
|
1441
|
+
app = Pyloid(app_name="Pyloid-App")
|
1442
|
+
|
1443
|
+
window = app.create_window("pyloid-window")
|
1444
|
+
window.set_maximum_size(1024, 768)
|
1445
|
+
|
1446
|
+
app.run()
|
1447
|
+
```
|
1448
|
+
"""
|
1449
|
+
self._window.setMaximumSize(max_width, max_height)
|
1450
|
+
|
1451
|
+
def get_minimum_size(self) -> Dict[str, int]:
|
1452
|
+
"""
|
1453
|
+
Returns the minimum size of the window.
|
1454
|
+
|
1455
|
+
Returns
|
1456
|
+
-------
|
1457
|
+
dict
|
1458
|
+
Dictionary containing the minimum width and height of the window
|
1459
|
+
|
1460
|
+
Examples
|
1461
|
+
--------
|
1462
|
+
```python
|
1463
|
+
app = Pyloid(app_name="Pyloid-App")
|
1464
|
+
|
1465
|
+
window = app.create_window("pyloid-window")
|
1466
|
+
min_size = window.get_minimum_size()
|
1467
|
+
print(min_size)
|
1468
|
+
|
1469
|
+
app.run()
|
1470
|
+
```
|
1471
|
+
"""
|
1472
|
+
return {
|
1473
|
+
"width": self._window.minimumWidth(),
|
1474
|
+
"height": self._window.minimumHeight(),
|
1475
|
+
}
|
1476
|
+
|
1477
|
+
def get_maximum_size(self) -> Dict[str, int]:
|
1478
|
+
"""
|
1479
|
+
Returns the maximum size of the window.
|
1480
|
+
|
1481
|
+
Returns
|
1482
|
+
-------
|
1483
|
+
dict
|
1484
|
+
Dictionary containing the maximum width and height of the window
|
1485
|
+
|
1486
|
+
Examples
|
1487
|
+
--------
|
1488
|
+
```python
|
1489
|
+
app = Pyloid(app_name="Pyloid-App")
|
1490
|
+
|
1491
|
+
window = app.create_window("pyloid-window")
|
1492
|
+
max_size = window.get_maximum_size()
|
1493
|
+
print(max_size)
|
1494
|
+
|
1495
|
+
app.run()
|
1496
|
+
```
|
1497
|
+
"""
|
1498
|
+
return {
|
1499
|
+
"width": self._window.maximumWidth(),
|
1500
|
+
"height": self._window.maximumHeight(),
|
1501
|
+
}
|
1502
|
+
|
1503
|
+
def get_resizable(self) -> bool:
|
1504
|
+
"""
|
1505
|
+
Returns the resizability of the window.
|
1506
|
+
|
1507
|
+
Returns
|
1508
|
+
-------
|
1509
|
+
bool
|
1510
|
+
True if the window is resizable, False otherwise
|
1511
|
+
|
1512
|
+
Examples
|
1513
|
+
--------
|
1514
|
+
```python
|
1515
|
+
app = Pyloid(app_name="Pyloid-App")
|
1516
|
+
|
1517
|
+
window = app.create_window("pyloid-window")
|
1518
|
+
resizable = window.get_resizable()
|
1519
|
+
print(resizable)
|
1520
|
+
|
1521
|
+
app.run()
|
1522
|
+
```
|
1523
|
+
"""
|
1524
|
+
return self.resizable
|
1525
|
+
|
1526
|
+
###########################################################################################
|
1527
|
+
# For Custom Pyside6 Features
|
1528
|
+
###########################################################################################
|
1529
|
+
def get_QMainWindow(self) -> QMainWindow:
|
1530
|
+
"""
|
1531
|
+
Returns the QMainWindow object of the window.
|
1532
|
+
|
1533
|
+
you can use all the features of QMainWindow for customizing the window.
|
1534
|
+
|
1535
|
+
Returns
|
1536
|
+
-------
|
1537
|
+
QMainWindow
|
1538
|
+
QMainWindow object of the window
|
1539
|
+
|
1540
|
+
Examples
|
1541
|
+
--------
|
1542
|
+
```python
|
1543
|
+
from PySide6.QtCore import Qt
|
1544
|
+
from pyloid import Pyloid
|
1545
|
+
|
1546
|
+
app = Pyloid(app_name="Pyloid-App")
|
1547
|
+
|
1548
|
+
window = app.create_window("pyloid-window")
|
1549
|
+
qmain = window.get_QMainWindow()
|
1550
|
+
|
1551
|
+
qmain.setWindowFlags(qmain.windowFlags() | Qt.WindowStaysOnTopHint) # window stays on top
|
1552
|
+
```
|
1553
|
+
"""
|
1554
|
+
return self._window
|
1555
|
+
|
1556
|
+
def get_QWebEngineView(self) -> CustomWebEngineView:
|
1557
|
+
"""
|
1558
|
+
Returns the CustomWebEngineView object which inherits from QWebEngineView.
|
1559
|
+
|
1560
|
+
Returns
|
1561
|
+
-------
|
1562
|
+
CustomWebEngineView
|
1563
|
+
CustomWebEngineView object of the window
|
1564
|
+
|
1565
|
+
Examples
|
1566
|
+
--------
|
1567
|
+
```python
|
1568
|
+
window = app.create_window("pyloid-window")
|
1569
|
+
web_view = window.get_QWebEngineView()
|
1570
|
+
|
1571
|
+
web_view.page().runJavaScript("console.log('Hello, Pyloid!')")
|
1572
|
+
```
|
1573
|
+
"""
|
1574
|
+
return self.web_view
|
1575
|
+
|
1576
|
+
###########################################################################################
|
1577
|
+
# QMainWindow flags
|
1578
|
+
###########################################################################################
|
1579
|
+
def set_window_stay_on_top(self, on_top: bool):
|
1580
|
+
"""
|
1581
|
+
Sets the window stay on top flag of the window.
|
1582
|
+
|
1583
|
+
Parameters
|
1584
|
+
----------
|
1585
|
+
on_top : bool
|
1586
|
+
True to keep the window on top, False otherwise
|
1587
|
+
|
1588
|
+
Examples
|
1589
|
+
--------
|
1590
|
+
```python
|
1591
|
+
window.set_window_stay_on_top(True)
|
1592
|
+
window.set_window_stay_on_top(False)
|
1593
|
+
```
|
1594
|
+
"""
|
1595
|
+
flags = self._window.windowFlags()
|
1596
|
+
if on_top:
|
1597
|
+
flags |= Qt.WindowStaysOnTopHint
|
1598
|
+
else:
|
1599
|
+
flags &= ~Qt.WindowStaysOnTopHint
|
1600
|
+
|
1601
|
+
# Maintain existing flags while only changing WindowStaysOnTopHint
|
1602
|
+
# Explicitly add the close button
|
1603
|
+
flags |= Qt.WindowCloseButtonHint
|
1604
|
+
|
1605
|
+
self._window.setWindowFlags(flags)
|
1606
|
+
|
1607
|
+
# Show the window again to apply the changes
|
1608
|
+
self._window.show()
|
1609
|
+
|
1610
|
+
def set_window_stay_on_bottom(self, on_bottom: bool):
|
1611
|
+
"""
|
1612
|
+
Sets the window stay on bottom flag of the window.
|
1613
|
+
|
1614
|
+
Parameters
|
1615
|
+
----------
|
1616
|
+
on_bottom : bool
|
1617
|
+
True to keep the window on bottom, False otherwise
|
1618
|
+
|
1619
|
+
Examples
|
1620
|
+
--------
|
1621
|
+
```python
|
1622
|
+
window.set_window_stay_on_bottom(True)
|
1623
|
+
window.set_window_stay_on_bottom(False)
|
1624
|
+
```
|
1625
|
+
"""
|
1626
|
+
flags = self._window.windowFlags()
|
1627
|
+
if on_bottom:
|
1628
|
+
flags |= Qt.WindowStaysOnBottomHint
|
1629
|
+
else:
|
1630
|
+
flags &= ~Qt.WindowStaysOnBottomHint
|
1631
|
+
|
1632
|
+
# Maintain existing flags while only changing WindowStaysOnBottomHint
|
1633
|
+
# Explicitly add the close button
|
1634
|
+
flags |= Qt.WindowCloseButtonHint
|
1635
|
+
|
1636
|
+
self._window.setWindowFlags(flags)
|
1637
|
+
|
1638
|
+
# Show the window again to apply the changes
|
1639
|
+
self._window.show()
|
1640
|
+
|
1641
|
+
###########################################################################################
|
1642
|
+
# Splash Screen
|
1643
|
+
###########################################################################################
|
1644
|
+
def set_static_image_splash_screen(
|
1645
|
+
self,
|
1646
|
+
image_path: str,
|
1647
|
+
close_on_load: bool = True,
|
1648
|
+
stay_on_top: bool = True,
|
1649
|
+
clickable: bool = True,
|
1650
|
+
position: str = "center",
|
1651
|
+
):
|
1652
|
+
"""
|
1653
|
+
Sets the static image splash screen of the window.
|
1654
|
+
|
1655
|
+
Parameters
|
1656
|
+
----------
|
1657
|
+
image_path : str
|
1658
|
+
Path to the image file
|
1659
|
+
close_on_load : bool, optional
|
1660
|
+
True to close the splash screen when the page is loaded, False otherwise (default is True)
|
1661
|
+
stay_on_top : bool, optional
|
1662
|
+
True to keep the splash screen on top, False otherwise (default is True)
|
1663
|
+
clickable : bool, optional
|
1664
|
+
True to make the splash screen clickable, False otherwise (default is True)
|
1665
|
+
if clickable is True, you can click the splash screen to close it.
|
1666
|
+
position : str, optional
|
1667
|
+
Position of the splash screen. Options are 'center', 'top-left', 'top-right', 'bottom-left', 'bottom-right' (default is 'center')
|
1668
|
+
|
1669
|
+
Examples
|
1670
|
+
--------
|
1671
|
+
```python
|
1672
|
+
window.set_image_splash_screen("./assets/loading.png", close_on_load=True, stay_on_top=True)
|
1673
|
+
```
|
1674
|
+
"""
|
1675
|
+
pixmap = QPixmap(image_path)
|
1676
|
+
|
1677
|
+
if not clickable:
|
1678
|
+
|
1679
|
+
class NonClickableSplashScreen(QSplashScreen):
|
1680
|
+
def mousePressEvent(self, event):
|
1681
|
+
pass # Ignore click events
|
1682
|
+
|
1683
|
+
splash = NonClickableSplashScreen(
|
1684
|
+
pixmap, Qt.WindowStaysOnTopHint if stay_on_top else Qt.WindowType(0)
|
1685
|
+
)
|
1686
|
+
else:
|
1687
|
+
splash = QSplashScreen(
|
1688
|
+
pixmap, Qt.WindowStaysOnTopHint if stay_on_top else Qt.WindowType(0)
|
1689
|
+
)
|
1690
|
+
|
1691
|
+
self.close_on_load = close_on_load
|
1692
|
+
self.splash_screen = splash
|
1693
|
+
self._position_splash_screen(position)
|
1694
|
+
self.splash_screen.show()
|
1695
|
+
|
1696
|
+
def set_gif_splash_screen(
|
1697
|
+
self,
|
1698
|
+
gif_path: str,
|
1699
|
+
close_on_load: bool = True,
|
1700
|
+
stay_on_top: bool = True,
|
1701
|
+
clickable: bool = True,
|
1702
|
+
position: str = "center",
|
1703
|
+
):
|
1704
|
+
"""
|
1705
|
+
Sets the gif splash screen of the window.
|
1706
|
+
|
1707
|
+
Parameters
|
1708
|
+
----------
|
1709
|
+
gif_path : str
|
1710
|
+
Path to the gif file
|
1711
|
+
close_on_load : bool, optional
|
1712
|
+
True to close the splash screen when the page is loaded, False otherwise (default is True)
|
1713
|
+
stay_on_top : bool, optional
|
1714
|
+
True to keep the splash screen on top, False otherwise (default is True)
|
1715
|
+
clickable : bool, optional
|
1716
|
+
True to make the splash screen clickable, False otherwise (default is True)
|
1717
|
+
if clickable is True, you can click the splash screen to close it.
|
1718
|
+
position : str, optional
|
1719
|
+
Position of the splash screen. Options are 'center', 'top-left', 'top-right', 'bottom-left', 'bottom-right' (default is 'center')
|
1720
|
+
|
1721
|
+
Examples
|
1722
|
+
--------
|
1723
|
+
```python
|
1724
|
+
window.set_gif_splash_screen("./assets/loading.gif", close_on_load=True, stay_on_top=True)
|
1725
|
+
```
|
1726
|
+
"""
|
1727
|
+
|
1728
|
+
if not clickable:
|
1729
|
+
|
1730
|
+
class NonClickableSplashScreen(QSplashScreen):
|
1731
|
+
def mousePressEvent(self, event):
|
1732
|
+
pass # Ignore click events
|
1733
|
+
|
1734
|
+
# Create splash screen (using animated GIF)
|
1735
|
+
splash = NonClickableSplashScreen(
|
1736
|
+
QPixmap(1, 1),
|
1737
|
+
Qt.WindowStaysOnTopHint if stay_on_top else Qt.WindowType(0),
|
1738
|
+
) # Start with 1x1 transparent pixmap
|
1739
|
+
else:
|
1740
|
+
splash = QSplashScreen(
|
1741
|
+
QPixmap(1, 1),
|
1742
|
+
Qt.WindowStaysOnTopHint if stay_on_top else Qt.WindowType(0),
|
1743
|
+
)
|
1744
|
+
|
1745
|
+
splash.setAttribute(Qt.WA_TranslucentBackground)
|
1746
|
+
|
1747
|
+
# Create QLabel for GIF animation
|
1748
|
+
label = QLabel(splash)
|
1749
|
+
movie = QMovie(gif_path)
|
1750
|
+
label.setMovie(movie)
|
1751
|
+
|
1752
|
+
# Adjust splash screen size to match GIF size
|
1753
|
+
movie.frameChanged.connect(
|
1754
|
+
lambda: splash.setFixedSize(movie.currentPixmap().size())
|
1755
|
+
)
|
1756
|
+
|
1757
|
+
# Start animation and show splash screen
|
1758
|
+
movie.start()
|
1759
|
+
self.close_on_load = close_on_load
|
1760
|
+
self.splash_screen = splash
|
1761
|
+
self._position_splash_screen(position)
|
1762
|
+
splash.show()
|
1763
|
+
|
1764
|
+
def _position_splash_screen(self, position: str):
|
1765
|
+
if not self.splash_screen:
|
1766
|
+
return
|
1767
|
+
|
1768
|
+
screen = self.app.primaryScreen().geometry()
|
1769
|
+
splash_size = self.splash_screen.size()
|
1770
|
+
|
1771
|
+
if position == "center":
|
1772
|
+
new_position = screen.center() - QPoint(
|
1773
|
+
splash_size.width() // 2, splash_size.height() // 2
|
1774
|
+
)
|
1775
|
+
elif position == "top-left":
|
1776
|
+
new_position = screen.topLeft()
|
1777
|
+
elif position == "top-right":
|
1778
|
+
new_position = screen.topRight() - QPoint(splash_size.width(), 0)
|
1779
|
+
elif position == "bottom-left":
|
1780
|
+
new_position = screen.bottomLeft() - QPoint(0, splash_size.height())
|
1781
|
+
elif position == "bottom-right":
|
1782
|
+
new_position = screen.bottomRight() - QPoint(
|
1783
|
+
splash_size.width(), splash_size.height()
|
1784
|
+
)
|
1785
|
+
else:
|
1786
|
+
new_position = screen.center() - QPoint(
|
1787
|
+
splash_size.width() // 2, splash_size.height() // 2
|
1788
|
+
)
|
1789
|
+
|
1790
|
+
self.splash_screen.move(new_position)
|
1791
|
+
|
1792
|
+
def close_splash_screen(self):
|
1793
|
+
"""
|
1794
|
+
Closes the splash screen if it exists.
|
1795
|
+
|
1796
|
+
Examples
|
1797
|
+
--------
|
1798
|
+
```python
|
1799
|
+
window.close_splash_screen()
|
1800
|
+
```
|
1801
|
+
"""
|
1802
|
+
if hasattr(self, "splash_screen") and self.splash_screen:
|
1803
|
+
self.splash_screen.close()
|
1804
|
+
self.close_on_load = None
|
1805
|
+
self.splash_screen = None
|
1806
|
+
|
1807
|
+
###########################################################################################
|
1808
|
+
# WebEngineView Attribute setting
|
1809
|
+
###########################################################################################
|
1810
|
+
def set_web_engine_view_attribute(self, attribute: QWebEngineSettings, on: bool):
|
1811
|
+
"""
|
1812
|
+
Sets the attribute of the WebEngineView.
|
1813
|
+
|
1814
|
+
Parameters
|
1815
|
+
----------
|
1816
|
+
attribute : QWebEngineSettings
|
1817
|
+
Attribute to set
|
1818
|
+
on : bool
|
1819
|
+
True to enable the attribute, False to disable it
|
1820
|
+
|
1821
|
+
Examples
|
1822
|
+
--------
|
1823
|
+
```python
|
1824
|
+
window.set_web_engine_view_attribute(QWebEngineSettings.WebAttribute.ScreenCaptureEnabled, False)
|
1825
|
+
```
|
1826
|
+
"""
|
1827
|
+
settings = self.web_view.settings()
|
1828
|
+
settings.setAttribute(attribute, on)
|
1829
|
+
|
1830
|
+
def is_web_engine_view_attribute(self, attribute: QWebEngineSettings) -> bool:
|
1831
|
+
"""
|
1832
|
+
Returns the attribute of the WebEngineView.
|
1833
|
+
|
1834
|
+
Parameters
|
1835
|
+
----------
|
1836
|
+
attribute : QWebEngineSettings
|
1837
|
+
Attribute to get
|
1838
|
+
|
1839
|
+
Returns
|
1840
|
+
-------
|
1841
|
+
bool
|
1842
|
+
True if the attribute is enabled, False otherwise
|
1843
|
+
|
1844
|
+
Examples
|
1845
|
+
--------
|
1846
|
+
```python
|
1847
|
+
window.is_web_engine_view_attribute(QWebEngineSettings.WebAttribute.ScreenCaptureEnabled)
|
1848
|
+
```
|
1849
|
+
"""
|
1850
|
+
settings = self.web_view.settings()
|
1851
|
+
return settings.testAttribute(attribute)
|
1852
|
+
|
1853
|
+
def set_permission_handler(self, feature: QWebEnginePage.Feature, handler):
|
1854
|
+
"""
|
1855
|
+
Sets a handler for a specific permission.
|
1856
|
+
|
1857
|
+
Parameters
|
1858
|
+
----------
|
1859
|
+
feature : QWebEnginePage.Feature
|
1860
|
+
The type of permission to set
|
1861
|
+
handler : callable
|
1862
|
+
The handler function to process the permission request
|
1863
|
+
|
1864
|
+
Examples
|
1865
|
+
--------
|
1866
|
+
```python
|
1867
|
+
def handle_camera(origin, feature):
|
1868
|
+
window.web_view.page().setFeaturePermission(
|
1869
|
+
origin,
|
1870
|
+
feature,
|
1871
|
+
QWebEnginePage.PermissionPolicy.PermissionGrantedByUser
|
1872
|
+
)
|
1873
|
+
|
1874
|
+
window.set_permission_handler(
|
1875
|
+
QWebEnginePage.Feature.MediaVideoCapture,
|
1876
|
+
handle_camera
|
1877
|
+
)
|
1878
|
+
```
|
1879
|
+
"""
|
1880
|
+
self.web_view.custom_page.setPermissionHandler(feature, handler)
|
1881
|
+
|
1882
|
+
def grant_permission(self, feature: QWebEnginePage.Feature):
|
1883
|
+
"""
|
1884
|
+
Automatically grants a specific permission when a request is made.
|
1885
|
+
|
1886
|
+
Parameters
|
1887
|
+
----------
|
1888
|
+
feature : QWebEnginePage.Feature
|
1889
|
+
The type of permission to automatically grant
|
1890
|
+
|
1891
|
+
Examples
|
1892
|
+
--------
|
1893
|
+
```python
|
1894
|
+
window.grant_permission(QWebEnginePage.Feature.MediaVideoCapture)
|
1895
|
+
```
|
1896
|
+
"""
|
1897
|
+
|
1898
|
+
def auto_grant(origin, feat):
|
1899
|
+
self.web_view.page().setFeaturePermission(
|
1900
|
+
origin, feat, QWebEnginePage.PermissionPolicy.PermissionGrantedByUser
|
1901
|
+
)
|
1902
|
+
|
1903
|
+
self.set_permission_handler(feature, auto_grant)
|
1904
|
+
|
1905
|
+
def deny_permission(self, feature: QWebEnginePage.Feature):
|
1906
|
+
"""
|
1907
|
+
Automatically denies a specific permission when a request is made.
|
1908
|
+
|
1909
|
+
Parameters
|
1910
|
+
----------
|
1911
|
+
feature : QWebEnginePage.Feature
|
1912
|
+
The type of permission to automatically deny
|
1913
|
+
|
1914
|
+
Examples
|
1915
|
+
--------
|
1916
|
+
```python
|
1917
|
+
window.deny_permission(QWebEnginePage.Feature.Notifications)
|
1918
|
+
```
|
1919
|
+
"""
|
1920
|
+
|
1921
|
+
def auto_deny(origin, feat):
|
1922
|
+
self.web_view.page().setFeaturePermission(
|
1923
|
+
origin, feat, QWebEnginePage.PermissionPolicy.PermissionDeniedByUser
|
1924
|
+
)
|
1925
|
+
|
1926
|
+
self.set_permission_handler(feature, auto_deny)
|
1927
|
+
|
1928
|
+
def set_desktop_media_handler(self, handler):
|
1929
|
+
"""
|
1930
|
+
데스크톱 미디어(화면/윈도우) 선택 핸들러를 설정합니다.
|
1931
|
+
|
1932
|
+
Parameters
|
1933
|
+
----------
|
1934
|
+
handler : callable
|
1935
|
+
요청을 처리할 핸들러 함수. QWebEngineDesktopMediaRequest 인자로 받습니다.
|
1936
|
+
|
1937
|
+
Examples
|
1938
|
+
--------
|
1939
|
+
```python
|
1940
|
+
def custom_media_handler(request):
|
1941
|
+
# 사용 가능한 화면 목록 출력
|
1942
|
+
for screen in request.screenList():
|
1943
|
+
print(f"Screen: {screen.name}")
|
1944
|
+
|
1945
|
+
# 사용 가능한 윈도우 목록 출력
|
1946
|
+
for window in request.windowList():
|
1947
|
+
print(f"Window: {window.name}")
|
1948
|
+
|
1949
|
+
# 첫 번째 화면 선택
|
1950
|
+
if request.screenList():
|
1951
|
+
request.selectScreen(request.screenList()[0])
|
1952
|
+
|
1953
|
+
window.set_desktop_media_handler(custom_media_handler)
|
1954
|
+
```
|
1955
|
+
"""
|
1956
|
+
self.web_view.custom_page.setDesktopMediaHandler(handler)
|