Version 563

closes #1511
This commit is contained in:
Hydrus Network Developer 2024-02-21 15:09:02 -06:00
parent 99dcd2a613
commit 2f5493642b
No known key found for this signature in database
GPG Key ID: 76249F053212133C
39 changed files with 541 additions and 419 deletions

View File

@ -7,6 +7,56 @@ title: Changelog
!!! note
This is the new changelog, only the most recent builds. For all versions, see the [old changelog](old_changelog.html).
## [Version 563](https://github.com/hydrusnetwork/hydrus/releases/tag/v563)
### macOS improvements
* Thanks to a user, we have multiple improvements for macOS!
* There is a new icon for the macOS .app build of hydrus
* The macOS app will now appear as "Hydrus" in the menu bar instead of "Hydrus Network"
* - Use the native global menu bar on macOS and some Linux desktop environments
* - "options" will now appear as "Preferences..." and be under the Hydrus menu on macOS
* - "exit" will now appear as "Quit Hydrus" and be under the Hydrus menu on macOS
* "exit and force shutdown maintenance", "restart", and "shortcuts" will now be under the Hydrus menu on macOS
* The hydrus system tray icon is now enabled for macOS and "minimise to system tray" will be in the Hydrus menu when in advanced mode
* macOS debug dialog menus are now disabled by default
* The macOS build of hydrus now uses pyoxidizer 0.24.0 and Python 3.10
* The command palette and hyperlinks colors in the default Qt stylesheet now use palette based colors that should change based on the Qt style
* one thing hydev did: on macOS, Cmd+W _should_ now close any dialog or non-main-gui window, just like the Escape key
### shortcuts
* by default, Alt+Home/End/Left/Right now does the new thumbnail rearranging. **assuming they do not conflict with an existing mapping, all users will recieve this on update**
* by default, the shortcuts system now converts all non-number 'numpad' inputs (e.g. 'numpad Home', 'numpad Return', 'numpad Left') to just be normal inputs. a bunch of different keyboards have whack numpad assignments for non-numpad keys, so if it isn't a number, let's not, by default, make a fuss over the distinction. you can return to the old behaviour by unchecking the new checkbox under _file->shortcuts_
* the default shortcuts now no longer spam numpad variants anywhere. existing users can delete the surplus mappings (under 'thumbnails' and maybe some of the 'media' sets) if they like
### some UI QoL
* the _tag service_ menu button that appears in the autocomplete panel and sometimes some other places in advanced mode now shows a proper check mark in its menu beside its current value
* the _location context_ menu button on the other side of an autocomplete panel and some other places also now shows a check mark in its menu beside its current value
* the `OR` button on search autocomplete that creates new OR predicates now inherits the current file search domain. it was previously defaulting at all times to the fallback file domain and 'all known tags'
* the current search predicates list also now inherits the file search domain when you edit an OR predicate currently in use, same deal
* removed the 'favourites' submenu from the taglist menu when no tags are selected
* in any import context, the file log's arrow menu now supports deleting all the 'unknown' (outstanding, unstarted) items or setting them all to 'skipped'. the 'abort imports' button (with the stop icon) in HDD and urls import pages is removed
### misc
* fixed yet another dumb problem with the datetime control's paste button--although the paste was now 'working' on the UI side, the control wasn't saving that result on dialog ok. the fixes both the datetime button and the modified/file service time multi-column list editing
* a core asynchronous thread-checking timer in the program has been rewritten from a 20ms-resolution busy-wait to a <1ms proper wait/notify system. a bunch of stuff that works in a thread is now much faster to recognise that blocking UI work is done, and it is more thread-polite about how it does it!
* in the `setup_venv` scripts, if it needs to delete an old venv directory but fails to do so, the script now dumps out with an error saying 'hey, you probably have it open in a terminal/IDE, please close that and try again'. previously, it would just charge on and produce an odd file permission error as, e.g., the new venv setup tried to overwrite the in-use python exe
* added a `help->debug->gui->isolate existing mpv widgets` command to force regeneration of mpv windows and help test-out/hack-fix various 'every other of my mpv views has no audio' and 'my mpv loses xxx property after a system sleep/wake cycle' problems. if I've been working with you on this stuff, please give it a go and let me know if new mpv window creation is good or what!
* added a `BUGFIX: Disable off-screen window rescue` checkbox to `options->gui` that stops windows that think they are spawning off-screen from repositioning to a known safe screen. several Qt versions have had trouble with enumerating all the screens in a multiple monitor setup and thus the safe coordinate space, so if you have been hit by false positives here, you can now turn it off! (issue #1511)
* fixed another couple instances of error texts with empty formatting braces `{}`
### tag repository
* mapping petitions fetched from the server will now max out at 500k mapping rows or 10k unique tags or ten seconds of construction time. we had a 250k-unique-tag petition this last week and it broke something, so I'm slapping a bunch of safety rails on. let me know if these are too strict, too liberal, or if it messes with the fetch workflow at all--I don't _think_ it will, but we'll see
### build stuff
* now they have had time to breathe, I optimised the recently split Github build scripts. the 'send to an ubuntu runner and then upload' step is now removed from all three, so they are natively uploaded in the first runner step. it works just a little nicer and faster now, although it did require learning how to truncate and export a variable to the Github Environment Variables file in Powershell, aiiieeeee
* also, Github is moving from Node 16 to Node 20 soon, and I have moved two of the four actions we rely on to their newer v20 versions. a third action should be ready to update next week, and another, a general download file function, I have replaced with curl (for macOS) and Powershell's magical Invoke-WebRequest adventure
## [Version 562](https://github.com/hydrusnetwork/hydrus/releases/tag/v562)
### misc
@ -433,52 +483,3 @@ title: Changelog
* thanks to a user, the Client API now has the ability to see and interact with the current popup messages in the popup toaster!
* fixed a stupid typo that I made in the new Client API options call. added a unit test to catch this in future, too
* the client api version is now 57
## [Version 553](https://github.com/hydrusnetwork/hydrus/releases/tag/v553)
### animated gif fixes
* fixed the **false positive** "serious I/O Error! This is a significant hard drive problem" problems saw in last week's animated gif 'has transparency' rescanning. it turns out the PIL animated gif renderer we rarely use was raising overly serious errors on a truncated frame (i.e. some borked file, not a borked OS hard drive access), and this was escalating up to the overall maintenance system, which was shutting down in panic. truncated gifs in the PIL renderer should now just either render the last frame over and over or rewind as soon as they hit a super problem
* as well as rendering, the duration and frame-counter code also handles these borked frames better, so animated gifs that have a single borked frame should now A) import without an error and B) get more accurate frame counts
* you may have seen this sort of error before where an mpv window seems to keep rendering the gif despite the scanbar hitting its right end. if you see a file like that, try right-clicking and hitting _manage->maintenance->regenerate file metadata_. might just fix it up!
* if mpv encounters one of these busted gifs, a selection of quiet-but-spammy "I don't know what happened, but MPV just reported a weird error, here it is" logging no longer happens. we basically know what happened, and mpv seems good at recovering
* fixed some PIL alpha gif rendering on backwards seek and loop
### slideshow tech
* _options->media_ has a new 'slideshow' section with five new options regarding slideshows and media with duration (video, audio). if you don't care too much, just leave them alone--they make slideshows transition better!
* first, there's a checkbox to say 'always play duration-having media completely once through before moving on'. this was the previous behaviour, now default off
* then there are two options, for a percentage of the current slideshow period and a flat seconds value, to say 'if the duration-having media is shorter than this amount of time, then move the slideshow on early'. this allows short gif loops to play a bit, but not for 15 minutes
* then there is an option, for a percentage of the current slideshow period, to say 'if the duration-having media has a duration between this amount and 100% of the slideshow period, then move on once it has played once through'. this allows for clean slideshow transitions for media that is just a bit shorter than the slideshow period
* then there is an option, again for a percentage of the slideshow period, to say 'if the duration-having media is _longer_ than the slideshow period plus this amount of time, then delay moving on until it is played once through'. this allows 35 second videos to complete fully in a 30 second slideshow while stopping a ten minute vid hogging its turn
* completely rewrote the slideshow timer tech
* did a little work bringing the experimental Qt media player up to proper slideshow capability, and neatened the associated code
* yes, hydev did write all these options for his repurposed slideshow computer because he was annoyed about his vidya captures and 500ms loops playing jank on a 30m slideshow period
### misc
* the file-info-summary lines that appear in the top row thumbnail menu submenu now show if a file has audio/transparency/exif/other metadata/icc profile
* the file-info-summary lines that appear in the top row thumbnail menu submenu and the top-center of the media viewer no longer list 'removed from x 5 days ago' for files that were moved internally between local file services. these statements were spammy and not helpful! if you really need them, are available in the 'manage times' dialog. sorry for the annoyance here
* trying to move a file from local file service x to y no longer triggers the archive delete lock, if you have that enabled (this was prohibiting the delete from x after the copy to y is done)
* the 'import options' button now labels itself with 'all default', 'all set', or 'some set' to quick-review what it is holding
* the stupidly named advanced users' `OR*` button is now just `advanced`
* the 'manage->regenerate' thumbnail menu is now called 'maintenance', and it always contains the whole file maintenance job list just as it appears in the main file maintenance panel. tooltips are the longer descriptions
* if you reduce a static check time in a checker options (watchers or subscriptions), the next check time should now recalculate correctly immediately. previously, the new check period wasn't kicking in until the next, delayed cycle. I have preserved the logic that tries to keep a static check time regular (which was the core problem here), where if you check every 7 days on saturday night, then delaying one time and running it on sunday night won't delay the check time phase along to next sunday--the next week it will be due on saturday again
* the system:rating edit panel has some tooltips that say 'Set "is" and leave rating null to search for "unrated".'. maybe this is annoying, maybe I should just add redundant 'unrated' checkboxes, let me know what you think
* when the file maintenance routine runs into a serious error (like we had with the false positive transparent gifs), the popup messages now include a file button for the problem file for easy referral
* if a file breaks MPV in a crashy-looking way, hydrus now makes a popup with a file button to it for easy referral
* fixed a typo issue with the recent temp folder recovery code that broke the temp folder for file imports on the hydrus file repository. sorry for the trouble, this slipped through unit testing because that too has a hacky temp folder solution!
### weird/specific stuff
* I've spaced out many of the initial library loads across the core boot init routine. fingers crossed, the splash screen will open earlier and report more as things each import, rather than taking ages to appear and then suddenly initialising everything real quick (on slow computers). also, a monolithic UI init job is broken up into pieces, which should let the splash update itself a little smoother as your client loads its style and stuff
* the file and database maintenance managers are now initialised in a better stage, and, like the subscription manager, they now do not start any work until the first session is fully loaded
* while pouring over the server code trying to find a petition miscount bug and/or a petition summary fetch bug (which I was entirely unsuccessful at), I stopped it 404ing when there are unexpectedly no petitions to fetch--it should now just give an empty list and reset the count serverside, which is an old behaviour that wasn't working quite right in the modern system. this will cost less CPU than commanding the full service service_info number reset. I will have to investigate the core problem of miscounts more closely to figure out the base problem here
* if two subscription jobs publish files to the same popup label (which merges the popup button to include both sets of files), this file list is now properly deduplicated (so if both subs picked up the same file(s), it now won't try to publish the same file twice). the basic popup button instantiation also clears out dupes
* the 'additional tags'-only tag import options in a subscription query can now no longer be set 'default'. this choice was unintended and has no current meaning if set
* building on last week, there are even fewer duplicate menu tooltips
* updated and clarified the text in _options->external programs_. also, if you try to put a command that does not include "%path%", it'll moan at you with a yes/no dialog
* when 'confirm sending files to trash' is off but 'use advanced file delete dialog' is also on, the advanced file delete dialog will now not pop up if there is only one normal local file service to delete from (it was always popping up before, since it exposes the physical file delete options and the dialog thought it wasn't a 'one-choice' delete)
* the forced-wait throttle that happens on several exception catches is reduced from 1s to 200ms
* I made the new job status queue properly thread-safe with a lock. I forgot to do it last week, whoops!
* fixed the build script to construct a file named .tar.zst for the Ubuntu release, not .tar.gz

View File

@ -34,6 +34,47 @@
<div class="content">
<h1 id="changelog"><a href="#changelog">changelog</a></h1>
<ul>
<li>
<h2 id="version_563"><a href="#version_563">version 563</a></h2>
<ul>
<li><h3>macOS improvements</h3></li>
<li>Thanks to a user, we have multiple improvements for macOS!</li>
<li>There is a new icon for the macOS .app build of hydrus</li>
<li>The macOS app will now appear as "Hydrus" in the menu bar instead of "Hydrus Network"</li>
<li>- Use the native global menu bar on macOS and some Linux desktop environments</li>
<li>- "options" will now appear as "Preferences..." and be under the Hydrus menu on macOS</li>
<li>- "exit" will now appear as "Quit Hydrus" and be under the Hydrus menu on macOS</li>
<li>"exit and force shutdown maintenance", "restart", and "shortcuts" will now be under the Hydrus menu on macOS</li>
<li>The hydrus system tray icon is now enabled for macOS and "minimise to system tray" will be in the Hydrus menu when in advanced mode</li>
<li>macOS debug dialog menus are now disabled by default</li>
<li>The macOS build of hydrus now uses pyoxidizer 0.24.0 and Python 3.10</li>
<li>The command palette and hyperlinks colors in the default Qt stylesheet now use palette based colors that should change based on the Qt style</li>
<li>one thing hydev did: on macOS, Cmd+W _should_ now close any dialog or non-main-gui window, just like the Escape key</li>
<li><h3>shortcuts</h3></li>
<li>by default, Alt+Home/End/Left/Right now does the new thumbnail rearranging. **assuming they do not conflict with an existing mapping, all users will recieve this on update**</li>
<li>by default, the shortcuts system now converts all non-number 'numpad' inputs (e.g. 'numpad Home', 'numpad Return', 'numpad Left') to just be normal inputs. a bunch of different keyboards have whack numpad assignments for non-numpad keys, so if it isn't a number, let's not, by default, make a fuss over the distinction. you can return to the old behaviour by unchecking the new checkbox under _file->shortcuts_</li>
<li>the default shortcuts now no longer spam numpad variants anywhere. existing users can delete the surplus mappings (under 'thumbnails' and maybe some of the 'media' sets) if they like</li>
<li><h3>some UI QoL</h3></li>
<li>the _tag service_ menu button that appears in the autocomplete panel and sometimes some other places in advanced mode now shows a proper check mark in its menu beside its current value</li>
<li>the _location context_ menu button on the other side of an autocomplete panel and some other places also now shows a check mark in its menu beside its current value</li>
<li>the `OR` button on search autocomplete that creates new OR predicates now inherits the current file search domain. it was previously defaulting at all times to the fallback file domain and 'all known tags'</li>
<li>the current search predicates list also now inherits the file search domain when you edit an OR predicate currently in use, same deal</li>
<li>removed the 'favourites' submenu from the taglist menu when no tags are selected</li>
<li>in any import context, the file log's arrow menu now supports deleting all the 'unknown' (outstanding, unstarted) items or setting them all to 'skipped'. the 'abort imports' button (with the stop icon) in HDD and urls import pages is removed</li>
<li><h3>misc</h3></li>
<li>fixed yet another dumb problem with the datetime control's paste button--although the paste was now 'working' on the UI side, the control wasn't saving that result on dialog ok. the fixes both the datetime button and the modified/file service time multi-column list editing</li>
<li>a core asynchronous thread-checking timer in the program has been rewritten from a 20ms-resolution busy-wait to a <1ms proper wait/notify system. a bunch of stuff that works in a thread is now much faster to recognise that blocking UI work is done, and it is more thread-polite about how it does it!</li>
<li>in the `setup_venv` scripts, if it needs to delete an old venv directory but fails to do so, the script now dumps out with an error saying 'hey, you probably have it open in a terminal/IDE, please close that and try again'. previously, it would just charge on and produce an odd file permission error as, e.g., the new venv setup tried to overwrite the in-use python exe</li>
<li>added a `help->debug->gui->isolate existing mpv widgets` command to force regeneration of mpv windows and help test-out/hack-fix various 'every other of my mpv views has no audio' and 'my mpv loses xxx property after a system sleep/wake cycle' problems. if I've been working with you on this stuff, please give it a go and let me know if new mpv window creation is good or what!</li>
<li>added a `BUGFIX: Disable off-screen window rescue` checkbox to `options->gui` that stops windows that think they are spawning off-screen from repositioning to a known safe screen. several Qt versions have had trouble with enumerating all the screens in a multiple monitor setup and thus the safe coordinate space, so if you have been hit by false positives here, you can now turn it off! (issue #1511)</li>
<li>fixed another couple instances of error texts with empty formatting braces `{}`</li>
<li><h3>tag repository</h3></li>
<li>mapping petitions fetched from the server will now max out at 500k mapping rows or 10k unique tags or ten seconds of construction time. we had a 250k-unique-tag petition this last week and it broke something, so I'm slapping a bunch of safety rails on. let me know if these are too strict, too liberal, or if it messes with the fetch workflow at all--I don't _think_ it will, but we'll see</li>
<li><h3>build stuff</h3></li>
<li>now they have had time to breathe, I optimised the recently split Github build scripts. the 'send to an ubuntu runner and then upload' step is now removed from all three, so they are natively uploaded in the first runner step. it works just a little nicer and faster now, although it did require learning how to truncate and export a variable to the Github Environment Variables file in Powershell, aiiieeeee</li>
<li>also, Github is moving from Node 16 to Node 20 soon, and I have moved two of the four actions we rely on to their newer v20 versions. a third action should be ready to update next week, and another, a general download file function, I have replaced with curl (for macOS) and Powershell's magical Invoke-WebRequest adventure</li>
</ul>
</li>
<li>
<h2 id="version_562"><a href="#version_562">version 562</a></h2>
<ul>

View File

@ -360,9 +360,7 @@ class APIPermissions( HydrusSerialisable.SerialisableBaseNamed ):
if num_files_allowed_to_see != num_files_asked_for:
error_text = 'You do not seem to have access to all those files! You asked to see {} files, but you were only authorised to see {} of them!'
error_text = error_text.format( HydrusData.ToHumanInt( num_files_asked_for ), HydrusData.ToHumanInt( num_files_allowed_to_see ) )
error_text = f'You do not seem to have access to all those files! You asked to see {HydrusData.ToHumanInt( num_files_asked_for )} files, but you were only authorised to see {HydrusData.ToHumanInt( num_files_allowed_to_see )} of them!'
raise HydrusExceptions.InsufficientCredentialsException( error_text )

View File

@ -446,15 +446,17 @@ class Controller( ClientControllerInterface.ClientControllerInterface, HydrusCon
# but I also don't want a hang, as we have seen with some GUI async job that got fired on shutdown and it seems some event queue was halted or deadlocked
# so, we'll give it 16ms to work, then we'll start testing for shutdown hang
done_event = job_status.GetDoneEvent()
while not job_status.IsDone():
done_event.wait( 1.0 )
if HG.model_shutdown or not self._qt_app_running:
raise HydrusExceptions.ShutdownException( 'Application is shutting down!' )
time.sleep( 0.02 )
if job_status.HasVariable( 'result' ):

View File

@ -565,18 +565,10 @@ def GetDefaultShortcuts():
ClientGUIShortcuts.Shortcut( ClientGUIShortcuts.SHORTCUT_TYPE_KEYBOARD_SPECIAL, ClientGUIShortcuts.SHORTCUT_KEY_SPECIAL_ENTER, ClientGUIShortcuts.SHORTCUT_PRESS_TYPE_PRESS, [] ),
CAC.ApplicationCommand.STATICCreateSimpleCommand( CAC.SIMPLE_CLOSE_MEDIA_VIEWER )
)
media_viewer.SetCommand(
ClientGUIShortcuts.Shortcut( ClientGUIShortcuts.SHORTCUT_TYPE_KEYBOARD_SPECIAL, ClientGUIShortcuts.SHORTCUT_KEY_SPECIAL_ENTER, ClientGUIShortcuts.SHORTCUT_PRESS_TYPE_PRESS, [ ClientGUIShortcuts.SHORTCUT_MODIFIER_KEYPAD ] ),
CAC.ApplicationCommand.STATICCreateSimpleCommand( CAC.SIMPLE_CLOSE_MEDIA_VIEWER )
)
media_viewer.SetCommand(
ClientGUIShortcuts.Shortcut( ClientGUIShortcuts.SHORTCUT_TYPE_KEYBOARD_SPECIAL, ClientGUIShortcuts.SHORTCUT_KEY_SPECIAL_RETURN, ClientGUIShortcuts.SHORTCUT_PRESS_TYPE_PRESS, [] ),
CAC.ApplicationCommand.STATICCreateSimpleCommand( CAC.SIMPLE_CLOSE_MEDIA_VIEWER )
)
media_viewer.SetCommand(
ClientGUIShortcuts.Shortcut( ClientGUIShortcuts.SHORTCUT_TYPE_KEYBOARD_SPECIAL, ClientGUIShortcuts.SHORTCUT_KEY_SPECIAL_RETURN, ClientGUIShortcuts.SHORTCUT_PRESS_TYPE_PRESS, [ ClientGUIShortcuts.SHORTCUT_MODIFIER_KEYPAD ] ),
CAC.ApplicationCommand.STATICCreateSimpleCommand( CAC.SIMPLE_CLOSE_MEDIA_VIEWER )
)
media_viewer.SetCommand(
ClientGUIShortcuts.Shortcut( ClientGUIShortcuts.SHORTCUT_TYPE_KEYBOARD_SPECIAL, ClientGUIShortcuts.SHORTCUT_KEY_SPECIAL_ESCAPE, ClientGUIShortcuts.SHORTCUT_PRESS_TYPE_PRESS, [] ),
CAC.ApplicationCommand.STATICCreateSimpleCommand( CAC.SIMPLE_CLOSE_MEDIA_VIEWER )
@ -616,18 +608,10 @@ def GetDefaultShortcuts():
ClientGUIShortcuts.Shortcut( ClientGUIShortcuts.SHORTCUT_TYPE_KEYBOARD_SPECIAL, ClientGUIShortcuts.SHORTCUT_KEY_SPECIAL_ENTER, ClientGUIShortcuts.SHORTCUT_PRESS_TYPE_PRESS, [] ),
CAC.ApplicationCommand.STATICCreateSimpleCommand( CAC.SIMPLE_LAUNCH_MEDIA_VIEWER )
)
thumbnails.SetCommand(
ClientGUIShortcuts.Shortcut( ClientGUIShortcuts.SHORTCUT_TYPE_KEYBOARD_SPECIAL, ClientGUIShortcuts.SHORTCUT_KEY_SPECIAL_ENTER, ClientGUIShortcuts.SHORTCUT_PRESS_TYPE_PRESS, [ ClientGUIShortcuts.SHORTCUT_MODIFIER_KEYPAD ] ),
CAC.ApplicationCommand.STATICCreateSimpleCommand( CAC.SIMPLE_LAUNCH_MEDIA_VIEWER )
)
thumbnails.SetCommand(
ClientGUIShortcuts.Shortcut( ClientGUIShortcuts.SHORTCUT_TYPE_KEYBOARD_SPECIAL, ClientGUIShortcuts.SHORTCUT_KEY_SPECIAL_RETURN, ClientGUIShortcuts.SHORTCUT_PRESS_TYPE_PRESS, [] ),
CAC.ApplicationCommand.STATICCreateSimpleCommand( CAC.SIMPLE_LAUNCH_MEDIA_VIEWER )
)
thumbnails.SetCommand(
ClientGUIShortcuts.Shortcut( ClientGUIShortcuts.SHORTCUT_TYPE_KEYBOARD_SPECIAL, ClientGUIShortcuts.SHORTCUT_KEY_SPECIAL_RETURN, ClientGUIShortcuts.SHORTCUT_PRESS_TYPE_PRESS, [ ClientGUIShortcuts.SHORTCUT_MODIFIER_KEYPAD ] ),
CAC.ApplicationCommand.STATICCreateSimpleCommand( CAC.SIMPLE_LAUNCH_MEDIA_VIEWER )
)
thumbnails.SetCommand(
ClientGUIShortcuts.Shortcut( ClientGUIShortcuts.SHORTCUT_TYPE_KEYBOARD_SPECIAL, ClientGUIShortcuts.SHORTCUT_KEY_SPECIAL_F12, ClientGUIShortcuts.SHORTCUT_PRESS_TYPE_PRESS, [] ),
@ -638,136 +622,89 @@ def GetDefaultShortcuts():
ClientGUIShortcuts.Shortcut( ClientGUIShortcuts.SHORTCUT_TYPE_KEYBOARD_SPECIAL, ClientGUIShortcuts.SHORTCUT_KEY_SPECIAL_HOME, ClientGUIShortcuts.SHORTCUT_PRESS_TYPE_PRESS, [] ),
CAC.ApplicationCommand.STATICCreateSimpleCommand( CAC.SIMPLE_MOVE_THUMBNAIL_FOCUS, simple_data = ( CAC.MOVE_HOME, CAC.SELECTION_STATUS_NORMAL ) )
)
thumbnails.SetCommand(
ClientGUIShortcuts.Shortcut( ClientGUIShortcuts.SHORTCUT_TYPE_KEYBOARD_SPECIAL, ClientGUIShortcuts.SHORTCUT_KEY_SPECIAL_HOME, ClientGUIShortcuts.SHORTCUT_PRESS_TYPE_PRESS, [ ClientGUIShortcuts.SHORTCUT_MODIFIER_KEYPAD ] ),
CAC.ApplicationCommand.STATICCreateSimpleCommand( CAC.SIMPLE_MOVE_THUMBNAIL_FOCUS, simple_data = ( CAC.MOVE_HOME, CAC.SELECTION_STATUS_NORMAL ) )
)
thumbnails.SetCommand(
ClientGUIShortcuts.Shortcut( ClientGUIShortcuts.SHORTCUT_TYPE_KEYBOARD_SPECIAL, ClientGUIShortcuts.SHORTCUT_KEY_SPECIAL_HOME, ClientGUIShortcuts.SHORTCUT_PRESS_TYPE_PRESS, [ ClientGUIShortcuts.SHORTCUT_MODIFIER_SHIFT ] ),
CAC.ApplicationCommand.STATICCreateSimpleCommand( CAC.SIMPLE_MOVE_THUMBNAIL_FOCUS, simple_data = ( CAC.MOVE_HOME, CAC.SELECTION_STATUS_SHIFT ) )
)
thumbnails.SetCommand(
ClientGUIShortcuts.Shortcut( ClientGUIShortcuts.SHORTCUT_TYPE_KEYBOARD_SPECIAL, ClientGUIShortcuts.SHORTCUT_KEY_SPECIAL_HOME, ClientGUIShortcuts.SHORTCUT_PRESS_TYPE_PRESS, [ ClientGUIShortcuts.SHORTCUT_MODIFIER_KEYPAD, ClientGUIShortcuts.SHORTCUT_MODIFIER_SHIFT ] ),
CAC.ApplicationCommand.STATICCreateSimpleCommand( CAC.SIMPLE_MOVE_THUMBNAIL_FOCUS, simple_data = ( CAC.MOVE_HOME, CAC.SELECTION_STATUS_SHIFT ) )
)
thumbnails.SetCommand(
ClientGUIShortcuts.Shortcut( ClientGUIShortcuts.SHORTCUT_TYPE_KEYBOARD_SPECIAL, ClientGUIShortcuts.SHORTCUT_KEY_SPECIAL_END, ClientGUIShortcuts.SHORTCUT_PRESS_TYPE_PRESS, [] ),
CAC.ApplicationCommand.STATICCreateSimpleCommand( CAC.SIMPLE_MOVE_THUMBNAIL_FOCUS, simple_data = ( CAC.MOVE_END, CAC.SELECTION_STATUS_NORMAL ) )
)
thumbnails.SetCommand(
ClientGUIShortcuts.Shortcut( ClientGUIShortcuts.SHORTCUT_TYPE_KEYBOARD_SPECIAL, ClientGUIShortcuts.SHORTCUT_KEY_SPECIAL_END, ClientGUIShortcuts.SHORTCUT_PRESS_TYPE_PRESS, [ ClientGUIShortcuts.SHORTCUT_MODIFIER_KEYPAD ] ),
CAC.ApplicationCommand.STATICCreateSimpleCommand( CAC.SIMPLE_MOVE_THUMBNAIL_FOCUS, simple_data = ( CAC.MOVE_END, CAC.SELECTION_STATUS_NORMAL ) )
)
thumbnails.SetCommand(
ClientGUIShortcuts.Shortcut( ClientGUIShortcuts.SHORTCUT_TYPE_KEYBOARD_SPECIAL, ClientGUIShortcuts.SHORTCUT_KEY_SPECIAL_END, ClientGUIShortcuts.SHORTCUT_PRESS_TYPE_PRESS, [ ClientGUIShortcuts.SHORTCUT_MODIFIER_SHIFT ] ),
CAC.ApplicationCommand.STATICCreateSimpleCommand( CAC.SIMPLE_MOVE_THUMBNAIL_FOCUS, simple_data = ( CAC.MOVE_END, CAC.SELECTION_STATUS_SHIFT ) )
)
thumbnails.SetCommand(
ClientGUIShortcuts.Shortcut( ClientGUIShortcuts.SHORTCUT_TYPE_KEYBOARD_SPECIAL, ClientGUIShortcuts.SHORTCUT_KEY_SPECIAL_END, ClientGUIShortcuts.SHORTCUT_PRESS_TYPE_PRESS, [ ClientGUIShortcuts.SHORTCUT_MODIFIER_KEYPAD, ClientGUIShortcuts.SHORTCUT_MODIFIER_SHIFT ] ),
CAC.ApplicationCommand.STATICCreateSimpleCommand( CAC.SIMPLE_MOVE_THUMBNAIL_FOCUS, simple_data = ( CAC.MOVE_END, CAC.SELECTION_STATUS_SHIFT ) )
)
thumbnails.SetCommand(
ClientGUIShortcuts.Shortcut( ClientGUIShortcuts.SHORTCUT_TYPE_KEYBOARD_SPECIAL, ClientGUIShortcuts.SHORTCUT_KEY_SPECIAL_LEFT, ClientGUIShortcuts.SHORTCUT_PRESS_TYPE_PRESS, [] ),
CAC.ApplicationCommand.STATICCreateSimpleCommand( CAC.SIMPLE_MOVE_THUMBNAIL_FOCUS, simple_data = ( CAC.MOVE_LEFT, CAC.SELECTION_STATUS_NORMAL ) )
)
thumbnails.SetCommand(
ClientGUIShortcuts.Shortcut( ClientGUIShortcuts.SHORTCUT_TYPE_KEYBOARD_SPECIAL, ClientGUIShortcuts.SHORTCUT_KEY_SPECIAL_LEFT, ClientGUIShortcuts.SHORTCUT_PRESS_TYPE_PRESS, [ ClientGUIShortcuts.SHORTCUT_MODIFIER_KEYPAD ] ),
CAC.ApplicationCommand.STATICCreateSimpleCommand( CAC.SIMPLE_MOVE_THUMBNAIL_FOCUS, simple_data = ( CAC.MOVE_LEFT, CAC.SELECTION_STATUS_NORMAL ) )
)
thumbnails.SetCommand(
ClientGUIShortcuts.Shortcut( ClientGUIShortcuts.SHORTCUT_TYPE_KEYBOARD_SPECIAL, ClientGUIShortcuts.SHORTCUT_KEY_SPECIAL_LEFT, ClientGUIShortcuts.SHORTCUT_PRESS_TYPE_PRESS, [ ClientGUIShortcuts.SHORTCUT_MODIFIER_SHIFT ] ),
CAC.ApplicationCommand.STATICCreateSimpleCommand( CAC.SIMPLE_MOVE_THUMBNAIL_FOCUS, simple_data = ( CAC.MOVE_LEFT, CAC.SELECTION_STATUS_SHIFT ) )
)
thumbnails.SetCommand(
ClientGUIShortcuts.Shortcut( ClientGUIShortcuts.SHORTCUT_TYPE_KEYBOARD_SPECIAL, ClientGUIShortcuts.SHORTCUT_KEY_SPECIAL_LEFT, ClientGUIShortcuts.SHORTCUT_PRESS_TYPE_PRESS, [ ClientGUIShortcuts.SHORTCUT_MODIFIER_KEYPAD, ClientGUIShortcuts.SHORTCUT_MODIFIER_SHIFT ] ),
CAC.ApplicationCommand.STATICCreateSimpleCommand( CAC.SIMPLE_MOVE_THUMBNAIL_FOCUS, simple_data = ( CAC.MOVE_LEFT, CAC.SELECTION_STATUS_SHIFT ) )
)
thumbnails.SetCommand(
ClientGUIShortcuts.Shortcut( ClientGUIShortcuts.SHORTCUT_TYPE_KEYBOARD_SPECIAL, ClientGUIShortcuts.SHORTCUT_KEY_SPECIAL_RIGHT, ClientGUIShortcuts.SHORTCUT_PRESS_TYPE_PRESS, [] ),
CAC.ApplicationCommand.STATICCreateSimpleCommand( CAC.SIMPLE_MOVE_THUMBNAIL_FOCUS, simple_data = ( CAC.MOVE_RIGHT, CAC.SELECTION_STATUS_NORMAL ) )
)
thumbnails.SetCommand(
ClientGUIShortcuts.Shortcut( ClientGUIShortcuts.SHORTCUT_TYPE_KEYBOARD_SPECIAL, ClientGUIShortcuts.SHORTCUT_KEY_SPECIAL_RIGHT, ClientGUIShortcuts.SHORTCUT_PRESS_TYPE_PRESS, [ ClientGUIShortcuts.SHORTCUT_MODIFIER_KEYPAD ] ),
CAC.ApplicationCommand.STATICCreateSimpleCommand( CAC.SIMPLE_MOVE_THUMBNAIL_FOCUS, simple_data = ( CAC.MOVE_RIGHT, CAC.SELECTION_STATUS_NORMAL ) )
)
thumbnails.SetCommand(
ClientGUIShortcuts.Shortcut( ClientGUIShortcuts.SHORTCUT_TYPE_KEYBOARD_SPECIAL, ClientGUIShortcuts.SHORTCUT_KEY_SPECIAL_RIGHT, ClientGUIShortcuts.SHORTCUT_PRESS_TYPE_PRESS, [ ClientGUIShortcuts.SHORTCUT_MODIFIER_SHIFT ] ),
CAC.ApplicationCommand.STATICCreateSimpleCommand( CAC.SIMPLE_MOVE_THUMBNAIL_FOCUS, simple_data = ( CAC.MOVE_RIGHT, CAC.SELECTION_STATUS_SHIFT ) )
)
thumbnails.SetCommand(
ClientGUIShortcuts.Shortcut( ClientGUIShortcuts.SHORTCUT_TYPE_KEYBOARD_SPECIAL, ClientGUIShortcuts.SHORTCUT_KEY_SPECIAL_RIGHT, ClientGUIShortcuts.SHORTCUT_PRESS_TYPE_PRESS, [ ClientGUIShortcuts.SHORTCUT_MODIFIER_KEYPAD, ClientGUIShortcuts.SHORTCUT_MODIFIER_SHIFT ] ),
CAC.ApplicationCommand.STATICCreateSimpleCommand( CAC.SIMPLE_MOVE_THUMBNAIL_FOCUS, simple_data = ( CAC.MOVE_RIGHT, CAC.SELECTION_STATUS_SHIFT ) )
)
thumbnails.SetCommand(
ClientGUIShortcuts.Shortcut( ClientGUIShortcuts.SHORTCUT_TYPE_KEYBOARD_SPECIAL, ClientGUIShortcuts.SHORTCUT_KEY_SPECIAL_UP, ClientGUIShortcuts.SHORTCUT_PRESS_TYPE_PRESS, [] ),
CAC.ApplicationCommand.STATICCreateSimpleCommand( CAC.SIMPLE_MOVE_THUMBNAIL_FOCUS, simple_data = ( CAC.MOVE_UP, CAC.SELECTION_STATUS_NORMAL ) )
)
thumbnails.SetCommand(
ClientGUIShortcuts.Shortcut( ClientGUIShortcuts.SHORTCUT_TYPE_KEYBOARD_SPECIAL, ClientGUIShortcuts.SHORTCUT_KEY_SPECIAL_UP, ClientGUIShortcuts.SHORTCUT_PRESS_TYPE_PRESS, [ ClientGUIShortcuts.SHORTCUT_MODIFIER_KEYPAD ] ),
CAC.ApplicationCommand.STATICCreateSimpleCommand( CAC.SIMPLE_MOVE_THUMBNAIL_FOCUS, simple_data = ( CAC.MOVE_UP, CAC.SELECTION_STATUS_NORMAL ) )
)
thumbnails.SetCommand(
ClientGUIShortcuts.Shortcut( ClientGUIShortcuts.SHORTCUT_TYPE_KEYBOARD_SPECIAL, ClientGUIShortcuts.SHORTCUT_KEY_SPECIAL_UP, ClientGUIShortcuts.SHORTCUT_PRESS_TYPE_PRESS, [ ClientGUIShortcuts.SHORTCUT_MODIFIER_SHIFT ] ),
CAC.ApplicationCommand.STATICCreateSimpleCommand( CAC.SIMPLE_MOVE_THUMBNAIL_FOCUS, simple_data = ( CAC.MOVE_UP, CAC.SELECTION_STATUS_SHIFT ) )
)
thumbnails.SetCommand(
ClientGUIShortcuts.Shortcut( ClientGUIShortcuts.SHORTCUT_TYPE_KEYBOARD_SPECIAL, ClientGUIShortcuts.SHORTCUT_KEY_SPECIAL_UP, ClientGUIShortcuts.SHORTCUT_PRESS_TYPE_PRESS, [ ClientGUIShortcuts.SHORTCUT_MODIFIER_KEYPAD, ClientGUIShortcuts.SHORTCUT_MODIFIER_SHIFT ] ),
CAC.ApplicationCommand.STATICCreateSimpleCommand( CAC.SIMPLE_MOVE_THUMBNAIL_FOCUS, simple_data = ( CAC.MOVE_UP, CAC.SELECTION_STATUS_SHIFT ) )
)
thumbnails.SetCommand(
ClientGUIShortcuts.Shortcut( ClientGUIShortcuts.SHORTCUT_TYPE_KEYBOARD_SPECIAL, ClientGUIShortcuts.SHORTCUT_KEY_SPECIAL_DOWN, ClientGUIShortcuts.SHORTCUT_PRESS_TYPE_PRESS, [] ),
CAC.ApplicationCommand.STATICCreateSimpleCommand( CAC.SIMPLE_MOVE_THUMBNAIL_FOCUS, simple_data = ( CAC.MOVE_DOWN, CAC.SELECTION_STATUS_NORMAL ) )
)
thumbnails.SetCommand(
ClientGUIShortcuts.Shortcut( ClientGUIShortcuts.SHORTCUT_TYPE_KEYBOARD_SPECIAL, ClientGUIShortcuts.SHORTCUT_KEY_SPECIAL_DOWN, ClientGUIShortcuts.SHORTCUT_PRESS_TYPE_PRESS, [ ClientGUIShortcuts.SHORTCUT_MODIFIER_KEYPAD ] ),
CAC.ApplicationCommand.STATICCreateSimpleCommand( CAC.SIMPLE_MOVE_THUMBNAIL_FOCUS, simple_data = ( CAC.MOVE_DOWN, CAC.SELECTION_STATUS_NORMAL ) )
)
thumbnails.SetCommand(
ClientGUIShortcuts.Shortcut( ClientGUIShortcuts.SHORTCUT_TYPE_KEYBOARD_SPECIAL, ClientGUIShortcuts.SHORTCUT_KEY_SPECIAL_DOWN, ClientGUIShortcuts.SHORTCUT_PRESS_TYPE_PRESS, [ ClientGUIShortcuts.SHORTCUT_MODIFIER_SHIFT ] ),
CAC.ApplicationCommand.STATICCreateSimpleCommand( CAC.SIMPLE_MOVE_THUMBNAIL_FOCUS, simple_data = ( CAC.MOVE_DOWN, CAC.SELECTION_STATUS_SHIFT ) )
)
thumbnails.SetCommand(
ClientGUIShortcuts.Shortcut( ClientGUIShortcuts.SHORTCUT_TYPE_KEYBOARD_SPECIAL, ClientGUIShortcuts.SHORTCUT_KEY_SPECIAL_DOWN, ClientGUIShortcuts.SHORTCUT_PRESS_TYPE_PRESS, [ ClientGUIShortcuts.SHORTCUT_MODIFIER_KEYPAD, ClientGUIShortcuts.SHORTCUT_MODIFIER_SHIFT ] ),
CAC.ApplicationCommand.STATICCreateSimpleCommand( CAC.SIMPLE_MOVE_THUMBNAIL_FOCUS, simple_data = ( CAC.MOVE_DOWN, CAC.SELECTION_STATUS_SHIFT ) )
)
thumbnails.SetCommand(
ClientGUIShortcuts.Shortcut( ClientGUIShortcuts.SHORTCUT_TYPE_KEYBOARD_SPECIAL, ClientGUIShortcuts.SHORTCUT_KEY_SPECIAL_PAGE_UP, ClientGUIShortcuts.SHORTCUT_PRESS_TYPE_PRESS, [] ),
CAC.ApplicationCommand.STATICCreateSimpleCommand( CAC.SIMPLE_MOVE_THUMBNAIL_FOCUS, simple_data = ( CAC.MOVE_PAGE_UP, CAC.SELECTION_STATUS_NORMAL ) )
)
thumbnails.SetCommand(
ClientGUIShortcuts.Shortcut( ClientGUIShortcuts.SHORTCUT_TYPE_KEYBOARD_SPECIAL, ClientGUIShortcuts.SHORTCUT_KEY_SPECIAL_PAGE_UP, ClientGUIShortcuts.SHORTCUT_PRESS_TYPE_PRESS, [ ClientGUIShortcuts.SHORTCUT_MODIFIER_KEYPAD ] ),
CAC.ApplicationCommand.STATICCreateSimpleCommand( CAC.SIMPLE_MOVE_THUMBNAIL_FOCUS, simple_data = ( CAC.MOVE_PAGE_UP, CAC.SELECTION_STATUS_NORMAL ) )
)
thumbnails.SetCommand(
ClientGUIShortcuts.Shortcut( ClientGUIShortcuts.SHORTCUT_TYPE_KEYBOARD_SPECIAL, ClientGUIShortcuts.SHORTCUT_KEY_SPECIAL_PAGE_UP, ClientGUIShortcuts.SHORTCUT_PRESS_TYPE_PRESS, [ ClientGUIShortcuts.SHORTCUT_MODIFIER_SHIFT ] ),
CAC.ApplicationCommand.STATICCreateSimpleCommand( CAC.SIMPLE_MOVE_THUMBNAIL_FOCUS, simple_data = ( CAC.MOVE_PAGE_UP, CAC.SELECTION_STATUS_SHIFT ) )
)
thumbnails.SetCommand(
ClientGUIShortcuts.Shortcut( ClientGUIShortcuts.SHORTCUT_TYPE_KEYBOARD_SPECIAL, ClientGUIShortcuts.SHORTCUT_KEY_SPECIAL_PAGE_UP, ClientGUIShortcuts.SHORTCUT_PRESS_TYPE_PRESS, [ ClientGUIShortcuts.SHORTCUT_MODIFIER_KEYPAD, ClientGUIShortcuts.SHORTCUT_MODIFIER_SHIFT ] ),
CAC.ApplicationCommand.STATICCreateSimpleCommand( CAC.SIMPLE_MOVE_THUMBNAIL_FOCUS, simple_data = ( CAC.MOVE_PAGE_UP, CAC.SELECTION_STATUS_SHIFT ) )
)
thumbnails.SetCommand(
ClientGUIShortcuts.Shortcut( ClientGUIShortcuts.SHORTCUT_TYPE_KEYBOARD_SPECIAL, ClientGUIShortcuts.SHORTCUT_KEY_SPECIAL_PAGE_DOWN, ClientGUIShortcuts.SHORTCUT_PRESS_TYPE_PRESS, [] ),
CAC.ApplicationCommand.STATICCreateSimpleCommand( CAC.SIMPLE_MOVE_THUMBNAIL_FOCUS, simple_data = ( CAC.MOVE_PAGE_DOWN, CAC.SELECTION_STATUS_NORMAL ) )
)
thumbnails.SetCommand(
ClientGUIShortcuts.Shortcut( ClientGUIShortcuts.SHORTCUT_TYPE_KEYBOARD_SPECIAL, ClientGUIShortcuts.SHORTCUT_KEY_SPECIAL_PAGE_DOWN, ClientGUIShortcuts.SHORTCUT_PRESS_TYPE_PRESS, [ ClientGUIShortcuts.SHORTCUT_MODIFIER_KEYPAD ] ),
CAC.ApplicationCommand.STATICCreateSimpleCommand( CAC.SIMPLE_MOVE_THUMBNAIL_FOCUS, simple_data = ( CAC.MOVE_PAGE_DOWN, CAC.SELECTION_STATUS_NORMAL ) )
)
thumbnails.SetCommand(
ClientGUIShortcuts.Shortcut( ClientGUIShortcuts.SHORTCUT_TYPE_KEYBOARD_SPECIAL, ClientGUIShortcuts.SHORTCUT_KEY_SPECIAL_PAGE_DOWN, ClientGUIShortcuts.SHORTCUT_PRESS_TYPE_PRESS, [ ClientGUIShortcuts.SHORTCUT_MODIFIER_SHIFT ] ),
CAC.ApplicationCommand.STATICCreateSimpleCommand( CAC.SIMPLE_MOVE_THUMBNAIL_FOCUS, simple_data = ( CAC.MOVE_PAGE_DOWN, CAC.SELECTION_STATUS_SHIFT ) )
)
thumbnails.SetCommand(
ClientGUIShortcuts.Shortcut( ClientGUIShortcuts.SHORTCUT_TYPE_KEYBOARD_SPECIAL, ClientGUIShortcuts.SHORTCUT_KEY_SPECIAL_PAGE_DOWN, ClientGUIShortcuts.SHORTCUT_PRESS_TYPE_PRESS, [ ClientGUIShortcuts.SHORTCUT_MODIFIER_KEYPAD, ClientGUIShortcuts.SHORTCUT_MODIFIER_SHIFT ] ),
CAC.ApplicationCommand.STATICCreateSimpleCommand( CAC.SIMPLE_MOVE_THUMBNAIL_FOCUS, simple_data = ( CAC.MOVE_PAGE_DOWN, CAC.SELECTION_STATUS_SHIFT ) )
ClientGUIShortcuts.Shortcut( ClientGUIShortcuts.SHORTCUT_TYPE_KEYBOARD_SPECIAL, ClientGUIShortcuts.SHORTCUT_KEY_SPECIAL_HOME, ClientGUIShortcuts.SHORTCUT_PRESS_TYPE_PRESS, [ ClientGUIShortcuts.SHORTCUT_MODIFIER_ALT ] ),
CAC.ApplicationCommand.STATICCreateSimpleCommand( CAC.SIMPLE_REARRANGE_THUMBNAILS, ( CAC.REARRANGE_THUMBNAILS_TYPE_COMMAND, CAC.MOVE_HOME ) )
)
thumbnails.SetCommand(
ClientGUIShortcuts.Shortcut( ClientGUIShortcuts.SHORTCUT_TYPE_KEYBOARD_SPECIAL, ClientGUIShortcuts.SHORTCUT_KEY_SPECIAL_END, ClientGUIShortcuts.SHORTCUT_PRESS_TYPE_PRESS, [ ClientGUIShortcuts.SHORTCUT_MODIFIER_ALT ] ),
CAC.ApplicationCommand.STATICCreateSimpleCommand( CAC.SIMPLE_REARRANGE_THUMBNAILS, ( CAC.REARRANGE_THUMBNAILS_TYPE_COMMAND, CAC.MOVE_END ) )
)
thumbnails.SetCommand(
ClientGUIShortcuts.Shortcut( ClientGUIShortcuts.SHORTCUT_TYPE_KEYBOARD_SPECIAL, ClientGUIShortcuts.SHORTCUT_KEY_SPECIAL_LEFT, ClientGUIShortcuts.SHORTCUT_PRESS_TYPE_PRESS, [ ClientGUIShortcuts.SHORTCUT_MODIFIER_ALT ] ),
CAC.ApplicationCommand.STATICCreateSimpleCommand( CAC.SIMPLE_REARRANGE_THUMBNAILS, ( CAC.REARRANGE_THUMBNAILS_TYPE_COMMAND, CAC.MOVE_LEFT ) )
)
thumbnails.SetCommand(
ClientGUIShortcuts.Shortcut( ClientGUIShortcuts.SHORTCUT_TYPE_KEYBOARD_SPECIAL, ClientGUIShortcuts.SHORTCUT_KEY_SPECIAL_RIGHT, ClientGUIShortcuts.SHORTCUT_PRESS_TYPE_PRESS, [ ClientGUIShortcuts.SHORTCUT_MODIFIER_ALT ] ),
CAC.ApplicationCommand.STATICCreateSimpleCommand( CAC.SIMPLE_REARRANGE_THUMBNAILS, ( CAC.REARRANGE_THUMBNAILS_TYPE_COMMAND, CAC.MOVE_RIGHT ) )
)
from hydrus.client.media import ClientMediaFileFilter

View File

@ -143,6 +143,10 @@ class ClientOptions( HydrusSerialisable.SerialisableBase ):
self._dictionary[ 'booleans' ][ 'show_related_tags' ] = True
self._dictionary[ 'booleans' ][ 'show_file_lookup_script_tags' ] = False
self._dictionary[ 'booleans' ][ 'shortcuts_merge_non_number_numpad' ] = True
self._dictionary[ 'booleans' ][ 'disable_get_safe_position_test' ] = False
self._dictionary[ 'booleans' ][ 'freeze_message_manager_when_mouse_on_other_monitor' ] = False
self._dictionary[ 'booleans' ][ 'freeze_message_manager_when_main_gui_minimised' ] = False

View File

@ -5,7 +5,7 @@ import typing
from hydrus.core import HydrusConstants as HC
from hydrus.core import HydrusData
from hydrus.core import HydrusGlobals as HG
from hydrus.core import HydrusExceptions
from hydrus.core import HydrusThreading
from hydrus.core import HydrusTime
@ -34,14 +34,15 @@ class JobStatus( object ):
self._i_am_an_ongoing_job = self._pausable or self._cancellable
self._done_event = threading.Event()
if self._i_am_an_ongoing_job:
self._i_am_done = False
self._job_finish_time = None
else:
self._i_am_done = True
self._done_event.set()
self._job_finish_time = HydrusTime.GetNowFloat()
@ -77,7 +78,7 @@ class JobStatus( object ):
if self._cancel_tests_regular_checker.Due():
if not self._i_am_done:
if not self._done_event.is_set():
if self._cancel_on_shutdown and HydrusThreading.IsThreadShuttingDown():
@ -157,7 +158,6 @@ class JobStatus( object ):
def Finish( self ):
self._i_am_done = True
self._job_finish_time = HydrusTime.GetNowFloat()
self._paused = False
@ -165,6 +165,8 @@ class JobStatus( object ):
self._pausable = False
self._cancellable = False
self._done_event.set()
def FinishAndDismiss( self, seconds = None ):
@ -185,6 +187,11 @@ class JobStatus( object ):
return self._creation_time
def GetDoneEvent( self ) -> threading.Event:
return self._done_event
def GetErrorException( self ) -> Exception:
if self._exception is None:
@ -288,7 +295,7 @@ class JobStatus( object ):
self._CheckCancelTests()
return self._i_am_done
return self._done_event.is_set()
def IsPausable( self ):

View File

@ -10092,6 +10092,54 @@ class DB( HydrusDB.HydrusDB ):
if version == 562:
try:
from hydrus.client.gui import ClientGUIShortcuts
from hydrus.client import ClientApplicationCommand as CAC
thumbnails_shortcuts_set: ClientGUIShortcuts.ShortcutSet = self.modules_serialisable.GetJSONDumpNamed( HydrusSerialisable.SERIALISABLE_TYPE_SHORTCUT_SET, 'thumbnails' )
jobs = [
(
ClientGUIShortcuts.Shortcut( ClientGUIShortcuts.SHORTCUT_TYPE_KEYBOARD_SPECIAL, ClientGUIShortcuts.SHORTCUT_KEY_SPECIAL_HOME, ClientGUIShortcuts.SHORTCUT_PRESS_TYPE_PRESS, [ ClientGUIShortcuts.SHORTCUT_MODIFIER_ALT ] ),
CAC.ApplicationCommand.STATICCreateSimpleCommand( CAC.SIMPLE_REARRANGE_THUMBNAILS, ( CAC.REARRANGE_THUMBNAILS_TYPE_COMMAND, CAC.MOVE_HOME ) )
),
(
ClientGUIShortcuts.Shortcut( ClientGUIShortcuts.SHORTCUT_TYPE_KEYBOARD_SPECIAL, ClientGUIShortcuts.SHORTCUT_KEY_SPECIAL_END, ClientGUIShortcuts.SHORTCUT_PRESS_TYPE_PRESS, [ ClientGUIShortcuts.SHORTCUT_MODIFIER_ALT ] ),
CAC.ApplicationCommand.STATICCreateSimpleCommand( CAC.SIMPLE_REARRANGE_THUMBNAILS, ( CAC.REARRANGE_THUMBNAILS_TYPE_COMMAND, CAC.MOVE_END ) )
),
(
ClientGUIShortcuts.Shortcut( ClientGUIShortcuts.SHORTCUT_TYPE_KEYBOARD_SPECIAL, ClientGUIShortcuts.SHORTCUT_KEY_SPECIAL_LEFT, ClientGUIShortcuts.SHORTCUT_PRESS_TYPE_PRESS, [ ClientGUIShortcuts.SHORTCUT_MODIFIER_ALT ] ),
CAC.ApplicationCommand.STATICCreateSimpleCommand( CAC.SIMPLE_REARRANGE_THUMBNAILS, ( CAC.REARRANGE_THUMBNAILS_TYPE_COMMAND, CAC.MOVE_LEFT ) )
),
(
ClientGUIShortcuts.Shortcut( ClientGUIShortcuts.SHORTCUT_TYPE_KEYBOARD_SPECIAL, ClientGUIShortcuts.SHORTCUT_KEY_SPECIAL_RIGHT, ClientGUIShortcuts.SHORTCUT_PRESS_TYPE_PRESS, [ ClientGUIShortcuts.SHORTCUT_MODIFIER_ALT ] ),
CAC.ApplicationCommand.STATICCreateSimpleCommand( CAC.SIMPLE_REARRANGE_THUMBNAILS, ( CAC.REARRANGE_THUMBNAILS_TYPE_COMMAND, CAC.MOVE_RIGHT ) )
)
]
for ( shortcut, command ) in jobs:
if not thumbnails_shortcuts_set.HasCommand( shortcut ):
thumbnails_shortcuts_set.SetCommand( shortcut, command )
self.modules_serialisable.SetJSONDump( thumbnails_shortcuts_set )
except Exception as e:
HydrusData.PrintException( e )
message = 'Trying to update some shortcuts failed! Please let hydrus dev know!'
self.pub_initial_message( message )
self._controller.frame_splash_status.SetTitleText( 'updated db to v{}'.format( HydrusData.ToHumanInt( version + 1 ) ) )
self._Execute( 'UPDATE version SET version = ?;', ( version + 1, ) )

View File

@ -492,6 +492,7 @@ class FrameGUI( CAC.ApplicationCommandProcessorMixin, ClientGUITopLevelWindows.M
self._canvas_frames = [] # Keep references to canvas frames so they won't get garbage collected (canvas frames don't have a parent)
self._persistent_mpv_widgets = []
self._isolated_mpv_widgets = []
self._have_shown_session_size_warning = False
@ -1393,6 +1394,15 @@ class FrameGUI( CAC.ApplicationCommandProcessorMixin, ClientGUITopLevelWindows.M
def _DebugIsolateMPVWindows( self ):
HydrusData.ShowText( f'Isolated {HydrusData.ToHumanInt( len( self._persistent_mpv_widgets ) )} MPV widgets.' )
self._isolated_mpv_widgets.extend( self._persistent_mpv_widgets )
self._persistent_mpv_widgets = []
def _DebugMakeDelayedModalPopup( self, cancellable ):
def do_it( controller, cancellable ):
@ -3339,18 +3349,18 @@ class FrameGUI( CAC.ApplicationCommandProcessorMixin, ClientGUITopLevelWindows.M
ClientGUIMenus.AppendSeparator( menu )
debug = ClientGUIMenus.GenerateMenu( menu )
debug_menu = ClientGUIMenus.GenerateMenu( menu )
debug_modes = ClientGUIMenus.GenerateMenu( debug )
debug_modes = ClientGUIMenus.GenerateMenu( debug_menu )
ClientGUIMenus.AppendMenuCheckItem( debug_modes, 'force idle mode', 'Make the client consider itself idle and fire all maintenance routines right now. This may hang the gui for a while.', HG.force_idle_mode, self._SwitchBoolean, 'force_idle_mode' )
ClientGUIMenus.AppendMenuCheckItem( debug_modes, 'no page limit mode', 'Let the user create as many pages as they want with no warnings or prohibitions.', HG.no_page_limit_mode, self._SwitchBoolean, 'no_page_limit_mode' )
ClientGUIMenus.AppendMenuCheckItem( debug_modes, 'thumbnail debug mode', 'Show some thumbnail debug info.', HG.thumbnail_debug_mode, self._SwitchBoolean, 'thumbnail_debug_mode' )
ClientGUIMenus.AppendMenuItem( debug_modes, 'simulate a wake from sleep', 'Tell the controller to pretend that it just woke up from sleep.', self._controller.SimulateWakeFromSleepEvent )
ClientGUIMenus.AppendMenu( debug, debug_modes, 'debug modes' )
ClientGUIMenus.AppendMenu( debug_menu, debug_modes, 'debug modes' )
profiling = ClientGUIMenus.GenerateMenu( debug )
profiling = ClientGUIMenus.GenerateMenu( debug_menu )
profile_mode_message = 'If something is running slow, you can turn on profile mode to have hydrus gather information on how long many jobs take to run.'
profile_mode_message += os.linesep * 2
@ -3364,9 +3374,9 @@ class FrameGUI( CAC.ApplicationCommandProcessorMixin, ClientGUITopLevelWindows.M
ClientGUIMenus.AppendMenuCheckItem( profiling, 'profile mode', 'Run detailed \'profiles\'.', HG.profile_mode, CG.client_controller.FlipProfileMode )
ClientGUIMenus.AppendMenuCheckItem( profiling, 'query planner mode', 'Run detailed \'query plans\'.', HG.query_planner_mode, CG.client_controller.FlipQueryPlannerMode )
ClientGUIMenus.AppendMenu( debug, profiling, 'profiling' )
ClientGUIMenus.AppendMenu( debug_menu, profiling, 'profiling' )
report_modes = ClientGUIMenus.GenerateMenu( debug )
report_modes = ClientGUIMenus.GenerateMenu( debug_menu )
ClientGUIMenus.AppendMenuCheckItem( report_modes, 'blurhash mode', 'Draw blurhashes instead of thumbnails.', HG.blurhash_mode, self._SwitchBoolean, 'blurhash_mode' )
ClientGUIMenus.AppendMenuCheckItem( report_modes, 'cache report mode', 'Have the image and thumb caches report their operation.', HG.cache_report_mode, self._SwitchBoolean, 'cache_report_mode' )
@ -3387,9 +3397,9 @@ class FrameGUI( CAC.ApplicationCommandProcessorMixin, ClientGUITopLevelWindows.M
ClientGUIMenus.AppendMenuCheckItem( report_modes, 'subprocess report mode', 'Report whenever an external process is called.', HG.subprocess_report_mode, self._SwitchBoolean, 'subprocess_report_mode' )
ClientGUIMenus.AppendMenuCheckItem( report_modes, 'subscription report mode', 'Have the subscription system report what it is doing.', HG.subscription_report_mode, self._SwitchBoolean, 'subscription_report_mode' )
ClientGUIMenus.AppendMenu( debug, report_modes, 'report modes' )
ClientGUIMenus.AppendMenu( debug_menu, report_modes, 'report modes' )
gui_actions = ClientGUIMenus.GenerateMenu( debug )
gui_actions = ClientGUIMenus.GenerateMenu( debug_menu )
default_location_context = CG.client_controller.new_options.GetDefaultLocalLocationContext()
@ -3409,23 +3419,24 @@ class FrameGUI( CAC.ApplicationCommandProcessorMixin, ClientGUITopLevelWindows.M
ClientGUIMenus.AppendMenuCheckItem( gui_actions, 'autocomplete delay mode', 'Delay all autocomplete requests at the database level by three seconds.', HG.autocomplete_delay_mode, self._SwitchBoolean, 'autocomplete_delay_mode' )
ClientGUIMenus.AppendMenuItem( gui_actions, 'make some popups', 'Throw some varied popups at the message manager, just to check it is working.', self._DebugMakeSomePopups )
ClientGUIMenus.AppendMenuItem( gui_actions, 'make a long text popup', 'Make a popup with text that will grow in size.', self._DebugLongTextPopup )
ClientGUIMenus.AppendMenuItem( gui_actions, 'make a popup in five seconds', 'Throw a delayed popup at the message manager, giving you time to minimise or otherwise alter the client before it arrives.', self._controller.CallLater, 5, HydrusData.ShowText, 'This is a delayed popup message.' )
ClientGUIMenus.AppendMenuItem( gui_actions, 'make a modal popup in five seconds', 'Throw up a delayed modal popup to test with. It will stay alive for five seconds.', self._DebugMakeDelayedModalPopup, True )
ClientGUIMenus.AppendMenuItem( gui_actions, 'make a non-cancellable modal popup in five seconds', 'Throw up a delayed modal popup to test with. It will stay alive for five seconds.', self._DebugMakeDelayedModalPopup, False )
ClientGUIMenus.AppendMenuItem( gui_actions, 'make a QMessageBox', 'Open a modal message dialog.', self._DebugMakeQMessageBox )
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', default_location_context )
ClientGUIMenus.AppendMenuItem( gui_actions, 'refresh pages menu in five seconds', 'Delayed refresh the pages menu, giving you time to minimise or otherwise alter the client before it arrives.', self._controller.CallLater, 5, self._menu_updater_pages.update )
ClientGUIMenus.AppendMenuItem( gui_actions, 'publish some sub files in five seconds', 'Publish some files like a subscription would.', self._controller.CallLater, 5, lambda: CG.client_controller.pub( 'imported_files_to_page', [ HydrusData.GenerateKey() for i in range( 5 ) ], 'example sub files' ) )
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, 'reset multi-column list settings to default', 'Reset all multi-column list widths and other display settings to default.', self._DebugResetColumnListManager )
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, 'isolate existing mpv widgets', 'Tell the client to hide and do not re-use all existing mpv widgets, forcing new ones to be created on next request. This helps test out busted mpv windows that lose audio etc..', self._DebugIsolateMPVWindows )
ClientGUIMenus.AppendMenuItem( gui_actions, 'make a long text popup', 'Make a popup with text that will grow in size.', self._DebugLongTextPopup )
ClientGUIMenus.AppendMenuItem( gui_actions, 'make a modal popup in five seconds', 'Throw up a delayed modal popup to test with. It will stay alive for five seconds.', self._DebugMakeDelayedModalPopup, True )
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', default_location_context )
ClientGUIMenus.AppendMenuItem( gui_actions, 'make a non-cancellable modal popup in five seconds', 'Throw up a delayed modal popup to test with. It will stay alive for five seconds.', self._DebugMakeDelayedModalPopup, False )
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, 'make a popup in five seconds', 'Throw a delayed popup at the message manager, giving you time to minimise or otherwise alter the client before it arrives.', self._controller.CallLater, 5, HydrusData.ShowText, 'This is a delayed popup message.' )
ClientGUIMenus.AppendMenuItem( gui_actions, 'make a QMessageBox', 'Open a modal message dialog.', self._DebugMakeQMessageBox )
ClientGUIMenus.AppendMenuItem( gui_actions, 'make some popups', 'Throw some varied popups at the message manager, just to check it is working.', self._DebugMakeSomePopups )
ClientGUIMenus.AppendMenuItem( gui_actions, 'publish some sub files in five seconds', 'Publish some files like a subscription would.', self._controller.CallLater, 5, lambda: CG.client_controller.pub( 'imported_files_to_page', [ HydrusData.GenerateKey() for i in range( 5 ) ], 'example sub files' ) )
ClientGUIMenus.AppendMenuItem( gui_actions, 'refresh pages menu in five seconds', 'Delayed refresh the pages menu, giving you time to minimise or otherwise alter the client before it arrives.', self._controller.CallLater, 5, self._menu_updater_pages.update )
ClientGUIMenus.AppendMenuItem( gui_actions, 'reset multi-column list settings to default', 'Reset all multi-column list widths and other display settings to default.', self._DebugResetColumnListManager )
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, CC.LAST_SESSION_SESSION_NAME )
ClientGUIMenus.AppendMenu( debug, gui_actions, 'gui actions' )
ClientGUIMenus.AppendMenu( debug_menu, gui_actions, 'gui actions' )
data_actions = ClientGUIMenus.GenerateMenu( debug )
data_actions = ClientGUIMenus.GenerateMenu( debug_menu )
ClientGUIMenus.AppendMenuCheckItem( data_actions, 'db ui-hang relief mode', 'Have UI-synchronised database jobs process pending Qt events while they wait.', HG.db_ui_hang_relief_mode, self._SwitchBoolean, 'db_ui_hang_relief_mode' )
ClientGUIMenus.AppendMenuItem( data_actions, 'review threads', 'Show current threads and what they are doing.', self._ReviewThreads )
@ -3436,9 +3447,9 @@ class FrameGUI( CAC.ApplicationCommandProcessorMixin, ClientGUITopLevelWindows.M
ClientGUIMenus.AppendSeparator( data_actions )
ClientGUIMenus.AppendMenuItem( data_actions, 'simulate program exit signal', 'Kill the program via a QApplication exit.', QW.QApplication.instance().exit )
ClientGUIMenus.AppendMenu( debug, data_actions, 'data actions' )
ClientGUIMenus.AppendMenu( debug_menu, data_actions, 'data actions' )
memory_actions = ClientGUIMenus.GenerateMenu( debug )
memory_actions = ClientGUIMenus.GenerateMenu( debug_menu )
ClientGUIMenus.AppendMenuItem( memory_actions, 'run fast memory maintenance', 'Tell all the fast caches to maintain themselves.', self._controller.MaintainMemoryFast )
ClientGUIMenus.AppendMenuItem( memory_actions, 'run slow memory maintenance', 'Tell all the slow caches to maintain themselves.', self._controller.MaintainMemorySlow )
@ -3453,23 +3464,23 @@ class FrameGUI( CAC.ApplicationCommandProcessorMixin, ClientGUITopLevelWindows.M
ClientGUIMenus.AppendMenuItem( memory_actions, 'WARNING, MEGA-LAGGY: print memory-use snapshot diff', 'Show memory use differences since the last snapshot.', self._DebugShowMemoryUseDifferences )
ClientGUIMenus.AppendMenu( debug, memory_actions, 'memory actions' )
ClientGUIMenus.AppendMenu( debug_menu, memory_actions, 'memory actions' )
network_actions = ClientGUIMenus.GenerateMenu( debug )
network_actions = ClientGUIMenus.GenerateMenu( debug_menu )
ClientGUIMenus.AppendMenuItem( network_actions, 'fetch a url', 'Fetch a URL using the network engine as per normal.', self._DebugFetchAURL )
ClientGUIMenus.AppendMenu( debug, network_actions, 'network actions' )
ClientGUIMenus.AppendMenu( debug_menu, network_actions, 'network actions' )
tests = ClientGUIMenus.GenerateMenu( debug )
tests = ClientGUIMenus.GenerateMenu( debug_menu )
ClientGUIMenus.AppendMenuItem( tests, '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.AppendMenuItem( tests, 'run the client api test', 'Run hydrus_dev\'s weekly Client API Test. Guaranteed to work and not mess up your session, ha ha.', self._RunClientAPITest )
ClientGUIMenus.AppendMenuItem( tests, 'run the server test on fresh server', 'This will try to initialise an already running server.', self._RunServerTest )
ClientGUIMenus.AppendMenu( debug, tests, 'tests, do not touch' )
ClientGUIMenus.AppendMenu( debug_menu, tests, 'tests, do not touch' )
ClientGUIMenus.AppendMenu( menu, debug, 'debug' )
ClientGUIMenus.AppendMenu( menu, debug_menu, 'debug' )
ClientGUIMenus.AppendSeparator( menu )

View File

@ -51,19 +51,17 @@ class GUICore( QC.QObject ):
return self._menu_open
def PopupMenu( self, window: QW.QWidget, menu: QW.QMenu ):
def PopupMenu( self, widget: QW.QWidget, menu: QW.QMenu ):
if HC.PLATFORM_MACOS and window.window().isModal():
if HC.PLATFORM_MACOS and widget.window().isModal():
# Ok, seems like Big Sur can't do menus at the moment lmao. it shows the menu but the mouse can't interact with it
from hydrus.core import HydrusGlobals as HG
if CG.client_controller.new_options.GetBoolean( 'do_macos_debug_dialog_menus' ):
from hydrus.client.gui import ClientGUICoreMenuDebug
ClientGUICoreMenuDebug.ShowMenuDialog( window, menu )
ClientGUICoreMenuDebug.ShowMenuDialog( widget, menu )
ClientGUIMenus.DestroyMenu( menu )

View File

@ -58,9 +58,12 @@ class Dialog( QP.Dialog ):
def keyPressEvent( self, event ):
( modifier, key ) = ClientGUIShortcuts.ConvertKeyEventToSimpleTuple( event )
shortcut = ClientGUIShortcuts.ConvertKeyEventToShortcut( event )
if key == QC.Qt.Key_Escape:
escape_shortcut = ClientGUIShortcuts.Shortcut( ClientGUIShortcuts.SHORTCUT_TYPE_KEYBOARD_SPECIAL, ClientGUIShortcuts.SHORTCUT_KEY_SPECIAL_ESCAPE, ClientGUIShortcuts.SHORTCUT_PRESS_TYPE_PRESS, [] )
command_w_shortcut = ClientGUIShortcuts.Shortcut( ClientGUIShortcuts.SHORTCUT_TYPE_KEYBOARD_CHARACTER, ord( 'W' ), ClientGUIShortcuts.SHORTCUT_PRESS_TYPE_PRESS, [ ClientGUIShortcuts.SHORTCUT_MODIFIER_CTRL ] )
if shortcut == escape_shortcut or ( HC.PLATFORM_MACOS and shortcut == command_w_shortcut ):
self.done( QW.QDialog.Rejected )

View File

@ -226,6 +226,7 @@ def PopulateFileSeedCacheMenu( win: QW.QWidget, menu: QW.QMenu, file_seed_cache:
num_deleted = file_seed_cache.GetFileSeedCount( CC.STATUS_DELETED )
num_errors = file_seed_cache.GetFileSeedCount( CC.STATUS_ERROR )
num_skipped = file_seed_cache.GetFileSeedCount( CC.STATUS_SKIPPED )
num_unknown = file_seed_cache.GetFileSeedCount( CC.STATUS_UNKNOWN )
if num_errors > 0:
@ -269,11 +270,23 @@ def PopulateFileSeedCacheMenu( win: QW.QWidget, menu: QW.QMenu, file_seed_cache:
ClientGUIMenus.AppendMenuItem( menu, 'delete {} \'skipped\' file import items from the queue'.format( HydrusData.ToHumanInt( num_skipped ) ), 'Tell this log to clear out skipped files, reducing the size of the queue.', ClearFileSeeds, win, file_seed_cache, ( CC.STATUS_SKIPPED, ) )
if num_unknown > 0:
ClientGUIMenus.AppendMenuItem( menu, 'delete {} \'unknown\' (i.e. unstarted) file import items from the queue'.format( HydrusData.ToHumanInt( num_unknown ) ), 'Tell this log to clear out any items that have not yet been started (or have been restarted and not yet worked on), reducing the size of the queue.', ClearFileSeeds, win, file_seed_cache, ( CC.STATUS_UNKNOWN, ) )
if len( file_seed_cache ) > 0:
ClientGUIMenus.AppendSeparator( menu )
ClientGUIMenus.AppendMenuItem( menu, 'delete everything from the queue', 'Tell this log to clear out everything, resetting the queue to empty.', ClearFileSeeds, win, file_seed_cache, ( CC.STATUS_UNKNOWN, CC.STATUS_SUCCESSFUL_AND_NEW, CC.STATUS_SUCCESSFUL_BUT_REDUNDANT, CC.STATUS_DELETED, CC.STATUS_ERROR, CC.STATUS_VETOED, CC.STATUS_SKIPPED, CC.STATUS_SUCCESSFUL_AND_CHILD_FILES ) )
ClientGUIMenus.AppendMenuItem( menu, f'delete everything ({HydrusData.ToHumanInt( len( file_seed_cache ) )} items) from the queue', 'Tell this log to clear out everything, resetting the queue to empty.', ClearFileSeeds, win, file_seed_cache, ( CC.STATUS_UNKNOWN, CC.STATUS_SUCCESSFUL_AND_NEW, CC.STATUS_SUCCESSFUL_BUT_REDUNDANT, CC.STATUS_DELETED, CC.STATUS_ERROR, CC.STATUS_VETOED, CC.STATUS_SKIPPED, CC.STATUS_SUCCESSFUL_AND_CHILD_FILES ) )
if num_unknown > 0:
ClientGUIMenus.AppendSeparator( menu )
ClientGUIMenus.AppendMenuItem( menu, 'set {} \'unknown\' (i.e. unstarted) file import items to \'skipped\''.format( HydrusData.ToHumanInt( num_unknown ) ), 'Tell this log to skip any outstanding items in the queue.', file_seed_cache.SetStatusToStatus, CC.STATUS_UNKNOWN, CC.STATUS_SKIPPED )
ClientGUIMenus.AppendSeparator( menu )

View File

@ -260,7 +260,7 @@ def GetLighterDarkerColour( colour, intensity = 3 ):
return colour.lighter( qt_intensity )
def GetMouseScreen():
def GetMouseScreen() -> typing.Optional[ QG.QScreen ]:
return QW.QApplication.screenAt( QG.QCursor.pos() )
@ -361,6 +361,12 @@ def MouseIsOnMyDisplay( window ):
mouse_screen = GetMouseScreen()
# something's busted!
if mouse_screen is None:
return True
return mouse_screen is window_screen
def MouseIsOverWidget( win: QW.QWidget ):

View File

@ -1353,6 +1353,9 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
frame_locations_panel = ClientGUICommon.StaticBox( self, 'frame locations' )
self._disable_get_safe_position_test = QW.QCheckBox( self._misc_panel )
self._disable_get_safe_position_test.setToolTip( 'If your windows keep getting \'rescued\' despite being in a good location, try this.' )
self._frame_locations = ClientGUIListCtrl.BetterListCtrl( frame_locations_panel, CGLC.COLUMN_LIST_FRAME_LOCATIONS.ID, 15, data_to_tuples_func = lambda x: (self._GetPrettyFrameLocationInfo( x ), self._GetPrettyFrameLocationInfo( x )), activation_callback = self.EditFrameLocations )
self._frame_locations_edit_button = QW.QPushButton( 'edit', frame_locations_panel )
@ -1386,6 +1389,8 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
self._use_qt_file_dialogs.setChecked( self._new_options.GetBoolean( 'use_qt_file_dialogs' ) )
self._disable_get_safe_position_test.setChecked( self._new_options.GetBoolean( 'disable_get_safe_position_test' ) )
for ( name, info ) in self._new_options.GetFrameLocations():
listctrl_list = QP.ListsToTuples( [ name ] + list( info ) )
@ -1427,7 +1432,18 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
text += os.linesep
text += 'This is an advanced control. If you aren\'t confident of what you are doing here, come back later!'
frame_locations_panel.Add( QW.QLabel( text, frame_locations_panel ), CC.FLAGS_EXPAND_PERPENDICULAR )
st = ClientGUICommon.BetterStaticText( frame_locations_panel, label = text )
st.setWordWrap( True )
frame_locations_panel.Add( st, CC.FLAGS_EXPAND_PERPENDICULAR )
rows = []
rows.append( ( 'BUGFIX: Disable off-screen window rescue: ', self._disable_get_safe_position_test ) )
gridbox = ClientGUICommon.WrapInGrid( self, rows )
frame_locations_panel.Add( gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
frame_locations_panel.Add( self._frame_locations, CC.FLAGS_EXPAND_BOTH_WAYS )
frame_locations_panel.Add( self._frame_locations_edit_button, CC.FLAGS_ON_RIGHT )
@ -1512,6 +1528,8 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
self._new_options.SetBoolean( 'do_macos_debug_dialog_menus', self._do_macos_debug_dialog_menus.isChecked() )
self._new_options.SetBoolean( 'use_qt_file_dialogs', self._use_qt_file_dialogs.isChecked() )
self._new_options.SetBoolean( 'disable_get_safe_position_test', self._disable_get_safe_position_test.isChecked() )
for listctrl_list in self._frame_locations.GetData():
( name, remember_size, remember_position, last_size, last_position, default_gravity, default_position, maximised, fullscreen ) = listctrl_list

View File

@ -28,23 +28,26 @@ def ManageShortcuts( win: QW.QWidget ):
shortcuts_manager = ClientGUIShortcuts.shortcuts_manager()
call_mouse_buttons_primary_secondary = CG.client_controller.new_options.GetBoolean( 'call_mouse_buttons_primary_secondary' )
shortcuts_merge_non_number_numpad = CG.client_controller.new_options.GetBoolean( 'shortcuts_merge_non_number_numpad' )
all_shortcuts = shortcuts_manager.GetShortcutSets()
with ClientGUITopLevelWindowsPanels.DialogEdit( win, 'manage shortcuts' ) as dlg:
panel = EditShortcutsPanel( dlg, call_mouse_buttons_primary_secondary, all_shortcuts )
panel = EditShortcutsPanel( dlg, call_mouse_buttons_primary_secondary, shortcuts_merge_non_number_numpad, all_shortcuts )
dlg.SetPanel( panel )
if dlg.exec() == QW.QDialog.Accepted:
( call_mouse_buttons_primary_secondary, shortcut_sets ) = panel.GetValue()
( call_mouse_buttons_primary_secondary, shortcuts_merge_non_number_numpad, shortcut_sets ) = panel.GetValue()
CG.client_controller.new_options.SetBoolean( 'call_mouse_buttons_primary_secondary', call_mouse_buttons_primary_secondary )
ClientGUIShortcuts.SetMouseLabels( call_mouse_buttons_primary_secondary )
CG.client_controller.new_options.SetBoolean( 'shortcuts_merge_non_number_numpad', shortcuts_merge_non_number_numpad )
dupe_shortcut_sets = [ shortcut_set.Duplicate() for shortcut_set in shortcut_sets ]
CG.client_controller.Write( 'serialisables_overwrite', [ HydrusSerialisable.SERIALISABLE_TYPE_SHORTCUT_SET ], dupe_shortcut_sets )
@ -360,7 +363,7 @@ class EditShortcutSetPanel( ClientGUIScrolledPanels.EditPanel ):
class EditShortcutsPanel( ClientGUIScrolledPanels.EditPanel ):
def __init__( self, parent, call_mouse_buttons_primary_secondary, all_shortcuts ):
def __init__( self, parent, call_mouse_buttons_primary_secondary, shortcuts_merge_non_number_numpad, all_shortcuts ):
ClientGUIScrolledPanels.EditPanel.__init__( self, parent )
@ -370,6 +373,9 @@ class EditShortcutsPanel( ClientGUIScrolledPanels.EditPanel ):
self._call_mouse_buttons_primary_secondary = QW.QCheckBox( self )
self._call_mouse_buttons_primary_secondary.setToolTip( 'Useful if you swap your buttons around.' )
self._shortcuts_merge_non_number_numpad = QW.QCheckBox( self )
self._shortcuts_merge_non_number_numpad.setToolTip( 'This means a "numpad" variant of Return/Home/Arrow etc.. is just counted as a normal one. Helps clear up a bunch of annoying keyboard mappings.' )
reserved_panel = ClientGUICommon.StaticBox( self, 'built-in hydrus shortcut sets' )
self._reserved_shortcuts = ClientGUIListCtrl.BetterListCtrl( reserved_panel, CGLC.COLUMN_LIST_SHORTCUT_SETS.ID, 6, data_to_tuples_func = self._GetTuples, activation_callback = self._EditReserved )
@ -397,6 +403,7 @@ class EditShortcutsPanel( ClientGUIScrolledPanels.EditPanel ):
#
self._call_mouse_buttons_primary_secondary.setChecked( call_mouse_buttons_primary_secondary )
self._shortcuts_merge_non_number_numpad.setChecked( shortcuts_merge_non_number_numpad )
reserved_shortcuts = [ shortcuts for shortcuts in all_shortcuts if shortcuts.GetName() in ClientGUIShortcuts.SHORTCUTS_RESERVED_NAMES ]
custom_shortcuts = [ shortcuts for shortcuts in all_shortcuts if shortcuts.GetName() not in ClientGUIShortcuts.SHORTCUTS_RESERVED_NAMES ]
@ -420,6 +427,7 @@ class EditShortcutsPanel( ClientGUIScrolledPanels.EditPanel ):
rows = []
rows.append( ( 'Treat all non-number numpad inputs as "normal": ', self._shortcuts_merge_non_number_numpad ) )
rows.append( ( 'Replace "left/right"-click with "primary/secondary": ', self._call_mouse_buttons_primary_secondary ) )
mouse_gridbox = ClientGUICommon.WrapInGrid( self, rows )
@ -463,6 +471,7 @@ class EditShortcutsPanel( ClientGUIScrolledPanels.EditPanel ):
self.widget().setLayout( vbox )
self._call_mouse_buttons_primary_secondary.clicked.connect( self._UpdateMouseLabels )
self._shortcuts_merge_non_number_numpad.clicked.connect( self._TempSaveNumpadMerge )
def _Add( self ):
@ -640,6 +649,15 @@ class EditShortcutsPanel( ClientGUIScrolledPanels.EditPanel ):
ClientGUIDialogsMessage.ShowInformation( self, message )
def _TempSaveNumpadMerge( self ):
# this is dumb, but we do want the behaviour to change instantly so this dialog reflects it, so there we go
shortcuts_merge_non_number_numpad = self._shortcuts_merge_non_number_numpad.isChecked()
CG.client_controller.new_options.SetBoolean( 'shortcuts_merge_non_number_numpad', shortcuts_merge_non_number_numpad )
def _UpdateMouseLabels( self ):
swap_labels = self._call_mouse_buttons_primary_secondary.isChecked()
@ -647,16 +665,17 @@ class EditShortcutsPanel( ClientGUIScrolledPanels.EditPanel ):
ClientGUIShortcuts.SetMouseLabels( swap_labels )
def GetValue( self ) -> typing.List[ ClientGUIShortcuts.ShortcutSet ]:
def GetValue( self ) -> typing.Tuple[ bool, bool, typing.List[ ClientGUIShortcuts.ShortcutSet ] ]:
call_mouse_buttons_primary_secondary = self._call_mouse_buttons_primary_secondary.isChecked()
shortcuts_merge_non_number_numpad = self._shortcuts_merge_non_number_numpad.isChecked()
shortcut_sets = []
shortcut_sets.extend( self._reserved_shortcuts.GetData() )
shortcut_sets.extend( self._custom_shortcuts.GetData() )
return ( call_mouse_buttons_primary_secondary, shortcut_sets )
return ( call_mouse_buttons_primary_secondary, shortcuts_merge_non_number_numpad, shortcut_sets )
class ShortcutWidget( QW.QWidget ):

View File

@ -38,7 +38,7 @@ SHORTCUT_MODIFIER_ALT = 1
SHORTCUT_MODIFIER_SHIFT = 2
SHORTCUT_MODIFIER_KEYPAD = 3
SHORTCUT_MODIFIER_GROUP_SWITCH = 4
SHORTCUT_MODIFIER_META = 5 # This is 'Control' in macOS, which is for system level stuff. They use 'Command' for Control stuff, which is helpfully mapped to Control in Qt. Just name nonsense
SHORTCUT_MODIFIER_META = 5 # This is 'Control' in macOS, which is for system level stuff. They use 'Command/Cmd' for Ctrl stuff, which is helpfully mapped to Ctrl in Qt, so you usually don't have to change anything. Just name nonsense
SHORTCUT_KEY_SPECIAL_SPACE = 0
SHORTCUT_KEY_SPECIAL_BACKSPACE = 1
@ -499,6 +499,7 @@ def ConvertQtKeyToShortcutKey( key_qt ):
def ConvertKeyEventToShortcut( event ):
key_qt = event.key()
@ -536,7 +537,22 @@ def ConvertKeyEventToShortcut( event ):
if event.modifiers() & QC.Qt.KeypadModifier:
modifiers.append( SHORTCUT_MODIFIER_KEYPAD )
do_it = True
if CG.client_controller.new_options.GetBoolean( 'shortcuts_merge_non_number_numpad' ):
it_is_a_number = ord( '0' ) <= key_ord <= ord( '9' )
if not it_is_a_number:
do_it = False
if do_it:
modifiers.append( SHORTCUT_MODIFIER_KEYPAD )
shortcut_press_type = SHORTCUT_PRESS_TYPE_PRESS
@ -701,11 +717,6 @@ def ConvertMouseEventToShortcut( event: QG.QMouseEvent ):
modifiers.append( SHORTCUT_MODIFIER_GROUP_SWITCH )
if event.modifiers() & QC.Qt.KeypadModifier:
modifiers.append( SHORTCUT_MODIFIER_KEYPAD )
shortcut = Shortcut( SHORTCUT_TYPE_MOUSE, key, shortcut_press_type, modifiers )
if HG.gui_report_mode:
@ -1163,6 +1174,7 @@ class Shortcut( HydrusSerialisable.SerialisableBase ):
raise HydrusExceptions.VetoException( 'Sorry, cannot increment that shortcut!' )
HydrusSerialisable.SERIALISABLE_TYPES_TO_OBJECT_TYPES[ HydrusSerialisable.SERIALISABLE_TYPE_SHORTCUT ] = Shortcut
class ShortcutSet( HydrusSerialisable.SerialisableBaseNamed ):
@ -1273,7 +1285,7 @@ class ShortcutSet( HydrusSerialisable.SerialisableBaseNamed ):
def DeleteShortcut( self, shortcut ):
def DeleteShortcut( self, shortcut: Shortcut ):
if shortcut in self._shortcuts_to_commands:
@ -1281,7 +1293,7 @@ class ShortcutSet( HydrusSerialisable.SerialisableBaseNamed ):
def GetCommand( self, shortcut ):
def GetCommand( self, shortcut: Shortcut ):
if shortcut in self._shortcuts_to_commands:
@ -1313,7 +1325,12 @@ class ShortcutSet( HydrusSerialisable.SerialisableBaseNamed ):
return list( self )
def SetCommand( self, shortcut, command ):
def HasCommand( self, shortcut: Shortcut ):
return self.GetCommand( shortcut ) is not None
def SetCommand( self, shortcut: Shortcut, command: CAC.ApplicationCommand ):
self._shortcuts_to_commands[ shortcut ] = command

View File

@ -586,7 +586,7 @@ class EditSubscriptionPanel( ClientGUIScrolledPanels.EditPanel ):
except HydrusExceptions.DataMissing:
ClientGUIDialogsMessage.ShowCritical( self, 'Important Error!', 'Some data for this query, "{}" was missing! This should have been dealt with when the dialog launched, so something is very wrong! Please exit the manage subscriptions dialog immediately, pause your subs, and contact hydrus dev!' )
ClientGUIDialogsMessage.ShowCritical( self, 'Important Error!', f'Some data for this query, "{old_query_header.GetQueryText()}" was missing! This should have been dealt with when the dialog launched, so something is very wrong! Please exit the manage subscriptions dialog immediately, pause your subs, and contact hydrus dev!' )
return

View File

@ -682,6 +682,9 @@ class DateTimesCtrl( QW.QWidget ):
datetime_value_range.AddValueQtDateTime( QC.QDateTime.currentDateTime() )
self._original_datetime_value_range = datetime_value_range
self._current_datetime_value_range = self._original_datetime_value_range
self._we_have_initialised_with_setvalue = False
self._none_time = QW.QCheckBox( self )
@ -858,7 +861,7 @@ class DateTimesCtrl( QW.QWidget ):
qt_datetime = QC.QDateTime.fromMSecsSinceEpoch( timestamp_ms, QC.QTimeZone.systemTimeZone() )
datetime_value_range = self._original_datetime_value_range.DuplicateWithNewQtDateTime( qt_datetime )
datetime_value_range = self._current_datetime_value_range.DuplicateWithNewQtDateTime( qt_datetime )
except Exception as e:
@ -891,7 +894,7 @@ class DateTimesCtrl( QW.QWidget ):
if self._none_time.isChecked():
return self._original_datetime_value_range.DuplicateWithNewQtDateTime( None )
return self._current_datetime_value_range.DuplicateWithNewQtDateTime( None )
qt_date = self._date.selectedDate()
@ -907,7 +910,7 @@ class DateTimesCtrl( QW.QWidget ):
qt_datetime = now
datetime_value_range = self._original_datetime_value_range.DuplicateWithNewQtDateTime( qt_datetime )
datetime_value_range = self._current_datetime_value_range.DuplicateWithNewQtDateTime( qt_datetime )
datetime_value_range.SetStepMS( int( self._step.GetValue() * 1000 ) )
@ -926,7 +929,14 @@ class DateTimesCtrl( QW.QWidget ):
return
self._original_datetime_value_range = datetime_value_range
if not self._we_have_initialised_with_setvalue:
self._original_datetime_value_range = datetime_value_range
self._we_have_initialised_with_setvalue = True
self._current_datetime_value_range = datetime_value_range
value_to_set_visually = datetime_value_range.GetBestVisualValue()

View File

@ -15,6 +15,11 @@ FUZZY_PADDING = 10
def GetSafePosition( position: QC.QPoint, frame_key ):
if CG.client_controller.new_options.GetBoolean( 'disable_get_safe_position_test' ):
return ( position, None )
# some window managers size the windows just off screen to cut off borders
# so choose a test position that's a little more lenient
@ -22,6 +27,9 @@ def GetSafePosition( position: QC.QPoint, frame_key ):
test_position = position + fuzzy_point
# note that some version of Qt cannot figure this out, they just deliver None for a particular monitor!
# https://github.com/hydrusnetwork/hydrus/issues/1511
screen = QW.QApplication.screenAt( test_position )
if screen is None:
@ -550,13 +558,16 @@ class NewDialog( QP.Dialog ):
def keyPressEvent( self, event ):
( modifier, key ) = ClientGUIShortcuts.ConvertKeyEventToSimpleTuple( event )
current_focus = QW.QApplication.focusWidget()
event_from_us = current_focus is not None and ClientGUIFunctions.IsQtAncestor( current_focus, self )
if event_from_us and key == QC.Qt.Key_Escape:
shortcut = ClientGUIShortcuts.ConvertKeyEventToShortcut( event )
escape_shortcut = ClientGUIShortcuts.Shortcut( ClientGUIShortcuts.SHORTCUT_TYPE_KEYBOARD_SPECIAL, ClientGUIShortcuts.SHORTCUT_KEY_SPECIAL_ESCAPE, ClientGUIShortcuts.SHORTCUT_PRESS_TYPE_PRESS, [] )
command_w_shortcut = ClientGUIShortcuts.Shortcut( ClientGUIShortcuts.SHORTCUT_TYPE_KEYBOARD_CHARACTER, ord( 'W' ), ClientGUIShortcuts.SHORTCUT_PRESS_TYPE_PRESS, [ ClientGUIShortcuts.SHORTCUT_MODIFIER_CTRL ] )
if event_from_us and ( shortcut == escape_shortcut or ( HC.PLATFORM_MACOS and shortcut == command_w_shortcut ) ):
self._TryEndModal( QW.QDialog.Rejected )

View File

@ -1,6 +1,6 @@
from qtpy import QtCore as QC
from qtpy import QtWidgets as QW
from hydrus.core import HydrusConstants as HC
from hydrus.core import HydrusExceptions
from hydrus.client import ClientConstants as CC
@ -228,9 +228,12 @@ class FrameThatTakesScrollablePanel( ClientGUITopLevelWindows.FrameThatResizes )
def keyPressEvent( self, event ):
( modifier, key ) = ClientGUIShortcuts.ConvertKeyEventToSimpleTuple( event )
shortcut = ClientGUIShortcuts.ConvertKeyEventToShortcut( event )
if key == QC.Qt.Key_Escape:
escape_shortcut = ClientGUIShortcuts.Shortcut( ClientGUIShortcuts.SHORTCUT_TYPE_KEYBOARD_SPECIAL, ClientGUIShortcuts.SHORTCUT_KEY_SPECIAL_ESCAPE, ClientGUIShortcuts.SHORTCUT_PRESS_TYPE_PRESS, [] )
command_w_shortcut = ClientGUIShortcuts.Shortcut( ClientGUIShortcuts.SHORTCUT_TYPE_KEYBOARD_CHARACTER, ord( 'W' ), ClientGUIShortcuts.SHORTCUT_PRESS_TYPE_PRESS, [ ClientGUIShortcuts.SHORTCUT_MODIFIER_CTRL ] )
if shortcut == escape_shortcut or ( HC.PLATFORM_MACOS and shortcut == command_w_shortcut ):
self.close()

View File

@ -3372,45 +3372,48 @@ class ListBoxTags( ListBox ):
to_add = set( selected_actual_tags ).difference( favourite_tags )
to_remove = set( selected_actual_tags ).intersection( favourite_tags )
favourites_menu = ClientGUIMenus.GenerateMenu( menu )
if len( to_add ) > 0:
if len( to_add ) + len( to_remove ) > 0:
if len( to_add ) == 1:
favourites_menu = ClientGUIMenus.GenerateMenu( menu )
if len( to_add ) > 0:
tag = list( to_add )[0]
if len( to_add ) == 1:
tag = list( to_add )[0]
label = f'Add "{tag}" to favourites'
else:
label = f'Add {HydrusData.ToHumanInt( len( to_add ) )} selected tags to favourites'
label = f'Add "{tag}" to favourites'
description = 'Add these tags to the favourites list.'
else:
label = f'Add {HydrusData.ToHumanInt( len( to_add ) )} selected tags to favourites'
ClientGUIMenus.AppendMenuItem( favourites_menu, label, description, add_favourite_tags, to_add )
description = 'Add these tags to the favourites list.'
ClientGUIMenus.AppendMenuItem( favourites_menu, label, description, add_favourite_tags, to_add )
if len( to_remove ) > 0:
if len( to_remove ) == 1:
if len( to_remove ) > 0:
tag = list( to_remove )[0]
if len( to_remove ) == 1:
tag = list( to_remove )[0]
label = f'Remove "{tag}" from favourites'
else:
label = f'Remove {HydrusData.ToHumanInt( len( to_remove ) )} selected tags from favourites'
label = f'Remove "{tag}" from favourites'
description = 'Add these tags to the favourites list.'
else:
label = f'Remove {HydrusData.ToHumanInt( len( to_remove ) )} selected tags from favourites'
ClientGUIMenus.AppendMenuItem( favourites_menu, label, description, remove_favourite_tags, to_remove )
description = 'Add these tags to the favourites list.'
ClientGUIMenus.AppendMenu( menu, favourites_menu, 'favourites' )
ClientGUIMenus.AppendMenuItem( favourites_menu, label, description, remove_favourite_tags, to_remove )
ClientGUIMenus.AppendMenu( menu, favourites_menu, 'favourites' )
#

View File

@ -1177,9 +1177,6 @@ class ManagementPanelImporterHDD( ManagementPanelImporter ):
self._pause_button = ClientGUICommon.BetterBitmapButton( self._import_queue_panel, CC.global_pixmaps().file_pause, self.Pause )
self._pause_button.setToolTip( 'pause/play imports' )
self._abort_button = ClientGUICommon.BetterBitmapButton( self._import_queue_panel, CC.global_pixmaps().stop, self.Abort )
self._abort_button.setToolTip( 'abort imports' )
self._hdd_import: ClientImportLocal.HDDImport = self._management_controller.GetVariable( 'hdd_import' )
file_import_options = self._hdd_import.GetFileImportOptions()
@ -1202,7 +1199,6 @@ class ManagementPanelImporterHDD( ManagementPanelImporter ):
QP.AddToLayout( hbox, self._current_action, CC.FLAGS_CENTER_PERPENDICULAR_EXPAND_DEPTH )
QP.AddToLayout( hbox, self._pause_button, CC.FLAGS_CENTER_PERPENDICULAR )
QP.AddToLayout( hbox, self._abort_button, CC.FLAGS_CENTER_PERPENDICULAR )
self._import_queue_panel.Add( hbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
self._import_queue_panel.Add( self._file_seed_cache_control, CC.FLAGS_EXPAND_PERPENDICULAR )
@ -1240,8 +1236,6 @@ class ManagementPanelImporterHDD( ManagementPanelImporter ):
self._current_action.setText( current_action )
self._abort_button.setEnabled( self._hdd_import.GetFileSeedCache().WorkToDo() )
def CheckAbleToClose( self ):
@ -1249,34 +1243,6 @@ class ManagementPanelImporterHDD( ManagementPanelImporter ):
raise HydrusExceptions.VetoException( 'This page is still importing.' )
def Abort( self ):
if not self._hdd_import.GetFileSeedCache().WorkToDo():
return
text = 'Stop the import here? You can either set the remainder of the queue to "skipped" or delete them.'
yes_tuples = []
yes_tuples.append( ( 'set to skipped', 'skip' ) )
yes_tuples.append( ( 'delete them', 'delete' ) )
try:
result = ClientGUIDialogsQuick.GetYesYesNo( self, text, yes_tuples = yes_tuples, no_label = 'forget it' )
except HydrusExceptions.CancelledException:
return
do_delete = result == 'delete'
self._hdd_import.AbortImport( do_delete = do_delete )
self._UpdateImportStatus()
def Pause( self ):
@ -3711,9 +3677,6 @@ class ManagementPanelImporterURLs( ManagementPanelImporter ):
self._pause_button = ClientGUICommon.BetterBitmapButton( self._import_queue_panel, CC.global_pixmaps().file_pause, self.Pause )
self._pause_button.setToolTip( 'pause/play files' )
self._abort_button = ClientGUICommon.BetterBitmapButton( self._import_queue_panel, CC.global_pixmaps().stop, self.Abort )
self._abort_button.setToolTip( 'abort files' )
self._file_download_control = ClientGUINetworkJobControl.NetworkJobControl( self._import_queue_panel )
self._urls_import: ClientImportSimpleURLs.URLsImport = self._management_controller.GetVariable( 'urls_import' )
@ -3751,7 +3714,6 @@ class ManagementPanelImporterURLs( ManagementPanelImporter ):
hbox = QP.HBoxLayout()
QP.AddToLayout( hbox, self._pause_button, CC.FLAGS_ON_RIGHT )
QP.AddToLayout( hbox, self._abort_button, CC.FLAGS_ON_RIGHT )
self._import_queue_panel.Add( hbox, CC.FLAGS_ON_RIGHT )
self._import_queue_panel.Add( self._file_seed_cache_control, CC.FLAGS_EXPAND_PERPENDICULAR )
@ -3847,8 +3809,6 @@ class ManagementPanelImporterURLs( ManagementPanelImporter ):
self._gallery_download_control.SetNetworkJob( gallery_network_job )
self._abort_button.setEnabled( self._urls_import.GetFileSeedCache().WorkToDo() )
def CheckAbleToClose( self ):
@ -3857,35 +3817,6 @@ class ManagementPanelImporterURLs( ManagementPanelImporter ):
raise HydrusExceptions.VetoException( 'This page is still importing.' )
def Abort( self ):
if not self._urls_import.GetFileSeedCache().WorkToDo():
return
text = 'Stop the import here? You can either set the remainder of the queue to "skipped" or delete them.'
yes_tuples = []
yes_tuples.append( ( 'set to skipped', 'skip' ) )
yes_tuples.append( ( 'delete them', 'delete' ) )
try:
result = ClientGUIDialogsQuick.GetYesYesNo( self, text, yes_tuples = yes_tuples, no_label = 'forget it' )
except HydrusExceptions.CancelledException:
return
do_delete = result == 'delete'
self._urls_import.AbortImport( do_delete = do_delete )
self._UpdateImportStatus()
def Pause( self ):

View File

@ -1832,8 +1832,6 @@ class AutoCompleteDropdownTagsRead( AutoCompleteDropdownTags ):
AutoCompleteDropdownTags.__init__( self, parent, location_context, tag_context.service_key )
self._predicates_listbox.SetPredicates( self._file_search_context.GetPredicates() )
self._location_context_button.SetAllKnownFilesAllowed( allow_all_known_files, True )
#
@ -1913,7 +1911,7 @@ class AutoCompleteDropdownTagsRead( AutoCompleteDropdownTags ):
self._dropdown_window.setLayout( vbox )
self._predicates_listbox.listBoxChanged.connect( self._SignalNewSearchState )
self._predicates_listbox.listBoxChanged.connect( self._NotifyPredicatesBoxChanged )
self._include_current_tags.valueChanged.connect( self._IncludeCurrentChanged )
self._include_pending_tags.valueChanged.connect( self._IncludePendingChanged )
@ -2016,7 +2014,11 @@ class AutoCompleteDropdownTagsRead( AutoCompleteDropdownTags ):
try:
predicates = ClientGUISearch.EditPredicates( self, predicates )
empty_file_search_context = self._file_search_context.Duplicate()
empty_file_search_context.SetPredicates( [] )
predicates = ClientGUISearch.EditPredicates( self, predicates, empty_file_search_context = empty_file_search_context )
except HydrusExceptions.CancelledException:
@ -2352,15 +2354,20 @@ class AutoCompleteDropdownTagsRead( AutoCompleteDropdownTags ):
def _SetupTopListBox( self ):
self._predicates_listbox = ListBoxTagsActiveSearchPredicates( self, self._page_key )
self._predicates_listbox = ListBoxTagsActiveSearchPredicates( self, self._page_key, self._file_search_context )
QP.AddToLayout( self._main_vbox, self._predicates_listbox, CC.FLAGS_EXPAND_BOTH_WAYS_SHY )
def _SignalNewSearchState( self ):
def _NotifyPredicatesBoxChanged( self ):
self._file_search_context.SetPredicates( self._predicates_listbox.GetPredicates() )
self._SignalNewSearchState()
def _SignalNewSearchState( self ):
file_search_context = self._file_search_context.Duplicate()
self.searchChanged.emit( file_search_context )
@ -2477,16 +2484,12 @@ class AutoCompleteDropdownTagsRead( AutoCompleteDropdownTags ):
def GetFileSearchContext( self ) -> ClientSearch.FileSearchContext:
fsc = self._file_search_context.Duplicate()
fsc.SetPredicates( self._predicates_listbox.GetPredicates() )
return fsc
return self._file_search_context.Duplicate()
def GetPredicates( self ) -> typing.Set[ ClientSearch.Predicate ]:
return self._predicates_listbox.GetPredicates()
return self._file_search_context.GetPredicates()
def IsSynchronised( self ):
@ -2540,7 +2543,7 @@ class AutoCompleteDropdownTagsRead( AutoCompleteDropdownTags ):
self._file_search_context = file_search_context.Duplicate()
self._predicates_listbox.SetPredicates( self._file_search_context.GetPredicates() )
self._predicates_listbox.SetFileSearchContext( self._file_search_context )
self._SetLocationContext( self._file_search_context.GetLocationContext() )
self._SetTagService( self._file_search_context.GetTagContext().service_key )
@ -2583,12 +2586,7 @@ class AutoCompleteDropdownTagsRead( AutoCompleteDropdownTags ):
class ListBoxTagsActiveSearchPredicates( ClientGUIListBoxes.ListBoxTagsPredicates ):
def __init__( self, parent: AutoCompleteDropdownTagsRead, page_key, initial_predicates = None ):
if initial_predicates is None:
initial_predicates = []
def __init__( self, parent: AutoCompleteDropdownTagsRead, page_key, file_search_context: ClientSearch.FileSearchContext ):
ClientGUIListBoxes.ListBoxTagsPredicates.__init__( self, parent, height_num_chars = 6 )
@ -2596,6 +2594,10 @@ class ListBoxTagsActiveSearchPredicates( ClientGUIListBoxes.ListBoxTagsPredicate
self._page_key = page_key
self._file_search_context = file_search_context
initial_predicates = self._file_search_context.GetPredicates()
if len( initial_predicates ) > 0:
terms = [ self._GenerateTermFromPredicate( predicate ) for predicate in initial_predicates ]
@ -2682,7 +2684,11 @@ class ListBoxTagsActiveSearchPredicates( ClientGUIListBoxes.ListBoxTagsPredicate
try:
edited_predicates = set( ClientGUISearch.EditPredicates( self, predicates ) )
empty_file_search_context = self._file_search_context.Duplicate()
empty_file_search_context.SetPredicates( [] )
edited_predicates = set( ClientGUISearch.EditPredicates( self, predicates, empty_file_search_context = empty_file_search_context ) )
except HydrusExceptions.CancelledException:
@ -2815,10 +2821,14 @@ class ListBoxTagsActiveSearchPredicates( ClientGUIListBoxes.ListBoxTagsPredicate
def SetPredicates( self, predicates ):
def SetFileSearchContext( self, file_search_context: ClientSearch.FileSearchContext ):
self._Clear()
self._file_search_context = file_search_context
predicates = self._file_search_context.GetPredicates()
terms = [ self._GenerateTermFromPredicate( predicate ) for predicate in predicates ]
self._AppendTerms( terms )
@ -2828,6 +2838,7 @@ class ListBoxTagsActiveSearchPredicates( ClientGUIListBoxes.ListBoxTagsPredicate
self._DataHasChanged()
class AutoCompleteDropdownTagsWrite( AutoCompleteDropdownTags ):
nullEntered = QC.Signal()

View File

@ -154,6 +154,8 @@ class LocationSearchContextButton( ClientGUICommon.BetterButton ):
last_seen_service_type = None
we_have_checked_something = False
for service in services:
if last_seen_service_type is not None and last_seen_service_type != service.GetServiceType():
@ -179,7 +181,14 @@ class LocationSearchContextButton( ClientGUICommon.BetterButton ):
desc += ' Note this includes files deleted from any domain at all, including those removed from one local file service but still in another local file service.'
ClientGUIMenus.AppendMenuItem( menu, name, desc, self.SetValue, location_context )
check_it = location_context == self._location_context
ClientGUIMenus.AppendMenuCheckItem( menu, name, desc, check_it, self.SetValue, location_context )
if check_it:
we_have_checked_something = True
last_seen_service_type = service.GetServiceType()
@ -189,13 +198,22 @@ class LocationSearchContextButton( ClientGUICommon.BetterButton ):
location_context = ClientLocation.LocationContext( current_service_keys = ( CC.COMBINED_LOCAL_MEDIA_SERVICE_KEY, ), deleted_service_keys = ( CC.COMBINED_LOCAL_MEDIA_SERVICE_KEY, ) )
ClientGUIMenus.AppendMenuItem( menu, 'all files ever imported or deleted', 'Change the current file domain to all current and deleted files your client has seen.', self.SetValue, location_context )
check_it = location_context == self._location_context
ClientGUIMenus.AppendMenuCheckItem( menu, 'all files ever imported or deleted', 'Change the current file domain to all current and deleted files your client has seen.', check_it, self.SetValue, location_context )
if check_it:
we_have_checked_something = True
ClientGUIMenus.AppendSeparator( menu )
ClientGUIMenus.AppendMenuItem( menu, 'multiple/deleted locations', 'Change the current file domain to something with multiple locations.', self._EditMultipleLocationContext )
check_it = not we_have_checked_something
ClientGUIMenus.AppendMenuCheckItem( menu, 'multiple/deleted locations', 'Change the current file domain to something with multiple locations.', check_it, self._EditMultipleLocationContext )
CGC.core().PopupMenu( self, menu )

View File

@ -20,7 +20,7 @@ from hydrus.client.search import ClientSearch
class ORPredicateControl( QW.QWidget ):
def __init__( self, parent: QW.QWidget, predicate: ClientSearch.Predicate ):
def __init__( self, parent: QW.QWidget, predicate: ClientSearch.Predicate, empty_file_search_context: typing.Optional[ ClientSearch.FileSearchContext ] = None ):
QW.QWidget.__init__( self, parent )
@ -31,13 +31,24 @@ class ORPredicateControl( QW.QWidget ):
raise Exception( 'Launched an ORPredicateControl without an OR Pred!' )
predicates = predicate.GetValue()
page_key = HydrusData.GenerateKey()
location_context = CG.client_controller.new_options.GetDefaultLocalLocationContext()
predicates = predicate.GetValue()
file_search_context = ClientSearch.FileSearchContext( location_context = location_context, predicates = predicates )
if empty_file_search_context is None:
location_context = CG.client_controller.new_options.GetDefaultLocalLocationContext()
file_search_context = ClientSearch.FileSearchContext( location_context = location_context, predicates = predicates )
else:
empty_file_search_context = empty_file_search_context.Duplicate()
empty_file_search_context.SetPredicates( predicates )
file_search_context = empty_file_search_context
self._search_control = ClientGUIACDropdown.AutoCompleteDropdownTagsRead( self, page_key, file_search_context, hide_favourites_edit_actions = True )

View File

@ -57,7 +57,7 @@ FLESH_OUT_SYSTEM_PRED_TYPES = {
ClientSearch.PREDICATE_TYPE_SYSTEM_FILE_VIEWING_STATS
}
def EditPredicates( widget: QW.QWidget, predicates: typing.Collection[ ClientSearch.Predicate ] ) -> typing.List[ ClientSearch.Predicate ]:
def EditPredicates( widget: QW.QWidget, predicates: typing.Collection[ ClientSearch.Predicate ], empty_file_search_context: typing.Optional[ ClientSearch.FileSearchContext ] = None ) -> typing.List[ ClientSearch.Predicate ]:
( editable_predicates, only_invertible_predicates, non_editable_predicates ) = GetEditablePredicates( predicates )
@ -86,7 +86,7 @@ def EditPredicates( widget: QW.QWidget, predicates: typing.Collection[ ClientSea
with ClientGUITopLevelWindowsPanels.DialogEdit( window, title ) as dlg:
panel = EditPredicatesPanel( dlg, predicates )
panel = EditPredicatesPanel( dlg, predicates, empty_file_search_context = empty_file_search_context )
dlg.SetPanel( panel )
@ -189,7 +189,7 @@ def GetEditablePredicates( predicates: typing.Collection[ ClientSearch.Predicate
class EditPredicatesPanel( ClientGUIScrolledPanels.EditPanel ):
def __init__( self, parent, predicates: typing.Collection[ ClientSearch.Predicate ] ):
def __init__( self, parent, predicates: typing.Collection[ ClientSearch.Predicate ], empty_file_search_context: typing.Optional[ ClientSearch.FileSearchContext ] = None ):
ClientGUIScrolledPanels.EditPanel.__init__( self, parent )
@ -223,7 +223,7 @@ class EditPredicatesPanel( ClientGUIScrolledPanels.EditPanel ):
if predicate_type == ClientSearch.PREDICATE_TYPE_OR_CONTAINER:
self._editable_pred_panels.append( ClientGUIPredicatesOR.ORPredicateControl( self, predicate ) )
self._editable_pred_panels.append( ClientGUIPredicatesOR.ORPredicateControl( self, predicate, empty_file_search_context = empty_file_search_context ) )
elif predicate_type in ( ClientSearch.PREDICATE_TYPE_TAG, ClientSearch.PREDICATE_TYPE_NAMESPACE, ClientSearch.PREDICATE_TYPE_WILDCARD ):
@ -998,7 +998,7 @@ class TagContextButton( ClientGUICommon.BetterButton ):
tag_context = ClientSearch.TagContext( service_key = service.GetServiceKey() )
ClientGUIMenus.AppendMenuItem( menu, service.GetName(), 'Change the current tag domain to {}.'.format( service.GetName() ), self.SetValue, tag_context )
ClientGUIMenus.AppendMenuCheckItem( menu, service.GetName(), 'Change the current tag domain to {}.'.format( service.GetName() ), tag_context == self._tag_context, self.SetValue, tag_context )
last_seen_service_type = service.GetServiceType()

View File

@ -3237,6 +3237,25 @@ class FileSeedCache( HydrusSerialisable.SerialisableBase ):
self._NotifyFileSeedsUpdated( updated_file_seeds )
def SetStatusToStatus( self, old_status, new_status ):
with self._lock:
file_seeds = self._GetFileSeeds( old_status )
for file_seed in file_seeds:
file_seed.SetStatus( new_status )
self._FixStatusesToFileSeeds( file_seeds )
self._SetStatusDirty()
self._NotifyFileSeedsUpdated( file_seeds )
def WorkToDo( self ):
with self._lock:

View File

@ -368,32 +368,6 @@ class HDDImport( HydrusSerialisable.SerialisableBase ):
def AbortImport( self, do_delete = False ):
if do_delete:
self._file_seed_cache.RemoveFileSeedsByStatus( ( CC.STATUS_UNKNOWN, ) )
else:
file_seeds = self._file_seed_cache.GetFileSeeds( CC.STATUS_UNKNOWN )
for file_seed in file_seeds:
file_seed.SetStatus( CC.STATUS_SKIPPED, note = 'import abandoned by user' )
self._file_seed_cache.NotifyFileSeedsUpdated( file_seeds )
with self._lock:
self._files_status = 'aborted'
self._SerialisableChangeMade()
def SetFileImportOptions( self, file_import_options: FileImportOptions.FileImportOptions ):
with self._lock:

View File

@ -1243,32 +1243,6 @@ class URLsImport( HydrusSerialisable.SerialisableBase ):
self._SerialisableChangeMade()
def AbortImport( self, do_delete = False ):
if do_delete:
self._file_seed_cache.RemoveFileSeedsByStatus( ( CC.STATUS_UNKNOWN, ) )
else:
file_seeds = self._file_seed_cache.GetFileSeeds( CC.STATUS_UNKNOWN )
for file_seed in file_seeds:
file_seed.SetStatus( CC.STATUS_SKIPPED, note = 'import abandoned by user' )
self._file_seed_cache.NotifyFileSeedsUpdated( file_seeds )
with self._lock:
self._files_status = 'aborted'
self._SerialisableChangeMade()
def PendURLs( self, urls, filterable_tags = None, additional_service_keys_to_tags = None ):

View File

@ -1022,6 +1022,7 @@ class FileSystemPredicates( object ):
def MustNotBeLocal( self ): return self._not_local
SEARCH_TYPE_AND = 0
SEARCH_TYPE_OR = 1

View File

@ -105,7 +105,7 @@ options = {}
# Misc
NETWORK_VERSION = 20
SOFTWARE_VERSION = 562
SOFTWARE_VERSION = 563
CLIENT_API_VERSION = 60
SERVER_THUMBNAIL_DIMENSIONS = ( 200, 200 )

View File

@ -3000,13 +3000,20 @@ class DB( HydrusDB.HydrusDB ):
def _RepositoryGetMappingPetition( self, service_id, petitioner_account_id, reason_id ) -> HydrusNetwork.Petition:
# we had a user petition 250k 'hash:abcdef...' tags with the same reason, and it overloaded the petition system trying to present them all
MAX_MAPPINGS_PER_PETITION = 500000
MAX_UNIQUE_TAG_IDS_PER_PETITION = 10000
TIME_ALLOWED = 10
time_started = HydrusTime.GetNowFloat()
( current_mappings_table_name, deleted_mappings_table_name, pending_mappings_table_name, petitioned_mappings_table_name ) = GenerateRepositoryMappingsTableNames( service_id )
petitioner_account = self._GetAccount( service_id, petitioner_account_id )
reason = self._GetReason( reason_id )
tag_ids_to_hash_ids = HydrusData.BuildKeyToListDict( self._Execute( f'SELECT service_tag_id, service_hash_id FROM {petitioned_mappings_table_name} WHERE account_id = ? AND reason_id = ?;', ( petitioner_account_id, reason_id ) ) )
tag_ids_to_hash_ids = HydrusData.BuildKeyToListDict( self._Execute( f'SELECT service_tag_id, service_hash_id FROM {petitioned_mappings_table_name} WHERE account_id = ? AND reason_id = ? LIMIT;', ( petitioner_account_id, reason_id, MAX_MAPPINGS_PER_PETITION ) ) )
if len( tag_ids_to_hash_ids ) == 0:
@ -3015,9 +3022,10 @@ class DB( HydrusDB.HydrusDB ):
contents = []
petition_pairs = list( tag_ids_to_hash_ids.items() )
# if this is a giganto petition, let's serve the large-count tags first
petition_pairs = sorted( tag_ids_to_hash_ids.items(), key = lambda t_and_h: len( t_and_h[1] ), reverse = True )
for ( service_tag_id, service_hash_ids ) in HydrusData.IterateListRandomlyAndFast( petition_pairs ):
for ( service_tag_id, service_hash_ids ) in petition_pairs[ : MAX_UNIQUE_TAG_IDS_PER_PETITION ]:
master_tag_id = self._RepositoryGetMasterTagId( service_id, service_tag_id )
@ -3031,6 +3039,11 @@ class DB( HydrusDB.HydrusDB ):
contents.append( content )
if HydrusTime.TimeHasPassedFloat( time_started + TIME_ALLOWED ):
break
action = HC.CONTENT_UPDATE_PETITION

View File

@ -3,15 +3,15 @@
pushd "$(dirname "$0")" || exit 1
if [ ! -d "venv" ]; then
echo "You need to set up a venv! Check the running from source help for more info!"
popd || exit 1
exit 1
echo "You need to set up a venv! Check the running from source help for more info!"
popd || exit 1
exit 1
fi
if ! source venv/bin/activate; then
echo "The venv failed to activate, stopping now!"
popd || exit 1
exit 1
popd || exit 1
exit 1
fi
# You can copy this file to 'client-user.sh' and add in your own launch parameters here if you like, and a git pull won't overwrite the file.

View File

@ -9,32 +9,32 @@ DESKTOP_DEST_PATH=$HOME/.local/share/applications/hydrus.desktop
echo "Install folder appears to be $INSTALL_DIR"
if [ ! -f "$DESKTOP_SOURCE_PATH" ]; then
echo "Sorry, I do not see the template file at $DESKTOP_SOURCE_PATH! Was it deleted, or this script moved?"
popd || exit 1
exit 1
echo "Sorry, I do not see the template file at $DESKTOP_SOURCE_PATH! Was it deleted, or this script moved?"
popd || exit 1
exit 1
fi
if [ -f "$DESKTOP_DEST_PATH" ]; then
echo "You already have a hydrus.desktop file at $DESKTOP_DEST_PATH. Would you like to overwrite it? y/n "
echo "You already have a hydrus.desktop file at $DESKTOP_DEST_PATH. Would you like to overwrite it? y/n "
else
echo "Create a hydrus.desktop file at $DESKTOP_DEST_PATH? y/n "
echo "Create a hydrus.desktop file at $DESKTOP_DEST_PATH? y/n "
fi
read -r affirm
if [ "$affirm" = "y" ]; then
:
:
elif [ "$affirm" = "n" ]; then
popd || exit
exit 0
popd || exit
exit 0
else
echo "Sorry, did not understand that input!"
popd || exit 1
exit 1
echo "Sorry, did not understand that input!"
popd || exit 1
exit 1
fi
sed -e "s#Exec=.*#Exec=${INSTALL_DIR}/hydrus_client.sh#" -e "s#Icon=.*#Icon=${INSTALL_DIR}/static/hydrus.png#" "$DESKTOP_SOURCE_PATH" > "$DESKTOP_DEST_PATH"

View File

@ -3,14 +3,14 @@
pushd "$(dirname "$0")" || exit 1
if [ ! -d "venv" ]; then
echo "You need to set up a venv! Check the running from source help for more info!"
popd || exit 1
exit 1
echo "You need to set up a venv! Check the running from source help for more info!"
popd || exit 1
exit 1
fi
if [ -d "help" ]; then
echo "Deleting old help..."
rm -rf help
echo "Deleting old help..."
rm -rf help
fi
echo "Creating new help..."

View File

@ -3,14 +3,14 @@
pushd "$(dirname "$0")" || exit 1
if [ ! -d "venv" ]; then
echo "You need to set up a venv! Check the running from source help for more info!"
popd || exit 1
exit 1
echo "You need to set up a venv! Check the running from source help for more info!"
popd || exit 1
exit 1
fi
if [ -d "help" ]; then
echo "Deleting old help..."
rm -rf help
echo "Deleting old help..."
rm -rf help
fi
echo "Creating new help..."

View File

@ -52,6 +52,16 @@ IF EXIST "venv\" (
)
IF EXIST "venv\" (
SET /P gumpf="It looks like the venv directory did not delete correctly. Do you have it activated in a terminal or IDE anywhere? Please close that and try this again!"
popd
EXIT /B 1
)
:questions
ECHO --------

View File

@ -44,6 +44,11 @@ else
read -r
fi
if [ -d "venv" ]; then
echo "It looks like the venv directory did not delete correctly. Do you have it activated in a terminal or IDE anywhere? Please close that and try this again!"
exit 1
fi
echo "--------"
echo "If your macOS is old, or you are on >=Python 3.11, do the advanced install. Let hydev know what works for you."
echo

View File

@ -44,6 +44,11 @@ else
read -r
fi
if [ -d "venv" ]; then
echo "It looks like the venv directory did not delete correctly. Do you have it activated in a terminal or IDE anywhere? Please close that and try this again!"
exit 1
fi
echo "--------"
echo "Users on older OSes or Python >=3.11 need the advanced install."
echo