1
0
mirror of https://github.com/EDCD/EDMarketConnector.git synced 2025-05-30 23:29:30 +03:00

Merge branch 'release-final-python27' into release-346

This commit is contained in:
Athanasius 2020-06-21 17:59:32 +01:00
commit 9be67224c1
20 changed files with 430 additions and 148 deletions

View File

@ -217,10 +217,9 @@ class AppWindow:
theme_titlebar.bind('<Button-1>', self.drag_start)
theme_titlebar.bind('<B1-Motion>', self.drag_continue)
theme_titlebar.bind('<ButtonRelease-1>', self.drag_end)
if platform == 'win32': # Can't work out how to deiconify on Linux
theme_minimize = tk.Label(self.theme_menubar, image=self.theme_minimize)
theme_minimize.grid(row=0, column=3, padx=2)
theme.button_bind(theme_minimize, self.oniconify, image=self.theme_minimize)
theme_minimize = tk.Label(self.theme_menubar, image=self.theme_minimize)
theme_minimize.grid(row=0, column=3, padx=2)
theme.button_bind(theme_minimize, self.oniconify, image=self.theme_minimize)
theme_close = tk.Label(self.theme_menubar, image=self.theme_close)
theme_close.grid(row=0, column=4, padx=2)
theme.button_bind(theme_close, self.onexit, image=self.theme_close)
@ -300,6 +299,15 @@ class AppWindow:
self.postprefs(False) # Companion login happens in callback from monitor
plugins_not_py3_last = config.getint('plugins_not_py3_last') or int(time())
if (plugins_not_py3_last + 86400) < int(time()) and len(plug.PLUGINS_not_py3):
import tkMessageBox
tkMessageBox.showinfo('Plugins Without Python 3.x Support',
"One or more of your enabled plugins do not yet have support for Python 3.x. Please see the list on the 'Plugins' tab of 'File' > 'Settings'. You should check if there is an updated version available, else alert the developer that they will need to update the code when EDMC moves to Python 3.x"
)
config.set('plugins_not_py3_last', int(time()))
# callback after the Preferences dialog is applied
def postprefs(self, dologin=True):
self.prefsdialog = None
@ -575,6 +583,10 @@ class AppWindow:
return # Startup or in CQC
if entry['event'] in ['StartUp', 'LoadGame'] and monitor.started:
# Disable WinSparkle automatic update checks, IFF configured to do so when in-game
if config.getint('disable_autoappupdatecheckingame') and 1:
self.updater.setAutomaticUpdatesCheck(False)
print 'Monitor: Disable WinSparkle automatic update checks'
# Can start dashboard monitoring
if not dashboard.start(self.w, monitor.started):
print "Can't start Status monitoring"
@ -594,6 +606,12 @@ class AppWindow:
if entry['event'] in ['StartUp', 'Location', 'Docked'] and monitor.station and not config.getint('output') & config.OUT_MKT_MANUAL and config.getint('output') & config.OUT_STATION_ANY and companion.session.state != companion.Session.STATE_AUTH:
self.w.after(int(SERVER_RETRY * 1000), self.getandsend)
if entry['event'] == 'ShutDown':
# Enable WinSparkle automatic update checks
# NB: Do this blindly, in case option got changed whilst in-game
self.updater.setAutomaticUpdatesCheck(True)
print 'Monitor: Enable WinSparkle automatic update checks'
# cAPI auth
def auth(self, event=None):
try:

View File

@ -142,6 +142,9 @@
/* [edsm.py] */
"Error: Can't connect to EDSM" = "Fout: Kan geen verbinding maken met EDSM";
/* [inara.py] */
"Error: Can't connect to Inara" = "Fout: Kan geen verbinding maken met Inara";
/* [edsm.py] */
"Error: EDSM {MSG}" = "Fout: EDSM {MSG}";
@ -154,11 +157,14 @@
/* Raised when the Companion API server thinks that the user has not purchased E:D. i.e. doesn't have the correct 'SKU'. [companion.py] */
"Error: Frontier server SKU problem" = "Fout: Frontier server SKU probleem";
/* [inara.py] */
"Error: Inara {MSG}" = "Fout: Inara {MSG}";
/* [companion.py] */
"Error: Invalid Credentials" = "Fout: Ongeldige inlog gegevens";
/* Raised when the user has multiple accounts and the username/password setting is not for the account they're currently playing OR the user has reset their Cmdr and the Companion API server is still returning data for the old Cmdr. [companion.py] */
"Error: Wrong Cmdr" = "Fout: Verkeerde CMDR";
"Error: Wrong Cmdr" = "Fout: Verkeerde Cmdr";
/* Item in the File menu on Windows. [EDMarketConnector.py] */
"Exit" = "Afsluiten";
@ -211,6 +217,9 @@
/* Hotkey/Shortcut settings prompt on Windows. [prefs.py] */
"Hotkey" = "Hotkey";
/* Section heading in settings. [inara.py] */
"Inara credentials" = "Inara inlog gegevens";
/* Hotkey/Shortcut settings prompt on OSX. [prefs.py] */
"Keyboard shortcut" = "Toetscombinaties";
@ -391,6 +400,12 @@
/* CQC rank. [stats.py] */
"Semi Professional" = "Semi Professional";
/* [edsm.py] */
"Send flight log and Cmdr status to EDSM" = "Stuur vlucht logboek en Cmdr status naar EDSM";
/* [inara.py] */
"Send flight log and Cmdr status to Inara" = "Stuur vlucht logboek en Cmdr status naar Inara";
/* Output setting. [eddn.py] */
"Send station data to the Elite Dangerous Data Network" = "Stuur station gegevens naar het Elite Dangerous Data Network";

View File

@ -143,7 +143,7 @@
"Error: Can't connect to EDSM" = "Błąd: Brak połączenia z EDSM";
/* [inara.py] */
"Error: Can't connect to Inara" = "Błąd: Nie można połączyć się do serwisu Inara";
"Error: Can't connect to Inara" = "Błąd: Nie można połączyć się z serwisem Inara";
/* [edsm.py] */
"Error: EDSM {MSG}" = "Błąd: EDSM {MSG}";

View File

