@@ -0,0 +1,398 @@ | |||
#!/usr/bin/env python3 | |||
# -*- coding: utf-8 -*- | |||
# PatchCanvas test application | |||
# Copyright (C) 2012 Filipe Coelho <falktx@gmail.com> | |||
# | |||
# This program is free software; you can redistribute it and/or modify | |||
# it under the terms of the GNU General Public License as published by | |||
# the Free Software Foundation; either version 2 of the License, or | |||
# any later version. | |||
# | |||
# This program is distributed in the hope that it will be useful, | |||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
# GNU General Public License for more details. | |||
# | |||
# For a full copy of the GNU General Public License see the COPYING file | |||
# Imports (Global) | |||
from PyQt4.QtCore import pyqtSlot, Qt, QSettings | |||
from PyQt4.QtGui import QApplication, QDialog, QDialogButtonBox, QPainter, QMainWindow | |||
# Imports (Custom Stuff) | |||
import patchcanvas | |||
import ui_catarina, icons_rc | |||
import ui_catarina_addgroup, ui_catarina_removegroup, ui_catarina_renamegroup | |||
import ui_catarina_addport, ui_catarina_removeport, ui_catarina_renameport | |||
import ui_catarina_connectports, ui_catarina_disconnectports | |||
from shared import * | |||
try: | |||
from PyQt4.QtOpenGL import QGLWidget | |||
hasGL = True | |||
except: | |||
hasGL = False | |||
iGroupId = 0 | |||
iGroupName = 1 | |||
iGroupSplit = 2 | |||
iGroupIcon = 3 | |||
iGroupPosId = 0 | |||
iGroupPosX_o = 1 | |||
iGroupPosY_o = 2 | |||
iGroupPosX_i = 3 | |||
iGroupPosY_i = 4 | |||
iPortGroup = 0 | |||
iPortId = 1 | |||
iPortName = 2 | |||
iPortMode = 3 | |||
iPortType = 4 | |||
iConnId = 0 | |||
iConnOutput = 1 | |||
iConnInput = 2 | |||
# Add Group Dialog | |||
class CatarinaAddGroupW(QDialog, ui_catarina_addgroup.Ui_CatarinaAddGroupW): | |||
def __init__(self, parent, group_list): | |||
QDialog.__init__(self, parent) | |||
self.setupUi(self) | |||
self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(False) | |||
self.m_group_list_names = [] | |||
for group in group_list: | |||
self.m_group_list_names.append(group[iGroupName]) | |||
self.connect(self, SIGNAL("accepted()"), SLOT("slot_setReturn()")) | |||
self.connect(self.le_group_name, SIGNAL("textChanged(QString)"), SLOT("slot_checkText(QString)")) | |||
self.ret_group_name = "" | |||
self.ret_group_split = False | |||
@pyqtSlot(str) | |||
def slot_checkText(self, text): | |||
check = bool(text and text not in self.m_group_list_names) | |||
self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(check) | |||
@pyqtSlot() | |||
def slot_setReturn(self): | |||
self.ret_group_name = self.le_group_name.text() | |||
self.ret_group_split = self.cb_split.isChecked() | |||
# Remove Group Dialog | |||
class CatarinaRemoveGroupW(QDialog, ui_catarina_removegroup.Ui_CatarinaRemoveGroupW): | |||
def __init__(self, parent, group_list): | |||
QDialog.__init__(self, parent) | |||
self.setupUi(self) | |||
self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(False) | |||
i = 0 | |||
for group in group_list: | |||
twi_group_id = QTableWidgetItem(str(group[iGroupId])) | |||
twi_group_name = QTableWidgetItem(group[iGroupName]) | |||
twi_group_split = QTableWidgetItem("Yes" if (group[iGroupSplit]) else "No") | |||
self.tw_group_list.insertRow(i) | |||
self.tw_group_list.setItem(i, 0, twi_group_id) | |||
self.tw_group_list.setItem(i, 1, twi_group_name) | |||
self.tw_group_list.setItem(i, 2, twi_group_split) | |||
i += 1 | |||
self.connect(self, SIGNAL("accepted()"), SLOT("slot_setReturn()")) | |||
self.connect(self.tw_group_list, SIGNAL("cellDoubleClicked(int, int)"), SLOT("accept()")) | |||
self.connect(self.tw_group_list, SIGNAL("currentCellChanged(int, int, int, int)"), SLOT("slot_checkCell(int)")) | |||
self.ret_group_id = -1 | |||
@pyqtSlot(int) | |||
def slot_checkCell(self, row): | |||
check = bool(row >= 0) | |||
self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(check) | |||
@pyqtSlot() | |||
def slot_setReturn(self): | |||
self.ret_group_id = int(self.tw_group_list.item(self.tw_group_list.currentRow(), 0).text()) | |||
# Rename Group Dialog | |||
class CatarinaRenameGroupW(QDialog, ui_catarina_renamegroup.Ui_CatarinaRenameGroupW): | |||
def __init__(self, parent, group_list): | |||
QDialog.__init__(self, parent) | |||
self.setupUi(self) | |||
self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(False) | |||
for group in group_list: | |||
self.cb_group_to_rename.addItem("%i - %s" % (group[iGroupId], group[iGroupName])) | |||
self.connect(self, SIGNAL("accepted()"), SLOT("slot_setReturn()")) | |||
self.connect(self.cb_group_to_rename, SIGNAL("currentIndexChanged(int)"), SLOT("slot_checkItem()")) | |||
self.connect(self.le_new_group_name, SIGNAL("textChanged(QString)"), SLOT("slot_checkText(QString)")) | |||
self.ret_group_id = -1 | |||
self.ret_new_group_name = "" | |||
@pyqtSlot() | |||
def slot_checkItem(self, index): | |||
self.checkText(self.le_new_group_name.text()) | |||
@pyqtSlot(str) | |||
def slot_checkText(self, text): | |||
if (self.cb_group_to_rename.count() > 0): | |||
group_name = self.cb_group_to_rename.currentText().split(" - ", 1)[1] | |||
check = bool(text and text != group_name) | |||
self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(check) | |||
@pyqtSlot() | |||
def slot_setReturn(self): | |||
self.ret_group_id = int(self.cb_group_to_rename.currentText().split(" - ", 1)[0]) | |||
self.ret_new_group_name = self.le_new_group_name.text() | |||
# Main Window | |||
class CatarinaMainW(QMainWindow, ui_catarina.Ui_CatarinaMainW): | |||
def __init__(self, parent=None): | |||
QMainWindow.__init__(self, parent) | |||
self.setupUi(self) | |||
self.settings = QSettings("Cadence", "Catarina") | |||
self.loadSettings() | |||
self.act_project_new.setIcon(getIcon("document-new")) | |||
self.act_project_open.setIcon(getIcon("document-open")) | |||
self.act_project_save.setIcon(getIcon("document-save")) | |||
self.act_project_save_as.setIcon(getIcon("document-save-as")) | |||
self.b_project_new.setIcon(getIcon("document-new")) | |||
self.b_project_open.setIcon(getIcon("document-open")) | |||
self.b_project_save.setIcon(getIcon("document-save")) | |||
self.b_project_save_as.setIcon(getIcon("document-save-as")) | |||
self.act_patchbay_add_group.setIcon(getIcon("list-add")) | |||
self.act_patchbay_remove_group.setIcon(getIcon("edit-delete")) | |||
self.act_patchbay_rename_group.setIcon(getIcon("edit-rename")) | |||
self.act_patchbay_add_port.setIcon(getIcon("list-add")) | |||
self.act_patchbay_remove_port.setIcon(getIcon("list-remove")) | |||
self.act_patchbay_rename_port.setIcon(getIcon("edit-rename")) | |||
self.act_patchbay_connect_ports.setIcon(getIcon("network-connect")) | |||
self.act_patchbay_disconnect_ports.setIcon(getIcon("network-disconnect")) | |||
self.b_group_add.setIcon(getIcon("list-add")) | |||
self.b_group_remove.setIcon(getIcon("edit-delete")) | |||
self.b_group_rename.setIcon(getIcon("edit-rename")) | |||
self.b_port_add.setIcon(getIcon("list-add")) | |||
self.b_port_remove.setIcon(getIcon("list-remove")) | |||
self.b_port_rename.setIcon(getIcon("edit-rename")) | |||
self.b_ports_connect.setIcon(getIcon("network-connect")) | |||
self.b_ports_disconnect.setIcon(getIcon("network-disconnect")) | |||
setIcons(self, ("canvas",)) | |||
self.scene = patchcanvas.PatchScene(self, self.graphicsView) | |||
self.graphicsView.setScene(self.scene) | |||
self.graphicsView.setRenderHint(QPainter.Antialiasing, bool(self.m_savedSettings["Canvas/Antialiasing"] == Qt.Checked)) | |||
self.graphicsView.setRenderHint(QPainter.TextAntialiasing, self.m_savedSettings["Canvas/TextAntialiasing"]) | |||
if (self.m_savedSettings["Canvas/UseOpenGL"] and hasGL): | |||
self.graphicsView.setViewport(QGLWidget(self.graphicsView)) | |||
self.graphicsView.setRenderHint(QPainter.HighQualityAntialiasing, self.m_savedSettings["Canvas/HighQualityAntialiasing"]) | |||
p_options = patchcanvas.options_t() | |||
p_options.theme_name = self.m_savedSettings["Canvas/Theme"] | |||
p_options.bezier_lines = self.m_savedSettings["Canvas/BezierLines"] | |||
p_options.antialiasing = self.m_savedSettings["Canvas/Antialiasing"] | |||
p_options.auto_hide_groups = self.m_savedSettings["Canvas/AutoHideGroups"] | |||
p_options.fancy_eyecandy = self.m_savedSettings["Canvas/FancyEyeCandy"] | |||
p_features = patchcanvas.features_t() | |||
p_features.group_info = False | |||
p_features.group_rename = True | |||
p_features.port_info = True | |||
p_features.port_rename = True | |||
p_features.handle_group_pos = False | |||
patchcanvas.set_options(p_options) | |||
patchcanvas.set_features(p_features) | |||
patchcanvas.init(self.scene, self.canvasCallback, DEBUG) | |||
self.connect(self.act_project_new, SIGNAL("triggered()"), SLOT("slot_projectNew()")) | |||
#self.connect(self.act_project_open, SIGNAL("triggered()"), SLOT("slot_projectOpen()")) | |||
#self.connect(self.act_project_save, SIGNAL("triggered()"), SLOT("slot_projectSave()")) | |||
#self.connect(self.act_project_save_as, SIGNAL("triggered()"), SLOT("slot_projectSaveAs()")) | |||
self.connect(self.b_project_new, SIGNAL("clicked()"), SLOT("slot_projectNew()")) | |||
#self.connect(self.b_project_open, SIGNAL("clicked()"), SLOT("slot_projectOpen()")) | |||
#self.connect(self.b_project_save, SIGNAL("clicked()"), SLOT("slot_projectSave()")) | |||
#self.connect(self.b_project_save_as, SIGNAL("clicked()"), SLOT("slot_projectSaveAs()")) | |||
self.connect(self.act_patchbay_add_group, SIGNAL("triggered()"), SLOT("slot_groupAdd()")) | |||
self.connect(self.act_patchbay_remove_group, SIGNAL("triggered()"), SLOT("slot_groupRemove()")) | |||
self.connect(self.act_patchbay_rename_group, SIGNAL("triggered()"), SLOT("slot_groupRename()")) | |||
#self.connect(self.act_patchbay_add_port, SIGNAL("triggered()"),SLOT("slot_portAdd()")) | |||
#self.connect(self.act_patchbay_remove_port, SIGNAL("triggered()"), SLOT("slot_portRemove()")) | |||
#self.connect(self.act_patchbay_rename_port, SIGNAL("triggered()"), SLOT("slot_portRename()")) | |||
#self.connect(self.act_patchbay_connect_ports, SIGNAL("triggered()"), SLOT("slot_connectPorts()")) | |||
#self.connect(self.act_patchbay_disconnect_ports, SIGNAL("triggered()"), SLOT("slot_disconnectPorts()")) | |||
self.connect(self.b_group_add, SIGNAL("clicked()"), SLOT("slot_groupAdd()")) | |||
self.connect(self.b_group_remove, SIGNAL("clicked()"), SLOT("slot_groupRemove()")) | |||
self.connect(self.b_group_rename, SIGNAL("clicked()"), SLOT("slot_groupRename()")) | |||
#self.connect(self.b_port_add, SIGNAL("clicked()"), SLOT("slot_portAdd()")) | |||
#self.connect(self.b_port_remove, SIGNAL("clicked()"), SLOT("slot_portRemove()")) | |||
#self.connect(self.b_port_rename, SIGNAL("clicked()"), SLOT("slot_portRename()")) | |||
#self.connect(self.b_ports_connect, SIGNAL("clicked()"), SLOT("slot_connectPorts()")) | |||
#self.connect(self.b_ports_disconnect, SIGNAL("clicked()"), SLOT("slot_disconnectPorts()")) | |||
#setCanvasConnections(self) | |||
#self.connect(self.act_settings_configure, SIGNAL("triggered()"), self.configureCatarina) | |||
self.connect(self.act_help_about, SIGNAL("triggered()"), SLOT("slot_aboutCatarina()")) | |||
self.connect(self.act_help_about_qt, SIGNAL("triggered()"), app, SLOT("aboutQt()")) | |||
#self.connect(self, SIGNAL("SIGUSR1()"), SLOT("slot_projectSave()")) | |||
# Dummy timer to keep events active | |||
self.m_updateTimer = self.startTimer(500) | |||
# Start Empty Project | |||
self.slot_projectNew() | |||
def canvasCallback(self, action, value1, value2, value_str): | |||
print(action, value1, value2, value_str) | |||
@pyqtSlot() | |||
def slot_projectNew(self): | |||
self.m_group_list = [] | |||
self.m_group_list_pos = [] | |||
self.m_port_list = [] | |||
self.m_connection_list = [] | |||
self.m_last_group_id = 1 | |||
self.m_last_port_id = 1 | |||
self.m_last_connection_id = 1 | |||
self.m_save_path = None | |||
patchcanvas.clear() | |||
@pyqtSlot() | |||
def slot_groupAdd(self): | |||
dialog = CatarinaAddGroupW(self, self.m_group_list) | |||
if (dialog.exec_()): | |||
group_id = self.m_last_group_id | |||
group_name = dialog.ret_group_name | |||
group_split = dialog.ret_group_split | |||
group_icon = patchcanvas.ICON_HARDWARE if (group_split) else patchcanvas.ICON_APPLICATION | |||
patchcanvas.addGroup(group_id, group_name, group_split, group_icon) | |||
group_obj = [None, None, None, None] | |||
group_obj[iGroupId] = group_id | |||
group_obj[iGroupName] = group_name | |||
group_obj[iGroupSplit] = group_split | |||
group_obj[iGroupIcon] = group_icon | |||
self.m_group_list.append(group_obj) | |||
self.m_last_group_id += 1 | |||
@pyqtSlot() | |||
def slot_groupRemove(self): | |||
dialog = CatarinaRemoveGroupW(self, self.m_group_list) | |||
if (dialog.exec_()): | |||
group_id = dialog.ret_group_id | |||
#for port in self.m_port_list: | |||
#if (group_id == port[iPortGroup]): | |||
#port_id = port[iPortId] | |||
#h = 0 | |||
#for j in range(len(self.connection_list)): | |||
#if (self.connection_list[j-h][iConnOutput] == port_id or self.connection_list[j-h][iConnInput] == port_id): | |||
#patchcanvas.disconnectPorts(self.connection_list[j-h][iConnId]) | |||
#self.connection_list.pop(j-h) | |||
#h += 1 | |||
#h = 0 | |||
#for i in range(len(self.port_list)): | |||
#if (self.port_list[i-h][iPortGroup] == group_id): | |||
#port_id = self.port_list[i-h][iPortId] | |||
#patchcanvas.removePort(port_id) | |||
#self.port_list.pop(i-h) | |||
#h += 1 | |||
#patchcanvas.removeGroup(group_id) | |||
#for i in range(len(self.group_list)): | |||
#if (self.group_list[i][iGroupId] == group_id): | |||
#self.group_list.pop(i) | |||
#break | |||
@pyqtSlot() | |||
def slot_groupRename(self): | |||
dialog = CatarinaRenameGroupW(self, self.m_group_list) | |||
if (dialog.exec_()): | |||
group_id = dialog.ret_group_id | |||
new_group_name = dialog.ret_new_group_name | |||
patchcanvas.renameGroup(group_id, new_group_name) | |||
for group in self.m_group_list: | |||
if (group[iGroupId] == group_id): | |||
group[iGroupName] = new_group_name | |||
break | |||
@pyqtSlot() | |||
def slot_aboutCatarina(self): | |||
QMessageBox.about(self, self.tr("About Catarina"), self.tr("<h3>Catarina</h3>" | |||
"<br>Version %s" | |||
"<br>Catarina is a testing ground for the 'PatchCanvas' module.<br>" | |||
"<br>Copyright (C) 2010-2012 falkTX") % (VERSION)) | |||
def saveSettings(self): | |||
self.settings.setValue("Geometry", self.saveGeometry()) | |||
self.settings.setValue("ShowToolbar", self.frame_toolbar.isVisible()) | |||
def loadSettings(self): | |||
self.restoreGeometry(self.settings.value("Geometry", "")) | |||
show_toolbar = self.settings.value("ShowToolbar", True, type=bool) | |||
self.act_settings_show_toolbar.setChecked(show_toolbar) | |||
self.frame_toolbar.setVisible(show_toolbar) | |||
self.m_savedSettings = { | |||
"Canvas/Theme": self.settings.value("Canvas/Theme", patchcanvas.getThemeName(patchcanvas.getDefaultTheme), type=str), | |||
"Canvas/BezierLines": self.settings.value("Canvas/BezierLines", True, type=bool), | |||
"Canvas/AutoHideGroups": self.settings.value("Canvas/AutoHideGroups", False, type=bool), | |||
"Canvas/FancyEyeCandy": self.settings.value("Canvas/FancyEyeCandy", False, type=bool), | |||
"Canvas/UseOpenGL": self.settings.value("Canvas/UseOpenGL", False, type=bool), | |||
"Canvas/Antialiasing": self.settings.value("Canvas/Antialiasing", Qt.PartiallyChecked, type=int), | |||
"Canvas/TextAntialiasing": self.settings.value("Canvas/TextAntialiasing", True, type=bool), | |||
"Canvas/HighQualityAntialiasing": self.settings.value("Canvas/HighQualityAntialiasing", False, type=bool) | |||
} | |||
def timerEvent(self, event): | |||
if (event.timerId() == self.m_updateTimer): | |||
self.update() | |||
QMainWindow.timerEvent(self, event) | |||
def closeEvent(self, event): | |||
self.saveSettings() | |||
QMainWindow.closeEvent(self, event) | |||
#--------------- main ------------------ | |||
if __name__ == '__main__': | |||
# App initialization | |||
app = QApplication(sys.argv) | |||
app.setApplicationName("Catarina") | |||
app.setApplicationVersion(VERSION) | |||
app.setOrganizationName("falkTX") | |||
app.setWindowIcon(QIcon(":/scalable/catarina.svg")) | |||
# Show GUI | |||
gui = CatarinaMainW() | |||
# Set-up custom signal handling | |||
set_up_signals(gui) | |||
#if (app.arguments().count() > 1): | |||
#gui.save_path = QStringStr(app.arguments()[1]) | |||
#gui.prepareToloadFile() | |||
gui.show() | |||
# App-Loop | |||
sys.exit(app.exec_()) |
@@ -0,0 +1,493 @@ | |||
#!/usr/bin/env python3 | |||
# -*- coding: utf-8 -*- | |||
# Patchbay Canvas engine using QGraphicsView/Scene | |||
# Copyright (C) 2012 Filipe Coelho <falktx@gmail.com> | |||
# | |||
# This program is free software; you can redistribute it and/or modify | |||
# it under the terms of the GNU General Public License as published by | |||
# the Free Software Foundation; either version 2 of the License, or | |||
# any later version. | |||
# | |||
# This program is distributed in the hope that it will be useful, | |||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
# GNU General Public License for more details. | |||
# | |||
# For a full copy of the GNU General Public License see the COPYING file | |||
# Imports (Global) | |||
from PyQt4.QtCore import pyqtSlot, qDebug, qCritical, qFatal, Qt, QObject, SIGNAL, SLOT | |||
from PyQt4.QtCore import QPointF, QRectF, QSettings | |||
from PyQt4.QtGui import QGraphicsScene, QGraphicsItem | |||
# Imports (Theme) | |||
from patchcanvas_theme import * | |||
PATCHCANVAS_ORGANISATION_NAME = "Cadence" | |||
# ------------------------------------------------------------------------------ | |||
# patchcanvas-api.h | |||
# Port Mode | |||
PORT_MODE_NULL = 0 | |||
PORT_MODE_INPUT = 1 | |||
PORT_MODE_OUTPUT = 2 | |||
# Port Type | |||
PORT_TYPE_NULL = 0 | |||
PORT_TYPE_AUDIO_JACK = 1 | |||
PORT_TYPE_MIDI_JACK = 2 | |||
PORT_TYPE_MIDI_A2J = 3 | |||
PORT_TYPE_MIDI_ALSA = 4 | |||
# Callback Action | |||
ACTION_GROUP_INFO = 0 # group_id, N, N | |||
ACTION_GROUP_RENAME = 1 # group_id, N, new_name | |||
ACTION_GROUP_SPLIT = 2 # group_id, N, N | |||
ACTION_GROUP_JOIN = 3 # group_id, N, N | |||
ACTION_PORT_INFO = 4 # port_id, N, N | |||
ACTION_PORT_RENAME = 5 # port_id, N, new_name | |||
ACTION_PORTS_CONNECT = 6 # out_id, in_id, N | |||
ACTION_PORTS_DISCONNECT = 7 # conn_id, N, N | |||
# Icon | |||
ICON_HARDWARE = 0 | |||
ICON_APPLICATION = 1 | |||
ICON_LADISH_ROOM = 2 | |||
# Split Option | |||
SPLIT_UNDEF = 0 | |||
SPLIT_NO = 1 | |||
SPLIT_YES = 2 | |||
# Canvas options | |||
class options_t(object): | |||
__slots__ = [ | |||
'theme_name', | |||
'bezier_lines', | |||
'antialiasing', | |||
'auto_hide_groups', | |||
'fancy_eyecandy' | |||
] | |||
# Canvas features | |||
class features_t(object): | |||
__slots__ = [ | |||
'group_info', | |||
'group_rename', | |||
'port_info', | |||
'port_rename', | |||
'handle_group_pos' | |||
] | |||
# ------------------------------------------------------------------------------ | |||
# patchcanvas.h | |||
# object types | |||
CanvasBoxType = QGraphicsItem.UserType + 1 | |||
CanvasIconType = QGraphicsItem.UserType + 2 | |||
CanvasPortType = QGraphicsItem.UserType + 3 | |||
CanvasLineType = QGraphicsItem.UserType + 4 | |||
CanvasBezierLineType = QGraphicsItem.UserType + 5 | |||
CanvasLineMovType = QGraphicsItem.UserType + 6 | |||
CanvasBezierLineMovType = QGraphicsItem.UserType + 7 | |||
# object lists | |||
class group_dict_t(object): | |||
__slots__ = [ | |||
'group_id', | |||
'group_name', | |||
'split', | |||
'icon', | |||
'widgets' | |||
] | |||
class port_dict_t(object): | |||
__slots__ = [ | |||
'group_id', | |||
'port_id', | |||
'port_name', | |||
'port_mode', | |||
'port_type', | |||
'widget' | |||
] | |||
class connection_dict_t(object): | |||
__slots__ = [ | |||
'connection_id', | |||
'port_in_id', | |||
'port_out_id', | |||
'widget' | |||
] | |||
# Main Canvas object | |||
class Canvas(object): | |||
__slots__ = [ | |||
'scene', | |||
'callback', | |||
'debug', | |||
'last_z_value', | |||
'last_group_id', | |||
'last_connection_id', | |||
'initial_pos', | |||
'group_list', | |||
'port_list', | |||
'connection_list', | |||
'postponed_groups', | |||
'qobject', | |||
'settings', | |||
'theme', | |||
'size_rect', | |||
'initiated' | |||
] | |||
# ------------------------------------------------------------------------------ | |||
# patchcanvas.cpp | |||
class CanvasObject(QObject): | |||
def __init__(self, parent): | |||
QObject.__init__(self, parent) | |||
@pyqtSlot() | |||
def CanvasPostponedGroups(self): | |||
CanvasPostponedGroups() | |||
@pyqtSlot() | |||
def PortContextMenuDisconnect(self): | |||
connection_id_try = self.sender().data().toInt() | |||
if (connection_id_try[1]): | |||
CanvasCallback(ACTION_PORTS_DISCONNECT, connection_id_try[0], 0, "") | |||
# Global objects | |||
canvas = Canvas() | |||
canvas.qobject = None | |||
canvas.settings = None | |||
canvas.theme = None | |||
canvas.initiated = False | |||
options = options_t() | |||
options.theme_name = getThemeName(getDefaultTheme()) | |||
options.bezier_lines = True | |||
options.antialiasing = Qt.PartiallyChecked | |||
options.auto_hide_groups = True | |||
options.fancy_eyecandy = False | |||
features = features_t() | |||
features.group_info = False | |||
features.group_rename = True | |||
features.port_info = False | |||
features.port_rename = True | |||
features.handle_group_pos = False | |||
# Internal functions | |||
def bool2str(check): | |||
return "True" if check else "False" | |||
# PatchCanvas API | |||
def set_options(new_options): | |||
if (canvas.initiated): return | |||
options.theme_name = new_options.theme_name | |||
options.bezier_lines = new_options.bezier_lines | |||
options.antialiasing = new_options.antialiasing | |||
options.auto_hide_groups = new_options.auto_hide_groups | |||
options.fancy_eyecandy = new_options.fancy_eyecandy | |||
def set_features(new_features): | |||
if (canvas.initiated): return | |||
features.group_info = new_features.group_info | |||
features.group_rename = new_features.group_rename | |||
features.port_info = new_features.port_info | |||
features.port_rename = new_features.port_rename | |||
features.handle_group_pos = new_features.handle_group_pos | |||
def init(scene, callback, debug=False): | |||
if (debug): | |||
qDebug("PatchCanvas::init(%s, %s, %s)" % (scene, callback, bool2str(debug))) | |||
if (canvas.initiated): | |||
qCritical("PatchCanvas::init() - already initiated") | |||
return | |||
if (not callback): | |||
qFatal("PatchCanvas::init() - fatal error: callback not set") | |||
return | |||
canvas.scene = scene | |||
canvas.callback = callback | |||
canvas.debug = debug | |||
canvas.last_z_value = 0 | |||
canvas.last_group_id = 0 | |||
canvas.last_connection_id = 0 | |||
canvas.initial_pos = QPointF(0, 0) | |||
canvas.group_list = [] | |||
canvas.port_list = [] | |||
canvas.connection_list = [] | |||
canvas.postponed_groups = [] | |||
if (not canvas.qobject): canvas.qobject = CanvasObject(None) | |||
if (not canvas.settings): canvas.settings = QSettings(PATCHCANVAS_ORGANISATION_NAME, "PatchCanvas") | |||
for i in range(Theme.THEME_MAX): | |||
this_theme_name = getThemeName(i) | |||
if (this_theme_name == options.theme_name): | |||
canvas.theme = Theme(i) | |||
break | |||
else: | |||
canvas.theme = Theme(getDefaultTheme()) | |||
canvas.size_rect = QRectF() | |||
canvas.scene.rubberbandByTheme() | |||
canvas.scene.setBackgroundBrush(canvas.theme.canvas_bg) | |||
canvas.initiated = True | |||
def clear(): | |||
if (canvas.debug): | |||
qDebug("patchcanvas::clear()") | |||
group_list_ids = [] | |||
port_list_ids = [] | |||
connection_list_ids = [] | |||
for i in range(len(canvas.group_list)): | |||
group_list_ids.append(canvas.group_list[i].group_id) | |||
for i in range(len(canvas.port_list)): | |||
port_list_ids.append(canvas.port_list[i].port_id) | |||
for i in range(len(canvas.connection_list)): | |||
connection_list_ids.append(canvas.connection_list[i].connection_id) | |||
for idx in connection_list_ids: | |||
disconnectPorts(idx) | |||
for idx in port_list_ids: | |||
removePort(tmp_port_list[i]) | |||
for idx in group_list_ids: | |||
removeGroup(idx) | |||
canvas.last_z_value = 0 | |||
canvas.last_group_id = 0 | |||
canvas.last_connection_id = 0 | |||
canvas.group_list = [] | |||
canvas.port_list = [] | |||
canvas.connection_list = [] | |||
canvas.postponed_groups = [] | |||
canvas.initiated = False | |||
def setInitialPos(x, y): | |||
if (canvas.debug): | |||
qDebug("patchcanvas::setInitialPos(%i, %i)" % (x, y)) | |||
canvas.initial_pos.setX(x) | |||
canvas.initial_pos.setY(y) | |||
def setCanvasSize(x, y, width, height): | |||
if (canvas.debug): | |||
qDebug("patchcanvas::setCanvasSize(%i, %i, %i, %i)" % (x, y, width, height)) | |||
canvas.size_rect.setX(x) | |||
canvas.size_rect.setY(y) | |||
canvas.size_rect.setWidth(width) | |||
canvas.size_rect.setHeight(height) | |||
# ------------------------------------------------------------------------------ | |||
# patchscene.cpp | |||
class PatchScene(QGraphicsScene): | |||
def __init__(self, parent, view): | |||
QGraphicsScene.__init__(self, parent) | |||
self.m_ctrl_down = False | |||
self.m_mouse_down_init = False | |||
self.m_mouse_rubberband = False | |||
self.m_rubberband = self.addRect(QRectF(0, 0, 0, 0)) | |||
self.m_rubberband.setZValue(-1) | |||
self.m_rubberband.hide() | |||
self.m_rubberband_selection = False | |||
self.m_rubberband_orig_point = QPointF(0, 0) | |||
self.m_view = view | |||
if (not self.m_view): | |||
qFatal("PatchCanvas::PatchScene() - Invalid view") | |||
def fixScaleFactor(self): | |||
scale = self.m_view.transform().m11() | |||
if (scale > 3.0): | |||
self.m_view.resetTransform() | |||
self.m_view.scale(3.0, 3.0) | |||
elif (scale < 0.2): | |||
self.m_view.resetTransform() | |||
self.m_view.scale(0.2, 0.2) | |||
self.emit(SIGNAL("scaleChanged(double)"), self.m_view.transform().m11()) | |||
def rubberbandByTheme(self): | |||
self.m_rubberband.setPen(canvas.theme.rubberband_pen) | |||
self.m_rubberband.setBrush(canvas.theme.rubberband_brush) | |||
def zoom_fit(self): | |||
min_x = min_y = max_x = max_y = None | |||
items_list = self.items() | |||
if (len(items_list) > 0): | |||
for i in range(len(items_list)): | |||
if (items_list[i].isVisible() and items_list[i].type() == CanvasBoxType): | |||
pos = items_list[i].scenePos() | |||
rect = items_list[i].boundingRect() | |||
if (min_x == None): | |||
min_x = pos.x() | |||
elif (pos.x() < min_x): | |||
min_x = pos.x() | |||
if (min_y == None): | |||
min_y = pos.y() | |||
elif (pos.y() < min_y): | |||
min_y = pos.y() | |||
if (max_x == None): | |||
max_x = pos.x()+rect.width() | |||
elif (pos.x()+rect.width() > max_x): | |||
max_x = pos.x()+rect.width() | |||
if (max_y == None): | |||
max_y = pos.y()+rect.height() | |||
elif (pos.y()+rect.height() > max_y): | |||
max_y = pos.y()+rect.height() | |||
self.m_view.fitInView(min_x, min_y, abs(max_x-min_x), abs(max_y-min_y), Qt.KeepAspectRatio) | |||
self.fixScaleFactor() | |||
def zoom_in(self): | |||
if (self.m_view.transform().m11() < 3.0): | |||
self.m_view.scale(1.2, 1.2) | |||
self.emit(SIGNAL("scaleChanged(double)"), self.m_view.transform().m11()) | |||
def zoom_out(self): | |||
if (self.m_view.transform().m11() > 0.2): | |||
self.m_view.scale(0.8, 0.8) | |||
self.emit(SIGNAL("scaleChanged(double)"), self.m_view.transform().m11()) | |||
def zoom_reset(self): | |||
self.m_view.resetTransform() | |||
self.emit(SIGNAL("scaleChanged(double)"), 1.0) | |||
def keyPressEvent(self, event): | |||
if (not self.m_view): | |||
event.reject() | |||
if (event.key() == Qt.Key_Control): | |||
self.m_ctrl_down = True | |||
if (event.key() == Qt.Key_Home): | |||
self.zoom_fit() | |||
event.accept() | |||
elif (self.m_ctrl_down): | |||
if (event.key() == Qt.Key_Plus): | |||
self.zoom_in() | |||
event.accept() | |||
elif (event.key() == Qt.Key_Minus): | |||
self.zoom_out() | |||
event.accept() | |||
elif (event.key() == Qt.Key_1): | |||
self.zoom_reset() | |||
event.accept() | |||
else: | |||
QGraphicsScene.keyPressEvent(self, event) | |||
else: | |||
QGraphicsScene.keyPressEvent(self, event) | |||
def keyReleaseEvent(self, event): | |||
if (event.key() == Qt.Key_Control): | |||
self.m_ctrl_down = False | |||
QGraphicsScene.keyReleaseEvent(self, event) | |||
def mousePressEvent(self, event): | |||
if (event.button() == Qt.LeftButton): | |||
self.m_mouse_down_init = True | |||
else: | |||
self.m_mouse_down_init = False | |||
self.m_mouse_rubberband = False | |||
QGraphicsScene.mousePressEvent(self, event) | |||
def mouseMoveEvent(self, event): | |||
if (self.m_mouse_down_init): | |||
self.m_mouse_rubberband = (len(self.selectedItems()) == 0) | |||
self.m_mouse_down_init = False | |||
if (self.m_mouse_rubberband): | |||
if (self.m_rubberband_selection == False): | |||
self.m_rubberband.show() | |||
self.m_rubberband_selection = True | |||
self.m_rubberband_orig_point = event.scenePos() | |||
pos = event.scenePos() | |||
if (pos.x() > self.m_rubberband_orig_point.x()): | |||
x = self.m_rubberband_orig_point.x() | |||
else: | |||
x = pos.x() | |||
if (pos.y() > self.m_rubberband_orig_point.y()): | |||
y = self.m_rubberband_orig_point.y() | |||
else: | |||
y = pos.y() | |||
self.m_rubberband.setRect(x, y, abs(pos.x()-self.m_rubberband_orig_point.x()), abs(pos.y()-self.m_rubberband_orig_point.y())) | |||
event.accept() | |||
else: | |||
QGraphicsScene.mouseMoveEvent(self, event) | |||
def mouseReleaseEvent(self, event): | |||
if (self.m_rubberband_selection): | |||
items_list = self.items() | |||
if (len(items_list) > 0): | |||
for item in items_list: | |||
if (item.isVisible() and item.type() == CanvasBoxType): | |||
item_rect = item.sceneBoundingRect() | |||
item_top_left = QPointF(item_rect.x(), item_rect.y()) | |||
item_bottom_right = QPointF(item_rect.x()+item_rect.width(), item_rect.y()+item_rect.height()) | |||
if (self.m_rubberband.contains(item_top_left) and self.m_rubberband.contains(item_bottom_right)): | |||
item.setSelected(True) | |||
self.m_rubberband.hide() | |||
self.m_rubberband.setRect(0, 0, 0, 0) | |||
self.m_rubberband_selection = False | |||
else: | |||
items_list = self.selectedItems() | |||
for item in items_list: | |||
if (item.isVisible() and item.type() == CanvasBoxType): | |||
item.checkItemPos() | |||
self.emit(SIGNAL("sceneGroupMoved(int, int, QPointF)"), item.getGroupId(), item.getSplittedMode(), item.scenePos()) | |||
self.m_mouse_down_init = False | |||
self.m_mouse_rubberband = False | |||
QGraphicsScene.mouseReleaseEvent(self, event) | |||
def wheelEvent(self, event): | |||
if (not self.m_view): | |||
event.reject() | |||
if (self.m_ctrl_down): | |||
factor = 1.41 ** (event.delta()/240.0) | |||
self.m_view.scale(factor, factor) | |||
self.fixScaleFactor() | |||
event.accept() | |||
else: | |||
QGraphicsScene.wheelEvent(self, event) |
@@ -0,0 +1,167 @@ | |||
#!/usr/bin/env python3 | |||
# -*- coding: utf-8 -*- | |||
# Patchbay Canvas Themes | |||
# Copyright (C) 2012 Filipe Coelho <falktx@gmail.com> | |||
# | |||
# This program is free software; you can redistribute it and/or modify | |||
# it under the terms of the GNU General Public License as published by | |||
# the Free Software Foundation; either version 2 of the License, or | |||
# any later version. | |||
# | |||
# This program is distributed in the hope that it will be useful, | |||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
# GNU General Public License for more details. | |||
# | |||
# For a full copy of the GNU General Public License see the COPYING file | |||
from PyQt4.QtCore import Qt | |||
from PyQt4.QtGui import QColor, QFont, QPen | |||
class Theme(object): | |||
# enum PortType | |||
THEME_PORT_SQUARE = 0 | |||
THEME_PORT_POLYGON = 1 | |||
# enum List | |||
THEME_MODERN_DARK = 0 | |||
THEME_CLASSIC_DARK = 1 | |||
THEME_MAX = 2 | |||
def __init__(self, idx): | |||
super(Theme, self).__init__() | |||
if (idx == self.THEME_MODERN_DARK): | |||
# Name this theme | |||
self.m_name = "Modern Dark" | |||
# Canvas | |||
self.canvas_bg = QColor(0,0,0) | |||
# Boxes | |||
self.box_pen = QPen(QColor(76,77,78), 1, Qt.SolidLine) | |||
self.box_pen_sel = QPen(QColor(206,207,208), 1, Qt.DashLine) | |||
self.box_bg_1 = QColor(32,34,35) | |||
self.box_bg_2 = QColor(43,47,48) | |||
self.box_shadow = QColor(89,89,89,180) | |||
self.box_text = QPen(QColor(240,240,240), 0) | |||
self.box_font_name = "Deja Vu Sans" | |||
self.box_font_size = 8 | |||
self.box_font_state = QFont.Bold | |||
# Ports | |||
self.port_audio_jack_pen = QPen(QColor(63,90,126), 1) | |||
self.port_audio_jack_pen_sel = QPen(QColor(63+30,90+30,126+30), 1) | |||
self.port_midi_jack_pen = QPen(QColor(159,44,42), 1) | |||
self.port_midi_jack_pen_sel = QPen(QColor(159+30,44+30,42+30), 1) | |||
self.port_midi_a2j_pen = QPen(QColor(137,76,43), 1) | |||
self.port_midi_a2j_pen_sel = QPen(QColor(137+30,76+30,43+30), 1) | |||
self.port_midi_alsa_pen = QPen(QColor(93,141,46), 1) | |||
self.port_midi_alsa_pen_sel = QPen(QColor(93+30,141+30,46+30), 1) | |||
self.port_audio_jack_bg = QColor(35,61,99) | |||
self.port_audio_jack_bg_sel = QColor(35+50,61+50,99+50) | |||
self.port_midi_jack_bg = QColor(120,15,16) | |||
self.port_midi_jack_bg_sel = QColor(120+50,15+50,16+50) | |||
self.port_midi_a2j_bg = QColor(101,47,16) | |||
self.port_midi_a2j_bg_sel = QColor(101+50,47+50,16+50) | |||
self.port_midi_alsa_bg = QColor(64,112,18) | |||
self.port_midi_alsa_bg_sel = QColor(64+50,112+50,18+50) | |||
self.port_text = QPen(QColor(250,250,250), 0) | |||
self.port_font_name = "Deja Vu Sans" | |||
self.port_font_size = 8 | |||
self.port_font_state = QFont.Normal | |||
self.port_mode = self.THEME_PORT_POLYGON | |||
# Lines | |||
self.line_audio_jack = QColor(63,90,126) | |||
self.line_audio_jack_sel = QColor(63+90,90+90,126+90) | |||
self.line_audio_jack_glow = QColor(100,100,200) | |||
self.line_midi_jack = QColor(159,44,42) | |||
self.line_midi_jack_sel = QColor(159+90,44+90,42+90) | |||
self.line_midi_jack_glow = QColor(200,100,100) | |||
self.line_midi_a2j = QColor(137,76,43) | |||
self.line_midi_a2j_sel = QColor(137+90,76+90,43+90) | |||
self.line_midi_a2j_glow = QColor(166,133,133) | |||
self.line_midi_alsa = QColor(93,141,46) | |||
self.line_midi_alsa_sel = QColor(93+90,141+90,46+90) | |||
self.line_midi_alsa_glow = QColor(100,200,100) | |||
self.rubberband_pen = QPen(QColor(206,207,208), 1, Qt.SolidLine) | |||
self.rubberband_brush = QColor(76,77,78,100) | |||
elif (idx == self.THEME_CLASSIC_DARK): | |||
# Name this theme | |||
self.m_name = "Classic Dark" | |||
# Canvas | |||
self.canvas_bg = QColor(0,0,0) | |||
# Boxes | |||
self.box_pen = QPen(QColor(147-70,151-70,143-70), 2, Qt.SolidLine) | |||
self.box_pen_sel = QPen(QColor(147,151,143), 2, Qt.DashLine) | |||
self.box_bg_1 = QColor(30,34,36) | |||
self.box_bg_2 = QColor(30,34,36) | |||
self.box_shadow = QColor(89,89,89,180) | |||
self.box_text = QPen(QColor(255,255,255), 0) | |||
self.box_font_name = "Sans" | |||
self.box_font_size = 9 | |||
self.box_font_state = QFont.Normal | |||
# Ports | |||
self.port_audio_jack_pen = QPen(QColor(35,61,99), 0) | |||
self.port_audio_jack_pen_sel = QPen(QColor(255,0,0), 0) | |||
self.port_midi_jack_pen = QPen(QColor(120,15,16), 0) | |||
self.port_midi_jack_pen_sel = QPen(QColor(255,0,0), 0) | |||
self.port_midi_a2j_pen = QPen(QColor(101,47,17), 0) | |||
self.port_midi_a2j_pen_sel = QPen(QColor(255,0,0), 0) | |||
self.port_midi_alsa_pen = QPen(QColor(63,112,19), 0) | |||
self.port_midi_alsa_pen_sel = QPen(QColor(255,0,0), 0) | |||
self.port_audio_jack_bg = QColor(35,61,99) | |||
self.port_audio_jack_bg_sel = QColor(255,0,0) | |||
self.port_midi_jack_bg = QColor(120,15,16) | |||
self.port_midi_jack_bg_sel = QColor(255,0,0) | |||
self.port_midi_a2j_bg = QColor(101,47,17) | |||
self.port_midi_a2j_bg_sel = QColor(255,0,0) | |||
self.port_midi_alsa_bg = QColor(63,112,19) | |||
self.port_midi_alsa_bg_sel = QColor(255,0,0) | |||
self.port_text = QPen(QColor(250,250,250), 0) | |||
self.port_font_name = "Sans" | |||
self.port_font_size = 8 | |||
self.port_font_state = QFont.Normal | |||
self.port_mode = self.THEME_PORT_SQUARE | |||
# Lines | |||
self.line_audio_jack = QColor(53,78,116) | |||
self.line_audio_jack_sel = QColor(255,0,0) | |||
self.line_audio_jack_glow = QColor(255,0,0) | |||
self.line_midi_jack = QColor(139,32,32) | |||
self.line_midi_jack_sel = QColor(255,0,0) | |||
self.line_midi_jack_glow = QColor(255,0,0) | |||
self.line_midi_a2j = QColor(120,65,33) | |||
self.line_midi_a2j_sel = QColor(255,0,0) | |||
self.line_midi_a2j_glow = QColor(255,0,0) | |||
self.line_midi_alsa = QColor(81,130,36) | |||
self.line_midi_alsa_sel = QColor(255,0,0) | |||
self.line_midi_alsa_glow = QColor(255,0,0) | |||
self.rubberband_pen = QPen(QColor(147,151,143), 2, Qt.SolidLine) | |||
self.rubberband_brush = QColor(35,61,99,100) | |||
def getDefaultTheme(): | |||
return Theme.THEME_MODERN_DARK | |||
def getThemeName(idx): | |||
if (idx == Theme.THEME_MODERN_DARK): | |||
return "Modern Dark" | |||
elif (idx == Theme.THEME_CLASSIC_DARK): | |||
return "Classic Dark" | |||
else: | |||
return "" |
@@ -210,3 +210,40 @@ def showWindow(self): | |||
self.showMaximized() | |||
else: | |||
self.showNormal() | |||
# Shared Icons | |||
def setIcons(self, modes): | |||
if ("canvas" in modes): | |||
self.act_canvas_arrange.setIcon(getIcon("view-sort-ascending")) | |||
self.act_canvas_refresh.setIcon(getIcon("view-refresh")) | |||
self.act_canvas_zoom_fit.setIcon(getIcon("zoom-fit-best")) | |||
self.act_canvas_zoom_in.setIcon(getIcon("zoom-in")) | |||
self.act_canvas_zoom_out.setIcon(getIcon("zoom-out")) | |||
self.act_canvas_zoom_100.setIcon(getIcon("zoom-original")) | |||
self.act_canvas_print.setIcon(getIcon("document-print")) | |||
self.b_canvas_zoom_fit.setIcon(getIcon("zoom-fit-best")) | |||
self.b_canvas_zoom_in.setIcon(getIcon("zoom-in")) | |||
self.b_canvas_zoom_out.setIcon(getIcon("zoom-out")) | |||
self.b_canvas_zoom_100.setIcon(getIcon("zoom-original")) | |||
if ("jack" in modes): | |||
self.act_jack_clear_xruns.setIcon(getIcon("edit-clear")) | |||
self.act_jack_configure.setIcon(getIcon("configure")) | |||
self.act_jack_render.setIcon(getIcon("media-record")) | |||
self.b_jack_clear_xruns.setIcon(getIcon("edit-clear")) | |||
self.b_jack_configure.setIcon(getIcon("configure")) | |||
self.b_jack_render.setIcon(getIcon("media-record")) | |||
if ("transport" in modes): | |||
self.act_transport_play.setIcon(getIcon("media-playback-start")) | |||
self.act_transport_stop.setIcon(getIcon("media-playback-stop")) | |||
self.act_transport_backwards.setIcon(getIcon("media-seek-backward")) | |||
self.act_transport_forwards.setIcon(getIcon("media-seek-forward")) | |||
self.b_transport_play.setIcon(getIcon("media-playback-start")) | |||
self.b_transport_stop.setIcon(getIcon("media-playback-stop")) | |||
self.b_transport_backwards.setIcon(getIcon("media-seek-backward")) | |||
self.b_transport_forwards.setIcon(getIcon("media-seek-forward")) | |||
if ("misc" in modes): | |||
self.act_quit.setIcon(getIcon("application-exit")) | |||
self.act_configure.setIcon(getIcon("configure")) |