* EDMC: Add --trace (to match EDMarketConnector.py) and TRACE as option to
--loglevel.
* EDMC: docstrings added.
* EDMCLogging: Set logger name based on if GUI or CLI.
* EDMarketConnector:
* Re-order imports.
* Misc. formatting cleanups.
* f-strings not .format().
* Removed un-necessary "# noqa: N806" comments.
As we might run into some special cases with users we need to log in
more detail what the locale is around our changes.
NB: Also contains some misc. PyCharm-enacted formatting changes, white
space, wrapping etc.
tkinter ends up calling something where utf8 characters won't work
because of the windows encoding, e.g. cp1252.
We can't set encoding just for the dialogs, as it's not thread safe. So
we'll just set it at startup instead. Utilising:
locale.setlocale(locale.LC_ALL, '')
to get things set up initially, so we can properly retrieve the language
to go with the encoding on the subsequent setlocale() call.
If we use the last configured value then the width of the Scale bar
changes as per the user's last setting, even though they might not have
yet restarted for all the rest of the UI to resize.
So if at startup tk-scaling is 1.33 then a user configured 200(%) will
set it to 2.66 for this run.
* Low end of scale bar set to 10, not 0, because now 0 makes absolutely
no sense.
* In theory the width of the scale bar, in pixels, is now also correctly
scaled.
Using a Tk.DoubleVar() with a locale where a comma is used as the
decimals separator leads to internal tk code recording values with the
comma but then other tk code not accepting that back, so it always
thinks the value is zero and the scale slider can't be moved.
Ref: https://stackoverflow.com/questions/45289237/tkinter-scale-slider-with-float-values-doesnt-work-with-locale-of-language-that
* Change to storing as a REG_DWORD under 'ui_scale' not 'ui_scaling'.
* Change all the code, except the call to *set* the tk scaling to use an
integer, with 100 = 100%, i.e. equivalent to the old 1.0.
* Update strings slightly, so translations will need updating too.
NB: The theme.default_ui_scale value for plugin authors to query is
still the float that tk returns.
If anything does `import logging` it's likely to grab a copy of
sys.stderr before we redirect it meaning things aren't properly
redirected when we start logging.
So put the redirect as early as possible in EDMarketConnector.py.
This will need a test implemented to be sure it's not accidentally
broken by addition of an import before this.
* If no ui_scaling yet set, set it to 'default'.
* 'default' added to dropdown choices.
Note that you still need an application restart for this to take effect.
* EDMCLogging.Logger.get_streamhandler() method to get the
logger_channel so prefs.py can call setLevel() on it.
* The top-level Logger level is always DEBUG.
* The RotatingFileHandler level is always DEBUG.
* The StreamHandler level is as configured by the user.
* EDMCLogging now creates a singleton of EDMCLogging.Logger and its
associated Logger.
* plug.py tweaked to only import EDMCLogging where it's needed for
getting/creating plugin loggers, else `import logging`.
* All logging duplicated into %TEMP%/{appname}/{logger_name}.log
* These are handled by the RotatingFileHandler, currently set to 1MiB
per file and 10 backup files.
* Do *NOT* setLevel() on the handlers, as we want to control the level
up at the logger instead. This would have caused the CL and GUI
selection of loglevel to NOT have any effect (hidden by default having
been DEBUG).
* EDMarketConnector.py now INFO logs its startup and exit.
* <module> works.
* top-level function in <module> works, presumably also any other file.
* Call from within classes works.
* Extra, commented out, test cases in EDMarketConnector.py
* Technically %(class)s can be e.g. A.B not just 'B' so say "name(s)".
* To not confuse EDMCLogging.getLogger() with logging.getLogger() it's
been renamed to get_logger().
* Note how we signal errors with finding class and/or qualname.
* Call out EDMCLogging.py in Contributing.md.
* Also now providers single caller_class_and_qualname() method to get
both strings, returned as a Tuple[str, str]. Either could be empty
if something went wrong.
* Rather than a fragile 'skip' this now:
1. Looks for the first up-stack frame with self of logging.Logger
1. Then looks for the next up-stack frame with self NOT of
logging.Logger. This should be the call site we want.
* Nothing should "from EDMarketConnector import logger" any more, so
we can move this back inside __main__ section
* We shouldn't rely on a magic number of frames to skip. Detect the
proper frame automatically.
This gets the function that called the logging, and if it's in a class
it looks up the function via getattr(<class>, <func name>) and then
uses __qualname__ to quickly get the fully qualified name.