@ -23,10 +23,10 @@
"Appearance" = "Внешний вид";
/* Successfully authenticated with the Frontier website. [EDMarketConnector.py] */
"Authentication successful" = "Аутентификация успешна";
"Authentication successful" = "Аутентификация успешно завершена";
/* Output setting. [prefs.py] */
"Automatically update on docking" = "Автоматически отправлять при стыковке";
"Automatically update on docking" = "Автоматически обновлять данные при стыковке";
/* Cmdr stats. [stats.py] */
"Balance" = "Баланс";
@ -38,7 +38,7 @@
"Broker" = "Брокер";
/* Folder selection button on Windows. [prefs.py] */
"Browse..." = "Обзор...";
"Browse..." = "Просмотреть...";
/* Federation rank. [stats.py] */
"Cadet" = "Кадет";
@ -50,7 +50,7 @@
"Change..." = "Обзор...";
/* Menu item. [EDMarketConnector.py] */
"Check for Updates..." = "Поиск обновлений...";
"Check for Updates..." = "Проверить наличие обновлений...";
/* Federation rank. [stats.py] */
"Chief Petty Officer" = "Главный старшина";
@ -62,7 +62,7 @@
"Combat" = "Боевой";
/* EDSM setting. [edsm.py] */
"Commander Name" = "Имя Пилота";
"Commander Name" = "Имя командира";
/* Combat rank. [stats.py] */
"Competent" = "Специалист";
@ -86,7 +86,7 @@
"Dangerous" = "Опасный";
/* Appearance theme setting. [prefs.py] */
"Dark" = "Темный";
"Dark" = "Темная";
/* Combat rank. [stats.py] */
"Deadly" = "Смертоносный";
@ -98,13 +98,13 @@
"Default" = "По умолчанию";
/* Output setting under 'Send system and scan data to the Elite Dangerous Data Network' new in E:D 2.2. [eddn.py] */
"Delay sending until docked" = "Отложить отправку до завершения стыковки";
"Delay sending until docked" = "Отложить отправку данных до завершения стыковки";
/* List of plugins in settings. [prefs.py] */
"Disabled Plugins" = "Отключенные расширения";
"Disabled Plugins" = "Отключенные плагины";
/* Help menu item. [EDMarketConnector.py] */
"Documentation" = "Документация";
"Documentation" = "Информация о плагине";
/* Empire rank. [stats.py] */
"Duke" = "Герцог";
@ -128,7 +128,7 @@
"Empire" = "Империя";
/* List of plugins in settings. [prefs.py] */
"Enabled Plugins" = "Включенные расширения";
"Enabled Plugins" = "Включенные плагины";
/* Federation rank. [stats.py] */
"Ensign" = "Энсин";
@ -137,34 +137,34 @@
"Entrepreneur" = "Предприниматель";
/* [eddn.py] */
"Error: Can't connect to EDDN" = "Ошибка: не удалось подключиться к EDDN";
"Error: Can't connect to EDDN" = "Ошибка: не удается подключиться к EDDN";
/* [edsm.py] */
"Error: Can't connect to EDSM" = "Ошибка: не удалось подключиться к EDSM";
/* [inara.py] */
"Error: Can't connect to Inara" = "Ошибка: Невозможно подключиться к Inara";
"Error: Can't connect to Inara" = "Ошибка: не удалось подключиться к Inara";
/* [edsm.py] */
"Error: EDSM {MSG}" = "Ошибка: EDSM {MSG}";
/* Raised when cannot contact the Companion API server. [companion.py] */
"Error: Frontier server is down" = "Ошибка: Сервер Фронтир выключен";
"Error: Frontier server is down" = "Ошибка: сервера Frontier недоступны";
/* Raised when Companion API server is returning old data, e.g. when the servers are too busy. [companion.py] */
"Error: Frontier server is lagging" = "Ошибка: Сервер Фронтир лаг'ает";
"Error: Frontier server is lagging" = "Ошибка: сервера Frontier нестабильны";
/* Raised when the Companion API server thinks that the user has not purchased E:D. i.e. doesn't have the correct 'SKU'. [companion.py] */
"Error: Frontier server SKU problem" = "Ошибка сервера аутентификации Frontier";
"Error: Frontier server SKU problem" = "Ошибка: проблема с сервером аутентификации Frontier";
/* [inara.py] */
"Error: Inara {MSG}" = "Ошибка: Inara {MSG}";
/* [companion.py] */
"Error: Invalid Credentials" = "Ошибка: неверное имя пользователя или пароль";
"Error: Invalid Credentials" = "Ошибка: неизвестное имя пользователя или неверный пароль";
/* Raised when the user has multiple accounts and the username/password setting is not for the account they're currently playing OR the user has reset their Cmdr and the Companion API server is still returning data for the old Cmdr. [companion.py] */
"Error: Wrong Cmdr" = "Ошибка: неверное имя";
"Error: Wrong Cmdr" = "Ошибка: неверное имя командира";
/* Item in the File menu on Windows. [EDMarketConnector.py] */
"Exit" = "Выход";
@ -179,7 +179,7 @@
"Federation" = "Федерация";
/* [EDMarketConnector.py] */
"Fetching data..." = "Получаем данные...";
"Fetching data..." = "Получение данных...";
/* Multicrew role. [EDMarketConnector.py] */
"Fighter" = "Истребитель";
@ -188,7 +188,7 @@
"File" = "Файл";
/* Section heading in settings. [prefs.py] */
"File location" = "Сохранять файлы";
"File location" = "Путь хранения файлов";
/* CQC rank. [stats.py] */
"Gladiator" = "Гладиатор";
@ -200,10 +200,10 @@
"Harmless" = "Безвредный";
/* Multicrew role. [EDMarketConnector.py] */
"Helm" = "Шлем";
"Helm" = "Командир корабля";
/* Menu title. [EDMarketConnector.py] */
"Help" = "Справка";
"Help" = "Дополнительно";
/* CQC rank. [stats.py] */
"Helpless" = "Беспомощный";
@ -233,7 +233,7 @@
"Language" = "Язык";
/* [EDMarketConnector.py] */
"Last updated at {HH}:{MM}:{SS}" = "Последнее обновление {HH}:{MM}:{SS}";
"Last updated at {HH}:{MM}:{SS}" = "Последнее обновление в {HH}:{MM}:{SS}";
/* Federation rank. [stats.py] */
"Lieutenant" = "Лейтенант";
@ -245,16 +245,16 @@
"Loan" = "Долг";
/* [EDMarketConnector.py] */
"Logging in..." = "Подключаемся...";
"Logging in..." = "Идет подключение...";
/* Empire rank. [stats.py] */
"Lord" = "Лорд";
/* [prefs.py] */
"Market data in CSV format file" = "Торговые данные в формате CSV";
"Market data in CSV format file" = "Данные рынков в формате CSV";
/* [prefs.py] */
"Market data in Trade Dangerous format file" = "Торговые данные в формате Trade Dangerous";
"Market data in Trade Dangerous format file" = "Данные рынков в формате Trade Dangerous";
/* Empire rank. [stats.py] */
"Marquis" = "Маркиз";
@ -281,7 +281,7 @@
"Mostly Penniless" = "Полунищий";
/* No hotkey/shortcut currently defined. [prefs.py] */
"None" = "Не назначена";
"None" = "Не назначено";
/* Dark theme color setting. [prefs.py] */
"Normal text" = "Обычный текст";
@ -293,7 +293,7 @@
"OK" = "OK";
/* Hotkey/Shortcut setting. [prefs.py] */
"Only when Elite: Dangerous is the active app" = "Только в окне Elite: Dangerous";
"Only when Elite: Dangerous is the active app" = "Только когда окно Elite: Dangerous активно";
/* Button that opens a folder in Explorer/Finder. [prefs.py] */
"Open" = "Открыть";
@ -326,13 +326,13 @@
"Play sound" = "Звуковое оповещение";
/* [prefs.py] */
"Please choose what data to save" = "Выберите какие данные сохранять";
"Please choose what data to save" = "Выберите какие данные стоит сохранять";
/* Tab heading in settings. [prefs.py] */
"Plugins" = "Расширения";
"Plugins" = "Плагины";
/* Section heading in settings. [prefs.py] */
"Plugins folder" = "Папка расширений";
"Plugins folder" = "Папка плагинов";
/* Federation rank. [stats.py] */
"Post Captain" = "Командир корабля";
@ -341,7 +341,7 @@
"Post Commander" = "Начальник гарнизона";
/* Ranking. [stats.py] */
"Powerplay" = "Политика";
"Powerplay" = "Ранг в Игре Сил";
/* [prefs.py] */
"Preferences" = "Настройки";
@ -386,13 +386,13 @@
"Recruit" = "Новобранец";
/* Help menu item. [EDMarketConnector.py] */
"Release Notes" = "Заметки о выпуске";
"Release Notes" = "Заметки о текущем релизе";
/* Multicrew role label in main window. [EDMarketConnector.py] */
"Role" = "Роль";
/* Menu item. [EDMarketConnector.py] */
"Save Raw Data..." = "Сохранить все данные..";
"Save Raw Data..." = "Сохранить \"сырые\" данные..";
/* Explorer rank. [stats.py] */
"Scout" = "Разведчик";
@ -401,13 +401,13 @@
"Semi Professional" = "Полупрофессионал";
/* [edsm.py] */
"Send flight log and Cmdr status to EDSM" = "Отправлять журнал полёта и состояние командира в EDSM";
"Send flight log and Cmdr status to EDSM" = "Отправлять данные полетного журнала и информацию статусе командира в EDSM";
/* [inara.py] */
"Send flight log and Cmdr status to Inara" = "Отправлять журнал полёта и состояние командира в Inara";
/* Output setting. [eddn.py] */
"Send station data to the Elite Dangerous Data Network" = "Отправлять информацию о станциях на Elite Dangerous Data Network";
"Send station data to the Elite Dangerous Data Network" = "Отправлять станционную информацию на Elite Dangerous Data Network";
/* Output setting new in E:D 2.2. [eddn.py] */
"Send system and scan data to the Elite Dangerous Data Network" = "Отправлять данные о системе и данные сканирования в Elite Dangerous Data Network";
@ -419,16 +419,16 @@
"Serf" = "Крепостной";
/* Item in the File menu on Windows. [EDMarketConnector.py] */
"Settings" = "Параметры";
"Settings" = "Настройки";
/* Main window. [EDMarketConnector.py] */
"Ship" = "Корабль";
"Ship" = "Судно";
/* Output setting. [prefs.py] */
"Ship loadout" = "Оборудование корабля";
"Ship loadout" = "Оборудование судна";
/* Status dialog title. [stats.py] */
"Ships" = "Корабли";
"Ships" = "Суда";
/* Setting to decide which ship outfitting website to link to - either E:D Shipyard or Coriolis. [prefs.py] */
"Shipyard" = "Верфи";
@ -440,10 +440,10 @@
"Station" = "Станция";
/* [EDMarketConnector.py] */
"Station doesn't have a market!" = "На этой станции нет рынка!";
"Station doesn't have a market!" = "На текущей станции рынок недоступен!";
/* [EDMarketConnector.py] */
"Station doesn't have anything!" = "На этой станции нет никаких услуг!";
"Station doesn't have anything!" = "На текущей станции никакие услуги не предоставляются!";
/* Menu item. [EDMarketConnector.py] */
"Status" = "Статус";
@ -455,28 +455,28 @@
"System" = "Система";
/* Appearance setting. [prefs.py] */
"Theme" = "Тема";
"Theme" = "Тема оформления";
/* Help text in settings. [prefs.py] */
"Tip: You can disable a plugin by{CR}adding '{EXT}' to its folder name" = "Подсказка: Отключить расширение можно{CR}добавив '{EXT}' к имени папки";
"Tip: You can disable a plugin by{CR}adding '{EXT}' to its folder name" = "Подсказка: Отключить конкретный плагин можно {CR} добавив '{EXT}' к названию папки";
/* Ranking. [stats.py] */
"Trade" = "Торговый";
/* Explorer rank. [stats.py] */
"Trailblazer" = "Путешественние";
"Trailblazer" = "Путешественник";
/* Appearance theme setting. [prefs.py] */
"Transparent" = "Прозрачный";
"Transparent" = "Прозрачная";
/* Trade rank. [stats.py] */
"Tycoon" = "Магнат";
/* Update button in main window. [EDMarketConnector.py] */
"Update" = "Обновить";
"Update" = "Обновить данные";
/* Status dialog subtitle - CR value of ship. [stats.py] */
"Value" = "Цена";
"Value" = "Стоимость";
/* Federation rank. [stats.py] */
"Vice Admiral" = "Вице-адмирал";
@ -491,20 +491,20 @@
"Warrant Officer" = "Уорент-офицер";
/* Shouldn't happen. [EDMarketConnector.py] */
"What are you flying?!" = "На чём вы летаете?";
"What are you flying?!" = "Невозможно определить тип судна!";
/* Shouldn't happen. [EDMarketConnector.py] */
"Where are you?!" = "Где вы?";
"Where are you?!" = "Невозможно определить местоположение!";
/* Shouldn't happen. [EDMarketConnector.py] */
"Who are you?!" = "Кто вы?";
"Who are you?!" = "Невозможно определить личность командира!";
/* Menu title on OSX. [EDMarketConnector.py] */
"Window" = "Окно";
/* [EDMarketConnector.py] */
"You're not docked at a station!" = "Вы не пришвартованы к станции!";
"You're not docked at a station!" = "Ваше судно не пристыковано к станции!";
/* Shortcut settings prompt on OSX. [prefs.py] */
"{APP} needs permission to use shortcuts" = "{APP} нужно разрешение использовать глобальные сочетания клавиш";
"{APP} needs permission to use shortcuts" = "{APP} требуется разрешение на использование глобальных сочетаний клавиш";

