Starting Xembbed code test
[klaudia.git] / src / mainw.py
blob60b984217b3b256077a9c50fe70bef52b7863072
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
5 # Imports
6 import jack
7 from os import path
8 from functools import partial
9 from time import ctime
10 from PyQt4.QtCore import Qt, QFile, QIODevice, QSize, QTextStream, QTimer, SIGNAL, SLOT
11 from PyQt4.QtGui import QDialog, QDialogButtonBox, QTableWidgetItem
12 from PyQt4.QtXml import QDomDocument
13 from PyKDE4.kdecore import i18n, KUrl
14 from PyKDE4.kdeui import KAction, KIcon, KMessageBox, KStandardAction, KXmlGuiWindow
15 from PyKDE4.kio import KFileDialog
16 import shared, renderw, tb_main, tb_apps, tb_connections, tb_mixer, ui_mainw, ui_dia_studios
19 # Main Window
20 class KlaudiaMainW(KXmlGuiWindow, ui_mainw.Ui_KlaudiaMainW):
21 def __init__(self, *args):
22 KXmlGuiWindow.__init__(self, *args)
23 self.setupUi(self)
25 # Load a project if an argument is given
26 if (shared.OpenFolder):
27 url = shared.OpenFolder
28 if (path.exists(url+"/project.klaudia")):
29 shared.p_folder = url
30 else:
31 KMessageBox.sorry(self, i18n("The Klaudia project cannot be loaded!"), i18n("Sorry"))
32 jack.detach()
33 exit(-1)
35 if (not shared.TESTING):
36 # Read project file
37 xml = QDomDocument()
38 filename = QFile(shared.p_folder+"/project.klaudia")
39 if not filename.open(QIODevice.ReadOnly):
40 print "error here, 1"
41 if not xml.setContent(filename):
42 print "error here, 2"
43 filename.close()
45 # Check if project file is not corrupted
46 content = xml.documentElement()
47 if (content.tagName() != "KLAUDIA"):
48 print "error here, 3"
50 # Get values from XML - the big code
51 node = content.firstChild()
52 while not node.isNull():
53 if (node.toElement().tagName() == "Properties"):
54 properties = node.toElement().firstChild()
55 while not properties.isNull():
56 name = properties.toElement().tagName()
57 text = properties.toElement().text()
58 if (name == "Name"): shared.p_name = text
59 elif (name == "Author"): shared.p_author = text
60 elif (name == "Composer"): shared.p_composer = text
61 elif (name == "Album"): shared.p_album = text
62 elif (name == "Track"): shared.p_track = text
63 elif (name == "BPM"): shared.p_bpm = text
64 elif (name == "Time-Start"): shared.p_time_start = text
65 elif (name == "Time-End"): shared.p_time_end = text
66 properties = properties.nextSibling()
67 node = node.nextSibling()
68 if (node.toElement().tagName() == "Ladish"):
69 properties = node.toElement().firstChild()
70 while not properties.isNull():
71 name = properties.toElement().tagName()
72 text = properties.toElement().text()
73 if (name == "Project-Name"): shared.ladish_project_name = text
74 properties = properties.nextSibling()
75 node = node.nextSibling()
77 if (not shared.TESTING):
78 if (str(shared.ladish_project_name) != str(shared.studioBus.GetName())):
79 print str(shared.ladish_project_name)
80 print str(shared.studioBus.GetName())
81 shared.controlBus.LoadStudio(str(shared.ladish_project_name))
82 shared.jackBus.StartServer()
83 if (not bool(shared.jackBus.IsStarted())):
84 shared.jackBus.StartServer()
85 jack.attach("Klaudia")
87 # Set BPM
88 self.spin_bpm.setValue(float(shared.p_bpm))
90 # Internal timers
91 self.jackTimer = QTimer()
92 self.cpuTimer = QTimer()
93 self.jackTimer.start(250)
94 self.cpuTimer.start(2000)
96 # Set-up actions
97 self.act_render = KAction(KIcon("media-record"), i18n("&Render..."), self)
98 self.act_export_zip = KAction(i18n("&Export Project to Zip file..."), self)
99 self.act_playpause = KAction(KIcon("media-playback-start"), i18n("&Play"), self)
100 self.act_playpause.setCheckable(True)
101 self.act_stop = KAction(KIcon("media-playback-stop"), i18n("&Stop"), self)
102 self.act_backwards = KAction(KIcon("media-seek-backward"), i18n("&Backwards"), self)
103 self.act_forwards = KAction(KIcon("media-seek-forward"), i18n("&Forwards"), self)
104 self.act_bf_16 = KAction(i18n("16"), self)
105 self.act_bf_32 = KAction(i18n("32"), self)
106 self.act_bf_64 = KAction(i18n("64"), self)
107 self.act_bf_128 = KAction(i18n("128"), self)
108 self.act_bf_256 = KAction(i18n("256"), self)
109 self.act_bf_512 = KAction(i18n("512"), self)
110 self.act_bf_1024 = KAction(i18n("1024"), self)
111 self.act_bf_2048 = KAction(i18n("2048"), self)
112 self.act_bf_4096 = KAction(i18n("4096"), self)
113 self.act_import_ladish = KAction(i18n("Import &Ladish Project..."), self)
114 self.act_show_fake_toolbar = KAction(i18n("Show &Toolbar"), self)
115 self.act_show_fake_toolbar.setCheckable(True)
116 self.act_show_fake_statusbar = KAction(i18n("Show St&atusbar"), self)
117 self.act_show_fake_statusbar.setCheckable(True)
119 self.act_export_zip.setEnabled(False)
121 self.act_bf_list = ( self.act_bf_16, self.act_bf_32, self.act_bf_64, self.act_bf_128, self.act_bf_256, self.act_bf_512,
122 self.act_bf_1024, self.act_bf_2048, self.act_bf_4096 )
124 size = jack.get_buffer_size()
125 for i in range(len(self.act_bf_list)):
126 self.act_bf_list[i].setCheckable(True)
127 if (self.act_bf_list[i].text().replace("&","") == str(size)):
128 self.act_bf_list[i].setChecked(True)
130 setting_tbV = True
131 self.act_show_fake_toolbar.setChecked(setting_tbV)
132 self.toolbarW.setVisible(setting_tbV)
134 setting_sbV = True
135 self.act_show_fake_statusbar.setChecked(setting_sbV)
136 self.statusbarW.setVisible(setting_sbV)
138 # Standard actions
139 KStandardAction.open(self.NIY, self.actionCollection())
140 KStandardAction.openRecent(self.NIY, self.actionCollection())
141 KStandardAction.save(self.saveProject, self.actionCollection())
142 KStandardAction.saveAs(self.saveAs, self.actionCollection())
143 KStandardAction.close(self.close, self.actionCollection())
144 KStandardAction.preferences(self.NIY, self.actionCollection())
146 # Add actions
147 self.actionCollection().addAction("render", self.act_render)
148 self.actionCollection().addAction("export_zip", self.act_export_zip)
149 self.actionCollection().addAction("tr_play", self.act_playpause)
150 self.actionCollection().addAction("tr_stop", self.act_stop)
151 self.actionCollection().addAction("tr_back", self.act_backwards)
152 self.actionCollection().addAction("tr_next", self.act_forwards)
153 self.actionCollection().addAction("bf_16", self.act_bf_16)
154 self.actionCollection().addAction("bf_32", self.act_bf_32)
155 self.actionCollection().addAction("bf_64", self.act_bf_64)
156 self.actionCollection().addAction("bf_128", self.act_bf_128)
157 self.actionCollection().addAction("bf_256", self.act_bf_256)
158 self.actionCollection().addAction("bf_512", self.act_bf_512)
159 self.actionCollection().addAction("bf_1024", self.act_bf_1024)
160 self.actionCollection().addAction("bf_2048", self.act_bf_2048)
161 self.actionCollection().addAction("bf_4096", self.act_bf_4096)
162 self.actionCollection().addAction("import_ladish", self.act_import_ladish)
163 self.actionCollection().addAction("show_fake_toolbar", self.act_show_fake_toolbar)
164 self.actionCollection().addAction("show_fake_statusbar", self.act_show_fake_statusbar)
166 # Set Button Icons
167 self.b_playpause.setIcon(KIcon("media-playback-start"))
168 self.b_stop.setIcon(KIcon("media-playback-stop"))
169 self.b_backwards.setIcon(KIcon("media-seek-backward"))
170 self.b_forwards.setIcon(KIcon("media-seek-forward"))
171 self.b_open.setIcon(KIcon("document-open"))
172 self.b_save.setIcon(KIcon("document-save"))
173 self.b_save_as.setIcon(KIcon("document-save-as"))
174 self.b_render.setIcon(KIcon("media-record"))
175 self.b_add_new.setIcon(KIcon("list-add"))
177 # Interact actions to buttons
178 self.connect(self.act_playpause, SIGNAL("triggered(bool)"), self.b_playpause, SLOT("setChecked(bool)"))
179 self.connect(self.b_playpause, SIGNAL("clicked(bool)"), self.act_playpause, SLOT("setChecked(bool)"))
181 # Custom toolbar and statusbar
182 self.connect(self.act_show_fake_toolbar, SIGNAL("triggered(bool)"), self.toolbarW, SLOT("setVisible(bool)"))
183 self.connect(self.act_show_fake_statusbar, SIGNAL("triggered(bool)"), self.statusbarW, SLOT("setVisible(bool)"))
185 # Connect actions to functions
186 self.connect(self.act_render, SIGNAL("triggered()"), self.renderW)
187 self.connect(self.act_playpause, SIGNAL("triggered(bool)"), self.jackPlayPause)
188 self.connect(self.act_stop, SIGNAL("triggered()"), self.jackStop)
189 self.connect(self.act_backwards, SIGNAL("triggered()"), self.jackBackwards)
190 self.connect(self.act_forwards, SIGNAL("triggered()"), self.jackForwards)
191 self.connect(self.act_bf_16, SIGNAL("triggered()"), partial(self.setBufferSize, 16))
192 self.connect(self.act_bf_32, SIGNAL("triggered()"), partial(self.setBufferSize, 32))
193 self.connect(self.act_bf_64, SIGNAL("triggered()"), partial(self.setBufferSize, 64))
194 self.connect(self.act_bf_128, SIGNAL("triggered()"), partial(self.setBufferSize, 128))
195 self.connect(self.act_bf_256, SIGNAL("triggered()"), partial(self.setBufferSize, 256))
196 self.connect(self.act_bf_512, SIGNAL("triggered()"), partial(self.setBufferSize, 512))
197 self.connect(self.act_bf_1024, SIGNAL("triggered()"), partial(self.setBufferSize, 1024))
198 self.connect(self.act_bf_2048, SIGNAL("triggered()"), partial(self.setBufferSize, 2048))
199 self.connect(self.act_bf_4096, SIGNAL("triggered()"), partial(self.setBufferSize, 4096))
200 self.connect(self.act_import_ladish, SIGNAL("triggered()"), self.importLadishStudio)
202 # Connect tab-buttons to pages
203 self.connect(self.tb_main, SIGNAL("clicked()"), partial(self.changeTabButton, "main"))
204 self.connect(self.tb_apps, SIGNAL("clicked()"), partial(self.changeTabButton, "apps"))
205 self.connect(self.tb_connections, SIGNAL("clicked()"), partial(self.changeTabButton, "connections"))
206 self.connect(self.tb_mixer, SIGNAL("clicked()"), partial(self.changeTabButton, "mixer"))
208 # Connect buttons to functions
209 self.connect(self.b_playpause, SIGNAL("clicked(bool)"), self.jackPlayPause)
210 self.connect(self.b_stop, SIGNAL("clicked()"), self.jackStop)
211 self.connect(self.b_backwards, SIGNAL("clicked()"), self.jackBackwards)
212 self.connect(self.b_forwards, SIGNAL("clicked()"), self.jackForwards)
213 self.connect(self.b_open, SIGNAL("clicked()"), self.NIY)
214 self.connect(self.b_save, SIGNAL("clicked()"), self.saveProject)
215 self.connect(self.b_save_as, SIGNAL("clicked()"), self.saveAs)
216 self.connect(self.b_render, SIGNAL("clicked()"), self.renderW)
217 self.connect(self.b_add_new, SIGNAL("clicked()"), self.addCustom)
219 # Connect other stuff
220 self.connect(self.cpuTimer, SIGNAL("timeout()"), self.updateDSPLoad)
221 self.connect(self.jackTimer, SIGNAL("timeout()"), self.updateTransport)
222 self.connect(self.transportSlider, SIGNAL("sliderReleased()"), self.updateSlider)
225 # Set-up the GUI (Part 1, create the KDE stuff)
226 self.setupGUI(QSize(600, 400), KXmlGuiWindow.Default, shared.Path+"/klaudia.rc")
228 # Set-up the GUI (Part 2, set custom menu, tool-bar and status-bar)
229 self.menuLayout.addWidget(self.menuBar())
230 self.toolBar().deleteLater()
231 self.statusBar().setVisible(False)
232 self.actionCollection().action(u'options_configure_toolbars').setVisible(False)
233 self.actionCollection().action(u'options_show_statusbar').setVisible(False)
234 self.connect(self.statusBar(), SIGNAL("messageChanged(QString)"), self.sbW_msg_label, SLOT("setText(QString)"))
236 # Set-up the GUI (Part 3, Load pages)
237 self.w_main = tb_main.MainW()
238 self.w_apps = tb_apps.AppsW()
239 self.w_connections = tb_connections.ConnectionsW()
240 self.w_mixer = tb_mixer.MixerW()
241 #self.w_main.setVisible(False)
242 self.w_apps.setVisible(False)
243 self.w_connections.setVisible(False)
244 self.w_mixer.setVisible(False)
246 # Set-up the GUI (Part 4, set default page)
247 self.w_main.setVisible(True)
248 self.centralW.layout().addWidget(self.w_main)
249 self.tb_main.setChecked(True)
250 self.currentTabPage = "main"
252 # Set-up the GUI (Part 5, update variables)
253 self.updateJackVars()
254 self.updateDSPLoad()
255 self.updateTransport()
258 def NIY(self):
259 KMessageBox.sorry(self, i18n("NOT IMPLEMENTED YET"), i18n("Sorry"))
261 def addCustom(self):
262 tb_main.NewAppW().exec_()
264 def saveProject(self):
265 url = shared.p_folder
266 filename = QFile(url+"/project.klaudia")
267 if not filename.open(QIODevice.WriteOnly):
268 KMessageBox.error(self, i18n("Cannot write to selected directory - Permission Denied"), i18n("Error"))
269 jack.detach()
270 exit(-2)
271 #raise IOError, unicode(filename.errorString())
272 stream = QTextStream(filename)
273 stream.setCodec("UTF-8")
274 stream << ("<?xml version='1.0' encoding='UTF-8'?>\n"
275 "<!DOCTYPE KLAUDIA>\n"
276 "<KLAUDIA VERSION='0.1'>\n"
277 "<Properties>\n"
278 " <Name>%s</Name>\n"
279 " <Author>%s</Author>\n"
280 " <Composer>%s</Composer>\n"
281 " <Album>%s</Album>\n"
282 " <Track>%s</Track>\n"
283 " <BPM>%s</BPM>\n"
284 " <Time-Start>%s</Time-Start>\n"
285 " <Time-End>%s</Time-End>\n"
286 "</Properties>\n"
287 "<Ladish>\n"
288 " <Project-Name>%s</Project-Name>\n"
289 "</Ladish>\n"
290 "</KLAUDIA>\n" % (shared.p_name, shared.p_author, shared.p_composer,
291 shared.p_album, shared.p_track, shared.p_bpm, shared.p_time_start, shared.p_time_end, shared.ladish_project_name))
292 iconfile = open(url+"/.directory", "w")
293 iconfile.write("[Desktop Entry]\nIcon=%s\n" % shared.p_icon)
294 iconfile.close()
295 shared.studioBus.Save()
297 def saveAs(self):
298 pUrl = KFileDialog.getExistingDirectoryUrl(KUrl(shared.p_folder), self, i18n("Open Klaudia Project Folder"))
299 if not pUrl.fileName():
300 return
301 elif not pUrl.isLocalFile():
302 KMessageBox.error(self, i18n("The Selected folder cannot be used for a Klaudia project!"), i18n("Error"))
303 return
304 shared.p_folder = str(pUrl.toLocalFile())
305 self.saveProject()
307 def jackPlayPause(self, play):
308 if (play):
309 jack.transport_start()
310 self.act_playpause.setText("&Pause")
311 self.act_playpause.setIcon(KIcon("media-playback-pause"))
312 self.b_playpause.setIcon(KIcon("media-playback-pause"))
313 else:
314 jack.transport_stop()
315 self.act_playpause.setText("&Play")
316 self.act_playpause.setIcon(KIcon("media-playback-start"))
317 self.b_playpause.setIcon(KIcon("media-playback-start"))
319 def jackStop(self):
320 self.jackPlayPause(False)
321 self.b_playpause.setChecked(False)
322 jack.transport_locate(0)
323 #jack.transport_locate(shared.Globals.sliderMinValue*jack.get_sample_rate())
325 def jackBackwards(self):
326 pos = int(jack.get_current_transport_frame()) - 100000
327 if pos < 0:
328 jack.transport_locate(0)
329 else:
330 jack.transport_locate(pos)
332 def jackForwards(self):
333 jack.transport_locate(jack.get_current_transport_frame()+100000)
335 def setBufferSize(self, size):
336 jack.set_buffer_size(size)
337 for i in range(len(self.act_bf_list)):
338 if (self.act_bf_list[i].text().replace("&","") == str(size)):
339 self.act_bf_list[i].setChecked(True)
340 else:
341 self.act_bf_list[i].setChecked(False)
343 def changeTabButton(self, button):
344 if (button != self.currentTabPage):
345 self.currentTabPage = button
346 # Remove central widget
347 self.centralW.layout().takeAt(0)
348 # Restore all button states
349 self.tb_main.setChecked(False)
350 self.tb_apps.setChecked(False)
351 self.tb_connections.setChecked(False)
352 self.tb_mixer.setChecked(False)
353 # Hide all pages (we only need one)
354 self.w_main.setVisible(False)
355 self.w_apps.setVisible(False)
356 self.w_connections.setVisible(False)
357 self.w_mixer.setVisible(False)
358 if (button == "main"):
359 self.tb_main.setChecked(True)
360 wToLoad = self.w_main
361 elif (button == "apps"):
362 self.tb_apps.setChecked(True)
363 wToLoad = self.w_apps
364 elif (button == "connections"):
365 self.tb_connections.setChecked(True)
366 wToLoad = self.w_connections
367 elif (button == "mixer"):
368 self.tb_mixer.setChecked(True)
369 wToLoad = self.w_mixer
370 self.centralW.layout().addWidget(wToLoad)
371 wToLoad.setVisible(True)
372 else:
373 if (button == "main"): self.tb_main.setChecked(True)
374 elif (button == "apps"): self.tb_apps.setChecked(True)
375 elif (button == "connections"): self.tb_connections.setChecked(True)
376 elif (button == "mixer"): self.tb_mixer.setChecked(True)
379 def updateTransport(self):
380 state = jack.get_transport_state()
381 frame = jack.get_current_transport_frame()
382 rate = jack.get_sample_rate()
384 # Jack State
385 if (state == 0):
386 self.b_playpause.setChecked(False)
387 self.act_playpause.setText("&Play")
388 self.act_playpause.setIcon(KIcon("media-playback-start"))
389 self.b_playpause.setIcon(KIcon("media-playback-start"))
390 elif (state == 1 or state == 2):
391 self.b_playpause.setChecked(True)
392 self.act_playpause.setText("&Pause")
393 self.act_playpause.setIcon(KIcon("media-playback-pause"))
394 self.b_playpause.setIcon(KIcon("media-playback-pause"))
396 # Current Time
397 time = frame / rate
398 secs = time % 60
399 mins = (time / 60) % 60
400 hrs = (time / 3600) % 60
401 secH = minH = hrsH = ""
402 if secs < 10: secH = "0"
403 if mins < 10: minH = "0"
404 if hrs < 10: hrsH = "0"
405 self.l_time.setText(hrsH+str(hrs)+":"+minH+str(mins)+":"+secH+str(secs))
407 # Transport Slider
408 if not self.transportSlider.isSliderDown():
409 self.transportSlider.setMinimum(shared.Globals.sliderMinValue)
410 self.transportSlider.setMaximum(shared.Globals.sliderMaxValue)
411 self.transportSlider.setValue(time)
413 def updateDSPLoad(self):
414 load = int(jack.get_cpu_load())
415 self.sbW_cpu_bar.setValue(load)
417 def updateSlider(self):
418 value = self.transportSlider.value()
419 jack.transport_locate(value*jack.get_sample_rate())
421 def updateJackVars(self):
422 self.sbW_jack_SampleRate.setText(str(jack.get_sample_rate())+" Hz")
423 self.sbW_jack_BufferSize.setText(str(jack.get_buffer_size()))
424 if (jack.is_realtime()):
425 self.sbW_jack_RT.setText("RT")
426 else:
427 self.sbW_jack_RT.setText("")
429 def updateSizes(self):
430 self.sbW_msg.setMinimumSize(self.width()/4, 16)
431 self.sbW_msg.setMaximumSize(self.width()/4, 16)
432 self.sbW_jack.setMaximumSize(self.width()/3, 16)
433 self.sbW_cpu.setMaximumSize(self.width()/4, 16)
434 self.sbW_drops.setMaximumSize(self.width()/8, 16)
436 def resizeEvent(self, event):
437 self.updateSizes()
439 def renderW(self):
440 renderw.KlaudiaRenderW().exec_()
442 def importLadishStudio(self):
443 StudiosW().exec_()
446 class StudiosW(QDialog, ui_dia_studios.Ui_StudiosW):
447 def __init__(self, *args):
448 QDialog.__init__(self, *args)
449 self.setupUi(self)
451 self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(False)
452 self.listStudio.setColumnWidth(0, 200)
454 self.connect(self, SIGNAL("accepted()"), self.Done)
455 self.connect(self.listStudio, SIGNAL("currentCellChanged(int, int, int, int)"), self.checkOkButton)
457 list_of_ladish_studios = shared.controlBus.GetStudioList()
458 for i in range(len(list_of_ladish_studios)):
459 name = QTableWidgetItem(str(list_of_ladish_studios[i][0]))
460 time = QTableWidgetItem(ctime(float(list_of_ladish_studios[i][1][u'Modification Time'])))
461 self.listStudio.insertRow(i)
462 self.listStudio.setItem(i, 0, name)
463 self.listStudio.setItem(i, 1, time)
465 self.listStudio.sortByColumn(0, Qt.AscendingOrder)
467 def Done(self):
468 jack.detach()
469 shared.controlBus.LoadStudio(str(self.listStudio.item(self.listStudio.currentRow(), 0).text()))
470 shared.jackBus.StartServer()
471 jack.attach("Klaudia")
473 def checkOkButton(self, row, b, c, d):
474 if (row < 0):
475 self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(False)
476 else:
477 self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(True)