class HelpWindow(QtWidgets.QWidget): # TODO: do not hardcode this path like this. __doc_path = os.path.abspath( os.path.join( os.path.abspath(os.path.dirname(__file__)), "..", "..", "doc", "index.html")) def __init__(self, parent=None): super().__init__(parent) # The help display. self.help_display = QWebView(parent=self) self.help_display.load(QtCore.QUrl("file://" + self.__doc_path)) # Close button. self.close_button = QtWidgets.QPushButton("Close") self.close_button.clicked.connect(self.close) # Layout for the widgets. layout = QtWidgets.QVBoxLayout() layout.addWidget(self.help_display) layout.addWidget(self.close_button) self.setLayout(layout) # Title self.setWindowTitle("{} help".format(version.progname))
class HelpView(SizePersistedDialog): ''' Modeless dialog for presenting HTML help content ''' def __init__(self, parent, icon, prefs, html=None, page=None, title=''): self.prefs = prefs #QDialog.__init__(self, parent=parent) super(HelpView, self).__init__(parent, 'help_dialog') self.setWindowTitle(title) self.setWindowIcon(icon) self.l = QVBoxLayout(self) self.setLayout(self.l) self.wv = QWebView() if html is not None: self.wv.setHtml(html) elif page is not None: self.wv.load(QUrl(page)) self.wv.setMinimumHeight(100) self.wv.setMaximumHeight(16777215) self.wv.setMinimumWidth(400) self.wv.setMaximumWidth(16777215) self.wv.setGeometry(0, 0, 400, 100) self.wv.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.l.addWidget(self.wv) # Sizing sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.sizePolicy().hasHeightForWidth()) self.setSizePolicy(sizePolicy) self.resize_dialog()
def PdfConverter(username): htmllink = "bootstrap_mod/usertemp/"+username+".html" app1 = QApplication(sys.argv) web = QWebView() link =QUrl.fromLocalFile(QFileInfo(htmllink).absoluteFilePath()) web.load(QUrl(link)) printer = QPrinter() printer.setPageSize(QPrinter.A4) printer.setOutputFormat(QPrinter.PdfFormat) Pdf_Generated_Name="bootstrap_mod/usertemp/"+username+".pdf" printer.setOutputFileName(Pdf_Generated_Name) web.print(printer) QApplication.exit() def convertIt(): web.print(printer) print("Pdf generated") QApplication.exit() web.loadFinished.connect(convertIt) sys.exit(app1.exec_()) return 0
def run_browser(url): app = QApplication(['info-display']) browser = QWebView() page = WebPage() browser.setPage(page) browser.load(QUrl(url)) browser.show() app.exec()
class Browser(object): def __init__(self): self.network_manager = QNetworkAccessManager() self.network_manager.createRequest = self._create_request self.network_manager.finished.connect(self._request_finished) self.web_page = QWebPage() self.web_page.setNetworkAccessManager(self.network_manager) self.web_view = QWebView() self.web_view.setPage(self.web_page) def _create_request(self, operation, request, data): print(data.readAll()) reply = QNetworkAccessManager.createRequest(self.network_manager, operation, request, data) return reply def _request_finished(self, reply): if not reply.error() == QNetworkReply.NoError: # request probably failed print(reply.error()) print(reply.errorString()) def _make_request(self, url): request = QNetworkRequest() request.setUrl(QUrl(url)) return request def _urlencode_post_data(self, post_data): post_params = QUrl() for (key, value) in post_data.items(): post_params.addQueryItem(key, value) return post_params.encodedQuery() def perform(self, url, method='GET', post_data=dict()): request = self._make_request(url) if method == 'GET': self.web_view.load(request) else: encoded_data = self._urlencode_post_data(post_data) request.setRawHeader('Content-Type', QByteArray('application/x-www-form-urlencoded')) self.web_view.load(request, QNetworkAccessManager.PostOperation, encoded_data)
class BrowserWidget(QWidget, itab_item.ITabItem): ############################################################################### # RecentProjectItem SIGNALS ############################################################################### """ openProject(QString) openPreferences() dontOpenStartPage() """ openProject = pyqtSignal(str) openPreferences = pyqtSignal() dontOpenStartPage = pyqtSignal() ############################################################################### def __init__(self, url, process=None, parent=None): super(BrowserWidget, self).__init__(parent) self._id = url self._process = process vbox = QVBoxLayout(self) #Web Frame self.webFrame = QWebView(self) self.webFrame.setAcceptDrops(False) self.webFrame.load(QUrl(url)) vbox.addWidget(self.webFrame) if process is not None: time.sleep(0.5) self.webFrame.load(QUrl(url)) self.webFrame.page().currentFrame().setScrollBarPolicy( Qt.Vertical, Qt.ScrollBarAsNeeded) self.webFrame.page().currentFrame().setScrollBarPolicy( Qt.Horizontal, Qt.ScrollBarAsNeeded) def start_page_operations(self, url): opt = file_manager.get_basename(url.toString()) print("señal", opt, "self.emit(SIGNAL(opt))") #self.emit(SIGNAL(opt)) def shutdown_pydoc(self): if self._process is not None: self._process.kill() def find_match(self, word, back=False, sensitive=False, whole=False): self.webFrame.page().findText(word)
class Browser(StickWidget): def __init__(self, url): super(Browser, self).__init__() self.view = QWebView(self) self.view.load(QUrl(url)) self.view.page().setLinkDelegationPolicy(QWebPage.DelegateAllLinks) self.view.page().linkClicked.connect(self.link_clicked) self.view.page().mainFrame().setScrollBarPolicy(Qt.Horizontal, Qt.ScrollBarAlwaysOff) self.view.settings().setUserStyleSheetUrl(QUrl.fromLocalFile(os.path.join(get_parent_dir(__file__), "scrollbar.css"))) self.layout.addWidget(self.view) def link_clicked(self, url): self.view.load(url)
class MainWindow(QMainWindow): def __init__(self, parent=None): super(MainWindow, self).__init__(parent) vbox = QVBoxLayout(self) self.webView = QWebView(self) self.setCentralWidget(self.webView) self.webView.setUrl(QUrl("http://127.0.0.1:5000")) self.webView.page().setLinkDelegationPolicy(QWebPage.DelegateAllLinks) self.webView.linkClicked.connect(self.linkClicked) self.setWindowTitle("Inventory DB") def linkClicked(self, url): print("Page changed: " + url.toString()) if url.toString().startswith("http://127.0.0.1"): self.webView.load(url) self.webView.page().setLinkDelegationPolicy(QWebPage.DelegateAllLinks) else: webbrowser.open(str(url.toString()))
class WebRender(QWidget): def __init__(self): super(WebRender, self).__init__() vbox = QVBoxLayout(self) #Web Frame self.webFrame = QWebView() QWebSettings.globalSettings().setAttribute( QWebSettings.DeveloperExtrasEnabled, True) vbox.addWidget(self.webFrame) def render_page(self, url): self.webFrame.load(QUrl('file:///' + url)) def render_from_html(self, html, url=None): url = url and QUrl(url) or "" self.webFrame.setHtml(html, url)
class MainWindow(QMainWindow): def __init__(self, url): super(MainWindow, self).__init__() self.view = QWebView(self) self.view.load(url) self.view.setFixedSize(890, 550) # comment out the following line to allow refresh for debugging self.view.setContextMenuPolicy(Qt.NoContextMenu) @staticmethod def display_dir_dialog(): dialog = QFileDialog() dir_path = QFileDialog.getExistingDirectory(dialog, "Select Directory") return dir_path @staticmethod def display_file_dialog(): dialog = QFileDialog() file_path = QFileDialog.getOpenFileName(dialog, "Select File") return file_path
class TestWindow(QWidget): TMPDIR = "tmp" TMPHTMLFILE = os.path.join(TMPDIR,'tmptext.html') def __init__(self,parent = None): QWidget.__init__(self,parent) if os.path.isdir(TestWindow.TMPDIR) == False: os.mkdir(TestWindow.TMPDIR) self.textEditor = TextEditor(self) self.webViewer = QWebView(self) self.setupUI() self.textEditor.textChangeSignal.connect(self.reloadText) def reloadText(self,dataDict): if self.webViewer.isVisible() == False: self.webViewer.show() with codecs.open( ResourceManager.getResourceAbsPath('template.html'),'r','utf-8' ) as templateFileObj: templateStr = templateFileObj.read() with open( TestWindow.TMPHTMLFILE,'wb' ) as tempFileObj: tempFileObj.write( (templateStr % dataDict[TextEditor.KEY_ORIGINALTXET]).encode('utf-8') ) self.webViewer.load(QUrl( "file:///%s" % os.path.abspath( TestWindow.TMPHTMLFILE ) )) def setupUI(self): self.webViewer.hide() layout = QHBoxLayout(self) self.splitter = QSplitter(self) self.splitter.addWidget(self.textEditor) self.splitter.addWidget(self.webViewer) layout.addWidget(self.splitter) self.textEditor.resize(400,600) self.webViewer.resize(400,600) self.resize(800,600) self.splitter.setStyleSheet("background-color:green;") self.textEditor.setStyleSheet("background-color:white;border:none;") self.webViewer.setStyleSheet("background-color:white;")
class WebRender(QWidget): """Render a web page inside the tools dock area.""" def __init__(self): super(WebRender, self).__init__() vbox, temporary_directory = QVBoxLayout(self), mkdtemp() # Web Frame self.webFrame = QWebView() # QWebView = QWebFrame + QWebSettings self.webFrame.setStyleSheet("QWebView{ background:#fff }") # no dark bg settings = self.webFrame.settings() # QWebSettings instance settings.setDefaultTextEncoding("utf-8") settings.setIconDatabasePath(temporary_directory) settings.setLocalStoragePath(temporary_directory) settings.setOfflineStoragePath(temporary_directory) settings.setOfflineWebApplicationCachePath(temporary_directory) settings.setAttribute(QWebSettings.DeveloperExtrasEnabled, True) settings.setAttribute(QWebSettings.LocalStorageEnabled, True) settings.setAttribute(QWebSettings.OfflineStorageDatabaseEnabled, True) settings.setAttribute(QWebSettings.PluginsEnabled, True) settings.setAttribute(QWebSettings.DnsPrefetchEnabled, True) settings.setAttribute(QWebSettings.JavascriptCanOpenWindows, True) settings.setAttribute(QWebSettings.JavascriptCanCloseWindows, True) settings.setAttribute(QWebSettings.JavascriptCanAccessClipboard, True) settings.setAttribute(QWebSettings.SpatialNavigationEnabled, True) settings.setAttribute( QWebSettings.LocalContentCanAccessRemoteUrls, True) settings.setAttribute( QWebSettings.OfflineWebApplicationCacheEnabled, True) vbox.addWidget(self.webFrame) def render_page(self, url): """Render a web page from a local file.""" self.webFrame.load(QUrl('file:///' + url)) def render_from_html(self, html, url=None): """Render a webpage from a string.""" url = url and QUrl(url) or "" self.webFrame.setHtml(html, url)
def __init__(self, program, web, courseName, endTime, sock, parent=None): QtWidgets.QMainWindow.__init__(self, parent) self.ui = uic.loadUi(config.config.ROOT_PATH + 'view/webView.ui', self) self.banProgram = program self.allowWeb = web self.sock = sock self.courseName = courseName self.endTime = datetime.datetime.strptime(endTime, "%Y-%m-%d %H:%M:%S") self.checkPopup = False for i in self.allowWeb: view = QWebView() view.load(QUrl(config.config.ALLOW_SITE[i])) self.ui.tabWidget.addTab(view, config.config.ALLOW_SITE[i].lstrip('https://www.')) self.ui.webView.load(QUrl('http://www.kookmin.ac.kr')) try: self.timer = QTimer(self) self.timer.timeout.connect(self.rest_time_display) self.timer.start(900) except Exception as e: print(e) self.ui.show() try: self.threadTimer = QTimer(self) self.threadTimer.timeout.connect(self.check_thread_run) self.mp = checkSystem.checkSystem(self.courseName, self.banProgram, self.sock) self.mp.start() self.threadTimer.start(41000) except Exception as e: print(e)
class Browser(StickWidget): def __init__(self, url): super(Browser, self).__init__() self.view = QWebView(self) self.layout.addWidget(self.view) self.view.settings().setAttribute(QWebSettings.PluginsEnabled, True) # enable plugins self.view.settings().setAttribute(QWebSettings.DeveloperExtrasEnabled, True) # enable dev tools self.view.settings().setUserStyleSheetUrl(QUrl.fromLocalFile(os.path.join(get_parent_dir(__file__), "scrollbar.css"))) self.view.load(QUrl(url)) self.view.page().setLinkDelegationPolicy(QWebPage.DelegateAllLinks) self.view.page().mainFrame().setScrollBarPolicy(Qt.Horizontal, Qt.ScrollBarAlwaysOff) # self.view.page().mainFrame().evaluateJavaScript(self.plugin_public_js) self.view.page().mainFrame().evaluateJavaScript(self.plugin_qvod_search_js) self.view.loadFinished.connect(self.url_load_finished) self.view.page().linkClicked.connect(self.link_clicked) def url_load_finished(self): # self.view.page().mainFrame().evaluateJavaScript("setTimeout(function () {startsearch(document)}, 3000)") self.view.page().mainFrame().evaluateJavaScript("setTimeout(function () {search()}, 3000)") def link_clicked(self, url): self.view.load(url) @property def plugin_qvod_search_js(self): fd = QFile("qvod/search.js") if fd.open(QIODevice.ReadOnly | QFile.Text): result = QTextStream(fd).readAll() fd.close() else: result = '' return result
def process_html(htmlpath, args, app): htmlurl = urllib.parse.quote(htmlpath.resolve().absolute().as_posix()) htmlQurl = QUrl("file://{}".format(htmlurl)) pdfOutputPath = htmlpath.with_suffix(".pdf") print("htmlpath:", htmlpath) print("htmlurl:", htmlurl) print("htmlQurl:", htmlQurl) print("pdfOutputPath:", pdfOutputPath) web = QWebView() web.page().settings().setAttribute(QWebSettings.DeveloperExtrasEnabled, True) printer = QPrinter() printer.setPageSize(QPrinter.A4) printer.setOutputFormat(QPrinter.PdfFormat) printer.setOutputFileName(str(pdfOutputPath)) def convertIt(): web.print_(printer) print("Pdf generated") def closeIt(): QApplication.exit() web.loadFinished.connect(convertIt) if args.show: web.show() else: web.loadFinished.connect(closeIt) web.load(htmlQurl)
class KuGou(QWidget): def __init__(self, parent = None): super(KuGou, self).__init__(parent) self.createLayout() self.createConnection() def createLayout(self): self.setWindowTitle("kugou player") #self.addressBar = QLineEdit() #self.goButton = QPushButton("&GO") #bl = QHBoxLayout() #bl.addWidget(self.addressBar) #bl.addWidget(self.goButton) self.webView = QWebView() layout = QVBoxLayout() layout.addWidget(self.webView) self.setLayout(layout) def createConnection(self): self.webView.load(QUrl("http://web.kugou.com"))
class Window(QMainWindow): def __init__(self, url, app): super(Window, self).__init__() self.progress = 0 self.app = app self.UI_FILE_PATH = 'ui/room_design.ui' self.CALLBACK = 'python_callback' self.MODELS_INFO = 'assets/models_info.json' self.TEXTURES_INFO = 'assets/textures_info.json' self.WIIMOTE_FREQUENCY = 50 self.DEFAULT_SCALE = 1.0 self.DEFAULT_ROTATION = 0.0 self.PLANE_XZ = 'xz' self.PLANE_XY = 'xy' self.PLANE_YZ = 'yz' self.WIN_X = 30 self.WIN_Y = 60 self.WIN_WIDTH = 1000 self.WIN_HEIGHT = 650 self.WIIMOTE_DEFAULT_ACCEL_SENSOR_VALUE = 512 self.WIIMOTE_MIN_ACCEL_SENSOR_VALUE = 407 self.WIIMOTE_MAX_ACCEL_SENSOR_VALUE = 610 self.WIIMOTE_SENSITIVITY = 10000 self.MESH_SELECT_TABLE_X_LEFT = 250 self.TEXTURE_SELECT_TABLE_X_LEFT = 50 self.SELECT_TABLES_Y = 665 screen_dimens = self.app.desktop().screenGeometry() self.url = url self.monitor_width = screen_dimens.width() self.monitor_height = screen_dimens.height() QNetworkProxyFactory.setUseSystemConfiguration(True) self.win = uic.loadUi(self.UI_FILE_PATH) self.wv = QWebView(self.win) js.SetupScene.init(self.wv) js.SetupScene.apply_callback(self.CALLBACK, self) self.wv.load(self.url) self.list_widget = self.win.list_widget self.mesh_select_table = None self.model_table = None self.texture_select_table = None self.setup_ui() self.meshes = [] self.selected_mesh = None self.mesh_translation = [] self.mesh_rotation = [] self.mesh_scale = [] self.is_first_b_button_callback = True self.wm_current_a_button_state = False self.address_line_edit = self.win.line_edit_address self.connect_btn = self.win.btn_connect self.connect_btn.clicked.connect(self.connect_wiimote) self.initial_accelerometer_data = None self.wiimote = wii.Wiimote(self.WIIMOTE_FREQUENCY, self.monitor_width, self.monitor_height) self.dpad_button_states = {} self.setup_wiimote() self.last_angle_y_rotation = self.DEFAULT_ROTATION self.last_scale_factor = self.DEFAULT_SCALE self.win.show() self.undo_utility = undo.UndoUtility() self.selected_plane = self.PLANE_XZ self.select_plane(self.selected_plane) # WIIMOTE BINDINGS & METHODS def setup_wiimote(self): self.wiimote.a_button_clicked.connect( lambda: self.on_wm_a_button_press(self.wiimote.accelerometer_data)) self.wiimote.a_button_released.connect(self.on_wm_a_button_release) self.wiimote.b_button_clicked.connect( lambda: self.on_wm_b_button_press(self.wiimote.accelerometer_data)) self.wiimote.ir_data_updated.connect( lambda: self.on_wm_ir_data_update(self.wiimote.pointer_location)) self.wiimote.b_button_released.connect(self.on_wm_b_button_release) self.wiimote.plus_button_clicked.connect(self.on_wm_plus_button_press) self.wiimote.minus_button_clicked.connect(self.on_wm_minus_button_press) self.wiimote.one_button_clicked.connect(self.request_undo) self.wiimote.two_button_clicked.connect(self.redo) self.dpad_button_states = { Qt.Key_Up: False, Qt.Key_Down: False, Qt.Key_Left: False, Qt.Key_Right: False, } self.wiimote.up_button_clicked.connect( lambda: self.on_wm_dpad_button_press(Qt.Key_Up)) self.wiimote.up_button_released.connect( lambda: self.on_wm_dpad_button_release(Qt.Key_Up)) self.wiimote.down_button_clicked.connect( lambda: self.on_wm_dpad_button_press(Qt.Key_Down)) self.wiimote.down_button_released.connect( lambda: self.on_wm_dpad_button_release(Qt.Key_Down)) self.wiimote.left_button_clicked.connect( lambda: self.on_wm_dpad_button_press(Qt.Key_Left)) self.wiimote.left_button_released.connect( lambda: self.on_wm_dpad_button_release(Qt.Key_Left)) self.wiimote.right_button_clicked.connect( lambda: self.on_wm_dpad_button_press(Qt.Key_Right)) self.wiimote.right_button_released.connect( lambda: self.on_wm_dpad_button_release(Qt.Key_Right)) self.wiimote.home_button_clicked.connect(self.on_wm_home_button_clicked) def on_wm_ir_data_update(self, data): x, y = data self.set_cursor_position(x, y, True) def on_wm_a_button_press(self, data): if self.wm_current_a_button_state: return self.wm_current_a_button_state = True self.simulate_mouse_press() if self.selected_mesh is not None: self.initial_accelerometer_data = data def on_wm_a_button_release(self): if self.wm_current_a_button_state is False: return self.wm_current_a_button_state = False self.simulate_mouse_release() def on_wm_b_button_press(self, data): if self.is_first_b_button_callback: js.SetupScene.save_state("wiimote_transform") self.initial_accelerometer_data = data self.is_first_b_button_callback = False if self.selected_mesh is not None: js.SetupScene.get_translation_rotation_scale(self.selected_mesh) self.handle_mesh_scaling_fine(data) self.handle_mesh_rotation_y(data) def on_wm_dpad_button_press(self, button): self.dpad_button_states[button] = True self.simulate_camera_event(QtGui.QKeyEvent.KeyPress, button) def on_wm_dpad_button_release(self, button): if self.dpad_button_states[button]: # = last frame: button was pressed self.simulate_camera_event(QtGui.QKeyEvent.KeyRelease, button) self.dpad_button_states[button] = False def on_wm_plus_button_press(self): if self.selected_mesh is not None: js.SetupScene.save_state("duplicate_mesh") if self.selected_mesh is not None: self.request_duplicate_mesh(self.selected_mesh) def on_wm_minus_button_press(self): if self.selected_mesh is not None: js.SetupScene.save_state("remove_mesh") self.delete_mesh(self.selected_mesh) def on_wm_home_button_clicked(self): js.SetupScene.set_camera_to_default() def on_wm_b_button_release(self): if len(self.mesh_rotation) != 0: self.last_angle_y_rotation = self.mesh_rotation[1] if len(self.mesh_scale) != 0: self.last_scale_factor = self.mesh_scale[0] if self.mesh_scale[0] < 0.1: js.SetupScene.scale_mesh_by_id(self.selected_mesh, 0.1, 0.1, 0.1) self.last_scale_factor = 0.1 js.SetupScene.on_scale_end() self.is_first_b_button_callback = True def connect_wiimote(self): address = self.address_line_edit.text() self.wiimote.connect(address) # UI SETUP def setup_ui(self): self.wv.setGeometry(self.WIN_X, self.WIN_Y, self.WIN_WIDTH, self.WIN_HEIGHT) self.wv.installEventFilter(self) self.win.installEventFilter(self) self.list_widget.selectionModel().selectionChanged.connect( self.mesh_selection_changed) self.setup_selection_tables() self.win.explain_controls_btn.clicked.connect(self.explain_controls) self.win.x_y_plane_btn.clicked.connect( lambda: self.select_plane(self.PLANE_XY)) self.win.x_z_plane_btn.clicked.connect( lambda: self.select_plane(self.PLANE_XZ)) self.win.y_z_plane_btn.clicked.connect( lambda: self.select_plane(self.PLANE_YZ)) self.win.x_y_plane_cam_btn.clicked.connect( lambda: self.target_cam_to_plane(self.PLANE_XY)) self.win.x_z_plane_cam_btn.clicked.connect( lambda: self.target_cam_to_plane(self.PLANE_XZ)) self.win.y_z_plane_cam_btn.clicked.connect( lambda: self.target_cam_to_plane(self.PLANE_YZ)) self.win.btn_new.clicked.connect(self.on_new_action) self.win.btn_save.clicked.connect(self.on_save_action) self.win.btn_load.clicked.connect(self.on_load_action) def set_bt_address(self, address): self.win.line_edit_address.setText(address) # SELECTION TABLES def setup_selection_tables(self): """ Sets up the mesh_select_table and texture_select_table """ # magic numbers = coordinates (the app is not responsive anyway) # mesh_select_table (for the models) self.mesh_select_table = model_table.ExpandableSelectionTable( self, 'Mesh', self.win) self.mesh_select_table.set_create_from_center(False) self.mesh_select_table.move( self.MESH_SELECT_TABLE_X_LEFT, self.SELECT_TABLES_Y + model_table.TABLE_ITEM_SIZE) self.mesh_select_table.itemSelectionChanged.connect( lambda: self.table_selection_changed(self.mesh_select_table)) self.read_selection_table_data(self.MODELS_INFO, self.mesh_select_table) # texture_select_table (ground, wall textures) self.texture_select_table = model_table.ExpandableSelectionTable( self, 'Texture', self.win) self.texture_select_table.set_create_from_center(False) self.texture_select_table.move( self.TEXTURE_SELECT_TABLE_X_LEFT, self.SELECT_TABLES_Y + model_table.TABLE_ITEM_SIZE) self.texture_select_table.itemSelectionChanged.connect( lambda: self.table_selection_changed(self.texture_select_table)) self.read_selection_table_data(self.TEXTURES_INFO, self.texture_select_table) def table_selection_changed(self, table): """ Callback method; simply de-selects the other table. "Table" refers to the mesh selection bar or the texture selection bar (which are, technically, tables). """ other_table = None if table == self.mesh_select_table: other_table = self.texture_select_table else: other_table = self.mesh_select_table if len(table.selectedIndexes()) > 0: other_table.lose_focus() def read_selection_table_data(self, file_path, selection_table): """ Reads the data from the file (e.g. assets/models_info.json) and sets up the seletion_table, i.e., tells it what categories there are & what icons and subitems they have. """ with open(file_path, 'r') as data_file: data = json.loads(data_file.read()) for category in data['categories']: selection_table.add_item(category) # ADDING MESHES / SELECTING TEXTURES def request_add_mesh(self, mesh_file_name, type_, name=None, transform="null", from_load=False): """ Tells the JS component to add a mesh (scene object). name: might be overwritten if it is already in use transform: location, rotation and scale (default="null" = at the center of the scene, scale = 1 & rotation = 0, 0, 0) from_load: when this method is called to load another state (True), no new "undo state" will be created. Default=False """ if not from_load: js.SetupScene.save_state("add_mesh") name = um.get_name_for_new_mesh(name, type_, self.meshes) mesh_file = open(mesh_file_name) data, json_data = um.read_file_as_js_string(mesh_file, True) mesh_file.close() texture_images = um.load_images_as_base64(json_data) js.SetupScene.add_mesh(data, name, texture_images, type_, transform, mesh_file_name) @QtCore.pyqtSlot(str) def js_mesh_loaded(self, mesh_name): """ Gets called when the JS component successfully created an object. """ self.list_widget.addItem(mesh_name) self.meshes.append(mesh_name) self.select_mesh(mesh_name) @QtCore.pyqtSlot(str, str) def js_mesh_load_error(self, mesh_name, error): """ Gets called when the JS component failed to create an object. """ print(mesh_name, error) def request_change_texture(self, file_name, name, type_, create_undo_point=True): if create_undo_point: js.SetupScene.save_state("change_texture") base64data = um.load_single_img_as_base64(file_name) js.SetupScene.set_texture(type_, name, base64data, file_name) # PLANE / CAMERA BUTTONS ON THE SIDE def target_cam_to_plane(self, plane): js.SetupScene.target_camera_to_plane(plane) def select_plane(self, which): self.selected_plane = which data = {self.PLANE_XY: [False, True, True], self.PLANE_XZ: [True, False, True], self.PLANE_YZ: [True, True, False], } self.win.x_y_plane_btn.setEnabled(data[which][0]) self.win.x_z_plane_btn.setEnabled(data[which][1]) self.win.y_z_plane_btn.setEnabled(data[which][2]) js.SetupScene.set_selected_plane(which) # OBJECT MANIPULATIONS def request_duplicate_mesh(self, mesh_id): js.SetupScene.save_state("duplicate_mesh") name = um.get_name_for_copy(mesh_id, self.meshes) js.SetupScene.duplicate_mesh(mesh_id, name) def handle_mesh_scaling_fine(self, data): scale_step = (self.WIIMOTE_DEFAULT_ACCEL_SENSOR_VALUE - self.WIIMOTE_MIN_ACCEL_SENSOR_VALUE) / \ self.WIIMOTE_SENSITIVITY scale = ((self.initial_accelerometer_data[1]-data[1]) * scale_step) if data[2] > self.WIIMOTE_DEFAULT_ACCEL_SENSOR_VALUE - 1: js.SetupScene.scale_mesh_by_id(self.selected_mesh, scale + self.last_scale_factor, scale + self.last_scale_factor, scale + self.last_scale_factor) def handle_mesh_rotation_y(self, data): right_angle = 90 angle_step_smoothing = 1.3 sensor_data_smoothing = 6 angle_step = (self.WIIMOTE_DEFAULT_ACCEL_SENSOR_VALUE - self.WIIMOTE_MIN_ACCEL_SENSOR_VALUE) / \ right_angle * np.pi / (right_angle * 2) angle = (self.initial_accelerometer_data[0]-data[0]) * angle_step / \ angle_step_smoothing if data[2] > self.WIIMOTE_DEFAULT_ACCEL_SENSOR_VALUE - \ sensor_data_smoothing: js.SetupScene.rotate_mesh_by_id(self.selected_mesh, 0, angle + self.last_angle_y_rotation, 0) @QtCore.pyqtSlot(str, str, str) def on_translation_rotation_scale_request(self, trans, rot, scale): self.get_mesh_properties(trans, 'trans') self.get_mesh_properties(rot, 'rot') self.get_mesh_properties(scale, 'scale') def get_mesh_properties(self, data, mode): if mode == 'trans': self.mesh_translation = js.deserialize_list(data) if mode == 'rot': self.mesh_rotation = js.deserialize_list(data) if mode == 'scale': self.mesh_scale = js.deserialize_list(data) # dragging happens in JS, Python is notified when it starts to enable Undo: @QtCore.pyqtSlot(str) def on_js_obj_drag_start(self, mesh_id): js.SetupScene.save_state("js_drag_translate") # DELETING / RESETTING def clear_all(self): """ Resets the entire scene & the undo utility. """ while self.list_widget.count() > 0: self.list_widget.takeItem(0) for mesh in self.meshes: js.SetupScene.remove_mesh(mesh) js.SetupScene.remove_texture("walls") js.SetupScene.remove_texture("carpet") self.meshes = [] self.selected_mesh = None self.undo_utility.reset() def delete_mesh(self, mesh_id): self.selected_mesh = None if mesh_id in self.meshes: index = self.meshes.index(mesh_id) del self.meshes[index] self.list_widget.takeItem(index) js.SetupScene.remove_mesh(mesh_id) # MESH SELECTION def mesh_selection_changed(self, b=0): """ Callback for when the selection in the list of scene objects changes. """ selected = self.list_widget.selectedIndexes() if len(selected) > 0: self.select_mesh(self.meshes[selected[0].row()], False) else: self.de_select_meshes(False) def select_mesh(self, obj_id, update_list=True, from_click=False): """ Sets the mesh as selected in the scene (gives it an outine) & in the list of objects. update_list: if the selection change came from the list widget, it should not be updated redundantly (=False) """ was_selected = self.selected_mesh == obj_id # = short for "the item that was selected was the item that was already # selected" self.selected_mesh = obj_id for mesh in self.meshes: if mesh == obj_id: continue js.SetupScene.remove_highlight_from_mesh(mesh) if update_list and not was_selected: if obj_id in self.meshes: # remove any selection: selection_model = self.list_widget.selectionModel() selection_model.clear() # add new selection: selected_index = self.meshes.index(obj_id) new_selection = self.list_widget.indexFromItem( self.list_widget.item(selected_index)) selection_model.select(new_selection, QtCore.QItemSelectionModel.Select) js.SetupScene.highlight_mesh(obj_id, from_click) def de_select_meshes(self, from_js=True): if from_js: selection_model = self.list_widget.selectionModel() selection_model.clear() self.selected_mesh = None for mesh in self.meshes: js.SetupScene.remove_highlight_from_mesh(mesh) @QtCore.pyqtSlot(str) def on_object_clicked(self, obj_id): if obj_id in self.meshes: self.select_mesh(obj_id, True, True) else: # == None; None does not work from JS self.de_select_meshes() @QtCore.pyqtSlot(str, str, str) def on_mesh_highlighted(self, obj_id, scale, y_rotation): self.last_scale_factor = float(scale) self.last_angle_y_rotation = float(y_rotation) self.mesh_rotation = [0, float(y_rotation), 0] self.mesh_scale = [float(scale), float(scale), float(scale)] # CURSOR AND CLICKS def set_cursor_position(self, x, y, absolute=True): if absolute: self.cursor().setPos(x, y) else: self.cursor().setPos(self.win.centralWidget.pos().x() + x, self.win.centralWidget.pos().y() + y) def get_cursor_position(self, absolute=True): if absolute: return self.cursor().pos() else: cursor_pos = self.cursor().pos() # trial and error y_fix = self.win.style().pixelMetric( QtWidgets.QStyle.PM_TitleBarHeight) * 1.5 + 1 return QtCore.QPoint(cursor_pos.x() - self.win.pos().x(), cursor_pos.y() - self.win.pos().y() - y_fix) def simulate_click(self): rel_cursor = self.get_cursor_position(False) abs_cursor = self.get_cursor_position(True) for ev_type in (QtGui.QMouseEvent.MouseButtonPress, QtGui.QMouseEvent.MouseButtonRelease): self.simulate_left_mouse_event(ev_type, rel_cursor, abs_cursor) def simulate_mouse_press(self): rel_cursor = self.get_cursor_position(False) abs_cursor = self.get_cursor_position(True) self.simulate_left_mouse_event(QtGui.QMouseEvent.MouseButtonPress, rel_cursor, abs_cursor) def simulate_mouse_release(self): rel_cursor = self.get_cursor_position(False) abs_cursor = self.get_cursor_position(True) self.simulate_left_mouse_event(QtGui.QMouseEvent.MouseButtonRelease, rel_cursor, abs_cursor) def simulate_left_mouse_event(self, ev_type, rel_cursor, abs_cursor): clicked_child = self.win.childAt(rel_cursor) if clicked_child is None: clicked_child = self.win global_child_coords = clicked_child.mapToGlobal(QtCore.QPoint(0, 0)) pos = QtCore.QPoint(abs_cursor.x() - global_child_coords.x(), abs_cursor.y() - global_child_coords.y()) event = QtGui.QMouseEvent(ev_type, pos, abs_cursor, QtCore.Qt.LeftButton, QtCore.Qt.LeftButton, QtCore.Qt.NoModifier) self.app.postEvent(clicked_child, event) def simulate_camera_event(self, type_, key): event = QtGui.QKeyEvent(type_, key, QtCore.Qt.NoModifier) self.app.postEvent(self.wv, event) # NEW, SAVE, LOAD, UNDO, REDO def on_new_action(self): x, y, ok = um.InputDialog.get_new_room_xz_dimensions() if ok: self.clear_all() js.SetupScene.create_new_scene(x, y) def on_save_action(self): js.SetupScene.save_state("save") def on_load_action(self): scene_json = um.FileDialog.load_json_from_file() if scene_json != '': self.load_state(scene_json) @QtCore.pyqtSlot(str, str) def save_state_result(self, scene_json, identifier): """ Callback for when JS successfully saved the scene. identifier: same string that was given to the JS component to mark why the state was saved. E.g., if it is "undo", that means it was created to be able to redo the scene & it should not be added to the undo list. """ scene_obj = json.loads(scene_json) scene_obj["selection"] = self.selected_mesh if \ (self.selected_mesh is not None) else "None" scene_json = json.dumps(scene_obj) if identifier == "undo": self.undo_utility.set_first_state_at_undo(scene_json) self.undo() elif identifier == "save": um.FileDialog.save_json_to_file(scene_json) else: self.undo_utility.add_action(identifier, scene_json) def load_state(self, scene_json): """ Load the state in scene_json (JSON String) while completely discarding the current state. Cannot be undone. """ self.clear_all() as_data = json.loads(scene_json) js.SetupScene.redo_scene(as_data["room"]["x"], as_data["room"]["y"]) for mesh in as_data["meshes"]: self.request_add_mesh(mesh["fileName"], mesh["type"], mesh["id"], json.dumps({ "pos": mesh["pos"], "rot": mesh["rot"], "scale": mesh["scale"] }), True) self.load_selection(as_data) self.load_floor_and_walls(as_data) def load_changed_state(self, current_state, next_state): """ Loads the "next_state" (JSON Object / dicts and lists) from the current state. Is faster than load_state & can be undone; use this method for "undo" and "redo" (when there are likely to be few changes between the states & undo must be possible). """ # delete meshes if they are not in the next state for mesh in current_state["meshes"]: found_mesh = False for other_mesh in next_state["meshes"]: if other_mesh["id"] == mesh["id"]: found_mesh = True break if not found_mesh: self.delete_mesh(mesh["id"]) # transform meshes for the next state / create them, if they don't # exist yet for mesh in next_state["meshes"]: found_mesh = False for previous_state in current_state["meshes"]: if previous_state["id"] == mesh["id"]: found_mesh = True self.load_transformations_for_mesh(mesh, previous_state) break if not found_mesh: self.request_add_mesh(mesh["fileName"], mesh["type"], mesh["id"], json.dumps({ "pos": mesh["pos"], "rot": mesh["rot"], "scale": mesh["scale"] }), True) self.load_selection(next_state) self.load_floor_and_walls(next_state) def load_selection(self, next_state): if next_state["selection"] is not "None": self.select_mesh(next_state["selection"]) else: self.de_select_meshes() def load_floor_and_walls(self, next_state): for texture_type in ("walls", "floor"): if texture_type in next_state: self.request_change_texture( next_state[texture_type]["fileName"], next_state[texture_type]["textureName"], next_state[texture_type]["type"], False) else: if texture_type == "floor": js.SetupScene.remove_texture("carpet") else: js.SetupScene.remove_texture(texture_type) def load_transformations_for_mesh(self, mesh, previous_state): pos_data_new = mesh["pos"] pos_data_old = previous_state["pos"] js.SetupScene.translate_mesh_by_id( mesh["id"], pos_data_new[0] - pos_data_old[0], pos_data_new[1] - pos_data_old[1], pos_data_new[2] - pos_data_old[2]) rot_data = mesh["rot"] js.SetupScene.rotate_mesh_by_id(mesh["id"], rot_data[0], rot_data[1], rot_data[2]) scale_data = mesh["scale"] js.SetupScene.scale_mesh_by_id( mesh["id"], scale_data[0], scale_data[1], scale_data[2], False) def request_undo(self): js.SetupScene.save_state("undo") def undo(self): undone_state = self.undo_utility.undo() current_state = self.undo_utility.current_state(True) if undone_state is not None: # self.load_state(undone_state["state"]) self.load_changed_state(json.loads(current_state["state"]), json.loads(undone_state["state"])) def redo(self): redone_state = self.undo_utility.redo() current_state = self.undo_utility.current_state(False) if redone_state is not None: # self.load_state(redone_state["state"]) self.load_changed_state(json.loads(current_state["state"]), json.loads(redone_state["state"])) # MISCELLANY @QtCore.pyqtSlot(str) def on_js_console_log(self, log): """ function for debugging purposes :param log: :return: """ print(log) def explain_controls(self): msg_box = QtWidgets.QMessageBox() pixmap = QtGui.QPixmap() pixmap.load("assets/img/wiimote_explain.png") # some 2:3 ratio that fits on most screens... pixmap = pixmap.scaled(534, 801) msg_box.setWindowTitle("WiiMote Controls - Info") msg_box.setIconPixmap(pixmap) msg_box.setDefaultButton(QtWidgets.QMessageBox.Ok) msg_box.show() msg_box.exec_() def eventFilter(self, source, event): """ Causes selection tables to lose focus (if they have the focus) and handles ctrl+z & ctrl+y """ if event.type() == QtGui.QMouseEvent.MouseButtonPress: self.mesh_select_table.lose_focus() self.texture_select_table.lose_focus() elif event.type() == QtGui.QKeyEvent.KeyRelease: if event.key() == 75: self.simulate_click() # z (undo if with ctrl) elif (source == self.win and event.key() == 90 and int(event.modifiers()) == QtCore.Qt.ControlModifier): self.request_undo() # y (redo if with ctrl) elif (source == self.win and event.key() == 89 and int(event.modifiers()) == QtCore.Qt.ControlModifier): self.redo() else: pass # print(event.key(), int(event.modifiers())) return super(Window, self).eventFilter(source, event)
class HostWindow(QMainWindow): # signals SIGTERM = pyqtSignal() SIGUSR1 = pyqtSignal() # -------------------------------------------------------------------------------------------------------- def __init__(self): QMainWindow.__init__(self) gCarla.gui = self URI = sys.argv[1] # ---------------------------------------------------------------------------------------------------- # Internal stuff self.fCurrentFrame = None self.fDocElemement = None self.fCanSetValues = False self.fNeedsShow = False self.fSizeSetup = False self.fQuitReceived = False self.fWasRepainted = False self.fPlugin = get_plugin_info(URI) self.fPorts = self.fPlugin['ports'] self.fPortSymbols = {} self.fPortValues = {} for port in self.fPorts['control']['input']: self.fPortSymbols[port['index']] = (port['symbol'], False) self.fPortValues[port['index']] = port['ranges']['default'] for port in self.fPorts['control']['output']: self.fPortSymbols[port['index']] = (port['symbol'], True) self.fPortValues[port['index']] = port['ranges']['default'] # ---------------------------------------------------------------------------------------------------- # Init pipe if len(sys.argv) == 7: self.fPipeClient = gCarla.utils.pipe_client_new( lambda s, msg: self.msgCallback(msg)) else: self.fPipeClient = None # ---------------------------------------------------------------------------------------------------- # Init Web server self.fWebServerThread = WebServerThread(self) self.fWebServerThread.start() # ---------------------------------------------------------------------------------------------------- # Set up GUI self.setContentsMargins(0, 0, 0, 0) self.fWebview = QWebView(self) #self.fWebview.setAttribute(Qt.WA_OpaquePaintEvent, False) #self.fWebview.setAttribute(Qt.WA_TranslucentBackground, True) self.setCentralWidget(self.fWebview) page = self.fWebview.page() page.setViewportSize(QSize(980, 600)) mainFrame = page.mainFrame() mainFrame.setScrollBarPolicy(Qt.Horizontal, Qt.ScrollBarAlwaysOff) mainFrame.setScrollBarPolicy(Qt.Vertical, Qt.ScrollBarAlwaysOff) palette = self.fWebview.palette() palette.setBrush(QPalette.Base, palette.brush(QPalette.Window)) page.setPalette(palette) self.fWebview.setPalette(palette) settings = self.fWebview.settings() settings.setAttribute(QWebSettings.DeveloperExtrasEnabled, True) self.fWebview.loadFinished.connect(self.slot_webviewLoadFinished) url = "http://127.0.0.1:%s/icon.html#%s" % (PORT, URI) print("url:", url) self.fWebview.load(QUrl(url)) # ---------------------------------------------------------------------------------------------------- # Connect actions to functions self.SIGTERM.connect(self.slot_handleSIGTERM) # ---------------------------------------------------------------------------------------------------- # Final setup self.fIdleTimer = self.startTimer(30) if self.fPipeClient is None: # testing, show UI only self.setWindowTitle("TestUI") self.fNeedsShow = True # -------------------------------------------------------------------------------------------------------- def closeExternalUI(self): self.fWebServerThread.stopWait() if self.fPipeClient is None: return if not self.fQuitReceived: self.send(["exiting"]) gCarla.utils.pipe_client_destroy(self.fPipeClient) self.fPipeClient = None def idleStuff(self): if self.fPipeClient is not None: gCarla.utils.pipe_client_idle(self.fPipeClient) self.checkForRepaintChanges() if self.fSizeSetup: return if self.fDocElemement is None or self.fDocElemement.isNull(): return pedal = self.fDocElemement.findFirst(".mod-pedal") if pedal.isNull(): return size = pedal.geometry().size() if size.width() <= 10 or size.height() <= 10: return # render web frame to image image = QImage(self.fWebview.page().viewportSize(), QImage.Format_ARGB32_Premultiplied) image.fill(Qt.transparent) painter = QPainter(image) self.fCurrentFrame.render(painter) painter.end() #image.save("/tmp/test.png") # get coordinates and size from image #x = -1 #y = -1 #lastx = -1 #lasty = -1 #bgcol = self.fHostColor.rgba() #for h in range(0, image.height()): #hasNonTransPixels = False #for w in range(0, image.width()): #if image.pixel(w, h) not in (0, bgcol): # 0xff070707): #hasNonTransPixels = True #if x == -1 or x > w: #x = w #lastx = max(lastx, w) #if hasNonTransPixels: ##if y == -1: ##y = h #lasty = h # set size and position accordingly #if -1 not in (x, lastx, lasty): #self.setFixedSize(lastx-x, lasty) #self.fCurrentFrame.setScrollPosition(QPoint(x, 0)) #else: # TODO that^ needs work if True: self.setFixedSize(size) # set initial values self.fCurrentFrame.evaluateJavaScript( "icongui.setPortValue(':bypass', 0, null)") for index in self.fPortValues.keys(): symbol, isOutput = self.fPortSymbols[index] value = self.fPortValues[index] if isOutput: self.fCurrentFrame.evaluateJavaScript( "icongui.setOutputPortValue('%s', %f)" % (symbol, value)) else: self.fCurrentFrame.evaluateJavaScript( "icongui.setPortValue('%s', %f, null)" % (symbol, value)) # final setup self.fCanSetValues = True self.fSizeSetup = True self.fDocElemement = None if self.fNeedsShow: self.show() def checkForRepaintChanges(self): if not self.fWasRepainted: return self.fWasRepainted = False if not self.fCanSetValues: return for index in self.fPortValues.keys(): symbol, isOutput = self.fPortSymbols[index] if isOutput: continue oldValue = self.fPortValues[index] newValue = self.fCurrentFrame.evaluateJavaScript( "icongui.getPortValue('%s')" % (symbol, )) if oldValue != newValue: self.fPortValues[index] = newValue self.send(["control", index, newValue]) # -------------------------------------------------------------------------------------------------------- @pyqtSlot(bool) def slot_webviewLoadFinished(self, ok): page = self.fWebview.page() page.repaintRequested.connect(self.slot_repaintRequested) self.fCurrentFrame = page.currentFrame() self.fDocElemement = self.fCurrentFrame.documentElement() def slot_repaintRequested(self): if self.fCanSetValues: self.fWasRepainted = True # -------------------------------------------------------------------------------------------------------- # Callback def msgCallback(self, msg): msg = charPtrToString(msg) if msg == "control": index = int(self.readlineblock()) value = float(self.readlineblock()) self.dspParameterChanged(index, value) elif msg == "program": index = int(self.readlineblock()) self.dspProgramChanged(index) elif msg == "midiprogram": bank = int(self.readlineblock()) program = float(self.readlineblock()) self.dspMidiProgramChanged(bank, program) elif msg == "configure": key = self.readlineblock() value = self.readlineblock() self.dspStateChanged(key, value) elif msg == "note": onOff = bool(self.readlineblock() == "true") channel = int(self.readlineblock()) note = int(self.readlineblock()) velocity = int(self.readlineblock()) self.dspNoteReceived(onOff, channel, note, velocity) elif msg == "atom": index = int(self.readlineblock()) size = int(self.readlineblock()) base64atom = self.readlineblock() # nothing to do yet elif msg == "urid": urid = int(self.readlineblock()) uri = self.readlineblock() # nothing to do yet elif msg == "uiOptions": sampleRate = float(self.readlineblock()) useTheme = bool(self.readlineblock() == "true") useThemeColors = bool(self.readlineblock() == "true") windowTitle = self.readlineblock() transWindowId = int(self.readlineblock()) self.uiTitleChanged(windowTitle) elif msg == "show": self.uiShow() elif msg == "focus": self.uiFocus() elif msg == "hide": self.uiHide() elif msg == "quit": self.fQuitReceived = True self.uiQuit() elif msg == "uiTitle": uiTitle = self.readlineblock() self.uiTitleChanged(uiTitle) else: print("unknown message: \"" + msg + "\"") # -------------------------------------------------------------------------------------------------------- def dspParameterChanged(self, index, value): self.fPortValues[index] = value if self.fCurrentFrame is not None and self.fCanSetValues: symbol, isOutput = self.fPortSymbols[index] if isOutput: self.fPortValues[index] = value self.fCurrentFrame.evaluateJavaScript( "icongui.setOutputPortValue('%s', %f)" % (symbol, value)) else: self.fCurrentFrame.evaluateJavaScript( "icongui.setPortValue('%s', %f, null)" % (symbol, value)) def dspProgramChanged(self, index): return def dspMidiProgramChanged(self, bank, program): return def dspStateChanged(self, key, value): return def dspNoteReceived(self, onOff, channel, note, velocity): return # -------------------------------------------------------------------------------------------------------- def uiShow(self): if self.fSizeSetup: self.show() else: self.fNeedsShow = True def uiFocus(self): if not self.fSizeSetup: return self.setWindowState((self.windowState() & ~Qt.WindowMinimized) | Qt.WindowActive) self.show() self.raise_() self.activateWindow() def uiHide(self): self.hide() def uiQuit(self): self.closeExternalUI() self.close() app.quit() def uiTitleChanged(self, uiTitle): self.setWindowTitle(uiTitle) # -------------------------------------------------------------------------------------------------------- # Qt events def closeEvent(self, event): self.closeExternalUI() QMainWindow.closeEvent(self, event) # there might be other qt windows open which will block carla-modgui from quitting app.quit() def timerEvent(self, event): if event.timerId() == self.fIdleTimer: self.idleStuff() QMainWindow.timerEvent(self, event) # -------------------------------------------------------------------------------------------------------- @pyqtSlot() def slot_handleSIGTERM(self): print("Got SIGTERM -> Closing now") self.close() # -------------------------------------------------------------------------------------------------------- # Internal stuff def readlineblock(self): if self.fPipeClient is None: return "" return gCarla.utils.pipe_client_readlineblock(self.fPipeClient, 5000) def send(self, lines): if self.fPipeClient is None or len(lines) == 0: return gCarla.utils.pipe_client_lock(self.fPipeClient) # this must never fail, we need to unlock at the end try: for line in lines: if line is None: line2 = "(null)" elif isinstance(line, str): line2 = line.replace("\n", "\r") elif isinstance(line, bool): line2 = "true" if line else "false" elif isinstance(line, int): line2 = "%i" % line elif isinstance(line, float): line2 = "%.10f" % line else: print("unknown data type to send:", type(line)) return gCarla.utils.pipe_client_write_msg(self.fPipeClient, line2 + "\n") except: pass gCarla.utils.pipe_client_flush_and_unlock(self.fPipeClient)
# Copyright 2014-2015 Florian Bruhin (The Compiler) <*****@*****.**> # # This file is part of qutebrowser. # # qutebrowser is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # qutebrowser is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with qutebrowser. If not, see <http://www.gnu.org/licenses/>. """Very simple browser for testing purposes.""" import sys from PyQt5.QtCore import QUrl from PyQt5.QtWidgets import QApplication from PyQt5.QtWebKitWidgets import QWebView app = QApplication(sys.argv) wv = QWebView() wv.load(QUrl(sys.argv[1])) wv.show() app.exec_()
class Browser(QWidget): """LilyPond documentation browser widget.""" def __init__(self, dockwidget): super(Browser, self).__init__(dockwidget) layout = QVBoxLayout(spacing=0) layout.setContentsMargins(0, 0, 0, 0) self.setLayout(layout) self.toolbar = tb = QToolBar() self.webview = QWebView(contextMenuPolicy=Qt.CustomContextMenu) self.chooser = QComboBox(sizeAdjustPolicy=QComboBox.AdjustToContents) self.search = SearchEntry(maximumWidth=200) layout.addWidget(self.toolbar) layout.addWidget(self.webview) ac = dockwidget.actionCollection ac.help_back.triggered.connect(self.webview.back) ac.help_forward.triggered.connect(self.webview.forward) ac.help_home.triggered.connect(self.showHomePage) ac.help_print.triggered.connect(self.slotPrint) self.webview.page().setNetworkAccessManager(lilydoc.network.accessmanager()) self.webview.page().setLinkDelegationPolicy(QWebPage.DelegateAllLinks) self.webview.page().linkClicked.connect(self.openUrl) self.webview.page().setForwardUnsupportedContent(True) self.webview.page().unsupportedContent.connect(self.slotUnsupported) self.webview.urlChanged.connect(self.slotUrlChanged) self.webview.customContextMenuRequested.connect(self.slotShowContextMenu) tb.addAction(ac.help_back) tb.addAction(ac.help_forward) tb.addSeparator() tb.addAction(ac.help_home) tb.addAction(ac.help_print) tb.addSeparator() tb.addWidget(self.chooser) tb.addWidget(self.search) self.chooser.activated[int].connect(self.showHomePage) self.search.textEdited.connect(self.slotSearchChanged) self.search.returnPressed.connect(self.slotSearchReturnPressed) dockwidget.mainwindow().iconSizeChanged.connect(self.updateToolBarSettings) dockwidget.mainwindow().toolButtonStyleChanged.connect(self.updateToolBarSettings) app.settingsChanged.connect(self.readSettings) self.readSettings() self.loadDocumentation() self.showInitialPage() app.settingsChanged.connect(self.loadDocumentation) app.translateUI(self) def readSettings(self): s = QSettings() s.beginGroup("documentation") ws = self.webview.page().settings() family = s.value("fontfamily", self.font().family(), str) size = s.value("fontsize", 16, int) ws.setFontFamily(QWebSettings.StandardFont, family) ws.setFontSize(QWebSettings.DefaultFontSize, size) fixed = textformats.formatData('editor').font ws.setFontFamily(QWebSettings.FixedFont, fixed.family()) ws.setFontSize(QWebSettings.DefaultFixedFontSize, fixed.pointSizeF() * 96 / 72) def keyPressEvent(self, ev): if ev.text() == "/": self.search.setFocus() else: super(Browser, self).keyPressEvent(ev) def translateUI(self): try: self.search.setPlaceholderText(_("Search...")) except AttributeError: pass # not in Qt 4.6 def showInitialPage(self): """Shows the preferred start page. If a local documentation instance already has a suitable version, just loads it. Otherwise connects to the allLoaded signal, that is emitted when all the documentation instances have loaded their version information and then shows the start page (if another page wasn't yet loaded). """ if self.webview.url().isEmpty(): docs = lilydoc.manager.docs() version = lilypondinfo.preferred().version() index = -1 if version: for num, doc in enumerate(docs): if doc.version() is not None and doc.version() >= version: index = num # a suitable documentation is found break if index == -1: # nothing found (or LilyPond version not available), # wait for loading or show the most recent version if not lilydoc.manager.loaded(): lilydoc.manager.allLoaded.connect(self.showInitialPage) return index = len(docs) - 1 self.chooser.setCurrentIndex(index) self.showHomePage() def loadDocumentation(self): """Puts the available documentation instances in the combobox.""" i = self.chooser.currentIndex() self.chooser.clear() for doc in lilydoc.manager.docs(): v = doc.versionString() if doc.isLocal(): t = _("(local)") else: t = _("({hostname})").format(hostname=doc.url().host()) self.chooser.addItem("{0} {1}".format(v or _("<unknown>"), t)) self.chooser.setCurrentIndex(i) if not lilydoc.manager.loaded(): lilydoc.manager.allLoaded.connect(self.loadDocumentation, -1) return def updateToolBarSettings(self): mainwin = self.parentWidget().mainwindow() self.toolbar.setIconSize(mainwin.iconSize()) self.toolbar.setToolButtonStyle(mainwin.toolButtonStyle()) def showManual(self): """Invoked when the user presses F1.""" self.slotHomeFrescobaldi() # TEMP def slotUrlChanged(self): ac = self.parentWidget().actionCollection ac.help_back.setEnabled(self.webview.history().canGoBack()) ac.help_forward.setEnabled(self.webview.history().canGoForward()) def openUrl(self, url): if url.path().endswith(('.ily', '.lyi', '.ly')): self.sourceViewer().showReply(lilydoc.network.get(url)) else: self.webview.load(url) def slotUnsupported(self, reply): helpers.openUrl(reply.url()) def slotSearchChanged(self): text = self.search.text() if not text.startswith(':'): self.webview.page().findText(text, QWebPage.FindWrapsAroundDocument) def slotSearchReturnPressed(self): text = self.search.text() if not text.startswith(':'): self.slotSearchChanged() else: pass # TODO: implement full doc search def sourceViewer(self): try: return self._sourceviewer except AttributeError: from . import sourceviewer self._sourceviewer = sourceviewer.SourceViewer(self) return self._sourceviewer def showHomePage(self): """Shows the homepage of the LilyPond documentation.""" i = self.chooser.currentIndex() if i < 0: i = 0 doc = lilydoc.manager.docs()[i] url = doc.home() if doc.isLocal(): path = url.toLocalFile() langs = lilydoc.network.langs() if langs: for lang in langs: if os.path.exists(path + '.' + lang + '.html'): path += '.' + lang break url = QUrl.fromLocalFile(path + '.html') self.webview.load(url) def slotPrint(self): printer = QPrinter() dlg = QPrintDialog(printer, self) dlg.setWindowTitle(app.caption(_("Print"))) if dlg.exec_(): self.webview.print_(printer) def slotShowContextMenu(self, pos): hit = self.webview.page().currentFrame().hitTestContent(pos) menu = QMenu() if hit.linkUrl().isValid(): a = self.webview.pageAction(QWebPage.CopyLinkToClipboard) a.setIcon(icons.get("edit-copy")) a.setText(_("Copy &Link")) menu.addAction(a) menu.addSeparator() a = menu.addAction(icons.get("window-new"), _("Open Link in &New Window")) a.triggered.connect((lambda url: lambda: self.slotNewWindow(url))(hit.linkUrl())) else: if hit.isContentSelected(): a = self.webview.pageAction(QWebPage.Copy) a.setIcon(icons.get("edit-copy")) a.setText(_("&Copy")) menu.addAction(a) menu.addSeparator() a = menu.addAction(icons.get("window-new"), _("Open Document in &New Window")) a.triggered.connect((lambda url: lambda: self.slotNewWindow(url))(self.webview.url())) if menu.actions(): menu.exec_(self.webview.mapToGlobal(pos)) def slotNewWindow(self, url): helpers.openUrl(url)
class Main(QtWidgets.QMainWindow): def __init__(self, parent=None): QtWidgets.QMainWindow.__init__(self, parent) self.initUI() def initUI(self): self.setGeometry(100, 100, 1400, 800) self.setWindowTitle("一个属于你的简单基因信息查询软件") #self.resize(800,500) self.tabs = QtWidgets.QTabWidget() self.webtab01 = QWebView() self.webtab01.setObjectName("GeneCard") self.webtab01.load(QtCore.QUrl("https://www.genecards.org/")) self.webtab02 = QWebView() self.webtab02.setObjectName("Web-Wiki") self.webtab02.load(QtCore.QUrl("https://en.wikipedia.org/wiki/")) self.webtab03 = QWebView() self.webtab03.setObjectName("ncbi") self.webtab03.load(QtCore.QUrl("https://www.ncbi.nlm.nih.gov/gene/")) self.webtab04 = QWebView() self.webtab04.setObjectName("COSMIC") self.webtab04.load(QtCore.QUrl("https://cancer.sanger.ac.uk/cosmic/")) self.webtab05 = QWebView() self.webtab05.setObjectName("Human Protein Altas") self.webtab05.load(QtCore.QUrl("https://www.proteinatlas.org/")) # self.webtab06 = QWebView() self.webtab06.setObjectName("Genetics Home Ref") self.webtab06.load(QtCore.QUrl("https://ghr.nlm.nih.gov/gene/")) # self.webtab07 = QWebView() self.webtab07.setObjectName("OMIM") self.webtab07.load(QtCore.QUrl("https://omim.org/")) # #self.webtab12 = QWebView() #self.webtab12.setObjectName("Web-谷歌") #self.webtab12.load(QtCore.QUrl("https://translate.google.cn/")) # #self.webtab13 = QWebView() #self.webtab13.setObjectName("Web-百度") #self.webtab13.load(QtCore.QUrl("http://fanyi.baidu.com/")) # #self.webtab15 = QWebView() #self.webtab15.setObjectName("Web-OzDict") #self.webtab15.load(QtCore.QUrl("https://www.onelook.com/")) self.tabs.addTab(self.webtab02, "WIKI") self.tabs.addTab(self.webtab06, "GeneticsHome") self.tabs.addTab(self.webtab03, "NCBI") self.tabs.addTab(self.webtab01, "GeneCard") self.tabs.addTab(self.webtab04, "COSMIC") self.tabs.addTab(self.webtab05, "HPA") self.tabs.addTab(self.webtab07, "OMIM") #self.tabs.addTab(self.webtab14, "维基百科") #self.tabs.addTab(self.webtab05, "UrbanDict") #self.tabs.addTab(self.webtab06, "Webster") #self.tabs.addTab(self.webtab15, "OneLook") self.inputLine = QtWidgets.QLineEdit() self.inputLine.setObjectName("inputLineEditor") self.inputLine.setFixedHeight(30) self.inputLine.textChanged.connect(self.translateText) self.outputLine = QtWidgets.QLineEdit() self.outputLine.setObjectName("outputLineEditor") self.outputLine.setFixedHeight(20) self.webtab01.loadStarted.connect(self.initURL) #self.outputLine.setText(self.webtab01.url().toString()) 还不太会搞这个的初始化 self.tabs.currentChanged.connect(self.getURL) #self.outputLine..connect(self.getURL) centralLayout = QtWidgets.QVBoxLayout() centralLayout.addWidget(self.inputLine, 1) centralLayout.addWidget(self.outputLine, 2) centralLayout.addWidget(self.tabs, 3) self.centralWidget = QtWidgets.QWidget() self.centralWidget.setLayout(centralLayout) self.setCentralWidget(self.centralWidget) def getURL(self): self.outputLine.setText(self.tabs.currentWidget().url().toString()) #print(self.tabs.currentIndex()) def initURL(self): self.outputLine.setText(self.webtab01.url().toString()) def translateText(self): word = self.inputLine.text() #print(word) self.webtab01.load( QtCore.QUrl( "https://www.genecards.org/Search/Keyword?queryString=" + word)) #self.webtab01.load(QtCore.QUrl("http://www.iciba.com/" + word)) self.webtab02.load(QtCore.QUrl("https://en.wikipedia.org/wiki/" + word)) self.webtab03.load( QtCore.QUrl("https://www.ncbi.nlm.nih.gov/gene/?term=" + word)) self.webtab04.load( QtCore.QUrl("https://cancer.sanger.ac.uk/cosmic/search?q=" + word)) self.webtab05.load( QtCore.QUrl("https://www.proteinatlas.org/search/" + word)) self.webtab06.load(QtCore.QUrl("https://ghr.nlm.nih.gov/gene/" + word)) self.webtab07.load( QtCore.QUrl( "https://omim.org/search/?index=entry&sort=score+desc%2C+prefix_sort+desc&start=1&limit=10&search=" + word))
from PyQt5.QtWidgets import QApplication from PyQt5.QtWebKitWidgets import QWebView from PyQt5.QtCore import QUrl app = QApplication([]) view = QWebView() view.load(QUrl("http://weixin.sogou.com")) view.show() app.exec_() # import sys # from PyQt5.QtWidgets import * # from PyQt5.QtCore import * # from PyQt5.QtWebKitWidgets import QWebPage # from lxml import etree # from lxml.etree import XPathEvalError # # #use QtWebkit to get the final webpage # app = QApplication([]) # # # def create_tree(html): # ''' # :param html:HTML或者XML字符串 # :return:tree树形结构对象 # ''' # try: # root = etree.HTML(html) # except ValueError: # root = etree.XML(html)
class BrowserController(QObject): def __init__(self, parent, fullscreen, sizes): """ This constructor function initializes a layout of the Arius output module but doesn`t displays it on the screen. Firstly, it creates a new app and detects screens configuration. Then, QGridLayout is created and its appearance is configured: margins and spaces between elements is set to 0. After that, we create three QWebViews: one for our main content view and two others for header and footer views. Immideately after creating these instances we assign them their heights according to percentages given by user and dimensions of the screen. Next, we remove scroll bars from top and bottom views and load predefined pages into them. Then we allow QWebKit to run all extensions it needs to render the page. After that, wee set the layout design as a grid of three rows. Finally, we create an updater object which will run in another stream and a timer instance which checks that stream for new commands from the server, and in case if there`s some update handles it. """ super(BrowserController, self).__init__() if not fullscreen and not sizes: print 'You must initialize windows size' raise Exception self._fullscreen = fullscreen if sizes: self._screen_width = sizes[0] self._screen_height = sizes[1] self._app = QtWidgets.QApplication(sys.argv) self._app.setStyle("Fusion") if self._fullscreen: self._get_screen_height() self._layout = QGridLayout() self._layout.setSpacing(0) self._layout.setContentsMargins(0, 0, 0, 0) self._main_browser = QWebView() main_page = FakePage(self) self._main_browser.setPage(main_page) self._top_browser = QWebView() self._bottom_browser = QWebView() self._top_browser_height = config[ 'output_header_height'] * self._screen_height self._bottom_browser_height = config[ 'output_footer_height'] * self._screen_height self._top_browser.setMaximumHeight(self._top_browser_height) self._top_browser.setMinimumHeight(self._top_browser_height) self._bottom_browser.setMaximumHeight(self._bottom_browser_height) self._bottom_browser.setMinimumHeight(self._bottom_browser_height) self._top_browser.page().mainFrame().setScrollBarPolicy( QtCore.Qt.Vertical, QtCore.Qt.ScrollBarAlwaysOff) self._main_browser.page().mainFrame().setScrollBarPolicy( QtCore.Qt.Vertical, QtCore.Qt.ScrollBarAlwaysOff) self._main_browser.page().mainFrame().setScrollBarPolicy( QtCore.Qt.Horizontal, QtCore.Qt.ScrollBarAlwaysOff) self._bottom_browser.page().mainFrame().setScrollBarPolicy( QtCore.Qt.Vertical, QtCore.Qt.ScrollBarAlwaysOff) self._top_browser_load_url(config['flask_server_home'] + config['output_browser_top_page']) self._bottom_browser_load_url(config['flask_server_home'] + config['output_browser_bottom_page']) self._main_browser.settings().setAttribute( QWebSettings.DeveloperExtrasEnabled, True) self._main_browser.settings().setAttribute(QWebSettings.PluginsEnabled, True) QWebSettings.setObjectCacheCapacities(0, 0, 0) self._main_browser.settings().setAttribute( QWebSettings.AcceleratedCompositingEnabled, True) self._main_browser.settings().setAttribute(QWebSettings.WebGLEnabled, True) self._layout.addWidget(self._top_browser, 1, 0) self._layout.addWidget(self._main_browser, 2, 0) self._layout.addWidget(self._bottom_browser, 3, 0) self._parent = parent self._parent.load_url.connect(self._load_url) self._parent.js_execution.connect(self._execute_js) self._parent.zooming.connect(self._zoom) def run(self): self._main_window = QWidget() # create a window as a QWidget self._main_window.setLayout(self._layout) # assign a layout to it if self._fullscreen: self._main_window.showFullScreen() # set full screen enabled else: self._main_window.resize(self._screen_width, self._screen_height) self._main_window.show() # and finally, show the window # and set a trigger to exit the app, as window is closed sys.exit(self._app.exec_()) def _get_screen_height(self): if self._fullscreen: output = subprocess.Popen('xrandr | grep "\*" | cut -d" " -f4', shell=True, stdout=subprocess.PIPE).communicate()[0] output = [int(val) for val in output.split('x')] self._screen_height = output[1] self._screen_width = output[0] else: output = [self._width, self._height] return output[0], output[1] def _zoom(self, factor): self._main_browser.page().mainFrame().setZoomFactor(factor) def _top_browser_load_url(self, url): self._top_browser.load(QUrl(url)) def _bottom_browser_load_url(self, url): self._bottom_browser.load(QUrl(url)) def _load_url(self, url): logger.debug('OPENING URL %s', url) self._main_browser.load(QUrl(url)) def _execute_js(self, string_js): logger.debug(string_js) self._main_browser.page().mainFrame().evaluateJavaScript(string_js)
class MainWindow(QMainWindow): def __init__(self, url): super(MainWindow, self).__init__() self.progress = 0 fd = QFile(":/jquery.min.js") if fd.open(QIODevice.ReadOnly | QFile.Text): self.jQuery = QTextStream(fd).readAll() fd.close() else: self.jQuery = '' QNetworkProxyFactory.setUseSystemConfiguration(True) self.view = QWebView(self) self.view.load(url) self.view.loadFinished.connect(self.adjustLocation) self.view.titleChanged.connect(self.adjustTitle) self.view.loadProgress.connect(self.setProgress) self.view.loadFinished.connect(self.finishLoading) self.locationEdit = QLineEdit(self) self.locationEdit.setSizePolicy( QSizePolicy.Expanding, self.locationEdit.sizePolicy().verticalPolicy()) self.locationEdit.returnPressed.connect(self.changeLocation) toolBar = self.addToolBar("Navigation") toolBar.addAction(self.view.pageAction(QWebPage.Back)) toolBar.addAction(self.view.pageAction(QWebPage.Forward)) toolBar.addAction(self.view.pageAction(QWebPage.Reload)) toolBar.addAction(self.view.pageAction(QWebPage.Stop)) toolBar.addWidget(self.locationEdit) viewMenu = self.menuBar().addMenu("&View") viewSourceAction = QAction("Page Source", self) viewSourceAction.triggered.connect(self.viewSource) viewMenu.addAction(viewSourceAction) effectMenu = self.menuBar().addMenu("&Effect") effectMenu.addAction("Highlight all links", self.highlightAllLinks) self.rotateAction = QAction(self.style().standardIcon( QStyle.SP_FileDialogDetailedView), "Turn images upside down", self, checkable=True, toggled=self.rotateImages) effectMenu.addAction(self.rotateAction) toolsMenu = self.menuBar().addMenu("&Tools") toolsMenu.addAction("Remove GIF images", self.removeGifImages) toolsMenu.addAction("Remove all inline frames", self.removeInlineFrames) toolsMenu.addAction("Remove all object elements", self.removeObjectElements) toolsMenu.addAction("Remove all embedded elements", self.removeEmbeddedElements) self.setCentralWidget(self.view) def viewSource(self): accessManager = self.view.page().networkAccessManager() request = QNetworkRequest(self.view.url()) reply = accessManager.get(request) reply.finished.connect(self.slotSourceDownloaded) def slotSourceDownloaded(self): reply = self.sender() self.textEdit = QTextEdit() self.textEdit.setAttribute(Qt.WA_DeleteOnClose) self.textEdit.show() self.textEdit.setPlainText(QTextStream(reply).readAll()) self.textEdit.resize(600, 400) reply.deleteLater() def adjustLocation(self): self.locationEdit.setText(self.view.url().toString()) def changeLocation(self): url = QUrl.fromUserInput(self.locationEdit.text()) self.view.load(url) self.view.setFocus() def adjustTitle(self): if 0 < self.progress < 100: self.setWindowTitle("%s (%s%%)" % (self.view.title(), self.progress)) else: self.setWindowTitle(self.view.title()) def setProgress(self, p): self.progress = p self.adjustTitle() def finishLoading(self): self.progress = 100 self.adjustTitle() self.view.page().mainFrame().evaluateJavaScript(self.jQuery) self.rotateImages(self.rotateAction.isChecked()) def highlightAllLinks(self): code = """$('a').each( function () { $(this).css('background-color', 'yellow') } )""" self.view.page().mainFrame().evaluateJavaScript(code) def rotateImages(self, invert): if invert: code = """ $('img').each( function () { $(this).css('-webkit-transition', '-webkit-transform 2s'); $(this).css('-webkit-transform', 'rotate(180deg)') } )""" else: code = """ $('img').each( function () { $(this).css('-webkit-transition', '-webkit-transform 2s'); $(this).css('-webkit-transform', 'rotate(0deg)') } )""" self.view.page().mainFrame().evaluateJavaScript(code) def removeGifImages(self): code = "$('[src*=gif]').remove()" self.view.page().mainFrame().evaluateJavaScript(code) def removeInlineFrames(self): code = "$('iframe').remove()" self.view.page().mainFrame().evaluateJavaScript(code) def removeObjectElements(self): code = "$('object').remove()" self.view.page().mainFrame().evaluateJavaScript(code) def removeEmbeddedElements(self): code = "$('embed').remove()" self.view.page().mainFrame().evaluateJavaScript(code)
class Browser(QWidget): """LilyPond documentation browser widget.""" def __init__(self, dockwidget): super(Browser, self).__init__(dockwidget) layout = QVBoxLayout(spacing=0) layout.setContentsMargins(0, 0, 0, 0) self.setLayout(layout) self.toolbar = tb = QToolBar() self.webview = QWebView(contextMenuPolicy=Qt.CustomContextMenu) self.chooser = QComboBox(sizeAdjustPolicy=QComboBox.AdjustToContents) self.search = SearchEntry(maximumWidth=200) layout.addWidget(self.toolbar) layout.addWidget(self.webview) ac = dockwidget.actionCollection ac.help_back.triggered.connect(self.webview.back) ac.help_forward.triggered.connect(self.webview.forward) ac.help_home.triggered.connect(self.showHomePage) ac.help_print.triggered.connect(self.slotPrint) self.webview.page().setNetworkAccessManager( lilydoc.network.accessmanager()) self.webview.page().setLinkDelegationPolicy(QWebPage.DelegateAllLinks) self.webview.page().linkClicked.connect(self.openUrl) self.webview.page().setForwardUnsupportedContent(True) self.webview.page().unsupportedContent.connect(self.slotUnsupported) self.webview.urlChanged.connect(self.slotUrlChanged) self.webview.customContextMenuRequested.connect( self.slotShowContextMenu) tb.addAction(ac.help_back) tb.addAction(ac.help_forward) tb.addSeparator() tb.addAction(ac.help_home) tb.addAction(ac.help_print) tb.addSeparator() tb.addWidget(self.chooser) tb.addWidget(self.search) self.chooser.activated[int].connect(self.showHomePage) self.search.textEdited.connect(self.slotSearchChanged) self.search.returnPressed.connect(self.slotSearchReturnPressed) dockwidget.mainwindow().iconSizeChanged.connect( self.updateToolBarSettings) dockwidget.mainwindow().toolButtonStyleChanged.connect( self.updateToolBarSettings) app.settingsChanged.connect(self.readSettings) self.readSettings() self.loadDocumentation() self.showInitialPage() app.settingsChanged.connect(self.loadDocumentation) app.translateUI(self) def readSettings(self): s = QSettings() s.beginGroup("documentation") ws = self.webview.page().settings() family = s.value("fontfamily", self.font().family(), str) size = s.value("fontsize", 16, int) ws.setFontFamily(QWebSettings.StandardFont, family) ws.setFontSize(QWebSettings.DefaultFontSize, size) fixed = textformats.formatData('editor').font ws.setFontFamily(QWebSettings.FixedFont, fixed.family()) ws.setFontSize(QWebSettings.DefaultFixedFontSize, fixed.pointSizeF() * 96 / 72) def keyPressEvent(self, ev): if ev.text() == "/": self.search.setFocus() else: super(Browser, self).keyPressEvent(ev) def translateUI(self): try: self.search.setPlaceholderText(_("Search...")) except AttributeError: pass # not in Qt 4.6 def showInitialPage(self): """Shows the preferred start page. If a local documentation instance already has a suitable version, just loads it. Otherwise connects to the allLoaded signal, that is emitted when all the documentation instances have loaded their version information and then shows the start page (if another page wasn't yet loaded). """ if self.webview.url().isEmpty(): docs = lilydoc.manager.docs() version = lilypondinfo.preferred().version() index = -1 if version: for num, doc in enumerate(docs): if doc.version() is not None and doc.version() >= version: index = num # a suitable documentation is found break if index == -1: # nothing found (or LilyPond version not available), # wait for loading or show the most recent version if not lilydoc.manager.loaded(): lilydoc.manager.allLoaded.connect(self.showInitialPage) return index = len(docs) - 1 self.chooser.setCurrentIndex(index) self.showHomePage() def loadDocumentation(self): """Puts the available documentation instances in the combobox.""" i = self.chooser.currentIndex() self.chooser.clear() for doc in lilydoc.manager.docs(): v = doc.versionString() if doc.isLocal(): t = _("(local)") else: t = _("({hostname})").format(hostname=doc.url().host()) self.chooser.addItem("{0} {1}".format(v or _("<unknown>"), t)) self.chooser.setCurrentIndex(i) if not lilydoc.manager.loaded(): lilydoc.manager.allLoaded.connect(self.loadDocumentation, -1) return def updateToolBarSettings(self): mainwin = self.parentWidget().mainwindow() self.toolbar.setIconSize(mainwin.iconSize()) self.toolbar.setToolButtonStyle(mainwin.toolButtonStyle()) def showManual(self): """Invoked when the user presses F1.""" self.slotHomeFrescobaldi() # TEMP def slotUrlChanged(self): ac = self.parentWidget().actionCollection ac.help_back.setEnabled(self.webview.history().canGoBack()) ac.help_forward.setEnabled(self.webview.history().canGoForward()) def openUrl(self, url): if url.path().endswith(('.ily', '.lyi', '.ly')): self.sourceViewer().showReply(lilydoc.network.get(url)) else: self.webview.load(url) def slotUnsupported(self, reply): helpers.openUrl(reply.url()) def slotSearchChanged(self): text = self.search.text() if not text.startswith(':'): self.webview.page().findText(text, QWebPage.FindWrapsAroundDocument) def slotSearchReturnPressed(self): text = self.search.text() if not text.startswith(':'): self.slotSearchChanged() else: pass # TODO: implement full doc search def sourceViewer(self): try: return self._sourceviewer except AttributeError: from . import sourceviewer self._sourceviewer = sourceviewer.SourceViewer(self) return self._sourceviewer def showHomePage(self): """Shows the homepage of the LilyPond documentation.""" i = self.chooser.currentIndex() if i < 0: i = 0 doc = lilydoc.manager.docs()[i] url = doc.home() if doc.isLocal(): path = url.toLocalFile() langs = lilydoc.network.langs() if langs: for lang in langs: if os.path.exists(path + '.' + lang + '.html'): path += '.' + lang break url = QUrl.fromLocalFile(path + '.html') self.webview.load(url) def slotPrint(self): printer = QPrinter() dlg = QPrintDialog(printer, self) dlg.setWindowTitle(app.caption(_("Print"))) if dlg.exec_(): self.webview.print_(printer) def slotShowContextMenu(self, pos): hit = self.webview.page().currentFrame().hitTestContent(pos) menu = QMenu() if hit.linkUrl().isValid(): a = self.webview.pageAction(QWebPage.CopyLinkToClipboard) a.setIcon(icons.get("edit-copy")) a.setText(_("Copy &Link")) menu.addAction(a) menu.addSeparator() a = menu.addAction(icons.get("window-new"), _("Open Link in &New Window")) a.triggered.connect( (lambda url: lambda: self.slotNewWindow(url))(hit.linkUrl())) else: if hit.isContentSelected(): a = self.webview.pageAction(QWebPage.Copy) a.setIcon(icons.get("edit-copy")) a.setText(_("&Copy")) menu.addAction(a) menu.addSeparator() a = menu.addAction(icons.get("window-new"), _("Open Document in &New Window")) a.triggered.connect((lambda url: lambda: self.slotNewWindow(url))( self.webview.url())) if menu.actions(): menu.exec_(self.webview.mapToGlobal(pos)) def slotNewWindow(self, url): helpers.openUrl(url)
class mainWindow(QMainWindow): """ Create main window """ def __init__(self): super(mainWindow, self).__init__() uic.loadUi('resources/ui/main.ui', self) self.mainPrintMessage("Welcome to pandapower version: " + pp.__version__ + "\nQt vesrion: " + QT_VERSION + "\nGUI version: " + _GUI_VERSION + "\n" + "\nNetwork variable stored in : net") self.embedIpythonInterpreter() # collections builder setup self.lastBusSelected = None self.embedCollectionsBuilder() self.load_pandapower_network(createSampleNetwork, "GUI Example Network") self.initialiseCollectionsPlot() self.ax.xaxis.set_visible(False) self.ax.yaxis.set_visible(False) self.ax.set_aspect('equal', 'datalim') self.ax.autoscale_view(True, True, True) self.collectionsDoubleClick = False self.tabWidget.setCurrentIndex(0) #set firtst tab # toolbar self.actionNew_Network.triggered.connect(self.mainEmptyClicked) self.actionLoad.triggered.connect(self.mainLoadClicked) self.actionSave.triggered.connect(self.mainSaveClicked) self.actionMV_oberrhein.triggered.connect( partial(self.load_pandapower_network, pnw.mv_oberrhein, "MV Oberrhein")) self.actionCase9.triggered.connect( partial(self.load_pandapower_network, pnw.case9, "IEEE Case 9")) self.actionAbout.triggered.connect(self.show_license) self.actionDocumentation.triggered.connect(self.show_docs) #main self.actionrunpp.triggered.connect(self.runpp) self.actionrunpp.setIcon(QIcon('resources/icons/runpp.png')) self.actionrunppOptions.triggered.connect(self.runpp_options) self.actionrunppOptions.setIcon( QIcon('resources/icons/runpp_options.png')) # inspect self.inspect_bus.clicked.connect( partial(self.show_element_table, "bus")) self.inspect_lines.clicked.connect( partial(self.show_element_table, "line")) self.inspect_load.clicked.connect( partial(self.show_element_table, "load")) self.inspect_switch.clicked.connect( partial(self.show_element_table, "switch")) self.inspect_sgen.clicked.connect( partial(self.show_element_table, "sgen")) self.inspect_ext_grid.clicked.connect( partial(self.show_element_table, "ext_grid")) self.inspect_trafo.clicked.connect( partial(self.show_element_table, "trafo")) self.inspect_trafo3w.clicked.connect( partial(self.show_element_table, "trafo3w")) self.inspect_gen.clicked.connect( partial(self.show_element_table, "gen")) self.inspect_shunt.clicked.connect( partial(self.show_element_table, "shunt")) self.inspect_impedance.clicked.connect( partial(self.show_element_table, "impedance")) self.inspect_ward.clicked.connect( partial(self.show_element_table, "ward")) self.inspect_xward.clicked.connect( partial(self.show_element_table, "xward")) self.inspect_dcline.clicked.connect( partial(self.show_element_table, "dcline")) self.inspect_measurement.clicked.connect( partial(self.show_element_table, "measurement")) # results self.res_bus.clicked.connect(partial(self.show_result_table, "res_bus")) self.res_lines.clicked.connect( partial(self.show_result_table, "res_line")) self.res_load.clicked.connect( partial(self.show_result_table, "res_load")) self.res_sgen.clicked.connect( partial(self.show_result_table, "res_sgen")) self.res_ext_grid.clicked.connect( partial(self.show_result_table, "res_ext_grid")) self.res_trafo.clicked.connect( partial(self.show_result_table, "res_trafo")) self.res_trafo3w.clicked.connect( partial(self.show_result_table, "res_trafo3w")) self.res_gen.clicked.connect(partial(self.show_result_table, "res_gen")) self.res_shunt.clicked.connect( partial(self.show_result_table, "res_shunt")) self.res_impedance.clicked.connect( partial(self.show_result_table, "res_sgen")) self.res_ward.clicked.connect( partial(self.show_result_table, "res_ward")) self.res_xward.clicked.connect( partial(self.show_result_table, "res_xward")) self.res_dcline.clicked.connect( partial(self.show_result_table, "res_dcline")) #interpreter self.runTests.clicked.connect(self.runPandapowerTests) self.show() def show_license(self): license_text = open("LICENSE", "r") self.license = QMessageBox() self.license.setIcon(QMessageBox.Information) self.license.setWindowTitle("pandapower GUI") self.license.setText(license_text.read()) self.license.show() def load_pandapower_network(self, network_function, name): net = network_function() self.load_network(net, name) def show_docs(self): self.docs = QWebView() self.docs.load(QUrl("https://pandapower.readthedocs.io")) self.docs.setWindowTitle("pandapower Documentation") self.docs.show() def printLineSeperator(self, ch="=", n=40): """ prints some characters """ return ch * n + "\n" def mainPrintMessage(self, message): #self.main_message.append(self.printLineSeperator()) self.main_message.append(message) self.main_message.append(self.printLineSeperator()) def embedIpythonInterpreter(self): """ embed an IPyton QT Console Interpreter """ self.ipyConsole = QIPythonWidget( customBanner="""Welcome to the console\nType \ whos to get list of variables \ \n =========== \n""") self.interpreter_vbox.addWidget(self.ipyConsole) self.ipyConsole.pushVariables({"pp": pp}) def mainEmptyClicked(self): net = pp.create_empty_network() self.load_network(net, "Empty Network") def mainLoadClicked(self): file_to_open = "" file_to_open = QFileDialog.getOpenFileName(filter="*.xlsx, *.p") if file_to_open[0] != "": fn = file_to_open[0] if fn.endswith(".xlsx"): try: net = pp.from_excel(file_to_open[0], convert=True) except: print("couldn't open %s" % fn) return elif file_to_open[0].endswith(".p"): try: net = pp.from_pickle(file_to_open[0], convert=True) except: print("couldn't open %s" % fn) return self.load_network(net) def load_network(self, net, name): self.net = net if not "_runpp_options" in self.net: self.net._runpp_options = dict() # self.ipyConsole.executeCommand("del(net)") #self.ipyConsole.clearTerminal() self.ipyConsole.printText("\n\n" + "-" * 40) self.ipyConsole.printText("\nNew net loaded \n") self.ipyConsole.printText("-" * 40 + "\n\n") self.ipyConsole.pushVariables({"net": self.net}) self.ipyConsole.executeCommand("net") self.initialiseCollectionsPlot() self.mainPrintMessage(name + " loaded") self.mainPrintMessage(str(self.net)) self.result_table.clear() self.element_table.clear() def mainSaveClicked(self): #filename = QFileDialog.getOpenFileName() filename = QFileDialog.getSaveFileName(self, 'Save net') print(filename[0]) try: pp.to_excel(self.net, filename[0]) self.mainPrintMessage("Saved case to: " + filename[0]) except: self.mainPrintMessage("Case not saved, maybe empty?") def runpp(self): try: pp.runpp(self.net, **self.net._runpp_options) self.mainPrintMessage(str(self.net)) except pp.LoadflowNotConverged: self.mainPrintMessage("Power Flow did not Converge!") except: self.mainPrintMessage("Error occured - empty network?") def runpp_options(self): try: runppOptions(self.net, parent=self) # self.options.show() except Exception as e: print(e) def lossesSummary(self): """ print the losses in each element that has losses """ # get total losses losses = 0.0 for i in self.net: if 'res' in i: if 'pl_kw' in self.net[i]: if not self.net[i]['pl_kw'].empty: print(i) # self.report_message.append(i) self.report_message.append(i) self.report_message.append( self.net[i]['pl_kw'].to_string()) print(self.net[i]['pl_kw']) losses += self.net[i]['pl_kw'].sum() self.report_message.append("Total Losses (kW)") self.report_message.append(str(losses)) # get total load total_load_kw = self.net.res_gen.sum() + self.net.res_sgen.sum() + \ self.net.res_ext_grid.sum() self.report_message.append("Total nett load flowing in network") self.report_message.append(str(total_load_kw['p_kw'])) # losses percentage self.report_message.append("% losses") loss_pct = losses / total_load_kw['p_kw'] self.report_message.append(str(abs(loss_pct * 100))) self.mainPrintMessage("Losses report generated. Check Report tab.") def show_element_table(self, element): self.show_table(element, self.element_table) def show_result_table(self, element): self.show_table(element, self.result_table) def show_table(self, element, table_widget): table = self.net[element] table_widget.setColumnCount(len(table.columns) + 1) table_widget.setRowCount(len(table)) header = ["index"] + table.columns.tolist() table_widget.setHorizontalHeaderLabels(header) for i, (idx, row) in enumerate(table.iterrows()): table_widget.setItem(i, 0, QTableWidgetItem(str(idx))) for k, value in enumerate(row.values, 1): print(i, k, value) table_widget.setItem(i, k, QTableWidgetItem(str(value))) table_widget.doubleClicked.connect( partial(self.table_doubleclicked, element, table_widget)) def table_doubleclicked(self, element, table_widget, cell): try: index = int(table_widget.item(cell.row(), 0).text()) self.open_element_window(element, index) except Exception as e: print(e) # res def res_bus_clicked(self): self.res_message.setHtml(str(self.net.res_bus.to_html())) def res_lines_clicked(self): self.res_message.setHtml(str(self.net.res_line.to_html())) # def res_switch_clicked(self): # self.res_message.setHtml(str(self.net.res_switch.to_html())) def res_load_clicked(self): self.res_message.setHtml(str(self.net.res_load.to_html())) def res_sgen_clicked(self): self.res_message.setHtml(str(self.net.res_sgen.to_html())) def res_ext_grid_clicked(self): self.res_message.setHtml(str(self.net.res_ext_grid.to_html())) def res_trafo_clicked(self): self.res_message.setHtml(str(self.net.res_trafo.to_html())) def res_trafo3w_clicked(self): self.res_message.setHtml(str(self.net.res_trafo3w.to_html())) def res_gen_clicked(self): self.res_message.setHtml(str(self.net.res_gen.to_html())) def res_shunt_clicked(self): self.res_message.setHtml(str(self.net.res_shunt.to_html())) def res_ward_clicked(self): self.res_message.setHtml(str(self.net.res_ward.to_html())) def res_xward_clicked(self): self.res_message.setHtml(str(self.net.res_xward.to_html())) def res_dcline_clicked(self): self.res_message.setHtml(str(self.net.res_dcline.to_html())) # interpreter def runPandapowerTests(self): self.ipyConsole.executeCommand("import pandapower.test as test") self.ipyConsole.executeCommand("print('Running tests ...')") #self.ipyConsole.executeCommand("test.run_all_tests()") # collections def initialiseCollectionsPlot(self): print("Inialise Collections") self.xmin = self.net.bus_geodata.x.min() self.xmax = self.net.bus_geodata.x.max() self.ymin = self.net.bus_geodata.y.min() self.ymax = self.net.bus_geodata.y.max() self.scale = max((self.xmax - self.xmin), (self.ymax - self.ymin)) self.collections = {} self.updateBusCollection() self.updateLineCollection() self.updateTrafoCollections() self.updateLoadCollections() self.updateExtGridCollections() print(self.collections) self.drawCollections() def drawCollections(self): self.ax.clear() for name, c in self.collections.items(): if c is not None: self.ax.add_collection(c) self.ax.set_xlim((self.xmin * 0.98, self.xmax * 1.02)) self.ax.set_ylim((self.ymin * 0.98, self.ymax * 1.02)) self.canvas.draw() print("Drew Collections") def updateBusCollection(self, redraw=False): bc = plot.create_bus_collection(self.net, size=self.scale * 0.01, zorder=2, picker=True, color="black", patch_type="rect", infofunc=lambda x: ("bus", x)) self.collections["bus"] = bc if redraw: self.drawCollections() def updateExtGridCollections(self, redraw=False): eg1, eg2 = plot.create_ext_grid_symbol_collection( self.net, size=self.scale * 0.05, zorder=2, picker=True, infofunc=lambda x: ("ext_grid", x)) self.collections["ext_grid1"] = eg1 self.collections["ext_grid2"] = eg2 if redraw: self.drawCollections() def updateLineCollection(self, redraw=False): lc = plot.create_line_collection(self.net, zorder=1, linewidths=1, picker=True, use_line_geodata=False, color="green", infofunc=lambda x: ("line", x)) self.collections["line"] = lc if redraw: self.drawCollections() def updateTrafoCollections(self, redraw=False): t1, t2 = plot.create_trafo_symbol_collection(self.net, picker=True, size=self.scale * 0.02, infofunc=lambda x: ("trafo", x)) self.collections["trafo1"] = t1 self.collections["trafo2"] = t2 if redraw: self.drawCollections() def updateLoadCollections(self, redraw=False): l1, l2 = plot.create_load_symbol_collection(self.net, size=self.scale * 0.02, picker=True, infofunc=lambda x: ("load", x)) self.collections["load1"] = l1 self.collections["load2"] = l2 if redraw: self.drawCollections() def clearMainCollectionBuilder(self): self.ax.clear() print("figure cleared") self.collections = {} self.drawCollections() def embedCollectionsBuilder(self): self.dpi = 100 self.fig = plt.Figure() self.canvas = FigureCanvas(self.fig) self.ax = self.fig.add_subplot(111) # self.ax.set_axis_bgcolor("white") # when a button is pressed on the canvas? self.canvas.mpl_connect('button_press_event', self.onCollectionsClick) #self.canvas.mpl_connect('button_release_event', self.onCollectionsClick) self.canvas.mpl_connect('pick_event', self.onCollectionsPick) mpl_toolbar = NavigationToolbar(self.canvas, self.main_build_frame) self.gridLayout.addWidget(self.canvas) self.gridLayout.addWidget(mpl_toolbar) self.fig.subplots_adjust(left=0.0, right=1, top=1, bottom=0, wspace=0.02, hspace=0.04) self.dragged = None def onCollectionsClick(self, event): print("clicked") self.collectionsDoubleClick = event.dblclick self.last = "clicked" if self.create_bus.isChecked(): geodata = (event.xdata, event.ydata) try: self.bus_window = BusWindow(self.net, self.updateBusCollection, geodata=geodata) except Exception as inst: print(inst) def onCollectionsPick(self, event): if self.collectionsDoubleClick == False: QTimer.singleShot( 200, partial(self.performcollectionsSingleClickActions, event)) def performcollectionsSingleClickActions(self, event): print("picked") collection = event.artist element, index = collection.info[event.ind[0]] print("====", event.ind[0]) print("====", collection) print("single") if self.collectionsDoubleClick: #ignore second click of collectionsDoubleClick if self.last == "doublecklicked": self.last = "clicked" else: self.last = "doublecklicked" print("Double Clicked a ", element) self.open_element_window(element, index) else: self.collectionsSingleClickActions(event, element, index) def open_element_window(self, element, index): if element == "bus": print("will build bus") self.element_window = BusWindow(self.net, self.updateBusCollection, index=index) elif element == "line": print("will bild line") print(index) self.element_window = LineWindow(self.net, self.updateLineCollection, index=index) elif element == "load": self.element_window = LoadWindow(self.net, self.updateLoadCollections, index=index) elif element == "trafo": print("trafo doubleclicked") def collectionsSingleClickActions(self, event, element, index): #what to do when single clicking on an element if element != "bus": return if self.create_line.isChecked(): if self.lastBusSelected is None: self.lastBusSelected = index elif self.lastBusSelected != index: #pp.create_line(self.net, self.lastBusSelected, index, length_km=1.0, std_type="NAYY 4x50 SE") self.build_message.setText( str(self.lastBusSelected) + "-" + str(index)) self.line_window = LineWindow(self.net, self.updateLineCollection, from_bus=self.lastBusSelected, to_bus=index) self.lastBusSelected = None elif self.create_trafo.isChecked(): if self.lastBusSelected is None: self.lastBusSelected = index elif self.lastBusSelected != index: pp.create_transformer(self.net, self.lastBusSelected, index, std_type="0.25 MVA 10/0.4 kV") self.lastBusSelected = None self.updateTrafoCollections() self.drawCollections() elif self.create_load.isChecked(): try: self.load_window = LoadWindow(self.net, self.updateLoadCollections, bus=index) except Exception as e: print(e) self.lastBusSelected = None
class HandlerClass: def __init__(self, halcomp, widgets, paths): self.h = halcomp self.w = widgets self.PATHS = paths self.gcodes = GCodes() self.valid = QtGui.QDoubleValidator(-999.999, 999.999, 3) KEYBIND.add_call('Key_F12', 'on_keycall_F12') KEYBIND.add_call('Key_Pause', 'on_keycall_pause') # some global variables self.run_time = 0 self.time_tenths = 0 self.timerOn = False self.home_all = False self.max_linear_velocity = INFO.MAX_LINEAR_VELOCITY * 60 self.system_list = [ "G54", "G55", "G56", "G57", "G58", "G59", "G59.1", "G59.2", "G59.3" ] self.slow_jog_factor = 10 self.reload_tool = 0 self.last_loaded_program = "" self.first_turnon = True self.lineedit_list = [ "work_height", "touch_height", "sensor_height", "laser_x", "laser_y", "sensor_x", "sensor_y", "search_vel", "probe_vel", "max_probe", "eoffset_count" ] self.onoff_list = ["frame_program", "frame_dro"] self.auto_list = ["chk_eoffsets", "cmb_gcode_history"] self.axis_a_list = [ "label_axis_a", "dro_axis_a", "action_zero_a", "axistoolbutton_a", "action_home_a", "widget_jog_angular", "widget_increments_angular", "a_plus_jogbutton", "a_minus_jogbutton" ] self.html = """<html> <head> <title>Test page for the download:// scheme</title> </head> <body> <h1>Setup Tab</h1> <p>If you select a file with .html as a file ending, it will be shown here..</p> <img src="file://%s" alt="lcnc_swoop" /> <hr /> <a href="http://www.linuxcnc.org/docs/2.8/html/gui/qtdragon.html">QtDragon Documentation link</a> </body> </html> """ % (os.path.join(paths.IMAGEDIR, 'lcnc_swoop.png')) STATUS.connect('general', self.dialog_return) STATUS.connect('state-on', lambda w: self.enable_onoff(True)) STATUS.connect('state-off', lambda w: self.enable_onoff(False)) STATUS.connect('mode-manual', lambda w: self.enable_auto(True)) STATUS.connect('mode-mdi', lambda w: self.enable_auto(True)) STATUS.connect('mode-auto', lambda w: self.enable_auto(False)) STATUS.connect('gcode-line-selected', lambda w, line: self.set_start_line(line)) STATUS.connect('hard-limits-tripped', self.hard_limit_tripped) STATUS.connect('interp-idle', lambda w: self.set_start_line(0)) STATUS.connect('program-pause-changed', lambda w, state: self.w.spindle_pause.setEnabled(state)) STATUS.connect('user-system-changed', self.user_system_changed) STATUS.connect('file-loaded', self.file_loaded) STATUS.connect('homed', self.homed) STATUS.connect('all-homed', self.all_homed) STATUS.connect('not-all-homed', self.not_all_homed) STATUS.connect('periodic', lambda w: self.update_runtimer()) STATUS.connect('command-running', lambda w: self.start_timer()) STATUS.connect('command-stopped', lambda w: self.stop_timer()) def class_patch__(self): self.old_fman = FM.load FM.load = self.load_code def initialized__(self): self.init_pins() self.init_preferences() self.init_widgets() self.w.stackedWidget_log.setCurrentIndex(0) self.w.stackedWidget.setCurrentIndex(0) self.w.stackedWidget_dro.setCurrentIndex(0) self.w.spindle_pause.setEnabled(False) self.w.btn_dimensions.setChecked(True) self.w.page_buttonGroup.buttonClicked.connect(self.main_tab_changed) self.w.filemanager.onUserClicked() self.w.filemanager_usb.onMediaClicked() # hide widgets for A axis if not present if "A" not in INFO.AVAILABLE_AXES: for i in self.axis_a_list: self.w[i].hide() self.w.lbl_increments_linear.setText("INCREMENTS") # set validators for lineEdit widgets for val in self.lineedit_list: self.w['lineEdit_' + val].setValidator(self.valid) ############################# # SPECIAL FUNCTIONS SECTION # ############################# def init_pins(self): # spindle control pins pin = self.h.newpin("spindle_amps", hal.HAL_FLOAT, hal.HAL_IN) hal_glib.GPin(pin).connect("value_changed", self.spindle_pwr_changed) pin = self.h.newpin("spindle_volts", hal.HAL_FLOAT, hal.HAL_IN) hal_glib.GPin(pin).connect("value_changed", self.spindle_pwr_changed) pin = self.h.newpin("spindle_fault", hal.HAL_U32, hal.HAL_IN) hal_glib.GPin(pin).connect("value_changed", self.spindle_fault_changed) pin = self.h.newpin("modbus-errors", hal.HAL_U32, hal.HAL_IN) hal_glib.GPin(pin).connect("value_changed", self.mb_errors_changed) # external offset control pins self.h.newpin("eoffset_enable", hal.HAL_BIT, hal.HAL_OUT) self.h.newpin("eoffset_clear", hal.HAL_BIT, hal.HAL_OUT) self.h.newpin("eoffset_count", hal.HAL_S32, hal.HAL_OUT) pin = self.h.newpin("eoffset_value", hal.HAL_FLOAT, hal.HAL_IN) hal_glib.GPin(pin).connect("value_changed", self.eoffset_changed) def init_preferences(self): if not self.w.PREFS_: self.add_status( "CRITICAL - no preference file found, enable preferences in screenoptions widget" ) return self.last_loaded_program = self.w.PREFS_.getpref( 'last_loaded_file', None, str, 'BOOK_KEEPING') self.reload_tool = self.w.PREFS_.getpref('Tool to load', 0, int, 'CUSTOM_FORM_ENTRIES') self.w.lineEdit_laser_x.setText( str( self.w.PREFS_.getpref('Laser X', 100, float, 'CUSTOM_FORM_ENTRIES'))) self.w.lineEdit_laser_y.setText( str( self.w.PREFS_.getpref('Laser Y', -20, float, 'CUSTOM_FORM_ENTRIES'))) self.w.lineEdit_sensor_x.setText( str( self.w.PREFS_.getpref('Sensor X', 10, float, 'CUSTOM_FORM_ENTRIES'))) self.w.lineEdit_sensor_y.setText( str( self.w.PREFS_.getpref('Sensor Y', 10, float, 'CUSTOM_FORM_ENTRIES'))) self.w.lineEdit_work_height.setText( str( self.w.PREFS_.getpref('Work Height', 20, float, 'CUSTOM_FORM_ENTRIES'))) self.w.lineEdit_touch_height.setText( str( self.w.PREFS_.getpref('Touch Height', 40, float, 'CUSTOM_FORM_ENTRIES'))) self.w.lineEdit_sensor_height.setText( str( self.w.PREFS_.getpref('Sensor Height', 40, float, 'CUSTOM_FORM_ENTRIES'))) self.w.lineEdit_search_vel.setText( str( self.w.PREFS_.getpref('Search Velocity', 40, float, 'CUSTOM_FORM_ENTRIES'))) self.w.lineEdit_probe_vel.setText( str( self.w.PREFS_.getpref('Probe Velocity', 10, float, 'CUSTOM_FORM_ENTRIES'))) self.w.lineEdit_max_probe.setText( str( self.w.PREFS_.getpref('Max Probe', 10, float, 'CUSTOM_FORM_ENTRIES'))) self.w.lineEdit_eoffset_count.setText( str( self.w.PREFS_.getpref('Eoffset count', 0, int, 'CUSTOM_FORM_ENTRIES'))) self.w.chk_eoffsets.setChecked( self.w.PREFS_.getpref('External offsets', False, bool, 'CUSTOM_FORM_ENTRIES')) self.w.chk_reload_program.setChecked( self.w.PREFS_.getpref('Reload program', False, bool, 'CUSTOM_FORM_ENTRIES')) self.w.chk_reload_tool.setChecked( self.w.PREFS_.getpref('Reload tool', False, bool, 'CUSTOM_FORM_ENTRIES')) self.w.chk_use_keyboard.setChecked( self.w.PREFS_.getpref('Use keyboard', False, bool, 'CUSTOM_FORM_ENTRIES')) self.w.chk_run_from_line.setChecked( self.w.PREFS_.getpref('Run from line', False, bool, 'CUSTOM_FORM_ENTRIES')) self.w.chk_use_virtual.setChecked( self.w.PREFS_.getpref('Use virtual keyboard', False, bool, 'CUSTOM_FORM_ENTRIES')) self.w.chk_alpha_mode.setChecked( self.w.PREFS_.getpref('Use alpha display mode', False, bool, 'CUSTOM_FORM_ENTRIES')) self.w.chk_inhibit_selection.setChecked( self.w.PREFS_.getpref('Inhibit display mouse selection', True, bool, 'CUSTOM_FORM_ENTRIES')) def closing_cleanup__(self): if not self.w.PREFS_: return self.w.PREFS_.putpref('last_loaded_directory', os.path.dirname(self.last_loaded_program), str, 'BOOK_KEEPING') self.w.PREFS_.putpref('last_loaded_file', self.last_loaded_program, str, 'BOOK_KEEPING') self.w.PREFS_.putpref('Tool to load', STATUS.get_current_tool(), int, 'CUSTOM_FORM_ENTRIES') self.w.PREFS_.putpref('Laser X', self.w.lineEdit_laser_x.text().encode('utf-8'), float, 'CUSTOM_FORM_ENTRIES') self.w.PREFS_.putpref('Laser Y', self.w.lineEdit_laser_y.text().encode('utf-8'), float, 'CUSTOM_FORM_ENTRIES') self.w.PREFS_.putpref('Sensor X', self.w.lineEdit_sensor_x.text().encode('utf-8'), float, 'CUSTOM_FORM_ENTRIES') self.w.PREFS_.putpref('Sensor Y', self.w.lineEdit_sensor_y.text().encode('utf-8'), float, 'CUSTOM_FORM_ENTRIES') self.w.PREFS_.putpref( 'Work Height', self.w.lineEdit_work_height.text().encode('utf-8'), float, 'CUSTOM_FORM_ENTRIES') self.w.PREFS_.putpref( 'Touch Height', self.w.lineEdit_touch_height.text().encode('utf-8'), float, 'CUSTOM_FORM_ENTRIES') self.w.PREFS_.putpref( 'Sensor Height', self.w.lineEdit_sensor_height.text().encode('utf-8'), float, 'CUSTOM_FORM_ENTRIES') self.w.PREFS_.putpref( 'Search Velocity', self.w.lineEdit_search_vel.text().encode('utf-8'), float, 'CUSTOM_FORM_ENTRIES') self.w.PREFS_.putpref('Probe Velocity', self.w.lineEdit_probe_vel.text().encode('utf-8'), float, 'CUSTOM_FORM_ENTRIES') self.w.PREFS_.putpref('Max Probe', self.w.lineEdit_max_probe.text().encode('utf-8'), float, 'CUSTOM_FORM_ENTRIES') self.w.PREFS_.putpref( 'Eoffset count', self.w.lineEdit_eoffset_count.text().encode('utf-8'), int, 'CUSTOM_FORM_ENTRIES') self.w.PREFS_.putpref('External offsets', self.w.chk_eoffsets.isChecked(), bool, 'CUSTOM_FORM_ENTRIES') self.w.PREFS_.putpref('Reload program', self.w.chk_reload_program.isChecked(), bool, 'CUSTOM_FORM_ENTRIES') self.w.PREFS_.putpref('Reload tool', self.w.chk_reload_tool.isChecked(), bool, 'CUSTOM_FORM_ENTRIES') self.w.PREFS_.putpref('Use keyboard', self.w.chk_use_keyboard.isChecked(), bool, 'CUSTOM_FORM_ENTRIES') self.w.PREFS_.putpref('Run from line', self.w.chk_run_from_line.isChecked(), bool, 'CUSTOM_FORM_ENTRIES') self.w.PREFS_.putpref('Use virtual keyboard', self.w.chk_use_virtual.isChecked(), bool, 'CUSTOM_FORM_ENTRIES') self.w.PREFS_.putpref('Use alpha display mode', self.w.chk_alpha_mode.isChecked(), bool, 'CUSTOM_FORM_ENTRIES') self.w.PREFS_.putpref('Inhibit display mouse selection', self.w.chk_inhibit_selection.isChecked(), bool, 'CUSTOM_FORM_ENTRIES') def init_widgets(self): self.w.main_tab_widget.setCurrentIndex(0) self.w.slider_jog_linear.setMaximum(self.max_linear_velocity) self.w.slider_jog_linear.setValue(INFO.DEFAULT_LINEAR_JOG_VEL) self.w.slider_jog_angular.setMaximum(INFO.MAX_ANGULAR_JOG_VEL) self.w.slider_jog_angular.setValue(INFO.DEFAULT_ANGULAR_JOG_VEL) self.w.slider_maxv_ovr.setMaximum(self.max_linear_velocity) self.w.slider_maxv_ovr.setValue(self.max_linear_velocity) self.w.slider_feed_ovr.setMaximum(INFO.MAX_FEED_OVERRIDE) self.w.slider_feed_ovr.setValue(100) self.w.slider_rapid_ovr.setMaximum(100) self.w.slider_rapid_ovr.setValue(100) self.w.slider_spindle_ovr.setMinimum(INFO.MIN_SPINDLE_OVERRIDE) self.w.slider_spindle_ovr.setMaximum(INFO.MAX_SPINDLE_OVERRIDE) self.w.slider_spindle_ovr.setValue(100) self.w.chk_override_limits.setChecked(False) self.w.chk_override_limits.setEnabled(False) self.w.lbl_maxv_percent.setText("100 %") self.w.lbl_max_rapid.setText(str(self.max_linear_velocity)) self.w.lbl_home_x.setText( INFO.get_error_safe_setting('JOINT_0', 'HOME', "50")) self.w.lbl_home_y.setText( INFO.get_error_safe_setting('JOINT_1', 'HOME', "50")) self.w.cmb_gcode_history.addItem("No File Loaded") self.w.cmb_gcode_history.wheelEvent = lambda event: None self.w.jogincrements_linear.wheelEvent = lambda event: None self.w.jogincrements_angular.wheelEvent = lambda event: None self.w.gcode_editor.hide() #set up gcode list self.gcodes.setup_list() # clickable frames self.w.frame_cycle_start.mousePressEvent = self.btn_start_clicked self.w.frame_home_all.mousePressEvent = self.btn_home_all_clicked # web view widget for SETUP page self.web_view = QWebView() self.w.verticalLayout_setup.addWidget(self.web_view) self.web_view.setHtml(self.html) # check for virtual keyboard enabled if self.w.chk_use_virtual.isChecked(): self.w.btn_keyboard.show() else: self.w.btn_keyboard.hide() TOOLBAR.configure_statusbar(self.w.statusbar, 'message_recall') def processed_focus_event__(self, receiver, event): if not self.w.chk_use_virtual.isChecked() or STATUS.is_auto_mode(): return if isinstance(receiver, QtWidgets.QLineEdit): if not receiver.isReadOnly(): self.w.btn_keyboard.setChecked(True) elif isinstance(receiver, QtWidgets.QTableView): self.w.btn_keyboard.setChecked(True) elif isinstance(receiver, QtWidgets.QCommonStyle): return def processed_key_event__(self, receiver, event, is_pressed, key, code, shift, cntrl): # when typing in MDI, we don't want keybinding to call functions # so we catch and process the events directly. # We do want ESC, F1 and F2 to call keybinding functions though if code not in (QtCore.Qt.Key_Escape, QtCore.Qt.Key_F1, QtCore.Qt.Key_F2, QtCore.Qt.Key_F12): # search for the top widget of whatever widget received the event # then check if it's one we want the keypress events to go to flag = False receiver2 = receiver while receiver2 is not None and not flag: if isinstance(receiver2, QtWidgets.QDialog): flag = True break if isinstance(receiver2, QtWidgets.QLineEdit): flag = True break if isinstance(receiver2, MDI_WIDGET): flag = True break if isinstance(receiver2, GCODE): flag = True break if isinstance(receiver2, TOOL_TABLE): flag = True break if isinstance(receiver2, OFFSET_VIEW): flag = True break receiver2 = receiver2.parent() if flag: if isinstance(receiver2, GCODE): # if in manual do our keybindings - otherwise # send events to gcode widget if STATUS.is_man_mode() == False: if is_pressed: receiver.keyPressEvent(event) event.accept() return True elif is_pressed: receiver.keyPressEvent(event) event.accept() return True else: event.accept() return True if event.isAutoRepeat(): return True # ok if we got here then try keybindings try: KEYBIND.call(self, event, is_pressed, shift, cntrl) event.accept() return True except NameError as e: if is_pressed: LOG.debug('Exception in KEYBINDING: {}'.format(e)) self.add_status('Exception in KEYBINDING: {}'.format(e)) except Exception as e: if is_pressed: LOG.debug('Exception in KEYBINDING:', exc_info=e) print 'Error in, or no function for: %s in handler file for-%s' % ( KEYBIND.convert(event), key) event.accept() return True ######################### # CALLBACKS FROM STATUS # ######################### def spindle_pwr_changed(self, data): # this calculation assumes the voltage is line to neutral # and that the synchronous motor spindle has a power factor of 0.9 power = self.h['spindle_volts'] * self.h[ 'spindle_amps'] * 2.7 # 3 x V x I x PF amps = "{:1.1f}".format(self.h['spindle_amps']) pwr = "{:1.1f}".format(power) self.w.lbl_spindle_amps.setText(amps) self.w.lbl_spindle_power.setText(pwr) def spindle_fault_changed(self, data): fault = hex(self.h['spindle_fault']) self.w.lbl_spindle_fault.setText(fault) def mb_errors_changed(self, data): errors = self.h['modbus-errors'] self.w.lbl_mb_errors.setText(str(errors)) def eoffset_changed(self, data): eoffset = "{:2.3f}".format(self.h['eoffset_value']) self.w.lbl_eoffset_value.setText(eoffset) def dialog_return(self, w, message): rtn = message.get('RETURN') name = message.get('NAME') plate_code = bool(message.get('ID') == '_touchplate_') sensor_code = bool(message.get('ID') == '_toolsensor_') wait_code = bool(message.get('ID') == '_wait_resume_') if plate_code and name == 'MESSAGE' and rtn is True: self.touchoff('touchplate') elif sensor_code and name == 'MESSAGE' and rtn is True: self.touchoff('sensor') elif wait_code and name == 'MESSAGE': self.h['eoffset_clear'] = False def user_system_changed(self, obj, data): sys = self.system_list[int(data) - 1] self.w.offset_table.selectRow(int(data) + 3) self.w.actionbutton_rel.setText(sys) def file_loaded(self, obj, filename): if filename is not None: self.add_status("Loaded file {}".format(filename)) self.w.progressBar.setValue(0) self.last_loaded_program = filename else: self.add_status("Filename not valid") def percent_loaded_changed(self, fraction): if fraction < 0: self.w.progressBar.setValue(0) self.w.progressBar.setFormat('Progress') else: self.w.progressBar.setValue(fraction) self.w.progressBar.setFormat('Loading: {}%'.format(fraction)) def percent_completed_changed(self, fraction): self.w.progressBar.setValue(fraction) if fraction < 0: self.w.progressBar.setValue(0) self.w.progressBar.setFormat('Progress') else: self.w.progressBar.setFormat('Completed: {}%'.format(fraction)) def homed(self, obj, joint): i = int(joint) axis = INFO.GET_NAME_FROM_JOINT.get(i).lower() try: self.w["dro_axis_{}".format(axis)].setProperty('homed', True) self.w["dro_axis_{}".format(axis)].setStyle( self.w["dro_axis_{}".format(axis)].style()) except: pass def all_homed(self, obj): self.home_all = True self.w.lbl_home_all.setText("ALL\nHOMED") if self.first_turnon is True: self.first_turnon = False if self.w.chk_reload_tool.isChecked(): command = "M61 Q{}".format(self.reload_tool) ACTION.CALL_MDI(command) if self.last_loaded_program is not None and self.w.chk_reload_program.isChecked( ): if os.path.isfile(self.last_loaded_program): self.w.cmb_gcode_history.addItem(self.last_loaded_program) self.w.cmb_gcode_history.setCurrentIndex( self.w.cmb_gcode_history.count() - 1) ACTION.OPEN_PROGRAM(self.last_loaded_program) self.w.manual_mode_button.setChecked(True) def not_all_homed(self, obj, list): self.home_all = False self.w.lbl_home_all.setText("HOME\nALL") for i in INFO.AVAILABLE_JOINTS: if str(i) in list: axis = INFO.GET_NAME_FROM_JOINT.get(i).lower() try: self.w["dro_axis_{}".format(axis)].setProperty( 'homed', False) self.w["dro_axis_{}".format(axis)].setStyle( self.w["dro_axis_{}".format(axis)].style()) except: pass def hard_limit_tripped(self, obj, tripped, list_of_tripped): self.add_status("Hard limits tripped") self.w.chk_override_limits.setEnabled(tripped) if not tripped: self.w.chk_override_limits.setChecked(False) def update_runtimer(self): if self.timerOn is False or STATUS.is_auto_paused(): return self.time_tenths += 1 if self.time_tenths == 10: self.time_tenths = 0 self.run_time += 1 hours, remainder = divmod(self.run_time, 3600) minutes, seconds = divmod(remainder, 60) self.w.lbl_runtime.setText("{:02d}:{:02d}:{:02d}".format( hours, minutes, seconds)) def start_timer(self): if STATUS.is_auto_running(): self.run_time = 0 self.timerOn = True def stop_timer(self): self.timerOn = False ####################### # CALLBACKS FROM FORM # ####################### # main button bar def main_tab_changed(self, btn): if STATUS.is_auto_mode(): self.add_status("Cannot switch pages while in AUTO mode") self.w.btn_main.setChecked(True) return index = btn.property("index") if index is None: return self.w.main_tab_widget.setCurrentIndex(index) if index == TAB_MAIN: self.w.btn_keyboard.setChecked(False) self.w.stackedWidget.setCurrentIndex(0) elif index == TAB_FILE: self.w.stackedWidget.setCurrentIndex(1) elif index == TAB_OFFSETS: self.w.stackedWidget.setCurrentIndex(2) elif index == TAB_TOOL: self.w.stackedWidget.setCurrentIndex(3) else: self.w.stackedWidget.setCurrentIndex(0) # gcode frame def cmb_gcode_history_clicked(self): if self.w.cmb_gcode_history.currentIndex() == 0: return filename = self.w.cmb_gcode_history.currentText().encode('utf-8') if filename == self.last_loaded_program: self.add_status("Selected program is already loaded") else: ACTION.OPEN_PROGRAM(filename) # program frame def btn_start_clicked(self, obj): if self.w.main_tab_widget.currentIndex() != 0: return if not STATUS.is_auto_mode(): self.add_status("Must be in AUTO mode to run a program") return start_line = int(self.w.lbl_start_line.text().encode('utf-8')) self.add_status("Started program from line {}".format(start_line)) self.run_time = 0 if start_line == 1: ACTION.RUN(start_line) else: # instantiate preset dialog info = '<b>Running from Line: {} <\b>'.format(start_line) mess = { 'NAME': 'RUNFROMLINE', 'TITLE': 'Preset Dialog', 'ID': '_RUNFROMLINE', 'MESSAGE': info, 'LINE': start_line } ACTION.CALL_DIALOG(mess) def btn_reload_file_clicked(self): if self.last_loaded_program: self.w.progressBar.setValue(0) self.add_status("Loaded program file {}".format( self.last_loaded_program)) ACTION.OPEN_PROGRAM(self.last_loaded_program) # DRO frame def btn_home_all_clicked(self, obj): if self.home_all is False: ACTION.SET_MACHINE_HOMING(-1) else: ACTION.SET_MACHINE_UNHOMED(-1) def btn_home_clicked(self): joint = self.w.sender().property('joint') axis = INFO.GET_NAME_FROM_JOINT.get(joint).lower() if self.w["dro_axis_{}".format(axis)].property('homed') is True: ACTION.SET_MACHINE_UNHOMED(joint) else: ACTION.SET_MACHINE_HOMING(joint) # tool frame def disable_pause_buttons(self, state): self.w.action_pause.setEnabled(not state) self.w.action_step.setEnabled(not state) if state: # set external offsets to lift spindle self.h['eoffset_enable'] = self.w.chk_eoffsets.isChecked() fval = float(self.w.lineEdit_eoffset_count.text()) self.h['eoffset_count'] = int(fval) else: self.h['eoffset_count'] = 0 self.h['eoffset_clear'] = True # instantiate warning box info = "Wait for spindle at speed signal before resuming" mess = { 'NAME': 'MESSAGE', 'ICON': 'WARNING', 'ID': '_wait_resume_', 'MESSAGE': 'CAUTION', 'MORE': info, 'TYPE': 'OK' } ACTION.CALL_DIALOG(mess) # override frame def slow_button_clicked(self, state): slider = self.w.sender().property('slider') current = self.w[slider].value() max = self.w[slider].maximum() if state: self.w.sender().setText("SLOW") self.w[slider].setMaximum(max / self.slow_jog_factor) self.w[slider].setValue(current / self.slow_jog_factor) self.w[slider].setPageStep(10) else: self.w.sender().setText("FAST") self.w[slider].setMaximum(max * self.slow_jog_factor) self.w[slider].setValue(current * self.slow_jog_factor) self.w[slider].setPageStep(100) def slider_maxv_changed(self, value): maxpc = (float(value) / self.max_linear_velocity) * 100 self.w.lbl_maxv_percent.setText("{:3.0f} %".format(maxpc)) def slider_rapid_changed(self, value): rapid = (float(value) / 100) * self.max_linear_velocity self.w.lbl_max_rapid.setText("{:4.0f}".format(rapid)) def btn_maxv_100_clicked(self): self.w.slider_maxv_ovr.setValue(self.max_linear_velocity) def btn_maxv_50_clicked(self): self.w.slider_maxv_ovr.setValue(self.max_linear_velocity / 2) # file tab def btn_gcode_edit_clicked(self, state): if not STATUS.is_on_and_idle(): return if state: self.w.filemanager.hide() self.w.widget_file_copy.hide() self.w.gcode_editor.show() self.w.gcode_editor.editMode() else: self.w.filemanager.show() self.w.widget_file_copy.show() self.w.gcode_editor.hide() self.w.gcode_editor.readOnlyMode() def btn_load_file_clicked(self): fname = self.w.filemanager.getCurrentSelected() if fname[1] is True: self.load_code(fname[0]) def btn_copy_file_clicked(self): if self.w.sender() == self.w.btn_copy_right: source = self.w.filemanager_usb.getCurrentSelected() target = self.w.filemanager.getCurrentSelected() elif self.w.sender() == self.w.btn_copy_left: source = self.w.filemanager.getCurrentSelected() target = self.w.filemanager_usb.getCurrentSelected() else: return if source[1] is False: self.add_status("Specified source is not a file") return if target[1] is True: destination = os.path.join(os.path.dirname(target[0]), os.path.basename(source[0])) else: destination = os.path.join(target[0], os.path.basename(source[0])) try: copyfile(source[0], destination) self.add_status("Copied file from {} to {}".format( source[0], destination)) except Exception as e: self.add_status("Unable to copy file. %s" % e) # offsets tab def btn_goto_sensor_clicked(self): x = float(self.w.lineEdit_sensor_x.text()) y = float(self.w.lineEdit_sensor_y.text()) if not STATUS.is_metric_mode(): x = x / 25.4 y = y / 25.4 ACTION.CALL_MDI("G90") ACTION.CALL_MDI_WAIT("G53 G0 Z0") command = "G53 G0 X{:3.4f} Y{:3.4f}".format(x, y) ACTION.CALL_MDI_WAIT(command, 10) def btn_ref_laser_clicked(self): x = float(self.w.lineEdit_laser_x.text()) y = float(self.w.lineEdit_laser_y.text()) if not STATUS.is_metric_mode(): x = x / 25.4 y = y / 25.4 self.add_status("Laser offsets set") command = "G10 L20 P0 X{:3.4f} Y{:3.4f}".format(x, y) ACTION.CALL_MDI(command) # tool tab def btn_m61_clicked(self): checked = self.w.tooloffsetview.get_checked_list() if len(checked) > 1: self.add_status("Select only 1 tool to load") elif checked: self.add_status("Loaded tool {}".format(checked[0])) ACTION.CALL_MDI("M61 Q{}".format(checked[0])) else: self.add_status("No tool selected") def btn_touchoff_clicked(self): if STATUS.get_current_tool() == 0: self.add_status("Cannot touchoff with no tool loaded") return if not STATUS.is_all_homed(): self.add_status("Must be homed to perform tool touchoff") return # instantiate dialog box sensor = self.w.sender().property('sensor') info = "Ensure tooltip is within {} mm of tool sensor and click OK".format( self.w.lineEdit_max_probe.text()) mess = { 'NAME': 'MESSAGE', 'ID': sensor, 'MESSAGE': 'TOOL TOUCHOFF', 'MORE': info, 'TYPE': 'OKCANCEL' } ACTION.CALL_DIALOG(mess) # status tab def btn_clear_status_clicked(self): STATUS.emit('update-machine-log', None, 'DELETE') def btn_save_status_clicked(self): text = self.w.machinelog.toPlainText() filename = self.w.lbl_clock.text().encode('utf-8') filename = 'status_' + filename.replace(' ', '_') + '.txt' self.add_status("Saving Status file to {}".format(filename)) with open(filename, 'w') as f: f.write(text) def btn_dimensions_clicked(self, state): self.w.gcodegraphics.show_extents_option = state self.w.gcodegraphics.clear_live_plotter() def chk_override_limits_checked(self, state): if state: print("Override limits set") ACTION.SET_LIMITS_OVERRIDE() else: print("Override limits not set") def chk_run_from_line_checked(self, state): if not state: self.w.lbl_start_line.setText('1') def chk_alpha_mode_clicked(self, state): self.w.gcodegraphics.set_alpha_mode(state) def chk_inhibit_display_selection_clicked(self, state): self.w.gcodegraphics.set_inhibit_selection(state) # settings tab def chk_use_virtual_changed(self, state): if state: self.w.btn_keyboard.show() else: self.w.btn_keyboard.hide() ##################### # GENERAL FUNCTIONS # ##################### def load_code(self, fname): if fname is None: return if fname.endswith(".ngc") or fname.endswith(".py"): self.w.cmb_gcode_history.addItem(fname) self.w.cmb_gcode_history.setCurrentIndex( self.w.cmb_gcode_history.count() - 1) ACTION.OPEN_PROGRAM(fname) self.add_status("Loaded program file : {}".format(fname)) self.w.main_tab_widget.setCurrentIndex(TAB_MAIN) elif fname.endswith(".html"): self.web_view.load(QtCore.QUrl.fromLocalFile(fname)) self.add_status("Loaded HTML file : {}".format(fname)) self.w.main_tab_widget.setCurrentIndex(TAB_SETUP) self.w.btn_setup.setChecked(True) else: self.add_status("Unknown or invalid filename") def disable_spindle_pause(self): self.h['eoffset_count'] = 0 if self.w.spindle_pause.isChecked(): self.w.spindle_pause.setChecked(False) def touchoff(self, selector): if selector == 'touchplate': z_offset = float(self.w.lineEdit_touch_height.text()) elif selector == 'sensor': z_offset = float(self.w.lineEdit_sensor_height.text()) - float( self.w.lineEdit_work_height.text()) else: self.add_alarm("Unknown touchoff routine specified") return self.add_status("Touchoff to {} started".format(selector)) max_probe = self.w.lineEdit_max_probe.text() search_vel = self.w.lineEdit_search_vel.text() probe_vel = self.w.lineEdit_probe_vel.text() ACTION.CALL_MDI("G21 G49") ACTION.CALL_MDI("G10 L20 P0 Z0") ACTION.CALL_MDI("G91") command = "G38.2 Z-{} F{}".format(max_probe, search_vel) if ACTION.CALL_MDI_WAIT(command, 10) == -1: ACTION.CALL_MDI("G90") return if ACTION.CALL_MDI_WAIT("G1 Z4.0"): ACTION.CALL_MDI("G90") return ACTION.CALL_MDI("G4 P0.5") command = "G38.2 Z-4.4 F{}".format(probe_vel) if ACTION.CALL_MDI_WAIT(command, 10) == -1: ACTION.CALL_MDI("G90") return command = "G10 L20 P0 Z{}".format(z_offset) ACTION.CALL_MDI_WAIT(command) command = "G1 Z10 F{}".format(search_vel) ACTION.CALL_MDI_WAIT(command) ACTION.CALL_MDI("G90") def kb_jog(self, state, joint, direction, fast=False, linear=True): if not STATUS.is_man_mode() or not STATUS.machine_is_on(): self.add_status('Machine must be ON and in Manual mode to jog') return if linear: distance = STATUS.get_jog_increment() rate = STATUS.get_jograte() / 60 else: distance = STATUS.get_jog_increment_angular() rate = STATUS.get_jograte_angular() / 60 if state: if fast: rate = rate * 2 ACTION.JOG(joint, direction, rate, distance) else: ACTION.JOG(joint, 0, 0, 0) def add_status(self, message): self._m = message print message self.w.statusbar.showMessage(self._m, 5000) STATUS.emit('update-machine-log', self._m, 'TIME') def enable_auto(self, state): for widget in self.auto_list: self.w[widget].setEnabled(state) if state is True: self.w.jogging_frame.show() if self.w.chk_use_virtual.isChecked(): self.w.btn_keyboard.show() else: self.w.jogging_frame.hide() self.w.btn_main.setChecked(True) self.w.btn_keyboard.setChecked(False) self.w.btn_keyboard.hide() self.w.main_tab_widget.setCurrentIndex(TAB_MAIN) self.w.stackedWidget.setCurrentIndex(0) def enable_onoff(self, state): if state: self.add_status("Machine ON") else: self.add_status("Machine OFF") self.w.spindle_pause.setChecked(False) self.h['eoffset_count'] = 0 for widget in self.onoff_list: self.w[widget].setEnabled(state) def set_start_line(self, line): if line == 0: self.w.lbl_start_line.setText('1') elif self.w.chk_run_from_line.isChecked(): self.w.lbl_start_line.setText(str(line)) else: self.w.lbl_start_line.setText('1') def use_keyboard(self): if self.w.chk_use_keyboard.isChecked(): return True else: self.add_status('Keyboard shortcuts are disabled') return False ##################### # KEY BINDING CALLS # ##################### def on_keycall_ESTOP(self, event, state, shift, cntrl): if state: ACTION.SET_ESTOP_STATE(True) def on_keycall_POWER(self, event, state, shift, cntrl): if state: ACTION.SET_MACHINE_STATE(False) def on_keycall_ABORT(self, event, state, shift, cntrl): if state: ACTION.ABORT() def on_keycall_HOME(self, event, state, shift, cntrl): if state and not STATUS.is_all_homed() and self.use_keyboard(): ACTION.SET_MACHINE_HOMING(-1) def on_keycall_pause(self, event, state, shift, cntrl): if state and STATUS.is_auto_mode() and self.use_keyboard(): ACTION.PAUSE() def on_keycall_XPOS(self, event, state, shift, cntrl): if self.use_keyboard() and not self.w.btn_keyboard.isChecked(): self.kb_jog(state, 0, 1, shift) def on_keycall_XNEG(self, event, state, shift, cntrl): if self.use_keyboard() and not self.w.btn_keyboard.isChecked(): self.kb_jog(state, 0, -1, shift) def on_keycall_YPOS(self, event, state, shift, cntrl): if self.use_keyboard() and not self.w.btn_keyboard.isChecked(): self.kb_jog(state, 1, 1, shift) def on_keycall_YNEG(self, event, state, shift, cntrl): if self.use_keyboard() and not self.w.btn_keyboard.isChecked(): self.kb_jog(state, 1, -1, shift) def on_keycall_ZPOS(self, event, state, shift, cntrl): if self.use_keyboard() and not self.w.btn_keyboard.isChecked(): self.kb_jog(state, 2, 1, shift) def on_keycall_ZNEG(self, event, state, shift, cntrl): if self.use_keyboard() and not self.w.btn_keyboard.isChecked(): self.kb_jog(state, 2, -1, shift) def on_keycall_APOS(self, event, state, shift, cntrl): if self.use_keyboard() and not self.w.btn_keyboard.isChecked(): self.kb_jog(state, 3, 1, shift, False) def on_keycall_ANEG(self, event, state, shift, cntrl): if self.use_keyboard() and not self.w.btn_keyboard.isChecked(): self.kb_jog(state, 3, -1, shift, False) def on_keycall_F12(self, event, state, shift, cntrl): if state: STYLEEDITOR.load_dialog() ############################## # required class boiler code # ############################## def __getitem__(self, item): return getattr(self, item) def __setitem__(self, item, value): return setattr(self, item, value)
class QUnFrameWindow(QWidget): """ 无边框窗口类 """ def __init__(self): super(QUnFrameWindow, self).__init__(None,Qt.FramelessWindowHint) # 设置为顶级窗口,无边框 # self._padding =8 # 设置边界宽度为8 #self.setAttribute(Qt.WA_TranslucentBackground) self.SHADOW_WIDTH = 8 self.initLayout() # 设置框架布局 #self.setMinimumSize(900,700) self.setMouseTracking(True) # 设置widget鼠标跟踪 self.initDrag() # 设置鼠标跟踪判断默认值 self.center() self.__dir = os.path.dirname(os.path.abspath(__file__)) #绘制边框阴影 def drawShadow(self,painter): #print('ddddddddddddddddddd') #绘制左上角、左下角、右上角、右下角、上、下、左、右边框 self.pixmaps=[self.__dir+'/shadow/shadow_left_top.png',self.__dir+'/shadow/shadow_left_bottom.png',self.__dir+'/shadow/shadow_right_top.png',self.__dir+'/shadow/shadow_right_bottom.png',self.__dir+'/shadow/shadow_top.png',self.__dir+'/shadow/shadow_bottom.png',self.__dir+'/shadow/shadow_left.png',self.__dir+'/shadow/shadow_right.png'] painter.drawPixmap(0, 0, self.SHADOW_WIDTH, self.SHADOW_WIDTH, QPixmap(self.pixmaps[0])) #左上角 painter.drawPixmap(self.width()-self.SHADOW_WIDTH, 0, self.SHADOW_WIDTH, self.SHADOW_WIDTH, QPixmap(self.pixmaps[2])) #右上角 painter.drawPixmap(0,self.height()-self.SHADOW_WIDTH, self.SHADOW_WIDTH, self.SHADOW_WIDTH, QPixmap(self.pixmaps[1])) #左下角 painter.drawPixmap(self.width()-self.SHADOW_WIDTH, self.height()-self.SHADOW_WIDTH, self.SHADOW_WIDTH, self.SHADOW_WIDTH, QPixmap(self.pixmaps[3])) #右下角 painter.drawPixmap(0, self.SHADOW_WIDTH, self.SHADOW_WIDTH, self.height()-2*self.SHADOW_WIDTH, QPixmap(self.pixmaps[6]).scaled(self.SHADOW_WIDTH, self.height()-2*self.SHADOW_WIDTH)) #左 painter.drawPixmap(self.width()-self.SHADOW_WIDTH, self.SHADOW_WIDTH, self.SHADOW_WIDTH, self.height()-2*self.SHADOW_WIDTH, QPixmap(self.pixmaps[7]).scaled(self.SHADOW_WIDTH, self.height()- 2*self.SHADOW_WIDTH)) #右 painter.drawPixmap(self.SHADOW_WIDTH, 0, self.width()-2*self.SHADOW_WIDTH, self.SHADOW_WIDTH, QPixmap(self.pixmaps[4]).scaled(self.width()-2*self.SHADOW_WIDTH, self.SHADOW_WIDTH)) #上 painter.drawPixmap(self.SHADOW_WIDTH, self.height()-self.SHADOW_WIDTH, self.width()-2*self.SHADOW_WIDTH, self.SHADOW_WIDTH, QPixmap(self.pixmaps[5]).scaled(self.width()-2*self.SHADOW_WIDTH, self.SHADOW_WIDTH)) #下 def paintEvent(self, event): painter = QPainter(self) self.drawShadow(painter) painter.setPen(Qt.NoPen) painter.setBrush(Qt.white) painter.drawRect(QRect(self.SHADOW_WIDTH, self.SHADOW_WIDTH, self.width() - 2 * self.SHADOW_WIDTH, self.height() - 2 * self.SHADOW_WIDTH)) def initDrag(self): # 设置鼠标跟踪判断扳机默认值 self._move_drag = False def initLayout(self): # 设置框架布局 self.setAttribute(Qt.WA_TranslucentBackground, True) # self.setWindowIcon(QIcon('logo.ico')) self.gridLayout = QGridLayout(self) self.gridLayout.setContentsMargins(8,8,8,8) self.gridLayout.setSpacing(0) self.gridLayout.setObjectName("gridLayout") #添加竖排layout self.verticalLayout =QVBoxLayout() self.verticalLayout.setContentsMargins(0, 0, 0, 0) self.verticalLayout.setObjectName("verticalLayout") self.gridLayout.addLayout(self.verticalLayout,0,0,0,0) self.label = QTitleLabel() self.label.setScaledContents(False) # self.label.setPixmap(QPixmap("title.png")) # # sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred) # sizePolicy.setHorizontalStretch(0) # sizePolicy.setVerticalStretch(0) # sizePolicy.setHeightForWidth(self.label.sizePolicy().hasHeightForWidth()) # self.label.setSizePolicy(sizePolicy) self.label.setFixedHeight(108) self.label.setContentsMargins(0,0,0,0) self.label.setMouseTracking(True) self.verticalLayout.addWidget(self.label) #在横排layput中添加按钮 #在上方横排下排生成一个横排放浏览器 self.horizontalLayout = QHBoxLayout() self.horizontalLayout.setContentsMargins(0,0,0,0)#浏览器右下方留出拖拽空间 self.horizontalLayout.setObjectName("horizontalLayout") self.verticalLayout.addLayout(self.horizontalLayout) #生成一个浏览器组件,添加到下方的横排layout self.webEngineView = QWebView() self.webEngineView.load(QUrl('file:///C:/Users/zhongcun/Desktop/project/app/mainWin/login/web/login.html')) self.webEngineView.setObjectName('webEngineView') #--------------------------------------------------- # self.webEngineView.page().setWebChannel(channeller) self.webEngineView.setContentsMargins(0,0,0,0) self.webEngineView.setMaximumSize(QSize(10000, 10000)) self.webEngineView.setMouseTracking(True) self.horizontalLayout.addWidget(self.webEngineView) def setCloseButton(self, bool): # 给widget定义一个setCloseButton函数,为True时设置一个关闭按钮 if bool == True: self._CloseButton = QTitleButton(b'\xef\x81\xb2'.decode("utf-8"), self) self._CloseButton.setObjectName("CloseButton") # 设置按钮的ObjectName以在qss样式表内定义不同的按钮样式 self._CloseButton.setToolTip("关闭") self._CloseButton.setMouseTracking(True) # 设置按钮鼠标跟踪(如不设,则按钮在widget上层,无法实现跟踪) self._CloseButton.setFixedHeight(36) #self._CloseButton.setFixedHeight(self._TitleLabel.height()) # 设置按钮高度为标题栏高度 self._CloseButton.clicked.connect(self.close) # 按钮信号连接到关闭窗口的槽函数 def setMinConfigButtons(self, bool): # 给widget定义一个setMinMaxButtons函数,为True时设置一组最小化最大化按钮 if bool == True: self._MinimumButton = QTitleButton(b'\xef\x80\xb0'.decode("utf-8"), self) self._MinimumButton.setObjectName("MinMaxButton") # 设置按钮的ObjectName以在qss样式表内定义不同的按钮样式 self._MinimumButton.setToolTip("最小化") self._MinimumButton.setMouseTracking(True) # 设置按钮鼠标跟踪(如不设,则按钮在widget上层,无法实现跟踪) self._MinimumButton.setFixedHeight(36) # 设置按钮高度为标题栏高度 #self._MinimumButton.setFixedHeight(self._TitleLabel.height()) # 设置按钮高度为标题栏高度 self._MinimumButton.clicked.connect(self.showMinimized) # 按钮信号连接到最小化窗口的槽函数 self._ConfigButton = QTitleButton(b'\xef\x81\x80'.decode("utf-8"), self) self._ConfigButton.setObjectName("MinMaxButton") # 设置按钮的ObjectName以在qss样式表内定义不同的按钮样式 self._ConfigButton.setToolTip("设置") self._ConfigButton.setMouseTracking(True) # 设置按钮鼠标跟踪(如不设,则按钮在widget上层,无法实现跟踪) self._ConfigButton.setFixedHeight(36) def resizeEvent(self, QResizeEvent): try: self._CloseButton.move(self.width() - self._CloseButton.width()-16, 16) except: pass try: self._MinimumButton.move(self.width() - (self._CloseButton.width() + 1) * 2 -16+2, 16) except: pass try: self._ConfigButton.move(self.width() - (self._CloseButton.width() + 1) * 3 -16+3, 16) except: pass def mousePressEvent(self, event): # 重写鼠标点击的事件 if (event.button() == Qt.LeftButton) and (event.y() < self.label.height()+16): # 鼠标左键点击标题栏区域 self._move_drag = True self.move_DragPosition = event.globalPos() - self.pos() event.accept() def mouseMoveEvent(self, QMouseEvent): if (Qt.LeftButton and self._move_drag): # 标题栏拖放窗口位置 self.move(QMouseEvent.globalPos() - self.move_DragPosition) #window.setContentsMargins(0,0,0,0) #动态调整阴影 QMouseEvent.accept() def mouseReleaseEvent(self, QMouseEvent): # 鼠标释放后,各扳机复位 self._move_drag = False #窗口居中 def center(self): qr = self.frameGeometry() # 获得主窗口的一个矩形特定几何图形。这包含了窗口的框架。 cp = QDesktopWidget().availableGeometry().center() # 算出相对于显示器的绝对值。 # 并且从这个绝对值中,我们获得了屏幕中心点。 qr.moveCenter(cp) # 矩形已经设置好了它的宽和高。现在我们把矩形的中心设置到屏幕的中间去。 # 矩形的大小并不会改变。 self.move(qr.topLeft()) # 移动了应用窗口的左上方的点到qr矩形的左上方的点,因此居中显示在我们的屏幕 def closeEvent(self, event): # try: # reply = QMessageBox.question(self, '提示', # "确认退出?", QMessageBox.Yes, QMessageBox.No) # print(reply == QMessageBox.Yes) # if reply == QMessageBox.Yes: event.ignore() # self.setVisible(False) print('---------') self.setVisible(False)
class BrowserController(QObject): def __init__(self, parent, fullscreen, sizes): """ This constructor function initializes a layout of the Arius output module but doesn`t displays it on the screen. Firstly, it creates a new app and detects screens configuration. Then, QGridLayout is created and its appearance is configured: margins and spaces between elements is set to 0. After that, we create three QWebViews: one for our main content view and two others for header and footer views. Immideately after creating these instances we assign them their heights according to percentages given by user and dimensions of the screen. Next, we remove scroll bars from top and bottom views and load predefined pages into them. Then we allow QWebKit to run all extensions it needs to render the page. After that, wee set the layout design as a grid of three rows. Finally, we create an updater object which will run in another stream and a timer instance which checks that stream for new commands from the server, and in case if there`s some update handles it. """ super(BrowserController, self).__init__() if not fullscreen and not sizes: print 'You must initialize windows size' raise Exception self._fullscreen = fullscreen if sizes: self._screen_width = sizes[0] self._screen_height = sizes[1] self._app = QtWidgets.QApplication(sys.argv) self._app.setStyle("Fusion") if self._fullscreen: self._get_screen_height() self._layout = QGridLayout() self._layout.setSpacing(0) self._layout.setContentsMargins(0, 0, 0, 0) self._main_browser = QWebView() main_page = FakePage(self) self._main_browser.setPage(main_page) self._top_browser = QWebView() self._bottom_browser = QWebView() self._top_browser_height = config['output_header_height'] * self._screen_height self._bottom_browser_height = config['output_footer_height'] * self._screen_height self._top_browser.setMaximumHeight(self._top_browser_height) self._top_browser.setMinimumHeight(self._top_browser_height) self._bottom_browser.setMaximumHeight(self._bottom_browser_height) self._bottom_browser.setMinimumHeight(self._bottom_browser_height) self._top_browser.page().mainFrame().setScrollBarPolicy( QtCore.Qt.Vertical, QtCore.Qt.ScrollBarAlwaysOff) self._main_browser.page().mainFrame().setScrollBarPolicy( QtCore.Qt.Vertical, QtCore.Qt.ScrollBarAlwaysOff) self._main_browser.page().mainFrame().setScrollBarPolicy( QtCore.Qt.Horizontal, QtCore.Qt.ScrollBarAlwaysOff) self._bottom_browser.page().mainFrame().setScrollBarPolicy( QtCore.Qt.Vertical, QtCore.Qt.ScrollBarAlwaysOff) self._top_browser_load_url( config['flask_server_home'] + config['output_browser_top_page']) self._bottom_browser_load_url( config['flask_server_home'] + config['output_browser_bottom_page']) self._main_browser.settings().setAttribute( QWebSettings.DeveloperExtrasEnabled, True) self._main_browser.settings().setAttribute(QWebSettings.PluginsEnabled, True) QWebSettings.setObjectCacheCapacities(0, 0, 0) self._main_browser.settings().setAttribute( QWebSettings.AcceleratedCompositingEnabled, True) self._main_browser.settings().setAttribute(QWebSettings.WebGLEnabled, True) self._layout.addWidget(self._top_browser, 1, 0) self._layout.addWidget(self._main_browser, 2, 0) self._layout.addWidget(self._bottom_browser, 3, 0) self._parent = parent self._parent.load_url.connect(self._load_url) self._parent.js_execution.connect(self._execute_js) self._parent.zooming.connect(self._zoom) def run(self): self._main_window = QWidget() # create a window as a QWidget self._main_window.setLayout(self._layout) # assign a layout to it if self._fullscreen: self._main_window.showFullScreen() # set full screen enabled else: self._main_window.resize(self._screen_width, self._screen_height) self._main_window.show() # and finally, show the window # and set a trigger to exit the app, as window is closed sys.exit(self._app.exec_()) def _get_screen_height(self): if self._fullscreen: output = subprocess.Popen('xrandr | grep "\*" | cut -d" " -f4', shell=True, stdout=subprocess.PIPE).communicate()[0] output = [int(val) for val in output.split('x')] self._screen_height = output[1] self._screen_width = output[0] else: output = [self._width, self._height] return output[0], output[1] def _zoom(self, factor): self._main_browser.page().mainFrame().setZoomFactor(factor) def _top_browser_load_url(self, url): self._top_browser.load(QUrl(url)) def _bottom_browser_load_url(self, url): self._bottom_browser.load(QUrl(url)) def _load_url(self, url): logger.debug('OPENING URL %s', url) self._main_browser.load(QUrl(url)) def _execute_js(self, string_js): logger.debug(string_js) self._main_browser.page().mainFrame().evaluateJavaScript(string_js)
class DrawLotteryView(QObject): def __init__(self, parent): super(DrawLotteryView, self).__init__() self.pwindow = parent #获取父窗口指针 self.states = QStateMachine() sinitinfo = QState() sltypeinfo = QState() sprizeinfo = QState() svnumberwindow = QState() sdrawlottery = QState() sfinal = QState() sinitinfo.addTransition(self.on_nextstep_event, sltypeinfo) sltypeinfo.addTransition(self.on_nextstep_event, sprizeinfo) sprizeinfo.addTransition(self.on_nextstep_event, svnumberwindow) svnumberwindow.addTransition(self.on_nextstep_event, sdrawlottery) sdrawlottery.addTransition(self.on_nextstep_event, sfinal) sfinal.addTransition(self.on_final_event, sinitinfo) sinitinfo.entered.connect(self.initinfo) sltypeinfo.entered.connect(self.viewltypeinfo) sprizeinfo.entered.connect(self.viewprizeinfo) svnumberwindow.entered.connect(self.viewnumberwindow) sdrawlottery.entered.connect(self.drawlottery) sfinal.entered.connect(self.final) self.states.addState(sinitinfo) self.states.addState(sltypeinfo) self.states.addState(sprizeinfo) self.states.addState(svnumberwindow) self.states.addState(sdrawlottery) self.states.addState(sfinal) self.states.setInitialState(sinitinfo) self.states.start() def show(self): #It is IMPERATIVE that all forward slashes are scrubbed out, otherwise QTWebKit seems to be # easily confused # kickOffHTML = 'file:///' + join(dirname(__file__).replace('\\', '/'), "www/test02.html").replace('\\', '/') # kickOffHTML = 'file:///' + join(dirname(__file__).replace('\\', '/'), "www/test02.html").replace('\\', '/') kickOffHTML = 'file:///' + QDir().absolutePath() + SysConfig().getlotterywindowspath() + 'index.html' # kickOffHTML = 'http://get.webgl.org/' # kickOffHTML = 'http://www.airtightinteractive.com/demos/js/nebula/' #This is basically a browser instance # self.gweb = QGraphicsWebView() self.web = QWebView() self.web.setMinimumSize(1024,680) self.web.setWindowFlags(Qt.FramelessWindowHint) #无边框 self.web.setContextMenuPolicy(0) #关闭右键 #Unlikely to matter but prefer to be waiting for callback then try to catch # it in time. self.web.loadFinished.connect(self.onLoad) self.web.load(QUrl(kickOffHTML)) self.web.show() # self.scene = QGraphicsScene() # self.scene.addItem(self.gweb) # self.view = QGraphicsView() # self.view.setScene(self.scene) # self.view.show() def onLoad(self): #如果mywebinterface未初始化,则初始化mywebinterface # if getattr(self, "mywebinterface", False) == False: # self.mywebinterface = WebInterface() #This is the body of a web browser tab self.myPage = self.web.page() self.myPage.settings().setAttribute(QWebSettings.DeveloperExtrasEnabled, True) self.myPage.settings().setAttribute(QWebSettings.JavascriptEnabled, True) self.myPage.settings().setAttribute(QWebSettings.WebGLEnabled, True) self.myPage.settings().setAttribute(QWebSettings.AcceleratedCompositingEnabled, True) #This is the actual context/frame a webpage is running in. # Other frames could include iframes or such. self.myFrame = self.myPage.mainFrame() # ATTENTION here's the magic that sets a bridge between Python to HTML self.myFrame.addToJavaScriptWindowObject("mywebinterface", self) #Tell the HTML side, we are open for business self.myFrame.evaluateJavaScript("ApplicationIsReady()") def initinfo(self): # print('initinfo') cltypes = self.pwindow.getcurrentqueue() if cltypes == None: self.isfinished = True else: self.isfinished = False self.currentltype = cltypes['ltype'] self.currentnumber = cltypes['number'] #当前奖项需要抽取的数量 self.drawcount = int(self.currentnumber) #当前抽奖序列的人数,需要重复抽奖的次数 self.isdrawing = False #当前是否为抽奖状态 @pyqtSlot() def nextstep(self): if self.isfinished: # print('close this window') self.on_message_event.emit('当前抽奖序列已抽取完毕!<br>按退出按钮退出!') else: if self.isdrawing and self.drawcount > 0: self.drawlottery() else: # print('nextstep') self.on_nextstep_event.emit() @pyqtSlot() def exitwindow(self): # print('exit window') self.web.close() def viewltypeinfo(self): # print('viewltypeinfo') self.on_viewltypeinfo_event.emit(self.currentltype, int(self.currentnumber)) def viewprizeinfo(self): # print('return prizeinfo') ls = SysConfig().getprizeitempicandnote(self.currentltype) icon = ls[0] notes = ls[1] self.on_viewprizeinfo_event.emit(icon, notes) def viewnumberwindow(self): # print('return viewnumberwindow') self.on_viewnumberwindow_event.emit() def drawlottery(self): # print('return drawlottery') progress = str(int(self.currentnumber)-self.drawcount+1) + '/' + self.currentnumber self.on_viewprogress_event.emit(progress) try: dl = DAllList().getwininfo() sid = dl[0][2] + dl[0][5] + dl[0][6] + dl[0][7] name = dl[1] DAllList().setwininfo(self.currentltype,dl[0]) except: sid = '0000' name = 'error, retry' self.drawcount = 0 self.isdrawing = False self.on_drawlottery_event.emit(sid, name) pass self.on_drawlottery_event.emit(sid, name) self.drawcount -= 1 if self.drawcount == 0: self.isdrawing = False else: self.isdrawing = True def final(self): print('final') self.on_final_event.emit() @pyqtSlot() def changesize(self): # print('changesize') if self.web.isFullScreen(): self.web.showNormal() else: self.web.showFullScreen() on_nextstep_event = pyqtSignal() on_viewltypeinfo_event = pyqtSignal(str, int) on_viewprizeinfo_event = pyqtSignal(str, str) on_viewluckguyinfo_event = pyqtSignal(str, str) on_viewnumberwindow_event = pyqtSignal() on_drawlottery_event = pyqtSignal(str, str) on_final_event = pyqtSignal() on_message_event = pyqtSignal(str) on_viewprogress_event = pyqtSignal(str) # app = QApplication(sys.argv) # # myWebApp = DrawLotteryView() # myWebApp.show() # # exit(app.exec_())
class OutputInterface: def __init__(self, fullscreen=False, sizes=None): """ This constructor function initializes a layout of the Arius output module but doesn`t displays it on the screen. Firstly, it creates a new app and detects screens configuration. Then, QGridLayout is created and its appearance is configured: margins and spaces between elements is set to 0. After that, we create three QWebViews: one for our main content view and two others for header and footer views. Immideately after creating these instances we assign them their heights according to percentages given by user and dimensions of the screen. Next, we remove scroll bars from top and bottom views and load predefined pages into them. Then we allow QWebKit to run all extensions it needs to render the page. After that, wee set the layout design as a grid of three rows. Finally, we create an updater object which will run in another stream and a timer instance which checks that stream for new commands from the server, and in case if there`s some update handles it. """ if not fullscreen and not sizes: print 'You must initialize windows size' raise Exception self._fullscreen = fullscreen if sizes: self._screen_width = sizes[0] self._screen_height = sizes[1] self._app = QtWidgets.QApplication(sys.argv) self._app.setStyle("Fusion") if self._fullscreen: self._get_screen_height() self._layout = QGridLayout() # create a main view of an app self._layout.setSpacing(0) # and do some design settings self._layout.setContentsMargins(0, 0, 0, 0) self._main_browser = QWebView() # create a main content view # and initialize zoom factor variable which will be used to control # zoom main_page = FakeBrowser(self) self._main_browser.setPage(main_page) self._zoom_factor = 1 self._top_browser = QWebView() # and create top and bottom views self._bottom_browser = QWebView() self._top_browser_height = config[ 'output_header_height'] * self._screen_height # calculate views sizes self._bottom_browser_height = config[ 'output_footer_height'] * self._screen_height self._top_browser.setMaximumHeight( self._top_browser_height) # and assign them to the views self._top_browser.setMinimumHeight(self._top_browser_height) self._bottom_browser.setMaximumHeight(self._bottom_browser_height) self._bottom_browser.setMinimumHeight(self._bottom_browser_height) self._top_browser.page().mainFrame().setScrollBarPolicy( QtCore.Qt.Vertical, QtCore.Qt.ScrollBarAlwaysOff) # remove the scroll bars self._main_browser.page().mainFrame().setScrollBarPolicy(QtCore.Qt.Vertical, QtCore.Qt.ScrollBarAlwaysOff) self._main_browser.page().mainFrame().setScrollBarPolicy(QtCore.Qt.Horizontal, QtCore.Qt.ScrollBarAlwaysOff) self._bottom_browser.page().mainFrame().setScrollBarPolicy( QtCore.Qt.Vertical, QtCore.Qt.ScrollBarAlwaysOff) self._top_browser_load_url( config['flask_server_home'] + config['output_browser_top_page']) # load default design self._bottom_browser_load_url( config['flask_server_home'] + config['output_browser_bottom_page']) self._main_browser.settings().setAttribute( QWebSettings.DeveloperExtrasEnabled, True) # enable console self._main_browser.settings().setAttribute( QWebSettings.PluginsEnabled, True) # enable plugins QWebSettings.setObjectCacheCapacities(0, 0, 0) # disable caching self._main_browser.settings().setAttribute( QWebSettings.AcceleratedCompositingEnabled, True) self._main_browser.settings().setAttribute( QWebSettings.WebGLEnabled, True) self._layout.addWidget(self._top_browser, 1, 0) # set views positions self._layout.addWidget(self._main_browser, 2, 0) self._layout.addWidget(self._bottom_browser, 3, 0) # create a RLock object to syncronyze threads. self._lock = threading.RLock() # and create an updater object self._updater = OutputUpdater(self._lock) self._updater.start() # which is ran in another non-blocking stream self.timeoutTimer = QTimer() # create a timer to check for commands tCallback = functools.partial( self._handle_command) # set a timer`s function self.timeoutTimer.timeout.connect(tCallback) self.timeoutTimer.start( config['output_update_frequency']) # and start it # as no data is displayed on the main view - curent content type is # None self._cur_filetype = None # text to speech speaker self._speaker = None # audioplayer self._player = Player() def run(self): """ This method is called to show the output module window after initializing all views in __init__. """ self._main_window = QWidget() # create a window as a QWidget self._main_window.setLayout(self._layout) # assign a layout to it if self._fullscreen: self._main_window.showFullScreen() # set full screen enabled else: self._main_window.resize(self._screen_width, self._screen_height) self._main_window.show() # and finally, show the window # and set a trigger to exit the app, as window is closed sys.exit(self._app.exec_()) logger.info('Finished') def _get_screen_height(self): """ This method is used to get dimensions of user`s screen. To do this is uses system utilite 'xrandr' via subprocess module, which is not very nice way, however we didn`t find anything better. """ if self._fullscreen: output = subprocess.Popen('xrandr | grep "\*" | cut -d" " -f4', shell=True, stdout=subprocess.PIPE).communicate()[0] output = [int(val) for val in output.split('x')] # logger.debug('Screen dimensions are: {} x {}'.format(output[0], output[1])) self._screen_height = output[1] self._screen_width = output[0] else: output = [self._width, self._height] return output[0], output[1] def _handle_command(self): """ This is a method called by an updater timer each given interval of time. It checks updater object and in case if there`s some new command it proceeds it. """ command = self._updater.get_state() # get a new command if command[0] != 'none' and command[0] != None: # if there`s a command, handle it logger.info('Handling command {}'.format(command)) # self._updater.reset() # clear the updater object command buffer # Commands for opening all types of content are handled by # calling of _load_content method with specified content type. # start music play if it was stopped if command[0] == 'OPEN_PDF': self._player.play() self._load_content('local_pdf', command[1]) elif command[0] == 'OPEN_URL': self._player.play() self._load_content('external_url', command[1]) elif command[0] == 'OPEN_LOCAL_PAGE': self._player.play() self._load_content('local_url', command[1]) elif command[0] == 'OPEN_VIDEO': self._player.stop() self._load_content('local_video', command[1]) # Command to open a system scren (e.g. {'type': 'OPEN_SCREEN', 'command':'OPEN_IDLE'}) # will be proceeded by sending a 'command' to a _load_screen method, which does futher # work. elif command[0] == 'OPEN_SCREEN': self._player.play() self._load_screen(command[1]) # Zoom commands are handled each with its own method. elif command[0] == 'ZOOM_IN': self._main_browser_zoom_in() elif command[0] == 'ZOOM_OUT': self._main_browser_zoom_out() elif command[0] == 'NO_ZOOM': self._main_browser_reset_zoom() # as well as scroll. elif command[0] == 'SCROLL_DOWN': self._main_browser_scroll_down() elif command[0] == 'SCROLL_UP': self._main_browser_scroll_up() elif command[0] == 'SCROLL_LEFT': self._main_browser_scroll_left() elif command[0] == 'SCROLL_RIGHT': self._main_browser_scroll_right() elif command[0] == 'CONTINIOUS_SCROLL_UP': pass elif command[0] == 'CONTINIOUS_SCROLL_DOWN': pass elif command[0] == 'STOP_SCROLL': pass # Following two commands are responsible for playing # video and pausing it. As a second part of the command # name of the video file should be given. elif command[0] == 'PLAY': self._player.stop() self._video_play() elif command[0] == 'PAUSE': self._video_pause() # These two commands are used for increasing or # decreasing video volume. elif command[0] == 'VOLUME_UP': self._volume_up() elif command[0] == 'VOLUME_DOWN': self._volume_down() # and these two are for text-to-speech # The 'command' body should be a text you # you want to hear. elif command[0] == "SPEAK": print 'SPEAK' self._player.play() self._speak_text(command[1]) elif command[0] == "STOP_SPEAK": self._player.play() self._speak_stop() elif command[0] == 'NEXT_PAGE': self._main_browser_next_page() elif command[0] == 'PREV_PAGE': self._main_browser_prev_page() elif command[0] == 'MUTE': self._mute() elif command[0] == 'UNMUTE': self._unmute() else: logger.info('command not recognized {}'.format(command)) else: pass def _load_screen(self, screen_type): """ This method is responsible for opening system Arius screens. It receives a type of screen, server wants to open and then displays it on the main content view. """ self._main_browser_reset_zoom() # set zoom level to 1 in case if previous page was zoomed # this a default path to the arius server url = 'http://' + config['flask_server_address'] + \ ':' + config['flask_server_port'] if screen_type == 'IDLE': # and here in depend of type of the required screen # we set the last path of the URL to the exact screen url = url + config['flask_server_idle_address'] elif screen_type == 'ERROR': url = url + config['flask_server_error_address'] elif screen_type == 'SEARCH': url = url + config['flask_server_search_address'] elif screen_type == 'SPEAKING': url = url + config['flask_server_speaking_address'] else: # and if the target screen wasn`t recognized we can also handle it logger.info('WRONG SCREEN TYPE: {}'.format(screen_type)) logger.debug( 'Opening {} screen on following address {}'.format(screen_type, url)) self._main_browser.load(QUrl(url)) def _custom_ua(self, url): """ This function returns a custom user agent data which is stored in config.py file as 'output_user_agent' """ return config['output_user_agent'] def _load_content(self, content_type, content): """ This method is for displaying some content of such types: local web pages, remote web pages, local pdf`s, local videos. It should be called like in the following example to work correctly: self._load_content('local_video', 'some_video.mp4') """ self._main_browser_reset_zoom() # reset zoom if content_type == 'local_url': source = config['flask_server_home'] + \ config['flask_server_local_page_client'] + \ content # simply get the file from flask-server by its relative path-id # specify type of currently opened file for zoom and scroll methods self._cur_filetype = "webpage" elif content_type == 'external_url': self._cur_filetype = "webpage" # no needs required as, link should be given in # 'http://yoursite.com/yourpage' source = content elif content_type == 'local_pdf': # to render PDF`s we use PDF.js, so we open its page and send it a # path for the target file. source = config['flask_server_home'] + \ config['flask_server_local_page_client'] + \ content self._cur_filetype = "pdf" elif content_type == 'local_video': # in case of opening local videos we need to modify the path to the video in the source code of # the videoplayer, so don`t die after reading this code. It works just in the same style as other # filetypes, but in a very weird way. source = config['flask_server_home'] + \ config['flask_server_video_addr_client'] + content logger.info('Opening video at {}'.format(source)) self._cur_filetype = "video" # Set a custom user agent to avoid message about deprecated version of # browser self._main_browser.page().userAgentForUrl = self._custom_ua logger.info('Loading data on address: {}'.format(source)) # Create a request to be able to set user-agent data. Without # it, it`s impossible to customize request data. request = QNetworkRequest() request.setUrl(QUrl(source)) request.setRawHeader("USER-AGENT", config['output_user_agent']) # and finally load the result self._main_browser.load(request) def _top_browser_load_url(self, url): """ This method just loads a given URL in the top content view. URL must contain protocol and the address. e.g. http://placehold.it/400x200 or file://data/somepage.html """ logger.info('Loading {} to the top content view.'.format(url)) self._top_browser.load(QUrl(url)) def _bottom_browser_load_url(self, url): """ This method just loads a given URL in the bottom content view. URL must contain protocol and the address. e.g. http://placehold.it/400x200 or file: """ logger.info('Loading {} to the bottom content view.'.format(url)) self._bottom_browser.load(QUrl(url)) def _main_browser_scroll_down(self): """ This method is called to smoothly scroll the page down. In dependence of the current content type if provides different implementations of scroll, but generally it gives the same result. """ logger.debug('Scrolling main content view down') scroll_js = open("scroll.js", "r").read() self._main_browser.page().mainFrame().evaluateJavaScript(scroll_js) if self._cur_filetype == "pdf": self._main_browser.page().mainFrame().evaluateJavaScript("smooth_vscroll_by(PDFViewerApplication.pdfViewer.container, 300, 1000);") elif self._cur_filetype == "webpage": self._main_browser.page().mainFrame().evaluateJavaScript("smooth_vscroll_by(document.body, 300, 1000);") def _main_browser_scroll_up(self): """ This method is called to smoothly scroll the page up. In dependence of the current content type if provides different implementations of scroll, but generally it gives the same result. """ logger.debug('Scrolling main content view down') scroll_js = open("scroll.js", "r").read() self._main_browser.page().mainFrame().evaluateJavaScript(scroll_js) if self._cur_filetype == "pdf": self._main_browser.page().mainFrame().evaluateJavaScript("smooth_vscroll_by(PDFViewerApplication.pdfViewer.container, -300, 1000);") elif self._cur_filetype == "webpage": self._main_browser.page().mainFrame().evaluateJavaScript("smooth_vscroll_by(document.body, -300, 1000);") def _main_browser_scroll_left(self): """ This method is called to smoothly scroll the page left. In dependence of the current content type if provides different implementations of scroll, but generally it gives the same result. """ logger.debug('Scrolling main content view left') scroll_js = open("scroll.js", "r").read() self._main_browser.page().mainFrame().evaluateJavaScript(scroll_js) if self._cur_filetype == "pdf": self._main_browser.page().mainFrame().evaluateJavaScript("smooth_hscroll_by(PDFViewerApplication.pdfViewer.container, -100, 1000);") elif self._cur_filetype == "webpage": self._main_browser.page().mainFrame().evaluateJavaScript("smooth_hscroll_by(document.body, -100, 1000);") def _main_browser_scroll_right(self): """ This method is called to smoothly scroll the page right. In dependence of the current content type if provides different implementations of scroll, but generally it gives the same result. """ logger.debug('Scrolling main content view right') scroll_js = open("scroll.js", "r").read() self._main_browser.page().mainFrame().evaluateJavaScript(scroll_js) if self._cur_filetype == "pdf": self._main_browser.page().mainFrame().evaluateJavaScript("smooth_hscroll_by(PDFViewerApplication.pdfViewer.container, 100, 1000);") elif self._cur_filetype == "webpage": self._main_browser.page().mainFrame().evaluateJavaScript("smooth_hscroll_by(document.body, 100, 1000);") def _main_browser_zoom_in(self): """ This methoud simply zooms main content view in. It works well with all content types. """ logger.debug('Zooming main content view in') if self._cur_filetype == "pdf": string_js = """PDFViewerApplication.zoomIn();""" self._main_browser.page().mainFrame().evaluateJavaScript(string_js) elif self._cur_filetype == "webpage": self._zoom_factor += .1 self._main_browser.page().mainFrame().setZoomFactor(self._zoom_factor) def _main_browser_reset_zoom(self): """ This method sets zoom level of the main content view to the default level. It should be called before loading a new page or in order to cancel any zoom changes. """ logger.debug('Resetting zoom in the main content view') if self._cur_filetype == "pdf": string_js = 'PDFViewerApplication.pdfViewer.currentScaleValue = "page-width"' self._main_browser.page().mainFrame().evaluateJavaScript(string_js) elif self._cur_filetype == "webpage": self._zoom_factor = 1 self._main_browser.page().mainFrame().setZoomFactor(self._zoom_factor) def _main_browser_zoom_out(self): """ This methoud simply zooms main content view out. It works well with all content types. """ logger.debug('Zooming main content view out') if self._cur_filetype == "pdf": string_js = """PDFViewerApplication.zoomOut();""" self._main_browser.page().mainFrame().evaluateJavaScript(string_js) elif self._cur_filetype == "webpage": self._zoom_factor -= .1 self._main_browser.page().mainFrame().setZoomFactor(self._zoom_factor) def _main_browser_next_page(self): logger.debug('Next page') script_js = """PDFViewerApplication.page++;""" self._main_browser.page().mainFrame().evaluateJavaScript(script_js) def _main_browser_prev_page(self): logger.debug('Previous page') script_js = """PDFViewerApplication.page--;""" self._main_browser.page().mainFrame().evaluateJavaScript(script_js) def _speak_text(self, input_text): """ This method is used to provide text-to-speech syntesizer for voice output. As a back-end of this method Mary TTS is used. Voices can be configured through config.py quit easy. """ logger.info('Speaking following text {}'.format(input_text)) if self._speaker is None: # if there wasn`t no tts requests before, create a TTS client self._speaker = Speaker(config['default_voice'], config[ "marytts_host"], config["marytts_port"]) # otherwise just stop previous speaking session (if Arius isn`t # speaking at the moment, nothing will crash) else: self._speaker.stop() # and send given text as a tts request. self._speaker.speak(input_text) def _speak_stop(self): """ This method simply stops current voice output. """ logger.info('Stop speaking') self._speaker.stop() def _video_play(self): """ This method is used to begin playing video on the current page. It should be run only if current content type is 'video'. """ logger.debug('Playing video') if self._cur_filetype == 'video': script_js = """video=document.getElementById("arius_videoplayer"); video.play()""" self._main_browser.page().mainFrame().evaluateJavaScript(script_js) def _video_pause(self): """ This method is used to pause currently playing video on the page. It should be run only if current content type is 'video'. """ logger.debug('Video paused') if self._cur_filetype == 'video': script_js = """video=document.getElementById("arius_videoplayer"); video.pause()""" self._main_browser.page().mainFrame().evaluateJavaScript(script_js) def _volume_up(self): """ This method is used to increase volume. It should be run only if current content type is 'video'. """ logger.debug('Increasing volume') if self._cur_filetype == 'video': script_js = """video=document.getElementById("arius_videoplayer"); video.volume+=0.2;""" self._main_browser.page().mainFrame().evaluateJavaScript(script_js) def _volume_down(self): """ This method is used to decrease volume. It should be run only if current content type is 'video'. """ logger.debug('Decreasing volume') if self._cur_filetype == 'video': script_js = """video=document.getElementById("arius_videoplayer"); video.volume-=0.2;""" self._main_browser.page().mainFrame().evaluateJavaScript(script_js) def _mute(self): if self._player: self._player.mute() if self._speaker: self._speaker.mute() def _unmute(self): if self._player: self._player.unmute() if self._speaker: self._speaker.unmute()
# Copyright 2014 Florian Bruhin (The Compiler) <*****@*****.**> # # This file is part of qutebrowser. # # qutebrowser is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # qutebrowser is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with qutebrowser. If not, see <http://www.gnu.org/licenses/>. """Very simple browser for testing purposes.""" import sys from PyQt5.QtCore import QUrl from PyQt5.QtWidgets import QApplication from PyQt5.QtWebKitWidgets import QWebView app = QApplication(sys.argv) wv = QWebView() wv.load(QUrl(sys.argv[1])) wv.show() app.exec_()
# Enable extra tools for developers from PyQt5.QtWebKit import QWebSettings web.page().settings().setAttribute(QWebSettings.DeveloperExtrasEnabled, True) # Enable right click on page web.page().mainFrame().evaluateJavaScript("var debug=true;") # Html and js Pyjs = Pyjs() Pyjs.debug = debug html = QUrl("file://" + os.getcwd() + "/static/index.html") # Load html and js web.load(html) web.page().mainFrame().addToJavaScriptWindowObject("Pyjs", Pyjs) # Show webview web.show() # Quit application ret = app.exec_() ### Execute before quit ### # Save window config config.set('window', 'width', str(web.frameGeometry().width())) config.set('window', 'height', str(web.frameGeometry().height())) config.set('window', 'x', str(web.geometry().x())) config.set('window', 'y', str(web.geometry().y())) with open(configFilePath, 'w') as file:
class ExperimentManagerUI(QtWidgets.QMainWindow): """ QT User interface """ def __init__(self): """ Initialize Experiment Manager UI """ super(ExperimentManagerUI, self).__init__() self.fs = os.sep self._initConf() self._initHomeApp() self._initLogging() self._initDefaultValues() self._initUI() self._initWidgets() def _initConf(self): """ Initialize config file """ self.conf_default_io = config['default_io'] self.conf_experimentmanager_ui = config['experimentmanager_ui'] self.conf_ui = config['ui'] def _initHomeApp(self): """ Initializes paths of the application """ self.homeFolder = os.path.expanduser("~") homeAppFolderName = self.conf_default_io['homeAppFolderName'] homeAppLogFolder = self.conf_default_io['homeAppLogFolder'] homeDataFolderName = self.conf_default_io['homeDataFolderName'] homeExperimentFolderName = self.conf_experimentmanager_ui['homeExperimentFolderName'] dataTarFileName = self.conf_experimentmanager_ui['dataTarFileName'] self.homeAppFolder = os.path.join(self.homeFolder, homeAppFolderName) self.homeAppLogFolder = os.path.join(self.homeAppFolder, homeAppLogFolder) self.homeDataFolder = os.path.join(self.homeFolder, homeDataFolderName) self.homeExperimentFolder = os.path.join(self.homeDataFolder, homeExperimentFolderName) self.homeDataLogFolder = os.path.join(self.homeDataFolder, 'logs') self.dataTarFile = getResourceLoc(dataTarFileName) if not os.path.exists(self.homeAppFolder): os.mkdir(self.homeAppFolder) if not os.path.exists(self.homeAppLogFolder): os.mkdir(self.homeAppLogFolder) if not os.path.exists(self.homeDataFolder): os.mkdir(self.homeDataFolder) if not os.path.exists(self.homeDataLogFolder): os.mkdir(self.homeDataLogFolder) if not os.path.exists(self.homeExperimentFolder): with tarfile.open(self.dataTarFile, "r:gz") as dataTar: dataTar.extractall(path=self.homeDataFolder) def _initLogging(self): """ Initializes paths of the application """ fileName = 'errors_' + windowTitle.replace(' ', '-').lower() + '.log' errorLogPath = os.path.join(self.homeAppLogFolder, fileName) if debug: level = logging.DEBUG else: level = logging.ERROR configureLogging(errorLogPath, level) def _initDefaultValues(self): """ Initialize default values """ # Load resource paths self.uiPath = getResourceLoc(self.conf_experimentmanager_ui['uiPath']) self.icoPath = getResourceLoc(self.conf_experimentmanager_ui['icoPath']) self.helpimgPath = getResourceLoc(self.conf_ui['helpimgPath']) self.aboutimgPath = getResourceLoc(self.conf_ui['aboutimgPath']) self.labelimgPath = getResourceLoc(self.conf_experimentmanager_ui['labelimgPath']) # set commands self.opensesamerunCommandAuto = findOpensesamerun() self.settingsExtension = self.conf_experimentmanager_ui['settingsExtension'] self.defaultName = 'default.' + self.settingsExtension self.extFilterSettings = self.settingsExtension + " (*." + self.settingsExtension + ")" self.extFilterAll = "All Files" + " (*)" # default folders self.destinationFolder = "" self._lastSelectedDestDir = "" self._lastSelectedSourceDir = "" self.pythonCommandManual = "" self.opensesamerunCommandManual = "" # default text self.windowTitle = self.conf_experimentmanager_ui['windowTitle'] self.StatusBoxHeight = int(self.conf_experimentmanager_ui['StatusBoxHeight']) # default folder self.sourceFolder = self.homeExperimentFolder print(self.sourceFolder) # default widget values self.defaultResolutionHorizontalInteger = int(self.conf_experimentmanager_ui['defaultResolutionHorizontalInteger']) self.defaultResolutionVerticalInteger = int(self.conf_experimentmanager_ui['defaultResolutionVerticalInteger']) self.extensionList = list(self.conf_experimentmanager_ui['extensionList']) def _initUI(self): """ Initializes the UI and sets button actions """ # icons self.helpIcon = QtGui.QIcon(self.helpimgPath) self.aboutIcon = QtGui.QIcon(self.aboutimgPath) self.windowIcon = QtGui.QIcon(self.icoPath) # images self.pixmap = QtGui.QPixmap(self.labelimgPath) # Load and setup UI uic.loadUi(self.uiPath, self) self.windowHorizontalResolution = self.width() if verbose: self.windowVerticalResolution = self.height() else: self.windowVerticalResolution = self.height() - self.StatusBoxHeight # set default window values self.setWindowIcon(self.windowIcon) self.setFixedSize(self.windowHorizontalResolution,self.windowVerticalResolution) self.setWindowTitle(self.windowTitle) self.center() # set icons self.docButton.setIcon(self.helpIcon) self.aboutButton.setIcon(self.aboutIcon) # set default values self.srcCheckBox.setChecked(False) self.inputFolderLocation.setText(self.sourceFolder) self.fullscreenCheckBox.setChecked(True) self.customResolutionCheckBox.setChecked(False) self.resolutionHorizontalSpinBox.setValue(self.defaultResolutionHorizontalInteger) self.resolutionVerticalSpinBox.setValue(self.defaultResolutionVerticalInteger) # set statusbox self.statusBox.setReadOnly(True) self.statusBox.hide() # show/hide default widgets self.pythonLabel.hide() self.pythonLineEdit.hide() self.pythonButton.hide() self.resolutionHorizontalLabel.hide() self.resolutionHorizontalSpinBox.hide() self.resolutionVerticalLabel.hide() self.resolutionVerticalSpinBox.hide() # set Gui image self.image.setPixmap(self.pixmap) # set context menu self.experimentListWidget.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) self.experimentListWidget.customContextMenuRequested.connect(self.listItemRightClicked) if not self.opensesamerunCommandAuto: self.opensesamerunLabel.show() self.opensesamerunLineEdit.show() self.opensesamerunButton.show() self.opensesameNotFoundLabel.show() else: self.opensesamerunLabel.hide() self.opensesamerunLineEdit.hide() self.opensesamerunButton.hide() self.opensesameNotFoundLabel.hide() # Set button actions self.inputFolderButton.clicked.connect(self.selectInputFolderLocation) self.logFolderButton.clicked.connect(self.selectLogFolderDestination) self.startButton.clicked.connect(self.startExperiments) self.docButton.clicked.connect(self.showDocWindow) self.aboutButton.clicked.connect(self.showAboutWindow) self.restoreSettingsButton.clicked.connect(self.selectOpenSettingsFile) self.saveSettingsButton.clicked.connect(self.selectSaveSettingsFile) self.opensesamerunButton.clicked.connect(self.selectOpensesamerunFile) self.pythonButton.clicked.connect(self.selectPythonFile) self.createOpenButton.clicked.connect(self.addOpenQuestion) self.createMCButton.clicked.connect(self.addMCQuestion) self.refreshButton.clicked.connect(self.refreshWidgets) self.resetButton.clicked.connect(self.resetButtonClicked) # set checkbox actions self.srcCheckBox.stateChanged.connect(self.updaterunFromSource) self.customResolutionCheckBox.stateChanged.connect(self.updateCustomResolution) if verbose: self.statusBox.show() else: pass if verbose and not debug: # Redirect console output to textbox in UI, printing stdout in black # and stderr in red sys.stdout = OutLog(self.statusBox, sys.stdout, QtGui.QColor(0,0,0)) if not hasattr(sys,'frozen'): sys.stderr = OutLog(self.statusBox, sys.stderr, QtGui.QColor(255,0,0)) else: sys.stderr = OutLog(self.statusBox, None, QtGui.QColor(255,0,0)) print("") else: pass def _initWidgets(self): # set empty strings, lists and dicts self.currentLangString = '' self.langList = [] self.languageComboBoxItemList = [] self.experimentFileListDict = {} self.widgetItemObjectListDict = {} self.widgetItemNameListDict = {} self.experimentListWidget.clear() self.languageComboBox.clear() self.refreshWidgets() def startExperiments(self): """ Starts the sanity checks and if passed then the execution of experiments. """ if not self.sourceFolder: errorMessage = "Please select a source folder containing the experiment files." print(errorMessage, file=sys.stderr) self.showErrorMessage(errorMessage) return elif not self.destinationFolder : errorMessage = "Please select a log folder." print(errorMessage, file=sys.stderr) self.showErrorMessage(errorMessage) elif not self.subjectLineEdit.text().strip().isdigit(): errorMessage = "Please enter an integer as subject number." print(errorMessage, file=sys.stderr) self.showErrorMessage(errorMessage) return elif not self.srcCheckBox.isChecked() and not self.opensesamerunCommandAuto and not self.opensesamerunCommandManual: error_message = "Please specify the path to the opensesamerun executable." print(error_message, file=sys.stderr) self.showErrorMessage(error_message) return elif self.srcCheckBox.isChecked() and not self.opensesamerunCommandManual: error_message = "Please specify the path to the opensesamerun source file." print(error_message, file=sys.stderr) self.showErrorMessage(error_message) return elif self.srcCheckBox.isChecked() and not self.pythonCommandManual: error_message = "Please specify the path to the python 2 executable." print(error_message, file=sys.stderr) self.showErrorMessage(error_message) return else: selectedSubjectNr = self.subjectLineEdit.displayText().strip() if self.srcCheckBox.isChecked(): self.opensesamerunCommand = self.opensesamerunCommandManual self.pythonCommand = self.pythonCommandManual else: if self.opensesamerunCommandAuto: self.opensesamerunCommand = self.opensesamerunCommandAuto self.pythonCommand = '' else: self.opensesamerunCommand = self.opensesamerunCommandManual self.pythonCommand = '' fullscreen = self.fullscreenCheckBox.isChecked() customResolution = self.customResolutionCheckBox.isChecked() if customResolution: resolutionHorizontal = str(self.resolutionHorizontalSpinBox.value()) resolutionVertical = str(self.resolutionVerticalSpinBox.value()) else: resolutionHorizontal = None resolutionVertical = None [selectedExperimentList, selectedLanguage] = self.getSelectedExperimentData() if not selectedExperimentList: errorMessage = "No experiments selected, please select at least one experiment." print(errorMessage, file=sys.stderr) self.showErrorMessage(errorMessage) return else: pass logFileExists = None logDestinationFilePathList = [] for experiment in selectedExperimentList: strippedExperiment = experiment logDestinationFolder = os.path.join(self.destinationFolder, selectedLanguage, strippedExperiment) logDestinationFile = 'subject-' + selectedSubjectNr + '.csv' logDestinationFilePath = os.path.join(logDestinationFolder, logDestinationFile) logDestinationFilePathList.append(logDestinationFilePath) try: os.makedirs(logDestinationFolder) except OSError as exc: # Python >2.5 if exc.errno == errno.EEXIST and os.path.isdir(logDestinationFolder): pass else: raise if os.path.isfile(logDestinationFilePath): logFileExists = True else: pass if logFileExists: overwriteCheck = self.confirmOverwriteEvent() if not overwriteCheck: return else: pass elif not logFileExists: overwriteCheck = True else: overwriteCheck = False return if overwriteCheck: print("Starting Experiment...") finishedExperiment = ExperimentManager(self.pythonCommand, self.opensesamerunCommand, self.sourceFolder, logDestinationFilePathList, selectedSubjectNr, selectedLanguage, selectedExperimentList, fullscreen, customResolution,resolutionHorizontal, resolutionVertical) if finishedExperiment: print("Output saved to " + self.destinationFolder) print("Ready.") return else: errorMessage = "Error: Could not start the experiments! Did you select the correct opensesamerun and Python File?" print(errorMessage, file=sys.stderr) self.showErrorMessage(errorMessage) return else: errorMessageList = [] self.showErrorMessage(''.join(errorMessageList)) return def listItemRightClicked(self, QPos): """ Add right click context menu to the ListWidget """ self.listMenu= QtWidgets.QMenu() renameItem = self.listMenu.addAction("Rename Questionnaire on disk") removeItem = self.listMenu.addAction("Delete Questionnaire from disk") if self.experimentListWidget.count() == 0: renameItem.setDisabled(True) removeItem.setDisabled(True) renameItem.triggered.connect(self.renameItemClicked) removeItem.triggered.connect(self.removeItemClicked) parentPosition = self.experimentListWidget.mapToGlobal(QtCore.QPoint(0, 0)) self.listMenu.move(parentPosition + QPos) self.listMenu.show() def renameItemClicked(self): """ Create right click rename method """ if self.experimentListWidget.count() == 0: return currentWidget = self.experimentListWidget.currentItem() currentItemName = self.experimentListWidget.currentItem().text() currentItemLanguage = self.languageComboBox.currentText() fileExistsCheck = True go = True noChange = False while fileExistsCheck and go and not noChange: destItemValueTuple = self.renameEvent(currentItemName) go = destItemValueTuple[1] destItemName = destItemValueTuple[0] srcFilePath = os.path.join(self.sourceFolder, currentItemLanguage, currentItemName) destFilePath = os.path.join(self.sourceFolder, currentItemLanguage, destItemName) if srcFilePath == destFilePath: noChange = True fileExistsCheck = os.path.exists(destFilePath) if fileExistsCheck and not noChange and go: errorMessage = "A questionnaire with that filename already exists, please select another name" self.showErrorMessage(errorMessage) if go and not fileExistsCheck: try: self.renameQuestionnaire(srcFilePath, destFilePath) except Exception: errorMessage = 'Access denied, cannot rename experiment, do you have the correct permissions?' self.showErrorMessage(errorMessage) return currentWidget.setText(destItemName) expindex = self.experimentFileListDict[currentItemLanguage].index(currentItemName) self.experimentFileListDict[currentItemLanguage][expindex] = destItemName nameindex = self.widgetItemNameListDict[currentItemLanguage].index(currentItemName) self.widgetItemNameListDict[currentItemLanguage][nameindex] = destItemName def removeItemClicked(self): """ Create right click remove item method """ if self.experimentListWidget.count() == 0: return currentWidget = self.experimentListWidget.currentItem() currentItemName = currentWidget.text() currentItemLanguage = self.languageComboBox.currentText() if self.confirmDeleteEvent(): try: self.removeQuestionnaire(currentItemName, currentItemLanguage) except Exception: errorMessage = 'Access denied, cannot delete experiment, do you have the correct permissions?' self.showErrorMessage(errorMessage) return widgetIndex = self.experimentListWidget.row(currentWidget) self.experimentListWidget.takeItem(widgetIndex) self.experimentFileListDict[currentItemLanguage].remove(currentItemName) self.widgetItemObjectListDict[currentItemLanguage].remove(currentWidget) self.widgetItemNameListDict[currentItemLanguage].remove(currentItemName) def renameQuestionnaire(self,srcFilePath,destFilePath): """ Rename item on disk """ if os.path.isfile(srcFilePath) and not os.path.exists(destFilePath): os.rename(srcFilePath, destFilePath) def removeQuestionnaire(self,fileName,lang): """ Remove item from disk """ filePath = os.path.join(self.sourceFolder, lang, fileName) if os.path.isfile(filePath): os.remove(filePath) def isWritable(self, path): """ Check if path is writable by creating a temp file """ try: testfile = tempfile.TemporaryFile(dir = path) testfile.close() return True except Exception: return False def resetButtonClicked(self): """ Reset the listwidget to initial state """ answer = self.confirmResetEvent() if answer: self._initWidgets() else: pass def renameEvent(self,original): """ Confirm box renaming item on disk """ reply = QtWidgets.QInputDialog.getText(self, "Please enter the new name.", "Filename:", QtWidgets.QLineEdit.Normal, original) return reply def addMCQuestion(self): """ Start the MC questionnaire creator with write check """ if self.isWritable(self.sourceFolder): self.mc = QuestionnaireCreatorUI(self.languageComboBox.currentText(),self.langList,self.sourceFolder,'mc') reply = self.mc.exec_() if reply: [widgetItemName, lang] = self.mc.getValues() self.processQuestion(widgetItemName, lang) else: pass else: errorMessage = 'Access denied, cannot write in questionnaire folder, please change questionnaire folder.' self.showErrorMessage(errorMessage) def addOpenQuestion(self): """ Start the open questionnaire creator with write check """ if self.isWritable(self.sourceFolder): self.open = QuestionnaireCreatorUI(self.languageComboBox.currentText(),self.langList,self.sourceFolder,'open') reply = self.open.exec_() if reply: [widgetItemName, lang] = self.open.getValues() self.processQuestion(widgetItemName, lang) else: pass else: errorMessage = 'Access denied, cannot write in questionnaire folder, please change questionnaire folder.' self.showErrorMessage(errorMessage) def processQuestion(self,widgetItemName, lang): """ Add the questionnaire to the dicts and widgets """ listWidgetItem = self.createListWidgetItem(widgetItemName) if lang not in self.widgetItemNameListDict: self.widgetItemNameListDict[lang] = [] if lang not in self.widgetItemObjectListDict: self.widgetItemObjectListDict[lang] = [] if lang not in self.experimentFileListDict: self.experimentFileListDict[lang] = [] else: pass self.widgetItemNameListDict[lang].append(widgetItemName) self.widgetItemObjectListDict[lang].append(listWidgetItem) self.experimentFileListDict[lang].append(widgetItemName) if lang == self.languageComboBox.currentText(): self.experimentListWidget.addItem(listWidgetItem) else: pass def startRestoreSettings(self, settingsFilePath): """ Restore settings from ini file """ if os.path.isfile(settingsFilePath): self.settingsRestore = QtCore.QSettings(settingsFilePath, QtCore.QSettings.IniFormat) self.refreshWidgets() self.restoreSettings() else: errorMessage = "File not found, nothing to restore." print(errorMessage, file=sys.stderr) self.showErrorMessage(errorMessage) def startSaveSettings(self, settingsFilePath): """ Save settings to ini file """ self.settingsSave = QtCore.QSettings(settingsFilePath, QtCore.QSettings.IniFormat) self.saveSettings() def selectSaveSettingsFile(self): """ Save settings file dialog """ selectedSettingsDest = QtWidgets.QFileDialog.getSaveFileName(self,"Save output as..", self.defaultName, self.extFilterSettings) # Prevent erasing previous entry on cancel press if selectedSettingsDest[0]: self.startSaveSettings(selectedSettingsDest[0]) def selectOpenSettingsFile(self): """ Open settings file dialog """ selectedSettingsLocation = QtWidgets.QFileDialog.getOpenFileName(self,"Open File..", self.defaultName, self.extFilterSettings) # Prevent erasing previous entry on cancel press if selectedSettingsLocation[0]: self.startRestoreSettings(selectedSettingsLocation[0]) def selectOpensesamerunFile(self): """ Set file to write output to """ selectedOpensesamerunLocation = QtWidgets.QFileDialog.getOpenFileName(self,"Open File..", self.homeFolder, self.extFilterAll) # Prevent erasing previous entry on cancel press if selectedOpensesamerunLocation[0]: self.opensesamerunCommandManual = selectedOpensesamerunLocation[0] self.opensesamerunLineEdit.setText(os.path.normpath(self.opensesamerunCommandManual)) def selectPythonFile(self): """ Set file to write output to """ selectedPythonLocation = QtWidgets.QFileDialog.getOpenFileName(self,"Open File..", self.homeFolder, self.extFilterAll) # Prevent erasing previous entry on cancel press if selectedPythonLocation[0]: self.pythonCommandManual = selectedPythonLocation[0] self.pythonLineEdit.setText(os.path.normpath(self.pythonCommandManual)) def selectInputFolderLocation(self): """ Select folder to read csv files from """ selectedFolder = QtWidgets.QFileDialog.getExistingDirectory(self, "Select Directory", directory=self.inputFolderLocation.text()) # Prevent erasing previous entry on cancel press if selectedFolder: self.sourceFolder = selectedFolder self.inputFolderLocation.setText(os.path.normpath(self.sourceFolder)) self._initWidgets() def selectLogFolderDestination(self): """ Set file to write output to """ selectedDest = QtWidgets.QFileDialog.getExistingDirectory(self, "Select Directory", directory=self.logFolderDestination.text()) # Prevent erasing previous entry on cancel press if selectedDest: self.destinationFolder = selectedDest self.logFolderDestination.setText(os.path.normpath(self.destinationFolder)) def refreshWidgets(self): """ Refresh widgets """ try: self.languageComboBox.currentIndexChanged.disconnect(self.updateListWidget) except Exception: pass self.emptyListWidget() self.updateDirs() self.updateListWidgetItems() self.updateComboBoxItems() self.fillListWidget() self.languageComboBox.currentIndexChanged.connect(self.updateListWidget) def updateDirs(self): """ Process and update directories """ langList = list(self.langList) for item in sorted(os.listdir(self.sourceFolder)): languageDir = os.path.join(self.sourceFolder, item) if os.path.isfile(languageDir): pass else: if item not in langList: self.langList.append(item) self.experimentFileListDict[item] = [] else: pass expFileList = [] for extension in self.extensionList: expFileList.extend(glob.glob(languageDir + self.fs + '*' + extension)) expFileList = sorted(expFileList) for expFilePath in expFileList: expFile = os.path.basename(expFilePath) if os.path.isfile(expFilePath): if expFile not in self.experimentFileListDict[item]: self.experimentFileListDict[item].append(expFile) else: pass else: pass for lang in langList: languageDir = os.path.join(self.sourceFolder, lang) if not os.path.isdir(languageDir): self.langList.remove(lang) del self.experimentFileListDict[lang] else: for expFile in self.experimentFileListDict[lang]: filePath = os.path.join(languageDir, expFile) if not os.path.isfile(filePath): self.experimentFileListDict[lang].remove(expFile) else: pass def updateComboBoxItems(self): """ Process and update the languageComboBox """ comboBoxItemList = list(self.languageComboBoxItemList) for lang in self.langList: if lang not in comboBoxItemList: self.languageComboBox.addItem(lang) self.languageComboBoxItemList.append(lang) else: pass for lang in comboBoxItemList: if lang not in self.langList: if self.languageComboBox.currentText() == lang: index1 = self.languageComboBox.findText(self.langList[0],QtCore.Qt.MatchExactly) self.languageComboBox.setCurrentIndex(index1) else: pass self.languageComboBoxItemList.remove(lang) index = self.languageComboBox.findText(lang,QtCore.Qt.MatchExactly) self.languageComboBox.removeItem(index) else: pass def updateListWidgetItems(self): """ Process and update the ListWidget items """ widgetItemNameListDictKeys = list(self.widgetItemNameListDict) for lang in self.langList: expnameList = self.experimentFileListDict[lang] if lang not in widgetItemNameListDictKeys: self.widgetItemNameListDict[lang] = [] self.widgetItemObjectListDict[lang] = [] else: pass for index in range(len(expnameList)): widgetItemName = expnameList[index] if widgetItemName not in self.widgetItemNameListDict[lang]: listWidgetItem = self.createListWidgetItem(widgetItemName) self.widgetItemNameListDict[lang].append(widgetItemName) self.widgetItemObjectListDict[lang].append(listWidgetItem) else: pass for lang in widgetItemNameListDictKeys: if lang not in self.langList: del self.widgetItemNameListDict[lang] del self.widgetItemObjectListDict[lang] else: for widgetItemName in self.widgetItemNameListDict[lang]: if widgetItemName not in self.experimentFileListDict[lang]: widgetIndex = self.widgetItemNameListDict[lang].index(widgetItemName) del self.widgetItemObjectListDict[lang][widgetIndex] del self.widgetItemNameListDict[lang][widgetIndex] else: pass def createListWidgetItem(self, widgetItem): """ Create a ListWidget item """ listWidgetItem = QtWidgets.QListWidgetItem(widgetItem) listWidgetItem.setCheckState(QtCore.Qt.Checked) listWidgetItem.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsUserCheckable | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsDragEnabled) return listWidgetItem def updateListWidget(self): """ Update the ListWidget """ self.emptyListWidget() self.fillListWidget() def emptyListWidget(self): """ Empty the ListWidget """ nrListWidgetItems = self.experimentListWidget.count() if not self.currentLangString == '' or not nrListWidgetItems == 0: lang = self.currentLangString widgetList = list(self.widgetItemObjectListDict[lang]) widgetNameList = list(self.widgetItemNameListDict[lang]) self.widgetItemNameListDict[lang] = [] self.widgetItemObjectListDict[lang] = [] for index in range(nrListWidgetItems): listWidgetItem = self.experimentListWidget.takeItem(0) widgetIndex = widgetList.index(listWidgetItem) self.widgetItemNameListDict[lang].append(widgetNameList[widgetIndex]) self.widgetItemObjectListDict[lang].append(listWidgetItem) def fillListWidget(self): """ Fill the ListWidget """ lang = self.languageComboBox.currentText() if not lang == '': listWidgetItemList = list(self.widgetItemObjectListDict[lang]) for row in range(len(listWidgetItemList)): self.experimentListWidget.insertItem(row,listWidgetItemList[row]) #self.experimentListWidget.item(row).setCheckState(QtCore.Qt.Checked) self.currentLangString = self.languageComboBox.currentText() def getSelectedExperimentData(self): """ Get the language and experiment list from the widgets """ selectedExperimentList = [] selectedLanguage = self.languageComboBox.currentText() nWidgets = self.experimentListWidget.count() for index in range(nWidgets): listWidgetItem = self.experimentListWidget.item(index) if listWidgetItem.checkState() == 2: selectedExperimentList.append(listWidgetItem.text()) return [selectedExperimentList, selectedLanguage] def updaterunFromSource(self): """ Get the language and experiment list from the widgets """ if self.srcCheckBox.isChecked(): self.pythonLabel.show() self.pythonLineEdit.show() self.pythonButton.show() self.opensesamerunLabel.show() self.opensesamerunLineEdit.show() self.opensesamerunButton.show() else: self.pythonLabel.hide() self.pythonLineEdit.hide() self.pythonButton.hide() if not self.opensesamerunCommandAuto: self.opensesamerunLabel.show() self.opensesamerunLineEdit.show() self.opensesamerunButton.show() else: self.opensesamerunLabel.hide() self.opensesamerunLineEdit.hide() self.opensesamerunButton.hide() def updateCustomResolution(self): """ Get the language and experiment list from the widgets """ if self.customResolutionCheckBox.isChecked(): self.resolutionHorizontalLabel.show() self.resolutionHorizontalSpinBox.show() self.resolutionVerticalLabel.show() self.resolutionVerticalSpinBox.show() else: self.resolutionHorizontalLabel.hide() self.resolutionHorizontalSpinBox.hide() self.resolutionVerticalLabel.hide() self.resolutionVerticalSpinBox.hide() def saveSettings(self): """ Save GUI values to ini file """ [selectedExperimentList, selectedLanguage] = self.getSelectedExperimentData() self.settingsSave.setValue('srcCheckBox', self.srcCheckBox.isChecked()) self.settingsSave.setValue('customResolutionCheckBox', self.customResolutionCheckBox.isChecked()) self.settingsSave.setValue('fullscreenCheckBox', self.fullscreenCheckBox.isChecked()) self.settingsSave.setValue('sourceFolder', self.sourceFolder) self.settingsSave.setValue('destinationFolder', self.destinationFolder) self.settingsSave.setValue('pythonCommandManual', self.pythonCommandManual) self.settingsSave.setValue('opensesamerunCommandManual', self.opensesamerunCommandManual) self.settingsSave.setValue('resolutionHorizontalSpinBox', self.resolutionHorizontalSpinBox.value()) self.settingsSave.setValue('resolutionVerticalSpinBox', self.resolutionVerticalSpinBox.value()) self.settingsSave.setValue('languageComboBox', selectedLanguage) self.settingsSave.setValue('selectedExperimentList', selectedExperimentList) def restoreSettings(self): """ Restore GUI values from ini file """ errorMessageList = [] errorMessageExperimentList = [] srcCheckBox = self.settingsRestore.value('srcCheckBox') self.srcCheckBox.setChecked(stringToBool(srcCheckBox)) fullscreenCheckBox = self.settingsRestore.value('fullscreenCheckBox') self.fullscreenCheckBox.setChecked(stringToBool(fullscreenCheckBox)) customResolutionCheckBox = self.settingsRestore.value('customResolutionCheckBox') self.customResolutionCheckBox.setChecked(stringToBool(customResolutionCheckBox)) resolutionHorizontalSpinBox = int(self.settingsRestore.value('resolutionHorizontalSpinBox')) self.resolutionHorizontalSpinBox.setValue(resolutionHorizontalSpinBox) resolutionVerticalSpinBox = int(self.settingsRestore.value('resolutionVerticalSpinBox')) self.resolutionVerticalSpinBox.setValue(resolutionVerticalSpinBox) sourceFolder = self.settingsRestore.value('sourceFolder') if sourceFolder: if os.path.isdir(sourceFolder): self.sourceFolder = sourceFolder self.inputFolderLocation.setText(os.path.normpath(sourceFolder)) self._initWidgets() else: errorMessageList.append('- Experiment folder not found! Using current experiment folder\n') else: pass destinationFolder = self.settingsRestore.value('destinationFolder') if destinationFolder: if os.path.isdir(destinationFolder): self.destinationFolder = destinationFolder self.logFolderDestination.setText(os.path.normpath(destinationFolder)) else: errorMessageList.append('- Log folder not found! Using current log folder\n') else: pass pythonCommandManual = self.settingsRestore.value('pythonCommandManual') if pythonCommandManual: if os.path.isfile(pythonCommandManual): self.pythonCommandManual = pythonCommandManual self.pythonLineEdit.setText(os.path.normpath(pythonCommandManual)) else: errorMessageList.append('- Python file not found! Using current Python file\n') else: pass opensesamerunCommandManual = self.settingsRestore.value('opensesamerunCommandManual') if opensesamerunCommandManual: if os.path.isfile(opensesamerunCommandManual): self.opensesamerunCommandManual = opensesamerunCommandManual self.opensesamerunLineEdit.setText(os.path.normpath(opensesamerunCommandManual)) else: errorMessageList.append('- Opensesamerun File not found! Using current Opensesamerun File\n') else: pass languageComboBox = self.settingsRestore.value('languageComboBox') if languageComboBox: index = self.languageComboBox.findText(languageComboBox) if index == -1: errorMessageList.append('- Language not found in current language folder! Cannot restore language and experiments.\n') self.showErrorMessage(''.join(errorMessageList)) return else: self.languageComboBox.setCurrentIndex(index) else: errorMessageList.append('- No language present in restore file! Cannot restore language and experiments.\n') self.showErrorMessage(''.join(errorMessageList)) return selectedExperimentList = self.settingsRestore.value('selectedExperimentList') if selectedExperimentList: nWidgets = self.experimentListWidget.count() for index in range(nWidgets): self.experimentListWidget.item(index).setCheckState(QtCore.Qt.Unchecked) counter = 0 for selectedExperiment in selectedExperimentList: try: [listWidgetItem] = self.experimentListWidget.findItems(selectedExperiment,QtCore.Qt.MatchExactly) currentIndex = self.experimentListWidget.row(listWidgetItem) targetWidget = self.experimentListWidget.takeItem(currentIndex) targetWidget.setCheckState(QtCore.Qt.Checked) self.experimentListWidget.insertItem(counter,targetWidget) counter += 1 except Exception: errorMessageExperimentList.append('- ' + selectedExperiment + ' not found in data, not restoring this item.\n') else: errorMessageList.append('- No experiments present in restore file! Cannot restore experiments.\n') self.showErrorMessage(''.join(errorMessageList)) return if errorMessageList: self.showErrorMessage(''.join(errorMessageList)) else: pass if errorMessageExperimentList: self.showErrorMessage(''.join(errorMessageExperimentList)) else: pass def center(self): """ Centers the main app window on the screen """ qr = self.frameGeometry() cp = QtWidgets.QDesktopWidget().availableGeometry().center() qr.moveCenter(cp) self.move(qr.topLeft()) def confirmDeleteEvent(self): """ Confirm box deleting item from disk """ message = "Are you sure to delete this questionnaire from disk?" reply = self.confirmEvent(message) return reply def confirmResetEvent(self): """ Confirm box deleting item from disk """ message = "Are you sure you want to reset the experiment selection and order?" reply = self.confirmEvent(message) return reply def confirmOverwriteEvent(self): """ Confirm box deleting item from disk """ message = "Log file(s) already exists, do you want to overwrite the log file(s)?" reply = self.confirmEvent(message) return reply def confirmEvent(self, message): """ Confirm box overwriting (log) files """ reply = QtWidgets.QMessageBox.question(self, 'Message', message, QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, QtWidgets.QMessageBox.No) if reply == QtWidgets.QMessageBox.Yes: reply = True else: reply = False return reply def showDocWindow(self): """ Shows documentation window (with help and licensing info) """ title = "Documentation" htmlFile = "helpfile.html" self.docWindow = QWebView() self.docWindow.closeEvent = self.closeDocWindow self.docWindow.setWindowTitle(title) self.docWindow.setWindowIcon(self.helpIcon) self.docWindow.load(QtCore.QUrl.fromLocalFile(getResourceLoc(htmlFile))) self.docWindow.show() def closeDocWindow(self,source): """ Callback function of the docWindow QWebView item. Destroys reference to doc window after its closed """ del(self.docWindow) def showAboutWindow(self): """ Shows about window """ about ="About" msgBox = QtWidgets.QMessageBox(self) msgBox.setWindowIcon(self.aboutIcon) msgBox.about(self, about, aboutString) def showErrorMessage(self, message): """ Shows error message """ error ="Error" msgBox = QtWidgets.QMessageBox(self) msgBox.about(self, error, message) def closeEvent(self, event): """ Confirm closing the main window """ message = "Are you sure to quit?" reply = self.confirmEvent(message) if reply: event.accept() else: event.ignore()
class Platform(QWidget): def __init__(self): super(Platform, self).__init__() self._desktop = QApplication.instance().desktop() self.__dir = os.path.dirname(os.path.dirname( os.path.abspath(__file__))) self.__WebContent() self.__initPosition() self.__connectJs() print("启动desk") def __WebContent(self): self.resize(self._desktop.screenGeometry().width() / 5, self._desktop.availableGeometry().height()) self.verticalLayout = QtWidgets.QVBoxLayout(self) self.verticalLayout.setContentsMargins(0, 0, 0, 0) self.webView = QWebView() # BASE_DIR = os.path.dirname(os.path.dirname(__file__)) # new = os.path.join(BASE_DIR, 'web') # # new_file_name = os.path.join(new, 'noticeContent.html') # fname = 'file:///'+new_file_name # print(fname) # self.webView.load(QUrl('file:///C:/Users/Administrator/Desktop/app/mainWin/desk/web/desk.html')) print('file:///' + self.__dir + '/web/desk.html') self.webView.load(QUrl('file:///' + self.__dir + '/web/desk.html')) # self.webView.load(QUrl(fname)) self.verticalLayout.addWidget(self.webView) # self.webView.loadFinished.connect(self.test) def onClose(self): #点击关闭按钮时 self.isShow = False QTimer.singleShot(100, self.closeAnimation) #启动弹回动画 def __initPosition(self): # 隐藏任务栏|去掉边框|顶层显示 self.setWindowFlags(Qt.Tool | Qt.X11BypassWindowManagerHint | Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint) # 是否在显示标志 self.isShow = True # 桌面 # self._desktop = QApplication.instance().desktop() # 窗口初始开始位置 self._startPos = QPoint( self._desktop.screenGeometry().width(), self._desktop.availableGeometry().height() - self.height()) print(self._startPos) # 窗口弹出结束位置 self._endPos = QPoint( self._desktop.screenGeometry().width() - self.width(), self._desktop.availableGeometry().height() - self.height()) print(self._endPos) # 初始化位置到右侧 self.move(self._startPos) # 动画 self.animation = QPropertyAnimation(self, b"pos") self.hide() # 先隐藏 super(Platform, self).show() def __connectJs(self): connector = ConnectJs(self) self.webView.page().mainFrame().javaScriptWindowObjectCleared.connect( lambda: self.webView.page().mainFrame( ).addToJavaScriptWindowObject('pyNotice', connector)) def add(self, content=""): if content: self.__notice_list.insert(0, content) self.webView.page().mainFrame().evaluateJavaScript( "pyNoticeAdd(%s)" % str(content)) def show(self, title="", content=""): # 显示动画 self.isShow = True self.animation.setStartValue(self.pos()) self.animation.setEndValue(self._endPos) self.animation.start() def closeAnimation(self): self.isShow = False self.animation.setStartValue(self.pos()) self.animation.setEndValue(self._startPos) self.animation.start()
class Spdom(WizardWidget): drag_label = "Spatial Domain <spdom>" acceptable_tags = ['spdom', 'bounding'] ui_class = UI_spdom.Ui_fgdc_spdom def __init__(self, root_widget=None): self.east = 180 self.west = -180 self.north = 90 self.south = -90 self.valid = True super(self.__class__, self).__init__() self.schema = 'bdp' self.root_widget = root_widget self.after_load = False self.in_xml_load = False self.has_rect = True self.completer = QCompleter() self.ui.fgdc_descgeog.setCompleter(self.completer) self.model = QStringListModel() self.completer.setModel(self.model) self.completer.setCaseSensitivity(0) fname = utils.get_resource_path("spatial/BNDCoords.csv") self.bnds_df = pd.read_csv(fname) self.model.setStringList(self.bnds_df['Name']) self.completer.popup().clicked.connect(self.on_completer_activated) self.completer.popup().selectionModel().selectionChanged.connect( self.on_completer_activated) # self.completer.popup().activated.connect(self.on_completer_activated) def build_ui(self): """ Build and modify this widget's GUI Returns ------- None """ self.ui = self.ui_class() self.ui.setupUi(self) if platform.system() == 'Darwin': map_fname = utils.get_resource_path('leaflet/map_mac.html') else: map_fname = utils.get_resource_path('leaflet/map.html') try: self.view = QWebView() self.view.page().mainFrame().addToJavaScriptWindowObject( "Spdom", self) self.view.setUrl(QUrl.fromLocalFile(map_fname)) self.frame = self.view.page().mainFrame() self.view.load( QUrl.fromLocalFile(QtCore.QDir.current().filePath(map_fname))) except AttributeError: self.view = QWebView() self.view.load( QUrl.fromLocalFile(QtCore.QDir.current().filePath(map_fname))) channel = QWebChannel(self.view.page()) jstr = """ var spdom; new QWebChannel(qt.webChannelTransport, function (channel) { spdom = channel.objects.spdom; });""" self.view.page().setWebChannel(channel) self.evaluate_js(jstr) channel.registerObject("spdom", self) self.ui.verticalLayout_3.addWidget(self.view) # setup drag-drop functionality for this widget and all it's children. self.setup_dragdrop(self) self.add_rect() self.raise_() def connect_events(self): self.ui.fgdc_eastbc.editingFinished.connect(self.coord_updated) self.ui.fgdc_westbc.editingFinished.connect(self.coord_updated) self.ui.fgdc_northbc.editingFinished.connect(self.coord_updated) self.ui.fgdc_southbc.editingFinished.connect(self.coord_updated) def on_completer_activated(self, model_index): try: cur_descgeog = model_index.data() except AttributeError: try: cur_descgeog = model_index.indexes()[0].data() except: return try: if self.bnds_df['Name'].str.contains(cur_descgeog).any(): self.ui.fgdc_eastbc.setText( str( float(self.bnds_df[self.bnds_df['Name'] == cur_descgeog]['east']))) self.ui.fgdc_westbc.setText( str( float(self.bnds_df[self.bnds_df['Name'] == cur_descgeog]['west']))) self.ui.fgdc_northbc.setText( str( float(self.bnds_df[self.bnds_df['Name'] == cur_descgeog]['north']))) self.ui.fgdc_southbc.setText( str( float(self.bnds_df[self.bnds_df['Name'] == cur_descgeog]['south']))) self.add_rect() self.update_map() except: pass # this is a convenience function. # If anything at all happens pass silently def complete_name(self): self.view.page().runJavaScript('addRect();', js_callback) def coord_updated(self): good_coords = self.all_good_coords() try: cur_name = self.sender().objectName() if 'fgdc' not in cur_name: return cur_value = self.sender().text() except AttributeError: cur_name = '' cur_value = '' try: cur_value = float(cur_value) except ValueError: pass msg = '' if type(cur_value) != float and cur_value != '': msg = 'number entered must be numeric only' elif cur_value == '': msg = '' elif cur_name in ['fgdc_westbc', 'fgdc_eastbc'] \ and -180 >= cur_value >= 180: msg = 'East or West coordinate must be within -180 and 180' elif cur_name in ['fgdc_southbc', 'fgdc_northbc'] \ and -90 >= cur_value >= 90: msg = 'North and South coordinates must be within -90 and 90' elif cur_name == 'fgdc_southbc': try: north = float(self.ui.fgdc_northbc.text()) if north <= cur_value: msg = 'North coordinate must be greater than South coordinate' except ValueError: pass elif cur_name == 'fgdc_northbc': try: south = float(self.ui.fgdc_southbc.text()) if south >= cur_value: msg = 'North coordinate must be greater than South coordinate' except ValueError: pass if msg: QMessageBox.warning(self, "Problem bounding coordinates", msg) if good_coords: self.add_rect() else: self.remove_rect() return self.update_map() def update_map(self): jstr = """east = {eastbc}; west = {westbc}; south = {southbc}; north = {northbc}; updateMap(); fitMap(); """.format( **{ 'eastbc': self.ui.fgdc_eastbc.text(), 'westbc': self.ui.fgdc_westbc.text(), 'northbc': self.ui.fgdc_northbc.text(), 'southbc': self.ui.fgdc_southbc.text(), }) self.evaluate_js(jstr) def add_rect(self): jstr = """addRect();""" self.evaluate_js(jstr) def remove_rect(self): if self.has_rect: self.has_rect = False jstr = """removeRect()""" self.evaluate_js(jstr) def evaluate_js(self, jstr): """ :param jstr: :return: """ try: self.frame.evaluateJavaScript(jstr) except: self.view.page().runJavaScript(jstr, js_callback) @pyqtSlot(float, float) def on_ne_move(self, lat, lng): if self.in_xml_load: n, e = lat, lng s = float(self.ui.fgdc_southbc.text()) w = float(self.ui.fgdc_westbc.text()) bounds = spatial_utils.format_bounding((w, e, n, s)) self.ui.fgdc_eastbc.setText(bounds[1]) self.ui.fgdc_northbc.setText(bounds[2]) @pyqtSlot(float, float) def on_nw_move(self, lat, lng): if self.in_xml_load: n, w = lat, lng s = float(self.ui.fgdc_southbc.text()) e = float(self.ui.fgdc_eastbc.text()) bounds = spatial_utils.format_bounding((w, e, n, s)) self.ui.fgdc_westbc.setText(bounds[0]) self.ui.fgdc_northbc.setText(bounds[2]) @pyqtSlot(float, float) def on_se_move(self, lat, lng): if self.in_xml_load: s, e = lat, lng n = float(self.ui.fgdc_northbc.text()) w = float(self.ui.fgdc_westbc.text()) bounds = spatial_utils.format_bounding((w, e, n, s)) self.ui.fgdc_eastbc.setText(bounds[1]) self.ui.fgdc_southbc.setText(bounds[3]) @pyqtSlot(float, float) def on_sw_move(self, lat, lng): if self.in_xml_load: s, w = lat, lng n = float(self.ui.fgdc_northbc.text()) e = float(self.ui.fgdc_eastbc.text()) bounds = spatial_utils.format_bounding((w, e, n, s)) self.ui.fgdc_westbc.setText(bounds[0]) self.ui.fgdc_southbc.setText(bounds[3]) def switch_schema(self, schema): self.schema = schema if schema == 'bdp': self.ui.fgdc_descgeog.show() self.ui.descgeog_label.show() self.ui.descgeog_star.show() else: self.ui.fgdc_descgeog.hide() self.ui.descgeog_label.hide() self.ui.descgeog_star.hide() def all_good_coords(self): try: if -180 > float(self.ui.fgdc_westbc.text()) > 180: return False if -180 > float(self.ui.fgdc_eastbc.text()) > 180: return False if -90 > float(self.ui.fgdc_southbc.text()) > 90: return False if -90 > float(self.ui.fgdc_northbc.text()) > 90: return False if float(self.ui.fgdc_northbc.text()) <= float( self.ui.fgdc_southbc.text()): return False return True except: return False def clear_widget(self): super(self.__class__, self).clear_widget() # self.view.page().mainFrame().addToJavaScriptWindowObject("Spdom", self) # map_fname = utils.get_resource_path('leaflet/map.html') # self.view.setUrl(QUrl.fromLocalFile(map_fname)) def showEvent(self, e): if not self.after_load: self.add_rect() self.update_map() jstr = "sw_marker.openPopup();" self.evaluate_js(jstr) self.after_load = True def to_xml(self): spdom = xml_node('spdom') if self.schema == 'bdp': descgeog = xml_node('descgeog', text=self.ui.fgdc_descgeog.text(), parent_node=spdom) bounding = xml_node('bounding', parent_node=spdom) westbc = xml_node('westbc', text=self.ui.fgdc_westbc.text(), parent_node=bounding) eastbc = xml_node('eastbc', text=self.ui.fgdc_eastbc.text(), parent_node=bounding) northbc = xml_node('northbc', text=self.ui.fgdc_northbc.text(), parent_node=bounding) southbc = xml_node('southbc', text=self.ui.fgdc_southbc.text(), parent_node=bounding) if self.original_xml is not None: boundalt = xml_utils.search_xpath(self.original_xml, 'bounding/boundalt') if boundalt is not None: spdom.append(deepcopy(boundalt)) dsgpoly_list = xml_utils.search_xpath(self.original_xml, 'dsgpoly', only_first=False) for dsgpoly in dsgpoly_list: spdom.append(deepcopy(dsgpoly)) return spdom def from_xml(self, spdom): self.in_xml_load = False self.original_xml = spdom self.clear_widget() utils.populate_widget(self, spdom) contents = xml_utils.node_to_dict(spdom, add_fgdc=False) if 'bounding' in contents: contents = contents['bounding'] try: if self.all_good_coords(): self.add_rect() self.update_map() else: self.remove_rect() except KeyError: self.remove_rect() self.in_xml_load = True
class HostWindow(QMainWindow): # signals SIGTERM = pyqtSignal() SIGUSR1 = pyqtSignal() # -------------------------------------------------------------------------------------------------------- def __init__(self): QMainWindow.__init__(self) gCarla.gui = self URI = sys.argv[1] # ---------------------------------------------------------------------------------------------------- # Internal stuff self.fCurrentFrame = None self.fDocElemement = None self.fCanSetValues = False self.fNeedsShow = False self.fSizeSetup = False self.fQuitReceived = False self.fWasRepainted = False self.fPlugin = get_plugin_info(URI) self.fPorts = self.fPlugin['ports'] self.fPortSymbols = {} self.fPortValues = {} for port in self.fPorts['control']['input'] + self.fPorts['control']['output']: self.fPortSymbols[port['index']] = port['symbol'] self.fPortValues [port['index']] = port['ranges']['default'] # ---------------------------------------------------------------------------------------------------- # Init pipe if len(sys.argv) == 7: self.fPipeClient = gCarla.utils.pipe_client_new(lambda s,msg: self.msgCallback(msg)) else: self.fPipeClient = None # ---------------------------------------------------------------------------------------------------- # Init Web server self.fWebServerThread = WebServerThread(self) self.fWebServerThread.start() # ---------------------------------------------------------------------------------------------------- # Set up GUI self.setContentsMargins(0, 0, 0, 0) self.fWebview = QWebView(self) #self.fWebview.setAttribute(Qt.WA_OpaquePaintEvent, False) #self.fWebview.setAttribute(Qt.WA_TranslucentBackground, True) self.setCentralWidget(self.fWebview) page = self.fWebview.page() page.setViewportSize(QSize(980, 600)) mainFrame = page.mainFrame() mainFrame.setScrollBarPolicy(Qt.Horizontal, Qt.ScrollBarAlwaysOff) mainFrame.setScrollBarPolicy(Qt.Vertical, Qt.ScrollBarAlwaysOff) palette = self.fWebview.palette() palette.setBrush(QPalette.Base, palette.brush(QPalette.Window)) page.setPalette(palette) self.fWebview.setPalette(palette) settings = self.fWebview.settings() settings.setAttribute(QWebSettings.DeveloperExtrasEnabled, True) self.fWebview.loadFinished.connect(self.slot_webviewLoadFinished) url = "http://127.0.0.1:%s/icon.html#%s" % (PORT, URI) print("url:", url) self.fWebview.load(QUrl(url)) # ---------------------------------------------------------------------------------------------------- # Connect actions to functions self.SIGTERM.connect(self.slot_handleSIGTERM) # ---------------------------------------------------------------------------------------------------- # Final setup self.fIdleTimer = self.startTimer(30) if self.fPipeClient is None: # testing, show UI only self.setWindowTitle("TestUI") self.fNeedsShow = True # -------------------------------------------------------------------------------------------------------- def closeExternalUI(self): self.fWebServerThread.stopWait() if self.fPipeClient is None: return if not self.fQuitReceived: self.send(["exiting"]) gCarla.utils.pipe_client_destroy(self.fPipeClient) self.fPipeClient = None def idleStuff(self): if self.fPipeClient is not None: gCarla.utils.pipe_client_idle(self.fPipeClient) self.checkForRepaintChanges() if self.fSizeSetup: return if self.fDocElemement is None or self.fDocElemement.isNull(): return pedal = self.fDocElemement.findFirst(".mod-pedal") if pedal.isNull(): return size = pedal.geometry().size() if size.width() <= 10 or size.height() <= 10: return # render web frame to image image = QImage(self.fWebview.page().viewportSize(), QImage.Format_ARGB32_Premultiplied) image.fill(Qt.transparent) painter = QPainter(image) self.fCurrentFrame.render(painter) painter.end() #image.save("/tmp/test.png") # get coordinates and size from image #x = -1 #y = -1 #lastx = -1 #lasty = -1 #bgcol = self.fHostColor.rgba() #for h in range(0, image.height()): #hasNonTransPixels = False #for w in range(0, image.width()): #if image.pixel(w, h) not in (0, bgcol): # 0xff070707): #hasNonTransPixels = True #if x == -1 or x > w: #x = w #lastx = max(lastx, w) #if hasNonTransPixels: ##if y == -1: ##y = h #lasty = h # set size and position accordingly #if -1 not in (x, lastx, lasty): #self.setFixedSize(lastx-x, lasty) #self.fCurrentFrame.setScrollPosition(QPoint(x, 0)) #else: # TODO that^ needs work if True: self.setFixedSize(size) # set initial values self.fCurrentFrame.evaluateJavaScript("icongui.setPortValue(':bypass', 0, null)") for index in self.fPortValues.keys(): symbol = self.fPortSymbols[index] value = self.fPortValues[index] self.fCurrentFrame.evaluateJavaScript("icongui.setPortValue('%s', %f, null)" % (symbol, value)) # final setup self.fCanSetValues = True self.fSizeSetup = True self.fDocElemement = None if self.fNeedsShow: self.show() def checkForRepaintChanges(self): if not self.fWasRepainted: return self.fWasRepainted = False if not self.fCanSetValues: return for index in self.fPortValues.keys(): symbol = self.fPortSymbols[index] oldValue = self.fPortValues[index] newValue = self.fCurrentFrame.evaluateJavaScript("icongui.getPortValue('%s')" % (symbol,)) if oldValue != newValue: self.fPortValues[index] = newValue self.send(["control", index, newValue]) # -------------------------------------------------------------------------------------------------------- @pyqtSlot(bool) def slot_webviewLoadFinished(self, ok): page = self.fWebview.page() page.repaintRequested.connect(self.slot_repaintRequested) self.fCurrentFrame = page.currentFrame() self.fDocElemement = self.fCurrentFrame.documentElement() def slot_repaintRequested(self): if self.fCanSetValues: self.fWasRepainted = True # -------------------------------------------------------------------------------------------------------- # Callback def msgCallback(self, msg): msg = charPtrToString(msg) if msg == "control": index = int(self.readlineblock()) value = float(self.readlineblock()) self.dspParameterChanged(index, value) elif msg == "program": index = int(self.readlineblock()) self.dspProgramChanged(index) elif msg == "midiprogram": bank = int(self.readlineblock()) program = float(self.readlineblock()) self.dspMidiProgramChanged(bank, program) elif msg == "configure": key = self.readlineblock() value = self.readlineblock() self.dspStateChanged(key, value) elif msg == "note": onOff = bool(self.readlineblock() == "true") channel = int(self.readlineblock()) note = int(self.readlineblock()) velocity = int(self.readlineblock()) self.dspNoteReceived(onOff, channel, note, velocity) elif msg == "atom": index = int(self.readlineblock()) size = int(self.readlineblock()) base64atom = self.readlineblock() # nothing to do yet elif msg == "urid": urid = int(self.readlineblock()) uri = self.readlineblock() # nothing to do yet elif msg == "uiOptions": sampleRate = float(self.readlineblock()) useTheme = bool(self.readlineblock() == "true") useThemeColors = bool(self.readlineblock() == "true") windowTitle = self.readlineblock() transWindowId = int(self.readlineblock()) self.uiTitleChanged(windowTitle) elif msg == "show": self.uiShow() elif msg == "focus": self.uiFocus() elif msg == "hide": self.uiHide() elif msg == "quit": self.fQuitReceived = True self.uiQuit() elif msg == "uiTitle": uiTitle = self.readlineblock() self.uiTitleChanged(uiTitle) else: print("unknown message: \"" + msg + "\"") # -------------------------------------------------------------------------------------------------------- def dspParameterChanged(self, index, value): self.fPortValues[index] = value if self.fCurrentFrame is not None and self.fCanSetValues: symbol = self.fPortSymbols[index] self.fCurrentFrame.evaluateJavaScript("icongui.setPortValue('%s', %f, null)" % (symbol, value)) def dspProgramChanged(self, index): return def dspMidiProgramChanged(self, bank, program): return def dspStateChanged(self, key, value): return def dspNoteReceived(self, onOff, channel, note, velocity): return # -------------------------------------------------------------------------------------------------------- def uiShow(self): if self.fSizeSetup: self.show() else: self.fNeedsShow = True def uiFocus(self): if not self.fSizeSetup: return self.setWindowState((self.windowState() & ~Qt.WindowMinimized) | Qt.WindowActive) self.show() self.raise_() self.activateWindow() def uiHide(self): self.hide() def uiQuit(self): self.closeExternalUI() self.close() app.quit() def uiTitleChanged(self, uiTitle): self.setWindowTitle(uiTitle) # -------------------------------------------------------------------------------------------------------- # Qt events def closeEvent(self, event): self.closeExternalUI() QMainWindow.closeEvent(self, event) # there might be other qt windows open which will block carla-modgui from quitting app.quit() def timerEvent(self, event): if event.timerId() == self.fIdleTimer: self.idleStuff() QMainWindow.timerEvent(self, event) # -------------------------------------------------------------------------------------------------------- @pyqtSlot() def slot_handleSIGTERM(self): print("Got SIGTERM -> Closing now") self.close() # -------------------------------------------------------------------------------------------------------- # Internal stuff def readlineblock(self): if self.fPipeClient is None: return "" return gCarla.utils.pipe_client_readlineblock(self.fPipeClient, 5000) def send(self, lines): if self.fPipeClient is None or len(lines) == 0: return gCarla.utils.pipe_client_lock(self.fPipeClient) # this must never fail, we need to unlock at the end try: for line in lines: if line is None: line2 = "(null)" elif isinstance(line, str): line2 = line.replace("\n", "\r") elif isinstance(line, bool): line2 = "true" if line else "false" elif isinstance(line, int): line2 = "%i" % line elif isinstance(line, float): line2 = "%.10f" % line else: print("unknown data type to send:", type(line)) return gCarla.utils.pipe_client_write_msg(self.fPipeClient, line2 + "\n") except: pass gCarla.utils.pipe_client_flush_and_unlock(self.fPipeClient)
#!/usr/bin/python import os from PyQt5.QtCore import QUrl from PyQt5.QtWebKit import QWebSettings from PyQt5.QtWebKitWidgets import QWebView from PyQt5.QtWidgets import QApplication app = QApplication(['mockup']) view = QWebView() settings = view.settings() settings.setAttribute(QWebSettings.DeveloperExtrasEnabled, True) view.load(QUrl.fromLocalFile(os.path.realpath('mockup.html'))) view.show() app.exec_()
class DrrrWindow(ShadowsWindow): def __init__(self): super(DrrrWindow, self).__init__() self.setWindowTitle("Drrr Chat Room") self.setWindowIcon(QIcon('./img/drrr.ico')) # w = WebView() # w.show() self.getSetting() self.WebView = QWebView() # self.WebView.load(QUrl("file:///E:/Project/DrrrPC/img/index.html")) self.WebView.setZoomFactor(0.8) self.WebView.loadStarted.connect(self.loadStarted) self.WebView.loadFinished.connect(self.loadFinished) self.WebView.loadProgress.connect(self.loading) # self.connect(self.WebView, SIGNAL("loadStarted(bool)"), self.loadStarted) # self.connect(self.WebView, SIGNAL("loadFinished(bool)"), self.loadFinished) # self.connect(self.WebView, SIGNAL("loadProgress(int)"), self.loading) self.cookieJar = QNetworkCookieJar() self.WebView.page().networkAccessManager().setCookieJar(self.cookieJar) # self.WebView.page().setLinkDelegationPolicy(QWebPage.DelegateAllLinks) self.WebView.page().linkClicked.connect(self.linkClicked) self.WebView.page().contentsChanged.connect(self.contentsChanged) # self.WebView.page().networkAccessManager().setHeader(QNetworkRequest.ContentTypeHeader, QVariant("text/html; charset=GBK")) self.WebView.page().javaScriptAlert = self._javascript_alert self.WebView.page().javaScriptConsoleMessage = self._javascript_console_message self.WebView.page().javaScriptConfirm = self._javascript_confirm self.WebView.page().javaScriptPrompt = self._javascript_prompt # NetworkAccessManager # self.NetworkAccessManager = QNetworkAccessManager() # self.WebView.page().setNetworkAccessManager(self.NetworkAccessManager) # self.NetworkAccessManager.finished.connect(self.NetworkAccessManagerReplyFinished) # self.NetworkAccessManager.get(QNetworkRequest(QUrl("http://www.baidu.com"))) self.old_manager = self.WebView.page().networkAccessManager() self.new_manager = NetworkAccessManager(self.old_manager) self.WebView.page().setNetworkAccessManager(self.new_manager) self.titlebar = titleBar() self.statusBar = StatusWindow() # 中心窗口布局 self.contentLayout = QVBoxLayout() self.contentWidget = QWidget() self.contentWidget.gridLayout = QtWidgets.QGridLayout(self.contentWidget) self.contentWidget.gridLayout.addLayout(self.contentLayout, 0, 0, 1, 1) self.contentLayout.addWidget(self.WebView) self.contentWidget.gridLayout.setContentsMargins(0,0,0,0) self.contentLayout.setContentsMargins(1,0,1,0) self.contentWidget.setStyleSheet(""" border-left: 1px solid black; border-right: 1px solid black; """) # self.titlebar.titlebarBotton = QPushButton(self.titlebar) # self.titlebar.titlebarBotton.setText('Push ME') # self.titlebar.titlebarBotton.clicked.connect(self.getData) self.main_layout = QVBoxLayout() self.main_layout.addWidget(self.titlebar) self.main_layout.addWidget(self.contentWidget) self.main_layout.addWidget(self.statusBar) self.main_layout.setSpacing(0) # 窗口属性 self.setWindowFlags(Qt.Widget | QtCore.Qt.FramelessWindowHint) self.setAttribute(Qt.WA_NoSystemBackground, True) self.setAttribute(QtCore.Qt.WA_TranslucentBackground,True) self.widget = QWidget() self.setCentralWidget(self.widget) self.widget.setLayout(self.main_layout) self.widget.setMouseTracking(True) # self.resize(500,650) self.resize(650,650) # self.setMaximumHeight(660) self.center() # 功能性功能开始 self.titlebar.min_button.clicked.connect(self.hideIt) self.titlebar.max_button.clicked.connect(self.MaxAndNormal) self.titlebar.close_button.clicked.connect(self.closeIt) # notice sound # self.player = self.WebView.setHtml(WaitingHTML) self.show() self.WebView.setStyleSheet(""" QWebView { background-color:black } QWebView::QScrollBar:Vertical { background-color:black } """) self.WebView.load(QUrl("http://drrr.com/")) def center(self,screenNum=0): '''多屏居中支持''' self.desktop = QApplication.desktop() screen = self.desktop.availableGeometry(screenNum) size = self.geometry() self.normalGeometry2 = QtCore.QRect((screen.width()-size.width())/2+screen.left(), (screen.height()-size.height())/2, size.width(),size.height()) self.setGeometry((screen.width()-size.width())/2+screen.left(), (screen.height()-size.height())/2, size.width(),size.height()) def keyPressEvent(self,event): # F11全屏切换 if event.key()==QtCore.Qt.Key_F11: self.MaxAndNormal() if event.key()==QtCore.Qt.Key_F4: self.WebView.page().mainFrame().setScrollBarPolicy(Qt.Vertical, Qt.ScrollBarAlwaysOff) def getData(self): # print self.bbb == None # print str(self.bbb) pass @QtCore.pyqtSlot(str) def play(self,content): # ["bubble","userin","userout"] print content QtMultimedia.QSound.play("./img/"+content+".wav") def readyRead(self): pass # print self.NetworkAccessManager.readAll() def NetworkAccessManagerReplyFinished(self,response): # NO USE # print response.readAll() # print response.header(QNetworkRequest.ContentTypeHeader) # print response.url() # self.bbb = response.readAll() response.deleteLater() def contentsChanged(self): # print 'contentsChanged' pass def _javascript_alert(self, webframe, message): print '_javascript_alert' def _javascript_console_message(self, message, line, sourceid): print "_javascript_console_message" def _javascript_confirm(self, webframe, message): print "_javascript_confirm" return QWebPage.javaScriptConfirm(self.WebView.page(), webframe, message) def _javascript_prompt(self, webframe, message, defaultvalue, result): print "_javascript_prompt" def linkClicked(self,url): print url def getSetting(self): '''获取应用设置''' self.settings = QtCore.QSettings("DrrrChatRoom.ini", QtCore.QSettings.IniFormat) def loadStarted(self): if 'http://drrr.com/' == str(self.WebView.url().toString()): frame = self.WebView.page().mainFrame() name = frame.findFirstElement("input#form-name.home-name-input") username = name.evaluateJavaScript("this.value") print username language = frame.findFirstElement("#form-language-select") language = language.evaluateJavaScript("this.value") print language frame.evaluateJavaScript(""" var iconFun = function(){ var elementsLI = document.getElementsByTagName('li') var length = document.getElementsByTagName('li').length; for(var i = 0; i < length ; ++i){ if(elementsLI[i].getElementsByTagName('div')[0].className.indexOf("active")>=0){ var icon = elementsLI[i].getElementsByTagName('input')[0].value; } } return icon }; """) icon = frame.evaluateJavaScript("""iconFun()""") print icon if username:self.settings.setValue('username',username) if language:self.settings.setValue("language",language) if icon: # self.settings.setValue("icon",icon) pass else: if self.settings.value('icon', None): icon = self.settings.value('icon',None) frame.findFirstElement('input[value="'+icon+'"]').evaluateJavaScript("this.click()") if "http://drrr.com/room/?ajax=1" in str(self.WebView.url().toString()): # print "quit room" pass print 'requestedUrl:' + self.WebView.page().mainFrame().requestedUrl().toString() def loadFinished(self, flag): self.statusBar.status.setText(u"Connected") # http://drrr.com/ if 'http://drrr.com/' == str(self.WebView.url().toString()): frame = self.WebView.page().mainFrame() name = frame.findFirstElement("input#form-name.home-name-input") if self.settings.value('username', None): name.setAttribute('value',self.settings.value('username', None)) language = frame.findFirstElement("#form-language-select") if self.settings.value('language', None): language.evaluateJavaScript(''' sel = document.getElementById("form-language-select"); for(var i = 0, j = sel.options.length; i < j; ++i) { if(sel.options[i].value === "'''+self.settings.value('language', "zh-CN")+'''") { sel.selectedIndex = i; break; } } ''') # language.setAttribute('value',self.settings.value('language', None)) if self.settings.value('icon', None): icon = self.settings.value('icon',None) frame.findFirstElement('input[value="'+icon+'"]').evaluateJavaScript("this.click()") # http://drrr.com/create_room/ if 'http://drrr.com/room/' in str(self.WebView.url().toString()): frame = self.WebView.page().mainFrame() frame.addToJavaScriptWindowObject("drrrWindow", self) frame.evaluateJavaScript(''' var volumeFun = function(b){ return b } ''') frame.evaluateJavaScript(''' var playFun = function(a){ this.volume = volumeFun; drrrWindow.play(a); return this }; ''') frame.evaluateJavaScript('''sound.play = playFun''') def loading(self, percent): self.statusBar.status.setText("Loading %d%%" % percent) def quit(self): sys.exit(0) # QtCore.QCoreApplication.instance().quit() def closeIt(self): self.animation = QtCore.QPropertyAnimation(self,"windowOpacity") self.animation.finished.connect(QtCore.QCoreApplication.instance().quit) self.animation.finished.connect(self.quit) self.animation.setDuration(300) self.animation.setStartValue(1) self.animation.setEndValue(0) self.animation.start() def hideIt(self): self.animation = QtCore.QPropertyAnimation(self,"windowOpacity") self.animation.finished.connect(self.showMinimized2) self.animation.setDuration(300) self.animation.setStartValue(1) self.animation.setEndValue(0) self.animation.start() def leaveEvent(self,event): self.setCursor(QtCore.Qt.ArrowCursor) def keyPressEvent(self,event): # F11全屏切换 if event.key()==QtCore.Qt.Key_F11: self.MaxAndNormal2() def MaxAndNormal2(self): '''全屏与正常大小间切换函数''' if self.showNormal3(): self.showFullScreen3() self.titlebar.hide() self.statusBar.hide() else: self.titlebar.show() self.statusBar.show() def MaxAndNormal(self): '''最大化与正常大小间切换函数''' if self.showNormal3(): self.showFullScreen3() def showEvent(self,event): self.animation = QtCore.QPropertyAnimation(self,"windowOpacity") self.animation.setDuration(300) self.animation.setStartValue(0) self.animation.setEndValue(1) self.animation.start() def showNormal2(self): self.showNormal() self.animationEndFlag = 1 # 动画停止 def showNormal3(self): if self.isFullScreen(): self.main_layout.setContentsMargins(10,7,10,7) self.animation = QtCore.QPropertyAnimation(self,"geometry") self.animation.setDuration(180) self.animation.setEndValue(self.normalGeometry2) self.animation.setStartValue(self.desktop.availableGeometry(self.desktop.screenNumber(self.widget))) self.animation.finished.connect(self.showNormal2) self.animationEndFlag = 0 self.animation.start() return 0 return 1 def showFullScreen2(self): self.animationEndFlag = 1 # 动画停止 self.showFullScreen() def showFullScreen3(self): if not self.isFullScreen(): self.main_layout.setContentsMargins(0,0,0,0) self.animation = QtCore.QPropertyAnimation(self,"geometry") self.animation.setDuration(180) self.animation.setStartValue(self.geometry()) self.animation.setEndValue(self.desktop.availableGeometry(self.desktop.screenNumber(self.widget))) self.animation.finished.connect(self.showFullScreen2) self.animationEndFlag = 0 self.animation.start() def showMinimized2(self): self.setWindowOpacity(1) self.showMinimized()
class EbookScramblePreviewDlg(QDialog): def __init__(self, ebook, orig, is_scrambled, fmap, parent=None): QDialog.__init__(self, parent=parent) self.setWindowFlags(Qt.Window) self.orig = orig self.ebook = ebook self.revfmap = {v: k for (k, v) in iteritems(fmap)} # create widgets lay = QVBoxLayout() self.setLayout(lay) buttonBox = QDialogButtonBox(QDialogButtonBox.Close) self.webview_orig = Webview() self.webview_scram = Webview() settings = self.webview_orig.settings() if hasattr(settings, 'setUserStyleSheetUrl'): # QWebView from QtWebKit style = 'body {%s}' % CSSBG cssurl = 'data:text/css;charset=utf-8;base64,' cssurl += as_base64_unicode(style) self.webview_orig.settings().setUserStyleSheetUrl(QUrl(cssurl)) self.webview_scram.settings().setUserStyleSheetUrl(QUrl(cssurl)) elif hasattr(self.webview_orig, 'setStyleSheet'): # QWebEngineView from QtWebEngine # setStyleSheet doesn't seem to work at the moment self.webview_orig.setStyleSheet('Webview {%s}' % CSSBG) self.webview_scram.setStyleSheet('Webview {%s}' % CSSBG) dummytext = '<body><p>*** Text content could not be displayed ...</p></body>' self.webview_orig.setHtml(dummytext) self.webview_scram.setHtml(dummytext) self.htmlList_orig = QListWidget() self.htmlList_scram = QListWidget() self.htmlList_orig.setMinimumWidth(300) self.htmlList_scram.setMinimumWidth(300) gpbox1 = QGroupBox() lay1 = QHBoxLayout() gpbox1.setLayout(lay1) lay1.addWidget(self.htmlList_orig) gpbox3 = QGroupBox() lay3 = QHBoxLayout() gpbox3.setLayout(lay3) lay3.addWidget(self.htmlList_scram) gpbox2 = QGroupBox('Original text content:') lay2 = QHBoxLayout() gpbox2.setLayout(lay2) lay2.addWidget(self.webview_orig) gpbox4 = QGroupBox('Original text content:') lay4 = QHBoxLayout() gpbox4.setLayout(lay4) lay4.addWidget(self.webview_scram) splitter = QSplitter(Qt.Horizontal) splitter.addWidget(gpbox1) splitter.addWidget(gpbox2) splitter.addWidget(gpbox3) splitter.addWidget(gpbox4) lay.addWidget(splitter) lay.addWidget(buttonBox) # create connect signals/slots buttonBox.rejected.connect(self.reject) self.htmlList_scram.currentRowChanged.connect( self.htmlList_currentRowChanged) self.htmlList_scram.itemDoubleClicked.connect( self.htmlList_itemDoubleClicked) self.htmlList_orig.setEnabled(False) self.htmlnames_scram = get_textnames(self.ebook) self.htmlnames_orig = tuple( [self.revfmap.get(an, an) for an in self.htmlnames_scram]) gpbox1.setTitle('Original HTML files: %s' % len(self.htmlnames_orig)) gpbox3.setTitle('Original HTML files: %s' % len(self.htmlnames_scram)) self.htmlList_orig.clear() self.htmlList_orig.addItems(self.htmlnames_orig) self.htmlList_scram.clear() self.htmlList_scram.addItems(self.htmlnames_scram) if not self.revfmap: gpbox1.setVisible(False) msg = '%s Preview: Original' % CAPTION if not is_scrambled: self.setWindowTitle(msg) gpbox1.setVisible(False) gpbox2.setVisible(False) else: self.setWindowTitle(msg + ' vs. Scrambled') gpbox3.setTitle('Scrambled HTML files: %s' % len(self.htmlnames_scram)) gpbox4.setTitle('Scrambled text content:') self.htmlList_scram.setCurrentRow(0) def htmlList_currentRowChanged(self, row): if row < 0: return name = self.htmlList_scram.currentItem().text() self.htmlList_orig.setCurrentRow(row) self.webview_refresh(name) def htmlList_itemDoubleClicked(self, item): name = item.text() self.webview_refresh(name) def webview_refresh(self, name): name_orig = self.revfmap.get(name, name) abspath_orig = self.orig.name_to_abspath(name_orig) abspath = self.ebook.name_to_abspath(name) if isinstance(self.webview_orig, QTextBrowser): self.webview_orig.setSource(QUrl.fromLocalFile(abspath_orig)) self.webview_scram.setSource(QUrl.fromLocalFile(abspath)) else: self.webview_orig.load(QUrl.fromLocalFile(abspath_orig)) self.webview_scram.load(QUrl.fromLocalFile(abspath))
import sys from PyQt5.QtWidgets import QMainWindow, QApplication, QWidget, QPushButton, QAction from PyQt5.QtCore import * from PyQt5.QtWebKitWidgets import QWebView, QWebPage app = QApplication(sys.argv) browser = QWebView() browser.load(QUrl("https://www.google.com")) browser.show() app.exec_()
from PyQt5.QtWidgets import QApplication from PyQt5.QtWebKitWidgets import QWebView from PyQt5.QtCore import QUrl app = QApplication([]) view = QWebView() view.load(QUrl("http://weixin.sogou.com")) view.show() app.exec_() # import sys # from PyQt5.QtWidgets import * # from PyQt5.QtCore import * # from PyQt5.QtWebKitWidgets import QWebPage # from lxml import etree # from lxml.etree import XPathEvalError # # #use QtWebkit to get the final webpage # app = QApplication([]) # # # def create_tree(html): # ''' # :param html:HTML或者XML字符串 # :return:tree树形结构对象 # ''' # try: # root = etree.HTML(html) # except ValueError: # root = etree.XML(html) # tree = etree.ElementTree(root)
from PyQt5.QtWidgets import QApplication from PyQt5.QtWebKit import QWebSettings from PyQt5.QtWebKitWidgets import QWebView def parse_args(): """Parse commandline arguments.""" parser = argparse.ArgumentParser() parser.add_argument('url', help='The URL to open') parser.add_argument('--plugins', '-p', help='Enable plugins', default=False, action='store_true') return parser.parse_known_args()[0] if __name__ == '__main__': args = parse_args() app = QApplication(sys.argv) wv = QWebView() wv.loadStarted.connect(lambda: print("Loading started")) wv.loadProgress.connect(lambda p: print("Loading progress: {}%".format(p))) wv.loadFinished.connect(lambda: print("Loading finished")) if args.plugins: wv.settings().setAttribute(QWebSettings.PluginsEnabled, True) wv.load(QUrl.fromUserInput(args.url)) wv.show() app.exec_()
class MainWindow(QMainWindow): def __init__(self, url): super(MainWindow, self).__init__() self.progress = 0 fd = QFile(":/jquery.min.js") if fd.open(QIODevice.ReadOnly | QFile.Text): self.jQuery = QTextStream(fd).readAll() fd.close() else: self.jQuery = '' QNetworkProxyFactory.setUseSystemConfiguration(True) self.view = QWebView(self) self.view.load(url) self.view.loadFinished.connect(self.adjustLocation) self.view.titleChanged.connect(self.adjustTitle) self.view.loadProgress.connect(self.setProgress) self.view.loadFinished.connect(self.finishLoading) self.locationEdit = QLineEdit(self) self.locationEdit.setSizePolicy(QSizePolicy.Expanding, self.locationEdit.sizePolicy().verticalPolicy()) self.locationEdit.returnPressed.connect(self.changeLocation) toolBar = self.addToolBar("Navigation") toolBar.addAction(self.view.pageAction(QWebPage.Back)) toolBar.addAction(self.view.pageAction(QWebPage.Forward)) toolBar.addAction(self.view.pageAction(QWebPage.Reload)) toolBar.addAction(self.view.pageAction(QWebPage.Stop)) toolBar.addWidget(self.locationEdit) viewMenu = self.menuBar().addMenu("&View") viewSourceAction = QAction("Page Source", self) viewSourceAction.triggered.connect(self.viewSource) viewMenu.addAction(viewSourceAction) effectMenu = self.menuBar().addMenu("&Effect") effectMenu.addAction("Highlight all links", self.highlightAllLinks) self.rotateAction = QAction( self.style().standardIcon(QStyle.SP_FileDialogDetailedView), "Turn images upside down", self, checkable=True, toggled=self.rotateImages) effectMenu.addAction(self.rotateAction) toolsMenu = self.menuBar().addMenu("&Tools") toolsMenu.addAction("Remove GIF images", self.removeGifImages) toolsMenu.addAction("Remove all inline frames", self.removeInlineFrames) toolsMenu.addAction("Remove all object elements", self.removeObjectElements) toolsMenu.addAction("Remove all embedded elements", self.removeEmbeddedElements) self.setCentralWidget(self.view) def viewSource(self): accessManager = self.view.page().networkAccessManager() request = QNetworkRequest(self.view.url()) reply = accessManager.get(request) reply.finished.connect(self.slotSourceDownloaded) def slotSourceDownloaded(self): reply = self.sender() self.textEdit = QTextEdit() self.textEdit.setAttribute(Qt.WA_DeleteOnClose) self.textEdit.show() self.textEdit.setPlainText(QTextStream(reply).readAll()) self.textEdit.resize(600, 400) reply.deleteLater() def adjustLocation(self): self.locationEdit.setText(self.view.url().toString()) def changeLocation(self): url = QUrl.fromUserInput(self.locationEdit.text()) self.view.load(url) self.view.setFocus() def adjustTitle(self): if 0 < self.progress < 100: self.setWindowTitle("%s (%s%%)" % (self.view.title(), self.progress)) else: self.setWindowTitle(self.view.title()) def setProgress(self, p): self.progress = p self.adjustTitle() def finishLoading(self): self.progress = 100 self.adjustTitle() self.view.page().mainFrame().evaluateJavaScript(self.jQuery) self.rotateImages(self.rotateAction.isChecked()) def highlightAllLinks(self): code = """$('a').each( function () { $(this).css('background-color', 'yellow') } )""" self.view.page().mainFrame().evaluateJavaScript(code) def rotateImages(self, invert): if invert: code = """ $('img').each( function () { $(this).css('-webkit-transition', '-webkit-transform 2s'); $(this).css('-webkit-transform', 'rotate(180deg)') } )""" else: code = """ $('img').each( function () { $(this).css('-webkit-transition', '-webkit-transform 2s'); $(this).css('-webkit-transform', 'rotate(0deg)') } )""" self.view.page().mainFrame().evaluateJavaScript(code) def removeGifImages(self): code = "$('[src*=gif]').remove()" self.view.page().mainFrame().evaluateJavaScript(code) def removeInlineFrames(self): code = "$('iframe').remove()" self.view.page().mainFrame().evaluateJavaScript(code) def removeObjectElements(self): code = "$('object').remove()" self.view.page().mainFrame().evaluateJavaScript(code) def removeEmbeddedElements(self): code = "$('embed').remove()" self.view.page().mainFrame().evaluateJavaScript(code)
class HTMLViewer(vip_base): def cb_initialize_plugin(self): # --------------------------- # Read configuration # --------------------------- self.config = self.pl_get_current_config_ref() content = self.config['content']['value'] isUrl = self.config['isUrl']['value'] # -------------------------------- # Create Widget # -------------------------------- self.WebView = QWebView() # This call is important, because the background structure needs to know the used widget! # In the background the qmidiwindow will becreated and the widget will be added self.pl_set_widget_for_internal_usage(self.WebView) print(isUrl) if isUrl == '1': url = QtCore.QUrl(content) self.WebView.load(url) else: self.WebView.setHtml(content) self.WebView.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) self.WebView.customContextMenuRequested.connect(self.show_context_menu) return True def show_context_menu(self, pos): gloPos = self.WebView.mapToGlobal(pos) self.cmenu = self.pl_create_control_context_menu() self.cmenu.exec_(gloPos) def cb_pause(self): # will be called, when plugin gets paused # can be used to get plugin in a defined state before pause # e.a. pass def cb_resume(self): # will be called when plugin gets resumed # can be used to wake up the plugin from defined pause state # e.a. reopen communication ports, files etc. pass def cb_execute(self, Data=None, block_name=None, plugin_uname=None): # Do main work here! # If this plugin is an IOP plugin, then there will be no Data parameter because it wont get data # If this plugin is a DPP, then it will get Data with data # param: Data is a Data hash and block_name is the block_name of Data origin # Data is a hash, so use ist like: Data[CORE_TIME_SIGNAL] = [t1, t2, ...] where CORE_TIME_SIGNAL is a signal_name # hash signal_name: value # Data could have multiple types stored in it e.a. Data['d1'] = int, Data['d2'] = [] pass def cb_set_parameter(self, name, value): # attetion: value is a string and need to be processed ! # if name == 'irgendeinParameter': # do that .... with value pass def cb_quit(self): # do something before plugin will close, e.a. close connections ... pass def cb_get_plugin_configuration(self): # # Implement a own part of the config # config is a hash of hass object # config_parameter_name : {} # config[config_parameter_name]['value'] NEEDS TO BE IMPLEMENTED # configs can be marked as advanced for create dialog # http://utilitymill.com/utility/Regex_For_Range config = { "content": { 'value': """<p> Insert your html code here </p>""", 'display_text': 'HTML Content', 'advanced': 'HTLM', 'tooltip': 'Plain html code to be displayed' }, "isUrl": { 'value': "0", 'display_text': "Content==Url?", 'tooltip': "Set to 1 if the content is an url that should be loaded", 'advanced': 'HTML' }, } return config def cb_plugin_meta_updated(self): """ Whenever the meta information is updated this function is called (if implemented). :return: """ #dplugin_info = self.dplugin_info pass
class QuestionnaireProcessorUI(QtWidgets.QMainWindow): """ QT User interface """ def __init__(self): """ Initialize Questionnaire Processor UI """ super(QuestionnaireProcessorUI, self).__init__() self._initConf() self._initHomeApp() self._initLogging() self._initDefaultValues() self._initUI() def _initConf(self): """ Initialize config file """ self.conf_default_io = config['default_io'] self.conf_questionnaireprocessor_ui = config['questionnaireprocessor_ui'] self.conf_ui = config['ui'] self.conf_default_input = config['default_input'] self.conf_format = config['format'] def _initHomeApp(self): """ Initializes paths of the application """ self.homeFolder = os.path.expanduser("~") homeAppFolderName = self.conf_default_io['homeAppFolderName'] homeAppLogFolder = self.conf_default_io['homeAppLogFolder'] self.homeAppFolder = os.path.join(self.homeFolder, homeAppFolderName) self.homeAppLogFolder = os.path.join(self.homeAppFolder, homeAppLogFolder) if not os.path.exists(self.homeAppFolder): os.mkdir(self.homeAppFolder) if not os.path.exists(self.homeAppLogFolder): os.mkdir(self.homeAppLogFolder) def _initLogging(self): """ Initializes paths of the application """ fileName = 'errors_' + windowTitle.replace(' ', '-').lower() + '.log' errorLogPath = os.path.join(self.homeAppLogFolder, fileName) if debug: level = logging.DEBUG else: level = logging.ERROR configureLogging(errorLogPath, level) def _initDefaultValues(self): """ Initializes default values """ # Load resources self.idKey = self.conf_default_input['idKey'] self.responseKey = self.conf_default_input['responseKey'] self.categoryKey = self.conf_default_input['categoryKey'] self.answerKey = self.conf_default_input['answerKey'] self.scoreKey = self.conf_default_input['scoreKey'] ## remove illegal characters self.illegalCharacterList = self.conf_format['illegalCharacterList'] self.defaultIdentityList = self.conf_default_input['defaultIdList'] self.defaultCategoryList = self.conf_default_input['defaultCategoryList'] self.defaultAnswerString = self.conf_default_input['defaultAnswerString'] self.defaultScoreList = self.conf_default_input['defaultScoreList'] # Load resource paths self.uiPath = getResourceLoc(self.conf_questionnaireprocessor_ui['uiPath']) self.icoPath = getResourceLoc(self.conf_questionnaireprocessor_ui['icoPath']) self.helpimgPath = getResourceLoc(self.conf_ui['helpimgPath']) self.aboutimgPath = getResourceLoc(self.conf_ui['aboutimgPath']) self.labelimgPath = getResourceLoc(self.conf_questionnaireprocessor_ui['labelimgPath']) # default folders self.sourceFolder = "" self.destinationFolder = "" self._lastSelectedDestDir = "" self._lastSelectedSourceDir = "" # default text self.windowTitle = self.conf_questionnaireprocessor_ui['windowTitle'] self.StatusBoxHeight = int(self.conf_questionnaireprocessor_ui['StatusBoxHeight']) def _initUI(self): """ Initializes the UI, sets button actions and default values """ # icons self.helpIcon = QtGui.QIcon(self.helpimgPath) self.aboutIcon = QtGui.QIcon(self.aboutimgPath) self.windowIcon = QtGui.QIcon(self.icoPath) # images self.pixmap = QtGui.QPixmap(self.labelimgPath) # Load and setup UI uic.loadUi(self.uiPath, self) self.windowHorizontalResolution = self.width() if verbose: self.windowVerticalResolution = self.height() else: self.windowVerticalResolution = self.height() - self.StatusBoxHeight # set default window values self.setWindowIcon(self.windowIcon) self.setFixedSize(self.windowHorizontalResolution,self.windowVerticalResolution) self.setWindowTitle(self.windowTitle) self.center() # set icons self.docButton.setIcon(self.helpIcon) self.aboutButton.setIcon(self.aboutIcon) # set default values self.idColumnLineEdit.setText(self.idKey) self.responseColumnLineEdit.setText(self.responseKey) self.categoryColumnLineEdit.setText(self.categoryKey) self.answerOptionColumnLineEdit.setText(self.answerKey) self.answerScoreColumnLineEdit.setText(self.scoreKey) self.idCustomPlainTextEdit.setPlainText('\n'.join(self.defaultIdentityList)) self.categoryCustomPlainTextEdit.setPlainText('\n'.join(self.defaultCategoryList)) self.answerCustomLineEdit.setText(self.defaultAnswerString) self.scoreCustomPlainTextEdit.setPlainText('\n'.join(self.defaultScoreList)) self.caseInsensitiveCheckBox.setChecked(True) # set statusbox self.statusBox.setReadOnly(True) self.statusBox.hide() # set GUI image self.image.setPixmap(self.pixmap) ## hide custom column elements self.idColumnLabel.hide() self.idColumnLineEdit.hide() self.responseColumnLabel.hide() self.responseColumnLineEdit.hide() self.categoryColumnLabel.hide() self.categoryColumnLineEdit.hide() self.answerOptionColumnLabel.hide() self.answerOptionColumnLineEdit.hide() self.answerScoreColumnLabel.hide() self.answerScoreColumnLineEdit.hide() #self.line_2.hide() ## hide custom score elements self.idCustomLabel.hide() self.idCustomPlainTextEdit.hide() self.categoryCustomLabel.hide() self.categoryCustomPlainTextEdit.hide() self.answerCustomLabel.hide() self.answerCustomLineEdit.hide() self.scoreCustomLabel.hide() self.scoreCustomPlainTextEdit.hide() self.customExperimentCheckBox.hide() # Set button actions self.inputFolderButton.clicked.connect(self.selectInputFolderLocation) self.outputFolderButton.clicked.connect(self.selectOutputFolderDestination) self.processButton.clicked.connect(self.startAnalysis) self.docButton.clicked.connect(self.showDocWindow) self.aboutButton.clicked.connect(self.showAboutWindow) # Set checkbox actions self.customColumnCheckBox.stateChanged.connect(self.updateCustomColumnWidgets) self.customExperimentCheckBox.stateChanged.connect(self.updateCustomExperimentWidgets) if verbose: self.statusBox.show() self.label.hide() else: pass if verbose and not debug: # Redirect console output to textbox in UI, printing stdout in black # and stderr in red sys.stdout = OutLog(self.statusBox, sys.stdout, QtGui.QColor(0,0,0)) if not hasattr(sys,'frozen'): sys.stderr = OutLog(self.statusBox, sys.stderr, QtGui.QColor(255,0,0)) else: sys.stderr = OutLog(self.statusBox, None, QtGui.QColor(255,0,0)) print("") else: pass def startAnalysis(self): """ Starts sanity checks and if passed starts the analyze operation. """ # check if sourceFolder is selected if self.sourceFolder == "": errorMessage = "Please select a source folder containing the data files to merge." print(errorMessage, file=sys.stderr) self.showErrorMessage(errorMessage) return # check if destinationFolder is selected elif self.destinationFolder == "": errorMessage = "Please specify a folder to save the results." print(errorMessage, file=sys.stderr) self.showErrorMessage(errorMessage) return else: print("Starting Analyze operation...") # set the default keys responseKey = self.responseKey idKey = self.idKey categoryKey = self.categoryKey answerKey = self.answerKey scoreKey = self.scoreKey idList = None categoryList = None answerList = None scoreList = None caseInsensitiveComparison = self.caseInsensitiveCheckBox.isChecked() if not self.customColumnCheckBox.isChecked(): # calculate score automatically custom = False analyzedDataset = QuestionnaireProcessor(self.sourceFolder, self.destinationFolder, responseKey, idKey, categoryKey, answerKey, scoreKey, idList, categoryList, answerList, scoreList, custom, caseInsensitiveComparison, self) if analyzedDataset: print("Output saved to " + self.destinationFolder) print("Ready.") else: pass elif self.customColumnCheckBox.isChecked() and not self.customExperimentCheckBox.isChecked(): # calculate score with the given column keys responseKey = self.responseColumnLineEdit.text() idKey = self.idColumnLineEdit.text() categoryKey = self.categoryColumnLineEdit.text() answerKey = self.answerOptionColumnLineEdit.text() scoreKey = self.answerScoreColumnLineEdit.text() custom = False ## check for illegal character stringCheck = None checkStringList = [responseKey, idKey, categoryKey, answerKey, scoreKey] for index in range(len(checkStringList)): for illegalCharacter in self.illegalCharacterList: if illegalCharacter in checkStringList[index]: checkStringList[index] = checkStringList[index].replace(illegalCharacter, '') stringCheck = True else: pass if stringCheck: errorMessage = "The following characters are not allowed and have been stripped: double-quote (\") and backslash (\\)" print(errorMessage, file=sys.stderr) self.showErrorMessage(errorMessage) self.responseColumnLineEdit.setText(checkStringList[0]) self.idColumnLineEdit.setText(checkStringList[1]) self.categoryColumnLineEdit.setText(checkStringList[2]) self.answerOptionColumnLineEdit.setText(checkStringList[3]) self.answerScoreColumnLineEdit.setText(checkStringList[4]) return else: pass if responseKey != "" and idKey != "" and categoryKey != "" and answerKey != "" and scoreKey != "": analyzedDataset = QuestionnaireProcessor(self.sourceFolder, self.destinationFolder, responseKey, idKey, categoryKey, answerKey, scoreKey, idList, categoryList, answerList, scoreList, custom, caseInsensitiveComparison, self) if analyzedDataset: print("Output saved to " + self.destinationFolder) print("Ready.") else: pass else: ## show error message if checks failed errorMessage = "Not all column names are defined." print(errorMessage, file=sys.stderr) self.showErrorMessage(errorMessage) return # calculate score with the custom given experiment data elif self.customColumnCheckBox.isChecked() and self.customExperimentCheckBox.isChecked(): # set custom bool custom = True # get values from the widgets responseKey = self.responseColumnLineEdit.text() idKey = self.idColumnLineEdit.text() idString = self.idCustomPlainTextEdit.toPlainText() answerString = self.answerCustomLineEdit.text() categoryString = self.categoryCustomPlainTextEdit.toPlainText() scoreString = self.scoreCustomPlainTextEdit.toPlainText() ## String Checks stringCheck = None checkStringList = [responseKey,idKey,idString, answerString, categoryString, scoreString] for index in range(len(checkStringList)): for illegalCharacter in self.illegalCharacterList: if illegalCharacter in checkStringList[index]: checkStringList[index] = checkStringList[index].replace(illegalCharacter, '') stringCheck = True else: pass if stringCheck: errorMessage = "The following characters are not allowed and have been stripped: double-quote (\"), backslash (\\) and tab" print(errorMessage, file=sys.stderr) self.showErrorMessage(errorMessage) self.responseColumnLineEdit.setText(checkStringList[0]) self.idColumnLineEdit.setText(checkStringList[1]) self.idCustomPlainTextEdit.setPlainText(checkStringList[2]) self.answerCustomLineEdit.setText(checkStringList[3]) self.categoryCustomPlainTextEdit.setPlainText(checkStringList[4]) self.scoreCustomPlainTextEdit.setPlainText(checkStringList[5]) return else: pass ## split string to list and remove last enter idList = (idString[:-1] if idString.endswith('\n') else idString).split('\n') categoryList = (categoryString[:-1] if categoryString.endswith('\n') else categoryString).split('\n') scoreList = (scoreString[:-1] if scoreString.endswith('\n') else scoreString).split('\n') ## clean up items idList = removeJunk(idList) categoryList = cleanUpStringList(categoryList,';') scoreList = cleanUpStringList(scoreList,';') answerString = cleanUpString(answerString,';') ## make answer list for counting answerItemList = answerString.split(';') ## determine number of elements ncategory = len(categoryList) nscore = len(scoreList) nid = len(idList) nanswers = len(answerItemList) ## replicate answer option to a list with length ntrials answerList = [] for index in range(len(idList)): answerList.append(answerString) ## number check nScoreItemList = [] checkList = [] for item in scoreList: scoreItemList = item.split(';') nScoreItemList.append(len(scoreItemList)) checkList.append(all(element.isdigit()==True for element in scoreItemList)) numberCheck = all(item==True for item in checkList) ## check if all score lines have equal number of elements (seperated by ;) and is equal to ## the number of elements of answers uniqueScoreItemList = list(set(nScoreItemList)) if len(uniqueScoreItemList)==1: uniqueItems = uniqueScoreItemList[0] if uniqueItems == nanswers: scoreCheck = True else: scoreCheck = False else: scoreCheck = False ## combine checks and show error if applicable if nid == ncategory == nscore and scoreCheck and numberCheck: analyzedDataset = QuestionnaireProcessor(self.sourceFolder, self.destinationFolder, responseKey, idKey, categoryKey, answerKey, scoreKey, idList, categoryList, answerList, scoreList, custom, caseInsensitiveComparison, self) if analyzedDataset: print("Output saved to " + self.destinationFolder) print("Ready.") else: pass else: ## show error messages if checks failed errorMessageList = [] if not (nid == ncategory == nscore and scoreCheck): errorMessageList.append('- Not all fields have the correct number of elements\n') if not numberCheck: errorMessageList.append('- Field \"score\" should contain only integers seperated by \";\", found other characters\n') self.showErrorMessage(''.join(errorMessageList)) return else: pass def updateCustomColumnWidgets(self): """ Show custom column widgets when checkbox is checked else hide """ if self.customColumnCheckBox.isChecked() : self.idColumnLabel.show() self.idColumnLineEdit.show() self.responseColumnLabel.show() self.responseColumnLineEdit.show() self.categoryColumnLabel.show() self.categoryColumnLineEdit.show() self.answerOptionColumnLabel.show() self.answerOptionColumnLineEdit.show() self.answerScoreColumnLabel.show() self.answerScoreColumnLineEdit.show() #self.line_2.show() self.customExperimentCheckBox.show() self.updateCustomExperimentWidgets() else: self.idColumnLabel.hide() self.idColumnLineEdit.hide() self.responseColumnLabel.hide() self.responseColumnLineEdit.hide() self.categoryColumnLabel.hide() self.categoryColumnLineEdit.hide() self.answerOptionColumnLabel.hide() self.answerOptionColumnLineEdit.hide() self.answerScoreColumnLabel.hide() self.answerScoreColumnLineEdit.hide() #self.line_2.hide() self.customExperimentCheckBox.hide() self.idCustomLabel.hide() self.idCustomPlainTextEdit.hide() self.categoryCustomLabel.hide() self.categoryCustomPlainTextEdit.hide() self.answerCustomLabel.hide() self.answerCustomLineEdit.hide() self.scoreCustomLabel.hide() self.scoreCustomPlainTextEdit.hide() self.customExperimentCheckBox.hide() def updateCustomExperimentWidgets(self): """ Show custom experiment widgets (and hide some column widget elements) when checkbox is checked else hide """ if self.customExperimentCheckBox.isChecked(): self.idCustomLabel.show() self.idCustomPlainTextEdit.show() self.categoryCustomLabel.show() self.categoryCustomPlainTextEdit.show() self.answerCustomLabel.show() self.answerCustomLineEdit.show() self.scoreCustomLabel.show() self.scoreCustomPlainTextEdit.show() self.categoryColumnLabel.hide() self.categoryColumnLineEdit.hide() self.answerOptionColumnLabel.hide() self.answerOptionColumnLineEdit.hide() self.answerScoreColumnLabel.hide() self.answerScoreColumnLineEdit.hide() else: self.idCustomLabel.hide() self.idCustomPlainTextEdit.hide() self.categoryCustomLabel.hide() self.categoryCustomPlainTextEdit.hide() self.answerCustomLabel.hide() self.answerCustomLineEdit.hide() self.scoreCustomLabel.hide() self.scoreCustomPlainTextEdit.hide() self.categoryColumnLabel.show() self.categoryColumnLineEdit.show() self.answerOptionColumnLabel.show() self.answerOptionColumnLineEdit.show() self.answerScoreColumnLabel.show() self.answerScoreColumnLineEdit.show() def selectInputFolderLocation(self): """ Select folder to read csv files from """ selectedFolder = QtWidgets.QFileDialog.getExistingDirectory(self, "Select Directory", directory=self.inputFolderLocation.text()) # Prevent erasing previous entry on cancel press if selectedFolder: self.sourceFolder = selectedFolder self.inputFolderLocation.setText(os.path.normpath(self.sourceFolder)) self.progressBar.setValue(0) def selectOutputFolderDestination(self): """ Set folder to write output to """ selectedDest = QtWidgets.QFileDialog.getExistingDirectory(self,"Save output in..", directory=self.outputFolderDestination.text()) # Prevent erasing previous entry on cancel press if selectedDest: self.destinationFolder = selectedDest self.outputFolderDestination.setText(os.path.normpath(self.destinationFolder)) self.progressBar.setValue(0) def center(self): """ Centers the main app window on the screen """ qr = self.frameGeometry() qr = self.frameGeometry() cp = QtWidgets.QDesktopWidget().availableGeometry().center() qr.moveCenter(cp) self.move(qr.topLeft()) def showDocWindow(self): """ Shows documentation window (with help and licensing info) """ title = "Documentation" htmlFile = "helpfile.html" self.docWindow = QWebView() self.docWindow.closeEvent = self.closeDocWindow self.docWindow.setWindowTitle(title) self.docWindow.setWindowIcon(self.helpIcon) self.docWindow.load(QtCore.QUrl.fromLocalFile(getResourceLoc(htmlFile))) self.docWindow.show() def closeDocWindow(self,source): """ Callback function of the docWindow QWebView item. Destroys reference to doc window after its closed """ del(self.docWindow) def showAboutWindow(self): """ Shows about window """ about ="About" msgBox = QtWidgets.QMessageBox(self) msgBox.setWindowIcon(self.aboutIcon) msgBox.about(self, about, aboutString) def showErrorMessage(self, message): """ Shows error message """ error ="Error" msgBox = QtWidgets.QMessageBox(self) msgBox.about(self, error, message) def confirmEvent(self, message): """ Confirm box """ reply = QtWidgets.QMessageBox.question(self, 'Message', message, QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, QtWidgets.QMessageBox.No) if reply == QtWidgets.QMessageBox.Yes: reply = True else: reply = False return reply def closeEvent(self, event): """ Confirm closing the main window """ message = "Are you sure to quit?" reply = self.confirmEvent(message) if reply: event.accept() else: event.ignore()
class Application(QApplication): _active_task = None _exit_timer = None _expects = None _expects_active = False _expects_if_timeout = None _expects_timer = None _frame_data = None _frame_data_lock = None _frame_timer = None _handlers = None _queue = None _trigger_delay_timer = None _visible = True log_event = pyqtSignal(int, str, str) name = '' def __init__(self, name, settings): super(Application, self).__init__([]) self.name = name self.settings = settings self.web_view = QWebView() self._exit_timer = QTimer(self) self._exit_timer.setSingleShot(True) self._exit_timer.setInterval(1000) self._expects = [] self._expects_if_timeout = [] self._expects_timer = QTimer(self) self._expects_timer.setSingleShot(True) self._expects_timer.timeout.connect(self._on_expects_timeout) self._frame_data = {} self._frame_data_lock = Lock() self._frame_timer = QTimer(self) self._frame_timer.start(3000) self._queue = Queue() self._trigger_delay_timer = QTimer(self) self._trigger_delay_timer.setSingleShot(True) self._visible = int(self.settings['application.visible']) self.web_page = WebPage(self.web_view) self.web_page.log_event.connect(self.log_event) self.web_page.frameCreated.connect(self._on_frame_created) self._on_frame_created(self.web_page.mainFrame()) #self.web_page.networkAccessManager().finished.connect( # self._on_http_response) self.web_view.setPage(self.web_page) st = self.web_page.settings() st.setAttribute(st.AutoLoadImages, int(self.settings['application.settings.load_images'])) st.setAttribute( st.JavaEnabled, int(self.settings['application.settings.java_enabled'])) st.setAttribute( st.PluginsEnabled, int(self.settings['application.settings.plugins_enabled'])) self.clear_handlers() # redirect qt related messages try: qInstallMessageHandler(self._pyqt5_null_message_handler) except NameError: qInstallMsgHandler(self._pyqt4_null_message_handler) def start(self): self.process_next_queue() if self._visible: self.web_view.show() return self.exec_() def add_queue(self, task, publish=False): self._queue.put(task) def process_next_queue(self): self._on_next_queue_trigger(self) def get_frame_related_data(self, frame): """ There is a pool of reusable frame data, because browser's frames were easily created and destroyed. """ with self._frame_data_lock: frame_data = self._frame_data.get(str(frame.objectName())) return frame_data def set_expects(self, expects): self._expects_active = True newlist = make_list(expects) for item in newlist: for key in item: if not key in ('path', 'hash', 'host', 'selector_exists', 'selector_not_exists', 'trigger', 'trigger_args', 'trigger_delay', 'trigger_wait_pageload', 'custom'): self.warn('"%s" is not a valid expect field.' % key) item['selector_exists'] = make_list(item.get('selector_exists')) item['selector_not_exists'] = make_list( item.get('selector_not_exists')) self._expects = newlist def set_timeout_expects(self, timeout, expects): self._expects_timer.start(timeout * 1000) self._expects_if_timeout = make_list(expects) def set_upload_files(self, filenames): self.web_page.upload_files = make_list(filenames) def add_handler(self, name, value): self._handlers[name] = value def clear_handlers(self): # clear handlers registration self._handlers = { 'core.next_queue': self._on_next_queue_trigger, 'core.page_not_found': self._on_page_not_found_trigger } def load(self, url): self.web_view.load(QUrl(url)) def info(self, message): self.log_event.emit(INFO, message, 'default') def debug(self, message): self.log_event.emit(DEBUG, message, 'default') def error(self, message): self.log_event.emit(ERROR, message, 'default') def warn(self, message): self.log_event.emit(WARNING, message, 'default') def exit(self, return_code): self.web_view.hide() self._expects_active = False self.web_view.stop() self._exit_timer.timeout.connect( partial(super(Application, self).exit, return_code)) self._exit_timer.start(1000) def _url_matched_expectation(self, expect, scheme, netloc, path, query, segment): if 'host' in expect and not re.match(expect['host'], netloc): self.debug('%s location.host: "%s" "%s"' % (expect['trigger'], expect['host'], netloc)) return False if 'path' in expect and not re.match(expect['path'], path): self.debug('%s location.pathname: "%s" "%s"' % (expect['trigger'], expect['path'], path)) return False if 'hash' in expect and not re.match(expect['hash'], segment): self.debug('%s location.hash: "%s" "%s"' % (expect['trigger'], expect['hash'], segment)) return False return True def process_expectations(self, expect, frame, urlparts): if not self._url_matched_expectation(expect, *urlparts): return document = frame.documentElement() for selector in expect.get('selector_exists', []): if document.findFirst(selector).isNull(): self.debug('%s selector_exists: %s' % (expect['trigger'], selector)) return for selector in expect.get('selector_not_exists', []): if not document.findFirst(selector).isNull(): self.debug('%s selector_not_exists: %s' % (expect['trigger'], selector)) return if 'custom' in expect and not expect['custom'](self, frame, *urlparts): return self.debug('%s triggered.' % expect['trigger']) self._expects_active = False trigger_delay = expect.get('trigger_delay', 0) if trigger_delay: try: self._trigger_delay_timer.timeout.disconnect() except Exception as e: pass self._trigger_delay_timer.timeout.connect( partial(self.trigger, frame=frame, trigger_name=expect['trigger'], trigger_args=expect.get('trigger_args', []))) self._trigger_delay_timer.start(trigger_delay * 1000) else: self.trigger(frame, expect['trigger'], expect.get('trigger_args', [])) def trigger(self, frame, trigger_name, trigger_args): if self._trigger_delay_timer.isActive(): self._trigger_delay_timer.stop() trigger_args = dict((str(key), trigger_args[key]) for \ key in trigger_args) if trigger_name in self._handlers: self._handlers[trigger_name](self, frame, **trigger_args) else: self.error('No handler for trigger %s.' % trigger_name) self.exit(-1) @staticmethod def _on_page_not_found_trigger(app, frame): app.error('PageNotFound: %s.' % frame.baseUrl().toString()) app.exit(-1) @staticmethod def _on_next_queue_trigger(app, frame=None): if not app._active_task is None: app._queue.task_done() try: app._active_task = task = app._queue.get(timeout=15) except Empty: app.info('No more task in the queue.') app.web_view.close() app.exit(0) return expects = make_list(task['expects']) for expect in expects: expect['trigger_wait_pageload'] = True app.set_expects(expects) app.load(task['goto']) def _on_expects_timeout(self): self.debug('No expectations were fulfilled after a periode.') self.set_expects(self._expects_if_timeout or []) self.web_page.triggerAction(QWebPage.Stop) def _on_frame_created(self, frame): """ Called when QWebPage created a QWebFrame """ frame_name = 'frame-' + uuid4().hex frame.setObjectName(frame_name) frame_data = { ENUM_FRAME_DATA_ACTIVE: False, ENUM_FRAME_DATA_TIMER_CALLBACK: partial(self._on_frame_timer, frame=frame), ENUM_FRAME_DATA_TIMER_COUNTER: 0, } with self._frame_data_lock: self._frame_data[frame_name] = frame_data self._frame_timer.timeout.connect( frame_data[ENUM_FRAME_DATA_TIMER_CALLBACK]) frame.destroyed.connect(self._on_frame_destroyed) #frame.javaScriptWindowObjectCleared.connect(partial( # self._on_frame_reset, frame=frame)) frame.loadFinished.connect(partial(self._on_frame_loaded, frame=frame)) def _on_frame_destroyed(self, frame): """ Called when QWebFrame was destroyed """ frame_name = str(frame.objectName()) with self._frame_data_lock: frame_data = self._frame_data.get(frame_name) if frame_data is None: return del self._frame_data[frame_name] self._frame_timer.timeout.disconnect( frame_data[ENUM_FRAME_DATA_TIMER_CALLBACK]) def _on_frame_loaded(self, success, frame=None): self._on_frame_reset(frame) def _on_frame_reset(self, frame=None): """ Called when the javascript `window` object were destroyed, usually just before page reload. """ if frame is None: # this happens if the frame was forced stop, probably, not sure self.error('Problem while loading the page.') self.exit(-1) return self.debug('DOMContentLoaded ' + frame.baseUrl().toString()) frame.evaluateJavaScript(""" window.bot = { click: function(el) { if (el.click) { el.click() } else if (el.fireEvent) { el.fireEvent('onclick'); } else { var evt = document.createEvent('Events'); evt.initEvent('click', true, false); el.dispatchEvent(evt); } }, }; """) frame_data = self.get_frame_related_data(frame) frame_data[ENUM_FRAME_DATA_TIMER_COUNTER] = 0 frame_data[ENUM_FRAME_DATA_ACTIVE] = True def _on_frame_timer(self, frame): if not self._expects_active: # we have obsolete expects return frame_data = self.get_frame_related_data(frame) if frame_data is None: return if not frame_data[ENUM_FRAME_DATA_ACTIVE]: # the frame hasn't been fully loaded return wait_pageload = False urlparts = urlsplit(str(frame.baseUrl().toString())) for expect in self._expects: # is it an obsolete frame if expect.get('trigger_wait_pageload', False) and \ frame_data[ENUM_FRAME_DATA_TIMER_COUNTER] > 0: wait_pageload = True continue self.process_expectations(expect, frame, urlparts) if not self._expects_active: # one of the triggers has been activated break if not wait_pageload: frame_data[ENUM_FRAME_DATA_TIMER_COUNTER] += 1 def _pyqt4_null_message_handler(self, msgtype, msg): """ Nuke Qt related error messages """ self.log_event.emit(DEBUG, str(msg), 'qt') def _pyqt5_null_message_handler(self, msgtype, msgctx, msg): """ Nuke Qt related error messages """ self.log_event.emit(DEBUG, str(msg), 'qt') def _on_http_response(self, response): error = response.error() if error == QNetworkReply.NoError: return url = str(response.url().toString()) scheme, netloc, path, query, segment = urlsplit(url) filename, ext = os.path.splitext(path) if len(ext) and ext[1:] in ('gif', 'css', 'js', 'png', 'jpg', 'jpeg', 'ico'): return status_code = int( response.attribute( QNetworkRequest.HttpStatusCodeAttribute).toInt()) for expect in self._expects: if self._url_matched_expectation(expect, scheme, netloc, path, query, segment): self.log_event.emit(WARNING, '%s: %s' % (status_code, url), 'http') break else: self.log_event.emit(DEBUG, '%s: %s' % (status_code, url), 'http')
class HTMLViewer(vip_base): def cb_initialize_plugin(self): # --------------------------- # Read configuration # --------------------------- self.config = self.pl_get_current_config_ref() content = self.config['content']['value'] isUrl = self.config['isUrl']['value'] # -------------------------------- # Create Widget # -------------------------------- self.WebView = QWebView() # This call is important, because the background structure needs to know the used widget! # In the background the qmidiwindow will becreated and the widget will be added self.pl_set_widget_for_internal_usage( self.WebView ) print(isUrl) if isUrl == '1': url = QtCore.QUrl(content) self.WebView.load(url) else: self.WebView.setHtml(content) self.WebView.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) self.WebView.customContextMenuRequested.connect(self.show_context_menu) return True def show_context_menu(self, pos): gloPos = self.WebView.mapToGlobal(pos) self.cmenu = self.pl_create_control_context_menu() self.cmenu.exec_(gloPos) def cb_pause(self): # will be called, when plugin gets paused # can be used to get plugin in a defined state before pause # e.a. pass def cb_resume(self): # will be called when plugin gets resumed # can be used to wake up the plugin from defined pause state # e.a. reopen communication ports, files etc. pass def cb_execute(self, Data=None, block_name = None, plugin_uname = None): # Do main work here! # If this plugin is an IOP plugin, then there will be no Data parameter because it wont get data # If this plugin is a DPP, then it will get Data with data # param: Data is a Data hash and block_name is the block_name of Data origin # Data is a hash, so use ist like: Data[CORE_TIME_SIGNAL] = [t1, t2, ...] where CORE_TIME_SIGNAL is a signal_name # hash signal_name: value # Data could have multiple types stored in it e.a. Data['d1'] = int, Data['d2'] = [] pass def cb_set_parameter(self, name, value): # attetion: value is a string and need to be processed ! # if name == 'irgendeinParameter': # do that .... with value pass def cb_quit(self): # do something before plugin will close, e.a. close connections ... pass def cb_get_plugin_configuration(self): # # Implement a own part of the config # config is a hash of hass object # config_parameter_name : {} # config[config_parameter_name]['value'] NEEDS TO BE IMPLEMENTED # configs can be marked as advanced for create dialog # http://utilitymill.com/utility/Regex_For_Range config = { "content": { 'value': """<p> Insert your html code here </p>""", 'display_text' : 'HTML Content', 'advanced' : '0', 'tooltip' : 'Plain html code to be displayed' }, "isUrl": { 'value': "0", 'display_text': "Content==Url?", 'tooltip': "Set to 1 if the content is an url that should be loaded", 'advanced' : '0' }, } return config def cb_plugin_meta_updated(self): """ Whenever the meta information is updated this function is called (if implemented). :return: """ #dplugin_info = self.dplugin_info pass
import sys from PyQt5.QtCore import * from PyQt5.QtGui import * from PyQt5.QtPrintSupport import QPrinter from PyQt5.QtWebKit import * from PyQt5.QtWebKitWidgets import QWebView from PyQt5.QtWidgets import QApplication app = QApplication(sys.argv) web = QWebView() # web.load(QUrl("http://www.google.com")) # web.show() # sourceHtml = open("../template/umaficha/ficha.html").read() local_url = QUrl.fromLocalFile("../template/umaficha/ficha.html") web.load(local_url) printer = QPrinter() printer.setPageSize(QPrinter.A4) printer.setOutputFormat(QPrinter.PdfFormat) printer.setOutputFileName("file.pdf") def convertIt(): web.print_(printer) print("Pdf generated") QApplication.exit() if __name__ == "__main__": convertIt()