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)
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)
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)