Audio plugin host https://kx.studio/carla
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

carla_shared.py 34KB

11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
10 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
10 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
7 years ago
7 years ago
10 years ago
7 years ago
7 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
9 years ago
9 years ago
9 years ago
11 years ago
10 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
10 years ago
10 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921
  1. #!/usr/bin/env python3
  2. # SPDX-FileCopyrightText: 2011-2024 Filipe Coelho <falktx@falktx.com>
  3. # SPDX-License-Identifier: GPL-2.0-or-later
  4. # ------------------------------------------------------------------------------------------------------------
  5. # Imports (Global)
  6. import os
  7. import sys
  8. from math import fmod
  9. # ------------------------------------------------------------------------------------------------------------
  10. # Imports (Signal)
  11. from signal import signal, SIGINT, SIGTERM
  12. try:
  13. from signal import SIGUSR1
  14. haveSIGUSR1 = True
  15. except:
  16. haveSIGUSR1 = False
  17. # ------------------------------------------------------------------------------------------------------------
  18. # Imports (PyQt)
  19. from qt_compat import qt_config
  20. if qt_config == 5:
  21. # import changed in PyQt 5.15.8, so try both
  22. try:
  23. from PyQt5.Qt import PYQT_VERSION_STR
  24. except ImportError:
  25. from PyQt5.QtCore import PYQT_VERSION_STR
  26. from PyQt5.QtCore import qFatal, QT_VERSION, QT_VERSION_STR, qWarning, QDir, QSettings
  27. from PyQt5.QtGui import QIcon
  28. from PyQt5.QtWidgets import QFileDialog, QMessageBox
  29. elif qt_config == 6:
  30. from PyQt6.QtCore import qFatal, PYQT_VERSION_STR, QT_VERSION, QT_VERSION_STR, qWarning, QDir, QSettings
  31. from PyQt6.QtGui import QIcon
  32. from PyQt6.QtWidgets import QFileDialog, QMessageBox
  33. # ------------------------------------------------------------------------------------------------------------
  34. # Imports (Custom)
  35. from carla_backend import (
  36. MAX_DEFAULT_PARAMETERS,
  37. ENGINE_PROCESS_MODE_MULTIPLE_CLIENTS,
  38. ENGINE_PROCESS_MODE_PATCHBAY,
  39. ENGINE_TRANSPORT_MODE_INTERNAL,
  40. ENGINE_TRANSPORT_MODE_JACK
  41. )
  42. from common import kIs64bit, HAIKU, LINUX, MACOS, WINDOWS, VERSION
  43. # ------------------------------------------------------------------------------------------------------------
  44. # Config
  45. # These will be modified during install
  46. X_LIBDIR_X = None
  47. X_DATADIR_X = None
  48. # ------------------------------------------------------------------------------------------------------------
  49. # Platform specific stuff
  50. if WINDOWS:
  51. WINDIR = os.getenv("WINDIR")
  52. # ------------------------------------------------------------------------------------------------------------
  53. # Set TMP
  54. envTMP = os.getenv("TMP")
  55. if envTMP is None:
  56. if WINDOWS:
  57. qWarning("TMP variable not set")
  58. TMP = QDir.tempPath()
  59. else:
  60. TMP = envTMP
  61. if not os.path.exists(TMP):
  62. qWarning("TMP does not exist")
  63. TMP = "/"
  64. del envTMP
  65. # ------------------------------------------------------------------------------------------------------------
  66. # Set HOME
  67. envHOME = os.getenv("HOME")
  68. if envHOME is None:
  69. if not WINDOWS:
  70. qWarning("HOME variable not set")
  71. HOME = QDir.toNativeSeparators(QDir.homePath())
  72. else:
  73. HOME = envHOME
  74. if not os.path.exists(HOME):
  75. qWarning("HOME does not exist")
  76. HOME = TMP
  77. del envHOME
  78. # ------------------------------------------------------------------------------------------------------------
  79. # Set PATH
  80. envPATH = os.getenv("PATH")
  81. if envPATH is None:
  82. qWarning("PATH variable not set")
  83. if MACOS:
  84. PATH = ("/opt/local/bin", "/usr/local/bin", "/usr/bin", "/bin")
  85. elif WINDOWS:
  86. PATH = (os.path.join(WINDIR, "system32"), WINDIR)
  87. else:
  88. PATH = ("/usr/local/bin", "/usr/bin", "/bin")
  89. else:
  90. PATH = envPATH.split(os.pathsep)
  91. del envPATH
  92. # ------------------------------------------------------------------------------------------------------------
  93. # Static MIDI CC list
  94. MIDI_CC_LIST = (
  95. "01 [0x01] Modulation",
  96. "02 [0x02] Breath",
  97. "04 [0x04] Foot",
  98. "05 [0x05] Portamento",
  99. "07 [0x07] Volume",
  100. "08 [0x08] Balance",
  101. "10 [0x0A] Pan",
  102. "11 [0x0B] Expression",
  103. "12 [0x0C] FX Control 1",
  104. "13 [0x0D] FX Control 2",
  105. "16 [0x10] General Purpose 1",
  106. "17 [0x11] General Purpose 2",
  107. "18 [0x12] General Purpose 3",
  108. "19 [0x13] General Purpose 4",
  109. "70 [0x46] Control 1 [Variation]",
  110. "71 [0x47] Control 2 [Timbre]",
  111. "72 [0x48] Control 3 [Release]",
  112. "73 [0x49] Control 4 [Attack]",
  113. "74 [0x4A] Control 5 [Brightness]",
  114. "75 [0x4B] Control 6 [Decay]",
  115. "76 [0x4C] Control 7 [Vib Rate]",
  116. "77 [0x4D] Control 8 [Vib Depth]",
  117. "78 [0x4E] Control 9 [Vib Delay]",
  118. "79 [0x4F] Control 10 [Undefined]",
  119. "80 [0x50] General Purpose 5",
  120. "81 [0x51] General Purpose 6",
  121. "82 [0x52] General Purpose 7",
  122. "83 [0x53] General Purpose 8",
  123. "84 [0x54] Portamento Control",
  124. "91 [0x5B] FX 1 Depth [Reverb]",
  125. "92 [0x5C] FX 2 Depth [Tremolo]",
  126. "93 [0x5D] FX 3 Depth [Chorus]",
  127. "94 [0x5E] FX 4 Depth [Detune]",
  128. "95 [0x5F] FX 5 Depth [Phaser]"
  129. )
  130. MAX_MIDI_CC_LIST_ITEM = 95
  131. # ------------------------------------------------------------------------------------------------------------
  132. # PatchCanvas defines
  133. CANVAS_ANTIALIASING_SMALL = 1
  134. CANVAS_EYECANDY_SMALL = 1
  135. # ------------------------------------------------------------------------------------------------------------
  136. # Carla Settings keys
  137. CARLA_KEY_MAIN_PROJECT_FOLDER = "Main/ProjectFolder" # str
  138. CARLA_KEY_MAIN_USE_PRO_THEME = "Main/UseProTheme" # bool
  139. CARLA_KEY_MAIN_PRO_THEME_COLOR = "Main/ProThemeColor" # str
  140. CARLA_KEY_MAIN_REFRESH_INTERVAL = "Main/RefreshInterval" # int
  141. CARLA_KEY_MAIN_CONFIRM_EXIT = "Main/ConfirmExit" # bool
  142. CARLA_KEY_MAIN_CLASSIC_SKIN = "Main/ClassicSkin" # bool
  143. CARLA_KEY_MAIN_SHOW_LOGS = "Main/ShowLogs" # bool
  144. CARLA_KEY_MAIN_SYSTEM_ICONS = "Main/SystemIcons" # bool
  145. CARLA_KEY_MAIN_EXPERIMENTAL = "Main/Experimental" # bool
  146. CARLA_KEY_CANVAS_THEME = "Canvas/Theme" # str
  147. CARLA_KEY_CANVAS_SIZE = "Canvas/Size" # str "NxN"
  148. CARLA_KEY_CANVAS_USE_BEZIER_LINES = "Canvas/UseBezierLines" # bool
  149. CARLA_KEY_CANVAS_AUTO_HIDE_GROUPS = "Canvas/AutoHideGroups" # bool
  150. CARLA_KEY_CANVAS_AUTO_SELECT_ITEMS = "Canvas/AutoSelectItems" # bool
  151. CARLA_KEY_CANVAS_EYE_CANDY = "Canvas/EyeCandy2" # bool
  152. CARLA_KEY_CANVAS_FANCY_EYE_CANDY = "Canvas/FancyEyeCandy" # bool
  153. CARLA_KEY_CANVAS_USE_OPENGL = "Canvas/UseOpenGL" # bool
  154. CARLA_KEY_CANVAS_ANTIALIASING = "Canvas/Antialiasing" # enum
  155. CARLA_KEY_CANVAS_HQ_ANTIALIASING = "Canvas/HQAntialiasing" # bool
  156. CARLA_KEY_CANVAS_INLINE_DISPLAYS = "Canvas/InlineDisplays" # bool
  157. CARLA_KEY_CANVAS_FULL_REPAINTS = "Canvas/FullRepaints" # bool
  158. CARLA_KEY_ENGINE_DRIVER_PREFIX = "Engine/Driver-"
  159. CARLA_KEY_ENGINE_AUDIO_DRIVER = "Engine/AudioDriver" # str
  160. CARLA_KEY_ENGINE_AUDIO_DEVICE = "Engine/AudioDevice" # str
  161. CARLA_KEY_ENGINE_BUFFER_SIZE = "Engine/BufferSize" # int
  162. CARLA_KEY_ENGINE_SAMPLE_RATE = "Engine/SampleRate" # int
  163. CARLA_KEY_ENGINE_TRIPLE_BUFFER = "Engine/TripleBuffer" # bool
  164. CARLA_KEY_ENGINE_PROCESS_MODE = "Engine/ProcessMode" # enum
  165. CARLA_KEY_ENGINE_TRANSPORT_MODE = "Engine/TransportMode" # enum
  166. CARLA_KEY_ENGINE_TRANSPORT_EXTRA = "Engine/TransportExtra" # str
  167. CARLA_KEY_ENGINE_FORCE_STEREO = "Engine/ForceStereo" # bool
  168. CARLA_KEY_ENGINE_PREFER_PLUGIN_BRIDGES = "Engine/PreferPluginBridges" # bool
  169. CARLA_KEY_ENGINE_PREFER_UI_BRIDGES = "Engine/PreferUiBridges" # bool
  170. CARLA_KEY_ENGINE_MANAGE_UIS = "Engine/ManageUIs" # bool
  171. CARLA_KEY_ENGINE_UIS_ALWAYS_ON_TOP = "Engine/UIsAlwaysOnTop" # bool
  172. CARLA_KEY_ENGINE_MAX_PARAMETERS = "Engine/MaxParameters" # int
  173. CARLA_KEY_ENGINE_RESET_XRUNS = "Engine/ResetXruns" # bool
  174. CARLA_KEY_ENGINE_UI_BRIDGES_TIMEOUT = "Engine/UiBridgesTimeout" # int
  175. CARLA_KEY_OSC_ENABLED = "OSC/Enabled"
  176. CARLA_KEY_OSC_TCP_PORT_ENABLED = "OSC/TCPEnabled"
  177. CARLA_KEY_OSC_TCP_PORT_NUMBER = "OSC/TCPNumber"
  178. CARLA_KEY_OSC_TCP_PORT_RANDOM = "OSC/TCPRandom"
  179. CARLA_KEY_OSC_UDP_PORT_ENABLED = "OSC/UDPEnabled"
  180. CARLA_KEY_OSC_UDP_PORT_NUMBER = "OSC/UDPNumber"
  181. CARLA_KEY_OSC_UDP_PORT_RANDOM = "OSC/UDPRandom"
  182. CARLA_KEY_PATHS_AUDIO = "Paths/Audio"
  183. CARLA_KEY_PATHS_MIDI = "Paths/MIDI"
  184. CARLA_KEY_PATHS_LADSPA = "Paths/LADSPA"
  185. CARLA_KEY_PATHS_DSSI = "Paths/DSSI"
  186. CARLA_KEY_PATHS_LV2 = "Paths/LV2"
  187. CARLA_KEY_PATHS_VST2 = "Paths/VST2"
  188. CARLA_KEY_PATHS_VST3 = "Paths/VST3"
  189. CARLA_KEY_PATHS_CLAP = "Paths/CLAP"
  190. CARLA_KEY_PATHS_SF2 = "Paths/SF2"
  191. CARLA_KEY_PATHS_SFZ = "Paths/SFZ"
  192. CARLA_KEY_PATHS_JSFX = "Paths/JSFX"
  193. CARLA_KEY_WINE_EXECUTABLE = "Wine/Executable" # str
  194. CARLA_KEY_WINE_AUTO_PREFIX = "Wine/AutoPrefix" # bool
  195. CARLA_KEY_WINE_FALLBACK_PREFIX = "Wine/FallbackPrefix" # str
  196. CARLA_KEY_WINE_RT_PRIO_ENABLED = "Wine/RtPrioEnabled" # bool
  197. CARLA_KEY_WINE_BASE_RT_PRIO = "Wine/BaseRtPrio" # int
  198. CARLA_KEY_WINE_SERVER_RT_PRIO = "Wine/ServerRtPrio" # int
  199. CARLA_KEY_EXPERIMENTAL_PLUGIN_BRIDGES = "Experimental/PluginBridges" # bool
  200. CARLA_KEY_EXPERIMENTAL_WINE_BRIDGES = "Experimental/WineBridges" # bool
  201. CARLA_KEY_EXPERIMENTAL_JACK_APPS = "Experimental/JackApplications" # bool
  202. CARLA_KEY_EXPERIMENTAL_EXPORT_LV2 = "Experimental/ExportLV2" # bool
  203. CARLA_KEY_EXPERIMENTAL_PREVENT_BAD_BEHAVIOUR = "Experimental/PreventBadBehaviour" # bool
  204. CARLA_KEY_EXPERIMENTAL_LOAD_LIB_GLOBAL = "Experimental/LoadLibGlobal" # bool
  205. # if pro theme is on and color is black
  206. CARLA_KEY_CUSTOM_PAINTING = "UseCustomPainting" # bool
  207. # ------------------------------------------------------------------------------------------------------------
  208. # Carla Settings defaults
  209. # Main
  210. CARLA_DEFAULT_MAIN_PROJECT_FOLDER = HOME
  211. CARLA_DEFAULT_MAIN_USE_PRO_THEME = True
  212. CARLA_DEFAULT_MAIN_PRO_THEME_COLOR = "Black"
  213. CARLA_DEFAULT_MAIN_REFRESH_INTERVAL = 20
  214. CARLA_DEFAULT_MAIN_CONFIRM_EXIT = True
  215. CARLA_DEFAULT_MAIN_CLASSIC_SKIN = False
  216. CARLA_DEFAULT_MAIN_SHOW_LOGS = bool(not WINDOWS)
  217. CARLA_DEFAULT_MAIN_SYSTEM_ICONS = False
  218. CARLA_DEFAULT_MAIN_EXPERIMENTAL = False
  219. # Canvas
  220. CARLA_DEFAULT_CANVAS_THEME = "Modern Dark"
  221. CARLA_DEFAULT_CANVAS_SIZE = "3100x2400"
  222. CARLA_DEFAULT_CANVAS_SIZE_WIDTH = 3100
  223. CARLA_DEFAULT_CANVAS_SIZE_HEIGHT = 2400
  224. CARLA_DEFAULT_CANVAS_USE_BEZIER_LINES = True
  225. CARLA_DEFAULT_CANVAS_AUTO_HIDE_GROUPS = True
  226. CARLA_DEFAULT_CANVAS_AUTO_SELECT_ITEMS = False
  227. CARLA_DEFAULT_CANVAS_EYE_CANDY = True
  228. CARLA_DEFAULT_CANVAS_FANCY_EYE_CANDY = False
  229. CARLA_DEFAULT_CANVAS_USE_OPENGL = False
  230. CARLA_DEFAULT_CANVAS_ANTIALIASING = CANVAS_ANTIALIASING_SMALL
  231. CARLA_DEFAULT_CANVAS_HQ_ANTIALIASING = False
  232. CARLA_DEFAULT_CANVAS_INLINE_DISPLAYS = False
  233. CARLA_DEFAULT_CANVAS_FULL_REPAINTS = False
  234. # Engine
  235. CARLA_DEFAULT_FORCE_STEREO = False
  236. CARLA_DEFAULT_PREFER_PLUGIN_BRIDGES = False
  237. CARLA_DEFAULT_PREFER_UI_BRIDGES = True
  238. CARLA_DEFAULT_MANAGE_UIS = True
  239. CARLA_DEFAULT_UIS_ALWAYS_ON_TOP = False
  240. CARLA_DEFAULT_MAX_PARAMETERS = MAX_DEFAULT_PARAMETERS
  241. CARLA_DEFAULT_RESET_XRUNS = False
  242. CARLA_DEFAULT_UI_BRIDGES_TIMEOUT = 4000
  243. CARLA_DEFAULT_AUDIO_BUFFER_SIZE = 512
  244. CARLA_DEFAULT_AUDIO_SAMPLE_RATE = 44100
  245. CARLA_DEFAULT_AUDIO_TRIPLE_BUFFER = False
  246. if HAIKU:
  247. CARLA_DEFAULT_AUDIO_DRIVER = "SDL"
  248. elif MACOS:
  249. CARLA_DEFAULT_AUDIO_DRIVER = "CoreAudio"
  250. elif WINDOWS:
  251. CARLA_DEFAULT_AUDIO_DRIVER = "Windows Audio"
  252. elif os.path.exists("/usr/bin/jackd") or os.path.exists("/usr/bin/jackdbus") or os.path.exists("/usr/bin/pw-jack"):
  253. CARLA_DEFAULT_AUDIO_DRIVER = "JACK"
  254. else:
  255. CARLA_DEFAULT_AUDIO_DRIVER = "PulseAudio"
  256. if CARLA_DEFAULT_AUDIO_DRIVER == "JACK":
  257. CARLA_DEFAULT_PROCESS_MODE = ENGINE_PROCESS_MODE_MULTIPLE_CLIENTS
  258. CARLA_DEFAULT_TRANSPORT_MODE = ENGINE_TRANSPORT_MODE_JACK
  259. else:
  260. CARLA_DEFAULT_PROCESS_MODE = ENGINE_PROCESS_MODE_PATCHBAY
  261. CARLA_DEFAULT_TRANSPORT_MODE = ENGINE_TRANSPORT_MODE_INTERNAL
  262. # OSC
  263. CARLA_DEFAULT_OSC_ENABLED = not (MACOS or WINDOWS)
  264. CARLA_DEFAULT_OSC_TCP_PORT_ENABLED = True
  265. CARLA_DEFAULT_OSC_TCP_PORT_NUMBER = 22752
  266. CARLA_DEFAULT_OSC_TCP_PORT_RANDOM = False
  267. CARLA_DEFAULT_OSC_UDP_PORT_ENABLED = True
  268. CARLA_DEFAULT_OSC_UDP_PORT_NUMBER = 22752
  269. CARLA_DEFAULT_OSC_UDP_PORT_RANDOM = False
  270. # Wine
  271. CARLA_DEFAULT_WINE_EXECUTABLE = "wine"
  272. CARLA_DEFAULT_WINE_AUTO_PREFIX = True
  273. CARLA_DEFAULT_WINE_FALLBACK_PREFIX = os.path.expanduser("~/.wine")
  274. CARLA_DEFAULT_WINE_RT_PRIO_ENABLED = True
  275. CARLA_DEFAULT_WINE_BASE_RT_PRIO = 15
  276. CARLA_DEFAULT_WINE_SERVER_RT_PRIO = 10
  277. # Experimental
  278. CARLA_DEFAULT_EXPERIMENTAL_PLUGIN_BRIDGES = False
  279. CARLA_DEFAULT_EXPERIMENTAL_WINE_BRIDGES = False
  280. CARLA_DEFAULT_EXPERIMENTAL_JACK_APPS = False
  281. CARLA_DEFAULT_EXPERIMENTAL_LV2_EXPORT = False
  282. CARLA_DEFAULT_EXPERIMENTAL_PREVENT_BAD_BEHAVIOUR = False
  283. CARLA_DEFAULT_EXPERIMENTAL_LOAD_LIB_GLOBAL = False
  284. # ------------------------------------------------------------------------------------------------------------
  285. # Default File Folders
  286. CARLA_DEFAULT_FILE_PATH_AUDIO = []
  287. CARLA_DEFAULT_FILE_PATH_MIDI = []
  288. # ------------------------------------------------------------------------------------------------------------
  289. # Default Plugin Folders (get)
  290. DEFAULT_LADSPA_PATH = ""
  291. DEFAULT_DSSI_PATH = ""
  292. DEFAULT_LV2_PATH = ""
  293. DEFAULT_VST2_PATH = ""
  294. DEFAULT_VST3_PATH = ""
  295. DEFAULT_CLAP_PATH = ""
  296. DEFAULT_SF2_PATH = ""
  297. DEFAULT_SFZ_PATH = ""
  298. DEFAULT_JSFX_PATH = ""
  299. if WINDOWS:
  300. splitter = ";"
  301. APPDATA = os.getenv("APPDATA")
  302. LOCALAPPDATA = os.getenv("LOCALAPPDATA", APPDATA)
  303. PROGRAMFILES = os.getenv("PROGRAMFILES")
  304. PROGRAMFILESx86 = os.getenv("PROGRAMFILES(x86)")
  305. COMMONPROGRAMFILES = os.getenv("COMMONPROGRAMFILES")
  306. COMMONPROGRAMFILESx86 = os.getenv("COMMONPROGRAMFILES(x86)")
  307. # Small integrity tests
  308. if not APPDATA:
  309. qFatal("APPDATA variable not set, cannot continue")
  310. sys.exit(1)
  311. if not PROGRAMFILES:
  312. qFatal("PROGRAMFILES variable not set, cannot continue")
  313. sys.exit(1)
  314. if not COMMONPROGRAMFILES:
  315. qFatal("COMMONPROGRAMFILES variable not set, cannot continue")
  316. sys.exit(1)
  317. DEFAULT_LADSPA_PATH = APPDATA + "\\LADSPA"
  318. DEFAULT_LADSPA_PATH += ";" + PROGRAMFILES + "\\LADSPA"
  319. DEFAULT_DSSI_PATH = APPDATA + "\\DSSI"
  320. DEFAULT_DSSI_PATH += ";" + PROGRAMFILES + "\\DSSI"
  321. DEFAULT_LV2_PATH = APPDATA + "\\LV2"
  322. DEFAULT_LV2_PATH += ";" + COMMONPROGRAMFILES + "\\LV2"
  323. DEFAULT_VST2_PATH = PROGRAMFILES + "\\VstPlugins"
  324. DEFAULT_VST2_PATH += ";" + PROGRAMFILES + "\\Steinberg\\VstPlugins"
  325. DEFAULT_JSFX_PATH = APPDATA + "\\REAPER\\Effects"
  326. #DEFAULT_JSFX_PATH += ";" + PROGRAMFILES + "\\REAPER\\InstallData\\Effects"
  327. if kIs64bit:
  328. DEFAULT_VST2_PATH += ";" + COMMONPROGRAMFILES + "\\VST2"
  329. DEFAULT_VST3_PATH = COMMONPROGRAMFILES + "\\VST3"
  330. DEFAULT_VST3_PATH += ";" + LOCALAPPDATA + "\\Programs\\Common\\VST3"
  331. DEFAULT_CLAP_PATH = COMMONPROGRAMFILES + "\\CLAP"
  332. DEFAULT_CLAP_PATH += ";" + LOCALAPPDATA + "\\Programs\\Common\\CLAP"
  333. DEFAULT_SF2_PATH = APPDATA + "\\SF2"
  334. DEFAULT_SFZ_PATH = APPDATA + "\\SFZ"
  335. if PROGRAMFILESx86:
  336. DEFAULT_LADSPA_PATH += ";" + PROGRAMFILESx86 + "\\LADSPA"
  337. DEFAULT_DSSI_PATH += ";" + PROGRAMFILESx86 + "\\DSSI"
  338. DEFAULT_VST2_PATH += ";" + PROGRAMFILESx86 + "\\VstPlugins"
  339. DEFAULT_VST2_PATH += ";" + PROGRAMFILESx86 + "\\Steinberg\\VstPlugins"
  340. #DEFAULT_JSFX_PATH += ";" + PROGRAMFILESx86 + "\\REAPER\\InstallData\\Effects"
  341. if COMMONPROGRAMFILESx86:
  342. DEFAULT_VST3_PATH += COMMONPROGRAMFILESx86 + "\\VST3"
  343. DEFAULT_CLAP_PATH += COMMONPROGRAMFILESx86 + "\\CLAP"
  344. elif HAIKU:
  345. splitter = ":"
  346. DEFAULT_LADSPA_PATH = HOME + "/.ladspa"
  347. DEFAULT_LADSPA_PATH += ":/system/add-ons/media/ladspaplugins"
  348. DEFAULT_LADSPA_PATH += ":/system/lib/ladspa"
  349. DEFAULT_DSSI_PATH = HOME + "/.dssi"
  350. DEFAULT_DSSI_PATH += ":/system/add-ons/media/dssiplugins"
  351. DEFAULT_DSSI_PATH += ":/system/lib/dssi"
  352. DEFAULT_LV2_PATH = HOME + "/.lv2"
  353. DEFAULT_LV2_PATH += ":/system/add-ons/media/lv2plugins"
  354. DEFAULT_VST2_PATH = HOME + "/.vst"
  355. DEFAULT_VST2_PATH += ":/system/add-ons/media/vstplugins"
  356. DEFAULT_VST3_PATH = HOME + "/.vst3"
  357. DEFAULT_VST3_PATH += ":/system/add-ons/media/vst3plugins"
  358. DEFAULT_CLAP_PATH = HOME + "/.clap"
  359. DEFAULT_CLAP_PATH += ":/system/add-ons/media/clapplugins"
  360. elif MACOS:
  361. splitter = ":"
  362. DEFAULT_LADSPA_PATH = HOME + "/Library/Audio/Plug-Ins/LADSPA"
  363. DEFAULT_LADSPA_PATH += ":/Library/Audio/Plug-Ins/LADSPA"
  364. DEFAULT_DSSI_PATH = HOME + "/Library/Audio/Plug-Ins/DSSI"
  365. DEFAULT_DSSI_PATH += ":/Library/Audio/Plug-Ins/DSSI"
  366. DEFAULT_LV2_PATH = HOME + "/Library/Audio/Plug-Ins/LV2"
  367. DEFAULT_LV2_PATH += ":/Library/Audio/Plug-Ins/LV2"
  368. DEFAULT_VST2_PATH = HOME + "/Library/Audio/Plug-Ins/VST"
  369. DEFAULT_VST2_PATH += ":/Library/Audio/Plug-Ins/VST"
  370. DEFAULT_VST3_PATH = HOME + "/Library/Audio/Plug-Ins/VST3"
  371. DEFAULT_VST3_PATH += ":/Library/Audio/Plug-Ins/VST3"
  372. DEFAULT_CLAP_PATH = HOME + "/Library/Audio/Plug-Ins/CLAP"
  373. DEFAULT_CLAP_PATH += ":/Library/Audio/Plug-Ins/CLAP"
  374. DEFAULT_JSFX_PATH = HOME + "/Library/Application Support/REAPER/Effects"
  375. #DEFAULT_JSFX_PATH += ":/Applications/REAPER.app/Contents/InstallFiles/Effects"
  376. else:
  377. splitter = ":"
  378. CONFIG_HOME = os.getenv("XDG_CONFIG_HOME", HOME + "/.config")
  379. DEFAULT_LADSPA_PATH = HOME + "/.ladspa"
  380. DEFAULT_LADSPA_PATH += ":/usr/lib/ladspa"
  381. DEFAULT_LADSPA_PATH += ":/usr/local/lib/ladspa"
  382. DEFAULT_DSSI_PATH = HOME + "/.dssi"
  383. DEFAULT_DSSI_PATH += ":/usr/lib/dssi"
  384. DEFAULT_DSSI_PATH += ":/usr/local/lib/dssi"
  385. DEFAULT_LV2_PATH = HOME + "/.lv2"
  386. DEFAULT_LV2_PATH += ":/usr/lib/lv2"
  387. DEFAULT_LV2_PATH += ":/usr/local/lib/lv2"
  388. DEFAULT_VST2_PATH = HOME + "/.vst"
  389. DEFAULT_VST2_PATH += ":/usr/lib/vst"
  390. DEFAULT_VST2_PATH += ":/usr/local/lib/vst"
  391. DEFAULT_VST2_PATH += HOME + "/.lxvst"
  392. DEFAULT_VST2_PATH += ":/usr/lib/lxvst"
  393. DEFAULT_VST2_PATH += ":/usr/local/lib/lxvst"
  394. DEFAULT_VST3_PATH = HOME + "/.vst3"
  395. DEFAULT_VST3_PATH += ":/usr/lib/vst3"
  396. DEFAULT_VST3_PATH += ":/usr/local/lib/vst3"
  397. DEFAULT_CLAP_PATH = HOME + "/.clap"
  398. DEFAULT_CLAP_PATH += ":/usr/lib/clap"
  399. DEFAULT_CLAP_PATH += ":/usr/local/lib/clap"
  400. DEFAULT_SF2_PATH = HOME + "/.sounds/sf2"
  401. DEFAULT_SF2_PATH += ":" + HOME + "/.sounds/sf3"
  402. DEFAULT_SF2_PATH += ":/usr/share/sounds/sf2"
  403. DEFAULT_SF2_PATH += ":/usr/share/sounds/sf3"
  404. DEFAULT_SF2_PATH += ":/usr/share/soundfonts"
  405. DEFAULT_SFZ_PATH = HOME + "/.sounds/sfz"
  406. DEFAULT_SFZ_PATH += ":/usr/share/sounds/sfz"
  407. DEFAULT_JSFX_PATH = CONFIG_HOME + "/REAPER/Effects"
  408. #DEFAULT_JSFX_PATH += ":" + "/opt/REAPER/InstallData/Effects"
  409. if not WINDOWS:
  410. winePrefix = os.getenv("WINEPREFIX")
  411. if not winePrefix:
  412. winePrefix = HOME + "/.wine"
  413. if os.path.exists(winePrefix):
  414. DEFAULT_VST2_PATH += ":" + winePrefix + "/drive_c/Program Files/VstPlugins"
  415. DEFAULT_VST2_PATH += ":" + winePrefix + "/drive_c/Program Files/VSTPlugins"
  416. DEFAULT_VST2_PATH += ":" + winePrefix + "/drive_c/Program Files/Steinberg/VstPlugins"
  417. DEFAULT_VST2_PATH += ":" + winePrefix + "/drive_c/Program Files/Steinberg/VSTPlugins"
  418. DEFAULT_VST2_PATH += ":" + winePrefix + "/drive_c/Program Files/Common Files/VST2"
  419. DEFAULT_VST3_PATH += ":" + winePrefix + "/drive_c/Program Files/Common Files/VST3"
  420. DEFAULT_CLAP_PATH += ":" + winePrefix + "/drive_c/Program Files/Common Files/CLAP"
  421. if kIs64bit and os.path.exists(winePrefix + "/drive_c/Program Files (x86)"):
  422. DEFAULT_VST2_PATH += ":" + winePrefix + "/drive_c/Program Files (x86)/VstPlugins"
  423. DEFAULT_VST2_PATH += ":" + winePrefix + "/drive_c/Program Files (x86)/VSTPlugins"
  424. DEFAULT_VST2_PATH += ":" + winePrefix + "/drive_c/Program Files (x86)/Steinberg/VstPlugins"
  425. DEFAULT_VST2_PATH += ":" + winePrefix + "/drive_c/Program Files (x86)/Steinberg/VSTPlugins"
  426. DEFAULT_VST3_PATH += ":" + winePrefix + "/drive_c/Program Files (x86)/Common Files/VST3"
  427. DEFAULT_CLAP_PATH += ":" + winePrefix + "/drive_c/Program Files (x86)/Common Files/CLAP"
  428. del winePrefix
  429. # ------------------------------------------------------------------------------------------------------------
  430. # Default Plugin Folders (set)
  431. readEnvVars = True
  432. if WINDOWS:
  433. # Check if running Wine. If yes, ignore env vars
  434. # pylint: disable=import-error
  435. from winreg import ConnectRegistry, OpenKey, CloseKey, HKEY_CURRENT_USER
  436. # pylint: enable=import-error
  437. _reg = ConnectRegistry(None, HKEY_CURRENT_USER)
  438. try:
  439. _key = OpenKey(_reg, r"SOFTWARE\Wine")
  440. CloseKey(_key)
  441. del _key
  442. readEnvVars = False
  443. except:
  444. pass
  445. CloseKey(_reg)
  446. del _reg
  447. if readEnvVars:
  448. CARLA_DEFAULT_LADSPA_PATH = os.getenv("LADSPA_PATH", DEFAULT_LADSPA_PATH).split(splitter)
  449. CARLA_DEFAULT_DSSI_PATH = os.getenv("DSSI_PATH", DEFAULT_DSSI_PATH).split(splitter)
  450. CARLA_DEFAULT_LV2_PATH = os.getenv("LV2_PATH", DEFAULT_LV2_PATH).split(splitter)
  451. CARLA_DEFAULT_VST2_PATH = os.getenv("VST_PATH", DEFAULT_VST2_PATH).split(splitter)
  452. CARLA_DEFAULT_VST3_PATH = os.getenv("VST3_PATH", DEFAULT_VST3_PATH).split(splitter)
  453. CARLA_DEFAULT_CLAP_PATH = os.getenv("CLAP_PATH", DEFAULT_CLAP_PATH).split(splitter)
  454. CARLA_DEFAULT_SF2_PATH = os.getenv("SF2_PATH", DEFAULT_SF2_PATH).split(splitter)
  455. CARLA_DEFAULT_SFZ_PATH = os.getenv("SFZ_PATH", DEFAULT_SFZ_PATH).split(splitter)
  456. CARLA_DEFAULT_JSFX_PATH = os.getenv("JSFX_PATH", DEFAULT_JSFX_PATH).split(splitter)
  457. else:
  458. CARLA_DEFAULT_LADSPA_PATH = DEFAULT_LADSPA_PATH.split(splitter)
  459. CARLA_DEFAULT_DSSI_PATH = DEFAULT_DSSI_PATH.split(splitter)
  460. CARLA_DEFAULT_LV2_PATH = DEFAULT_LV2_PATH.split(splitter)
  461. CARLA_DEFAULT_VST2_PATH = DEFAULT_VST2_PATH.split(splitter)
  462. CARLA_DEFAULT_VST3_PATH = DEFAULT_VST3_PATH.split(splitter)
  463. CARLA_DEFAULT_CLAP_PATH = DEFAULT_CLAP_PATH.split(splitter)
  464. CARLA_DEFAULT_SF2_PATH = DEFAULT_SF2_PATH.split(splitter)
  465. CARLA_DEFAULT_SFZ_PATH = DEFAULT_SFZ_PATH.split(splitter)
  466. CARLA_DEFAULT_JSFX_PATH = DEFAULT_JSFX_PATH.split(splitter)
  467. # ------------------------------------------------------------------------------------------------------------
  468. # Default Plugin Folders (cleanup)
  469. del DEFAULT_LADSPA_PATH
  470. del DEFAULT_DSSI_PATH
  471. del DEFAULT_LV2_PATH
  472. del DEFAULT_VST2_PATH
  473. del DEFAULT_VST3_PATH
  474. del DEFAULT_CLAP_PATH
  475. del DEFAULT_SF2_PATH
  476. del DEFAULT_SFZ_PATH
  477. # ------------------------------------------------------------------------------------------------------------
  478. # Global Carla object
  479. class CarlaObject():
  480. def __init__(self):
  481. self.cnprefix = "" # Client name prefix
  482. self.gui = None # Host Window
  483. self.nogui = False # Skip UI
  484. self.term = False # Terminated by OS signal
  485. self.felib = None # Frontend lib object
  486. self.utils = None # Utils object
  487. gCarla = CarlaObject()
  488. # ------------------------------------------------------------------------------------------------------------
  489. # Set CWD
  490. CWD = sys.path[0]
  491. if not CWD:
  492. CWD = os.path.dirname(sys.argv[0])
  493. # make it work with cxfreeze
  494. if os.path.isfile(CWD):
  495. CWD = os.path.dirname(CWD)
  496. if CWD.endswith("/lib"):
  497. CWD = CWD.rsplit("/lib",1)[0]
  498. CXFREEZE = True
  499. if not WINDOWS:
  500. os.environ['CARLA_MAGIC_FILE'] = os.path.join(CWD, "magic.mgc")
  501. else:
  502. CXFREEZE = False
  503. # ------------------------------------------------------------------------------------------------------------
  504. # Set DLL_EXTENSION
  505. if WINDOWS:
  506. DLL_EXTENSION = "dll"
  507. elif MACOS:
  508. DLL_EXTENSION = "dylib"
  509. else:
  510. DLL_EXTENSION = "so"
  511. # ------------------------------------------------------------------------------------------------------------
  512. # Find decimal points for a parameter, using step and stepSmall
  513. def countDecimalPoints(step, stepSmall):
  514. if stepSmall >= 1.0:
  515. return 0
  516. if step >= 1.0:
  517. return 2
  518. count = 0
  519. value = fmod(abs(step), 1)
  520. while 0.0001 < value < 0.999 and count < 6:
  521. value = fmod(value*10, 1)
  522. count += 1
  523. return count
  524. # ------------------------------------------------------------------------------------------------------------
  525. # Check if a value is a number (float support)
  526. def isNumber(value):
  527. try:
  528. float(value)
  529. return True
  530. except:
  531. return False
  532. # ------------------------------------------------------------------------------------------------------------
  533. # Convert a value to a list
  534. def toList(value):
  535. if value is None:
  536. return []
  537. if not isinstance(value, list):
  538. return [value]
  539. return value
  540. # ------------------------------------------------------------------------------------------------------------
  541. # Get Icon from user theme, using our own as backup (Oxygen)
  542. def getIcon(icon, size, qrcformat):
  543. return QIcon.fromTheme(icon, QIcon(":/%ix%i/%s.%s" % (size, size, icon, qrcformat)))
  544. # ------------------------------------------------------------------------------------------------------------
  545. # Handle some basic command-line arguments shared between all carla variants
  546. def handleInitialCommandLineArguments(file):
  547. initName = os.path.basename(file) if (file is not None and os.path.dirname(file) in PATH) else sys.argv[0]
  548. libPrefix = None
  549. readPrefixNext = False
  550. for arg in sys.argv[1:]:
  551. if arg.startswith("--with-appname="):
  552. initName = os.path.basename(arg.replace("--with-appname=", ""))
  553. elif arg.startswith("--with-libprefix="):
  554. libPrefix = arg.replace("--with-libprefix=", "")
  555. elif arg.startswith("--osc-gui="):
  556. gCarla.nogui = int(arg.replace("--osc-gui=", ""))
  557. elif arg.startswith("--cnprefix="):
  558. gCarla.cnprefix = arg.replace("--cnprefix=", "")
  559. elif arg == "--cnprefix":
  560. readPrefixNext = True
  561. elif arg == "--gdb":
  562. pass
  563. elif arg in ("-n", "--n", "-no-gui", "--no-gui", "-nogui", "--nogui"):
  564. gCarla.nogui = True
  565. elif MACOS and arg.startswith("-psn_"):
  566. pass
  567. elif arg in ("-h", "--h", "-help", "--help"):
  568. print("Usage: %s [OPTION]... [FILE|URL]" % initName)
  569. print("")
  570. print(" where FILE can be a Carla project or preset file to be loaded, or URL if using Carla-Control")
  571. print("")
  572. print(" and OPTION can be one or more of the following:")
  573. print("")
  574. print(" --cnprefix\t Set a prefix for client names in multi-client mode.")
  575. if isinstance(gCarla.nogui, bool):
  576. if X_LIBDIR_X is not None:
  577. print(" --gdb \t Run Carla inside gdb.")
  578. print(" -n,--no-gui \t Run Carla headless, don't show UI.")
  579. print("")
  580. print(" -h,--help \t Print this help text and exit.")
  581. print(" -v,--version \t Print version information and exit.")
  582. print("")
  583. if not isinstance(gCarla.nogui, bool):
  584. print("NOTE: when using %s the FILE is only valid the first time the backend is started" % initName)
  585. sys.exit(1)
  586. sys.exit(0)
  587. elif arg in ("-v", "--v", "-version", "--version"):
  588. pathBinaries, pathResources = getPaths(libPrefix)
  589. print("Using Carla version %s" % VERSION)
  590. print(" Python version: %s" % sys.version.split(" ",1)[0])
  591. print(" Qt version: %s" % QT_VERSION_STR)
  592. print(" PyQt version: %s" % PYQT_VERSION_STR)
  593. print(" Binary dir: %s" % pathBinaries)
  594. print(" Resources dir: %s" % pathResources)
  595. sys.exit(1 if gCarla.nogui else 0)
  596. elif readPrefixNext:
  597. readPrefixNext = False
  598. gCarla.cnprefix = arg
  599. if gCarla.nogui and not isinstance(gCarla.nogui, bool):
  600. if os.fork():
  601. # pylint: disable=protected-access
  602. os._exit(0)
  603. # pylint: enable=protected-access
  604. else:
  605. os.setsid()
  606. return (initName, libPrefix)
  607. # ------------------------------------------------------------------------------------------------------------
  608. # Get initial project file (as passed in the command-line parameters)
  609. def getInitialProjectFile(skipExistCheck = False):
  610. # NOTE: PyQt mishandles unicode characters, we directly use sys.argv instead of qApp->arguments()
  611. # see https://riverbankcomputing.com/pipermail/pyqt/2015-January/035395.html
  612. args = sys.argv[1:]
  613. readPrefixNext = False
  614. for arg in args:
  615. if readPrefixNext:
  616. readPrefixNext = False
  617. continue
  618. if arg.startswith("--cnprefix="):
  619. continue
  620. if arg.startswith("--osc-gui="):
  621. continue
  622. if arg.startswith("--with-appname="):
  623. continue
  624. if arg.startswith("--with-libprefix="):
  625. continue
  626. if arg == "--cnprefix":
  627. readPrefixNext = True
  628. continue
  629. if arg in ("-n", "--n", "-no-gui", "--no-gui", "-nogui", "--nogui", "--gdb"):
  630. continue
  631. if MACOS and arg.startswith("-psn_"):
  632. continue
  633. arg = os.path.expanduser(arg)
  634. if skipExistCheck or os.path.exists(arg):
  635. return arg
  636. return None
  637. # ------------------------------------------------------------------------------------------------------------
  638. # Get paths (binaries, resources)
  639. def getPaths(libPrefix = None):
  640. CWDl = CWD.lower()
  641. # adjust for special distros
  642. libdir = os.path.basename(os.path.normpath(X_LIBDIR_X)) if X_LIBDIR_X else "lib"
  643. datadir = os.path.basename(os.path.normpath(X_DATADIR_X)) if X_DATADIR_X else "share"
  644. # standalone, installed system-wide linux
  645. if libPrefix is not None:
  646. pathBinaries = os.path.join(libPrefix, libdir, "carla")
  647. pathResources = os.path.join(libPrefix, datadir, "carla", "resources")
  648. # standalone, local source
  649. elif CWDl.endswith("frontend"):
  650. pathBinaries = os.path.abspath(os.path.join(CWD, "..", "..", "bin"))
  651. pathResources = os.path.join(pathBinaries, "resources")
  652. # plugin
  653. elif CWDl.endswith("resources"):
  654. # installed system-wide linux
  655. if CWDl.endswith("/share/carla/resources"):
  656. pathBinaries = os.path.abspath(os.path.join(CWD, "..", "..", "..", libdir, "carla"))
  657. pathResources = CWD
  658. # local source
  659. elif CWDl.endswith("native-plugins%sresources" % os.sep):
  660. pathBinaries = os.path.abspath(os.path.join(CWD, "..", "..", "..", "bin"))
  661. pathResources = CWD
  662. # other
  663. else:
  664. pathBinaries = os.path.abspath(os.path.join(CWD, ".."))
  665. pathResources = CWD
  666. # everything else
  667. else:
  668. pathBinaries = CWD
  669. pathResources = os.path.join(pathBinaries, "resources")
  670. return (pathBinaries, pathResources)
  671. # ------------------------------------------------------------------------------------------------------------
  672. # Signal handler
  673. # TODO move to carla_host.py or something
  674. def signalHandler(sig, frame):
  675. if sig in (SIGINT, SIGTERM):
  676. gCarla.term = True
  677. if gCarla.gui is not None:
  678. gCarla.gui.SIGTERM.emit()
  679. elif haveSIGUSR1 and sig == SIGUSR1:
  680. if gCarla.gui is not None:
  681. gCarla.gui.SIGUSR1.emit()
  682. def setUpSignals():
  683. signal(SIGINT, signalHandler)
  684. signal(SIGTERM, signalHandler)
  685. if not haveSIGUSR1:
  686. return
  687. signal(SIGUSR1, signalHandler)
  688. # ------------------------------------------------------------------------------------------------------------
  689. # QLineEdit and QPushButton combo
  690. def getAndSetPath(parent, lineEdit):
  691. newPath = QFileDialog.getExistingDirectory(parent, parent.tr("Set Path"), lineEdit.text(), QFileDialog.ShowDirsOnly)
  692. if newPath:
  693. lineEdit.setText(newPath)
  694. return newPath
  695. # ------------------------------------------------------------------------------------------------------------
  696. # Backwards-compatible horizontalAdvance/width call, depending on Qt version
  697. def fontMetricsHorizontalAdvance(fontMetrics, string):
  698. if QT_VERSION >= 0x50b00:
  699. return fontMetrics.horizontalAdvance(string)
  700. return fontMetrics.width(string)
  701. # ------------------------------------------------------------------------------------------------------------
  702. # Custom QMessageBox which resizes itself to fit text
  703. class QMessageBoxWithBetterWidth(QMessageBox):
  704. def __init__(self, parent):
  705. QMessageBox.__init__(self, parent)
  706. def showEvent(self, event):
  707. fontMetrics = self.fontMetrics()
  708. lines = self.text().strip().split("\n") + self.informativeText().strip().split("\n")
  709. if lines:
  710. width = 0
  711. for line in lines:
  712. width = max(fontMetricsHorizontalAdvance(fontMetrics, line), width)
  713. self.layout().setColumnMinimumWidth(2, width + 12)
  714. QMessageBox.showEvent(self, event)
  715. # ------------------------------------------------------------------------------------------------------------
  716. # Custom MessageBox
  717. # pylint: disable=too-many-arguments
  718. def CustomMessageBox(parent, icon, title, text,
  719. extraText="",
  720. buttons=QMessageBox.Yes|QMessageBox.No,
  721. defButton=QMessageBox.No):
  722. msgBox = QMessageBoxWithBetterWidth(parent)
  723. msgBox.setIcon(icon)
  724. msgBox.setWindowTitle(title)
  725. msgBox.setText(text)
  726. msgBox.setInformativeText(extraText)
  727. msgBox.setStandardButtons(buttons)
  728. msgBox.setDefaultButton(defButton)
  729. return msgBox.exec_()
  730. # pylint: enable=too-many-arguments
  731. # ------------------------------------------------------------------------------------------------------------