Exemple #1
0
class MyWindow(QMainWindow):
    def __init__(self):
        super(MyWindow, self).__init__()
        self.lz = []
        self.ts = 0
        self.zahlenListe = []
        dir = os.path.dirname(sys.argv[0])
        self.settingsfile = "%s/%s" % (dir, "Lotto.conf")
        self.zahlen = "%s/%s" % (dir, "zahlen.txt")
        print(self.settingsfile)
        self.settings = QSettings(self.settingsfile, QSettings.IniFormat)
        self.setStyleSheet(stylesheet(self))
        self.lottolink = 'https://www.dielottozahlende.net/lotto-6-aus-49'
        self.mysuper = 5
        self.model = QStandardItemModel(self)
        self.model.setRowCount(7)
        self.tableview = QTableView(self)
        self.tableview.setSortingEnabled(False)
        self.tableview.setGridStyle(1)
        if int(sys.version[0]) > 2:
            self.tableview.setFixedHeight(149)
        else:
            self.tableview.setFixedHeight(171)
        self.tableview.setSizeAdjustPolicy(QAbstractScrollArea.AdjustIgnored)
        self.tableview.horizontalHeader().setStretchLastSection(True)
        self.tableview.verticalHeader().setStretchLastSection(False)
        self.tableview.setCornerButtonEnabled(True)
        self.tableview.setShowGrid(True)
        self.tableview.setModel(self.model)
        self.tableview.hideRow(6)
        self.tableview.hideColumn(13)
        self.pushButtonLoad = QPushButton()
        self.pushButtonLoad.setText("Zahlen holen")
        self.pushButtonLoad.setIcon(QIcon.fromTheme("view-refresh"))
        self.pushButtonLoad.clicked.connect(self.getLotto)
        self.pushButtonLoad.setFixedWidth(110)
        self.pushButtonLoad.setFixedHeight(24)

        self.zahlenAction = QAction(QIcon.fromTheme("edit"),
                                    "Editor",
                                    self,
                                    triggered=self.edit_Tipps,
                                    shortcut="F5")
        self.addAction(self.zahlenAction)

        self.superAction = QAction(QIcon.fromTheme("edit"),
                                   "Superzahl",
                                   self,
                                   triggered=self.setMySuper,
                                   shortcut="F6")
        self.addAction(self.superAction)

        self.infoAction = QAction(QIcon.fromTheme("help-info"),
                                  "Information",
                                  self,
                                  triggered=self.showInfo,
                                  shortcut="F1")
        self.addAction(self.infoAction)

        self.lbl = QLabel()

        self.hbox = QHBoxLayout()
        self.hbox.addWidget(self.pushButtonLoad)

        grid = QVBoxLayout()
        grid.setSpacing(10)
        grid.addLayout(self.hbox)
        grid.addWidget(self.tableview)
        grid.addWidget(self.lbl)

        mywidget = QWidget()
        mywidget.setLayout(grid)
        self.setCentralWidget(mywidget)

        self.readSettings()
        self.tableview.resizeColumnsToContents()
        self.tableview.resizeRowsToContents()
        self.setHeaders()

        print("Wilkommen bei Lotto")
        self.statusBar().showMessage(
            "%s %s" % ("Willkommen bei LottoCheck",
                       " *** F5 Lottozahlen ändern *** F6 Superzahl ändern"),
            0)

    def findTableItems(self):
        model = self.tableview.model()
        self.tableview.selectionModel().clearSelection()
        print(self.zahlenListe)
        for column in range(12):
            start = model.index(0, column)
            for zahl in self.zahlenListe:

                matches = model.match(start, Qt.DisplayRole, str(zahl), 1,
                                      Qt.MatchExactly)
                if matches:
                    index = matches[0]
                    self.tableview.selectionModel().select(
                        index, QItemSelectionModel.Select)

    def saveNumbers(self):
        print(self.model.columnCount(), "Columns")
        textData = ""
        fileName, _ = QFileDialog.getSaveFileName(self, "Open File",
                                                  "~/lottozahlen.csv",
                                                  "CSV Files (*.csv)")
        if fileName:
            print("%s %s" % (fileName, "saved"))
            f = open(fileName, 'w')
            for col in range(self.model.columnCount()):
                textData += str(self.model.horizontalHeaderItem(col).text())
                textData += "\t"
            textData += "\n"
            for row in range(self.model.rowCount() - 1):
                for col in range(self.model.columnCount()):
                    textData += str(self.model.data(self.model.index(row,
                                                                     col)))
                    textData += "\t"
                textData += "\n"
            f.write(textData)
            f.close()

    def edit_Tipps(self):
        self.edWin = Editor()
        with open(self.zahlen, 'r') as f:
            text = f.read()
            self.edWin.tipp_editor.setPlainText(text)
            f.close()
        self.edWin.isModified = False

    def showInfo(self):
        link = "<p><a title='Axel Schneider' href='http://goodoldsongs.jimdo.com' target='_blank'> \
                Axel Schneider</a></p>"
        message = "<h2>LottoCheck 1.0</h2><h4>6 aus 49</h4>created by " + link + " with PyQt5<br>©September 2019<br>" \
                            + "<br>Copyright © 2017 The Qt Company Ltd and other contributors." \
                            + "<br>Qt and the Qt logo are trademarks of The Qt Company Ltd."  \
                            + "<br><br>F5 = Tipps ändern" \
                            + "<br>F6 = Superzahl ändern"
        self.msgbox(message)

    def msgbox(self, message):
        msg = QMessageBox(1, "Information", message, QMessageBox.Ok)
        msg.exec_()

    def getLotto(self):
        if not self.lz == []:
            print("values already here", self.lz)
            self.compare()
        else:
            self.lottolink = "https://www.lotto.de/lotto-6aus49/lottozahlen"
            print("getting Values")
            source = requests.get(self.lottolink).text
            soup = bsoup(source, 'lxml')
            lottoliste = []

            for td in soup.find_all(class_='LottoBall__circle'):
                lottoliste.append(td.text)

            result = lottoliste
            self.zahlenListe = result[:6]
            self.lz = result
            theSuper = lottoliste[6]
            print("Gewinnzahlen:\n", self.zahlenListe)
            self.ts = theSuper
            print("theSuper", self.ts)
            for i in range(6):
                self.model.setData(self.model.index(i, 12), result[i])
                self.model.item(i, 12).setTextAlignment(Qt.AlignCenter)
            self.compare()

    def getSpiel77(self):
        self.lottolink = "https://www.lotto.de/lotto-6aus49/lottozahlen"
        source = requests.get(self.lottolink).text
        soup = bsoup(source, 'lxml')
        result = soup.find(class_='WinningNumbersAdditionalGame__text').text

        print("Spiel 77: ", result)

        super6 = soup.find(class_='WinningNumbersAdditionalGame__text'
                           ).parent.find_next_siblings()[0].text
        print("Super 6: ", super6)

        date = soup.find(class_="WinningNumbers__date").text

        self.lbl.setText(self.lbl.text() + "\n" + date + "\n\n" +
                         result.replace("Spiel 77", "Spiel 77: ") + "\n" +
                         super6.replace("Super 6", "Super 6: "))

    def compare(self):
        ### compare all tipps
        print("self.lz: ", self.lz)
        self.lz = [int(x) for x in self.lz[:6]]
        print(self.mysuper, self.lz)
        self.lbl.clear()
        tipp = []
        for x in range(len(self.tliste)):
            t = []
            tipp = [int(x) for x in self.tliste[x]]
            #            print(tipp)
            for a in self.lz:
                if int(a) in tipp:
                    print(a, "in tipp", str(x + 1))
                    t.append(a)

            rtext = ""
            print("len(t) ", len(t))
            if len(t) == 2 and self.mysuper == self.ts:
                rtext += self.lbl.text()
                rtext += '\ngewonnen in Tipp '
                rtext += str(int(x) + 1)
                rtext += " : "
                rtext += str(t)
                rtext += " *** "
                rtext += str(len(t))
                rtext += "er ***"
                rtext += ' + Superzahl'
                self.lbl.setText(rtext)
            elif len(t) > 2:
                if self.mysuper == self.ts:
                    rtext += self.lbl.text()
                    rtext += '\ngewonnen in Tipp '
                    rtext += str(int(x) + 1)
                    rtext += " : "
                    rtext += str(t)
                    rtext += " *** "
                    rtext += str(len(t))
                    rtext += "er ***"
                    rtext += ' + Superzahl'
                    self.lbl.setText(rtext)
                else:
                    rtext += self.lbl.text()
                    rtext += '\ngewonnen in Tipp '
                    rtext += str(int(x) + 1)
                    rtext += " : "
                    rtext += str(t)
                    rtext += " *** "
                    rtext += str(len(t))
                    rtext += "er ***"
                    self.lbl.setText(rtext)

        if self.lbl.text() == "":
            self.lbl.setText("leider nichts gewonnen ...\n")
        self.statusBar().showMessage(
            "%s %s %s %s" %
            ("Gewinnzahlen: ",
             (', '.join(str(x)
                        for x in self.lz)), " *** Superzahl: ", str(self.ts)),
            0)
        self.getSpiel77()
        self.findTableItems()

    def setHeaders(self):
        self.tableview.horizontalHeader().setVisible(True)
        self.tableview.verticalHeader().setVisible(False)
        for x in range(self.model.columnCount() - 1):
            self.model.setHeaderData(x, Qt.Horizontal,
                                     "%s %s" % ("Tipp", (x + 1)))
        self.model.setHeaderData(self.model.columnCount() - 1, Qt.Horizontal,
                                 "Gewinnzahlen")
        self.tableview.setAlternatingRowColors(True)
        self.tableview.resizeColumnsToContents()
        self.tableview.resizeRowsToContents()

    def closeEvent(self, event):
        print("Goodbye ...")
        self.writeSettings()

    def setMySuper(self):
        s = int(self.mysuper)
        dlg = QInputDialog()
        ms, ok = dlg.getInt(self, 'Superzahl:', "", s, 0, 9, 1, Qt.Dialog)
        if ok:
            self.mysuper = ms
            print("Superzahl =", self.mysuper)

    def readSettings(self):
        if self.settings.contains("mysuper"):
            self.mysuper = self.settings.value("mysuper")
        else:
            self.setMySuper()
        print("Superzahl:", self.mysuper)
        self.tliste = []
        with open(self.zahlen, 'r') as f:
            text = f.read()
            f.close()
            for line in text.splitlines():
                self.tliste.append(line.split(","))
            self.model.setColumnCount(len(self.tliste) + 1)
            for x in range(0, len(self.tliste)):
                tipp = self.tliste[x]
                for i in range(len(tipp)):
                    self.model.setData(self.model.index(i, x), tipp[i])
                    self.model.item(i, x).setTextAlignment(Qt.AlignCenter)

    def writeSettings(self):
        self.settings.setValue("mysuper", self.mysuper)
