class MainWindow(QWidget): def __init__(self, parent=None): QWidget.__init__(self, parent) self.title = 'Flightradar24 parser by DroidCatRu' self.setWindowTitle(self.title) self.resize(700, 500) self.view = QWebEngineView(self) self.setpage() self.textbox = QLineEdit(self) self.textbox.move(20, 10) self.textbox.resize(280, 20) btn = QPushButton('Показать рейс', self) btn.resize(btn.sizeHint()) btn.move(300, 10) grid = QGridLayout() grid.addWidget(self.view) self.setLayout(grid) btn.clicked.connect(self.clikbtn) # Кнопка обновления карты self.show() def setpage(self, id=airplane_id): mypage = MyPage(self.view) self.view.setPage(mypage) s = getmaphtml(id) mypage.setHtml(s) def clikbtn(self): QMessageBox.question(self, 'Не увлекайтесь', "Стоимость одной загрузки карты: 0,46 рубля. Студенты тоже есть хотят. +79093636316 (сбер)", QMessageBox.Ok, QMessageBox.Ok) id = self.textbox.text() self.setpage(id) self.view.reload()
class MainWindow(QMainWindow): """Main window for the GUI - displays flask webpage""" # constructor def __init__(self, port): # call parent constructor # hide window border super().__init__(flags=Qt.FramelessWindowHint) # window name self.setWindowTitle("Flask hook") # create view for web page self.web_engine_view = QWebEngineView() # load flask page self.web_engine_view.setUrl(QUrl("http://127.0.0.1:{}".format(port))) # update view self.web_engine_view.reload() # create main layout self.main_layout = QVBoxLayout() # add web view to layout self.main_layout.addWidget(self.web_engine_view) # self.main_layout.setContentsMargins(5, 40, 5, 5) # clear margins self.main_layout.setContentsMargins(0, 0, 0, 0) # create main widget self.central_widget = QWidget() # apply layout to main widget self.central_widget.setLayout(self.main_layout) # apply main widget to window self.setCentralWidget(self.central_widget)
class MyApp(QWidget): def __init__(self): super().__init__() self.setWindowTitle('Folium in PyQt Example') self.window_width, self.window_height = 480, 480 self.setMinimumSize(self.window_width, self.window_height) layout = QVBoxLayout() self.setLayout(layout) self.x = 37.40494 self.y = 127.11130 m = folium.Map( # tiles='Stamen Terrain', zoom_start=18, location=(self.x, self.y) ) # save map data to data object self.data = io.BytesIO() m.save(self.data, close_file=False) m.save("map.html") self.webView = QWebEngineView() filepath = os.path.abspath(os.path.join(os.path.dirname(__file__), "map.html")) self.webView.load(QUrl.fromLocalFile(filepath)) layout.addWidget(self.webView) def keyPressEvent(self, e): if e.key() == Qt.Key_Right: self.x, self.y = self.x, self.y+0.0005 elif e.key() == Qt.Key_Left: self.x, self.y = self.x, self.y-0.0005 elif e.key() == Qt.Key_Up: self.x, self.y = self.x+0.0005, self.y elif e.key() == Qt.Key_Down: self.x, self.y = self.x-0.0005, self.y m = folium.Map(zoom_start=18,location=(self.x, self.y)) m.save(self.data, close_file=False) m.save("map.html") self.webView.reload()
class TkPyHelpWidget(QDialog): url = f'http://localhost:{port}/' title: str = 'TkPy3 帮助' def __init__(self, parent=None): super(TkPyHelpWidget, self).__init__(parent) self.vboxlayout = QVBoxLayout() self.resize(1000, 500) self.setWindowTitle(self.title) self.view = QWebEngineView() self.reset_to_home = QPushButton('回到主页') self.setLayout(self.vboxlayout) self.go_home() self.vboxlayout.addWidget(self.reset_to_home, 0) self.vboxlayout.addWidget(self.view, 1) self.reset_to_home.setWhatsThis('回到主页') self.reset_to_home.setToolTip(self.url) def go_home(self): self.view.load(QUrl(self.url)) self.view.reload()
class App(QWidget): def __init__(self): super().__init__() self.setWindowTitle("change map") self.disply_width = 640 self.display_height = 480 self.browser = QWebEngineView() self.show_location(0,0) current_dir = os.path.dirname(os.path.abspath(__file__)) filename = os.path.join(current_dir, 'show_city.html') url = QUrl.fromLocalFile(filename) self.browser.setUrl(url) vbox = QVBoxLayout() vbox.addWidget(self.browser) self.setLayout(vbox) self.thread = VocalThread() self.thread.get_vocal_message.connect(self.search_location) self.thread.start() @pyqtSlot(str) def search_location(self, text): result = requests.get("http://www.mapquestapi.com/geocoding/v1/address?key=your-key&location="+text) print(text) result_text = result.text lat = float(re.findall(r'(?<="lat")(?:\s*\:\s*)(.{0,23}?(?=,))', result_text, re.IGNORECASE+re.DOTALL)[0]) lng = float(re.findall(r'(?<="lng")(?:\s*\:\s*)(.{0,23}?(?=}))', result_text, re.IGNORECASE+re.DOTALL)[0]) statuscode = int(re.findall(r'(?<="statuscode")(?:\s*\:\s*)(.{0,23}?(?=,))', result_text, re.IGNORECASE+re.DOTALL)[0]) if statuscode == 0: self.show_location(lat, lng) self.reload_map() def reload_map(self): self.browser.reload() def show_location(self, lat, lng): mappy = folium.Map(location=[lat,lng],tiles = "OpenStreetMap",zoom_start = 13) folium.Marker([lat,lng]).add_to(mappy) mappy.save('show_city.html')
def display_cif(cif: 'CifContainer'): app = QApplication(sys.argv) # w = QWidget() w = QWebEngineView() w.heightForWidth(1) app.setActiveWindow(w) jsmoldir = TemporaryDirectory() mol = make_molecule(cif) content = write_html.write(mol, 250, 250) Path(jsmoldir.name).joinpath("./jsmol.htm").write_text(data=content, encoding="utf-8", errors='ignore') copy2(Path(__file__).parent.joinpath('jquery.min.js'), jsmoldir.name) copy2( Path(__file__).parent.joinpath('JSmol_dk.nojq.lite.js'), jsmoldir.name) print(Path(jsmoldir.name).joinpath("./jsmol.htm").resolve()) w.load( QUrl.fromLocalFile( str(Path(jsmoldir.name).joinpath("./jsmol.htm").resolve()))) w.show() w.reload() sys.exit(app.exec_())
class MainWindow(QWidget): def __init__(self): QWidget.__init__(self) self.setGeometry(100, 100, 300, 200) self.show() self.grid = QGridLayout(self) self.grid.setContentsMargins(0, 0, 0, 0) self.web = QWebEngineView() self.web.load(QUrl(link)) self.grid.addWidget(self.web, 0, 0) self.web2 = QWebEngineView() self.web2.hide() self.web2.load(QUrl(link)) self.grid.addWidget(self.web2, 0, 0) timer = QtCore.QTimer(self) timer.timeout.connect(self.refresh) timer.start(int(sys.argv[-1])) def refresh(self): print(int(time.time())) def finished(): browser, another = self.web, self.web2 if another.isHidden(): browser, another = another, browser time.sleep(1) browser.show() another.hide() if self.web2.isHidden(): self.web2.reload() self.web2.loadFinished.connect(finished) else: self.web.reload() self.web.loadFinished.connect(finished)
class TeamtoolBrowser(QWidget, design.Ui_Form): def __init__(self, parent=None): super(TeamtoolBrowser, self).__init__(parent) self.setupUi(self) self.browser = QWebEngineView() self.profile = QWebEngineProfile("somestorage", self.browser) self.webpage = QWebEnginePage(self.profile, self.browser) self.browser.setPage(self.webpage) self.initUI() self.eventUI() def eventUI(self): self.btnRefresh.clicked.connect(lambda: self.browse(refresh=True)) self.codeEdit.textChanged.connect(lambda: self.browse(offline=True)) self.fileView.doubleClicked.connect(self.selectFile) self.urlBar.returnPressed.connect(lambda: self.browse()) self.webpage.loadStarted.connect(lambda: self.startLoading()) self.webpage.loadFinished.connect(lambda: self.onLoadFinished()) self.btnPrev.clicked.connect(lambda: self.prev()) self.btnNext.clicked.connect(lambda: self.next()) def prev(self): ''' History: go back ''' self.webpage.page().triggerAction(QWebEnginePage.Back) self.urlBar.setText(self.webpage.url().toString()) def next(self): ''' History: move forward ''' self.webpage.page().triggerAction(QWebEnginePage.Forward) self.urlBar.setText(self.webpage.url().toString()) def startLoading(self): ''' When page starts to load ''' self.loadingAnimation.start() self.labelLoading.setVisible(True) def onLoadFinished(self): ''' When page has finished to load ''' self.loadingAnimation.stop() self.labelLoading.setVisible(False) if self.webpage.history().canGoBack(): self.btnPrev.setEnabled(True) else: self.btnPrev.setEnabled(False) if self.webpage.history().canGoForward(): self.btnNext.setEnabled(True) else: self.btnNext.setEnabled(False) self.urlBar.setText(self.webpage.url().toString()) def browse(self, refresh=False, offline=False): ''' Browse to urlBar URL or render a text/html string ''' # Offline mode, load plaintext html code if offline: self.webpage.setHtml(self.codeEdit.toPlainText()) return # Online mode if refresh: url = self.browser.reload() return url = self.urlBar.text() if not re.match('http://|https://', url, re.I): url = f'http://{url}' # print('===============>', os.path.join('statics/button', 'lock-ssl.png'), QUrl(url).scheme()) if QUrl(url).scheme() == 'https': print('HTTPS') self.httpsicon.setPixmap(self.pixmap_ssl) else: print('HTTP') self.httpsicon.setPixmap(self.pixmap_nossl) print(f'Browse to this URL: {url}') if isinstance(url, str) and url != '': self.webpage.setUrl(QUrl(url)) def selectFile(self, index): ''' Select a file in file system ''' if not self.fileModel.isDir(index) and index.data().lower().endswith( ('.html', '.txt')): with open(self.fileModel.fileInfo(index).absoluteFilePath(), 'r', encoding='utf-8') as f: self.webpage.setHtml(f.read()) else: print('Can not read this file...') def initUI(self): ''' Configuration of widgets ''' self.gLayoutBrowser.addWidget(self.browser) self.loadingAnimation = QMovie('loading.gif') self.loadingAnimation.setScaledSize(QtCore.QSize(24, 24)) self.labelLoading.setMovie(self.loadingAnimation) self.labelLoading.setVisible(False) self.fileModel = QFileSystemModel() self.fileModel.setRootPath(QtCore.QDir.currentPath()) self.fileView.setModel(self.fileModel) self.fileModel.setNameFilters(('.html', '.txt')) self.fileView.setColumnWidth(0, 170) self.fileView.setColumnWidth(1, 50) self.fileView.setColumnWidth(2, 50) self.splitterMain.setStretchFactor(1, 3) self.splitterSidebar.setStretchFactor(0, 1) self.urlBar.setPlaceholderText('Tapez ici votre URL') self.loadPage(online=True) self.pixmap_ssl = QPixmap(os.path.join('static/button', 'lock-ssl.png')) self.pixmap_nossl = QPixmap( os.path.join('static/button', 'lock-nossl.png')) print(f'w: {self.pixmap_ssl.width()} | h: {self.pixmap_ssl.height()}') self.httpsicon.setPixmap(self.pixmap_nossl) self.setGeometry(300, 300, 1280, 720) self.updateTitle() # self.codeEdit = TxtInput([QPushButton('Hello'), QPushButton('World')]) # self.editor = CustomTextEditor(txtInput=self.codeEdit) self.codeEdit = QPlainTextEdit() self.splitterSidebar.addWidget(self.codeEdit) self.show() def loadPage(self, online=True): ''' Load home page ''' if online: self.browser.setUrl(QUrl("https://www.google.com")) return with open('home.html', 'r') as f: html = f.read() self.browser.setHtml(html) def updateTitle(self): title = self.browser.page().title() self.setWindowTitle(f'{title}')
class Browser(QWidget): def __init__(self): super().__init__() # Create a search url widget self.search_url = QLineEdit() self.search_url.setStyleSheet( "background-color:black; color:white; border-radius:14px; margin:0px,10px,0px,10px; border:2px solid grey; padding: 2px 10px 2px 10px; font-weight:bold;" ) self.search_url.setFont(QtGui.QFont("sans-serif", 11)) self.search_url.setPlaceholderText("Search in Google or Type a URL") self.search_url.setMinimumWidth(500) self.search_url.returnPressed.connect(self.webb) self.search_url.setEnabled(True) self.search_url.setAlignment(Qt.AlignCenter) # Create a vbox vbox = QVBoxLayout() # Back Button self.back_button = QPushButton() self.back_button.setIcon(self.style().standardIcon( QStyle.SP_ArrowBack)) self.back_button.setEnabled(False) self.back_button.clicked.connect(self.go_back) # forword Button self.forword_button = QPushButton() self.forword_button.setIcon(self.style().standardIcon( QStyle.SP_ArrowForward)) self.forword_button.clicked.connect(self.go_fast) # Reload Button self.reload_button = QPushButton() self.reload_button.setIcon(self.style().standardIcon( QStyle.SP_BrowserReload)) self.reload_button.clicked.connect(self.go_reload_buton) # Reload Button self.stop_button = QPushButton() self.stop_button.setIcon(self.style().standardIcon( QStyle.SP_BrowserStop)) self.stop_button.setEnabled(False) self.stop_button.clicked.connect(self.go_stop_button) # Create a Hbox hbox = QHBoxLayout() hbox.addWidget(self.back_button) hbox.addWidget(self.forword_button) hbox.addWidget(self.reload_button) hbox.addWidget(self.stop_button) hbox.addWidget(self.search_url) self.web = QWebEngineView() self.web.load(QUrl(f"https://google.com/")) self.web.show() vbox.addLayout(hbox) vbox.addWidget(self.web) self.setLayout(vbox) self.show() def webb(self): self.stop_button.setEnabled(True) self.back_button.setEnabled(True) if 'http://' or 'https://' not in self.search_url: self.web.load(QUrl(f"https://{self.search_url.text()}/")) self.web.show() else: self.web.load(QUrl(self.search_url.text())) self.web.show() def go_back(self): self.web.back() def go_fast(self): self.web.forward() def go_reload_buton(self): self.web.reload() def go_stop_button(self): self.web.stop()
class App(QWidget): def __init__(self): super().__init__() self.data = io.BytesIO() self.x = 37.40494 self.y = 127.11130 self.loadMap(self.x, self.y) self.arcreading = 270 self.adder = 0.1 self.initUI() self.ccd = ccd_thread.Thread(self) self.ccd.changePixmap.connect(self.setCcdImage) self.ccd.deamon = True self.ccd.start() js.control_start() def closeEvent(self, e): self.ccd.stop() @pyqtSlot(QImage, QImage) def setCcdImage(self, image, image2): self.ccdLabel.setPixmap(QPixmap.fromImage(image)) self.swirLabel.setPixmap(QPixmap.fromImage(image2)) def convertQImageToMat(self, incomingImage): ''' Converts a QImage into an opencv MAT format ''' incomingImage = incomingImage.convertToFormat(4) width = incomingImage.width() height = incomingImage.height() ptr = incomingImage.bits() ptr.setsize(incomingImage.byteCount()) arr = np.array(ptr).reshape(height, width, 4) # Copies the data return arr def loadMap(self, x, y): m = folium.Map(zoom_start=16, location=(x,y)) m.save(self.data, close_file=False) m.save("map.html") def initUI(self): self.setWindowTitle('Mobile Laser') box = QHBoxLayout() gb = QGroupBox() box.addWidget(gb) ccdBox = QVBoxLayout() # create a label self.ccdLabel = QLabel(self) self.ccdLabel.resize(1920, 1080) ccdBox.addWidget(self.ccdLabel) gb.setLayout(ccdBox) swirbox = QHBoxLayout() gb = QGroupBox() swirbox.addWidget(gb) swirGpsBox = QVBoxLayout() box.addLayout(swirGpsBox) self.swirLabel = QLabel(self) self.swirLabel.resize(640, 360) swirGpsBox.addWidget(self.swirLabel) self.webView = QWebEngineView() filepath = os.path.abspath(os.path.join(os.path.dirname(__file__), "map.html")) self.webView.load(QUrl.fromLocalFile(filepath)) swirGpsBox.addWidget(self.webView) gb.setLayout(swirGpsBox) paintBox = QHBoxLayout() gb = QGroupBox() gb.setFixedHeight(200) paintBox.addWidget(gb) # 전체 배치 vbox = QVBoxLayout() vbox.addLayout(box) vbox.addLayout(paintBox) self.setLayout(vbox) self.show() def paintEvent(self, event): arcwidth = 50 # arc width self.painter = QPainter(self) # create a painter object self.painter.setRenderHint(QPainter.Antialiasing) # tune up painter self.painter.setPen(QPen(Qt.green, arcwidth, cap=Qt.FlatCap)) # 그리기 함수의 호출 부분 self.drawPoints(self, self.painter) self.painter.end() def drawPoints(self, event, painter): kanvasx = 50 # binding box origin: x kanvasy = 650 # binding box origin: y kanvasheight = 75 # binding box height kanvaswidth = 75 # binding box width arcsize = 270 # arc angle between start and end. arcwidth = 50 # arc width # ---------- the following lines simulate sensor reading. ----------- if self.arcreading > arcsize or self.arcreading < 0: # variable to make arc move self.adder = -self.adder # arcreading corresponds to the # value to be indicated by the arc. self.arcreading = self.arcreading + self.adder # --------------------- end simulation ------------------------------ #print(arcreading) # drawArc syntax: # drawArc(x_axis, y_axis, width, length, startAngle, spanAngle) painter.drawArc(kanvasx, kanvasy, # binding box: x0, y0, pixels kanvasheight + arcwidth, # binding box: height kanvaswidth + arcwidth, # binding box: width int((arcsize + (180 - arcsize) / 2)*16), # arc start point, degrees (?) int(-self.arcreading*16)) # arc span painter.drawArc(kanvasx+190, kanvasy, # binding box: x0, y0, pixels kanvasheight + arcwidth, # binding box: height kanvaswidth + arcwidth, # binding box: width int((arcsize + (180 - arcsize) / 2)*16), # arc start point, degrees (?) int(-self.arcreading*16)) # arc span painter.drawArc(kanvasx+380, kanvasy, # binding box: x0, y0, pixels kanvasheight + arcwidth, # binding box: height kanvaswidth + arcwidth, # binding box: width int((arcsize + (180 - arcsize) / 2)*16), # arc start point, degrees (?) int(-self.arcreading*16)) # arc span painter.drawArc(kanvasx+570, kanvasy, # binding box: x0, y0, pixels kanvasheight + arcwidth, # binding box: height kanvaswidth + arcwidth, # binding box: width int((arcsize + (180 - arcsize) / 2)*16), # arc start point, degrees (?) int(-self.arcreading*16)) # arc span painter.drawArc(kanvasx+760, kanvasy, # binding box: x0, y0, pixels kanvasheight + arcwidth, # binding box: height kanvaswidth + arcwidth, # binding box: width int((arcsize + (180 - arcsize) / 2)*16), # arc start point, degrees (?) int(-self.arcreading*16)) # arc span painter.setFont(QFont('Times New Roman', 24, weight=QFont.Bold)) painter.setPen(QPen(Qt.black, arcwidth, cap=Qt.FlatCap)) painter.drawText(kanvasx+40, kanvasy+70, str(int(self.arcreading))) def keyPressEvent(self, e): if e.key() == Qt.Key_Right: self.x, self.y = self.x, self.y+0.0005 self.arcreading += 10 elif e.key() == Qt.Key_Left: self.x, self.y = self.x, self.y-0.0005 self.arcreading -= 10 elif e.key() == Qt.Key_Up: self.x, self.y = self.x+0.0005, self.y self.arcreading += 1 elif e.key() == Qt.Key_Down: self.x, self.y = self.x-0.0005, self.y self.arcreading -= 1 m = folium.Map(zoom_start=18,location=(self.x, self.y)) m.save(self.data, close_file=False) m.save("map.html") self.webView.reload() self.update()
class Main(QtWidgets.QMainWindow): def __init__(self): QtWidgets.QMainWindow.__init__(self) self.CreateUI() def CreateUI(self): # Create Necessary Widgets self.centralwidget = QtWidgets.QWidget(self) self.line = QtWidgets.QLineEdit(self) self.line.setMinimumSize(500, 20) self.line.setStyleSheet('font-size:15px;') # Enter URL self.enter = QtWidgets.QPushButton(self) self.enter.resize(0,0) self.enter.clicked.connect(self.set_url) self.enter.setShortcut('Return') # Reload Button self.reload = QtWidgets.QPushButton('↻',self) self.reload.setMinimumSize(20, 20) self.reload.setShortcut('F5') self.reload.setStyleSheet('font-size:23px;') self.reload.clicked.connect(self.reload_page) # Back Button self.back = QtWidgets.QPushButton('←',self) self.back.setMinimumSize(20, 20) self.back.setStyleSheet('font-size:23px;') self.back.clicked.connect(self.go_back) # Forward Button self.forward = QtWidgets.QPushButton('→',self) self.forward.setMinimumSize(20, 20) self.forward.setStyleSheet('font-size:23px;') self.forward.clicked.connect(self.go_forwardard) # Create Progress Bar self.pbar = QtWidgets.QProgressBar() self.pbar.setMaximumWidth(120) # Update Progress Bar, Window Title etc. self.web = QWebEngineView(loadProgress = self.pbar.setValue, loadFinished = self.pbar.hide, loadStarted = self.pbar.show, titleChanged = self.setWindowTitle) self.web.setMinimumSize(1200, 600) # Check for url changes self.web.urlChanged.connect(self.if_url_changed) # Check for user liink hovering self.web.page().linkHovered.connect(self.if_link_hover) # Set Grid grid = QtWidgets.QGridLayout() # Set Widget Locations grid.addWidget(self.back,0,0, 1, 1) grid.addWidget(self.line,0,3, 1, 1) grid.addWidget(self.forward,0,1, 1, 1) grid.addWidget(self.reload,0,2, 1, 1) grid.addWidget(self.web, 2, 0, 1, 6) self.centralwidget.setLayout(grid) # Window Settings # Default Window Size and Location self.setGeometry(25, 100, 1200, 600) # Window Title self.setWindowTitle('Browsey') # Window Icon self.setWindowIcon(QtGui.QIcon('')) # Window Colour self.setStyleSheet('background-color:') # Window Status Bar self.status = self.statusBar() self.status.addPermanentWidget(self.pbar) self.status.hide() self.setCentralWidget(self.centralwidget) # Function to Handle URLs def set_url(self): # Get default URL global url url = self.line.text() # Set URL Prefixes http = 'http://' www = 'www.' # Check and Set if www in url and http not in url: url = http + url elif http in url and www not in url: url = url[:7] + www + url[7:] # If no suffix used make it a google search elif '.' not in url: url = 'http://www.google.com/search?q='+url elif http and www not in url: url = http + www + url self.line.setText(url) # Load site at URL self.web.load(QtCore.QUrl(url)) # Show URL self.status.show() # Navigation functions def go_back(self): self.web.back() def go_forwardard(self): self.web.forwardard() def reload_page(self): self.web.reload() #Check if user has entered new URL def if_url_changed(self): self.line.setText(self.web.url().toString()) # Show link in statusbar if user hovers def if_link_hover(self, l): self.status.showMessage(l)
class MainWidget(QWidget): def __init__(self, url): super().__init__() fontDB = QFontDatabase() fontDB.addApplicationFont(':/mono-font') # fontDB.addApplicationFont(':/Taipei-font') screen = QDesktopWidget().screenGeometry() self.width, self.height = screen.width(), screen.height() self.html = '' self.url = url self.GI = None self.DL = None self.web_view = QWebEngineView(self) self.web_view.load(QUrl(self.url)) self.web_view.page().titleChanged.connect(self.get_html) btnExit = QPushButton('', self) btnExit.setIcon(QIcon(':/exit-icon')) btnExit.setIconSize(QSize(32, 32)) btnExit.setToolTip('Exit') btnExit.clicked.connect(QApplication.quit) self.btnHome = QPushButton('', self) self.btnHome.setIcon(QIcon(':/home-icon')) self.btnHome.setIconSize(QSize(32, 32)) self.btnHome.setToolTip('Home') self.btnHome.clicked.connect(self.goHome) self.btnBack = QPushButton('', self) self.btnBack.setIcon(QIcon(':/go_back-icon')) self.btnBack.setIconSize(QSize(32, 32)) self.btnBack.setToolTip('Backward') self.btnBack.clicked.connect(self.goBack) self.btnBack.setDisabled(True) self.btnNext = QPushButton('', self) self.btnNext.setIcon(QIcon(':/go_forward-icon')) self.btnNext.setIconSize(QSize(32, 32)) self.btnNext.setToolTip('Forward') self.btnNext.clicked.connect(self.goForward) self.btnNext.setDisabled(True) self.cmbDownList = QComboBox(self) self.cmbDownList.setMinimumHeight(40) # font = self.cmbDownList.font() font = QFont('Liberation Mono') font.setPointSize(14) self.cmbDownList.setFont(font) self.cmbDownList.setIconSize(QSize(24, 24)) self.cmbDownList.currentIndexChanged.connect(self.onIndexChanged) self.cmbDownList.setToolTip('Select a stream to download') self.cmbCapsList = QComboBox(self) self.cmbCapsList.setMinimumHeight(40) # font = QFont('Taipei Sans TC Beta') font = self.cmbCapsList.font() font.setPointSize(14) self.cmbCapsList.setFont(font) self.cmbCapsList.setToolTip('Select a caption/subtitle to download') btnSettings = QPushButton('', self) btnSettings.setIcon(QIcon(':/settings-icon')) btnSettings.setIconSize(QSize(32, 32)) btnSettings.setToolTip('Settings') btnSettings.clicked.connect(self.onSettings) self.btnDLoad = QPushButton('', self) self.btnDLoad.setIcon(QIcon(':/download-icon')) self.btnDLoad.setIconSize(QSize(32, 32)) self.btnDLoad.setToolTip('Download') self.btnDLoad.clicked.connect(self.goDownload) self.btnDLoad.setDisabled(True) self.progressBar = QProgressBar(self) self.progressBar.setMinimum(0) self.progressBar.setMaximum(100) hBox1 = QHBoxLayout() hBox1.addWidget(btnExit, 0) hBox1.addWidget(self.btnHome, 0) hBox1.addWidget(self.btnBack, 0) hBox1.addWidget(self.btnNext, 0) hBox1.addWidget(self.cmbDownList, 1) hBox1.addWidget(self.cmbCapsList, 0) hBox1.addWidget(btnSettings, 0) hBox1.addWidget(self.btnDLoad, 0) vBox = QVBoxLayout() vBox.addLayout(hBox1) vBox.addWidget(self.web_view) vBox.addWidget(self.progressBar) self.setLayout(vBox) self.setWindowTitle('Youtube Download Helper') self.setGeometry(QRect(round((self.width - 760) / 2), round((self.height - 550) / 2), 760, 550)) self.setMinimumSize(QSize(760, 550)) def store_html(self, html): # print('store_html()') self.html = html if self.web_view.page().action(QWebEnginePage.Back).isEnabled(): self.btnBack.setEnabled(True) else: self.btnBack.setDisabled(True) if self.web_view.page().action(QWebEnginePage.Forward).isEnabled(): self.btnNext.setEnabled(True) else: self.btnNext.setDisabled(True) if self.web_view.title() != 'YouTube' and self.web_view.title() != 'https://www.youtube.com': # print(self.web_view.title()) self.btnHome.setEnabled(True) else: self.btnHome.setDisabled(True) self.cmbDownList.clear() self.cmbCapsList.clear() url = self.web_view.page().url().url() if 'video-id' in self.html and '?v=' in url: # fp = open(self.web_view.title() + '.html', 'w') # fp.write(self.html) # fp.close() self.GI = GetItem(url) self.GI.addItem.connect(self.onAddItem) self.GI.addCaption.connect(self.onAddCaption) self.GI.finished.connect(self.onAddItemFinished) self.GI.start() else: self.btnDLoad.setDisabled(True) def get_html(self): # print('get_html') self.web_view.page().toHtml(self.store_html) def goHome(self): self.web_view.setUrl(QUrl(self.url)) def goBack(self): self.web_view.back() def goForward(self): self.web_view.forward() def goDownload(self): global download_ongoing download_ongoing = True self.btnDLoad.setDisabled(True) self.progressBar.setValue(0) self.DL = DownLoad(self.web_view.page().url().url(), self.cmbDownList.currentIndex(), self.cmbCapsList.currentIndex()) self.DL.valueChanged.connect(self.onValueChanged) self.DL.dlCompleted.connect(self.onDlCompleted) self.DL.start() def onAddItem(self, icon, item): self.cmbDownList.addItem(QIcon(icon), item) def onAddCaption(self, cap): self.cmbCapsList.addItem(cap) def onAddItemFinished(self): if not download_ongoing: self.btnDLoad.setEnabled(True) def onValueChanged(self, per): self.progressBar.setValue(round(per)) # print('%.2f%% completed' % per) def onDlCompleted(self): global download_ongoing download_ongoing = False self.btnDLoad.setDisabled(True) self.progressBar.setValue(0) def onIndexChanged(self): if not download_ongoing: self.btnDLoad.setEnabled(True) def onSettings(self): sWnd = SettingsDlg(self.width, self.height) sWnd.exec() if sWnd.SettingsChanged: # print('Settings saved!') if self.web_view.title() != 'YouTube' and self.web_view.title() != 'https://www.youtube.com': self.web_view.reload()
class MainWindow(QMainWindow): """Main window class.""" def __init__(self): """Init class.""" super(MainWindow, self).__init__() self.pixmap_syncthingui = QPixmap(":/images/syncthingui.svg") tf = QTransform() self.pixmap_syncthingui0 = QPixmap(":/images/syncthingui.svg") tf.rotate(90.0) self.pixmap_syncthingui1 = self.pixmap_syncthingui0.transformed(tf) tf.rotate(180.0) self.pixmap_syncthingui2 = self.pixmap_syncthingui0.transformed(tf) tf.rotate(270.0) self.pixmap_syncthingui3 = self.pixmap_syncthingui0.transformed(tf) self.init_gui() self.init_menu() self.init_systray() self.run() def init_gui(self): """init gui setup""" self.setWindowIcon(QIcon(self.pixmap_syncthingui)) self.progressbar = QProgressBar() self.statusBar().showMessage(getoutput(SYNCTHING + ' --version')) self.statusBar().addPermanentWidget(self.progressbar) self.setWindowTitle(__doc__.strip().capitalize()) self.setMinimumSize(900, 600) self.setMaximumSize(1280, 1024) self.resize(self.minimumSize()) self.center() # QWebView # self.view = QWebView(self) self.view = QWebEngineView(self) self.view.loadStarted.connect(self.start_loading) self.view.loadFinished.connect(self.finish_loading) self.view.loadProgress.connect(self.loading) self.view.titleChanged.connect(self.set_title) self.view.page().linkHovered.connect( lambda link_txt: self.statusBar().showMessage(link_txt[:99], 3000)) QShortcut("Ctrl++", self, activated=lambda: self.view.setZoomFactor(self.view.zoomFactor() + 0.2)) QShortcut("Ctrl+-", self, activated=lambda: self.view.setZoomFactor(self.view.zoomFactor() - 0.2)) QShortcut("Ctrl+0", self, activated=lambda: self.view.setZoomFactor(1)) QShortcut("Ctrl+q", self, activated=lambda: self.close()) # syncthing console self.consolewidget = QWidget(self) # TODO: start at specify (w,h) self.consolewidget.setMinimumSize(QSize(200, 100)) # TODO: setStyleSheet # self.consolewidget.setStyleSheet("margin:0px; padding: 0px; \ # border:1px solid rgb(0, 0, 0);") # border-radius: 40px;") # TODO read syncthing console visible from setting # self.consolewidget.setVisible(False) # self.consolewidget.showEvent # self.consoletextedit = QPlainTextEdit(parent=self.consolewidget) self.consoletoolbar = QWidget(self) hlayout = QHBoxLayout() hlayout self.consoletoolbar.setLayout(hlayout) self.consoletextedit = QTextEdit(parent=self.consolewidget) self.consoletextedit.setWordWrapMode(QTextOption.NoWrap) # self.consoletextedit.setStyleSheet(" border:1px solid rgb(0, 0, 0);") # self.consoletextedit.setStyleSheet("margin:0px; padding: 0px;") layout = QVBoxLayout() layout.addWidget(self.consoletoolbar) layout.addWidget(self.consoletextedit) self.consolewidget.setLayout(layout) self.splitter = QSplitter(Qt.Vertical) self.splitter.addWidget(self.view) self.splitter.addWidget(self.consolewidget) # process self.process = QProcess() self.process.error.connect(self._process_failed) # QProcess emits `readyRead` when there is data to be read self.process.readyRead.connect(self._process_dataReady) self.process.stateChanged.connect(self._process_stateChanged) # Just to prevent accidentally running multiple times # Disable the button when process starts, and enable it when it finishes # self.process.started.connect(lambda: self.runButton.setEnabled(False)) # self.process.finished.connect(lambda: self.runButton.setEnabled(True)) # backend options self.chrt = QCheckBox("Smooth CPU ", checked=True) self.ionice = QCheckBox("Smooth HDD ", checked=True) self.chrt.setToolTip("Use Smooth CPUs priority (recommended)") self.ionice.setToolTip("Use Smooth HDDs priority (recommended)") self.chrt.setStatusTip(self.chrt.toolTip()) self.ionice.setStatusTip(self.ionice.toolTip()) # main toolbar self.toolbar = self.addToolBar("SyncthinGUI Toolbar") # self.toolbar.addAction(QIcon.fromTheme("media-playback-stop"), self.toolbar.addAction(QIcon(":/images/stop.svg"), "Stop Sync", lambda: self.syncthing_stop()) # self.toolbar.addAction(QIcon.fromTheme("media-playback-start"), self.toolbar.addAction(QIcon(":/images/start.svg"), "Restart Sync", lambda: self.run()) self.toolbar.addSeparator() self.toolbar.addWidget(self.chrt) self.toolbar.addWidget(self.ionice) self.toolbar.addSeparator() # TODO: test event API self.toolbar.addAction(QIcon(":/images/start.svg"), "test ", lambda: self.test()) # final gui setup self.setCentralWidget(self.splitter) def test(self): """ test some function """ print("test") def init_menu(self): """init menu setup""" # file menu file_menu = self.menuBar().addMenu("File") # TODO: setting menu item file_menu.addAction("Exit", lambda: self.close()) # Syncthing menu sync_menu = self.menuBar().addMenu("Syncthing") sync_menu.addAction("Start Syncronization", lambda: self.run()) sync_menu.addAction("Stop Syncronization", lambda: self.syncthing_stop()) # TODO: restart # TODO: reflash F5 sync_menu.addAction("Open in external browser", lambda: open_new_tab(URL)) # view menu view_menu = self.menuBar().addMenu("View") # TODO: syncthing console menu view_menu.addAction("syncthing console", lambda: self.show_console) # zoom_menu = view_menu.addMenu("Zoom browser") zoom_menu.addAction( "Zoom In", lambda: self.view.setZoomFactor(self.view.zoomFactor() + .2)) zoom_menu.addAction( "Zoom Out", lambda: self.view.setZoomFactor(self.view.zoomFactor() - .2)) zoom_menu.addAction( "Zoom To...", lambda: self.view.setZoomFactor(QInputDialog.getInt( self, __doc__, "<b>Zoom factor ?:", 1, 1, 9)[0])) zoom_menu.addAction("Zoom Reset", lambda: self.view.setZoomFactor(1)) view_menu.addSeparator() act = view_menu.addAction("View Page Source", lambda: self.view_syncthing_source) act.setDisabled(True) # window menu window_menu = self.menuBar().addMenu("&Window") window_menu.addAction("Minimize", lambda: self.showMinimized()) window_menu.addAction("Maximize", lambda: self.showMaximized()) window_menu.addAction("Restore", lambda: self.showNormal()) window_menu.addAction("Center", lambda: self.center()) window_menu.addAction("Top-Left", lambda: self.move(0, 0)) window_menu.addAction("To Mouse", lambda: self.move_to_mouse_position()) window_menu.addAction("Fullscreen", lambda: self.showFullScreen()) window_menu.addSeparator() window_menu.addAction("Increase size", lambda: self.resize( self.size().width() * 1.2, self.size().height() * 1.2)) window_menu.addAction("Decrease size", lambda: self.resize( self.size().width() // 1.2, self.size().height() // 1.2)) window_menu.addAction("Minimum size", lambda: self.resize(self.minimumSize())) window_menu.addAction("Maximum size", lambda: self.resize(self.maximumSize())) window_menu.addAction("Horizontal Wide", lambda: self.resize( self.maximumSize().width(), self.minimumSize().height())) window_menu.addAction("Vertical Tall", lambda: self.resize( self.minimumSize().width(), self.maximumSize().height())) window_menu.addSeparator() window_menu.addAction("Disable Resize", lambda: self.setFixedSize(self.size())) # help menu help_menu = self.menuBar().addMenu("&Help") help_menu.addAction("Support Forum", lambda: open_new_tab(HELP_URL_0)) help_menu.addAction("Lastest Release", lambda: open_new_tab(HELP_URL_1)) help_menu.addAction("Documentation", lambda: open_new_tab(HELP_URL_2)) help_menu.addAction("Bugs", lambda: open_new_tab(HELP_URL_3)) help_menu.addAction("Source Code", lambda: open_new_tab(HELP_URL_4)) help_menu.addSeparator() help_menu.addAction("About Qt 5", lambda: QMessageBox.aboutQt(self)) help_menu.addAction("About Python 3", lambda: open_new_tab('https://www.python.org')) help_menu.addAction("About " + __doc__, lambda: QMessageBox.about(self, __doc__, HELPMSG)) help_menu.addSeparator() help_menu.addAction("Keyboard Shortcuts", lambda: QMessageBox.information(self, __doc__, SHORTCUTS)) help_menu.addAction("View GitHub Repo", lambda: open_new_tab(__url__)) if not sys.platform.startswith("win"): help_menu.addAction("Show Source Code", lambda: self.view_source()) help_menu.addSeparator() help_menu.addAction("Check Updates", lambda: self.check_for_updates()) def init_systray(self): """init system tray icon""" # self.tray = QSystemTrayIcon(QIcon(self.pixmap_syncthingui), self) self.tray = AnimatedSysTrayIcon(QIcon(self.pixmap_syncthingui), self) self.tray.add_ani_icon(QIcon(self.pixmap_syncthingui0)) self.tray.add_ani_icon(QIcon(self.pixmap_syncthingui1)) self.tray.add_ani_icon(QIcon(self.pixmap_syncthingui2)) self.tray.add_ani_icon(QIcon(self.pixmap_syncthingui3)) self.tray.setToolTip(__doc__.strip().capitalize()) traymenu = QMenu(self) traymenu.addAction(__doc__).setDisabled(True) traymenu.addSeparator() # to test animate # traymenu.addAction("start", lambda: self.tray.animate_start()) # traymenu.addAction("stop", lambda: self.tray.animate_stop()) # traymenu.addSeparator() traymenu.addAction("Stop Sync", lambda: self.syncthing_stop()) traymenu.addAction("Restart Sync", lambda: self.run()) traymenu.addSeparator() traymenu.addAction("Show", lambda: self.show_gui()) traymenu.addAction("Hide", lambda: self.hide()) traymenu.addSeparator() # traymenu.addAction("Open Web", lambda: open_new_tab(URL)) # traymenu.addAction("Quit All", lambda: self.close()) traymenu.addAction("Quit All", lambda: self.app_exit()) self.tray.setContextMenu(traymenu) self.tray.show() def show_gui(self): """ Helper method to show UI, this should not be needed, but I discovered. """ self.showNormal() # webview require 70Mb to show webpage self.view.load(QUrl(URL)) def syncthing_start(self): """syncthing start""" self.run() def syncthing_stop(self): """syncthing stop""" print("try to stop syncthing") self.process.kill() # check there is no other syncthing is running! for proc in psutil.process_iter(): # check whether the process name matches # print("procress: %s " % proc.name()) if proc.name() == SYNCTHING: proc.kill() def run(self): """Run bitch run!.""" # Stop first! self.syncthing_stop() command_to_run_syncthing = " ".join(( "ionice --ignore --class 3" if self.ionice.isChecked() else "", "chrt --verbose --idle 0" if self.chrt.isChecked() else "", SYNCTHING, "-no-browser")) print(command_to_run_syncthing) self.process.start(command_to_run_syncthing) if not self.process.waitForStarted(): self._process_failed() @pyqtSlot() def _process_failed(self): """Read and return errors.""" self.statusBar().showMessage("ERROR:Fail:Syncthing blow up in pieces!") print("ERROR:Fail:Syncthing blow up in pieces! Wheres your God now?") return str(self.process.readAllStandardError()).strip().lower() @pyqtSlot() def _process_dataReady(self): """get process stdout/strerr when data ready""" # TODO: format the msg to remove extra b and \n msg = str(self.process.readAll()) lines = msg.split("'") tmp = lines[1] tmp = tmp.splitlines(0) lines = tmp[0].split("\\n") for line in lines: if line != "": # print("1: %s" % line) self.consoletextedit.append(line) self.consoletextedit.ensureCursorVisible() # autoscroll to last line's first character self.consoletextedit.moveCursor(QTextCursor.End) self.consoletextedit.moveCursor(QTextCursor.StartOfLine) @pyqtSlot(QProcess.ProcessState) def _process_stateChanged(self, state): """ procress_stateChanged """ # TODO handle procress_stateChanged print("procress_stateChanged: %s" % state) def center(self): """Center Window on the Current Screen,with Multi-Monitor support.""" window_geometry = self.frameGeometry() mousepointer_position = QApplication.desktop().cursor().pos() screen = QApplication.desktop().screenNumber(mousepointer_position) centerpoint = QApplication.desktop().screenGeometry(screen).center() window_geometry.moveCenter(centerpoint) self.move(window_geometry.topLeft()) def move_to_mouse_position(self): """Center the Window on the Current Mouse position.""" window_geometry = self.frameGeometry() window_geometry.moveCenter(QApplication.desktop().cursor().pos()) self.move(window_geometry.topLeft()) def show_console(self): """Show syncthing console""" visible = not self.consolewidget.isVisible print("bVisible: %s" % visible) self.consolewidget.setVisible(True) self.consolewidget.resize(QSize(200, 100)) def view_source(self): """ TODO: Call methods to load and display source code.""" # call(('xdg-open ' if sys.platform.startswith("linux") else 'open ') # + __file__, shell=True) pass def view_syncthing_source(self): """Call methods to load and display web page source code.""" print("view_syncthing_source start") # access_manager = self.view.page().networkAccessManager() # reply = access_manager.get(QNetworkRequest(self.view.url())) # reply.finished.connect(self.slot_source_downloaded) def slot_source_downloaded(self): """Show actual page source code.""" reply = self.sender() # TODO: highlight html source editor/viewer self.textedit = QPlainTextEdit() self.textedit.setAttribute(Qt.WA_DeleteOnClose) self.textedit.setReadOnly(True) self.textedit.setPlainText(QTextStream(reply).readAll()) self.textedit.show() reply.deleteLater() @pyqtSlot() def start_loading(self): """show progressbar when downloading data""" self.progressbar.show() @pyqtSlot(bool) def finish_loading(self, finished): """Finished loading content.""" if not finished: # TODO: When loading fail, what should we do? print("load fail") if self.process.state() == QProcess.NotRunning: self.run() self.view.reload() # if self.process.state != QProcess.Running: # print("syncthing is not running: %s" % self.process.state()) # pass print("finish_loading: %s" % finished) # TODO: WebEngineView does not have following function? # self.view.settings().clearMemoryCaches() # self.view.settings().clearIconDatabase() # print("finish_loading %s" % datetime.strftime(datetime.now(), # '%Y-%m-%d %H:%M:%S')) # TODO: following line need 6 sec to finish!! # TODO: (" INFO: Loading Web UI increases >250Mb RAM!.") # self.view.page().mainFrame().evaluateJavaScript(BASE_JS) # print("finish_loading %s" % datetime.strftime(datetime.now(), # '%Y-%m-%d %H:%M:%S')) self.progressbar.hide() @pyqtSlot(int) def loading(self, idx): """loading content""" #print("loading %s" % idx) self.progressbar.setValue(idx) @pyqtSlot(str) def set_title(self, title): """set title when webview's title change""" # print("title: %s" % title) if len(title.strip()) > 0: self.setWindowTitle(self.view.title()[:99]) def check_for_updates(self): """Method to check for updates from Git repo versus this version.""" # print("TODO: https://github.com/coolshou/syncthingui/releases/latest") print("__version__: %s" % __version__) ''' this_version = str(open(__file__).read()) print("this_version: %s" % this_version) last_version = str(request.urlopen(__source__).read().decode("utf8")) print("last_version: %s" % last_version) TODO: previous use file compare, when diff then there is new file!! if this_version != last_version: m = "Theres new Version available!<br>Download update from the web" else: m = "No new updates!<br>You have the lastest version of" + __doc__ return QMessageBox.information(self, __doc__.title(), "<b>" + m) ''' def closeEvent(self, event): """Ask to Quit.""" if self.tray.isVisible(): if self.tray.supportsMessages(): self.tray.showMessage("Info", "The program will keep running in the " "system tray. To terminate the program," " choose <b>Quit</b> in the context " "menu of the system tray entry.") else: print(" System tray not supports balloon messages ") self.hide() event.ignore() def app_exit(self): """exit app""" # TODO: do we need to show UI when doing close? # self.show_gui() # TODO: show QMessageBox on all virtual desktop the_conditional_is_true = QMessageBox.question( self, __doc__.title(), 'Quit %s?' % __doc__, QMessageBox.Yes | QMessageBox.No, QMessageBox.No) == QMessageBox.Yes if the_conditional_is_true: self.syncthing_stop() self.ani_stop = True QApplication.instance().quit quit()
class FlowchartView(q.QWidget): selectRequested = qc.pyqtSignal(int) eventNameVisibilityChanged = qc.pyqtSignal(bool) eventParamVisibilityChanged = qc.pyqtSignal(bool) # View -> Core readySignal = qc.pyqtSignal() reloadedSignal = qc.pyqtSignal() eventSelected = qc.pyqtSignal(int) def __init__(self, parent, flow_data: FlowData) -> None: super().__init__(parent) self.flow_data: FlowData = flow_data self.is_current = True self.selected_event: typing.Optional[Event] = None self.showEventParams = False self.initWidgets() self.initLayout() self.connectWidgets() def initWidgets(self) -> None: self.web_object = FlowchartWebObject(self) self.flow_data.flowDataChanged.connect(self.onFlowDataChanged) self.flow_data.fileLoaded.connect(self.web_object.fileLoaded) self.selectRequested.connect(self.web_object.selectRequested) self.eventNameVisibilityChanged.connect( self.web_object.eventNameVisibilityChanged) self.eventParamVisibilityChanged.connect( self.onEventParamVisibilityChanged) self.eventParamVisibilityChanged.connect( self.web_object.eventParamVisibilityChanged) self.view = QWebEngineView() self.view.setContextMenuPolicy(qc.Qt.NoContextMenu) self.channel = QWebChannel() self.channel.registerObject('widget', self.web_object) self.view.page().setWebChannel(self.channel) self.view.page().setBackgroundColor(qg.QColor(0x38, 0x38, 0x38)) self.view.setUrl(qc.QUrl.fromLocalFile(get_path('assets/index.html'))) self.entry_point_view = q.QListView(self) self.ep_proxy_model = qc.QSortFilterProxyModel(self) self.ep_proxy_model.setSourceModel(self.flow_data.entry_point_model) self.ep_proxy_model.setFilterKeyColumn(-1) self.entry_point_view.setModel(self.ep_proxy_model) self.ep_search = SearchBar() self.ep_search.hide() self.container_model = ContainerModel(self) self.container_view = ContainerView(None, self.container_model, self.flow_data) self.container_stacked_widget = q.QStackedWidget() self.container_stacked_widget.addWidget(q.QWidget()) self.container_stacked_widget.addWidget(self.container_view) self.update_timer = qc.QTimer(self) self.update_timer.timeout.connect(self.web_object.flowDataChanged) self.update_timer.setSingleShot(True) def initLayout(self) -> None: left_pane_splitter = q.QSplitter(qc.Qt.Vertical) ep_widget = q.QWidget() ep_layout = q.QVBoxLayout(ep_widget) ep_layout.setContentsMargins(0, 0, 0, 0) ep_layout.addWidget(self.entry_point_view, stretch=1) ep_layout.addWidget(self.ep_search) left_pane_splitter.addWidget(ep_widget) left_pane_splitter.addWidget(self.container_stacked_widget) left_pane_splitter.setSizes([ int(left_pane_splitter.height() * 0.6), int(left_pane_splitter.height() * 0.4) ]) splitter = q.QSplitter() splitter.addWidget(left_pane_splitter) splitter.addWidget(self.view) splitter.setSizes( [int(splitter.width() * 0.3), int(splitter.width() * 0.7)]) layout = q.QHBoxLayout(self) layout.addWidget(splitter) layout.setContentsMargins(0, 0, 0, 0) def connectWidgets(self) -> None: self.ep_search.connectToFilterModel(self.ep_proxy_model) self.ep_search.addFindShortcut(self) self.flow_data.flowDataChanged.connect( lambda reason: self.entry_point_view.clearSelection()) self.entry_point_view.selectionModel().selectionChanged.connect( self.onEntryPointSelected) connect_model_change_signals(self.container_model, self.flow_data, FlowDataChangeReason.EventParameters) self.eventSelected.connect(self.onEventSelectedInWebView) self.flow_data.flowDataChanged.connect( lambda reason: self.refreshParamModel()) self.reloadedSignal.connect(self.onWebViewReloaded) def onEventParamVisibilityChanged(self, show: bool) -> None: self.showEventParams = show def setIsCurrentView(self, is_current: bool) -> None: self.is_current = is_current if is_current and self.update_timer.isActive(): self.update_timer.stop() self.web_object.flowDataChanged.emit() def export(self) -> None: if not self.flow_data.flow: return path = q.QFileDialog.getSaveFileName( self, 'Select a location for the graph data', self.flow_data.flow.name + '.json', 'Data (*.json)')[0] if not path: return data = self.web_object.getData() try: with open(path, 'w') as f: json.dump(data, f, default=lambda x: str(x)) except: q.QMessageBox.critical(self, 'Export graph data', 'Failed to write to ' + path) def reload(self) -> None: self.view.reload() def onWebViewReloaded(self) -> None: if not self.selected_event or not self.flow_data.flow or not self.flow_data.flow.flowchart: return try: new_idx = self.flow_data.flow.flowchart.events.index( self.selected_event) self.selectRequested.emit(new_idx) except ValueError: self.container_model.set(None) self.container_stacked_widget.setCurrentIndex(0) def refreshParamModel(self) -> bool: if self.selected_event and hasattr(self.selected_event.data, 'params'): if not self.selected_event.data.params: # type: ignore self.selected_event.data.params = Container() # type: ignore self.container_model.set( self.selected_event.data.params) # type: ignore self.container_stacked_widget.setCurrentIndex(1) return True return False def onEventSelectedInWebView(self, idx: int) -> None: if idx >= 0: event = self.flow_data.flow.flowchart.events[idx] self.selected_event = event if self.refreshParamModel(): return else: self.selected_event = None self.container_model.set(None) self.container_stacked_widget.setCurrentIndex(0) def onFlowDataChanged(self, reason: FlowDataChangeReason) -> None: should_reload = bool(reason & (FlowDataChangeReason.Reset | FlowDataChangeReason.Actors | FlowDataChangeReason.Events)) if self.showEventParams: should_reload = should_reload or bool( reason & FlowDataChangeReason.EventParameters) if not should_reload: return if self.is_current: self.web_object.flowDataChanged.emit() else: self.update_timer.start(15 * 1000) def onEntryPointSelected(self, selected, deselected) -> None: if len(selected.indexes()) != 1: return idx = selected.indexes()[0] self.selectRequested.emit(-1000 - self.ep_proxy_model.mapToSource(idx).row()) def delayedSelect(self, event: Event) -> None: try: qc.QTimer.singleShot( 1000, lambda: self.selectRequested.emit( self.flow_data.flow.flowchart.events.index(event))) except ValueError: pass def webEditEvent(self, idx: int) -> None: if idx < 0: return show_event_editor(self, self.flow_data, idx) def webAddEntryPoint(self, event_idx: int) -> None: if event_idx < 0: return ep_name, ok = q.QInputDialog.getText(self, 'Add entry point', f'Name of the new entry point:', q.QLineEdit.Normal) if not ok or not ep_name: return ep = EntryPoint(ep_name) assert self.flow_data.flow and self.flow_data.flow.flowchart ep.main_event.v = self.flow_data.flow.flowchart.events[event_idx] self.flow_data.entry_point_model.append(ep) def webRemoveEntryPoint(self, ep_idx: int) -> None: try: self.flow_data.entry_point_model.removeRow(ep_idx) except IndexError as e: q.QMessageBox.critical( self, 'Bug', f'An error has occurred: {e}\n\nPlease report this issue and mention what you were doing when this message showed up.' ) def addNewEvent(self) -> typing.Optional[Event]: return add_new_event(self, self.flow_data) def webAddEventAbove(self, parent_indices: typing.List[int], event_idx: int) -> None: if event_idx < 0: return assert self.flow_data.flow and self.flow_data.flow.flowchart event = self.flow_data.flow.flowchart.events[event_idx] parent_events = [ self.flow_data.flow.flowchart.events[i] for i in parent_indices if i >= 0 ] list_widget = CheckableEventParentListWidget(None, event, parent_events) if parent_events: dialog = q.QDialog( self, qc.Qt.WindowTitleHint | qc.Qt.WindowSystemMenuHint) dialog.setWindowTitle('Add new event above...') btn_box = q.QDialogButtonBox(q.QDialogButtonBox.Ok | q.QDialogButtonBox.Cancel) btn_box.accepted.connect(dialog.accept) btn_box.rejected.connect(dialog.reject) dialog_layout = q.QVBoxLayout(dialog) dialog_layout.addWidget( q.QLabel( 'Please select links that should be modified to point to the new event you are going to add.' )) dialog_layout.addWidget(list_widget) dialog_layout.addWidget(btn_box) ret = dialog.exec_() if not ret: return new_parent = self.addNewEvent() if not new_parent: return self._doAddEventAbove(list_widget.getSelectedEvents(), event, new_parent) self.flow_data.flowDataChanged.emit(FlowDataChangeReason.Events) self.delayedSelect(new_parent) def _doAddEventAbove(self, parents: typing.List[typing.Tuple[ Event, typing.List[typing.Any]]], event: Event, new_parent: Event) -> None: # Update the parents to point to the new parent. for parent, branches in parents: if isinstance(parent.data, ActionEvent) or isinstance( parent.data, JoinEvent) or isinstance( parent.data, SubFlowEvent): # Easy case: just set the next pointer to the new parent. parent.data.nxt.v = new_parent # For switch and fork events, update all branches that currently point to the event. elif isinstance(parent.data, SwitchEvent): for case in branches: if parent.data.cases[case].v == event: parent.data.cases[case].v = new_parent elif isinstance(parent.data, ForkEvent): for i, fork in enumerate(branches): if fork.v == event: parent.data.forks[i].v = new_parent