1
1
from typing import List , Union
2
2
3
- from qtpy .QtWidgets import QTextEdit , QScrollArea , QGraphicsDropShadowEffect , QVBoxLayout , QApplication , QHBoxLayout , QSizePolicy
4
- from qtpy .QtCore import Signal , Qt , QMimeData , QEvent , QPoint
5
- from qtpy .QtGui import QColor , QFocusEvent , QInputMethodEvent , QDragEnterEvent , QDragMoveEvent , QDropEvent , QKeyEvent , QTextCursor , QMouseEvent , QDrag , QPixmap , QKeySequence
3
+ from qtpy .QtWidgets import QStackedWidget , QSizePolicy , QTextEdit , QScrollArea , QGraphicsDropShadowEffect , QVBoxLayout , QApplication , QHBoxLayout , QSizePolicy , QLabel , QLineEdit
4
+ from qtpy .QtCore import Signal , Qt , QMimeData , QEvent , QPoint , QSize
5
+ from qtpy .QtGui import QIntValidator , QColor , QFocusEvent , QInputMethodEvent , QDragEnterEvent , QDragMoveEvent , QDropEvent , QKeyEvent , QTextCursor , QMouseEvent , QDrag , QPixmap , QKeySequence
6
6
import keyboard
7
7
import webbrowser
8
8
import numpy as np
9
9
10
10
from .stylewidgets import Widget , SeparatorWidget , ClickableLabel , IgnoreMouseLabel , ScrollBar
11
11
from .textitem import TextBlock
12
12
from utils .config import pcfg
13
+ from utils .logger import logger as LOGGER
13
14
14
15
15
16
STYLE_TRANSPAIR_CHECKED = "background-color: rgba(30, 147, 229, 20%);"
@@ -303,20 +304,106 @@ def setPlainTextAndKeepUndoStack(self, text: str):
303
304
class TransTextEdit (SourceTextEdit ):
304
305
pass
305
306
307
+ class RowIndexEditor (QLineEdit ):
306
308
309
+ focus_out = Signal ()
310
+
311
+ def __init__ (self , parent = None ):
312
+ super ().__init__ (parent = parent )
313
+ self .setValidator (QIntValidator ())
314
+ self .setReadOnly (True )
315
+ self .setTextMargins (0 , 0 , 0 , 0 )
316
+
317
+ def focusOutEvent (self , e : QFocusEvent ) -> None :
318
+ super ().focusOutEvent (e )
319
+ self .focus_out .emit ()
320
+
321
+ def sizeHint (self ):
322
+ size = super ().sizeHint ()
323
+ return QSize (1 , size .height ())
324
+
325
+
326
+ class RowIndexLabel (QStackedWidget ):
327
+
328
+ submmit_idx = Signal (int )
329
+
330
+ def __init__ (self , text : str = None , parent = None ):
331
+ super ().__init__ (parent = parent )
332
+ self .lineedit = RowIndexEditor (parent = self )
333
+ self .lineedit .focus_out .connect (self .on_lineedit_focusout )
334
+
335
+ self .show_label = QLabel (self )
336
+ self .text = self .show_label .text
337
+
338
+ self .addWidget (self .show_label )
339
+ self .addWidget (self .lineedit )
340
+ self .setCurrentIndex (0 )
341
+
342
+ if text is not None :
343
+ self .setText (text )
344
+ self .setSizePolicy (QSizePolicy .Policy .Maximum , QSizePolicy .Policy .Maximum )
345
+
346
+ def setText (self , text ):
347
+ if isinstance (text , int ):
348
+ text = str (text )
349
+ self .show_label .setText (text )
350
+ self .lineedit .setText (text )
351
+
352
+ def keyPressEvent (self , e : QKeyEvent ) -> None :
353
+ super ().keyPressEvent (e )
307
354
355
+ key = e .key ()
356
+ if key == Qt .Key .Key_Return :
357
+ self .try_update_idx ()
358
+
359
+ def try_update_idx (self ):
360
+ idx_str = self .lineedit .text ().strip ()
361
+ if not idx_str :
362
+ return
363
+ if self .text () == idx_str :
364
+ return
365
+ try :
366
+ idx = int (idx_str )
367
+ self .lineedit .setReadOnly (True )
368
+ self .submmit_idx .emit (idx )
369
+
370
+ except Exception as e :
371
+ LOGGER .warning (f'Invalid index str: { idx } ' )
372
+
373
+ def mouseDoubleClickEvent (self , e : QMouseEvent ) -> None :
374
+ self .startEdit ()
375
+ return super ().mouseDoubleClickEvent (e )
376
+
377
+ def startEdit (self ) -> None :
378
+ self .setCurrentIndex (1 )
379
+ self .lineedit .setReadOnly (False )
380
+ self .lineedit .setFocus ()
381
+
382
+ def on_lineedit_focusout (self ):
383
+ edited = not self .lineedit .isReadOnly ()
384
+ self .lineedit .setReadOnly (True )
385
+ self .setCurrentIndex (0 )
386
+ if edited :
387
+ self .try_update_idx ()
388
+
389
+ def mousePressEvent (self , e : QMouseEvent ) -> None :
390
+ e .ignore ()
391
+ return super ().mousePressEvent (e )
392
+
308
393
309
394
class TransPairWidget (Widget ):
310
395
311
396
check_state_changed = Signal (object , bool , bool )
312
397
drag_move = Signal (int )
398
+ idx_edited = Signal (int , int )
313
399
314
400
def __init__ (self , textblock : TextBlock = None , idx : int = None , fold : bool = False , * args , ** kwargs ) -> None :
315
401
super ().__init__ (* args , ** kwargs )
316
402
self .e_source = SourceTextEdit (idx , self , fold )
317
403
self .e_trans = TransTextEdit (idx , self , fold )
318
- self .idx_label = IgnoreMouseLabel ( self )
404
+ self .idx_label = RowIndexLabel ( idx , self )
319
405
self .idx_label .setText (str (idx + 1 ).zfill (2 )) # showed index start from 1!
406
+ self .submmit_idx = self .idx_label .submmit_idx .connect (self .on_idx_edited )
320
407
self .textblock = textblock
321
408
self .idx = idx
322
409
self .checked = False
@@ -339,6 +426,10 @@ def __init__(self, textblock: TextBlock = None, idx: int = None, fold: bool = Fa
339
426
340
427
self .setAcceptDrops (True )
341
428
429
+ def on_idx_edited (self , new_idx : int ):
430
+ new_idx -= 1
431
+ self .idx_edited .emit (self .idx , new_idx )
432
+
342
433
def dragEnterEvent (self , e : QDragEnterEvent ) -> None :
343
434
if isinstance (e .source (), TransPairWidget ):
344
435
e .accept ()
@@ -369,7 +460,7 @@ def _set_checked_state(self, checked: bool):
369
460
else :
370
461
self .setStyleSheet ("" )
371
462
372
- def mouseReleaseEvent (self , e : QMouseEvent ) -> None :
463
+ def update_checkstate_by_mousevent (self , e : QMouseEvent ):
373
464
if e .button () == Qt .MouseButton .LeftButton :
374
465
modifiers = e .modifiers ()
375
466
if modifiers & Qt .KeyboardModifier .ShiftModifier and modifiers & Qt .KeyboardModifier .ControlModifier :
@@ -378,7 +469,15 @@ def mouseReleaseEvent(self, e: QMouseEvent) -> None:
378
469
shift_pressed = modifiers == Qt .KeyboardModifier .ShiftModifier
379
470
ctrl_pressed = modifiers == Qt .KeyboardModifier .ControlModifier
380
471
self .check_state_changed .emit (self , shift_pressed , ctrl_pressed )
381
- return super ().mouseReleaseEvent (e )
472
+
473
+ def mousePressEvent (self , e : QMouseEvent ) -> None :
474
+ if not self .checked :
475
+ self .update_checkstate_by_mousevent (e )
476
+ return super ().mousePressEvent (e )
477
+
478
+ def mouseDoubleClickEvent (self , event : QMouseEvent ) -> None :
479
+ print ("double click" )
480
+ return super ().mouseDoubleClickEvent (event )
382
481
383
482
def updateIndex (self , idx : int ):
384
483
if self .idx != idx :
@@ -464,7 +563,21 @@ def mouseMoveEvent(self, e: QMouseEvent) -> None:
464
563
new_maps = np .where (drags != new_pos )
465
564
if len (new_maps ) == 0 :
466
565
return
566
+
467
567
drags_ori , drags_tgt = drags [new_maps ], new_pos [new_maps ]
568
+ result_list = list (range (len (self .pairwidget_list )))
569
+ to_insert = []
570
+ for ii , src_idx in enumerate (drags_ori ):
571
+ pos = src_idx - ii
572
+ to_insert .append (result_list .pop (pos ))
573
+ for ii , tgt_idx in enumerate (drags_tgt ):
574
+ result_list .insert (tgt_idx , to_insert [ii ])
575
+ drags_ori , drags_tgt = [], []
576
+ for ii , idx in enumerate (result_list ):
577
+ if ii != idx :
578
+ drags_ori .append (idx )
579
+ drags_tgt .append (ii )
580
+
468
581
self .rearrange_blks .emit ((drags_ori , drags_tgt ))
469
582
470
583
return super ().mouseMoveEvent (e )
@@ -486,12 +599,14 @@ def set_drag_style(self, pos: int, clear_style: bool = False):
486
599
def clearDrag (self ):
487
600
self .drag_to_pos = - 1
488
601
if self .drag is not None :
489
- self .drag .cancel ()
602
+ try :
603
+ self .drag .cancel ()
604
+ except RuntimeError :
605
+ pass
490
606
self .drag = None
491
607
492
608
def dragMoveEvent (self , e : QDragMoveEvent ) -> None :
493
609
e .accept ()
494
- e .position ()
495
610
return super ().dragMoveEvent (e )
496
611
497
612
def dragEnterEvent (self , e : QDragEnterEvent ):
@@ -508,7 +623,27 @@ def handle_drag_pos(self, to_pos: int):
508
623
self .set_drag_style (self .drag_to_pos , True )
509
624
self .drag_to_pos = to_pos
510
625
self .set_drag_style (to_pos )
626
+
627
+ def on_idx_edited (self , src_idx : int , tgt_idx : int ):
628
+ src_idx_ori = tgt_idx
629
+ tgt_idx = max (min (tgt_idx , len (self .pairwidget_list ) - 1 ), 0 )
630
+ if src_idx_ori != tgt_idx :
631
+ self .pairwidget_list [src_idx ].idx_label .setText (str (src_idx + 1 ).zfill (2 ))
632
+ if src_idx == tgt_idx :
633
+ return
634
+ ids_ori , ids_tgt = [src_idx ], [tgt_idx ]
511
635
636
+ if src_idx < tgt_idx :
637
+ for idx in range (src_idx + 1 , tgt_idx + 1 ):
638
+ ids_ori .append (idx )
639
+ ids_tgt .append (idx - 1 )
640
+ else :
641
+ for idx in range (tgt_idx , src_idx ):
642
+ ids_ori .append (idx )
643
+ ids_tgt .append (idx + 1 )
644
+ self .rearrange_blks .emit ((ids_ori , ids_tgt , (tgt_idx , src_idx )))
645
+ # self.ensureVisible
646
+
512
647
def addPairWidget (self , pairwidget : TransPairWidget ):
513
648
self .vlayout .insertWidget (pairwidget .idx , pairwidget )
514
649
pairwidget .check_state_changed .connect (self .on_widget_checkstate_changed )
@@ -612,7 +747,6 @@ def set_selected_list(self, selection_indices: List):
612
747
if idx == 0 :
613
748
self .sel_anchor_widget = pw
614
749
615
-
616
750
def clearAllSelected (self , emit_signal = True ):
617
751
self .sel_anchor_widget = None
618
752
if len (self .checked_list ) > 0 :
0 commit comments