Exemple #2
0
class StickyLoad(QDialog):
    def __init__(self):
        super().__init__()
        self.resize(600, 400)
        # Получение id стикера для загрузки
        self.setLayout(self.myLayout())
        # Словарь горячих клавиш
        self.hotkeys = {
            (Qt.Key_W, int(Qt.ControlModifier)): self.close,
            (Qt.Key_Q, int(Qt.ControlModifier)): qApp.myQuit,
            (Qt.Key_Enter - 1, int(Qt.NoModifier)): self.getSIDandClose,
            (Qt.Key_Delete, int(Qt.NoModifier)): self.deleteRow
        }
        self.view.doubleClicked.connect(self.getSIDandClose)
        self.exec_()

    def myLayout(self):
        qmodel = QtSql.QSqlQueryModel()
        qmodel.setQuery("select id, text from sticky order by id desc")
        self.view = QTableView(self)
        self.view.setModel(qmodel)
        self.view.verticalHeader().hide()
        self.view.resizeColumnToContents(0)
        self.view.horizontalHeader().setStretchLastSection(True)
        self.view.setSelectionBehavior(QTableView.SelectRows)
        self.view.setSelectionMode(QTableView.SingleSelection)
        self.view.selectRow(0)
        layout = QVBoxLayout()
        layout.addWidget(self.view)
        layout.setContentsMargins(0, 0, 0, 0)
        return layout

    def getSID(self):
        index = self.view.selectedIndexes()[0]
        self.sid = self.view.model().data(index)

    def getSIDandClose(self):
        self.getSID()
        self.close()

    def keyPressEvent(self, event):
        self.hotkeys.get((event.key(), int(event.modifiers())), lambda: None)()

    def deleteRow(self):
        self.getSID()
        qmodel = QtSql.QSqlQueryModel()
        qmodel.setQuery("delete from sticky where id = {}".format(self.sid))
        deletedRow = self.view.selectedIndexes()[0].row()
        for x in (i for j in (range(deletedRow + 1,
                                    self.view.model().rowCount()),
                              range(deletedRow - 1, -1, -1)) for i in j):
            if self.view.isRowHidden(x):
                continue
            rowToSelecet = x
            break
        try:
            self.view.selectRow(rowToSelecet)
        except:
            self.view.clearSelection()
        self.view.hideRow(deletedRow)
        del self.sid

    def __repr__(self):
        return repr(self.sid)
Exemple #3
0
class App(QMainWindow):
    def __init__(self):
        super().__init__()
        self.title = 'EOVSA Imager'
        self.left = 0
        self.top = 0
        self.width = 1200
        self.height = 900
        self.setWindowTitle(self.title)
        self.setGeometry(self.left, self.top, self.width, self.height)
        self._main = QWidget()
        self.setCentralWidget(self._main)
        self.initUI()
        self.threadpool = QThreadPool()

    def initUI(self):

        self.statusBar = QStatusBar()
        self.setStatusBar(self.statusBar)
        self.progressBar = QProgressBar()
        self.progressBar.setGeometry(10, 10, 200, 15)

        layout = QVBoxLayout()
        # Initialize tab screen
        self.tabs = QTabWidget()
        tab1 = QWidget()
        tab2 = QWidget()
        tab3 = QWidget()
        tab4 = QWidget()
        tab5 = QWidget()
        tab6 = QWidget()

        # Add tabs
        self.tabs.addTab(tab1, "Data Select")
        self.tabs.addTab(tab2, "Viewer")
        self.tabs.addTab(tab3, "Imager")
        self.tabs.addTab(tab4, "Selfcal")
        self.tabs.addTab(tab5, "Production")
        self.tabs.addTab(tab6, "Export")

        # Each tab's user interface is complex, so this splits them into separate functions.
        self.initUItab1()
        self.initUItab2()
        self.initUItab3()
        self.initUItab4()
        self.initUItab5()
        self.initUItab6()

        self.tabs.currentChanged.connect(self.tabChanged)

        # Add tabs to widget
        layout.addWidget(self.tabs)
        self._main.setLayout(layout)

        self.show()

    def tabChanged(self, i):
        if i == 2:
            self.update_params()

