1#!/usr/local/bin/python3.8 2# -*- coding: utf-8 -*- 3 4# Common/Shared code 5# Copyright (C) 2010-2018 Filipe Coelho <falktx@falktx.com> 6# 7# This program is free software; you can redistribute it and/or modify 8# it under the terms of the GNU General Public License as published by 9# the Free Software Foundation; either version 2 of the License, or 10# any later version. 11# 12# This program is distributed in the hope that it will be useful, 13# but WITHOUT ANY WARRANTY; without even the implied warranty of 14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15# GNU General Public License for more details. 16# 17# For a full copy of the GNU General Public License see the COPYING file 18 19# ------------------------------------------------------------------------------------------------------------ 20# Imports (Global) 21 22import os 23import sys 24from codecs import open as codecopen 25from unicodedata import normalize 26 27if True: 28 from PyQt5.QtCore import pyqtSignal, qWarning 29 from PyQt5.QtGui import QIcon 30 from PyQt5.QtWidgets import QApplication, QFileDialog, QMessageBox 31else: 32 from PyQt4.QtCore import pyqtSignal, qWarning 33 from PyQt4.QtGui import QIcon 34 from PyQt4.QtGui import QApplication, QFileDialog, QMessageBox 35 36# ------------------------------------------------------------------------------------------------------------ 37# Set Platform 38 39if sys.platform == "darwin": 40 from PyQt5.QtGui import qt_mac_set_menubar_icons 41 qt_mac_set_menubar_icons(False) 42 HAIKU = False 43 LINUX = False 44 MACOS = True 45 WINDOWS = False 46elif "haiku" in sys.platform: 47 HAIKU = True 48 LINUX = False 49 MACOS = False 50 WINDOWS = False 51elif "linux" in sys.platform: 52 HAIKU = False 53 LINUX = True 54 MACOS = False 55 WINDOWS = False 56elif sys.platform in ("win32", "win64", "cygwin"): 57 WINDIR = os.getenv("WINDIR") 58 HAIKU = False 59 LINUX = False 60 MACOS = False 61 WINDOWS = True 62else: 63 HAIKU = False 64 LINUX = False 65 MACOS = False 66 WINDOWS = False 67 68# ------------------------------------------------------------------------------------------------------------ 69# Try Import Signal 70 71try: 72 from signal import signal, SIGINT, SIGTERM, SIGUSR1, SIGUSR2 73 haveSignal = True 74except: 75 haveSignal = False 76 77# ------------------------------------------------------------------------------------------------------------ 78# Safe exception hook, needed for PyQt5 79 80def sys_excepthook(typ, value, tback): 81 return sys.__excepthook__(typ, value, tback) 82 83sys.excepthook = sys_excepthook 84 85# ------------------------------------------------------------------------------------------------------------ 86# Set Version 87 88VERSION = "0.9.0" 89 90# ------------------------------------------------------------------------------------------------------------ 91# Set Debug mode 92 93DEBUG = bool("-d" in sys.argv or "-debug" in sys.argv or "--debug" in sys.argv) 94 95# ------------------------------------------------------------------------------------------------------------ 96# Global variables 97 98global gGui 99gGui = None 100 101# ------------------------------------------------------------------------------------------------------------ 102# Set TMP 103 104TMP = os.getenv("TMP") 105 106if TMP is None: 107 if WINDOWS: 108 qWarning("TMP variable not set") 109 TMP = os.path.join(WINDIR, "temp") 110 else: 111 TMP = "/tmp" 112 113# ------------------------------------------------------------------------------------------------------------ 114# Set HOME 115 116HOME = os.getenv("HOME") 117 118if HOME is None: 119 HOME = os.path.expanduser("~") 120 121 if not WINDOWS: 122 qWarning("HOME variable not set") 123 124if not os.path.exists(HOME): 125 qWarning("HOME does not exist") 126 HOME = TMP 127 128# ------------------------------------------------------------------------------------------------------------ 129# Set PATH 130 131PATH = os.getenv("PATH") 132 133if PATH is None: 134 qWarning("PATH variable not set") 135 136 if MACOS: 137 PATH = ("/opt/local/bin", "/usr/local/bin", "/usr/bin", "/bin") 138 elif WINDOWS: 139 PATH = (os.path.join(WINDIR, "system32"), WINDIR) 140 else: 141 PATH = ("/usr/local/bin", "/usr/bin", "/bin") 142 143else: 144 PATH = PATH.split(os.pathsep) 145 146# ------------------------------------------------------------------------------------------------------------ 147# Remove/convert non-ascii chars from a string 148 149def asciiString(string): 150 return normalize("NFKD", string).encode("ascii", "ignore").decode("utf-8") 151 152# ------------------------------------------------------------------------------------------------------------ 153# Convert a ctypes c_char_p into a python string 154 155def cString(value): 156 if not value: 157 return "" 158 if isinstance(value, str): 159 return value 160 return value.decode("utf-8", errors="ignore") 161 162# ------------------------------------------------------------------------------------------------------------ 163# Check if a value is a number (float support) 164 165def isNumber(value): 166 try: 167 float(value) 168 return True 169 except: 170 return False 171 172# ------------------------------------------------------------------------------------------------------------ 173# Convert a value to a list 174 175def toList(value): 176 if value is None: 177 return [] 178 elif not isinstance(value, list): 179 return [value] 180 else: 181 return value 182 183# ------------------------------------------------------------------------------------------------------------ 184# Unicode open 185 186def uopen(filename, mode="r"): 187 return codecopen(filename, encoding="utf-8", mode=mode) 188 189# ------------------------------------------------------------------------------------------------------------ 190# QLineEdit and QPushButton combo 191 192def getAndSetPath(self_, currentPath, lineEdit): 193 newPath = QFileDialog.getExistingDirectory(self_, self_.tr("Set Path"), currentPath, QFileDialog.ShowDirsOnly) 194 if newPath: 195 lineEdit.setText(newPath) 196 return newPath 197 198# ------------------------------------------------------------------------------------------------------------ 199# Get Icon from user theme, using our own as backup (Oxygen) 200 201def getIcon(icon, size=16): 202 return QIcon.fromTheme(icon, QIcon(":/%ix%i/%s.png" % (size, size, icon))) 203 204# ------------------------------------------------------------------------------------------------------------ 205# Custom MessageBox 206 207def CustomMessageBox(self_, icon, title, text, extraText="", buttons=QMessageBox.Yes|QMessageBox.No, defButton=QMessageBox.No): 208 msgBox = QMessageBox(self_) 209 msgBox.setIcon(icon) 210 msgBox.setWindowTitle(title) 211 msgBox.setText(text) 212 msgBox.setInformativeText(extraText) 213 msgBox.setStandardButtons(buttons) 214 msgBox.setDefaultButton(defButton) 215 return msgBox.exec_() 216 217# ------------------------------------------------------------------------------------------------------------ 218# Signal handler 219 220def setUpSignals(self_): 221 global gGui 222 223 if gGui is None: 224 gGui = self_ 225 226 if not haveSignal: 227 return 228 229 signal(SIGINT, signalHandler) 230 signal(SIGTERM, signalHandler) 231 signal(SIGUSR1, signalHandler) 232 signal(SIGUSR2, signalHandler) 233 234 gGui.SIGTERM.connect(closeWindowHandler) 235 gGui.SIGUSR2.connect(showWindowHandler) 236 237def signalHandler(sig, frame): 238 global gGui 239 240 if gGui is None: 241 return 242 243 if sig in (SIGINT, SIGTERM): 244 gGui.SIGTERM.emit() 245 elif sig == SIGUSR1: 246 gGui.SIGUSR1.emit() 247 elif sig == SIGUSR2: 248 gGui.SIGUSR2.emit() 249 250def closeWindowHandler(): 251 global gGui 252 253 if gGui is None: 254 return 255 256 gGui.hide() 257 gGui.close() 258 QApplication.instance().quit() 259 260 gGui = None 261 262def showWindowHandler(): 263 global gGui 264 265 if gGui is None: 266 return 267 268 if gGui.isMaximized(): 269 gGui.showMaximized() 270 else: 271 gGui.showNormal() 272 273# ------------------------------------------------------------------------------------------------------------ 274# Shared Icons 275 276def setIcons(self_, modes): 277 global gGui 278 279 if gGui is None: 280 gGui = self_ 281 282 if "canvas" in modes: 283 gGui.ui.act_canvas_arrange.setIcon(getIcon("view-sort-ascending")) 284 gGui.ui.act_canvas_refresh.setIcon(getIcon("view-refresh")) 285 gGui.ui.act_canvas_zoom_fit.setIcon(getIcon("zoom-fit-best")) 286 gGui.ui.act_canvas_zoom_in.setIcon(getIcon("zoom-in")) 287 gGui.ui.act_canvas_zoom_out.setIcon(getIcon("zoom-out")) 288 gGui.ui.act_canvas_zoom_100.setIcon(getIcon("zoom-original")) 289 gGui.ui.b_canvas_zoom_fit.setIcon(getIcon("zoom-fit-best")) 290 gGui.ui.b_canvas_zoom_in.setIcon(getIcon("zoom-in")) 291 gGui.ui.b_canvas_zoom_out.setIcon(getIcon("zoom-out")) 292 gGui.ui.b_canvas_zoom_100.setIcon(getIcon("zoom-original")) 293 294 if "jack" in modes: 295 gGui.ui.act_jack_clear_xruns.setIcon(getIcon("edit-clear")) 296 gGui.ui.act_jack_configure.setIcon(getIcon("configure")) 297 gGui.ui.act_jack_render.setIcon(getIcon("media-record")) 298 gGui.ui.b_jack_clear_xruns.setIcon(getIcon("edit-clear")) 299 gGui.ui.b_jack_configure.setIcon(getIcon("configure")) 300 gGui.ui.b_jack_render.setIcon(getIcon("media-record")) 301 302 if "transport" in modes: 303 gGui.ui.act_transport_play.setIcon(getIcon("media-playback-start")) 304 gGui.ui.act_transport_stop.setIcon(getIcon("media-playback-stop")) 305 gGui.ui.act_transport_backwards.setIcon(getIcon("media-seek-backward")) 306 gGui.ui.act_transport_forwards.setIcon(getIcon("media-seek-forward")) 307 gGui.ui.b_transport_play.setIcon(getIcon("media-playback-start")) 308 gGui.ui.b_transport_stop.setIcon(getIcon("media-playback-stop")) 309 gGui.ui.b_transport_backwards.setIcon(getIcon("media-seek-backward")) 310 gGui.ui.b_transport_forwards.setIcon(getIcon("media-seek-forward")) 311 312 if "misc" in modes: 313 gGui.ui.act_quit.setIcon(getIcon("application-exit")) 314 gGui.ui.act_configure.setIcon(getIcon("configure")) 315