Version 232
This commit is contained in:
parent
ab1bd6d208
commit
0341eff594
|
@ -8,6 +8,38 @@
|
|||
<div class="content">
|
||||
<h3>changelog</h3>
|
||||
<ul>
|
||||
<li><h3>version 232</h3></li>
|
||||
<ul>
|
||||
<li>finished png object sharing system, created some gui for it</li>
|
||||
<li>switched the top half of the new png export to wx code, which can render with prettier system fonts</li>
|
||||
<li>you can now 'export' parsing scripts to png from manage parsing scripts for easy sharing</li>
|
||||
<li>you can also 'import' parsing script pngs in the same dialog!</li>
|
||||
<li>the file lookup tag suggestions panel now listens for media update events (so if you browse the media viewer with manage tags open, it'll keep up with the current file)</li>
|
||||
<li>added a 'add all' button to quickly add all tags from file lookup suggested tags panel</li>
|
||||
<li>created a 'script management' control for file lookup suggested tags panel and added some script text status updates for it (progress gauge doesn't do anything yet)</li>
|
||||
<li>added basic 'cancel' support to script management control--mid-http cancel support will come with the gauges</li>
|
||||
<li>added url support to thread notification objects, and hooked script code into it</li>
|
||||
<li>added a url menu button to script management control, so you can launch any parsed urls the script found in your browser</li>
|
||||
<li>html parsing formulas now support positive and negative front and end character culling</li>
|
||||
<li>html parsing formulas now support prepending and appending arbitrary text</li>
|
||||
<li>added some more pretty text to the html formula summary to explain when they do the new culling or adding</li>
|
||||
<li>script 404s and other network errors are now caught neatly and reported</li>
|
||||
<li>link node 404s and other network errors are now reported and recovered from</li>
|
||||
<li>fixed favourite file lookup script selection on initialisation</li>
|
||||
<li>fixed parsing veto test to include membership of search string in result, not just exact match</li>
|
||||
<li>fixed html formula tag attribute fetching</li>
|
||||
<li>updated os x release to python 2.7.10</li>
|
||||
<li>fixed os x about window</li>
|
||||
<li>fixed v231 linux release image rendering crash</li>
|
||||
<li>updated linux release to wx 3.0.2.0</li>
|
||||
<li>updated linux release to opencv 3.1.0</li>
|
||||
<li>fixed rendering for 16-bit-per-channel images</li>
|
||||
<li>fixed a potential out-of-order settagboxfocus event issue on panel initialisation</li>
|
||||
<li>created a 'menubutton' control and switched existing code over</li>
|
||||
<li>created some 'nullipotent panel' dialog code to handle some new stuff</li>
|
||||
<li>misc fixes</li>
|
||||
<li>bunch of misc refactoring</li>
|
||||
</ul>
|
||||
<li><h3>version 231</h3></li>
|
||||
<ul>
|
||||
<li>added file lookup scripts suggested tags control and appropriate options panel</li>
|
||||
|
|
|
@ -11,17 +11,13 @@
|
|||
<h3>what you will need</h3>
|
||||
<p>You will need to install python 2.7 and a number of python modules. Most of it you can get through pip. I think this will do for most systems:</p>
|
||||
<ul>
|
||||
<li>(sudo) pip install beautifulsoup4 hsaudiotag lxml lz4 mp3play nose numpy pafy Pillow psutil pycrypto PyPDF2 PySocks python-potr PyYAML requests Send2Trash twisted</li>
|
||||
<li>pip install beautifulsoup4 hsaudiotag lxml lz4 nose numpy pafy Pillow psutil pycrypto PyPDF2 PySocks python-potr PyYAML requests Send2Trash twisted</li>
|
||||
</ul>
|
||||
<p>Although you may want to do them one at a time, or in a different order. Your specific system may also need some of them from different sources and will need some complicated things installed separately. The best way to figure it out is just to keep running client.pyw and see what it complains about missing.</p>
|
||||
<p>I use Ubuntu 14.04, which also requires something like:</p>
|
||||
<p>I use Ubuntu 16.10, which also requires something like:</p>
|
||||
<p><ul>
|
||||
<li>sudo apt-get install python2.7-dev</li>
|
||||
<li>sudo apt-get install python-numpy</li>
|
||||
<li>sudo apt-get install python-opencv</li>
|
||||
<li>sudo apt-get install python-wxversion</li>
|
||||
<li>and installing a recent version of wxPython, which is difficult and best done <a href="http://wiki.wxpython.org/CheckInstall">like so</a></li>
|
||||
<li>If you install wxPython as above and you get missing .so errors, try running 'sudo ldconfig'.</li>
|
||||
<li>sudo apt-get install python-wxgtk3.0</li>
|
||||
</ul></p>
|
||||
<p>YMMV. Feel free to email me if you run into trouble or discover any neat tricks.</p>
|
||||
<p>OS X 10.9 is similar, though wx is simpler. I use the cocoa package <a href="http://wxpython.org/download.php#osx">straight from wxPython's site</a>. This should do the rest:</p>
|
||||
|
|
|
@ -13,7 +13,8 @@ import ClientGUIManagement
|
|||
import ClientGUIMenus
|
||||
import ClientGUIPages
|
||||
import ClientGUIParsing
|
||||
import ClientGUIScrolledPanels
|
||||
import ClientGUIScrolledPanelsManagement
|
||||
import ClientGUIScrolledPanelsReview
|
||||
import ClientGUITopLevelWindows
|
||||
import ClientDownloading
|
||||
import ClientMedia
|
||||
|
@ -372,6 +373,8 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
|
|||
|
||||
|
||||
|
||||
time.sleep( 5 )
|
||||
|
||||
HydrusData.ShowText( u'Creating admin service\u2026' )
|
||||
|
||||
admin_service_key = HydrusData.GenerateKey()
|
||||
|
@ -397,8 +400,6 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
|
|||
|
||||
self._controller.WriteSynchronous( 'update_services', edit_log )
|
||||
|
||||
time.sleep( 2 )
|
||||
|
||||
HydrusData.ShowText( 'Admin service initialised.' )
|
||||
|
||||
wx.CallAfter( ClientGUIFrames.ShowKeys, 'access', ( access_key, ) )
|
||||
|
@ -407,6 +408,8 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
|
|||
|
||||
#
|
||||
|
||||
time.sleep( 5 )
|
||||
|
||||
HydrusData.ShowText( u'Creating tag and file services\u2026' )
|
||||
|
||||
tag_options = HC.DEFAULT_OPTIONS[ HC.TAG_REPOSITORY ]
|
||||
|
@ -1372,7 +1375,7 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
|
|||
|
||||
with ClientGUITopLevelWindows.DialogManage( self, title, frame_key ) as dlg:
|
||||
|
||||
panel = ClientGUIScrolledPanels.ManageOptionsPanel( dlg )
|
||||
panel = ClientGUIScrolledPanelsManagement.ManageOptionsPanel( dlg )
|
||||
|
||||
dlg.SetPanel( panel )
|
||||
|
||||
|
@ -1782,7 +1785,7 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
|
|||
|
||||
frame = ClientGUITopLevelWindows.FrameThatTakesScrollablePanel( self, self._controller.PrepStringForDisplay( 'Review Services' ), 'review_services' )
|
||||
|
||||
panel = ClientGUIScrolledPanels.ReviewServices( frame, self._controller )
|
||||
panel = ClientGUIScrolledPanelsReview.ReviewServicesPanel( frame, self._controller )
|
||||
|
||||
frame.SetPanel( panel )
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ import ClientGUICommon
|
|||
import ClientGUIDialogs
|
||||
import ClientGUIDialogsManage
|
||||
import ClientGUIHoverFrames
|
||||
import ClientGUIScrolledPanels
|
||||
import ClientGUIScrolledPanelsManagement
|
||||
import ClientGUITopLevelWindows
|
||||
import ClientMedia
|
||||
import ClientRatings
|
||||
|
@ -26,7 +26,6 @@ import time
|
|||
import traceback
|
||||
import urllib
|
||||
import wx
|
||||
import wx.media
|
||||
import ClientRendering
|
||||
import HydrusData
|
||||
import HydrusGlobals
|
||||
|
@ -1189,7 +1188,7 @@ class Canvas( wx.Window ):
|
|||
|
||||
manage_tags = ClientGUITopLevelWindows.FrameThatTakesScrollablePanel( self, title, frame_key )
|
||||
|
||||
panel = ClientGUIScrolledPanels.ManageTagsPanel( manage_tags, self._file_service_key, ( self._current_display_media, ), immediate_commit = True, canvas_key = self._canvas_key )
|
||||
panel = ClientGUIScrolledPanelsManagement.ManageTagsPanel( manage_tags, self._file_service_key, ( self._current_display_media, ), immediate_commit = True, canvas_key = self._canvas_key )
|
||||
|
||||
manage_tags.SetPanel( panel )
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ import HydrusConstants as HC
|
|||
import ClientCaches
|
||||
import ClientData
|
||||
import ClientConstants as CC
|
||||
import ClientGUIMenus
|
||||
import ClientRatings
|
||||
import itertools
|
||||
import os
|
||||
|
@ -3179,6 +3180,27 @@ class ListCtrlAutoWidth( wx.ListCtrl, ListCtrlAutoWidthMixin ):
|
|||
for index in indices: self.DeleteItem( index )
|
||||
|
||||
|
||||
class MenuButton( BetterButton ):
|
||||
|
||||
def __init__( self, parent, label, menu_items ):
|
||||
|
||||
BetterButton.__init__( self, parent, label, self.DoMenu )
|
||||
|
||||
self._menu_items = menu_items
|
||||
|
||||
|
||||
def DoMenu( self ):
|
||||
|
||||
menu = wx.Menu()
|
||||
|
||||
for ( title, description, callable ) in self._menu_items:
|
||||
|
||||
ClientGUIMenus.AppendMenuItem( menu, title, description, self, callable )
|
||||
|
||||
|
||||
HydrusGlobals.client_controller.PopupMenu( self, menu )
|
||||
|
||||
|
||||
class NoneableSpinCtrl( wx.Panel ):
|
||||
|
||||
def __init__( self, parent, message, none_phrase = 'no limit', min = 0, max = 1000000, unit = None, multiplier = 1, num_dimensions = 1 ):
|
||||
|
|
|
@ -11,7 +11,7 @@ import ClientGUIDialogs
|
|||
import ClientDownloading
|
||||
import ClientGUIOptionsPanels
|
||||
import ClientGUIPredicates
|
||||
import ClientGUIScrolledPanels
|
||||
import ClientGUIScrolledPanelsEdit
|
||||
import ClientGUITopLevelWindows
|
||||
import ClientImporting
|
||||
import ClientMedia
|
||||
|
@ -3063,7 +3063,7 @@ class DialogManageImportFoldersEdit( ClientGUIDialogs.Dialog ):
|
|||
|
||||
with ClientGUITopLevelWindows.DialogEdit( self, 'file import status' ) as dlg:
|
||||
|
||||
panel = ClientGUIScrolledPanels.EditSeedCachePanel( dlg, HydrusGlobals.client_controller, dupe_seed_cache )
|
||||
panel = ClientGUIScrolledPanelsEdit.EditSeedCachePanel( dlg, HydrusGlobals.client_controller, dupe_seed_cache )
|
||||
|
||||
dlg.SetPanel( panel )
|
||||
|
||||
|
@ -5561,7 +5561,7 @@ class DialogManageSubscriptions( ClientGUIDialogs.Dialog ):
|
|||
|
||||
with ClientGUITopLevelWindows.DialogEdit( self, 'file import status' ) as dlg:
|
||||
|
||||
panel = ClientGUIScrolledPanels.EditSeedCachePanel( dlg, HydrusGlobals.client_controller, dupe_seed_cache )
|
||||
panel = ClientGUIScrolledPanelsEdit.EditSeedCachePanel( dlg, HydrusGlobals.client_controller, dupe_seed_cache )
|
||||
|
||||
dlg.SetPanel( panel )
|
||||
|
||||
|
@ -5677,7 +5677,10 @@ class DialogManageTagCensorship( ClientGUIDialogs.Dialog ):
|
|||
|
||||
page = self._tag_services.GetCurrentPage()
|
||||
|
||||
page.SetTagBoxFocus()
|
||||
if page is not None:
|
||||
|
||||
page.SetTagBoxFocus()
|
||||
|
||||
|
||||
|
||||
def EventOK( self, event ):
|
||||
|
@ -5695,7 +5698,10 @@ class DialogManageTagCensorship( ClientGUIDialogs.Dialog ):
|
|||
|
||||
page = self._tag_services.GetCurrentPage()
|
||||
|
||||
wx.CallAfter( page.SetTagBoxFocus )
|
||||
if page is not None:
|
||||
|
||||
wx.CallAfter( page.SetTagBoxFocus )
|
||||
|
||||
|
||||
|
||||
class _Panel( wx.Panel ):
|
||||
|
@ -5846,7 +5852,10 @@ class DialogManageTagParents( ClientGUIDialogs.Dialog ):
|
|||
|
||||
page = self._tag_repositories.GetCurrentPage()
|
||||
|
||||
page.SetTagBoxFocus()
|
||||
if page is not None:
|
||||
|
||||
page.SetTagBoxFocus()
|
||||
|
||||
|
||||
|
||||
def EventMenu( self, event ):
|
||||
|
@ -5884,7 +5893,10 @@ class DialogManageTagParents( ClientGUIDialogs.Dialog ):
|
|||
|
||||
page = self._tag_repositories.GetCurrentPage()
|
||||
|
||||
wx.CallAfter( page.SetTagBoxFocus )
|
||||
if page is not None:
|
||||
|
||||
wx.CallAfter( page.SetTagBoxFocus )
|
||||
|
||||
|
||||
|
||||
class _Panel( wx.Panel ):
|
||||
|
@ -6432,7 +6444,10 @@ class DialogManageTagSiblings( ClientGUIDialogs.Dialog ):
|
|||
|
||||
page = self._tag_repositories.GetCurrentPage()
|
||||
|
||||
page.SetTagBoxFocus()
|
||||
if page is not None:
|
||||
|
||||
page.SetTagBoxFocus()
|
||||
|
||||
|
||||
|
||||
def EventMenu( self, event ):
|
||||
|
@ -6470,7 +6485,10 @@ class DialogManageTagSiblings( ClientGUIDialogs.Dialog ):
|
|||
|
||||
page = self._tag_repositories.GetCurrentPage()
|
||||
|
||||
wx.CallAfter( page.SetTagBoxFocus )
|
||||
if page is not None:
|
||||
|
||||
wx.CallAfter( page.SetTagBoxFocus )
|
||||
|
||||
|
||||
|
||||
class _Panel( wx.Panel ):
|
||||
|
|
|
@ -15,7 +15,7 @@ import ClientGUICollapsible
|
|||
import ClientGUICommon
|
||||
import ClientGUIDialogs
|
||||
import ClientGUIMedia
|
||||
import ClientGUIScrolledPanels
|
||||
import ClientGUIScrolledPanelsEdit
|
||||
import ClientGUITopLevelWindows
|
||||
import ClientImporting
|
||||
import ClientMedia
|
||||
|
@ -1844,7 +1844,7 @@ class ManagementPanelGalleryImport( ManagementPanel ):
|
|||
|
||||
frame = ClientGUITopLevelWindows.FrameThatTakesScrollablePanel( self, title, frame_key )
|
||||
|
||||
panel = ClientGUIScrolledPanels.EditSeedCachePanel( frame, self._controller, seed_cache )
|
||||
panel = ClientGUIScrolledPanelsEdit.EditSeedCachePanel( frame, self._controller, seed_cache )
|
||||
|
||||
frame.SetPanel( panel )
|
||||
|
||||
|
@ -1992,7 +1992,7 @@ class ManagementPanelHDDImport( ManagementPanel ):
|
|||
|
||||
frame = ClientGUITopLevelWindows.FrameThatTakesScrollablePanel( self, title, frame_key )
|
||||
|
||||
panel = ClientGUIScrolledPanels.EditSeedCachePanel( frame, self._controller, seed_cache )
|
||||
panel = ClientGUIScrolledPanelsEdit.EditSeedCachePanel( frame, self._controller, seed_cache )
|
||||
|
||||
frame.SetPanel( panel )
|
||||
|
||||
|
@ -2369,7 +2369,7 @@ class ManagementPanelPageOfImagesImport( ManagementPanel ):
|
|||
|
||||
frame = ClientGUITopLevelWindows.FrameThatTakesScrollablePanel( self, title, frame_key )
|
||||
|
||||
panel = ClientGUIScrolledPanels.EditSeedCachePanel( frame, self._controller, seed_cache )
|
||||
panel = ClientGUIScrolledPanelsEdit.EditSeedCachePanel( frame, self._controller, seed_cache )
|
||||
|
||||
frame.SetPanel( panel )
|
||||
|
||||
|
@ -3170,7 +3170,7 @@ class ManagementPanelThreadWatcherImport( ManagementPanel ):
|
|||
|
||||
frame = ClientGUITopLevelWindows.FrameThatTakesScrollablePanel( self, title, frame_key )
|
||||
|
||||
panel = ClientGUIScrolledPanels.EditSeedCachePanel( frame, self._controller, seed_cache )
|
||||
panel = ClientGUIScrolledPanelsEdit.EditSeedCachePanel( frame, self._controller, seed_cache )
|
||||
|
||||
frame.SetPanel( panel )
|
||||
|
||||
|
@ -3448,7 +3448,7 @@ class ManagementPanelURLsImport( ManagementPanel ):
|
|||
|
||||
frame = ClientGUITopLevelWindows.FrameThatTakesScrollablePanel( self, title, frame_key )
|
||||
|
||||
panel = ClientGUIScrolledPanels.EditSeedCachePanel( frame, self._controller, seed_cache )
|
||||
panel = ClientGUIScrolledPanelsEdit.EditSeedCachePanel( frame, self._controller, seed_cache )
|
||||
|
||||
frame.SetPanel( panel )
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ import ClientGUICommon
|
|||
import ClientGUIDialogs
|
||||
import ClientGUIDialogsManage
|
||||
import ClientGUICanvas
|
||||
import ClientGUIScrolledPanels
|
||||
import ClientGUIScrolledPanelsManagement
|
||||
import ClientGUITopLevelWindows
|
||||
import ClientMedia
|
||||
import collections
|
||||
|
@ -799,7 +799,7 @@ class MediaPanel( ClientMedia.ListeningMediaList, wx.ScrolledWindow ):
|
|||
|
||||
with ClientGUITopLevelWindows.DialogManage( self, title, frame_key ) as dlg:
|
||||
|
||||
panel = ClientGUIScrolledPanels.ManageTagsPanel( dlg, self._file_service_key, self._selected_media )
|
||||
panel = ClientGUIScrolledPanelsManagement.ManageTagsPanel( dlg, self._file_service_key, self._selected_media )
|
||||
|
||||
dlg.SetPanel( panel )
|
||||
|
||||
|
|
|
@ -7,12 +7,16 @@ menus_to_menu_item_data = collections.defaultdict( set )
|
|||
|
||||
def AppendMenu( menu, submenu, label ):
|
||||
|
||||
label.replace( '&', '&&' )
|
||||
|
||||
menu.AppendMenu( CC.ID_NULL, label, submenu )
|
||||
|
||||
menus_to_submenus[ menu ].add( submenu )
|
||||
|
||||
def AppendMenuItem( menu, label, description, event_handler, callable, *args, **kwargs ):
|
||||
|
||||
label.replace( '&', '&&' )
|
||||
|
||||
menu_item = menu.Append( wx.ID_ANY, label, description )
|
||||
|
||||
l_callable = GetLambdaCallable( callable, *args, **kwargs )
|
||||
|
|
|
@ -3,17 +3,24 @@ 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 ):
|
||||
|
@ -91,6 +98,12 @@ class EditHTMLFormulaPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
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 )
|
||||
|
@ -123,7 +136,13 @@ So, to find the 'src' of the first <img> tag beneath all <span> tags with the cl
|
|||
1st img tag'
|
||||
attribute: src'
|
||||
|
||||
Leave the 'attribute' blank to fetch the string of the tag (i.e. <p>This part</p>).'''
|
||||
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 )
|
||||
|
||||
|
@ -131,7 +150,7 @@ Leave the 'attribute' blank to fetch the string of the tag (i.e. <p>This part</p
|
|||
|
||||
#
|
||||
|
||||
( tag_rules, content_rule ) = formula.ToTuple()
|
||||
( tag_rules, content_rule, culling_and_adding ) = formula.ToTuple()
|
||||
|
||||
for rule in tag_rules:
|
||||
|
||||
|
@ -169,13 +188,21 @@ Leave the 'attribute' blank to fetch the string of the tag (i.e. <p>This part</p
|
|||
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( ClientGUICommon.WrapInText( self._content_rule, edit_panel, 'attribute to fetch: ' ), CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
vbox.AddF( gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
|
||||
edit_panel.SetSizer( vbox )
|
||||
|
||||
|
@ -296,7 +323,9 @@ Leave the 'attribute' blank to fetch the string of the tag (i.e. <p>This part</p
|
|||
content_rule = None
|
||||
|
||||
|
||||
formula = ClientParsing.ParseFormulaHTML( tags_rules, content_rule )
|
||||
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
|
||||
|
||||
|
@ -368,7 +397,12 @@ class EditNodes( wx.Panel ):
|
|||
|
||||
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 )
|
||||
|
||||
self._add_button = ClientGUICommon.BetterButton( self, 'add', self.Add )
|
||||
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 )
|
||||
|
||||
|
@ -415,16 +449,6 @@ class EditNodes( wx.Panel ):
|
|||
return ( ( name, node_type, produces ), ( node, node_type, produces ) )
|
||||
|
||||
|
||||
def Add( self ):
|
||||
|
||||
menu = wx.Menu()
|
||||
|
||||
ClientGUIMenus.AppendMenuItem( menu, 'content node', 'A node that parses the given data for content.', self, self.AddContentNode )
|
||||
ClientGUIMenus.AppendMenuItem( menu, 'link node', 'A node that parses the given data for a link, which it then pursues.', self, self.AddLinkNode )
|
||||
|
||||
HydrusGlobals.client_controller.PopupMenu( self, menu )
|
||||
|
||||
|
||||
def AddContentNode( self ):
|
||||
|
||||
dlg_title = 'edit content node'
|
||||
|
@ -868,11 +892,15 @@ The 'veto' type will tell the parent panel that this page, while it returned 200
|
|||
|
||||
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( data, referral_url, desired_content )
|
||||
results = node.Parse( job_key, data, referral_url, desired_content )
|
||||
|
||||
result_lines = [ '*** RESULTS BEGIN ***' ]
|
||||
|
||||
|
@ -934,8 +962,7 @@ class EditParseNodeContentLinkPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
self._formula_description.Disable()
|
||||
|
||||
self._edit_formula = wx.Button( formula_panel, label = 'edit formula' )
|
||||
self._edit_formula.Bind( wx.EVT_BUTTON, self.EventEditFormula )
|
||||
self._edit_formula = ClientGUICommon.BetterButton( formula_panel, 'edit formula', self.EditFormula )
|
||||
|
||||
children_panel = ClientGUICommon.StaticBox( edit_panel, 'content parsing children' )
|
||||
|
||||
|
@ -1043,7 +1070,7 @@ The formula should attempt to parse full or relative urls. If the url is relativ
|
|||
|
||||
|
||||
|
||||
def EventEditFormula( self, event ):
|
||||
def EditFormula( self ):
|
||||
|
||||
dlg_title = 'edit html formula'
|
||||
|
||||
|
@ -1088,11 +1115,15 @@ The formula should attempt to parse full or relative urls. If the url is relativ
|
|||
|
||||
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( data, referral_url )
|
||||
parsed_urls = node.ParseURLs( job_key, data, referral_url )
|
||||
|
||||
if len( parsed_urls ) > 0:
|
||||
|
||||
|
@ -1362,7 +1393,11 @@ And pass that html to a number of 'parsing children' that will each look through
|
|||
|
||||
try:
|
||||
|
||||
example_data = script.FetchData( file_identifier )
|
||||
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 )
|
||||
|
||||
|
@ -1384,10 +1419,14 @@ And pass that html to a number of 'parsing children' that will each look through
|
|||
|
||||
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( data, desired_content )
|
||||
results = script.Parse( job_key, data, desired_content )
|
||||
|
||||
result_lines = [ '*** RESULTS BEGIN ***' ]
|
||||
|
||||
|
@ -1443,23 +1482,31 @@ class ManageParsingScriptsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
|
||||
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 )
|
||||
|
||||
self._add_button = wx.Button( self, label = 'add' )
|
||||
self._add_button.Bind( wx.EVT_BUTTON, self.EventAdd )
|
||||
menu_items = []
|
||||
|
||||
self._copy_button = wx.Button( self, label = 'copy' )
|
||||
self._copy_button.Bind( wx.EVT_BUTTON, self.EventCopy )
|
||||
menu_items.append( ( 'file lookup script', 'A script that fetches content for a known file.', self.AddFileLookupScript ) )
|
||||
|
||||
self._paste_button = wx.Button( self, label = 'paste' )
|
||||
self._paste_button.Bind( wx.EVT_BUTTON, self.EventPaste )
|
||||
self._add_button = ClientGUICommon.MenuButton( self, 'add', menu_items )
|
||||
|
||||
self._duplicate_button = wx.Button( self, label = 'duplicate' )
|
||||
self._duplicate_button.Bind( wx.EVT_BUTTON, self.EventDuplicate )
|
||||
menu_items = []
|
||||
|
||||
self._edit_button = wx.Button( self, label = 'edit' )
|
||||
self._edit_button.Bind( wx.EVT_BUTTON, self.EventEdit )
|
||||
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._delete_button = wx.Button( self, label = 'delete' )
|
||||
self._delete_button.Bind( wx.EVT_BUTTON, self.EventDelete )
|
||||
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 )
|
||||
|
||||
#
|
||||
|
||||
|
@ -1479,7 +1526,7 @@ class ManageParsingScriptsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
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._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 )
|
||||
|
@ -1521,15 +1568,6 @@ class ManageParsingScriptsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
|
||||
|
||||
|
||||
def Add( self ):
|
||||
|
||||
menu = wx.Menu()
|
||||
|
||||
ClientGUIMenus.AppendMenuItem( menu, 'file lookup script', 'A script that fetches content for a known file.', self, self.AddFileLookupScript )
|
||||
|
||||
HydrusGlobals.client_controller.PopupMenu( self, menu )
|
||||
|
||||
|
||||
def AddFileLookupScript( self ):
|
||||
|
||||
name = 'new script'
|
||||
|
@ -1601,18 +1639,6 @@ class ManageParsingScriptsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
|
||||
|
||||
|
||||
def Copy( 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 Delete( self ):
|
||||
|
||||
self._scripts.RemoveAllSelected()
|
||||
|
@ -1682,7 +1708,36 @@ class ManageParsingScriptsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
|
||||
|
||||
|
||||
def Paste( self ):
|
||||
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():
|
||||
|
||||
|
@ -1708,6 +1763,10 @@ class ManageParsingScriptsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
|
||||
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:
|
||||
|
||||
|
@ -1720,33 +1779,244 @@ class ManageParsingScriptsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
|
||||
|
||||
|
||||
def EventAdd( self, event ):
|
||||
def ImportFromPng( self ):
|
||||
|
||||
self.Add()
|
||||
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!' )
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def EventCopy( self, event ):
|
||||
|
||||
class ScriptManagementControl( wx.Panel ):
|
||||
|
||||
def __init__( self, parent ):
|
||||
|
||||
self.Copy()
|
||||
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 EventDelete( self, event ):
|
||||
def _Reset( self ):
|
||||
|
||||
self.Delete()
|
||||
self._status.SetLabelText( '' )
|
||||
self._gauge.SetRange( 1 )
|
||||
self._gauge.SetValue( 0 )
|
||||
|
||||
self._link_button.Disable()
|
||||
self._cancel_button.Disable()
|
||||
|
||||
|
||||
def EventDuplicate( self, event ):
|
||||
def _Update( self ):
|
||||
|
||||
self.Duplicate()
|
||||
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 EventEdit( self, event ):
|
||||
def TIMEREventUpdate( self, event ):
|
||||
|
||||
self.Edit()
|
||||
with self._lock:
|
||||
|
||||
self._Update()
|
||||
|
||||
if self._job_key is not None:
|
||||
|
||||
self._update_timer.Start( 100, wx.TIMER_ONE_SHOT )
|
||||
|
||||
|
||||
|
||||
|
||||
def EventPaste( self, event ):
|
||||
def EventCancelButton( self, event ):
|
||||
|
||||
self.Paste()
|
||||
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 )
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,327 @@
|
|||
import ClientConstants as CC
|
||||
import ClientGUICommon
|
||||
import ClientGUIScrolledPanels
|
||||
import HydrusConstants as HC
|
||||
import wx
|
||||
|
||||
class EditFrameLocationPanel( ClientGUIScrolledPanels.EditPanel ):
|
||||
|
||||
def __init__( self, parent, info ):
|
||||
|
||||
ClientGUIScrolledPanels.EditPanel.__init__( self, parent )
|
||||
|
||||
self._original_info = info
|
||||
|
||||
self._remember_size = wx.CheckBox( self, label = 'remember size' )
|
||||
self._remember_position = wx.CheckBox( self, label = 'remember position' )
|
||||
|
||||
self._last_size = ClientGUICommon.NoneableSpinCtrl( self, 'last size', none_phrase = 'none set', min = 100, max = 1000000, unit = None, num_dimensions = 2 )
|
||||
self._last_position = ClientGUICommon.NoneableSpinCtrl( self, 'last position', none_phrase = 'none set', min = -1000000, max = 1000000, unit = None, num_dimensions = 2 )
|
||||
|
||||
self._default_gravity_x = ClientGUICommon.BetterChoice( self )
|
||||
|
||||
self._default_gravity_x.Append( 'by default, expand to width of parent', 1 )
|
||||
self._default_gravity_x.Append( 'by default, expand width as much as needed', -1 )
|
||||
|
||||
self._default_gravity_y = ClientGUICommon.BetterChoice( self )
|
||||
|
||||
self._default_gravity_y.Append( 'by default, expand to height of parent', 1 )
|
||||
self._default_gravity_y.Append( 'by default, expand height as much as needed', -1 )
|
||||
|
||||
self._default_position = ClientGUICommon.BetterChoice( self )
|
||||
|
||||
self._default_position.Append( 'by default, position off the top-left corner of parent', 'topleft' )
|
||||
self._default_position.Append( 'by default, position centered on the parent', 'center' )
|
||||
|
||||
self._maximised = wx.CheckBox( self, label = 'start maximised' )
|
||||
self._fullscreen = wx.CheckBox( self, label = 'start fullscreen' )
|
||||
|
||||
#
|
||||
|
||||
( name, remember_size, remember_position, last_size, last_position, default_gravity, default_position, maximised, fullscreen ) = self._original_info
|
||||
|
||||
self._remember_size.SetValue( remember_size )
|
||||
self._remember_position.SetValue( remember_position )
|
||||
|
||||
self._last_size.SetValue( last_size )
|
||||
self._last_position.SetValue( last_position )
|
||||
|
||||
( x, y ) = default_gravity
|
||||
|
||||
self._default_gravity_x.SelectClientData( x )
|
||||
self._default_gravity_y.SelectClientData( y )
|
||||
|
||||
self._default_position.SelectClientData( default_position )
|
||||
|
||||
self._maximised.SetValue( maximised )
|
||||
self._fullscreen.SetValue( fullscreen )
|
||||
|
||||
#
|
||||
|
||||
vbox = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
text = 'Setting frame location info for ' + name + '.'
|
||||
|
||||
vbox.AddF( wx.StaticText( self, label = text ), CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
vbox.AddF( self._remember_size, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
vbox.AddF( self._remember_position, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
vbox.AddF( self._last_size, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
vbox.AddF( self._last_position, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
vbox.AddF( self._default_gravity_x, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
vbox.AddF( self._default_gravity_y, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
vbox.AddF( self._default_position, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
vbox.AddF( self._maximised, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
vbox.AddF( self._fullscreen, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
self.SetSizer( vbox )
|
||||
|
||||
|
||||
def GetValue( self ):
|
||||
|
||||
( name, remember_size, remember_position, last_size, last_position, default_gravity, default_position, maximised, fullscreen ) = self._original_info
|
||||
|
||||
remember_size = self._remember_size.GetValue()
|
||||
remember_position = self._remember_position.GetValue()
|
||||
|
||||
last_size = self._last_size.GetValue()
|
||||
last_position = self._last_position.GetValue()
|
||||
|
||||
x = self._default_gravity_x.GetChoice()
|
||||
y = self._default_gravity_y.GetChoice()
|
||||
|
||||
default_gravity = [ x, y ]
|
||||
|
||||
default_position = self._default_position.GetChoice()
|
||||
|
||||
maximised = self._maximised.GetValue()
|
||||
fullscreen = self._fullscreen.GetValue()
|
||||
|
||||
return ( name, remember_size, remember_position, last_size, last_position, default_gravity, default_position, maximised, fullscreen )
|
||||
|
||||
|
||||
class EditMediaViewOptionsPanel( ClientGUIScrolledPanels.EditPanel ):
|
||||
|
||||
def __init__( self, parent, info ):
|
||||
|
||||
ClientGUIScrolledPanels.EditPanel.__init__( self, parent )
|
||||
|
||||
self._original_info = info
|
||||
|
||||
( self._mime, media_show_action, preview_show_action, ( media_scale_up, media_scale_down, preview_scale_up, preview_scale_down, exact_zooms_only, scale_up_quality, scale_down_quality ) ) = self._original_info
|
||||
|
||||
possible_actions = CC.media_viewer_capabilities[ self._mime ]
|
||||
|
||||
self._media_show_action = ClientGUICommon.BetterChoice( self )
|
||||
self._preview_show_action = ClientGUICommon.BetterChoice( self )
|
||||
|
||||
for action in possible_actions:
|
||||
|
||||
self._media_show_action.Append( CC.media_viewer_action_string_lookup[ action ], action )
|
||||
|
||||
if action != CC.MEDIA_VIEWER_ACTION_DO_NOT_SHOW:
|
||||
|
||||
self._preview_show_action.Append( CC.media_viewer_action_string_lookup[ action ], action )
|
||||
|
||||
|
||||
|
||||
self._media_show_action.Bind( wx.EVT_CHOICE, self.EventActionChange )
|
||||
self._preview_show_action.Bind( wx.EVT_CHOICE, self.EventActionChange )
|
||||
|
||||
self._media_scale_up = ClientGUICommon.BetterChoice( self )
|
||||
self._media_scale_down = ClientGUICommon.BetterChoice( self )
|
||||
self._preview_scale_up = ClientGUICommon.BetterChoice( self )
|
||||
self._preview_scale_down = ClientGUICommon.BetterChoice( self )
|
||||
|
||||
for scale_action in ( CC.MEDIA_VIEWER_SCALE_100, CC.MEDIA_VIEWER_SCALE_MAX_REGULAR, CC.MEDIA_VIEWER_SCALE_TO_CANVAS ):
|
||||
|
||||
text = CC.media_viewer_scale_string_lookup[ scale_action ]
|
||||
|
||||
self._media_scale_up.Append( text, scale_action )
|
||||
self._preview_scale_up.Append( text, scale_action )
|
||||
|
||||
if scale_action != CC.MEDIA_VIEWER_SCALE_100:
|
||||
|
||||
self._media_scale_down.Append( text, scale_action )
|
||||
self._preview_scale_down.Append( text, scale_action )
|
||||
|
||||
|
||||
|
||||
self._exact_zooms_only = wx.CheckBox( self, label = 'only permit half and double zooms' )
|
||||
self._exact_zooms_only.SetToolTipString( 'This limits zooms to 25%, 50%, 100%, 200%, 400%, and so on. It makes for fast resize and is useful for files that often have flat colours and hard edges, which often scale badly otherwise. The \'canvas fit\' zoom will still be inserted.' )
|
||||
|
||||
self._scale_up_quality = ClientGUICommon.BetterChoice( self )
|
||||
|
||||
for zoom in ( CC.ZOOM_NEAREST, CC.ZOOM_LINEAR, CC.ZOOM_CUBIC, CC.ZOOM_LANCZOS4 ):
|
||||
|
||||
self._scale_up_quality.Append( CC.zoom_string_lookup[ zoom ], zoom )
|
||||
|
||||
|
||||
self._scale_down_quality = ClientGUICommon.BetterChoice( self )
|
||||
|
||||
for zoom in ( CC.ZOOM_NEAREST, CC.ZOOM_LINEAR, CC.ZOOM_AREA ):
|
||||
|
||||
self._scale_down_quality.Append( CC.zoom_string_lookup[ zoom ], zoom )
|
||||
|
||||
|
||||
#
|
||||
|
||||
self._media_show_action.SelectClientData( media_show_action )
|
||||
self._preview_show_action.SelectClientData( preview_show_action )
|
||||
|
||||
self._media_scale_up.SelectClientData( media_scale_up )
|
||||
self._media_scale_down.SelectClientData( media_scale_down )
|
||||
self._preview_scale_up.SelectClientData( preview_scale_up )
|
||||
self._preview_scale_down.SelectClientData( preview_scale_down )
|
||||
|
||||
self._exact_zooms_only.SetValue( exact_zooms_only )
|
||||
|
||||
self._scale_up_quality.SelectClientData( scale_up_quality )
|
||||
self._scale_down_quality.SelectClientData( scale_down_quality )
|
||||
|
||||
#
|
||||
|
||||
vbox = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
text = 'Setting media view options for ' + HC.mime_string_lookup[ self._mime ] + '.'
|
||||
|
||||
vbox.AddF( wx.StaticText( self, label = text ), CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
vbox.AddF( ClientGUICommon.WrapInText( self._media_show_action, self, 'media viewer show action:' ), CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
vbox.AddF( ClientGUICommon.WrapInText( self._preview_show_action, self, 'preview show action:' ), CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
|
||||
if possible_actions == CC.no_support:
|
||||
|
||||
self._media_scale_up.Hide()
|
||||
self._media_scale_down.Hide()
|
||||
self._preview_scale_up.Hide()
|
||||
self._preview_scale_down.Hide()
|
||||
|
||||
self._exact_zooms_only.Hide()
|
||||
|
||||
self._scale_up_quality.Hide()
|
||||
self._scale_down_quality.Hide()
|
||||
|
||||
else:
|
||||
|
||||
rows = []
|
||||
|
||||
rows.append( ( 'if the media is smaller than the media viewer canvas: ', self._media_scale_up ) )
|
||||
rows.append( ( 'if the media is larger than the media viewer canvas: ', self._media_scale_down ) )
|
||||
rows.append( ( 'if the media is smaller than the preview canvas: ', self._preview_scale_up) )
|
||||
rows.append( ( 'if the media is larger than the preview canvas: ', self._preview_scale_down ) )
|
||||
|
||||
gridbox = ClientGUICommon.WrapInGrid( self, rows )
|
||||
|
||||
vbox.AddF( gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
|
||||
vbox.AddF( self._exact_zooms_only, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
vbox.AddF( wx.StaticText( self, label = 'Nearest neighbour is fast and ugly, 8x8 lanczos and area resampling are slower but beautiful.' ), CC.FLAGS_VCENTER )
|
||||
|
||||
vbox.AddF( ClientGUICommon.WrapInText( self._scale_up_quality, self, '>100% (interpolation) quality:' ), CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
vbox.AddF( ClientGUICommon.WrapInText( self._scale_down_quality, self, '<100% (decimation) quality:' ), CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
|
||||
|
||||
if self._mime == HC.APPLICATION_FLASH:
|
||||
|
||||
self._scale_up_quality.Disable()
|
||||
self._scale_down_quality.Disable()
|
||||
|
||||
|
||||
self.SetSizer( vbox )
|
||||
|
||||
|
||||
def EventActionChange( self, event ):
|
||||
|
||||
if self._media_show_action.GetChoice() in CC.no_support and self._preview_show_action.GetChoice() in CC.no_support:
|
||||
|
||||
self._media_scale_up.Disable()
|
||||
self._media_scale_down.Disable()
|
||||
self._preview_scale_up.Disable()
|
||||
self._preview_scale_down.Disable()
|
||||
|
||||
self._exact_zooms_only.Disable()
|
||||
|
||||
self._scale_up_quality.Disable()
|
||||
self._scale_down_quality.Disable()
|
||||
|
||||
else:
|
||||
|
||||
self._media_scale_up.Enable()
|
||||
self._media_scale_down.Enable()
|
||||
self._preview_scale_up.Enable()
|
||||
self._preview_scale_down.Enable()
|
||||
|
||||
self._exact_zooms_only.Enable()
|
||||
|
||||
self._scale_up_quality.Enable()
|
||||
self._scale_down_quality.Enable()
|
||||
|
||||
|
||||
if self._mime == HC.APPLICATION_FLASH:
|
||||
|
||||
self._scale_up_quality.Disable()
|
||||
self._scale_down_quality.Disable()
|
||||
|
||||
|
||||
|
||||
def GetValue( self ):
|
||||
|
||||
media_show_action = self._media_show_action.GetChoice()
|
||||
preview_show_action = self._preview_show_action.GetChoice()
|
||||
|
||||
media_scale_up = self._media_scale_up.GetChoice()
|
||||
media_scale_down = self._media_scale_down.GetChoice()
|
||||
preview_scale_up = self._preview_scale_up.GetChoice()
|
||||
preview_scale_down = self._preview_scale_down.GetChoice()
|
||||
|
||||
exact_zooms_only = self._exact_zooms_only.GetValue()
|
||||
|
||||
scale_up_quality = self._scale_up_quality.GetChoice()
|
||||
scale_down_quality = self._scale_down_quality.GetChoice()
|
||||
|
||||
return ( self._mime, media_show_action, preview_show_action, ( media_scale_up, media_scale_down, preview_scale_up, preview_scale_down, exact_zooms_only, scale_up_quality, scale_down_quality ) )
|
||||
|
||||
|
||||
class EditSeedCachePanel( ClientGUIScrolledPanels.EditPanel ):
|
||||
|
||||
def __init__( self, parent, controller, seed_cache ):
|
||||
|
||||
ClientGUIScrolledPanels.EditPanel.__init__( self, parent )
|
||||
|
||||
self._controller = controller
|
||||
self._seed_cache = seed_cache
|
||||
|
||||
self._text = wx.StaticText( self, label = 'initialising' )
|
||||
self._seed_cache_control = ClientGUICommon.SeedCacheControl( self, self._seed_cache )
|
||||
|
||||
vbox = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
vbox.AddF( self._text, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
vbox.AddF( self._seed_cache_control, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
|
||||
self.SetSizer( vbox )
|
||||
|
||||
self._controller.sub( self, 'NotifySeedUpdated', 'seed_cache_seed_updated' )
|
||||
|
||||
wx.CallAfter( self._UpdateText )
|
||||
|
||||
|
||||
def _UpdateText( self ):
|
||||
|
||||
( status, ( total_processed, total ) ) = self._seed_cache.GetStatus()
|
||||
|
||||
self._text.SetLabelText( status )
|
||||
|
||||
self.Layout()
|
||||
|
||||
|
||||
def GetValue( self ):
|
||||
|
||||
return self._seed_cache
|
||||
|
||||
|
||||
def NotifySeedUpdated( self, seed ):
|
||||
|
||||
self._UpdateText()
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,99 @@
|
|||
import ClientConstants as CC
|
||||
import ClientGUICommon
|
||||
import ClientGUIScrolledPanels
|
||||
import ClientParsing
|
||||
import ClientSerialisable
|
||||
import HydrusConstants as HC
|
||||
import wx
|
||||
|
||||
class PngExportPanel( ClientGUIScrolledPanels.ReviewPanel ):
|
||||
|
||||
def __init__( self, parent, payload_obj ):
|
||||
|
||||
ClientGUIScrolledPanels.ReviewPanel.__init__( self, parent )
|
||||
|
||||
self._payload_obj = payload_obj
|
||||
|
||||
self._filepicker = wx.FilePickerCtrl( self, style = wx.FLP_SAVE, wildcard = 'PNG (*.png)|*.png' )
|
||||
self._filepicker.Bind( wx.EVT_FILEPICKER_CHANGED, self.EventChanged )
|
||||
|
||||
self._title = wx.TextCtrl( self )
|
||||
self._title.Bind( wx.EVT_TEXT, self.EventChanged )
|
||||
|
||||
self._payload_type = wx.TextCtrl( self )
|
||||
|
||||
self._text = wx.TextCtrl( self )
|
||||
|
||||
self._export = ClientGUICommon.BetterButton( self, 'export', self.Export )
|
||||
|
||||
#
|
||||
|
||||
( payload_type, payload_string ) = ClientSerialisable.GetPayloadTypeAndString( self._payload_obj )
|
||||
|
||||
self._payload_type.SetValue( payload_type )
|
||||
|
||||
self._payload_type.Disable()
|
||||
|
||||
self.EventChanged( None )
|
||||
|
||||
#
|
||||
|
||||
rows = []
|
||||
|
||||
rows.append( ( 'export path: ', self._filepicker ) )
|
||||
rows.append( ( 'title: ', self._title ) )
|
||||
rows.append( ( 'payload type: ', self._payload_type ) )
|
||||
rows.append( ( 'description (optional): ', self._text ) )
|
||||
rows.append( ( '', self._export ) )
|
||||
|
||||
gridbox = ClientGUICommon.WrapInGrid( self, rows )
|
||||
|
||||
self.SetSizer( gridbox )
|
||||
|
||||
|
||||
def EventChanged( self, event ):
|
||||
|
||||
problems = []
|
||||
|
||||
path = self._filepicker.GetPath()
|
||||
|
||||
if path == '' or path is None:
|
||||
|
||||
problems.append( 'select a path' )
|
||||
|
||||
|
||||
if self._title.GetValue() == '':
|
||||
|
||||
problems.append( 'set a title' )
|
||||
|
||||
|
||||
if len( problems ) == 0:
|
||||
|
||||
self._export.SetLabelText( 'export' )
|
||||
|
||||
self._export.Enable()
|
||||
|
||||
else:
|
||||
|
||||
self._export.SetLabelText( ' and '.join( problems ) )
|
||||
|
||||
self._export.Disable()
|
||||
|
||||
|
||||
|
||||
def Export( self ):
|
||||
|
||||
( payload_type, payload_string ) = ClientSerialisable.GetPayloadTypeAndString( self._payload_obj )
|
||||
|
||||
title = self._title.GetValue()
|
||||
text = self._text.GetValue()
|
||||
path = self._filepicker.GetPath()
|
||||
|
||||
if not path.endswith( '.png' ):
|
||||
|
||||
path += '.png'
|
||||
|
||||
|
||||
ClientSerialisable.DumpToPng( payload_string, title, payload_type, text, path )
|
||||
|
||||
|
|
@ -2,10 +2,13 @@ import ClientConstants as CC
|
|||
import ClientData
|
||||
import ClientGUICommon
|
||||
import ClientGUIDialogs
|
||||
import ClientGUIParsing
|
||||
import ClientParsing
|
||||
import ClientSearch
|
||||
import ClientThreading
|
||||
import collections
|
||||
import HydrusConstants as HC
|
||||
import HydrusData
|
||||
import HydrusGlobals
|
||||
import HydrusSerialisable
|
||||
import wx
|
||||
|
@ -35,6 +38,11 @@ class ListBoxTagsSuggestionsFavourites( ClientGUICommon.ListBoxTagsStrings ):
|
|||
self._activate_callable( tags )
|
||||
|
||||
|
||||
|
||||
def ActivateAll( self ):
|
||||
|
||||
self._activate_callable( self.GetTags() )
|
||||
|
||||
'''
|
||||
# Maybe reinclude this if per-column autoresizing is desired and not completely buggy
|
||||
def SetTags( self, tags ):
|
||||
|
@ -255,18 +263,21 @@ class RelatedTagsPanel( wx.Panel ):
|
|||
|
||||
class FileLookupScriptTagsPanel( wx.Panel ):
|
||||
|
||||
def __init__( self, parent, service_key, media, activate_callable ):
|
||||
def __init__( self, parent, service_key, media, activate_callable, canvas_key = None ):
|
||||
|
||||
wx.Panel.__init__( self, parent )
|
||||
|
||||
self._service_key = service_key
|
||||
self._media = media
|
||||
self._canvas_key = canvas_key
|
||||
|
||||
scripts = HydrusGlobals.client_controller.Read( 'serialisable_named', HydrusSerialisable.SERIALISABLE_TYPE_PARSE_ROOT_FILE_LOOKUP )
|
||||
|
||||
script_names_to_scripts = { script.GetName() : script for script in scripts }
|
||||
|
||||
self._script_choice = ClientGUICommon.BetterChoice( self )
|
||||
|
||||
for script in scripts:
|
||||
for ( name, script ) in script_names_to_scripts.items():
|
||||
|
||||
self._script_choice.Append( script.GetName(), script )
|
||||
|
||||
|
@ -275,20 +286,64 @@ class FileLookupScriptTagsPanel( wx.Panel ):
|
|||
|
||||
favourite_file_lookup_script = new_options.GetNoneableString( 'favourite_file_lookup_script' )
|
||||
|
||||
self._script_choice.SelectClientData( favourite_file_lookup_script )
|
||||
if favourite_file_lookup_script in script_names_to_scripts:
|
||||
|
||||
self._script_choice.SelectClientData( script_names_to_scripts[ favourite_file_lookup_script ] )
|
||||
|
||||
else:
|
||||
|
||||
self._script_choice.Select( 0 )
|
||||
|
||||
|
||||
fetch_button = ClientGUICommon.BetterButton( self, 'fetch tags', self.FetchTags )
|
||||
|
||||
self._script_management = ClientGUIParsing.ScriptManagementControl( self )
|
||||
|
||||
self._tags = ListBoxTagsSuggestionsFavourites( self, activate_callable, sort_tags = True )
|
||||
|
||||
self._add_all = ClientGUICommon.BetterButton( self, 'add all', self._tags.ActivateAll )
|
||||
|
||||
vbox = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
vbox.AddF( self._script_choice, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
vbox.AddF( fetch_button, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
vbox.AddF( self._script_management, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
vbox.AddF( self._add_all, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
vbox.AddF( self._tags, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
|
||||
self._SetTags( [] )
|
||||
|
||||
self.SetSizer( vbox )
|
||||
|
||||
if self._canvas_key is not None:
|
||||
|
||||
HydrusGlobals.client_controller.sub( self, 'CanvasHasNewMedia', 'canvas_new_display_media' )
|
||||
|
||||
|
||||
|
||||
def _SetTags( self, tags ):
|
||||
|
||||
self._tags.SetTags( tags )
|
||||
|
||||
if len( tags ) == 0:
|
||||
|
||||
self._add_all.Disable()
|
||||
|
||||
else:
|
||||
|
||||
self._add_all.Enable()
|
||||
|
||||
|
||||
|
||||
def CanvasHasNewMedia( self, canvas_key, new_media_singleton ):
|
||||
|
||||
if canvas_key == self._canvas_key:
|
||||
|
||||
self._media = ( new_media_singleton.Duplicate(), )
|
||||
|
||||
self._SetTags( [] )
|
||||
|
||||
|
||||
|
||||
def FetchTags( self ):
|
||||
|
||||
|
@ -315,11 +370,24 @@ class FileLookupScriptTagsPanel( wx.Panel ):
|
|||
file_identifier = script.ConvertMediaToFileIdentifier( m )
|
||||
|
||||
|
||||
content_results = script.DoQuery( file_identifier, 'all' )
|
||||
stop_time = HydrusData.GetNow() + 30
|
||||
|
||||
job_key = ClientThreading.JobKey( cancellable = True, stop_time = stop_time )
|
||||
|
||||
self._script_management.SetJobKey( job_key )
|
||||
|
||||
desired_content = 'all'
|
||||
|
||||
HydrusGlobals.client_controller.CallToThread( self.THREADFetchTags, script, job_key, file_identifier, desired_content )
|
||||
|
||||
|
||||
def THREADFetchTags( self, script, job_key, file_identifier, desired_content ):
|
||||
|
||||
content_results = script.DoQuery( job_key, file_identifier, desired_content )
|
||||
|
||||
tags = ClientParsing.GetTagsFromContentResults( content_results )
|
||||
|
||||
self._tags.SetTags( tags )
|
||||
wx.CallAfter( self._SetTags, tags )
|
||||
|
||||
|
||||
class SuggestedTagsPanel( wx.Panel ):
|
||||
|
@ -369,7 +437,7 @@ class SuggestedTagsPanel( wx.Panel ):
|
|||
|
||||
if self._new_options.GetBoolean( 'show_file_lookup_script_tags' ) and len( media ) == 1:
|
||||
|
||||
file_lookup_script_tags = FileLookupScriptTagsPanel( panel_parent, service_key, media, activate_callable )
|
||||
file_lookup_script_tags = FileLookupScriptTagsPanel( panel_parent, service_key, media, activate_callable, canvas_key = self._canvas_key )
|
||||
|
||||
panels.append( ( 'file lookup scripts', file_lookup_script_tags ) )
|
||||
|
||||
|
|
|
@ -240,17 +240,22 @@ class DialogThatTakesScrollablePanel( DialogThatResizes ):
|
|||
|
||||
DialogThatResizes.__init__( self, parent, title, frame_key )
|
||||
|
||||
self._apply = wx.Button( self, id = wx.ID_OK, label = 'apply' )
|
||||
self._apply.Bind( wx.EVT_BUTTON, self.EventOk )
|
||||
self._apply.SetForegroundColour( ( 0, 128, 0 ) )
|
||||
|
||||
self._cancel = wx.Button( self, id = wx.ID_CANCEL, label = 'cancel' )
|
||||
self._cancel.SetForegroundColour( ( 128, 0, 0 ) )
|
||||
self._InitialiseButtons()
|
||||
|
||||
self.Bind( wx.EVT_MENU, self.EventMenu )
|
||||
self.Bind( CC.EVT_SIZE_CHANGED, self.EventChildSizeChanged )
|
||||
|
||||
|
||||
def _GetButtonBox( self ):
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
def _InitialiseButtons( self ):
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
def EventChildSizeChanged( self, event ):
|
||||
|
||||
if self._panel is not None:
|
||||
|
@ -299,10 +304,7 @@ class DialogThatTakesScrollablePanel( DialogThatResizes ):
|
|||
|
||||
self._panel = panel
|
||||
|
||||
buttonbox = wx.BoxSizer( wx.HORIZONTAL )
|
||||
|
||||
buttonbox.AddF( self._apply, CC.FLAGS_VCENTER )
|
||||
buttonbox.AddF( self._cancel, CC.FLAGS_VCENTER )
|
||||
buttonbox = self._GetButtonBox()
|
||||
|
||||
vbox = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
|
@ -316,11 +318,31 @@ class DialogThatTakesScrollablePanel( DialogThatResizes ):
|
|||
self._panel.SetupScrolling()
|
||||
|
||||
|
||||
class DialogEdit( DialogThatTakesScrollablePanel ):
|
||||
class DialogThatTakesScrollablePanelClose( DialogThatTakesScrollablePanel ):
|
||||
|
||||
def _GetButtonBox( self ):
|
||||
|
||||
buttonbox = wx.BoxSizer( wx.HORIZONTAL )
|
||||
|
||||
buttonbox.AddF( self._close, CC.FLAGS_VCENTER )
|
||||
|
||||
return buttonbox
|
||||
|
||||
|
||||
def _InitialiseButtons( self ):
|
||||
|
||||
self._close = wx.Button( self, id = wx.ID_OK, label = 'close' )
|
||||
self._close.Bind( wx.EVT_BUTTON, self.EventOk )
|
||||
|
||||
self._cancel = wx.Button( self, id = wx.ID_CANCEL )
|
||||
self._cancel.Hide()
|
||||
|
||||
|
||||
class DialogNullipotent( DialogThatTakesScrollablePanelClose ):
|
||||
|
||||
def __init__( self, parent, title ):
|
||||
|
||||
DialogThatTakesScrollablePanel.__init__( self, parent, title, 'regular_dialog' )
|
||||
DialogThatTakesScrollablePanelClose.__init__( self, parent, title, 'regular_dialog' )
|
||||
|
||||
|
||||
def EventOk( self, event ):
|
||||
|
@ -330,7 +352,43 @@ class DialogEdit( DialogThatTakesScrollablePanel ):
|
|||
self.EndModal( wx.ID_OK )
|
||||
|
||||
|
||||
class DialogManage( DialogThatTakesScrollablePanel ):
|
||||
class DialogThatTakesScrollablePanelApplyCancel( DialogThatTakesScrollablePanel ):
|
||||
|
||||
def _GetButtonBox( self ):
|
||||
|
||||
buttonbox = wx.BoxSizer( wx.HORIZONTAL )
|
||||
|
||||
buttonbox.AddF( self._apply, CC.FLAGS_VCENTER )
|
||||
buttonbox.AddF( self._cancel, CC.FLAGS_VCENTER )
|
||||
|
||||
return buttonbox
|
||||
|
||||
|
||||
def _InitialiseButtons( self ):
|
||||
|
||||
self._apply = wx.Button( self, id = wx.ID_OK, label = 'apply' )
|
||||
self._apply.Bind( wx.EVT_BUTTON, self.EventOk )
|
||||
self._apply.SetForegroundColour( ( 0, 128, 0 ) )
|
||||
|
||||
self._cancel = wx.Button( self, id = wx.ID_CANCEL, label = 'cancel' )
|
||||
self._cancel.SetForegroundColour( ( 128, 0, 0 ) )
|
||||
|
||||
|
||||
class DialogEdit( DialogThatTakesScrollablePanelApplyCancel ):
|
||||
|
||||
def __init__( self, parent, title ):
|
||||
|
||||
DialogThatTakesScrollablePanelApplyCancel.__init__( self, parent, title, 'regular_dialog' )
|
||||
|
||||
|
||||
def EventOk( self, event ):
|
||||
|
||||
SaveTLWSizeAndPosition( self, self._frame_key )
|
||||
|
||||
self.EndModal( wx.ID_OK )
|
||||
|
||||
|
||||
class DialogManage( DialogThatTakesScrollablePanelApplyCancel ):
|
||||
|
||||
def EventOk( self, event ):
|
||||
|
||||
|
|
|
@ -56,6 +56,13 @@ def GenerateNumpyImage( path ):
|
|||
|
||||
numpy_image = cv2.imread( path, flags = IMREAD_UNCHANGED )
|
||||
|
||||
if numpy_image.dtype == 'uint16':
|
||||
|
||||
numpy_image /= 256
|
||||
|
||||
numpy_image = numpy.array( numpy_image, dtype = 'uint8' )
|
||||
|
||||
|
||||
if numpy_image is None: # doesn't support static gifs and some random other stuff
|
||||
|
||||
pil_image = HydrusImageHandling.GeneratePILImage( path )
|
||||
|
@ -104,6 +111,7 @@ def GenerateNumPyImageFromPILImage( pil_image ):
|
|||
|
||||
def GeneratePerceptualHash( path ):
|
||||
|
||||
|
||||
numpy_image = GenerateNumpyImage( path )
|
||||
|
||||
( y, x, depth ) = numpy_image.shape
|
||||
|
@ -118,7 +126,7 @@ def GeneratePerceptualHash( path ):
|
|||
|
||||
numpy_image_bgr = numpy_image[ :, :, :3 ]
|
||||
|
||||
numpy_image_gray_bare = cv2.cvtColor( numpy_image_bgr, cv2.COLOR_BGR2GRAY )
|
||||
numpy_image_gray_bare = cv2.cvtColor( numpy_image_bgr, cv2.COLOR_RGB2GRAY )
|
||||
|
||||
# create a white greyscale canvas
|
||||
|
||||
|
@ -130,7 +138,7 @@ def GeneratePerceptualHash( path ):
|
|||
|
||||
else:
|
||||
|
||||
numpy_image_gray = cv2.cvtColor( numpy_image, cv2.COLOR_BGR2GRAY )
|
||||
numpy_image_gray = cv2.cvtColor( numpy_image, cv2.COLOR_RGB2GRAY )
|
||||
|
||||
|
||||
numpy_image_tiny = cv2.resize( numpy_image_gray, ( 32, 32 ), interpolation = cv2.INTER_AREA )
|
||||
|
|
|
@ -7,6 +7,7 @@ import HydrusGlobals
|
|||
import HydrusSerialisable
|
||||
import HydrusTags
|
||||
import os
|
||||
import time
|
||||
import urlparse
|
||||
|
||||
def ChildHasDesiredContent( child, desired_content ):
|
||||
|
@ -65,7 +66,7 @@ def ConvertParsableContentToPrettyString( parsable_content, include_veto = False
|
|||
return ', '.join( pretty_strings )
|
||||
|
||||
|
||||
def GetChildrenContent( children, data, referral_url, desired_content ):
|
||||
def GetChildrenContent( job_key, children, data, referral_url, desired_content ):
|
||||
|
||||
for child in children:
|
||||
|
||||
|
@ -81,7 +82,7 @@ def GetChildrenContent( children, data, referral_url, desired_content ):
|
|||
|
||||
if ChildHasDesiredContent( child, desired_content ):
|
||||
|
||||
child_content = child.Parse( data, referral_url, desired_content )
|
||||
child_content = child.Parse( job_key, data, referral_url, desired_content )
|
||||
|
||||
content.extend( child_content )
|
||||
|
||||
|
@ -155,30 +156,73 @@ def RenderTagRule( ( name, attrs, index ) ):
|
|||
class ParseFormulaHTML( HydrusSerialisable.SerialisableBase ):
|
||||
|
||||
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_PARSE_FORMULA_HTML
|
||||
SERIALISABLE_VERSION = 1
|
||||
SERIALISABLE_VERSION = 2
|
||||
|
||||
def __init__( self, tag_rules = None, content_rule = None ):
|
||||
def __init__( self, tag_rules = None, content_rule = None, culling_and_adding = None ):
|
||||
|
||||
if tag_rules is None:
|
||||
|
||||
tag_rules = [ ( 'a', {}, None ) ]
|
||||
|
||||
|
||||
if culling_and_adding is None:
|
||||
|
||||
culling_and_adding = ( 0, 0, '', '' )
|
||||
|
||||
|
||||
self._tag_rules = tag_rules
|
||||
|
||||
self._content_rule = content_rule
|
||||
|
||||
# I need extra rules here for chopping stuff off the beginning or end and appending or prepending strings
|
||||
self._culling_and_adding = culling_and_adding
|
||||
|
||||
|
||||
def _CullAndAdd( self, text ):
|
||||
|
||||
( cull_front, cull_back, prepend, append ) = self._culling_and_adding
|
||||
|
||||
if cull_front != 0:
|
||||
|
||||
text = text[ cull_front : ]
|
||||
|
||||
|
||||
if cull_back != 0:
|
||||
|
||||
text = text[ : - cull_back ]
|
||||
|
||||
|
||||
if text == '':
|
||||
|
||||
return None
|
||||
|
||||
|
||||
text = prepend + text + append
|
||||
|
||||
return text
|
||||
|
||||
|
||||
def _GetSerialisableInfo( self ):
|
||||
|
||||
return ( self._tag_rules, self._content_rule )
|
||||
return ( self._tag_rules, self._content_rule, self._culling_and_adding )
|
||||
|
||||
|
||||
def _InitialiseFromSerialisableInfo( self, serialisable_info ):
|
||||
|
||||
( self._tag_rules, self._content_rule ) = serialisable_info
|
||||
( self._tag_rules, self._content_rule, self._culling_and_adding ) = serialisable_info
|
||||
|
||||
|
||||
def _UpdateSerialisableInfo( self, version, old_serialisable_info ):
|
||||
|
||||
if version == 1:
|
||||
|
||||
( tag_rules, content_rule ) = old_serialisable_info
|
||||
|
||||
culling_and_adding = ( 0, 0, '', '' )
|
||||
|
||||
new_serialisable_info = ( tag_rules, content_rule, culling_and_adding )
|
||||
|
||||
return ( 2, new_serialisable_info )
|
||||
|
||||
|
||||
|
||||
def _ParseContent( self, root ):
|
||||
|
@ -191,7 +235,7 @@ class ParseFormulaHTML( HydrusSerialisable.SerialisableBase ):
|
|||
|
||||
if root.has_attr( self._content_rule ):
|
||||
|
||||
result = root[ self._content_rule ]
|
||||
result = root[ self._content_rule ][0]
|
||||
|
||||
else:
|
||||
|
||||
|
@ -199,13 +243,13 @@ class ParseFormulaHTML( HydrusSerialisable.SerialisableBase ):
|
|||
|
||||
|
||||
|
||||
if result == '':
|
||||
if result == '' or result is None:
|
||||
|
||||
return None
|
||||
|
||||
else:
|
||||
|
||||
return result
|
||||
return self._CullAndAdd( result )
|
||||
|
||||
|
||||
|
||||
|
@ -291,6 +335,50 @@ class ParseFormulaHTML( HydrusSerialisable.SerialisableBase ):
|
|||
pretty_strings.append( 'get the ' + self._content_rule + ' attribute of those tags' )
|
||||
|
||||
|
||||
cull_munge_strings = []
|
||||
|
||||
( cull_front, cull_back, prepend, append ) = self._culling_and_adding
|
||||
|
||||
if cull_front > 0:
|
||||
|
||||
cull_munge_strings.append( 'the first ' + HydrusData.ConvertIntToPrettyString( cull_front ) + ' characters' )
|
||||
|
||||
elif cull_front < 0:
|
||||
|
||||
cull_munge_strings.append( 'all but the last ' + HydrusData.ConvertIntToPrettyString( abs( cull_front ) ) + ' characters' )
|
||||
|
||||
|
||||
if cull_back > 0:
|
||||
|
||||
cull_munge_strings.append( 'the last ' + HydrusData.ConvertIntToPrettyString( cull_back ) + ' characters' )
|
||||
|
||||
elif cull_back < 0:
|
||||
|
||||
cull_munge_strings.append( 'all but the first ' + HydrusData.ConvertIntToPrettyString( abs( cull_back ) ) + ' characters' )
|
||||
|
||||
|
||||
if len( cull_munge_strings ) > 0:
|
||||
|
||||
pretty_strings.append( 'remove ' + ' and '.join( cull_munge_strings ) )
|
||||
|
||||
|
||||
add_munge_strings = []
|
||||
|
||||
if prepend != '':
|
||||
|
||||
add_munge_strings.append( 'prepend "' + prepend + '"' )
|
||||
|
||||
|
||||
if append != '':
|
||||
|
||||
add_munge_strings.append( 'append "' + append + '"' )
|
||||
|
||||
|
||||
if len( add_munge_strings ) > 0:
|
||||
|
||||
pretty_strings.append( ' and '.join( add_munge_strings ) )
|
||||
|
||||
|
||||
separator = os.linesep + 'and then '
|
||||
|
||||
pretty_multiline_string = separator.join( pretty_strings )
|
||||
|
@ -300,7 +388,7 @@ class ParseFormulaHTML( HydrusSerialisable.SerialisableBase ):
|
|||
|
||||
def ToTuple( self ):
|
||||
|
||||
return ( self._tag_rules, self._content_rule )
|
||||
return ( self._tag_rules, self._content_rule, self._culling_and_adding )
|
||||
|
||||
|
||||
HydrusSerialisable.SERIALISABLE_TYPES_TO_OBJECT_TYPES[ HydrusSerialisable.SERIALISABLE_TYPE_PARSE_FORMULA_HTML ] = ParseFormulaHTML
|
||||
|
@ -365,7 +453,7 @@ class ParseNodeContent( HydrusSerialisable.SerialisableBase ):
|
|||
return { ( self._name, self._content_type, self._additional_info ) }
|
||||
|
||||
|
||||
def Parse( self, data, referral_url, desired_content ):
|
||||
def Parse( self, job_key, data, referral_url, desired_content ):
|
||||
|
||||
content_description = ( self._name, self._content_type, self._additional_info )
|
||||
|
||||
|
@ -466,34 +554,67 @@ class ParseNodeContentLink( HydrusSerialisable.SerialisableBase ):
|
|||
return children_parsable_content
|
||||
|
||||
|
||||
def Parse( self, data, referral_url, desired_content ):
|
||||
def Parse( self, job_key, data, referral_url, desired_content ):
|
||||
|
||||
search_urls = self.ParseURLs( data, referral_url )
|
||||
search_urls = self.ParseURLs( job_key, data, referral_url )
|
||||
|
||||
content = []
|
||||
|
||||
for search_url in search_urls:
|
||||
|
||||
headers = { 'Referer' : referral_url }
|
||||
|
||||
response = ClientNetworking.RequestsGet( search_url, headers = headers )
|
||||
try:
|
||||
|
||||
job_key.SetVariable( 'script_status', 'fetching ' + search_url )
|
||||
|
||||
headers = { 'Referer' : referral_url }
|
||||
|
||||
response = ClientNetworking.RequestsGet( search_url, headers = headers )
|
||||
|
||||
except HydrusExceptions.NotFoundException:
|
||||
|
||||
job_key.SetVariable( 'script_status', '404 - nothing found' )
|
||||
|
||||
time.sleep( 2 )
|
||||
|
||||
continue
|
||||
|
||||
except HydrusExceptions.NetworkException as e:
|
||||
|
||||
job_key.SetVariable( 'script_status', 'Network error! Details written to log.' )
|
||||
|
||||
HydrusData.PrintException( e )
|
||||
|
||||
time.sleep( 2 )
|
||||
|
||||
continue
|
||||
|
||||
|
||||
linked_data = response.content
|
||||
|
||||
children_content = GetChildrenContent( self._children, linked_data, search_url, desired_content )
|
||||
children_content = GetChildrenContent( job_key, self._children, linked_data, search_url, desired_content )
|
||||
|
||||
content.extend( children_content )
|
||||
|
||||
if job_key.IsCancelled():
|
||||
|
||||
raise HydrusExceptions.CancelledException()
|
||||
|
||||
|
||||
|
||||
return content
|
||||
|
||||
|
||||
def ParseURLs( self, data, referral_url ):
|
||||
def ParseURLs( self, job_key, data, referral_url ):
|
||||
|
||||
basic_urls = self._formula.Parse( data )
|
||||
|
||||
absolute_urls = [ urlparse.urljoin( referral_url, basic_url ) for basic_url in basic_urls ]
|
||||
|
||||
for url in absolute_urls:
|
||||
|
||||
job_key.AddURL( url )
|
||||
|
||||
|
||||
return absolute_urls
|
||||
|
||||
|
||||
|
@ -620,7 +741,9 @@ class ParseRootFileLookup( HydrusSerialisable.SerialisableBaseNamed ):
|
|||
|
||||
|
||||
|
||||
def FetchData( self, file_identifier ):
|
||||
def FetchData( self, job_key, file_identifier ):
|
||||
|
||||
# add gauge report hook and cancel support here
|
||||
|
||||
request_args = dict( self._static_args )
|
||||
|
||||
|
@ -636,24 +759,39 @@ class ParseRootFileLookup( HydrusSerialisable.SerialisableBaseNamed ):
|
|||
raise Exception( 'Cannot have a file as an argument on a GET query!' )
|
||||
|
||||
|
||||
rendered_url = self._url + '?' + '&'.join( ( HydrusData.ToByteString( key ) + '=' + HydrusData.ToByteString( value ) for ( key, value ) in request_args.items() ) )
|
||||
|
||||
job_key.SetVariable( 'script_status', 'fetching ' + rendered_url )
|
||||
|
||||
job_key.AddURL( rendered_url )
|
||||
|
||||
response = ClientNetworking.RequestsGet( self._url, params = request_args )
|
||||
|
||||
elif self._query_type == HC.POST:
|
||||
|
||||
if self._file_identifier_type == FILE_IDENTIFIER_TYPE_FILE:
|
||||
|
||||
job_key.SetVariable( 'script_status', 'uploading file' )
|
||||
|
||||
path = file_identifier
|
||||
|
||||
files = { self._file_identifier_arg_name : open( path, 'rb' ) }
|
||||
|
||||
else:
|
||||
|
||||
job_key.SetVariable( 'script_status', 'uploading identifier' )
|
||||
|
||||
files = None
|
||||
|
||||
|
||||
response = ClientNetworking.RequestsPost( self._url, data = request_args, files = files )
|
||||
|
||||
|
||||
if job_key.IsCancelled():
|
||||
|
||||
raise HydrusExceptions.CancelledException()
|
||||
|
||||
|
||||
data = response.content
|
||||
|
||||
return data
|
||||
|
@ -671,13 +809,52 @@ class ParseRootFileLookup( HydrusSerialisable.SerialisableBaseNamed ):
|
|||
return children_parsable_content
|
||||
|
||||
|
||||
def DoQuery( self, file_identifier, desired_content ):
|
||||
def DoQuery( self, job_key, file_identifier, desired_content ):
|
||||
|
||||
# this should eventually take a job_key that will be propagated down and will have obeyed cancel and so on
|
||||
|
||||
data = self.FetchData( file_identifier )
|
||||
|
||||
return self.Parse( data, desired_content )
|
||||
try:
|
||||
|
||||
try:
|
||||
|
||||
data = self.FetchData( job_key, file_identifier )
|
||||
|
||||
except HydrusExceptions.NotFoundException:
|
||||
|
||||
job_key.SetVariable( 'script_status', '404 - nothing found' )
|
||||
|
||||
return []
|
||||
|
||||
except HydrusExceptions.NetworkException as e:
|
||||
|
||||
job_key.SetVariable( 'script_status', 'Network error!' )
|
||||
|
||||
HydrusData.ShowException( e )
|
||||
|
||||
return []
|
||||
|
||||
|
||||
content_results = self.Parse( job_key, data, desired_content )
|
||||
|
||||
if len( content_results ) == 0:
|
||||
|
||||
job_key.SetVariable( 'script_status', 'Did not find anything.' )
|
||||
|
||||
else:
|
||||
|
||||
job_key.SetVariable( 'script_status', 'Found ' + HydrusData.ConvertIntToPrettyString( len( content_results ) ) + ' rows.' )
|
||||
|
||||
|
||||
return content_results
|
||||
|
||||
except HydrusExceptions.CancelledException:
|
||||
|
||||
job_key.SetVariable( 'script_status', 'Cancelled!' )
|
||||
|
||||
return []
|
||||
|
||||
finally:
|
||||
|
||||
job_key.Finish()
|
||||
|
||||
|
||||
|
||||
def UsesUserInput( self ):
|
||||
|
@ -685,9 +862,9 @@ class ParseRootFileLookup( HydrusSerialisable.SerialisableBaseNamed ):
|
|||
return self._file_identifier_type == FILE_IDENTIFIER_TYPE_USER_INPUT
|
||||
|
||||
|
||||
def Parse( self, data, desired_content ):
|
||||
def Parse( self, job_key, data, desired_content ):
|
||||
|
||||
content = GetChildrenContent( self._children, data, self._url, desired_content )
|
||||
content = GetChildrenContent( job_key, self._children, data, self._url, desired_content )
|
||||
|
||||
return content
|
||||
|
||||
|
|
|
@ -1,9 +1,140 @@
|
|||
import ClientConstants as CC
|
||||
import ClientImageHandling
|
||||
import ClientParsing
|
||||
import cv2
|
||||
import HydrusConstants as HC
|
||||
import HydrusData
|
||||
import HydrusSerialisable
|
||||
import numpy
|
||||
import os
|
||||
import struct
|
||||
import wx
|
||||
|
||||
if cv2.__version__.startswith( '2' ):
|
||||
|
||||
IMREAD_UNCHANGED = cv2.CV_LOAD_IMAGE_UNCHANGED
|
||||
|
||||
else:
|
||||
|
||||
IMREAD_UNCHANGED = cv2.IMREAD_UNCHANGED
|
||||
|
||||
|
||||
png_font = cv2.FONT_HERSHEY_TRIPLEX
|
||||
greyscale_text_color = 0
|
||||
|
||||
title_size = 0.7
|
||||
payload_type_size = 0.5
|
||||
text_size = 0.4
|
||||
|
||||
def CreateTopImage( width, title, payload_type, text ):
|
||||
|
||||
text_extent_bmp = wx.EmptyBitmap( 20, 20, 24 )
|
||||
|
||||
dc = wx.MemoryDC( text_extent_bmp )
|
||||
|
||||
text_font = wx.SystemSettings.GetFont( wx.SYS_DEFAULT_GUI_FONT )
|
||||
|
||||
basic_font_size = text_font.GetPointSize()
|
||||
|
||||
payload_type_font = wx.SystemSettings.GetFont( wx.SYS_DEFAULT_GUI_FONT )
|
||||
|
||||
payload_type_font.SetPointSize( int( basic_font_size * 1.4 ) )
|
||||
|
||||
title_font = wx.SystemSettings.GetFont( wx.SYS_DEFAULT_GUI_FONT )
|
||||
|
||||
title_font.SetPointSize( int( basic_font_size * 2.0 ) )
|
||||
|
||||
dc.SetFont( text_font )
|
||||
( gumpf, text_line_height ) = dc.GetTextExtent( 'abcdefghijklmnopqrstuvwxyz' )
|
||||
|
||||
dc.SetFont( payload_type_font )
|
||||
( gumpf, payload_type_line_height ) = dc.GetTextExtent( 'abcdefghijklmnopqrstuvwxyz' )
|
||||
|
||||
dc.SetFont( title_font )
|
||||
( gumpf, title_line_height ) = dc.GetTextExtent( 'abcdefghijklmnopqrstuvwxyz' )
|
||||
|
||||
del dc
|
||||
del text_extent_bmp
|
||||
|
||||
text_lines = WrapText( text, width, text_size, 1 )
|
||||
|
||||
if len( text_lines ) == 0:
|
||||
|
||||
text_total_height = 0
|
||||
|
||||
else:
|
||||
|
||||
text_total_height = ( text_line_height + 4 ) * len( text_lines )
|
||||
|
||||
text_total_height += 6 # to bring the last 4 padding up to 10 padding
|
||||
|
||||
|
||||
top_height = 10 + title_line_height + 10 + payload_type_line_height + 10 + text_total_height
|
||||
|
||||
#
|
||||
|
||||
top_bmp = wx.EmptyBitmap( width, top_height, 24 )
|
||||
|
||||
dc = wx.MemoryDC( top_bmp )
|
||||
|
||||
dc.SetBackground( wx.Brush( wx.WHITE ) )
|
||||
|
||||
dc.Clear()
|
||||
|
||||
#
|
||||
|
||||
dc.DrawBitmap( CC.GlobalBMPs.file_repository, width - 16 - 5, 5 )
|
||||
|
||||
#
|
||||
|
||||
current_y = 10
|
||||
|
||||
dc.SetFont( title_font )
|
||||
|
||||
( t_width, t_height ) = dc.GetTextExtent( title )
|
||||
|
||||
dc.DrawText( title, ( width - t_width ) / 2, current_y )
|
||||
|
||||
current_y += t_height + 10
|
||||
|
||||
dc.SetFont( payload_type_font )
|
||||
|
||||
( t_width, t_height ) = dc.GetTextExtent( payload_type )
|
||||
|
||||
dc.DrawText( payload_type, ( width - t_width ) / 2, current_y )
|
||||
|
||||
current_y += t_height + 10
|
||||
|
||||
dc.SetFont( text_font )
|
||||
|
||||
for text_line in text_lines:
|
||||
|
||||
( t_width, t_height ) = dc.GetTextExtent( text_line )
|
||||
|
||||
dc.DrawText( text_line, ( width - t_width ) / 2, current_y )
|
||||
|
||||
current_y += t_height + 4
|
||||
|
||||
|
||||
del dc
|
||||
|
||||
data = top_bmp.ConvertToImage().GetData()
|
||||
|
||||
top_image_rgb = numpy.fromstring( data, dtype = 'uint8' ).reshape( ( top_height, width, 3 ) )
|
||||
|
||||
top_bmp.Destroy()
|
||||
|
||||
top_image = cv2.cvtColor( top_image_rgb, cv2.COLOR_RGB2GRAY )
|
||||
|
||||
top_height_header = struct.pack( '!H', top_height )
|
||||
|
||||
( byte0, byte1 ) = top_height_header
|
||||
|
||||
top_image[0][0] = ord( byte0 )
|
||||
top_image[0][1] = ord( byte1 )
|
||||
|
||||
return top_image
|
||||
|
||||
def DumpToPng( payload, title, payload_type, text, path ):
|
||||
|
||||
payload_length = len( payload )
|
||||
|
@ -14,35 +145,14 @@ def DumpToPng( payload, title, payload_type, text, path ):
|
|||
|
||||
width = max( 512, square_width )
|
||||
|
||||
payload_height = float( payload_string_length ) / width
|
||||
payload_height = int( float( payload_string_length ) / width )
|
||||
|
||||
if float( payload_string_length ) / width % 1.0 > 0:
|
||||
|
||||
payload_height += 1
|
||||
|
||||
|
||||
# given this width, figure out how much height we need for title and text and object type
|
||||
# does cv have gettextentent or similar?
|
||||
# getTextSize looks like the one
|
||||
# intelligently wrap the text to fit into our width sans padding
|
||||
|
||||
# we now know our height
|
||||
top_height = 250
|
||||
|
||||
top_image = numpy.empty( ( top_height, width ), dtype = 'uint8' )
|
||||
|
||||
top_image.fill( 255 )
|
||||
|
||||
# draw hydrus icon in the corner
|
||||
# draw title
|
||||
# draw payload_type
|
||||
# draw text
|
||||
top_height_header = struct.pack( '!H', top_height )
|
||||
|
||||
( byte0, byte1 ) = top_height_header
|
||||
|
||||
top_image[0][0] = ord( byte0 )
|
||||
top_image[0][1] = ord( byte1 )
|
||||
top_image = CreateTopImage( width, title, payload_type, text )
|
||||
|
||||
payload_length_header = struct.pack( '!I', payload_length )
|
||||
|
||||
|
@ -52,31 +162,113 @@ def DumpToPng( payload, title, payload_type, text, path ):
|
|||
|
||||
payload_image = numpy.fromstring( full_payload_string, dtype = 'uint8' ).reshape( ( payload_height, width ) )
|
||||
|
||||
# if this works out wrong, you can change axis or do stack_vertically instead or something. one of those will do the trick
|
||||
finished_image = numpy.concatenate( top_image, payload_image )
|
||||
finished_image = numpy.concatenate( ( top_image, payload_image ) )
|
||||
|
||||
# make sure this is how cv 'params' work
|
||||
cv2.imwrite( path, finished_image, params = { cv2.IMWRITE_PNG_COMPRESSION : 9 } )
|
||||
cv2.imwrite( path, finished_image, [ cv2.IMWRITE_PNG_COMPRESSION, 9 ] )
|
||||
|
||||
def GetPayloadTypeAndString( payload_obj ):
|
||||
|
||||
payload_string = payload_obj.DumpToNetworkString()
|
||||
|
||||
if isinstance( payload_obj, ClientParsing.ParseRootFileLookup ):
|
||||
|
||||
payload_obj_type_string = 'File Lookup Script'
|
||||
|
||||
|
||||
payload_type = payload_obj_type_string + ' - ' + HydrusData.ConvertIntToBytes( len( payload_string ) )
|
||||
|
||||
return ( payload_type, payload_string )
|
||||
|
||||
def LoadFromPng( path ):
|
||||
|
||||
numpy_image = cv2.imread( path )
|
||||
try:
|
||||
|
||||
numpy_image = cv2.imread( path, flags = IMREAD_UNCHANGED )
|
||||
|
||||
except Exception as e:
|
||||
|
||||
HydrusData.ShowException( e )
|
||||
|
||||
raise Exception( 'That did not appear to be a valid image!' )
|
||||
|
||||
|
||||
( height, width ) = numpy_image.shape
|
||||
|
||||
complete_data = numpy_image.tostring()
|
||||
|
||||
top_height_header = complete_data[:2]
|
||||
|
||||
( top_height, ) = struct.unpack( '!H', top_height_header )
|
||||
|
||||
full_payload_string = complete_data[ width * top_height : ]
|
||||
|
||||
payload_length_header = full_payload_string[:4]
|
||||
|
||||
( payload_length, ) = struct.unpack( '!I', payload_length_header )
|
||||
|
||||
payload = full_payload_string[ 4 : 4 + payload_length ]
|
||||
try:
|
||||
|
||||
( height, width ) = numpy_image.shape
|
||||
|
||||
complete_data = numpy_image.tostring()
|
||||
|
||||
top_height_header = complete_data[:2]
|
||||
|
||||
( top_height, ) = struct.unpack( '!H', top_height_header )
|
||||
|
||||
full_payload_string = complete_data[ width * top_height : ]
|
||||
|
||||
payload_length_header = full_payload_string[:4]
|
||||
|
||||
( payload_length, ) = struct.unpack( '!I', payload_length_header )
|
||||
|
||||
payload = full_payload_string[ 4 : 4 + payload_length ]
|
||||
|
||||
except Exception as e:
|
||||
|
||||
HydrusData.ShowException( e )
|
||||
|
||||
raise Exception( 'The image was fine, but it did not seem to have hydrus data encoded in it!' )
|
||||
|
||||
|
||||
return payload
|
||||
|
||||
def TextExceedsWidth( text, width, size, thickness ):
|
||||
|
||||
( ( tw, th ), baseline ) = cv2.getTextSize( text, png_font, size, thickness )
|
||||
|
||||
return tw > width
|
||||
|
||||
def WrapText( text, width, size, thickness ):
|
||||
|
||||
words = text.split( ' ' )
|
||||
|
||||
lines = []
|
||||
|
||||
next_line = []
|
||||
|
||||
for word in words:
|
||||
|
||||
if word == '':
|
||||
|
||||
continue
|
||||
|
||||
|
||||
potential_next_line = list( next_line )
|
||||
|
||||
potential_next_line.append( word )
|
||||
|
||||
if TextExceedsWidth( ' '.join( potential_next_line ), width, size, thickness ):
|
||||
|
||||
if len( potential_next_line ) == 1: # one very long word
|
||||
|
||||
lines.append( ' '.join( potential_next_line ) )
|
||||
|
||||
next_line = []
|
||||
|
||||
else:
|
||||
|
||||
lines.append( ' '.join( next_line ) )
|
||||
|
||||
next_line = [ word ]
|
||||
|
||||
|
||||
else:
|
||||
|
||||
next_line = potential_next_line
|
||||
|
||||
|
||||
|
||||
if len( next_line ) > 0:
|
||||
|
||||
lines.append( ' '.join( next_line ) )
|
||||
|
||||
|
||||
return lines
|
||||
|
|
@ -37,6 +37,7 @@ class JobKey( object ):
|
|||
self._longer_pause_period = 1000
|
||||
self._next_longer_pause = HydrusData.GetNow() + self._longer_pause_period
|
||||
|
||||
self._urls = []
|
||||
self._variable_lock = threading.Lock()
|
||||
self._variables = dict()
|
||||
|
||||
|
@ -91,6 +92,14 @@ class JobKey( object ):
|
|||
|
||||
|
||||
|
||||
def AddURL( self, url ):
|
||||
|
||||
with self._variable_lock:
|
||||
|
||||
self._urls.append( url )
|
||||
|
||||
|
||||
|
||||
def Begin( self ): self._begun.set()
|
||||
|
||||
def CanBegin( self ):
|
||||
|
@ -110,11 +119,18 @@ class JobKey( object ):
|
|||
return True
|
||||
|
||||
|
||||
def Cancel( self ):
|
||||
def Cancel( self, seconds = None ):
|
||||
|
||||
self._cancelled.set()
|
||||
|
||||
self.Finish()
|
||||
if seconds is None:
|
||||
|
||||
self._cancelled.set()
|
||||
|
||||
self.Finish()
|
||||
|
||||
else:
|
||||
|
||||
wx.CallAfter( wx.CallLater, seconds * 1000, self.Cancel )
|
||||
|
||||
|
||||
|
||||
def Delete( self, seconds = None ):
|
||||
|
@ -139,7 +155,17 @@ class JobKey( object ):
|
|||
time.sleep( 0.00001 )
|
||||
|
||||
|
||||
def Finish( self ): self._done.set()
|
||||
def Finish( self, seconds = None ):
|
||||
|
||||
if seconds is None:
|
||||
|
||||
self._done.set()
|
||||
|
||||
else:
|
||||
|
||||
wx.CallAfter( wx.CallLater, seconds * 1000, self.Finish )
|
||||
|
||||
|
||||
|
||||
def GetIfHasVariable( self, name ):
|
||||
|
||||
|
@ -156,7 +182,18 @@ class JobKey( object ):
|
|||
|
||||
|
||||
|
||||
def GetKey( self ): return self._key
|
||||
def GetKey( self ):
|
||||
|
||||
return self._key
|
||||
|
||||
|
||||
def GetURLs( self ):
|
||||
|
||||
with self._variable_lock:
|
||||
|
||||
return list( self._urls )
|
||||
|
||||
|
||||
|
||||
def HasVariable( self, name ):
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ import threading
|
|||
import time
|
||||
import traceback
|
||||
|
||||
if HC.PLATFORM_WINDOWS: import mp3play
|
||||
#if HC.PLATFORM_WINDOWS: import mp3play
|
||||
|
||||
parsed_noises = {}
|
||||
|
||||
|
@ -60,7 +60,8 @@ def GetWMADuration( path ):
|
|||
length_in_ms = int( length_in_seconds * 1000 )
|
||||
|
||||
return length_in_ms
|
||||
|
||||
|
||||
'''
|
||||
def PlayNoise( name ):
|
||||
|
||||
if HC.PLATFORM_OSX: return
|
||||
|
@ -80,4 +81,4 @@ def PlayNoise( name ):
|
|||
noise = parsed_noises[ name ]
|
||||
|
||||
noise.play()
|
||||
|
||||
'''
|
|
@ -44,7 +44,7 @@ options = {}
|
|||
# Misc
|
||||
|
||||
NETWORK_VERSION = 17
|
||||
SOFTWARE_VERSION = 231
|
||||
SOFTWARE_VERSION = 232
|
||||
|
||||
UNSCALED_THUMBNAIL_DIMENSIONS = ( 200, 200 )
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import os
|
||||
|
||||
class CancelledException( Exception ): pass
|
||||
class CantRenderWithCVException( Exception ): pass
|
||||
class DataMissing( Exception ): pass
|
||||
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 443 B |
Binary file not shown.
After Width: | Height: | Size: 532 B |
Loading…
Reference in New Issue