hydrus/hydrus/client/gui/ClientGUIControls.py

2227 lines
76 KiB
Python
Raw Normal View History

2019-01-09 22:59:03 +00:00
from . import ClientConstants as CC
from . import ClientGUICommon
2020-03-04 22:12:53 +00:00
from . import ClientGUICore as CGC
2019-01-09 22:59:03 +00:00
from . import ClientGUIDialogs
2019-06-26 21:27:18 +00:00
from . import ClientGUIFunctions
2019-01-09 22:59:03 +00:00
from . import ClientGUIListCtrl
from . import ClientGUIMenus
from . import ClientGUIScrolledPanels
from . import ClientGUITime
from . import ClientGUITopLevelWindows
from . import ClientParsing
from . import HydrusConstants as HC
from . import HydrusData
from . import HydrusExceptions
from . import HydrusGlobals as HG
from . import HydrusNetworking
from . import HydrusText
2019-11-14 03:56:30 +00:00
from qtpy import QtCore as QC
from qtpy import QtWidgets as QW
from qtpy import QtGui as QG
from . import QtPorting as QP
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
2018-08-01 20:44:57 +00:00
columns = [ ( 'max allowed', 14 ), ( 'every', 16 ) ]
2018-10-17 21:00:09 +00:00
self._listctrl = ClientGUIListCtrl.BetterListCtrl( listctrl_panel, 'bandwidth_rules', 8, 10, columns, 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() )
self._listctrl.Sort( 0 )
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 )
with ClientGUITopLevelWindows.DialogEdit( self, 'edit rule' ) as dlg:
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
2017-10-18 19:41:25 +00:00
for rule in selected_rules:
2017-03-02 02:14:56 +00:00
with ClientGUITopLevelWindows.DialogEdit( self, 'edit rule' ) as dlg:
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
else:
break
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 )
2019-11-14 03:56:30 +00:00
self._max_allowed_requests = QP.MakeQSpinBox( 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
2019-11-14 03:56:30 +00:00
QP.AddToLayout( hbox, self._max_allowed_bytes, CC.FLAGS_VCENTER )
QP.AddToLayout( hbox, self._max_allowed_requests, CC.FLAGS_VCENTER )
QP.AddToLayout( hbox, self._bandwidth_type, CC.FLAGS_VCENTER )
QP.AddToLayout( hbox, ClientGUICommon.BetterStaticText(self,' every '), CC.FLAGS_VCENTER )
QP.AddToLayout( hbox, self._time_delta, CC.FLAGS_VCENTER )
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
2019-11-14 03:56:30 +00:00
self._spin = QP.MakeQSpinBox( 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
2019-11-14 03:56:30 +00:00
QP.AddToLayout( hbox, self._spin, CC.FLAGS_VCENTER )
QP.AddToLayout( hbox, self._unit, CC.FLAGS_VCENTER )
2018-04-25 22:07:52 +00:00
2019-11-14 03:56:30 +00:00
self.setLayout( hbox )
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
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()
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
def SetValue( self, value ):
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
2018-10-17 21:00:09 +00:00
class EditStringConverterPanel( ClientGUIScrolledPanels.EditPanel ):
2018-03-14 21:01:02 +00:00
2018-10-17 21:00:09 +00:00
def __init__( self, parent, string_converter, example_string_override = None ):
2018-03-14 21:01:02 +00:00
2018-10-17 21:00:09 +00:00
ClientGUIScrolledPanels.EditPanel.__init__( self, parent )
2018-03-14 21:01:02 +00:00
2018-10-17 21:00:09 +00:00
transformations_panel = ClientGUIListCtrl.BetterListCtrlPanel( self )
2018-03-14 21:01:02 +00:00
2018-10-17 21:00:09 +00:00
columns = [ ( '#', 3 ), ( 'transformation', 30 ), ( 'result', -1 ) ]
2018-03-14 21:01:02 +00:00
2018-10-17 21:00:09 +00:00
self._transformations = ClientGUIListCtrl.BetterListCtrl( transformations_panel, 'string_converter_transformations', 7, 35, columns, self._ConvertTransformationToListCtrlTuples, delete_key_callback = self._DeleteTransformation, activation_callback = self._EditTransformation )
2018-03-14 21:01:02 +00:00
2018-10-17 21:00:09 +00:00
transformations_panel.SetListCtrl( self._transformations )
2018-03-14 21:01:02 +00:00
2018-10-17 21:00:09 +00:00
transformations_panel.AddButton( 'add', self._AddTransformation )
transformations_panel.AddButton( 'edit', self._EditTransformation, enabled_only_on_selection = True )
transformations_panel.AddDeleteButton()
2018-03-14 21:01:02 +00:00
2018-10-17 21:00:09 +00:00
transformations_panel.AddSeparator()
2018-03-14 21:01:02 +00:00
2018-10-17 21:00:09 +00:00
transformations_panel.AddButton( 'move up', self._MoveUp, enabled_check_func = self._CanMoveUp )
transformations_panel.AddButton( 'move down', self._MoveDown, enabled_check_func = self._CanMoveDown )
2018-03-14 21:01:02 +00:00
2019-11-14 03:56:30 +00:00
self._example_string = QW.QLineEdit( self )
2018-03-14 21:01:02 +00:00
#
2018-10-17 21:00:09 +00:00
self._transformations.AddDatas( [ ( i + 1, transformation_type, data ) for ( i, ( transformation_type, data ) ) in enumerate( string_converter.transformations ) ] )
2018-03-14 21:01:02 +00:00
2018-10-17 21:00:09 +00:00
if example_string_override is None:
2018-03-14 21:01:02 +00:00
2019-11-14 03:56:30 +00:00
self._example_string.setText( string_converter.example_string )
2018-03-14 21:01:02 +00:00
else:
2019-11-14 03:56:30 +00:00
self._example_string.setText( example_string_override )
2018-03-14 21:01:02 +00:00
2018-10-17 21:00:09 +00:00
self._transformations.UpdateDatas() # to refresh, now they are all in the list
2018-03-14 21:01:02 +00:00
2018-10-17 21:00:09 +00:00
self._transformations.Sort( 0 )
2018-03-14 21:01:02 +00:00
2018-10-17 21:00:09 +00:00
#
2018-04-25 22:07:52 +00:00
2018-10-17 21:00:09 +00:00
rows = []
2018-04-25 22:07:52 +00:00
2018-10-17 21:00:09 +00:00
rows.append( ( 'example string: ', self._example_string ) )
2018-04-25 22:07:52 +00:00
2018-10-17 21:00:09 +00:00
gridbox = ClientGUICommon.WrapInGrid( self, rows )
2018-03-14 21:01:02 +00:00
2019-11-14 03:56:30 +00:00
vbox = QP.VBoxLayout()
2018-03-14 21:01:02 +00:00
2019-11-14 03:56:30 +00:00
QP.AddToLayout( vbox, transformations_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
QP.AddToLayout( vbox, gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
2018-03-22 00:03:33 +00:00
2019-11-14 03:56:30 +00:00
self.widget().setLayout( vbox )
2018-03-22 00:03:33 +00:00
2018-10-17 21:00:09 +00:00
#
2019-11-14 03:56:30 +00:00
self._example_string.textChanged.connect( self.EventUpdate )
2018-03-22 00:03:33 +00:00
2018-03-14 21:01:02 +00:00
2018-10-17 21:00:09 +00:00
def _AddTransformation( self ):
2018-03-14 21:01:02 +00:00
2018-10-17 21:00:09 +00:00
transformation_type = ClientParsing.STRING_TRANSFORMATION_APPEND_TEXT
2020-02-12 22:50:37 +00:00
data = 'extra text'
2018-10-17 21:00:09 +00:00
2020-02-05 22:55:21 +00:00
try:
string_converter = self._GetValue()
example_string_at_this_point = string_converter.Convert( self._example_string.text() )
except:
example_string_at_this_point = self._example_string.text()
2018-10-17 21:00:09 +00:00
with ClientGUITopLevelWindows.DialogEdit( self, 'edit transformation', frame_key = 'deeply_nested_dialog' ) as dlg:
2018-03-14 21:01:02 +00:00
2020-02-05 22:55:21 +00:00
panel = self._TransformationPanel( dlg, transformation_type, data, example_string_at_this_point )
2018-03-14 21:01:02 +00:00
2018-10-17 21:00:09 +00:00
dlg.SetPanel( panel )
2018-03-14 21:01:02 +00:00
2019-11-14 03:56:30 +00:00
if dlg.exec() == QW.QDialog.Accepted:
2018-10-17 21:00:09 +00:00
2019-11-14 03:56:30 +00:00
number = self._transformations.topLevelItemCount() + 1
2018-10-17 21:00:09 +00:00
( transformation_type, data ) = panel.GetValue()
enumerated_transformation = ( number, transformation_type, data )
self._transformations.AddDatas( ( enumerated_transformation, ) )
2018-03-14 21:01:02 +00:00
2018-10-17 21:00:09 +00:00
self._transformations.UpdateDatas() # need to refresh string after the insertion, so the new row can be included in the parsing calcs
2017-03-22 22:38:15 +00:00
2018-10-17 21:00:09 +00:00
self._transformations.Sort()
2017-07-27 00:47:13 +00:00
2018-10-17 21:00:09 +00:00
def _CanMoveDown( self ):
2018-08-01 20:44:57 +00:00
2018-10-17 21:00:09 +00:00
selected_data = self._transformations.GetData( only_selected = True )
2017-03-22 22:38:15 +00:00
2018-10-17 21:00:09 +00:00
if len( selected_data ) == 1:
( number, transformation_type, data ) = selected_data[0]
2019-11-14 03:56:30 +00:00
if number < self._transformations.topLevelItemCount():
2018-10-17 21:00:09 +00:00
return True
2017-07-27 00:47:13 +00:00
2018-10-17 21:00:09 +00:00
return False
2017-03-22 22:38:15 +00:00
2018-10-17 21:00:09 +00:00
def _CanMoveUp( self ):
2017-03-22 22:38:15 +00:00
2018-10-17 21:00:09 +00:00
selected_data = self._transformations.GetData( only_selected = True )
2017-12-06 22:06:56 +00:00
2018-10-17 21:00:09 +00:00
if len( selected_data ) == 1:
( number, transformation_type, data ) = selected_data[0]
if number > 1:
return True
2017-03-22 22:38:15 +00:00
2018-10-17 21:00:09 +00:00
return False
2017-03-22 22:38:15 +00:00
2018-10-17 21:00:09 +00:00
def _ConvertTransformationToListCtrlTuples( self, transformation ):
2017-03-22 22:38:15 +00:00
2018-10-17 21:00:09 +00:00
( number, transformation_type, data ) = transformation
2017-03-22 22:38:15 +00:00
2018-10-17 21:00:09 +00:00
pretty_number = HydrusData.ToHumanInt( number )
2019-01-09 22:59:03 +00:00
pretty_transformation = ClientParsing.StringConverter.TransformationToString( ( transformation_type, data ) )
2017-03-22 22:38:15 +00:00
2018-10-17 21:00:09 +00:00
string_converter = self._GetValue()
2017-12-06 22:06:56 +00:00
2018-10-17 21:00:09 +00:00
try:
2019-11-14 03:56:30 +00:00
pretty_result = ClientParsing.MakeParsedTextPretty( string_converter.Convert( self._example_string.text(), number ) )
2018-10-17 21:00:09 +00:00
except HydrusExceptions.StringConvertException as e:
pretty_result = str( e )
2017-12-06 22:06:56 +00:00
2018-10-17 21:00:09 +00:00
display_tuple = ( pretty_number, pretty_transformation, pretty_result )
sort_tuple = ( number, number, number )
2017-12-06 22:06:56 +00:00
return ( display_tuple, sort_tuple )
2018-10-17 21:00:09 +00:00
def _DeleteTransformation( self ):
2017-03-22 22:38:15 +00:00
2018-10-17 21:00:09 +00:00
if len( self._transformations.GetData( only_selected = True ) ) > 0:
2017-03-22 22:38:15 +00:00
2019-07-24 21:39:02 +00:00
text = 'Delete all selected?'
2019-08-07 22:59:53 +00:00
from . import ClientGUIDialogsQuick
2019-07-24 21:39:02 +00:00
result = ClientGUIDialogsQuick.GetYesNo( self, text )
2019-11-14 03:56:30 +00:00
if result == QW.QDialog.Accepted:
2017-03-22 22:38:15 +00:00
2019-07-24 21:39:02 +00:00
self._transformations.DeleteSelected()
2017-03-22 22:38:15 +00:00
2018-10-17 21:00:09 +00:00
# now we need to shuffle up any missing numbers
2019-11-14 03:56:30 +00:00
num_rows = self._transformations.topLevelItemCount()
2018-10-17 21:00:09 +00:00
i = 1
search_i = i
2017-03-22 22:38:15 +00:00
2018-10-17 21:00:09 +00:00
while i <= num_rows:
2017-03-22 22:38:15 +00:00
2018-10-17 21:00:09 +00:00
try:
transformation = self._GetTransformation( search_i )
if search_i != i:
self._transformations.DeleteDatas( ( transformation, ) )
( search_i, transformation_type, data ) = transformation
transformation = ( i, transformation_type, data )
self._transformations.AddDatas( ( transformation, ) )
i += 1
search_i = i
2017-03-22 22:38:15 +00:00
2018-10-17 21:00:09 +00:00
except HydrusExceptions.DataMissing:
search_i += 1
2017-03-22 22:38:15 +00:00
2018-10-17 21:00:09 +00:00
self._transformations.UpdateDatas()
self._transformations.Sort()
2017-03-22 22:38:15 +00:00
2018-10-17 21:00:09 +00:00
def _EditTransformation( self ):
2017-03-22 22:38:15 +00:00
2018-10-17 21:00:09 +00:00
selected_data = self._transformations.GetData( only_selected = True )
for enumerated_transformation in selected_data:
2017-03-22 22:38:15 +00:00
2018-10-17 21:00:09 +00:00
( number, transformation_type, data ) = enumerated_transformation
2017-03-22 22:38:15 +00:00
2020-02-05 22:55:21 +00:00
try:
string_converter = self._GetValue()
example_string_at_this_point = string_converter.Convert( self._example_string.text(), number - 1 )
except:
example_string_at_this_point = self._example_string.text()
2018-10-17 21:00:09 +00:00
with ClientGUITopLevelWindows.DialogEdit( self, 'edit transformation', frame_key = 'deeply_nested_dialog' ) as dlg:
2020-02-05 22:55:21 +00:00
panel = self._TransformationPanel( dlg, transformation_type, data, example_string_at_this_point )
2018-10-17 21:00:09 +00:00
dlg.SetPanel( panel )
2017-03-22 22:38:15 +00:00
2019-11-14 03:56:30 +00:00
if dlg.exec() == QW.QDialog.Accepted:
2017-03-22 22:38:15 +00:00
2018-10-17 21:00:09 +00:00
self._transformations.DeleteDatas( ( enumerated_transformation, ) )
2017-03-22 22:38:15 +00:00
2018-10-17 21:00:09 +00:00
( transformation_type, data ) = panel.GetValue()
2017-03-22 22:38:15 +00:00
2018-10-17 21:00:09 +00:00
enumerated_transformation = ( number, transformation_type, data )
2017-03-22 22:38:15 +00:00
2018-10-17 21:00:09 +00:00
self._transformations.AddDatas( ( enumerated_transformation, ) )
2017-03-22 22:38:15 +00:00
else:
2017-12-06 22:06:56 +00:00
break
2017-03-22 22:38:15 +00:00
2018-10-17 21:00:09 +00:00
self._transformations.UpdateDatas()
self._transformations.Sort()
def _GetTransformation( self, desired_number ):
for transformation in self._transformations.GetData():
2017-03-22 22:38:15 +00:00
2018-10-17 21:00:09 +00:00
( number, transformation_type, data ) = transformation
2017-03-22 22:38:15 +00:00
2018-10-17 21:00:09 +00:00
if number == desired_number:
return transformation
2017-12-06 22:06:56 +00:00
2018-10-17 21:00:09 +00:00
raise HydrusExceptions.DataMissing()
2017-03-22 22:38:15 +00:00
2018-10-17 21:00:09 +00:00
def _GetValue( self ):
2017-03-22 22:38:15 +00:00
2018-10-17 21:00:09 +00:00
enumerated_transformations = list( self._transformations.GetData() )
2017-03-22 22:38:15 +00:00
2018-10-17 21:00:09 +00:00
enumerated_transformations.sort()
2017-03-22 22:38:15 +00:00
2018-10-17 21:00:09 +00:00
transformations = [ ( transformation_type, data ) for ( number, transformation_type, data ) in enumerated_transformations ]
2017-07-05 21:09:28 +00:00
2019-11-14 03:56:30 +00:00
example_string = self._example_string.text()
2017-07-05 21:09:28 +00:00
2018-10-17 21:00:09 +00:00
string_converter = ClientParsing.StringConverter( transformations, example_string )
2017-07-12 20:03:45 +00:00
2018-10-17 21:00:09 +00:00
return string_converter
2017-08-16 21:58:06 +00:00
2018-10-17 21:00:09 +00:00
def _MoveDown( self ):
2017-07-12 20:03:45 +00:00
2018-10-17 21:00:09 +00:00
selected_transformation = self._transformations.GetData( only_selected = True )[0]
2017-07-12 20:03:45 +00:00
2018-10-17 21:00:09 +00:00
( number, transformation_type, data ) = selected_transformation
2017-07-05 21:09:28 +00:00
2018-10-17 21:00:09 +00:00
swap_transformation = self._GetTransformation( number + 1 )
2017-07-05 21:09:28 +00:00
2018-10-17 21:00:09 +00:00
self._SwapTransformations( selected_transformation, swap_transformation )
2017-07-05 21:09:28 +00:00
2018-10-17 21:00:09 +00:00
self._transformations.UpdateDatas()
2017-07-05 21:09:28 +00:00
2018-10-17 21:00:09 +00:00
self._transformations.Sort()
2017-07-05 21:09:28 +00:00
2018-10-17 21:00:09 +00:00
def _MoveUp( self ):
2017-07-12 20:03:45 +00:00
2018-10-17 21:00:09 +00:00
selected_transformation = self._transformations.GetData( only_selected = True )[0]
2017-07-12 20:03:45 +00:00
2018-10-17 21:00:09 +00:00
( number, transformation_type, data ) = selected_transformation
2017-07-12 20:03:45 +00:00
2018-10-17 21:00:09 +00:00
swap_transformation = self._GetTransformation( number - 1 )
2017-07-12 20:03:45 +00:00
2018-10-17 21:00:09 +00:00
self._SwapTransformations( selected_transformation, swap_transformation )
2017-07-05 21:09:28 +00:00
2018-10-17 21:00:09 +00:00
self._transformations.UpdateDatas()
2017-07-05 21:09:28 +00:00
2018-10-17 21:00:09 +00:00
self._transformations.Sort()
2017-07-05 21:09:28 +00:00
2018-10-17 21:00:09 +00:00
def _SwapTransformations( self, one, two ):
2018-09-19 21:54:51 +00:00
2018-10-17 21:00:09 +00:00
selected_data = self._transformations.GetData( only_selected = True )
2018-09-19 21:54:51 +00:00
2018-10-17 21:00:09 +00:00
one_selected = one in selected_data
two_selected = two in selected_data
self._transformations.DeleteDatas( ( one, two ) )
( number_1, transformation_type_1, data_1 ) = one
( number_2, transformation_type_2, data_2 ) = two
one = ( number_2, transformation_type_1, data_1 )
two = ( number_1, transformation_type_2, data_2 )
self._transformations.AddDatas( ( one, two ) )
if one_selected:
2018-09-19 21:54:51 +00:00
2018-10-17 21:00:09 +00:00
self._transformations.SelectDatas( ( one, ) )
2018-09-19 21:54:51 +00:00
2018-10-17 21:00:09 +00:00
if two_selected:
2018-09-19 21:54:51 +00:00
2018-10-17 21:00:09 +00:00
self._transformations.SelectDatas( ( two, ) )
2018-09-19 21:54:51 +00:00
2018-10-17 21:00:09 +00:00
2019-11-14 03:56:30 +00:00
def EventUpdate( self, text ):
2018-09-19 21:54:51 +00:00
2018-10-17 21:00:09 +00:00
self._transformations.UpdateDatas()
2018-09-19 21:54:51 +00:00
2018-10-17 21:00:09 +00:00
def GetValue( self ):
2017-08-30 20:27:47 +00:00
2018-10-17 21:00:09 +00:00
string_converter = self._GetValue()
try:
2017-08-30 20:27:47 +00:00
2019-11-14 03:56:30 +00:00
string_converter.Convert( self._example_string.text() )
2017-08-30 20:27:47 +00:00
2018-10-17 21:00:09 +00:00
except HydrusExceptions.StringConvertException:
2017-08-30 20:27:47 +00:00
2018-10-17 21:00:09 +00:00
raise HydrusExceptions.VetoException( 'Please enter an example text that can be converted!' )
2017-08-30 20:27:47 +00:00
2018-10-17 21:00:09 +00:00
return string_converter
2017-08-30 20:27:47 +00:00
2018-10-17 21:00:09 +00:00
class _TransformationPanel( ClientGUIScrolledPanels.EditPanel ):
2017-07-05 21:09:28 +00:00
2020-02-05 22:55:21 +00:00
def __init__( self, parent, transformation_type, data, example_text ):
2017-07-12 20:03:45 +00:00
2018-10-17 21:00:09 +00:00
ClientGUIScrolledPanels.EditPanel.__init__( self, parent )
2017-07-05 21:09:28 +00:00
2018-10-17 21:00:09 +00:00
self._transformation_type = ClientGUICommon.BetterChoice( self )
2017-07-05 21:09:28 +00:00
2019-12-05 05:29:32 +00:00
for t_type in ( ClientParsing.STRING_TRANSFORMATION_REMOVE_TEXT_FROM_BEGINNING, ClientParsing.STRING_TRANSFORMATION_REMOVE_TEXT_FROM_END, ClientParsing.STRING_TRANSFORMATION_CLIP_TEXT_FROM_BEGINNING, ClientParsing.STRING_TRANSFORMATION_CLIP_TEXT_FROM_END, ClientParsing.STRING_TRANSFORMATION_PREPEND_TEXT, ClientParsing.STRING_TRANSFORMATION_APPEND_TEXT, ClientParsing.STRING_TRANSFORMATION_ENCODE, ClientParsing.STRING_TRANSFORMATION_DECODE, ClientParsing.STRING_TRANSFORMATION_REVERSE, ClientParsing.STRING_TRANSFORMATION_REGEX_SUB, ClientParsing.STRING_TRANSFORMATION_DATE_DECODE, ClientParsing.STRING_TRANSFORMATION_DATE_ENCODE, ClientParsing.STRING_TRANSFORMATION_INTEGER_ADDITION ):
2017-07-05 21:09:28 +00:00
2020-02-12 22:50:37 +00:00
self._transformation_type.addItem( ClientParsing.transformation_type_str_lookup[ t_type ], t_type )
2017-07-05 21:09:28 +00:00
2018-10-17 21:00:09 +00:00
2020-02-05 22:55:21 +00:00
self._example_string = QW.QLineEdit( self )
min_width = ClientGUIFunctions.ConvertTextToPixelWidth( self._example_string, 96 )
self._example_string.setMinimumWidth( min_width )
2020-04-16 00:09:42 +00:00
self._example_text = example_text
if isinstance( self._example_text, bytes ):
self._example_string.setText( repr( self._example_text ) )
else:
self._example_string.setText( self._example_text )
2020-02-05 22:55:21 +00:00
self._example_transformation = QW.QLineEdit( self )
2020-02-12 22:50:37 +00:00
#
2020-02-05 22:55:21 +00:00
2020-02-12 22:50:37 +00:00
self._example_string.setReadOnly( True )
self._example_transformation.setReadOnly( True )
2020-02-05 22:55:21 +00:00
2019-11-14 03:56:30 +00:00
self._data_text = QW.QLineEdit( self )
self._data_number = QP.MakeQSpinBox( self, min=0, max=65535 )
2018-10-17 21:00:09 +00:00
self._data_encoding = ClientGUICommon.BetterChoice( self )
2019-11-14 03:56:30 +00:00
self._data_regex_repl = QW.QLineEdit( self )
self._data_date_link = ClientGUICommon.BetterHyperLink( self, 'link to date info', 'https://docs.python.org/3/library/datetime.html#strftime-strptime-behavior' )
2019-12-05 05:29:32 +00:00
self._data_timezone_decode = ClientGUICommon.BetterChoice( self )
self._data_timezone_encode = ClientGUICommon.BetterChoice( self )
2019-11-14 03:56:30 +00:00
self._data_timezone_offset = QP.MakeQSpinBox( self, min=-86400, max=86400 )
2018-10-17 21:00:09 +00:00
2020-03-18 21:35:57 +00:00
for e in ( 'hex', 'base64', 'url percent encoding' ):
2017-07-05 21:09:28 +00:00
2019-11-14 03:56:30 +00:00
self._data_encoding.addItem( e, e )
2017-07-05 21:09:28 +00:00
2019-12-05 05:29:32 +00:00
self._data_timezone_decode.addItem( 'UTC', HC.TIMEZONE_GMT )
self._data_timezone_decode.addItem( 'Local', HC.TIMEZONE_LOCAL )
self._data_timezone_decode.addItem( 'Offset', HC.TIMEZONE_OFFSET )
self._data_timezone_encode.addItem( 'UTC', HC.TIMEZONE_GMT )
self._data_timezone_encode.addItem( 'Local', HC.TIMEZONE_LOCAL )
2017-07-05 21:09:28 +00:00
2018-10-17 21:00:09 +00:00
#
2017-07-12 20:03:45 +00:00
2019-07-24 21:39:02 +00:00
self._transformation_type.SetValue( transformation_type )
2017-07-12 20:03:45 +00:00
2020-02-12 22:50:37 +00:00
self._data_number.setValue( 1 )
2018-08-08 20:29:54 +00:00
2018-10-17 21:00:09 +00:00
#
if transformation_type in ( ClientParsing.STRING_TRANSFORMATION_DECODE, ClientParsing.STRING_TRANSFORMATION_ENCODE ):
2017-07-12 20:03:45 +00:00
2019-07-24 21:39:02 +00:00
self._data_encoding.SetValue( data )
2017-07-12 20:03:45 +00:00
2018-10-17 21:00:09 +00:00
elif transformation_type == ClientParsing.STRING_TRANSFORMATION_REGEX_SUB:
( pattern, repl ) = data
2020-02-12 22:50:37 +00:00
self._data_text.setText( pattern )
2019-11-14 03:56:30 +00:00
self._data_regex_repl.setText( repl )
2018-10-17 21:00:09 +00:00
elif transformation_type == ClientParsing.STRING_TRANSFORMATION_DATE_DECODE:
( phrase, timezone_type, timezone_offset ) = data
2019-11-14 03:56:30 +00:00
self._data_text.setText( phrase )
2019-12-05 05:29:32 +00:00
self._data_timezone_decode.SetValue( timezone_type )
2019-11-14 03:56:30 +00:00
self._data_timezone_offset.setValue( timezone_offset )
2018-10-17 21:00:09 +00:00
2019-12-05 05:29:32 +00:00
elif transformation_type == ClientParsing.STRING_TRANSFORMATION_DATE_ENCODE:
( phrase, timezone_type ) = data
self._data_text.setText( phrase )
self._data_timezone_encode.SetValue( timezone_type )
2018-10-17 21:00:09 +00:00
elif data is not None:
if isinstance( data, int ):
2017-07-12 20:03:45 +00:00
2019-11-14 03:56:30 +00:00
self._data_number.setValue( data )
2018-10-17 21:00:09 +00:00
else:
2019-11-14 03:56:30 +00:00
self._data_text.setText( data )
2017-07-12 20:03:45 +00:00
2018-08-08 20:29:54 +00:00
2018-10-17 21:00:09 +00:00
#
2018-08-08 20:29:54 +00:00
2018-10-17 21:00:09 +00:00
rows = []
2018-08-08 20:29:54 +00:00
2020-02-12 22:50:37 +00:00
# This mess needs to be all replaced with a nice QFormLayout subclass that can do row hide/show
# or just a whole separate panel for each transformation type, but w/e
2020-02-05 22:55:21 +00:00
2020-02-12 22:50:37 +00:00
self._data_text_label = ClientGUICommon.BetterStaticText( self, 'string data: ' )
self._data_number_label = ClientGUICommon.BetterStaticText( self, 'number data: ' )
self._data_encoding_label = ClientGUICommon.BetterStaticText( self, 'encoding data: ' )
self._data_regex_repl_label = ClientGUICommon.BetterStaticText( self, 'regex replacement: ' )
self._data_date_link_label = ClientGUICommon.BetterStaticText( self, 'date info: ' )
self._data_timezone_decode_label = ClientGUICommon.BetterStaticText( self, 'date decode timezone: ' )
self._data_timezone_offset_label = ClientGUICommon.BetterStaticText( self, 'timezone offset: ' )
self._data_timezone_encode_label = ClientGUICommon.BetterStaticText( self, 'date encode timezone: ' )
2020-02-05 22:55:21 +00:00
2020-02-12 22:50:37 +00:00
rows.append( ( 'example string: ', self._example_string ) )
rows.append( ( 'transformed string: ', self._example_transformation ) )
rows.append( ( 'transformation type: ', self._transformation_type ) )
rows.append( ( self._data_text_label, self._data_text ) )
rows.append( ( self._data_number_label, self._data_number ) )
rows.append( ( self._data_encoding_label, self._data_encoding ) )
rows.append( ( self._data_regex_repl_label, self._data_regex_repl ) )
rows.append( ( self._data_date_link_label, self._data_date_link ) )
rows.append( ( self._data_timezone_decode_label, self._data_timezone_decode ) )
rows.append( ( self._data_timezone_offset_label, self._data_timezone_offset ) )
rows.append( ( self._data_timezone_encode_label, self._data_timezone_encode ) )
self._control_gridbox = ClientGUICommon.WrapInGrid( self, rows )
2018-10-17 21:00:09 +00:00
2019-11-14 03:56:30 +00:00
vbox = QP.VBoxLayout()
2018-10-17 21:00:09 +00:00
2020-02-12 22:50:37 +00:00
QP.AddToLayout( vbox, self._control_gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
QP.AddToLayout( vbox, QW.QWidget( self ), CC.FLAGS_EXPAND_BOTH_WAYS )
2018-10-17 21:00:09 +00:00
2019-11-14 03:56:30 +00:00
self.widget().setLayout( vbox )
2018-10-17 21:00:09 +00:00
2020-02-12 22:50:37 +00:00
self._UpdateDataControls()
2018-10-17 21:00:09 +00:00
#
2019-11-14 03:56:30 +00:00
self._transformation_type.currentIndexChanged.connect( self._UpdateDataControls )
2020-02-12 22:50:37 +00:00
self._transformation_type.currentIndexChanged.connect( self._UpdateExampleText )
self._data_text.textEdited.connect( self._UpdateExampleText )
self._data_number.valueChanged.connect( self._UpdateExampleText )
self._data_encoding.currentIndexChanged.connect( self._UpdateExampleText )
self._data_regex_repl.textEdited.connect( self._UpdateExampleText )
self._data_timezone_decode.currentIndexChanged.connect( self._UpdateExampleText )
self._data_timezone_offset.valueChanged.connect( self._UpdateExampleText )
self._data_timezone_encode.currentIndexChanged.connect( self._UpdateExampleText )
2019-12-05 05:29:32 +00:00
self._data_timezone_decode.currentIndexChanged.connect( self._UpdateDataControls )
self._data_timezone_encode.currentIndexChanged.connect( self._UpdateDataControls )
2018-10-17 21:00:09 +00:00
2020-02-05 22:55:21 +00:00
self._UpdateExampleText()
2018-10-17 21:00:09 +00:00
def _UpdateDataControls( self ):
2020-02-12 22:50:37 +00:00
self._data_text_label.setVisible( False )
self._data_number_label.setVisible( False )
self._data_encoding_label.setVisible( False )
self._data_regex_repl_label.setVisible( False )
self._data_date_link_label.setVisible( False )
self._data_timezone_decode_label.setVisible( False )
self._data_timezone_offset_label.setVisible( False )
self._data_timezone_encode_label.setVisible( False )
self._data_text.setVisible( False )
self._data_number.setVisible( False )
self._data_encoding.setVisible( False )
self._data_regex_repl.setVisible( False )
self._data_date_link.setVisible( False )
self._data_timezone_decode.setVisible( False )
self._data_timezone_offset.setVisible( False )
self._data_timezone_encode.setVisible( False )
2018-10-17 21:00:09 +00:00
2019-07-24 21:39:02 +00:00
transformation_type = self._transformation_type.GetValue()
2018-10-17 21:00:09 +00:00
if transformation_type in ( ClientParsing.STRING_TRANSFORMATION_ENCODE, ClientParsing.STRING_TRANSFORMATION_DECODE ):
2017-07-05 21:09:28 +00:00
2020-02-12 22:50:37 +00:00
self._data_encoding_label.setVisible( True )
self._data_encoding.setVisible( True )
elif transformation_type in ( ClientParsing.STRING_TRANSFORMATION_PREPEND_TEXT, ClientParsing.STRING_TRANSFORMATION_APPEND_TEXT, ClientParsing.STRING_TRANSFORMATION_DATE_DECODE, ClientParsing.STRING_TRANSFORMATION_DATE_ENCODE, ClientParsing.STRING_TRANSFORMATION_REGEX_SUB ):
2018-08-08 20:29:54 +00:00
2020-02-12 22:50:37 +00:00
self._data_text_label.setVisible( True )
self._data_text.setVisible( True )
2017-07-05 21:09:28 +00:00
2020-02-12 22:50:37 +00:00
data_text_label = 'string data: '
2018-10-17 21:00:09 +00:00
2020-02-12 22:50:37 +00:00
if transformation_type == ClientParsing.STRING_TRANSFORMATION_PREPEND_TEXT:
data_text_label = 'text to prepend: '
elif transformation_type == ClientParsing.STRING_TRANSFORMATION_APPEND_TEXT:
2018-10-17 21:00:09 +00:00
2020-02-12 22:50:37 +00:00
data_text_label = 'text to append: '
2018-10-17 21:00:09 +00:00
2020-02-12 22:50:37 +00:00
elif transformation_type in ( ClientParsing.STRING_TRANSFORMATION_DATE_DECODE, ClientParsing.STRING_TRANSFORMATION_DATE_ENCODE ):
self._data_date_link_label.setVisible( True )
self._data_date_link.setVisible( True )
if transformation_type == ClientParsing.STRING_TRANSFORMATION_DATE_DECODE:
data_text_label = 'date decode phrase: '
self._data_timezone_decode_label.setVisible( True )
self._data_timezone_decode.setVisible( True )
if self._data_timezone_decode.GetValue() == HC.TIMEZONE_OFFSET:
self._data_timezone_offset_label.setVisible( True )
self._data_timezone_offset.setVisible( True )
2018-10-17 21:00:09 +00:00
2020-02-12 22:50:37 +00:00
elif transformation_type == ClientParsing.STRING_TRANSFORMATION_DATE_ENCODE:
2018-10-17 21:00:09 +00:00
2020-02-12 22:50:37 +00:00
data_text_label = 'date encode phrase: '
self._data_timezone_encode_label.setVisible( True )
self._data_timezone_encode.setVisible( True )
elif transformation_type == ClientParsing.STRING_TRANSFORMATION_REGEX_SUB:
2018-10-17 21:00:09 +00:00
2020-02-12 22:50:37 +00:00
data_text_label = 'regex pattern: '
2019-12-05 05:29:32 +00:00
2020-02-12 22:50:37 +00:00
self._data_regex_repl_label.setVisible( True )
self._data_regex_repl.setVisible( True )
2019-12-05 05:29:32 +00:00
2018-10-17 21:00:09 +00:00
2020-02-12 22:50:37 +00:00
self._data_text_label.setText( data_text_label )
2018-10-17 21:00:09 +00:00
elif transformation_type in ( ClientParsing.STRING_TRANSFORMATION_REMOVE_TEXT_FROM_BEGINNING, ClientParsing.STRING_TRANSFORMATION_REMOVE_TEXT_FROM_END, ClientParsing.STRING_TRANSFORMATION_CLIP_TEXT_FROM_BEGINNING, ClientParsing.STRING_TRANSFORMATION_CLIP_TEXT_FROM_END, ClientParsing.STRING_TRANSFORMATION_INTEGER_ADDITION ):
2020-02-12 22:50:37 +00:00
self._data_number_label.setVisible( True )
self._data_number.setVisible( True )
2018-10-17 21:00:09 +00:00
if transformation_type == ClientParsing.STRING_TRANSFORMATION_INTEGER_ADDITION:
2019-11-14 03:56:30 +00:00
self._data_number.setMinimum( -65535 )
2018-10-17 21:00:09 +00:00
else:
2019-11-14 03:56:30 +00:00
self._data_number.setMinimum( 0 )
2018-10-17 21:00:09 +00:00
2020-02-12 22:50:37 +00:00
data_number_label = 'number data: '
if transformation_type == ClientParsing.STRING_TRANSFORMATION_REMOVE_TEXT_FROM_BEGINNING:
data_number_label = 'characters to remove: '
elif transformation_type == ClientParsing.STRING_TRANSFORMATION_REMOVE_TEXT_FROM_END:
data_number_label = 'characters to remove: '
elif transformation_type == ClientParsing.STRING_TRANSFORMATION_CLIP_TEXT_FROM_BEGINNING:
data_number_label = 'characters to take: '
elif transformation_type == ClientParsing.STRING_TRANSFORMATION_CLIP_TEXT_FROM_END:
data_number_label = 'characters to take: '
elif transformation_type == ClientParsing.STRING_TRANSFORMATION_INTEGER_ADDITION:
data_number_label = 'number to add: '
2018-10-17 21:00:09 +00:00
2020-02-12 22:50:37 +00:00
self._data_number_label.setText( data_number_label )
2017-07-05 21:09:28 +00:00
2018-10-17 21:00:09 +00:00
2020-02-05 22:55:21 +00:00
def _UpdateExampleText( self ):
try:
transformations = [ self.GetValue() ]
2020-04-16 00:09:42 +00:00
string_converter = ClientParsing.StringConverter( transformations, self._example_text )
2020-02-05 22:55:21 +00:00
2020-04-16 00:09:42 +00:00
example_transformation = string_converter.Convert( self._example_text )
2020-02-05 22:55:21 +00:00
2020-02-12 22:50:37 +00:00
try:
self._example_transformation.setText( str( example_transformation ) )
except:
self._example_transformation.setText( repr( example_transformation ) )
2020-02-05 22:55:21 +00:00
except Exception as e:
self._example_transformation.setText( str( e ) )
2018-10-17 21:00:09 +00:00
def GetValue( self ):
2017-07-05 21:09:28 +00:00
2019-07-24 21:39:02 +00:00
transformation_type = self._transformation_type.GetValue()
2018-10-17 21:00:09 +00:00
if transformation_type in ( ClientParsing.STRING_TRANSFORMATION_ENCODE, ClientParsing.STRING_TRANSFORMATION_DECODE ):
2017-07-05 21:09:28 +00:00
2019-07-24 21:39:02 +00:00
data = self._data_encoding.GetValue()
2017-07-05 21:09:28 +00:00
2018-10-17 21:00:09 +00:00
elif transformation_type in ( ClientParsing.STRING_TRANSFORMATION_PREPEND_TEXT, ClientParsing.STRING_TRANSFORMATION_APPEND_TEXT ):
2017-07-05 21:09:28 +00:00
2019-11-14 03:56:30 +00:00
data = self._data_text.text()
2018-10-17 21:00:09 +00:00
elif transformation_type in ( ClientParsing.STRING_TRANSFORMATION_REMOVE_TEXT_FROM_BEGINNING, ClientParsing.STRING_TRANSFORMATION_REMOVE_TEXT_FROM_END, ClientParsing.STRING_TRANSFORMATION_CLIP_TEXT_FROM_BEGINNING, ClientParsing.STRING_TRANSFORMATION_CLIP_TEXT_FROM_END, ClientParsing.STRING_TRANSFORMATION_INTEGER_ADDITION ):
2019-11-14 03:56:30 +00:00
data = self._data_number.value()
2018-10-17 21:00:09 +00:00
elif transformation_type == ClientParsing.STRING_TRANSFORMATION_REGEX_SUB:
2020-02-12 22:50:37 +00:00
pattern = self._data_text.text()
2019-11-14 03:56:30 +00:00
repl = self._data_regex_repl.text()
2018-10-17 21:00:09 +00:00
data = ( pattern, repl )
elif transformation_type == ClientParsing.STRING_TRANSFORMATION_DATE_DECODE:
2017-07-05 21:09:28 +00:00
2019-11-14 03:56:30 +00:00
phrase = self._data_text.text()
2019-12-05 05:29:32 +00:00
timezone_time = self._data_timezone_decode.GetValue()
2019-11-14 03:56:30 +00:00
timezone_offset = self._data_timezone_offset.value()
2018-10-17 21:00:09 +00:00
data = ( phrase, timezone_time, timezone_offset )
2019-12-05 05:29:32 +00:00
elif transformation_type == ClientParsing.STRING_TRANSFORMATION_DATE_ENCODE:
phrase = self._data_text.text()
timezone_time = self._data_timezone_encode.GetValue()
data = ( phrase, timezone_time )
2018-10-17 21:00:09 +00:00
else:
data = None
return ( transformation_type, data )
2017-07-05 21:09:28 +00:00
2018-10-17 21:00:09 +00:00
class EditStringMatchPanel( ClientGUIScrolledPanels.EditPanel ):
def __init__( self, parent, string_match = None ):
2017-07-05 21:09:28 +00:00
2018-10-17 21:00:09 +00:00
ClientGUIScrolledPanels.EditPanel.__init__( self, parent )
if string_match is None:
2017-07-05 21:09:28 +00:00
2018-10-17 21:00:09 +00:00
string_match = ClientParsing.StringMatch()
2017-07-05 21:09:28 +00:00
2018-10-17 21:00:09 +00:00
self._match_type = ClientGUICommon.BetterChoice( self )
2017-07-05 21:09:28 +00:00
2019-11-14 03:56:30 +00:00
self._match_type.addItem( 'any characters', ClientParsing.STRING_MATCH_ANY )
self._match_type.addItem( 'fixed characters', ClientParsing.STRING_MATCH_FIXED )
self._match_type.addItem( 'character set', ClientParsing.STRING_MATCH_FLEXIBLE )
self._match_type.addItem( 'regex', ClientParsing.STRING_MATCH_REGEX )
2018-10-17 21:00:09 +00:00
2019-11-14 03:56:30 +00:00
self._match_value_text_input = QW.QLineEdit( self )
2018-10-17 21:00:09 +00:00
self._match_value_flexible_input = ClientGUICommon.BetterChoice( self )
2019-11-14 03:56:30 +00:00
self._match_value_flexible_input.addItem( 'alphabetic characters (a-zA-Z)', ClientParsing.ALPHA )
self._match_value_flexible_input.addItem( 'alphanumeric characters (a-zA-Z0-9)', ClientParsing.ALPHANUMERIC )
self._match_value_flexible_input.addItem( 'numeric characters (0-9)', ClientParsing.NUMERIC )
2018-10-17 21:00:09 +00:00
self._min_chars = ClientGUICommon.NoneableSpinCtrl( self, min = 1, max = 65535, unit = 'characters', none_phrase = 'no limit' )
self._max_chars = ClientGUICommon.NoneableSpinCtrl( self, min = 1, max = 65535, unit = 'characters', none_phrase = 'no limit' )
2019-11-14 03:56:30 +00:00
self._example_string = QW.QLineEdit( self )
2018-10-17 21:00:09 +00:00
self._example_string_matches = ClientGUICommon.BetterStaticText( self )
#
self.SetValue( string_match )
#
rows = []
rows.append( ( 'match type: ', self._match_type ) )
rows.append( ( 'match text: ', self._match_value_text_input ) )
rows.append( ( 'match value (character set): ', self._match_value_flexible_input ) )
2020-04-08 21:10:11 +00:00
rows.append( ( 'minimum allowed number of characters: ', self._min_chars ) )
2018-10-17 21:00:09 +00:00
rows.append( ( 'maximum allowed number of characters: ', self._max_chars ) )
rows.append( ( 'example string: ', self._example_string ) )
gridbox = ClientGUICommon.WrapInGrid( self, rows )
2019-11-14 03:56:30 +00:00
vbox = QP.VBoxLayout()
2018-10-17 21:00:09 +00:00
2019-11-14 03:56:30 +00:00
QP.AddToLayout( vbox, gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
QP.AddToLayout( vbox, self._example_string_matches, CC.FLAGS_EXPAND_PERPENDICULAR )
2018-10-17 21:00:09 +00:00
2019-11-14 03:56:30 +00:00
self.widget().setLayout( vbox )
2018-10-17 21:00:09 +00:00
#
2019-11-14 03:56:30 +00:00
self._match_type.currentIndexChanged.connect( self._UpdateControls )
self._match_value_text_input.textChanged.connect( self._UpdateControls )
self._match_value_flexible_input.currentIndexChanged.connect( self._UpdateControls )
self._min_chars.valueChanged.connect( self._UpdateControls )
self._max_chars.valueChanged.connect( self._UpdateControls )
self._example_string.textChanged.connect( self._UpdateControls )
2017-07-12 20:03:45 +00:00
2017-07-05 21:09:28 +00:00
2018-10-17 21:00:09 +00:00
def _GetValue( self ):
2017-08-16 21:58:06 +00:00
2019-07-24 21:39:02 +00:00
match_type = self._match_type.GetValue()
2018-10-17 21:00:09 +00:00
if match_type == ClientParsing.STRING_MATCH_ANY:
match_value = ''
elif match_type == ClientParsing.STRING_MATCH_FLEXIBLE:
2019-07-24 21:39:02 +00:00
match_value = self._match_value_flexible_input.GetValue()
2018-10-17 21:00:09 +00:00
else:
2019-11-14 03:56:30 +00:00
match_value = self._match_value_text_input.text()
2018-10-17 21:00:09 +00:00
min_chars = self._min_chars.GetValue()
max_chars = self._max_chars.GetValue()
2019-11-14 03:56:30 +00:00
example_string = self._example_string.text()
2018-10-17 21:00:09 +00:00
string_match = ClientParsing.StringMatch( match_type = match_type, match_value = match_value, min_chars = min_chars, max_chars = max_chars, example_string = example_string )
return string_match
2017-08-16 21:58:06 +00:00
2018-10-17 21:00:09 +00:00
def _UpdateControls( self ):
2017-07-05 21:09:28 +00:00
2019-07-24 21:39:02 +00:00
match_type = self._match_type.GetValue()
2018-10-17 21:00:09 +00:00
if match_type == ClientParsing.STRING_MATCH_ANY:
2018-08-01 20:44:57 +00:00
2019-11-14 03:56:30 +00:00
self._match_value_text_input.setEnabled( False )
self._match_value_flexible_input.setEnabled( False )
2018-10-17 21:00:09 +00:00
elif match_type == ClientParsing.STRING_MATCH_FLEXIBLE:
2019-11-14 03:56:30 +00:00
self._match_value_text_input.setEnabled( False )
self._match_value_flexible_input.setEnabled( True )
2017-07-19 21:21:41 +00:00
2018-08-01 20:44:57 +00:00
else:
2017-07-19 21:21:41 +00:00
2019-11-14 03:56:30 +00:00
self._match_value_text_input.setEnabled( True )
self._match_value_flexible_input.setEnabled( False )
2018-10-17 21:00:09 +00:00
if match_type == ClientParsing.STRING_MATCH_FIXED:
self._min_chars.SetValue( None )
self._max_chars.SetValue( None )
2019-11-14 03:56:30 +00:00
self._min_chars.setEnabled( False )
self._max_chars.setEnabled( False )
2018-10-17 21:00:09 +00:00
2019-11-14 03:56:30 +00:00
self._example_string.blockSignals( True ) # Temporarily block the text changed signal here so we won't end up in infinite recursion
self._example_string.setText( self._match_value_text_input.text() )
self._example_string.blockSignals( False )
2018-10-17 21:00:09 +00:00
2019-11-14 03:56:30 +00:00
self._example_string_matches.setText( '' )
2018-10-17 21:00:09 +00:00
else:
2019-11-14 03:56:30 +00:00
self._min_chars.setEnabled( True )
self._max_chars.setEnabled( True )
2018-10-17 21:00:09 +00:00
string_match = self._GetValue()
try:
2018-08-01 20:44:57 +00:00
2019-11-14 03:56:30 +00:00
string_match.Test( self._example_string.text() )
2018-08-01 20:44:57 +00:00
2019-11-14 03:56:30 +00:00
self._example_string_matches.setText( 'Example matches ok!' )
2020-02-26 22:28:52 +00:00
self._example_string_matches.setObjectName( 'HydrusValid' )
self._example_string_matches.style().polish( self._example_string_matches )
2018-10-17 21:00:09 +00:00
except HydrusExceptions.StringMatchException as e:
2019-01-09 22:59:03 +00:00
reason = str( e )
2018-10-17 21:00:09 +00:00
2019-11-14 03:56:30 +00:00
self._example_string_matches.setText( 'Example does not match - '+reason )
2020-02-26 22:28:52 +00:00
self._example_string_matches.setObjectName( 'HydrusInvalid' )
self._example_string_matches.style().polish( self._example_string_matches )
2018-08-01 20:44:57 +00:00
2017-07-27 00:47:13 +00:00
2017-07-05 21:09:28 +00:00
2018-10-17 21:00:09 +00:00
def GetValue( self ):
string_match = self._GetValue()
try:
2017-07-05 21:09:28 +00:00
2019-11-14 03:56:30 +00:00
string_match.Test( self._example_string.text() )
2018-10-17 21:00:09 +00:00
except HydrusExceptions.StringMatchException:
raise HydrusExceptions.VetoException( 'Please enter an example text that matches the given rules!' )
2017-07-05 21:09:28 +00:00
2018-10-17 21:00:09 +00:00
return string_match
2018-02-07 23:40:33 +00:00
2018-10-17 21:00:09 +00:00
def SetValue( self, string_match ):
( match_type, match_value, min_chars, max_chars, example_string ) = string_match.ToTuple()
2019-07-24 21:39:02 +00:00
self._match_type.SetValue( match_type )
2018-10-17 21:00:09 +00:00
if match_type == ClientParsing.STRING_MATCH_FLEXIBLE:
2019-07-24 21:39:02 +00:00
self._match_value_flexible_input.SetValue( match_value )
2018-10-17 21:00:09 +00:00
else:
2019-07-24 21:39:02 +00:00
self._match_value_flexible_input.SetValue( ClientParsing.ALPHA )
2018-10-17 21:00:09 +00:00
2019-11-14 03:56:30 +00:00
self._match_value_text_input.setText( match_value )
2018-10-17 21:00:09 +00:00
self._min_chars.SetValue( min_chars )
self._max_chars.SetValue( max_chars )
2019-11-14 03:56:30 +00:00
self._example_string.setText( example_string )
2018-10-17 21:00:09 +00:00
self._UpdateControls()
2018-02-07 23:40:33 +00:00
2019-11-14 03:56:30 +00:00
class NoneableBytesControl( QW.QWidget ):
valueChanged = QC.Signal()
2018-10-17 21:00:09 +00:00
def __init__( self, parent, initial_value = 65536, none_label = 'no limit' ):
2018-02-07 23:40:33 +00:00
2019-11-14 03:56:30 +00:00
QW.QWidget.__init__( self, parent )
2018-02-07 23:40:33 +00:00
2018-10-17 21:00:09 +00:00
self._bytes = BytesControl( self )
2019-11-14 03:56:30 +00:00
self._none_checkbox = QW.QCheckBox( none_label, self )
2018-10-17 21:00:09 +00:00
#
self.SetValue( initial_value )
#
2019-11-14 03:56:30 +00:00
hbox = QP.HBoxLayout()
2018-10-17 21:00:09 +00:00
2019-11-14 03:56:30 +00:00
QP.AddToLayout( hbox, self._bytes, CC.FLAGS_SIZER_VCENTER )
QP.AddToLayout( hbox, self._none_checkbox, CC.FLAGS_VCENTER )
2018-10-17 21:00:09 +00:00
2019-11-14 03:56:30 +00:00
self.setLayout( hbox )
2018-10-17 21:00:09 +00:00
#
2019-11-14 03:56:30 +00:00
self._none_checkbox.clicked.connect( self._UpdateEnabled )
self._bytes.valueChanged.connect( self._HandleValueChanged )
self._none_checkbox.clicked.connect( self._HandleValueChanged )
2018-02-07 23:40:33 +00:00
2018-10-17 21:00:09 +00:00
def _UpdateEnabled( self ):
2018-02-07 23:40:33 +00:00
2019-11-14 03:56:30 +00:00
if self._none_checkbox.isChecked():
2018-02-07 23:40:33 +00:00
2019-11-14 03:56:30 +00:00
self._bytes.setEnabled( False )
2018-02-07 23:40:33 +00:00
2018-10-17 21:00:09 +00:00
else:
2018-02-07 23:40:33 +00:00
2019-11-14 03:56:30 +00:00
self._bytes.setEnabled( True )
2018-02-07 23:40:33 +00:00
2018-10-17 21:00:09 +00:00
2019-11-14 03:56:30 +00:00
def _HandleValueChanged( self ):
2018-10-17 21:00:09 +00:00
2019-11-14 03:56:30 +00:00
self.valueChanged.emit()
2018-10-17 21:00:09 +00:00
def GetValue( self ):
2019-11-14 03:56:30 +00:00
if self._none_checkbox.isChecked():
2018-02-07 23:40:33 +00:00
2018-10-17 21:00:09 +00:00
return None
else:
return self._bytes.GetValue()
2018-02-07 23:40:33 +00:00
2019-11-14 03:56:30 +00:00
def setToolTip( self, text ):
2018-02-07 23:40:33 +00:00
2019-11-14 03:56:30 +00:00
QW.QWidget.setToolTip( self, text )
2018-10-17 21:00:09 +00:00
2019-11-14 03:56:30 +00:00
for c in self.children():
2018-10-17 21:00:09 +00:00
2019-11-14 03:56:30 +00:00
if isinstance( c, QW.QWidget ):
c.setToolTip( text )
2018-10-17 21:00:09 +00:00
2018-02-07 23:40:33 +00:00
def SetValue( self, value ):
2018-10-17 21:00:09 +00:00
if value is None:
2019-11-14 03:56:30 +00:00
self._none_checkbox.setChecked( True )
2018-10-17 21:00:09 +00:00
else:
2019-11-14 03:56:30 +00:00
self._none_checkbox.setChecked( False )
2018-10-17 21:00:09 +00:00
self._bytes.SetValue( value )
self._UpdateEnabled()
2019-11-14 03:56:30 +00:00
class NetworkJobControl( QW.QFrame ):
2018-10-17 21:00:09 +00:00
def __init__( self, parent ):
2019-11-14 03:56:30 +00:00
QW.QFrame.__init__( self, parent )
self.setFrameStyle( QW.QFrame.Box | QW.QFrame.Raised )
2018-10-17 21:00:09 +00:00
self._network_job = None
self._download_started = False
self._auto_override_bandwidth_rules = False
2019-11-14 03:56:30 +00:00
self._left_text = ClientGUICommon.BetterStaticText( self, ellipsize_end = True )
self._right_text = ClientGUICommon.BetterStaticText( self )
self._right_text.setAlignment( QC.Qt.AlignRight | QC.Qt.AlignVCenter )
2018-10-17 21:00:09 +00:00
2019-11-14 03:56:30 +00:00
self._last_right_min_width = 0
2018-10-17 21:00:09 +00:00
self._gauge = ClientGUICommon.Gauge( self )
2020-03-11 21:52:11 +00:00
self._cog_button = ClientGUICommon.BetterBitmapButton( self, CC.global_pixmaps().cog, self._ShowCogMenu )
self._cancel_button = ClientGUICommon.BetterBitmapButton( self, CC.global_pixmaps().stop, self.Cancel )
2018-10-17 21:00:09 +00:00
#
self._Update()
#
2019-11-14 03:56:30 +00:00
st_hbox = QP.HBoxLayout()
2018-10-17 21:00:09 +00:00
2019-11-14 03:56:30 +00:00
QP.AddToLayout( st_hbox, self._left_text, CC.FLAGS_EXPAND_BOTH_WAYS )
QP.AddToLayout( st_hbox, self._right_text, CC.FLAGS_VCENTER )
2018-10-17 21:00:09 +00:00
2019-11-14 03:56:30 +00:00
left_vbox = QP.VBoxLayout()
2018-10-17 21:00:09 +00:00
2019-11-14 03:56:30 +00:00
QP.AddToLayout( left_vbox, st_hbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
QP.AddToLayout( left_vbox, self._gauge, CC.FLAGS_EXPAND_BOTH_WAYS )
2018-10-17 21:00:09 +00:00
2019-11-14 03:56:30 +00:00
hbox = QP.HBoxLayout()
2018-10-17 21:00:09 +00:00
2019-11-14 03:56:30 +00:00
QP.AddToLayout( hbox, left_vbox, CC.FLAGS_EXPAND_SIZER_BOTH_WAYS )
QP.AddToLayout( hbox, self._cog_button, CC.FLAGS_VCENTER )
QP.AddToLayout( hbox, self._cancel_button, CC.FLAGS_VCENTER )
2018-10-17 21:00:09 +00:00
2019-11-14 03:56:30 +00:00
self.setLayout( hbox )
2018-10-17 21:00:09 +00:00
def _ShowCogMenu( self ):
2019-11-14 03:56:30 +00:00
menu = QW.QMenu()
2018-10-17 21:00:09 +00:00
if self._network_job is not None:
2019-08-07 22:59:53 +00:00
if self._network_job.CurrentlyWaitingOnConnectionError():
2019-11-28 01:11:46 +00:00
2019-11-14 03:56:30 +00:00
ClientGUIMenus.AppendMenuItem( menu, 'reattempt connection now', 'Stop waiting on a connection error and reattempt the job now.', self._network_job.OverrideConnectionErrorWait )
2019-08-07 22:59:53 +00:00
2019-11-28 01:11:46 +00:00
if self._network_job.CurrentlyWaitingOnServersideBandwidth():
ClientGUIMenus.AppendMenuItem( menu, 'reattempt request now (server reports low bandwidth)', 'Stop waiting on a serverside bandwidth delay and reattempt the job now.', self._network_job.OverrideServersideBandwidthWait )
2018-10-17 21:00:09 +00:00
if self._network_job.ObeysBandwidth():
2019-11-28 01:11:46 +00:00
2019-11-14 03:56:30 +00:00
ClientGUIMenus.AppendMenuItem( menu, 'override bandwidth rules for this job', 'Tell the current job to ignore existing bandwidth rules and go ahead anyway.', self._network_job.OverrideBandwidth )
2018-10-17 21:00:09 +00:00
if not self._network_job.TokensOK():
2019-11-28 01:11:46 +00:00
2019-11-14 03:56:30 +00:00
ClientGUIMenus.AppendMenuItem( menu, 'override gallery slot requirements for this job', 'Force-allow this download to proceed, ignoring the normal gallery wait times.', self._network_job.OverrideToken )
2018-10-17 21:00:09 +00:00
ClientGUIMenus.AppendSeparator( menu )
2020-03-04 22:12:53 +00:00
2019-11-14 03:56:30 +00:00
ClientGUIMenus.AppendMenuCheckItem( menu, 'auto-override bandwidth rules for all jobs here after five seconds', 'Ignore existing bandwidth rules for all jobs under this control, instead waiting a flat five seconds.', self._auto_override_bandwidth_rules, self.FlipAutoOverrideBandwidth )
2018-10-17 21:00:09 +00:00
2020-03-04 22:12:53 +00:00
CGC.core().PopupMenu( self._cog_button, menu )
2018-10-17 21:00:09 +00:00
def _OverrideBandwidthIfAppropriate( self ):
if self._network_job is None or self._network_job.NoEngineYet():
return
else:
if self._auto_override_bandwidth_rules and HydrusData.TimeHasPassed( self._network_job.GetCreationTime() + 5 ):
self._network_job.OverrideBandwidth()
def _Update( self ):
if self._network_job is None or self._network_job.NoEngineYet():
2019-11-14 03:56:30 +00:00
self._left_text.setText( '' )
self._right_text.setText( '' )
2018-10-17 21:00:09 +00:00
self._gauge.SetRange( 1 )
self._gauge.SetValue( 0 )
can_cancel = False
else:
if self._network_job.IsDone():
can_cancel = False
else:
can_cancel = True
( status_text, current_speed, bytes_read, bytes_to_read ) = self._network_job.GetStatus()
2019-11-14 03:56:30 +00:00
self._left_text.setText( status_text )
2018-10-17 21:00:09 +00:00
if not self._download_started and current_speed > 0:
self._download_started = True
speed_text = ''
if self._download_started and not self._network_job.HasError():
if bytes_read is not None:
if bytes_to_read is not None and bytes_read != bytes_to_read:
speed_text += HydrusData.ConvertValueRangeToBytes( bytes_read, bytes_to_read )
else:
2019-01-09 22:59:03 +00:00
speed_text += HydrusData.ToHumanBytes( bytes_read )
2018-10-17 21:00:09 +00:00
if current_speed != bytes_to_read: # if it is a real quick download, just say its size
2019-01-09 22:59:03 +00:00
speed_text += ' ' + HydrusData.ToHumanBytes( current_speed ) + '/s'
2018-10-17 21:00:09 +00:00
2019-11-14 03:56:30 +00:00
self._right_text.setText( speed_text )
2018-10-17 21:00:09 +00:00
2019-06-26 21:27:18 +00:00
right_width = ClientGUIFunctions.ConvertTextToPixelWidth( self._right_text, len( speed_text ) )
2018-10-17 21:00:09 +00:00
2019-11-14 03:56:30 +00:00
right_min_width = right_width
2018-10-17 21:00:09 +00:00
2019-11-14 03:56:30 +00:00
if right_min_width != self._last_right_min_width:
2018-10-17 21:00:09 +00:00
2019-11-14 03:56:30 +00:00
self._last_right_min_width = right_min_width
2018-10-17 21:00:09 +00:00
2019-11-14 03:56:30 +00:00
self._right_text.setMinimumWidth( right_min_width )
2018-10-17 21:00:09 +00:00
self._gauge.SetRange( bytes_to_read )
self._gauge.SetValue( bytes_read )
if can_cancel:
2019-11-14 03:56:30 +00:00
if not self._cancel_button.isEnabled():
2018-10-17 21:00:09 +00:00
2019-11-14 03:56:30 +00:00
self._cancel_button.setEnabled( True )
2018-10-17 21:00:09 +00:00
else:
2019-11-14 03:56:30 +00:00
if self._cancel_button.isEnabled():
2018-10-17 21:00:09 +00:00
2019-11-14 03:56:30 +00:00
self._cancel_button.setEnabled( False )
2018-10-17 21:00:09 +00:00
def Cancel( self ):
if self._network_job is not None:
2018-10-31 21:41:14 +00:00
self._network_job.Cancel( 'Cancelled by user.' )
2018-10-17 21:00:09 +00:00
def ClearNetworkJob( self ):
self.SetNetworkJob( None )
def FlipAutoOverrideBandwidth( self ):
self._auto_override_bandwidth_rules = not self._auto_override_bandwidth_rules
def SetNetworkJob( self, network_job ):
if network_job is None:
if self._network_job is not None:
self._network_job = None
self._Update()
HG.client_controller.gui.UnregisterUIUpdateWindow( self )
else:
if self._network_job != network_job:
self._network_job = network_job
self._download_started = False
HG.client_controller.gui.RegisterUIUpdateWindow( self )
def TIMERUIUpdate( self ):
self._OverrideBandwidthIfAppropriate()
if HG.client_controller.gui.IShouldRegularlyUpdate( self ):
self._Update()
class StringConverterButton( ClientGUICommon.BetterButton ):
2019-11-14 03:56:30 +00:00
stringConverterUpdate = QC.Signal()
2018-10-17 21:00:09 +00:00
def __init__( self, parent, string_converter ):
ClientGUICommon.BetterButton.__init__( self, parent, 'edit string converter', self._Edit )
self._string_converter = string_converter
self._example_string_override = None
self._UpdateLabel()
def _Edit( self ):
with ClientGUITopLevelWindows.DialogEdit( self, 'edit string converter', frame_key = 'deeply_nested_dialog' ) as dlg:
panel = EditStringConverterPanel( dlg, self._string_converter, example_string_override = self._example_string_override )
dlg.SetPanel( panel )
2019-11-14 03:56:30 +00:00
if dlg.exec() == QW.QDialog.Accepted:
2018-10-17 21:00:09 +00:00
self._string_converter = panel.GetValue()
self._UpdateLabel()
2019-11-14 03:56:30 +00:00
self.stringConverterUpdate.emit()
2018-10-17 21:00:09 +00:00
def _UpdateLabel( self ):
num_rules = len( self._string_converter.transformations )
if num_rules == 0:
label = 'no string transformations'
else:
label = HydrusData.ToHumanInt( num_rules ) + ' string transformations'
2019-11-14 03:56:30 +00:00
self.setText( label )
2018-10-17 21:00:09 +00:00
def GetValue( self ):
return self._string_converter
def SetExampleString( self, example_string ):
self._example_string_override = example_string
def SetValue( self, string_converter ):
self._string_converter = string_converter
self._UpdateLabel()
class StringMatchButton( ClientGUICommon.BetterButton ):
def __init__( self, parent, string_match ):
ClientGUICommon.BetterButton.__init__( self, parent, 'edit string match', self._Edit )
self._string_match = string_match
self._UpdateLabel()
def _Edit( self ):
with ClientGUITopLevelWindows.DialogEdit( self, 'edit string match', frame_key = 'deeply_nested_dialog' ) as dlg:
panel = EditStringMatchPanel( dlg, self._string_match )
dlg.SetPanel( panel )
2019-11-14 03:56:30 +00:00
if dlg.exec() == QW.QDialog.Accepted:
2018-10-17 21:00:09 +00:00
self._string_match = panel.GetValue()
self._UpdateLabel()
def _UpdateLabel( self ):
2019-01-09 22:59:03 +00:00
label = self._string_match.ToString()
2018-10-17 21:00:09 +00:00
2019-11-14 03:56:30 +00:00
self.setText( label )
2018-10-17 21:00:09 +00:00
def GetValue( self ):
return self._string_match
def SetValue( self, string_match ):
self._string_match = string_match
self._UpdateLabel()
2019-11-14 03:56:30 +00:00
class StringMatchToStringMatchDictControl( QW.QWidget ):
2018-11-14 23:10:55 +00:00
def __init__( self, parent, initial_dict, min_height = 10, key_name = 'key' ):
2019-11-14 03:56:30 +00:00
QW.QWidget.__init__( self, parent )
2018-11-14 23:10:55 +00:00
self._key_name = key_name
listctrl_panel = ClientGUIListCtrl.BetterListCtrlPanel( self )
columns = [ ( self._key_name, 20 ), ( 'matching', -1 ) ]
self._listctrl = ClientGUIListCtrl.BetterListCtrl( listctrl_panel, 'key_to_string_match', min_height, 36, columns, self._ConvertDataToListCtrlTuples, use_simple_delete = True, activation_callback = self._Edit )
listctrl_panel.SetListCtrl( self._listctrl )
listctrl_panel.AddButton( 'add', self._Add )
listctrl_panel.AddButton( 'edit', self._Edit, enabled_only_on_selection = True )
listctrl_panel.AddDeleteButton()
#
2019-01-09 22:59:03 +00:00
self._listctrl.AddDatas( list(initial_dict.items()) )
2018-11-14 23:10:55 +00:00
self._listctrl.Sort()
#
2019-11-14 03:56:30 +00:00
vbox = QP.VBoxLayout()
2018-11-14 23:10:55 +00:00
2019-11-14 03:56:30 +00:00
QP.AddToLayout( vbox, listctrl_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
2018-11-14 23:10:55 +00:00
2019-11-14 03:56:30 +00:00
self.setLayout( vbox )
2018-11-14 23:10:55 +00:00
def _ConvertDataToListCtrlTuples( self, data ):
( key_string_match, value_string_match ) = data
2019-01-09 22:59:03 +00:00
pretty_key = key_string_match.ToString()
pretty_value = value_string_match.ToString()
2018-11-14 23:10:55 +00:00
display_tuple = ( pretty_key, pretty_value )
sort_tuple = ( pretty_key, pretty_value )
return ( display_tuple, sort_tuple )
def _Add( self ):
with ClientGUITopLevelWindows.DialogEdit( self, 'edit ' + self._key_name ) as dlg:
string_match = ClientParsing.StringMatch()
panel = EditStringMatchPanel( dlg, string_match )
dlg.SetPanel( panel )
2019-11-14 03:56:30 +00:00
if dlg.exec() == QW.QDialog.Accepted:
2018-11-14 23:10:55 +00:00
key_string_match = panel.GetValue()
else:
return
with ClientGUITopLevelWindows.DialogEdit( self, 'edit match' ) as dlg:
string_match = ClientParsing.StringMatch()
panel = EditStringMatchPanel( dlg, string_match )
dlg.SetPanel( panel )
2019-11-14 03:56:30 +00:00
if dlg.exec() == QW.QDialog.Accepted:
2018-11-14 23:10:55 +00:00
value_string_match = panel.GetValue()
data = ( key_string_match, value_string_match )
self._listctrl.AddDatas( ( data, ) )
def _Edit( self ):
for data in self._listctrl.GetData( only_selected = True ):
( key_string_match, value_string_match ) = data
with ClientGUITopLevelWindows.DialogEdit( self, 'edit ' + self._key_name ) as dlg:
panel = EditStringMatchPanel( dlg, key_string_match )
dlg.SetPanel( panel )
2019-11-14 03:56:30 +00:00
if dlg.exec() == QW.QDialog.Accepted:
2018-11-14 23:10:55 +00:00
key_string_match = panel.GetValue()
else:
break
with ClientGUITopLevelWindows.DialogEdit( self, 'edit match' ) as dlg:
panel = EditStringMatchPanel( dlg, value_string_match )
dlg.SetPanel( panel )
2019-11-14 03:56:30 +00:00
if dlg.exec() == QW.QDialog.Accepted:
2018-11-14 23:10:55 +00:00
value_string_match = panel.GetValue()
else:
break
self._listctrl.DeleteDatas( ( data, ) )
edited_data = ( key_string_match, value_string_match )
self._listctrl.AddDatas( ( edited_data, ) )
self._listctrl.Sort()
def GetValue( self ):
value_dict = dict( self._listctrl.GetData() )
return value_dict
2018-10-17 21:00:09 +00:00
class StringToStringDictButton( ClientGUICommon.BetterButton ):
def __init__( self, parent, label ):
ClientGUICommon.BetterButton.__init__( self, parent, label, self._Edit )
self._value = {}
def _Edit( self ):
with ClientGUITopLevelWindows.DialogEdit( self, 'edit string dictionary' ) as dlg:
panel = ClientGUIScrolledPanels.EditSingleCtrlPanel( dlg )
control = StringToStringDictControl( panel, self._value )
panel.SetControl( control )
dlg.SetPanel( panel )
2019-11-14 03:56:30 +00:00
if dlg.exec() == QW.QDialog.Accepted:
2018-10-17 21:00:09 +00:00
self._value = control.GetValue()
def GetValue( self ):
return self._value
def SetValue( self, value ):
self._value = value
2019-11-14 03:56:30 +00:00
class StringToStringDictControl( QW.QWidget ):
listCtrlChanged = QC.Signal()
2018-10-17 21:00:09 +00:00
2019-07-10 22:38:30 +00:00
def __init__( self, parent, initial_dict, min_height = 10, key_name = 'key', value_name = 'value', allow_add_delete = True, edit_keys = True ):
2018-10-17 21:00:09 +00:00
2019-11-14 03:56:30 +00:00
QW.QWidget.__init__( self, parent )
2018-10-17 21:00:09 +00:00
self._key_name = key_name
self._value_name = value_name
2019-07-10 22:38:30 +00:00
self._edit_keys = edit_keys
2018-10-17 21:00:09 +00:00
listctrl_panel = ClientGUIListCtrl.BetterListCtrlPanel( self )
columns = [ ( self._key_name, 20 ), ( self._value_name, -1 ) ]
2019-07-10 22:38:30 +00:00
use_simple_delete = allow_add_delete
self._listctrl = ClientGUIListCtrl.BetterListCtrl( listctrl_panel, 'key_to_value', min_height, 36, columns, self._ConvertDataToListCtrlTuples, use_simple_delete = use_simple_delete, activation_callback = self._Edit )
2019-11-14 03:56:30 +00:00
self._listctrl.listCtrlChanged.connect( self.listCtrlChanged )
2018-10-17 21:00:09 +00:00
listctrl_panel.SetListCtrl( self._listctrl )
2019-07-10 22:38:30 +00:00
if allow_add_delete:
listctrl_panel.AddButton( 'add', self._Add )
2018-10-17 21:00:09 +00:00
listctrl_panel.AddButton( 'edit', self._Edit, enabled_only_on_selection = True )
2019-07-10 22:38:30 +00:00
if allow_add_delete:
listctrl_panel.AddDeleteButton()
2018-10-17 21:00:09 +00:00
#
2019-01-09 22:59:03 +00:00
self._listctrl.AddDatas( list(initial_dict.items()) )
2018-10-17 21:00:09 +00:00
self._listctrl.Sort()
#
2019-11-14 03:56:30 +00:00
vbox = QP.VBoxLayout()
2018-10-17 21:00:09 +00:00
2019-11-14 03:56:30 +00:00
QP.AddToLayout( vbox, listctrl_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
2018-10-17 21:00:09 +00:00
2019-11-14 03:56:30 +00:00
self.setLayout( vbox )
2018-10-17 21:00:09 +00:00
def _ConvertDataToListCtrlTuples( self, data ):
( key, value ) = data
display_tuple = ( key, value )
sort_tuple = ( key, value )
return ( display_tuple, sort_tuple )
def _Add( self ):
with ClientGUIDialogs.DialogTextEntry( self, 'enter the ' + self._key_name, allow_blank = False ) as dlg:
2019-11-14 03:56:30 +00:00
if dlg.exec() == QW.QDialog.Accepted:
2018-10-17 21:00:09 +00:00
key = dlg.GetValue()
if key in self._GetExistingKeys():
2019-11-14 03:56:30 +00:00
QW.QMessageBox.warning( self, 'Warning', 'That {} already exists!'.format( self._key_name ) )
2018-10-17 21:00:09 +00:00
return
with ClientGUIDialogs.DialogTextEntry( self, 'enter the ' + self._value_name, allow_blank = True ) as dlg:
2019-11-14 03:56:30 +00:00
if dlg.exec() == QW.QDialog.Accepted:
2018-10-17 21:00:09 +00:00
value = dlg.GetValue()
data = ( key, value )
self._listctrl.AddDatas( ( data, ) )
def _Edit( self ):
for data in self._listctrl.GetData( only_selected = True ):
( key, value ) = data
2019-07-10 22:38:30 +00:00
if self._edit_keys:
2018-10-17 21:00:09 +00:00
2019-07-10 22:38:30 +00:00
with ClientGUIDialogs.DialogTextEntry( self, 'edit the ' + self._key_name, default = key, allow_blank = False ) as dlg:
2018-10-17 21:00:09 +00:00
2019-11-14 03:56:30 +00:00
if dlg.exec() == QW.QDialog.Accepted:
2019-07-10 22:38:30 +00:00
edited_key = dlg.GetValue()
if edited_key != key and edited_key in self._GetExistingKeys():
2019-11-14 03:56:30 +00:00
QW.QMessageBox.warning( self, 'Warning', 'That {} already exists!'.format( self._key_name ) )
2019-07-10 22:38:30 +00:00
break
2018-10-17 21:00:09 +00:00
2019-07-10 22:38:30 +00:00
else:
2018-10-17 21:00:09 +00:00
break
2019-07-10 22:38:30 +00:00
else:
edited_key = key
2018-10-17 21:00:09 +00:00
with ClientGUIDialogs.DialogTextEntry( self, 'edit the ' + self._value_name, default = value, allow_blank = True ) as dlg:
2019-11-14 03:56:30 +00:00
if dlg.exec() == QW.QDialog.Accepted:
2018-10-17 21:00:09 +00:00
edited_value = dlg.GetValue()
else:
break
self._listctrl.DeleteDatas( ( data, ) )
edited_data = ( edited_key, edited_value )
self._listctrl.AddDatas( ( edited_data, ) )
self._listctrl.Sort()
def _GetExistingKeys( self ):
return { key for ( key, value ) in self._listctrl.GetData() }
def GetValue( self ):
value_dict = dict( self._listctrl.GetData() )
return value_dict
2019-11-14 03:56:30 +00:00
class StringToStringMatchDictControl( QW.QWidget ):
2018-10-17 21:00:09 +00:00
def __init__( self, parent, initial_dict, min_height = 10, key_name = 'key' ):
2019-11-14 03:56:30 +00:00
QW.QWidget.__init__( self, parent )
2018-10-17 21:00:09 +00:00
self._key_name = key_name
listctrl_panel = ClientGUIListCtrl.BetterListCtrlPanel( self )
columns = [ ( self._key_name, 20 ), ( 'matching', -1 ) ]
self._listctrl = ClientGUIListCtrl.BetterListCtrl( listctrl_panel, 'key_to_string_match', min_height, 36, columns, self._ConvertDataToListCtrlTuples, use_simple_delete = True, activation_callback = self._Edit )
listctrl_panel.SetListCtrl( self._listctrl )
listctrl_panel.AddButton( 'add', self._Add )
listctrl_panel.AddButton( 'edit', self._Edit, enabled_only_on_selection = True )
listctrl_panel.AddDeleteButton()
#
2019-01-09 22:59:03 +00:00
self._listctrl.AddDatas( list(initial_dict.items()) )
2018-10-17 21:00:09 +00:00
self._listctrl.Sort()
#
2019-11-14 03:56:30 +00:00
vbox = QP.VBoxLayout()
2018-10-17 21:00:09 +00:00
2019-11-14 03:56:30 +00:00
QP.AddToLayout( vbox, listctrl_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
2018-10-17 21:00:09 +00:00
2019-11-14 03:56:30 +00:00
self.setLayout( vbox )
2018-10-17 21:00:09 +00:00
def _ConvertDataToListCtrlTuples( self, data ):
( key, string_match ) = data
2019-01-09 22:59:03 +00:00
pretty_string_match = string_match.ToString()
2018-10-17 21:00:09 +00:00
display_tuple = ( key, pretty_string_match )
sort_tuple = ( key, pretty_string_match )
return ( display_tuple, sort_tuple )
def _Add( self ):
with ClientGUIDialogs.DialogTextEntry( self, 'enter the ' + self._key_name, allow_blank = False ) as dlg:
2019-11-14 03:56:30 +00:00
if dlg.exec() == QW.QDialog.Accepted:
2018-10-17 21:00:09 +00:00
key = dlg.GetValue()
if key in self._GetExistingKeys():
2019-11-14 03:56:30 +00:00
QW.QMessageBox.warning( self, 'Warning', 'That {} already exists!'.format( self._key_name ) )
2018-10-17 21:00:09 +00:00
return
with ClientGUITopLevelWindows.DialogEdit( self, 'edit match' ) as dlg:
string_match = ClientParsing.StringMatch()
panel = EditStringMatchPanel( dlg, string_match )
dlg.SetPanel( panel )
2019-11-14 03:56:30 +00:00
if dlg.exec() == QW.QDialog.Accepted:
2018-10-17 21:00:09 +00:00
string_match = panel.GetValue()
data = ( key, string_match )
self._listctrl.AddDatas( ( data, ) )
def _Edit( self ):
for data in self._listctrl.GetData( only_selected = True ):
( key, string_match ) = data
with ClientGUIDialogs.DialogTextEntry( self, 'edit the ' + self._key_name, default = key, allow_blank = False ) as dlg:
2019-11-14 03:56:30 +00:00
if dlg.exec() == QW.QDialog.Accepted:
2018-10-17 21:00:09 +00:00
edited_key = dlg.GetValue()
if edited_key != key and edited_key in self._GetExistingKeys():
2019-11-14 03:56:30 +00:00
QW.QMessageBox.warning( self, 'Warning', 'That {} already exists!'.format( self._key_name ) )
2018-10-17 21:00:09 +00:00
break
else:
break
with ClientGUITopLevelWindows.DialogEdit( self, 'edit match' ) as dlg:
string_match = ClientParsing.StringMatch()
panel = EditStringMatchPanel( dlg, string_match )
dlg.SetPanel( panel )
2019-11-14 03:56:30 +00:00
if dlg.exec() == QW.QDialog.Accepted:
2018-10-17 21:00:09 +00:00
edited_string_match = panel.GetValue()
else:
break
self._listctrl.DeleteDatas( ( data, ) )
edited_data = ( edited_key, edited_string_match )
self._listctrl.AddDatas( ( edited_data, ) )
self._listctrl.Sort()
def _GetExistingKeys( self ):
return { key for ( key, value ) in self._listctrl.GetData() }
def GetValue( self ):
value_dict = dict( self._listctrl.GetData() )
return value_dict
2018-02-07 23:40:33 +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
#
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 )
QP.AddToLayout( hbox, self._paste_button, CC.FLAGS_VCENTER )
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, ) )
self._text_input.setText( '' )
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