#
# Data Select Tab User Interface
#

    def initUItab1(self):
        # Create main layout (a Vertical Layout)
        mainlayout = QVBoxLayout()

        # Create Data Select tab
        upperbox = QHBoxLayout()  # Has two hboxes: leftbox and msinfobox
        leftbox = QVBoxLayout(
        )  # Has a gridlayout: filenamebox; and groupBox: selectBox
        # Filename entry
        filenamebox = QGridLayout()
        leftbox.addLayout(filenamebox)
        # Create LineEdit widget for ms filename
        self.msentry = QLineEdit()
        self.fname = '<Select or enter a valid ms filename>'
        self.msentry.resize(8 * len(self.fname), 20)
        self.ms = None  # No ms yet
        self.msdata = None  # No amplitude data read from ms yet
        self.msentry.setText(self.fname)
        self.msentry.returnPressed.connect(self.on_return)
        filenamebox.addWidget(QLabel("MS Filename"), 0, 0)
        filenamebox.addWidget(self.msentry, 1, 0, 1, 4)
        # Create Browse button
        myButton1 = QPushButton("Browse")
        myButton1.clicked.connect(self.on_click)
        filenamebox.addWidget(myButton1, 1, 5)
        upperbox.addLayout(leftbox)

        # Create label and TextEdit widget for ms information
        msinfobox = QVBoxLayout()
        upperbox.addLayout(msinfobox)
        self.infoEdit = QTextEdit()
        #f = QFont("Courier",9)
        #self.infoEdit.setCurrentFont(f)
        self.infoEdit.setReadOnly(True)
        self.infoEdit.setMinimumHeight(300)
        self.infoEdit.setMinimumWidth(550)
        msinfobox.addWidget(QLabel("MS Information"))
        msinfobox.addWidget(self.infoEdit)
        mainlayout.addLayout(upperbox)

        # Data Selection
        selectBox = QGroupBox("Data Selection Criteria")
        selectarea = QFormLayout()
        self.trangeEdit = QLineEdit()
        selectarea.addRow("Timerange", self.trangeEdit)
        self.spwEdit = QLineEdit()
        selectarea.addRow("Sp. Window", self.spwEdit)
        self.baselineEdit = QLineEdit()
        selectarea.addRow("Baseline", self.baselineEdit)
        self.stokesEdit = QLineEdit()
        selectarea.addRow("Stokes", self.stokesEdit)
        self.uvrangeEdit = QLineEdit()
        selectarea.addRow("UV range", self.uvrangeEdit)
        selectBox.setLayout(selectarea)
        leftbox.addWidget(selectBox)
        drawselect = QPushButton('Draw Selection on Plot')
        drawselect.clicked.connect(self.drawsel)
        leftbox.addWidget(drawselect)
        leftbox.addStretch(1)

        #hbox.addStretch(1)

        playarea = QHBoxLayout()
        playarea.addStretch(1)
        xferarea = QVBoxLayout()
        xferDownRight = QPushButton('Use Selection for Plot Limits')
        xferDownRight.setIcon(QIcon('icons/down_right.png'))
        xferDownRight.clicked.connect(self.xferDR)
        xferarea.addWidget(xferDownRight)
        xferUpLeft = QPushButton('Use Plot Limits for Selection')
        xferUpLeft.setIcon(QIcon('icons/up_left.png'))
        xferUpLeft.clicked.connect(self.xferUL)
        xferarea.addWidget(xferUpLeft)
        xferarea.addStretch(1)
        playarea.addLayout(xferarea)
        # Add a figure to the canvas.
        plotarea = QVBoxLayout()
        self.speccanvas = FigureCanvas(Figure(figsize=(8, 3)))
        plotarea.addWidget(self.speccanvas)
        xferarea.addWidget(NavigationToolbar(self.speccanvas, self))
        maxmin = QHBoxLayout()
        minlabel = QLabel('Min[sfu]')
        maxlabel = QLabel('Max[sfu]')
        self.minentry = QLineEdit()
        self.maxentry = QLineEdit()
        maxmin.addStretch(1)
        maxmin.addWidget(minlabel)
        maxmin.addWidget(self.minentry)
        maxmin.addWidget(maxlabel)
        maxmin.addWidget(self.maxentry)
        maxmin.addStretch(1)
        self.ignore_gaps = QCheckBox('Ignore Gaps')
        maxmin.addWidget(self.ignore_gaps)
        maxmin.addStretch(1)
        self.minentry.returnPressed.connect(self.plot_data)
        self.maxentry.returnPressed.connect(self.plot_data)
        plotarea.addLayout(maxmin)
        playarea.addLayout(plotarea)
        #        self.addToolBar(Qt.BottomToolBarArea, NavigationToolbar(self.speccanvas, self))
        self.dspec_ax = self.speccanvas.figure.subplots()
        baseline_layout = QGridLayout()
        baseline_layout.setSpacing(1)
        ant1 = []
        ant2 = []
        nant = 13
        bl2ord = get_bl2ord(nant)  # Lookup table for 13-ant baseline matrix
        nbl = int(nant * (nant + 1) / 2)
        self.blcheck = [
            0
        ] * nbl  # This will hold the check box widgets for the baselines
        self.blchecked = [False] * nbl  # This is the state of the buttons
        self.prev = None
        for i in range(nant):
            ant1.append(QPushButton('{:2d}'.format(i + 1)))
            ant1[-1].setCheckable(True)
            ant1[-1].setMaximumSize(12, 12)  # Ant button size in pixels
            ant1[-1].setStyleSheet("font : 8px;")
            ant1[-1].clicked.connect(partial(self.ant_click, ant1[-1]))
            baseline_layout.addWidget(
                ant1[-1], 0, i)  # Grid location of ant buttons along top
            ant2.append(QPushButton('{:2d}'.format(i + 1)))
            ant2[-1].setCheckable(True)
            ant2[-1].setMaximumSize(12, 12)  # Ant button size in pixels
            ant2[-1].setStyleSheet("font : 8px;")
            ant2[-1].clicked.connect(partial(self.ant_click, ant2[-1]))
            baseline_layout.addWidget(
                ant2[-1], i + 1,
                nant)  # Grid location of ant buttons along side
            for j in range(i, nant):
                if i == j:
                    self.blcheck[bl2ord[i, j]] = QCheckBox("")
                    button = self.blcheck[bl2ord[i, j]]  # Just saves typing
                    button.setStyleSheet("background-color : #8888ff;")
                else:
                    self.blcheck[bl2ord[i, j]] = QCheckBox("")
                    button = self.blcheck[bl2ord[i, j]]  # Just saves typing
                    button.setStyleSheet("background-color : #ff8888;")
                button.setMaximumSize(12, 12)
                button.clicked.connect(self.baselineClicked)
                baseline_layout.addWidget(
                    button, i + 1, j)  # Grid location of baseline buttons
        self.ant1buttons = ant1
        self.ant2buttons = ant2
        unsel = QPushButton('Unselect All')
        unsel.setStyleSheet("font : 10px;")
        unsel.clicked.connect(partial(self.sel_clicked, False))
        sel = QPushButton('Select All')
        sel.setStyleSheet("font : 10px;")
        sel.clicked.connect(partial(self.sel_clicked, True))
        baseline_layout.addWidget(unsel, 9, 0, 2, 7)
        baseline_layout.addWidget(sel, 11, 0, 2, 7)
        space = QVBoxLayout()
        # Add Polarization Button Group
        self.polGroup = QButtonGroup()
        self.polGroup.buttonClicked.connect(self.get_data)
        xxButton = QRadioButton("XX")
        xxButton.setChecked(True)
        yyButton = QRadioButton("YY")
        xyButton = QRadioButton("XY")
        yxButton = QRadioButton("YX")
        rrButton = QRadioButton("RR")
        llButton = QRadioButton("LL")
        self.polGroup.addButton(xxButton)
        self.polGroup.addButton(yyButton)
        self.polGroup.addButton(xyButton)
        self.polGroup.addButton(yxButton)
        self.polGroup.addButton(rrButton)
        self.polGroup.addButton(llButton)
        pspace = QHBoxLayout()
        pspace.addWidget(xxButton)
        pspace.addWidget(yyButton)
        pspace.addWidget(xyButton)
        pspace.addWidget(yxButton)
        pspace.addWidget(rrButton)
        pspace.addWidget(llButton)
        space.addStretch(1)
        space.addLayout(pspace)
        space.addWidget(QLabel('Baseline Selection Map'))
        space.addLayout(baseline_layout)
        space.addStretch(1)
        gobutton = QPushButton('Go')
        gobutton.clicked.connect(self.get_data)
        gobutton.setStyleSheet("background-color : #ffff88;")
        space.addWidget(gobutton)
        playarea.addLayout(space)
        mainlayout.addLayout(playarea)
        self.tabs.widget(0).setLayout(mainlayout)

#
# Viewer Tab User Interface
#

    def initUItab2(self):
        # Create main layout (a Vertical Layout)
        mainlayout = QVBoxLayout()
        mainlayout.addWidget(QLabel("This is the viewer tab"))
        self.tabs.widget(1).setLayout(mainlayout)

