hydrus/include/ClientGUIParsing.py

2022 lines
66 KiB
Python

import ClientConstants as CC
import ClientGUICommon
import ClientGUIDialogs
import ClientGUIMenus
import ClientGUIScrolledPanels
import ClientGUISerialisable
import ClientGUITopLevelWindows
import ClientNetworking
import ClientParsing
import ClientSerialisable
import ClientThreading
import HydrusConstants as HC
import HydrusData
import HydrusGlobals
import HydrusSerialisable
import HydrusTags
import os
import threading
import webbrowser
import wx
ID_TIMER_SCRIPT_UPDATE = wx.NewId()
class EditHTMLTagRulePanel( ClientGUIScrolledPanels.EditPanel ):
def __init__( self, parent, rule ):
ClientGUIScrolledPanels.EditPanel.__init__( self, parent )
( name, attrs, index ) = rule
self._name = wx.TextCtrl( self )
self._attrs = ClientGUICommon.EditStringToStringDict( self, attrs )
message = 'index to fetch'
self._index = ClientGUICommon.NoneableSpinCtrl( self, message, none_phrase = 'get all', min = 0, max = 255 )
#
self._name.SetValue( name )
self._index.SetValue( index )
#
vbox = wx.BoxSizer( wx.VERTICAL )
rows = []
rows.append( ( 'tag name: ', self._name ) )
gridbox = ClientGUICommon.WrapInGrid( self, rows )
vbox.AddF( gridbox, CC.FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( self._attrs, CC.FLAGS_EXPAND_BOTH_WAYS )
vbox.AddF( self._index, CC.FLAGS_EXPAND_PERPENDICULAR )
self.SetSizer( vbox )
def GetValue( self ):
name = self._name.GetValue()
attrs = self._attrs.GetValue()
index = self._index.GetValue()
return ( name, attrs, index )
class EditHTMLFormulaPanel( ClientGUIScrolledPanels.EditPanel ):
def __init__( self, parent, formula, example_data ):
ClientGUIScrolledPanels.EditPanel.__init__( self, parent )
notebook = wx.Notebook( self )
#
edit_panel = wx.Panel( notebook )
edit_panel.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_FRAMEBK ) )
self._tag_rules = wx.ListBox( edit_panel, style = wx.LB_SINGLE )
self._tag_rules.Bind( wx.EVT_LEFT_DCLICK, self.EventEdit )
self._add_rule = ClientGUICommon.BetterButton( edit_panel, 'add', self.Add )
self._edit_rule = ClientGUICommon.BetterButton( edit_panel, 'edit', self.Edit )
self._move_rule_up = ClientGUICommon.BetterButton( edit_panel, u'\u2191', self.MoveUp )
self._delete_rule = ClientGUICommon.BetterButton( edit_panel, 'X', self.Delete )
self._move_rule_down = ClientGUICommon.BetterButton( edit_panel, u'\u2193', self.MoveDown )
self._content_rule = wx.TextCtrl( edit_panel )
self._cull_front = wx.SpinCtrl( edit_panel, min = -65535, max = 65535 )
self._cull_back = wx.SpinCtrl( edit_panel, min = -65535, max = 65535 )
self._prepend = wx.TextCtrl( edit_panel )
self._append = wx.TextCtrl( edit_panel )
#
test_panel = wx.Panel( notebook )
test_panel.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_FRAMEBK ) )
self._example_data = ClientGUICommon.SaneMultilineTextCtrl( test_panel )
self._example_data.SetMinSize( ( -1, 200 ) )
self._example_data.SetValue( example_data )
self._run_test = ClientGUICommon.BetterButton( test_panel, 'test parse', self.TestParse )
self._results = ClientGUICommon.SaneMultilineTextCtrl( test_panel )
self._results.SetMinSize( ( -1, 200 ) )
#
info_panel = wx.Panel( notebook )
message = '''This searches html for simple strings, which it returns to its parent.
The html's branches will be searched recursively by each tag rule in turn and then the given attribute of the final tags will be returned.
So, to find the 'src' of the first <img> tag beneath all <span> tags with the class 'content', use:
'all span tags with class=content'
1st img tag'
attribute: src'
Leave the 'attribute' blank to fetch the string of the tag (i.e. <p>This part</p>).
Note that you can set _negative_ numbers for the 'remove characters' parts, which will remove all but that many of the opposite end's characters. For instance:
remove 2 from the beginning of 'abcdef' gives 'cdef'
remove -2 from the beginning of 'abcdef' gives 'ef'.'''
info_st = wx.StaticText( info_panel, label = message )
info_st.Wrap( 400 )
#
( tag_rules, content_rule, culling_and_adding ) = formula.ToTuple()
for rule in tag_rules:
pretty_rule = ClientParsing.RenderTagRule( rule )
self._tag_rules.Append( pretty_rule, rule )
if content_rule is None:
content_rule = ''
self._content_rule.SetValue( content_rule )
self._results.SetValue( 'Successfully parsed results will be printed here.' )
#
udd_button_vbox = wx.BoxSizer( wx.VERTICAL )
udd_button_vbox.AddF( ( 20, 20 ), CC.FLAGS_EXPAND_SIZER_BOTH_WAYS )
udd_button_vbox.AddF( self._move_rule_up, CC.FLAGS_VCENTER )
udd_button_vbox.AddF( self._delete_rule, CC.FLAGS_VCENTER )
udd_button_vbox.AddF( self._move_rule_down, CC.FLAGS_VCENTER )
udd_button_vbox.AddF( ( 20, 20 ), CC.FLAGS_EXPAND_SIZER_BOTH_WAYS )
tag_rules_hbox = wx.BoxSizer( wx.HORIZONTAL )
tag_rules_hbox.AddF( self._tag_rules, CC.FLAGS_EXPAND_BOTH_WAYS )
tag_rules_hbox.AddF( udd_button_vbox, CC.FLAGS_VCENTER )
ae_button_hbox = wx.BoxSizer( wx.HORIZONTAL )
ae_button_hbox.AddF( self._add_rule, CC.FLAGS_VCENTER )
ae_button_hbox.AddF( self._edit_rule, CC.FLAGS_VCENTER )
rows = []
rows.append( ( 'attribute to fetch: ', self._content_rule ) )
rows.append( ( 'remove this number of characters from the beginning: ', self._cull_front ) )
rows.append( ( 'remove this number of characters from the end: ', self._cull_back ) )
rows.append( ( 'prepend this: ', self._prepend ) )
rows.append( ( 'append this: ', self._append ) )
gridbox = ClientGUICommon.WrapInGrid( edit_panel, rows )
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( tag_rules_hbox, CC.FLAGS_EXPAND_BOTH_WAYS )
vbox.AddF( ae_button_hbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
vbox.AddF( gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
edit_panel.SetSizer( vbox )
#
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( self._example_data, CC.FLAGS_EXPAND_BOTH_WAYS )
vbox.AddF( self._run_test, CC.FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( self._results, CC.FLAGS_EXPAND_BOTH_WAYS )
test_panel.SetSizer( vbox )
#
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( info_st, CC.FLAGS_EXPAND_BOTH_WAYS )
info_panel.SetSizer( vbox )
#
notebook.AddPage( edit_panel, 'edit', select = True )
notebook.AddPage( test_panel, 'test', select = False )
notebook.AddPage( info_panel, 'info', select = False )
#
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( notebook, CC.FLAGS_EXPAND_SIZER_BOTH_WAYS )
self.SetSizer( vbox )
def Add( self ):
dlg_title = 'edit tag rule'
with ClientGUITopLevelWindows.DialogEdit( self, dlg_title ) as dlg:
new_rule = ( 'a', {}, None )
panel = EditHTMLTagRulePanel( dlg, new_rule )
dlg.SetPanel( panel )
if dlg.ShowModal() == wx.ID_OK:
rule = panel.GetValue()
pretty_rule = ClientParsing.RenderTagRule( rule )
self._tag_rules.Append( pretty_rule, rule )
def Delete( self ):
selection = self._tag_rules.GetSelection()
if selection != wx.NOT_FOUND:
if self._tag_rules.GetCount() == 1:
wx.MessageBox( 'A parsing formula needs at least one tag rule!' )
else:
self._tag_rules.Delete( selection )
def Edit( self ):
selection = self._tag_rules.GetSelection()
if selection != wx.NOT_FOUND:
rule = self._tag_rules.GetClientData( selection )
dlg_title = 'edit tag rule'
with ClientGUITopLevelWindows.DialogEdit( self, dlg_title ) as dlg:
panel = EditHTMLTagRulePanel( dlg, rule )
dlg.SetPanel( panel )
if dlg.ShowModal() == wx.ID_OK:
rule = panel.GetValue()
pretty_rule = ClientParsing.RenderTagRule( rule )
self._tag_rules.SetString( selection, pretty_rule )
self._tag_rules.SetClientData( selection, rule )
def EventEdit( self, event ):
self.Edit()
def GetValue( self ):
tags_rules = [ self._tag_rules.GetClientData( i ) for i in range( self._tag_rules.GetCount() ) ]
content_rule = self._content_rule.GetValue()
if content_rule == '':
content_rule = None
culling_and_adding = ( self._cull_front.GetValue(), self._cull_back.GetValue(), self._prepend.GetValue(), self._append.GetValue() )
formula = ClientParsing.ParseFormulaHTML( tags_rules, content_rule, culling_and_adding )
return formula
def MoveDown( self ):
selection = self._tag_rules.GetSelection()
if selection != wx.NOT_FOUND and selection + 1 < self._tag_rules.GetCount():
pretty_rule = self._tag_rules.GetString( selection )
rule = self._tag_rules.GetClientData( selection )
self._tag_rules.Delete( selection )
self._tag_rules.Insert( pretty_rule, selection + 1, rule )
def MoveUp( self ):
selection = self._tag_rules.GetSelection()
if selection != wx.NOT_FOUND and selection > 0:
pretty_rule = self._tag_rules.GetString( selection )
rule = self._tag_rules.GetClientData( selection )
self._tag_rules.Delete( selection )
self._tag_rules.Insert( pretty_rule, selection - 1, rule )
def TestParse( self ):
formula = self.GetValue()
html = self._example_data.GetValue()
try:
results = formula.Parse( html )
results = [ '*** RESULTS BEGIN ***' ] + results + [ '*** RESULTS END ***' ]
results_text = os.linesep.join( results )
self._results.SetValue( results_text )
except Exception as e:
HydrusData.ShowException( e )
message = 'Could not parse!'
wx.MessageBox( message )
class EditNodes( wx.Panel ):
def __init__( self, parent, nodes, referral_url_callable, example_data_callable ):
wx.Panel.__init__( self, parent )
self._referral_url_callable = referral_url_callable
self._example_data_callable = example_data_callable
self._nodes = ClientGUICommon.SaneListCtrl( self, 200, [ ( 'name', 120 ), ( 'node type', 80 ), ( 'produces', -1 ) ], delete_key_callback = self.Delete, activation_callback = self.Edit, use_display_tuple_for_sort = True )
menu_items = []
menu_items.append( ( 'content node', 'A node that parses the given data for content.', self.AddContentNode ) )
menu_items.append( ( 'link node', 'A node that parses the given data for a link, which it then pursues.', self.AddLinkNode ) )
self._add_button = ClientGUICommon.MenuButton( self, 'add', menu_items )
self._copy_button = ClientGUICommon.BetterButton( self, 'copy', self.Copy )
self._paste_button = ClientGUICommon.BetterButton( self, 'paste', self.Paste )
self._duplicate_button = ClientGUICommon.BetterButton( self, 'duplicate', self.Duplicate )
self._edit_button = ClientGUICommon.BetterButton( self, 'edit', self.Edit )
self._delete_button = ClientGUICommon.BetterButton( self, 'delete', self.Delete )
#
for node in nodes:
( display_tuple, data_tuple ) = self._ConvertNodeToTuples( node )
self._nodes.Append( display_tuple, data_tuple )
#
vbox = wx.BoxSizer( wx.VERTICAL )
button_hbox = wx.BoxSizer( wx.HORIZONTAL )
button_hbox.AddF( self._add_button, CC.FLAGS_VCENTER )
button_hbox.AddF( self._copy_button, CC.FLAGS_VCENTER )
button_hbox.AddF( self._paste_button, CC.FLAGS_VCENTER )
button_hbox.AddF( self._duplicate_button, CC.FLAGS_VCENTER )
button_hbox.AddF( self._edit_button, CC.FLAGS_VCENTER )
button_hbox.AddF( self._delete_button, CC.FLAGS_VCENTER )
vbox.AddF( self._nodes, CC.FLAGS_EXPAND_BOTH_WAYS )
vbox.AddF( button_hbox, CC.FLAGS_BUTTON_SIZER )
self.SetSizer( vbox )
def _ConvertNodeToTuples( self, node ):
( name, node_type, produces ) = node.ToPrettyStrings()
return ( ( name, node_type, produces ), ( node, node_type, produces ) )
def AddContentNode( self ):
dlg_title = 'edit content node'
empty_node = ClientParsing.ParseNodeContent()
panel_class = EditParseNodeContentPanel
self.AddNode( dlg_title, empty_node, panel_class )
def AddLinkNode( self ):
dlg_title = 'edit link node'
empty_node = ClientParsing.ParseNodeContentLink()
panel_class = EditParseNodeContentLinkPanel
self.AddNode( dlg_title, empty_node, panel_class )
def AddNode( self, dlg_title, empty_node, panel_class ):
with ClientGUITopLevelWindows.DialogEdit( self, dlg_title ) as dlg_edit:
referral_url = self._referral_url_callable()
example_data = self._example_data_callable()
panel = panel_class( dlg_edit, empty_node, referral_url, example_data )
dlg_edit.SetPanel( panel )
if dlg_edit.ShowModal() == wx.ID_OK:
new_node = panel.GetValue()
( display_tuple, data_tuple ) = self._ConvertNodeToTuples( new_node )
self._nodes.Append( display_tuple, data_tuple )
def Copy( self ):
for i in self._nodes.GetAllSelected():
( node, node_type, produces ) = self._nodes.GetClientData( i )
node_json = node.DumpToString()
HydrusGlobals.client_controller.pub( 'clipboard', 'text', node_json )
def Delete( self ):
self._nodes.RemoveAllSelected()
def Duplicate( self ):
nodes_to_dupe = []
for i in self._nodes.GetAllSelected():
( node, node_type, produces ) = self._nodes.GetClientData( i )
nodes_to_dupe.append( node )
for node in nodes_to_dupe:
dupe_node = node.Duplicate()
( display_tuple, data_tuple ) = self._ConvertNodeToTuples( dupe_node )
self._nodes.Append( display_tuple, data_tuple )
def Edit( self ):
for i in self._nodes.GetAllSelected():
( node, node_type, produces ) = self._nodes.GetClientData( i )
with ClientGUITopLevelWindows.DialogEdit( self, 'edit node' ) as dlg:
if isinstance( node, ClientParsing.ParseNodeContent):
panel_class = EditParseNodeContentPanel
elif isinstance( node, ClientParsing.ParseNodeContentLink ):
panel_class = EditParseNodeContentLinkPanel
referral_url = self._referral_url_callable()
example_data = self._example_data_callable()
panel = panel_class( dlg, node, referral_url, example_data )
dlg.SetPanel( panel )
if dlg.ShowModal() == wx.ID_OK:
edited_node = panel.GetValue()
( display_tuple, data_tuple ) = self._ConvertNodeToTuples( edited_node )
self._nodes.UpdateRow( i, display_tuple, data_tuple )
def GetValue( self ):
nodes = [ node for ( node, node_type, produces ) in self._nodes.GetClientData() ]
return nodes
def Paste( self ):
if wx.TheClipboard.Open():
data = wx.TextDataObject()
wx.TheClipboard.GetData( data )
wx.TheClipboard.Close()
raw_text = data.GetText()
try:
obj = HydrusSerialisable.CreateFromString( raw_text )
if isinstance( obj, ( ClientParsing.ParseNodeContent, ClientParsing.ParseNodeContentLink ) ):
node = obj
( display_tuple, data_tuple ) = self._ConvertNodeToTuples( node )
self._nodes.Append( display_tuple, data_tuple )
except:
wx.MessageBox( 'I could not understand what was in the clipboard' )
else:
wx.MessageBox( 'I could not get permission to access the clipboard.' )
class EditParseNodeContentPanel( ClientGUIScrolledPanels.EditPanel ):
def __init__( self, parent, node, referral_url = None, example_data = None ):
ClientGUIScrolledPanels.EditPanel.__init__( self, parent )
if referral_url is None:
referral_url = 'test-url.com/test_query'
self._referral_url = referral_url
if example_data is None:
example_data = ''
notebook = wx.Notebook( self )
#
self._edit_panel = wx.Panel( notebook )
self._edit_panel.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_FRAMEBK ) )
self._name = wx.TextCtrl( self._edit_panel )
self._content_panel = ClientGUICommon.StaticBox( self._edit_panel, 'content type' )
self._content_type = ClientGUICommon.BetterChoice( self._content_panel )
self._content_type.Append( 'tags', HC.CONTENT_TYPE_MAPPINGS )
self._content_type.Append( 'veto', HC.CONTENT_TYPE_VETO )
self._content_type.Bind( wx.EVT_CHOICE, self.EventContentTypeChange )
# bind an event here when I add new content types that will dynamically hide/show the namespace/rating stuff and relayout as needed
# it should have a forced name or something. whatever we'll use to discriminate between rating services on 'import options - ratings'
# (this probably means sending and EditPanel size changed event or whatever)
self._mappings_panel = wx.Panel( self._content_panel )
self._namespace = wx.TextCtrl( self._mappings_panel )
self._veto_panel = wx.Panel( self._content_panel )
self._veto_if_matches_found = wx.CheckBox( self._veto_panel )
self._match_if_text_present = wx.CheckBox( self._veto_panel )
self._search_text = wx.TextCtrl( self._veto_panel )
formula_panel = ClientGUICommon.StaticBox( self._edit_panel, 'formula' )
self._formula_description = ClientGUICommon.SaneMultilineTextCtrl( formula_panel )
self._formula_description.SetMinSize( ( -1, 200 ) )
self._formula_description.Disable()
self._edit_formula = ClientGUICommon.BetterButton( formula_panel, 'edit formula', self.EditFormula )
#
test_panel = wx.Panel( notebook )
test_panel.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_FRAMEBK ) )
self._example_data = ClientGUICommon.SaneMultilineTextCtrl( test_panel )
self._example_data.SetMinSize( ( -1, 200 ) )
self._test_parse = ClientGUICommon.BetterButton( test_panel, 'test parse', self.TestParse )
self._results = ClientGUICommon.SaneMultilineTextCtrl( test_panel )
self._results.SetMinSize( ( -1, 200 ) )
#
info_panel = wx.Panel( notebook )
message = '''This node takes html from its parent and applies a parsing formula to it to search for content.
Select the content type and set any additional info to further modify what the formula returns.
The 'veto' type will tell the parent panel that this page, while it returned 200 OK, is nonetheless incorrect (e.g. the searched-for image does not exist, so you have been redirected back to a default gallery page) and so no parsing should be done on it. If the value in the additional info box exists anywhere in what the formula finds, the veto will be raised.'''
info_st = wx.StaticText( info_panel, label = message )
info_st.Wrap( 400 )
#
( name, content_type, self._current_formula, additional_info ) = node.ToTuple()
self._name.SetValue( name )
self._content_type.SelectClientData( content_type )
if content_type == HC.CONTENT_TYPE_MAPPINGS:
namespace = additional_info
self._namespace.SetValue( namespace )
elif content_type == HC.CONTENT_TYPE_VETO:
( veto_if_matches_found, match_if_text_present, search_text ) = additional_info
self._veto_if_matches_found.SetValue( veto_if_matches_found )
self._match_if_text_present.SetValue( match_if_text_present )
self._search_text.SetValue( search_text )
self._formula_description.SetValue( self._current_formula.ToPrettyMultilineString() )
self._example_data.SetValue( example_data )
self._results.SetValue( 'Successfully parsed results will be printed here.' )
#
rows = []
rows.append( ( 'namespace: ', self._namespace ) )
gridbox = ClientGUICommon.WrapInGrid( self._mappings_panel, rows )
self._mappings_panel.SetSizer( gridbox )
#
rows = []
rows.append( ( 'veto if matches found: ', self._veto_if_matches_found ) )
rows.append( ( 'match if text present: ', self._match_if_text_present ) )
rows.append( ( 'search text: ', self._search_text ) )
gridbox = ClientGUICommon.WrapInGrid( self._veto_panel, rows )
self._veto_panel.SetSizer( gridbox )
#
rows = []
rows.append( ( 'content type: ', self._content_type ) )
gridbox = ClientGUICommon.WrapInGrid( self._content_panel, rows )
self._content_panel.AddF( gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
self._content_panel.AddF( self._mappings_panel, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
self._content_panel.AddF( self._veto_panel, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
#
formula_panel.AddF( self._formula_description, CC.FLAGS_EXPAND_BOTH_WAYS )
formula_panel.AddF( self._edit_formula, CC.FLAGS_EXPAND_PERPENDICULAR )
#
vbox = wx.BoxSizer( wx.VERTICAL )
rows = []
rows.append( ( 'name or description (optional): ', self._name ) )
gridbox = ClientGUICommon.WrapInGrid( self._edit_panel, rows )
vbox.AddF( gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
vbox.AddF( self._content_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( formula_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
self._edit_panel.SetSizer( vbox )
#
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( self._example_data, CC.FLAGS_EXPAND_BOTH_WAYS )
vbox.AddF( self._test_parse, CC.FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( self._results, CC.FLAGS_EXPAND_BOTH_WAYS )
test_panel.SetSizer( vbox )
#
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( info_st, CC.FLAGS_EXPAND_BOTH_WAYS )
info_panel.SetSizer( vbox )
#
notebook.AddPage( self._edit_panel, 'edit', select = True )
notebook.AddPage( test_panel, 'test', select = False )
notebook.AddPage( info_panel, 'info', select = False )
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( notebook, CC.FLAGS_EXPAND_SIZER_BOTH_WAYS )
self.SetSizer( vbox )
self.EventContentTypeChange( None )
def EventContentTypeChange( self, event ):
choice = self._content_type.GetChoice()
if choice == HC.CONTENT_TYPE_MAPPINGS:
self._veto_panel.Hide()
self._mappings_panel.Show()
elif choice == HC.CONTENT_TYPE_VETO:
self._mappings_panel.Hide()
self._veto_panel.Show()
self._content_panel.Layout()
self._edit_panel.Layout()
def EditFormula( self ):
dlg_title = 'edit html formula'
with ClientGUITopLevelWindows.DialogEdit( self, dlg_title ) as dlg:
example_data = self._example_data.GetValue()
panel = EditHTMLFormulaPanel( dlg, self._current_formula, example_data )
dlg.SetPanel( panel )
if dlg.ShowModal() == wx.ID_OK:
self._current_formula = panel.GetValue()
self._formula_description.SetValue( self._current_formula.ToPrettyMultilineString() )
def GetValue( self ):
name = self._name.GetValue()
content_type = self._content_type.GetChoice()
if content_type == HC.CONTENT_TYPE_MAPPINGS:
namespace = self._namespace.GetValue()
additional_info = namespace
else:
veto_if_matches_found = self._veto_if_matches_found.GetValue()
match_if_text_present = self._match_if_text_present.GetValue()
search_text = self._search_text.GetValue()
additional_info = ( veto_if_matches_found, match_if_text_present, search_text )
formula = self._current_formula
node = ClientParsing.ParseNodeContent( name = name, content_type = content_type, formula = formula, additional_info = additional_info )
return node
def TestParse( self ):
node = self.GetValue()
try:
stop_time = HydrusData.GetNow() + 30
job_key = ClientThreading.JobKey( cancellable = True, stop_time = stop_time )
data = self._example_data.GetValue()
referral_url = self._referral_url
desired_content = 'all'
results = node.Parse( job_key, data, referral_url, desired_content )
result_lines = [ '*** RESULTS BEGIN ***' ]
result_lines.extend( ( ClientParsing.ConvertContentResultToPrettyString( result ) for result in results ) )
result_lines.append( '*** RESULTS END ***' )
results_text = os.linesep.join( result_lines )
self._results.SetValue( results_text )
except Exception as e:
HydrusData.ShowException( e )
message = 'Could not parse!'
wx.MessageBox( message )
class EditParseNodeContentLinkPanel( ClientGUIScrolledPanels.EditPanel ):
def __init__( self, parent, node, referral_url = None, example_data = None ):
ClientGUIScrolledPanels.EditPanel.__init__( self, parent )
if referral_url is None:
referral_url = 'test-url.com/test_query'
self._referral_url = referral_url
if example_data is None:
example_data = ''
self._my_example_url = None
notebook = wx.Notebook( self )
( name, self._current_formula, children ) = node.ToTuple()
#
edit_panel = wx.Panel( notebook )
edit_panel.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_FRAMEBK ) )
self._name = wx.TextCtrl( edit_panel )
formula_panel = ClientGUICommon.StaticBox( edit_panel, 'formula' )
self._formula_description = ClientGUICommon.SaneMultilineTextCtrl( formula_panel )
self._formula_description.SetMinSize( ( -1, 200 ) )
self._formula_description.Disable()
self._edit_formula = ClientGUICommon.BetterButton( formula_panel, 'edit formula', self.EditFormula )
children_panel = ClientGUICommon.StaticBox( edit_panel, 'content parsing children' )
self._children = EditNodes( children_panel, children, self.GetExampleURL, self.GetExampleData )
#
test_panel = wx.Panel( notebook )
test_panel.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_FRAMEBK ) )
self._example_data = ClientGUICommon.SaneMultilineTextCtrl( test_panel )
self._example_data.SetMinSize( ( -1, 200 ) )
self._example_data.SetValue( example_data )
self._test_parse = wx.Button( test_panel, label = 'test parse' )
self._test_parse.Bind( wx.EVT_BUTTON, self.EventTestParse )
self._results = ClientGUICommon.SaneMultilineTextCtrl( test_panel )
self._results.SetMinSize( ( -1, 200 ) )
self._test_fetch_result = wx.Button( test_panel, label = 'try fetching the first result' )
self._test_fetch_result.Bind( wx.EVT_BUTTON, self.EventTestFetchResult )
self._test_fetch_result.Disable()
self._my_example_data = ClientGUICommon.SaneMultilineTextCtrl( test_panel )
#
info_panel = wx.Panel( notebook )
message = '''This node looks for one or more urls in the data it is given, requests each in turn, and gives the results to its children for further parsing.
If your previous query result responds with links to where the actual content is, use this node to bridge the gap.
The formula should attempt to parse full or relative urls. If the url is relative (like href="/page/123"), it will be appended to the referral url given by this node's parent. It will then attempt to GET them all.'''
info_st = wx.StaticText( info_panel, label = message )
info_st.Wrap( 400 )
#
self._name.SetValue( name )
self._formula_description.SetValue( self._current_formula.ToPrettyMultilineString() )
#
formula_panel.AddF( self._formula_description, CC.FLAGS_EXPAND_BOTH_WAYS )
formula_panel.AddF( self._edit_formula, CC.FLAGS_EXPAND_PERPENDICULAR )
children_panel.AddF( self._children, CC.FLAGS_EXPAND_BOTH_WAYS )
#
vbox = wx.BoxSizer( wx.VERTICAL )
rows = []
rows.append( ( 'name or description (optional): ', self._name ) )
gridbox = ClientGUICommon.WrapInGrid( edit_panel, rows )
vbox.AddF( gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
vbox.AddF( formula_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
vbox.AddF( children_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
edit_panel.SetSizer( vbox )
#
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( self._example_data, CC.FLAGS_EXPAND_BOTH_WAYS )
vbox.AddF( self._test_parse, CC.FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( self._results, CC.FLAGS_EXPAND_BOTH_WAYS )
vbox.AddF( self._test_fetch_result, CC.FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( self._my_example_data, CC.FLAGS_EXPAND_BOTH_WAYS )
test_panel.SetSizer( vbox )
#
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( info_st, CC.FLAGS_EXPAND_BOTH_WAYS )
info_panel.SetSizer( vbox )
#
notebook.AddPage( edit_panel, 'edit', select = True )
notebook.AddPage( test_panel, 'test', select = False )
notebook.AddPage( info_panel, 'info', select = False )
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( notebook, CC.FLAGS_EXPAND_SIZER_BOTH_WAYS )
self.SetSizer( vbox )
def EditFormula( self ):
dlg_title = 'edit html formula'
with ClientGUITopLevelWindows.DialogEdit( self, dlg_title ) as dlg:
example_data = self._example_data.GetValue()
panel = EditHTMLFormulaPanel( dlg, self._current_formula, example_data )
dlg.SetPanel( panel )
if dlg.ShowModal() == wx.ID_OK:
self._current_formula = panel.GetValue()
self._formula_description.SetValue( self._current_formula.ToPrettyMultilineString() )
def EventTestFetchResult( self, event ):
try:
headers = { 'Referer' : self._referral_url }
response = ClientNetworking.RequestsGet( self._my_example_url, headers = headers )
self._my_example_data.SetValue( response.content )
except Exception as e:
self._my_example_data.SetValue( 'fetch failed' )
raise
def EventTestParse( self, event ):
node = self.GetValue()
try:
stop_time = HydrusData.GetNow() + 30
job_key = ClientThreading.JobKey( cancellable = True, stop_time = stop_time )
data = self._example_data.GetValue()
referral_url = self._referral_url
desired_content = 'all'
parsed_urls = node.ParseURLs( job_key, data, referral_url )
if len( parsed_urls ) > 0:
self._my_example_url = parsed_urls[0]
self._test_fetch_result.Enable()
result_lines = [ '*** RESULTS BEGIN ***' ]
result_lines.extend( parsed_urls )
result_lines.append( '*** RESULTS END ***' )
results_text = os.linesep.join( result_lines )
self._results.SetValue( results_text )
except Exception as e:
HydrusData.ShowException( e )
message = 'Could not parse!'
wx.MessageBox( message )
def GetExampleData( self ):
return self._my_example_data.GetValue()
def GetExampleURL( self ):
if self._my_example_url is not None:
return self._my_example_url
else:
return ''
def GetValue( self ):
name = self._name.GetValue()
formula = self._current_formula
children = self._children.GetValue()
node = ClientParsing.ParseNodeContentLink( name = name, formula = formula, children = children )
return node
class EditParsingScriptFileLookupPanel( ClientGUIScrolledPanels.EditPanel ):
def __init__( self, parent, script ):
ClientGUIScrolledPanels.EditPanel.__init__( self, parent )
( name, url, query_type, file_identifier_type, file_identifier_encoding, file_identifier_arg_name, static_args, children ) = script.ToTuple()
#
notebook = wx.Notebook( self )
#
edit_panel = wx.Panel( notebook )
edit_panel.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_FRAMEBK ) )
self._name = wx.TextCtrl( edit_panel )
query_panel = ClientGUICommon.StaticBox( edit_panel, 'query' )
self._url = wx.TextCtrl( query_panel )
self._url.SetValue( url )
self._query_type = ClientGUICommon.BetterChoice( query_panel )
self._query_type.Append( 'GET', HC.GET )
self._query_type.Append( 'POST', HC.POST )
self._file_identifier_type = ClientGUICommon.BetterChoice( query_panel )
for t in [ ClientParsing.FILE_IDENTIFIER_TYPE_FILE, ClientParsing.FILE_IDENTIFIER_TYPE_MD5, ClientParsing.FILE_IDENTIFIER_TYPE_SHA1, ClientParsing.FILE_IDENTIFIER_TYPE_SHA256, ClientParsing.FILE_IDENTIFIER_TYPE_SHA512, ClientParsing.FILE_IDENTIFIER_TYPE_USER_INPUT ]:
self._file_identifier_type.Append( ClientParsing.file_identifier_string_lookup[ t ], t )
self._file_identifier_encoding = ClientGUICommon.BetterChoice( query_panel )
for e in [ HC.ENCODING_RAW, HC.ENCODING_HEX, HC.ENCODING_BASE64 ]:
self._file_identifier_encoding.Append( HC.encoding_string_lookup[ e ], e )
self._file_identifier_arg_name = wx.TextCtrl( query_panel )
static_args_panel = ClientGUICommon.StaticBox( query_panel, 'static arguments' )
self._static_args = ClientGUICommon.EditStringToStringDict( static_args_panel, static_args )
children_panel = ClientGUICommon.StaticBox( edit_panel, 'content parsing children' )
self._children = EditNodes( children_panel, children, self.GetExampleURL, self.GetExampleData )
#
test_panel = wx.Panel( notebook )
test_panel.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_FRAMEBK ) )
self._example_data = ''
self._test_arg = wx.TextCtrl( test_panel )
self._test_arg.SetValue( 'enter example file path, hex hash, or raw user input here' )
self._fetch_data = wx.Button( test_panel, label = 'fetch response' )
self._fetch_data.Bind( wx.EVT_BUTTON, self.EventFetchData )
self._example_data = ClientGUICommon.SaneMultilineTextCtrl( test_panel )
self._example_data.SetMinSize( ( -1, 200 ) )
self._test_parsing = wx.Button( test_panel, label = 'test parse (note if you have \'link\' nodes, they will make their requests)' )
self._test_parsing.Bind( wx.EVT_BUTTON, self.EventTestParse )
self._results = ClientGUICommon.SaneMultilineTextCtrl( test_panel )
self._results.SetMinSize( ( -1, 200 ) )
#
info_panel = wx.Panel( notebook )
message = '''This script looks up tags for a single file.
It will download the result of a query that might look something like this:
http://www.file-lookup.com/form.php?q=getsometags&md5=[md5-in-hex]
And pass that html to a number of 'parsing children' that will each look through it in turn and try to find tags.'''
info_st = wx.StaticText( info_panel )
info_st.SetLabelText( message )
info_st.Wrap( 400 )
#
self._name.SetValue( name )
self._query_type.SelectClientData( query_type )
self._file_identifier_type.SelectClientData( file_identifier_type )
self._file_identifier_encoding.SelectClientData( file_identifier_encoding )
self._file_identifier_arg_name.SetValue( file_identifier_arg_name )
self._results.SetValue( 'Successfully parsed results will be printed here.' )
#
rows = []
rows.append( ( 'url', self._url ) )
rows.append( ( 'query type: ', self._query_type ) )
rows.append( ( 'file identifier type: ', self._file_identifier_type ) )
rows.append( ( 'file identifier encoding: ', self._file_identifier_encoding ) )
rows.append( ( 'file identifier GET/POST argument name: ', self._file_identifier_arg_name ) )
gridbox = ClientGUICommon.WrapInGrid( query_panel, rows )
static_args_panel.AddF( self._static_args, CC.FLAGS_EXPAND_BOTH_WAYS )
query_message = 'This query will be executed first.'
query_panel.AddF( wx.StaticText( query_panel, label = query_message ), CC.FLAGS_EXPAND_PERPENDICULAR )
query_panel.AddF( gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
query_panel.AddF( static_args_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
children_message = 'The data returned by the query will be passed to each of these children for content parsing.'
children_panel.AddF( wx.StaticText( children_panel, label = children_message ), CC.FLAGS_EXPAND_PERPENDICULAR )
children_panel.AddF( self._children, CC.FLAGS_EXPAND_BOTH_WAYS )
vbox = wx.BoxSizer( wx.VERTICAL )
rows = []
rows.append( ( 'script name: ', self._name ) )
gridbox = ClientGUICommon.WrapInGrid( edit_panel, rows )
vbox.AddF( gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
vbox.AddF( query_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
vbox.AddF( children_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
edit_panel.SetSizer( vbox )
#
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( self._test_arg, CC.FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( self._fetch_data, CC.FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( self._example_data, CC.FLAGS_EXPAND_BOTH_WAYS )
vbox.AddF( self._test_parsing, CC.FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( self._results, CC.FLAGS_EXPAND_BOTH_WAYS )
test_panel.SetSizer( vbox )
#
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( info_st, CC.FLAGS_EXPAND_BOTH_WAYS )
info_panel.SetSizer( vbox )
#
notebook.AddPage( edit_panel, 'edit', select = True )
notebook.AddPage( test_panel, 'test', select = False )
notebook.AddPage( info_panel, 'info', select = False )
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( notebook, CC.FLAGS_EXPAND_SIZER_BOTH_WAYS )
self.SetSizer( vbox )
def EventFetchData( self, event ):
script = self.GetValue()
test_arg = self._test_arg.GetValue()
file_identifier_type = self._file_identifier_type.GetChoice()
if file_identifier_type == ClientParsing.FILE_IDENTIFIER_TYPE_FILE:
if not os.path.exists( test_arg ):
wx.MessageBox( 'That file does not exist!' )
return
file_identifier = test_arg
elif file_identifier_type == ClientParsing.FILE_IDENTIFIER_TYPE_USER_INPUT:
file_identifier = test_arg
else:
file_identifier = test_arg.decode( 'hex' )
try:
stop_time = HydrusData.GetNow() + 30
job_key = ClientThreading.JobKey( cancellable = True, stop_time = stop_time )
example_data = script.FetchData( job_key, file_identifier )
self._example_data.SetValue( example_data )
except Exception as e:
HydrusData.ShowException( e )
message = 'Could not fetch data!'
message += os.linesep * 2
message += HydrusData.ToUnicode( e )
wx.MessageBox( message )
def EventTestParse( self, event ):
script = self.GetValue()
try:
stop_time = HydrusData.GetNow() + 30
job_key = ClientThreading.JobKey( cancellable = True, stop_time = stop_time )
data = self._example_data.GetValue()
desired_content = 'all'
results = script.Parse( job_key, data, desired_content )
result_lines = [ '*** RESULTS BEGIN ***' ]
result_lines.extend( ( ClientParsing.ConvertContentResultToPrettyString( result ) for result in results ) )
result_lines.append( '*** RESULTS END ***' )
results_text = os.linesep.join( result_lines )
self._results.SetValue( results_text )
except Exception as e:
HydrusData.ShowException( e )
message = 'Could not parse!'
wx.MessageBox( message )
def GetExampleData( self ):
return self._example_data.GetValue()
def GetExampleURL( self ):
return self._url.GetValue()
def GetValue( self ):
name = self._name.GetValue()
url = self._url.GetValue()
query_type = self._query_type.GetChoice()
file_identifier_type = self._file_identifier_type.GetChoice()
file_identifier_encoding = self._file_identifier_encoding.GetChoice()
file_identifier_arg_name = self._file_identifier_arg_name.GetValue()
static_args = self._static_args.GetValue()
children = self._children.GetValue()
script = ClientParsing.ParseRootFileLookup( name, url = url, query_type = query_type, file_identifier_type = file_identifier_type, file_identifier_encoding = file_identifier_encoding, file_identifier_arg_name = file_identifier_arg_name, static_args = static_args, children = children )
return script
class ManageParsingScriptsPanel( ClientGUIScrolledPanels.ManagePanel ):
def __init__( self, parent ):
ClientGUIScrolledPanels.ManagePanel.__init__( self, parent )
self._scripts = ClientGUICommon.SaneListCtrl( self, 200, [ ( 'name', 140 ), ( 'query type', 80 ), ( 'script type', 80 ), ( 'produces', -1 ) ], delete_key_callback = self.Delete, activation_callback = self.Edit, use_display_tuple_for_sort = True )
menu_items = []
menu_items.append( ( 'file lookup script', 'A script that fetches content for a known file.', self.AddFileLookupScript ) )
self._add_button = ClientGUICommon.MenuButton( self, 'add', menu_items )
menu_items = []
menu_items.append( ( 'to clipboard', 'Serialise the script and put it on your clipboard.', self.ExportToClipboard ) )
menu_items.append( ( 'to png', 'Serialise the script and encode it to an image file you can easily share with other hydrus users.', self.ExportToPng ) )
self._export_button = ClientGUICommon.MenuButton( self, 'export', menu_items )
menu_items = []
menu_items.append( ( 'from clipboard', 'Load a script from text in your clipboard.', self.ImportFromClipboard ) )
menu_items.append( ( 'from png', 'Load a script from an encoded png.', self.ImportFromPng ) )
self._paste_button = ClientGUICommon.MenuButton( self, 'import', menu_items )
self._duplicate_button = ClientGUICommon.BetterButton( self, 'duplicate', self.Duplicate )
self._edit_button = ClientGUICommon.BetterButton( self, 'edit', self.Edit )
self._delete_button = ClientGUICommon.BetterButton( self, 'delete', self.Delete )
#
scripts = HydrusGlobals.client_controller.Read( 'serialisable_named', HydrusSerialisable.SERIALISABLE_TYPE_PARSE_ROOT_FILE_LOOKUP )
for script in scripts:
( display_tuple, data_tuple ) = self._ConvertScriptToTuples( script )
self._scripts.Append( display_tuple, data_tuple )
#
vbox = wx.BoxSizer( wx.VERTICAL )
button_hbox = wx.BoxSizer( wx.HORIZONTAL )
button_hbox.AddF( self._add_button, CC.FLAGS_VCENTER )
button_hbox.AddF( self._export_button, CC.FLAGS_VCENTER )
button_hbox.AddF( self._paste_button, CC.FLAGS_VCENTER )
button_hbox.AddF( self._duplicate_button, CC.FLAGS_VCENTER )
button_hbox.AddF( self._edit_button, CC.FLAGS_VCENTER )
button_hbox.AddF( self._delete_button, CC.FLAGS_VCENTER )
vbox.AddF( self._scripts, CC.FLAGS_EXPAND_BOTH_WAYS )
vbox.AddF( button_hbox, CC.FLAGS_BUTTON_SIZER )
self.SetSizer( vbox )
def _ConvertScriptToTuples( self, script ):
( name, query_type, script_type, produces ) = script.ToPrettyStrings()
return ( ( name, query_type, script_type, produces ), ( script, query_type, script_type, produces ) )
def _SetNonDupeName( self, script ):
name = script.GetName()
current_names = { script.GetName() for ( script, query_type, script_type, produces ) in self._scripts.GetClientData() }
if name in current_names:
i = 1
original_name = name
while name in current_names:
name = original_name + ' (' + str( i ) + ')'
i += 1
script.SetName( name )
def AddFileLookupScript( self ):
name = 'new script'
url = ''
query_type = HC.GET
file_identifier_type = ClientParsing.FILE_IDENTIFIER_TYPE_MD5
file_identifier_encoding = HC.ENCODING_BASE64
file_identifier_arg_name = 'md5'
static_args = {}
children = []
dlg_title = 'edit file metadata lookup script'
empty_script = ClientParsing.ParseRootFileLookup( name, url = url, query_type = query_type, file_identifier_type = file_identifier_type, file_identifier_encoding = file_identifier_encoding, file_identifier_arg_name = file_identifier_arg_name, static_args = static_args, children = children)
panel_class = EditParsingScriptFileLookupPanel
self.AddScript( dlg_title, empty_script, panel_class )
def AddScript( self, dlg_title, empty_script, panel_class ):
with ClientGUITopLevelWindows.DialogEdit( self, dlg_title ) as dlg_edit:
panel = panel_class( dlg_edit, empty_script )
dlg_edit.SetPanel( panel )
if dlg_edit.ShowModal() == wx.ID_OK:
new_script = panel.GetValue()
self._SetNonDupeName( new_script )
( display_tuple, data_tuple ) = self._ConvertScriptToTuples( new_script )
self._scripts.Append( display_tuple, data_tuple )
def CommitChanges( self ):
scripts = [ script for ( script, query_type, script_type, produces ) in self._scripts.GetClientData() ]
file_lookup_scripts = [ script for script in scripts if isinstance( script, ClientParsing.ParseRootFileLookup ) ]
stuff_to_save = []
stuff_to_save.append( ( HydrusSerialisable.SERIALISABLE_TYPE_PARSE_ROOT_FILE_LOOKUP, file_lookup_scripts ) )
for ( serialisable_type, scripts ) in stuff_to_save:
existing_names = set( HydrusGlobals.client_controller.Read( 'serialisable_names', serialisable_type ) )
save_names = { script.GetName() for script in scripts }
for script in scripts:
HydrusGlobals.client_controller.Write( 'serialisable', script )
deletee_names = existing_names.difference( save_names )
for name in deletee_names:
HydrusGlobals.client_controller.Write( 'delete_serialisable_named', serialisable_type, name )
def Delete( self ):
self._scripts.RemoveAllSelected()
def Duplicate( self ):
scripts_to_dupe = []
for i in self._scripts.GetAllSelected():
( script, query_type, script_type, produces ) = self._scripts.GetClientData( i )
scripts_to_dupe.append( script )
for script in scripts_to_dupe:
dupe_script = script.Duplicate()
self._SetNonDupeName( dupe_script )
( display_tuple, data_tuple ) = self._ConvertScriptToTuples( dupe_script )
self._scripts.Append( display_tuple, data_tuple )
def Edit( self ):
for i in self._scripts.GetAllSelected():
( script, query_type, script_type, produces ) = self._scripts.GetClientData( i )
if isinstance( script, ClientParsing.ParseRootFileLookup ):
panel_class = EditParsingScriptFileLookupPanel
dlg_title = 'edit file lookup script'
with ClientGUITopLevelWindows.DialogEdit( self, dlg_title ) as dlg:
original_name = script.GetName()
panel = panel_class( dlg, script )
dlg.SetPanel( panel )
if dlg.ShowModal() == wx.ID_OK:
edited_script = panel.GetValue()
name = edited_script.GetName()
if name != original_name:
self._SetNonDupeName( edited_script )
( display_tuple, data_tuple ) = self._ConvertScriptToTuples( edited_script )
self._scripts.UpdateRow( i, display_tuple, data_tuple )
def ExportToClipboard( self ):
for i in self._scripts.GetAllSelected():
( script, query_type, script_type, produces ) = self._scripts.GetClientData( i )
script_json = script.DumpToString()
HydrusGlobals.client_controller.pub( 'clipboard', 'text', script_json )
def ExportToPng( self ):
for i in self._scripts.GetAllSelected():
( script, query_type, script_type, produces ) = self._scripts.GetClientData( i )
with ClientGUITopLevelWindows.DialogNullipotent( self, 'export script to png' ) as dlg:
panel = ClientGUISerialisable.PngExportPanel( dlg, script )
dlg.SetPanel( panel )
dlg.ShowModal()
def ImportFromClipboard( self ):
if wx.TheClipboard.Open():
data = wx.TextDataObject()
wx.TheClipboard.GetData( data )
wx.TheClipboard.Close()
raw_text = data.GetText()
try:
obj = HydrusSerialisable.CreateFromString( raw_text )
if isinstance( obj, ClientParsing.ParseRootFileLookup ):
script = obj
self._SetNonDupeName( script )
( display_tuple, data_tuple ) = self._ConvertScriptToTuples( script )
self._scripts.Append( display_tuple, data_tuple )
else:
wx.MessageBox( 'That was not a script--it was a: ' + type( obj ).__name__ )
except Exception as e:
wx.MessageBox( 'I could not understand what was in the clipboard' )
else:
wx.MessageBox( 'I could not get permission to access the clipboard.' )
def ImportFromPng( self ):
with wx.FileDialog( self, 'select the png with the encoded script', wildcard = 'PNG (*.png)|*.png' ) as dlg:
if dlg.ShowModal() == wx.ID_OK:
path = dlg.GetPath()
try:
payload = ClientSerialisable.LoadFromPng( path )
except Exception as e:
wx.MessageBox( str( e ) )
return
try:
obj = HydrusSerialisable.CreateFromNetworkString( payload )
if isinstance( obj, ClientParsing.ParseRootFileLookup ):
script = obj
self._SetNonDupeName( script )
( display_tuple, data_tuple ) = self._ConvertScriptToTuples( script )
self._scripts.Append( display_tuple, data_tuple )
else:
wx.MessageBox( 'That was not a script--it was a: ' + type( obj ).__name__ )
except:
wx.MessageBox( 'I could not understand what was encoded in the png!' )
class ScriptManagementControl( wx.Panel ):
def __init__( self, parent ):
wx.Panel.__init__( self, parent )
self._job_key = None
self._lock = threading.Lock()
self._recent_urls = []
main_panel = ClientGUICommon.StaticBox( self, 'script control' )
self._status = wx.StaticText( main_panel )
self._gauge = ClientGUICommon.Gauge( main_panel )
self._link_button = wx.BitmapButton( main_panel, bitmap = CC.GlobalBMPs.link )
self._link_button.Bind( wx.EVT_BUTTON, self.EventLinkButton )
self._link_button.SetToolTipString( 'urls found by the script' )
self._cancel_button = wx.BitmapButton( main_panel, bitmap = CC.GlobalBMPs.stop )
self._cancel_button.Bind( wx.EVT_BUTTON, self.EventCancelButton )
self.Bind( wx.EVT_TIMER, self.TIMEREventUpdate, id = ID_TIMER_SCRIPT_UPDATE )
self._update_timer = wx.Timer( self, id = ID_TIMER_SCRIPT_UPDATE )
#
hbox = wx.BoxSizer( wx.HORIZONTAL )
hbox.AddF( self._gauge, CC.FLAGS_EXPAND_BOTH_WAYS )
hbox.AddF( self._link_button, CC.FLAGS_VCENTER )
hbox.AddF( self._cancel_button, CC.FLAGS_VCENTER )
main_panel.AddF( self._status, CC.FLAGS_EXPAND_PERPENDICULAR )
main_panel.AddF( hbox, CC.FLAGS_EXPAND_PERPENDICULAR )
#
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( main_panel, CC.FLAGS_EXPAND_SIZER_BOTH_WAYS )
self.SetSizer( vbox )
#
self._Reset()
def _Reset( self ):
self._status.SetLabelText( '' )
self._gauge.SetRange( 1 )
self._gauge.SetValue( 0 )
self._link_button.Disable()
self._cancel_button.Disable()
def _Update( self ):
if self._job_key is None:
self._Reset()
else:
if self._job_key.HasVariable( 'script_status' ):
status = self._job_key.GetIfHasVariable( 'script_status' )
else:
status = ''
if status != self._status.GetLabelText():
self._status.SetLabelText( status )
if self._job_key.HasVariable( 'script_gauge' ):
( value, range ) = self._job_key.GetIfHasVariable( 'script_gauge' )
else:
( value, range ) = ( 0, 1 )
if value is None or range is None:
self._gauge.Pulse()
else:
self._gauge.SetRange( range )
self._gauge.SetValue( value )
urls = self._job_key.GetURLs()
if len( urls ) == 0:
if self._link_button.IsEnabled():
self._link_button.Disable()
else:
if not self._link_button.IsEnabled():
self._link_button.Enable()
if self._job_key.IsDone():
if self._cancel_button.IsEnabled():
self._cancel_button.Disable()
else:
if not self._cancel_button.IsEnabled():
self._cancel_button.Enable()
def TIMEREventUpdate( self, event ):
with self._lock:
self._Update()
if self._job_key is not None:
self._update_timer.Start( 100, wx.TIMER_ONE_SHOT )
def EventCancelButton( self, event ):
with self._lock:
if self._job_key is not None:
self._job_key.Cancel()
def EventLinkButton( self, event ):
with self._lock:
if self._job_key is None:
return
urls = self._job_key.GetURLs()
menu = wx.Menu()
for url in urls:
ClientGUIMenus.AppendMenuItem( menu, url, 'launch this url in your browser', self, webbrowser.open, url )
HydrusGlobals.client_controller.PopupMenu( self, menu )
def SetJobKey( self, job_key ):
with self._lock:
self._job_key = job_key
self._update_timer.Start( 100, wx.TIMER_ONE_SHOT )