Commit b4c576f8 authored by Michael Büsch's avatar Michael Büsch

gui: Add basic project support

Signed-off-by: Michael Büsch's avatarMichael Buesch <[email protected]>
parent cf2cc148
......@@ -52,6 +52,18 @@ class GenericSource(object):
def isFileBacked(self):
return bool(self.filepath)
def writeFileBacking(self):
"Write the backing file, if any."
if not self.isFileBacked():
return
awlFileWrite(self.filepath, self.sourceBytes, encoding="binary")
def forceNonFileBacked(self, newIdentifier):
"Convert this source to a non-file-backed source."
if self.isFileBacked():
self.filepath = ""
self.identifier = newIdentifier
def toBase64(self):
return base64.b64encode(self.sourceBytes).decode("ascii")
......@@ -94,14 +106,53 @@ class SymTabSource(GenericSource):
self.sourceBytes[:])
class Project(object):
def __init__(self, projectFile, awlSources=[], symTabSources=[], cpuSpecs=None):
self.projectFile = projectFile
self.awlSources = awlSources
self.symTabSources = symTabSources
def __init__(self, projectFile, awlSources=[], symTabSources=[],
cpuSpecs=None, obTempPresetsEn=False, extInsnsEn=False):
self.setProjectFile(projectFile)
self.setAwlSources(awlSources)
self.setSymTabSources(symTabSources)
if not cpuSpecs:
cpuSpecs = S7CPUSpecs()
self.setCpuSpecs(cpuSpecs)
self.setObTempPresetsEn(obTempPresetsEn)
self.setExtInsnsEn(extInsnsEn)
def setProjectFile(self, filename):
self.projectFile = filename
def getProjectFile(self):
return self.projectFile
def setAwlSources(self, awlSources):
self.awlSources = awlSources
def getAwlSources(self):
return self.awlSources
def setSymTabSources(self, symTabSources):
self.symTabSources = symTabSources
def getSymTabSources(self):
return self.symTabSources
def setCpuSpecs(self, cpuSpecs):
self.cpuSpecs = cpuSpecs
def getCpuSpecs(self):
return self.cpuSpecs
def setObTempPresetsEn(self, obTempPresetsEn):
self.obTempPresetsEn = obTempPresetsEn
def getObTempPresetsEn(self):
return self.obTempPresetsEn
def setExtInsnsEn(self, extInsnsEn):
self.extInsnsEn = extInsnsEn
def getExtInsnsEn(self):
return self.extInsnsEn
@classmethod
def dataIsProject(cls, data):
magic = b"[AWLSIM_PROJECT]"
......@@ -127,6 +178,8 @@ class Project(object):
awlSources = []
symTabSources = []
cpuSpecs = S7CPUSpecs()
obTempPresetsEn = False
extInsnsEn = False
try:
p = _ConfigParser()
p.readfp(StringIO(text), projectFile)
......@@ -158,6 +211,10 @@ class Project(object):
if p.has_option("CPU", "nr_accus"):
nrAccus = p.getint("CPU", "nr_accus")
cpuSpecs.setNrAccus(nrAccus)
if p.has_option("CPU", "ob_startinfo_enable"):
obTempPresetsEn = p.getboolean("CPU", "ob_startinfo_enable")
if p.has_option("CPU", "ext_insns_enable"):
extInsnsEn = p.getboolean("CPU", "ext_insns_enable")
# SYMBOLS section
for i in range(0xFFFF):
......@@ -181,7 +238,9 @@ class Project(object):
return cls(projectFile = projectFile,
awlSources = awlSources,
symTabSources = symTabSources,
cpuSpecs = cpuSpecs)
cpuSpecs = cpuSpecs,
obTempPresetsEn = obTempPresetsEn,
extInsnsEn = extInsnsEn)
@classmethod
def fromFile(cls, filename):
......@@ -230,6 +289,8 @@ class Project(object):
lines.append("awl_%d=%s" % (i, awlSrc.toBase64()))
lines.append("mnemonics=%d" % self.cpuSpecs.getConfiguredMnemonics())
lines.append("nr_accus=%d" % self.cpuSpecs.nrAccus)
lines.append("ob_startinfo_enable=%d" % int(bool(self.obTempPresetsEn)))
lines.append("ext_insns_enable=%d" % int(bool(self.extInsnsEn)))
lines.append("")
lines.append("[SYMBOLS]")
......@@ -251,3 +312,14 @@ class Project(object):
"No file name specified.")
text = self.toText(projectFile)
awlFileWrite(projectFile, text, encoding="utf8")
for awlSrc in self.awlSources:
awlSrc.writeFileBacking()
for symSrc in self.symTabSources:
symSrc.writeFileBacking()
def allFileBackingsToInternal(self):
"Convert all file backed sources to internal sources."
for i, awlSrc in enumerate(self.awlSources):
awlSrc.forceNonFileBacked("#%d" % i)
for i, symSrc in enumerate(self.symTabSources):
symSrc.forceNonFileBacked("#%d" % i)
......@@ -31,8 +31,6 @@ class CpuConfigDialog(QDialog):
self.simClient = simClient
self.setWindowTitle("CPU configuration")
self.__updateBlocked = 0
self.setLayout(QGridLayout(self))
label = QLabel("Number of accumulator registers", self)
......@@ -61,18 +59,10 @@ class CpuConfigDialog(QDialog):
self.closeButton = QPushButton("Close", self)
self.layout().addWidget(self.closeButton, 4, 1)
self.accuCombo.currentIndexChanged.connect(self.__configChanged)
self.mnemonicsCombo.currentIndexChanged.connect(self.__configChanged)
self.obTempCheckBox.stateChanged.connect(self.__configChanged)
self.extInsnsCheckBox.stateChanged.connect(self.__configChanged)
self.closeButton.released.connect(self.accept)
#FIXME this should be loaded from .awlpro file
def loadConfig(self):
return#XXX
cpu = self.sim.getCPU()
specs = cpu.getSpecs()
self.__updateBlocked += 1
def loadFromProject(self, project):
specs = project.getCpuSpecs()
index = self.accuCombo.findData(specs.nrAccus)
assert(index >= 0)
......@@ -83,27 +73,23 @@ class CpuConfigDialog(QDialog):
self.mnemonicsCombo.setCurrentIndex(index)
self.obTempCheckBox.setCheckState(
Qt.Checked if cpu.obTempPresetsEnabled() else\
Qt.Checked if project.getObTempPresetsEn() else\
Qt.Unchecked
)
self.__updateBlocked -= 1
self.extInsnsCheckBox.setCheckState(
Qt.Checked if project.getExtInsnsEn() else\
Qt.Unchecked
)
def uploadToCPU(self):
def saveToProject(self, project):
mnemonics = self.mnemonicsCombo.itemData(self.mnemonicsCombo.currentIndex())
nrAccus = self.accuCombo.itemData(self.accuCombo.currentIndex())
obTempEnabled = self.obTempCheckBox.checkState() == Qt.Checked
extInsnsEnabled = self.extInsnsCheckBox.checkState() == Qt.Checked
specs = self.simClient.getCpuSpecs()
if specs:
specs.setConfiguredMnemonics(mnemonics)
specs.setNrAccus(nrAccus)
self.simClient.setCpuSpecs(specs)
self.simClient.enableOBTempPresets(obTempEnabled)
self.simClient.enableExtendedInsns(extInsnsEnabled)
def __configChanged(self):
if self.__updateBlocked:
return
self.uploadToCPU()
specs = project.getCpuSpecs()
specs.setConfiguredMnemonics(mnemonics)
specs.setNrAccus(nrAccus)
project.setObTempPresetsEn(obTempEnabled)
project.setExtInsnsEn(extInsnsEnabled)
......@@ -28,7 +28,12 @@ from awlsim.gui.awlsimclient import *
class CpuWidget(QWidget):
# Signal: The CPU run-state changed
runStateChanged = Signal(int)
# Signal: The online-diag state changed
onlineDiagChanged = Signal(bool)
# Signal: Have a new instruction dump
haveInsnDump = Signal(AwlSimMessage_INSNSTATE)
EnumGen.start
STATE_STOP = EnumGen.item
......@@ -46,11 +51,9 @@ class CpuWidget(QWidget):
client = self.mainWidget.getSimClient()
client.haveCpuDump.connect(self.__handleCpuDump)
client.haveInsnDump.connect(self.mainWidget.codeEdit.updateCpuStats_afterInsn)
client.haveInsnDump.connect(self.haveInsnDump)
client.haveMemoryUpdate.connect(self.__handleMemoryUpdate)
self.mainWidget.codeEdit.visibleRangeChanged.connect(self.__updateOnlineViewState)
group = QGroupBox("CPU status", self)
group.setLayout(QGridLayout(group))
self.runButton = QRadioButton("RUN", group)
......@@ -172,17 +175,8 @@ class CpuWidget(QWidget):
self.runButton.setEnabled(False) # Redraws the radio button
self.runButton.setEnabled(True)
awlCode = self.mainWidget.getCodeEditWidget().getCode()
if not awlCode.strip():
MessageBox.error(self, "No AWL/STL code available. Cannot run.")
self.stop()
return
try:
awlCode = awlCode.encode("latin_1")
except UnicodeError:
MessageBox.error(self, "AWL/STL code contains invalid characters.")
self.stop()
return
project = self.mainWidget.getProject()
awlSources = self.mainWidget.projectWidget.getAwlSources()
try:
if self.mainWidget.coreConfigDialog.shouldSpawnServer():
......@@ -217,13 +211,17 @@ class CpuWidget(QWidget):
client.setRunState(False)
client.reset()
self.mainWidget.cpuConfigDialog.uploadToCPU()
client.setCpuSpecs(project.getCpuSpecs())
client.enableOBTempPresets(project.getObTempPresetsEn())
client.enableExtendedInsns(project.getExtInsnsEn())
self.__uploadMemReadAreas()
self.__updateOnlineViewState()
self.__setState(self.STATE_LOAD)
client.loadHardwareModule("dummy")
client.loadCode(AwlSource("gui", None, awlCode))
for awlSource in awlSources:
client.loadCode(awlSource)
client.setRunState(True)
except AwlParserError as e:
MessageBox.handleAwlParserError(self, e)
......@@ -304,12 +302,15 @@ class CpuWidget(QWidget):
self.stop()
def __updateOnlineViewState(self):
en = self.onlineViewCheckBox.checkState() == Qt.Checked
self.mainWidget.codeEdit.enableCpuStats(en)
onlineDiagEn = self.onlineViewCheckBox.checkState() == Qt.Checked
self.onlineDiagChanged.emit(onlineDiagEn)
def updateVisibleLineRange(self, source, fromLine, toLine):
onlineDiagEn = self.onlineViewCheckBox.checkState() == Qt.Checked
try:
client = self.mainWidget.getSimClient()
if en:
fromLine, toLine = self.mainWidget.codeEdit.getVisibleLineRange()
#TODO also send source identity to client
if onlineDiagEn and source:
client.setInsnStateDump(fromLine, toLine, sync=False)
else:
client.setInsnStateDump(0, 0, sync=False)
......
......@@ -124,6 +124,7 @@ class EditWidget(QPlainTextEdit):
self.lineNumWidget = LineNumSubWidget(self)
self.cpuStatsWidget = CpuStatsSubWidget(self)
self.__source = AwlSource("<gui>")
self.__runStateCopy = CpuWidget.STATE_STOP
self.__nextHdrUpdate = 0
self.__hdrAniStat = 0
......@@ -142,6 +143,46 @@ class EditWidget(QPlainTextEdit):
self.lineNumWidget.wasScrolled.connect(self.__forwardWheelEvent)
self.cpuStatsWidget.wasScrolled.connect(self.__forwardWheelEvent)
def setSource(self, source):
self.__textChangeBlocked += 1
try:
self.__source = source.dup()
self.__source.sourceBytes = b""
sourceBytes = source.sourceBytes
try:
sourceText = sourceBytes.decode(AwlParser.TEXT_ENCODING,
errors="strict")
except UnicodeError:
MessageBox.error(self, "The AWL/STL code contains "
"non-%s-characters. These were ignored and stripped "
"from the code." % AwlParser.TEXT_ENCODING)
sourceText = sourceBytes.decode(AwlParser.TEXT_ENCODING,
errors="ignore")
self.setPlainText(sourceText)
self.resetCpuStats()
finally:
self.__textChangeBlocked -= 1
def getSource(self):
source = self.__source.dup()
sourceText = self.toPlainText()
# Convert to DOS-style line endings
sourceText = "\r\n".join(sourceText.splitlines()) + "\r\n"
# Convert to binary
try:
sourceBytes = sourceText.encode(AwlParser.TEXT_ENCODING,
errors="strict")
source.sourceBytes = sourceBytes
except UnicodeError:
MessageBox.error(self, "The AWL/STL code contains "
"non-%s-characters. These were ignored and stripped "
"from the code." % AwlParser.TEXT_ENCODING)
sourceBytes = sourceText.encode(AwlParser.TEXT_ENCODING,
errors="ignore")
source.sourceBytes = sourceBytes
self.setSource(source)
return source
def runStateChanged(self, newState):
self.__runStateCopy = newState
if newState == CpuWidget.STATE_INIT:
......@@ -407,12 +448,3 @@ class EditWidget(QPlainTextEdit):
return
self.codeChanged.emit()
self.resetCpuStats()
def loadCode(self, code):
self.__textChangeBlocked += 1
self.setPlainText(code)
self.resetCpuStats()
self.__textChangeBlocked -= 1
def getCode(self):
return self.toPlainText()
......@@ -24,6 +24,7 @@ from awlsim.core.compat import *
from awlsim.gui.util import *
from awlsim.gui.editwidget import *
from awlsim.gui.projectwidget import *
from awlsim.gui.cpuconfig import *
from awlsim.gui.coreconfig import *
......@@ -40,14 +41,13 @@ class MainWidget(QWidget):
self.simClient = GuiAwlSimClient()
self.cpuConfigDialog = CpuConfigDialog(self, self.simClient)
self.coreConfigDialog = CoreConfigDialog(self, self.simClient)
self.splitter = QSplitter(Qt.Horizontal)
self.layout().addWidget(self.splitter, 0, 0)
self.codeEdit = EditWidget(self)
self.splitter.addWidget(self.codeEdit)
self.projectWidget = ProjectWidget(self)
self.splitter.addWidget(self.projectWidget)
self.cpuWidget = CpuWidget(self, self)
self.splitter.addWidget(self.cpuWidget)
......@@ -55,10 +55,13 @@ class MainWidget(QWidget):
self.filename = None
self.dirty = False
self.codeEdit.codeChanged.connect(self.__codeChanged)
self.codeEdit.codeChanged.connect(self.cpuWidget.stop)
self.projectWidget.codeChanged.connect(self.__somethingChanged)
self.projectWidget.symTabChanged.connect(self.__somethingChanged)
self.projectWidget.visibleLinesChanged.connect(self.cpuWidget.updateVisibleLineRange)
self.cpuWidget.runStateChanged.connect(self.__runStateChanged)
self.runStateChanged.connect(self.codeEdit.runStateChanged)
self.cpuWidget.onlineDiagChanged.connect(self.projectWidget.handleOnlineDiagChange)
self.cpuWidget.haveInsnDump.connect(self.projectWidget.handleInsnDump)
self.runStateChanged.connect(self.projectWidget.updateRunState)
def isDirty(self):
return self.dirty
......@@ -69,30 +72,36 @@ class MainWidget(QWidget):
def getSimClient(self):
return self.simClient
def getCodeEditWidget(self):
return self.codeEdit
def getCpuWidget(self):
return self.cpuWidget
def __codeChanged(self):
def getProject(self):
return self.projectWidget.getProject()
def __somethingChanged(self):
self.cpuWidget.stop()
self.dirty = True
self.dirtyChanged.emit(self.dirty)
def loadFile(self, filename):
try:
data = awlFileRead(filename)
except AwlParserError as e:
res = self.projectWidget.loadProjectFile(filename)
if not res:
return False
except AwlSimError as e:
QMessageBox.critical(self,
"File read failed", str(e))
"Failed to load project file", str(e))
return False
self.codeEdit.loadCode(data)
self.filename = filename
self.dirty = not bool(self.getProject().getProjectFile())
self.dirtyChanged.emit(self.dirty)
return True
def load(self):
fn, fil = QFileDialog.getOpenFileName(self,
"Open AWL/STL source", "",
"Open project", "",
"Awlsim project or AWL/STL source (*.awlpro *.awl);;"
"Awlsim project (*.awlpro);;"
"AWL source (*.awl);;"
"All files (*)")
if not fn:
......@@ -100,12 +109,15 @@ class MainWidget(QWidget):
self.loadFile(fn)
def saveFile(self, filename):
code = self.codeEdit.getCode()
try:
awlFileWrite(filename, code)
except AwlParserError as e:
res = self.projectWidget.saveProjectFile(filename)
if res == 0: # Failure
return False
elif res < 0: # Force save-as
return self.save(newFile=True)
except AwlSimError as e:
QMessageBox.critical(self,
"Failed to write file", str(e))
"Failed to write project file", str(e))
return False
self.dirty = False
self.dirtyChanged.emit(self.dirty)
......@@ -115,19 +127,23 @@ class MainWidget(QWidget):
def save(self, newFile=False):
if newFile or not self.filename:
fn, fil = QFileDialog.getSaveFileName(self,
"AWL/STL source save as", "",
"AWL source (*.awl)",
"*.awl")
"Awlsim project save as", "",
"Awlsim project (*.awlpro)",
"*.awlpro")
if not fn:
return
if not fn.endswith(".awl"):
fn += ".awl"
if not fn.endswith(".awlpro"):
fn += ".awlpro"
return self.saveFile(fn)
else:
return self.saveFile(self.filename)
def cpuConfig(self):
self.cpuConfigDialog.exec_()
project = self.getProject()
dlg = CpuConfigDialog(self, self.simClient)
dlg.loadFromProject(project)
if dlg.exec_() == dlg.Accepted:
dlg.saveToProject(project)
def coreConfig(self):
self.coreConfigDialog.exec_()
......@@ -162,6 +178,17 @@ class MainWindow(QMainWindow):
menu.addAction("&Exit...", self.close)
self.menuBar().addMenu(menu)
menu = QMenu("&Templates", self)
menu.addAction("Insert &OB", self.insertOB)
menu.addAction("Insert F&C", self.insertFC)
menu.addAction("Insert F&B", self.insertFB)
menu.addAction("Insert &instance-DB", self.insertInstanceDB)
menu.addAction("Insert &DB", self.insertGlobalDB)
menu.addSeparator()
menu.addAction("Insert FC C&ALL", self.insertFCcall)
menu.addAction("Insert FB CA&LL", self.insertFBcall)
self.menuBar().addMenu(menu)
menu = QMenu("&Simulator", self)
menu.addAction("&Awlsim core settings...", self.coreConfig)
menu.addAction("&CPU config...", self.cpuConfig)
......@@ -184,6 +211,27 @@ class MainWindow(QMainWindow):
if awlSource:
self.centralWidget().loadFile(awlSource)
def insertOB(self):
pass#TODO
def insertFC(self):
pass#TODO
def insertFB(self):
pass#TODO
def insertInstanceDB(self):
pass#TODO
def insertGlobalDB(self):
pass#TODO
def insertFCcall(self):
pass#TODO
def insertFBcall(self):
pass#TODO
def runEventLoop(self):
return self.qApplication.exec_()
......
# -*- coding: utf-8 -*-
#
# AWL simulator - GUI project widget
#
# Copyright 2014 Michael Buesch <[email protected]>
#
# 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
# (at your option) 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.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
from __future__ import division, absolute_import, print_function, unicode_literals
from awlsim.core.compat import *
from awlsim.gui.util import *
from awlsim.gui.sourcetabs import *
class ProjectWidget(QWidget):
# Signal: Some source changed
codeChanged = Signal()
# Signal: Some symbol table changed
symTabChanged = Signal()
# Signal: The visible AWL line range changed
# Parameters are: source, visibleFromLine, visibleToLine
visibleLinesChanged = Signal(AwlSource, int, int)
def __init__(self, parent=None):
QWidget.__init__(self, parent)
self.setLayout(QGridLayout())
self.__project = Project(None) # Empty project
hbox = QHBoxLayout()
self.srcButton = QRadioButton("Sources", self)
self.srcButton.setChecked(True)
hbox.addWidget(self.srcButton)
self.symTabButton = QRadioButton("Symbol tables", self)
self.symTabButton.setEnabled(False)#TODO
hbox.addWidget(self.symTabButton)
hbox.addStretch()
self.layout().addLayout(hbox, 0, 0)
self.awlTabs = AwlSourceTabWidget(self)
self.symTabs = SymSourceTabWidget(self)
self.stack = QStackedWidget(self)
self.stack.addWidget(self.awlTabs)
self.stack.addWidget(self.symTabs)
self.layout().addWidget(self.stack, 1, 0)
self.srcButton.toggled.connect(self.__mainSelectionChanged)
self.symTabButton.toggled.connect(self.__mainSelectionChanged)
self.awlTabs.sourceChanged.connect(self.codeChanged)
self.symTabs.sourceChanged.connect(self.symTabChanged)
self.awlTabs.visibleLinesChanged.connect(self.visibleLinesChanged)
def handleOnlineDiagChange(self, enabled):
self.awlTabs.handleOnlineDiagChange(enabled)
def handleInsnDump(self, insnDumpMsg):
self.awlTabs.handleInsnDump(insnDumpMsg)
def __mainSelectionChanged(self):
if self.srcButton.isChecked():
self.stack.setCurrentWidget(self.awlTabs)
elif self.symTabButton.isChecked():
self.stack.setCurrentWidget(self.symTabs)
def updateRunState(self, newRunState):
self.awlTabs.updateRunState(newRunState)
self.symTabs.updateRunState(newRunState)
def getProject(self):
"""Returns the project description object (class Project).
Do _not_ use awlSources and symTabs from this project!"""
return self.__project
def getAwlSources(self):
"Returns a list of AwlSource()s"
return self.awlTabs.getSources()
def __loadProject(self, project):
self.__project = project
if len(self.__project.getAwlSources()) > 1:
#TODO
raise AwlSimError("No support for projects with "
"multiple AWL sources, yet.")
if self.__project.getSymTabSources():
#TODO
raise AwlSimError("No support for projects with "
"symbol tables, yet.")
self.awlTabs.setSources(self.__project.getAwlSources())
#TODO self.symTabs.setSources(self.__project.getSymTabSources())
def __loadPlainAwlSource(self, filename):
project = Project(None) # Create an ad-hoc project
srcs = [ AwlSource.fromFile(filename, filename), ]
project.setAwlSources(srcs)
self.__loadProject(project)
QMessageBox.information(self,
"Opened plain AWL/STL file",
"The plain AWL/STL source file \n'%s'\n has sucessfully "
"been opened.\n\n"
"If you click on 'save', you will be asked to create "
"a project file for this source." % filename)
def loadProjectFile(self, filename):
if Project.fileIsProject(filename):
self.__loadProject(Project.fromFile(filename))
else:
# This might be a plain AWL-file.
# Try to load it.
self.__loadPlainAwlSource(filename)
return 1
def saveProjectFile(self, filename):
isAdHoc = not self.__project.getProjectFile()
if isAdHoc:
srcs = self.__project.getAwlSources()
assert(len(srcs) == 1)
if filename == srcs[0].filepath:
# This is an ad-hoc project, that was created from
# a plain AWL file. Do not overwrite the AWL file.
# Ask the user to create an .awlpro file.
res = QMessageBox.question(self,
"Create Awlsim project file?",
"The current project was created ad-hoc from a "
"plain AWL/STL file.\n"
"Can not save without creating a project file.\n\n"
"Do you want to create a project file?",
QMessageBox.Yes, QMessageBox.No)
if res != QMessageBox.Yes:
return 0
# The user has to choose a new project file name.
# Signal this to our caller.
return -1
self.__project.setProjectFile(filename)
self.__project.setAwlSources(self.getAwlSources())
#TODO self.__project.setSymTabSources(
self.__project.allFileBackingsToInternal()
self.__project.toFile()
if isAdHoc:
# We got converted to a real project. Update the tabs.
self.awlTabs.setSources(self.__project.getAwlSources())
#TODO self.symTabs.setSources(self.__project.getSymTabSources())
return 1
# -*- coding: utf-8 -*-
#
# AWL simulator - GUI source tabs
#
# Copyright 2014 Michael Buesch <[email protected]>
#
# 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
# (at your option) 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.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
from __future__ import division, absolute_import, print_function, unicode_literals
from awlsim.core.compat import *
from awlsim.gui.editwidget import *
from awlsim.gui.util import *
class SourceTabCorner(QWidget):
# Signal: Add new source
add = Signal()
# Signal: Delete current source
delete = Signal()
# Signal: Edit current source parameters
params = Signal()
def __init__(self, itemName, parent=None):