#
# Imager Tab User Interface
#

    def initUItab3(self):
        # Create main layout (a Horizontal Layout)
        mainlayout = QHBoxLayout()
        # Create a left and right side (both Vertical Layouts)
        leftlayout = QVBoxLayout()
        rightlayout = QVBoxLayout()

        # Create a table for interacting with parameters
        self.table = QTableView()
        self.table.doubleClicked.connect(self.tblRowClicked)
        # For playing with, I will make a static table of tclean parameters
        tbl = [
            ["Data Selection", "", []],
            ["  vis", "''", []],
            #["  field","''",[]],
            [
                "  spw", "''", [],
                "Spectral Window:\n default: ''=all; examples:\n spw='0~2,4'; spectral windows 0,1,2,4 (all channels)\n spw='0:5~61'; spw 0, channels 5 to 61\n spw='<2';   spectral windows less than 2 (i.e. 0,1)"
            ],
            [
                "  timerange", "''", [],
                "Range of time to select from data\n default: '' (all); examples:\n timerange = 'YYYY/MM/DD/hh:mm:ss~YYYY/MM/DD/hh:mm:ss'\n Note: if YYYY/MM/DD is missing date defaults to first day in data set\n timerange='09:14:0~09:54:0' picks 40 min on first day\n timerange='25:00:00~27:30:00' picks 1 hr to 3 hr 30 min on NEXT day\n timerange='09:44:00' pick data within one integration of time\n timerange='> 10:24:00' data after this time"
            ],
            [
                "  uvrange", "''", [],
                "Select data within uvrange (default unit is meters) [default: '' (all)]\n examples:\n uvrange='0~1000klambda'; uvrange from 0-1000 kilo-lambda\n uvrange='> 4klambda';uvranges greater than 4 kilo lambda"
            ],
            [
                "  antenna", "''", [],
                "Select data on 0-based antenna/baseline index [default: '' (all)]\n examples:\n antenna='0~5,7~12&0~5,7~12'; all baselines not including antenna index 6\n antenna='5&6;7&8'; baselines 5-6 and 7-8\n antenna='5'; all baselines with antenna index 5\n antenna='5,6,9'; all baselines with antenna index numbers 5,6,9"
            ],
            ["  datacolumn", "'data'", ["'data'", "'corrected'", "'model'"]],
            ["Image Definition", "", []],
            ["  imagename", "''", []],
            ["  imsize", "[128,128]", []],
            ["  cellsize", "'2arcsec'", []],
            [
                "  phaseshift", "[0, 0]", [],
                "X, Y offset of center of map from Sun Center"
            ],
            [
                "  stokes", "'XX'",
                ["'XX'", "'YY'", "'I'", "'V'", "'IV'", "'RR'", "'LL'"]
            ],
            ["  startmodel", "''", []],
            ["  specmode", "'mfs'", ["'mfs'", "'cubedata'"]],
            ["Deconvolution Options", "", []],
            [
                "  deconvolver", "'multiscale'",
                [
                    "'hogbom'", "'clark'", "'clarkstokes'", "'multiscale'",
                    "'mem'"
                ]
            ],
            ["  scales", "[1,5,10]", []],
            ["  restoringbeam", "''", []],
            ["  pbcor", "False", ['True', 'False']],
            ["Weighting", "", []],
            [
                "  weighting", "'briggs'",
                ["'natural'", "'uniform'", "'briggs'"]
            ],
            ["  robust", "0.5", []],
            ["  uvtaper", "''", []],
            ["Other Options", "", []],
            ["  niter", "0", []],
            ["  gain", "0.1", []],
            ["  threshold", "0", []],
            ["  interactive", "False", ['True', 'False']],
            ["  mask", "''", []]
        ]

        self.table.verticalHeader().setDefaultSectionSize(
            14)  # Sets height of cells to 14px
        self.table.setModel(TableModel(
            tbl))  # The TableModel class is defined at the top of this file

        # For parameters that have to be only a limited set of fixed values, create a combobox dropdown
        # for editing them.
        for idx in range(len(tbl)):
            if len(tbl[idx]) > 2:
                if len(tbl[idx][2]) > 1:
                    i = self.table.model().index(idx, 2)
                    c = QComboBox()
                    for item in tbl[idx][2]:
                        c.addItem(item)
                    c.currentTextChanged.connect(self.handleCombo)
                    c.index = self.table.model().index(idx, 1)
                    self.table.setIndexWidget(i, c)

        self.table.resizeColumnsToContents()
        self.table.model().dataChanged.connect(self.update_view)

        # Determine the header rows in the table (indicated by NOT starting with a blank space).
        self.headerrows = []
        for i, tblrow in enumerate(tbl):
            if tbl[i][0][0] != ' ':
                self.headerrows.append(i)
                self.table.setSpan(i, 0, 1, 2)
        self.headerrows.append(
            len(tbl))  #Add length of table, for finding length of last section

        titlelayout = QHBoxLayout()
        titlelayout.addWidget(QLabel("TCLEAN Parameters"))
        titlelayout.addSpacing(100)
        updateButton = QPushButton("Update Parameters")
        titlelayout.addWidget(updateButton)
        updateButton.clicked.connect(self.update_params)
        titlelayout.addSpacing(100)
        scriptButton = QPushButton("Save to CASA Script")
        titlelayout.addWidget(scriptButton)
        scriptButton.clicked.connect(self.save2CASAscript)
        titlelayout.addStretch(1)
        tablelayout = QHBoxLayout()
        self.table.setMinimumSize(600, 300)
        tablelayout.addWidget(self.table)
        self.nofits = QCheckBox('Skip conversion to FITS')
        self.nocleanup = QCheckBox('Keep CASA image files')
        tablelayout.addStretch(1)
        leftlayout.addLayout(titlelayout)
        leftlayout.addLayout(tablelayout)
        leftlayout.addWidget(self.nofits)
        leftlayout.addWidget(self.nocleanup)
        self.scriptEdit = QTextEdit()
        #f = QFont("Courier",9)
        #self.infoEdit.setCurrentFont(f)
        self.scriptEdit.setReadOnly(True)
        self.scriptEdit.setMinimumHeight(300)
        self.scriptEdit.setMinimumWidth(550)
        leftlayout.addWidget(QLabel("Generated Script"))
        leftlayout.addWidget(self.scriptEdit)
        execButton = QPushButton('Execute Script')
        execButton.clicked.connect(self.execscript)
        eblayout = QHBoxLayout()
        eblayout.addWidget(execButton)
        eblayout.addStretch(1)
        leftlayout.addLayout(eblayout)
        leftlayout.addStretch(1)
        mainlayout.addLayout(leftlayout)

        self.imgcanvas = FigureCanvas(Figure(figsize=(7, 6)))
        rightlayout.addWidget(self.imgcanvas)
        rightlayout.addWidget(NavigationToolbar(self.imgcanvas, self))
        self.img_ax = self.imgcanvas.figure.subplots()

        mainlayout.addLayout(rightlayout)

        self.tabs.widget(2).setLayout(mainlayout)

    def save2CASAscript(self):
        script = ['from casatasks import tclean']
        tbl = self.table.model()._data
        for k, row in enumerate(tbl):
            if row[0][0] == ' ':
                # This is a parameter row.  Most such rows can be defined directly, but some require translation
                param = row[0].lstrip()
                if param == 'phaseshift':
                    # Determine appropriate phase center and set accordingly
                    try:
                        pshift = [
                            float(i) for i in row[1].replace('[', '').replace(
                                ']', '').split(',')
                        ]
                    except:
                        self.statusBar.showMessage(
                            'Error translating phaseshift parameter--using zero',
                            2000)
                        pshift = [0.0, 0.0]
                    script.append("phasecenter='" +
                                  pshift2pcenter(pshift, self.ms) + "'")
                elif param == 'imagename':
                    if row[1] == "''":
                        msstem = os.path.basename(
                            os.path.splitext(self.fname)[0])
                        script.append("imagename='images/" + msstem + "'")
                    else:
                        script.append(param + "=" + row[1])
                else:
                    if row[1] == "''":
                        script.append(param + "=''")
                    else:
                        script.append(param + "=" + row[1])

        script.append(
            "tclean(vis=vis, selectdata=True, field='', spw=spw, timerange=timerange, uvrange=uvrange, antenna=antenna, scan='', observation='', intent='', datacolumn=datacolumn, imagename=imagename, imsize=imsize, cell=cellsize, phasecenter=phasecenter, stokes=stokes, projection='SIN', startmodel='', specmode=specmode, reffreq='', nchan=-1, start='', width='', outframe='LSRK', veltype='radio', restfreq=[], interpolation='linear', perchanweightdensity=True, gridder='standard', facets=1, psfphasecenter='', chanchunks=1, wprojplanes=1, vptable='', mosweight=True, aterm=True, psterm=False, wbawp=True, conjbeams=False, cfcache='', usepointing=False, computepastep=360.0, rotatepastep=360.0, pointingoffsetsigdev=[], pblimit=0.2, normtype='flatnoise', deconvolver=deconvolver, scales=scales, nterms=2, smallscalebias=0.0, restoration=True, restoringbeam=restoringbeam, pbcor=pbcor, outlierfile='', weighting=weighting, robust=robust, noise='1.0Jy', npixels=0, uvtaper=uvtaper, niter=niter, gain=gain, threshold=threshold, nsigma=0.0, cycleniter=-1, cyclefactor=1.0, minpsffraction=0.05, maxpsffraction=0.8, interactive=interactive, usemask='user', mask=mask, pbmask=0.0, sidelobethreshold=3.0, noisethreshold=5.0, lownoisethreshold=1.5, negativethreshold=0.0, smoothfactor=1.0, minbeamfrac=0.3, cutthreshold=0.01, growiterations=75, dogrowprune=True, minpercentchange=-1.0, verbose=False, fastnoise=True, restart=True, savemodel='none', calcres=True, calcpsf=True, parallel=False)"
        )
        if not self.nofits.isChecked():
            script.append("import helioim2fits as hf")
            script.append(
                "fitsfile = hf.imreg(vis=vis,imagefile=imagename+'.image',fitsdir='fits',timerange=timerange)"
            )
        if not self.nocleanup.isChecked():
            script.append("import glob, shutil")
            script.append(
                "for file in glob.glob(imagename+'.*'): shutil.rmtree(file)")
            if self.nofits.isChecked():
                self.statusBar.showMessage(
                    'Warning! You have selected no FITs output and do not keep CASA images, so you will get no output!',
                    2000)
        self.script = script
        self.scriptEdit.setPlainText('\n'.join(script))

    def execscript(self):
        #print('Before call:')
        #print(exec)
        #print(['\n'.join(self.script)])
        worker = Worker(exec, '\n'.join(self.script))
        worker.signals.finished.connect(self.thread_complete)
        self.threadpool.start(worker)
        print('The thread is started--just waiting for the signal.')

    def thread_complete(self):
        print('The script execution thread is done!')
        print(
            'The fits file should have been completed, and can be read now...')
        sleep(1)  # Make sure file is closed...
        list_of_files = glob.glob('fits/*')
        latest_file = max(list_of_files, key=os.path.getctime)
        img, h = fits.getdata(latest_file, header=True)
        img.shape = (h['NAXIS2'], h['NAXIS1'])
        self.img_ax.cla()
        xval = np.linspace((h['CRVAL1'] - h['NAXIS1']) * h['CDELT1'] / 2.,
                           (h['CRVAL1'] + h['NAXIS1']) * h['CDELT1'] / 2.,
                           h['NAXIS1'])
        yval = np.linspace((h['CRVAL2'] - h['NAXIS2']) * h['CDELT2'] / 2.,
                           (h['CRVAL2'] + h['NAXIS2']) * h['CDELT2'] / 2.,
                           h['NAXIS2'])
        im = self.img_ax.pcolormesh(xval, yval, img)

        self.img_ax.set_aspect('equal')
        self.img_ax.set_ylabel('Y [arcsec]')
        self.img_ax.set_xlabel('X [arcsec]')
        self.img_ax.set_title(latest_file)
        self.imgcanvas.draw()

    def update_params(self):
        ''' Take entries from Data Select tab and update the tclean parameter table
        '''
        tbl = self.table.model()._data
        params = ['vis', 'spw', 'timerange', 'uvrange', 'antenna', 'stokes']
        curvalues = [
            self.fname,
            self.spwEdit.text(),
            self.trangeEdit.text(),
            self.uvrangeEdit.text(),
            self.baselineEdit.text(),
            self.stokesEdit.text()
        ]
        for k, row in enumerate(tbl):
            r = row[0][2:]  # parameter name (after removing two blank spaces)
            for idx, p in enumerate(params):
                if r == p:
                    i = self.table.model().index(
                        k, 1)  # Table index of parameter value
                    # Write the new value into the table (all of these are strings, so include quotes)
                    self.table.model().setData(i, "'" + curvalues[idx] + "'",
                                               Qt.EditRole)
                    if r == 'stokes':
                        # Stokes has a combobox, so set it according to the updated parameter
                        i2 = self.table.model().index(
                            k, 2)  # Table index of combobox widget
                        c = self.table.indexWidget(i2)
                        loc = c.findText("'" + curvalues[idx] + "'")
                        if loc == -1:
                            # If curvalue is not found in the combobox, indicate stokes as unset.
                            self.table.model().setData(i, '<unset>',
                                                       Qt.EditRole)
                        else:
                            c.setCurrentIndex(loc)

    def update_view(self):
        self.table.resizeColumnsToContents()

    def handleCombo(self):
        sender = self.sender()
        text = sender.currentText()
        self.table.model().setData(sender.index, text, Qt.EditRole)

    def tblRowClicked(self, index):
        ''' If a header row is double-clicked, show or hide the rows corresponding to that section
        '''
        hrows = self.headerrows  # Row numbers corresponding to headers
        for n, row in enumerate(hrows):
            if index.row() == row:
                # The row of the clicked cell is a header row, so identify the lines between it
                # and the next header and toggle hidden
                k1 = hrows[n] + 1
                k2 = hrows[n + 1]
                if self.table.isRowHidden(k1):
                    # Rows are aleady hidden, so show them
                    for i in range(k1, k2):
                        self.table.showRow(i)
                else:
                    # Rows are visible, so hide them
                    for i in range(k1, k2):
                        self.table.hideRow(i)
                break
