tradedangerous 12.7.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. py.typed +1 -0
  2. trade.py +49 -0
  3. tradedangerous/__init__.py +43 -0
  4. tradedangerous/cache.py +1381 -0
  5. tradedangerous/cli.py +136 -0
  6. tradedangerous/commands/TEMPLATE.py +74 -0
  7. tradedangerous/commands/__init__.py +244 -0
  8. tradedangerous/commands/buildcache_cmd.py +102 -0
  9. tradedangerous/commands/buy_cmd.py +427 -0
  10. tradedangerous/commands/commandenv.py +372 -0
  11. tradedangerous/commands/exceptions.py +94 -0
  12. tradedangerous/commands/export_cmd.py +150 -0
  13. tradedangerous/commands/import_cmd.py +222 -0
  14. tradedangerous/commands/local_cmd.py +243 -0
  15. tradedangerous/commands/market_cmd.py +207 -0
  16. tradedangerous/commands/nav_cmd.py +252 -0
  17. tradedangerous/commands/olddata_cmd.py +270 -0
  18. tradedangerous/commands/parsing.py +221 -0
  19. tradedangerous/commands/rares_cmd.py +298 -0
  20. tradedangerous/commands/run_cmd.py +1521 -0
  21. tradedangerous/commands/sell_cmd.py +262 -0
  22. tradedangerous/commands/shipvendor_cmd.py +60 -0
  23. tradedangerous/commands/station_cmd.py +68 -0
  24. tradedangerous/commands/trade_cmd.py +181 -0
  25. tradedangerous/commands/update_cmd.py +67 -0
  26. tradedangerous/corrections.py +55 -0
  27. tradedangerous/csvexport.py +234 -0
  28. tradedangerous/db/__init__.py +27 -0
  29. tradedangerous/db/adapter.py +192 -0
  30. tradedangerous/db/config.py +107 -0
  31. tradedangerous/db/engine.py +259 -0
  32. tradedangerous/db/lifecycle.py +332 -0
  33. tradedangerous/db/locks.py +208 -0
  34. tradedangerous/db/orm_models.py +500 -0
  35. tradedangerous/db/paths.py +113 -0
  36. tradedangerous/db/utils.py +661 -0
  37. tradedangerous/edscupdate.py +565 -0
  38. tradedangerous/edsmupdate.py +474 -0
  39. tradedangerous/formatting.py +210 -0
  40. tradedangerous/fs.py +156 -0
  41. tradedangerous/gui.py +1146 -0
  42. tradedangerous/mapping.py +133 -0
  43. tradedangerous/mfd/__init__.py +103 -0
  44. tradedangerous/mfd/saitek/__init__.py +3 -0
  45. tradedangerous/mfd/saitek/directoutput.py +678 -0
  46. tradedangerous/mfd/saitek/x52pro.py +195 -0
  47. tradedangerous/misc/checkpricebounds.py +287 -0
  48. tradedangerous/misc/clipboard.py +49 -0
  49. tradedangerous/misc/coord64.py +83 -0
  50. tradedangerous/misc/csvdialect.py +57 -0
  51. tradedangerous/misc/derp-sentinel.py +35 -0
  52. tradedangerous/misc/diff-system-csvs.py +159 -0
  53. tradedangerous/misc/eddb.py +81 -0
  54. tradedangerous/misc/eddn.py +349 -0
  55. tradedangerous/misc/edsc.py +437 -0
  56. tradedangerous/misc/edsm.py +121 -0
  57. tradedangerous/misc/importeddbstats.py +54 -0
  58. tradedangerous/misc/prices-json-exp.py +179 -0
  59. tradedangerous/misc/progress.py +194 -0
  60. tradedangerous/plugins/__init__.py +249 -0
  61. tradedangerous/plugins/edcd_plug.py +371 -0
  62. tradedangerous/plugins/eddblink_plug.py +861 -0
  63. tradedangerous/plugins/edmc_batch_plug.py +133 -0
  64. tradedangerous/plugins/spansh_plug.py +2647 -0
  65. tradedangerous/prices.py +211 -0
  66. tradedangerous/submit-distances.py +422 -0
  67. tradedangerous/templates/Added.csv +37 -0
  68. tradedangerous/templates/Category.csv +17 -0
  69. tradedangerous/templates/RareItem.csv +143 -0
  70. tradedangerous/templates/TradeDangerous.sql +338 -0
  71. tradedangerous/tools.py +40 -0
  72. tradedangerous/tradecalc.py +1302 -0
  73. tradedangerous/tradedb.py +2320 -0
  74. tradedangerous/tradeenv.py +313 -0
  75. tradedangerous/tradeenv.pyi +109 -0
  76. tradedangerous/tradeexcept.py +131 -0
  77. tradedangerous/tradeorm.py +183 -0
  78. tradedangerous/transfers.py +192 -0
  79. tradedangerous/utils.py +243 -0
  80. tradedangerous/version.py +16 -0
  81. tradedangerous-12.7.6.dist-info/METADATA +106 -0
  82. tradedangerous-12.7.6.dist-info/RECORD +87 -0
  83. tradedangerous-12.7.6.dist-info/WHEEL +5 -0
  84. tradedangerous-12.7.6.dist-info/entry_points.txt +3 -0
  85. tradedangerous-12.7.6.dist-info/licenses/LICENSE +373 -0
  86. tradedangerous-12.7.6.dist-info/top_level.txt +2 -0
  87. tradegui.py +24 -0
