2019-11-14 03:56:30 +00:00
#This file is licensed under the Do What the Fuck You Want To Public License aka WTFPL
import os
from . import HydrusConstants as HC
# If not explicitely set, prefer PySide2 instead of the qtpy default which is PyQt5
# It is important that this runs on startup *before* anything is imported from qtpy.
# Since test.py, client.py and client.pyw all import this module first before any other Qt related ones, this requirement is satisfied.
if not ' QT_API ' in os . environ :
try :
import PySide2
2019-12-05 05:29:32 +00:00
os . environ [ ' QT_API ' ] = ' pyside2 '
2019-11-14 03:56:30 +00:00
except ImportError :
pass
from . import HydrusData
from . import HydrusGlobals as HG
from . import ClientConstants as CC
#
import qtpy
from qtpy import QtCore as QC
from qtpy import QtWidgets as QW
from qtpy import QtGui as QG
import math
from collections import defaultdict
if qtpy . PYQT5 :
import sip
def isValid ( obj ) :
if isinstance ( obj , sip . simplewrapper ) :
return not sip . isdeleted ( obj )
return True
elif qtpy . PYSIDE2 :
import shiboken2
isValid = shiboken2 . isValid
else :
raise RuntimeError ( ' You need either PySide2 or PyQt5 ' )
def MonkeyPatchMissingMethods ( ) :
if qtpy . PYQT5 :
def QPointToTuple ( self ) :
return ( self . x ( ) , self . y ( ) )
def QSizeToTuple ( self ) :
return ( self . width ( ) , self . height ( ) )
def QColorToTuple ( self ) :
return ( self . red ( ) , self . green ( ) , self . blue ( ) , self . alpha ( ) )
QC . QPoint . toTuple = QPointToTuple
QC . QPointF . toTuple = QPointToTuple
QC . QSize . toTuple = QSizeToTuple
QC . QSizeF . toTuple = QSizeToTuple
QG . QColor . toTuple = QColorToTuple
2019-11-20 23:10:46 +00:00
def MonkeyPatchGetSaveFileName ( original_function ) :
def new_function ( * args , * * kwargs ) :
if ' selectedFilter ' in kwargs :
kwargs [ ' initialFilter ' ] = kwargs [ ' selectedFilter ' ]
del kwargs [ ' selectedFilter ' ]
return original_function ( * args , * * kwargs )
return new_function
QW . QFileDialog . getSaveFileName = MonkeyPatchGetSaveFileName ( QW . QFileDialog . getSaveFileName )
2019-11-14 03:56:30 +00:00
class HBoxLayout ( QW . QHBoxLayout ) :
def __init__ ( self , margin = 2 , spacing = 2 ) :
QW . QHBoxLayout . __init__ ( self )
self . setMargin ( margin )
self . setSpacing ( spacing )
def setMargin ( self , val ) :
self . setContentsMargins ( val , val , val , val )
class VBoxLayout ( QW . QVBoxLayout ) :
def __init__ ( self , margin = 2 , spacing = 2 ) :
QW . QVBoxLayout . __init__ ( self )
self . setMargin ( margin )
self . setSpacing ( spacing )
def setMargin ( self , val ) :
self . setContentsMargins ( val , val , val , val )
class LabelledSlider ( QW . QWidget ) :
def __init__ ( self , parent = None ) :
QW . QWidget . __init__ ( self , parent )
self . setLayout ( VBoxLayout ( spacing = 2 ) )
top_layout = HBoxLayout ( spacing = 2 )
self . _min_label = QW . QLabel ( )
self . _max_label = QW . QLabel ( )
self . _value_label = QW . QLabel ( )
self . _slider = QW . QSlider ( )
self . _slider . setOrientation ( QC . Qt . Horizontal )
self . _slider . setTickInterval ( 1 )
self . _slider . setTickPosition ( QW . QSlider . TicksBothSides )
top_layout . addWidget ( self . _min_label )
top_layout . addWidget ( self . _slider )
top_layout . addWidget ( self . _max_label )
self . layout ( ) . addLayout ( top_layout )
self . layout ( ) . addWidget ( self . _value_label )
self . _value_label . setAlignment ( QC . Qt . AlignVCenter | QC . Qt . AlignHCenter )
self . layout ( ) . setAlignment ( self . _value_label , QC . Qt . AlignHCenter )
self . _slider . valueChanged . connect ( self . _UpdateLabels )
self . _UpdateLabels ( )
def _UpdateLabels ( self ) :
self . _min_label . setText ( str ( self . _slider . minimum ( ) ) )
self . _max_label . setText ( str ( self . _slider . maximum ( ) ) )
self . _value_label . setText ( str ( self . _slider . value ( ) ) )
def GetValue ( self ) :
return self . _slider . value ( )
def SetRange ( self , min , max ) :
self . _slider . setRange ( min , max )
self . _UpdateLabels ( )
def SetValue ( self , value ) :
self . _slider . setValue ( value )
self . _UpdateLabels ( )
def SplitterVisibleCount ( splitter ) :
count = 0
for i in range ( splitter . count ( ) ) :
if splitter . widget ( i ) . isVisible ( ) : count + = 1
return count
class DirPickerCtrl ( QW . QWidget ) :
dirPickerChanged = QC . Signal ( )
def __init__ ( self , parent ) :
QW . QWidget . __init__ ( self , parent )
layout = HBoxLayout ( spacing = 2 )
self . _path_edit = QW . QLineEdit ( self )
self . _button = QW . QPushButton ( ' browse ' , self )
self . _button . clicked . connect ( self . _Browse )
self . _path_edit . textEdited . connect ( self . _TextEdited )
layout . addWidget ( self . _path_edit )
layout . addWidget ( self . _button )
self . setLayout ( layout )
2019-12-11 23:18:37 +00:00
2019-11-14 03:56:30 +00:00
def SetPath ( self , path ) :
self . _path_edit . setText ( path )
2019-12-11 23:18:37 +00:00
2019-11-14 03:56:30 +00:00
def GetPath ( self ) :
return self . _path_edit . text ( )
2019-12-11 23:18:37 +00:00
2019-11-14 03:56:30 +00:00
def _Browse ( self ) :
2019-12-11 23:18:37 +00:00
existing_path = self . _path_edit . text ( )
path = QW . QFileDialog . getExistingDirectory ( self , ' ' , existing_path )
2019-11-14 03:56:30 +00:00
if path == ' ' :
return
path = os . path . normpath ( path )
self . _path_edit . setText ( path )
if os . path . exists ( path ) :
self . dirPickerChanged . emit ( )
def _TextEdited ( self , text ) :
if os . path . exists ( text ) :
self . dirPickerChanged . emit ( )
class FilePickerCtrl ( QW . QWidget ) :
filePickerChanged = QC . Signal ( )
def __init__ ( self , parent = None , wildcard = None ) :
QW . QWidget . __init__ ( self , parent )
layout = HBoxLayout ( spacing = 2 )
self . _path_edit = QW . QLineEdit ( self )
self . _button = QW . QPushButton ( ' browse ' , self )
self . _button . clicked . connect ( self . _Browse )
self . _path_edit . textEdited . connect ( self . _TextEdited )
layout . addWidget ( self . _path_edit )
layout . addWidget ( self . _button )
self . setLayout ( layout )
self . _save_mode = False
self . _wildcard = wildcard
def SetPath ( self , path ) :
self . _path_edit . setText ( path )
def GetPath ( self ) :
return self . _path_edit . text ( )
def SetSaveMode ( self , save_mode ) :
self . _save_mode = save_mode
def _Browse ( self ) :
2019-12-11 23:18:37 +00:00
existing_path = self . _path_edit . text ( )
2019-11-14 03:56:30 +00:00
if self . _save_mode :
if self . _wildcard :
2019-12-11 23:18:37 +00:00
path = QW . QFileDialog . getSaveFileName ( self , ' ' , existing_path , filter = self . _wildcard , selectedFilter = self . _wildcard ) [ 0 ]
2019-11-14 03:56:30 +00:00
else :
2019-12-11 23:18:37 +00:00
path = QW . QFileDialog . getSaveFileName ( self , ' ' , existing_path ) [ 0 ]
2019-11-14 03:56:30 +00:00
else :
if self . _wildcard :
2019-12-11 23:18:37 +00:00
path = QW . QFileDialog . getOpenFileName ( self , ' ' , existing_path , filter = self . _wildcard , selectedFilter = self . _wildcard ) [ 0 ]
2019-11-14 03:56:30 +00:00
else :
2019-12-11 23:18:37 +00:00
path = QW . QFileDialog . getOpenFileName ( self , ' ' , existing_path ) [ 0 ]
2019-11-14 03:56:30 +00:00
if path == ' ' :
return
path = os . path . normpath ( path )
self . _path_edit . setText ( path )
if self . _save_mode or os . path . exists ( path ) :
self . filePickerChanged . emit ( )
def _TextEdited ( self , text ) :
if self . _save_mode or os . path . exists ( text ) :
self . filePickerChanged . emit ( )
class TabBar ( QW . QTabBar ) :
def __init__ ( self , parent = None ) :
QW . QTabBar . __init__ ( self , parent )
self . setMouseTracking ( True )
self . setAcceptDrops ( True )
self . _supplementary_drop_target = None
self . _last_clicked_tab_index = - 1
2020-01-22 21:04:43 +00:00
self . _last_clicked_global_pos = None
2019-11-14 03:56:30 +00:00
def AddSupplementaryTabBarDropTarget ( self , drop_target ) :
self . _supplementary_drop_target = drop_target
2020-01-22 21:04:43 +00:00
def clearLastClickedTabInfo ( self ) :
2019-11-14 03:56:30 +00:00
self . _last_clicked_tab_index = - 1
2020-01-22 21:04:43 +00:00
self . _last_clicked_global_pos = None
2019-11-14 03:56:30 +00:00
def mouseMoveEvent ( self , e ) :
e . ignore ( )
def mousePressEvent ( self , event ) :
if event . button ( ) == QC . Qt . LeftButton :
self . _last_clicked_tab_index = self . tabAt ( event . pos ( ) )
2020-01-22 21:04:43 +00:00
self . _last_clicked_global_pos = event . globalPos ( )
2019-11-14 03:56:30 +00:00
QW . QTabBar . mousePressEvent ( self , event )
def dragEnterEvent ( self , event ) :
2019-12-05 05:29:32 +00:00
if ' application/hydrus-tab ' in event . mimeData ( ) . formats ( ) :
2019-11-14 03:56:30 +00:00
event . ignore ( )
2019-12-05 05:29:32 +00:00
else :
event . accept ( )
2019-11-14 03:56:30 +00:00
2019-12-05 05:29:32 +00:00
def dragMoveEvent ( self , event ) :
if ' application/hydrus-tab ' not in event . mimeData ( ) . formats ( ) :
2019-11-14 03:56:30 +00:00
tab_index = self . tabAt ( event . pos ( ) )
2019-12-05 05:29:32 +00:00
2019-11-14 03:56:30 +00:00
if tab_index != - 1 :
2019-12-05 05:29:32 +00:00
self . parentWidget ( ) . setCurrentIndex ( tab_index )
2019-11-14 03:56:30 +00:00
else :
event . ignore ( )
2019-12-05 05:29:32 +00:00
2019-11-14 03:56:30 +00:00
2020-01-22 21:04:43 +00:00
def lastClickedTabInfo ( self ) :
2019-11-14 03:56:30 +00:00
2020-01-22 21:04:43 +00:00
return ( self . _last_clicked_tab_index , self . _last_clicked_global_pos )
2019-11-14 03:56:30 +00:00
def dropEvent ( self , event ) :
if self . _supplementary_drop_target :
self . _supplementary_drop_target . eventFilter ( self , event )
else :
event . ignore ( )
# A heavily extended/tweaked version of https://forum.qt.io/topic/67542/drag-tabs-between-qtabwidgets/
class TabWidgetWithDnD ( QW . QTabWidget ) :
pageDragAndDropped = QC . Signal ( QW . QWidget , QW . QWidget )
def __init__ ( self , parent = None ) :
QW . QTabWidget . __init__ ( self , parent )
self . setTabBar ( TabBar ( self ) )
self . setAcceptDrops ( True )
self . _tab_bar = self . tabBar ( )
self . _supplementary_drop_target = None
2019-12-05 05:29:32 +00:00
2019-11-14 03:56:30 +00:00
def _LayoutPagesHelper ( self ) :
current_index = self . currentIndex ( )
for i in range ( self . count ( ) ) :
self . setCurrentIndex ( i )
if isinstance ( self . widget ( i ) , TabWidgetWithDnD ) :
self . widget ( i ) . _LayoutPagesHelper ( )
self . setCurrentIndex ( current_index )
def LayoutPages ( self ) :
# Momentarily switch to each page, then back, forcing a layout update.
# If this is not done, the splitters on the hidden pages won't resize their widgets properly when we restore
# splitter sizes after this, since they would never became visible.
# We first have to climb up the widget hierarchy and go down recursively from the root tab widget,
# since it's not enough to make a page visible if its a nested page: all of its ancestor pages have to be visible too.
# This shouldn't be visible to users since we switch back immediately.
# There is probably a proper way to do this...
highest_ancestor_of_same_type = self
parent = self . parentWidget ( )
while parent is not None :
if isinstance ( parent , TabWidgetWithDnD ) :
highest_ancestor_of_same_type = parent
parent = parent . parentWidget ( )
highest_ancestor_of_same_type . _LayoutPagesHelper ( ) # This does the actual recursive descent and making pages visible
# This is a hack that adds an additional drop target to the tab bar. The added drop target will get drop events from the tab bar.
# Used to make the case of files/media droppend onto tabs work.
def AddSupplementaryTabBarDropTarget ( self , drop_target ) :
self . _supplementary_drop_target = drop_target
self . tabBar ( ) . AddSupplementaryTabBarDropTarget ( drop_target )
def mouseMoveEvent ( self , e ) :
2020-01-22 21:04:43 +00:00
2019-11-14 03:56:30 +00:00
if self . currentWidget ( ) and self . currentWidget ( ) . rect ( ) . contains ( self . currentWidget ( ) . mapFromGlobal ( self . mapToGlobal ( e . pos ( ) ) ) ) :
QW . QTabWidget . mouseMoveEvent ( self , e )
if e . buttons ( ) != QC . Qt . LeftButton :
return
2019-12-05 05:29:32 +00:00
my_mouse_pos = e . pos ( )
global_mouse_pos = self . mapToGlobal ( my_mouse_pos )
tab_bar_mouse_pos = self . _tab_bar . mapFromGlobal ( global_mouse_pos )
2019-11-20 23:10:46 +00:00
2019-12-05 05:29:32 +00:00
if not self . _tab_bar . rect ( ) . contains ( tab_bar_mouse_pos ) :
2019-11-14 03:56:30 +00:00
return
if not isinstance ( self . _tab_bar , TabBar ) :
return
2020-01-22 21:04:43 +00:00
( clicked_tab_index , clicked_global_pos ) = self . _tab_bar . lastClickedTabInfo ( )
2019-11-14 03:56:30 +00:00
if clicked_tab_index == - 1 :
return
2020-01-22 21:04:43 +00:00
if e . globalPos ( ) == clicked_global_pos :
# don't start a drag until movement
return
2019-11-14 03:56:30 +00:00
tab_rect = self . _tab_bar . tabRect ( clicked_tab_index )
pixmap = QG . QPixmap ( tab_rect . size ( ) )
self . _tab_bar . render ( pixmap , QC . QPoint ( ) , QG . QRegion ( tab_rect ) )
mimeData = QC . QMimeData ( )
2019-12-05 05:29:32 +00:00
mimeData . setData ( ' application/hydrus-tab ' , b ' ' )
2019-11-14 03:56:30 +00:00
drag = QG . QDrag ( self . _tab_bar )
drag . setMimeData ( mimeData )
drag . setPixmap ( pixmap )
cursor = QG . QCursor ( QC . Qt . OpenHandCursor )
2019-12-05 05:29:32 +00:00
drag . setHotSpot ( QC . QPoint ( 0 , 0 ) )
# this puts the tab pixmap exactly where we picked it up, but it looks bad
# drag.setHotSpot( tab_bar_mouse_pos - tab_rect.topLeft() )
2019-11-14 03:56:30 +00:00
drag . setDragCursor ( cursor . pixmap ( ) , QC . Qt . MoveAction )
drag . exec_ ( QC . Qt . MoveAction )
def dragEnterEvent ( self , e ) :
if self . currentWidget ( ) and self . currentWidget ( ) . rect ( ) . contains ( self . currentWidget ( ) . mapFromGlobal ( self . mapToGlobal ( e . pos ( ) ) ) ) :
return QW . QTabWidget . dragEnterEvent ( self , e )
2019-12-05 05:29:32 +00:00
if ' application/hydrus-tab ' in e . mimeData ( ) . formats ( ) :
2019-11-14 03:56:30 +00:00
e . accept ( )
else :
e . ignore ( )
def dragMoveEvent ( self , event ) :
2019-12-05 05:29:32 +00:00
2019-11-14 03:56:30 +00:00
#if self.currentWidget() and self.currentWidget().rect().contains( self.currentWidget().mapFromGlobal( self.mapToGlobal( event.pos() ) ) ): return QW.QTabWidget.dragMoveEvent( self, event )
2019-12-05 05:29:32 +00:00
screen_pos = self . mapToGlobal ( event . pos ( ) )
tab_pos = self . _tab_bar . mapFromGlobal ( screen_pos )
tab_index = self . _tab_bar . tabAt ( tab_pos )
2019-11-14 03:56:30 +00:00
if tab_index != - 1 :
2019-11-20 23:10:46 +00:00
shift_down = event . keyboardModifiers ( ) & QC . Qt . ShiftModifier
2019-11-14 03:56:30 +00:00
2019-11-20 23:10:46 +00:00
self . setCurrentIndex ( tab_index )
2019-11-14 03:56:30 +00:00
2019-12-05 05:29:32 +00:00
if ' application/hydrus-tab ' not in event . mimeData ( ) . formats ( ) :
2019-11-14 03:56:30 +00:00
event . reject ( )
#return QW.QTabWidget.dragMoveEvent( self, event )
def dragLeaveEvent ( self , e ) :
#if self.currentWidget() and self.currentWidget().rect().contains( self.currentWidget().mapFromGlobal( self.mapToGlobal( e.pos() ) ) ): return QW.QTabWidget.dragLeaveEvent( self, e )
e . accept ( )
def addTab ( self , widget , * args , * * kwargs ) :
if isinstance ( widget , TabWidgetWithDnD ) :
widget . AddSupplementaryTabBarDropTarget ( self . _supplementary_drop_target )
QW . QTabWidget . addTab ( self , widget , * args , * * kwargs )
def insertTab ( self , index , widget , * args , * * kwargs ) :
if isinstance ( widget , TabWidgetWithDnD ) :
widget . AddSupplementaryTabBarDropTarget ( self . _supplementary_drop_target )
QW . QTabWidget . insertTab ( self , index , widget , * args , * * kwargs )
def dropEvent ( self , e ) :
if self . currentWidget ( ) and self . currentWidget ( ) . rect ( ) . contains ( self . currentWidget ( ) . mapFromGlobal ( self . mapToGlobal ( e . pos ( ) ) ) ) :
return QW . QTabWidget . dropEvent ( self , e )
2019-12-05 05:29:32 +00:00
if ' application/hydrus-tab ' not in e . mimeData ( ) . formats ( ) : #Page dnd has no associated mime data
2019-11-14 03:56:30 +00:00
e . ignore ( )
return
w = self
source_tab_bar = e . source ( )
if not isinstance ( source_tab_bar , TabBar ) :
return
2020-01-22 21:04:43 +00:00
( source_page_index , source_page_click_global_pos ) = source_tab_bar . lastClickedTabInfo ( )
2019-11-14 03:56:30 +00:00
2020-01-22 21:04:43 +00:00
source_tab_bar . clearLastClickedTabInfo ( )
2019-11-14 03:56:30 +00:00
source_notebook = source_tab_bar . parentWidget ( )
source_page = source_notebook . widget ( source_page_index )
source_name = source_tab_bar . tabText ( source_page_index )
while w is not None :
if source_page == w :
# you cannot drop a page of pages inside itself
return
w = w . parentWidget ( )
e . setDropAction ( QC . Qt . MoveAction )
e . accept ( )
counter = self . count ( )
2019-12-05 05:29:32 +00:00
screen_pos = self . mapToGlobal ( e . pos ( ) )
tab_pos = self . tabBar ( ) . mapFromGlobal ( screen_pos )
dropped_on_tab_index = self . tabBar ( ) . tabAt ( tab_pos )
2019-11-14 03:56:30 +00:00
if source_notebook == self and dropped_on_tab_index == source_page_index :
return # if we drop on ourself, make no action, even on the right edge
dropped_on_left_edge = False
dropped_on_right_edge = False
if dropped_on_tab_index != - 1 :
EDGE_PADDING = 15
tab_rect = self . tabBar ( ) . tabRect ( dropped_on_tab_index )
edge_size = QC . QSize ( EDGE_PADDING , tab_rect . height ( ) )
left_edge_rect = QC . QRect ( tab_rect . topLeft ( ) , edge_size )
right_edge_rect = QC . QRect ( tab_rect . topRight ( ) - QC . QPoint ( EDGE_PADDING , 0 ) , edge_size )
dropped_on_left_edge = left_edge_rect . contains ( e . pos ( ) )
dropped_on_right_edge = right_edge_rect . contains ( e . pos ( ) )
if counter == 0 :
self . addTab ( source_page , source_name )
else :
if dropped_on_tab_index == - 1 :
insert_index = counter
else :
insert_index = dropped_on_tab_index
if dropped_on_right_edge :
insert_index + = 1
if self == source_notebook :
if insert_index == source_page_index + 1 and not dropped_on_left_edge :
pass # in this special case, moving it confidently one to the right, we will disobey the normal rules and indeed move one to the right, rather than no-op
elif insert_index > source_page_index :
# we are inserting to our right, which needs a shift since we will be removing ourselves from the list
insert_index - = 1
if source_notebook == self and insert_index == source_page_index :
return # if we mean to insert on ourself, make no action
self . insertTab ( insert_index , source_page , source_name )
shift_down = e . keyboardModifiers ( ) & QC . Qt . ShiftModifier
follow_dropped_page = not shift_down
new_options = HG . client_controller . new_options
if new_options . GetBoolean ( ' reverse_page_shift_drag_behaviour ' ) :
follow_dropped_page = not follow_dropped_page
if follow_dropped_page :
self . setCurrentIndex ( self . indexOf ( source_page ) )
2019-11-20 23:10:46 +00:00
else :
if source_page_index > 1 :
neighbour_page = source_notebook . widget ( source_page_index - 1 )
page_key = neighbour_page . GetPageKey ( )
else :
page_key = source_notebook . GetPageKey ( )
HG . client_controller . gui . ShowPage ( page_key )
2019-11-14 03:56:30 +00:00
self . pageDragAndDropped . emit ( source_page , source_tab_bar )
def DeleteAllNotebookPages ( notebook ) :
while notebook . count ( ) > 0 :
tab = notebook . widget ( 0 )
notebook . removeTab ( 0 )
tab . deleteLater ( )
def SplitVertically ( splitter , w1 , w2 , hpos ) :
splitter . setOrientation ( QC . Qt . Horizontal )
if w1 . parentWidget ( ) != splitter :
splitter . addWiget ( w1 )
w1 . setVisible ( True )
if w2 . parentWidget ( ) != splitter :
splitter . addWiget ( w2 )
w2 . setVisible ( True )
total_sum = sum ( splitter . sizes ( ) )
if hpos < 0 :
splitter . setSizes ( [ total_sum + hpos , - hpos ] )
elif hpos > 0 :
splitter . setSizes ( [ hpos , total_sum - hpos ] )
def SplitHorizontally ( splitter , w1 , w2 , vpos ) :
splitter . setOrientation ( QC . Qt . Vertical )
2019-11-20 23:10:46 +00:00
2019-11-14 03:56:30 +00:00
if w1 . parentWidget ( ) != splitter :
splitter . addWiget ( w1 )
w1 . setVisible ( True )
if w2 . parentWidget ( ) != splitter :
splitter . addWiget ( w2 )
w2 . setVisible ( True )
total_sum = sum ( splitter . sizes ( ) )
if vpos < 0 :
splitter . setSizes ( [ total_sum + vpos , - vpos ] )
elif vpos > 0 :
splitter . setSizes ( [ vpos , total_sum - vpos ] )
def MakeQLabelWithAlignment ( label , parent , align ) :
res = QW . QLabel ( label , parent )
res . setAlignment ( align )
return res
class GridLayout ( QW . QGridLayout ) :
def __init__ ( self , cols = 1 , spacing = 2 ) :
QW . QGridLayout . __init__ ( self )
self . _col_count = cols
self . setMargin ( 2 )
self . setSpacing ( spacing )
def GetFixedColumnCount ( self ) :
return self . _col_count
def setMargin ( self , val ) :
self . setContentsMargins ( val , val , val , val )
2019-11-28 01:11:46 +00:00
2019-11-14 03:56:30 +00:00
def AddToLayout ( layout , item , flag = None , alignment = None , sizePolicy = None ) :
if isinstance ( layout , GridLayout ) :
cols = layout . GetFixedColumnCount ( )
count = layout . count ( )
row = math . floor ( count / cols )
col = count % cols
if isinstance ( item , QW . QLayout ) :
layout . addLayout ( item , row , col )
elif isinstance ( item , QW . QWidget ) :
layout . addWidget ( item , row , col )
elif isinstance ( item , tuple ) :
spacer = QW . QPushButton ( ) #QW.QSpacerItem( 0, 0, QW.QSizePolicy.Expanding, QW.QSizePolicy.Fixed )
layout . addWidget ( spacer , row , col )
spacer . setVisible ( False )
return
else :
if isinstance ( item , QW . QLayout ) :
layout . addLayout ( item )
if alignment is not None :
layout . setAlignment ( item , alignment )
elif isinstance ( item , QW . QWidget ) :
layout . addWidget ( item )
if alignment is not None :
layout . setAlignment ( item , alignment )
elif isinstance ( item , tuple ) :
layout . addStretch ( 1 )
return
if isinstance ( item , QW . QWidget ) :
if sizePolicy is not None :
item . setSizePolicy ( sizePolicy [ 0 ] , sizePolicy [ 1 ] )
2020-02-05 22:55:21 +00:00
expand_both_ways = flag in ( CC . FLAGS_EXPAND_BOTH_WAYS , CC . FLAGS_EXPAND_SIZER_BOTH_WAYS , CC . FLAGS_EXPAND_BOTH_WAYS_POLITE , CC . FLAGS_EXPAND_BOTH_WAYS_SHY )
2019-11-14 03:56:30 +00:00
zero_border = False
# This is kind of a mess right now, adjustments might be needed
# Left all the original wx definitions of the flags as comments for future reference
if flag is None or flag == CC . FLAGS_NONE :
pass
elif flag == CC . FLAGS_SMALL_INDENT :
pass
#item.setContentsMargins( 2, 2, 2, 2 )
#wx.SizerFlags( 0 ).Border( wx.ALL, 2 )
elif flag == CC . FLAGS_BIG_INDENT :
pass
#item.setContentsMargins( 10, 10, 10, 10 )
#wx.SizerFlags( 0 ).Border( wx.ALL, 10 )
elif flag == CC . FLAGS_CENTER :
layout . setAlignment ( item , QC . Qt . AlignVCenter | QC . Qt . AlignHCenter )
#item.setContentsMargins( 2, 2, 2, 2 )
#wx.SizerFlags( 0 ).Border( wx.ALL, 2 )
elif flag == CC . FLAGS_EXPAND_PERPENDICULAR :
2020-01-16 02:08:23 +00:00
if isinstance ( item , QW . QWidget ) :
if isinstance ( layout , QW . QHBoxLayout ) :
h_policy = QW . QSizePolicy . Fixed
v_policy = QW . QSizePolicy . Expanding
else :
h_policy = QW . QSizePolicy . Expanding
v_policy = QW . QSizePolicy . Fixed
item . setSizePolicy ( h_policy , v_policy )
2019-11-14 03:56:30 +00:00
#wx.SizerFlags( 0 ).Border( wx.ALL, 2 ).Expand()
#item.setContentsMargins( 2, 2, 2, 2 )
elif flag == CC . FLAGS_EXPAND_DEPTH_ONLY :
#if isinstance( item, QW.QWidget ): item.setSizePolicy( item.sizePolicy().verticalPolicy(), QW.QSizePolicy.Expanding )
if isinstance ( layout , QW . QVBoxLayout ) or isinstance ( layout , QW . QHBoxLayout ) : layout . setStretchFactor ( item , 5 )
#item.setContentsMargins( 2, 2, 2, 2 )
#wx.SizerFlags( 5 ).Border( wx.ALL, 2 ).Align( wx.ALIGN_CENTER_VERTICAL )
elif flag == CC . FLAGS_SIZER_CENTER :
if isinstance ( layout , QW . QVBoxLayout ) or isinstance ( layout , QW . QHBoxLayout ) : layout . setStretchFactor ( item , 5 )
layout . setAlignment ( item , QC . Qt . AlignHCenter | QC . Qt . AlignVCenter )
zero_border = True
#item.setContentsMargins( 0, 0, 0, 0 )
#wx.SizerFlags( 5 ).Center()
elif flag == CC . FLAGS_EXPAND_SIZER_PERPENDICULAR :
zero_border = True
#wx.SizerFlags( 0 ).Expand()
#item.setContentsMargins( 0, 0, 0, 0 )
elif flag == CC . FLAGS_EXPAND_SIZER_BOTH_WAYS :
2020-01-16 02:08:23 +00:00
zero_border = True
2019-11-14 03:56:30 +00:00
#item.setContentsMargins( 0, 0, 0, 0 )
#wx.SizerFlags( 5 ).Expand()
elif flag == CC . FLAGS_EXPAND_SIZER_DEPTH_ONLY :
if isinstance ( layout , QW . QVBoxLayout ) or isinstance ( layout , QW . QHBoxLayout ) : layout . setStretchFactor ( item , 5 )
layout . setAlignment ( item , QC . Qt . AlignVCenter )
zero_border = True
#item.setContentsMargins( 0, 0, 0, 0 )
#wx.SizerFlags( 5 ).Align( wx.ALIGN_CENTER_VERTICAL )
elif flag == CC . FLAGS_BUTTON_SIZER :
item . insertStretch ( 0 , 10 )
layout . setAlignment ( item , QC . Qt . AlignRight )
zero_border = True
#item.setContentsMargins( 0, 0, 0, 0 )
#wx.SizerFlags( 0 ).Align( wx.ALIGN_RIGHT )
elif flag == CC . FLAGS_LONE_BUTTON :
layout . setAlignment ( item , QC . Qt . AlignRight )
zero_border = True
#item.setContentsMargins( 2, 2, 2, 2 )
#wx.SizerFlags( 0 ).Border( wx.ALL, 2 ).Align( wx.ALIGN_RIGHT )
elif flag == CC . FLAGS_VCENTER :
layout . setAlignment ( item , QC . Qt . AlignVCenter )
zero_border = True
#item.setContentsMargins( 2, 2, 2, 2 )
#wx.SizerFlags( 0 ).Border( wx.ALL, 2 ).Align( wx.ALIGN_CENTER_VERTICAL )
elif flag == CC . FLAGS_SIZER_VCENTER :
layout . setAlignment ( item , QC . Qt . AlignVCenter )
zero_border = True
#item.setContentsMargins( 0, 0, 0, 0 )
#wx.SizerFlags( 0 ).Align( wx.ALIGN_CENTRE_VERTICAL )
elif flag == CC . FLAGS_VCENTER_EXPAND_DEPTH_ONLY :
if isinstance ( layout , QW . QVBoxLayout ) or isinstance ( layout , QW . QHBoxLayout ) : layout . setStretchFactor ( item , 5 )
layout . setAlignment ( item , QC . Qt . AlignVCenter )
zero_border = True
#item.setContentsMargins( 2, 2, 2, 2 )
#wx.SizerFlags( 5 ).Border( wx.ALL, 2 ).Align( wx.ALIGN_CENTER_VERTICAL )
2020-01-16 02:08:23 +00:00
if expand_both_ways :
if isinstance ( item , QW . QWidget ) :
item . setSizePolicy ( QW . QSizePolicy . Expanding , QW . QSizePolicy . Expanding )
if isinstance ( layout , QW . QVBoxLayout ) or isinstance ( layout , QW . QHBoxLayout ) :
2020-02-05 22:55:21 +00:00
if flag in ( CC . FLAGS_EXPAND_BOTH_WAYS , CC . FLAGS_EXPAND_SIZER_BOTH_WAYS ) :
2020-01-16 02:08:23 +00:00
stretch_factor = 5
elif flag == CC . FLAGS_EXPAND_BOTH_WAYS_POLITE :
stretch_factor = 3
elif flag == CC . FLAGS_EXPAND_BOTH_WAYS_SHY :
stretch_factor = 1
layout . setStretchFactor ( item , stretch_factor )
2019-11-14 03:56:30 +00:00
if zero_border :
margin = 0
if isinstance ( item , QW . QFrame ) :
margin = item . frameWidth ( )
item . setContentsMargins ( margin , margin , margin , margin )
def EscapeMnemonics ( str ) :
return str . replace ( " & " , " && " )
def ScrollAreaVisibleRect ( scroll_area ) :
if not scroll_area . widget ( ) : return QC . QRect ( 0 , 0 , 0 , 0 )
rect = scroll_area . widget ( ) . visibleRegion ( ) . boundingRect ( )
# Do not allow it to be smaller than the scroll area's viewport size:
if rect . width ( ) < scroll_area . viewport ( ) . width ( ) :
rect . setWidth ( scroll_area . viewport ( ) . width ( ) )
if rect . height ( ) < scroll_area . viewport ( ) . height ( ) :
rect . setHeight ( scroll_area . viewport ( ) . height ( ) )
return rect
def AdjustOpacity ( image , opacity_factor ) :
new_image = QG . QImage ( image . width ( ) , image . height ( ) , QG . QImage . Format_RGBA8888 )
new_image . fill ( QC . Qt . transparent )
painter = QG . QPainter ( new_image )
painter . setOpacity ( opacity_factor )
painter . drawImage ( 0 , 0 , image )
return new_image
def DrawText ( painter , x , y , text ) :
boundingRect = painter . fontMetrics ( ) . size ( QC . Qt . TextSingleLine , text )
painter . drawText ( QC . QRectF ( x , y , boundingRect . width ( ) , boundingRect . height ( ) ) , text )
def ToKeySequence ( modifiers , key ) :
if isinstance ( modifiers , QC . Qt . KeyboardModifiers ) :
seq_str = ' '
for modifier in [ QC . Qt . ShiftModifier , QC . Qt . ControlModifier , QC . Qt . AltModifier , QC . Qt . MetaModifier , QC . Qt . KeypadModifier , QC . Qt . GroupSwitchModifier ] :
if modifiers & modifier : seq_str + = QG . QKeySequence ( modifier ) . toString ( )
seq_str + = QG . QKeySequence ( key ) . toString ( )
return QG . QKeySequence ( seq_str )
else : return QG . QKeySequence ( key + modifiers )
def AddShortcut ( widget , modifier , key , callable , * args ) :
shortcut = QW . QShortcut ( widget )
shortcut . setKey ( ToKeySequence ( modifier , key ) )
shortcut . setContext ( QC . Qt . WidgetWithChildrenShortcut )
shortcut . activated . connect ( lambda : callable ( * args ) )
def AdjustColour ( colour , percent ) :
percent = percent / 100
return QG . QColor ( colour . red ( ) + colour . red ( ) * percent , colour . green ( ) + colour . green ( ) * percent , colour . blue ( ) + colour . blue ( ) * percent , colour . alpha ( ) )
2019-12-05 05:29:32 +00:00
2019-11-14 03:56:30 +00:00
class BusyCursor :
def __enter__ ( self ) :
QW . QApplication . setOverrideCursor ( QC . Qt . WaitCursor )
def __exit__ ( self , exc_type , exc_val , exc_tb ) :
QW . QApplication . restoreOverrideCursor ( )
def GetBackgroundColour ( widget ) :
return widget . palette ( ) . color ( QG . QPalette . Window )
2020-01-29 22:08:37 +00:00
CallAfterEventType = QC . QEvent . Type ( QC . QEvent . registerEventType ( ) )
2019-11-14 03:56:30 +00:00
class CallAfterEvent ( QC . QEvent ) :
def __init__ ( self , fn , * args , * * kwargs ) :
2020-01-29 22:08:37 +00:00
QC . QEvent . __init__ ( self , CallAfterEventType )
2019-11-14 03:56:30 +00:00
self . _fn = fn
self . _args = args
self . _kwargs = kwargs
2020-01-22 21:04:43 +00:00
2019-11-14 03:56:30 +00:00
def Execute ( self ) :
2020-01-22 21:04:43 +00:00
if self . _fn is not None :
self . _fn ( * self . _args , * * self . _kwargs )
2020-02-19 21:48:36 +00:00
class CallAfterEventCatcher ( QC . QObject ) :
2019-11-14 03:56:30 +00:00
2020-02-19 21:48:36 +00:00
def __init__ ( self , parent ) :
2019-11-14 03:56:30 +00:00
QC . QObject . __init__ ( self , parent )
2020-02-19 21:48:36 +00:00
self . installEventFilter ( self )
2019-11-14 03:56:30 +00:00
def eventFilter ( self , watched , event ) :
2020-01-29 22:08:37 +00:00
if event . type ( ) == CallAfterEventType and isinstance ( event , CallAfterEvent ) :
2019-11-14 03:56:30 +00:00
event . Execute ( )
2020-02-12 22:50:37 +00:00
event . accept ( )
2019-11-14 03:56:30 +00:00
return True
return False
def CallAfter ( fn , * args , * * kwargs ) :
QW . QApplication . instance ( ) . postEvent ( QW . QApplication . instance ( ) . call_after_catcher , CallAfterEvent ( fn , * args , * * kwargs ) )
2020-01-29 22:08:37 +00:00
QW . QApplication . instance ( ) . eventDispatcher ( ) . wakeUp ( )
2019-11-14 03:56:30 +00:00
def ClearLayout ( layout , delete_widgets = False ) :
while layout . count ( ) > 0 :
item = layout . itemAt ( 0 )
if delete_widgets :
if item . widget ( ) :
item . widget ( ) . deleteLater ( )
elif item . layout ( ) :
ClearLayout ( item . layout ( ) , delete_widgets = True )
item . layout ( ) . deleteLater ( )
else :
spacer = item . layout ( ) . spacerItem ( )
del spacer
layout . removeItem ( item )
2020-01-29 22:08:37 +00:00
2019-11-14 03:56:30 +00:00
def ListWidgetGetStringSelection ( widget ) :
for i in range ( widget . count ( ) ) :
if widget . item ( i ) . isSelected ( ) : return widget . item ( i ) . text ( )
return None
2020-01-29 22:08:37 +00:00
2019-11-14 03:56:30 +00:00
def GetClientData ( widget , idx ) :
if isinstance ( widget , QW . QComboBox ) :
return widget . itemData ( idx , QC . Qt . UserRole )
elif isinstance ( widget , CheckListBox ) :
return widget . item ( idx ) . data ( QC . Qt . UserRole )
elif isinstance ( widget , QW . QTreeWidget ) :
return widget . topLevelItem ( idx ) . data ( 0 , QC . Qt . UserRole )
elif isinstance ( widget , QW . QListWidget ) :
return widget . item ( idx ) . data ( QC . Qt . UserRole )
else :
raise ValueError ( ' Unknown widget class in GetClientData ' )
def Unsplit ( splitter , widget ) :
if widget . parentWidget ( ) == splitter :
widget . setVisible ( False )
def GetSystemColour ( colour ) :
return QG . QPalette ( ) . color ( colour )
2019-11-20 23:10:46 +00:00
def CenterOnWindow ( parent , window ) :
parent_window = parent . window ( )
window . move ( parent_window . mapToGlobal ( parent_window . rect ( ) . center ( ) ) - window . rect ( ) . center ( ) )
def CenterOnScreen ( window ) :
2019-11-14 03:56:30 +00:00
window . move ( QW . QApplication . desktop ( ) . availableGeometry ( ) . center ( ) - window . rect ( ) . center ( ) )
def WarningHandler ( msg_type , context , str ) :
if msg_type == QC . QtWarningMsg :
print ( str )
def TupleToQColor ( tup ) :
return QG . QColor ( * tup )
def TupleToQPoint ( tup ) :
if isinstance ( tup , QC . QPoint ) :
raise ValueError ( ' Unnecessary use of TupleToQPoint ' )
else :
return QC . QPoint ( tup [ 0 ] , tup [ 1 ] )
def TupleToQSize ( tup ) :
if isinstance ( tup , QC . QSize ) :
raise ValueError ( ' Unnecessary use of TupleToQSize ' )
else :
return QC . QSize ( tup [ 0 ] , tup [ 1 ] )
def ListWidgetDelete ( widget , idx ) :
if isinstance ( idx , QC . QModelIndex ) :
idx = idx . row ( )
if idx != - 1 :
item = widget . takeItem ( idx )
del item
def ListWidgetGetSelection ( widget ) :
for i in range ( widget . count ( ) ) :
if widget . item ( i ) . isSelected ( ) : return i
return - 1
def ListWidgetGetStrings ( widget ) :
strings = [ ]
for i in range ( widget . count ( ) ) :
strings . append ( widget . item ( i ) . text ( ) )
return strings
def ListWidgetIndexForString ( widget , string ) :
for i in range ( widget . count ( ) ) :
if widget . item ( i ) . text ( ) == string : return i
return - 1
def ListWidgetIsSelected ( widget , idx ) :
if idx == - 1 : return False
return widget . item ( idx ) . isSelected ( )
def ListWidgetSetSelection ( widget , idxs ) :
widget . clearSelection ( )
if not isinstance ( idxs , list ) :
idxs = [ idxs ]
for idx in idxs :
if idx != - 1 : widget . item ( idx ) . setSelected ( True )
def MakeQSpinBox ( parent = None , initial = None , min = None , max = None , width = None ) :
spinbox = QW . QSpinBox ( parent )
if min is not None : spinbox . setMinimum ( min )
if max is not None : spinbox . setMaximum ( max )
if initial is not None : spinbox . setValue ( initial )
if width is not None : spinbox . setMinimumWidth ( width )
return spinbox
def SetInitialSize ( widget , size ) :
if hasattr ( widget , ' SetInitialSize ' ) :
widget . SetInitialSize ( size )
return
if isinstance ( size , tuple ) :
size = QC . QSize ( size [ 0 ] , size [ 1 ] )
if size . width ( ) > = 0 : widget . setMinimumWidth ( size . width ( ) )
if size . height ( ) > = 0 : widget . setMinimumHeight ( size . height ( ) )
def SetBackgroundColour ( widget , colour ) :
widget . setAutoFillBackground ( True )
object_name = widget . objectName ( )
if not object_name :
object_name = str ( id ( widget ) )
widget . setObjectName ( object_name )
if isinstance ( colour , QG . QColor ) :
widget . setStyleSheet ( ' # {} {{ background-color: {} }} ' . format ( object_name , colour . name ( ) ) )
2019-12-11 23:18:37 +00:00
2019-11-14 03:56:30 +00:00
elif isinstance ( colour , tuple ) :
widget . setStyleSheet ( ' # {} {{ background-color: {} }} ' . format ( object_name , TupleToQColor ( colour ) . name ( ) ) )
else :
widget . setStyleSheet ( ' # {} {{ background-color: {} }} ' . format ( object_name , QG . QColor ( colour ) . name ( ) ) )
2019-12-11 23:18:37 +00:00
2019-11-14 03:56:30 +00:00
def SetForegroundColour ( widget , colour ) :
widget . setAutoFillBackground ( True )
object_name = widget . objectName ( )
if not object_name :
object_name = str ( id ( widget ) )
2019-12-11 23:18:37 +00:00
2019-11-14 03:56:30 +00:00
widget . setObjectName ( object_name )
2019-12-11 23:18:37 +00:00
2019-11-14 03:56:30 +00:00
if isinstance ( colour , QG . QColor ) :
widget . setStyleSheet ( ' # {} {{ color: {} }} ' . format ( object_name , colour . name ( ) ) )
elif isinstance ( colour , tuple ) :
widget . setStyleSheet ( ' # {} {{ color: {} }} ' . format ( object_name , TupleToQColor ( colour ) . name ( ) ) )
else :
widget . setStyleSheet ( ' # {} {{ color: {} }} ' . format ( object_name , QG . QColor ( colour ) . name ( ) ) )
def SetStringSelection ( combobox , string ) :
index = combobox . findText ( string )
if index != - 1 :
combobox . setCurrentIndex ( index )
def SetClientSize ( widget , size ) :
if isinstance ( size , tuple ) :
size = QC . QSize ( size [ 0 ] , size [ 1 ] )
if size . width ( ) < 0 : size . setWidth ( widget . width ( ) )
if size . height ( ) < 0 : size . setHeight ( widget . height ( ) )
widget . resize ( size )
def SetMinClientSize ( widget , size ) :
if isinstance ( size , tuple ) :
size = QC . QSize ( size [ 0 ] , size [ 1 ] )
if size . width ( ) > = 0 : widget . setMinimumWidth ( size . width ( ) )
if size . height ( ) > = 0 : widget . setMinimumHeight ( size . height ( ) )
class StatusBar ( QW . QStatusBar ) :
def __init__ ( self , status_widths ) :
QW . QStatusBar . __init__ ( self )
self . _labels = [ ]
for w in status_widths :
label = QW . QLabel ( )
self . _labels . append ( label )
if w < 0 :
self . addWidget ( label , - 1 * w )
else :
label . setFixedWidth ( w )
self . addWidget ( label )
def SetStatusText ( self , text , index ) :
self . _labels [ index ] . setText ( text )
class AboutDialogInfo :
def __init__ ( self ) :
self . name = ' '
self . version = ' '
self . description = ' '
self . license = ' '
self . developers = [ ]
self . website = ' '
def SetName ( self , name ) :
self . name = name
def SetVersion ( self , version ) :
self . version = version
def SetDescription ( self , description ) :
self . description = description
def SetLicense ( self , license ) :
self . license = license
def SetDevelopers ( self , developers_list ) :
self . developers = developers_list
def SetWebSite ( self , url ) :
self . website = url
class UIActionSimulator :
def __init__ ( self ) :
pass
def Char ( self , key , text = None ) :
ev1 = QG . QKeyEvent ( QC . QEvent . KeyPress , key , QC . Qt . NoModifier , text = text )
ev2 = QG . QKeyEvent ( QC . QEvent . KeyRelease , key , QC . Qt . NoModifier , text = text )
QW . QApplication . postEvent ( QW . QApplication . focusWidget ( ) , ev1 )
QW . QApplication . postEvent ( QW . QApplication . focusWidget ( ) , ev2 )
class AboutBox ( QW . QDialog ) :
def __init__ ( self , parent , about_info ) :
QW . QDialog . __init__ ( self , parent )
self . setWindowFlag ( QC . Qt . WindowContextHelpButtonHint , on = False )
2019-12-05 05:29:32 +00:00
2019-11-20 23:10:46 +00:00
self . setAttribute ( QC . Qt . WA_DeleteOnClose )
2019-11-14 03:56:30 +00:00
self . setWindowIcon ( QG . QIcon ( HG . client_controller . frame_icon_pixmap ) )
2019-11-20 23:10:46 +00:00
layout = QW . QVBoxLayout ( self )
2019-11-14 03:56:30 +00:00
self . setWindowTitle ( ' About ' + about_info . name )
icon_label = QW . QLabel ( self )
name_label = QW . QLabel ( about_info . name , self )
version_label = QW . QLabel ( about_info . version , self )
tabwidget = QW . QTabWidget ( self )
desc_panel = QW . QWidget ( self )
desc_label = QW . QLabel ( about_info . description , self )
url_label = QW . QLabel ( ' <a href= " {0} " > {0} </a> ' . format ( about_info . website ) , self )
credits = QW . QTextEdit ( self )
license = QW . QTextEdit ( self )
close_button = QW . QPushButton ( ' close ' , self )
icon_label . setPixmap ( HG . client_controller . frame_icon_pixmap )
layout . addWidget ( icon_label , alignment = QC . Qt . AlignHCenter )
name_label_font = name_label . font ( )
name_label_font . setBold ( True )
name_label . setFont ( name_label_font )
layout . addWidget ( name_label , alignment = QC . Qt . AlignHCenter )
layout . addWidget ( version_label , alignment = QC . Qt . AlignHCenter )
layout . addWidget ( tabwidget , alignment = QC . Qt . AlignHCenter )
tabwidget . addTab ( desc_panel , ' Description ' )
tabwidget . addTab ( credits , ' Credits ' )
tabwidget . addTab ( license , ' License ' )
tabwidget . setCurrentIndex ( 0 )
credits . setPlainText ( ' Created by ' + ' , ' . join ( about_info . developers ) )
credits . setReadOnly ( True )
credits . setAlignment ( QC . Qt . AlignHCenter )
license . setPlainText ( about_info . license )
license . setReadOnly ( True )
desc_layout = QW . QVBoxLayout ( )
desc_layout . addWidget ( desc_label , alignment = QC . Qt . AlignHCenter )
desc_label . setWordWrap ( True )
desc_label . setAlignment ( QC . Qt . AlignHCenter | QC . Qt . AlignVCenter )
desc_layout . addWidget ( url_label , alignment = QC . Qt . AlignHCenter )
url_label . setTextFormat ( QC . Qt . RichText )
url_label . setTextInteractionFlags ( QC . Qt . TextBrowserInteraction )
url_label . setOpenExternalLinks ( True )
desc_panel . setLayout ( desc_layout )
layout . addWidget ( close_button , alignment = QC . Qt . AlignRight )
close_button . clicked . connect ( self . accept )
self . setLayout ( layout )
self . exec_ ( )
class CheckListBox ( QW . QListWidget ) :
checkListBoxChanged = QC . Signal ( int )
rightClicked = QC . Signal ( )
def __init__ ( self , parent = None ) :
QW . QListWidget . __init__ ( self , parent )
self . itemClicked . connect ( self . _ItemCheckStateChanged )
self . setSelectionMode ( QW . QAbstractItemView . ExtendedSelection )
def Check ( self , index , state = True ) :
item = self . item ( index )
item . setFlags ( item . flags ( ) | QC . Qt . ItemIsUserCheckable )
if state :
item . setCheckState ( QC . Qt . Checked )
else :
item . setCheckState ( QC . Qt . Unchecked )
def IsChecked ( self , index ) :
return self . item ( index ) . checkState ( ) == QC . Qt . Checked
def GetCheckedItems ( self ) :
indices = [ ]
for i in range ( self . count ( ) ) :
if self . item ( i ) . checkState ( ) == QC . Qt . Checked : indices . append ( i )
return indices
def GetSelections ( self ) :
indices = [ ]
for i in range ( self . count ( ) ) :
if self . item ( i ) . isSelected ( ) : indices . append ( i )
return indices
def SetCheckedItems ( self , items ) :
for i in range ( self . count ( ) ) :
if i in items :
self . item ( i ) . setCheckState ( QC . Qt . Checked )
else :
self . item ( i ) . setCheckState ( QC . Qt . Unchecked )
def Append ( self , str , client_data ) :
item = QW . QListWidgetItem ( )
item . setFlags ( item . flags ( ) | QC . Qt . ItemIsUserCheckable )
item . setCheckState ( QC . Qt . Unchecked )
item . setText ( str )
item . setData ( QC . Qt . UserRole , client_data )
self . addItem ( item )
def _ItemCheckStateChanged ( self , item ) :
self . checkListBoxChanged . emit ( self . row ( item ) )
def GetChecked ( self ) :
result = [ GetClientData ( self , index ) for index in self . GetCheckedItems ( ) ]
return result
def SetCheckedData ( self , datas ) :
for index in range ( self . count ( ) ) :
data = GetClientData ( self , index )
check_it = data in datas
self . Check ( index , check_it )
def mousePressEvent ( self , event ) :
if event . button ( ) == QC . Qt . RightButton :
self . rightClicked . emit ( )
2019-12-11 23:18:37 +00:00
2019-11-14 03:56:30 +00:00
else :
QW . QListWidget . mousePressEvent ( self , event )
class RadioBox ( QW . QFrame ) :
radioBoxChanged = QC . Signal ( )
def __init__ ( self , parent = None , choices = [ ] , vertical = False ) :
QW . QFrame . __init__ ( self , parent )
2019-12-18 22:06:34 +00:00
self . setFrameStyle ( QW . QFrame . Box | QW . QFrame . Raised )
2019-11-14 03:56:30 +00:00
if vertical :
self . setLayout ( VBoxLayout ( ) )
else :
self . setLayout ( HBoxLayout ( ) )
self . _choices = [ ]
for choice in choices :
radiobutton = QW . QRadioButton ( choice , self )
self . _choices . append ( radiobutton )
radiobutton . clicked . connect ( self . radioBoxChanged )
self . layout ( ) . addWidget ( radiobutton )
if vertical and len ( self . _choices ) :
self . _choices [ 0 ] . setChecked ( True )
elif len ( self . _choices ) :
self . _choices [ - 1 ] . setChecked ( True )
def GetCurrentIndex ( self ) :
for i in range ( len ( self . _choices ) ) :
if self . _choices [ i ] . isChecked ( ) : return i
return - 1
def SetStringSelection ( self , str ) :
for i in range ( len ( self . _choices ) ) :
if self . _choices [ i ] . text ( ) == str :
self . _choices [ i ] . setChecked ( True )
return
def GetStringSelection ( self ) :
for i in range ( len ( self . _choices ) ) :
if self . _choices [ i ] . isChecked ( ) : return self . _choices [ i ] . text ( )
return None
def Select ( self , idx ) :
self . _choices [ idx ] . setChecked ( True )
# Adapted from https://doc.qt.io/qt-5/qtwidgets-widgets-elidedlabel-example.html
class EllipsizedLabel ( QW . QLabel ) :
def __init__ ( self , parent = None , ellipsize_end = False ) :
QW . QLabel . __init__ ( self , parent )
self . _ellipsize_end = ellipsize_end
2019-11-20 23:10:46 +00:00
def minimumSizeHint ( self ) :
2019-11-14 03:56:30 +00:00
if self . _ellipsize_end :
2019-11-20 23:10:46 +00:00
return self . sizeHint ( )
else :
return QW . QLabel . minimumSizeHint ( self )
2019-11-14 03:56:30 +00:00
def setText ( self , text ) :
QW . QLabel . setText ( self , text )
self . update ( )
2019-11-20 23:10:46 +00:00
2019-11-14 03:56:30 +00:00
def sizeHint ( self ) :
2019-11-20 23:10:46 +00:00
if self . _ellipsize_end :
2019-11-14 03:56:30 +00:00
2019-11-20 23:10:46 +00:00
num_lines = self . text ( ) . count ( ' \n ' ) + 1
2019-11-14 03:56:30 +00:00
2019-11-20 23:10:46 +00:00
line_width = self . fontMetrics ( ) . lineWidth ( )
line_height = self . fontMetrics ( ) . lineSpacing ( )
2019-11-14 03:56:30 +00:00
2019-11-20 23:10:46 +00:00
size_hint = QC . QSize ( 3 * line_width , num_lines * line_height )
else :
size_hint = QW . QLabel . sizeHint ( self )
2019-11-14 03:56:30 +00:00
return size_hint
2019-11-20 23:10:46 +00:00
2019-11-14 03:56:30 +00:00
def paintEvent ( self , event ) :
if not self . _ellipsize_end :
QW . QLabel . paintEvent ( self , event )
return
2019-11-20 23:10:46 +00:00
2019-11-14 03:56:30 +00:00
painter = QG . QPainter ( self )
fontMetrics = painter . fontMetrics ( )
text_lines = self . text ( ) . split ( ' \n ' )
line_spacing = fontMetrics . lineSpacing ( )
2019-11-20 23:10:46 +00:00
current_y = 0
2019-11-14 03:56:30 +00:00
done = False
2019-11-20 23:10:46 +00:00
my_width = self . width ( )
2019-11-14 03:56:30 +00:00
for text_line in text_lines :
2019-11-20 23:10:46 +00:00
elided_line = fontMetrics . elidedText ( text_line , QC . Qt . ElideRight , my_width )
x = 0
width = my_width
height = line_spacing
flags = self . alignment ( )
painter . drawText ( x , current_y , width , height , flags , elided_line )
# old hacky line that doesn't support alignment flags
#painter.drawText( QC.QPoint( 0, current_y + fontMetrics.ascent() ), elided_line )
current_y + = line_spacing
# old code that did multiline wrap width stuff
'''
2019-11-14 03:56:30 +00:00
text_layout = QG . QTextLayout ( text_line , painter . font ( ) )
2019-11-20 23:10:46 +00:00
2019-11-14 03:56:30 +00:00
text_layout . beginLayout ( )
2019-11-20 23:10:46 +00:00
2019-11-14 03:56:30 +00:00
while True :
line = text_layout . createLine ( )
2019-11-20 23:10:46 +00:00
2019-11-14 03:56:30 +00:00
if not line . isValid ( ) : break
2019-11-20 23:10:46 +00:00
2019-11-14 03:56:30 +00:00
line . setLineWidth ( self . width ( ) )
2019-11-20 23:10:46 +00:00
2019-11-14 03:56:30 +00:00
next_line_y = y + line_spacing
if self . height ( ) > = next_line_y + line_spacing :
line . draw ( painter , QC . QPoint ( 0 , y ) )
y = next_line_y
2019-11-20 23:10:46 +00:00
2019-11-14 03:56:30 +00:00
else :
last_line = text_line [ line . textStart ( ) : ]
elided_last_line = fontMetrics . elidedText ( last_line , QC . Qt . ElideRight , self . width ( ) )
painter . drawText ( QC . QPoint ( 0 , y + fontMetrics . ascent ( ) ) , elided_last_line )
done = True
2019-11-20 23:10:46 +00:00
2019-11-14 03:56:30 +00:00
break
2019-11-20 23:10:46 +00:00
2019-11-14 03:56:30 +00:00
text_layout . endLayout ( )
if done : break
2019-11-20 23:10:46 +00:00
'''
2019-11-14 03:56:30 +00:00
class Dialog ( QW . QDialog ) :
def __init__ ( self , parent = None , * * kwargs ) :
title = None
if ' title ' in kwargs :
title = kwargs [ ' title ' ]
del kwargs [ ' title ' ]
QW . QDialog . __init__ ( self , parent , * * kwargs )
self . setWindowFlag ( QC . Qt . WindowContextHelpButtonHint , on = False )
if title is not None :
self . setWindowTitle ( title )
self . _closed_by_user = False
def closeEvent ( self , event ) :
if event . spontaneous ( ) :
self . _closed_by_user = True
QW . QDialog . closeEvent ( self , event )
# True if the dialog was closed by the user clicking on the X on the titlebar (so neither reject nor accept was chosen - the dialog result is still reject in this case though)
def WasCancelled ( self ) :
return self . _closed_by_user
def SetCancelled ( self , closed ) :
self . _closed_by_user = closed
def __enter__ ( self ) :
return self
2019-12-05 05:29:32 +00:00
def __exit__ ( self , exc_type , exc_val , exc_tb ) :
2019-11-14 03:56:30 +00:00
2019-12-05 05:29:32 +00:00
if isValid ( self ) :
self . deleteLater ( )
2019-11-14 03:56:30 +00:00
class PasswordEntryDialog ( Dialog ) :
def __init__ ( self , parent , message , caption ) :
Dialog . __init__ ( self , parent )
self . setWindowTitle ( caption )
self . _ok_button = QW . QPushButton ( ' OK ' , self )
self . _ok_button . clicked . connect ( self . accept )
self . _cancel_button = QW . QPushButton ( ' Cancel ' , self )
self . _cancel_button . clicked . connect ( self . reject )
self . _password = QW . QLineEdit ( self )
self . _password . setEchoMode ( QW . QLineEdit . Password )
self . setLayout ( QW . QVBoxLayout ( ) )
entry_layout = QW . QHBoxLayout ( )
entry_layout . addWidget ( QW . QLabel ( message , self ) )
entry_layout . addWidget ( self . _password )
button_layout = QW . QHBoxLayout ( )
button_layout . addStretch ( 1 )
button_layout . addWidget ( self . _cancel_button )
button_layout . addWidget ( self . _ok_button )
self . layout ( ) . addLayout ( entry_layout )
self . layout ( ) . addLayout ( button_layout )
2019-11-28 01:11:46 +00:00
2019-11-14 03:56:30 +00:00
def GetValue ( self ) :
return self . _password . text ( )
2019-11-28 01:11:46 +00:00
2019-11-14 03:56:30 +00:00
class DirDialog ( QW . QFileDialog ) :
def __init__ ( self , parent = None , message = None ) :
QW . QFileDialog . __init__ ( self , parent )
if message is not None : self . setWindowTitle ( message )
self . setAcceptMode ( QW . QFileDialog . AcceptOpen )
self . setFileMode ( QW . QFileDialog . Directory )
self . setOption ( QW . QFileDialog . ShowDirsOnly , True )
def __enter__ ( self ) :
return self
def __exit__ ( self , exc_type , exc_val , exc_tb ) :
self . deleteLater ( )
def _GetSelectedFiles ( self ) :
return [ os . path . normpath ( path ) for path in self . selectedFiles ( ) ]
def GetPath ( self ) :
sel = self . _GetSelectedFiles ( )
if len ( sel ) > 0 :
return sel [ 0 ]
return None
class FileDialog ( QW . QFileDialog ) :
def __init__ ( self , parent = None , message = None , acceptMode = QW . QFileDialog . AcceptOpen , fileMode = QW . QFileDialog . ExistingFile , defaultFile = None , wildcard = None ) :
QW . QFileDialog . __init__ ( self , parent )
if message is not None : self . setWindowTitle ( message )
self . setAcceptMode ( acceptMode )
self . setFileMode ( fileMode )
if defaultFile : self . setDirectory ( defaultFile )
if wildcard : self . setNameFilter ( wildcard )
def __enter__ ( self ) :
return self
def __exit__ ( self , exc_type , exc_val , exc_tb ) :
self . deleteLater ( )
def _GetSelectedFiles ( self ) :
return [ os . path . normpath ( path ) for path in self . selectedFiles ( ) ]
def GetPath ( self ) :
sel = self . _GetSelectedFiles ( )
if len ( sel ) > 0 :
return sel [ 0 ]
return None
def GetPaths ( self ) :
return self . _GetSelectedFiles ( )
# A QTreeWidget where if an item is (un)checked, all its children are also (un)checked, recursively
class TreeWidgetWithInheritedCheckState ( QW . QTreeWidget ) :
def __init__ ( self , * args , * * kwargs ) :
2019-12-11 23:18:37 +00:00
QW . QTreeWidget . __init__ ( self , * args , * * kwargs )
2019-11-14 03:56:30 +00:00
2019-12-18 22:06:34 +00:00
self . itemClicked . connect ( self . _HandleItemClickedForCheckStateUpdate )
2019-11-14 03:56:30 +00:00
2019-12-11 23:18:37 +00:00
2019-11-14 03:56:30 +00:00
def _HandleItemClickedForCheckStateUpdate ( self , item , column ) :
2019-12-18 22:06:34 +00:00
self . _UpdateCheckState ( item , item . checkState ( 0 ) )
2019-11-14 03:56:30 +00:00
2019-12-11 23:18:37 +00:00
2019-11-14 03:56:30 +00:00
def _UpdateCheckState ( self , item , check_state ) :
2019-12-18 22:06:34 +00:00
# this is an int, should be a checkstate
item . setCheckState ( 0 , check_state )
2019-11-14 03:56:30 +00:00
for i in range ( item . childCount ( ) ) :
self . _UpdateCheckState ( item . child ( i ) , check_state )
class ColourPickerCtrl ( QW . QPushButton ) :
def __init__ ( self , parent = None ) :
QW . QPushButton . __init__ ( self , parent )
self . _colour = QG . QColor ( 0 , 0 , 0 , 0 )
self . clicked . connect ( self . _ChooseColour )
self . _highlighted = False
def SetColour ( self , colour ) :
self . _colour = colour
self . _UpdatePixmap ( )
def _UpdatePixmap ( self ) :
px = QG . QPixmap ( self . contentsRect ( ) . height ( ) , self . contentsRect ( ) . height ( ) )
painter = QG . QPainter ( px )
colour = self . _colour
if self . _highlighted :
colour = self . _colour . lighter ( 125 ) # 25% lighter
painter . fillRect ( px . rect ( ) , QG . QBrush ( colour ) )
painter . end ( )
self . setIcon ( QG . QIcon ( px ) )
self . setIconSize ( px . size ( ) )
self . setFlat ( True )
self . setFixedSize ( px . size ( ) )
def enterEvent ( self , event ) :
self . _highlighted = True
self . _UpdatePixmap ( )
def leaveEvent ( self , event ) :
self . _highlighted = False
self . _UpdatePixmap ( )
def GetColour ( self ) :
return self . _colour
def _ChooseColour ( self ) :
new_colour = QW . QColorDialog . getColor ( initial = self . _colour )
if new_colour . isValid ( ) :
self . SetColour ( new_colour )
def ListsToTuples ( l ) : # Since lists are not hashable, we need to (recursively) convert lists to tuples in data that is to be added to BetterListCtrl
if isinstance ( l , list ) or isinstance ( l , tuple ) :
return tuple ( map ( ListsToTuples , l ) )
else :
return l
class WidgetEventFilter ( QC . QObject ) :
_mouse_tracking_required = { ' EVT_MOTION ' , ' EVT_MOUSE_EVENTS ' }
_strong_focus_required = { ' EVT_KEY_DOWN ' }
def __init__ ( self , parent_widget ) :
self . _parent_widget = parent_widget
QC . QObject . __init__ ( self , parent_widget )
parent_widget . installEventFilter ( self )
self . _callback_map = defaultdict ( list )
self . _user_moved_window = False # There is no EVT_MOVE_END in Qt so some trickery is required.
def _ExecuteCallbacks ( self , event_name , event ) :
if not event_name in self . _callback_map : return
event_killed = False
for callback in self . _callback_map [ event_name ] :
if not callback ( event ) : event_killed = True
return event_killed
def eventFilter ( self , watched , event ) :
# Once somehow this got called with no _parent_widget set - which is probably fixed now but leaving the check just in case, wew
# Might be worth debugging this later if it still occurs - the only way I found to reproduce it is to run the help > debug > initialize server command
if not hasattr ( self , ' _parent_widget ' ) or not isValid ( self . _parent_widget ) : return False
2019-11-20 23:10:46 +00:00
2019-11-14 03:56:30 +00:00
type = event . type ( )
2019-11-20 23:10:46 +00:00
2019-11-14 03:56:30 +00:00
event_killed = False
if type == QC . QEvent . KeyPress :
event_killed = event_killed or self . _ExecuteCallbacks ( ' EVT_KEY_DOWN ' , event )
elif type == QC . QEvent . Close :
event_killed = event_killed or self . _ExecuteCallbacks ( ' EVT_CLOSE ' , event )
elif type == QC . QEvent . WindowStateChange :
if isValid ( self . _parent_widget ) :
if self . _parent_widget . isMinimized ( ) or ( event . oldState ( ) & QC . Qt . WindowMinimized ) : event_killed = event_killed or self . _ExecuteCallbacks ( ' EVT_ICONIZE ' , event )
if self . _parent_widget . isMaximized ( ) or ( event . oldState ( ) & QC . Qt . WindowMaximized ) : event_killed = event_killed or self . _ExecuteCallbacks ( ' EVT_MAXIMIZE ' , event )
elif type == QC . QEvent . MouseMove :
event_killed = event_killed or self . _ExecuteCallbacks ( ' EVT_MOTION ' , event )
event_killed = event_killed or self . _ExecuteCallbacks ( ' EVT_MOUSE_EVENTS ' , event )
elif type == QC . QEvent . MouseButtonDblClick :
if event . button ( ) == QC . Qt . LeftButton :
event_killed = event_killed or self . _ExecuteCallbacks ( ' EVT_LEFT_DCLICK ' , event )
elif event . button ( ) == QC . Qt . RightButton :
event_killed = event_killed or self . _ExecuteCallbacks ( ' EVT_RIGHT_DCLICK ' , event )
event_killed = event_killed or self . _ExecuteCallbacks ( ' EVT_MOUSE_EVENTS ' , event )
elif type == QC . QEvent . MouseButtonPress :
if event . buttons ( ) & QC . Qt . LeftButton : event_killed = event_killed or self . _ExecuteCallbacks ( ' EVT_LEFT_DOWN ' , event )
if event . buttons ( ) & QC . Qt . MiddleButton : event_killed = event_killed or self . _ExecuteCallbacks ( ' EVT_MIDDLE_DOWN ' , event )
if event . buttons ( ) & QC . Qt . RightButton : event_killed = event_killed or self . _ExecuteCallbacks ( ' EVT_RIGHT_DOWN ' , event )
event_killed = event_killed or self . _ExecuteCallbacks ( ' EVT_MOUSE_EVENTS ' , event )
elif type == QC . QEvent . MouseButtonRelease :
if event . buttons ( ) & QC . Qt . LeftButton : event_killed = event_killed or self . _ExecuteCallbacks ( ' EVT_LEFT_UP ' , event )
event_killed = event_killed or self . _ExecuteCallbacks ( ' EVT_MOUSE_EVENTS ' , event )
elif type == QC . QEvent . Wheel :
event_killed = event_killed or self . _ExecuteCallbacks ( ' EVT_MOUSEWHEEL ' , event )
event_killed = event_killed or self . _ExecuteCallbacks ( ' EVT_MOUSE_EVENTS ' , event )
elif type == QC . QEvent . Scroll :
event_killed = event_killed or self . _ExecuteCallbacks ( ' EVT_SCROLLWIN ' , event )
elif type == QC . QEvent . Move :
event_killed = event_killed or self . _ExecuteCallbacks ( ' EVT_MOVE ' , event )
if isValid ( self . _parent_widget ) and self . _parent_widget . isVisible ( ) :
self . _user_moved_window = True
elif type == QC . QEvent . Resize :
event_killed = event_killed or self . _ExecuteCallbacks ( ' EVT_SIZE ' , event )
elif type == QC . QEvent . NonClientAreaMouseButtonPress :
self . _user_moved_window = False
elif type == QC . QEvent . NonClientAreaMouseButtonRelease :
if self . _user_moved_window :
event_killed = event_killed or self . _ExecuteCallbacks ( ' EVT_MOVE_END ' , event )
self . _user_moved_window = False
2020-02-12 22:50:37 +00:00
if event_killed :
event . accept ( )
return True
2019-11-14 03:56:30 +00:00
return False
def _AddCallback ( self , evt_name , callback ) :
if evt_name in self . _mouse_tracking_required :
self . _parent_widget . setMouseTracking ( True )
if evt_name in self . _strong_focus_required :
self . _parent_widget . setFocusPolicy ( QC . Qt . StrongFocus )
self . _callback_map [ evt_name ] . append ( callback )
def EVT_CLOSE ( self , callback ) :
self . _AddCallback ( ' EVT_CLOSE ' , callback )
def EVT_ICONIZE ( self , callback ) :
self . _AddCallback ( ' EVT_ICONIZE ' , callback )
def EVT_KEY_DOWN ( self , callback ) :
self . _AddCallback ( ' EVT_KEY_DOWN ' , callback )
def EVT_LEFT_DCLICK ( self , callback ) :
self . _AddCallback ( ' EVT_LEFT_DCLICK ' , callback )
def EVT_RIGHT_DCLICK ( self , callback ) :
self . _AddCallback ( ' EVT_RIGHT_DCLICK ' , callback )
def EVT_LEFT_DOWN ( self , callback ) :
self . _AddCallback ( ' EVT_LEFT_DOWN ' , callback )
def EVT_LEFT_UP ( self , callback ) :
self . _AddCallback ( ' EVT_LEFT_UP ' , callback )
def EVT_MAXIMIZE ( self , callback ) :
self . _AddCallback ( ' EVT_MAXIMIZE ' , callback )
def EVT_MIDDLE_DOWN ( self , callback ) :
self . _AddCallback ( ' EVT_MIDDLE_DOWN ' , callback )
def EVT_MOTION ( self , callback ) :
self . _AddCallback ( ' EVT_MOTION ' , callback )
def EVT_MOUSE_EVENTS ( self , callback ) :
self . _AddCallback ( ' EVT_MOUSE_EVENTS ' , callback )
def EVT_MOUSEWHEEL ( self , callback ) :
self . _AddCallback ( ' EVT_MOUSEWHEEL ' , callback )
def EVT_MOVE ( self , callback ) :
self . _AddCallback ( ' EVT_MOVE ' , callback )
def EVT_MOVE_END ( self , callback ) :
self . _AddCallback ( ' EVT_MOVE_END ' , callback )
def EVT_RIGHT_DOWN ( self , callback ) :
self . _AddCallback ( ' EVT_RIGHT_DOWN ' , callback )
def EVT_SCROLLWIN ( self , callback ) :
self . _AddCallback ( ' EVT_SCROLLWIN ' , callback )
def EVT_SIZE ( self , callback ) :
self . _AddCallback ( ' EVT_SIZE ' , callback )
# wew lad
# https://stackoverflow.com/questions/46456238/checkbox-not-visible-inside-combobox
class CheckBoxDelegate ( QW . QStyledItemDelegate ) :
def __init__ ( self , parent = None ) :
super ( CheckBoxDelegate , self ) . __init__ ( parent )
def createEditor ( self , parent , op , idx ) :
self . editor = QW . QCheckBox ( parent )
class CollectComboCtrl ( QW . QComboBox ) :
itemChanged = QC . Signal ( )
def __init__ ( self , parent , media_collect ) :
QW . QComboBox . __init__ ( self , parent )
self . view ( ) . pressed . connect ( self . _HandleItemPressed )
if QW . QApplication . style ( ) . metaObject ( ) . className ( ) == " QFusionStyle " :
self . setItemDelegate ( CheckBoxDelegate ( ) )
self . setModel ( QG . QStandardItemModel ( self ) )
text_and_data_tuples = set ( )
sort_by = HC . options [ ' sort_by ' ]
for ( sort_by_type , namespaces ) in sort_by :
text_and_data_tuples . update ( namespaces )
2019-11-20 23:10:46 +00:00
2019-11-14 03:56:30 +00:00
text_and_data_tuples = list ( [ ( namespace , ( ' namespace ' , namespace ) ) for namespace in text_and_data_tuples ] )
text_and_data_tuples . sort ( )
ratings_services = HG . client_controller . services_manager . GetServices ( ( HC . LOCAL_RATING_LIKE , HC . LOCAL_RATING_NUMERICAL ) )
for ratings_service in ratings_services :
text_and_data_tuples . append ( ( ratings_service . GetName ( ) , ( ' rating ' , ratings_service . GetServiceKey ( ) ) ) )
for ( text , data ) in text_and_data_tuples :
self . Append ( text , data )
# Trick to display custom text
self . _cached_text = ' '
2019-11-20 23:10:46 +00:00
if media_collect . DoesACollect ( ) :
CallAfter ( self . SetCollectByValue , media_collect )
2019-11-14 03:56:30 +00:00
def paintEvent ( self , e ) :
painter = QW . QStylePainter ( self )
painter . setPen ( self . palette ( ) . color ( QG . QPalette . Text ) )
opt = QW . QStyleOptionComboBox ( )
self . initStyleOption ( opt )
opt . currentText = self . _cached_text
painter . drawComplexControl ( QW . QStyle . CC_ComboBox , opt )
painter . drawControl ( QW . QStyle . CE_ComboBoxLabel , opt )
def GetValues ( self ) :
namespaces = [ ]
rating_service_keys = [ ]
for index in self . GetCheckedItems ( ) :
( collect_type , collect_data ) = GetClientData ( self , index )
if collect_type == ' namespace ' :
namespaces . append ( collect_data )
elif collect_type == ' rating ' :
rating_service_keys . append ( collect_data )
collect_strings = self . GetCheckedStrings ( )
if len ( collect_strings ) > 0 :
description = ' collect by ' + ' - ' . join ( collect_strings )
else :
description = ' no collections '
return ( namespaces , rating_service_keys , description )
def hidePopup ( self ) :
if not self . view ( ) . underMouse ( ) :
QW . QComboBox . hidePopup ( self )
def SetValue ( self , text ) :
self . _cached_text = text
self . setCurrentText ( text )
def SetCollectByValue ( self , media_collect ) :
try :
indices_to_check = [ ]
for index in range ( self . count ( ) ) :
( collect_type , collect_data ) = GetClientData ( self , index )
p1 = collect_type == ' namespace ' and collect_data in media_collect . namespaces
p2 = collect_type == ' rating ' and collect_data in media_collect . rating_service_keys
if p1 or p2 :
indices_to_check . append ( index )
if len ( indices_to_check ) > 0 :
self . SetCheckedItems ( indices_to_check )
self . itemChanged . emit ( )
except Exception as e :
HydrusData . ShowText ( ' Failed to set a collect-by value! ' )
HydrusData . ShowException ( e )
def SetCheckedItems ( self , indices_to_check ) :
for idx in range ( self . count ( ) ) :
item = self . model ( ) . item ( idx )
if idx in indices_to_check :
item . setCheckState ( QC . Qt . Checked )
else :
item . setCheckState ( QC . Qt . Unchecked )
def GetCheckedItems ( self ) :
indices = [ ]
for idx in range ( self . count ( ) ) :
item = self . model ( ) . item ( idx )
if item . checkState ( ) == QC . Qt . Checked : indices . append ( idx )
return indices
def GetCheckedStrings ( self ) :
strings = [ ]
for idx in range ( self . count ( ) ) :
item = self . model ( ) . item ( idx )
if item . checkState ( ) == QC . Qt . Checked : strings . append ( item . text ( ) )
return strings
def Append ( self , str , data ) :
self . addItem ( str , userData = data )
item = self . model ( ) . item ( self . count ( ) - 1 , 0 )
item . setCheckState ( QC . Qt . Unchecked )
def _HandleItemPressed ( self , index ) :
item = self . model ( ) . itemFromIndex ( index )
if item . checkState ( ) == QC . Qt . Checked :
item . setCheckState ( QC . Qt . Unchecked )
else :
item . setCheckState ( QC . Qt . Checked )
self . SetValue ( self . _cached_text )
self . itemChanged . emit ( )