hydrus/hydrus/client/gui/widgets/ClientGUIControls.py

524 lines
16 KiB
Python
Raw Normal View History

2020-05-13 19:03:16 +00:00
import typing
2019-11-14 03:56:30 +00:00
from qtpy import QtCore as QC
from qtpy import QtWidgets as QW
2020-04-22 21:00:35 +00:00
from hydrus.core import HydrusConstants as HC
from hydrus.core import HydrusData
from hydrus.core import HydrusExceptions
from hydrus.core import HydrusGlobals as HG
from hydrus.core import HydrusText
2021-04-07 21:26:45 +00:00
from hydrus.core.networking import HydrusNetworking
2020-07-29 20:52:44 +00:00
2020-04-22 21:00:35 +00:00
from hydrus.client import ClientConstants as CC
from hydrus.client.gui import ClientGUIFunctions
2020-04-22 21:00:35 +00:00
from hydrus.client.gui import ClientGUIScrolledPanels
from hydrus.client.gui import ClientGUITime
2020-04-29 21:44:12 +00:00
from hydrus.client.gui import ClientGUITopLevelWindowsPanels
2020-04-22 21:00:35 +00:00
from hydrus.client.gui import QtPorting as QP
2020-07-15 20:52:09 +00:00
from hydrus.client.gui.lists import ClientGUIListConstants as CGLC
from hydrus.client.gui.lists import ClientGUIListCtrl
2021-03-17 21:59:28 +00:00
from hydrus.client.gui.widgets import ClientGUICommon
2017-03-02 02:14:56 +00:00
class BandwidthRulesCtrl( ClientGUICommon.StaticBox ):
def __init__( self, parent, bandwidth_rules ):
ClientGUICommon.StaticBox.__init__( self, parent, 'bandwidth rules' )
2017-10-18 19:41:25 +00:00
listctrl_panel = ClientGUIListCtrl.BetterListCtrlPanel( self )
2017-03-02 02:14:56 +00:00
2020-07-15 20:52:09 +00:00
# example for later:
'''
def sort_call( desired_columns, rule ):
( bandwidth_type, time_delta, max_allowed ) = rule
sort_time_delta = SafeNoneInt( time_delta )
result = {}
result[ CGLC.COLUMN_LIST_BANDWIDTH_RULES.MAX_ALLOWED ] = max_allowed
result[ CGLC.COLUMN_LIST_BANDWIDTH_RULES.EVERY ] = sort_time_delta
return result
2018-08-01 20:44:57 +00:00
2020-07-15 20:52:09 +00:00
def display_call( desired_columns, rule ):
( bandwidth_type, time_delta, max_allowed ) = rule
if bandwidth_type == HC.BANDWIDTH_TYPE_DATA:
pretty_max_allowed = HydrusData.ToHumanBytes( max_allowed )
elif bandwidth_type == HC.BANDWIDTH_TYPE_REQUESTS:
pretty_max_allowed = '{} requests'.format( HydrusData.ToHumanInt( max_allowed ) )
pretty_time_delta = HydrusData.TimeDeltaToPrettyTimeDelta( time_delta )
result = {}
result[ CGLC.COLUMN_LIST_BANDWIDTH_RULES.MAX_ALLOWED ] = pretty_max_allowed
result[ CGLC.COLUMN_LIST_BANDWIDTH_RULES.EVERY ] = pretty_time_delta
return result
'''
self._listctrl = ClientGUIListCtrl.BetterListCtrl( listctrl_panel, CGLC.COLUMN_LIST_BANDWIDTH_RULES.ID, 8, self._ConvertRuleToListCtrlTuples, use_simple_delete = True, activation_callback = self._Edit )
2017-07-27 00:47:13 +00:00
listctrl_panel.SetListCtrl( self._listctrl )
listctrl_panel.AddButton( 'add', self._Add )
listctrl_panel.AddButton( 'edit', self._Edit, enabled_only_on_selection = True )
2018-10-17 21:00:09 +00:00
listctrl_panel.AddDeleteButton()
2017-03-02 02:14:56 +00:00
#
2017-10-18 19:41:25 +00:00
self._listctrl.AddDatas( bandwidth_rules.GetRules() )
2020-07-15 20:52:09 +00:00
self._listctrl.Sort()
2017-03-02 02:14:56 +00:00
#
2018-01-03 22:37:30 +00:00
self.Add( listctrl_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
2017-03-02 02:14:56 +00:00
def _Add( self ):
rule = ( HC.BANDWIDTH_TYPE_DATA, None, 1024 * 1024 * 100 )
2020-04-29 21:44:12 +00:00
with ClientGUITopLevelWindowsPanels.DialogEdit( self, 'edit rule' ) as dlg:
2017-03-02 02:14:56 +00:00
panel = self._EditPanel( dlg, rule )
dlg.SetPanel( panel )
2019-11-14 03:56:30 +00:00
if dlg.exec() == QW.QDialog.Accepted:
2017-03-02 02:14:56 +00:00
new_rule = panel.GetValue()
2017-10-18 19:41:25 +00:00
self._listctrl.AddDatas( ( new_rule, ) )
2017-03-02 02:14:56 +00:00
2017-10-18 19:41:25 +00:00
self._listctrl.Sort()
2017-03-02 02:14:56 +00:00
2018-10-17 21:00:09 +00:00
def _ConvertRuleToListCtrlTuples( self, rule ):
2017-03-02 02:14:56 +00:00
( bandwidth_type, time_delta, max_allowed ) = rule
2018-07-04 20:48:28 +00:00
pretty_time_delta = HydrusData.TimeDeltaToPrettyTimeDelta( time_delta )
2017-03-02 02:14:56 +00:00
if bandwidth_type == HC.BANDWIDTH_TYPE_DATA:
2019-01-09 22:59:03 +00:00
pretty_max_allowed = HydrusData.ToHumanBytes( max_allowed )
2017-03-02 02:14:56 +00:00
elif bandwidth_type == HC.BANDWIDTH_TYPE_REQUESTS:
2018-07-04 20:48:28 +00:00
pretty_max_allowed = HydrusData.ToHumanInt( max_allowed ) + ' requests'
2017-03-02 02:14:56 +00:00
2019-01-09 22:59:03 +00:00
sort_time_delta = ClientGUIListCtrl.SafeNoneInt( time_delta )
sort_tuple = ( max_allowed, sort_time_delta )
2018-03-14 21:01:02 +00:00
display_tuple = ( pretty_max_allowed, pretty_time_delta )
2017-10-18 19:41:25 +00:00
return ( display_tuple, sort_tuple )
2017-03-02 02:14:56 +00:00
def _Edit( self ):
2017-10-18 19:41:25 +00:00
selected_rules = self._listctrl.GetData( only_selected = True )
2017-03-02 02:14:56 +00:00
2022-03-09 22:18:23 +00:00
edited_datas = []
2017-10-18 19:41:25 +00:00
for rule in selected_rules:
2017-03-02 02:14:56 +00:00
2020-04-29 21:44:12 +00:00
with ClientGUITopLevelWindowsPanels.DialogEdit( self, 'edit rule' ) as dlg:
2017-03-02 02:14:56 +00:00
panel = self._EditPanel( dlg, rule )
dlg.SetPanel( panel )
2019-11-14 03:56:30 +00:00
if dlg.exec() == QW.QDialog.Accepted:
2017-03-02 02:14:56 +00:00
edited_rule = panel.GetValue()
2017-10-18 19:41:25 +00:00
self._listctrl.DeleteDatas( ( rule, ) )
2017-03-02 02:14:56 +00:00
2017-10-18 19:41:25 +00:00
self._listctrl.AddDatas( ( edited_rule, ) )
2017-03-02 02:14:56 +00:00
2022-03-09 22:18:23 +00:00
edited_datas.append( edited_rule )
2017-03-02 02:14:56 +00:00
else:
break
2022-03-09 22:18:23 +00:00
self._listctrl.SelectDatas( edited_datas )
2017-10-18 19:41:25 +00:00
self._listctrl.Sort()
2017-03-02 02:14:56 +00:00
def GetValue( self ):
bandwidth_rules = HydrusNetworking.BandwidthRules()
2017-10-18 19:41:25 +00:00
for rule in self._listctrl.GetData():
( bandwidth_type, time_delta, max_allowed ) = rule
2017-03-02 02:14:56 +00:00
bandwidth_rules.AddRule( bandwidth_type, time_delta, max_allowed )
return bandwidth_rules
class _EditPanel( ClientGUIScrolledPanels.EditPanel ):
def __init__( self, parent, rule ):
ClientGUIScrolledPanels.EditPanel.__init__( self, parent )
self._bandwidth_type = ClientGUICommon.BetterChoice( self )
2019-11-14 03:56:30 +00:00
self._bandwidth_type.addItem( 'data', HC.BANDWIDTH_TYPE_DATA )
self._bandwidth_type.addItem( 'requests', HC.BANDWIDTH_TYPE_REQUESTS )
2017-03-02 02:14:56 +00:00
2019-11-14 03:56:30 +00:00
self._bandwidth_type.currentIndexChanged.connect( self._UpdateEnabled )
2017-03-02 02:14:56 +00:00
2018-03-14 21:01:02 +00:00
self._max_allowed_bytes = BytesControl( self )
2022-03-30 20:28:13 +00:00
self._max_allowed_requests = ClientGUICommon.BetterSpinBox( self, min=1, max=1048576 )
2017-03-02 02:14:56 +00:00
2018-03-14 21:01:02 +00:00
self._time_delta = ClientGUITime.TimeDeltaButton( self, min = 1, days = True, hours = True, minutes = True, seconds = True, monthly_allowed = True )
2017-03-02 02:14:56 +00:00
#
( bandwidth_type, time_delta, max_allowed ) = rule
2019-07-24 21:39:02 +00:00
self._bandwidth_type.SetValue( bandwidth_type )
2017-03-02 02:14:56 +00:00
self._time_delta.SetValue( time_delta )
if bandwidth_type == HC.BANDWIDTH_TYPE_DATA:
2018-03-14 21:01:02 +00:00
self._max_allowed_bytes.SetValue( max_allowed )
else:
2019-11-14 03:56:30 +00:00
self._max_allowed_requests.setValue( max_allowed )
2017-03-02 02:14:56 +00:00
2018-03-14 21:01:02 +00:00
self._UpdateEnabled()
2017-03-02 02:14:56 +00:00
#
2019-11-14 03:56:30 +00:00
hbox = QP.HBoxLayout()
2017-03-02 02:14:56 +00:00
2020-07-29 20:52:44 +00:00
QP.AddToLayout( hbox, self._max_allowed_bytes, CC.FLAGS_CENTER_PERPENDICULAR )
QP.AddToLayout( hbox, self._max_allowed_requests, CC.FLAGS_CENTER_PERPENDICULAR )
QP.AddToLayout( hbox, self._bandwidth_type, CC.FLAGS_CENTER_PERPENDICULAR )
QP.AddToLayout( hbox, ClientGUICommon.BetterStaticText(self,' every '), CC.FLAGS_CENTER_PERPENDICULAR )
QP.AddToLayout( hbox, self._time_delta, CC.FLAGS_CENTER_PERPENDICULAR )
2017-03-02 02:14:56 +00:00
2019-11-14 03:56:30 +00:00
self.widget().setLayout( hbox )
2017-03-02 02:14:56 +00:00
2018-03-14 21:01:02 +00:00
def _UpdateEnabled( self ):
2017-03-02 02:14:56 +00:00
2019-07-24 21:39:02 +00:00
bandwidth_type = self._bandwidth_type.GetValue()
2017-03-02 02:14:56 +00:00
if bandwidth_type == HC.BANDWIDTH_TYPE_DATA:
2019-11-14 03:56:30 +00:00
self._max_allowed_bytes.show()
self._max_allowed_requests.hide()
2017-03-02 02:14:56 +00:00
elif bandwidth_type == HC.BANDWIDTH_TYPE_REQUESTS:
2019-11-14 03:56:30 +00:00
self._max_allowed_bytes.hide()
self._max_allowed_requests.show()
2017-03-02 02:14:56 +00:00
def GetValue( self ):
2019-07-24 21:39:02 +00:00
bandwidth_type = self._bandwidth_type.GetValue()
2017-03-02 02:14:56 +00:00
time_delta = self._time_delta.GetValue()
if bandwidth_type == HC.BANDWIDTH_TYPE_DATA:
2018-03-14 21:01:02 +00:00
max_allowed = self._max_allowed_bytes.GetValue()
elif bandwidth_type == HC.BANDWIDTH_TYPE_REQUESTS:
2019-11-14 03:56:30 +00:00
max_allowed = self._max_allowed_requests.value()
2017-03-02 02:14:56 +00:00
return ( bandwidth_type, time_delta, max_allowed )
2017-03-08 23:23:12 +00:00
2019-11-14 03:56:30 +00:00
class BytesControl( QW.QWidget ):
valueChanged = QC.Signal()
2018-03-14 21:01:02 +00:00
def __init__( self, parent, initial_value = 65536 ):
2019-11-14 03:56:30 +00:00
QW.QWidget.__init__( self, parent )
2018-03-22 00:03:33 +00:00
2022-03-30 20:28:13 +00:00
self._spin = ClientGUICommon.BetterSpinBox( self, min=0, max=1048576 )
2018-03-14 21:01:02 +00:00
self._unit = ClientGUICommon.BetterChoice( self )
2019-11-14 03:56:30 +00:00
self._unit.addItem( 'B', 1 )
self._unit.addItem( 'KB', 1024 )
self._unit.addItem( 'MB', 1024 * 1024 )
self._unit.addItem( 'GB', 1024 * 1024 * 1024 )
2018-03-14 21:01:02 +00:00
#
self.SetValue( initial_value )
#
2019-11-14 03:56:30 +00:00
hbox = QP.HBoxLayout()
2018-04-25 22:07:52 +00:00
2020-07-29 20:52:44 +00:00
QP.AddToLayout( hbox, self._spin, CC.FLAGS_CENTER_PERPENDICULAR )
QP.AddToLayout( hbox, self._unit, CC.FLAGS_CENTER_PERPENDICULAR )
2018-04-25 22:07:52 +00:00
2019-11-14 03:56:30 +00:00
self.setLayout( hbox )
min_width = ClientGUIFunctions.ConvertTextToPixelWidth( self._unit, 8 )
self._unit.setMinimumWidth( min_width )
2018-04-25 22:07:52 +00:00
2019-11-14 03:56:30 +00:00
self._spin.valueChanged.connect( self._HandleValueChanged )
self._unit.currentIndexChanged.connect( self._HandleValueChanged )
2018-04-25 22:07:52 +00:00
2021-05-05 20:12:11 +00:00
2019-11-14 03:56:30 +00:00
def _HandleValueChanged( self, val ):
2018-04-25 22:07:52 +00:00
2019-11-14 03:56:30 +00:00
self.valueChanged.emit()
2021-02-24 22:35:18 +00:00
2018-04-25 22:07:52 +00:00
2018-03-14 21:01:02 +00:00
def GetSeparatedValue( self ):
2019-11-14 03:56:30 +00:00
return (self._spin.value(), self._unit.GetValue())
2018-03-14 21:01:02 +00:00
def GetValue( self ):
2019-11-14 03:56:30 +00:00
return self._spin.value() * self._unit.GetValue()
2018-03-14 21:01:02 +00:00
def SetSeparatedValue( self, value, unit ):
2019-11-14 03:56:30 +00:00
return (self._spin.setValue( value ), self._unit.SetValue( unit ))
2018-03-14 21:01:02 +00:00
2020-05-13 19:03:16 +00:00
def SetValue( self, value: int ):
2018-03-14 21:01:02 +00:00
max_unit = 1024 * 1024 * 1024
unit = 1
while value % 1024 == 0 and unit < max_unit:
2019-01-09 22:59:03 +00:00
value //= 1024
2018-03-14 21:01:02 +00:00
unit *= 1024
2019-11-14 03:56:30 +00:00
self._spin.setValue( value )
2019-07-24 21:39:02 +00:00
self._unit.SetValue( unit )
2018-03-14 21:01:02 +00:00
2020-05-13 19:03:16 +00:00
class NoneableBytesControl( QW.QWidget ):
valueChanged = QC.Signal()
2018-03-14 21:01:02 +00:00
2020-05-13 19:03:16 +00:00
def __init__( self, parent, initial_value = 65536, none_label = 'no limit' ):
2018-03-14 21:01:02 +00:00
2020-05-13 19:03:16 +00:00
QW.QWidget.__init__( self, parent )
2018-03-14 21:01:02 +00:00
2020-05-13 19:03:16 +00:00
self._bytes = BytesControl( self )
2018-03-14 21:01:02 +00:00
2020-05-13 19:03:16 +00:00
self._none_checkbox = QW.QCheckBox( none_label, self )
2018-03-14 21:01:02 +00:00
2020-05-13 19:03:16 +00:00
#
2018-03-14 21:01:02 +00:00
2020-05-13 19:03:16 +00:00
self.SetValue( initial_value )
2018-03-14 21:01:02 +00:00
2020-05-13 19:03:16 +00:00
#
2018-03-14 21:01:02 +00:00
2020-05-13 19:03:16 +00:00
hbox = QP.HBoxLayout()
2018-03-14 21:01:02 +00:00
2020-07-29 20:52:44 +00:00
QP.AddToLayout( hbox, self._bytes, CC.FLAGS_CENTER_PERPENDICULAR )
QP.AddToLayout( hbox, self._none_checkbox, CC.FLAGS_CENTER_PERPENDICULAR )
2018-03-14 21:01:02 +00:00
2020-05-13 19:03:16 +00:00
self.setLayout( hbox )
2018-03-14 21:01:02 +00:00
#
2020-05-13 19:03:16 +00:00
self._none_checkbox.clicked.connect( self._UpdateEnabled )
self._bytes.valueChanged.connect( self._HandleValueChanged )
self._none_checkbox.clicked.connect( self._HandleValueChanged )
2018-03-14 21:01:02 +00:00
2020-05-13 19:03:16 +00:00
def _UpdateEnabled( self ):
if self._none_checkbox.isChecked():
2018-03-14 21:01:02 +00:00
2020-05-13 19:03:16 +00:00
self._bytes.setEnabled( False )
2018-03-14 21:01:02 +00:00
else:
2020-05-13 19:03:16 +00:00
self._bytes.setEnabled( True )
2018-03-14 21:01:02 +00:00
2020-05-13 19:03:16 +00:00
def _HandleValueChanged( self ):
2018-04-25 22:07:52 +00:00
2020-05-13 19:03:16 +00:00
self.valueChanged.emit()
2018-03-14 21:01:02 +00:00
2020-05-13 19:03:16 +00:00
def GetValue( self ):
2018-03-14 21:01:02 +00:00
2020-05-13 19:03:16 +00:00
if self._none_checkbox.isChecked():
return None
else:
return self._bytes.GetValue()
2018-03-22 00:03:33 +00:00
2020-05-13 19:03:16 +00:00
def setToolTip( self, text ):
2018-03-22 00:03:33 +00:00
2020-05-13 19:03:16 +00:00
QW.QWidget.setToolTip( self, text )
2018-10-17 21:00:09 +00:00
2020-05-13 19:03:16 +00:00
for c in self.children():
if isinstance( c, QW.QWidget ):
c.setToolTip( text )
2018-03-22 00:03:33 +00:00
2018-03-14 21:01:02 +00:00
2020-05-13 19:03:16 +00:00
def SetValue( self, value ):
2018-10-17 21:00:09 +00:00
2020-05-13 19:03:16 +00:00
if value is None:
2020-02-05 22:55:21 +00:00
2020-05-13 19:03:16 +00:00
self._none_checkbox.setChecked( True )
2020-02-05 22:55:21 +00:00
2020-05-13 19:03:16 +00:00
else:
2020-02-05 22:55:21 +00:00
2020-05-13 19:03:16 +00:00
self._none_checkbox.setChecked( False )
2020-02-05 22:55:21 +00:00
2020-05-13 19:03:16 +00:00
self._bytes.SetValue( value )
2020-02-05 22:55:21 +00:00
2020-05-13 19:03:16 +00:00
self._UpdateEnabled()
2018-03-14 21:01:02 +00:00
2020-05-13 19:03:16 +00:00
2019-11-14 03:56:30 +00:00
class TextAndPasteCtrl( QW.QWidget ):
2018-05-16 20:09:50 +00:00
2018-08-08 20:29:54 +00:00
def __init__( self, parent, add_callable, allow_empty_input = False ):
2018-05-16 20:09:50 +00:00
self._add_callable = add_callable
2018-08-08 20:29:54 +00:00
self._allow_empty_input = allow_empty_input
2018-05-16 20:09:50 +00:00
2019-11-14 03:56:30 +00:00
QW.QWidget.__init__( self, parent )
2018-05-16 20:09:50 +00:00
2019-11-14 03:56:30 +00:00
self._text_input = QW.QLineEdit( self )
self._text_input.installEventFilter( ClientGUICommon.TextCatchEnterEventFilter( self._text_input, self.EnterText ) )
2018-05-16 20:09:50 +00:00
2020-03-11 21:52:11 +00:00
self._paste_button = ClientGUICommon.BetterBitmapButton( self, CC.global_pixmaps().paste, self._Paste )
2019-11-14 03:56:30 +00:00
self._paste_button.setToolTip( 'Paste multiple inputs from the clipboard. Assumes the texts are newline-separated.' )
2018-05-16 20:09:50 +00:00
self.setFocusProxy( self._text_input )
2018-05-16 20:09:50 +00:00
#
2019-11-14 03:56:30 +00:00
hbox = QP.HBoxLayout()
2018-05-16 20:09:50 +00:00
2019-11-14 03:56:30 +00:00
QP.AddToLayout( hbox, self._text_input, CC.FLAGS_EXPAND_BOTH_WAYS )
2020-07-29 20:52:44 +00:00
QP.AddToLayout( hbox, self._paste_button, CC.FLAGS_CENTER_PERPENDICULAR )
2018-05-16 20:09:50 +00:00
2019-11-14 03:56:30 +00:00
self.setLayout( hbox )
2018-05-16 20:09:50 +00:00
def _Paste( self ):
2019-06-19 22:08:48 +00:00
try:
raw_text = HG.client_controller.GetClipboardText()
except HydrusExceptions.DataMissing as e:
2019-11-14 03:56:30 +00:00
QW.QMessageBox.critical( self, 'Error', str(e) )
2019-06-19 22:08:48 +00:00
return
2018-05-16 20:09:50 +00:00
try:
2018-08-08 20:29:54 +00:00
texts = [ text for text in HydrusText.DeserialiseNewlinedTexts( raw_text ) ]
if not self._allow_empty_input:
texts = [ text for text in texts if text != '' ]
2018-05-16 20:09:50 +00:00
if len( texts ) > 0:
self._add_callable( texts )
except:
2019-11-14 03:56:30 +00:00
QW.QMessageBox.critical( self, 'Error', 'I could not understand what was in the clipboard' )
2018-05-16 20:09:50 +00:00
2019-11-14 03:56:30 +00:00
def EnterText( self ):
2018-05-16 20:09:50 +00:00
2019-11-14 03:56:30 +00:00
text = self._text_input.text()
2018-05-16 20:09:50 +00:00
2019-11-14 03:56:30 +00:00
text = HydrusText.StripIOInputLine( text )
if text == '' and not self._allow_empty_input:
2018-05-16 20:09:50 +00:00
2019-11-14 03:56:30 +00:00
return
2018-05-16 20:09:50 +00:00
2019-11-14 03:56:30 +00:00
self._add_callable( ( text, ) )
2020-12-23 23:07:58 +00:00
self._text_input.clear()
2019-11-14 03:56:30 +00:00
2018-05-16 20:09:50 +00:00
def GetValue( self ):
2019-11-14 03:56:30 +00:00
return self._text_input.text()
def setPlaceholderText( self, text ):
self._text_input.setPlaceholderText( text )
2018-05-16 20:09:50 +00:00
def SetValue( self, text ):
2019-11-14 03:56:30 +00:00
self._text_input.setText( text )
2018-05-16 20:09:50 +00:00