@@ -0,0 +1,678 @@
1
+ """
2
+ DirectOutput.py - Saitek DirectOutput.dll Python Wrapper
3
+
4
+ Version: 0.3
5
+ Author: Oliver "kfsone" Smith <oliver@kfs.org> 2014
6
+ Original Author: Frazzle
7
+
8
+ Description: Python wrapper class for DirectOutput functions.
9
+
10
+ This module consists of two classes - DirectOutput and DirectOutputDevice
11
+
12
+ DirectOutput directly calls C functions within DirectOutput.dll to allow Python control of the Saitek X52 Pro MFD and LEDs. Implemented as a class to allow sharing of dll object amongst functions
13
+
14
+ DirectOutputDevice is a wrapper around DirectOutput which automates setup and persists the device handle across functions. This class can be directly called or inherited to control an individual device (eg. X52 Pro)
15
+
16
+ Thanks to Spksh and ellF for the C# version of the wrapper which was very helpful in implementing this.
17
+ Thanks to Frazzle for the first Python version (no-longer compatible with the saitek driver).
18
+
19
+ Example Usage:
20
+
21
+ device = DirectOutputDevice()
22
+ device.AddPage(0, "Test", True)
23
+ device.SetString(0, 0, "Test String")
24
+
25
+ import time, sys
26
+ while True:
27
+ try:
28
+ time.sleep(1)
29
+ except:
30
+ #This is used to catch Ctrl+C, calling finish method is *very* important to de-initalize device.
31
+ device.finish()
32
+ sys.exit()
33
+
34
+ Saitek appear to periodically change the DLLs functions. At time of writing,
35
+ these are the functions listed in the DLL:
36
+
37
+ DirectOutput_Initialize
38
+ DirectOutput_Deinitialize
39
+ DirectOutput_AddPage
40
+ DirectOutput_DeleteFile
41
+ DirectOutput_DisplayFile
42
+ DirectOutput_Enumerate
43
+ DirectOutput_GetDeviceInstance
44
+ DirectOutput_GetDeviceType
45
+ DirectOutput_RegisterDeviceCallback
46
+ DirectOutput_RegisterPageCallback
47
+ DirectOutput_RegisterSoftButtonCallback
48
+ DirectOutput_RemovePage
49
+ DirectOutput_SaveFile
50
+ DirectOutput_SendServerFile
51
+ DirectOutput_SendServerMsg
52
+ DirectOutput_SetImage
53
+ DirectOutput_SetImageFromFile
54
+ DirectOutput_SetLed
55
+ DirectOutput_SetProfile
56
+ DirectOutput_SetString
57
+
58
+ DirectOutput_StartServer
59
+ DirectOutput_CloseServer
60
+
61
+ """
62
+
63
+ from tradedangerous.mfd import MissingDeviceError
64
+
65
+ import ctypes
66
+ import ctypes.wintypes
67
+ import logging
68
+ import os
69
+ import platform
70
+ import sys
71
+ import time
72
+
73
+ S_OK = 0x00000000
74
+ E_HANDLE = 0x80070006
75
+ E_INVALIDARG = 0x80070057
76
+ E_OUTOFMEMORY = 0x8007000E
77
+ E_PAGENOTACTIVE = -0xfbffff # Something munges it from it's actual value
78
+ E_BUFFERTOOSMALL = -0xfc0000
79
+ E_NOTIMPL = 0x80004001
80
+ ERROR_DEV_NOT_EXIST = 55
81
+
82
+ SOFTBUTTON_SELECT = 0x00000001
83
+ SOFTBUTTON_UP = 0x00000002
84
+ SOFTBUTTON_DOWN = 0x00000004
85
+
86
+
87
+ class DirectOutput:
88
+ def __init__(self, dll_path):
89
+ """
90
+ Creates python object to interact with DirecOutput.dll
91
+
92
+ Required Arguments:
93
+ dll_path -- String containing DirectOutput.dll location.
94
+
95
+ """
96
+ logging.debug("DirectOutput.__init__")
97
+ self.DirectOutputDLL = ctypes.WinDLL(dll_path)
98
+
99
+ def Initialize(self, application_name):
100
+ """
101
+ Function to call DirectOutput_Initialize
102
+
103
+ Required Arguments:
104
+ application_name -- String representing name of applicaiton - must be unique per-application
105
+
106
+ Returns:
107
+ S_OK: The call completed sucesfully
108
+ E_OUTOFMEMORY: There was insufficient memory to complete this call.
109
+ E_INVALIDARG: The argument is invalid
110
+ E_HANDLE: The DirectOutputManager process could not be found
111
+
112
+ """
113
+ logging.debug("DirectOutput.Initialize")
114
+ return self.DirectOutputDLL.DirectOutput_Initialize(ctypes.wintypes.LPWSTR(application_name))
115
+
116
+ def Deinitialize(self):
117
+ """
118
+ Direct function call to DirectOutput_Deinitialize
119
+
120
+ Returns:
121
+ S_OK: The call completed successfully.
122
+ E_HANDLE: DirectOutput was not initialized or was already deinitialized.
123
+ """
124
+ logging.debug("DirectOutput.Deinitialize")
125
+ return self.DirectOutputDLL.DirectOutput_Deinitialize()
126
+
127
+ def RegisterDeviceCallback(self, function):
128
+ """
129
+ Registers self.DeviceCallback to be called when devices get registered
130
+
131
+ Required Arugments:
132
+ function -- Function to call when a device registers
133
+
134
+ Returns:
135
+ S_OK: The call completed successfully
136
+ E_HANDLE: DirectOutput was not initialized.
137
+
138
+ """
139
+ logging.debug("DirectOutput.RegisterDeviceCallback")
140
+ return self.DirectOutputDLL.DirectOutput_RegisterDeviceCallback(function, 0)
141
+
142
+ def Enumerate(self, function):
143
+ """
144
+ Direct call to DirectOutput_Enumerate
145
+
146
+ Returns:
147
+ S_OK: The call completed successfully
148
+ E_HANDLE: DirectOutput was not initialized.
149
+
150
+ """
151
+ logging.debug("DirectOutput.Enumerate")
152
+ return self.DirectOutputDLL.DirectOutput_Enumerate(function, 0)
153
+
154
+
155
+ def RegisterSoftButtonCallback(self, device_handle, function):
156
+ """
157
+ Registers a function to be called when a soft button changes
158
+
159
+ Required Arugments:
160
+ device_handle -- ID of device
161
+ function -- Function to call when a soft button changes
162
+
163
+ Returns:
164
+ S_OK: The call completed successfully.
165
+ E_HANDLE: The device handle specified is invalid.
166
+ """
167
+
168
+ logging.debug("DirectOutput.RegisterSoftButtonCallback({}, {})".format(device_handle, function))
169
+ return self.DirectOutputDLL.DirectOutput_RegisterSoftButtonCallback(ctypes.wintypes.HANDLE(device_handle), function, 0)
170
+
171
+ def RegisterPageCallback(self, device_handle, function):
172
+ """
173
+ Registers a function to be called when page changes
174
+
175
+ Required Arugments:
176
+ device_handle -- ID of device
177
+ function -- Function to call when a page changes
178
+
179
+ Returns:
180
+ S_OK: The call completed successfully.
181
+ E_HANDLE: The device handle specified is invalid.
182
+ """
183
+ logging.debug("DirectOutput.RegisterPageCallback({}, {})".format(device_handle,function))
184
+ return self.DirectOutputDLL.DirectOutput_RegisterPageCallback(ctypes.wintypes.HANDLE(device_handle), function, 0)
185
+
186
+ def SetProfile(self, device_handle, profile):
187
+ """
188
+ Sets the profile used on the device.
189
+
190
+ Required Arguments:
191
+ device_handle -- ID of device
192
+ profile -- full path of the profile to activate. passing None will clear the profile.
193
+ """
194
+ logging.debug("DirectOutput.SetProfile({}, {})".format(device_handle, profile))
195
+ if profile:
196
+ return self.DirectOutputDLL.DirectOutput_SetProfile(ctypes.wintypes.HANDLE(device_handle), len(profile), ctypes.wintypes.LPWSTR(profile))
197
+ else:
198
+ return self.DirectOutputDLL.DirectOutput_SetProfile(ctypes.wintypes.HANDLE(device_handle), 0, 0)
199
+
200
+ def AddPage(self, device_handle, page, name, active):
201
+ """
202
+ Adds a page to the MFD
203
+
204
+ Required Arguments:
205
+ device_handle -- ID of device
206
+ page -- page ID to add
207
+ name -- String specifying page name
208
+ active -- True if page is to become the active page, if False this will not change the active page
209
+
210
+ Returns:
211
+ S_OK: The call completed successfully.
212
+ E_OUTOFMEMORY: Insufficient memory to complete the request.
213
+ E_INVALIDARG: The dwPage parameter already exists.
214
+ E_HANDLE: The device handle specified is invalid.
215
+
216
+ """
217
+ logging.debug("DirectOutput.AddPage({}, {}, {}, {})".format(device_handle, page, name, active))
218
+ return self.DirectOutputDLL.DirectOutput_AddPage(ctypes.wintypes.HANDLE(device_handle), page, active)
219
+
220
+ def RemovePage(self, device_handle, page):
221
+ """
222
+ Removes a page from the MFD
223
+
224
+ Required Arguments:
225
+ device_handle -- ID of device
226
+ page -- page ID to remove
227
+
228
+ Returns:
229
+ S_OK: The call completed successfully.
230
+ E_INVALIDARG: The dwPage argument does not reference a valid page id.
231
+ E_HANDLE: The device handle specified is invalid.
232
+
233
+ """
234
+ logging.debug("DirectOutput.RemovePage({}, {})".format(device_handle, page))
235
+ return self.DirectOutputDLL.DirectOutput_RemovePage(ctypes.wintypes.HANDLE(device_handle), page)
236
+
237
+ def SetLed(self, device_handle, page, led, value):
238
+ """
239
+ Sets LED state on a given page
240
+
241
+ Required Arguments:
242
+ device_handle -- ID of device
243
+ page -- page number
244
+ led -- ID of LED to change
245
+ value -- value to set LED (1 = on, 0 = off)
246
+
247
+ Returns:
248
+ S_OK: The call completes successfully.
249
+ E_INVALIDARG: The dwPage argument does not reference a valid page id, or the dwLed argument does not specifiy a valid LED id.
250
+ E_HANDLE: The device handle specified is invalid
251
+
252
+ """
253
+ logging.debug("DirectOutput.SetLed({}, {}, {}, {})".format(device_handle, page, led, value))
254
+ return self.DirectOutputDLL.DirectOutput_SetLed(ctypes.wintypes.HANDLE(device_handle), page, led, value)
255
+
256
+
257
+ def SetString(self, device_handle, page, line, string):
258
+ """
259
+ Sets a string to display on the MFD
260
+
261
+ Required Arguments:
262
+ device_handle -- ID of device
263
+ page -- the ID of the page to add the string to
264
+ line -- the line to display the string on (0 = top, 1 = middle, 2 = bottom)
265
+ string -- the string to display
266
+
267
+ Returns:
268
+ S_OK: The call completes successfully.
269
+ E_INVALIDARG: The dwPage argument does not reference a valid page id, or the dwString argument does not reference a valid string id.
270
+ E_OUTOFMEMORY: Insufficient memory to complete the request.
271
+ E_HANDLE: The device handle specified is invalid.
272
+
273
+ """
274
+ logging.debug("DirectOutput.SetString({}, {}, {}, {})".format(device_handle, page, line, string))
275
+ return self.DirectOutputDLL.DirectOutput_SetString(ctypes.wintypes.HANDLE(device_handle), page, line, len(string), ctypes.wintypes.LPWSTR(string))
276
+
277
+
278
+ class DirectOutputDevice:
279
+
280
+ class Buttons:
281
+
282
+ select, up, down = False, False, False
283
+
284
+ def __init__(self, bitmask):
285
+ self.bitmask = bitmask
286
+ if bitmask == 1:
287
+ self.select = True
288
+ elif bitmask == 2:
289
+ self.up = True
290
+ elif bitmask == 3:
291
+ self.up = True
292
+ self.select = True
293
+ elif bitmask == 4:
294
+ self.down = True
295
+ elif bitmask == 5:
296
+ self.down = True
297
+ self.select = True
298
+ elif bitmask == 6:
299
+ self.up = True
300
+ self.down = True
301
+ elif bitmask == 7:
302
+ self.up = True
303
+ self.down = True
304
+ self.select = True
305
+
306
+ def __repr__(self):
307
+ return "Select: "+str(self.select)+" Up: "+str(self.up)+" Down: "+str(self.down)
308
+
309
+ application_name = "GenericDevice"
310
+ device_handle = None
311
+ direct_output = None
312
+ debug_level = 0
313
+
314
+ def __init__(self, debug_level=0, name=None):
315
+ """
316
+ Initialises device, creates internal state (device_handle) and registers callbacks.
317
+
318
+ """
319
+
320
+ logging.info("DirectOutputDevice.__init__")
321
+
322
+ prog_dir = os.environ["ProgramFiles"]
323
+ if platform.machine().endswith('86'):
324
+ # 32-bit machine, nothing to worry about
325
+ pass
326
+ elif platform.machine().endswith('64'):
327
+ # 64-bit machine, are we a 32-bit python?
328
+ if platform.architecture()[0] == '32bit':
329
+ prog_dir = os.environ["ProgramFiles(x86)"]
330
+ dll_path = os.path.join(prog_dir, "Logitech\\DirectOutput\\DirectOutput.dll")
331
+
332
+ self.application_name = name or DirectOutputDevice.application_name
333
+ self.debug_level = debug_level
334
+
335
+ try:
336
+ logging.debug("DirectOutputDevice -> DirectOutput: {}".format(dll_path))
337
+ self.direct_output = DirectOutput(dll_path)
338
+ logging.debug("direct_output = {}".format(self.direct_output))
339
+ except WindowsError as e:
340
+ logging.warning("DLLError: {}: {}".format(dll_path, e.winerror))
341
+ raise DLLError(e.winerror) from None
342
+
343
+ result = self.direct_output.Initialize(self.application_name)
344
+ if result != S_OK:
345
+ logging.warning("direct_output.Initialize returned {}".format(result))
346
+ raise DirectOutputError(result)
347
+
348
+ logging.info("Creating callback closures.")
349
+ self.onDevice_closure = self._OnDeviceClosure()
350
+ logging.debug("onDevice_closure is {}".format(self.onDevice_closure))
351
+ self.onEnumerate_closure = self._OnEnumerateClosure()
352
+ logging.debug("onEnumerate_closure is {}".format(self.onEnumerate_closure))
353
+ self.onPage_closure = self._OnPageClosure()
354
+ logging.debug("onPage_closure is {}".format(self.onPage_closure))
355
+ self.onSoftButton_closure = self._OnSoftButtonClosure()
356
+ logging.debug("onSoftButton_closure is {}".format(self.onSoftButton_closure))
357
+
358
+ result = self.direct_output.RegisterDeviceCallback(self.onDevice_closure)
359
+ if result != S_OK:
360
+ logging.warning("RegisterDeviceCallback failed: {}".format(result))
361
+ self.finish()
362
+ raise DirectOutputError(result)
363
+
364
+ result = self.direct_output.Enumerate(self.onEnumerate_closure)
365
+ if result != S_OK:
366
+ logging.warning("Enumerate failed: {}".format(result))
367
+ self.finish()
368
+ raise DirectOutputError(result)
369
+
370
+ if not self.device_handle:
371
+ logging.warning("No device handle")
372
+ self.finish()
373
+ raise MissingDeviceError()
374
+
375
+ result = self.direct_output.RegisterSoftButtonCallback(self.device_handle, self.onSoftButton_closure)
376
+ if result != S_OK:
377
+ logging.warning("RegisterSoftButtonCallback failed")
378
+ self.finish()
379
+ raise DirectOutputError(result)
380
+
381
+ result = self.direct_output.RegisterPageCallback(self.device_handle, self.onPage_closure)
382
+ if result != S_OK:
383
+ logging.warning("RegisterPageCallback failed")
384
+ self.finish()
385
+ raise DirectOutputError(result)
386
+
387
+
388
+ def __del__(self, *args, **kwargs):
389
+ logging.debug("DirectOutputDevice.__del__")
390
+ self.finish()
391
+
392
+
393
+ def finish(self):
394
+ """
395
+ De-initializes DLL. Must be called before program exit
396
+
397
+ """
398
+ if self.direct_output:
399
+ logging.info("DirectOutputDevice deinitializing")
400
+ self.direct_output.Deinitialize()
401
+ self.direct_output = None
402
+ else:
403
+ logging.debug("nothing to do in finish()")
404
+
405
+
406
+ def _OnDeviceClosure(self):
407
+ """
408
+ Returns a pointer to function that calls self._OnDevice method. This allows class methods to be called from within DirectOutput.dll
409
+
410
+ http://stackoverflow.com/questions/7259794/how-can-i-get-methods-to-work-as-callbacks-with-python-ctypes
411
+
412
+ """
413
+ OnDevice_Proto = ctypes.WINFUNCTYPE(None, ctypes.c_void_p, ctypes.c_bool, ctypes.c_void_p)
414
+
415
+ def func(hDevice, bAdded, pvContext):
416
+ logging.info("device callback closure func: {}, {}, {}".format(hDevice, bAdded, pvContext))
417
+ self._OnDevice(hDevice, bAdded, pvContext)
418
+
419
+ return OnDevice_Proto(func)
420
+
421
+
422
+ def _OnEnumerateClosure(self):
423
+ """
424
+ Returns a pointer to function that calls self._OnEnumerate method. This allows class methods to be called from within DirectOutput.dll
425
+
426
+ http://stackoverflow.com/questions/7259794/how-can-i-get-methods-to-work-as-callbacks-with-python-ctypes
427
+
428
+ """
429
+ OnEnumerate_Proto = ctypes.WINFUNCTYPE(None, ctypes.c_void_p, ctypes.c_void_p)
430
+
431
+ def func(hDevice, pvContext):
432
+ logging.info("enumerate callback closure func: {}, {}".format(hDevice, pvContext))
433
+ self._OnEnumerate(hDevice, pvContext)
434
+
435
+ return OnEnumerate_Proto(func)
436
+
437
+
438
+ def _OnPageClosure(self):
439
+ """
440
+ Returns a pointer to function that calls self._OnPage method. This allows class methods to be called from within DirectOutput.dll
441
+
442
+ http://stackoverflow.com/questions/7259794/how-can-i-get-methods-to-work-as-callbacks-with-python-ctypes
443
+
444
+ """
445
+ OnPage_Proto = ctypes.WINFUNCTYPE(None, ctypes.c_void_p, ctypes.wintypes.DWORD, ctypes.c_bool, ctypes.c_void_p)
446
+
447
+ def func(hDevice, dwPage, bActivated, pvContext):
448
+ logging.info("page callback closure: {}, {}, {}, {}".format(hDevice, dwPage, bActivated, pvContext))
449
+ self._OnPage(hDevice, dwPage, bActivated, pvContext)
450
+
451
+ return OnPage_Proto(func)
452
+
453
+
454
+ def _OnSoftButtonClosure(self):
455
+ """
456
+ Returns a pointer to function that calls self._OnSoftButton method. This allows class methods to be called from within DirectOutput.dll
457
+
458
+ http://stackoverflow.com/questions/7259794/how-can-i-get-methods-to-work-as-callbacks-with-python-ctypes
459
+
460
+ """
461
+ OnSoftButton_Proto = ctypes.WINFUNCTYPE(None, ctypes.c_void_p, ctypes.wintypes.DWORD, ctypes.c_void_p)
462
+
463
+ def func(hDevice, dwButtons, pvContext):
464
+ logging.info("soft button callback closure: {}, {}, {}".format(hDevice, dwButtons, pvContext))
465
+ self._OnSoftButton(hDevice, dwButtons, pvContext)
466
+
467
+ return OnSoftButton_Proto(func)
468
+
469
+
470
+ def _OnDevice(self, hDevice, bAdded, pvContext):
471
+ """
472
+ Internal function to register device handle
473
+
474
+ """
475
+ if not bAdded:
476
+ raise NotImplementedError("Received a message that a device went away.")
477
+ if self.device_handle and self.device_handle != hDevice:
478
+ raise IndexError("Too many Saitek devices present")
479
+ logging.info("_OnDevice")
480
+ self.device_handle = hDevice
481
+
482
+
483
+ def _OnEnumerate(self, hDevice, pvContext):
484
+ """
485
+ Internal function to process a device enumeration
486
+
487
+ """
488
+ logging.info("_OnEnumerate")
489
+ self._OnDevice(hDevice, True, pvContext)
490
+
491
+
492
+ def _OnPage(self, hDevice, dwPage, bActivated, pvContext):
493
+ """
494
+ Method called when page changes. Calls self.OnPage to hide hDevice and pvContext from end-user
495
+
496
+ """
497
+ logging.info("_OnPage")
498
+ self.OnPage(dwPage, bActivated)
499
+
500
+
501
+ def _OnSoftButton(self, hDevice, dwButtons, pvContext):
502
+ """
503
+ Method called when soft button changes. Calls self.OnSoftButton to hide hDevice and pvContext from end-user. Also hides change of page softbutton and press-up.
504
+
505
+ """
506
+ logging.info("_OnSoftButton")
507
+ self.OnSoftButton(self.Buttons(dwButtons))
508
+
509
+
510
+ def OnPage(self, page, activated):
511
+ """
512
+ Method called when a page changes. This should be overwritten by inheriting class
513
+
514
+ Required Arguments:
515
+ page -- page_id passed to AddPage
516
+ activated -- true if this page has become the active page, false if this page was the active page
517
+
518
+ """
519
+ logging.info("OnPage({}, {})".format(page, activated))
520
+
521
+
522
+ def OnSoftButton(self, buttons):
523
+ """
524
+ Method called when a soft button changes. This should be overwritten by inheriting class
525
+
526
+ Required Arguments:
527
+ buttons - Buttons object representing button state
528
+
529
+ """
530
+ logging.info("OnSoftButton({})".format(buttons))
531
+
532
+
533
+ def SetProfile(self, profile):
534
+ """
535
+ Sets the profile used on the device.
536
+
537
+ Required Arguments:
538
+ device_handle -- ID of device
539
+ profile -- full path of the profile to activate. passing None will clear the profile.
540
+ """
541
+ logging.debug("SetProfile({})".format(profile))
542
+ return self.direct_output.SetProfile(self.device_handle, profile)
543
+
544
+
545
+ def AddPage(self, page, name, active):
546
+ """
547
+ Adds a page to the MFD. If overriden by a derived class, you should
548
+ call super().AddPage(*args, **kwargs)
549
+
550
+ Required Arguments:
551
+ page -- page ID to add
552
+ name -- String specifying page name
553
+ active -- True if page is to become the active page, if False this will not change the active page
554
+
555
+ """
556
+ logging.info("AddPage({}, {}, {})".format(page, name, active))
557
+ self.direct_output.AddPage(self.device_handle, page, name, active)
558
+
559
+
560
+ def RemovePage(self, page):
561
+ """
562
+ Removes a page from the MFD
563
+
564
+ Required Arguments:
565
+ page -- page ID to remove
566
+
567
+ """
568
+ logging.info("RemovePage({})".format(page))
569
+ result = self.direct_output.RemovePage(self.device_handle, page)
570
+ if result != S_OK:
571
+ logging.error("RemovePage failed: {}".format(result))
572
+ self.finish()
573
+ raise DirectOutputError(result)
574
+
575
+
576
+ def SetString(self, page, line, string):
577
+ """
578
+ Sets a string to display on the MFD
579
+
580
+ Required Arguments:
581
+ page -- the ID of the page to add the string to
582
+ line -- the line to display the string on (0 = top, 1 = middle, 2 = bottom)
583
+ string -- the string to display
584
+ """
585
+ logging.debug("SetString({}, {}, {})".format(page, line, string))
586
+ result = self.direct_output.SetString(self.device_handle, page, line, string)
587
+ if result != S_OK:
588
+ logging.warning("SetString failed: {}".format(result))
589
+ self.finish()
590
+ raise DirectOutputError(result)
591
+
592
+
593
+ def SetLed(self, page, led, value):
594
+ """
595
+ Sets LED state on a given page
596
+
597
+ Required Arguments:
598
+ page -- page number
599
+ led -- ID of LED to change
600
+ value -- value to set LED (1 = on, 0 = off)
601
+
602
+ """
603
+ logging.debug("SetLed({}, {}, {})".format(page, led, value))
604
+ result = self.direct_output.SetLed(self.device_handle, page, led, value)
605
+ if result != S_OK:
606
+ logging.warning("SetLed failed: {}".format(result))
607
+ self.finish()
608
+ raise DirectOutputError(result)
609
+
610
+
611
+ class DeviceNotFoundError(Exception):
612
+
613
+ def __str__(self):
614
+ return "No Compatible Device Found"
615
+
616
+
617
+ class DLLError(Exception):
618
+
619
+ def __init__(self, error_code):
620
+ self.error_code = error_code
621
+ if error_code == 126:
622
+ self.msg = "specified file does not exist"
623
+ elif error_code == 193:
624
+ self.msg = "possible 32/64 bit mismatch between Python interpreter and DLL. Make sure you have installed both the 32- and 64-bit driver from Saitek's website"
625
+ else:
626
+ self.msg = "unspecified error"
627
+
628
+ def __str__(self):
629
+ return "Unable to load DirectOutput.dll - "+self.msg
630
+
631
+
632
+ class DirectOutputError(Exception):
633
+ Errors = {
634
+ E_HANDLE : "Invalid device handle specified.",
635
+ E_INVALIDARG : "An argument is invalid, and I don't mean it has a poorly leg.",
636
+ E_OUTOFMEMORY : "Download more RAM.",
637
+ E_PAGENOTACTIVE : "Page not active, stupid page.",
638
+ E_BUFFERTOOSMALL : "Buffer used was too small. Use a bigger buffer. See also E_OUTOFMEMORY.",
639
+ E_NOTIMPL : "Feature not implemented, allegedly"
640
+ }
641
+
642
+ def __init__(self, error_code):
643
+ self.error_code = error_code
644
+ if error_code in self.Errors:
645
+ self.msg = self.Errors[error_code]
646
+ else:
647
+ self.msg = "Unspecified DirectOutput Error - "+str(hex(error_code))
648
+
649
+ def __str__(self):
650
+ return self.msg
651
+
652
+
653
+ if __name__ == '__main__':
654
+ # If you want it to go to a file?
655
+ # logging.basicConfig(filename='directoutput.log', filemode='w', level=logging.DEBUG, format='%(asctime)s %(name)s [%(filename)s:%(lineno)d] %(message)s')
656
+ # If you want less verbose logging?
657
+ # logging.basicConfig(level=logging.INFO, format='%(asctime)s %(name)s [%(filename)s:%(lineno)d] %(message)s')
658
+ logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(name)s [%(filename)s:%(lineno)d] %(message)s')
659
+
660
+ device = DirectOutputDevice(debug_level=1)
661
+ print("Device initialized")
662
+
663
+ device.AddPage(0, "Test", True)
664
+ print("Test Page added")
665
+
666
+ device.SetString(0, 0, "Test String")
667
+ print("Test String added")
668
+
669
+ device.AddPage(1, "Other", False)
670
+ device.AddPage(2, "Another", False)
671
+
672
+ while True:
673
+ try:
674
+ time.sleep(1)
675
+ except: # noqa: E722
676
+ # This is used to catch Ctrl+C, calling finish method is *very* important to de-initalize device.
677
+ device.finish()
678
+ sys.exit()