#
# Selfcal Tab User Interface
#

    def initUItab4(self):
        # Create main layout (a Vertical Layout)
        mainlayout = QVBoxLayout()
        mainlayout.addWidget(QLabel("This is the selfcal tab"))
        self.tabs.widget(3).setLayout(mainlayout)

#
# Production Tab User Interface
#

    def initUItab5(self):
        # Create main layout (a Vertical Layout)
        mainlayout = QVBoxLayout()
        mainlayout.addWidget(QLabel("This is the production tab"))
        self.tabs.widget(4).setLayout(mainlayout)


#
# Export Tab User Interface
#

    def initUItab6(self):
        # Create main layout (a Vertical Layout)
        mainlayout = QVBoxLayout()
        mainlayout.addWidget(QLabel("This is the export tab"))
        self.tabs.widget(5).setLayout(mainlayout)

    def baselineClicked(self):
        button = self.sender()
        if button.isChecked():
            if not self.prev is None:
                if self.prev.isChecked():
                    self.prev.setChecked(False)
            self.prev = button
            self.get_data()

    def on_return(self):
        ''' Called when the ms filename LineEdit widget gets a carriage-return.
            Trys to connect to the ms and return some info (no data read at this time)
        '''
        self.fname = self.msentry.text()
        self.msdata = None
        try:
            if self.ms:
                self.ms.close()
            else:
                self.ms = mstool()
            self.ms.open(self.fname)
            self.msentry.setText(self.fname)
            lines = pr_summary(self.ms)
            self.infoEdit.setPlainText('\n'.join(lines))
        except:
            self.statusBar.showMessage('Filename is not a valid ms', 2000)
            self.fname = '<Select or enter a valid ms filename>'
            self.msentry.setText(self.fname)
            self.infoEdit.setPlainText('')
        try:
            t1str, t2str = lines[2].replace('-', '/').split(': ')[1:3]
            trange = t1str[0:10] + '/' + t1str[11:21] + '~' + t2str[
                0:10] + '/' + t2str[11:21]
        except:
            trange = '<unset>'
        try:
            spwstr = lines[-1].strip().split(' ')[0]
            spw = '0~' + spwstr
        except:
            spw = '<unset>'
        self.trangeEdit.setText(trange)
        self.spwEdit.setText(spw)
        self.baselineEdit.setText('0~12&0~12')
        self.stokesEdit.setText('XX')
        self.uvrangeEdit.setText('0~2km')
        if spwstr == '30':
            self.ignore_gaps.setChecked(True)
        else:
            self.ignore_gaps.setChecked(False)

    def on_click(self):
        ''' Handle Browse button '''
        self.fname = QFileDialog.getExistingDirectory(self, 'Select MS', './',
                                                      QFileDialog.ShowDirsOnly)
        self.msentry.setText(self.fname)
        self.on_return()

    def ant_click(self, button):
        ''' An antenna button was clicked, so check the checkboxes corresponding
            to the currently checked antennas.  button is a dictionary with keys
            'button' (actual button handle) and 'iant' (ordinal location in list
            of buttons).  
            Each antenna has two buttons, so we have to ensure that both are toggled.  
            iant is an integer from 0-25.  If even, toggle buttons iant and iant+1.  
            If odd, toggle buttons iant and iant-1.
        '''
        k = 0
        ant = -1
        for b in self.ant1buttons:
            if b == button:
                self.ant2buttons[k].setChecked(button.isChecked())
                ant = k
                break
            else:
                k += 1
        k = 0
        for b in self.ant2buttons:
            if b == button:
                self.ant1buttons[k].setChecked(button.isChecked())
                ant = k
                break
            else:
                k += 1
        self.statusBar.showMessage('Antenna ' + str(ant + 1) + ' hit', 1000)
        nant = 13
        bl2ord = get_bl2ord(nant)
        # Reflect antenna state on baselines
        for i in range(nant):
            for j in range(i, nant):
                if i != j:
                    if i == ant:
                        self.blcheck[bl2ord[i,
                                            j]].setChecked(button.isChecked())
                    if j == ant:
                        self.blcheck[bl2ord[i,
                                            j]].setChecked(button.isChecked())
        # Have to go back and set any baselines whose antennas are checked.
        for i in range(nant):
            if self.ant1buttons[i].isChecked():
                for k in range(i):
                    self.blcheck[bl2ord[k, i]].setChecked(True)
                for j in range(i + 1, nant):
                    self.blcheck[bl2ord[i, j]].setChecked(True)

    def sel_clicked(self, state):
        nant = 13
        bl2ord = get_bl2ord(nant)
        for i in range(nant):
            self.ant1buttons[i].setChecked(state)
            self.ant2buttons[i].setChecked(state)
            for j in range(i, nant):
                self.blcheck[bl2ord[i, j]].setChecked(state)

    def get_data(self):
        # Find which baselines are checked:
        bl = []
        blid = []
        isauto = []
        ord13 = get_bl2ord(13)
        ord16 = get_bl2ord(16)
        for i in range(13):
            for j in range(i, 13):
                if self.blcheck[ord13[i, j]].isChecked():
                    bl.append(ord16[i, j])
                    isauto.append(i == j)
                    blid.append([i, j])
        if len(bl) == 0:
            self.statusBar.showMessage('No baselines have been selected.',
                                       2000)
            return
        if len(bl) != 1:
            self.statusBar.showMessage('Only one baseline can be shown.', 2000)
            return

        # If no data have been read, read the data
        if self.msdata is None:
            self.read_msdata(fillnan=np.nan)
        npol, nf, nbl, nt = self.msdata['data'].shape
        self.plotbl = bl[0]

        # Determine which polarization state is selected by radiobuttons
        polnum = {'XX': 0, 'YY': 1, 'XY': 2, 'YX': 3, 'RR': 4, 'LL': 5}
        ipol = polnum[self.polGroup.checkedButton().text()]

        if ipol < 4:
            # The selected polarization is just one of the native polarizations (XX, YY, XY, or YX)
            spec = np.abs(self.msdata['data'][ipol, :, self.plotbl])
        elif ipol == 4:
            # The selected polarization is RR (not implemented because we only have amplitudes...)
            xx = self.msdata['data'][0, :, self.plotbl]
            yy = self.msdata['data'][1, :, self.plotbl]
            xy = self.msdata['data'][2, :, self.plotbl]
            yx = self.msdata['data'][3, :, self.plotbl]
            rr = (xx + yy + 1j * (xy - yx)) / 2
            spec = np.abs(rr)
        elif ipol == 5:
            # The selected polarization is LL (not implemented because we only have amplitudes...)
            xx = self.msdata['data'][0, :, self.plotbl]
            yy = self.msdata['data'][1, :, self.plotbl]
            xy = self.msdata['data'][2, :, self.plotbl]
            yx = self.msdata['data'][3, :, self.plotbl]
            ll = (xx + yy - 1j * (xy - yx)) / 2
            spec = np.abs(ll)

        i, j = blid[0]
        if isauto[0]:
            self.ptitle = 'Auto-Correlation for Ant {}'.format(i + 1)
        else:
            self.ptitle = 'Cross-Correlation for Baseline {}-{}'.format(
                i + 1, j + 1)
        spec1d = spec.flatten()
        spec1dg = np.sort(spec1d[~np.isnan(spec1d)])
        if len(spec1dg) == 0:
            self.statusBar.showMessage('This baseline is all NaN', 2000)
            self.dspec_ax.set_title('This baseline is all NaN')
            self.speccanvas.draw()
        else:
            n95 = int(len(spec1dg) * 0.95)
            ampmax = spec1dg[n95]
            ampmin = spec1dg[0]
            self.minentry.setText('{:10.2f}'.format(ampmin / 10000.))
            self.maxentry.setText('{:10.2f}'.format(ampmax / 10000.))
            self.plot_data()

    def plot_data(self):
        ''' Plot the data selected by get_data().
        '''
        # Determine which polarization state is selected by radiobuttons
        polnum = {'XX': 0, 'YY': 1, 'XY': 2, 'YX': 3, 'RR': 4, 'LL': 5}
        ipol = polnum[self.polGroup.checkedButton().text()]

        if ipol < 4:
            # The selected polarizatin is just one of the native polarizations (XX, YY, XY, or YX)
            spec = np.abs(self.msdata['data'][ipol, :, self.plotbl])
        elif ipol == 4:
            # The selected polarization is RR (not implemented because we only have amplitudes...)
            xx = self.msdata['data'][0, :, self.plotbl]
            yy = self.msdata['data'][1, :, self.plotbl]
            xy = self.msdata['data'][2, :, self.plotbl]
            yx = self.msdata['data'][3, :, self.plotbl]
            rr = (xx + yy + 1j * (xy - yx)) / 2
            spec = np.abs(rr)
        elif ipol == 5:
            # The selected polarization is LL (not implemented because we only have amplitudes...)
            xx = self.msdata['data'][0, :, self.plotbl]
            yy = self.msdata['data'][1, :, self.plotbl]
            xy = self.msdata['data'][2, :, self.plotbl]
            yx = self.msdata['data'][3, :, self.plotbl]
            ll = (xx + yy - 1j * (xy - yx)) / 2
            spec = np.abs(ll)

        try:
            minval = float(self.minentry.text()) * 10000.
        except:
            self.statusBar.showMessage('Could not interpret minimum sfu value',
                                       2000)
            self.minentry.setText('{:10.2f}'.format(np.nanmin(spec) / 10000.))
            minval = float(self.minentry.text()) * 10000.

        try:
            maxval = float(self.maxentry.text()) * 10000.
        except:
            self.statusBar.showMessage('Could not interpret maximum sfu value',
                                       2000)
            self.maxentry.setText('{:10.2f}'.format(np.nanmax(spec) / 10000.))
            maxval = float(self.maxentry.text()) * 10000.

        for i in range(len(self.dspec_ax.images)):
            # Clear any existing images without changing the axis limits
            self.dspec_ax.images[i].remove()

        fghz = self.msdata['freq'] / 1e9
        times = self.msdata['time']
        nf, nt = spec.shape
        if not self.ignore_gaps.isChecked():
            # Insert nan rows and columns in gaps
            df = np.median(fghz[1:] - fghz[:-1])
            fbreak, = np.where(fghz[1:] - fghz[:-1] > 2 * df)
            for n, fb in enumerate(fbreak):
                loc = fb + n + 1
                fghz = np.concatenate(
                    (fghz[:loc], np.array([fghz[loc - 1] + df]), fghz[loc:]))
                spec = np.concatenate((spec[:loc], np.zeros(
                    (1, nt)) + np.nan, spec[loc:]), 0)
            nf, nt = spec.shape
            dt = np.median(times[1:] - times[:-1])
            tbreak, = np.where(times[1:] - times[:-1] > 2 * dt)
            for n, tb in enumerate(tbreak):
                loc = tb + n + 1
                times = np.concatenate(
                    (times[:loc], np.array([times[loc - 1] + dt]),
                     times[loc:]))
                spec = np.concatenate((spec[:, :loc], np.zeros(
                    (nf, 1)) + np.nan, spec[:, loc:]), 1)
            nf, nt = spec.shape

        pd = Time(times / 86400., format='mjd').plot_date
        im = self.dspec_ax.pcolormesh(pd, fghz, np.clip(spec, minval, maxval))
        self.dspec_ax.xaxis_date()
        self.dspec_ax.xaxis.set_major_formatter(DateFormatter("%H:%M"))
        self.dspec_ax.set_ylabel('Frequency [GHz]')
        self.dspec_ax.set_xlabel('Time [UT]')

        #        self.dspec_ax.imshow(np.clip(spec, minval, maxval), interpolation='nearest', aspect='auto', origin='lower')
        self.dspec_ax.set_title(self.ptitle)
        self.speccanvas.draw()

    def drawsel(self):
        ''' Uses timerange, spw, and stokes selection to draw a box on the plot.
        '''
        #if len(self.dspec_ax.get_images()) == 0:
        #    # No plot exists, so exit
        #    self.statusBar.showMessage("No plot exists yet. Please select a baseline to plot first.", 2000)
        #    return
        pd1, pd2 = trange2pd(self.trangeEdit.text())
        if pd1 is None:
            self.statusBar.showMessage(pd2, 2000)
            return
        f1, f2 = spw2frange(self.spwEdit.text(), self.msdata)
        if f1 is None:
            self.statusBar.showMessage(f2, 2000)
            return
        # Remove any existing lines before plotting a new one
        for line in self.dspec_ax.get_lines():
            line.remove()
        self.dspec_ax.plot([pd1, pd2, pd2, pd1, pd1], [f1, f1, f2, f2, f1],
                           color='white')
        self.speccanvas.draw()

    def xferUL(self):
        ''' Uses the plot limits to define the timerange and spw selections.
        '''
        trange = pd2trange(self.dspec_ax.get_xlim())
        self.trangeEdit.setText(trange)
        f1, f2 = self.dspec_ax.get_ylim()
        idx1, = np.where(self.msdata['freq'] >= f1 * 1.e9)
        idx2, = np.where(self.msdata['freq'] <= f2 * 1.e9)
        spw = str(self.msdata['spwlist'][idx1[0]]) + '~' + str(
            self.msdata['spwlist'][idx2[-1]])
        self.spwEdit.setText(spw)

    def xferDR(self):
        ''' Uses timerange, spw, and stokes selection to define the plot limits.
        '''
        pd = trange2pd(self.trangeEdit.text())
        if pd[0] is None:
            self.statusBar.showMessage(pd[1], 2000)
            return
        frange = spw2frange(self.spwEdit.text(), self.msdata)
        if frange[0] is None:
            self.statusBar.showMessage(frange[1], 2000)
            return
        self.dspec_ax.set_xlim(pd)
        self.dspec_ax.set_ylim(frange)
        self.speccanvas.draw()

    def read_msdata(self, fillnan=None):
        ''' Read amplitudes for ALL data in the measurement set, returning
            a dictionary of the data, list of times, and list of frequencies.
            Fill flagged data with fillnan value if not None.
        '''
        ms = self.ms
        #if type(ms) != casatools.ms.ms:
        #    self.statusBar.showMessage('Not connected to a valid ms!',2000)
        #    return

        self.statusBar.addWidget(self.progressBar)
        self.progressBar.setFormat('Reading file...%p% done.')
        self.progressBar.setValue(0)
        self.progressBar.show()

        ms.selectinit(datadescid=0, reset=True)
        spwinfo = ms.getspectralwindowinfo()
        nspw = len(spwinfo.keys())
        spec = []
        freq = []
        #time = []
        spwlist = []
        for descid in range(len(spwinfo.keys())):
            ms.selectinit(datadescid=0, reset=True)
            ms.selectinit(datadescid=descid)
            data = ms.getdata(['data', 'time', 'axis_info'], ifraxis=True)
            spec_ = data['data']
            freq_ = data['axis_info']['freq_axis']['chan_freq']
            #time_ = data['time']
            spwlist += [descid] * len(freq_)
            if fillnan is not None:
                flag_ = ms.getdata(['flag', 'time', 'axis_info'],
                                   ifraxis=True)['flag']
                if type(fillnan) in [int, float]:
                    spec_[flag_] = float(fillnan)
                else:
                    spec_[flag_] = 0.0
            spec.append(spec_)
            freq.append(freq_)
            #time.append(time_)
            self.progressBar.setValue(100 * (descid + 1) / nspw)
        spec = np.concatenate(spec, axis=1)
        freq = np.concatenate(freq, axis=0)
        nf = freq.shape[0]
        freq.shape = (nf, )
        times = data['time']
        ms.selectinit(datadescid=0, reset=True)
        self.statusBar.removeWidget(self.progressBar)
        self.dspec_ax.cla(
        )  # Clear plot axis in preparation for a new dynamic spectrum
        self.msdata = {
            'data': spec,
            'freq': freq,
            'time': times,
            'name': ms.name,
            'spwlist': np.array(spwlist)
        }
