hydrus/hydrus/client/gui/QtPorting.py

2786 lines
74 KiB
Python
Raw Normal View History

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
# 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 as e:
2019-11-14 03:56:30 +00:00
pass
#
import qtpy
from qtpy import QtCore as QC
from qtpy import QtWidgets as QW
from qtpy import QtGui as QG
2020-07-29 20:52:44 +00:00
2019-11-14 03:56:30 +00:00
import math
2020-05-06 21:31:41 +00:00
import typing
2020-07-29 20:52:44 +00:00
from collections import defaultdict
2019-11-14 03:56:30 +00:00
if qtpy.PYQT5:
2020-04-01 21:51:42 +00:00
import sip # pylint: disable=E0401
2020-04-22 21:00:35 +00:00
2019-11-14 03:56:30 +00:00
def isValid( obj ):
if isinstance( obj, sip.simplewrapper ):
return not sip.isdeleted( obj )
return True
elif qtpy.PYSIDE2:
import shiboken2
2020-04-22 21:00:35 +00:00
2019-11-14 03:56:30 +00:00
isValid = shiboken2.isValid
2020-04-22 21:00:35 +00:00
2019-11-14 03:56:30 +00:00
else:
2020-04-22 21:00:35 +00:00
2019-11-14 03:56:30 +00:00
raise RuntimeError( 'You need either PySide2 or PyQt5' )
2020-04-22 21:00:35 +00:00
from hydrus.core import HydrusConstants as HC
from hydrus.core import HydrusData
from hydrus.core import HydrusGlobals as HG
2020-07-29 20:52:44 +00:00
2020-04-22 21:00:35 +00:00
from hydrus.client import ClientConstants as CC
2019-11-14 03:56:30 +00:00
def MonkeyPatchMissingMethods():
if qtpy.PYQT5:
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() ):
2020-04-01 21:51:42 +00:00
if splitter.widget( i ).isVisibleTo( splitter ): count += 1
2019-11-14 03:56:30 +00:00
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()
2020-04-16 00:09:42 +00:00
if HG.client_controller.new_options.GetBoolean( 'use_qt_file_dialogs' ):
options = QW.QFileDialog.Options( QW.QFileDialog.DontUseNativeDialog )
else:
options = QW.QFileDialog.Options()
path = QW.QFileDialog.getExistingDirectory( self, '', existing_path, options = options )
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()
2020-03-18 21:35:57 +00:00
def __init__( self, parent = None, wildcard = None, starting_directory = None ):
2019-11-14 03:56:30 +00:00
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
2020-03-18 21:35:57 +00:00
self._starting_directory = starting_directory
2019-11-14 03:56:30 +00:00
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()
2020-03-18 21:35:57 +00:00
if existing_path == '' and self._starting_directory is not None:
existing_path = self._starting_directory
2020-04-16 00:09:42 +00:00
if HG.client_controller.new_options.GetBoolean( 'use_qt_file_dialogs' ):
options = QW.QFileDialog.Options( QW.QFileDialog.DontUseNativeDialog )
else:
options = QW.QFileDialog.Options()
2019-11-14 03:56:30 +00:00
if self._save_mode:
if self._wildcard:
2020-04-16 00:09:42 +00:00
path = QW.QFileDialog.getSaveFileName( self, '', existing_path, filter = self._wildcard, selectedFilter = self._wildcard, options = options )[0]
2019-11-14 03:56:30 +00:00
else:
2020-04-16 00:09:42 +00:00
path = QW.QFileDialog.getSaveFileName( self, '', existing_path, options = options )[0]
2019-11-14 03:56:30 +00:00
else:
2020-04-16 00:09:42 +00:00
2019-11-14 03:56:30 +00:00
if self._wildcard:
2020-04-16 00:09:42 +00:00
path = QW.QFileDialog.getOpenFileName( self, '', existing_path, filter = self._wildcard, selectedFilter = self._wildcard, options = options )[0]
2019-11-14 03:56:30 +00:00
else:
2020-04-16 00:09:42 +00:00
path = QW.QFileDialog.getOpenFileName( self, '', existing_path, options = options )[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 ):
2020-07-15 20:52:09 +00:00
tabDoubleLeftClicked = QC.Signal( int )
tabMiddleClicked = QC.Signal( int )
2020-07-22 20:59:16 +00:00
tabSpaceDoubleLeftClicked = QC.Signal()
tabSpaceDoubleMiddleClicked = QC.Signal()
2019-11-14 03:56:30 +00:00
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
2020-05-13 19:03:16 +00:00
def event( self, event ):
return QW.QTabBar.event( self, event )
2019-11-14 03:56:30 +00:00
def mouseMoveEvent( self, e ):
e.ignore()
def mousePressEvent( self, event ):
2020-07-22 20:59:16 +00:00
index = self.tabAt( event.pos() )
2019-11-14 03:56:30 +00:00
if event.button() == QC.Qt.LeftButton:
2020-07-22 20:59:16 +00:00
self._last_clicked_tab_index = index
2019-11-14 03:56:30 +00:00
2020-01-22 21:04:43 +00:00
self._last_clicked_global_pos = event.globalPos()
2021-03-17 21:59:28 +00:00
QW.QTabBar.mousePressEvent( self, event )
def mouseReleaseEvent( self, event ):
index = self.tabAt( event.pos() )
if event.button() == QC.Qt.MiddleButton:
2020-07-15 20:52:09 +00:00
if index != -1:
self.tabMiddleClicked.emit( index )
return
2019-11-14 03:56:30 +00:00
2021-03-17 21:59:28 +00:00
QW.QTabBar.mouseReleaseEvent( self, event )
2019-11-14 03:56:30 +00:00
2020-07-15 20:52:09 +00:00
def mouseDoubleClickEvent( self, event ):
2020-07-22 20:59:16 +00:00
index = self.tabAt( event.pos() )
2020-07-15 20:52:09 +00:00
if event.button() == QC.Qt.LeftButton:
2020-07-22 20:59:16 +00:00
if index == -1:
self.tabSpaceDoubleLeftClicked.emit()
else:
2020-07-15 20:52:09 +00:00
self.tabDoubleLeftClicked.emit( index )
2020-07-22 20:59:16 +00:00
return
2020-07-15 20:52:09 +00:00
2020-07-22 20:59:16 +00:00
elif event.button() == QC.Qt.MiddleButton:
2020-07-15 20:52:09 +00:00
2020-07-22 20:59:16 +00:00
if index == -1:
self.tabSpaceDoubleMiddleClicked.emit()
else:
self.tabMiddleClicked.emit( index )
2020-07-15 20:52:09 +00:00
2020-07-22 20:59:16 +00:00
return
QW.QTabBar.mouseDoubleClickEvent( self, event )
2020-07-15 20:52:09 +00:00
2019-11-14 03:56:30 +00:00
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 ):
2020-05-13 19:03:16 +00:00
2019-11-14 03:56:30 +00:00
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 ):
2020-05-13 19:03:16 +00:00
# hydev adds: I no longer call this, as I moved splitter setting to a thing called per page when page is first visibly shown
# leaving it here for now in case I need it again
2019-11-14 03:56:30 +00:00
# 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()
2020-04-01 21:51:42 +00:00
def SplitVertically( splitter: QW.QSplitter, w1, w2, hpos ):
2019-11-14 03:56:30 +00:00
splitter.setOrientation( QC.Qt.Horizontal )
if w1.parentWidget() != splitter:
2020-04-01 21:51:42 +00:00
splitter.addWidget( w1 )
2019-11-14 03:56:30 +00:00
w1.setVisible( True )
if w2.parentWidget() != splitter:
2020-04-01 21:51:42 +00:00
splitter.addWidget( w2 )
2019-11-14 03:56:30 +00:00
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 ] )
2020-04-01 21:51:42 +00:00
def SplitHorizontally( splitter: QW.QSplitter, w1, w2, vpos ):
2019-11-14 03:56:30 +00:00
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:
2020-04-01 21:51:42 +00:00
splitter.addWidget( w1 )
2019-11-14 03:56:30 +00:00
w1.setVisible( True )
if w2.parentWidget() != splitter:
2020-04-01 21:51:42 +00:00
splitter.addWidget( w2 )
2019-11-14 03:56:30 +00:00
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
2020-07-29 20:52:44 +00:00
def AddToLayout( layout, item, flag = None, alignment = None ):
2019-11-14 03:56:30 +00:00
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
zero_border = False
if flag is None or flag == CC.FLAGS_NONE:
2020-07-29 20:52:44 +00:00
2019-11-14 03:56:30 +00:00
pass
2020-07-29 20:52:44 +00:00
elif flag in ( CC.FLAGS_CENTER, CC.FLAGS_ON_LEFT, CC.FLAGS_ON_RIGHT, CC.FLAGS_CENTER_PERPENDICULAR, CC.FLAGS_CENTER_PERPENDICULAR_EXPAND_DEPTH ):
if flag == CC.FLAGS_CENTER:
alignment = QC.Qt.AlignVCenter | QC.Qt.AlignHCenter
if flag == CC.FLAGS_ON_LEFT:
alignment = QC.Qt.AlignLeft | QC.Qt.AlignVCenter
elif flag == CC.FLAGS_ON_RIGHT:
alignment = QC.Qt.AlignRight | QC.Qt.AlignVCenter
elif flag in ( CC.FLAGS_CENTER_PERPENDICULAR, CC.FLAGS_CENTER_PERPENDICULAR_EXPAND_DEPTH ):
if isinstance( layout, QW.QHBoxLayout ):
alignment = QC.Qt.AlignVCenter
else:
alignment = QC.Qt.AlignHCenter
layout.setAlignment( item, alignment )
if flag == CC.FLAGS_CENTER_PERPENDICULAR_EXPAND_DEPTH:
if isinstance( layout, QW.QVBoxLayout ) or isinstance( layout, QW.QHBoxLayout ):
layout.setStretchFactor( item, 5 )
if isinstance( item, QW.QLayout ):
zero_border = True
2020-07-22 20:59:16 +00:00
elif flag in ( CC.FLAGS_EXPAND_PERPENDICULAR, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR ):
if flag == CC.FLAGS_EXPAND_SIZER_PERPENDICULAR:
zero_border = True
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 )
2020-07-29 20:52:44 +00:00
elif 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 ):
if flag == CC.FLAGS_EXPAND_SIZER_BOTH_WAYS:
zero_border = True
2020-01-16 02:08:23 +00:00
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 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 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 ) )
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
2021-06-30 21:27:35 +00:00
if HG.callto_profile_mode:
summary = 'Profiling CallAfter Event: {}'.format( event._fn )
HydrusData.Profile( summary, 'event.Execute()', globals(), locals(), min_duration_ms = 3, show_summary = True )
else:
event.Execute()
2019-11-14 03:56:30 +00:00
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 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()
2020-02-26 22:28:52 +00:00
window.move( parent_window.frameGeometry().center() - window.rect().center() )
2019-11-14 03:56:30 +00:00
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 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 ]
2020-04-29 21:44:12 +00:00
count = widget.count()
2019-11-14 03:56:30 +00:00
for idx in idxs:
2020-04-29 21:44:12 +00:00
if 0 <= idx <= count -1:
widget.item( idx ).setSelected( True )
2019-11-14 03:56:30 +00:00
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 ):
2020-02-26 22:28:52 +00:00
colour = QG.QColor( *colour )
widget.setStyleSheet( '#{} {{ background-color: {} }}'.format( object_name, colour.name() ) )
2019-11-14 03:56:30 +00:00
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()) )
2020-02-26 22:28:52 +00:00
2019-11-14 03:56:30 +00:00
elif isinstance( colour, tuple ):
2020-02-26 22:28:52 +00:00
colour = QG.QColor( *colour )
widget.setStyleSheet( '#{} {{ color: {} }}'.format( object_name, colour.name() ) )
2019-11-14 03:56:30 +00:00
else:
widget.setStyleSheet( '#{} {{ color: {} }}'.format( object_name, QG.QColor( colour ).name() ) )
2020-02-26 22:28:52 +00:00
2019-11-14 03:56:30 +00:00
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 )
2020-06-17 21:31:54 +00:00
self._labels = []
2019-11-14 03:56:30 +00:00
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 )
2020-03-11 21:52:11 +00:00
2019-11-14 03:56:30 +00:00
2020-05-06 21:31:41 +00:00
def SetStatusText( self, text, index, tooltip = None ):
2019-11-14 03:56:30 +00:00
2020-05-06 21:31:41 +00:00
if tooltip is None:
tooltip = text
cell = self._labels[ index ]
if cell.text() != text:
cell.setText( text )
if cell.toolTip() != tooltip:
cell.setToolTip( tooltip )
2020-03-11 21:52:11 +00:00
2019-11-14 03:56:30 +00:00
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
2020-04-29 21:44:12 +00:00
def Char( self, widget, key, text = None ):
if widget is None:
widget = QW.QApplication.focusWidget()
2019-11-14 03:56:30 +00:00
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.instance().postEvent( widget, ev1 )
QW.QApplication.instance().postEvent( widget, ev2 )
2019-11-14 03:56:30 +00:00
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 )
2020-04-16 00:09:42 +00:00
2019-11-14 03:56:30 +00:00
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 ):
2020-03-18 21:35:57 +00:00
result = [ self.item( index ).data( QC.Qt.UserRole ) for index in self.GetCheckedItems() ]
2019-11-14 03:56:30 +00:00
return result
def SetCheckedData( self, datas ):
for index in range( self.count() ):
2020-05-20 21:36:02 +00:00
data = self.item( index ).data( QC.Qt.UserRole )
check_it = data in datas
self.Check( index, check_it )
2019-11-14 03:56:30 +00:00
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 )
2020-05-20 21:36:02 +00:00
2019-11-14 03:56:30 +00:00
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
2020-11-18 22:15:21 +00:00
2019-11-14 03:56:30 +00:00
def GetStringSelection( self ):
for i in range( len( self._choices ) ):
if self._choices[ i ].isChecked(): return self._choices[ i ].text()
return None
2020-11-18 22:15:21 +00:00
def SetValue( self, data ):
pass
2019-11-14 03:56:30 +00:00
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 ):
2020-09-02 21:10:41 +00:00
try:
QW.QLabel.setText( self, text )
except ValueError:
QW.QLabel.setText( self, repr( text ) )
2019-11-14 03:56:30 +00:00
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 )
2020-04-16 00:09:42 +00:00
if HG.client_controller.new_options.GetBoolean( 'use_qt_file_dialogs' ):
self.setOption( QW.QFileDialog.DontUseNativeDialog, True )
2019-11-14 03:56:30 +00:00
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 ):
2020-04-16 00:09:42 +00:00
def __init__( self, parent = None, message = None, acceptMode = QW.QFileDialog.AcceptOpen, fileMode = QW.QFileDialog.ExistingFile, default_filename = None, default_directory = None, wildcard = None, defaultSuffix = None ):
2019-11-14 03:56:30 +00:00
QW.QFileDialog.__init__( self, parent )
if message is not None:
self.setWindowTitle( message )
2019-11-14 03:56:30 +00:00
self.setAcceptMode( acceptMode )
2019-11-14 03:56:30 +00:00
self.setFileMode( fileMode )
if default_directory is not None:
self.setDirectory( default_directory )
if defaultSuffix is not None:
self.setDefaultSuffix( defaultSuffix )
if default_filename is not None:
self.selectFile( default_filename )
2019-11-14 03:56:30 +00:00
if wildcard:
self.setNameFilter( wildcard )
2020-04-16 00:09:42 +00:00
if HG.client_controller.new_options.GetBoolean( 'use_qt_file_dialogs' ):
self.setOption( QW.QFileDialog.DontUseNativeDialog, True )
2019-11-14 03:56:30 +00:00
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 ):
2020-05-20 21:36:02 +00:00
_mouse_tracking_required = { 'EVT_MOUSE_EVENTS' }
2019-11-14 03:56:30 +00:00
_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_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_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 ):
2021-05-27 00:09:06 +00:00
2019-11-14 03:56:30 +00:00
itemChanged = QC.Signal()
2021-05-27 00:09:06 +00:00
2019-11-14 03:56:30 +00:00
def __init__( self, parent, media_collect ):
QW.QComboBox.__init__( self, parent )
self.view().pressed.connect( self._HandleItemPressed )
2020-03-18 21:35:57 +00:00
# this was previously 'if Fusion style only', but as it works for normal styles too, it is more helpful to have it always on
self.setItemDelegate( CheckBoxDelegate() )
2019-11-14 03:56:30 +00:00
self.setModel( QG.QStandardItemModel( self ) )
2021-05-27 00:09:06 +00:00
2019-11-14 03:56:30 +00:00
text_and_data_tuples = set()
2021-05-27 00:09:06 +00:00
for media_sort in HG.client_controller.new_options.GetDefaultNamespaceSorts():
namespaces = media_sort.GetNamespaces()
2019-11-14 03:56:30 +00:00
text_and_data_tuples.update( namespaces )
2019-11-20 23:10:46 +00:00
2021-05-27 00:09:06 +00:00
2020-05-13 19:03:16 +00:00
text_and_data_tuples = sorted( ( ( namespace, ( 'namespace', namespace ) ) for namespace in text_and_data_tuples ) )
2019-11-14 03:56:30 +00:00
2020-03-11 21:52:11 +00:00
ratings_services = HG.client_controller.services_manager.GetServices( ( HC.LOCAL_RATING_LIKE, HC.LOCAL_RATING_NUMERICAL ) )
2021-05-27 00:09:06 +00:00
2019-11-14 03:56:30 +00:00
for ratings_service in ratings_services:
text_and_data_tuples.append( ( ratings_service.GetName(), ('rating', ratings_service.GetServiceKey() ) ) )
2020-03-11 21:52:11 +00:00
2019-11-14 03:56:30 +00:00
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():
2020-03-18 21:35:57 +00:00
(collect_type, collect_data) = self.itemData( index, QC.Qt.UserRole )
2019-11-14 03:56:30 +00:00
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:
2020-03-04 22:12:53 +00:00
2019-11-14 03:56:30 +00:00
description = 'collect by ' + '-'.join( collect_strings )
2020-03-04 22:12:53 +00:00
2019-11-14 03:56:30 +00:00
else:
description = 'no collections'
2020-03-04 22:12:53 +00:00
2019-11-14 03:56:30 +00:00
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:
2020-03-11 21:52:11 +00:00
indices_to_check = []
2019-11-14 03:56:30 +00:00
for index in range( self.count() ):
2020-03-18 21:35:57 +00:00
( collect_type, collect_data ) = self.itemData( index, QC.Qt.UserRole )
2019-11-14 03:56:30 +00:00
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 )
2020-03-11 21:52:11 +00:00
self.SetCheckedItems( indices_to_check )
2019-11-14 03:56:30 +00:00
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:
2020-03-11 21:52:11 +00:00
2019-11-14 03:56:30 +00:00
item.setCheckState( QC.Qt.Checked )
2020-03-11 21:52:11 +00:00
2019-11-14 03:56:30 +00:00
else:
2020-03-11 21:52:11 +00:00
2019-11-14 03:56:30 +00:00
item.setCheckState( QC.Qt.Unchecked )
2020-03-11 21:52:11 +00:00
2019-11-14 03:56:30 +00:00
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()