Version 377
This commit is contained in:
parent
129676d9ba
commit
50771138ca
|
@ -8,6 +8,48 @@
|
|||
<div class="content">
|
||||
<h3>changelog</h3>
|
||||
<ul>
|
||||
<li><h3>version 377</h3></li>
|
||||
<ul>
|
||||
<li>qt:</li>
|
||||
<li>all non-menubar menus across the program now launch on click release. some previously launched on click press. a variety of related click event behaviour is cleaned up, particularly with thumbnail/tag selection on the click down. this also fixes some users' menus immediately activating the first entry on slow clicks in some ui styles</li>
|
||||
<li>I think I fixed the annoying single-frame delayed size-down resize on media viewer hover frames when changing media!</li>
|
||||
<li>the vast majority of old wx panel background colour hacks are removed, so custom stylesheets should now cover much more of the UI</li>
|
||||
<li>improved the new custom style and stylesheet setting, resetting, and error handling code, particularly for not re-applying the same style or stylesheet twice, and for handling un-re-settable styles (seems to be defaults initialised by third-party OS-wide Qt style) gracefully</li>
|
||||
<li>fixed hyperlinks not using the custom web browser launch path as set in the options</li>
|
||||
<li>fixed the 'migrate entire db' and 'set thumb location' buttons in the migrate database dialog</li>
|
||||
<li>fixed a typo bug when launching the url selection tree after adding an ipfs directory to download</li>
|
||||
<li>fixed two typo bugs when editing regex favourites and simple downloader formulae</li>
|
||||
<li>fixed an issue where custom shortcut sets could not be deleted</li>
|
||||
<li>fixed a typo in the edit account type panel</li>
|
||||
<li>fixed sorting the login listctrl when there are session logins mixed with non-session logins</li>
|
||||
<li>removed some old media viewer hover window display/raise hacks</li>
|
||||
<li>retired the 'always show hover windows' debug mode</li>
|
||||
<li>the media viewer will no longer perform any drag calculations on anything but left-click drag</li>
|
||||
<li>misc Qt code refactoring/cleanup</li>
|
||||
<li>.</li>
|
||||
<li>url searching:</li>
|
||||
<li>the database now stores 'known url' domain information more efficiently. it will take a few moments/minutes to reshape the db when updating</li>
|
||||
<li>system:known url's exact url search now runs extremely fast. this will only affect new predicates of this type, not those in existing sessions</li>
|
||||
<li>system:known url's domain search now runs much faster and matches subdomains of the given domain. this will only affect new predicates of this type, not those in existing sessions</li>
|
||||
<li>system:known url's url class search now runs much faster. this will only affect new predicates of this type, not those in existing sessions</li>
|
||||
<li>when entering a regex system:known url predicate, the dialog will now not OK (throwing up an error dialog) if the regex is invalid</li>
|
||||
<li>.</li>
|
||||
<li>the rest:</li>
|
||||
<li>the shortcut system now allows all text characters. if it has text, it should work, but it is the wild west in terms of modifier labelling. anything unusual on your keyboard like ctrl+alt+e to make æ will _display_ as ctrl+alt+æ, but the same key combination will match up in the program all correct</li>
|
||||
<li>added shortcut actions 'pan_top_edge', 'pan_bottom_edge', 'pan_left_edge', 'pan_right_edge' to the media viewer shortcut set that will move the current image so the respective edge is aligned with the larger canvas's</li>
|
||||
<li>added shortcut actions 'pan_horizontal_center' and 'pan_vertical_center' to do as above but center on that axis</li>
|
||||
<li>session save now hangs the UI significantly less, whether triggered by user command or auto-saving 'last session'</li>
|
||||
<li>saving of last/exit sessions on client close is a little faster</li>
|
||||
<li>the call to refresh thumbnail file info (and redraw if needed) when a file is imported or has metadata-regenererating file maintenance done will now only call for files that are actually loaded, run faster per file, run faster when the client has large collections in its session, and not hang the ui thread when waiting for the new media info to arrive</li>
|
||||
<li>like regular popups, modal popups (like those created when big vacuum/analyze jobs jump in) will now only appear if the main gui or an on-parent child has OS focus</li>
|
||||
<li>the main gui/on-parent child OS focus test now includes misc child windows like the autocomplete results hover window</li>
|
||||
<li>network jobs that fail for one reason or another will now be more reliably cleaned up, and their connections returned to the connection pool. this may fix the 'too many open file handles' errors some users were seeing after long term unreliable network traffic</li>
|
||||
<li>fixed an issue where some thumbnails that were trashed or physically deleted were being removed from 'all known files' and file repository views when it was not appropriate</li>
|
||||
<li>connection and downloader retry time options now have a wider min/max range when in advanced mode, with an accompanying warning label for the connection panel</li>
|
||||
<li>checker options times now have a wider min/max range when in advanced mode, with an accompanying warning label</li>
|
||||
<li>cleaned up some shutdown reporting text</li>
|
||||
<li>misc debug improvements</li>
|
||||
</ul>
|
||||
<li><h3>version 376</h3></li>
|
||||
<ul>
|
||||
<li>subscriptions:</li>
|
||||
|
|
|
@ -400,6 +400,14 @@ class MediaResultCache( object ):
|
|||
|
||||
|
||||
|
||||
def HasFile( self, hash_id ):
|
||||
|
||||
with self._lock:
|
||||
|
||||
return hash_id in self._hash_ids_to_media_results
|
||||
|
||||
|
||||
|
||||
def NewForceRefreshTags( self ):
|
||||
|
||||
# repo sync or tag migration occurred, so we need complete refresh
|
||||
|
|
|
@ -271,9 +271,10 @@ page_file_count_display_string_lookup[ PAGE_FILE_COUNT_DISPLAY_ALL ] = 'for all
|
|||
page_file_count_display_string_lookup[ PAGE_FILE_COUNT_DISPLAY_ONLY_IMPORTERS ] = 'for import pages'
|
||||
page_file_count_display_string_lookup[ PAGE_FILE_COUNT_DISPLAY_NONE ] = 'for no pages'
|
||||
|
||||
SHORTCUT_TYPE_KEYBOARD_ASCII = 0
|
||||
SHORTCUT_TYPE_KEYBOARD_CHARACTER = 0
|
||||
SHORTCUT_TYPE_MOUSE = 1
|
||||
SHORTCUT_TYPE_KEYBOARD_SPECIAL = 2
|
||||
SHORTCUT_TYPE_NOT_ALLOWED = 3
|
||||
|
||||
SHORTCUT_MODIFIER_CTRL = 0
|
||||
SHORTCUT_MODIFIER_ALT = 1
|
||||
|
@ -407,7 +408,7 @@ SHORTCUTS_RESERVED_NAMES = [ 'archive_delete_filter', 'duplicate_filter', 'media
|
|||
# shortcut commands
|
||||
|
||||
SHORTCUTS_MEDIA_ACTIONS = [ 'manage_file_tags', 'manage_file_ratings', 'manage_file_urls', 'manage_file_notes', 'archive_file', 'inbox_file', 'delete_file', 'export_files', 'export_files_quick_auto_export', 'remove_file_from_view', 'open_file_in_external_program', 'open_selection_in_new_page', 'launch_the_archive_delete_filter', 'copy_bmp', 'copy_file', 'copy_path', 'copy_sha256_hash', 'get_similar_to_exact', 'get_similar_to_very_similar', 'get_similar_to_similar', 'get_similar_to_speculative', 'duplicate_media_set_alternate', 'duplicate_media_set_alternate_collections', 'duplicate_media_set_custom', 'duplicate_media_set_focused_better', 'duplicate_media_set_focused_king', 'duplicate_media_set_same_quality', 'open_known_url' ]
|
||||
SHORTCUTS_MEDIA_VIEWER_ACTIONS = [ 'move_animation_to_previous_frame', 'move_animation_to_next_frame', 'switch_between_fullscreen_borderless_and_regular_framed_window', 'pan_up', 'pan_down', 'pan_left', 'pan_right', 'zoom_in', 'zoom_out', 'switch_between_100_percent_and_canvas_zoom', 'flip_darkmode' ]
|
||||
SHORTCUTS_MEDIA_VIEWER_ACTIONS = [ 'move_animation_to_previous_frame', 'move_animation_to_next_frame', 'switch_between_fullscreen_borderless_and_regular_framed_window', 'pan_up', 'pan_down', 'pan_left', 'pan_right', 'pan_top_edge', 'pan_bottom_edge', 'pan_left_edge', 'pan_right_edge', 'pan_vertical_center', 'pan_horizontal_center', 'zoom_in', 'zoom_out', 'switch_between_100_percent_and_canvas_zoom', 'flip_darkmode' ]
|
||||
SHORTCUTS_MEDIA_VIEWER_BROWSER_ACTIONS = [ 'view_next', 'view_first', 'view_last', 'view_previous' ]
|
||||
SHORTCUTS_MAIN_GUI_ACTIONS = [ 'refresh', 'refresh_all_pages', 'refresh_page_of_pages_pages', 'new_page', 'new_page_of_pages', 'new_duplicate_filter_page', 'new_gallery_downloader_page', 'new_url_downloader_page', 'new_simple_downloader_page', 'new_watcher_downloader_page', 'synchronised_wait_switch', 'set_media_focus', 'show_hide_splitters', 'set_search_focus', 'unclose_page', 'close_page', 'redo', 'undo', 'flip_darkmode', 'check_all_import_folders', 'flip_debug_force_idle_mode_do_not_set_this', 'show_and_focus_manage_tags_favourite_tags', 'show_and_focus_manage_tags_related_tags', 'show_and_focus_manage_tags_file_lookup_script_tags', 'show_and_focus_manage_tags_recent_tags', 'focus_media_viewer' ]
|
||||
SHORTCUTS_DUPLICATE_FILTER_ACTIONS = [ 'duplicate_filter_this_is_better_and_delete_other', 'duplicate_filter_this_is_better_but_keep_both', 'duplicate_filter_exactly_the_same', 'duplicate_filter_alternates', 'duplicate_filter_false_positive', 'duplicate_filter_custom_action', 'duplicate_filter_skip', 'duplicate_filter_back' ]
|
||||
|
|
|
@ -124,6 +124,8 @@ class Controller( HydrusController.HydrusController ):
|
|||
self._alive_page_keys = set()
|
||||
self._closed_page_keys = set()
|
||||
|
||||
self._last_last_session_hash = None
|
||||
|
||||
self._last_mouse_position = None
|
||||
self._menu_open = False
|
||||
self._previously_idle = False
|
||||
|
@ -919,7 +921,7 @@ class Controller( HydrusController.HydrusController ):
|
|||
|
||||
try:
|
||||
|
||||
ClientGUIStyle.SetStyle( qt_style_name )
|
||||
ClientGUIStyle.SetStyleFromName( qt_style_name )
|
||||
|
||||
except Exception as e:
|
||||
|
||||
|
@ -933,7 +935,7 @@ class Controller( HydrusController.HydrusController ):
|
|||
|
||||
try:
|
||||
|
||||
ClientGUIStyle.SetStylesheet( qt_stylesheet_name )
|
||||
ClientGUIStyle.SetStylesheetFromPath( qt_stylesheet_name )
|
||||
|
||||
except Exception as e:
|
||||
|
||||
|
@ -1364,6 +1366,27 @@ class Controller( HydrusController.HydrusController ):
|
|||
|
||||
|
||||
|
||||
def SaveGUISession( self, session ):
|
||||
|
||||
name = session.GetName()
|
||||
|
||||
if name == 'last session':
|
||||
|
||||
session_hash = hashlib.sha256( bytes( session.DumpToString(), 'utf-8' ) ).digest()
|
||||
|
||||
if session_hash == self._last_last_session_hash:
|
||||
|
||||
return
|
||||
|
||||
|
||||
self._last_last_session_hash = session_hash
|
||||
|
||||
|
||||
self.WriteSynchronous( 'serialisable', session )
|
||||
|
||||
self.pub( 'notify_new_sessions' )
|
||||
|
||||
|
||||
def SetRunningTwistedServices( self, services ):
|
||||
|
||||
def TWISTEDDoIt():
|
||||
|
@ -1510,6 +1533,8 @@ class Controller( HydrusController.HydrusController ):
|
|||
|
||||
self.DoIdleShutdownWork()
|
||||
|
||||
self.pub( 'splash_set_status_subtext', '' )
|
||||
|
||||
except:
|
||||
|
||||
self._ReportShutdownException()
|
||||
|
@ -1520,6 +1545,8 @@ class Controller( HydrusController.HydrusController ):
|
|||
|
||||
try:
|
||||
|
||||
self.pub( 'splash_set_status_text', 'waiting for twisted to exit' )
|
||||
|
||||
self.SetRunningTwistedServices( [] )
|
||||
|
||||
except:
|
||||
|
|
|
@ -1473,8 +1473,10 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
self._c.execute( 'CREATE TABLE IF NOT EXISTS external_master.texts ( text_id INTEGER PRIMARY KEY, text TEXT UNIQUE );' )
|
||||
|
||||
self._c.execute( 'CREATE TABLE IF NOT EXISTS external_master.urls ( url_id INTEGER PRIMARY KEY, domain TEXT, url TEXT UNIQUE );' )
|
||||
self._CreateIndex( 'external_master.urls', [ 'domain' ] )
|
||||
self._c.execute( 'CREATE TABLE IF NOT EXISTS external_master.url_domains ( domain_id INTEGER PRIMARY KEY, domain TEXT UNIQUE );' )
|
||||
|
||||
self._c.execute( 'CREATE TABLE IF NOT EXISTS external_master.urls ( url_id INTEGER PRIMARY KEY, domain_id INTEGER, url TEXT UNIQUE );' )
|
||||
self._CreateIndex( 'external_master.urls', [ 'domain_id' ] )
|
||||
|
||||
# inserts
|
||||
|
||||
|
@ -3724,16 +3726,22 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
if len( new_file_info ) > 0:
|
||||
|
||||
hashes = set()
|
||||
hashes_that_need_refresh = set()
|
||||
|
||||
for ( hash_id, hash ) in new_file_info:
|
||||
|
||||
self._weakref_media_result_cache.DropMediaResult( hash_id, hash )
|
||||
|
||||
hashes.add( hash )
|
||||
if self._weakref_media_result_cache.HasFile( hash_id ):
|
||||
|
||||
self._weakref_media_result_cache.DropMediaResult( hash_id, hash )
|
||||
|
||||
hashes_that_need_refresh.add( hash )
|
||||
|
||||
|
||||
|
||||
self._controller.pub( 'new_file_info', hashes )
|
||||
if len( hashes_that_need_refresh ) > 0:
|
||||
|
||||
self._controller.pub( 'new_file_info', hashes_that_need_refresh )
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -5087,7 +5095,7 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
# start with some quick ways to populate query_hash_ids
|
||||
|
||||
def update_qhi( query_hash_ids, some_hash_ids, force_create_new_set = False ):
|
||||
def intersection_update_qhi( query_hash_ids, some_hash_ids, force_create_new_set = False ):
|
||||
|
||||
if query_hash_ids is None:
|
||||
|
||||
|
@ -5148,7 +5156,7 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
|
||||
|
||||
query_hash_ids = update_qhi( query_hash_ids, or_query_hash_ids )
|
||||
query_hash_ids = intersection_update_qhi( query_hash_ids, or_query_hash_ids )
|
||||
|
||||
|
||||
return query_hash_ids
|
||||
|
@ -5185,7 +5193,7 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
specific_hash_ids = self._GetHashIds( matching_sha256_hashes )
|
||||
|
||||
query_hash_ids = update_qhi( query_hash_ids, specific_hash_ids )
|
||||
query_hash_ids = intersection_update_qhi( query_hash_ids, specific_hash_ids )
|
||||
|
||||
|
||||
#
|
||||
|
@ -5201,7 +5209,7 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
modified_timestamp_hash_ids = self._STS( self._c.execute( 'SELECT hash_id FROM file_modified_timestamps WHERE {};'.format( pred_string ) ) )
|
||||
|
||||
query_hash_ids = update_qhi( query_hash_ids, modified_timestamp_hash_ids )
|
||||
query_hash_ids = intersection_update_qhi( query_hash_ids, modified_timestamp_hash_ids )
|
||||
|
||||
|
||||
#
|
||||
|
@ -5223,7 +5231,7 @@ class DB( HydrusDB.HydrusDB ):
|
|||
all_similar_hash_ids.update( similar_hash_ids )
|
||||
|
||||
|
||||
query_hash_ids = update_qhi( query_hash_ids, all_similar_hash_ids )
|
||||
query_hash_ids = intersection_update_qhi( query_hash_ids, all_similar_hash_ids )
|
||||
|
||||
|
||||
for ( operator, value, rating_service_key ) in system_predicates.GetRatingsPredicates():
|
||||
|
@ -5239,7 +5247,7 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
rating_hash_ids = self._STI( self._c.execute( 'SELECT hash_id FROM local_ratings WHERE service_id = ?;', ( service_id, ) ) )
|
||||
|
||||
query_hash_ids = update_qhi( query_hash_ids, rating_hash_ids )
|
||||
query_hash_ids = intersection_update_qhi( query_hash_ids, rating_hash_ids )
|
||||
|
||||
else:
|
||||
|
||||
|
@ -5287,13 +5295,13 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
rating_hash_ids = self._STI( self._c.execute( 'SELECT hash_id FROM local_ratings WHERE service_id = ? AND ' + predicate + ';', ( service_id, ) ) )
|
||||
|
||||
query_hash_ids = update_qhi( query_hash_ids, rating_hash_ids )
|
||||
query_hash_ids = intersection_update_qhi( query_hash_ids, rating_hash_ids )
|
||||
|
||||
|
||||
|
||||
if system_predicates.MustBeInbox():
|
||||
|
||||
query_hash_ids = update_qhi( query_hash_ids, self._inbox_hash_ids, force_create_new_set = True )
|
||||
query_hash_ids = intersection_update_qhi( query_hash_ids, self._inbox_hash_ids, force_create_new_set = True )
|
||||
|
||||
|
||||
for ( operator, num_relationships, dupe_type ) in system_predicates.GetDuplicateRelationshipCountPredicates():
|
||||
|
@ -5313,7 +5321,7 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
dupe_hash_ids = self._DuplicatesGetHashIdsFromDuplicateCountPredicate( file_service_key, operator, num_relationships, dupe_type )
|
||||
|
||||
query_hash_ids = update_qhi( query_hash_ids, dupe_hash_ids )
|
||||
query_hash_ids = intersection_update_qhi( query_hash_ids, dupe_hash_ids )
|
||||
|
||||
|
||||
|
||||
|
@ -5334,7 +5342,7 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
viewing_hash_ids = self._GetHashIdsFromFileViewingStatistics( view_type, viewing_locations, operator, viewing_value )
|
||||
|
||||
query_hash_ids = update_qhi( query_hash_ids, viewing_hash_ids )
|
||||
query_hash_ids = intersection_update_qhi( query_hash_ids, viewing_hash_ids )
|
||||
|
||||
|
||||
|
||||
|
@ -5355,7 +5363,7 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
tag_query_hash_ids = self._GetHashIdsFromTag( file_service_key, tag_service_key, tag, include_current_tags, include_pending_tags, allowed_hash_ids = query_hash_ids )
|
||||
|
||||
query_hash_ids = update_qhi( query_hash_ids, tag_query_hash_ids )
|
||||
query_hash_ids = intersection_update_qhi( query_hash_ids, tag_query_hash_ids )
|
||||
|
||||
if query_hash_ids == set():
|
||||
|
||||
|
@ -5367,14 +5375,14 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
namespace_query_hash_ids = HydrusData.IntelligentMassIntersect( ( self._GetHashIdsFromNamespace( file_service_key, tag_service_key, namespace, include_current_tags, include_pending_tags, include_siblings = True ) for namespace in namespaces_to_include ) )
|
||||
|
||||
query_hash_ids = update_qhi( query_hash_ids, namespace_query_hash_ids )
|
||||
query_hash_ids = intersection_update_qhi( query_hash_ids, namespace_query_hash_ids )
|
||||
|
||||
|
||||
if len( wildcards_to_include ) > 0:
|
||||
|
||||
wildcard_query_hash_ids = HydrusData.IntelligentMassIntersect( ( self._GetHashIdsFromWildcard( file_service_key, tag_service_key, wildcard, include_current_tags, include_pending_tags ) for wildcard in wildcards_to_include ) )
|
||||
|
||||
query_hash_ids = update_qhi( query_hash_ids, wildcard_query_hash_ids )
|
||||
query_hash_ids = intersection_update_qhi( query_hash_ids, wildcard_query_hash_ids )
|
||||
|
||||
|
||||
|
||||
|
@ -5419,7 +5427,7 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
king_hash_ids = self._DuplicatesFilterKingHashIds( query_hash_ids )
|
||||
|
||||
query_hash_ids = update_qhi( query_hash_ids, king_hash_ids )
|
||||
query_hash_ids = intersection_update_qhi( query_hash_ids, king_hash_ids )
|
||||
|
||||
|
||||
if not done_files_info_predicates and ( need_file_domain_cross_reference or there_are_simple_files_info_preds_to_search_for ):
|
||||
|
@ -5428,13 +5436,13 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
if file_service_key == CC.COMBINED_FILE_SERVICE_KEY:
|
||||
|
||||
query_hash_ids = update_qhi( query_hash_ids, self._STI( self._SelectFromList( 'SELECT hash_id FROM files_info WHERE ' + ' AND '.join( files_info_predicates ) + ';', query_hash_ids ) ) )
|
||||
query_hash_ids = intersection_update_qhi( query_hash_ids, self._STI( self._SelectFromList( 'SELECT hash_id FROM files_info WHERE ' + ' AND '.join( files_info_predicates ) + ';', query_hash_ids ) ) )
|
||||
|
||||
else:
|
||||
|
||||
files_info_predicates.insert( 0, 'service_id = ' + str( file_service_id ) )
|
||||
|
||||
query_hash_ids = update_qhi( query_hash_ids, self._STI( self._SelectFromList( 'SELECT hash_id FROM current_files NATURAL JOIN files_info WHERE ' + ' AND '.join( files_info_predicates ) + ';', query_hash_ids ) ) )
|
||||
query_hash_ids = intersection_update_qhi( query_hash_ids, self._STI( self._SelectFromList( 'SELECT hash_id FROM current_files NATURAL JOIN files_info WHERE ' + ' AND '.join( files_info_predicates ) + ';', query_hash_ids ) ) )
|
||||
|
||||
|
||||
done_files_info_predicates = True
|
||||
|
@ -5498,14 +5506,14 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
service_id = self._GetServiceId( service_key )
|
||||
|
||||
query_hash_ids = update_qhi( query_hash_ids, self._STI( self._c.execute( 'SELECT hash_id FROM current_files WHERE service_id = ?;', ( service_id, ) ) ) )
|
||||
query_hash_ids = intersection_update_qhi( query_hash_ids, self._STI( self._c.execute( 'SELECT hash_id FROM current_files WHERE service_id = ?;', ( service_id, ) ) ) )
|
||||
|
||||
|
||||
for service_key in file_services_to_include_pending:
|
||||
|
||||
service_id = self._GetServiceId( service_key )
|
||||
|
||||
query_hash_ids = update_qhi( query_hash_ids, self._STI( self._c.execute( 'SELECT hash_id FROM file_transfers WHERE service_id = ?;', ( service_id, ) ) ) )
|
||||
query_hash_ids = intersection_update_qhi( query_hash_ids, self._STI( self._c.execute( 'SELECT hash_id FROM file_transfers WHERE service_id = ?;', ( service_id, ) ) ) )
|
||||
|
||||
|
||||
for service_key in file_services_to_exclude_current:
|
||||
|
@ -5562,7 +5570,7 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
hash_ids = zero_hash_ids.union( accurate_except_zero_hash_ids )
|
||||
|
||||
query_hash_ids = update_qhi( query_hash_ids, hash_ids )
|
||||
query_hash_ids = intersection_update_qhi( query_hash_ids, hash_ids )
|
||||
|
||||
|
||||
|
||||
|
@ -5587,7 +5595,7 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
hash_ids = zero_hash_ids.union( accurate_except_zero_hash_ids )
|
||||
|
||||
query_hash_ids = update_qhi( query_hash_ids, hash_ids )
|
||||
query_hash_ids = intersection_update_qhi( query_hash_ids, hash_ids )
|
||||
|
||||
|
||||
|
||||
|
@ -5614,7 +5622,7 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
if must_be_local:
|
||||
|
||||
query_hash_ids = update_qhi( query_hash_ids, local_hash_ids )
|
||||
query_hash_ids = intersection_update_qhi( query_hash_ids, local_hash_ids )
|
||||
|
||||
elif must_not_be_local:
|
||||
|
||||
|
@ -5632,7 +5640,7 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
if operator: # inclusive
|
||||
|
||||
query_hash_ids = update_qhi( query_hash_ids, url_hash_ids )
|
||||
query_hash_ids = intersection_update_qhi( query_hash_ids, url_hash_ids )
|
||||
|
||||
else:
|
||||
|
||||
|
@ -5702,7 +5710,7 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
elif num_tags_nonzero:
|
||||
|
||||
query_hash_ids = update_qhi( query_hash_ids, nonzero_tag_query_hash_ids )
|
||||
query_hash_ids = intersection_update_qhi( query_hash_ids, nonzero_tag_query_hash_ids )
|
||||
|
||||
|
||||
|
||||
|
@ -5719,7 +5727,7 @@ class DB( HydrusDB.HydrusDB ):
|
|||
good_tag_count_hash_ids.update( zero_hash_ids )
|
||||
|
||||
|
||||
query_hash_ids = update_qhi( query_hash_ids, good_tag_count_hash_ids )
|
||||
query_hash_ids = intersection_update_qhi( query_hash_ids, good_tag_count_hash_ids )
|
||||
|
||||
|
||||
if job_key.IsCancelled():
|
||||
|
@ -5735,7 +5743,7 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
good_hash_ids = self._GetHashIdsThatHaveTagAsNum( file_service_key, tag_service_key, namespace, num, '>', include_current_tags, include_pending_tags )
|
||||
|
||||
query_hash_ids = update_qhi( query_hash_ids, good_hash_ids )
|
||||
query_hash_ids = intersection_update_qhi( query_hash_ids, good_hash_ids )
|
||||
|
||||
|
||||
if 'max_tag_as_number' in simple_preds:
|
||||
|
@ -5744,7 +5752,7 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
good_hash_ids = self._GetHashIdsThatHaveTagAsNum( file_service_key, tag_service_key, namespace, num, '<', include_current_tags, include_pending_tags )
|
||||
|
||||
query_hash_ids = update_qhi( query_hash_ids, good_hash_ids )
|
||||
query_hash_ids = intersection_update_qhi( query_hash_ids, good_hash_ids )
|
||||
|
||||
|
||||
if job_key.IsCancelled():
|
||||
|
@ -5984,34 +5992,155 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
if hash_ids is None:
|
||||
|
||||
query = self._c.execute( 'SELECT hash_id, url FROM url_map NATURAL JOIN urls;' )
|
||||
search_hash_ids = None
|
||||
|
||||
else:
|
||||
|
||||
query = self._SelectFromList( 'SELECT hash_id, url FROM url_map NATURAL JOIN urls WHERE hash_id in {};', hash_ids )
|
||||
|
||||
|
||||
result_hash_ids = set()
|
||||
|
||||
for ( hash_id, url ) in query:
|
||||
|
||||
if rule_type in ( 'url_class', 'url_match' ):
|
||||
if len( hash_ids ) <= 1000:
|
||||
|
||||
if rule.Matches( url ):
|
||||
|
||||
result_hash_ids.add( hash_id )
|
||||
|
||||
search_hash_ids = hash_ids
|
||||
|
||||
else:
|
||||
|
||||
if re.search( rule, url ) is not None:
|
||||
|
||||
result_hash_ids.add( hash_id )
|
||||
|
||||
search_hash_ids = None
|
||||
|
||||
|
||||
|
||||
return result_hash_ids
|
||||
if rule_type == 'exact_match':
|
||||
|
||||
url = rule
|
||||
|
||||
result_hash_ids = self._STS( self._c.execute( 'SELECT hash_id FROM url_map NATURAL JOIN urls WHERE url = ?;', ( url, ) ) )
|
||||
|
||||
if hash_ids is not None:
|
||||
|
||||
result_hash_ids.intersection_update( hash_ids )
|
||||
|
||||
|
||||
return result_hash_ids
|
||||
|
||||
elif rule_type in ( 'url_class', 'url_match' ):
|
||||
|
||||
url_class = rule
|
||||
|
||||
domain = url_class.GetDomain()
|
||||
|
||||
if url_class.MatchesSubdomains():
|
||||
|
||||
domain_ids = self._GetURLDomainAndSubdomainIds( domain )
|
||||
|
||||
else:
|
||||
|
||||
domain_ids = ( self._GetURLDomainId( domain ), )
|
||||
|
||||
|
||||
result_hash_ids = set()
|
||||
|
||||
for domain_id in domain_ids:
|
||||
|
||||
domain_result_hash_ids = set()
|
||||
|
||||
if search_hash_ids is None:
|
||||
|
||||
query = self._c.execute( 'SELECT hash_id, url FROM url_map NATURAL JOIN urls WHERE domain_id = ?;', ( domain_id, ) )
|
||||
|
||||
else:
|
||||
|
||||
query = self._SelectFromList( 'SELECT hash_id, url FROM url_map NATURAL JOIN urls WHERE domain_id = ' + str( domain_id ) + ' AND hash_id in {};', search_hash_ids )
|
||||
|
||||
|
||||
for ( hash_id, url ) in query:
|
||||
|
||||
if url_class.Matches( url ):
|
||||
|
||||
domain_result_hash_ids.add( hash_id )
|
||||
|
||||
|
||||
|
||||
result_hash_ids.update( domain_result_hash_ids )
|
||||
|
||||
|
||||
if hash_ids is not None and search_hash_ids is None:
|
||||
|
||||
result_hash_ids.intersection_update( hash_ids )
|
||||
|
||||
|
||||
return result_hash_ids
|
||||
|
||||
elif rule_type in 'domain':
|
||||
|
||||
domain = rule
|
||||
|
||||
# if we search for site.com, we also want artist.site.com or www.site.com or cdn2.site.com
|
||||
domain_ids = self._GetURLDomainAndSubdomainIds( domain )
|
||||
|
||||
result_hash_ids = set()
|
||||
|
||||
for domain_id in domain_ids:
|
||||
|
||||
if search_hash_ids is None:
|
||||
|
||||
query = self._c.execute( 'SELECT DISTINCT hash_id FROM url_map NATURAL JOIN urls WHERE domain_id = ?;', ( domain_id, ) )
|
||||
|
||||
else:
|
||||
|
||||
query = self._SelectFromList( 'SELECT DISTINCT hash_id FROM url_map NATURAL JOIN urls WHERE domain_id = ' + str( domain_id ) + ' AND hash_id in {};', search_hash_ids )
|
||||
|
||||
|
||||
domain_result_hash_ids = self._STS( query )
|
||||
|
||||
result_hash_ids.update( domain_result_hash_ids )
|
||||
|
||||
|
||||
if hash_ids is not None and search_hash_ids is None:
|
||||
|
||||
result_hash_ids.intersection_update( hash_ids )
|
||||
|
||||
|
||||
return result_hash_ids
|
||||
|
||||
else:
|
||||
|
||||
if search_hash_ids is None:
|
||||
|
||||
query = self._c.execute( 'SELECT hash_id, url FROM url_map NATURAL JOIN urls;' )
|
||||
|
||||
else:
|
||||
|
||||
query = self._SelectFromList( 'SELECT hash_id, url FROM url_map NATURAL JOIN urls WHERE hash_id in {};', search_hash_ids )
|
||||
|
||||
|
||||
result_hash_ids = set()
|
||||
|
||||
for ( hash_id, url ) in query:
|
||||
|
||||
if rule_type in ( 'url_class', 'url_match' ):
|
||||
|
||||
url_class = rule
|
||||
|
||||
if url_class.Matches( url ):
|
||||
|
||||
result_hash_ids.add( hash_id )
|
||||
|
||||
|
||||
else:
|
||||
|
||||
regex = rule
|
||||
|
||||
if re.search( regex, url ) is not None:
|
||||
|
||||
result_hash_ids.add( hash_id )
|
||||
|
||||
|
||||
|
||||
|
||||
if hash_ids is not None and search_hash_ids is None:
|
||||
|
||||
result_hash_ids.intersection_update( hash_ids )
|
||||
|
||||
|
||||
return result_hash_ids
|
||||
|
||||
|
||||
|
||||
def _GetHashIdsFromWildcard( self, file_service_key, tag_service_key, wildcard, include_current_tags, include_pending_tags ):
|
||||
|
@ -7866,6 +7995,38 @@ class DB( HydrusDB.HydrusDB ):
|
|||
return self._GetHashes( hash_ids )
|
||||
|
||||
|
||||
def _GetURLDomainId( self, domain ):
|
||||
|
||||
result = self._c.execute( 'SELECT domain_id FROM url_domains WHERE domain = ?;', ( domain, ) ).fetchone()
|
||||
|
||||
if result is None:
|
||||
|
||||
self._c.execute( 'INSERT INTO url_domains ( domain ) VALUES ( ? );', ( domain, ) )
|
||||
|
||||
domain_id = self._c.lastrowid
|
||||
|
||||
else:
|
||||
|
||||
( domain_id, ) = result
|
||||
|
||||
|
||||
return domain_id
|
||||
|
||||
|
||||
def _GetURLDomainAndSubdomainIds( self, domain ):
|
||||
|
||||
domain_ids = set()
|
||||
|
||||
domain_ids.add( self._GetURLDomainId( domain ) )
|
||||
|
||||
for ( domain_id, ) in self._c.execute( 'SELECT domain_id FROM url_domains WHERE domain LIKE ?;', ( '%.{}'.format( domain ), ) ):
|
||||
|
||||
domain_ids.add( domain_id )
|
||||
|
||||
|
||||
return domain_ids
|
||||
|
||||
|
||||
def _GetURLId( self, url ):
|
||||
|
||||
result = self._c.execute( 'SELECT url_id FROM urls WHERE url = ?;', ( url, ) ).fetchone()
|
||||
|
@ -7881,7 +8042,9 @@ class DB( HydrusDB.HydrusDB ):
|
|||
domain = 'unknown.com'
|
||||
|
||||
|
||||
self._c.execute( 'INSERT INTO urls ( domain, url ) VALUES ( ?, ? );', ( domain, url ) )
|
||||
domain_id = self._GetURLDomainId( domain )
|
||||
|
||||
self._c.execute( 'INSERT INTO urls ( domain_id, url ) VALUES ( ?, ? );', ( domain_id, url ) )
|
||||
|
||||
url_id = self._c.lastrowid
|
||||
|
||||
|
@ -8071,9 +8234,12 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
status = CC.STATUS_SUCCESSFUL_AND_NEW
|
||||
|
||||
self._weakref_media_result_cache.DropMediaResult( hash_id, hash )
|
||||
|
||||
self._controller.pub( 'new_file_info', set( ( hash, ) ) )
|
||||
if self._weakref_media_result_cache.HasFile( hash_id ):
|
||||
|
||||
self._weakref_media_result_cache.DropMediaResult( hash_id, hash )
|
||||
|
||||
self._controller.pub( 'new_file_info', set( ( hash, ) ) )
|
||||
|
||||
|
||||
|
||||
if HG.file_import_report_mode:
|
||||
|
@ -13221,6 +13387,50 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
|
||||
|
||||
if version == 376:
|
||||
|
||||
result = self._c.execute( 'SELECT 1 FROM external_master.sqlite_master WHERE name = ?;', ( 'url_domains', ) ).fetchone()
|
||||
|
||||
try:
|
||||
|
||||
if result is None:
|
||||
|
||||
self._controller.pub( 'splash_set_status_subtext', 'compressing url storage--creating' )
|
||||
|
||||
self._c.execute( 'ALTER TABLE urls RENAME TO urls_old;' )
|
||||
|
||||
self._c.execute( 'CREATE TABLE IF NOT EXISTS external_master.url_domains ( domain_id INTEGER PRIMARY KEY, domain TEXT UNIQUE );' )
|
||||
|
||||
self._c.execute( 'CREATE TABLE IF NOT EXISTS external_master.urls ( url_id INTEGER PRIMARY KEY, domain_id INTEGER, url TEXT UNIQUE );' )
|
||||
|
||||
self._controller.pub( 'splash_set_status_subtext', 'compressing url storage--populating domains' )
|
||||
|
||||
self._c.execute( 'INSERT INTO url_domains ( domain ) SELECT DISTINCT domain FROM urls_old;' )
|
||||
|
||||
self._controller.pub( 'splash_set_status_subtext', 'compressing url storage--populating urls' )
|
||||
|
||||
self._c.execute( 'INSERT INTO urls ( url_id, domain_id, url ) SELECT url_id, domain_id, url FROM urls_old NATURAL JOIN url_domains;' )
|
||||
|
||||
self._controller.pub( 'splash_set_status_subtext', 'compressing url storage--indexing' )
|
||||
|
||||
self._CreateIndex( 'external_master.urls', [ 'domain_id' ] )
|
||||
|
||||
self._c.execute( 'DROP TABLE urls_old;' )
|
||||
|
||||
self._controller.pub( 'splash_set_status_subtext', 'compressing url storage--optimising' )
|
||||
|
||||
self._c.execute( 'ANALYZE external_master.urls;' )
|
||||
|
||||
|
||||
except Exception as e:
|
||||
|
||||
HydrusData.Print( 'Could not update URL storage!' )
|
||||
HydrusData.PrintException( e )
|
||||
|
||||
raise
|
||||
|
||||
|
||||
|
||||
self._controller.pub( 'splash_set_title_text', 'updated db to v' + str( version + 1 ) )
|
||||
|
||||
self._c.execute( 'UPDATE version SET version = ?;', ( version + 1, ) )
|
||||
|
|
|
@ -162,7 +162,7 @@ def DAEMONSynchroniseRepositories( controller ):
|
|||
return
|
||||
|
||||
|
||||
time.sleep( 3 )
|
||||
time.sleep( 1 )
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -345,13 +345,13 @@ def GetDefaultShortcuts():
|
|||
media.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD_SPECIAL, CC.SHORTCUT_KEY_SPECIAL_F7, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'archive_file' ) )
|
||||
media.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD_SPECIAL, CC.SHORTCUT_KEY_SPECIAL_F7, [ CC.SHORTCUT_MODIFIER_SHIFT ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'inbox_file' ) )
|
||||
|
||||
media.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD_ASCII, ord( 'E' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'open_file_in_external_program' ) )
|
||||
media.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD_CHARACTER, ord( 'E' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'open_file_in_external_program' ) )
|
||||
|
||||
media.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD_ASCII, ord( 'R' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'remove_file_from_view' ) )
|
||||
media.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD_CHARACTER, ord( 'R' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'remove_file_from_view' ) )
|
||||
|
||||
media.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD_SPECIAL, CC.SHORTCUT_KEY_SPECIAL_F12, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'launch_the_archive_delete_filter' ) )
|
||||
|
||||
media.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD_ASCII, ord( 'C' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'copy_file' ) )
|
||||
media.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD_CHARACTER, ord( 'C' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'copy_file' ) )
|
||||
|
||||
shortcuts.append( media )
|
||||
|
||||
|
@ -360,15 +360,15 @@ def GetDefaultShortcuts():
|
|||
main_gui.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD_SPECIAL, CC.SHORTCUT_KEY_SPECIAL_F5, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'refresh' ) )
|
||||
main_gui.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD_SPECIAL, CC.SHORTCUT_KEY_SPECIAL_F9, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'new_page' ) )
|
||||
|
||||
main_gui.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD_ASCII, ord( 'I' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'synchronised_wait_switch' ) )
|
||||
main_gui.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD_ASCII, ord( 'M' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'set_media_focus' ) )
|
||||
main_gui.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD_ASCII, ord( 'R' ), [ CC.SHORTCUT_MODIFIER_CTRL, CC.SHORTCUT_MODIFIER_SHIFT ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'show_hide_splitters' ) )
|
||||
main_gui.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD_ASCII, ord( 'S' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'set_search_focus' ) )
|
||||
main_gui.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD_ASCII, ord( 'T' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'new_page' ) )
|
||||
main_gui.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD_ASCII, ord( 'U' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'unclose_page' ) )
|
||||
main_gui.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD_ASCII, ord( 'W' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'close_page' ) )
|
||||
main_gui.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD_ASCII, ord( 'Y' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'redo' ) )
|
||||
main_gui.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD_ASCII, ord( 'Z' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'undo' ) )
|
||||
main_gui.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD_CHARACTER, ord( 'I' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'synchronised_wait_switch' ) )
|
||||
main_gui.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD_CHARACTER, ord( 'M' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'set_media_focus' ) )
|
||||
main_gui.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD_CHARACTER, ord( 'R' ), [ CC.SHORTCUT_MODIFIER_CTRL, CC.SHORTCUT_MODIFIER_SHIFT ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'show_hide_splitters' ) )
|
||||
main_gui.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD_CHARACTER, ord( 'S' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'set_search_focus' ) )
|
||||
main_gui.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD_CHARACTER, ord( 'T' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'new_page' ) )
|
||||
main_gui.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD_CHARACTER, ord( 'U' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'unclose_page' ) )
|
||||
main_gui.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD_CHARACTER, ord( 'W' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'close_page' ) )
|
||||
main_gui.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD_CHARACTER, ord( 'Y' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'redo' ) )
|
||||
main_gui.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD_CHARACTER, ord( 'Z' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'undo' ) )
|
||||
|
||||
shortcuts.append( main_gui )
|
||||
|
||||
|
@ -394,14 +394,14 @@ def GetDefaultShortcuts():
|
|||
|
||||
media_viewer = ClientGUIShortcuts.ShortcutSet( 'media_viewer' )
|
||||
|
||||
media_viewer.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD_ASCII, ord( 'B' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'move_animation_to_previous_frame' ) )
|
||||
media_viewer.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD_ASCII, ord( 'N' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'move_animation_to_next_frame' ) )
|
||||
media_viewer.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD_CHARACTER, ord( 'B' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'move_animation_to_previous_frame' ) )
|
||||
media_viewer.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD_CHARACTER, ord( 'N' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'move_animation_to_next_frame' ) )
|
||||
|
||||
media_viewer.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD_ASCII, ord( 'F' ), [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'switch_between_fullscreen_borderless_and_regular_framed_window' ) )
|
||||
media_viewer.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD_CHARACTER, ord( 'F' ), [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'switch_between_fullscreen_borderless_and_regular_framed_window' ) )
|
||||
|
||||
media_viewer.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD_ASCII, ord( 'Z' ), [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'switch_between_100_percent_and_canvas_zoom' ) )
|
||||
media_viewer.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD_ASCII, ord( '+' ), [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'zoom_in' ) )
|
||||
media_viewer.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD_ASCII, ord( '-' ), [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'zoom_out' ) )
|
||||
media_viewer.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD_CHARACTER, ord( 'Z' ), [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'switch_between_100_percent_and_canvas_zoom' ) )
|
||||
media_viewer.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD_CHARACTER, ord( '+' ), [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'zoom_in' ) )
|
||||
media_viewer.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD_CHARACTER, ord( '-' ), [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'zoom_out' ) )
|
||||
|
||||
media_viewer.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_SCROLL_UP, [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'zoom_in' ) )
|
||||
media_viewer.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_SCROLL_DOWN, [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'zoom_out' ) )
|
||||
|
|
|
@ -384,7 +384,6 @@ class FrameGUI( ClientGUITopLevelWindows.MainFrameThatResizes ):
|
|||
|
||||
self._widget_event_filter.EVT_LEFT_DCLICK( self.EventFrameNewPage )
|
||||
self._widget_event_filter.EVT_MIDDLE_DOWN( self.EventFrameNewPage )
|
||||
self._widget_event_filter.EVT_RIGHT_DOWN( self.EventFrameNotebookMenu )
|
||||
self._widget_event_filter.EVT_SET_FOCUS( self.EventFocus )
|
||||
self._widget_event_filter.EVT_ICONIZE( self.EventIconize )
|
||||
|
||||
|
@ -834,11 +833,15 @@ class FrameGUI( ClientGUITopLevelWindows.MainFrameThatResizes ):
|
|||
|
||||
if result == QW.QDialog.Accepted:
|
||||
|
||||
self._notebook.SaveGUISession( 'last session' )
|
||||
self._notebook.SaveGUISession( 'exit session' )
|
||||
session = self._notebook.GetCurrentGUISession( 'last session' )
|
||||
|
||||
# session save causes a db read in the menu refresh, so let's put this off just a bit
|
||||
self._controller.CallLater( 1.5, self._controller.Write, 'backup', path )
|
||||
self._controller.SaveGUISession( session )
|
||||
|
||||
session.SetName( 'exit session' )
|
||||
|
||||
self._controller.SaveGUISession( session )
|
||||
|
||||
self._controller.Write( 'backup', path )
|
||||
|
||||
|
||||
|
||||
|
@ -2301,22 +2304,36 @@ class FrameGUI( ClientGUITopLevelWindows.MainFrameThatResizes ):
|
|||
qt_style_name = self._controller.new_options.GetNoneableString( 'qt_style_name' )
|
||||
qt_stylesheet_name = self._controller.new_options.GetNoneableString( 'qt_stylesheet_name' )
|
||||
|
||||
if qt_style_name is None:
|
||||
try:
|
||||
|
||||
ClientGUIStyle.SetStyle( ClientGUIStyle.ORIGINAL_STYLE )
|
||||
if qt_style_name is None:
|
||||
|
||||
ClientGUIStyle.SetStyleFromName( ClientGUIStyle.ORIGINAL_STYLE_NAME )
|
||||
|
||||
else:
|
||||
|
||||
ClientGUIStyle.SetStyleFromName( qt_style_name )
|
||||
|
||||
|
||||
else:
|
||||
except Exception as e:
|
||||
|
||||
ClientGUIStyle.SetStyle( qt_style_name )
|
||||
HydrusData.ShowException( e )
|
||||
|
||||
|
||||
if qt_stylesheet_name is None:
|
||||
try:
|
||||
|
||||
ClientGUIStyle.ClearStylesheet()
|
||||
if qt_stylesheet_name is None:
|
||||
|
||||
ClientGUIStyle.ClearStylesheet()
|
||||
|
||||
else:
|
||||
|
||||
ClientGUIStyle.SetStylesheetFromPath( qt_stylesheet_name )
|
||||
|
||||
|
||||
else:
|
||||
except Exception as e:
|
||||
|
||||
ClientGUIStyle.SetStylesheet( qt_stylesheet_name )
|
||||
HydrusData.ShowException( e )
|
||||
|
||||
|
||||
self._controller.pub( 'wake_daemons' )
|
||||
|
@ -3562,9 +3579,9 @@ The password is cleartext here but obscured in the entry dialog. Enter a blank p
|
|||
|
||||
|
||||
|
||||
if self.isMinimized() or dialog_open:
|
||||
if self.isMinimized() or dialog_open or not ClientGUIFunctions.TLWOrChildIsActive( self ):
|
||||
|
||||
self._controller.CallLaterQtSafe(self, 2, self.AddModalMessage, job_key)
|
||||
self._controller.CallLaterQtSafe( self, 0.5, self.AddModalMessage, job_key )
|
||||
|
||||
else:
|
||||
|
||||
|
@ -3594,21 +3611,30 @@ The password is cleartext here but obscured in the entry dialog. Enter a blank p
|
|||
|
||||
if only_save_last_session_during_idle and not self._controller.CurrentlyIdle():
|
||||
|
||||
next_call_delay = 60
|
||||
self._controller.CallLaterQtSafe( self, 60, self.AutoSaveLastSession )
|
||||
|
||||
else:
|
||||
|
||||
if HC.options[ 'default_gui_session' ] == 'last session':
|
||||
|
||||
self._notebook.SaveGUISession( 'last session' )
|
||||
session = self._notebook.GetCurrentGUISession( 'last session' )
|
||||
|
||||
callable = self.AutoSaveLastSession
|
||||
|
||||
last_session_save_period_minutes = self._controller.new_options.GetInteger( 'last_session_save_period_minutes' )
|
||||
|
||||
next_call_delay = last_session_save_period_minutes * 60
|
||||
|
||||
def do_it( controller, session, win, next_call_delay, callable ):
|
||||
|
||||
controller.SaveGUISession( session )
|
||||
|
||||
controller.CallLaterQtSafe( win, next_call_delay, callable )
|
||||
|
||||
|
||||
self._controller.CallToThread( do_it, self._controller, session, self, next_call_delay, callable )
|
||||
|
||||
|
||||
last_session_save_period_minutes = self._controller.new_options.GetInteger( 'last_session_save_period_minutes' )
|
||||
|
||||
next_call_delay = last_session_save_period_minutes * 60
|
||||
|
||||
|
||||
self._controller.CallLaterQtSafe( self, next_call_delay, self.AutoSaveLastSession )
|
||||
|
||||
|
||||
def DeleteAllClosedPages( self ):
|
||||
|
@ -3706,11 +3732,18 @@ The password is cleartext here but obscured in the entry dialog. Enter a blank p
|
|||
self._notebook.EventNewPageFromScreenPosition( screen_position )
|
||||
|
||||
|
||||
def EventFrameNotebookMenu( self, event ):
|
||||
def mouseReleaseEvent( self, event ):
|
||||
|
||||
if event.button() != QC.Qt.RightButton:
|
||||
|
||||
ClientGUITopLevelWindows.MainFrameThatResizes.mouseReleaseEvent( self, event )
|
||||
|
||||
return
|
||||
|
||||
|
||||
screen_position = QG.QCursor.pos()
|
||||
|
||||
self._notebook.EventMenuFromScreenPosition( screen_position )
|
||||
self._notebook.ShowMenuFromScreenPosition( screen_position )
|
||||
|
||||
|
||||
def EventIconize( self, event ):
|
||||
|
@ -3849,13 +3882,6 @@ The password is cleartext here but obscured in the entry dialog. Enter a blank p
|
|||
|
||||
try:
|
||||
|
||||
self._notebook.SaveGUISession( 'last session' )
|
||||
self._notebook.SaveGUISession( 'exit session' )
|
||||
|
||||
self._DestroyTimers()
|
||||
|
||||
self.DeleteAllClosedPages() # Obsolote comment, preserved just in case: wx crashes if any are left in here, wew
|
||||
|
||||
if self._message_manager:
|
||||
|
||||
self._message_manager.CleanBeforeDestroy()
|
||||
|
@ -3863,7 +3889,7 @@ The password is cleartext here but obscured in the entry dialog. Enter a blank p
|
|||
self._message_manager.hide()
|
||||
|
||||
|
||||
self._notebook.CleanBeforeDestroy()
|
||||
#
|
||||
|
||||
if self._new_options.GetBoolean( 'saving_sash_positions_on_exit' ):
|
||||
|
||||
|
@ -3872,6 +3898,29 @@ The password is cleartext here but obscured in the entry dialog. Enter a blank p
|
|||
|
||||
ClientGUITopLevelWindows.SaveTLWSizeAndPosition( self, self._frame_key )
|
||||
|
||||
for tlw in QW.QApplication.topLevelWidgets():
|
||||
|
||||
tlw.hide()
|
||||
|
||||
|
||||
#
|
||||
|
||||
session = self._notebook.GetCurrentGUISession( 'last session' )
|
||||
|
||||
self._controller.SaveGUISession( session )
|
||||
|
||||
session.SetName( 'exit session' )
|
||||
|
||||
self._controller.SaveGUISession( session )
|
||||
|
||||
#
|
||||
|
||||
self._DestroyTimers()
|
||||
|
||||
self.DeleteAllClosedPages() # Obsolote comment, preserved just in case: wx crashes if any are left in here, wew
|
||||
|
||||
self._notebook.CleanBeforeDestroy()
|
||||
|
||||
self._controller.WriteSynchronous( 'save_options', HC.options )
|
||||
|
||||
self._controller.WriteSynchronous( 'serialisable', self._new_options )
|
||||
|
@ -3881,11 +3930,6 @@ The password is cleartext here but obscured in the entry dialog. Enter a blank p
|
|||
HydrusData.PrintException( e )
|
||||
|
||||
|
||||
for tlw in QW.QApplication.topLevelWidgets():
|
||||
|
||||
tlw.hide()
|
||||
|
||||
|
||||
if HG.emergency_exit:
|
||||
|
||||
self.deleteLater()
|
||||
|
@ -4451,7 +4495,7 @@ The password is cleartext here but obscured in the entry dialog. Enter a blank p
|
|||
ClientGUIMenus.AppendMenuItem( gui_actions, 'make a new page in five seconds', 'Throw a delayed page at the main notebook, giving you time to minimise or otherwise alter the client before it arrives.', self._controller.CallLater, 5, self._controller.pub, 'new_page_query', CC.LOCAL_FILE_SERVICE_KEY )
|
||||
ClientGUIMenus.AppendMenuItem( gui_actions, 'make a parentless text ctrl dialog', 'Make a parentless text control in a dialog to test some character event catching.', self._DebugMakeParentlessTextCtrl )
|
||||
ClientGUIMenus.AppendMenuItem( gui_actions, 'force a main gui layout now', 'Tell the gui to relayout--useful to test some gui bootup layout issues.', self.adjustSize )
|
||||
ClientGUIMenus.AppendMenuItem( gui_actions, 'save \'last session\' gui session', 'Make an immediate save of the \'last session\' gui session. Mostly for testing crashes, where last session is not saved correctly.', self._notebook.SaveGUISession, 'last session' )
|
||||
ClientGUIMenus.AppendMenuItem( gui_actions, 'save \'last session\' gui session', 'Make an immediate save of the \'last session\' gui session. Mostly for testing crashes, where last session is not saved correctly.', self.ProposeSaveGUISession, 'last session' )
|
||||
ClientGUIMenus.AppendMenuItem( gui_actions, 'run the ui test', 'Run hydrus_dev\'s weekly UI Test. Guaranteed to work and not mess up your session, ha ha.', self._RunUITest )
|
||||
|
||||
ClientGUIMenus.AppendMenu( debug, gui_actions, 'gui actions' )
|
||||
|
@ -4697,10 +4741,10 @@ The password is cleartext here but obscured in the entry dialog. Enter a blank p
|
|||
continue
|
||||
|
||||
|
||||
ClientGUIMenus.AppendMenuItem( save, name, 'Save the existing open pages as a session.', self._notebook.SaveGUISession, name )
|
||||
ClientGUIMenus.AppendMenuItem( save, name, 'Save the existing open pages as a session.', self.ProposeSaveGUISession, name )
|
||||
|
||||
|
||||
ClientGUIMenus.AppendMenuItem( save, 'as new session', 'Save the existing open pages as a session.', self._notebook.SaveGUISession )
|
||||
ClientGUIMenus.AppendMenuItem( save, 'as new session', 'Save the existing open pages as a session.', self.ProposeSaveGUISession )
|
||||
|
||||
ClientGUIMenus.AppendMenu( sessions, save, 'save' )
|
||||
|
||||
|
@ -5264,6 +5308,76 @@ The password is cleartext here but obscured in the entry dialog. Enter a blank p
|
|||
return command_processed
|
||||
|
||||
|
||||
def ProposeSaveGUISession( self, name = None, suggested_name = '', notebook = None ):
|
||||
|
||||
if notebook is None:
|
||||
|
||||
notebook = self._notebook
|
||||
|
||||
|
||||
if name is None:
|
||||
|
||||
while True:
|
||||
|
||||
with ClientGUIDialogs.DialogTextEntry( self, 'Enter a name for the new session.', default = suggested_name ) as dlg:
|
||||
|
||||
if dlg.exec() == QW.QDialog.Accepted:
|
||||
|
||||
name = dlg.GetValue()
|
||||
|
||||
if name in ClientGUIPages.RESERVED_SESSION_NAMES:
|
||||
|
||||
QW.QMessageBox.critical( self, 'Error', 'Sorry, you cannot have that name! Try another.' )
|
||||
|
||||
else:
|
||||
|
||||
existing_session_names = self._controller.Read( 'serialisable_names', HydrusSerialisable.SERIALISABLE_TYPE_GUI_SESSION )
|
||||
|
||||
if name in existing_session_names:
|
||||
|
||||
message = 'Session "{}" already exists! Do you want to overwrite it?'.format( name )
|
||||
|
||||
result, closed_by_user = ClientGUIDialogsQuick.GetYesNo( self, message, title = 'Overwrite existing session?', yes_label = 'yes, overwrite', no_label = 'no, choose another name', check_for_cancelled = True )
|
||||
|
||||
if closed_by_user:
|
||||
|
||||
return
|
||||
|
||||
elif result == QW.QDialog.Rejected:
|
||||
|
||||
continue
|
||||
|
||||
|
||||
|
||||
break
|
||||
|
||||
|
||||
else:
|
||||
|
||||
return
|
||||
|
||||
|
||||
|
||||
|
||||
elif name not in ClientGUIPages.RESERVED_SESSION_NAMES: # i.e. a human asked to do this
|
||||
|
||||
message = 'Overwrite this session?'
|
||||
|
||||
result = ClientGUIDialogsQuick.GetYesNo( self, message, title = 'Overwrite existing session?', yes_label = 'yes, overwrite', no_label = 'no' )
|
||||
|
||||
if result != QW.QDialog.Accepted:
|
||||
|
||||
return
|
||||
|
||||
|
||||
|
||||
#
|
||||
|
||||
session = notebook.GetCurrentGUISession( name )
|
||||
|
||||
self._controller.CallToThread( self._controller.SaveGUISession, session )
|
||||
|
||||
|
||||
def RefreshMenu( self ):
|
||||
|
||||
if not QP.isValid( self ) or not self or self.isMinimized():
|
||||
|
|
|
@ -783,13 +783,13 @@ class AutoCompleteDropdown( QW.QWidget ):
|
|||
|
||||
def _ShouldShow( self ):
|
||||
|
||||
tlp_active = self.window().isActiveWindow() or self._dropdown_window.isActiveWindow()
|
||||
tlw_active = self.window().isActiveWindow() or self._dropdown_window.isActiveWindow()
|
||||
|
||||
visible = self._text_ctrl.isVisible()
|
||||
|
||||
focus_remains_on_self_or_children = ClientGUIFunctions.WindowOrAnyTLPChildHasFocus( self )
|
||||
focus_remains_on_self_or_children = ClientGUIFunctions.WidgetOrAnyTLWChildHasFocus( self )
|
||||
|
||||
return tlp_active and visible and focus_remains_on_self_or_children
|
||||
return tlw_active and visible and focus_remains_on_self_or_children
|
||||
|
||||
|
||||
def _ShouldTakeResponsibilityForEnter( self ):
|
||||
|
|
|
@ -1221,6 +1221,51 @@ class Canvas( QW.QWidget ):
|
|||
return True
|
||||
|
||||
|
||||
def _DoEdgePan( self, pan_type ):
|
||||
|
||||
if self._current_media is None:
|
||||
|
||||
return
|
||||
|
||||
|
||||
my_size = self.size()
|
||||
media_size = self._media_container.size()
|
||||
|
||||
delta_x = 0
|
||||
delta_y = 0
|
||||
|
||||
if pan_type == 'pan_top_edge':
|
||||
|
||||
delta_y = - self._media_window_pos.y()
|
||||
|
||||
elif pan_type == 'pan_left_edge':
|
||||
|
||||
delta_x = - self._media_window_pos.x()
|
||||
|
||||
elif pan_type == 'pan_bottom_edge':
|
||||
|
||||
delta_y = my_size.height() - ( self._media_window_pos.y() + media_size.height() )
|
||||
|
||||
elif pan_type == 'pan_right_edge':
|
||||
|
||||
delta_x = my_size.width() - ( self._media_window_pos.x() + media_size.width() )
|
||||
|
||||
elif pan_type == 'pan_vertical_center':
|
||||
|
||||
delta_y = ( my_size.height() / 2 ) - ( self._media_window_pos.y() + ( media_size.height() / 2 ) )
|
||||
|
||||
elif pan_type == 'pan_horizontal_center':
|
||||
|
||||
delta_x = ( my_size.width() / 2 ) - ( self._media_window_pos.x() + ( media_size.width() / 2 ) )
|
||||
|
||||
|
||||
delta = QC.QPoint( delta_x, delta_y )
|
||||
|
||||
self._media_window_pos += delta
|
||||
|
||||
self._DrawCurrentMedia()
|
||||
|
||||
|
||||
def _DoManualPan( self, delta_x_step, delta_y_step ):
|
||||
|
||||
if self._current_media is None:
|
||||
|
@ -1355,7 +1400,7 @@ class Canvas( QW.QWidget ):
|
|||
|
||||
def _IShouldCatchShortcutEvent( self, event = None ):
|
||||
|
||||
return ClientGUIShortcuts.IShouldCatchShortcutEvent( self, event = event, child_tlp_classes_who_can_pass_up = ( ClientGUIHoverFrames.FullscreenHoverFrame, ) )
|
||||
return ClientGUIShortcuts.IShouldCatchShortcutEvent( self, event = event, child_tlw_classes_who_can_pass_up = ( ClientGUIHoverFrames.FullscreenHoverFrame, ) )
|
||||
|
||||
|
||||
def _MaintainZoom( self, previous_media ):
|
||||
|
@ -2167,6 +2212,10 @@ class Canvas( QW.QWidget ):
|
|||
|
||||
self._DoManualPan( 1, 0 )
|
||||
|
||||
elif action in ( 'pan_top_edge', 'pan_bottom_edge', 'pan_left_edge', 'pan_right_edge', 'pan_vertical_center', 'pan_horizontal_center' ):
|
||||
|
||||
self._DoEdgePan( action )
|
||||
|
||||
elif action == 'pause_media':
|
||||
|
||||
self._PauseCurrentMedia()
|
||||
|
@ -2332,11 +2381,11 @@ class CanvasPanel( Canvas ):
|
|||
HG.client_controller.sub( self, 'ProcessContentUpdates', 'content_updates_gui' )
|
||||
|
||||
|
||||
def mousePressEvent( self, event ):
|
||||
def mouseReleaseEvent( self, event ):
|
||||
|
||||
if event.button() != QC.Qt.RightButton:
|
||||
|
||||
event.ignore()
|
||||
Canvas.mouseReleaseEvent( self, event )
|
||||
|
||||
return
|
||||
|
||||
|
@ -2804,6 +2853,11 @@ class CanvasWithHovers( CanvasWithDetails ):
|
|||
|
||||
def EventDragBegin( self, event ):
|
||||
|
||||
if event.button() != QC.Qt.LeftButton:
|
||||
|
||||
return True
|
||||
|
||||
|
||||
point = event.pos()
|
||||
|
||||
self.BeginDrag( point = point )
|
||||
|
@ -2813,6 +2867,11 @@ class CanvasWithHovers( CanvasWithDetails ):
|
|||
|
||||
def EventDragEnd( self, event ):
|
||||
|
||||
if event.button() != QC.Qt.LeftButton:
|
||||
|
||||
return True
|
||||
|
||||
|
||||
self._last_drag_pos = None
|
||||
|
||||
return True # was: event.ignore()
|
||||
|
@ -2826,7 +2885,7 @@ class CanvasWithHovers( CanvasWithDetails ):
|
|||
|
||||
show_mouse = self.cursor() == QG.QCursor( QC.Qt.ArrowCursor )
|
||||
|
||||
is_dragging = ( event.type() == QC.QEvent.MouseMove and event.buttons() != QC.Qt.NoButton ) and self._last_drag_pos is not None
|
||||
is_dragging = ( event.type() == QC.QEvent.MouseMove and event.buttons() == QC.Qt.LeftButton ) and self._last_drag_pos is not None
|
||||
has_moved = event_pos != self._last_motion_pos
|
||||
|
||||
if is_dragging:
|
||||
|
@ -2889,6 +2948,8 @@ class CanvasWithHovers( CanvasWithDetails ):
|
|||
self.setCursor( QG.QCursor( QC.Qt.BlankCursor ) )
|
||||
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def FullscreenSwitch( self, canvas_key ):
|
||||
|
||||
|
@ -4683,11 +4744,11 @@ class CanvasMediaListBrowser( CanvasMediaListNavigable ):
|
|||
|
||||
|
||||
|
||||
def mousePressEvent( self, event ):
|
||||
def mouseReleaseEvent( self, event ):
|
||||
|
||||
if event.button() != QC.Qt.RightButton:
|
||||
|
||||
event.ignore()
|
||||
CanvasMediaListNavigable.mouseReleaseEvent( self, event )
|
||||
|
||||
return
|
||||
|
||||
|
|
|
@ -268,7 +268,6 @@ class BetterColourControl( QP.ColourPickerCtrl ):
|
|||
QP.ColourPickerCtrl.__init__( self, *args, **kwargs )
|
||||
|
||||
self._widget_event_filter = QP.WidgetEventFilter( self )
|
||||
self._widget_event_filter.EVT_RIGHT_DOWN( self.EventMenu )
|
||||
|
||||
|
||||
def _ImportHexFromClipboard( self ):
|
||||
|
@ -314,7 +313,14 @@ class BetterColourControl( QP.ColourPickerCtrl ):
|
|||
self.SetColour( colour )
|
||||
|
||||
|
||||
def EventMenu( self, event ):
|
||||
def mouseReleaseEvent( self, event ):
|
||||
|
||||
if event.button() != QC.Qt.RightButton:
|
||||
|
||||
QP.ColourPickerCtrl.mouseReleaseEvent( self, event )
|
||||
|
||||
return
|
||||
|
||||
|
||||
menu = QW.QMenu()
|
||||
|
||||
|
@ -446,10 +452,16 @@ class BetterHyperLink( BetterStaticText ):
|
|||
|
||||
self.setTextFormat( QC.Qt.RichText )
|
||||
self.setTextInteractionFlags( QC.Qt.TextBrowserInteraction )
|
||||
self.setOpenExternalLinks( True )
|
||||
|
||||
self.setText( '<a href="{}">{}</a>'.format( url, label ) )
|
||||
|
||||
self.linkActivated.connect( self.Activated )
|
||||
|
||||
|
||||
def Activated( self ):
|
||||
|
||||
ClientPaths.LaunchURLInWebBrowser( self._url )
|
||||
|
||||
|
||||
class BufferedWindow( QW.QWidget ):
|
||||
|
||||
|
@ -1057,8 +1069,6 @@ class ListBook( QW.QWidget ):
|
|||
|
||||
QW.QWidget.__init__( self, *args, **kwargs )
|
||||
|
||||
QP.SetBackgroundColour( self, QP.GetSystemColour( QG.QPalette.Button ) )
|
||||
|
||||
self._keys_to_active_pages = {}
|
||||
self._keys_to_proto_pages = {}
|
||||
|
||||
|
@ -1067,8 +1077,6 @@ class ListBook( QW.QWidget ):
|
|||
|
||||
self._empty_panel = QW.QWidget( self )
|
||||
|
||||
QP.SetBackgroundColour( self._empty_panel, QP.GetSystemColour( QG.QPalette.Button ) )
|
||||
|
||||
self._current_key = None
|
||||
|
||||
self._current_panel = self._empty_panel
|
||||
|
@ -2383,6 +2391,7 @@ class RegexButton( BetterButton ):
|
|||
ClientGUIMenus.AppendSeparator( submenu )
|
||||
|
||||
for ( regex_phrase, description ) in HC.options[ 'regex_favourites' ]:
|
||||
|
||||
ClientGUIMenus.AppendMenuItem( submenu, description, 'copy this phrase to the clipboard', HG.client_controller.pub, 'clipboard', 'text', regex_phrase )
|
||||
|
||||
|
||||
|
@ -2424,8 +2433,6 @@ class StaticBox( QW.QFrame ):
|
|||
|
||||
self._spacer = QW.QSpacerItem( 0, 0, QW.QSizePolicy.Minimum, QW.QSizePolicy.MinimumExpanding )
|
||||
|
||||
QP.SetBackgroundColour( self, QP.GetSystemColour( QG.QPalette.Button ) )
|
||||
|
||||
self._sizer = QP.VBoxLayout()
|
||||
|
||||
normal_font = QW.QApplication.font()
|
||||
|
|
|
@ -108,9 +108,9 @@ class Dialog( QP.Dialog ):
|
|||
|
||||
if parent is not None and position == 'topleft':
|
||||
|
||||
parent_tlp = self.parentWidget().window()
|
||||
parent_tlw = self.parentWidget().window()
|
||||
|
||||
( pos_x, pos_y ) = parent_tlp.pos().toTuple()
|
||||
( pos_x, pos_y ) = parent_tlw.pos().toTuple()
|
||||
|
||||
pos = QC.QPoint( pos_x + 50, pos_y + 50 )
|
||||
|
||||
|
@ -125,8 +125,6 @@ class Dialog( QP.Dialog ):
|
|||
|
||||
self._new_options = HG.client_controller.new_options
|
||||
|
||||
QP.SetBackgroundColour( self, QP.GetSystemColour( QG.QPalette.Button ) )
|
||||
|
||||
self.setWindowIcon( QG.QIcon( HG.client_controller.frame_icon_pixmap ) )
|
||||
|
||||
self._widget_event_filter = QP.WidgetEventFilter( self )
|
||||
|
@ -974,7 +972,7 @@ class DialogSelectFromURLTree( Dialog ):
|
|||
def __init__( self, parent, url_tree ):
|
||||
|
||||
Dialog.__init__( self, parent, 'select items' )
|
||||
|
||||
|
||||
self._tree = QP.TreeWidgetWithInheritedCheckState( self )
|
||||
|
||||
self._ok = ClientGUICommon.BetterButton( self, 'OK', self.done, QW.QDialog.Accepted )
|
||||
|
@ -996,7 +994,7 @@ class DialogSelectFromURLTree( Dialog ):
|
|||
self._tree.addTopLevelItem( root_item )
|
||||
|
||||
self._AddDirectory( root_item, children )
|
||||
|
||||
|
||||
root_item.setExpanded( True )
|
||||
|
||||
#
|
||||
|
|
|
@ -178,8 +178,6 @@ class DialogManageRatings( ClientGUIDialogs.Dialog ):
|
|||
|
||||
QW.QWidget.__init__( self, parent )
|
||||
|
||||
QP.SetBackgroundColour( self, QP.GetSystemColour( QG.QPalette.Button ) )
|
||||
|
||||
self._services = services
|
||||
|
||||
self._media = media
|
||||
|
|
|
@ -374,7 +374,6 @@ class FileSeedCacheButton( ClientGUICommon.BetterBitmapButton ):
|
|||
self.setToolTip( 'open detailed file import status--right-click for quick actions, if applicable' )
|
||||
|
||||
self._widget_event_filter = QP.WidgetEventFilter( self )
|
||||
self._widget_event_filter.EVT_RIGHT_DOWN( self.EventShowMenu )
|
||||
|
||||
|
||||
def _ClearFileSeeds( self, statuses_to_remove ):
|
||||
|
@ -533,9 +532,9 @@ class FileSeedCacheButton( ClientGUICommon.BetterBitmapButton ):
|
|||
|
||||
file_seed_cache = self._file_seed_cache_get_callable()
|
||||
|
||||
tlp = self.window()
|
||||
tlw = self.window()
|
||||
|
||||
if isinstance( tlp, QP.Dialog ):
|
||||
if isinstance( tlw, QP.Dialog ):
|
||||
|
||||
if self._file_seed_cache_set_callable is None: # throw up a dialog that edits the file_seed cache in place
|
||||
|
||||
|
@ -578,7 +577,14 @@ class FileSeedCacheButton( ClientGUICommon.BetterBitmapButton ):
|
|||
|
||||
|
||||
|
||||
def EventShowMenu( self, event ):
|
||||
def mouseReleaseEvent( self, event ):
|
||||
|
||||
if event.button() != QC.Qt.RightButton:
|
||||
|
||||
ClientGUICommon.BetterBitmapButton.mouseReleaseEvent( self, event )
|
||||
|
||||
return
|
||||
|
||||
|
||||
menu = QW.QMenu()
|
||||
|
||||
|
|
|
@ -283,9 +283,9 @@ def ClientToScreen( win, pos ):
|
|||
|
||||
if isinstance( pos, tuple ): pos = QP.TupleToQPoint( pos )
|
||||
|
||||
tlp = win.window()
|
||||
tlw = win.window()
|
||||
|
||||
if win.isVisible() and tlp.isVisible():
|
||||
if win.isVisible() and tlw.isVisible():
|
||||
|
||||
return win.mapToGlobal( pos )
|
||||
|
||||
|
@ -307,22 +307,24 @@ def ConvertTextToPixelWidth( window, char_cols ):
|
|||
|
||||
return int( window.fontMetrics().boundingRect( char_cols * 'x' ).width() * MAGIC_TEXT_PADDING )
|
||||
|
||||
def GetTLPParents( window ):
|
||||
def GetTLWParents( widget ):
|
||||
|
||||
window = window.window()
|
||||
widget_tlw = widget.window()
|
||||
|
||||
parents = []
|
||||
parent_tlws = []
|
||||
|
||||
parent = window.parentWidget()
|
||||
parent = widget_tlw.parentWidget()
|
||||
|
||||
while parent is not None:
|
||||
|
||||
parents.append( parent )
|
||||
parent_tlw = parent.window()
|
||||
|
||||
parent = parent.parentWidget()
|
||||
parent_tlws.append( parent_tlw )
|
||||
|
||||
parent = parent_tlw.parentWidget()
|
||||
|
||||
|
||||
return parents
|
||||
return parent_tlws
|
||||
|
||||
def IsQtAncestor( child, ancestor, through_tlws = False ):
|
||||
|
||||
|
@ -379,11 +381,32 @@ def SetBitmapButtonBitmap( button, bitmap ):
|
|||
|
||||
button.last_bitmap = bitmap
|
||||
|
||||
def TLPIsActive( window ):
|
||||
def TLWIsActive( window ):
|
||||
|
||||
return window.window() == QW.QApplication.activeWindow()
|
||||
|
||||
def WindowOrAnyTLPChildHasFocus( window ):
|
||||
def TLWOrChildIsActive( win ):
|
||||
|
||||
current_focus_tlw = QW.QApplication.activeWindow()
|
||||
|
||||
if current_focus_tlw is None:
|
||||
|
||||
return False
|
||||
|
||||
|
||||
if current_focus_tlw == win:
|
||||
|
||||
return True
|
||||
|
||||
|
||||
if win in GetTLWParents( current_focus_tlw ):
|
||||
|
||||
return True
|
||||
|
||||
|
||||
return False
|
||||
|
||||
def WidgetOrAnyTLWChildHasFocus( window ):
|
||||
|
||||
active_window = QW.QApplication.activeWindow()
|
||||
|
||||
|
@ -396,6 +419,7 @@ def WindowOrAnyTLPChildHasFocus( window ):
|
|||
|
||||
if widget is None:
|
||||
|
||||
# take active window in lieu of focus, if it is unavailable
|
||||
widget = active_window
|
||||
|
||||
|
||||
|
|
|
@ -296,7 +296,6 @@ class GallerySeedLogButton( ClientGUICommon.BetterBitmapButton ):
|
|||
self.setToolTip( 'open detailed gallery log--right-click for quick actions, if applicable' )
|
||||
|
||||
self._widget_event_filter = QP.WidgetEventFilter( self )
|
||||
self._widget_event_filter.EVT_RIGHT_DOWN( self.EventShowMenu )
|
||||
|
||||
|
||||
def _ClearGallerySeeds( self, statuses_to_remove ):
|
||||
|
@ -471,9 +470,9 @@ class GallerySeedLogButton( ClientGUICommon.BetterBitmapButton ):
|
|||
|
||||
gallery_seed_log = self._gallery_seed_log_get_callable()
|
||||
|
||||
tlp = self.window()
|
||||
tlw = self.window()
|
||||
|
||||
if isinstance( tlp, QP.Dialog ):
|
||||
if isinstance( tlw, QP.Dialog ):
|
||||
|
||||
if self._gallery_seed_log_set_callable is None: # throw up a dialog that edits the gallery_seed log in place
|
||||
|
||||
|
@ -516,7 +515,14 @@ class GallerySeedLogButton( ClientGUICommon.BetterBitmapButton ):
|
|||
|
||||
|
||||
|
||||
def EventShowMenu( self, event ):
|
||||
def mouseReleaseEvent( self, event ):
|
||||
|
||||
if event.button() != QC.Qt.RightButton:
|
||||
|
||||
ClientGUICommon.BetterBitmapButton.mouseReleaseEvent( self, event )
|
||||
|
||||
return
|
||||
|
||||
|
||||
menu = QW.QMenu()
|
||||
|
||||
|
|
|
@ -43,7 +43,6 @@ class FullscreenHoverFrame( QW.QFrame ):
|
|||
|
||||
self._last_ideal_position = None
|
||||
|
||||
QP.SetBackgroundColour( self, QP.GetSystemColour( QG.QPalette.Button ) )
|
||||
self.setCursor( QG.QCursor( QC.Qt.ArrowCursor ) )
|
||||
|
||||
self._hide_until = None
|
||||
|
@ -60,9 +59,9 @@ class FullscreenHoverFrame( QW.QFrame ):
|
|||
raise NotImplementedError()
|
||||
|
||||
|
||||
def _SizeAndPosition( self ):
|
||||
def _SizeAndPosition( self, force = False ):
|
||||
|
||||
if self.parentWidget().isVisible():
|
||||
if self.parentWidget().isVisible() or force:
|
||||
|
||||
( should_resize, my_ideal_size, my_ideal_position ) = self._GetIdealSizeAndPosition()
|
||||
|
||||
|
@ -71,13 +70,6 @@ class FullscreenHoverFrame( QW.QFrame ):
|
|||
self.resize( QP.TupleToQSize( my_ideal_size ) )
|
||||
|
||||
|
||||
changes_occurred = should_resize or self.pos() != QP.TupleToQPoint( my_ideal_position )
|
||||
|
||||
if HC.PLATFORM_MACOS and changes_occurred and self._always_on_top:
|
||||
|
||||
self.raise_()
|
||||
|
||||
|
||||
self.move( QP.TupleToQPoint( my_ideal_position ) )
|
||||
|
||||
|
||||
|
@ -99,38 +91,21 @@ class FullscreenHoverFrame( QW.QFrame ):
|
|||
|
||||
def TIMERUIUpdate( self ):
|
||||
|
||||
current_focus_tlp = QW.QApplication.activeWindow()
|
||||
current_focus_tlw = QW.QApplication.activeWindow()
|
||||
|
||||
focus_is_on_descendant = ClientGUIFunctions.IsQtAncestor( current_focus_tlp, self._my_canvas.window(), through_tlws = True )
|
||||
focus_has_right_window_type = isinstance( current_focus_tlp, ( ClientGUICanvas.CanvasFrame, FullscreenHoverFrame ) )
|
||||
focus_is_on_descendant = ClientGUIFunctions.IsQtAncestor( current_focus_tlw, self._my_canvas.window(), through_tlws = True )
|
||||
focus_has_right_window_type = isinstance( current_focus_tlw, ( ClientGUICanvas.CanvasFrame, FullscreenHoverFrame ) )
|
||||
|
||||
focus_is_good = focus_is_on_descendant and focus_has_right_window_type
|
||||
|
||||
new_options = HG.client_controller.new_options
|
||||
|
||||
if self._always_on_top or new_options.GetBoolean( 'always_show_hover_windows' ):
|
||||
if self._always_on_top:
|
||||
|
||||
self._SizeAndPosition()
|
||||
|
||||
self.show()
|
||||
|
||||
if HC.PLATFORM_MACOS:
|
||||
|
||||
( mouse_x, mouse_y ) = QG.QCursor.pos().toTuple()
|
||||
|
||||
( my_x, my_y ) = self.mapToGlobal( self.pos() ).toTuple()
|
||||
|
||||
( my_width, my_height ) = self.size().toTuple()
|
||||
|
||||
in_actual_x = my_x <= mouse_x and mouse_x <= my_x + my_width
|
||||
in_actual_y = my_y <= mouse_y and mouse_y <= my_y + my_height
|
||||
|
||||
if in_actual_x and in_actual_y and focus_is_good:
|
||||
|
||||
self.raise_()
|
||||
|
||||
|
||||
|
||||
return
|
||||
|
||||
|
||||
|
@ -195,11 +170,11 @@ class FullscreenHoverFrame( QW.QFrame ):
|
|||
|
||||
dialog_open = False
|
||||
|
||||
tlps = QW.QApplication.topLevelWidgets()
|
||||
tlws = QW.QApplication.topLevelWidgets()
|
||||
|
||||
for tlp in tlps:
|
||||
for tlw in tlws:
|
||||
|
||||
if isinstance( tlp, QW.QDialog ) and not isinstance( tlp, ClientGUICanvas.CanvasFrame ) and tlp.isModal():
|
||||
if isinstance( tlw, QW.QDialog ) and not isinstance( tlw, ClientGUICanvas.CanvasFrame ) and tlw.isModal():
|
||||
|
||||
dialog_open = True
|
||||
|
||||
|
@ -211,7 +186,7 @@ class FullscreenHoverFrame( QW.QFrame ):
|
|||
|
||||
mouse_is_over_something_important = mouse_is_near_animation_bar # this used to have the flash media window test to ensure mouse over flash window hid hovers going over it
|
||||
|
||||
hide_focus_is_good = focus_is_good or current_focus_tlp is None # don't hide if focus is either gone to another problem or temporarily sperging-out due to a click-transition or similar
|
||||
hide_focus_is_good = focus_is_good or current_focus_tlw is None # don't hide if focus is either gone to another problem or temporarily sperging-out due to a click-transition or similar
|
||||
|
||||
ready_to_show = in_position and not mouse_is_over_something_important and focus_is_good and not dialog_open and not menu_open
|
||||
ready_to_hide = not menu_open and ( not in_position or dialog_open or not hide_focus_is_good )
|
||||
|
@ -231,7 +206,7 @@ class FullscreenHoverFrame( QW.QFrame ):
|
|||
tuples.append( ( 'mouse near animation bar: ', mouse_is_near_animation_bar ) )
|
||||
tuples.append( ( 'focus is good: ', focus_is_good ) )
|
||||
tuples.append( ( 'focus is on descendant: ', focus_is_on_descendant ) )
|
||||
tuples.append( ( 'current focus tlp: ', current_focus_tlp ) )
|
||||
tuples.append( ( 'current focus tlw: ', current_focus_tlw ) )
|
||||
|
||||
message = os.linesep * 2 + os.linesep.join( ( a + str( b ) for ( a, b ) in tuples ) )
|
||||
|
||||
|
@ -251,20 +226,6 @@ class FullscreenHoverFrame( QW.QFrame ):
|
|||
|
||||
self.show()
|
||||
|
||||
# in case focus jumps around from the show, let's test it and raise as needed
|
||||
|
||||
current_focus_tlp = QW.QApplication.activeWindow()
|
||||
|
||||
focus_is_on_descendant = ClientGUIFunctions.IsQtAncestor( current_focus_tlp, self._my_canvas.window(), through_tlws = True )
|
||||
focus_has_right_window_type = isinstance( current_focus_tlp, ( ClientGUICanvas.CanvasFrame, FullscreenHoverFrame ) )
|
||||
|
||||
focus_is_good = focus_is_on_descendant and focus_has_right_window_type
|
||||
|
||||
if not focus_is_good:
|
||||
|
||||
self.raise_()
|
||||
|
||||
|
||||
|
||||
elif ready_to_hide:
|
||||
|
||||
|
@ -605,6 +566,8 @@ class FullscreenHoverFrameTop( FullscreenHoverFrame ):
|
|||
|
||||
def _GetIdealSizeAndPosition( self ):
|
||||
|
||||
# clip this and friends to availableScreenGeometry for size and position, not rely 100% on parent
|
||||
|
||||
parent_window = self.parentWidget().window()
|
||||
|
||||
( parent_width, parent_height ) = parent_window.size().toTuple()
|
||||
|
@ -744,8 +707,6 @@ class FullscreenHoverFrameTop( FullscreenHoverFrame ):
|
|||
self._undelete_button.show()
|
||||
|
||||
|
||||
self._SizeAndPosition()
|
||||
|
||||
|
||||
|
||||
def _ResetText( self ):
|
||||
|
@ -915,6 +876,11 @@ class FullscreenHoverFrameTop( FullscreenHoverFrame ):
|
|||
|
||||
self._ResetButtons()
|
||||
|
||||
# minimumsize is not immediately updated without this
|
||||
self.layout().activate()
|
||||
|
||||
self._SizeAndPosition( force = True )
|
||||
|
||||
|
||||
|
||||
def SetIndexString( self, canvas_key, text ):
|
||||
|
@ -1223,6 +1189,11 @@ class FullscreenHoverFrameTopRight( FullscreenHoverFrame ):
|
|||
|
||||
self._ResetData()
|
||||
|
||||
# minimumsize is not immediately updated without this
|
||||
self.layout().activate()
|
||||
|
||||
self._SizeAndPosition( force = True )
|
||||
|
||||
|
||||
|
||||
class FullscreenHoverFrameTags( FullscreenHoverFrame ):
|
||||
|
|
|
@ -2101,7 +2101,6 @@ class TagImportOptionsButton( ClientGUICommon.BetterButton ):
|
|||
#
|
||||
|
||||
self._widget_event_filter = QP.WidgetEventFilter( self )
|
||||
self._widget_event_filter.EVT_RIGHT_DOWN( self.EventShowMenu )
|
||||
|
||||
|
||||
def _Copy( self ):
|
||||
|
@ -2184,7 +2183,14 @@ class TagImportOptionsButton( ClientGUICommon.BetterButton ):
|
|||
|
||||
|
||||
|
||||
def EventShowMenu( self, event ):
|
||||
def mouseReleaseEvent( self, event ):
|
||||
|
||||
if event.button() != QC.Qt.RightButton:
|
||||
|
||||
ClientGUICommon.BetterButton.mouseReleaseEvent( self, event )
|
||||
|
||||
return
|
||||
|
||||
|
||||
menu = QW.QMenu()
|
||||
|
||||
|
|
|
@ -864,6 +864,7 @@ class ListBox( QW.QScrollArea ):
|
|||
self._widget_event_filter = QP.WidgetEventFilter( self.widget() )
|
||||
|
||||
self._widget_event_filter.EVT_LEFT_DOWN( self.EventMouseSelect )
|
||||
self._widget_event_filter.EVT_RIGHT_DOWN( self.EventMouseSelect )
|
||||
self._widget_event_filter.EVT_LEFT_DCLICK( self.EventDClick )
|
||||
|
||||
|
||||
|
@ -1571,7 +1572,6 @@ class ListBoxTags( ListBox ):
|
|||
|
||||
self._UpdateBackgroundColour()
|
||||
|
||||
self._widget_event_filter.EVT_RIGHT_DOWN( self.EventMouseRightClick )
|
||||
self._widget_event_filter.EVT_MIDDLE_DOWN( self.EventMouseMiddleClick )
|
||||
|
||||
HG.client_controller.sub( self, 'ForceTagRecalc', 'refresh_all_tag_presentation_gui' )
|
||||
|
@ -1835,9 +1835,14 @@ class ListBoxTags( ListBox ):
|
|||
|
||||
|
||||
|
||||
def EventMouseRightClick( self, event ):
|
||||
def mouseReleaseEvent( self, event ):
|
||||
|
||||
self._HandleClick( event )
|
||||
if event.button() != QC.Qt.RightButton:
|
||||
|
||||
ListBox.mouseReleaseEvent( self, event )
|
||||
|
||||
return
|
||||
|
||||
|
||||
if len( self._ordered_terms ) > 0:
|
||||
|
||||
|
@ -2077,6 +2082,7 @@ class ListBoxTags( ListBox ):
|
|||
|
||||
|
||||
HG.client_controller.PopupMenu( self, menu )
|
||||
|
||||
|
||||
|
||||
def GetSelectedTags( self ):
|
||||
|
|
|
@ -345,7 +345,7 @@ class BetterListCtrl( QW.QTreeWidget ):
|
|||
|
||||
|
||||
def EventColumnClick( self, col ):
|
||||
|
||||
|
||||
if col == self._sort_column:
|
||||
|
||||
self._sort_asc = not self._sort_asc
|
||||
|
|
|
@ -670,7 +670,16 @@ class EditLoginsPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
sort_validity += ' - ' + validity_error_text
|
||||
|
||||
|
||||
sort_logged_in = ( logged_in, login_expiry )
|
||||
if login_expiry is None:
|
||||
|
||||
sort_login_expiry = HydrusData.GetNow() + 45 * 60
|
||||
|
||||
else:
|
||||
|
||||
sort_login_expiry = login_expiry
|
||||
|
||||
|
||||
sort_logged_in = ( logged_in, sort_login_expiry )
|
||||
|
||||
if HydrusData.TimeHasPassed( no_work_until ):
|
||||
|
||||
|
|
|
@ -685,8 +685,6 @@ class ManagementPanel( QW.QScrollArea ):
|
|||
|
||||
self.verticalScrollBar().valueChanged.connect( managementScrollbarValueChanged )
|
||||
|
||||
QP.SetBackgroundColour( self, QP.GetSystemColour( QG.QPalette.Button ) )
|
||||
|
||||
self._controller = controller
|
||||
self._management_controller = management_controller
|
||||
|
||||
|
@ -3039,7 +3037,7 @@ class ManagementPanelImporterSimpleDownloader( ManagementPanelImporter ):
|
|||
|
||||
if dlg_2.exec() == QW.QDialog.Accepted:
|
||||
|
||||
name = dlg_2.value()
|
||||
name = dlg_2.GetValue()
|
||||
|
||||
else:
|
||||
|
||||
|
|
|
@ -2790,8 +2790,6 @@ class MediaPanelThumbnails( MediaPanel ):
|
|||
self.verticalScrollBar().setSingleStep( int( round( thumbnail_span_height * thumbnail_scroll_rate ) ) )
|
||||
|
||||
self._widget_event_filter = QP.WidgetEventFilter( self.widget() )
|
||||
self._widget_event_filter.EVT_LEFT_DOWN( self.EventLeftDown )
|
||||
self._widget_event_filter.EVT_RIGHT_DOWN( self.EventShowMenu )
|
||||
self._widget_event_filter.EVT_LEFT_DCLICK( self.EventMouseFullScreen )
|
||||
self._widget_event_filter.EVT_MIDDLE_DOWN( self.EventMouseFullScreen )
|
||||
|
||||
|
@ -2807,7 +2805,7 @@ class MediaPanelThumbnails( MediaPanel ):
|
|||
self._UpdateScrollBars()
|
||||
|
||||
HG.client_controller.sub( self, 'MaintainPageCache', 'memory_maintenance_pulse' )
|
||||
HG.client_controller.sub( self, 'NewFileInfo', 'new_file_info' )
|
||||
HG.client_controller.sub( self, 'NotifyNewFileInfo', 'new_file_info' )
|
||||
HG.client_controller.sub( self, 'NewThumbnails', 'new_thumbnails' )
|
||||
HG.client_controller.sub( self, 'ThumbnailsReset', 'notify_complete_thumbnail_reset' )
|
||||
HG.client_controller.sub( self, 'RedrawAllThumbnails', 'refresh_all_tag_presentation_gui' )
|
||||
|
@ -3507,17 +3505,6 @@ class MediaPanelThumbnails( MediaPanel ):
|
|||
|
||||
|
||||
|
||||
def EventLeftDown( self, event ):
|
||||
|
||||
self._drag_init_coordinates = QG.QCursor.pos()
|
||||
|
||||
self._HitMedia( self._GetThumbnailUnderMouse( event ), event.modifiers() & QC.Qt.ControlModifier, event.modifiers() & QC.Qt.ShiftModifier )
|
||||
|
||||
# this specifically does not scroll to media, as for clicking (esp. double-clicking attempts), the scroll can be jarring
|
||||
|
||||
return True # was: event.ignore()
|
||||
|
||||
|
||||
def EventMouseFullScreen( self, event ):
|
||||
|
||||
t = self._GetThumbnailUnderMouse( event )
|
||||
|
@ -3551,6 +3538,15 @@ class MediaPanelThumbnails( MediaPanel ):
|
|||
self._parent = parent
|
||||
|
||||
|
||||
def mousePressEvent( self, event ):
|
||||
|
||||
self._parent._drag_init_coordinates = QG.QCursor.pos()
|
||||
|
||||
self._parent._HitMedia( self._parent._GetThumbnailUnderMouse( event ), event.modifiers() & QC.Qt.ControlModifier, event.modifiers() & QC.Qt.ShiftModifier )
|
||||
|
||||
# this specifically does not scroll to media, as for clicking (esp. double-clicking attempts), the scroll can be jarring
|
||||
|
||||
|
||||
def paintEvent( self, event ):
|
||||
|
||||
painter = QG.QPainter( self )
|
||||
|
@ -3647,7 +3643,14 @@ class MediaPanelThumbnails( MediaPanel ):
|
|||
self._last_client_size = QP.ScrollAreaVisibleRect( self ).size().toTuple()
|
||||
|
||||
|
||||
def EventShowMenu( self, event ):
|
||||
def mouseReleaseEvent( self, event ):
|
||||
|
||||
if event.button() != QC.Qt.RightButton:
|
||||
|
||||
QW.QScrollArea.mouseReleaseEvent( self, event )
|
||||
|
||||
return
|
||||
|
||||
|
||||
new_options = HG.client_controller.new_options
|
||||
|
||||
|
@ -3655,13 +3658,6 @@ class MediaPanelThumbnails( MediaPanel ):
|
|||
|
||||
services_manager = HG.client_controller.services_manager
|
||||
|
||||
thumbnail = self._GetThumbnailUnderMouse( event )
|
||||
|
||||
if thumbnail is not None:
|
||||
|
||||
self._HitMedia( thumbnail, event.modifiers() & QC.Qt.ControlModifier, event.modifiers() & QC.Qt.ShiftModifier )
|
||||
|
||||
|
||||
all_locations_managers = [ media.GetLocationsManager() for media in self._sorted_media ]
|
||||
selected_locations_managers = [ media.GetLocationsManager() for media in self._selected_media ]
|
||||
|
||||
|
@ -4581,18 +4577,6 @@ class MediaPanelThumbnails( MediaPanel ):
|
|||
self._DeleteAllDirtyPages()
|
||||
|
||||
|
||||
def NewFileInfo( self, hashes ):
|
||||
|
||||
affected_media = self._GetMedia( hashes )
|
||||
|
||||
for media in affected_media:
|
||||
|
||||
media.RefreshFileInfo()
|
||||
|
||||
|
||||
self._RedrawMedia( affected_media )
|
||||
|
||||
|
||||
def NewThumbnails( self, hashes ):
|
||||
|
||||
affected_thumbnails = self._GetMedia( hashes )
|
||||
|
@ -4603,6 +4587,34 @@ class MediaPanelThumbnails( MediaPanel ):
|
|||
|
||||
|
||||
|
||||
def NotifyNewFileInfo( self, hashes ):
|
||||
|
||||
def qt_do_update( hashes_to_media_results ):
|
||||
|
||||
affected_media = self._GetMedia( set( hashes_to_media_results.keys() ) )
|
||||
|
||||
for media in affected_media:
|
||||
|
||||
media.UpdateFileInfo( hashes_to_media_results )
|
||||
|
||||
|
||||
self._RedrawMedia( affected_media )
|
||||
|
||||
|
||||
def do_it( win, callable, affected_hashes ):
|
||||
|
||||
media_results = HG.client_controller.Read( 'media_results', affected_hashes )
|
||||
|
||||
hashes_to_media_results = { media_result.GetHash() : media_result for media_result in media_results }
|
||||
|
||||
HG.client_controller.CallLaterQtSafe( win, 0, qt_do_update, hashes_to_media_results )
|
||||
|
||||
|
||||
affected_hashes = self._hashes.intersection( hashes )
|
||||
|
||||
HG.client_controller.CallToThread( do_it, self, do_it, affected_hashes )
|
||||
|
||||
|
||||
def RedrawAllThumbnails( self ):
|
||||
|
||||
self._DirtyAllPages()
|
||||
|
@ -4628,6 +4640,8 @@ class MediaPanelThumbnails( MediaPanel ):
|
|||
|
||||
child.setParent( None )
|
||||
child.deleteLater()
|
||||
|
||||
|
||||
|
||||
def ctrl_space_callback( self ):
|
||||
|
||||
|
|
|
@ -897,15 +897,12 @@ class PagesNotebook( QP.TabWidgetWithDnD ):
|
|||
|
||||
self._closed_pages = []
|
||||
|
||||
self._last_last_session_hash = None
|
||||
|
||||
self._controller.sub( self, 'RefreshPageName', 'refresh_page_name' )
|
||||
self._controller.sub( self, 'NotifyPageUnclosed', 'notify_page_unclosed' )
|
||||
|
||||
self._widget_event_filter = QP.WidgetEventFilter( self )
|
||||
self._widget_event_filter.EVT_LEFT_DCLICK( self.EventLeftDoubleClick )
|
||||
self._widget_event_filter.EVT_MIDDLE_DOWN( self.EventMiddleClick )
|
||||
self._widget_event_filter.EVT_RIGHT_DOWN( self.EventMenu )
|
||||
self._widget_event_filter.EVT_LEFT_DOWN( lambda ev: ev.accept() )
|
||||
self._widget_event_filter.EVT_LEFT_DOWN( lambda ev: ev.accept() )
|
||||
|
||||
|
@ -1468,6 +1465,7 @@ class PagesNotebook( QP.TabWidgetWithDnD ):
|
|||
|
||||
ClientGUIMenus.AppendMenuItem( menu, 'rename page', 'Rename this page.', self._RenamePage, tab_index )
|
||||
|
||||
|
||||
ClientGUIMenus.AppendMenuItem( menu, 'new page', 'Choose a new page.', self._ChooseNewPage )
|
||||
|
||||
if click_over_tab:
|
||||
|
@ -1511,8 +1509,8 @@ class PagesNotebook( QP.TabWidgetWithDnD ):
|
|||
ClientGUIMenus.AppendMenuItem( menu, 'send this page down to a new page of pages', 'Make a new page of pages and put this page in it.', self._SendPageToNewNotebook, tab_index )
|
||||
|
||||
if can_go_right:
|
||||
ClientGUIMenus.AppendMenuItem( menu, 'send pages to the right to a new page of pages', 'Make a new page of pages and put all the pages to the right into it.', self._SendRightPagesToNewNotebook, tab_index )
|
||||
|
||||
ClientGUIMenus.AppendMenuItem( menu, 'send pages to the right to a new page of pages', 'Make a new page of pages and put all the pages to the right into it.', self._SendRightPagesToNewNotebook, tab_index )
|
||||
|
||||
if click_over_page_of_pages and page.count() > 0:
|
||||
|
||||
|
@ -1534,6 +1532,7 @@ class PagesNotebook( QP.TabWidgetWithDnD ):
|
|||
submenu = QW.QMenu( menu )
|
||||
|
||||
for name in existing_session_names:
|
||||
|
||||
ClientGUIMenus.AppendMenuItem( submenu, name, 'Load this session here.', self.AppendGUISession, name )
|
||||
|
||||
|
||||
|
@ -1550,9 +1549,11 @@ class PagesNotebook( QP.TabWidgetWithDnD ):
|
|||
|
||||
continue
|
||||
|
||||
ClientGUIMenus.AppendMenuItem( submenu, name, 'Save this page of pages to the session.', page.SaveGUISession, name )
|
||||
|
||||
ClientGUIMenus.AppendMenuItem( submenu, 'create a new session', 'Save this page of pages to the session.', page.SaveGUISession, suggested_name=page.GetName() )
|
||||
ClientGUIMenus.AppendMenuItem( submenu, name, 'Save this page of pages to the session.', self._controller.gui.ProposeSaveGUISession, notebook = page, name = name )
|
||||
|
||||
|
||||
ClientGUIMenus.AppendMenuItem( submenu, 'create a new session', 'Save this page of pages to the session.', self._controller.gui.ProposeSaveGUISession, notebook = page, suggested_name = page.GetName() )
|
||||
|
||||
ClientGUIMenus.AppendMenu( menu, submenu, 'save this page of pages to a session' )
|
||||
|
||||
|
@ -1796,14 +1797,21 @@ class PagesNotebook( QP.TabWidgetWithDnD ):
|
|||
|
||||
|
||||
|
||||
def EventMenu( self, event ):
|
||||
def mouseReleaseEvent( self, event ):
|
||||
|
||||
if event.button() != QC.Qt.RightButton:
|
||||
|
||||
QP.TabWidgetWithDnD.mouseReleaseEvent( self, event )
|
||||
|
||||
return
|
||||
|
||||
|
||||
screen_position = ClientGUIFunctions.ClientToScreen( self, event.pos() )
|
||||
|
||||
self._ShowMenu( screen_position )
|
||||
|
||||
|
||||
def EventMenuFromScreenPosition( self, position ):
|
||||
def ShowMenuFromScreenPosition( self, position ):
|
||||
|
||||
notebook = self._GetNotebookFromScreenPosition( position )
|
||||
|
||||
|
@ -1869,6 +1877,18 @@ class PagesNotebook( QP.TabWidgetWithDnD ):
|
|||
return {}
|
||||
|
||||
|
||||
def GetCurrentGUISession( self, name ):
|
||||
|
||||
session = GUISession( name )
|
||||
|
||||
for page in self._GetPages():
|
||||
|
||||
session.AddPageTuple( page )
|
||||
|
||||
|
||||
return session
|
||||
|
||||
|
||||
def GetCurrentMediaPage( self ):
|
||||
|
||||
page = self.currentWidget()
|
||||
|
@ -2728,92 +2748,6 @@ class PagesNotebook( QP.TabWidgetWithDnD ):
|
|||
|
||||
|
||||
|
||||
def SaveGUISession( self, name = None, suggested_name = '' ):
|
||||
|
||||
if name is None:
|
||||
|
||||
while True:
|
||||
|
||||
with ClientGUIDialogs.DialogTextEntry( self, 'Enter a name for the new session.', default = suggested_name ) as dlg:
|
||||
|
||||
if dlg.exec() == QW.QDialog.Accepted:
|
||||
|
||||
name = dlg.GetValue()
|
||||
|
||||
if name in RESERVED_SESSION_NAMES:
|
||||
|
||||
QW.QMessageBox.critical( self, 'Error', 'Sorry, you cannot have that name! Try another.' )
|
||||
|
||||
else:
|
||||
|
||||
existing_session_names = self._controller.Read( 'serialisable_names', HydrusSerialisable.SERIALISABLE_TYPE_GUI_SESSION )
|
||||
|
||||
if name in existing_session_names:
|
||||
|
||||
message = 'Session "{}" already exists! Do you want to overwrite it?'.format( name )
|
||||
|
||||
result, closed_by_user = ClientGUIDialogsQuick.GetYesNo( self, message, title = 'Overwrite existing session?', yes_label = 'yes, overwrite', no_label = 'no, choose another name', check_for_cancelled = True )
|
||||
|
||||
if closed_by_user:
|
||||
|
||||
return
|
||||
|
||||
elif result == QW.QDialog.Rejected:
|
||||
|
||||
continue
|
||||
|
||||
|
||||
|
||||
break
|
||||
|
||||
|
||||
else:
|
||||
|
||||
return
|
||||
|
||||
|
||||
|
||||
|
||||
elif name not in RESERVED_SESSION_NAMES: # i.e. a human asked to do this
|
||||
|
||||
message = 'Overwrite this session?'
|
||||
|
||||
result = ClientGUIDialogsQuick.GetYesNo( self, message, title = 'Overwrite existing session?', yes_label = 'yes, overwrite', no_label = 'no' )
|
||||
|
||||
if result != QW.QDialog.Accepted:
|
||||
|
||||
return
|
||||
|
||||
|
||||
|
||||
#
|
||||
|
||||
session = GUISession( name )
|
||||
|
||||
for page in self._GetPages():
|
||||
|
||||
session.AddPageTuple( page )
|
||||
|
||||
|
||||
#
|
||||
|
||||
if name == 'last session':
|
||||
|
||||
session_hash = hashlib.sha256( bytes( session.DumpToString(), 'utf-8' ) ).digest()
|
||||
|
||||
if session_hash == self._last_last_session_hash:
|
||||
|
||||
return
|
||||
|
||||
|
||||
self._last_last_session_hash = session_hash
|
||||
|
||||
|
||||
self._controller.WriteSynchronous( 'serialisable', session )
|
||||
|
||||
self._controller.pub( 'notify_new_sessions' )
|
||||
|
||||
|
||||
def SetName( self, name ):
|
||||
|
||||
self._name = name
|
||||
|
|
|
@ -236,8 +236,6 @@ class ReviewServicePanel( QW.QWidget ):
|
|||
|
||||
QW.QWidget.__init__( self, parent )
|
||||
|
||||
QP.SetBackgroundColour( self, QP.GetSystemColour( QG.QPalette.Button ) )
|
||||
|
||||
self._service = service
|
||||
|
||||
service_type = self._service.GetServiceType()
|
||||
|
|
|
@ -532,8 +532,6 @@ class EditCompoundFormulaPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
edit_panel = ClientGUICommon.StaticBox( self, 'edit' )
|
||||
|
||||
QP.SetBackgroundColour( edit_panel, QP.GetSystemColour( QG.QPalette.Button ) )
|
||||
|
||||
self._formulae = QW.QListWidget( edit_panel )
|
||||
self._formulae.setSelectionMode( QW.QAbstractItemView.SingleSelection )
|
||||
self._formulae.itemDoubleClicked.connect( self.Edit )
|
||||
|
@ -560,8 +558,6 @@ class EditCompoundFormulaPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
test_panel = ClientGUICommon.StaticBox( self, 'test' )
|
||||
|
||||
QP.SetBackgroundColour( test_panel, QP.GetSystemColour( QG.QPalette.Button ) )
|
||||
|
||||
self._test_panel = TestPanel( test_panel, self.GetValue, test_context = test_context )
|
||||
|
||||
#
|
||||
|
@ -771,8 +767,6 @@ class EditContextVariableFormulaPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
edit_panel = ClientGUICommon.StaticBox( self, 'edit' )
|
||||
|
||||
QP.SetBackgroundColour( edit_panel, QP.GetSystemColour( QG.QPalette.Button ) )
|
||||
|
||||
self._variable_name = QW.QLineEdit( edit_panel )
|
||||
|
||||
( variable_name, string_match, string_converter ) = formula.ToTuple()
|
||||
|
@ -785,8 +779,6 @@ class EditContextVariableFormulaPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
test_panel = ClientGUICommon.StaticBox( self, 'test' )
|
||||
|
||||
QP.SetBackgroundColour( test_panel, QP.GetSystemColour( QG.QPalette.Button ) )
|
||||
|
||||
self._test_panel = TestPanel( test_panel, self.GetValue, test_context = test_context )
|
||||
|
||||
#
|
||||
|
@ -1225,8 +1217,6 @@ class EditHTMLFormulaPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
edit_panel = ClientGUICommon.StaticBox( self, 'edit' )
|
||||
|
||||
QP.SetBackgroundColour( edit_panel, QP.GetSystemColour( QG.QPalette.Button ) )
|
||||
|
||||
self._tag_rules = QW.QListWidget( edit_panel )
|
||||
self._tag_rules.setSelectionMode( QW.QAbstractItemView.SingleSelection )
|
||||
|
||||
|
@ -1262,8 +1252,6 @@ class EditHTMLFormulaPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
test_panel = ClientGUICommon.StaticBox( self, 'test' )
|
||||
|
||||
QP.SetBackgroundColour( test_panel, QP.GetSystemColour( QG.QPalette.Button ) )
|
||||
|
||||
self._test_panel = TestPanel( test_panel, self.GetValue, test_context = test_context )
|
||||
|
||||
#
|
||||
|
@ -1585,8 +1573,6 @@ class EditJSONFormulaPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
edit_panel = ClientGUICommon.StaticBox( self, 'edit' )
|
||||
|
||||
QP.SetBackgroundColour( edit_panel, QP.GetSystemColour( QG.QPalette.Button ) )
|
||||
|
||||
self._parse_rules = QW.QListWidget( edit_panel )
|
||||
self._parse_rules.setSelectionMode( QW.QAbstractItemView.SingleSelection )
|
||||
self._parse_rules.itemDoubleClicked.connect( self.Edit )
|
||||
|
@ -1617,8 +1603,6 @@ class EditJSONFormulaPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
test_panel = ClientGUICommon.StaticBox( self, 'test' )
|
||||
|
||||
QP.SetBackgroundColour( test_panel, QP.GetSystemColour( QG.QPalette.Button ) )
|
||||
|
||||
self._test_panel = TestPanel( test_panel, self.GetValue, test_context = test_context )
|
||||
|
||||
#
|
||||
|
@ -1825,16 +1809,12 @@ class EditContentParserPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
test_panel = ClientGUICommon.StaticBox( self, 'test' )
|
||||
|
||||
QP.SetBackgroundColour( test_panel, QP.GetSystemColour( QG.QPalette.Button ) )
|
||||
|
||||
self._test_panel = TestPanel( test_panel, self.GetValue, test_context = test_context )
|
||||
|
||||
#
|
||||
|
||||
self._edit_panel = ClientGUICommon.StaticBox( self, 'edit' )
|
||||
|
||||
QP.SetBackgroundColour( self._edit_panel, QP.GetSystemColour( QG.QPalette.Button ) )
|
||||
|
||||
self._name = QW.QLineEdit( self._edit_panel )
|
||||
|
||||
self._content_panel = ClientGUICommon.StaticBox( self._edit_panel, 'content type' )
|
||||
|
@ -2644,8 +2624,6 @@ class EditParseNodeContentLinkPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
edit_panel = QW.QWidget( notebook )
|
||||
|
||||
QP.SetBackgroundColour( edit_panel, QP.GetSystemColour( QG.QPalette.Button ) )
|
||||
|
||||
self._name = QW.QLineEdit( edit_panel )
|
||||
|
||||
get_example_parsing_context = lambda: {}
|
||||
|
@ -2660,8 +2638,6 @@ class EditParseNodeContentLinkPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
test_panel = QW.QWidget( notebook )
|
||||
|
||||
QP.SetBackgroundColour( test_panel, QP.GetSystemColour( QG.QPalette.Button ) )
|
||||
|
||||
self._example_data = QW.QPlainTextEdit( test_panel )
|
||||
|
||||
self._example_data.setMinimumHeight( 200 )
|
||||
|
@ -2912,8 +2888,6 @@ class EditPageParserPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
main_panel = QW.QWidget( edit_notebook )
|
||||
|
||||
QP.SetBackgroundColour( main_panel, QP.GetSystemColour( QG.QPalette.Button ) )
|
||||
|
||||
self._name = QW.QLineEdit( main_panel )
|
||||
|
||||
#
|
||||
|
@ -2942,8 +2916,6 @@ class EditPageParserPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
sub_page_parsers_notebook_panel = QW.QWidget( edit_notebook )
|
||||
|
||||
QP.SetBackgroundColour( sub_page_parsers_notebook_panel, QP.GetSystemColour( QG.QPalette.Button ) )
|
||||
|
||||
#
|
||||
|
||||
sub_page_parsers_panel = ClientGUIListCtrl.BetterListCtrlPanel( sub_page_parsers_notebook_panel )
|
||||
|
@ -2962,8 +2934,6 @@ class EditPageParserPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
test_panel = ClientGUICommon.StaticBox( self, 'test' )
|
||||
|
||||
QP.SetBackgroundColour( test_panel, QP.GetSystemColour( QG.QPalette.Button ) )
|
||||
|
||||
test_url_fetch_panel = ClientGUICommon.StaticBox( test_panel, 'fetch test data from url' )
|
||||
|
||||
self._test_url = QW.QLineEdit( test_url_fetch_panel )
|
||||
|
@ -2984,8 +2954,6 @@ class EditPageParserPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
content_parsers_panel = QW.QWidget( edit_notebook )
|
||||
|
||||
QP.SetBackgroundColour( content_parsers_panel, QP.GetSystemColour( QG.QPalette.Button ) )
|
||||
|
||||
#
|
||||
|
||||
permitted_content_types = [ HC.CONTENT_TYPE_URLS, HC.CONTENT_TYPE_MAPPINGS, HC.CONTENT_TYPE_HASH, HC.CONTENT_TYPE_TIMESTAMP, HC.CONTENT_TYPE_TITLE, HC.CONTENT_TYPE_VETO ]
|
||||
|
@ -3461,8 +3429,6 @@ class EditParsingScriptFileLookupPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
edit_panel = QW.QWidget( notebook )
|
||||
|
||||
QP.SetBackgroundColour( edit_panel, QP.GetSystemColour( QG.QPalette.Button ) )
|
||||
|
||||
self._name = QW.QLineEdit( edit_panel )
|
||||
|
||||
query_panel = ClientGUICommon.StaticBox( edit_panel, 'query' )
|
||||
|
@ -3499,8 +3465,6 @@ class EditParsingScriptFileLookupPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
test_panel = QW.QWidget( notebook )
|
||||
|
||||
QP.SetBackgroundColour( test_panel, QP.GetSystemColour( QG.QPalette.Button ) )
|
||||
|
||||
self._test_script_management = ScriptManagementControl( test_panel )
|
||||
|
||||
self._test_arg = QW.QLineEdit( test_panel )
|
||||
|
@ -4246,6 +4210,7 @@ class ScriptManagementControl( QW.QWidget ):
|
|||
menu = QW.QMenu()
|
||||
|
||||
for url in urls:
|
||||
|
||||
ClientGUIMenus.AppendMenuItem( menu, url, 'launch this url in your browser', ClientPaths.LaunchURLInWebBrowser, url )
|
||||
|
||||
|
||||
|
@ -4274,8 +4239,6 @@ class TestPanel( QW.QWidget ):
|
|||
test_context = ( {}, '' )
|
||||
|
||||
|
||||
QP.SetBackgroundColour( self, QP.GetSystemColour( QG.QPalette.Button ) )
|
||||
|
||||
self._object_callable = object_callable
|
||||
|
||||
self._example_parsing_context = ClientGUIControls.StringToStringDictButton( self, 'edit example parsing context' )
|
||||
|
|
|
@ -541,8 +541,6 @@ class PopupMessageManager( QW.QWidget ):
|
|||
|
||||
self.setSizePolicy( QW.QSizePolicy.MinimumExpanding, QW.QSizePolicy.Preferred )
|
||||
|
||||
QP.SetBackgroundColour( self, QP.GetSystemColour( QG.QPalette.Button ) )
|
||||
|
||||
self._last_best_size_i_fit_on = ( 0, 0 )
|
||||
|
||||
self._max_messages_to_display = 10
|
||||
|
@ -660,9 +658,9 @@ class PopupMessageManager( QW.QWidget ):
|
|||
|
||||
|
||||
|
||||
current_focus_tlp = QW.QApplication.activeWindow()
|
||||
current_focus_tlw = QW.QApplication.activeWindow()
|
||||
|
||||
main_gui_is_active = current_focus_tlp in ( self, parent )
|
||||
main_gui_is_active = current_focus_tlw in ( self, parent )
|
||||
|
||||
if new_options.GetBoolean( 'hide_message_manager_on_gui_deactive' ) and not self._DisplayingError():
|
||||
|
||||
|
@ -719,28 +717,11 @@ class PopupMessageManager( QW.QWidget ):
|
|||
|
||||
going_to_bug_out_at_hide_or_show = possibly_on_hidden_virtual_desktop
|
||||
|
||||
current_focus_tlp = QW.QApplication.activeWindow()
|
||||
current_focus_tlw = QW.QApplication.activeWindow()
|
||||
|
||||
main_gui_is_active = current_focus_tlp in ( self, gui_frame )
|
||||
self_is_active = current_focus_tlw == self
|
||||
|
||||
on_top_frame_is_active = False
|
||||
|
||||
if not main_gui_is_active and current_focus_tlp is not None:
|
||||
|
||||
c_f_tlp_is_resizing_frame = isinstance( current_focus_tlp, ClientGUITopLevelWindows.FrameThatResizes )
|
||||
|
||||
frame_parent = current_focus_tlp.parentWidget()
|
||||
|
||||
if c_f_tlp_is_resizing_frame and frame_parent is not None:
|
||||
|
||||
c_f_tlp_is_child_frame_of_main_gui = frame_parent.window() == gui_frame
|
||||
|
||||
if c_f_tlp_is_child_frame_of_main_gui:
|
||||
|
||||
on_top_frame_is_active = True
|
||||
|
||||
|
||||
|
||||
main_gui_or_child_window_is_active = ClientGUIFunctions.TLWOrChildIsActive( gui_frame )
|
||||
|
||||
num_messages_displayed = self._message_vbox.count()
|
||||
|
||||
|
@ -765,9 +746,8 @@ class PopupMessageManager( QW.QWidget ):
|
|||
|
||||
|
||||
|
||||
# Unhiding tends to raise the main gui tlp, which is annoying if a media viewer window has focus
|
||||
# Qt port note: the on_top_frame_is_active part was uncommented originally, but it IS annoying since it leads to flickering (e.g. open the options window with this uncommented to see it in action)
|
||||
show_is_not_annoying = main_gui_is_active or self._DisplayingError() or on_top_frame_is_active
|
||||
# Unhiding tends to raise the main gui tlw in some window managers, which is annoying if a media viewer window has focus
|
||||
show_is_not_annoying = main_gui_or_child_window_is_active or self._DisplayingError()
|
||||
|
||||
ok_to_show = show_is_not_annoying and not going_to_bug_out_at_hide_or_show
|
||||
|
||||
|
@ -1189,7 +1169,7 @@ class PopupMessageDialogPanel( QW.QWidget ):
|
|||
# The problem is that when the window is created, the initial size is too small because all widgets are empty before the first update,
|
||||
# but after it updates it doesn't want to resize the window, rather it just adds scrollbars.
|
||||
# This is a manual fix. Need to find a better solution...
|
||||
|
||||
|
||||
self.layout().update()
|
||||
self._message_window.adjustSize()
|
||||
self.adjustSize()
|
||||
|
|
|
@ -189,7 +189,7 @@ class InputFileSystemPredicate( ClientGUIScrolledPanels.EditPanel ):
|
|||
self._predicate_panel = predicate_class( self )
|
||||
self._parent = parent
|
||||
|
||||
self._ok = QW.QPushButton( 'OK', self )
|
||||
self._ok = QW.QPushButton( 'ok', self )
|
||||
self._ok.clicked.connect( self._DoOK )
|
||||
QP.SetForegroundColour( self._ok, (0,128,0) )
|
||||
|
||||
|
@ -205,6 +205,19 @@ class InputFileSystemPredicate( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
def _DoOK( self ):
|
||||
|
||||
try:
|
||||
|
||||
self._predicate_panel.CheckCanOK()
|
||||
|
||||
except Exception as e:
|
||||
|
||||
message = 'Cannot OK: {}'.format( e )
|
||||
|
||||
QW.QMessageBox.warning( self, 'Warning', message )
|
||||
|
||||
return
|
||||
|
||||
|
||||
predicates = self._predicate_panel.GetPredicates()
|
||||
|
||||
self._parent.SubPanelOK( predicates )
|
||||
|
@ -250,6 +263,11 @@ class PanelPredicateSystem( QW.QWidget ):
|
|||
|
||||
PREDICATE_TYPE = None
|
||||
|
||||
def CheckCanOK( self ):
|
||||
|
||||
pass
|
||||
|
||||
|
||||
def GetInfo( self ):
|
||||
|
||||
raise NotImplementedError()
|
||||
|
@ -866,18 +884,18 @@ class PanelPredicateSystemKnownURLsExactURL( PanelPredicateSystem ):
|
|||
|
||||
if operator:
|
||||
|
||||
operator_description = 'has this url: '
|
||||
operator_description = 'has url: '
|
||||
|
||||
else:
|
||||
|
||||
operator_description = 'does not have this url: '
|
||||
operator_description = 'does not have url: '
|
||||
|
||||
|
||||
rule_type = 'regex'
|
||||
rule_type = 'exact_match'
|
||||
|
||||
exact_url = self._exact_url.text()
|
||||
|
||||
rule = re.escape( exact_url )
|
||||
rule = exact_url
|
||||
|
||||
description = operator_description + exact_url
|
||||
|
||||
|
@ -927,11 +945,11 @@ class PanelPredicateSystemKnownURLsDomain( PanelPredicateSystem ):
|
|||
operator_description = 'does not have a url with domain: '
|
||||
|
||||
|
||||
rule_type = 'regex'
|
||||
rule_type = 'domain'
|
||||
|
||||
domain = self._domain.text()
|
||||
|
||||
rule = r'^https?\:\/\/(www[^\.]*\.)?' + re.escape( domain ) + r'\/.*'
|
||||
rule = domain
|
||||
|
||||
description = operator_description + domain
|
||||
|
||||
|
@ -966,6 +984,20 @@ class PanelPredicateSystemKnownURLsRegex( PanelPredicateSystem ):
|
|||
self.setLayout( hbox )
|
||||
|
||||
|
||||
def CheckCanOK( self ):
|
||||
|
||||
regex = self._regex.text()
|
||||
|
||||
try:
|
||||
|
||||
re.compile( regex )
|
||||
|
||||
except Exception as e:
|
||||
|
||||
raise Exception( 'Cannot compile that regex: {}'.format( e ) )
|
||||
|
||||
|
||||
|
||||
def GetInfo( self ):
|
||||
|
||||
operator = self._operator.GetValue()
|
||||
|
|
|
@ -102,7 +102,7 @@ class EditAccountTypePanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
#
|
||||
|
||||
self._title.SetValue( title )
|
||||
self._title.setText( title )
|
||||
|
||||
#
|
||||
|
||||
|
@ -142,7 +142,7 @@ class EditAccountTypePanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
def GetValue( self ):
|
||||
|
||||
title = self._title.GetValue()
|
||||
title = self._title.text()
|
||||
|
||||
permissions = {}
|
||||
|
||||
|
@ -3275,7 +3275,7 @@ class EditRegexFavourites( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
if dlg_2.exec() == QW.QDialog.Accepted:
|
||||
|
||||
description = dlg_2.value()
|
||||
description = dlg_2.GetValue()
|
||||
|
||||
edited_row = ( regex_phrase, description )
|
||||
|
||||
|
|
|
@ -1707,21 +1707,48 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
|
||||
QW.QWidget.__init__( self, parent )
|
||||
|
||||
self._new_options = HG.client_controller.new_options
|
||||
|
||||
general = ClientGUICommon.StaticBox( self, 'general' )
|
||||
|
||||
self._verify_regular_https = QW.QCheckBox( general )
|
||||
|
||||
self._network_timeout = QP.MakeQSpinBox( general, min = 3, max = 600 )
|
||||
if self._new_options.GetBoolean( 'advanced_mode' ):
|
||||
|
||||
network_timeout_min = 1
|
||||
network_timeout_max = 86400 * 30
|
||||
|
||||
error_wait_time_min = 1
|
||||
error_wait_time_max = 86400 * 30
|
||||
|
||||
max_network_jobs_max = 1000
|
||||
|
||||
max_network_jobs_per_domain_max = 100
|
||||
|
||||
else:
|
||||
|
||||
network_timeout_min = 3
|
||||
network_timeout_max = 600
|
||||
|
||||
error_wait_time_min = 3
|
||||
error_wait_time_max = 1800
|
||||
|
||||
max_network_jobs_max = 30
|
||||
|
||||
max_network_jobs_per_domain_max = 5
|
||||
|
||||
|
||||
self._network_timeout = QP.MakeQSpinBox( general, min = network_timeout_min, max = network_timeout_max )
|
||||
self._network_timeout.setToolTip( 'If a network connection cannot be made in this duration or, if once started, it experiences uninterrupted inactivity for six times this duration, it will be abandoned.' )
|
||||
|
||||
self._connection_error_wait_time = QP.MakeQSpinBox( general, min = 3, max = 1800 )
|
||||
self._connection_error_wait_time = QP.MakeQSpinBox( general, min = error_wait_time_min, max = error_wait_time_max )
|
||||
self._connection_error_wait_time.setToolTip( 'If a network connection times out as above, it will wait increasing multiples of this base time before retrying.' )
|
||||
|
||||
self._serverside_bandwidth_wait_time = QP.MakeQSpinBox( general, min = 3, max = 1800 )
|
||||
self._serverside_bandwidth_wait_time = QP.MakeQSpinBox( general, min = error_wait_time_min, max = error_wait_time_max )
|
||||
self._serverside_bandwidth_wait_time.setToolTip( 'If a server returns a failure status code indicating it is short on bandwidth, the network job will wait increasing multiples of this base time before retrying.' )
|
||||
|
||||
self._max_network_jobs = QP.MakeQSpinBox( general, min = 1, max = 30 )
|
||||
self._max_network_jobs_per_domain = QP.MakeQSpinBox( general, min = 1, max = 5 )
|
||||
self._max_network_jobs = QP.MakeQSpinBox( general, min = 1, max = max_network_jobs_max )
|
||||
self._max_network_jobs_per_domain = QP.MakeQSpinBox( general, min = 1, max = max_network_jobs_per_domain_max )
|
||||
|
||||
#
|
||||
|
||||
|
@ -1732,8 +1759,6 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
|
||||
#
|
||||
|
||||
self._new_options = HG.client_controller.new_options
|
||||
|
||||
self._verify_regular_https.setChecked( self._new_options.GetBoolean( 'verify_regular_https' ) )
|
||||
|
||||
self._http_proxy.SetValue( self._new_options.GetNoneableString( 'http_proxy' ) )
|
||||
|
@ -1748,6 +1773,19 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
|
||||
#
|
||||
|
||||
if self._new_options.GetBoolean( 'advanced_mode' ):
|
||||
|
||||
label = 'As you are in advanced mode, these options have very low and high limits. Be very careful about lowering delay time or raising max number of connections too far, as things will break.'
|
||||
|
||||
st = ClientGUICommon.BetterStaticText( general, label = label )
|
||||
|
||||
QP.SetForegroundColour( st, ( 127, 0, 0 ) )
|
||||
|
||||
st.setWordWrap( True )
|
||||
|
||||
general.Add( st, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
|
||||
rows = []
|
||||
|
||||
rows.append( ( 'network timeout (seconds): ', self._network_timeout ) )
|
||||
|
@ -1759,7 +1797,7 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
|
||||
gridbox = ClientGUICommon.WrapInGrid( general, rows )
|
||||
|
||||
general.Add( gridbox, CC.FLAGS_EXPAND_SIZER_BOTH_WAYS )
|
||||
general.Add( gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
|
||||
text = 'Enter strings such as "http://ip:port" or "http://user:pass@ip:port". It should take affect immediately on dialog ok.'
|
||||
text += os.linesep * 2
|
||||
|
@ -1775,7 +1813,11 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
text += 'It does not look like you have socks support! If you want it, try adding "pysocks" (or "requests[socks]")!'
|
||||
|
||||
|
||||
proxy_panel.Add( QW.QLabel( text, proxy_panel ), CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
st = ClientGUICommon.BetterStaticText( proxy_panel, text )
|
||||
|
||||
st.setWordWrap( True )
|
||||
|
||||
proxy_panel.Add( st, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
rows = []
|
||||
|
||||
|
@ -1784,7 +1826,7 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
|
||||
gridbox = ClientGUICommon.WrapInGrid( proxy_panel, rows )
|
||||
|
||||
proxy_panel.Add( gridbox, CC.FLAGS_EXPAND_SIZER_BOTH_WAYS )
|
||||
proxy_panel.Add( gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
|
||||
#
|
||||
|
||||
|
@ -1869,9 +1911,18 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
self._show_new_on_file_seed_short_summary = QW.QCheckBox( misc )
|
||||
self._show_deleted_on_file_seed_short_summary = QW.QCheckBox( misc )
|
||||
|
||||
self._subscription_network_error_delay = ClientGUITime.TimeDeltaButton( misc, min = 600, days = True, hours = True, minutes = True )
|
||||
self._subscription_other_error_delay = ClientGUITime.TimeDeltaButton( misc, min = 600, days = True, hours = True, minutes = True )
|
||||
self._downloader_network_error_delay = ClientGUITime.TimeDeltaButton( misc, min = 600, days = True, hours = True, minutes = True )
|
||||
if self._new_options.GetBoolean( 'advanced_mode' ):
|
||||
|
||||
delay_min = 1
|
||||
|
||||
else:
|
||||
|
||||
delay_min = 600
|
||||
|
||||
|
||||
self._subscription_network_error_delay = ClientGUITime.TimeDeltaButton( misc, min = delay_min, days = True, hours = True, minutes = True, seconds = True )
|
||||
self._subscription_other_error_delay = ClientGUITime.TimeDeltaButton( misc, min = delay_min, days = True, hours = True, minutes = True, seconds = True )
|
||||
self._downloader_network_error_delay = ClientGUITime.TimeDeltaButton( misc, min = delay_min, days = True, hours = True, minutes = True, seconds = True )
|
||||
|
||||
#
|
||||
|
||||
|
@ -2580,9 +2631,6 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
self._secret_discord_dnd_fix = QW.QCheckBox( self )
|
||||
self._secret_discord_dnd_fix.setToolTip( 'This saves the lag but is potentially dangerous, as it (may) treat the from-db-files-drag as a move rather than a copy and hence only works when the drop destination will not consume the files. It requires an additional secret Alternate key to unlock.' )
|
||||
|
||||
self._always_show_hover_windows = QW.QCheckBox( self )
|
||||
self._always_show_hover_windows.setToolTip( 'If your window manager doesn\'t like showing the hover windows on mouse-over (typically on some Linux flavours), please try this out and give the dev feedback on this forced size and position accuracy!' )
|
||||
|
||||
self._hide_message_manager_on_gui_iconise = QW.QCheckBox( self )
|
||||
self._hide_message_manager_on_gui_iconise.setToolTip( 'If your message manager does not automatically minimise with your main gui, try this. It can lead to unusual show and positioning behaviour on window managers that do not support it, however.' )
|
||||
|
||||
|
@ -2619,8 +2667,6 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
|
||||
self._secret_discord_dnd_fix.setChecked( self._new_options.GetBoolean( 'secret_discord_dnd_fix' ) )
|
||||
|
||||
self._always_show_hover_windows.setChecked( self._new_options.GetBoolean( 'always_show_hover_windows' ) )
|
||||
|
||||
self._hide_message_manager_on_gui_iconise.setChecked( self._new_options.GetBoolean( 'hide_message_manager_on_gui_iconise' ) )
|
||||
self._hide_message_manager_on_gui_deactive.setChecked( self._new_options.GetBoolean( 'hide_message_manager_on_gui_deactive' ) )
|
||||
|
||||
|
@ -2647,7 +2693,6 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
rows.append( ( 'BUGFIX: Force this width as the minimum width for all popup messages: ', self._popup_message_force_min_width ) )
|
||||
rows.append( ( 'BUGFIX: Discord file drag-and-drop fix (works for <=25, <200MB file DnDs): ', self._discord_dnd_fix ) )
|
||||
rows.append( ( 'EXPERIMENTAL BUGFIX: Secret discord file drag-and-drop fix: ', self._secret_discord_dnd_fix ) )
|
||||
rows.append( ( 'BUGFIX: Always show media viewer hover windows: ', self._always_show_hover_windows ) )
|
||||
rows.append( ( 'BUGFIX: Hide the popup message manager when the main gui is minimised: ', self._hide_message_manager_on_gui_iconise ) )
|
||||
rows.append( ( 'BUGFIX: Hide the popup message manager when the main gui loses focus: ', self._hide_message_manager_on_gui_deactive ) )
|
||||
|
||||
|
@ -2726,7 +2771,6 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
|
||||
self._new_options.SetBoolean( 'discord_dnd_fix', self._discord_dnd_fix.isChecked() )
|
||||
self._new_options.SetBoolean( 'secret_discord_dnd_fix', self._secret_discord_dnd_fix.isChecked() )
|
||||
self._new_options.SetBoolean( 'always_show_hover_windows', self._always_show_hover_windows.isChecked() )
|
||||
self._new_options.SetBoolean( 'hide_message_manager_on_gui_iconise', self._hide_message_manager_on_gui_iconise.isChecked() )
|
||||
self._new_options.SetBoolean( 'hide_message_manager_on_gui_deactive', self._hide_message_manager_on_gui_deactive.isChecked() )
|
||||
|
||||
|
@ -3324,6 +3368,7 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
|
||||
|
||||
|
||||
|
||||
def EventZoomsChanged( self, text ):
|
||||
|
||||
try:
|
||||
|
@ -3833,7 +3878,7 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
self._qt_style_name = ClientGUICommon.BetterChoice( self )
|
||||
self._qt_stylesheet_name = ClientGUICommon.BetterChoice( self )
|
||||
|
||||
self._qt_style_name.addItem( 'use default ("{}")'.format( ClientGUIStyle.ORIGINAL_STYLE ), None )
|
||||
self._qt_style_name.addItem( 'use default ("{}")'.format( ClientGUIStyle.ORIGINAL_STYLE_NAME ), None )
|
||||
|
||||
try:
|
||||
|
||||
|
@ -3900,22 +3945,36 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
qt_style_name = self._qt_style_name.GetValue()
|
||||
qt_stylesheet_name = self._qt_stylesheet_name.GetValue()
|
||||
|
||||
if qt_style_name is None:
|
||||
try:
|
||||
|
||||
ClientGUIStyle.SetStyle( ClientGUIStyle.ORIGINAL_STYLE )
|
||||
if qt_style_name is None:
|
||||
|
||||
ClientGUIStyle.SetStyleFromName( ClientGUIStyle.ORIGINAL_STYLE_NAME )
|
||||
|
||||
else:
|
||||
|
||||
ClientGUIStyle.SetStyleFromName( qt_style_name )
|
||||
|
||||
|
||||
else:
|
||||
except Exception as e:
|
||||
|
||||
ClientGUIStyle.SetStyle( qt_style_name )
|
||||
QW.QMessageBox.critical( self, 'Critical', 'Could not apply style: {}'.format( str( e ) ) )
|
||||
|
||||
|
||||
if qt_stylesheet_name is None:
|
||||
try:
|
||||
|
||||
ClientGUIStyle.ClearStylesheet()
|
||||
if qt_stylesheet_name is None:
|
||||
|
||||
ClientGUIStyle.ClearStylesheet()
|
||||
|
||||
else:
|
||||
|
||||
ClientGUIStyle.SetStylesheetFromPath( qt_stylesheet_name )
|
||||
|
||||
|
||||
else:
|
||||
except Exception as e:
|
||||
|
||||
ClientGUIStyle.SetStylesheet( qt_stylesheet_name )
|
||||
QW.QMessageBox.critical( self, 'Critical', 'Could not apply stylesheet: {}'.format( str( e ) ) )
|
||||
|
||||
|
||||
|
||||
|
@ -4891,7 +4950,7 @@ class ManageShortcutsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
|
||||
if result == QW.QDialog.Accepted:
|
||||
|
||||
self._custom_shortcuts.RemoveAllSelected()
|
||||
self._custom_shortcuts.DeleteSelected()
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -548,7 +548,7 @@ class MigrateDatabasePanel( ClientGUIScrolledPanels.ReviewPanel ):
|
|||
|
||||
with QP.DirDialog( self, message = 'Choose new database location.' ) as dlg:
|
||||
|
||||
dlg.SetPath( source )
|
||||
dlg.setDirectory( source )
|
||||
|
||||
if dlg.exec() == QW.QDialog.Accepted:
|
||||
|
||||
|
@ -728,7 +728,7 @@ class MigrateDatabasePanel( ClientGUIScrolledPanels.ReviewPanel ):
|
|||
|
||||
if self._ideal_thumbnails_location_override is not None:
|
||||
|
||||
dlg.SetPath( self._ideal_thumbnails_location_override )
|
||||
dlg.setDirectory( self._ideal_thumbnails_location_override )
|
||||
|
||||
|
||||
if dlg.exec() == QW.QDialog.Accepted:
|
||||
|
|
|
@ -2,8 +2,6 @@ from . import ClientConstants as CC
|
|||
from . import ClientData
|
||||
from . import ClientGUICommon
|
||||
from . import ClientGUIFunctions
|
||||
from . import ClientGUIScrolledPanels
|
||||
from . import ClientGUITopLevelWindows
|
||||
from . import HydrusConstants as HC
|
||||
from . import HydrusData
|
||||
from . import HydrusGlobals as HG
|
||||
|
@ -12,23 +10,46 @@ from qtpy import QtCore as QC
|
|||
from qtpy import QtWidgets as QW
|
||||
from qtpy import QtGui as QG
|
||||
from . import QtPorting as QP
|
||||
|
||||
# ok, the problem here is that I get key codes that are converted, so if someone does shift+1 on a US keyboard, this ends up with Shift+! same with ctrl+alt+ to get accented characters
|
||||
# it isn't really a big deal since everything still lines up, but the QGuiApplicationPrivate::platformIntegration()->possibleKeys(e) to get some variant of 'yeah this is just !' seems unavailable for python
|
||||
# it is basically a display bug, but it'd be nice to have it working right
|
||||
def ConvertQtKeyToShortcutKey( key_qt ):
|
||||
|
||||
if key_qt in CC.special_key_shortcut_enum_lookup:
|
||||
|
||||
key_ord = CC.special_key_shortcut_enum_lookup[ key_qt ]
|
||||
|
||||
return ( CC.SHORTCUT_TYPE_KEYBOARD_SPECIAL, key_ord )
|
||||
|
||||
else:
|
||||
|
||||
try:
|
||||
|
||||
key_ord = int( key_qt )
|
||||
|
||||
key_chr = chr( key_ord )
|
||||
|
||||
# this is turbo lower() that converts Scharfes S (beta) to 'ss'
|
||||
key_chr = key_chr.casefold()[0]
|
||||
|
||||
casefold_key_ord = ord( key_chr )
|
||||
|
||||
return ( CC.SHORTCUT_TYPE_KEYBOARD_CHARACTER, casefold_key_ord )
|
||||
|
||||
except:
|
||||
|
||||
return ( CC.SHORTCUT_TYPE_NOT_ALLOWED, key_ord )
|
||||
|
||||
|
||||
|
||||
def ConvertKeyEventToShortcut( event ):
|
||||
|
||||
key = event.key()
|
||||
key_qt = event.key()
|
||||
|
||||
if key in CC.special_key_shortcut_enum_lookup or ClientData.OrdIsSensibleASCII( key ):
|
||||
|
||||
if key in CC.special_key_shortcut_enum_lookup:
|
||||
|
||||
shortcut_type = CC.SHORTCUT_TYPE_KEYBOARD_SPECIAL
|
||||
key = CC.special_key_shortcut_enum_lookup[ key ]
|
||||
|
||||
else:
|
||||
|
||||
shortcut_type = CC.SHORTCUT_TYPE_KEYBOARD_ASCII
|
||||
key = int( key )
|
||||
|
||||
( shortcut_type, key_ord ) = ConvertQtKeyToShortcutKey( key_qt )
|
||||
|
||||
if shortcut_type != CC.SHORTCUT_TYPE_NOT_ALLOWED:
|
||||
|
||||
modifiers = []
|
||||
|
||||
|
@ -66,7 +87,7 @@ def ConvertKeyEventToShortcut( event ):
|
|||
modifiers.append( CC.SHORTCUT_MODIFIER_KEYPAD )
|
||||
|
||||
|
||||
shortcut = Shortcut( shortcut_type, key, modifiers )
|
||||
shortcut = Shortcut( shortcut_type, key_ord, modifiers )
|
||||
|
||||
if HG.gui_report_mode:
|
||||
|
||||
|
@ -158,7 +179,7 @@ def ConvertMouseEventToShortcut( event ):
|
|||
|
||||
return None
|
||||
|
||||
def IShouldCatchShortcutEvent( evt_handler, event = None, child_tlp_classes_who_can_pass_up = None ):
|
||||
def IShouldCatchShortcutEvent( evt_handler, event = None, child_tlw_classes_who_can_pass_up = None ):
|
||||
|
||||
do_focus_test = True
|
||||
|
||||
|
@ -170,13 +191,13 @@ def IShouldCatchShortcutEvent( evt_handler, event = None, child_tlp_classes_who_
|
|||
|
||||
if do_focus_test:
|
||||
|
||||
if not ClientGUIFunctions.TLPIsActive( evt_handler ):
|
||||
if not ClientGUIFunctions.TLWIsActive( evt_handler ):
|
||||
|
||||
if child_tlp_classes_who_can_pass_up is not None:
|
||||
if child_tlw_classes_who_can_pass_up is not None:
|
||||
|
||||
child_tlp_has_focus = ClientGUIFunctions.WindowOrAnyTLPChildHasFocus( evt_handler ) and isinstance( QW.QApplication.activeWindow(), child_tlp_classes_who_can_pass_up )
|
||||
child_tlw_has_focus = ClientGUIFunctions.WidgetOrAnyTLWChildHasFocus( evt_handler ) and isinstance( QW.QApplication.activeWindow(), child_tlw_classes_who_can_pass_up )
|
||||
|
||||
if not child_tlp_has_focus:
|
||||
if not child_tlw_has_focus:
|
||||
|
||||
return False
|
||||
|
||||
|
@ -213,7 +234,7 @@ class Shortcut( HydrusSerialisable.SerialisableBase ):
|
|||
modifiers = []
|
||||
|
||||
|
||||
if shortcut_type == CC.SHORTCUT_TYPE_KEYBOARD_ASCII and ClientData.OrdIsAlphaUpper( shortcut_key ):
|
||||
if shortcut_type == CC.SHORTCUT_TYPE_KEYBOARD_CHARACTER and ClientData.OrdIsAlphaUpper( shortcut_key ):
|
||||
|
||||
shortcut_key += 32 # convert A to a
|
||||
|
||||
|
@ -299,7 +320,7 @@ class Shortcut( HydrusSerialisable.SerialisableBase ):
|
|||
330 : ord( '6' ),
|
||||
331 : ord( '7' ),
|
||||
332 : ord( '8' ),
|
||||
332 : ord( '9' ),
|
||||
333 : ord( '9' ),
|
||||
388 : ord( '+' ),
|
||||
392 : ord( '/' ),
|
||||
390 : ord( '-' ),
|
||||
|
@ -322,7 +343,7 @@ class Shortcut( HydrusSerialisable.SerialisableBase ):
|
|||
|
||||
( shortcut_type, shortcut_key, modifiers ) = old_serialisable_info
|
||||
|
||||
if shortcut_type == CC.SHORTCUT_TYPE_KEYBOARD_ASCII:
|
||||
if shortcut_type == CC.SHORTCUT_TYPE_KEYBOARD_CHARACTER:
|
||||
|
||||
if shortcut_key in wx_to_qt_flat_conversion:
|
||||
|
||||
|
@ -352,7 +373,7 @@ class Shortcut( HydrusSerialisable.SerialisableBase ):
|
|||
|
||||
|
||||
|
||||
if shortcut_type == CC.SHORTCUT_TYPE_KEYBOARD_ASCII:
|
||||
if shortcut_type == CC.SHORTCUT_TYPE_KEYBOARD_CHARACTER:
|
||||
|
||||
if ClientData.OrdIsAlphaUpper( shortcut_key ):
|
||||
|
||||
|
@ -403,7 +424,7 @@ class Shortcut( HydrusSerialisable.SerialisableBase ):
|
|||
|
||||
components.append( CC.special_key_shortcut_str_lookup[ self._shortcut_key ] )
|
||||
|
||||
elif self._shortcut_type == CC.SHORTCUT_TYPE_KEYBOARD_ASCII and ClientData.OrdIsSensibleASCII( self._shortcut_key ):
|
||||
elif self._shortcut_type == CC.SHORTCUT_TYPE_KEYBOARD_CHARACTER:
|
||||
|
||||
if ClientData.OrdIsAlphaUpper( self._shortcut_key ):
|
||||
|
||||
|
@ -658,7 +679,7 @@ class ShortcutSet( HydrusSerialisable.SerialisableBaseNamed ):
|
|||
|
||||
modifiers = []
|
||||
|
||||
shortcut = Shortcut( CC.SHORTCUT_TYPE_KEYBOARD_ASCII, key, modifiers )
|
||||
shortcut = Shortcut( CC.SHORTCUT_TYPE_KEYBOARD_CHARACTER, key, modifiers )
|
||||
|
||||
if serialisable_service_key is None:
|
||||
|
||||
|
|
|
@ -6,12 +6,14 @@ from qtpy import QtWidgets as QW
|
|||
|
||||
STYLESHEET_DIR = os.path.join( HC.BASE_DIR, 'static', 'qss' )
|
||||
|
||||
ORIGINAL_STYLE = None
|
||||
ORIGINAL_STYLE_NAME = None
|
||||
CURRENT_STYLE_NAME = None
|
||||
ORIGINAL_STYLESHEET = None
|
||||
CURRENT_STYLESHEET = None
|
||||
|
||||
def ClearStylesheet():
|
||||
|
||||
QW.QApplication.instance().setStyleSheet( ORIGINAL_STYLESHEET )
|
||||
SetStyleSheet( ORIGINAL_STYLESHEET )
|
||||
|
||||
def GetAvailableStyles():
|
||||
|
||||
|
@ -19,10 +21,6 @@ def GetAvailableStyles():
|
|||
|
||||
return list( QW.QStyleFactory.keys() )
|
||||
|
||||
def GetCurrentStyleName():
|
||||
|
||||
return QW.QApplication.instance().style().objectName()
|
||||
|
||||
def GetAvailableStylesheets():
|
||||
|
||||
if not os.path.exists( STYLESHEET_DIR ) or not os.path.isdir( STYLESHEET_DIR ):
|
||||
|
@ -46,33 +44,57 @@ def GetAvailableStylesheets():
|
|||
|
||||
def InitialiseDefaults():
|
||||
|
||||
global ORIGINAL_STYLE
|
||||
global ORIGINAL_STYLE_NAME
|
||||
global CURRENT_STYLE_NAME
|
||||
|
||||
ORIGINAL_STYLE = GetCurrentStyleName()
|
||||
ORIGINAL_STYLE_NAME = QW.QApplication.instance().style().objectName()
|
||||
CURRENT_STYLE_NAME = ORIGINAL_STYLE_NAME
|
||||
|
||||
global ORIGINAL_STYLESHEET
|
||||
global CURRENT_STYLESHEET
|
||||
|
||||
ORIGINAL_STYLESHEET = QW.QApplication.instance().styleSheet()
|
||||
CURRENT_STYLESHEET = ORIGINAL_STYLESHEET
|
||||
|
||||
def SetStyle( name ):
|
||||
def SetStyleFromName( name ):
|
||||
|
||||
current_name = GetCurrentStyleName()
|
||||
global CURRENT_STYLE_NAME
|
||||
|
||||
if name == current_name:
|
||||
if name == CURRENT_STYLE_NAME:
|
||||
|
||||
return
|
||||
|
||||
|
||||
if name in QW.QStyleFactory.keys():
|
||||
if name in GetAvailableStyles():
|
||||
|
||||
QW.QApplication.instance().setStyle( QW.QStyleFactory.create( name ) )
|
||||
try:
|
||||
|
||||
QW.QApplication.instance().setStyle( name )
|
||||
|
||||
CURRENT_STYLE_NAME = name
|
||||
|
||||
except Exception as e:
|
||||
|
||||
raise HydrusExceptions.DataMissing( 'Style "{}" could not be generated/applied. If this is the default, perhaps a third-party custom style, you may have to restart the client to re-set it. Extra error info: {}'.format( name, e ) )
|
||||
|
||||
|
||||
else:
|
||||
|
||||
raise HydrusExceptions.DataMissing( 'Style "{}" does not exist!'.format( name ) )
|
||||
raise HydrusExceptions.DataMissing( 'Style "{}" does not exist! If this is the default, perhaps a third-party custom style, you may have to restart the client to re-set it.'.format( name ) )
|
||||
|
||||
|
||||
def SetStylesheet( filename ):
|
||||
def SetStyleSheet( stylesheet ):
|
||||
|
||||
global CURRENT_STYLESHEET
|
||||
|
||||
if CURRENT_STYLESHEET != stylesheet:
|
||||
|
||||
QW.QApplication.instance().setStyleSheet( stylesheet )
|
||||
|
||||
CURRENT_STYLESHEET = stylesheet
|
||||
|
||||
|
||||
def SetStylesheetFromPath( filename ):
|
||||
|
||||
path = os.path.join( STYLESHEET_DIR, filename )
|
||||
|
||||
|
@ -86,5 +108,5 @@ def SetStylesheet( filename ):
|
|||
qss = f.read()
|
||||
|
||||
|
||||
QW.QApplication.instance().setStyleSheet( qss )
|
||||
SetStyleSheet( qss )
|
||||
|
||||
|
|
|
@ -1270,17 +1270,17 @@ class ManageTagsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
|
||||
elif action == 'focus_media_viewer':
|
||||
|
||||
tlps = ClientGUIFunctions.GetTLPParents( self )
|
||||
tlws = ClientGUIFunctions.GetTLWParents( self )
|
||||
|
||||
from . import ClientGUICanvas
|
||||
|
||||
command_processed = False
|
||||
|
||||
for tlp in tlps:
|
||||
for tlw in tlws:
|
||||
|
||||
if isinstance( tlp, ClientGUICanvas.CanvasFrame ):
|
||||
if isinstance( tlw, ClientGUICanvas.CanvasFrame ):
|
||||
|
||||
tlp.TakeFocusForUser()
|
||||
tlw.TakeFocusForUser()
|
||||
|
||||
command_processed = True
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ from . import ClientGUITopLevelWindows
|
|||
from . import ClientImporting
|
||||
from . import ClientImportOptions
|
||||
from . import HydrusData
|
||||
from . import HydrusGlobals as HG
|
||||
import os
|
||||
from qtpy import QtCore as QC
|
||||
from qtpy import QtWidgets as QW
|
||||
|
@ -51,19 +52,34 @@ class EditCheckerOptions( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
#
|
||||
|
||||
if HG.client_controller.new_options.GetBoolean( 'advanced_mode' ):
|
||||
|
||||
never_faster_than_min = 1
|
||||
never_slower_than_min = 1
|
||||
|
||||
flat_check_period_min = 1
|
||||
|
||||
else:
|
||||
|
||||
never_faster_than_min = 30
|
||||
never_slower_than_min = 600
|
||||
|
||||
flat_check_period_min = 180
|
||||
|
||||
|
||||
self._reactive_check_panel = ClientGUICommon.StaticBox( self, 'reactive checking' )
|
||||
|
||||
self._intended_files_per_check = QP.MakeQSpinBox( self._reactive_check_panel, min=1, max=1000 )
|
||||
|
||||
self._never_faster_than = TimeDeltaCtrl( self._reactive_check_panel, min = 30, days = True, hours = True, minutes = True, seconds = True )
|
||||
self._never_faster_than = TimeDeltaCtrl( self._reactive_check_panel, min = never_faster_than_min, days = True, hours = True, minutes = True, seconds = True )
|
||||
|
||||
self._never_slower_than = TimeDeltaCtrl( self._reactive_check_panel, min = 600, days = True, hours = True, minutes = True )
|
||||
self._never_slower_than = TimeDeltaCtrl( self._reactive_check_panel, min = never_slower_than_min, days = True, hours = True, minutes = True, seconds = True )
|
||||
|
||||
#
|
||||
|
||||
self._static_check_panel = ClientGUICommon.StaticBox( self, 'static checking' )
|
||||
|
||||
self._flat_check_period = TimeDeltaCtrl( self._static_check_panel, min = 180, days = True, hours = True, minutes = True )
|
||||
self._flat_check_period = TimeDeltaCtrl( self._static_check_panel, min = flat_check_period_min, days = True, hours = True, minutes = True, seconds = True )
|
||||
|
||||
#
|
||||
|
||||
|
@ -79,6 +95,8 @@ class EditCheckerOptions( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
#
|
||||
|
||||
#
|
||||
|
||||
rows = []
|
||||
|
||||
rows.append( ( 'intended new files per check: ', self._intended_files_per_check ) )
|
||||
|
@ -113,6 +131,20 @@ class EditCheckerOptions( ClientGUIScrolledPanels.EditPanel ):
|
|||
QP.AddToLayout( vbox, help_hbox, CC.FLAGS_BUTTON_SIZER )
|
||||
QP.AddToLayout( vbox, defaults_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
QP.AddToLayout( vbox, gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
|
||||
if HG.client_controller.new_options.GetBoolean( 'advanced_mode' ):
|
||||
|
||||
label = 'As you are in advanced mode, these options have extremely low limits. This is intended only for testing and small scale private network tasks. Do not use very fast check times for real world use on public websites, as it is wasteful and rude, hydrus will be overloaded with high-CPU parsing work, and you may get your IP banned.'
|
||||
|
||||
st = ClientGUICommon.BetterStaticText( self, label = label )
|
||||
|
||||
QP.SetForegroundColour( st, ( 127, 0, 0 ) )
|
||||
|
||||
st.setWordWrap( True )
|
||||
|
||||
QP.AddToLayout( vbox, st, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
|
||||
QP.AddToLayout( vbox, self._reactive_check_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
QP.AddToLayout( vbox, self._static_check_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
|
|
|
@ -336,8 +336,6 @@ class NewDialog( QP.Dialog ):
|
|||
|
||||
self._new_options = HG.client_controller.new_options
|
||||
|
||||
QP.SetBackgroundColour( self, QP.GetSystemColour( QG.QPalette.Button ) )
|
||||
|
||||
self.setWindowIcon( QG.QIcon( HG.client_controller.frame_icon_pixmap ) )
|
||||
|
||||
HG.client_controller.ResetIdleTimer()
|
||||
|
@ -483,28 +481,35 @@ class NewDialog( QP.Dialog ):
|
|||
|
||||
if event_object is not None:
|
||||
|
||||
tlp = event_object.window()
|
||||
tlw = event_object.window()
|
||||
|
||||
if tlp != self:
|
||||
if tlw != self:
|
||||
|
||||
return
|
||||
|
||||
|
||||
|
||||
self._TryEndModal( QW.QDialog.Accepted )
|
||||
|
||||
|
||||
|
||||
def EventDialogButtonCancel( self ):
|
||||
|
||||
if not self or not QP.isValid( self ):
|
||||
|
||||
return
|
||||
|
||||
|
||||
event_object = self.sender()
|
||||
|
||||
if event_object is not None:
|
||||
|
||||
tlp = event_object.window()
|
||||
tlw = event_object.window()
|
||||
|
||||
if tlp != self:
|
||||
if tlw != self:
|
||||
|
||||
return
|
||||
|
||||
|
||||
|
||||
self._TryEndModal( QW.QDialog.Rejected )
|
||||
|
||||
|
@ -784,8 +789,6 @@ class Frame( QW.QWidget ):
|
|||
|
||||
self._last_move_pub = 0.0
|
||||
|
||||
QP.SetBackgroundColour( self, QP.GetSystemColour( QG.QPalette.Button ) )
|
||||
|
||||
self.setWindowIcon( QG.QIcon( HG.client_controller.frame_icon_pixmap ) )
|
||||
|
||||
self._widget_event_filter = QP.WidgetEventFilter( self )
|
||||
|
@ -829,8 +832,6 @@ class MainFrame( QW.QMainWindow ):
|
|||
|
||||
self._new_options = HG.client_controller.new_options
|
||||
|
||||
QP.SetBackgroundColour( self, QP.GetSystemColour( QG.QPalette.Button ) )
|
||||
|
||||
self.setWindowIcon( QG.QIcon( HG.client_controller.frame_icon_pixmap ) )
|
||||
|
||||
self._widget_event_filter = QP.WidgetEventFilter( self )
|
||||
|
|
|
@ -1746,13 +1746,25 @@ class SubscriptionsManager( object ):
|
|||
|
||||
with self._lock:
|
||||
|
||||
message = '{} subs: {}'.format( HydrusData.ToHumanInt( len( self._current_subscription_names ) ), ', '.join( self._current_subscription_names ) )
|
||||
subs = list( self._current_subscription_names )
|
||||
subs.sort()
|
||||
|
||||
running = list( self._running_subscriptions.keys() )
|
||||
running.sort()
|
||||
|
||||
cannot_run = list( self._names_that_cannot_run )
|
||||
cannot_run.sort()
|
||||
|
||||
next_times = list( self._names_to_next_work_time.items() )
|
||||
next_times.sort( key = lambda n, nwt: nwt )
|
||||
|
||||
message = '{} subs: {}'.format( HydrusData.ToHumanInt( len( self._current_subscription_names ) ), ', '.join( subs ) )
|
||||
message += os.linesep * 2
|
||||
message += '{} running: {}'.format( HydrusData.ToHumanInt( len( self._running_subscriptions ) ), ', '.join( self._running_subscriptions.keys() ) )
|
||||
message += '{} running: {}'.format( HydrusData.ToHumanInt( len( self._running_subscriptions ) ), ', '.join( running ) )
|
||||
message += os.linesep * 2
|
||||
message += '{} not runnable: {}'.format( HydrusData.ToHumanInt( len( self._names_that_cannot_run ) ), ', '.join( self._names_that_cannot_run ) )
|
||||
message += '{} not runnable: {}'.format( HydrusData.ToHumanInt( len( self._names_that_cannot_run ) ), ', '.join( cannot_run ) )
|
||||
message += os.linesep * 2
|
||||
message += '{} next times: {}'.format( HydrusData.ToHumanInt( len( self._names_to_next_work_time ) ), ', '.join( ( '{}: {}'.format( name, HydrusData.TimestampToPrettyTimeDelta( next_work_time ) ) for ( name, next_work_time ) in self._names_to_next_work_time.items() ) ) )
|
||||
message += '{} next times: {}'.format( HydrusData.ToHumanInt( len( self._names_to_next_work_time ) ), ', '.join( ( '{}: {}'.format( name, HydrusData.TimestampToPrettyTimeDelta( next_work_time ) ) for ( name, next_work_time ) in next_times ) ) )
|
||||
|
||||
HydrusData.ShowText( message )
|
||||
|
||||
|
|
|
@ -1703,7 +1703,7 @@ class MediaList( object ):
|
|||
|
||||
physically_deleted = service_key in ( CC.TRASH_SERVICE_KEY, CC.COMBINED_LOCAL_FILE_SERVICE_KEY )
|
||||
trashed = service_key in local_file_domains
|
||||
deleted_from_our_domain = service_key = self._file_service_key
|
||||
deleted_from_our_domain = service_key == self._file_service_key
|
||||
|
||||
physically_deleted_and_local_view = physically_deleted and self._file_service_key in all_local_file_services
|
||||
|
||||
|
@ -2075,19 +2075,19 @@ class MediaCollection( MediaList, Media ):
|
|||
self._RecalcInternals()
|
||||
|
||||
|
||||
def RefreshFileInfo( self ):
|
||||
def ResetService( self, service_key ):
|
||||
|
||||
for media in self._sorted_media:
|
||||
|
||||
media.RefreshFileInfo()
|
||||
|
||||
MediaList.ResetService( self, service_key )
|
||||
|
||||
self._RecalcInternals()
|
||||
|
||||
|
||||
def ResetService( self, service_key ):
|
||||
def UpdateFileInfo( self, hashes_to_media_results ):
|
||||
|
||||
MediaList.ResetService( self, service_key )
|
||||
for media in self._sorted_media:
|
||||
|
||||
media.UpdateFileInfo( hashes_to_media_results )
|
||||
|
||||
|
||||
self._RecalcInternals()
|
||||
|
||||
|
@ -2431,13 +2431,13 @@ class MediaSingleton( Media ):
|
|||
return True
|
||||
|
||||
|
||||
def RefreshFileInfo( self ):
|
||||
def UpdateFileInfo( self, hashes_to_media_results ):
|
||||
|
||||
media_results = HG.client_controller.Read( 'media_results', ( self._media_result.GetHash(), ) )
|
||||
hash = self.GetHash()
|
||||
|
||||
if len( media_results ) > 0:
|
||||
if hash in hashes_to_media_results:
|
||||
|
||||
media_result = media_results[0]
|
||||
media_result = hashes_to_media_results[ hash ]
|
||||
|
||||
self._media_result = media_result
|
||||
|
||||
|
|
|
@ -3127,6 +3127,11 @@ class URLClass( HydrusSerialisable.SerialisableBaseNamed ):
|
|||
|
||||
|
||||
|
||||
def MatchesSubdomains( self ):
|
||||
|
||||
return self._match_subdomains
|
||||
|
||||
|
||||
def Normalise( self, url ):
|
||||
|
||||
p = urllib.parse.urlparse( url )
|
||||
|
|
|
@ -1021,12 +1021,14 @@ class NetworkJob( object ):
|
|||
|
||||
while not request_completed:
|
||||
|
||||
try:
|
||||
if self._IsCancelled():
|
||||
|
||||
if self._IsCancelled():
|
||||
|
||||
return
|
||||
|
||||
return
|
||||
|
||||
|
||||
response = None
|
||||
|
||||
try:
|
||||
|
||||
response = self._SendRequestAndGetResponse()
|
||||
|
||||
|
@ -1163,6 +1165,15 @@ class NetworkJob( object ):
|
|||
|
||||
self._WaitOnConnectionError( 'read timed out' )
|
||||
|
||||
finally:
|
||||
|
||||
if response is not None:
|
||||
|
||||
# if full data was not read, the response will hang around in connection pool longer than we want
|
||||
# so just an explicit close here
|
||||
response.close()
|
||||
|
||||
|
||||
|
||||
|
||||
except Exception as e:
|
||||
|
|
|
@ -44,8 +44,6 @@ class ClientOptions( HydrusSerialisable.SerialisableBase ):
|
|||
|
||||
self._dictionary[ 'booleans' ][ 'advanced_mode' ] = False
|
||||
|
||||
self._dictionary[ 'booleans' ][ 'always_show_hover_windows' ] = False
|
||||
|
||||
self._dictionary[ 'booleans' ][ 'apply_all_parents_to_all_services' ] = False
|
||||
self._dictionary[ 'booleans' ][ 'apply_all_siblings_to_all_services' ] = False
|
||||
self._dictionary[ 'booleans' ][ 'filter_inbox_and_archive_predicates' ] = False
|
||||
|
|
|
@ -67,7 +67,7 @@ options = {}
|
|||
# Misc
|
||||
|
||||
NETWORK_VERSION = 18
|
||||
SOFTWARE_VERSION = 376
|
||||
SOFTWARE_VERSION = 377
|
||||
CLIENT_API_VERSION = 11
|
||||
|
||||
SERVER_THUMBNAIL_DIMENSIONS = ( 200, 200 )
|
||||
|
|
|
@ -216,17 +216,22 @@ class DirPickerCtrl( QW.QWidget ):
|
|||
|
||||
self.setLayout( layout )
|
||||
|
||||
|
||||
def SetPath( self, path ):
|
||||
|
||||
self._path_edit.setText( path )
|
||||
|
||||
|
||||
def GetPath( self ):
|
||||
|
||||
return self._path_edit.text()
|
||||
|
||||
|
||||
def _Browse( self ):
|
||||
|
||||
path = QW.QFileDialog.getExistingDirectory( None, '', self._path_edit.text() )
|
||||
existing_path = self._path_edit.text()
|
||||
|
||||
path = QW.QFileDialog.getExistingDirectory( self, '', existing_path )
|
||||
|
||||
if path == '':
|
||||
|
||||
|
@ -295,27 +300,29 @@ class FilePickerCtrl( QW.QWidget ):
|
|||
|
||||
|
||||
def _Browse( self ):
|
||||
|
||||
|
||||
existing_path = self._path_edit.text()
|
||||
|
||||
if self._save_mode:
|
||||
|
||||
if self._wildcard:
|
||||
|
||||
path = QW.QFileDialog.getSaveFileName( None, '', self._path_edit.text(), filter = self._wildcard, selectedFilter = self._wildcard )[0]
|
||||
path = QW.QFileDialog.getSaveFileName( self, '', existing_path, filter = self._wildcard, selectedFilter = self._wildcard )[0]
|
||||
|
||||
else:
|
||||
|
||||
path = QW.QFileDialog.getSaveFileName( None, '', self._path_edit.text() )[0]
|
||||
path = QW.QFileDialog.getSaveFileName( self, '', existing_path )[0]
|
||||
|
||||
|
||||
else:
|
||||
|
||||
if self._wildcard:
|
||||
|
||||
path = QW.QFileDialog.getOpenFileName( None, '', self._path_edit.text(), filter = self._wildcard, selectedFilter = self._wildcard )[0]
|
||||
path = QW.QFileDialog.getOpenFileName( self, '', existing_path, filter = self._wildcard, selectedFilter = self._wildcard )[0]
|
||||
|
||||
else:
|
||||
|
||||
path = QW.QFileDialog.getOpenFileName( None, '', self._path_edit.text() )[0]
|
||||
path = QW.QFileDialog.getOpenFileName( self, '', existing_path )[0]
|
||||
|
||||
|
||||
|
||||
|
@ -1249,7 +1256,6 @@ def GetSystemColour( colour ):
|
|||
|
||||
return QG.QPalette().color( colour )
|
||||
|
||||
|
||||
def CenterOnWindow( parent, window ):
|
||||
|
||||
parent_window = parent.window()
|
||||
|
@ -1402,7 +1408,7 @@ def SetBackgroundColour( widget, colour ):
|
|||
if isinstance( colour, QG.QColor ):
|
||||
|
||||
widget.setStyleSheet( '#{} {{ background-color: {} }}'.format( object_name, colour.name()) )
|
||||
|
||||
|
||||
elif isinstance( colour, tuple ):
|
||||
|
||||
widget.setStyleSheet( '#{} {{ background-color: {} }}'.format( object_name, TupleToQColor( colour ).name() ) )
|
||||
|
@ -1410,8 +1416,8 @@ def SetBackgroundColour( widget, colour ):
|
|||
else:
|
||||
|
||||
widget.setStyleSheet( '#{} {{ background-color: {} }}'.format( object_name, QG.QColor( colour ).name() ) )
|
||||
|
||||
|
||||
|
||||
|
||||
def SetForegroundColour( widget, colour ):
|
||||
|
||||
widget.setAutoFillBackground( True )
|
||||
|
@ -1421,8 +1427,9 @@ def SetForegroundColour( widget, colour ):
|
|||
if not object_name:
|
||||
|
||||
object_name = str( id( widget ) )
|
||||
|
||||
|
||||
widget.setObjectName( object_name )
|
||||
|
||||
|
||||
if isinstance( colour, QG.QColor ):
|
||||
|
||||
|
@ -1735,7 +1742,7 @@ class CheckListBox( QW.QListWidget ):
|
|||
if event.button() == QC.Qt.RightButton:
|
||||
|
||||
self.rightClicked.emit()
|
||||
|
||||
|
||||
else:
|
||||
|
||||
QW.QListWidget.mousePressEvent( self, event )
|
||||
|
@ -2139,14 +2146,16 @@ class TreeWidgetWithInheritedCheckState( QW.QTreeWidget ):
|
|||
|
||||
def __init__( self, *args, **kwargs ):
|
||||
|
||||
QW.QTreeWidget.__init__( *args, **kwargs )
|
||||
QW.QTreeWidget.__init__( self, *args, **kwargs )
|
||||
|
||||
self.itemClicked.connect( self._UpdateCheckState )
|
||||
|
||||
|
||||
def _HandleItemClickedForCheckStateUpdate( self, item, column ):
|
||||
|
||||
self._UpdateCheckState( item, item.checkState() )
|
||||
|
||||
|
||||
def _UpdateCheckState( self, item, check_state ):
|
||||
|
||||
item.setCheckState( check_state )
|
||||
|
|
|
@ -399,8 +399,8 @@ class TestSerialisables( unittest.TestCase ):
|
|||
shortcuts.append( ( ClientGUIShortcuts.Shortcut(), 'f7' ) )
|
||||
|
||||
shortcuts.append( ( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD_SPECIAL, CC.SHORTCUT_KEY_SPECIAL_SPACE, [] ), 'space' ) )
|
||||
shortcuts.append( ( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD_ASCII, ord( 'a' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), 'ctrl+a' ) )
|
||||
shortcuts.append( ( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD_ASCII, ord( 'A' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), 'ctrl+a' ) )
|
||||
shortcuts.append( ( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD_CHARACTER, ord( 'a' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), 'ctrl+a' ) )
|
||||
shortcuts.append( ( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD_CHARACTER, ord( 'A' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), 'ctrl+a' ) )
|
||||
shortcuts.append( ( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD_SPECIAL, CC.SHORTCUT_KEY_SPECIAL_HOME, [ CC.SHORTCUT_MODIFIER_ALT, CC.SHORTCUT_MODIFIER_CTRL ] ), 'ctrl+alt+home' ) )
|
||||
|
||||
shortcuts.append( ( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_LEFT, [] ), 'left-click' ) )
|
||||
|
@ -437,8 +437,8 @@ class TestSerialisables( unittest.TestCase ):
|
|||
command_3 = ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_CONTENT, ( CC.DEFAULT_LOCAL_TAG_SERVICE_KEY, HC.CONTENT_TYPE_MAPPINGS, HC.CONTENT_UPDATE_FLIP, 'test' ) )
|
||||
|
||||
k_shortcut_1 = ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD_SPECIAL, CC.SHORTCUT_KEY_SPECIAL_SPACE, [] )
|
||||
k_shortcut_2 = ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD_ASCII, ord( 'a' ), [ CC.SHORTCUT_MODIFIER_CTRL ] )
|
||||
k_shortcut_3 = ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD_ASCII, ord( 'A' ), [ CC.SHORTCUT_MODIFIER_CTRL ] )
|
||||
k_shortcut_2 = ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD_CHARACTER, ord( 'a' ), [ CC.SHORTCUT_MODIFIER_CTRL ] )
|
||||
k_shortcut_3 = ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD_CHARACTER, ord( 'A' ), [ CC.SHORTCUT_MODIFIER_CTRL ] )
|
||||
k_shortcut_4 = ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD_SPECIAL, CC.SHORTCUT_KEY_SPECIAL_HOME, [ CC.SHORTCUT_MODIFIER_ALT, CC.SHORTCUT_MODIFIER_CTRL ] )
|
||||
|
||||
m_shortcut_1 = ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_LEFT, [] )
|
||||
|
|
Loading…
Reference in New Issue