Skip to content

Commit 516e1fe

Browse files
authored
Merge pull request #47 from Rexeh/1.3
Version 1.3 Merge
2 parents c29a3f4 + fba0670 commit 516e1fe

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+664
-194
lines changed

config.cfg

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
[DEFAULT]
1+
[Preferences]
2+
NoBindText = No Bind
3+
4+
[Logging]
25
EnableLogging = 1
36
# 1 = Minimum, 3 = Full Debug
47
LogLevel = 1

dill.dll

523 KB
Binary file not shown.

images/discord.png

1.52 KB
Loading

images/donate.png

4.24 KB
Loading

readme.md

Lines changed: 4 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -2,59 +2,13 @@
22

33
![Joystick Diagrams](https://s3-eu-west-1.amazonaws.com/joystick-diagram.com/Joystick-Diagram-info.png#2)
44

5-
## Current Release (1.2.1) - 23/01/2020
5+
# Official Website
6+
http://joystick-diagram.com/
67

7-
### What's new?
8-
9-
- Hotfix to DCS folder structure crashing app
10-
11-
[Release Notes - V1.2.1](https://github.com/Rexeh/joystick-diagrams/releases/tag/V1.2.1)
12-
13-
[Download - Joystick Diagrams (V1.2.1)](https://github.com/Rexeh/joystick-diagrams/releases/download/V1.2.1/joystick_diagrams_1_2_1.zip)
14-
15-
## What is this tool for?
16-
Learning flight simulators is a daunting task, with lots of buttons to remember. I built this tool for myself, and then thought others might also enjoy it, automating what is a time consuming task!
17-
18-
- Export your joystick/throttle/custom HID buttons to a SVG, and print with your browser
19-
- Overlay your buttons onto an actual Joystick profile/picture
20-
- Learn your joystick setup easier and be a better pilot!
21-
22-
## What it's do?
23-
24-
### General
25-
- Custom SVG templates can be built to suit your joystick, throttle, custom HID device
26-
- Diagram style only limited by your design skills! :)
27-
28-
### Joystick Gremlin
29-
- Importing/inheriance of base templates into button profile exports
30-
- Support for multiple profiles in Joystick Gremlin
31-
32-
### DCS World
33-
- Export any of your plane/helicopter configurations to diagram
34-
- Select which profiles you want to export
35-
36-
## What's it not do?
37-
- Output POV hats (Coming V1.3)
38-
- Output AXIS binds (Coming V1.3)
39-
- Make toast
40-
41-
### Support for other games
42-
Have a game you want support added for? Get in touch, send me configs and I'll take a look
43-
44-
## Installation
45-
None required, extract the latest .zip, and run joystick-diagrams.exe
46-
47-
### From Source
48-
Want to run from source? You'll need Python 3.8+
49-
Use setup.py to get up and running
50-
51-
# Templates/Supported Joysticks
52-
It comes with a set of templates for popular sticks/throttles, but you're also able to create your own with ease!
53-
54-
[Please read the about templates here](templates/readme.md)
8+
Please use the website for documentation and information
559

5610
# Bugs
57-
This is still an early release, if you find any bugs, or think you have a slightly different configuration that I might not support let me know!
11+
This project is still early days, if you encounter any issues please raise a bug ticket or reach out on Discord.
5812

5913
# Support / Beer fund
6014

src/adaptors/dcs_world.py

Lines changed: 57 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
import ply.yacc as yacc
77
import functions.helper as helper
88
import adaptors.joystick_diagram_interface as jdi
9-
import adaptors.dcs_world_lex # Do not remove - PLY production requirement
10-
import adaptors.dcs_world_parse # Do not remove - PLY production requirement
9+
import adaptors.dcs_world_lex # pylint: disable=unused-import
10+
import adaptors.dcs_world_parse # pylint: disable=unused-import
1111

1212
class DCSWorld_Parser(jdi.JDinterface):
1313

@@ -62,15 +62,26 @@ def __validateProfile(self, item):
6262
def getValidatedProfiles(self):
6363
''' Expose Valid Profiles only to UI '''
6464
if self.remove_easy_modes:
65-
return list(filter(lambda x: False if self.__easy_mode in x else True, self.valid_profiles))
65+
return list(filter(lambda x: False if self.__easy_mode in x else True, self.valid_profiles))
6666
else:
6767
return self.valid_profiles
6868

6969
def convert_button_format(self, button):
7070
''' Convert DCS Buttons to match expected "BUTTON_X" format '''
71-
new = button.split('_')[1]
72-
rep = new.replace("BTN", "BUTTON_")
73-
return rep
71+
split = button.split('_')
72+
73+
if len(split) == 2:
74+
if split[1][0:3] == "BTN":
75+
return split[1].replace("BTN","BUTTON_")
76+
elif split[1].isalpha():
77+
return "AXIS_{}".format(split[1])
78+
elif split[1][0:6] == "SLIDER":
79+
return "AXIS_SLIDER_{}".format(split[1][6:])
80+
else:
81+
return split[1]
82+
83+
elif len(split) == 4:
84+
return "{button}_{pov}_{dir}".format(button = split[1].replace("BTN","POV"), pov = split[2][3], dir = split[3])
7485

7586
def processProfiles(self, profile_list=[]):
7687
if len(profile_list)>0:
@@ -99,25 +110,46 @@ def processProfiles(self, profile_list=[]):
99110
except FileNotFoundError:
100111
raise FileExistsError("DCS: File {} no longer found - It has been moved/deleted from directory".format(joystick_file))
101112
else:
102-
data = self.parseFile()
103-
writeVal = False
104-
buttonArray = {}
105-
if 'keyDiffs' in data.keys():
106-
for value in data['keyDiffs'].values():
107-
for item, attribute in value.items():
108-
if item=='name':
109-
name = attribute
110-
if item=='added':
111-
button = self.convert_button_format(attribute[1]['key'])
112-
writeVal = True
113-
if writeVal:
114-
buttonArray.update({
115-
button : name
116-
})
117-
writeVal = False
118-
self.update_joystick_dictionary(joystick_device,profile, False, buttonArray)
113+
dictionary_2 = self.parseFile()
114+
115+
button_map = self.create_joystick_map(dictionary_2)
116+
117+
self.update_joystick_dictionary(joystick_device,profile, False, button_map)
119118
return self.joystick_dictionary
120119

120+
def create_joystick_map(self, data):
121+
writeVal = False
122+
buttonArray = {}
123+
124+
if 'keyDiffs' in data.keys():
125+
for value in data['keyDiffs'].values():
126+
for item, attribute in value.items():
127+
if item=='name':
128+
name = attribute
129+
if item=='added':
130+
button = self.convert_button_format(attribute[1]['key'])
131+
writeVal = True
132+
if writeVal:
133+
buttonArray.update({
134+
button : name
135+
})
136+
writeVal = False
137+
138+
if 'axisDiffs' in data.keys():
139+
for value in data['axisDiffs'].values():
140+
for item, attribute in value.items():
141+
if item=='name':
142+
name = attribute
143+
if item in ['added', 'changed']:
144+
axis = self.convert_button_format(attribute[1]['key'])
145+
writeVal = True
146+
if writeVal:
147+
buttonArray.update({
148+
axis : name
149+
})
150+
writeVal = False
151+
return buttonArray
152+
121153
def parseFile(self):
122154
# pylint: disable=unused-variable
123155
tokens = ('LCURLY', 'RCURLY', 'STRING', 'NUMBER', 'LBRACE', 'RBRACE', 'COMMA', 'EQUALS', 'TRUE', 'FALSE', 'DOUBLE_VAL')
@@ -216,5 +248,6 @@ def p_error(t):
216248
try:
217249
data = parser.parse(self.file)
218250
except Exception as error:
219-
print(error)
251+
helper.log(error, "error")
252+
raise ("DCS Parser Exception")
220253
return data

src/adaptors/joystick_diagram_interface.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ def __init__(self):
99
def update_joystick_dictionary(self, device, mode, inherit, buttons):
1010
data = {
1111
"Buttons": buttons,
12-
"Axis": "",
12+
"Axis": '',
1313
"Inherit": inherit}
1414

1515
if device in self.joystick_dictionary:

src/adaptors/joystick_gremlin.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,16 @@ def __init__(self,filepath):
2525
self.buttonArray = None
2626
self.inheritModes = {}
2727
self.usingInheritance = False
28+
self. position_map = {
29+
1 : 'U',
30+
2 : 'UR',
31+
3 : 'R',
32+
4 : 'DR',
33+
5 : 'D',
34+
6 : 'DL',
35+
7 : 'L',
36+
8 : 'UL'
37+
}
2838

2939
def get_device_names(self):
3040
self.devices = self.getDevices()
@@ -64,7 +74,9 @@ def createDictionary(self, profiles=[]):
6474
self.currentMode = self.getSingleMode()
6575
helper.log("Selected Mode: {}".format(self.currentMode), 'debug')
6676
self.buttons = self.getModeButtons()
77+
self.hats = self.getModeHats()
6778
self.extractButtons()
79+
self.extractHats()
6880
self.update_joystick_dictionary(self.currentdevice,
6981
self.currentMode,
7082
self.currentInherit,
@@ -92,6 +104,9 @@ def getDevices(self):
92104
def getModeButtons(self):
93105
return self.mode.getElementsByTagName('button')
94106

107+
def getModeHats(self):
108+
return self.mode.getElementsByTagName('hat')
109+
95110
def getDeviceModes(self):
96111
return self.device.getElementsByTagName('mode')
97112

@@ -125,5 +140,55 @@ def extractButtons(self):
125140
})
126141
return self.buttonArray
127142

143+
def extractHats(self):
144+
145+
for i in self.hats:
146+
hat_id = i.getAttribute('id')
147+
helper.log("Hat ID: {}".format(hat_id), 'debug')
148+
149+
if i.getAttribute('description'):
150+
hat_description = i.getAttribute('description')
151+
helper.log("Hat has description: {}".format(hat_description), 'debug')
152+
else:
153+
hat_description = ''
154+
155+
hat_containers = i.getElementsByTagName('container')
156+
157+
if(hat_containers):
158+
helper.log("Has containers: {}".format(hat_containers.length), 'debug')
159+
160+
for c in hat_containers:
161+
162+
hat_positions = c.getElementsByTagName('action-set')
163+
hat_count = hat_positions.length
164+
increment = 8/hat_count
165+
pos = 1
166+
helper.log("We have {} hat positions".format(hat_count), 'debug')
167+
168+
for hp in hat_positions:
169+
170+
if hp.getElementsByTagName('description'):
171+
# Ignore more than 1 description. always use first
172+
hat_direction_description = hp.getElementsByTagName('description')[0].getAttribute('description')
173+
else:
174+
hat_direction_description = hat_description
175+
176+
helper.log("POV Position: {}".format(self.position_map[pos]), 'debug')
177+
178+
self.buttonArray.update ({
179+
'POV_{ID}_{DIR}'.format(
180+
ID=i.getAttribute('id'),
181+
DIR=self.position_map[pos]
182+
):str(hat_direction_description)
183+
})
184+
185+
pos = pos + increment
186+
else:
187+
helper.log("No container found for hat: {}".format(hat_id), 'error')
188+
189+
190+
191+
192+
128193
def getDeviceCount(self):
129194
return self.file.getElementsByTagName('device').length

src/classes/export.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
from os import path
22
import os
33
from pathlib import Path
4+
import config
45
import re
56
import html
67
import functions.helper as helper
78
from PyQt5 import QtWidgets, uic, QtGui
89

10+
911
class Export:
1012

1113
def __init__(self, joystick_listing, parser_id="UNKNOWN", custom_no_bind="No Bind"):
@@ -14,14 +16,17 @@ def __init__(self, joystick_listing, parser_id="UNKNOWN", custom_no_bind="No Bin
1416
self.file_name_divider = "_"
1517
self.joystick_listing = joystick_listing
1618
self.export_progress = None
17-
self.no_bind_text = custom_no_bind
19+
self.no_bind_text = config.noBindText
1820
self.executor = parser_id
1921
self.error_bucket = []
2022

2123
def export_config(self, progress_bar=None):
2224

2325
joystick_count = len(self.joystick_listing)
2426

27+
helper.log('Export Started with {} joysticks'.format(joystick_count), 'debug')
28+
helper.log('Export Data: {}'.format(self.joystick_listing), 'debug')
29+
2530
if isinstance(progress_bar, QtWidgets.QProgressBar):
2631
progress_bar.setValue(0)
2732
progress_increment = 100/joystick_count
@@ -39,7 +44,7 @@ def export_config(self, progress_bar=None):
3944
if isinstance(progress_bar, QtWidgets.QProgressBar):
4045
progress_bar.setValue(progress_bar.value() + (progress_increment/progress_increment_modes))
4146
else:
42-
self.error_bucket.append("No Template for: {}".format(joystick))
47+
self.error_bucket.append("No Template file found for: {}".format(joystick))
4348

4449
if isinstance(progress_bar, QtWidgets.QProgressBar):
4550
progress_bar.setValue(progress_bar.value() + progress_increment)
@@ -64,14 +69,17 @@ def create_directory(self,directory):
6469
return False
6570

6671
def get_template(self, joystick):
72+
73+
joystick = joystick.strip()
74+
6775
if path.exists(self.templates_directory + joystick + ".svg"):
6876
data = Path(os.path.join(self.templates_directory, joystick + ".svg")).read_text(encoding="utf-8")
6977
return data
7078
else:
7179
return False
7280

7381
def save_template(self, joystick, mode, template):
74-
output_path = self.export_directory + self.executor + "_" + joystick + "_" + mode + ".svg"
82+
output_path = self.export_directory + self.executor + "_" + joystick.strip() + "_" + mode + ".svg"
7583
if not os.path.exists(self.export_directory):
7684
self.create_directory(self.export_directory)
7785
try:

src/config.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,15 @@
55
Config.read('./config.cfg')
66

77
# Write logs
8-
debug = Config.getboolean('DEFAULT', 'EnableLogging')
9-
debugLevel = Config.getint('DEFAULT', 'LogLevel')
8+
debug = Config.getboolean('Logging', 'EnableLogging', fallback=1)
9+
debugLevel = Config.getint('Logging', 'LogLevel', fallback=1)
10+
11+
# Params
12+
noBindText = Config.get('Preferences', 'NoBindText', fallback='No Bind')
1013

1114
# Export out SVG files - for development only (leave as = 1)
1215
export = 1
1316

1417
## Program can automatically open in browser as it creates, specify below if you want this. Only supports Chrome right now.
15-
openinbrowser = Config.getboolean('BROWSER', 'OpenTemplatesInBrowser')
16-
chrome_path=Config.get('BROWSER', 'ChromePath')
18+
openinbrowser = Config.getboolean('BROWSER', 'OpenTemplatesInBrowser', fallback=0)
19+
chrome_path=Config.get('BROWSER', 'ChromePath', fallback="")

0 commit comments

Comments
 (0)