View File

@ -132,7 +132,7 @@ def plugin_app(parent):
Once you have created your plugin and EDMC has loaded it there are three other functions you can define to be notified by EDMC when something happens: `journal_entry()`, `dashboard_entry()` and `cmdr_data()`.
Your events all get called on the main tkinter loop so be sure not to block for very long or the EDMC will appear to freeze. If you have a long running operation then you should take a look at how to do background updates in tkinter - http://effbot.org/zone/tkinter-threads.htm
Your events all get called on the main Tkinter loop so be sure not to block for very long or the app will appear to freeze. If you have a long running operation such as sending or receiving data from an external server then you should do this in a separate worker Thread. You can send work items to the worker thread over a Queue. Tkinter is not thread-safe so you should not access any Tkinter resources (including widgets and variables) from worker threads - doing so may cause the app to crash intermittently. You can signal back to the main thread using Tkinter's `event_generate()` widget method, generating a user-defined event that you have previously registered with the [`bind_all()`](http://effbot.org/tkinterbook/tkinter-events-and-bindings.htm) widget method. See the [EDSM plugin](https://github.com/Marginal/EDMarketConnector/blob/master/plugins/edsm.py) for an example of these techniques.
### Journal Entry
@ -279,3 +279,12 @@ EDMC now lets you disable a plugin without deleting it, simply rename the plugin
Disabled and enabled plugins are listed on the "Plugins" Settings tab
# Migration to Python 3.7
In a future release EDMC will use Python **3.7**, instead of the Python 2.7 it has historically used. As of mid-2020 there is a [python3 branch](https://github.com/EDCD/EDMarketConnector/tree/python3) to test migrated plugins against. This is a brief outline of the steps required to migrate a plugin from earlier versions of EDMC:
- Rename the function `plugin_start` to `plugin_start3(plugin_dir)`. Plugins without a `plugin_start3` function will be listed as disabled on EDMC's "Plugins" tab and a message like "plugin SuperSpaceHelper needs migrating" will appear in the log.
- Check that callback functions `plugin_prefs`, `prefs_changed`, `journal_entry`, `dashboard_entry` and `cmdr_data` if used are declared with the correct number of arguments. Older versions of this app were tolerant of missing arguments in these function declarations.
- Port the code to Python 3.7. The [2to3](https://docs.python.org/3/library/2to3.html) tool can automate much of this work.
Depending on the complexity of the plugin it may be feasible to make it compatible with both EDMC 3.4 + Python 2.7 and later EDMC + Python 3.7. [Here's](https://python-future.org/compatible_idioms.html) a guide on writing Python 2/3 compatible code and [here's](https://github.com/Marginal/HabZone/commit/3c41cd41d5ad81ef36aab40e967e3baf77b4bd06) an example of the changes required for a simple plugin.

View File

@ -1,3 +1,14 @@
**2020-06-16 21:00 UTC**
**Due to a lack of time to continue with maintenance at this point Marginal (also known as Otis on the Discord) has kindly transferred ownership of this github repository to the EDCD Organisation.**
**EDCD has some volunteers ready to take over maintenance, so over the next few days you can expect at least one fresh release to get things fully transferred (necessary to change the URL that EDMC uses to check for new versions). After that we'll work hard at getting all of the Pull Requests addressed before moving on to any other outstanding Issues.**
**Initially Athanasius will be the principle maintainer and releaser before passing the reins over to "LCU No Fool Like One" who has kindly agreed to take over as primary maintainer.**
**Any questions or further offers of help can be directed to the Discord #edmc channel as below.**
[![Discord chat](https://img.shields.io/discord/164411426939600896.svg?style=social&label=Discord%20chat)](https://discord.gg/usQ5e6n)
Elite: Dangerous Market Connector (EDMC)
@ -361,3 +372,16 @@ License
Copyright © 2015-2018 Jonathan Harris.
Licensed under the [GNU Public License (GPL)](http://www.gnu.org/licenses/gpl-2.0.html) version 2 or later.
**2020-06-16 21:00 UTC**
**Due to a lack of time to continue with maintenance at this point Marginal (also known as Otis on the Discord) has kindly transferred ownership of this github repository to the EDCD Organisation.**
**EDCD has some volunteers ready to take over maintenance, so over the next few days you can expect at least one fresh release to get things fully transferred (necessary to change the URL that EDMC uses to check for new versions). After that we'll work hard at getting all of the Pull Requests addressed before moving on to any other outstanding Issues.**
**Initially Athanasius will be the principle maintainer and releaser before passing the reins over to "LCU No Fool Like One" who has kindly agreed to take over as primary maintainer.**
**Any questions or further offers of help can be directed to the Discord #edmc channel as below.**
[![Discord chat](https://img.shields.io/discord/164411426939600896.svg?style=social&label=Discord%20chat)](https://discord.gg/usQ5e6n)

View File

@ -217,3 +217,4 @@ id,symbol,category,name
128924331,Alexandrite,Minerals,Alexandrite
128924332,Opal,Minerals,Void Opals
128924333,RockforthFertiliser,Chemicals,Rockforth Fertiliser
128924334,AgronomicTreatment,Chemicals,Agronomic Treatment

1 id symbol category name
217 128924331 Alexandrite Minerals Alexandrite
218 128924332 Opal Minerals Void Opals
219 128924333 RockforthFertiliser Chemicals Rockforth Fertiliser
220 128924334 AgronomicTreatment Chemicals Agronomic Treatment

View File

@ -23,7 +23,9 @@ holdoff = 60 # be nice
timeout = 10 # requests timeout
auth_timeout = 30 # timeout for initial auth
CLIENT_ID = os.getenv('CLIENT_ID') or '227cd239-ab8c-4728-9d3c-d8f588f247bd' # Obtain from https://auth.frontierstore.net/client/signup
# Currently the "Elite Dangerous Market Connector (EDCD/Athanasius)" one in
# Athanasius' Frontier account
CLIENT_ID = os.getenv('CLIENT_ID') or 'fb88d428-9110-475f-a3d2-dc151c2b9c7a' # Obtain from https://auth.frontierstore.net/client/signup
SERVER_AUTH = 'https://auth.frontierstore.net'
URL_AUTH = '/auth'
URL_TOKEN = '/token'

View File

@ -8,10 +8,10 @@ from sys import platform
appname = 'EDMarketConnector'
applongname = 'E:D Market Connector'
appcmdname = 'EDMC'
appversion = '3.4.4.0'
appversion = '3.4.6.0'
update_feed = 'https://raw.githubusercontent.com/EDCD/EDMarketConnector/releases/edmarketconnector.xml'
update_interval = 47*60*60
update_interval = 8*60*60
if platform=='darwin':

@ -1 +1 @@
Subproject commit cee78b312a862df6b8ef9c1eff077108f24a0b9e
Subproject commit bb1e5ba89acb940465714363b12c250ba399b5b9

148
docs/RELEASING.md Normal file
View File

@ -0,0 +1,148 @@
Introduction
===
This document aims to enable anyone to quickly get up to speed on how to:
1. Build a Windows .exe for the application
1. Package that .exe into an .msi file for distribution
1. Handle the files generated so the application automatically detects new available versions and asks the user to upgrade.
Note that for Windows only a 32-bit application is supported at this time. This is principally due to the Windows Registry handling in config.py.
Environment
---
You will need several pieces of software installed, or the files from their .zip archives, in order to build the .exe and generate the .msi
1. [WiX Toolset](https://wixtoolset.org/): 3.11.2 is the most recently tested version.
1. [WinSparkle](https://github.com/vslavik/winsparkle): `winsparkle.dll` and `winsparkle.pdb` from the release's .zip file. v0.7.0 is the most recently tested version. Copy the two files, found at `<zip file>\<version>\Release`, into your checkout of the EDMC git files.
1. [Windows SDK](https://developer.microsoft.com/en-US/windows/downloads/windows-10-sdk/). This is needed for the internationalisation support in EDMC. [Windows 10 SDK, version 1903 (10.0.18362.1)](https://go.microsoft.com/fwlink/?linkid=2083338) is the most recently tested version. Technically you only need the following components: `MSI Tools`, `Windows SDK for Desktop C++ x86 Apps` (which will auto-select some others). NB: If you have need to uninstall this it's "Windows Software Development Kit - Windows 10.0.18362.1" in "Apps & Features", *not* "Windows SDK AddOn".
1. [Python](https://python.org): 32-bit version of Python 3.7 for Windows. [v3.7.7](https://www.python.org/downloads/release/python-377/) is the most recently tested version. You need the `Windows x86 executable installer` file, for the 32-bit version.
1. You'll now need to 'pip install' several python modules
1. `pip install certifi==2019.9.11` (because a later version doesn't work with py2exe, causing cacert.pem to not be found)
1. `pip install requests`
1. `pip install watchdog`
1. [py2exe](https://github.com/albertosottile/py2exe):
1. Install the python module. There are two options here.
1. You can use the latest release version [0.9.3.2](https://github.com/albertosottile/py2exe/releases/tag/v0.9.3.2) and the current Marginal 'python3' branch as-is. This contains a small hack in `setup.py` to ensure `sqlite3.dll` is packaged.
pip install py2exe-0.9.3.2-cp37-none-win32.whl
1. Or you can use a pre-release version, [0.9.4.0](https://bintray.com/alby128/py2exe/download_file?file_path=py2exe-0.9.4.0-cp37-none-win32.whl), see [this py2exe issue](https://github.com/albertosottile/py2exe/issues/23#issuecomment-541359225), which packages that DLL file correctly.
pip install py2exe-0.9.4.0-cp37-none-win32.whl
You can then edit out the following line from `setup.py`, but it does no harm:
%s/DLLs/sqlite3.dll' % (sys.base_prefix),
1. `pip install keyring==19.2.0` (because newer tries to get importlib_metadata in a way that doesn't work)
As dependencies also get pulled in here's a list of what ends up in site-packages, and their versions:
cachetools-4.1.0.dist-info
certifi-2019.9.11.dist-info
chardet-3.0.4.dist-info
entrypoints-0.3.dist-info
future-0.18.2-py3.7.egg-info
keyring-19.2.0.dist-info
pathtools-0.1.2-py3.7.egg-info
pefile-2019.4.18-py3.7.egg-info
py2exe-0.9.4.0.dist-info
pywin32_ctypes-0.2.0.dist-info
requests-2.23.0.dist-info
setuptools-41.2.0.dist-info
urllib3-1.25.9.dist-info
watchdog-0.10.2-py3.7.egg-info
If you are using different versions of any of these tools then please ensure that the paths where they're installed match the associated lines in `setup.py`. i.e. if you're using later WiX you might need to edit the WIXPATH line, and likewise the SDKPATH line if you're using a later Windows SDK kit.
Necessary Edits
---
There are some things that you should always change before running your own version of EDMC
1. The Frontier CAPI client ID. This is hardcoded in companion.py, but can be overridden by setting a CLIENT_ID environment variable.
There are other things that you should probably change, but can get away with leaving at the upstream values, especially if you only you are going to use the resulting .exe and/or .msi files. **But** realise that the resulting program will still try to check for new versions at the main URL unless you change that.
1. Copyright and 'Company' texts. These are in `setup.py`. Search for `'copyright'` and `'company_name'`.
1. Location of release files. To change this edit `setup.py`. Look for the `appcast.write()` statement and change the `url="...` line.
1. Application names, version and URL the file with latest release information. These are all in the `config.py` file. See the `from config import ...` lines in setup.py.
1. appname: The short appname, e.g. 'EDMarketConnector'
1. applongname: The long appname, e.g. 'E:D Market Connector'
1. appcmdname: The CLI appname, e.g. 'EDMC'
1. appversion: The current version, e.g. '3.5.0.0'
1. update_feed: The URL where the application looks for current latest version information. This URL should be hosting a renamed (so the full URL doesn't change over application versions) version of the appcast_win_<version>.xml file. The original upstream value is `https://marginal.org.uk/edmarketconnector.xml`
Packaging & Installer Generation
---
You'll want to do the .exe and .msi generation in a `cmd.exe` window, not e.g. a 'Git bash' window.
Assuming the correct python.exe is associated with .py files then simply run:
setup.py py2exe
else you might need this, which assumes correct python.exe is in your PATH:
python.exe setup.py py2exe
else you'll have to specify the path to python.exe:
"%LOCALAPPDATA%\Programs\Python\Python37-32\python.exe" setup.py py2exe
Output will be something like (`...` denoting parts elided for brevity):
running py2exe
...
Building 'dist.win32\EDMC.exe'.
Building 'dist.win32\EDMarketConnector.exe'.
Building shared code archive 'dist.win32\library.zip'.
...
Windows Installer XML Toolset Compiler version 3.11.1.2318
Copyright (c) .NET Foundation and contributors. All rights reserved.
...
Package language = 1033,1029,1031,1034,1035,1036,1038,1040,1041,1043,1045,1046,1049,1058,1062,2052,2070,2074,0, ProductLanguage = 1029, Database codepage = 0
MsiTran V 5.0
Copyright (c) Microsoft Corporation. All Rights Reserved
...
DonePackage language = 1033,1029,1031,1034,1035,1036,1038,1040,1041,1043,1045,1046,1049,1058,1062,2052,2070,2074,0, ProductLanguage = 0, Database codepage = 0
MsiTran V 5.0
Copyright (c) Microsoft Corporation. All Rights Reserved
Done
You should now have one new/updated folder `dist.win32` and two new files (version number dependent): `EDMarketConnector_win_350.msi` and `appcast_win_350.xml`. If you want to just check the generated .exe files then they're in that `dist.win32` folder.
Now check that the `EDMarketConnector.exe` in the `dist.win32` folder does run without errors.
Finally, uninstall your current version of ED Market Connector and re-install using the newly generated .msi file. Check the resulting installation does work (the installer will run the program for you).
Distribution
---
It is recommended to keep all the files for distribution on github, including the 'update_feed' file. So once you have tested the new .msi file
1. So as to make backing out any mistakes easier create a new branch for this release, e.g. `release-350`. 'release' is in full so as not to clash with the tag `rel-350` which could cause confusion. Ensure all the relevant commits, and no more than them, are present in this branch. This branch will be local to only you, you shouldn't ever push it to GitHub unless asked to by another maintainer.
1. You should have already decided on the new version number, as it's specified in `config.py`. You'll need to redo the `.msi` build if you forgot. **Remember to do a fresh git commit for this change.**
1. Keep in mind that despite being specified as, e.g. '3.5.0.0' the `setup.py` code only takes note of the first 3 parts for deciding the release number. i.e. `3.5.0.1` results in the same `rel-350/EDMarketConnector_win_350.msi` as '3.5.0.0' does. Also the installer won't think that '3.5.0.1' is any newer than '3.5.0.0'.
1. Prepare a changelog text for the release. You'll need this both for the GitHub release and the contents of the `edmarketconnector.xml` file.
1. Update `edmarketconnector.xml` to add this changelog text to the correct section(s).
1. You'll need to change the `<title>` and `<description>` texts to reflect the latest version and the additional changelog.
1. Update the `url`, `sparkle:version` and `length` elements of the `<enclosure>` section as per the latest `appcast_win_<version>.xml` file generated by the build process.
1. **DO NOT git commit this change or push to github**. *We need to get the github release in place first before changing the file that running EDMC clients will check.*
1. Add a git tag for the release, which you'll refer to when actually creating the release:
1. The tag should match the `rel-XYZ` part of the URL in the `appcast_win_XYX.msi` file. e.g. `git tag -a rel-350`
1. Now merge this release-specific branch into the `releases` branch and push it to GitHub.
1. `git checkout releases`
1. `git merge release-350`
1. As you push the changes ensure the new tag is also pushed: `git push --tags origin` (perform the suggested `git push --set-upstream origin releases` if prompted to).
1. Craft a new github Release, using the new tag so as to reference the correct commit. Include the .msi file for Windows (the Source Code files are added by github based on the release tag). Use the changelog text you already prepared.
1. Check that the URL for the release that you specified in `edmarketconnector.xml` actually matches where github has placed the `.msi` file.
1. **NOW commit the latest `edmarketconnector.xml` changes to the `releases` branch and push to github.**
This is the step that fully publishes the release for running EDMC instances to pick up on 'Check for Updates'. Yes, this means that this step isn't included in the git tag for the release, but the alternative (with hosting the file via github raw URL) is to have a race condition where a running EDMC instance might check the file and see there's a new version *before that new version is actually available for download*.
1. `git checkout releases`
1. `git commit -m "Make release 350 live" edmarketconnector.xml`
1. `git push origin`

View File

@ -431,8 +431,8 @@ class EDLogs(FileSystemEventHandler):
self.station = None
self.stationtype = None
self.stationservices = None
elif entry['event'] in ['Location', 'FSDJump', 'Docked']:
if entry['event'] == 'Location':
elif entry['event'] in ['Location', 'FSDJump', 'Docked', 'CarrierJump']:
if entry['event'] in ('Location', 'CarrierJump'):
self.planet = entry.get('Body') if entry.get('BodyType') == 'Planet' else None
elif entry['event'] == 'FSDJump':
self.planet = None

View File

@ -278,7 +278,7 @@ fighter_rating_map = {
}
misc_internal_map = {
('detailedsurfacescanner', 'tiny') : ('Detailed Surface Scanner', 'C'),
('detailedsurfacescanner', 'tiny') : ('Detailed Surface Scanner', 'I'),
('dockingcomputer', 'advanced') : ('Advanced Docking Computer', 'E'),
('dockingcomputer', 'standard') : ('Standard Docking Computer', 'E'),
'planetapproachsuite' : ('Planetary Approach Suite', 'I'),

View File

@ -65,6 +65,7 @@ GuiFocusCodex = 11
# List of loaded Plugins
PLUGINS = []
PLUGINS_not_py3 = []
# For asynchronous error display
last_error = {
@ -195,6 +196,14 @@ def load_plugins(master):
print_exc()
PLUGINS.extend(sorted(found, key = lambda p: operator.attrgetter('name')(p).lower()))
#########################################################
# Detect plugins that aren't yet ready for Python 3.x
#########################################################
for p in PLUGINS:
if p.module and p.folder and not p._get_func('plugin_start3'):
PLUGINS_not_py3.append(p)
#########################################################
imp.release_lock()
def provides(fn_name):

View File

@ -396,8 +396,8 @@ def journal_entry(cmdr, is_beta, system, station, entry, state):
return filtered
# Track location
if entry['event'] in ['Location', 'FSDJump', 'Docked']:
if entry['event'] == 'Location':
if entry['event'] in ['Location', 'FSDJump', 'Docked', 'CarrierJump']:
if entry['event'] in ('Location', 'CarrierJump'):
this.planet = entry.get('Body') if entry.get('BodyType') == 'Planet' else None
elif entry['event'] == 'FSDJump':
this.planet = None
@ -413,10 +413,7 @@ def journal_entry(cmdr, is_beta, system, station, entry, state):
# Send interesting events to EDDN, but not when on a crew
if (config.getint('output') & config.OUT_SYS_EDDN and not state['Captain'] and
(entry['event'] == 'Location' or
entry['event'] == 'FSDJump' or
entry['event'] == 'Docked' or
entry['event'] == 'Scan') and
(entry['event'] in ('Location', 'FSDJump', 'Docked', 'Scan', 'SAASignalsFound', 'CarrierJump')) and
('StarPos' in entry or this.coordinates)):
# strip out properties disallowed by the schema
for thing in ['ActiveFine', 'CockpitBreach', 'BoostUsed', 'FuelLevel', 'FuelUsed', 'JumpDist', 'Latitude', 'Longitude', 'Wanted']:

View File

@ -298,7 +298,7 @@ def worker():
plug.show_error(_('Error: EDSM {MSG}').format(MSG=msg))
else:
for e, r in zip(pending, reply['events']):
if not closing and e['event'] in ['StartUp', 'Location', 'FSDJump']:
if not closing and e['event'] in ['StartUp', 'Location', 'FSDJump', 'CarrierJump']:
# Update main window's system status
this.lastlookup = r
this.system.event_generate('<<EDSMStatus>>', when="tail") # calls update_status in main thread
@ -345,13 +345,13 @@ def should_send(entries):
return False
# Call edsm_notify_system() in this and other interested plugins with EDSM's response to a 'StartUp', 'Location' or 'FSDJump' event
# Call edsm_notify_system() in this and other interested plugins with EDSM's response to a 'StartUp', 'Location', 'FSDJump' or 'CarrierJump' event
def update_status(event=None):
for plugin in plug.provides('edsm_notify_system'):
plug.invoke(plugin, None, 'edsm_notify_system', this.lastlookup)
# Called with EDSM's response to a 'StartUp', 'Location' or 'FSDJump' event. https://www.edsm.net/en/api-journal-v1
# Called with EDSM's response to a 'StartUp', 'Location', 'FSDJump' or 'CarrierJump' event. https://www.edsm.net/en/api-journal-v1
# msgnum: 1xx = OK, 2xx = fatal error, 3xx = error, 4xx = ignorable errors.
def edsm_notify_system(reply):
if not reply:

View File

@ -374,9 +374,29 @@ def journal_entry(cmdr, is_beta, system, station, entry, state):
[
OrderedDict([
('minorfactionName', f['Name']),
('minorfactionReputation', f['MyReputation']),
('minorfactionReputation', f['MyReputation']/100.0),
]) for f in entry['Factions']
])
elif entry['event'] == 'CarrierJump':
this.system = None
add_event('addCommanderTravelCarrierJump', entry['timestamp'],
OrderedDict([
('starsystemName', entry['StarSystem']),
('stationName', entry['StationName']),
('marketID', entry['MarketID']),
('shipType', state['ShipType']),
('shipGameID', state['ShipID']),
]))
if entry.get('Factions'):
add_event('setCommanderReputationMinorFaction', entry['timestamp'],
[
OrderedDict([
('minorfactionName', f['Name']),
('minorfactionReputation', f['MyReputation']/100.0),
]) for f in entry['Factions']
])
# Ignore the following 'Docked' event
this.suppress_docked = True
# Override standard URL functions
if config.get('system_provider') == 'Inara':
@ -613,20 +633,25 @@ def journal_entry(cmdr, is_beta, system, station, entry, state):
add_event('addCommanderCombatDeath', entry['timestamp'], data)
elif entry['event'] == 'Interdicted':
add_event('addCommanderCombatInterdicted', entry['timestamp'],
OrderedDict([('starsystemName', system),
('opponentName', entry['Interdictor']),
('isPlayer', entry['IsPlayer']),
('isSubmit', entry['Submitted']),
]))
data = OrderedDict([('starsystemName', system),
('isPlayer', entry['IsPlayer']),
('isSubmit', entry['Submitted']),
])
if 'Interdictor' in entry:
data['opponentName'] = entry['Interdictor']
elif 'Faction' in entry:
data['opponentName'] = entry['Faction']
elif 'Power' in entry:
data['opponentName'] = entry['Power']
add_event('addCommanderCombatInterdicted', entry['timestamp'], data)
elif entry['event'] == 'Interdiction':
data = OrderedDict([('starsystemName', system),
('isPlayer', entry['IsPlayer']),
('isSuccess', entry['Success']),
])
if 'Interdictor' in entry:
data['opponentName'] = entry['Interdictor']
if 'Interdicted' in entry:
data['opponentName'] = entry['Interdicted']
elif 'Faction' in entry:
data['opponentName'] = entry['Faction']
elif 'Power' in entry:
@ -821,7 +846,7 @@ def worker():
print 'Inara\t%s %s\t%s' % (reply_event['eventStatus'], reply_event.get('eventStatusText', ''), json.dumps(data_event))
if reply_event['eventStatus'] // 100 != 2:
plug.show_error(_('Error: Inara {MSG}').format(MSG = '%s, %s' % (data_event['eventName'], reply_event.get('eventStatusText', reply_event['eventStatus']))))
if data_event['eventName'] in ['addCommanderTravelDock', 'addCommanderTravelFSDJump', 'setCommanderTravelLocation']:
if data_event['eventName'] in ['addCommanderTravelCarrierJump', 'addCommanderTravelDock', 'addCommanderTravelFSDJump', 'setCommanderTravelLocation']:
this.lastlocation = reply_event.get('eventData', {})
this.system_link.event_generate('<<InaraLocation>>', when="tail") # calls update_location in main thread
elif data_event['eventName'] in ['addCommanderShip', 'setCommanderShip']:

View File

@ -184,6 +184,13 @@ class PreferencesDialog(tk.Toplevel):
self.hotkey_play_btn = nb.Checkbutton(configframe, text=_('Play sound'), variable=self.hotkey_play, state = self.hotkey_code and tk.NORMAL or tk.DISABLED) # Hotkey/Shortcut setting
self.hotkey_play_btn.grid(columnspan=4, padx=PADX, sticky=tk.W)
# Option to disabled Automatic Check For Updates whilst in-game
ttk.Separator(configframe, orient=tk.HORIZONTAL).grid(columnspan=4, padx=PADX, pady=PADY*4, sticky=tk.EW)
self.disable_autoappupdatecheckingame = tk.IntVar(value = config.getint('disable_autoappupdatecheckingame'))
self.disable_autoappupdatecheckingame_btn = nb.Checkbutton(configframe, text=_('Disable Automatic Application Updates Check when in-game'), variable=self.disable_autoappupdatecheckingame, command=self.disable_autoappupdatecheckingame_changed)
self.disable_autoappupdatecheckingame_btn.grid(columnspan=4, padx=PADX, sticky=tk.W)
ttk.Separator(configframe, orient=tk.HORIZONTAL).grid(columnspan=4, padx=PADX, pady=PADY*4, sticky=tk.EW)
nb.Label(configframe, text=_('Preferred websites')).grid(row=30, columnspan=4, padx=PADX, sticky=tk.W) # Settings prompt for preferred ship loadout, system and station info websites
@ -273,6 +280,20 @@ class PreferencesDialog(tk.Toplevel):
label = nb.Label(plugsframe, text='%s (%s)' % (plugin.folder, plugin.name))
label.grid(columnspan=2, padx=PADX*2, sticky=tk.W)
############################################################
# Show which plugins don't have Python 3.x support
############################################################
if len(plug.PLUGINS_not_py3):
ttk.Separator(plugsframe, orient=tk.HORIZONTAL).grid(columnspan=3, padx=PADX, pady=PADY * 8, sticky=tk.EW)
nb.Label(plugsframe, text=_('Plugins Without Python 3.x Support:')+':').grid(padx=PADX, sticky=tk.W)
for plugin in plug.PLUGINS_not_py3:
if plugin.folder: # 'system' ones have this set to None to suppress listing in Plugins prefs tab
nb.Label(plugsframe, text=plugin.name).grid(columnspan=2, padx=PADX*2, sticky=tk.W)
HyperlinkLabel(plugsframe, text=_('Information on migrating plugins'), background=nb.Label().cget('background'), url='https://github.com/EDCD/EDMarketConnector/blob/master/PLUGINS.md#migration-to-python-37', underline=True).grid(columnspan=2, padx=PADX, sticky=tk.W)
############################################################
disabled_plugins = [x for x in plug.PLUGINS if x.folder and not x.module]
if len(disabled_plugins):
ttk.Separator(plugsframe, orient=tk.HORIZONTAL).grid(columnspan=3, padx=PADX, pady=PADY * 8, sticky=tk.EW)
@ -419,6 +440,11 @@ class PreferencesDialog(tk.Toplevel):
self.logdir.set(config.default_journal_dir)
self.outvarchanged()
def disable_autoappupdatecheckingame_changed(self):
config.set('disable_autoappupdatecheckingame', self.disable_autoappupdatecheckingame.get())
# If it's now False, re-enable WinSparkle ? Need access to the AppWindow.updater variable to call down
def themecolorbrowse(self, index):
(rgb, color) = tkColorChooser.askcolor(self.theme_colors[index], title=self.theme_prompts[index], parent=self.parent)
if color:

123
theme.py
View File

@ -15,6 +15,8 @@ from ttkHyperlinkLabel import HyperlinkLabel
from config import appname, applongname, config
if __debug__:
from traceback import print_exc
if platform == 'win32':
import ctypes
@ -33,65 +35,66 @@ elif platform == 'linux2':
Atom = c_ulong
Display = c_void_p # Opaque
# Sending ClientMessage to WM using XSendEvent()
SubstructureNotifyMask = 1<<19
SubstructureRedirectMask = 1<<20
ClientMessage = 33
PropModeReplace = 0
PropModePrepend = 1
PropModeAppend = 2
_NET_WM_STATE_REMOVE = 0
_NET_WM_STATE_ADD = 1
_NET_WM_STATE_TOGGLE = 2
# From xprops.h
MWM_HINTS_FUNCTIONS = 1 << 0
MWM_HINTS_DECORATIONS = 1 << 1
MWM_HINTS_INPUT_MODE = 1 << 2
MWM_HINTS_STATUS = 1 << 3
MWM_FUNC_ALL = 1 << 0
MWM_FUNC_RESIZE = 1 << 1
MWM_FUNC_MOVE = 1 << 2
MWM_FUNC_MINIMIZE = 1 << 3
MWM_FUNC_MAXIMIZE = 1 << 4
MWM_FUNC_CLOSE = 1 << 5
MWM_DECOR_ALL = 1 << 0
MWM_DECOR_BORDER = 1 << 1
MWM_DECOR_RESIZEH = 1 << 2
MWM_DECOR_TITLE = 1 << 3
MWM_DECOR_MENU = 1 << 4
MWM_DECOR_MINIMIZE = 1 << 5
MWM_DECOR_MAXIMIZE = 1 << 6
class XClientMessageEvent_data(Union):
class MotifWmHints(Structure):
_fields_ = [
('b', c_char * 20),
('s', c_short * 10),
('l', c_long * 5),
('flags', c_ulong),
('functions', c_ulong),
('decorations', c_ulong),
('input_mode', c_long),
('status', c_ulong),
]
class XClientMessageEvent(Structure):
_fields_ = [
('type', c_int),
('serial', c_ulong),
('send_event', c_int),
('display', POINTER(Display)),
('window', Window),
('message_type', Atom),
('format', c_int),
('data', XClientMessageEvent_data),
]
class XEvent(Union):
_fields_ = [
('xclient', XClientMessageEvent),
]
xlib = cdll.LoadLibrary('libX11.so.6')
XFlush = xlib.XFlush
XFlush.argtypes = [POINTER(Display)]
XFlush.restype = c_int
XInternAtom = xlib.XInternAtom
XInternAtom.restype = Atom
XInternAtom.argtypes = [POINTER(Display), c_char_p, c_int]
XOpenDisplay = xlib.XOpenDisplay
XOpenDisplay.argtypes = [c_char_p]
XOpenDisplay.restype = POINTER(Display)
XQueryTree = xlib.XQueryTree
XQueryTree.argtypes = [POINTER(Display), Window, POINTER(Window), POINTER(Window), POINTER(Window), POINTER(c_uint)]
XQueryTree.restype = c_int
XSendEvent = xlib.XSendEvent
XSendEvent.argtypes = [POINTER(Display), Window, c_int, c_long, POINTER(XEvent)]
XSendEvent.restype = c_int
try:
xlib = cdll.LoadLibrary('libX11.so.6')
XInternAtom = xlib.XInternAtom
XInternAtom.argtypes = [POINTER(Display), c_char_p, c_int]
XInternAtom.restype = Atom
XChangeProperty = xlib.XChangeProperty
XChangeProperty.argtypes = [POINTER(Display), Window, Atom, Atom, c_int, c_int, POINTER(MotifWmHints), c_int]
XChangeProperty.restype = c_int
XFlush = xlib.XFlush
XFlush.argtypes = [POINTER(Display)]
XFlush.restype = c_int
XOpenDisplay = xlib.XOpenDisplay
XOpenDisplay.argtypes = [c_char_p]
XOpenDisplay.restype = POINTER(Display)
XQueryTree = xlib.XQueryTree
XQueryTree.argtypes = [POINTER(Display), Window, POINTER(Window), POINTER(Window), POINTER(Window), POINTER(c_uint)]
XQueryTree.restype = c_int
dpy = xlib.XOpenDisplay(None)
XA_ATOM = Atom(4)
net_wm_state = XInternAtom(dpy, '_NET_WM_STATE', False)
net_wm_state_above = XInternAtom(dpy, '_NET_WM_STATE_ABOVE', False)
net_wm_state_sticky = XInternAtom(dpy, '_NET_WM_STATE_STICKY', False)
net_wm_state_skip_pager = XInternAtom(dpy, '_NET_WM_STATE_SKIP_PAGER', False)
net_wm_state_skip_taskbar = XInternAtom(dpy, '_NET_WM_STATE_SKIP_TASKBAR', False)
motif_wm_hints_property = XInternAtom(dpy, b'_MOTIF_WM_HINTS', False)
motif_wm_hints_normal = MotifWmHints(MWM_HINTS_FUNCTIONS | MWM_HINTS_DECORATIONS,
MWM_FUNC_RESIZE | MWM_FUNC_MOVE | MWM_FUNC_MINIMIZE | MWM_FUNC_CLOSE,
MWM_DECOR_BORDER | MWM_DECOR_RESIZEH | MWM_DECOR_TITLE | MWM_DECOR_MENU | MWM_DECOR_MINIMIZE,
0, 0)
motif_wm_hints_dark = MotifWmHints(MWM_HINTS_FUNCTIONS | MWM_HINTS_DECORATIONS,
MWM_FUNC_RESIZE | MWM_FUNC_MOVE | MWM_FUNC_MINIMIZE | MWM_FUNC_CLOSE,
0, 0, 0)
except:
if __debug__: print_exc()
dpy = None
@ -356,25 +359,19 @@ class _Theme:
else:
root.withdraw()
# https://www.tcl-lang.org/man/tcl/TkCmd/wm.htm#M19
# https://specifications.freedesktop.org/wm-spec/wm-spec-latest.html#STACKINGORDER
root.attributes('-type', theme and 'splash' or 'normal')
root.update_idletasks() # Size gets recalculated here
root.deiconify()
root.wait_visibility() # need main window to be displayed before returning
if dpy and theme:
# Try to display in the taskbar
if dpy:
xroot = Window()
parent = Window()
children = Window()
nchildren = c_uint()
XQueryTree(dpy, root.winfo_id(), byref(xroot), byref(parent), byref(children), byref(nchildren))
# https://specifications.freedesktop.org/wm-spec/wm-spec-latest.html#idm140200472615568
xevent = XEvent(xclient = XClientMessageEvent(ClientMessage, 0, 0, None, parent, net_wm_state, 32, XClientMessageEvent_data(l = (_NET_WM_STATE_REMOVE, net_wm_state_skip_pager, net_wm_state_skip_taskbar, 1, 0))))
XSendEvent(dpy, xroot, False, SubstructureRedirectMask | SubstructureNotifyMask, byref(xevent))
xevent = XEvent(xclient = XClientMessageEvent(ClientMessage, 0, 0, None, parent, net_wm_state, 32, XClientMessageEvent_data(l = (_NET_WM_STATE_REMOVE, net_wm_state_sticky, 0, 1, 0))))
XSendEvent(dpy, xroot, False, SubstructureRedirectMask | SubstructureNotifyMask, byref(xevent))
XChangeProperty(dpy, parent, motif_wm_hints_property, motif_wm_hints_property, 32, PropModeReplace, theme and motif_wm_hints_dark or motif_wm_hints_normal, 5)
XFlush(dpy)
else:
root.overrideredirect(theme and 1 or 0)
root.deiconify()
root.wait_visibility() # need main window to be displayed before returning
if not self.minwidth:
self.minwidth = root.winfo_width() # Minimum width = width on first creation

View File

@ -19,6 +19,9 @@ if not getattr(sys, 'frozen', False):
def __init__(self, master):
self.root = master
def setAutomaticUpdatesCheck(self, onoroff):
return
def checkForUpdates(self):
thread = threading.Thread(target = self.worker, name = 'update worker')
thread.daemon = True
@ -56,6 +59,10 @@ elif sys.platform=='darwin':
# can't load framework - not frozen or not included in app bundle?
self.updater = None
def setAutomaticUpdatesCheck(self, onoroff):
if self.updater:
self.updater.win_sparkle_set_automatic_check_for_updates(onoroff)
def checkForUpdates(self):
if self.updater:
self.updater.checkForUpdates_(None)
@ -98,6 +105,10 @@ elif sys.platform=='win32':
print_exc()
self.updater = None
def setAutomaticUpdatesCheck(self, onoroff):
if self.updater:
self.updater.win_sparkle_set_automatic_check_for_updates(onoroff)
def checkForUpdates(self):
if self.updater:
self.updater.win_sparkle_check_update_with_ui()