Version 214

This commit is contained in:
Hydrus Network Developer 2016-07-13 12:37:44 -05:00
parent 7e5baabffb
commit 2b1f40cf2a
21 changed files with 2062 additions and 1984 deletions

View File

@ -1,14 +1,15 @@
## Hydrus Image Tagging Network (Client and Server)
## Hydrus Network (Client and Server)
The hydrus network client is an application written for anon and other internet-fluent media nerds who have large image/swf collections. It browses with tags instead of folders, a little like a *booru on your desktop. Tags and files can be anonymously shared through custom servers that any user may run. Everything is free, and the source code is included with the release. It is developed for Windows, but fairly functional builds for Linux and OS X are released at the same time.
The hydrus network client is an application written for Anon and other internet-fluent media nerds who have large image/swf/webm collections. It browses with tags instead of folders, a little like a *booru on your desktop. Tags and files can be anonymously shared through custom servers that any user may run. Everything is free, and the source code is included with the release. It is developed for Windows, but fairly functional builds for Linux and OS X are released at the same time.
The software is constantly being improved. I put out a new release every Wednesday by 8pm Eastern.
I am continually working on the software and try to put out a new release every Wednesday by 8pm Eastern.
This github repository is currently a weekly sync with my home dev environment, where I work on hydrus by myself. Feel free to fork, but please don't make pull requests at this time.
This github repository is currently a weekly sync with my home dev environment, where I work on hydrus by myself. Feel free to fork, but please don't make pull requests at this time. I am also not active on Github, so if you have feedback of any sort, please email me, post on my 8chan board, or message me on tumblr or twitter.
The program can do quite a lot! Please check out the help inside the release or [here](http://hydrusnetwork.github.io/hydrus/help).
The client can do quite a lot! Please check out the help inside the release or [here](http://hydrusnetwork.github.io/hydrus/help), which includes a comprehensive getting started guide.
* [homepage](http://hydrusnetwork.github.io/hydrus/)
* [email](mailto:hydrus.admin@gmail.com)
* [8chan board](https://8ch.net/hydrus/index.html)
* [twitter](https://twitter.com/hydrusnetwork)
* [tumblr](http://hydrus.tumblr.com/)

View File

@ -8,6 +8,36 @@
<div class="content">
<h3>changelog</h3>
<ul>
<li><h3>version 214</h3></li>
<ul>
<li>wrote a new resizing dialog 'edit' class for simple resizing dialogs</li>
<li>added comprehensive frame size and position options for the new system to the gui options panel</li>
<li>moved review services frame to the new sizing system</li>
<li>windows that initialise maximised will correctly return to their last remembered size and position on a restore event</li>
<li>maximising a window by dragging it to the top edge of the screen should remember last position as the initial drag start position more reliably</li>
<li>positioning code is a little safer</li>
<li>fixed some missing recalculation of best/min size for wx.notebooks after page change</li>
<li>fixed missing recalculation of scrolledpanel's virtualsize after child wx.notebook's page change</li>
<li>fixed bad parentage for file import status frames</li>
<li>hid file import status button in manage import folders and subscriptions dialogs for non-Windows, as this is very broken, and the parentage fix wasn't enough</li>
<li>improved fuzzy padding on size calculations</li>
<li>refactored and cleaned and harmonised a bunch of the new window resizing code</li>
<li>suggested tags - favourites tag entry in options is now a live autocomplete dropdown</li>
<li>cleaned suggested tags - favourites layout in options</li>
<li>suggested tags listbox now sets its width to exactly fit its tags</li>
<li>improved workflow logic of removing/petitioning siblings and parents (shouldn't get stuck in loops as much now)</li>
<li>tag listboxes will update when tag siblings change</li>
<li>the 'auto-replace siblings' state on manage tags will no longer incorrectly apply to removal actions</li>
<li>import status caches now display errors in a more straightforward way</li>
<li>errors sent to import status caches are now also printed to the log</li>
<li>simplified how database exceptions are caught and displayed</li>
<li>database exceptions now preserve the original exception type</li>
<li>fixed db-side traceback line spacing in database exceptions</li>
<li>improved general database exception rendering</li>
<li>fixed imports for videos with negative start time</li>
<li>deleting videos from the trash that are currently rendering should be more reliable</li>
<li>crash.log now goes to the db dir, unless that isn't determined yet or is unwritable, in which case the traceback goes to console</li>
</ul>
<li><h3>version 213</h3></li>
<ul>
<li>created a new tag listbox for the new suggested tags control</li>

View File

@ -2162,7 +2162,7 @@ class TagSiblingsManager( object ):
self._lock = threading.Lock()
self._controller.sub( self, 'RefreshSiblings', 'notify_new_siblings' )
self._controller.sub( self, 'RefreshSiblings', 'notify_new_siblings_data' )
def _CollapseTags( self, tags ):

View File

@ -10,6 +10,12 @@ import wx.lib.newevent
ID_NULL = wx.NewId()
# timers
ID_TIMER_UPDATES = wx.NewId()
#
CAN_HIDE_MOUSE = True
# Hue is generally 200, Sat and Lum changes based on need

View File

@ -2295,7 +2295,8 @@ class DB( HydrusDB.HydrusDB ):
self.pub_after_commit( 'notify_new_pending' )
self.pub_after_commit( 'notify_new_siblings' )
self.pub_after_commit( 'notify_new_siblings_data' )
self.pub_after_commit( 'notify_new_siblings_gui' )
self.pub_after_commit( 'notify_new_parents' )
self.pub_service_updates_after_commit( { service_key : [ HydrusData.ServiceUpdate( HC.SERVICE_UPDATE_DELETE_PENDING ) ] } )
@ -5175,14 +5176,20 @@ class DB( HydrusDB.HydrusDB ):
HydrusData.ShowText( 'The client is running out of memory! Restart it ASAP!' )
( etype, value, tb ) = sys.exc_info()
db_traceback = os.linesep.join( traceback.format_exception( etype, value, tb ) )
new_e = HydrusExceptions.DBException( HydrusData.ToUnicode( e ), 'Unknown Caller, probably GUI.', db_traceback )
if job.IsSynchronous(): job.PutResult( new_e )
else: HydrusData.ShowException( new_e )
if job.IsSynchronous():
db_traceback = 'Database ' + traceback.format_exc()
first_line = HydrusData.ToUnicode( type( e ).__name__ ) + ': ' + HydrusData.ToUnicode( e )
new_e = HydrusExceptions.DBException( first_line, db_traceback )
job.PutResult( new_e )
else:
HydrusData.ShowException( e )
def _ProcessContentUpdatePackage( self, service_key, content_update_package, job_key ):
@ -5805,7 +5812,8 @@ class DB( HydrusDB.HydrusDB ):
if notify_new_pending: self.pub_after_commit( 'notify_new_pending' )
if notify_new_siblings:
self.pub_after_commit( 'notify_new_siblings' )
self.pub_after_commit( 'notify_new_siblings_data' )
self.pub_after_commit( 'notify_new_siblings_gui' )
self.pub_after_commit( 'notify_new_parents' )
elif notify_new_parents:
@ -7753,11 +7761,13 @@ class DB( HydrusDB.HydrusDB ):
prefixes_to_locations = dict( self._c.execute( 'SELECT prefix, location FROM client_files_locations;' ).fetchall() )
( total_to_do, ) = self._c.execute( 'SELECT COUNT( * ) FROM files_info WHERE mime IN ( ?, ? );', ( HC.IMAGE_PNG, HC.IMAGE_GIF ) ).fetchone()
for ( i, ( hash, mime ) ) in enumerate( self._c.execute( 'SELECT hash, mime FROM files_info, hashes USING ( hash_id ) WHERE mime IN ( ?, ? );', ( HC.IMAGE_PNG, HC.IMAGE_GIF ) ) ):
if ( i + 1 ) % 50 == 0:
self._controller.pub( 'splash_set_status_text', 'regenerating thumbnails: ' + HydrusData.ConvertIntToPrettyString( i + 1 ) )
self._controller.pub( 'splash_set_status_text', 'regenerating thumbnails: ' + HydrusData.ConvertValueRangeToPrettyString( i + 1, total_to_do ) )
hash_hex = hash.encode( 'hex' )

View File

@ -39,27 +39,18 @@ def CatchExceptionClient( etype, value, tb ):
try:
trace_list = traceback.format_tb( tb )
trace = ''.join( trace_list )
job_key = ClientThreading.JobKey()
if etype == HydrusExceptions.ShutdownException:
return
elif etype == HydrusExceptions.DBException:
( text, caller_traceback, db_traceback ) = value
job_key.SetVariable( 'popup_title', 'Database Error!' )
job_key.SetVariable( 'popup_text_1', text )
job_key.SetVariable( 'popup_caller_traceback', caller_traceback )
job_key.SetVariable( 'popup_db_traceback', db_traceback )
else:
trace_list = traceback.format_tb( tb )
trace = ''.join( trace_list )
if etype == wx.PyDeadObjectError:
HydrusData.Print( 'Got a PyDeadObjectError, which can probably be ignored, but here it is anyway:' )
@ -69,9 +60,11 @@ def CatchExceptionClient( etype, value, tb ):
return
first_line = HydrusData.ToUnicode( value ).split( os.linesep )[0]
try: job_key.SetVariable( 'popup_title', HydrusData.ToUnicode( etype.__name__ ) )
except: job_key.SetVariable( 'popup_title', HydrusData.ToUnicode( etype ) )
job_key.SetVariable( 'popup_text_1', HydrusData.ToUnicode( value ) )
job_key.SetVariable( 'popup_text_1', first_line )
job_key.SetVariable( 'popup_traceback', trace )
@ -258,34 +251,28 @@ def GetMediasTagCount( pool, tag_service_key = CC.COMBINED_TAG_SERVICE_KEY, coll
def ShowExceptionClient( e ):
( etype, value, tb ) = sys.exc_info()
if etype is None:
etype = type( e )
value = HydrusData.ToUnicode( e )
trace = ''.join( traceback.format_stack() )
else:
trace = ''.join( traceback.format_exception( etype, value, tb ) )
job_key = ClientThreading.JobKey()
if isinstance( e, HydrusExceptions.ShutdownException ):
return
elif isinstance( e, HydrusExceptions.DBException ):
( text, caller_traceback, db_traceback ) = e.args
job_key.SetVariable( 'popup_title', 'Database Error!' )
job_key.SetVariable( 'popup_text_1', text )
job_key.SetVariable( 'popup_caller_traceback', caller_traceback )
job_key.SetVariable( 'popup_db_traceback', db_traceback )
else:
( etype, value, tb ) = sys.exc_info()
if etype is None:
etype = type( e )
value = HydrusData.ToUnicode( e )
trace = ''.join( traceback.format_stack() )
else: trace = ''.join( traceback.format_exception( etype, value, tb ) )
if etype == wx.PyDeadObjectError:
HydrusData.Print( 'Got a PyDeadObjectError, which can probably be ignored, but here it is anyway:' )
@ -299,7 +286,10 @@ def ShowExceptionClient( e ):
else: title = HydrusData.ToUnicode( etype )
job_key.SetVariable( 'popup_title', title )
job_key.SetVariable( 'popup_text_1', HydrusData.ToUnicode( value ) )
first_line = HydrusData.ToUnicode( value ).split( os.linesep )[0]
job_key.SetVariable( 'popup_text_1', first_line )
job_key.SetVariable( 'popup_traceback', trace )
@ -457,6 +447,8 @@ class ClientOptions( HydrusSerialisable.SerialisableBase ):
self._dictionary[ 'frame_locations' ][ 'manage_tags_dialog' ] = ( False, False, None, None, ( -1, 1 ), 'topleft', False, False )
self._dictionary[ 'frame_locations' ][ 'manage_tags_frame' ] = ( False, False, None, None, ( -1, 1 ), 'topleft', False, False )
self._dictionary[ 'frame_locations' ][ 'media_viewer' ] = ( True, True, ( 640, 480 ), ( 70, 70 ), ( -1, -1 ), 'topleft', True, True )
self._dictionary[ 'frame_locations' ][ 'regular_dialog' ] = ( False, False, None, None, ( -1, -1 ), 'topleft', False, False )
self._dictionary[ 'frame_locations' ][ 'review_services' ] = ( False, True, None, None, ( -1, -1 ), 'topleft', False, False )
#
@ -621,6 +613,11 @@ class ClientOptions( HydrusSerialisable.SerialisableBase ):
return self._dictionary[ 'frame_locations' ][ frame_key ]
def GetFrameLocations( self ):
return self._dictionary[ 'frame_locations' ].items()
def GetNoneableInteger( self, name ):
with self._lock:
@ -1875,7 +1872,8 @@ class ServiceRepository( ServiceRestricted ):
finally:
HydrusGlobals.client_controller.pub( 'notify_new_pending' )
HydrusGlobals.client_controller.pub( 'notify_new_siblings' )
HydrusGlobals.client_controller.pub( 'notify_new_siblings_data' )
HydrusGlobals.client_controller.pub( 'notify_new_siblings_gui' )
HydrusGlobals.client_controller.pub( 'notify_new_parents' )

File diff suppressed because it is too large Load Diff

View File

@ -8,6 +8,7 @@ import ClientGUIDialogs
import ClientGUIDialogsManage
import ClientGUIHoverFrames
import ClientGUIPanels
import ClientGUITopLevelWindows
import ClientMedia
import ClientRatings
import collections
@ -898,7 +899,7 @@ class Canvas( wx.Window ):
title = 'manage tags'
frame_key = 'manage_tags_frame'
manage_tags = ClientGUICommon.FrameThatResizesAndTakesPanel( self, title, frame_key )
manage_tags = ClientGUITopLevelWindows.FrameThatTakesScrollablePanel( self, title, frame_key )
panel = ClientGUIPanels.ManageTagsPanel( manage_tags, self._file_service_key, ( self._current_display_media, ), immediate_commit = True, canvas_key = self._canvas_key )
@ -1697,11 +1698,11 @@ class CanvasPanel( Canvas ):
class CanvasFrame( ClientGUICommon.FrameThatResizes ):
class CanvasFrame( ClientGUITopLevelWindows.FrameThatResizes ):
def __init__( self, parent ):
ClientGUICommon.FrameThatResizes.__init__( self, parent, 'hydrus client media viewer', 'media_viewer', float_on_parent = False )
ClientGUITopLevelWindows.FrameThatResizes.__init__( self, parent, 'hydrus client media viewer', 'media_viewer', float_on_parent = False )
def Close( self ):
@ -1736,7 +1737,7 @@ class CanvasFrame( ClientGUICommon.FrameThatResizes ):
self.SetSizer( vbox )
self._SetSizeAndPosition( self._frame_key )
ClientGUITopLevelWindows.SetTLWSizeAndPosition( self, self._frame_key )
self.Show( True )

View File

@ -1800,370 +1800,6 @@ class FitResistantStaticText( wx.StaticText ):
class Frame( wx.Frame ):
def __init__( self, parent, title, float_on_parent = True ):
HydrusGlobals.client_controller.ResetIdleTimer()
if parent is None:
pos = wx.DefaultPosition
style = wx.DEFAULT_FRAME_STYLE
else:
if isinstance( parent, wx.TopLevelWindow ):
parent_tlp = parent
else:
parent_tlp = parent.GetTopLevelParent()
( tlp_x, tlp_y ) = parent_tlp.GetPositionTuple()
pos = ( tlp_x + 50, tlp_y + 50 )
if float_on_parent:
style = wx.DEFAULT_FRAME_STYLE | wx.FRAME_FLOAT_ON_PARENT
else:
style = wx.DEFAULT_FRAME_STYLE
wx.Frame.__init__( self, parent, title = title, pos = pos, style = style )
self._new_options = HydrusGlobals.client_controller.GetNewOptions()
self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ) )
self.SetIcon( wx.Icon( os.path.join( HC.STATIC_DIR, 'hydrus.ico' ), wx.BITMAP_TYPE_ICO ) )
def _ExpandSizeIfPossible( self, frame_key, desired_size_delta ):
( remember_size, remember_position, last_size, last_position, default_gravity, default_position, maximised, fullscreen ) = self._new_options.GetFrameLocation( frame_key )
parent = self.GetParent()
if not self.IsMaximized() and not self.IsFullScreen():
( current_width, current_height ) = self.GetSize()
( desired_delta_width, desired_delta_height ) = desired_size_delta
min_width = current_width + desired_delta_width
min_height = current_height + desired_delta_height
if parent is None:
width = min_width + 20
height = min_height + 20
else:
( parent_window_width, parent_window_height ) = self.GetParent().GetTopLevelParent().GetSize()
max_width = parent_window_width - 100
max_height = parent_window_height - 100
( width_gravity, height_gravity ) = default_gravity
if width_gravity == -1:
width = min_width
else:
width = int( width_gravity * max_width )
if height_gravity == -1:
height = min_height
else:
height = int( height_gravity * max_height )
if width > current_width or height > current_height:
self.SetSize( ( width, height ) )
def _GetSafePosition( self, position ):
( p_x, p_y ) = position
# some window managers size the windows just off screen to cut off borders
( test_x, test_y ) = ( p_x + 20, p_y + 20 )
display_index = wx.Display.GetFromPoint( ( test_x, test_y ) )
if display_index == wx.NOT_FOUND:
return wx.DefaultPosition
else:
display = wx.Display( display_index )
geometry = display.GetGeometry()
x_bad = test_x < geometry.x or test_x > geometry.x + geometry.width
y_bad = test_y < geometry.y or test_y > geometry.y + geometry.height
if x_bad or y_bad:
return wx.DefaultPosition
else:
return position
def _SaveSizeAndPosition( self, frame_key ):
( remember_size, remember_position, last_size, last_position, default_gravity, default_position, maximised, fullscreen ) = self._new_options.GetFrameLocation( frame_key )
maximised = self.IsMaximized()
fullscreen = self.IsFullScreen()
if not ( maximised or fullscreen ):
# when dragging window up to be maximised, reported position is sometimes a bit dodgy
# so, filter out the invalid answers
if self._GetSafePosition( self.GetPositionTuple() ) != wx.DefaultPosition:
last_size = self.GetSizeTuple()
last_position = self._GetSafePosition( self.GetPositionTuple() )
self._new_options.SetFrameLocation( frame_key, remember_size, remember_position, last_size, last_position, default_gravity, default_position, maximised, fullscreen )
def _SetSizeAndPosition( self, frame_key ):
( remember_size, remember_position, last_size, last_position, default_gravity, default_position, maximised, fullscreen ) = self._new_options.GetFrameLocation( frame_key )
parent = self.GetParent()
if remember_size and last_size is not None:
( width, height ) = last_size
else:
( min_width, min_height ) = self.GetEffectiveMinSize()
min_width += 30
min_height += 30
if parent is None:
width = min_width
height = min_height
else:
( parent_window_width, parent_window_height ) = self.GetParent().GetTopLevelParent().GetSize()
max_width = parent_window_width - 100
max_height = parent_window_height - 100
( width_gravity, height_gravity ) = default_gravity
if width_gravity == -1:
width = min_width
else:
width = int( width_gravity * max_width )
if height_gravity == -1:
height = min_height
else:
height = int( height_gravity * max_height )
self.SetInitialSize( ( width, height ) )
if maximised:
self.Maximize()
if fullscreen:
wx.CallAfter( self.ShowFullScreen, True, wx.FULLSCREEN_ALL )
#
pos = None
if remember_position and last_position is not None:
pos = last_position
elif default_position == 'topleft' and parent is not None:
if isinstance( parent, wx.TopLevelWindow ):
parent_tlp = parent
else:
parent_tlp = parent.GetTopLevelParent()
( pos_x, pos_y ) = parent_tlp.GetPositionTuple()
pos = ( pos_x + 50, pos_y + 50 )
elif default_position == 'center':
wx.CallAfter( self.Center )
if pos is not None:
pos = self._GetSafePosition( pos )
self.SetPosition( pos )
def SetInitialSize( self, ( width, height ) ):
( display_width, display_height ) = wx.GetDisplaySize()
width = min( display_width, width )
height = min( display_height, height )
wx.Frame.SetInitialSize( self, ( width, height ) )
min_width = min( 240, width )
min_height = min( 180, height )
self.SetMinSize( ( min_width, min_height ) )
class FrameThatResizes( Frame ):
def __init__( self, parent, title, frame_key, float_on_parent = True ):
self._frame_key = frame_key
Frame.__init__( self, parent, title, float_on_parent )
self.Bind( wx.EVT_SIZE, self.EventSizeInfoChanged )
self.Bind( wx.EVT_MOVE, self.EventSizeInfoChanged )
self.Bind( wx.EVT_CLOSE, self.EventSizeInfoChanged )
self.Bind( wx.EVT_MAXIMIZE, self.EventSizeInfoChanged )
def EventSizeInfoChanged( self, event ):
self._SaveSizeAndPosition( self._frame_key )
event.Skip()
class FrameThatResizesAndTakesPanel( FrameThatResizes ):
def __init__( self, parent, title, frame_key, float_on_parent = True ):
self._panel = None
FrameThatResizes.__init__( self, parent, title, frame_key, float_on_parent )
self._ok = wx.Button( self, label = 'close' )
self._ok.Bind( wx.EVT_BUTTON, self.EventCloseButton )
self.Bind( wx.EVT_MENU, self.EventMenu )
self.Bind( CC.EVT_SIZE_CHANGED, self.EventChildSizeChanged )
def EventMenu( self, event ):
action = ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetAction( event.GetId() )
if action is not None:
( command, data ) = action
if command == 'ok':
self.Close()
else:
event.Skip()
def EventCloseButton( self, event ):
self.Close()
def EventChildSizeChanged( self, event ):
if self._panel is not None:
( current_panel_width, current_panel_height ) = self._panel.GetSize()
( desired_panel_width, desired_panel_height ) = self._panel.GetVirtualSize()
desired_delta_width = max( 0, desired_panel_width - current_panel_width )
desired_delta_height = max( 0, desired_panel_height - current_panel_height )
if desired_delta_width > 0 or desired_delta_height > 0:
self._ExpandSizeIfPossible( self._frame_key, ( desired_delta_width, desired_delta_height ) )
def SetPanel( self, panel ):
self._panel = panel
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( self._panel, CC.FLAGS_EXPAND_BOTH_WAYS )
vbox.AddF( self._ok, CC.FLAGS_LONE_BUTTON )
self.SetSizer( vbox )
self._SetSizeAndPosition( self._frame_key )
self.Show( True )
self._panel.SetupScrolling()
class Gauge( wx.Gauge ):
def __init__( self, *args, **kwargs ):
@ -2613,6 +2249,7 @@ class ListBook( wx.Panel ):
class ListBox( wx.ScrolledWindow ):
TEXT_X_PADDING = 3
delete_key_activates = False
def __init__( self, parent, min_height = 250 ):
@ -2888,7 +2525,7 @@ class ListBox( wx.ScrolledWindow ):
dc.SetTextForeground( text_colour )
( x, y ) = ( 3, i * self._text_y )
( x, y ) = ( self.TEXT_X_PADDING, i * self._text_y )
dc.DrawText( text, x, y )
@ -3074,6 +2711,11 @@ class ListBox( wx.ScrolledWindow ):
else: return self._strings_to_terms[ s ]
def GetIdealHeight( self ):
return self._text_y * len( self._ordered_strings ) + 20
def SetTexts( self, ordered_strings ):
if ordered_strings != self._ordered_strings:
@ -3101,6 +2743,8 @@ class ListBoxTags( ListBox ):
self.Bind( wx.EVT_MIDDLE_DOWN, self.EventMouseMiddleClick )
self.Bind( wx.EVT_MENU, self.EventMenu )
HydrusGlobals.client_controller.sub( self, 'SiblingsHaveChanged', 'notify_new_siblings_gui' )
def _GetNamespaceColours( self ): return HC.options[ 'namespace_colours' ]
@ -3406,6 +3050,11 @@ class ListBoxTags( ListBox ):
return self._selected_terms
def SiblingsHaveChanged( self ):
pass
class ListBoxTagsAutocompleteDropdown( ListBoxTags ):
has_counts = True
@ -3847,6 +3496,11 @@ class ListBoxTagsStrings( ListBoxTags ):
self._RecalcTags()
def SiblingsHaveChanged( self ):
self._RecalcTags()
class ListBoxTagsStringsAddRemove( ListBoxTagsStrings ):
def __init__( self, parent, removed_callable = None, show_sibling_text = True ):
@ -3947,6 +3601,22 @@ class ListBoxTagsSuggestions( ListBoxTagsStrings ):
def SetTags( self, tags ):
ListBoxTagsStrings.SetTags( self, tags )
if len( tags ) > 0:
dc = wx.MemoryDC( self._client_bmp )
dc.SetFont( wx.SystemSettings.GetFont( wx.SYS_DEFAULT_GUI_FONT ) )
width = max( ( dc.GetTextExtent( s )[0] for s in self._ordered_strings ) )
self.SetMinClientSize( ( width + 2 * self.TEXT_X_PADDING, -1 ) )
class ListBoxTagsPredicates( ListBoxTags ):
delete_key_activates = True
@ -4396,6 +4066,11 @@ class ListBoxTagsSelection( ListBoxTags ):
self._last_media = media
def SiblingsHaveChanged( self ):
self.SetTagsByMedia( self._last_media, force_reload = True )
class ListBoxTagsSelectionHoverFrame( ListBoxTagsSelection ):
def __init__( self, parent, canvas_key ):
@ -6271,7 +5946,7 @@ class SeedCacheControl( SaneListCtrl ):
pretty_status = CC.status_string_lookup[ status ]
pretty_added = HydrusData.ConvertTimestampToPrettyAgo( added_timestamp )
pretty_modified = HydrusData.ConvertTimestampToPrettyAgo( last_modified_timestamp )
pretty_note = note
pretty_note = note.split( os.linesep )[0]
return ( pretty_seed, pretty_status, pretty_added, pretty_modified, pretty_note )
@ -6813,75 +6488,6 @@ class RadioBox( StaticBox ):
def SetString( self, index, text ): self._indices_to_radio_buttons[ index ].SetLabelText( text )
class ShowKeys( Frame ):
def __init__( self, key_type, keys ):
if key_type == 'registration': title = 'Registration Keys'
elif key_type == 'access': title = 'Access Keys'
# give it no parent, so this doesn't close when the dialog is closed!
Frame.__init__( self, None, HydrusGlobals.client_controller.PrepStringForDisplay( title ) )
self._key_type = key_type
self._keys = keys
#
self._text_ctrl = wx.TextCtrl( self, style = wx.TE_MULTILINE | wx.TE_READONLY | wx.TE_DONTWRAP )
self._save_to_file = wx.Button( self, label = 'save to file' )
self._save_to_file.Bind( wx.EVT_BUTTON, self.EventSaveToFile )
self._done = wx.Button( self, id = wx.ID_OK, label = 'done' )
self._done.Bind( wx.EVT_BUTTON, self.EventDone )
#
if key_type == 'registration': prepend = 'r'
else: prepend = ''
self._text = os.linesep.join( [ prepend + key.encode( 'hex' ) for key in self._keys ] )
self._text_ctrl.SetValue( self._text )
#
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( self._text_ctrl, CC.FLAGS_EXPAND_BOTH_WAYS )
vbox.AddF( self._save_to_file, CC.FLAGS_LONE_BUTTON )
vbox.AddF( self._done, CC.FLAGS_LONE_BUTTON )
self.SetSizer( vbox )
( x, y ) = self.GetEffectiveMinSize()
if x < 500: x = 500
if y < 200: y = 200
self.SetInitialSize( ( x, y ) )
self.Show( True )
def EventDone( self, event ): self.Close()
def EventSaveToFile( self, event ):
filename = 'keys.txt'
with wx.FileDialog( None, style=wx.FD_SAVE, defaultFile = filename ) as dlg:
if dlg.ShowModal() == wx.ID_OK:
path = HydrusData.ToUnicode( dlg.GetPath() )
with open( path, 'wb' ) as f: f.write( HydrusData.ToByteString( self._text ) )
class WaitingPolitelyStaticText( wx.StaticText ):
def __init__( self, parent, page_key ):

View File

@ -18,6 +18,7 @@ import ClientFiles
import ClientGUICommon
import ClientGUICollapsible
import ClientGUIPredicates
import ClientGUITopLevelWindows
import ClientThreading
import collections
import gc
@ -301,199 +302,6 @@ class Dialog( wx.Dialog ):
HydrusGlobals.client_controller.ResetIdleTimer()
def _ExpandSizeIfPossible( self, frame_key, desired_size_delta ):
( remember_size, remember_position, last_size, last_position, default_gravity, default_position, maximised, fullscreen ) = self._new_options.GetFrameLocation( frame_key )
parent = self.GetParent()
if not self.IsMaximized() and not self.IsFullScreen():
( current_width, current_height ) = self.GetSize()
( desired_delta_width, desired_delta_height ) = desired_size_delta
min_width = current_width + desired_delta_width
min_height = current_height + desired_delta_height
if parent is None:
width = min_width + 20
height = min_height + 20
else:
( parent_window_width, parent_window_height ) = self.GetParent().GetTopLevelParent().GetSize()
max_width = parent_window_width - 100
max_height = parent_window_height - 100
( width_gravity, height_gravity ) = default_gravity
if width_gravity == -1:
width = min_width
else:
width = int( width_gravity * max_width )
if height_gravity == -1:
height = min_height
else:
height = int( height_gravity * max_height )
if width > current_width or height > current_height:
self.SetSize( ( width, height ) )
def _GetSafePosition( self, position ):
display_index = wx.Display.GetFromPoint( position )
if display_index == wx.NOT_FOUND:
position = wx.DefaultPosition
else:
display = wx.Display( display_index )
geometry = display.GetGeometry()
( p_x, p_y ) = position
x_bad = p_x < geometry.x or p_x > geometry.x + geometry.width
y_bad = p_y < geometry.y or p_y > geometry.y + geometry.height
if x_bad or y_bad:
position = wx.DefaultPosition
return position
def _SaveSizeAndPosition( self, frame_key ):
( remember_size, remember_position, last_size, last_position, default_gravity, default_position, maximised, fullscreen ) = self._new_options.GetFrameLocation( frame_key )
last_size = self.GetSizeTuple()
last_position = self._GetSafePosition( self.GetPositionTuple() )
self._new_options.SetFrameLocation( frame_key, remember_size, remember_position, last_size, last_position, default_gravity, default_position, maximised, fullscreen )
def _SetSizeAndPosition( self, frame_key ):
( remember_size, remember_position, last_size, last_position, default_gravity, default_position, maximised, fullscreen ) = self._new_options.GetFrameLocation( frame_key )
parent = self.GetParent()
if remember_size and last_size is not None:
( width, height ) = last_size
else:
( min_width, min_height ) = self.GetEffectiveMinSize()
min_width += 30
min_height += 30
if parent is None:
width = min_width
height = min_height
else:
( parent_window_width, parent_window_height ) = self.GetParent().GetTopLevelParent().GetSize()
max_width = parent_window_width - 100
max_height = parent_window_height - 100
( width_gravity, height_gravity ) = default_gravity
if width_gravity == -1:
width = min_width
else:
width = int( width_gravity * max_width )
if height_gravity == -1:
height = min_height
else:
height = int( height_gravity * max_height )
self.SetInitialSize( ( width, height ) )
if maximised:
self.Maximize()
if fullscreen:
wx.CallAfter( self.ShowFullScreen, True, wx.FULLSCREEN_ALL )
#
pos = None
if remember_position and last_position is not None:
pos = last_position
elif default_position == 'topleft' and parent is not None:
if isinstance( parent, wx.TopLevelWindow ):
parent_tlp = parent
else:
parent_tlp = parent.GetTopLevelParent()
( pos_x, pos_y ) = parent_tlp.GetPositionTuple()
pos = ( pos_x + 50, pos_y + 50 )
elif default_position == 'center':
wx.CallAfter( self.Center )
if pos is not None:
pos = self._GetSafePosition( pos )
self.SetPosition( pos )
def EventDialogButton( self, event ): self.EndModal( event.GetId() )
def SetInitialSize( self, ( width, height ) ):
@ -511,92 +319,6 @@ class Dialog( wx.Dialog ):
self.SetMinSize( ( min_width, min_height ) )
class DialogManageApply( Dialog ):
def __init__( self, parent, title, frame_key ):
self._frame_key = frame_key
self._panel = None
Dialog.__init__( self, parent, title )
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.Bind( wx.EVT_MENU, self.EventMenu )
self.Bind( CC.EVT_SIZE_CHANGED, self.EventChildSizeChanged )
def EventChildSizeChanged( self, event ):
if self._panel is not None:
( current_panel_width, current_panel_height ) = self._panel.GetSize()
( desired_panel_width, desired_panel_height ) = self._panel.GetVirtualSize()
desired_delta_width = max( 0, desired_panel_width - current_panel_width )
desired_delta_height = max( 0, desired_panel_height - current_panel_height )
if desired_delta_width > 0 or desired_delta_height > 0:
self._ExpandSizeIfPossible( self._frame_key, ( desired_delta_width, desired_delta_height ) )
def EventMenu( self, event ):
action = ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetAction( event.GetId() )
if action is not None:
( command, data ) = action
if command == 'ok':
self.EventOk( None )
else:
event.Skip()
def EventOk( self, event ):
self._panel.CommitChanges()
self._SaveSizeAndPosition( self._frame_key )
self.EndModal( wx.ID_OK )
def SetPanel( self, panel ):
self._panel = panel
buttonbox = wx.BoxSizer( wx.HORIZONTAL )
buttonbox.AddF( self._apply, CC.FLAGS_MIXED )
buttonbox.AddF( self._cancel, CC.FLAGS_MIXED )
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( self._panel, CC.FLAGS_EXPAND_BOTH_WAYS )
vbox.AddF( buttonbox, CC.FLAGS_BUTTON_SIZER )
self.SetSizer( vbox )
self._SetSizeAndPosition( self._frame_key )
self._panel.SetupScrolling()
class DialogAdvancedContentUpdate( Dialog ):
COPY = 0
@ -1095,7 +817,7 @@ class DialogGenerateNewAccounts( Dialog ):
registration_keys = response[ 'registration_keys' ]
ClientGUICommon.ShowKeys( 'registration', registration_keys )
ClientGUITopLevelWindows.ShowKeys( 'registration', registration_keys )
finally: self.EndModal( wx.ID_OK )

View File

@ -2721,6 +2721,11 @@ class DialogManageImportFoldersEdit( ClientGUIDialogs.Dialog ):
self._seed_cache_button.Bind( wx.EVT_BUTTON, self.EventSeedCache )
self._seed_cache_button.SetToolTipString( 'open detailed file import status' )
if not HC.PLATFORM_WINDOWS:
self._seed_cache_button.Hide()
#
self._file_box = ClientGUICommon.StaticBox( self._panel, 'file options' )
@ -3058,7 +3063,9 @@ class DialogManageImportFoldersEdit( ClientGUIDialogs.Dialog ):
seed_cache = self._import_folder.GetSeedCache()
HydrusGlobals.client_controller.pub( 'show_seed_cache', seed_cache )
import ClientGUI
ClientGUI.FrameSeedCache( self, HydrusGlobals.client_controller, seed_cache )
def GetInfo( self ):
@ -5352,6 +5359,11 @@ class DialogManageSubscriptions( ClientGUIDialogs.Dialog ):
self._seed_cache_button.Bind( wx.EVT_BUTTON, self.EventSeedCache )
self._seed_cache_button.SetToolTipString( 'open detailed url cache status' )
if not HC.PLATFORM_WINDOWS:
self._seed_cache_button.Hide()
self._reset_cache_button = wx.Button( self._info_panel, label = ' reset url cache on dialog ok ' )
self._reset_cache_button.Bind( wx.EVT_BUTTON, self.EventResetCache )
@ -5565,7 +5577,9 @@ class DialogManageSubscriptions( ClientGUIDialogs.Dialog ):
seed_cache = self._original_subscription.GetSeedCache()
HydrusGlobals.client_controller.pub( 'show_seed_cache', seed_cache )
import ClientGUI
ClientGUI.FrameSeedCache( self, HydrusGlobals.client_controller, seed_cache )
def EventSiteChanged( self, event ): self._PresentForSiteType()
@ -6179,7 +6193,7 @@ class DialogManageTagParents( ClientGUIDialogs.Dialog ):
if potential_child == potential_parent: return False
current_pairs = self._current_statuses_to_pairs[ HC.CURRENT ].union( self._current_statuses_to_pairs[ HC.PENDING ] )
current_pairs = self._current_statuses_to_pairs[ HC.CURRENT ].union( self._current_statuses_to_pairs[ HC.PENDING ] ).difference( self._current_statuses_to_pairs[ HC.PETITIONED ] )
current_children = { child for ( child, parent ) in current_pairs }
@ -6770,7 +6784,7 @@ class DialogManageTagSiblings( ClientGUIDialogs.Dialog ):
( potential_old, potential_new ) = potential_pair
current_pairs = self._current_statuses_to_pairs[ HC.CURRENT ].union( self._current_statuses_to_pairs[ HC.PENDING ] )
current_pairs = self._current_statuses_to_pairs[ HC.CURRENT ].union( self._current_statuses_to_pairs[ HC.PENDING ] ).difference( self._current_statuses_to_pairs[ HC.PETITIONED ] )
current_olds = { old for ( old, new ) in current_pairs }
@ -6862,7 +6876,7 @@ class DialogManageTagSiblings( ClientGUIDialogs.Dialog ):
do_it = True
current_pairs = self._current_statuses_to_pairs[ HC.CURRENT ].union( self._current_statuses_to_pairs[ HC.PENDING ] )
current_pairs = self._current_statuses_to_pairs[ HC.CURRENT ].union( self._current_statuses_to_pairs[ HC.PENDING ] ).difference( self._current_statuses_to_pairs[ HC.PETITIONED ] )
current_olds = { current_old for ( current_old, current_new ) in current_pairs }
@ -6890,7 +6904,7 @@ class DialogManageTagSiblings( ClientGUIDialogs.Dialog ):
current_pairs = self._current_statuses_to_pairs[ HC.CURRENT ].union( self._current_statuses_to_pairs[ HC.PENDING ] )
current_pairs = self._current_statuses_to_pairs[ HC.CURRENT ].union( self._current_statuses_to_pairs[ HC.PENDING ] ).difference( self._current_statuses_to_pairs[ HC.PETITIONED ] )
current_olds = { current_old for ( current_old, current_new ) in current_pairs }

View File

@ -1822,7 +1822,9 @@ class ManagementPanelGalleryImport( ManagementPanel ):
seed_cache = self._gallery_import.GetSeedCache()
self._controller.pub( 'show_seed_cache', seed_cache )
import ClientGUI
ClientGUI.FrameSeedCache( self, HydrusGlobals.client_controller, seed_cache )
def SetSearchFocus( self, page_key ):
@ -1963,7 +1965,9 @@ class ManagementPanelHDDImport( ManagementPanel ):
seed_cache = self._hdd_import.GetSeedCache()
self._controller.pub( 'show_seed_cache', seed_cache )
import ClientGUI
ClientGUI.FrameSeedCache( self, HydrusGlobals.client_controller, seed_cache )
def TestAbleToClose( self ):
@ -2333,7 +2337,9 @@ class ManagementPanelPageOfImagesImport( ManagementPanel ):
seed_cache = self._page_of_images_import.GetSeedCache()
self._controller.pub( 'show_seed_cache', seed_cache )
import ClientGUI
ClientGUI.FrameSeedCache( self, HydrusGlobals.client_controller, seed_cache )
def SetSearchFocus( self, page_key ):
@ -3129,7 +3135,9 @@ class ManagementPanelThreadWatcherImport( ManagementPanel ):
seed_cache = self._thread_watcher_import.GetSeedCache()
self._controller.pub( 'show_seed_cache', seed_cache )
import ClientGUI
ClientGUI.FrameSeedCache( self, HydrusGlobals.client_controller, seed_cache )
def EventTimesToCheck( self, event ):

View File

@ -8,6 +8,7 @@ import ClientGUIDialogs
import ClientGUIDialogsManage
import ClientGUICanvas
import ClientGUIPanels
import ClientGUITopLevelWindows
import ClientMedia
import collections
import HydrusExceptions
@ -386,6 +387,11 @@ class MediaPanel( ClientMedia.ListeningMediaList, wx.ScrolledWindow ):
if file_service_key in local_file_services:
if file_service_key == CC.TRASH_SERVICE_KEY:
self._SetFocussedMedia( None )
HydrusGlobals.client_controller.Write( 'content_updates', { file_service_key : [ HydrusData.ContentUpdate( HC.CONTENT_TYPE_FILES, HC.CONTENT_UPDATE_DELETE, hashes ) ] } )
else:
@ -785,7 +791,7 @@ class MediaPanel( ClientMedia.ListeningMediaList, wx.ScrolledWindow ):
title = 'manage tags for ' + HydrusData.ConvertIntToPrettyString( num_files ) + ' files'
frame_key = 'manage_tags_dialog'
with ClientGUIDialogs.DialogManageApply( self, title, frame_key ) as dlg:
with ClientGUITopLevelWindows.DialogManage( self, title, frame_key ) as dlg:
panel = ClientGUIPanels.ManageTagsPanel( dlg, self._file_service_key, self._selected_media )

View File

@ -1399,14 +1399,14 @@ class MessagePanel( wx.Panel ):
# here starts the message reboot code
class IMFrame( ClientGUICommon.Frame ):
class IMFrame( ClientGUITopLevelWindows.Frame ):
def __init__( self, parent, me_account, them_account, context ):
me_name = me_account.GetNameBlah()
them_name = them_account.GetNameBlah()
ClientGUICommon.Frame.__init__( self, parent, title = me_name + ' talking to ' + them_name )
ClientGUITopLevelWindows.Frame.__init__( self, parent, title = me_name + ' talking to ' + them_name )
self._me_label = MeLabel( self, me_account ) # maybe these two should be the same, and infer me/them status itself
self._them_label = ThemLabel( self, them_account )

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,536 @@
import ClientCaches
import ClientConstants as CC
import HydrusConstants as HC
import HydrusData
import HydrusGlobals
import os
import wx
CHILD_POSITION_PADDING = 50
FUZZY_PADDING = 30
def GetSafePosition( position ):
( p_x, p_y ) = position
# some window managers size the windows just off screen to cut off borders
# so choose a test position that's a little more lenient
( test_x, test_y ) = ( p_x + FUZZY_PADDING, p_y + FUZZY_PADDING )
display_index = wx.Display.GetFromPoint( ( test_x, test_y ) )
if display_index == wx.NOT_FOUND:
return wx.DefaultPosition
else:
return position
def GetSafeSize( tlw, min_size, gravity ):
( min_width, min_height ) = min_size
parent = tlw.GetParent()
if parent is None:
width = min_width
height = min_height
else:
( parent_window_width, parent_window_height ) = parent.GetTopLevelParent().GetSize()
max_width = parent_window_width - 2 * CHILD_POSITION_PADDING
max_height = parent_window_height - 2 * CHILD_POSITION_PADDING
( width_gravity, height_gravity ) = gravity
if width_gravity == -1:
width = min_width
else:
width = int( width_gravity * max_width )
if height_gravity == -1:
height = min_height
else:
height = int( height_gravity * max_height )
( display_width, display_height ) = wx.GetDisplaySize()
width = min( display_width, width )
height = min( display_height, height )
return ( width, height )
def ExpandTLWIfPossible( tlw, frame_key, desired_size_delta ):
new_options = HydrusGlobals.client_controller.GetNewOptions()
( remember_size, remember_position, last_size, last_position, default_gravity, default_position, maximised, fullscreen ) = new_options.GetFrameLocation( frame_key )
if not tlw.IsMaximized() and not tlw.IsFullScreen():
( current_width, current_height ) = tlw.GetSize()
( desired_delta_width, desired_delta_height ) = desired_size_delta
min_width = current_width + desired_delta_width + FUZZY_PADDING
min_height = current_height + desired_delta_height + FUZZY_PADDING
( width, height ) = GetSafeSize( tlw, ( min_width, min_height ), default_gravity )
if width > current_width or height > current_height:
tlw.SetSize( ( width, height ) )
def SaveTLWSizeAndPosition( tlw, frame_key ):
new_options = HydrusGlobals.client_controller.GetNewOptions()
( remember_size, remember_position, last_size, last_position, default_gravity, default_position, maximised, fullscreen ) = new_options.GetFrameLocation( frame_key )
maximised = tlw.IsMaximized()
fullscreen = tlw.IsFullScreen()
if not ( maximised or fullscreen ):
safe_position = GetSafePosition( tlw.GetPositionTuple() )
if safe_position != wx.DefaultPosition:
last_size = tlw.GetSizeTuple()
last_position = safe_position
new_options.SetFrameLocation( frame_key, remember_size, remember_position, last_size, last_position, default_gravity, default_position, maximised, fullscreen )
def SetTLWSizeAndPosition( tlw, frame_key ):
new_options = HydrusGlobals.client_controller.GetNewOptions()
( remember_size, remember_position, last_size, last_position, default_gravity, default_position, maximised, fullscreen ) = new_options.GetFrameLocation( frame_key )
parent = tlw.GetParent()
if remember_size and last_size is not None:
( width, height ) = last_size
else:
( min_width, min_height ) = tlw.GetEffectiveMinSize()
min_width += FUZZY_PADDING
min_height += FUZZY_PADDING
( width, height ) = GetSafeSize( tlw, ( min_width, min_height ), default_gravity )
tlw.SetInitialSize( ( width, height ) )
min_width = min( 240, width )
min_height = min( 240, height )
tlw.SetMinSize( ( min_width, min_height ) )
#
if remember_position and last_position is not None:
safe_position = GetSafePosition( last_position )
tlw.SetPosition( safe_position )
elif default_position == 'topleft':
if parent is not None:
if isinstance( parent, wx.TopLevelWindow ):
parent_tlp = parent
else:
parent_tlp = parent.GetTopLevelParent()
( parent_x, parent_y ) = parent_tlp.GetPositionTuple()
tlw.SetPosition( ( parent_x + CHILD_POSITION_PADDING, parent_y + CHILD_POSITION_PADDING ) )
else:
safe_position = GetSafePosition( ( 0 + CHILD_POSITION_PADDING, 0 + CHILD_POSITION_PADDING ) )
tlw.SetPosition( safe_position )
elif default_position == 'center':
wx.CallAfter( tlw.Center )
# if these aren't callafter, the size and pos calls don't stick if a restore event happens
if maximised:
wx.CallAfter( tlw.Maximize )
if fullscreen:
wx.CallAfter( tlw.ShowFullScreen, True, wx.FULLSCREEN_ALL )
class NewDialog( wx.Dialog ):
def __init__( self, parent, title ):
style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER
if not HC.PLATFORM_LINUX and parent is not None:
style |= wx.FRAME_FLOAT_ON_PARENT
wx.Dialog.__init__( self, parent, title = title, style = style )
self._new_options = HydrusGlobals.client_controller.GetNewOptions()
self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ) )
self.SetIcon( wx.Icon( os.path.join( HC.STATIC_DIR, 'hydrus.ico' ), wx.BITMAP_TYPE_ICO ) )
self.Bind( wx.EVT_BUTTON, self.EventDialogButton )
HydrusGlobals.client_controller.ResetIdleTimer()
def EventDialogButton( self, event ): self.EndModal( event.GetId() )
class DialogThatResizes( NewDialog ):
def __init__( self, parent, title, frame_key ):
self._frame_key = frame_key
NewDialog.__init__( self, parent, title )
class DialogThatTakesScrollablePanel( DialogThatResizes ):
def __init__( self, parent, title, frame_key ):
self._panel = None
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.Bind( wx.EVT_MENU, self.EventMenu )
self.Bind( CC.EVT_SIZE_CHANGED, self.EventChildSizeChanged )
def EventChildSizeChanged( self, event ):
if self._panel is not None:
# the min size here is to compensate for wx.Notebook and anything else that don't update virtualsize on page change
( current_panel_width, current_panel_height ) = self._panel.GetSize()
( desired_panel_width, desired_panel_height ) = self._panel.GetVirtualSize()
( min_panel_width, min_panel_height ) = self._panel.GetEffectiveMinSize()
desired_delta_width = max( 0, desired_panel_width - current_panel_width, min_panel_width - current_panel_width )
desired_delta_height = max( 0, desired_panel_height - current_panel_height, min_panel_height - current_panel_height )
if desired_delta_width > 0 or desired_delta_height > 0:
ExpandTLWIfPossible( self, self._frame_key, ( desired_delta_width, desired_delta_height ) )
def EventMenu( self, event ):
action = ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetAction( event.GetId() )
if action is not None:
( command, data ) = action
if command == 'ok':
self.EventOk( None )
else:
event.Skip()
def EventOk( self, event ):
raise NotImplementedError()
def SetPanel( self, panel ):
self._panel = panel
buttonbox = wx.BoxSizer( wx.HORIZONTAL )
buttonbox.AddF( self._apply, CC.FLAGS_MIXED )
buttonbox.AddF( self._cancel, CC.FLAGS_MIXED )
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( self._panel, CC.FLAGS_EXPAND_BOTH_WAYS )
vbox.AddF( buttonbox, CC.FLAGS_BUTTON_SIZER )
self.SetSizer( vbox )
SetTLWSizeAndPosition( self, self._frame_key )
self._panel.SetupScrolling()
class DialogEdit( DialogThatTakesScrollablePanel ):
def __init__( self, parent, title ):
DialogThatTakesScrollablePanel.__init__( self, parent, title, 'regular_dialog' )
def EventOk( self, event ):
SaveTLWSizeAndPosition( self, self._frame_key )
self.EndModal( wx.ID_OK )
class DialogManage( DialogThatTakesScrollablePanel ):
def EventOk( self, event ):
self._panel.CommitChanges()
SaveTLWSizeAndPosition( self, self._frame_key )
self.EndModal( wx.ID_OK )
class Frame( wx.Frame ):
def __init__( self, parent, title, float_on_parent = True ):
style = wx.DEFAULT_FRAME_STYLE
if float_on_parent:
style |= wx.FRAME_FLOAT_ON_PARENT
wx.Frame.__init__( self, parent, title = title, style = style )
self._new_options = HydrusGlobals.client_controller.GetNewOptions()
self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ) )
self.SetIcon( wx.Icon( os.path.join( HC.STATIC_DIR, 'hydrus.ico' ), wx.BITMAP_TYPE_ICO ) )
HydrusGlobals.client_controller.ResetIdleTimer()
class FrameThatResizes( Frame ):
def __init__( self, parent, title, frame_key, float_on_parent = True ):
self._frame_key = frame_key
Frame.__init__( self, parent, title, float_on_parent )
self.Bind( wx.EVT_SIZE, self.EventSizeAndPositionChanged )
self.Bind( wx.EVT_MOVE_END, self.EventSizeAndPositionChanged )
self.Bind( wx.EVT_CLOSE, self.EventSizeAndPositionChanged )
self.Bind( wx.EVT_MAXIMIZE, self.EventSizeAndPositionChanged )
def EventSizeAndPositionChanged( self, event ):
SaveTLWSizeAndPosition( self, self._frame_key )
event.Skip()
class FrameThatTakesScrollablePanel( FrameThatResizes ):
def __init__( self, parent, title, frame_key, float_on_parent = True ):
self._panel = None
FrameThatResizes.__init__( self, parent, title, frame_key, float_on_parent )
self._ok = wx.Button( self, label = 'close' )
self._ok.Bind( wx.EVT_BUTTON, self.EventCloseButton )
self.Bind( wx.EVT_MENU, self.EventMenu )
self.Bind( CC.EVT_SIZE_CHANGED, self.EventChildSizeChanged )
def EventMenu( self, event ):
action = ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetAction( event.GetId() )
if action is not None:
( command, data ) = action
if command == 'ok':
self.Close()
else:
event.Skip()
def EventCloseButton( self, event ):
self.Close()
def EventChildSizeChanged( self, event ):
if self._panel is not None:
# the min size here is to compensate for wx.Notebook and anything else that don't update virtualsize on page change
( current_panel_width, current_panel_height ) = self._panel.GetSize()
( desired_panel_width, desired_panel_height ) = self._panel.GetVirtualSize()
( min_panel_width, min_panel_height ) = self._panel.GetEffectiveMinSize()
desired_delta_width = max( 0, desired_panel_width - current_panel_width, min_panel_width - current_panel_width )
desired_delta_height = max( 0, desired_panel_height - current_panel_height, min_panel_height - current_panel_height )
if desired_delta_width > 0 or desired_delta_height > 0:
ExpandTLWIfPossible( self, self._frame_key, ( desired_delta_width, desired_delta_height ) )
def SetPanel( self, panel ):
self._panel = panel
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( self._panel, CC.FLAGS_EXPAND_BOTH_WAYS )
vbox.AddF( self._ok, CC.FLAGS_LONE_BUTTON )
self.SetSizer( vbox )
SetTLWSizeAndPosition( self, self._frame_key )
self.Show( True )
self._panel.SetupScrolling()
class ShowKeys( Frame ):
def __init__( self, key_type, keys ):
if key_type == 'registration': title = 'Registration Keys'
elif key_type == 'access': title = 'Access Keys'
# give it no parent, so this doesn't close when the dialog is closed!
Frame.__init__( self, None, HydrusGlobals.client_controller.PrepStringForDisplay( title ) )
self._key_type = key_type
self._keys = keys
#
self._text_ctrl = wx.TextCtrl( self, style = wx.TE_MULTILINE | wx.TE_READONLY | wx.TE_DONTWRAP )
self._save_to_file = wx.Button( self, label = 'save to file' )
self._save_to_file.Bind( wx.EVT_BUTTON, self.EventSaveToFile )
self._done = wx.Button( self, label = 'done' )
self._done.Bind( wx.EVT_BUTTON, self.EventDone )
#
if key_type == 'registration': prepend = 'r'
else: prepend = ''
self._text = os.linesep.join( [ prepend + key.encode( 'hex' ) for key in self._keys ] )
self._text_ctrl.SetValue( self._text )
#
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( self._text_ctrl, CC.FLAGS_EXPAND_BOTH_WAYS )
vbox.AddF( self._save_to_file, CC.FLAGS_LONE_BUTTON )
vbox.AddF( self._done, CC.FLAGS_LONE_BUTTON )
self.SetSizer( vbox )
( x, y ) = self.GetEffectiveMinSize()
if x < 500: x = 500
if y < 200: y = 200
self.SetInitialSize( ( x, y ) )
self.Show( True )
def EventDone( self, event ):
self.Close()
def EventSaveToFile( self, event ):
filename = 'keys.txt'
with wx.FileDialog( None, style=wx.FD_SAVE, defaultFile = filename ) as dlg:
if dlg.ShowModal() == wx.ID_OK:
path = HydrusData.ToUnicode( dlg.GetPath() )
with open( path, 'wb' ) as f: f.write( HydrusData.ToByteString( self._text ) )

View File

@ -242,21 +242,15 @@ class GalleryImport( HydrusSerialisable.SerialisableBase ):
except HydrusExceptions.MimeException as e:
error_text = HydrusData.ToUnicode( e )
status = CC.STATUS_UNINTERESTING_MIME
self._seed_cache.UpdateSeedStatus( url, status, note = error_text )
self._seed_cache.UpdateSeedStatus( url, status )
except Exception:
error_text = traceback.format_exc()
HydrusData.Print( error_text )
except Exception as e:
status = CC.STATUS_FAILED
self._seed_cache.UpdateSeedStatus( url, status, note = error_text )
self._seed_cache.UpdateSeedStatus( url, status, exception = e )
with self._lock:
@ -720,20 +714,15 @@ class HDDImport( HydrusSerialisable.SerialisableBase ):
except HydrusExceptions.MimeException as e:
error_text = HydrusData.ToUnicode( e )
status = CC.STATUS_UNINTERESTING_MIME
self._paths_cache.UpdateSeedStatus( path, status, note = error_text )
self._paths_cache.UpdateSeedStatus( path, status )
except Exception as e:
error_text = traceback.format_exc()
HydrusData.Print( error_text )
status = CC.STATUS_FAILED
self._paths_cache.UpdateSeedStatus( path, status, note = error_text )
self._paths_cache.UpdateSeedStatus( path, status, exception = e )
with self._lock:
@ -1141,14 +1130,13 @@ class ImportFolder( HydrusSerialisable.SerialisableBaseNamed ):
self._path_cache.UpdateSeedStatus( path, CC.STATUS_UNINTERESTING_MIME )
except Exception:
except Exception as e:
error_text = traceback.format_exc()
HydrusData.Print( 'A file failed to import from import folder ' + self._name + ':' )
HydrusData.Print( error_text )
self._path_cache.UpdateSeedStatus( path, CC.STATUS_FAILED, note = error_text )
self._path_cache.UpdateSeedStatus( path, CC.STATUS_FAILED, exception = e )
@ -1345,20 +1333,15 @@ class PageOfImagesImport( HydrusSerialisable.SerialisableBase ):
except HydrusExceptions.MimeException as e:
error_text = HydrusData.ToUnicode( e )
status = CC.STATUS_UNINTERESTING_MIME
self._urls_cache.UpdateSeedStatus( file_url, status, note = error_text )
self._urls_cache.UpdateSeedStatus( file_url, status )
except Exception:
error_text = traceback.format_exc()
HydrusData.Print( error_text )
except Exception as e:
status = CC.STATUS_FAILED
self._urls_cache.UpdateSeedStatus( file_url, status, note = error_text )
self._urls_cache.UpdateSeedStatus( file_url, status, exception = e )
with self._lock:
@ -1956,10 +1939,22 @@ class SeedCache( HydrusSerialisable.SerialisableBase ):
def UpdateSeedStatus( self, seed, status, note = '' ):
def UpdateSeedStatus( self, seed, status, note = '', exception = None ):
with self._lock:
if exception is not None:
first_line = HydrusData.ToUnicode( exception ).split( os.linesep )[0]
note = first_line + u'\u2026 (Copy note to see full error)'
note += os.linesep
note += traceback.format_exc()
HydrusData.Print( 'Error when processing ' + seed + '!' )
HydrusData.Print( traceback.format_exc() )
note = HydrusData.ToUnicode( note )
seed_info = self._seeds_to_info[ seed ]
@ -2173,22 +2168,17 @@ class Subscription( HydrusSerialisable.SerialisableBaseNamed ):
except HydrusExceptions.MimeException as e:
error_text = HydrusData.ToUnicode( e )
status = CC.STATUS_UNINTERESTING_MIME
self._seed_cache.UpdateSeedStatus( url, status, note = error_text )
self._seed_cache.UpdateSeedStatus( url, status )
except Exception as e:
error_count += 1
error_text = traceback.format_exc()
HydrusData.Print( error_text )
status = CC.STATUS_FAILED
self._seed_cache.UpdateSeedStatus( url, status, note = error_text )
self._seed_cache.UpdateSeedStatus( url, status, exception = e )
time.sleep( 10 )
@ -2646,20 +2636,15 @@ class ThreadWatcherImport( HydrusSerialisable.SerialisableBase ):
except HydrusExceptions.MimeException as e:
error_text = HydrusData.ToUnicode( e )
status = CC.STATUS_UNINTERESTING_MIME
self._urls_cache.UpdateSeedStatus( file_url, status, note = error_text )
self._urls_cache.UpdateSeedStatus( file_url, status )
except Exception as e:
error_text = traceback.format_exc()
HydrusData.Print( error_text )
status = CC.STATUS_FAILED
self._urls_cache.UpdateSeedStatus( file_url, status, note = error_text )
self._urls_cache.UpdateSeedStatus( file_url, status, exception = e )
with self._lock:

View File

@ -48,7 +48,7 @@ options = {}
# Misc
NETWORK_VERSION = 17
SOFTWARE_VERSION = 213
SOFTWARE_VERSION = 214
UNSCALED_THUMBNAIL_DIMENSIONS = ( 200, 200 )

View File

@ -1829,26 +1829,13 @@ class JobDatabase( object ):
while True:
if self._result_ready.wait( 5 ) == True: break
if self._result_ready.wait( 2 ) == True: break
elif HydrusGlobals.model_shutdown: raise HydrusExceptions.ShutdownException( 'Application quit before db could serve result!' )
if isinstance( self._result, Exception ):
if isinstance( self._result, HydrusExceptions.DBException ):
( text, gumpf, db_traceback ) = self._result.args
trace_list = traceback.format_stack()
caller_traceback = 'Stack Trace (most recent call last):' + os.linesep * 2 + os.linesep.join( trace_list )
raise HydrusExceptions.DBException( text, caller_traceback, db_traceback )
else:
raise self._result
raise self._result
else:

View File

@ -1,6 +1,15 @@
import os
class CantRenderWithCVException( Exception ): pass
class DataMissing( Exception ): pass
class DBException( Exception ): pass
class DBException( Exception ):
def __str__( self ):
return os.linesep.join( self.args )
class DBAccessException( Exception ): pass
class FileMissingException( Exception ): pass
class MimeException( Exception ): pass

View File

@ -151,7 +151,7 @@ def Hydrusffmpeg_parse_infos(filename, print_infos=False):
if 'start:' in line:
m = re.search( '(start\\: )' + '[0-9]\\.[0-9]*', line )
m = re.search( '(start\\: )' + '-?[0-9]\\.[0-9]*', line )
start_offset = float( line[ m.start() + 7 : m.end() ] )