Skip to content

Commit

Permalink
Merge pull request #50 from Rexeh/1.4
Browse files Browse the repository at this point in the history
1.4 Release merge into main
  • Loading branch information
Rexeh authored Mar 31, 2021
2 parents 64950c2 + 50f1a7f commit 0fd2382
Show file tree
Hide file tree
Showing 18 changed files with 488 additions and 27 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ temp/
diagrams/*.svg
play*.py
tests/__pycache__/*.pyc
untitled.ui
*.ui
*.bat
Binary file added images/3rd_party/sc.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 6 additions & 5 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,29 +25,30 @@
"test",
"http",
"email",
"distutils"
"distutils",
"ssl"
],
"optimize": 2,
}

setup(
name='Joystick Diagrams',
version='1.2.1',
version='1.4',
description='Automatically create diagrams for your throttles, joysticks and custom HID devices',
long_description=long_description,
long_description_content_type='text/markdown',
url='https://github.com/Rexeh/joystick-diagrams',
author='Robert Cox',
keywords='joystick, HID, diagrams, joystick gremlin',
keywords='joystick, HID, diagrams, joystick gremlin, dcs world',
packages=find_packages(),
python_requires='>=3.8, <4',
install_requires=['pillow'],
project_urls={
'Documentation' : 'https://joystick-diagrams.com/',
'Bug Reports': 'https://github.com/Rexeh/joystick-diagrams/issues',
'Funding': 'https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=WLLDYGQM5Z39W&source=url',
'Source': 'https://github.com/Rexeh/joystick-diagrams/src',
},

options={'build_exe': build_options},
executables = [Executable("./src/joystick_diagrams.py", base = base, icon = './images/logo.ico')]
executables = [Executable("./src/joystick_diagrams.py", base = base, icon = './images/logo.ico', copyright='Robert Cox - joystick-diagrams.com')]
)
2 changes: 1 addition & 1 deletion src/adaptors/joystick_gremlin.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ def __init__(self,filepath):
self.buttonArray = None
self.inheritModes = {}
self.usingInheritance = False
self. position_map = {
self.position_map = {
1 : 'U',
2 : 'UR',
3 : 'R',
Expand Down
185 changes: 185 additions & 0 deletions src/adaptors/starship_citizen.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
'''Starship Citizen XML Parser for use with Joystick Diagrams'''
import os
from pathlib import Path
from xml.dom import minidom
import functions.helper as helper
import adaptors.joystick_diagram_interface as jdi

class StarshipCitizen(jdi.JDinterface):

def __init__(self, file_path):
jdi.JDinterface.__init__(self)
self.file_path = file_path
self.data = self.__load_file()
self.hat_formats = {
'up': "U",
'down': "D",
'left': "L",
'right': "R"}
self.hat = None
self.devices = {}
self.button_array = {}


def __load_file(self):
if os.path.exists(self.file_path):
if (os.path.splitext(self.file_path))[1] == '.xml':
data = Path(self.file_path).read_text(encoding="utf-8")
try:
self.__validate_file(data)
except Exception:
raise Exception("File is not a valid Starcraft Citizen XML")
else:
return data
else:
raise Exception("File must be an XML file")
else:
raise FileNotFoundError("File not found")


def __validate_file(self, data):
try:
parsed_xml = minidom.parseString(data)
except ValueError:
raise Exception("File is not a valid Starcraft Citizen XML")
else:
if (len(parsed_xml.getElementsByTagName('devices'))==1 and
len(parsed_xml.getElementsByTagName('options'))>0 and
len(parsed_xml.getElementsByTagName('actionmap'))>0):
return True
else:
raise Exception

def parse_map(self, bind_map):
segments = bind_map.split("_")
helper.log("Bind Information: {}".format(segments), 'debug')
bind_device = segments[0]
device_object = self.get_stored_device(bind_device)
helper.log("Device: {}".format(device_object), 'debug')
if device_object is None:
c_map = None
return (device_object,c_map)
if segments[1] == '':
c_map = None
return (device_object,c_map)
elif segments[1][0:6] == 'button':
button_id = segments[1][6:]
c_map = 'BUTTON_{id}'.format(id=button_id)
return (device_object,c_map)
elif segments[1][0:3] == 'hat':
pov_id = segments[1][3:]
pov_dir = self.convert_hat_format(segments[2])
c_map = 'POV_{id}_{dir}'.format(id=pov_id,dir=pov_dir)
return (device_object,c_map)
elif segments[1][0] in ('x','y','z'):
axis = segments[1][0]
c_map = 'AXIS_{axis}'.format(axis=axis)
return (device_object,c_map)
elif segments[1][0:3] == 'rot':
axis = segments[1][3:]
c_map = 'AXIS_R{axis}'.format(axis=axis)
return (device_object,c_map)
elif segments[1][0:6] == 'slider':
slider_id = segments[1][6:]
c_map = 'AXIS_SLIDER_{id}'.format(id=slider_id )
return (device_object,c_map)
else:
c_map = None
return (device_object,c_map)


def get_human_readable_name(self):
# Future for str replacements
pass


def convert_hat_format(self, hat):
#TODO
helper.log("Convert Hat: {}".format(hat), 'debug')
return self.hat_formats[hat]


def extract_device_information(self, option):
''' Accepts parsed OPTION from Starship Citizen XML'''
name = (option.getAttribute('Product')[0:(len(option.getAttribute('Product'))-38)]).strip()
guid = option.getAttribute('Product')[-37:-2] #GUID Fixed
return {
'name': name,
'guid' : guid
}

def get_stored_device(self, device):

if device in self.devices:
return self.devices[device]
else:
return None


def add_device(self, option):
''' Accepts parsed OPTION from Starship Citizen XML'''
self.devices.update({
self.device_id(option.getAttribute('type'),option.getAttribute('instance')) : self.extract_device_information(option)
})
helper.log("Device List: {}".format(self.devices), 'debug')


def process_name(self, name):
helper.log("Bind Name: {}".format(name), 'debug')
name = name.split("_")
if len(name) == 1:
return name[0].capitalize()
else:
return (" ".join(name[1:])).capitalize()


def build_button_map(self, device,button, name):
if device in self.button_array:
self.button_array[device].update( {button:name } )
else:
self.button_array.update({
device : {
button : name
}
})


def device_id(self, device_type, instance):
if device_type == 'keyboard':
t = "kb"
elif device_type == 'joystick':
t = "js"
else:
t = "mo"
return "{type}{instance}".format(type=t, instance=instance)


def parse(self):
parse = minidom.parseString(self.data)
joysticks = parse.getElementsByTagName('options')
for j in joysticks:
self.add_device(j)
actions = parse.getElementsByTagName('actionmap')

for i in actions:
helper.log("Bind Category: {}".format(self.process_name(i.getAttribute('name'))), 'debug')
single_actions = i.getElementsByTagName('action')
for action in single_actions:
name = self.process_name(action.getAttribute('name'))
binds = action.getElementsByTagName('rebind')
helper.log("Binds in group: {}".format(binds), 'debug')
for bind in binds:
bind = bind.getAttribute('input')
button = self.parse_map(bind)
helper.log("Parsed Control: {}".format(button), 'debug')
if(button and button[1] is not None):
helper.log("Valid button, adding to map", 'debug')
self.build_button_map(button[0]['name'], button[1], name)
helper.log("Button Map is now: {}".format(self.button_array))
else:
helper.log("Button not valid, skipping", 'debug')

for item in self.button_array:
self.update_joystick_dictionary(item, "Default", False, self.button_array[item])

return self.joystick_dictionary
2 changes: 0 additions & 2 deletions src/classes/export.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,7 @@ def create_directory(self,directory):
return False

def get_template(self, joystick):

joystick = joystick.strip()

if path.exists(self.templates_directory + joystick + ".svg"):
data = Path(os.path.join(self.templates_directory, joystick + ".svg")).read_text(encoding="utf-8")
return data
Expand Down
18 changes: 9 additions & 9 deletions src/functions/helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,19 @@
import html

# Logging Init
logDir = './logs/'
logFile = 'jv.log'
LOG_DIR = './logs/'
LOG_FILE = 'jv.log'
logger = logging.getLogger('jv')
webbrowser.register('chrome', None,webbrowser.BackgroundBrowser(config.chrome_path))

def createDirectory(directory):
def create_directory(directory):
if not os.path.exists(directory):
return os.makedirs(directory)
else:
log("Failed to create directory: {}".format(directory), 'error')
return False
def log(text, level='info', exec_s=False):

def log(text, level='info', exc_info=False):
#Accepted Levels
# info, warning, error
if config.debug:
Expand All @@ -33,14 +33,14 @@ def log(text, level='info', exec_s=False):
logger.error(text, exc_info=True)
else:
logger.debug(text, exc_info=True)


def getVersion():
return "Version: " + version.VERSION

if not os.path.exists(logDir):
createDirectory(logDir)
hdlr = logging.FileHandler(logDir + logFile)
if not os.path.exists(LOG_DIR):
create_directory(LOG_DIR)
hdlr = logging.FileHandler(LOG_DIR + LOG_FILE)
formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
hdlr.setFormatter(formatter)
logger.addHandler(hdlr)
Expand Down
37 changes: 36 additions & 1 deletion src/joystick_diagrams.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from ui import Ui_MainWindow
import adaptors.dcs_world as dcs
import adaptors.joystick_gremlin as jg
import adaptors.starship_citizen as sc
import classes.export as export
import functions.helper as helper
import version
Expand All @@ -22,6 +23,7 @@ def __init__(self, *args, obj=None, **kwargs):
self.dcs_selected_directory_label.setText('')
self.dcs_parser_instance = None
self.jg_parser_instance = None

self.dcs_easy_mode_checkbox.stateChanged.connect(self.easy_mode_checkbox_action)
self.dcs_directory_select_button.clicked.connect(self.set_dcs_directory)
self.export_button.clicked.connect(self.export_profiles)
Expand All @@ -33,6 +35,11 @@ def __init__(self, *args, obj=None, **kwargs):
# JG UI Setup
self.jg_select_profile_button.clicked.connect(self.set_jg_file)

# SC UI Setup
self.sc_file = None
self.sc_parser_instance = None
self.sc_select_button.clicked.connect(self.set_sc_file)

def setVersion(self):
version_text = version.VERSION
self.label_9.setText(version_text)
Expand All @@ -49,6 +56,11 @@ def change_export_button(self):
self.export_button.setEnabled(1)
else:
self.export_button.setDisabled(1)
elif self.parser_selector.currentIndex() == 2:
if self.sc_parser_instance:
self.export_button.setEnabled(1)
else:
self.export_button.setDisabled(1)
else:
self.export_button.setDisabled(1)

Expand Down Expand Up @@ -99,6 +111,26 @@ def load_dcs_directory(self):
self.dcs_profiles_list.clear()
self.dcs_profiles_list.addItems(self.dcs_parser_instance.getValidatedProfiles())

def set_sc_file(self):
self.clear_info()
self.sc_file = QtWidgets.QFileDialog.getOpenFileName(self,"Select Star Citizen Config file",None,"XMl Files (*.xml)")[0]
if self.sc_file:
self.load_sc_file()
else:
self.print_to_info("No File Selected")

def load_sc_file(self):
try:
self.sc_parser_instance = sc.StarshipCitizen(self.sc_file)
self.enable_profile_load_button(self.sc_select_button)
self.export_button.setEnabled(1)
self.print_to_info('Succesfully loaded Star Citizen profile')
except Exception as e:
self.disable_profile_load_button(self.sc_select_button)
self.export_button.setEnabled(0)
self.sc_file = None
self.print_to_info("Error Loading File: {}".format(e))

def set_jg_file(self):
self.jg_file = QtWidgets.QFileDialog.getOpenFileName(self,"Select Joystick Gremlin Config file",None,"Gremlin XMl Files (*.xml)")[0]

Expand All @@ -123,7 +155,7 @@ def load_jg_file(self):
self.disable_profile_load_button(self.jg_select_profile_button)
self.jg_profile_list.clear()
self.export_button.setEnabled(0)
raise
raise Exception(e)

def export_profiles(self):
if self.parser_selector.currentIndex() == 0: ## JOYSTICK GREMLIN
Expand All @@ -148,6 +180,9 @@ def export_profiles(self):
else:
data = self.dcs_parser_instance.processProfiles()
self.export_to_svg(data, 'DCS')
elif self.parser_selector.currentIndex() == 2: ## SC
data = self.sc_parser_instance.parse()
self.export_to_svg(data, 'StarCitizen')
else:
pass # no other tabs have functionality right now

Expand Down
Loading

0 comments on commit 0fd2382

Please sign in to comment.