class Viewer(QMainWindow):
    def __init__(self, parent=None):
      super(Viewer, self).__init__(parent)
      self.filename = ""
      self.fname = ""
      self.csv_file = ''
      self.isChanged = False
      self.mychannels_file = 'myradio.txt'
      self.mychannels_file_backup = 'myradio.txt_backup'
      self.setGeometry(0, 0, 1000, 600)
      self.lb = QTableView()
      self.lb.horizontalHeader().hide()
      self.model = QStandardItemModel()
      self.lb.setModel(self.model)
      self.model.itemChanged.connect(self.finishedEdit)
      self.lb.setEditTriggers(QAbstractItemView.DoubleClicked)
      self.lb.setSelectionBehavior(self.lb.SelectRows)
      self.lb.setSelectionMode(self.lb.SingleSelection)
      self.lb.setDragDropMode(self.lb.InternalMove)
      self.setStyleSheet(stylesheet(self))
      self.lb.setAcceptDrops(True)
      self.setCentralWidget(self.lb)
      self.setContentsMargins(10, 10, 10, 10)
      self.statusBar().showMessage("Willkommen", 0)
      self.setWindowTitle("myRadio Listeneditor")
      self.setWindowIcon(QIcon.fromTheme("multimedia-playlist"))
      #self.createMenuBar()
      self.createToolBar()
      self.create_backup()
      self.show()
      print("Hello")
      self.open_channels(self.mychannels_file)
      self.lb.setFocus()
      
    def finishedEdit(self):
        self.isChanged = True
      
    def setChanged(self):
        self.isChanged = True
      
    def msgbox(self, message):
        msg = QMessageBox(2, "Information", message, QMessageBox.Ok)
        msg.exec()

      
    def create_backup(self):
        if shutil.copyfile(self.mychannels_file, self.mychannels_file_backup):
            self.msgbox('myradio.txt_backup wurde erstellt')

    def closeEvent(self, event):
        print(self.isChanged)
        if  self.isChanged == True:
            quit_msg = "<b>Das Dokument wurde geändert.<br>Wollen Sie die Änderungen speichern?</ b>"
            reply = QMessageBox.question(self, 'Speichern', 
                     quit_msg, QMessageBox.Yes, QMessageBox.No)
            if reply == QMessageBox.Yes:
                event.accept()
                self.save_file(self.filename)
        else:
            print("keine Änderungen.")

    def createMenuBar(self):
        bar=self.menuBar()
        self.filemenu=bar.addMenu("Datei")
        self.separatorAct = self.filemenu.addSeparator()
        self.filemenu.addAction(QIcon.fromTheme("document-open"), "Datei öffnen",  self.openFile, QKeySequence.Open) 
        self.filemenu.addAction(QIcon.fromTheme("document-save-as"), "Speichern als ...",  self.writeCSV, QKeySequence.SaveAs) 

    def createToolBar(self):
        tb = self.addToolBar("Werkzeuge")
        tb.setIconSize(QSize(16, 16))
        
        self.findfield = QLineEdit(placeholderText = "finden ...")
        self.findfield.setClearButtonEnabled(True)
        self.findfield.setFixedWidth(200)
        tb.addWidget(self.findfield)
        
        tb.addSeparator()
        
        self.replacefield = QLineEdit(placeholderText = "ersetzen mit ...")
        self.replacefield.setClearButtonEnabled(True)
        self.replacefield.setFixedWidth(200)
        tb.addWidget(self.replacefield)
        
        tb.addSeparator()
        
        btn = QToolButton()
        btn.setText("alles ersetzen")
        btn.setToolTip("alles ersetzen")
        btn.clicked.connect(self.replace_in_table)
        tb.addWidget(btn)
        
        tb.addSeparator()

        del_btn = QToolButton()
        del_btn.setIcon(QIcon.fromTheme("edit-delete"))
        del_btn.setToolTip("Zeile löschen")
        del_btn.clicked.connect(self.del_row)
        tb.addWidget(del_btn)
        
        tb.addSeparator()
        
        add_btn = QToolButton()
        add_btn.setIcon(QIcon.fromTheme("add"))
        add_btn.setToolTip("Zeile hinzufügen")
        add_btn.clicked.connect(self.add_row)
        tb.addWidget(add_btn)

        move_down_btn = QToolButton()
        move_down_btn.setIcon(QIcon.fromTheme("down"))
        move_down_btn.setToolTip("nach unten bewegen")
        move_down_btn.clicked.connect(self.move_down)
        tb.addWidget(move_down_btn)
        
        move_up_up = QToolButton()
        move_up_up.setIcon(QIcon.fromTheme("up"))
        move_up_up.setToolTip("nach oben bewegen")
        move_up_up.clicked.connect(self.move_up)
        tb.addWidget(move_up_up)
        
        spacer = QWidget()
        spacer.setSizePolicy(QSizePolicy.Expanding, 0)
        tb.addWidget(spacer)
        
        self.filter_field = QLineEdit(placeholderText = "Filtern (Enter drücken)")
        self.filter_field.setClearButtonEnabled(True)
        self.filter_field.setToolTip("Suchbegriff einfügen und Enter drücken")
        self.filter_field.setFixedWidth(200)
        self.filter_field.returnPressed.connect(self.filter_table)
        self.filter_field.textChanged.connect(self.update_filter)
        tb.addWidget(self.filter_field)
        
        
    def move_down(self):
        if self.model.rowCount() < 1:
            return
        row = self.lb.selectionModel().selection().indexes()[0].row()
        if row > self.model.rowCount() - 2:
            return
        nextrow = row + 1
        itemList = self.model.takeRow(row)
        self.model.insertRow(nextrow,itemList)
        self.isChanged = True
        self.lb.selectRow(nextrow)
        
    def move_up(self):
        if self.model.rowCount() < 1:
            return
        row = self.lb.selectionModel().selection().indexes()[0].row()
        if row < 1:
            return
        nextrow = row - 1
        itemList = self.model.takeRow(row)
        self.model.insertRow(nextrow,itemList)
        self.isChanged = True
        self.lb.selectRow(nextrow)
        
    def del_row(self): 
        if self.model.rowCount() < 1:
            return
        row = self.lb.selectionModel().selection().indexes()[0].row()
        self.model.takeRow(row)
        self.isChanged = True
        self.lb.selectRow(row)
            
    def add_row(self): 
        if self.model.rowCount() < 1:
            return
        print("Zeile hinzufügen")
        newrow = ['name', 'group', 'url']       
        items = [QStandardItem(field) for field in newrow]
        self.model.appendRow(items)
        self.isChanged = True
        self.lb.selectRow(self.model.rowCount() - 1)
                

    def load_channels_file(self):
        if self.isChanged == True:
            save_msg = "<b>Das Dokument wurde geändert.<br>Wollen Sie die Änderungen speichern?</ b>"
            reply = QMessageBox.question(self, 'Speichern', 
                     save_msg, QMessageBox.Yes, QMessageBox.No)
            if reply == QMessageBox.Yes:
                self.writeCSV()
                self.open_channels()
            else:
                self.isChanged = False
                self.open_channels()
        else:
            self.isChanged = False
            self.open_channels()
        
    def open_channels(self, fileName):
        if fileName:
            self.convert_to_csv(fileName)
            print(fileName + " geladen")
            with open(self.csv_file, 'r') as f:
                i = 0
                reader = csv.reader(f, delimiter = '\t')
                self.model.clear()
                for row in reader:  
                    items = [QStandardItem(field) for field in row]
                    self.model.appendRow(items)
                    self.model.setHeaderData(i - 1, Qt.Horizontal, str(i))
                    i = i + 1     
                self.lb.resizeColumnsToContents()           
                self.lb.selectRow(0)
                self.statusBar().showMessage(f"{fileName} loaded", 0)
                self.isChanged = False
                self.lb.verticalHeader().setMinimumWidth(24)
                self.filename = fileName
             
    def convert_to_csv(self, fileName):
        #mychannels_file = 'myradio.txt'
        channels_list = open(fileName, 'r').read().splitlines()
        csv_content = ""
        group = ""
        name = ""
        url = ""

        for x in reversed(range(len(channels_list))):
            line = channels_list[x]
            if line == "":
                print(f"empty line {x} removed")
                del(channels_list[x])
               
        i = 0
        for x in range(0, len(channels_list)):
            line = channels_list[x]
            while True:
                if line.startswith("--"):
                    group = line.replace("-- ", "").replace(" --", "")
                    break
                    continue

                elif not line.startswith("--"):
                    ch_line = line.split(",")
                    name = ch_line[0]
                    url = ch_line[1]
                    i += 1
                    break
                    
            if not name == "" and not name == channels_list[x-1].partition(",")[0]:        
                csv_content += (f'{name}\t{group}\t{url}\n')

        self.csv_file = '/tmp/mychannels.csv'
        with open(self.csv_file, 'w') as f:        
            f.write(csv_content)
            
    def writeCSV(self):
        fileName, _ = QFileDialog.getSaveFileName(self, "Datei speichern", self.mychannels_file,"TVPlayer2 List (*.txt)")
        if fileName:
            self.save_file(fileName)
            
    def save_file(self, fileName):
        # save temporary csv
        f = open(self.csv_file, 'w')

        content = ""
        
        for row in range(self.lb.model().rowCount()):
            itemlist = []
            for column in range(self.lb.model().columnCount()):
                itemlist.append(self.model.item(row, column).text())
            
            content += '\t'.join(itemlist)
            content += '\n'
        with open(self.csv_file, 'w') as f:
            f.write(content)
            
                
            
            # convert to txt
            channels_list = open(self.csv_file, 'r').read().splitlines()

            group = ""
            name = ""
            url = ""
            out_list = []

            for x in range(len(channels_list)):
                line = channels_list[x].split('\t')
                name = line[0]
                group = line[1]
                url = line[2]
                
                out_list.append(f"-- {group} --")
                out_list.append(f'{name},{url}')
                

            tlist = self.ordered_set(out_list)
            m3u_content = '\n'.join(tlist)
            m3u_content += '\n'

            with open(fileName, 'w') as f:        
                f.write(m3u_content)

            print(fileName + " gespeichert")
            self.isChanged = False
            
    def ordered_set(self, in_list):
        out_list = []
        added = set()
        for val in in_list:
            if not val in added:
                out_list.append(val)
                added.add(val)
        return out_list


    def replace_in_table(self):
        if self.model.rowCount() < 1:
            return
        searchterm = self.findfield.text()
        replaceterm = self.replacefield.text()
        if searchterm == "":
            return
        else:
            for i in range(self.lb.model().columnCount() - 1):
                indexes = self.lb.model().match(
                                    self.lb.model().index(0, i),
                                    Qt.DisplayRole,
                                    searchterm,
                                    -1,
                                    Qt.MatchContains
                                )
                for ix in indexes:
                    old_item = self.model.item(ix.row(), i)
                    new_item = old_item.text().replace(searchterm, replaceterm)
                    self.model.setItem(ix.row(), i, QStandardItem(new_item))
                self.lb.resizeColumnsToContents()
                self.isChanged = True
                
    def filter_table(self):
        if self.model.rowCount() < 1:
            return
        searchterm = self.filter_field.text()  
    
        row_list = []
        self.lb.clearSelection()

        for i in range(self.lb.model().columnCount()-1):
            indexes = self.lb.model().match(
                                self.lb.model().index(0, i),
                                Qt.DisplayRole,
                                searchterm,
                                -1,
                                Qt.MatchContains
                            )
            for ix in indexes:
                self.lb.selectRow(ix.row())    
                row_list.append(ix.row()) 
                
        for x in range(self.lb.model().rowCount()):
            if not x in row_list:
                self.lb.hideRow(x)    
       
    def update_filter(self):
        if self.filter_field.text() == "":
            for x in range(self.lb.model().rowCount()):
                self.lb.showRow(x)