class ColumnResizer(QObject): def __init__(self): super(ColumnResizer, self).__init__() self._widgets = [] self._gridColumnInfoList = [] self._updateTimer = QTimer(self) self._updateTimer.setSingleShot(True) self._updateTimer.setInterval(0) self._updateTimer.timeout.connect(self._updateWidth) # Public methods def addWidgetsFromLayout(self, layout, column): """ :type layout: QGridLayout :type column: int """ assert column >= 0 if isinstance(layout, QGridLayout): self._addWidgetsFromGridLayout(layout, column) else: print "ColumnResizerResizer does not support layouts of type:", type(layout) def eventFilter(self, obj, event): """ Overrides QObject.eventFilter() """ if event.type() == QEvent.Resize: self._scheduleWidthUpdate() return False # Private methods @Slot() def _updateWidth(self): width = 0 for widget in self._widgets: width = max(widget.sizeHint().width(), width) for info in self._gridColumnInfoList: info[0].setColumnMinimumWidth(info[1], width) def _addWidget(self, widget): self._widgets.append(widget) widget.installEventFilter(self) self._scheduleWidthUpdate() def _addWidgetsFromGridLayout(self, layout, column): for row in range(layout.rowCount()): item = layout.itemAtPosition(row, column) if not item: continue widget = item.widget() if not widget: continue self._addWidget(widget) self._gridColumnInfoList.append([layout, column]) def _scheduleWidthUpdate(self): self._updateTimer.start()
def test_encode_wrong_order_id(self): # python test_edit_timetracks.py TestEditTimetracks.test_encode_wrong_order_id app = self.app widget = self.widget today = date.today() widget.set_employee_and_date(self.employee.employee_id, today) QTest.keyEvent(QTest.Click, app.focusWidget(), Qt.Key_F5, Qt.ShiftModifier) # modifier, delay app.processEvents() QTest.keyEvent(QTest.Click, app.focusWidget(), Qt.Key_9) # modifier, delay app.processEvents() QTest.keyEvent(QTest.Click, app.focusWidget(), Qt.Key_9) # modifier, delay app.processEvents() QTest.keyEvent(QTest.Click, app.focusWidget(), Qt.Key_9) # modifier, delay app.processEvents() timer = QTimer() timer.timeout.connect(self._click_order_inexistant_box) timer.setSingleShot(True) timer.start(250) self.test_sucess = False QTest.keyEvent(QTest.Click, app.focusWidget(), Qt.Key_Enter) app.processEvents() assert self.test_sucess
class WidgetFader(object): ''' For a given widget in the UI, this fader attaches a timer that hides that widget after a specified interval ''' def make_active(self): ''' shows the widget then sets the hide timer ''' self.controls.show() self.timer.start(self.time_out) def make_inactive(self): self.controls.hide() def __init__(self, controls, time_out=1500): ''' Constructor ''' self.timer = QTimer() self.timer.timeout.connect(self.make_inactive) self.timer.setSingleShot(True) self.time_out = time_out self.controls = controls self.controls.hide()
def statusBarTimer(self): q = self.q ret = QTimer(q) ret.setSingleShot(True) ret.setInterval(5000) ret.timeout.connect(q.statusBar().hide) return ret
class WidgetFader(object): ''' For a given widget in the UI, this fader attaches a timer that hides that widget after a specified interval ''' def make_active(self): ''' shows the widget then sets the hide timer ''' self.controls.show() self.timer.start(self.time_out) def make_inactive(self): self.controls.hide() def __init__(self, controls, time_out=1500): ''' Constructor ''' self.timer = QTimer() self.timer.timeout.connect(self.make_inactive) self.timer.setSingleShot(True) self.time_out = time_out self.controls = controls self.controls.hide()
def test_print_preorder(self): # python test_integration.py TestEditOrderParts.test_print_preorder app = self.app widget = self.widget self._make_basic_preorder() self._encode_imputable_operation("Description2") self._encode_imputable_operation("Description3") QTest.keyEvent(QTest.Click, app.focusWidget(), Qt.Key_S, Qt.ControlModifier) # modifier, delay app.processEvents() # We're going to have a modal dialog for the # preorder print. So we prepare to click on it. timer = QTimer() timer.timeout.connect(self._fill_preorder_print_dialog) timer.setSingleShot(True) timer.start(250) self.prepare_to_click_dialog("set_preorder_sent_state", 3) widget.preorderPrint() order = dao.order_dao.find_by_id(widget._current_order.order_id) self.assertEqual("ZuluHeader", order.preorder_print_note) self.assertEqual("ZuluFooter", order.preorder_print_note_footer)
class Panel(QWidget): def __init__(self, parent=None, instr=None, lock=None, title="Instrument Panel"): # This class derivates from a Qt Widget so we have to call # the class builder ".__init__()" QWidget.__init__(self) # "self" is now a Qt Widget, then we load the user interface # generated with QtDesigner and call it self.ui self.ui = Keithley6221_Ui.Ui_Panel() # Now we have to feed the GUI building method of this object (self.ui) # with the current Qt Widget 'self', but the widgets from the design will actually be built as children # of the object self.ui self.ui.setupUi(self) self.setWindowTitle(title) self.reserved_access_to_instr = lock self.instr = instr self.monitor_timer = QTimer() # The timer would not wait for the completion of the task otherwise self.monitor_timer.setSingleShot(True) self.monitor_timer.timeout.connect(self.monitor) self.firsttime = 0 # bug: if the box is checked in the .ui file, the system freezes # if self.ui.monitor.isChecked():self.monitor() def monitor(self, state=1): if state != 1: self.monitor_timer.stop() elif state and not (self.monitor_timer.isActive()): with self.reserved_access_to_instr: I = self.instr.query_current_source_amplitude() Vcomp = self.instr.query_voltage_compliance() outstate = self.instr.query_output_ON() self.ui.I_disp.setText(str(I * 1e6) + u" μA") self.ui.V_disp.setText(str(Vcomp) + " V") self.ui.outputON.setChecked(outstate) self.monitor_timer.start(self.ui.refresh_rate.value() * 1000) def update_timer_timeout(self, secs): # The value must be converted to milliseconds self.monitor_timer.setInterval(secs * 1000) def change_I(self, value=0): with self.reserved_access_to_instr: self.instr.set_current_source_amplitude(value * 1e6) def change_V_comp(self, value=0): with self.reserved_access_to_instr: self.instr.set_voltage_compliance(value) def switch_output(self, value=False): if value: with self.reserved_access_to_instr: self.instr.output_ON() else: with self.reserved_access_to_instr: self.instr.output_OFF() def reset_inst(self): with self.reserved_access_to_instr: self.instr.reset()
def test_change_order_customer(self): customer2 = self._customer_dao.make( "AAAA") # Name chosen so it happens first int he customer dialog self._customer_dao.save(customer2) app = self.app widget = self.widget mw = self.mw self._make_basic_preorder() self._encode_imputable_operation("Description2") self._encode_imputable_operation("Description3") QTest.keyEvent(QTest.Click, app.focusWidget(), Qt.Key_S, Qt.ControlModifier) # modifier, delay app.processEvents() # Make sure the test is set up correctly preorder = self.order_dao.find_by_id_frozen( widget._current_order.order_id) assert preorder.customer_id == self.customer.customer_id # We're going to have a modal dialog for the new # customer name. So we prepare to click on it. timer = QTimer() timer.timeout.connect(self._lookup) timer.setSingleShot(True) timer.start(1000) widget.change_customer() # app.exec_() # Wait for the timer to shoot mainlog.debug("test_change_order_customer" * 10) mainlog.debug(app.focusWidget()) widget.setFocus( Qt.OtherFocusReason) # This refocus was introduced for linux app.processEvents() QTest.keyEvent(QTest.Click, app.focusWidget(), Qt.Key_S, Qt.ControlModifier) # modifier, delay app.processEvents() mainlog.debug("test_change_order_customer") preorder = self.order_dao.find_by_id_frozen( widget._current_order.order_id) assert preorder.customer_id == customer2.customer_id mainlog.debug("test_change_order_customer") self.order_dao.delete(preorder.order_id) self.dao.customer_dao.delete(customer2.customer_id)
def test_change_order_customer_cancelled(self): customer2 = self._customer_dao.make( "AAAA") # Name chosen so it happens first int he customer dialog self._customer_dao.save(customer2) app = self.app widget = self.widget mw = self.mw self._make_basic_preorder() self._encode_imputable_operation("Description2") self._encode_imputable_operation("Description3") QTest.keyEvent(QTest.Click, app.focusWidget(), Qt.Key_S, Qt.ControlModifier) # modifier, delay app.processEvents() # Make sure the test is set up correctly preorder = self.order_dao.find_by_id_frozen( widget._current_order.order_id) assert preorder.customer_id == self.customer.customer_id # We're going to have a modal dialog for the new # customer name. So we prepare to click on it. timer = QTimer() timer.timeout.connect(self._cancel_dialog) timer.setSingleShot(True) timer.start(1000) # Had 250, my linux seems to prefer 1000 widget.change_customer() # blocks app.processEvents() # app.exec_() # Wait for the timer to shoot widget.setFocus( Qt.OtherFocusReason) # This refocus was introduced for linux for i in range(1000): app.processEvents() # Linux needs a break mainlog.debug("Pressing ctrl-S on this widget : {}".format( app.focusWidget())) # app.exec_() QTest.keyEvent(QTest.Click, app.focusWidget(), Qt.Key_S, Qt.ControlModifier) # modifier, delay app.processEvents() preorder = self.order_dao.find_by_id_frozen( widget._current_order.order_id) assert preorder.customer_id == self.customer.customer_id self.order_dao.delete(preorder.order_id) self.dao.customer_dao.delete(customer2.customer_id)
def onLoopRequested(loopRequest): print 'l: ', l if l: l.pop() led.toggle(not led.isToggled()) ledTimer = QTimer(toggleObject) ledTimer.setSingleShot(True) ledTimer.setInterval(200) ledTimer.timeout.connect( partial(loopRequest.completeRequest, True if l else False)) ledTimer.start() if not l: l.extend(range(20))
class MyGlWidget(QGLWidget): "PySideApp uses Qt library to create an opengl context, listen to keyboard events, and clean up" def __init__(self, renderer, glformat, app): "Creates an OpenGL context and a window, and acquires OpenGL resources" super(MyGlWidget, self).__init__(glformat) self.renderer = renderer self.app = app # Use a timer to rerender as fast as possible self.timer = QTimer(self) self.timer.setSingleShot(True) self.timer.setInterval(0) self.timer.timeout.connect(self.render_vr) # Accept keyboard events self.setFocusPolicy(Qt.StrongFocus) def __enter__(self): "setup for RAII using 'with' keyword" return self def __exit__(self, type_arg, value, traceback): "cleanup for RAII using 'with' keyword" self.dispose_gl() def initializeGL(self): if self.renderer is not None: self.renderer.init_gl() self.timer.start() def paintGL(self): "render scene one time" self.renderer.render_scene() self.swapBuffers() # Seems OK even in single-buffer mode def render_vr(self): self.makeCurrent() self.paintGL() self.doneCurrent() self.timer.start() # render again real soon now def disposeGL(self): if self.renderer is not None: self.makeCurrent() self.renderer.dispose_gl() self.doneCurrent() def keyPressEvent(self, event): "press ESCAPE to quit the application" key = event.key() if key == Qt.Key_Escape: self.app.quit()
def test_change_order_part_state(self): order = self._make_order() order_part = self._order_part_dao.make(order) order_part.description = u"Part 2" order_part.position = 2 self._order_part_dao.save(order_part) order_part = self._order_part_dao.make(order) order_part.description = u"Part 3" order_part.position = 3 self._order_part_dao.save(order_part) self._order_dao.change_order_state( order.order_id, OrderStatusType.order_ready_for_production) # self.order_overview_widget.month_today() self.order_overview_widget.refresh_action() self.order_overview_widget.retake_focus() # self.app.exec_() # Select the first order part (we assume they are properly ordered) v = self.order_overview_widget.current_orders_overview.table_view v.setCurrentIndex(v.model().index(0, 0)) QTest.mouseMove(v) # QTest.mouseClick(self.order_overview_widget.current_orders_overview.table_view, # Qt.RightButton, delay=500) # self.order_overview_widget.current_orders_overview.popup_parts() timer = QTimer() timer.timeout.connect(self._click_context_menu) timer.setSingleShot(True) timer.start(1000) # I can't have the menu to popup using museClicks, so I # do it this way. FIXME Taht's not right because nothing prooves # that my context menu is shown on mouse clicks... self.app.sendEvent( self.order_overview_widget.current_orders_overview.table_view, QContextMenuEvent(QContextMenuEvent.Keyboard, QPoint(10, 10))) # self.app.exec_() order = self.order_dao.find_by_id(order.order_id) self.assertEqual(OrderPartStateType.aborted, order.parts[0].state) self.assertEqual(OrderPartStateType.ready_for_production, order.parts[1].state) self.assertEqual(OrderPartStateType.ready_for_production, order.parts[2].state)
class MyGlWidget(QGLWidget): "PySideApp uses Qt library to create an opengl context, listen to keyboard events, and clean up" def __init__(self, renderer, glformat, app): "Creates an OpenGL context and a window, and acquires OpenGL resources" super(MyGlWidget, self).__init__(glformat) self.renderer = renderer self.app = app # Use a timer to rerender as fast as possible self.timer = QTimer(self) self.timer.setSingleShot(True) self.timer.setInterval(0) self.timer.timeout.connect(self.render_vr) # Accept keyboard events self.setFocusPolicy(Qt.StrongFocus) def __enter__(self): "setup for RAII using 'with' keyword" return self def __exit__(self, type_arg, value, traceback): "cleanup for RAII using 'with' keyword" self.dispose_gl() def initializeGL(self): if self.renderer is not None: self.renderer.init_gl() self.timer.start() def paintGL(self): "render scene one time" self.renderer.render_scene() self.swapBuffers() # Seems OK even in single-buffer mode def render_vr(self): self.makeCurrent() self.paintGL() self.doneCurrent() self.timer.start() # render again real soon now def disposeGL(self): if self.renderer is not None: self.makeCurrent() self.renderer.dispose_gl() self.doneCurrent() def keyPressEvent(self, event): "press ESCAPE to quit the application" key = event.key() if key == Qt.Key_Escape: self.app.quit()
def downlaod(self, url, timeout=60): loop = QEventLoop() timer = QTimer() timer.setSingleShot(True) timer.timeout.connect(loop.quit) self.loadFinished.connect(loop.quit) self.load(QUrl(url)) timer.start(timeout * 1000) loop.exec_() if timer.isActive(): timer.stop() return self.html() else: print 'Request timed out: ' + url
def onLoopRequested(loopRequest): print 'l: ', l if l: l.pop() led.toggle(not led.isToggled()) ledTimer = QTimer(toggleObject) ledTimer.setSingleShot(True) ledTimer.setInterval(200) ledTimer.timeout.connect( partial( loopRequest.completeRequest, True if l else False ) ) ledTimer.start() if not l: l.extend(range(20))
class Browser(QWidget): def __init__(self): super(Browser, self).__init__() self.layout = QStackedLayout(self) self.layout.setStackingMode(QStackedLayout.StackAll) self.hostname = os.uname()[1] self.timer = QTimer() self.timer.setSingleShot(False) self.timer.start(2000) self._init_sites() self.remote_shell = RemoteShell(self) self.connect(self.timer, SIGNAL("timeout()"), self, SLOT("show_next()")) self.show_next() def _init_sites(self): self.sites = list() self.site_id = -1 url = QUrl("https://www.qt.io/developers/") self.sites.append(Site(self, url, 5, 1)) url = QUrl("https://wiki.qt.io/PySide") self.sites.append(Site(self, url, 5, 1)) url = QUrl("https://www.python.org/") self.sites.append(Site(self, url, 5, 1)) @property def current_site(self): return self.sites[self.site_id] def show_next(self): self.timer.stop() previous_id = self.site_id self.site_id = (self.site_id + 1) % len(self.sites) current_site = self.current_site self.timer.start(current_site.time * 1000) current_site.show() print("show " + current_site.url.toString().encode()) if previous_id >= 0: self.sites[previous_id].hide()
def open(self, url, timeout=60): """Wait for download to complete and return result""" loop = QEventLoop() timer = QTimer() timer.setSingleShot(True) timer.timeout.connect(loop.quit) self.loadFinished.connect(loop.quit) self.load(QUrl(url)) timer.start(timeout * 1000) loop.exec_() # delay here until download finished if timer.isActive(): # downloaded successfully timer.stop() return self.html() else: # timed out print 'Request timed out:', url
def open(self, url, timeout=60): """Wait for download to complete and return result""" loop = QEventLoop() timer = QTimer() timer.setSingleShot(True) timer.timeout.connect(loop.quit) self.loadFinished.connect(loop.quit) self.load(QUrl(url)) timer.start(timeout * 1000) loop.exec_() # delay here until download finished if timer.isActive(): # downloaded successfully timer.stop() return self.html() else: # timed out print 'Request timed out:', url
def open(self, url, timeout=60): """wait for download to complete and return result""" loop = QEventLoop() timer = QTimer() timer.setSingleShot(True) # True表示触发定时器后,仅执行事件一次 timer.timeout.connect(loop.quit) # 若超时,则连接loop.quit,退出事件循环 self.loadFinished.connect(loop.quit) self.load(url) timer.start(timeout * 1000) # 定时器以ms为单位,设置超时时间为60s loop.exec_() # 等待网页加载完成后,在执行后面的代码 if timer.isActive(): # downloaded successfully timer.stop() return self.html() else: # timed out print 'Request timed out:', url
class Render(QWebView): def __init__(self, url, filename, image_crop, translate_page, parent=None): super(Render, self).__init__(parent) self.image_crop = image_crop self.fileName = time.strftime("%Y%m%d%H%M%S", time.localtime()) + "_test.jpg" self.finished = False # Settings s = self.page().settings() s.setAttribute(QWebSettings.AutoLoadImages, True) s.setAttribute(QWebSettings.PluginsEnabled, True) s.setAttribute(QWebSettings.JavascriptEnabled, True) s.setAttribute(QWebSettings.JavaEnabled, False) s.setAttribute(QWebSettings.JavascriptCanOpenWindows, False) s.setAttribute(QWebSettings.DeveloperExtrasEnabled, True) #self.page().mainFrame().setScrollBarPolicy(Qt.Horizontal, Qt.ScrollBarAlwaysOff) self.page().mainFrame().setScrollBarPolicy(Qt.Vertical, Qt.ScrollBarAlwaysOff) self.timerScreen = QTimer() self.timerScreen.setInterval(10000) self.timerScreen.setSingleShot(True) self.timerScreen.timeout.connect(self.takeScreenshot) self.loadFinished.connect(self.timerScreen.start) self.load(QUrl(url)) @Slot(QNetworkReply) def takeScreenshot(self): [x, y, width, height] = self.image_crop frame = self.page().mainFrame() size = frame.contentsSize() size.setWidth(1000) size.setHeight(2000) self.page().setViewportSize(size) image = QImage(self.page().viewportSize(), QImage.Format_ARGB32) painter = QPainter(image) frame.render(painter) painter.end() image1 = image.copy(x, y, width, height) image1.save(self.fileName) self.finished = True
class Render(QWebView): def __init__(self, url, filename, image_crop, translate_page, parent=None): super(Render, self).__init__(parent) self.image_crop = image_crop self.fileName = time.strftime("%Y%m%d%H%M%S",time.localtime()) +"_test.jpg" self.finished = False # Settings s = self.page().settings() s.setAttribute(QWebSettings.AutoLoadImages, True) s.setAttribute(QWebSettings.PluginsEnabled, True) s.setAttribute(QWebSettings.JavascriptEnabled, True) s.setAttribute(QWebSettings.JavaEnabled, False) s.setAttribute(QWebSettings.JavascriptCanOpenWindows, False) s.setAttribute(QWebSettings.DeveloperExtrasEnabled, True) #self.page().mainFrame().setScrollBarPolicy(Qt.Horizontal, Qt.ScrollBarAlwaysOff) self.page().mainFrame().setScrollBarPolicy(Qt.Vertical, Qt.ScrollBarAlwaysOff) self.timerScreen = QTimer() self.timerScreen.setInterval(10000) self.timerScreen.setSingleShot(True) self.timerScreen.timeout.connect(self.takeScreenshot) self.loadFinished.connect(self.timerScreen.start) self.load(QUrl(url)) @Slot(QNetworkReply) def takeScreenshot(self): [x,y,width,height] = self.image_crop frame = self.page().mainFrame() size = frame.contentsSize() size.setWidth(1000) size.setHeight(2000) self.page().setViewportSize(size) image = QImage(self.page().viewportSize(), QImage.Format_ARGB32) painter = QPainter(image) frame.render(painter) painter.end() image1 = image.copy(x,y,width,height) image1.save(self.fileName) self.finished = True
def _create_timer(self, interval, single_shot, handler): timer = QTimer() timer.setInterval(1000 * interval) timer.setSingleShot(single_shot) self._timers.add(timer) wref = weakref.ref(timer) def callback(): timer = wref() if single_shot and timer: timer.stop() self._timers.discard(timer) self._processor.submit(handler) timer.timeout.connect(callback) def cancel(): timer = wref() if timer: timer.stop() self._timers.discard(timer) handler.cancel_callback = cancel timer.start() return timer
class SearchReplaceLabel(SearchHighlighterLabel): replace_committed = Signal(str, str) def __init__(self, text, replaceColor=Qt.yellow, highlightColor=Qt.red, replaceTimeout=2): super(SearchReplaceLabel, self).__init__(text, highlightColor) self._replace_color = replaceColor self.replace_committed.connect(self._replaceText) self._replacement_fade_timer = QTimer() self._replacement_fade_timer.setSingleShot(replaceTimeout) self._replacement_fade_timer.timeout.connect(self._putOriginalText) def _replaceText(self, before, after): self.setText(self._original_text) # replace formatting after recent find activity self._original_text = self._original_text.replace(before, after) replacement = self._original_text.replace(before, "<font color='{0}'>{1}</font>".\ format(self._getColorString(self._replace_color), after)) self.setText(replacement) self._replacement_fade_timer.start() def _putOriginalText(self): self.setText(self._original_text)
class GlobalTimer: """All parsing and highlighting is done in main loop thread. If parsing is being done for long time, main loop gets blocked. Therefore SyntaxHighlighter controls, how long parsign is going, and, if too long, schedules timer and releases main loop. One global timer is used by all Qutepart instances, because main loop time usage must not depend on opened files count """ def __init__(self): self._timer = QTimer() self._timer.setSingleShot(True) self._timer.timeout.connect(self._onTimer) self._scheduledCallbacks = [] def isActive(self): return self._timer.isActive() def scheduleCallback(self, callback): if not callback in self._scheduledCallbacks: self._scheduledCallbacks.append(callback) self._timer.start() def unScheduleCallback(self, callback): if callback in self._scheduledCallbacks: self._scheduledCallbacks.remove(callback) if not self._scheduledCallbacks: self._timer.stop() def isCallbackScheduled(self, callback): return callback in self._scheduledCallbacks def _onTimer(self): if self._scheduledCallbacks: callback = self._scheduledCallbacks.pop() callback() if self._scheduledCallbacks: self._timer.start()
class GlobalTimer: """All parsing and highlighting is done in main loop thread. If parsing is being done for long time, main loop gets blocked. Therefore SyntaxHighlighter controls, how long parsign is going, and, if too long, schedules timer and releases main loop. One global timer is used by all Qutepart instances, because main loop time usage must not depend on opened files count """ def __init__(self): self._timer = QTimer() self._timer.setSingleShot(True) self._timer.timeout.connect(self._onTimer) self._scheduledCallbacks = [] def isActive(self): return self._timer.isActive() def scheduleCallback(self, callback): if not callback in self._scheduledCallbacks: self._scheduledCallbacks.append(callback) self._timer.start() def unScheduleCallback(self, callback): if callback in self._scheduledCallbacks: self._scheduledCallbacks.remove(callback) if not self._scheduledCallbacks: self._timer.stop() def isCallbackScheduled(self, callback): return callback in self._scheduledCallbacks def _onTimer(self): if self._scheduledCallbacks: callback = self._scheduledCallbacks.pop() callback() if self._scheduledCallbacks: self._timer.start()
class _GlobalUpdateWordSetTimer: """Timer updates word set, when editor is idle. (5 sec. after last change) Timer is global, for avoid situation, when all instances update set simultaneously """ _IDLE_TIMEOUT_MS = 1000 def __init__(self): self._timer = QTimer() self._timer.setSingleShot(True) self._timer.timeout.connect(self._onTimer) self._scheduledMethods = [] def schedule(self, method): if not method in self._scheduledMethods: self._scheduledMethods.append(method) self._timer.start(self._IDLE_TIMEOUT_MS) def _onTimer(self): method = self._scheduledMethods.pop() method() if self._scheduledMethods: self._timer.start(self._IDLE_TIMEOUT_MS)
class QtOneSecondTimer(OneSecondTimer): """ Concrete implementation of the timer based on Qt classes """ def __init__(self): OneSecondTimer.__init__(self) self.__timer = QTimer() self.__timer.setInterval(1000) self.__timer.setSingleShot(False) self.__timer.timeout.connect(self._timerElapsed) def start(self): """Start the timer""" self.__timer.start() def _timerElapsed(self): """Process notification from the timer""" self.elapsed.fire() def stop(self): """Stop the timer""" self.__timer.stop()
def test_delete_order_happy(self): app = self.app widget = self.widget mw = self.mw self._make_basic_preorder() QTest.keyEvent(QTest.Click, app.focusWidget(), Qt.Key_S, Qt.ControlModifier) # modifier, delay app.processEvents() deleted_order_id = widget._current_order.order_id # We're going to have a modal dialog for the new # customer name. So we prepare to click on it. timer = QTimer() timer.timeout.connect(self._accept_dialog) timer.setSingleShot(True) timer.start(250) widget.delete_button_clicked() assert not self.order_dao.find_by_id_frozen(deleted_order_id, resilient=True)
class _CompletionList(QListView): """Completion list widget """ closeMe = pyqtSignal() itemSelected = pyqtSignal(int) tabPressed = pyqtSignal() _MAX_VISIBLE_ROWS = 20 # no any technical reason, just for better UI _ROW_MARGIN = 6 def __init__(self, qpart, model): QListView.__init__(self, qpart.viewport()) self.setItemDelegate(HTMLDelegate(self)) self._qpart = qpart self.setFont(qpart.font()) self.setCursor(QCursor(Qt.PointingHandCursor)) self.setFocusPolicy(Qt.NoFocus) self.setModel(model) self._selectedIndex = -1 # if cursor moved, we shall close widget, if its position (and model) hasn't been updated self._closeIfNotUpdatedTimer = QTimer() self._closeIfNotUpdatedTimer.setInterval(200) self._closeIfNotUpdatedTimer.setSingleShot(True) self._closeIfNotUpdatedTimer.timeout.connect(self._afterCursorPositionChanged) qpart.installEventFilter(self) qpart.cursorPositionChanged.connect(self._onCursorPositionChanged) self.clicked.connect(lambda index: self.itemSelected.emit(index.row())) self.updateGeometry() self.show() qpart.setFocus() def __del__(self): """Without this empty destructor Qt prints strange trace QObject::startTimer: QTimer can only be used with threads started with QThread when exiting """ pass def del_(self): """Explicitly called destructor. Removes widget from the qpart """ self._closeIfNotUpdatedTimer.stop() self._qpart.removeEventFilter(self) self._qpart.cursorPositionChanged.disconnect(self._onCursorPositionChanged) # if object is deleted synchronously, Qt crashes after it on events handling QTimer.singleShot(0, lambda: self.setParent(None)) def sizeHint(self): """QWidget.sizeHint implementation Automatically resizes the widget according to rows count FIXME very bad algorithm. Remove all this margins, if you can """ width = max([self.fontMetrics().width(word) \ for word in self.model().words]) width = width * 1.4 # FIXME bad hack. invent better formula width += 30 # margin # drawn with scrollbar without +2. I don't know why rowCount = min(self.model().rowCount(), self._MAX_VISIBLE_ROWS) height = (self.sizeHintForRow(0) * rowCount) + self._ROW_MARGIN return QSize(width, height) def minimumHeight(self): """QWidget.minimumSizeHint implementation """ return self.sizeHintForRow(0) + self._ROW_MARGIN def _horizontalShift(self): """List should be plased such way, that typed text in the list is under typed text in the editor """ strangeAdjustment = 2 # I don't know why. Probably, won't work on other systems and versions return self.fontMetrics().width(self.model().typedText()) + strangeAdjustment def updateGeometry(self): """Move widget to point under cursor """ WIDGET_BORDER_MARGIN = 5 SCROLLBAR_WIDTH = 30 # just a guess sizeHint = self.sizeHint() width = sizeHint.width() height = sizeHint.height() cursorRect = self._qpart.cursorRect() parentSize = self.parentWidget().size() spaceBelow = parentSize.height() - cursorRect.bottom() - WIDGET_BORDER_MARGIN spaceAbove = cursorRect.top() - WIDGET_BORDER_MARGIN if height <= spaceBelow or \ spaceBelow > spaceAbove: yPos = cursorRect.bottom() if height > spaceBelow and \ spaceBelow > self.minimumHeight(): height = spaceBelow width = width + SCROLLBAR_WIDTH else: if height > spaceAbove and \ spaceAbove > self.minimumHeight(): height = spaceAbove width = width + SCROLLBAR_WIDTH yPos = max(3, cursorRect.top() - height) xPos = cursorRect.right() - self._horizontalShift() if xPos + width + WIDGET_BORDER_MARGIN > parentSize.width(): xPos = max(3, parentSize.width() - WIDGET_BORDER_MARGIN - width) self.setGeometry(xPos, yPos, width, height) self._closeIfNotUpdatedTimer.stop() def _onCursorPositionChanged(self): """Cursor position changed. Schedule closing. Timer will be stopped, if widget position is being updated """ self._closeIfNotUpdatedTimer.start() def _afterCursorPositionChanged(self): """Widget position hasn't been updated after cursor position change, close widget """ self.closeMe.emit() def eventFilter(self, object, event): """Catch events from qpart Move selection, select item, or close themselves """ if event.type() == QEvent.KeyPress and event.modifiers() == Qt.NoModifier: if event.key() == Qt.Key_Escape: self.closeMe.emit() return True elif event.key() == Qt.Key_Down: if self._selectedIndex + 1 < self.model().rowCount(): self._selectItem(self._selectedIndex + 1) return True elif event.key() == Qt.Key_Up: if self._selectedIndex - 1 >= 0: self._selectItem(self._selectedIndex - 1) return True elif event.key() in (Qt.Key_Enter, Qt.Key_Return): if self._selectedIndex != -1: self.itemSelected.emit(self._selectedIndex) return True elif event.key() == Qt.Key_Tab: self.tabPressed.emit() return True elif event.type() == QEvent.FocusOut: self.closeMe.emit() return False def _selectItem(self, index): """Select item in the list """ self._selectedIndex = index self.setCurrentIndex(self.model().createIndex(index, 0))
from PySide.QtCore import QTimer from maya import cmds from maya.OpenMaya import MEventMessage from maya.api.OpenMaya import MFn import mampy from mampy.core.selectionlist import DagpathList from mampy.core.exceptions import NothingSelected logger = logging.getLogger(__name__) TIMER = QTimer() TIMER.setSingleShot(True) TIMER_SET = False SELECT_CHANGE_EVENT = None HIDDEN_CHILDREN = set() def is_isolated(): return cmds.isolateSelect(get_active_panel(), q=True, state=True) def on_selection_changed(*args): if TIMER.isActive(): TIMER.stop() if not get_selected_objects(): return
class QtReactor(posixbase.PosixReactorBase): implements(IReactorFDSet) def __init__(self): self._reads = {} self._writes = {} self._notifiers = {} self._timer = QTimer() self._timer.setSingleShot(True) QObject.connect(self._timer, SIGNAL("timeout()"), self.iterate) if QCoreApplication.instance() is None: # Application Object has not been started yet self.qApp=QCoreApplication([]) self._ownApp=True else: self.qApp = QCoreApplication.instance() self._ownApp=False self._blockApp = None posixbase.PosixReactorBase.__init__(self) def _add(self, xer, primary, type): """ Private method for adding a descriptor from the event loop. It takes care of adding it if new or modifying it if already added for another state (read -> read/write for example). """ if xer not in primary: primary[xer] = TwistedSocketNotifier(None, self, xer, type) def addReader(self, reader): """ Add a FileDescriptor for notification of data available to read. """ self._add(reader, self._reads, QSocketNotifier.Read) def addWriter(self, writer): """ Add a FileDescriptor for notification of data available to write. """ self._add(writer, self._writes, QSocketNotifier.Write) def _remove(self, xer, primary): """ Private method for removing a descriptor from the event loop. It does the inverse job of _add, and also add a check in case of the fd has gone away. """ if xer in primary: notifier = primary.pop(xer) notifier.shutdown() def removeReader(self, reader): """ Remove a Selectable for notification of data available to read. """ self._remove(reader, self._reads) def removeWriter(self, writer): """ Remove a Selectable for notification of data available to write. """ self._remove(writer, self._writes) def removeAll(self): """ Remove all selectables, and return a list of them. """ rv = self._removeAll(self._reads, self._writes) return rv def getReaders(self): return self._reads.keys() def getWriters(self): return self._writes.keys() def callLater(self,howlong, *args, **kargs): rval = super(QtReactor,self).callLater(howlong, *args, **kargs) self.reactorInvocation() return rval def reactorInvocation(self): self._timer.stop() self._timer.setInterval(0) self._timer.start() def _iterate(self, delay=None, fromqt=False): """See twisted.internet.interfaces.IReactorCore.iterate. """ self.runUntilCurrent() self.doIteration(delay, fromqt) iterate = _iterate def doIteration(self, delay=None, fromqt=False): 'This method is called by a Qt timer or by network activity on a file descriptor' if not self.running and self._blockApp: self._blockApp.quit() self._timer.stop() delay = max(delay, 1) if not fromqt: self.qApp.processEvents(QEventLoop.AllEvents, delay * 1000) if self.timeout() is None: timeout = 0.1 elif self.timeout() == 0: timeout = 0 else: timeout = self.timeout() self._timer.setInterval(timeout * 1000) self._timer.start() def runReturn(self, installSignalHandlers=True): self.startRunning(installSignalHandlers=installSignalHandlers) self.reactorInvocation() def run(self, installSignalHandlers=True): if self._ownApp: self._blockApp = self.qApp else: self._blockApp = QEventLoop() self.runReturn() self._blockApp.exec_()
def ocrWindowTimer(self): # periodically check selected window ret = QTimer(self.q) ret.setSingleShot(False) #ret.setInterval(2000) # TODO: Allow change this value ret.timeout.connect(self.ocrWindow) return ret
class Loader(QMainWindow): def __init__(self, parent=None): super(Loader, self).__init__(parent) self.initUI() def updateDeviceList(self): self.statusBar().showMessage("Device list updating...", timeout=STATUS_BAR_TIMEOUT) self.deviceListWidget.updateList() self.statusBar().showMessage("Device list updated finished!", timeout=STATUS_BAR_TIMEOUT) def getFileName(self, ext): fname = QFileDialog.getOpenFileName(self, 'Open file', '/home') # if fname[0]: # f = open(fname[0], 'r') # with f: # data = f.read() # self.textEdit.setText(data) def initUI(self): # textEdit = QTextEdit() # self.setCentralWidget(textEdit) # self.setStyleSheet("QGroupBox { border: 1px solid gray; padding: 5px;}"); # Action to quit program exitAction = QAction(QIcon(None), 'Quit', self) exitAction.setShortcut('Ctrl+Q') exitAction.setStatusTip('Exit application') exitAction.triggered.connect(self.close) # # Action to update device list # self.refreshAction = QAction(QIcon('img/reload.png'), 'Refresh', self) # self.refreshAction.setShortcut('F5') # self.refreshAction.setStatusTip('Refresh list of connected devices.') # self.refreshAction.triggered.connect(self.updateDeviceList) # Action to show program information helpAction = QAction(QIcon(None), 'Help', self) helpAction.setShortcut('F1') helpAction.triggered.connect(self.showHelpDialog) # Action to help aboutAction = QAction(QIcon(None), 'About', self) aboutAction.triggered.connect(self.showAboutDialog) self.statusBar() # Add the file menu menubar = self.menuBar() fileMenu = menubar.addMenu('&File') # fileMenu.addAction(self.refreshAction) fileMenu.addAction(exitAction) fileMenu = menubar.addMenu('&Help') fileMenu.addAction(helpAction) fileMenu.addAction(aboutAction) # # Add the toolbar # toolbar = self.addToolBar('Exit') # # toolbar.addAction(self.refreshAction) # toolbar.setMovable(False) # Add the main windows widgets self.deviceListWidget = DeviceList(self.programDeviceHandler, self.infoDeviceHandler, self.resetDeviceHandler) self.fileSelectorWidget = FileSelector() self.setStyleSheet(""" QStatusBar { border-top: 1px solid #CCC; } QToolBar { border-top: 1px solid #DDD; border-bottom: 1px solid #CCC; } """) gbox = QGroupBox("Connected USB devices:") gboxLayout = QVBoxLayout() gboxLayout.addWidget(self.deviceListWidget) gbox.setLayout(gboxLayout) self.refreshEvent = QTimer() self.refreshEvent.setInterval(1250) self.refreshEvent.timeout.connect(self.USBUpdate) self.refreshEvent.start() layout = QVBoxLayout() layout.addWidget(self.fileSelectorWidget) layout.addWidget(gbox) self.setCentralWidget(QWidget()) self.centralWidget().setLayout(layout) self.setMinimumSize(620, 700) self.setMaximumWidth(620) self.setWindowFlags(Qt.Window | Qt.WindowMinimizeButtonHint | Qt.WindowCloseButtonHint) self.setGeometry(300, 300, 350, 250) self.setWindowTitle('keyplus layout and firmware loader') self.show() def process_layout(self, layout_json_obj, layout_file, device_id): try: settings_gen = layout.parser.SettingsGenerator( layout_json_obj, None) layout_data = settings_gen.gen_layout_section(device_id) settings_data = settings_gen.gen_settings_section(device_id) return layout_data, settings_data except (layout.parser.ParseError, layout.parser.ParseKeycodeError) as err: error_msg_box(str(err)) self.statusBar().showMessage( 'Error parsing "{}"'.format(layout_file), timeout=STATUS_BAR_TIMEOUT * 2) return None, None def abort_update(self, target_device): try: target_device.close() except: pass self.deviceListWidget.updateList() @Slot(str) def programDeviceHandler(self, device_path): target_device = self.tryOpenDevicePath(device_path) if target_device == None: self.abort_update(target_device) return programmingMode = self.fileSelectorWidget.getProgramingInfo() if is_bootloader_device( target_device ) and programmingMode != FileSelector.ScopeFirmware: error_msg_box( "Can only upload firmware while bootloader is running. " "Either reset it, or upload a firmware hex instead") self.abort_update(target_device) return if programmingMode == FileSelector.ScopeLayout: self.statusBar().showMessage("Started updating layout", timeout=STATUS_BAR_TIMEOUT) layout_file = self.fileSelectorWidget.getLayoutFile() if layout_file == '': error_msg_box("No layout file given.") self.abort_update(target_device) return else: pass layout_json_obj = None with open(layout_file) as file_obj: try: layout_json_obj = yaml.safe_load(file_obj.read()) except Exception as err: error_msg_box("Syntax error in yaml file: " + str(err)) self.abort_update(target_device) return device_info = protocol.get_device_info(target_device) layout_data, settings_data = self.process_layout( layout_json_obj, layout_file, device_info.id) if layout_data == None or settings_data == None: return protocol.update_layout_section(target_device, layout_data) protocol.update_settings_section(target_device, settings_data, keep_rf=True) protocol.reset_device(target_device) self.statusBar().showMessage("Finished updating layout", timeout=STATUS_BAR_TIMEOUT) elif programmingMode == FileSelector.ScopeDevice: layout_file = self.fileSelectorWidget.getRFLayoutFile() rf_file = self.fileSelectorWidget.getRFFile() target_id = self.fileSelectorWidget.getTargetID() self.statusBar().showMessage("Started updating RF settings", timeout=STATUS_BAR_TIMEOUT) if layout_file == '': error_msg_box("No layout file given.") self.abort_update(target_device) return elif rf_file == '': error_msg_box("No RF settings file given.") self.abort_update(target_device) return elif target_id == None: error_msg_box("No device id file given.") self.abort_update(target_device) return layout_json_obj = None rf_json_obj = None with open(layout_file) as file_obj: try: layout_json_obj = yaml.safe_load(file_obj.read()) except Exception as err: error_msg_box("Syntax error in yaml file: " + str(err)) self.abort_update(target_device) return with open(rf_file) as file_obj: try: rf_json_obj = yaml.safe_load(file_obj.read()) except Exception as err: error_msg_box("Syntax error in yaml file: " + str(err)) self.abort_update(target_device) return try: settings_gen = layout.parser.SettingsGenerator( layout_json_obj, rf_json_obj) except ParseError as err: error_msg_box("Error Generating RF settings data: " + str(err)) self.abort_update(target_device) return layout_data = settings_gen.gen_layout_section(target_id) settings_data = settings_gen.gen_settings_section(target_id) protocol.update_settings_section(target_device, settings_data) protocol.update_layout_section(target_device, layout_data) protocol.reset_device(target_device) self.statusBar().showMessage("Finished updating RF settings", timeout=STATUS_BAR_TIMEOUT) elif programmingMode == FileSelector.ScopeFirmware: fw_file = self.fileSelectorWidget.getFirmwareFile() self.statusBar().showMessage("Starting update firmware", timeout=STATUS_BAR_TIMEOUT) if fw_file == '': error_msg_box("No firmware file given.") else: if is_xusb_bootloader_device(target_device): self.program_xusb_boot_firmware_hex(target_device, fw_file) elif is_keyplus_device(target_device): try: serial_num = target_device.serial_number boot_vid, boot_pid = protocol.enter_bootloader( target_device) self.bootloaderProgramTimer = QTimer() self.bootloaderProgramTimer.setInterval(3000) self.bootloaderProgramTimer.setSingleShot(True) self.bootloaderProgramTimer.timeout.connect( lambda: self.programFirmwareHex( boot_vid, boot_pid, serial_num, fw_file)) self.bootloaderProgramTimer.start() except (easyhid.HIDException, protocol.KBProtocolException): error_msg_box( "Programming hex file failed: '{}'".format( fw_file)) else: try: target_device.close() except: pass raise Exception("Unimplementend programming mode") def programFirmwareHex(self, boot_vid, boot_pid, serial_num, file_name): device = None for i in range(1): en = easyhid.Enumeration(vid=boot_vid, pid=boot_pid).find() # Look for devices with matching serial_num number for dev in en: if dev.serial_number == serial_num: device = dev break # if a device was found with matching vid:pid, but it doesn't have # a matching serial_num number, then assume that the bootloader/firmware # doesn't set the serial_num number to the same value, so just program # the first matching device if len(en) != 0: device = en[0] break if device == None: error_msg_box("Couldn't connect to the device's bootloader") return else: if self.tryOpenDevice(device): return self.program_xusb_boot_firmware_hex(device, file_name) self.statusBar().showMessage("Finished updating firmware", timeout=STATUS_BAR_TIMEOUT) def program_xusb_boot_firmware_hex(self, device, file_name): try: xusb_boot.write_hexfile(device, file_name) except xusb_boot.BootloaderException as err: error_msg_box("Error programming the bootloader to hex file: " + str(err)) finally: device.close() def tryOpenDevicePath(self, device_path): try: device = easyhid.Enumeration().find(path=device_path)[0] device.open() return device except: msg_box( description="Failed to open device! Check it is still present " "and you have permission to write to it.", title="USB Device write error") return None def tryOpenDevice(self, device): try: device.open() return False except: msg_box( description="Failed to open device! Check it is still present " "and you have permission to write to it.", title="USB Device write error") return True @Slot(str) def resetDeviceHandler(self, device_path): device = self.tryOpenDevicePath(device_path) if device == None: return if is_keyplus_device(device): protocol.reset_device(device) elif is_xusb_bootloader_device(device): xusb_boot.reset(device) elif is_nrf24lu1p_bootloader_device(device): print("TODO: reset: ", device_path, file=sys.stderr) else: print("Can't reset device: ", device_path, file=sys.stderr) @Slot(str) def infoDeviceHandler(self, device_path): device = self.tryOpenDevicePath(device_path) if device == None: return settingsInfo = protocol.get_device_info(device) firmwareInfo = protocol.get_firmware_info(device) rfInfo = protocol.get_rf_info(device) if firmwareInfo.has_at_least_version('0.2.2'): errorInfo = protocol.get_error_info(device) else: errorInfo = None device.close() header = ["Attribute", "Value"] device_settings = [ ("Device ID", settingsInfo.id), ("Device name", settingsInfo.device_name_str()), ("Device serial number", device.serial_number), ("Last layout update", settingsInfo.timestamp_str()), ("Default report mode", settingsInfo.default_report_mode_str()), ("Matrix scan mode", settingsInfo.scan_mode_str()), ("Matrix columns", settingsInfo.col_count), ("Matrix rows", settingsInfo.row_count), ("Settings stored CRC", hex(settingsInfo.crc)), ("Settings computed CRC", hex(settingsInfo.computed_crc)), ("USB", not (settingsInfo.has_usb_disabled() or not firmwareInfo.has_fw_support_usb())), ("I2C", not (settingsInfo.has_i2c_disabled() or not firmwareInfo.has_fw_support_i2c())), ("nRF24 wireless", not (settingsInfo.has_nrf24_disabled() or not firmwareInfo.has_fw_support_nrf24())), ("Unifying mouse", not (settingsInfo.has_unifying_mouse_disabled() or not firmwareInfo.has_fw_support_unifying())), ("Bluetooth", not (settingsInfo.has_bluetooth_disabled() or not firmwareInfo.has_fw_support_bluetooth())), ("RF pipe0", binascii.hexlify(rfInfo.pipe0).decode('ascii')), ("RF pipe1", binascii.hexlify(rfInfo.pipe1).decode('ascii')), ("RF pipe2", "{:02x}".format(rfInfo.pipe2)), ("RF pipe3", "{:02x}".format(rfInfo.pipe3)), ("RF pipe4", "{:02x}".format(rfInfo.pipe4)), ("RF pipe5", "{:02x}".format(rfInfo.pipe5)), ("RF channel", str(rfInfo.channel)), ("RF auto retransmit count", str(rfInfo.arc)), ("RF data rate", protocol.data_rate_to_str(rfInfo.data_rate)), ] firmware_settings = [ ("Firmware version", "{}.{}.{}".format(firmwareInfo.version_major, firmwareInfo.version_minor, firmwareInfo.version_patch)), ("Firmware build date", str(datetime.datetime.fromtimestamp(firmwareInfo.timestamp))), ("Firmware git hash", "{:08x}".format(firmwareInfo.git_hash)), ("Layout storage size", firmwareInfo.layout_flash_size), ("Bootloader VID", "{:04x}".format(firmwareInfo.bootloader_vid)), ("Bootloader PID", "{:04x}".format(firmwareInfo.bootloader_pid)), ("Support scanning", firmwareInfo.has_fw_support_scanning()), ("Support scanning col to row", firmwareInfo.has_fw_support_scanning_col_row()), ("Support scanning row to col", firmwareInfo.has_fw_support_scanning_row_col()), ("Media keys", firmwareInfo.has_fw_support_key_media()), ("Mouse keys", firmwareInfo.has_fw_support_key_mouse()), ("Layer keys", firmwareInfo.has_fw_support_key_layers()), ("Sticky keys", firmwareInfo.has_fw_support_key_sticky()), ("Tap keys", firmwareInfo.has_fw_support_key_tap()), ("Hold keys", firmwareInfo.has_fw_support_key_hold()), ("Support 6KRO", firmwareInfo.has_fw_support_6kro()), ("Support NKRO", firmwareInfo.has_fw_support_key_hold()), ("Support indicator LEDs", firmwareInfo.has_fw_support_led_indicators()), ("Support LED backlighting", firmwareInfo.has_fw_support_led_backlighting()), ("Support ws2812 LEDs", firmwareInfo.has_fw_support_led_ws2812()), ("Support USB", firmwareInfo.has_fw_support_usb()), ("Support nRF24 wireless", firmwareInfo.has_fw_support_nrf24()), ("Support Unifying", firmwareInfo.has_fw_support_unifying()), ("Support I2C", firmwareInfo.has_fw_support_i2c()), ("Support Bluetooth", firmwareInfo.has_fw_support_bluetooth()), ] if errorInfo: error_codes = [] for code in errorInfo.get_error_codes(): error_codes.append((errorInfo.error_code_to_name(code), code)) else: error_codes = [ ('Error codes require firmware version 0.2.2 or greater', ) ] self.info_window = DeviceInformationWindow( self, header, device_settings, firmware_settings, error_codes, ) self.info_window.setModal(True) self.info_window.exec_() self.deviceListWidget.updateList() def USBUpdate(self): self.deviceListWidget.updateList() def showAboutDialog(self): QMessageBox.about(self, "About keyplus Loader", """ The keyplus layout and firmware loader. """) def showHelpDialog(self): QMessageBox.about( self, "keyplus Loader Help", """ This is the layout and firmware loader for the keyplus keyboard firmware. The layout files are *.yaml files. For documentation and examples see here: TODO The rf files are *.yaml files. For documentation and examples see here: TODO The firmware loader accepts *.hex files. For the latest keyplus firmware see here: TODO """)
class Fight(QObject): def __init__(self, mw): super().__init__() self._mw = mw # Кнопка "Отнять у слабого" self._css_path_button_fight = "div[class='button-big btn f1']" # Кнопка поедания Сникерса self._css_path_button_snikers = 'div[onclick*=snikers]' # Кнопка использования Тонуса self._css_path_button_use_tonus = 'div[onclick*=tonus]' # Таймер для ожидания загрузки страницы с выбором противника self._timer_enemy_load = QTimer() self._timer_enemy_load.setInterval(333) self._timer_enemy_load.timeout.connect(self._check_enemy_load) # Таймер для поиска противника self._timer_next_enemy = QTimer() self._timer_next_enemy.setInterval(1000) self._timer_next_enemy.setSingleShot(True) self._timer_next_enemy.timeout.connect(self._next_enemy) # Информация о противнике: имя, уровень, url self.enemy_name = None self.enemy_level = None self.enemy_url = None # Выигрыш / проигрыш. Если проигрыш, self.is_winner будет равен False self.received_money = None # True если победили мы, False если противник и None если ничья self.is_winner = None # Минимальная разница в уровне с противником. Эта величина вычитается из текущего уровня персонажа. self.min_diff_levels = 0 # Максимальная разница в уровне с противником. Эта величина добавляется к текущему уровню персонажа. # Нельзя нападать на противника, у которого уровень больше трех от нашего self.max_diff_levels = 3 # Сигнал вызывается, когда противник на странице найден -- например, страница загрузилась _enemy_load_finished = Signal() # Сигнал вызывается, когда противник подходит для нападения _enemy_found = Signal() def is_ready(self): """Возвращает True, если вызов метода run будет иметь смысл -- можем напасть, иначе False.""" try: # TODO: для того, чтобы метод self.fight.is_ready() работал правильно, текущим адресом должны # быть Закоулки -- метод has_snikers, используемый в is_ready работает только в Закоулках # Идем в Закоулки self._mw.alley() # TODO: рефакторинг с self._timeout_fight() if self._timeout_fight() is not None: logger.info('Напасть можно будет через %s секунд.', self._timeout_fight()) logger.info('self._timeout_fight() = %s.', self._timeout_fight()) logger.info('self.has_snickers() = %s.', self.has_snickers()) logger.info('self.is_ready() = %s.', self._timeout_fight() is None or self.has_snickers()) # True, если таймер закончился или есть Сникерс return self._timeout_fight() is None or self.has_snickers() except MoswarClosedError: raise except Exception as e: raise MoswarBotError(e) return False def _timeout_fight(self): """Функция возвращает количество оставшихся секунд до возможности напасть. Если секунд осталось 0 или меньше 0, то вернется None.""" for timeout in self._mw.doc.findAll('[id*=timeout]'): timer = timeout.attribute('timer') if timer and 'alley' in timeout.attribute('href'): timer = int(timer) return timer if timer > 0 else None def run(self): """Функция для нападения на игроков. Ищем слабого горожанина (заброшенного персонажа) -- не нужно привлекать внимание к боту. Уровень противника в пределах нашего +/- 1 """ try: if self._mw._used: logger.warn('Бот в данный момент занят процессом "%s". Выхожу из функции.', self._mw._used_process) return self._mw._used_process = "Нападение на игроков" logger.debug('Выполняю задание "%s".', self._mw._used_process) self._mw.alley() # TODO: оптимиизровать использование сникерсов -- если они есть, сразу использовать и нападать и так, # пока не будут потрачены все if not self.is_ready(): logger.debug('Нападать еще нельзя.') return self._mw._used = True # TODO: если есть тонус, использовать, чтобы сразу напасть # TODO: флаг на разрешение использования тонуса, чтобы сразу напасть # self.use_tonus() # Если не получилось съесть Сникерс, восстанавливаем по старинке if not self.eat_snickers(): if self._mw.current_hp() < self._mw.max_hp(): self._mw.restore_hp.run() logger.debug('Нажимаю на кнопку "Отнять у слабого".') # TODO: в одном из запусков дальше этой строки, похоже дело не пошло, возможно, страница с кнопкой # не прогрузилась # Кликаем на кнопку "Отнять у слабого" self._mw.click_tag(self._css_path_button_fight) # Если не нашли подходящего противника, смотрим следующего if not self._check_enemy(): self._timer_next_enemy.start() # Ожидаем пока противник не будет найден loop = QEventLoop() self._enemy_found.connect(loop.quit) loop.exec_() logger.debug('Нападаем на "%s" [%s]: %s.', self.enemy_name, self.enemy_level, self.enemy_url) # Кликаем на кнопку "Напасть" self._mw.click_tag('.button-fight a') # Перемотка битвы forward = '#controls-forward' # Ждем пока после клика прогрузится страница и появится элемент Waitable(self._mw).wait(forward) # Перематываем бой self._mw.click_tag(forward) # Обрабатываем результаты боя self.handle_results() except MoswarClosedError: raise except Exception as e: raise MoswarBotError(e) finally: self._mw._used = False def name_winner(self): """Функция возвращает имя победителя в драке.""" try: name = self._mw.doc.findFirst('.result div').toPlainText() name = name.replace('Победитель:', '') name = name[:name.rindex('[')] return name.strip() except Exception as e: raise MoswarElementIsMissError(e) def handle_results(self): """Обработка результата боя.""" result = '.result' # Ждем пока после клика прогрузится страница и появится элемент Waitable(self._mw).wait(result) # Найдем элемент, в котором будут все результаты боя result = self._mw.doc.findFirst(result) if 'Ничья!' in result.toPlainText(): self.is_winner = None self.received_money = 0 logger.debug('Результат боя: Ничья.') return # Проверим по именам кто победил self.is_winner = self._mw.name() == self.name_winner() tugriki = result.findFirst('.tugriki').toPlainText().replace(',', '') tugriki = int(tugriki) self.received_money = tugriki # Сначала покажем выигранные монет и опыт, потом все остальное. Список используется для того, чтобы # порядок вывода результата боя был в порядке добавления элементов в этот список result_item_keys = ['Монеты', 'Опыт'] result_dict = { 'Монеты': tugriki, 'Опыт': result.findFirst('.expa').toPlainText(), } neft = result.findFirst('.neft') if not neft.isNull(): result_item_keys.append('Нефть') result_dict['Нефть'] = int(neft.toPlainText()) # Искры не всегда будут -- обычно перед праздниками они появляются sparkles = result.findFirst('.sparkles') if not sparkles.isNull(): result_item_keys.append('Искры') result_dict['Искры'] = sparkles.toPlainText() for img in result.findAll('.object-thumb'): obj = img.findFirst('img').attribute('alt') count = img.findFirst('.count').toPlainText() result_dict[obj] = count result_item_keys.append(obj) result_list = list() for key in result_item_keys: result_list.append(' {}: {}'.format(key, result_dict[key])) result_str = 'Результат боя:' if not self.is_winner: result_str += ' Бой проигран. Вся награда достается противнику.' result_str += '\n' result_str += '\n'.join(result_list) logger.debug(result_str) # TODO: работает только в Закоулках def has_tonus(self): """Функция возвратит True, если можно использовать Тонус для сброса таймера, иначе False.""" button = self._mw.doc.findFirst(self._css_path_button_use_tonus) return not button.isNull() def use_tonus(self): """Функция для использования Тонуса, для сброса таймаута между драками. Возвращает True, если получилось, иначе False.""" if self.has_tonus(): logger.debug('Использую Тонус.') self._mw.click_tag(self._css_path_button_use_tonus) # TODO: если Тонуса будет не хватать, то появится окошко с предложением восстановить за плату # Ждем пока после клика прогрузится страница и появится элемент Waitable(self._mw).wait(self._css_path_button_use_tonus) return True return False # TODO: работает только в Закоулках def has_snickers(self): """Функция возвратит True, если можно съесть Сникерс, иначе False.""" button = self._mw.doc.findFirst(self._css_path_button_snikers) return not button.isNull() def eat_snickers(self): """Функция для съедания Сникерса. Возвращает True, если получилось съесть, иначе False.""" if self.has_snickers(): logger.debug('Съедаю сникерс.') self._mw.click_tag(self._css_path_button_snikers) # Ждем пока после клика прогрузится страница и появится элемент Waitable(self._mw).wait(self._css_path_button_fight) return True return False def _check_enemy_load(self): """Функция для ожидания загрузки страницы с выбором противника.""" enemy = self._mw.doc.findFirst('.fighter2') # Если нашли элемент, описывающий противника if not enemy.isNull(): self._enemy_load_finished.emit() self._timer_enemy_load.stop() def _check_enemy(self): """Функция ищет противника на текущей странице и проверяет его тип и уровень. Возвращает True если нашелся подходящий противник, иначе False. """ self._timer_enemy_load.start() loop = QEventLoop() self._enemy_load_finished.connect(loop.quit) loop.exec_() enemy = self._mw.doc.findFirst('.fighter2') # Определим тип противника -- нам нужен горожанин (нпс) is_npc = enemy.findFirst('.npc') is_npc = not is_npc.isNull() # Узнаем уровень противника level = enemy.findFirst('.level') level = level.toPlainText() level = level.replace('[', '').replace(']', '') level = int(level) # Гиперссылка на профиль противника a = enemy.findFirst('a') # Имя противника name = a.toPlainText() # Адрес противника url = urljoin(self._mw.moswar_url, a.attribute('href')) my_level = self._mw.level() # TODO: добавить ограничение на количество попыток найти гражданина, перед тем как напасть на игрока # Проверяем, что уровень противника находится в пределе диапазона check_level = my_level - self.min_diff_levels <= level <= my_level + self.max_diff_levels found = is_npc and check_level if found: self.enemy_name = name self.enemy_level = level self.enemy_url = url return found def _next_enemy(self): """Функция для поиска следующего противника.""" logger.debug('Ищем следующего противника.') # Кликаем на кнопку "Искать другого" self._mw.click_tag(".button-search a") # Если нашли противника if self._check_enemy(): self._enemy_found.emit() self._timer_next_enemy.stop() else: # Ищем дальше self._timer_next_enemy.start()
class QTReactor(PosixReactorBase): """ Qt based reactor. """ implements(IReactorFDSet) _timer = None def __init__(self): self._reads = {} self._writes = {} self._timer=QTimer() self._timer.setSingleShot(True) if QCoreApplication.startingUp(): self.qApp=QCoreApplication([]) self._ownApp=True else: self.qApp = QCoreApplication.instance() self._ownApp=False self._blockApp = None self._readWriteQ=[] """ some debugging instrumentation """ self._doSomethingCount=0 PosixReactorBase.__init__(self) def addReader(self, reader): if not reader in self._reads: self._reads[reader] = TwistedSocketNotifier(self, reader, QSocketNotifier.Read) def addWriter(self, writer): if not writer in self._writes: self._writes[writer] = TwistedSocketNotifier(self, writer, QSocketNotifier.Write) def removeReader(self, reader): if reader in self._reads: #self._reads[reader].shutdown() #del self._reads[reader] self._reads.pop(reader).shutdown() def removeWriter(self, writer): if writer in self._writes: self._writes[writer].shutdown() #del self._writes[writer] self._writes.pop(writer) def removeAll(self): return self._removeAll(self._reads, self._writes) def getReaders(self): return self._reads.keys() def getWriters(self): return self._writes.keys() def callLater(self,howlong, *args, **kargs): rval = super(QTReactor,self).callLater(howlong, *args, **kargs) self.reactorInvocation() return rval def crash(self): super(QTReactor,self).crash() def iterate(self,delay=0.0): t=self.running # not sure I entirely get the state of running self.running=True self._timer.stop() # in case its not (rare?) try: if delay == 0.0: self.reactorInvokePrivate() self._timer.stop() # supports multiple invocations else: endTime = delay + time.time() self.reactorInvokePrivate() while True: t = endTime - time.time() if t <= 0.0: return self.qApp.processEvents(QEventLoop.AllEvents | QEventLoop.WaitForMoreEvents,t*1010) finally: self.running=t def addReadWrite(self,t): self._readWriteQ.append(t) def runReturn(self, installSignalHandlers=True): QObject.connect(self._timer, SIGNAL("timeout()"), self.reactorInvokePrivate) self.startRunning(installSignalHandlers=installSignalHandlers) self._timer.start(0) def run(self, installSignalHandlers=True): try: if self._ownApp: self._blockApp=self.qApp else: self._blockApp = fakeApplication() self.runReturn(installSignalHandlers) self._blockApp.exec_() finally: self._timer.stop() # should already be stopped def reactorInvocation(self): self._timer.setInterval(0) def reactorInvokePrivate(self): if not self.running: self._blockApp.quit() self._doSomethingCount += 1 self.runUntilCurrent() t = self.timeout() if t is None: t=0.1 else: t = min(t,0.1) self._timer.setInterval(t*1010) self.qApp.processEvents() # could change interval self._timer.start() def doIteration(self): assert False, "doiteration is invalid call"
class InfoBar(object): """Adds a info bar to the bottom of the main window""" # Should be stored and restored from uistate.ini infoSettings = { "showCPU": True, "showMemory": True, "showDisk": True, "showNetwork": True } def __init__(self): global gEnableResourceMonitoring self.enableResourceMonitoring = gEnableResourceMonitoring self.bar = hiero.ui.mainWindow().statusBar() self.updateMonitorIntervalMS = gUpdateIntervalMS # The monitor update time in milliseconds. self.timer = QTimer() self.timer.setSingleShot(False) self.timer.timeout.connect(self.updateStatusBar) self.currentDiskIOBytes = psutil.disk_io_counters().read_bytes self.currentNetworkBytesReceived = psutil.net_io_counters().bytes_recv # This observes the current pid (the App process id) via psutil, and reports back if self.enableResourceMonitoring: self.processHelper = PSUtilProcessWrapper() # The frameServer instance self.frameServerInstance = nukestudio.frameServer # Initialise the Status Bar self.setupUI() # We haven't started monitoring at this point self.isMonitoring = False # Begin monitoring after a few secs to give frame server time to start up properly QTimer.singleShot(gInitialDelayMS, self.startMonitoring) def getLatestStyleSheet(self): styleFile = os.path.join(cwd, "style.stylesheet") with open(styleFile, "r") as fh: return fh.read() def hide(self): """Hide the info bar""" self.bar.setHidden(True) def show(self): """Show the info bar""" self.bar.setHidden(False) def rendersExistInQueue(self): """Return whether renders exist in the Frame server queue""" return len(self.frameServerInstance.renderQueue.requestsInProgress) > 0 def _handleServerUnreachable(self): """This is called when the server becomes unreachable""" print "[WARNING]: Nuke Frame Server was not reachable." self.frameserverStatusLabel.setPixmap(QPixmap("icons:Offline.png")) self.restartServerButton.setHidden(False) def _setResourcesLabelColour(self, memRatio, cpuUsage): """Sets the Resources label to be red if the memory usage gets too high""" if memRatio > 0.9: color = QColor(Qt.red) alpha = 220 values = "{r}, {g}, {b}, {a}".format(r=color.red(), g=color.green(), b=color.blue(), a=alpha) #self.nukeResourcesLabel.setStyleSheet("QLabel { vertical-align: middle; font: 10pt; color: rgba("+values+"); }") else: #self.nukeResourcesLabel.setStyleSheet("QLabel { vertical-align: middle; font: 10pt; }") return def restartServerAsync(self): self.frameServerInstance.stop() self.frameServerInstance.start() QTimer.singleShot(3000, self.startMonitoring) def restartServer(self): """Called to restart the Nuke Frame Server, done asynchronously""" self.stopMonitoring() self.restartServerButton.setHidden(True) #self.nukeResourcesLabel.setText("Re-Starting Frame Server...") QTimer.singleShot(150, self.restartServerAsync) # Displays the resources label, based on settings. def buildResourcesLabel(self): if self.isMonitoring: print "Building resources Label..." def updateResourcesStatusLabel(self): """Updates the Memory Label String""" if self.enableResourceMonitoring: if self.memoryString.isVisible(): self.updateMemoryLayout() if self.cpuString.isVisible(): self.updateCPULayout() if self.diskString.isVisible(): self.updateDiskLayout() if self.networkString.isVisible(): self.updateNetworkLayout() #totalSystemMemoryGB = self.processHelper.totalSystemMemory() #memRatio = currentMemUsageGB / totalSystemMemoryGB # This little test makes the label red if the memory usage exceeds 90% of the maximum allowed #self._setResourcesLabelColour( memRatio, currentCPUUsageAsPercentatge ) def updateMemoryLayout(self): currentMemUsageGB = self.processHelper.nukeMemoryUsageInGB() currentMemUsageAsPercentage = self.processHelper.nukeMemoryUsageAsPercentage( ) self.memoryString.setText( "%.2f GB (%.1f%%)" % (currentMemUsageGB, currentMemUsageAsPercentage)) def updateCPULayout(self): currentCPUUsageAsPercentatge = self.processHelper.nukeCPUUsageAsPercentage( ) self.cpuString.setText("%.1f%%" % currentCPUUsageAsPercentatge) def updateDiskLayout(self): diskMBPerSec = self._diskMBPerSec() self.diskString.setText("%.2f MB/s" % diskMBPerSec) def updateNetworkLayout(self): networkMBPerSec = self._networkMBPerSec() self.networkString.setText("%.2f MB/s" % networkMBPerSec) def _diskMBPerSec(self): """Returns Total Disk Read+Write speed in MB/s""" oldBytes = self.currentDiskIOBytes DISKS = psutil.disk_io_counters(perdisk=True) readWriteBytes = [(DISKS[disk].read_bytes, DISKS[disk].write_bytes) for disk in DISKS.keys()] newBytes = sum([sum(x) for x in zip(*readWriteBytes)]) bytesDiff = newBytes - oldBytes self.currentDiskIOBytes = newBytes bytesPerSecond = (newBytes - oldBytes) / (self.updateMonitorIntervalMS / 1000) MBPerSecond = bytesToMB(bytesPerSecond) return MBPerSecond def _networkMBPerSec(self): """Returns Total Network Read+Write speed in MB/s""" oldBytes = self.currentNetworkBytesReceived NET = psutil.net_io_counters(pernic=True) readWriteBytes = [(NET[adapter].bytes_recv, NET[adapter].bytes_sent) for adapter in NET.keys()] newBytes = sum([sum(x) for x in zip(*readWriteBytes)]) bytesDiff = newBytes - oldBytes self.currentNetworkBytesReceived = newBytes bytesPerSecond = (newBytes - oldBytes) / (self.updateMonitorIntervalMS / 1000) MBPerSecond = bytesToMB(bytesPerSecond) return MBPerSecond def setupUI(self): """Initialise the UI""" self.bar.setStyleSheet(self.getLatestStyleSheet()) #self.bar.setFixedHeight(30) self.frameserverStatusLabel = QLabel("") # Resources self.cpuIconPath = os.path.join(gIconPath, "cpu.png") self.memoryIconPath = os.path.join(gIconPath, "memory.png") self.diskReadIconPath = os.path.join(gIconPath, "disk_read.png") self.networkReadIconPath = os.path.join(gIconPath, "net_read.png") # MEMORY SECTION self.memoryImageButton = QPushButton( QPixmap( (self.memoryIconPath)).scaledToHeight(20, Qt.SmoothTransformation), "") self.memoryImageButton.setObjectName("show_button_memory") self.memoryImageButton.setToolTip( "Click to toggle monitoring of 'Real Memory' usage") self.memoryString = QLabel("MEMORY (GB)") self.memoryString.setToolTip( "'Real Memory' usage of this Nuke Session") self.memoryImageButton.clicked.connect( lambda: self.show_button_clicked(self.memoryString)) # CPU SECTION self.cpuImageButton = QPushButton( QPixmap( (self.cpuIconPath)).scaledToHeight(20, Qt.SmoothTransformation), "") self.cpuImageButton.setObjectName("show_button_cpu") self.cpuImageButton.setToolTip( "Click to toggle monitoring of CPU usage of this Nuke Session") self.cpuString = QLabel("CPU (%)") self.cpuString.setToolTip("CPU usage of this Nuke Session") self.cpuImageButton.clicked.connect( lambda: self.show_button_clicked(self.cpuString)) # DISK SECTION self.diskImageButton = QPushButton( QPixmap((self.diskReadIconPath)).scaledToHeight( 20, Qt.SmoothTransformation), "") self.diskImageButton.setObjectName("show_button_disk") self.diskImageButton.setToolTip( "Click to toggle monitoring of Disk Read+Write usage for this machine" ) self.diskString = QLabel("DISK (MB/s)") self.diskImageButton.clicked.connect( lambda: self.show_button_clicked(self.diskString)) self.diskString.setToolTip("Disk Read+Write usage for this machine") # NETWORK SECTION self.networkImageButton = QPushButton( QPixmap((self.networkReadIconPath)).scaledToHeight( 20, Qt.SmoothTransformation), "") self.networkImageButton.setObjectName("show_button_network") self.networkImageButton.setToolTip( "Click to toggle monitoring of Network Read+Write traffic") self.networkString = QLabel("NETWORK (MB/s)") self.networkString.setToolTip( "Total Network Read+Write traffic for this machine") self.networkImageButton.clicked.connect( lambda: self.show_button_clicked(self.networkString)) # Settings Button - Displays what options should be shown in the Status Bar self.settingsButton = QPushButton() self.settingsButton.setIcon(QIcon("icons:Settings.png")) self.settingsButton.clicked.connect(self.showSettings) # Build the layout based on Preferences #self.cpuWidget.setVisible(self.infoSettings['showCPU']) #self.memoryWidget.setVisible(self.infoSettings['showMemory']) #self.diskWidget.setVisible(self.infoSettings['showDisk']) #self.networkWidget.setVisible(self.infoSettings['showNetwork']) self.restartServerButton = QPushButton( QPixmap("icons:TransformRotateRight.png").scaledToHeight( 20, Qt.SmoothTransformation), "") self.restartServerButton.setFixedHeight(16) self.restartServerButton.clicked.connect(self.restartServer) self.restartServerButton.setHidden(True) self.restartServerButton.setFlat(True) self.restartServerButton.setToolTip( "Click here to restart the Nuke Frameserver") self.frameServerIsRendering = False self.spinnerMovie = QMovie("icons:RenderingSpinner.gif") self.spinnerMovie.start() self.bar.addPermanentWidget(self.cpuImageButton) self.bar.addPermanentWidget(self.cpuString) self.bar.addPermanentWidget(self.memoryImageButton) self.bar.addPermanentWidget(self.memoryString) self.bar.addPermanentWidget(self.diskImageButton) self.bar.addPermanentWidget(self.diskString) self.bar.addPermanentWidget(self.networkImageButton) self.bar.addPermanentWidget(self.networkString) self.bar.addPermanentWidget(self.frameserverStatusLabel) self.bar.addPermanentWidget(self.restartServerButton) self.bar.addPermanentWidget(self.settingsButton) def show_button_clicked(self, sender): sender.setVisible(not sender.isVisible()) def _updateUIForServerIsRunning(self): """Updates the UI for when the server is reachable""" #self.frameserverStatusLabel.setToolTip("Nuke Frame Server is reachable") self.getFrameServerWorkers() self.frameserverStatusLabel.setPixmap(QPixmap("icons:OK.png")) self.restartServerButton.setHidden(True) def showSettings(self): dialog = SettingsDialog() dialog.show() # Returns a nicely formatted list of Frame Server workers def getFrameServerWorkers(self): statusString = str(self.frameServerInstance.getStatus(1000)) workers = re.findall("workerStatus \{.*?\}", statusString) if len(workers) == 0: self.frameserverStatusLabel.setToolTip( "Unable to determine number of frame server workers.") return prettyWorkersString = "Frame Server Status (%i workers):\n" % len( workers) + "\n".join(workers) self.frameserverStatusLabel.setToolTip(prettyWorkersString) def updateStatusBar(self): """Updates the Status bar widgets depending on whether the frameServer is reachable""" #print "Status: ", str(self.frameServerInstance.getStatus(10)) # DEBUG - Stylesheet Changes can be applied here and seen live #self.bar.setStyleSheet( self.getLatestStyleSheet() ) try: isRunning = self.frameServerInstance.isRunning(0.25) if isRunning and not self.rendersExistInQueue(): self._updateUIForServerIsRunning() self.frameServerIsRendering = False elif isRunning and self.rendersExistInQueue(): if self.frameServerIsRendering == False: self.frameServerIsRendering = True self.frameserverStatusLabel.setPixmap(None) self.frameserverStatusLabel.setMovie(self.spinnerMovie) else: self._handleServerUnreachable() self.updateResourcesStatusLabel() except: self._handleServerUnreachable() self.updateResourcesStatusLabel() def startMonitoring(self): """This timer fires every X milliseconds to update the status.""" self.timer.start(self.updateMonitorIntervalMS) self.isMonitoring = True def stopMonitoring(self): """Stops the monitoring process""" self.timer.stop() self.isMonitoring = False
app = QApplication(sys.argv) win = app.desktop().screenGeometry() cfg = config.XiboConfig(arg.config) signal.signal(signal.SIGINT, lambda s, f: app.quit()) if not os.path.isfile(arg.config): print print 'The configuration file %s is not exists.' % arg.config print 'Creating default configuration ...' cfg.save() print "Please edit the '%s' file and then rerun xibopy again." % arg.config print sys.exit(0) r = -1 with ui.XiboWindow(cfg) as w: t = QTimer() t.setSingleShot(True) t.timeout.connect(w.showFullScreen) t.start(1000) w.setGeometry(win) w.show() r = app.exec_() print 'Exiting, please wait ...' sys.exit(r) # # #
class Jaime(QObject,Singleton): instance = None logger = None def __init__(self): if Jaime.instance: raise Exception("Can't call to constructor with another instance created") self.tabs_widget = QTabWidget() self.view = QWebView() self.page = QWebPage() self.config = SafeConfigParser() Logger.getLoggerFor(self.__class__) self.tabs_widget.insertTab(0,self.view,'label') self.tabs = {} self.graph_file = None self.close_tab_timer = QTimer() self.close_tab_timer.setSingleShot(False) #cada 30 segundos se cierra un tab self.close_tab_timer.setInterval(10000) self.view.setPage(self.page) self.tabs['mainTab'] = self.view self.network_manager = CustomNetworkAccessManager.getInstance() self.navigator = Navigator.getInstance() self.route_node = YahooRouteNode.getInstance() self.graph_parser = GraphParser.getInstance() self.page.setNetworkAccessManager(self.network_manager) def loadConfig(self,config_file): self.config.read(config_file) def loadParam(self,name,value): name = name.strip() # print 'get param [%s]' % name if not self.config.has_section('PARAMS'): # print 'cree la seccion' self.config.add_section('PARAMS') self.config.set('PARAMS',name.strip(),value) # print 'seteo %s a %s ' % (name,value) def getParam(self,name,default=None): name = name.strip() # print 'get param [%s]' % name if self.config.has_section('PARAMS') and \ self.config.has_option('PARAMS',name): # print 'get param 1 %s' % name return self.config.get('PARAMS',name) if default != None: return default return None def toggleDelegationPolicy(self, delegate=None): if self.page.linkDelegationPolicy() == QWebPage.DontDelegateLinks or \ ( isinstance(delegate,bool) and delegate ): self.logger.info('cambio a delegate links') self.page.setLinkDelegationPolicy(QWebPage.DelegateAllLinks) elif self.page.linkDelegationPolicy() == QWebPage.DelegateAllLinks or \ ( isinstance(delegate,bool) and not delegate ): self.logger.info('cambio a dont delegate links') self.page.setLinkDelegationPolicy(QWebPage.DontDelegateLinks) else: self.logger.warn("Can't set delegation policy") def setGraph(self,filename): self.graph_file = filename def start(self): self.logger.info('---------------------------- Jaime start work ---------------------------------') self.logger.info('Graph file = %s' % self.graph_file) if self.config.has_section('PARAMS') : self.logger.info('[PARAMS]') for name,value in self.config.items('PARAMS'): self.logger.info(' %s = %s' % (name,value)) self.page.setNetworkAccessManager(self.network_manager) self.page.loadFinished.connect(self.navigator.processPageLoadFinished) self.page.loadStarted.connect(self.navigator.processLoadStarted) self.page.linkClicked.connect(self.openLinkOnTab) self.close_tab_timer.timeout.connect(self.closeOpenTab) self.graph_parser.loadGraph(self.graph_file) if not self.navigator.takeEntryPoint(): self.finishWork() self.tabs_widget.show() # self.tabs_widget.showMaximized() def finishWork(self): self.logger.info('Jaime termina su funcionamiento') QApplication.closeAllWindows() def openLinkOnTab(self,link): l = len(self.tabs) new_tab_key = 'newTab_%s' % time.time() self.tabs[new_tab_key] = QWebView() self.tabs[new_tab_key].load(link) self.tabs_widget.insertTab(self.tabs_widget.count(),self.tabs[new_tab_key],new_tab_key) if self.close_tab_timer.timerId() == -1 : self.logger.info('starteo el close_tab_timer') self.close_tab_timer.start() def closeOpenTab(self): if len(self.tabs) == 1 and self.close_tab_timer.timerId() != -1 : self.logger.info('stopeo el close_tab_timer') self.close_tab_timer.stop() return ks = self.tabs.keys() ks.remove('mainTab') ks.sort() last_key = ks[0] index = None for i in range(len(self.tabs)): if self.tabs_widget.tabText(i) == last_key: index = i break if index: del self.tabs[last_key] self.tabs_widget.removeTab(index) else: # print 'stopeo el close_tab_timer' self.logger.error('no se encontro tab para remover con nombre %s' % last_key)
class Macro_editor(QWidget): def __init__(self,parent=None,title='Macro editor'): # This class derivates from a Qt Widget so we have to call # the class builder ".__init__()" QWidget.__init__(self) # "self" is now a Qt Widget, then we instantiate the user interface # generated with QtDesigner and call it self.ui self.ui = Ui_Macro_editor() # Now we have to feed the GUI building method of this object (self.ui) # with a reference to the Qt Widget where it will appear, i.e. itself # but the elements of the GUI will actually be children of the GUI (i.e of self.ui not of self) self.ui.setupUi(self) self.setWindowTitle(title) # List the Macro command that will be loaded and available # into the Macro editor. # Once you have created a new Macro command class in this file # you must add it in this list for it to be available in # the Macro editor. # You may also remove commands from this list if you want to deactivate them self.valid_list_of_commands=[ AngleCommand(), CommentsCommand(), WaitCommand(), WaitForEpoch(), SetFieldCommand(), WaitForHStableCommand(), WaitForMeasureCommand(), SetPersistFieldCommand(), SetTempCommand(), SetHeaterCommand(), WaitForTStableCommand(), SetTempVTICommand(), SetVTIHeaterCommand(), SetSaveFileCommand(), StartMeasureCommand(), StopMeasureCommand(), EmailCommand(), EmailFileCommand(), EmailDirCommand(), SetICommand(), SetVCommand()] ### Dirty FIX: the macro editor is in a layout and a group box which somehow ### put it way down the list of children, so we need the 6th order parent of that to get to the main ! ##self.main=self.parent().parent().parent().parent().parent().parent() ### FIXED:the main class now simply gives a link to itself when it instantiates the Macro Editor ### this has the added benefit of keeping track of which class may use the main self.main=parent self.macro_isActive=False self.macro_timer = QTimer() #Use a single shot timer in between commands #otherwise it may fire again before the task is completed self.macro_timer.setSingleShot(True) self.macro_timer.timeout.connect(self.next_command) #initiate syntax highlighting for the macro editor self.highlighter = MacroHighlighter(self.ui.macro_textbox.document(),self.valid_list_of_commands) #update the list of available macro commands in the user interface model=QStandardItemModel() parentItem = model.invisibleRootItem() for command in self.valid_list_of_commands: parentItem.appendRow(QStandardItem(command.label)) self.ui.macrocommandtree.setModel(model) def save_macro(self): #the static method calls the native file system method fileName = QFileDialog.getSaveFileName(self,"Save Macro",dir= "./Macro",filter="Macro file (*.mac)") #open() does not work with 'unicode' type object, conversion is needed fileName=fileName[0].encode('utf8') if fileName!="": #get the macro text from frontpanel text box mac_unicode=self.ui.macro_textbox.toPlainText() #print fileName #(u'C:/Python27/Lib/site-packages/pyqtgraph/examples/test.txt', u'All Files (*.*)') #WARNING: write() and open() do not work with 'unicode' type object #they have to be converted to a string first (i.e. a list of bytes) savefile=open(fileName,'w') savefile.write(mac_unicode.encode('utf8')) savefile.close() def open_macro(self): fileName = QFileDialog.getOpenFileName(self,"Open Macro",dir= "./Macro",filter="Macro file (*.mac)") #open() does not work with 'unicode' type object, conversion is needed fileName=fileName[0].encode('utf8') if fileName!="": open_file=open(fileName,'r') self.ui.macro_textbox.setPlainText(unicode(open_file.read(-1),encoding='utf-8')) #'-1' to read the whole file at once open_file.close() def run_macro(self): if not(self.macro_isActive): self.macro_isActive=True self.current_macro=self.ui.macro_textbox.toPlainText().encode('utf8').split('\n') self.current_line=0 self.cur_mac_max=len(self.current_macro) #single shot timer print "Starting Macro" self.macro_timer.start(0) def next_command(self): stdwtime=500 if self.current_line<self.cur_mac_max: #update the info line with the line being analyzed action=self.current_macro[self.current_line] self.ui.mac_curr_line.setText("line "+str(self.current_line)+": "+action) next_move=1 wait_time=500 for command in self.valid_list_of_commands: #run the command, if command matches the line being analyzed if command.regexp.indexIn(action)!=-1: command.run(self.main) next_move=command.next_move wait_time=command.wait_time break #break the for loop #go to line N+next_move of the current macro, after wait_time milliseconds self.current_line+=next_move self.macro_timer.start(wait_time) else: #end of macro reached self.stop_macro() def stop_macro(self): self.macro_timer.stop() self.macro_isActive=False #TODO : signal/slot !!! if self.main.measurements_thread.isAlive(): self.main.stop_measurements() print "End of Macro"
class Quiz(QFrame): def __init__(self, options, parent=None): super(Quiz, self).__init__(parent) self.options = options """Session Info""" self.status = QFrame() #session message self.status.message = QLabel(u'') #achievements self.status.achievements = Achievements() self.status.info = QLabel(u'') self.status.progress = QProgressBar() self.status.layout = QVBoxLayout() #layout self.status.layout.addWidget(self.status.info) self.status.layout.addWidget(self.status.progress) self.status.layout.addWidget(self.status.message) self.status.setLayout(self.status.layout) #mouse event filter self.status.filter = StatusFilter(self.status) self.status.setAttribute(Qt.WA_Hover, True) self.status.installEventFilter(self.status.filter) """Items Info""" self.info = QFrame() self.info.reading = QLabel(u'') self.info.item = QLabel(u'') self.info.components = QLabel(u'') separator_one = QFrame() separator_one.setFrameShape(QFrame.HLine) separator_one.setFrameShadow(QFrame.Sunken) separator_two = QFrame() separator_two.setFrameShape(QFrame.HLine) separator_two.setFrameShadow(QFrame.Sunken) self.info.layout = QVBoxLayout() self.info.layout.addWidget(self.info.reading) self.info.layout.addWidget(separator_one) self.info.layout.addWidget(self.info.item) self.info.layout.addWidget(separator_two) self.info.layout.addWidget(self.info.components) self.info.setLayout(self.info.layout) """Verbose Info""" self.allInfo = QFrame() self.allInfo.layout = QGridLayout() self.allInfo.setLayout(self.allInfo.layout) #the rest is (should be) generated on the fly """Kanji info""" self.kanjiInfo = QFrame() self.kanjiInfo.layout = QVBoxLayout() self.kanjiInfo.info = QLabel(u'') self.kanjiInfo.layout.addWidget(self.kanjiInfo.info) self.kanjiInfo.setLayout(self.kanjiInfo.layout) """Kanji groups""" self.kanjiGroups = QFrame() self.kanjiGroups.layout = QVBoxLayout() self.kanjiGroups.info = QLabel(u'') self.kanjiGroups.layout.addWidget(self.kanjiGroups.info) self.kanjiGroups.setLayout(self.kanjiGroups.layout) """Global Flags""" #self.correct = False """Quiz Dialog""" self.filter = Filter() #### visual components ### self.countdown = QProgressBar() self.sentence = QLabel(u'') self.var_1st = QPushButton(u'') self.var_2nd = QPushButton(u'') self.var_3rd = QPushButton(u'') self.var_4th = QPushButton(u'') self.answered = QPushButton(u'') self.answered.hide() ### layouts #### self.layout_vertical = QVBoxLayout() #main self.layout_horizontal = QHBoxLayout() #buttons self.layout_horizontal.addWidget(self.var_1st) self.layout_horizontal.addWidget(self.var_2nd) self.layout_horizontal.addWidget(self.var_3rd) self.layout_horizontal.addWidget(self.var_4th) self.layout_vertical.addWidget(self.countdown) self.layout_vertical.addWidget(self.sentence) self.layout_vertical.addLayout(self.layout_horizontal) self.layout_horizontal.addWidget(self.answered) self.setLayout(self.layout_vertical) ### utility components ### self.trayIcon = QSystemTrayIcon(self) self.trayMenu = QMenu() self.gifLoading = QMovie('../res/cube.gif') self.gifLoading.frameChanged.connect(self.updateTrayIcon) ### initializing ### self.initializeResources() ### timers ### self.nextQuizTimer = QTimer() self.nextQuizTimer.setSingleShot(True) self.nextQuizTimer.timeout.connect(self.showQuiz) self.countdownTimer = QTimer() self.countdownTimer.setSingleShot(True) self.countdownTimer.timeout.connect(self.timeIsOut) self.trayUpdater = None #self.trayUpdater = RepeatTimer(1.0, self.updateTrayTooltip, self.options.getRepetitionInterval() * 60) self.remaining = 0 """Start!""" if self.options.isQuizStartingAtLaunch(): self.waitUntilNextTimeslot() self.trayIcon.setToolTip('Quiz has started automatically!') self.pauseAction.setText('&Pause') self.trayIcon.showMessage('Loading complete! (took ~'+ str(self.loadingTime.seconds) + ' seconds) Quiz underway.', 'Lo! Quiz already in progress!' + self.loadingStatus, QSystemTrayIcon.MessageIcon.Warning, 10000) else: self.trayIcon.setToolTip('Quiz is not initiated!') self.trayIcon.showMessage('Loading complete! (took ~' + str(self.loadingTime.seconds) + ' seconds) Standing by.', 'Quiz has not started yet! If you wish, you could start it manually or enable autostart by default.' + self.loadingStatus, QSystemTrayIcon.MessageIcon.Information, 10000 ) self.setWindowIcon(QIcon(PATH_TO_RES + ICONS + 'suzu.png')) """Test calls here:""" ### ... ### #self.connect(self.hooker, SIGNAL('noQdict'), self.noQdict) self.gem = self.saveGeometry() def startUpdatingTrayTooltip(self): self.remaining = self.nextQuizTimer.interval() if self.trayUpdater is not None and self.trayUpdater.isAlive(): self.trayUpdater.cancel() self.trayUpdater = RepeatTimer(1.0, self.updateTrayTooltip, self.options.getRepetitionInterval() * 60) self.trayUpdater.start() def updateTrayTooltip(self): self.remaining -= UPDATE_FREQ self.trayIcon.setToolTip('Next quiz in ' + (str(self.remaining/UPDATE_FREQ) + ' seconds')) # def noQdict(self): # self.showSessionMessage('Nope, cannot show quick dictionary during actual quiz.') #################################### # Initialization procedures # #################################### def initializeResources(self): """Initialize Options""" # self.options = Options() self.loadingStatus = u'' self.qload = QuickLoad(self.options) if self.options.isLoadingOnStart(): self.qload.exec_() """Pre-initialization""" self.animationTimer = () self.progressTimer = () self.grid_layout =() """Initialize Statistics""" self.stats = Stats() """Config Here""" self.initializeComposition() self.initializeComponents() self.setMenus() self.trayIcon.show() #self.startTrayLoading() """"Initialize Dictionaries (will take a some time!)""" time_start = datetime.now() self.trayIcon.showMessage('Loading...', 'Initializing dictionaries', QSystemTrayIcon.MessageIcon.Information, 20000 ) # kanji composition # if self.options.isLoadingRadk(): self.rdk = RadkDict() else: self.loadingStatus += '--> Radikt disabled!\n' # edict dictionary if self.options.isLoadingEdict(): edict_file = resource_filename('cjktools_data', 'dict/je_edict') self.edict = auto_format.load_dictionary(edict_file) else: self.edict = None self.loadingStatus += '--> Edict disabled!\n' # kanjidict dictionary # if self.options.isLoadingKdict(): self.kjd = kanjidic.Kanjidic() else: self.kjd = None self.loadingStatus += '--> Kanjidict disabled!\n' # Kanji.Odyssey groups # self.groups = KanjiGrouper() if self.options.isLoadingGroups(): self.groups.loadgroupsFromDump() else: self.loadingStatus += '--> Kanji.Odyssey disabled!\n' """Initializing srs system""" self.trayIcon.showMessage('Loading...', 'Initializing databases', QSystemTrayIcon.MessageIcon.Information, 20000 ) self.srs = srsScheduler() if self.options.isLoadingDb(): self.srs.initializeCurrentSession(self.options.getQuizMode(), self.options.getSessionSize()) else: self.loadingStatus += '--> Database disabled!\n' """Jmdict lookup""" self.jmdict = DictionaryLookup() if self.options.isLoadingJmdict(): self.jmdict.loadJmdictFromDumpRegex() self.jmdict.joinTables() else: self.loadingStatus += '--> Jmdict disabled!\n' """Manual add dialog""" self.manualAddDialog = ManualAdd(self.srs.db) if self.loadingStatus != '': self.loadingStatus = '\n\n' + self.loadingStatus time_end = datetime.now() self.loadingTime = time_end - time_start #################################### # Composition and appearance # #################################### def initializeComposition(self): """Main Dialog""" self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint) self.setFocusPolicy(Qt.StrongFocus) self.setFrameStyle(QFrame.StyledPanel | QFrame.Raised) #NB: This font will be used in buttons self.setFont(QFont(Fonts.TukusiMyoutyouProLB, self.options.getQuizFontSize())) desktop = QApplication.desktop().screenGeometry() self.setGeometry(QRect(desktop.width() - H_INDENT, desktop.height() - V_INDENT, D_WIDTH, D_HEIGHT)) self.setStyleSheet("QWidget { background-color: rgb(252, 252, 252); }") """Info dialog""" self.info.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint) self.info.setFrameStyle(QFrame.StyledPanel | QFrame.Raised) self.info.setGeometry(QRect(desktop.width() - H_INDENT - I_WIDTH - I_INDENT, desktop.height() - V_INDENT, I_WIDTH, I_HEIGHT)) self.info.setStyleSheet("QWidget { background-color: rgb(252, 252, 252); }") """Verbose info dialog""" self.allInfo.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint) self.allInfo.setFrameStyle(QFrame.StyledPanel | QFrame.Raised) self.allInfo.setGeometry(QRect(desktop.width() - H_INDENT - I_WIDTH - I_INDENT, desktop.height() - V_INDENT, I_WIDTH, I_HEIGHT)) self.allInfo.setStyleSheet("QWidget { background-color: rgb(252, 252, 252); }") """Session message""" self.status.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint) self.status.setFrameStyle(QFrame.StyledPanel | QFrame.Raised) self.status.setGeometry(QRect(desktop.width() - H_INDENT, desktop.height() - V_INDENT - S_HEIGHT - S_INDENT - S_CORRECTION, S_WIDTH, S_HEIGHT)) self.status.setMinimumSize(S_WIDTH, S_HEIGHT) # self.status.setMinimumWidth(S_WIDTH) self.status.setStyleSheet("QWidget { background-color: rgb(252, 252, 252); }") self.setMask(roundCorners(self.rect(),5)) # self.status.setMask(roundCorners(self.status.rect(),5)) #self.info.setMask(roundCorners(self.info.rect(),5)) #self.allInfo.setMask(roundCorners(self.allInfo.rect(),5)) """Kanji info""" self.kanjiInfo.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint) self.kanjiInfo.setFrameStyle(QFrame.StyledPanel | QFrame.Raised) self.kanjiInfo.setGeometry(QRect(desktop.width() - H_INDENT - K_WIDTH - K_INDENT, desktop.height() - V_INDENT, K_WIDTH, K_HEIGHT)) self.kanjiInfo.setStyleSheet("QWidget { background-color: rgb(252, 252, 252); }") """Kanji groups""" self.kanjiGroups.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint) self.kanjiGroups.setFrameStyle(QFrame.StyledPanel | QFrame.Raised) self.kanjiGroups.setGeometry(QRect(desktop.width() - H_INDENT - G_WIDTH - G_INDENT, desktop.height() - V_INDENT, G_WIDTH, G_HEIGHT)) self.kanjiGroups.setStyleSheet("QWidget { background-color: rgb(250, 250, 250); }") # self.setMask(roundCorners(self.rect(),5)) # self.status.setMask(roundCorners(self.status.rect(),5)) def initializeComponents(self): self.countdown.setMaximumHeight(6) self.countdown.setRange(0, self.options.getCountdownInterval() * 100) self.countdown.setTextVisible(False) self.countdown.setStyleSheet("QProgressbar { background-color: rgb(255, 255, 255); }") #self.setFont(QFont(Fonts.SyoutyouProEl, 40))#self.options.getQuizFontSize())) self.sentence.setAlignment(Qt.AlignmentFlag.AlignCenter) #self.sentence.setFont(QFont(Fonts.HiragiNoMarugotoProW4, self.options.getSentenceFontSize())) self.sentence.setFont(QFont(self.options.getSentenceFont(), self.options.getSentenceFontSize())) self.sentence.setWordWrap(True) self.trayIcon.setIcon(QIcon(PATH_TO_RES + TRAY + 'active.png')) self.status.message.setFont(QFont('Cambria', self.options.getMessageFontSize())) self.status.layout.setAlignment(Qt.AlignCenter) self.status.message.setWordWrap(False) self.status.message.setAlignment(Qt.AlignCenter) self.status.layout.setMargin(0) self.status.info.setHidden(True) self.status.progress.setHidden(True) self.status.progress.setMaximumHeight(10) self.status.progress.setRange(0, self.status.achievements.threshold) self.status.layout.setAlignment(Qt.AlignCenter) self.status.info.setAlignment(Qt.AlignCenter) self.status.info.setFont(QFont(Fonts.RyuminPr5, 13)) self.status.info.setWordWrap(False) self.status.gem = self.status.saveGeometry() self.info.item.setFont(QFont(Fonts.HiragiNoMyoutyouProW3, 36)) self.info.reading.setFont(QFont(Fonts.HiragiNoMyoutyouProW3, 16)) self.info.components.setFont((QFont(Fonts.HiragiNoMyoutyouProW3, 14))) #self.info.item.setWordWrap(True) self.info.components.setWordWrap(True) #self.info.layout.setAlignment(Qt.AlignCenter) self.info.layout.setMargin(0) #self.info.layout.setSizeConstraint(self.info.layout.SetFixedSize) #NB: would work nice, if the anchor point was in right corner self.info.reading.setAlignment(Qt.AlignCenter) self.info.item.setAlignment(Qt.AlignCenter) self.info.components.setAlignment(Qt.AlignCenter) #self.info.setLayoutDirection(Qt.RightToLeft) self.info.reading.setStyleSheet("QLabel { color: rgb(155, 155, 155); }") self.info.components.setStyleSheet("QLabel { color: rgb(100, 100, 100); }") self.kanjiInfo.info.setFont(QFont(Fonts.MSMyoutyou, 14.5)) self.kanjiInfo.info.setAlignment(Qt.AlignCenter) self.kanjiInfo.info.setWordWrap(True) self.kanjiInfo.layout.setMargin(0) self.kanjiGroups.info.setFont(QFont(Fonts.MSMyoutyou, 18.5)) self.kanjiGroups.info.setAlignment(Qt.AlignCenter) self.kanjiGroups.info.setWordWrap(True) self.kanjiGroups.layout.setMargin(0) #NB: ... self.answered.setMaximumWidth(D_WIDTH) self.answered.setFont(QFont('Calibri', 11)) #################################### # Updating content # #################################### def updateContent(self): """Resetting multi-label sentence""" if self.grid_layout != (): for i in range(0, self.grid_layout.count()): self.grid_layout.itemAt(i).widget().hide() self.layout_vertical.removeItem(self.grid_layout) self.grid_layout.setParent(None) self.update() if self.sentence.isHidden(): self.sentence.show() self.showButtonsQuiz() """Getting actual content""" self.srs.getNextItem() example = self.srs.getCurrentExample() # checking for no example case if example is None: self.manualAddDialog.setProblemKanji(self.srs.getCurrentItemKanji()) done = self.manualAddDialog.exec_() if done == 0: self.updateContent() elif done == 1: self.updateContent() else: pass else: example = example.replace(self.srs.getWordFromExample(), u"<font color='blue'>" + self.srs.getWordFromExample() + u"</font>") # checking sentence length if len(self.srs.currentExample.sentence) > SENTENCE_MAX: self.sentence.setFont(QFont(self.options.getSentenceFont(), MIN_FONT_SIZE)) else: self.sentence.setFont(QFont(self.options.getSentenceFont(), self.options.getSentenceFontSize())) #temporary debug info: # print len(example), self.sentence.font() self.sentence.setText(example) readings = self.srs.getQuizVariants() changeFont = False for item in readings: if len(item) > BUTTON_KANA_MAX : changeFont = True try: for i in range(0, self.layout_horizontal.count()): if i > 3: break self.layout_horizontal.itemAt(i).widget().setText(u'') if changeFont: self.layout_horizontal.itemAt(i).widget().setStyleSheet('QPushButton { font-family: ' + self.options.getQuizFont() + '; font-size: 11pt; }') else: self.layout_horizontal.itemAt(i).widget().setStyleSheet('QPushButton { font-family: ' + self.options.getQuizFont() + '; font-size: %spt; }' % self.options.getQuizFontSize()) self.layout_horizontal.itemAt(i).widget().setText(readings[i]) except: log.debug(u'Not enough quiz variants for ' + self.srs.getCurrentItem()) def getReadyPostLayout(self): self.sentence.hide() self.update() self.grid_layout = QGridLayout() self.grid_layout.setSpacing(0) self.labels = [] columns_mod = 0 #font size depending on sentence length if len(self.srs.currentExample.sentence) > SENTENCE_MAX: font = QFont(self.options.getSentenceFont(), MIN_FONT_SIZE); columns_mod = 6 else: font = QFont(self.options.getSentenceFont(), self.options.getSentenceFontSize()) #row, column, rows span, columns span, max columns i = 0; j = 0; r = 1; c = 1; n = COLUMNS_MAX + columns_mod for word in self.srs.parseCurrentExample(): label = QLabel(word) label.setFont(font) label.setAttribute(Qt.WA_Hover, True) label.installEventFilter(self.filter) self.labels.append(label) if len(label.text()) > 1: c = len(label.text()) else: c = 1 #Don't ask, really if j + c > n: i = i + 1; j = 0 self.grid_layout.addWidget(self.labels.pop(), i, j, r, c) #NB: Ehh, pop should remove label from list, shouldn't it? if j <= n: j = j + c else: j = 0; i = i + 1 self.grid_layout.setAlignment(Qt.AlignCenter) self.layout_vertical.insertLayout(1, self.grid_layout) self.update() def hideButtonsQuiz(self): self.var_1st.hide() self.var_2nd.hide() self.var_3rd.hide() self.var_4th.hide() self.answered.clicked.connect(self.hideQuizAndWaitForNext) self.answered.show() def showButtonsQuiz(self): self.var_1st.show() self.var_2nd.show() self.var_3rd.show() self.var_4th.show() self.answered.hide() self.answered.disconnect() #################################### # Timers and animations # #################################### def waitUntilNextTimeslot(self): #if self.nextQuizTimer.isActive(): self.nextQuizTimer.stop() self.nextQuizTimer.start(self.options.getRepetitionInterval() * 60 * 1000) #options are in minutes NB: how do neatly I convert minutes to ms? self.startUpdatingTrayTooltip() def beginCountdown(self): self.trayIcon.setToolTip('Quiz in progress!') self.pauseAction.setText('&Pause') # self.pauseAction.setShortcut('P') self.countdownTimer.start(self.options.getCountdownInterval() * 1000) self.progressTimer = RepeatTimer(0.01, self.updateCountdownBar, self.options.getCountdownInterval() * 100) self.progressTimer.start() def updateCountdownBar(self): self.countdown.setValue(self.countdown.value() - 1) #print self.countdown.value() self.countdown.update() #NB: without .update() recursive repaint crushes qt def fade(self): if self.windowOpacity() == 1: self.animationTimer = RepeatTimer(0.025, self.fadeOut, 40) self.animationTimer.start() else: self.animationTimer = RepeatTimer(0.025, self.fadeIn, 40) self.animationTimer.start() def fadeIn(self): self.setWindowOpacity(self.windowOpacity() + 0.1) def fadeOut(self): self.setWindowOpacity(self.windowOpacity() - 0.1) def stopCountdown(self): self.progressTimer.cancel() self.countdownTimer.stop() self.countdown.setValue(0) #################################### # Actions and events # #################################### def setMenus(self): self.showQuizAction = QAction('&Quiz me now!', self, triggered=self.showQuiz) self.showQuizAction.setIcon(QIcon(PATH_TO_RES + TRAY + NOW_ICON)) self.trayMenu.addAction(self.showQuizAction) self.pauseAction = QAction('&Start quiz!', self, triggered=self.pauseQuiz) self.pauseAction.setIcon(QIcon(PATH_TO_RES + TRAY + START_ICON)) self.trayMenu.addAction(self.pauseAction) self.trayMenu.addSeparator() self.quickDictAction = QAction('Quick &dictionary', self, triggered=self.showQuickDict) self.quickDictAction.setIcon(QIcon(PATH_TO_RES + TRAY + DICT_ICON)) self.trayMenu.addAction(self.quickDictAction) self.optionsAction = QAction('&Options', self, triggered=self.showOptions) self.optionsAction.setIcon(QIcon(PATH_TO_RES + TRAY + OPTIONS_ICON)) self.trayMenu.addAction(self.optionsAction) self.quickLoadAction = QAction('Quick &load', self, triggered=self.showQuickLoad) self.quickLoadAction.setIcon(QIcon(PATH_TO_RES + TRAY + LOAD_ICON)) self.trayMenu.addAction(self.quickLoadAction) self.trayMenu.addSeparator() self.aboutAction = QAction('&About', self, triggered=self.showAbout) self.aboutAction.setIcon(QIcon(PATH_TO_RES + TRAY + ABOUT_ICON)) self.trayMenu.addAction(self.aboutAction) self.globalStatsAction = QAction('&Global statistics', self, triggered=self.showGlobalStatistics) self.globalStatsAction.setIcon(QIcon(PATH_TO_RES + TRAY + STAT_ICON)) self.trayMenu.addAction(self.globalStatsAction) self.utilAction = QAction('U&tilities', self, triggered=self.showToolsDialog) self.utilAction.setIcon(QIcon(PATH_TO_RES + TRAY + UTILS_ICON)) self.trayMenu.addAction(self.utilAction) self.trayMenu.addSeparator() self.quitAction = QAction('&Exit', self, triggered=self.saveAndExit) self.quitAction.setIcon(QIcon(PATH_TO_RES + TRAY + CLOSE_ICON)) self.trayMenu.addAction(self.quitAction) self.trayIcon.setContextMenu(self.trayMenu) self.trayIcon.activated.connect(self.onTrayIconActivated) def onTrayIconActivated(self, reason): ''' if reason == QSystemTrayIcon.DoubleClick: print 'tray icon double clicked' ''' if reason == QSystemTrayIcon.Trigger: if self.isHidden(): self.trayIcon.showMessage('Current session statistics:', 'Running time:\t\t' + self.stats.getRunningTime() + '\nItems seen:\t\t' + str(self.stats.totalItemSeen) + '\nCorrect answers:\t\t' + str(self.stats.answeredCorrect) + '\nWrong answers:\t\t' + self.stats.getIncorrectAnswersCount() + '\nCorrect ratio:\t\t' + self.stats.getCorrectRatioPercent() + #'\nQuiz total time:\t\t' + self.stats.getQuizActive() + '\nQuiz paused time:\t\t' + self.stats.getPausedTime() + '\nTotal pondering time:\t' + self.stats.getMusingsTime() + '\nTotal post-quiz time:\t' + self.stats.getQuizTime() + '\nAverage pondering:\t' + self.stats.getAverageMusingTime() + '\nAverage post-quiz:\t' + self.stats.getAveragePostQuizTime(), QSystemTrayIcon.MessageIcon.Information, 20000) def setButtonsActions(self): if self.var_1st.text() == self.srs.getCorrectAnswer(): self.var_1st.clicked.connect(self.correctAnswer) else: self.var_1st.clicked.connect(self.wrongAnswer) if self.var_2nd.text() == self.srs.getCorrectAnswer(): self.var_2nd.clicked.connect(self.correctAnswer) else: self.var_2nd.clicked.connect(self.wrongAnswer) if self.var_3rd.text() == self.srs.getCorrectAnswer(): self.var_3rd.clicked.connect(self.correctAnswer) else: self.var_3rd.clicked.connect(self.wrongAnswer) if self.var_4th.text() == self.srs.getCorrectAnswer(): self.var_4th.clicked.connect(self.correctAnswer) else: self.var_4th.clicked.connect(self.wrongAnswer) self.var_1st.setShortcut('1') self.var_2nd.setShortcut('2') self.var_3rd.setShortcut('3') self.var_4th.setShortcut('4') def resetButtonsActions(self): self.var_1st.disconnect() self.var_2nd.disconnect() self.var_3rd.disconnect() self.var_4th.disconnect() def postAnswerActions(self): self.stats.musingsStopped() self.stats.postQuizStarted() self.stopCountdown() self.hideButtonsQuiz() self.getReadyPostLayout() def refocusQuiz(self): self.answered.setShortcut('Space') self.activateWindow() self.answered.setFocus() def correctAnswer(self): self.postAnswerActions() self.srs.answeredCorrect() self.stats.quizAnsweredCorrect() self.checkTranslationSize(self.srs.getCurrentSentenceTranslation()) self.status.achievements.correctAnswer() self.showSessionMessage(u'<font color=green>Correct: ' + self.srs.getCorrectAnswer() + '</font>\t|\tNext quiz: ' + self.srs.getNextQuizTime() + '\t|\t<font color=' + self.srs.getLeitnerGradeAndColor()['color'] + '>Grade: ' + self.srs.getLeitnerGradeAndColor()['grade'] + ' (' + self.srs.getLeitnerGradeAndColor()['name'] + ')<font>') self.refocusQuiz() def wrongAnswer(self): self.postAnswerActions() self.srs.answeredWrong() self.stats.quizAnsweredWrong() self.checkTranslationSize(self.srs.getCurrentSentenceTranslation()) self.status.achievements.wrongAnswer() self.showSessionMessage(u'<font color=tomato>Wrong! Should be: '+ self.srs.getCorrectAnswer() + '</font>\t|\tNext quiz: ' + self.srs.getNextQuizTime() + '\t|\t<font color=' + self.srs.getLeitnerGradeAndColor()['color'] + '>Grade: ' + self.srs.getLeitnerGradeAndColor()['grade'] + ' (' + self.srs.getLeitnerGradeAndColor()['name'] + ')<font>') self.refocusQuiz() def timeIsOut(self): self.stats.musingsStopped() self.stats.postQuizStarted() QTimer.singleShot(50, self.hideButtonsQuiz) #NB: slight artificial lag to prevent recursive repaint crush (when mouse is suddenly over repainted button) self.getReadyPostLayout() self.srs.answeredWrong() self.stats.quizAnsweredWrong() self.checkTranslationSize(self.srs.getCurrentSentenceTranslation()) self.status.achievements.wrongAnswer() self.showSessionMessage(u'<font color=tomato>Timeout! Should be: ' + self.srs.getCorrectAnswer() + '</font>\t|\tNext quiz: ' + self.srs.getNextQuizTime() + '\t|\t<font color=' + self.srs.getLeitnerGradeAndColor()['color'] + '>Grade: ' + self.srs.getLeitnerGradeAndColor()['grade'] + ' (' + self.srs.getLeitnerGradeAndColor()['name'] + ')<font>') self.refocusQuiz() def checkTranslationSize(self, translation): if len(translation) > TRANSLATION_CHARS_LIMIT: self.answered.setStyleSheet('QPushButton { font-size: 9pt; }') space_indices = [i for i, value in enumerate(translation) if value == ' '] find_nearest_index = lambda value,list : min(list, key = lambda x:abs(x - value)) nearest_index = find_nearest_index(TRANSLATION_CHARS_LIMIT, space_indices) translation = translation[:nearest_index] + '\n' + translation[nearest_index + 1:] else: self.answered.setStyleSheet('QPushButton { font-size: 11pt; }') self.answered.setText(translation) def hideQuizAndWaitForNext(self): self.stats.postQuizEnded() self.status.hide() self.info.hide() self.allInfo.hide() self.resetButtonsActions() self.setWindowOpacity(1) self.fade() QTimer.singleShot(1000, self.hide) self.waitUntilNextTimeslot() self.updater.mayUpdate = True def pauseQuiz(self): if self.isHidden(): if self.pauseAction.text() == '&Pause': self.nextQuizTimer.stop() self.pauseAction.setText('&Unpause') # self.pauseAction.setShortcut('U') self.trayIcon.setToolTip('Quiz paused!') self.trayIcon.setIcon(QIcon(PATH_TO_RES + TRAY + 'inactive.png')) self.trayUpdater.cancel() self.pauseAction.setIcon(QIcon(PATH_TO_RES + TRAY + START_ICON)) self.stats.pauseStarted() self.updater.mayUpdate = True elif self.pauseAction.text() == '&Start quiz!': self.waitUntilNextTimeslot() self.pauseAction.setText('&Pause') # self.pauseAction.setShortcut('P') self.trayIcon.setToolTip('Quiz in progress!') self.trayIcon.setIcon(QIcon(PATH_TO_RES + TRAY + 'active.png')) self.pauseAction.setIcon(QIcon(PATH_TO_RES + TRAY + PAUSE_ICON)) else: self.waitUntilNextTimeslot() self.pauseAction.setText('&Pause') # self.pauseAction.setShortcut('P') self.trayIcon.setToolTip('Quiz in progress!') self.trayIcon.setIcon(QIcon(PATH_TO_RES + TRAY + 'active.png')) self.pauseAction.setIcon(QIcon(PATH_TO_RES + TRAY + PAUSE_ICON)) self.stats.pauseEnded() self.updater.mayUpdate = False else: self.showSessionMessage(u'Sorry, cannot pause while quiz in progress!') def showQuiz(self): if self.isHidden(): self.updateContent() self.setButtonsActions() #self.restoreGeometry(self.gem) self.trayIcon.setIcon(QIcon(PATH_TO_RES + TRAY + 'active.png')) self.show() self.setWindowOpacity(0) self.fade() self.countdown.setValue(self.options.getCountdownInterval() * 100) self.beginCountdown() self.stats.musingsStarted() if self.nextQuizTimer.isActive(): self.nextQuizTimer.stop() self.updater.mayUpdate = False else: self.showSessionMessage(u'Quiz is already underway!') def showOptions(self): self.optionsDialog.show() def showAbout(self): self.about.show() def showQuickDict(self): self.qdict.showQDict = True def showGlobalStatistics(self): self.statistics.show() def showToolsDialog(self): self.tools.show() def showQuickLoad(self): self.qload.show() def startTrayLoading(self): self.gifLoading.start() #self.iconTimer = QTimer() #self.iconTimer.timeout.connect(self.updateTrayIcon) #self.iconTimer.start(100) def stopTrayLoading(self): self.gifLoading.stop() def updateTrayIcon(self): self.trayIcon.setIcon(self.gifLoading.currentPixmap()) def showSessionMessage(self, message): """Shows info message""" self.status.message.setText(message) if self.status.achievements.achieved is not None: self.status.info.setText(self.status.achievements.achieved[1] + '\t( ' + self.status.achievements.achieved[0] + ' )') self.status.progress.hide() self.status.move(self.status.x(), self.status.y() - 15) self.status.info.show() # self.status.setMask(roundCorners(self.status.rect(),5)) else: self.status.info.setText(u'') self.status.info.hide() self.status.restoreGeometry(self.status.gem) # self.status.setMask(roundCorners(self.status.rect(),5)) # print self.status.y() self.status.adjustSize() self.status.show() def saveAndExit(self): self.hide() self.status.hide() self.allInfo.hide() self.kanjiInfo.hide() self.trayIcon.showMessage('Shutting down...', 'Saving session', QSystemTrayIcon.MessageIcon.Information, 20000 ) if self.countdownTimer.isActive(): self.countdownTimer.stop() if self.nextQuizTimer.isActive(): self.nextQuizTimer.stop() if self.progressTimer != () and self.progressTimer.isAlive(): self.progressTimer.cancel() if self.trayUpdater is not None and self.trayUpdater.isAlive() : self.trayUpdater.cancel() self.rehash.checkSessionResults() self.srs.endCurrentSession(self.stats) self.trayIcon.hide() self.hooker.stop() self.updater.stop() self.optionsDialog.close() self.about.close() self.qdict.close() self.close() def addReferences(self, about, options, qdict, updater, tools, statistics, web, rehash): self.about = about self.optionsDialog = options self.qdict = qdict self.updater = updater self.tools = tools self.statistics = statistics self.status.web = web self.rehash = rehash def initGlobalHotkeys(self): def toggleWidgetFlag(): self.qdict.showQDict = True self.hooker = GlobalHotkeyManager(toggleWidgetFlag , 'Q') self.hooker.setDaemon(True) self.hooker.start() def showEvent(self, event): self.restoreGeometry(self.gem)
class InfoBar(object): """Adds a info bar to the bottom of the main window""" # Should be stored and restored from uistate.ini infoSettings = {"showCPU": True, "showMemory": True, "showDisk": True, "showNetwork": True } def __init__(self): global gEnableResourceMonitoring self.enableResourceMonitoring = gEnableResourceMonitoring self.bar = hiero.ui.mainWindow().statusBar() self.updateMonitorIntervalMS = gUpdateIntervalMS # The monitor update time in milliseconds. self.timer = QTimer() self.timer.setSingleShot(False) self.timer.timeout.connect(self.updateStatusBar) self.currentDiskIOBytes = psutil.disk_io_counters().read_bytes self.currentNetworkBytesReceived = psutil.net_io_counters().bytes_recv # This observes the current pid (the App process id) via psutil, and reports back if self.enableResourceMonitoring: self.processHelper = PSUtilProcessWrapper() # The frameServer instance self.frameServerInstance = nukestudio.frameServer # Initialise the Status Bar self.setupUI() # We haven't started monitoring at this point self.isMonitoring = False # Begin monitoring after a few secs to give frame server time to start up properly QTimer.singleShot(gInitialDelayMS, self.startMonitoring) def getLatestStyleSheet(self): styleFile = os.path.join( cwd, "style.stylesheet") with open(styleFile, "r") as fh: return fh.read() def hide(self): """Hide the info bar""" self.bar.setHidden(True) def show(self): """Show the info bar""" self.bar.setHidden(False) def rendersExistInQueue(self): """Return whether renders exist in the Frame server queue""" return len(self.frameServerInstance.renderQueue.requestsInProgress) > 0 def _handleServerUnreachable(self): """This is called when the server becomes unreachable""" print "[WARNING]: Nuke Frame Server was not reachable." self.frameserverStatusLabel.setPixmap(QPixmap("icons:Offline.png")); self.restartServerButton.setHidden(False) def _setResourcesLabelColour(self, memRatio, cpuUsage): """Sets the Resources label to be red if the memory usage gets too high""" if memRatio > 0.9: color = QColor( Qt.red ) alpha = 220 values = "{r}, {g}, {b}, {a}".format(r = color.red(), g = color.green(), b = color.blue(), a = alpha ) #self.nukeResourcesLabel.setStyleSheet("QLabel { vertical-align: middle; font: 10pt; color: rgba("+values+"); }") else: #self.nukeResourcesLabel.setStyleSheet("QLabel { vertical-align: middle; font: 10pt; }") return def restartServerAsync(self): self.frameServerInstance.stop() self.frameServerInstance.start() QTimer.singleShot(3000, self.startMonitoring) def restartServer(self): """Called to restart the Nuke Frame Server, done asynchronously""" self.stopMonitoring() self.restartServerButton.setHidden(True) #self.nukeResourcesLabel.setText("Re-Starting Frame Server...") QTimer.singleShot(150, self.restartServerAsync) # Displays the resources label, based on settings. def buildResourcesLabel(self): if self.isMonitoring: print "Building resources Label..." def updateResourcesStatusLabel(self): """Updates the Memory Label String""" if self.enableResourceMonitoring: if self.memoryString.isVisible(): self.updateMemoryLayout() if self.cpuString.isVisible(): self.updateCPULayout() if self.diskString.isVisible(): self.updateDiskLayout() if self.networkString.isVisible(): self.updateNetworkLayout() #totalSystemMemoryGB = self.processHelper.totalSystemMemory() #memRatio = currentMemUsageGB / totalSystemMemoryGB # This little test makes the label red if the memory usage exceeds 90% of the maximum allowed #self._setResourcesLabelColour( memRatio, currentCPUUsageAsPercentatge ) def updateMemoryLayout(self): currentMemUsageGB = self.processHelper.nukeMemoryUsageInGB() currentMemUsageAsPercentage = self.processHelper.nukeMemoryUsageAsPercentage() self.memoryString.setText("%.2f GB (%.1f%%)" % (currentMemUsageGB, currentMemUsageAsPercentage)) def updateCPULayout(self): currentCPUUsageAsPercentatge = self.processHelper.nukeCPUUsageAsPercentage() self.cpuString.setText("%.1f%%" % currentCPUUsageAsPercentatge) def updateDiskLayout(self): diskMBPerSec = self._diskMBPerSec() self.diskString.setText("%.2f MB/s" % diskMBPerSec) def updateNetworkLayout(self): networkMBPerSec = self._networkMBPerSec() self.networkString.setText("%.2f MB/s" % networkMBPerSec) def _diskMBPerSec(self): """Returns Total Disk Read+Write speed in MB/s""" oldBytes = self.currentDiskIOBytes DISKS = psutil.disk_io_counters(perdisk=True) readWriteBytes =[(DISKS[disk].read_bytes, DISKS[disk].write_bytes) for disk in DISKS.keys()] newBytes = sum([sum(x) for x in zip(*readWriteBytes)]) bytesDiff = newBytes-oldBytes self.currentDiskIOBytes = newBytes bytesPerSecond = (newBytes-oldBytes)/(self.updateMonitorIntervalMS/1000) MBPerSecond = bytesToMB(bytesPerSecond) return MBPerSecond def _networkMBPerSec(self): """Returns Total Network Read+Write speed in MB/s""" oldBytes = self.currentNetworkBytesReceived NET = psutil.net_io_counters(pernic=True) readWriteBytes =[(NET[adapter].bytes_recv, NET[adapter].bytes_sent) for adapter in NET.keys()] newBytes = sum([sum(x) for x in zip(*readWriteBytes)]) bytesDiff = newBytes-oldBytes self.currentNetworkBytesReceived = newBytes bytesPerSecond = (newBytes-oldBytes)/(self.updateMonitorIntervalMS/1000) MBPerSecond = bytesToMB(bytesPerSecond) return MBPerSecond def setupUI(self): """Initialise the UI""" self.bar.setStyleSheet( self.getLatestStyleSheet() ) #self.bar.setFixedHeight(30) self.frameserverStatusLabel = QLabel("") # Resources self.cpuIconPath = os.path.join(gIconPath, "cpu.png") self.memoryIconPath = os.path.join(gIconPath, "memory.png") self.diskReadIconPath = os.path.join(gIconPath, "disk_read.png") self.networkReadIconPath = os.path.join(gIconPath, "net_read.png") # MEMORY SECTION self.memoryImageButton = QPushButton(QPixmap((self.memoryIconPath)).scaledToHeight(20, Qt.SmoothTransformation),"") self.memoryImageButton.setObjectName("show_button_memory") self.memoryImageButton.setToolTip("Click to toggle monitoring of 'Real Memory' usage") self.memoryString = QLabel("MEMORY (GB)") self.memoryString.setToolTip("'Real Memory' usage of this Nuke Session") self.memoryImageButton.clicked.connect(lambda: self.show_button_clicked(self.memoryString)) # CPU SECTION self.cpuImageButton = QPushButton(QPixmap((self.cpuIconPath)).scaledToHeight(20, Qt.SmoothTransformation),"") self.cpuImageButton.setObjectName("show_button_cpu") self.cpuImageButton.setToolTip("Click to toggle monitoring of CPU usage of this Nuke Session") self.cpuString = QLabel("CPU (%)") self.cpuString.setToolTip("CPU usage of this Nuke Session") self.cpuImageButton.clicked.connect(lambda: self.show_button_clicked(self.cpuString)) # DISK SECTION self.diskImageButton = QPushButton(QPixmap((self.diskReadIconPath)).scaledToHeight(20, Qt.SmoothTransformation),"") self.diskImageButton.setObjectName("show_button_disk") self.diskImageButton.setToolTip("Click to toggle monitoring of Disk Read+Write usage for this machine") self.diskString = QLabel("DISK (MB/s)") self.diskImageButton.clicked.connect(lambda: self.show_button_clicked(self.diskString)) self.diskString.setToolTip("Disk Read+Write usage for this machine") # NETWORK SECTION self.networkImageButton = QPushButton(QPixmap((self.networkReadIconPath)).scaledToHeight(20, Qt.SmoothTransformation),"") self.networkImageButton.setObjectName("show_button_network") self.networkImageButton.setToolTip("Click to toggle monitoring of Network Read+Write traffic") self.networkString = QLabel("NETWORK (MB/s)") self.networkString.setToolTip("Total Network Read+Write traffic for this machine") self.networkImageButton.clicked.connect(lambda: self.show_button_clicked(self.networkString)) # Settings Button - Displays what options should be shown in the Status Bar self.settingsButton = QPushButton() self.settingsButton.setIcon(QIcon("icons:Settings.png")) self.settingsButton.clicked.connect(self.showSettings) # Build the layout based on Preferences #self.cpuWidget.setVisible(self.infoSettings['showCPU']) #self.memoryWidget.setVisible(self.infoSettings['showMemory']) #self.diskWidget.setVisible(self.infoSettings['showDisk']) #self.networkWidget.setVisible(self.infoSettings['showNetwork']) self.restartServerButton = QPushButton(QPixmap("icons:TransformRotateRight.png").scaledToHeight(20, Qt.SmoothTransformation),"") self.restartServerButton.setFixedHeight(16) self.restartServerButton.clicked.connect(self.restartServer) self.restartServerButton.setHidden(True) self.restartServerButton.setFlat(True) self.restartServerButton.setToolTip("Click here to restart the Nuke Frameserver") self.frameServerIsRendering = False self.spinnerMovie = QMovie("icons:RenderingSpinner.gif") self.spinnerMovie.start() self.bar.addPermanentWidget(self.cpuImageButton) self.bar.addPermanentWidget(self.cpuString) self.bar.addPermanentWidget(self.memoryImageButton) self.bar.addPermanentWidget(self.memoryString) self.bar.addPermanentWidget(self.diskImageButton) self.bar.addPermanentWidget(self.diskString) self.bar.addPermanentWidget(self.networkImageButton) self.bar.addPermanentWidget(self.networkString) self.bar.addPermanentWidget(self.frameserverStatusLabel) self.bar.addPermanentWidget(self.restartServerButton) self.bar.addPermanentWidget(self.settingsButton) def show_button_clicked(self, sender): sender.setVisible(not sender.isVisible()) def _updateUIForServerIsRunning(self): """Updates the UI for when the server is reachable""" #self.frameserverStatusLabel.setToolTip("Nuke Frame Server is reachable") self.getFrameServerWorkers() self.frameserverStatusLabel.setPixmap(QPixmap("icons:OK.png")) self.restartServerButton.setHidden(True) def showSettings(self): dialog = SettingsDialog() dialog.show() # Returns a nicely formatted list of Frame Server workers def getFrameServerWorkers(self): statusString = str(self.frameServerInstance.getStatus(1000)) workers = re.findall("workerStatus \{.*?\}", statusString) if len(workers) == 0: self.frameserverStatusLabel.setToolTip("Unable to determine number of frame server workers.") return prettyWorkersString = "Frame Server Status (%i workers):\n" % len(workers) + "\n".join(workers) self.frameserverStatusLabel.setToolTip(prettyWorkersString) def updateStatusBar(self): """Updates the Status bar widgets depending on whether the frameServer is reachable""" #print "Status: ", str(self.frameServerInstance.getStatus(10)) # DEBUG - Stylesheet Changes can be applied here and seen live #self.bar.setStyleSheet( self.getLatestStyleSheet() ) try: isRunning = self.frameServerInstance.isRunning(0.25) if isRunning and not self.rendersExistInQueue(): self._updateUIForServerIsRunning() self.frameServerIsRendering = False elif isRunning and self.rendersExistInQueue(): if self.frameServerIsRendering == False: self.frameServerIsRendering = True self.frameserverStatusLabel.setPixmap(None) self.frameserverStatusLabel.setMovie(self.spinnerMovie) else: self._handleServerUnreachable() self.updateResourcesStatusLabel() except: self._handleServerUnreachable() self.updateResourcesStatusLabel() def startMonitoring(self): """This timer fires every X milliseconds to update the status.""" self.timer.start(self.updateMonitorIntervalMS) self.isMonitoring = True def stopMonitoring(self): """Stops the monitoring process""" self.timer.stop() self.isMonitoring = False
class Bar(QToolBar): clicked = Signal(str) def __init__(self, orientation=Qt.Horizontal, parent=None): super().__init__(parent) self.setOrientation(orientation) policy = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed, QSizePolicy.ButtonBox) policy.setHeightForWidth(True) policy.setWidthForHeight(True) self.setSizePolicy(policy) self.setFloatable(False) self.setToolTip("""<p><b>Goto Letter</b></p> <p>Left-click a letter to navigate to the first entry starting with that letter.</p> <p>Right-click a letter to navigate to the first entry starting with the most recently left-clicked letter, plus any subsequent right-clicked letters, and this letter.</p> <p>(One left-click or a fifth right-click clears the previous letters.)</p> <p>For example, left-click <i>S</i> to go to the first “s” entry. Then right-click <i>A</i> to go to the first “sa” entry, then right-click <i>T</i> to go to the first “sat” entry.</p>""") self.timer = QTimer(self) self.timer.setSingleShot(True) self.timer.timeout.connect(self.onTimeout) size = self.font().pointSizeF() strategy = QFont.StyleStrategy(QFont.PreferOutline | QFont.PreferAntialias | QFont.PreferQuality) self.mainFont = QFont("courier new") self.mainFont.setFixedPitch(True) self.mainFont.setStyleStrategy(strategy) self.mainFont.setStyleHint(QFont.Monospace) self.mainFont.setPointSizeF(size * 1.5) self.mainFont.setBold(True) self.mainFont.setItalic(False) self.setFont(self.mainFont) self.smallFont = QFont("helvetica") self.smallFont.setStyleStrategy(strategy) self.smallFont.setStyleHint(QFont.Times) self.smallFont.setPointSizeF(size * 1.25) self.smallFont.setBold(False) self.smallFont.setItalic(False) self.letters = string.ascii_uppercase self.word = None def sizeHint(self): return self.minimumSizeHint() def minimumSizeHint(self): count = len(self.letters) widthAll = self.fontMetrics().width(self.letters) * H_GAP_PC widthW = self.fontMetrics().width("WW") heightOne = self.fontMetrics().height() if self.orientation == Qt.Vertical: return QSize(widthW, (heightOne * count) + HANDLE_OFFSET) return QSize(widthAll + HANDLE_OFFSET, heightOne * V_GAP_PC) def paintEvent(self, event): super().paintEvent(event) letterPen = QPen(Qt.darkGreen if self.isEnabled() else Qt.lightGray) wordPen = QPen(Qt.darkCyan if self.isEnabled() else Qt.lightGray) painter = QPainter(self) painter.setRenderHints(QPainter.TextAntialiasing) painter.setPen(letterPen) heightOne = self.fontMetrics().height() widthOne = self.fontMetrics().width("W") if self.orientation == Qt.Vertical: y = HANDLE_OFFSET for c in self.letters: rect = QRectF(X_OFFSET, y, widthOne, heightOne) painter.drawText(rect, Qt.AlignCenter, c) y += heightOne if self.word is not None: painter.setFont(self.smallFont) painter.setPen(wordPen) rect = self.rect().adjusted(0, HANDLE_OFFSET, -widthOne / 3, 0) painter.drawText(rect, Qt.AlignTop | Qt.AlignRight | Qt.TextWordWrap, "\n".join(self.word.lower())) painter.setPen(letterPen) painter.setFont(self.mainFont) else: widthOne *= H_GAP_PC x = HANDLE_OFFSET for c in self.letters: rect = QRectF(x, 0, widthOne, heightOne * V_GAP_PC) painter.drawText(rect, Qt.AlignCenter, c) x += widthOne if self.word is not None: painter.setFont(self.smallFont) painter.setPen(wordPen) rect = self.rect().adjusted(HANDLE_OFFSET, 0, 0, 0) painter.drawText(rect, Qt.AlignBottom | Qt.AlignLeft, self.word.lower()) painter.setPen(letterPen) painter.setFont(self.mainFont) def mousePressEvent(self, event): if self.orientation == Qt.Vertical: if event.y() < HANDLE_OFFSET: event.ignore() return heightOne = self.fontMetrics().height() i = clamp(0, int((event.y() - HANDLE_OFFSET) / heightOne), len(self.letters) - 1) else: if event.x() < HANDLE_OFFSET: event.ignore() return widthOne = self.fontMetrics().width("W") * H_GAP_PC i = clamp(0, int((event.x() - HANDLE_OFFSET) / widthOne), len(self.letters) - 1) event.accept() word = self.letters[i] if event.button() == Qt.LeftButton or self.word is None: self.word = word elif event.button() == Qt.RightButton: if len(self.word) >= 4: self.word = word elif self.word is not None: self.word += word if self.word is not None: self.clicked.emit(self.word.lower()) self.timer.start(10000) self.update() def contextMenuEvent(self, event): if ((self.orientation == Qt.Vertical and event.y() < HANDLE_OFFSET) or (self.orientation == Qt.Horizontal and event.x() < HANDLE_OFFSET)): event.ignore() else: event.accept() def setOrientation(self, orientation): self.orientation = orientation self.update() def heightForWidth(self, width): count = len(self.letters) heightOne = self.fontMetrics().height() widthAll = self.fontMetrics().width(self.letters) * H_GAP_PC if widthAll > width: # Needs to be vertical return heightOne * count return heightOne * V_GAP_PC # Needs to be horizontal def onTimeout(self): self.timer.stop() if self.word is not None: self.word = None self.update()
class Browser(QWidget): def __init__(self): super(Browser, self).__init__() self.layout = QStackedLayout(self) self.frontView = QWebView(self) self.backView = QWebView(self) self.frontView.page().networkAccessManager().sslErrors.connect(self.onSslErrors) self.backView.page().networkAccessManager().sslErrors.connect(self.onSslErrors) self.hostname = os.uname()[1] self.layout.setStackingMode(QStackedLayout.StackAll) self.layout.addWidget(self.frontView) self.layout.addWidget(self.backView) self.commands = { 'help' : self.cmdHelp, 'refresh' : self.cmdRefresh, 'add' : self.cmdAdd, 'del' : self.cmdDel, 'list' : self.cmdList, 'zoom' : self.cmdZoom, 'fs' : self.cmdFs, 'exit' : self.cmdExit, 'next' : self.cmdNext, 'restart' : self.cmdRestart, 'upgrade' : self.cmdUpgrade, } self.timer = QTimer() self.timer.setSingleShot(False) self.sites = list() self.siteId = -1 self.socket = False url = QUrl("http://dmr.tokiogroup.com/rrs.php") self.sites.append(Site(url, 10, 1)) url = QUrl("http://dmr.tokiogroup.com/lastheard.php") self.sites.append(Site(url, 10, 1)) self.server = QTcpServer() self.server.listen(QHostAddress.Any, 4242) self.connect(self.server, SIGNAL("newConnection()"), self, SLOT("onConnection()")); self.connect(self.timer, SIGNAL("timeout()"), self, SLOT("goNext()")); self.goNext() self.goNext() def goNext(self): self.cmdNext(list()) def onConnection(self): self.socket = self.server.nextPendingConnection() self.socket.write(self.hostname + ' % ') self.connect(self.socket, SIGNAL("readyRead()"), self, SLOT("onDataReceived()")); def print_(self, text): if (self.socket != False): self.socket.write(text + '\n') print(text) def onDataReceived(self): data = self.socket.readAll().data().strip() try: args = data.split(' ') map(lambda x : x.strip(), args) self.commands.get(args[0])(args) except Exception: self.print_('>> syntax error') self.printCommandsList() if self.socket.isOpen(): self.socket.write(self.hostname + ' % ') def printCommandsList(self): self.print_('avaible commands:') for command in self.commands: self.print_(' ' + command) def onSslErrors(self, reply, errors): url = unicode(reply.url().toString()) reply.ignoreSslErrors() # commands def cmdHelp(self, args): self.print_('>> help') self.printCommandsList() def cmdRefresh(self, args): self.print_('>> refresh ' + self.url.toString().encode()) self.frontView.reload() def cmdAdd(self, args): self.print_('>> add ' + args[1] + ' ' + args[2] + ' ' + args[3]) self.sites.append(Site(QUrl(args[1]), int(args[2], int(args[3])))) def cmdDel(self, args): self.print_('>> del ' + args[1]) self.sites.pop(int(args[1])) def cmdList(self, args): self.print_('>> list') self.print_('current list:') sitesCount = len(self.sites) i = 0 while i < sitesCount: self.print_('%1c[%1d] %2ds : %3s' % ((i==self.siteId) and '*' or ' ' ,i, self.sites[i].time, self.sites[i].url.toString().encode())) i = i + 1 def cmdZoom(self, args): self.print_('>> zoom ' + args[1]) self.frontView.setZoomFactor(float(args[1])) def cmdFs(self, args): self.print_('>> fs ' + args[1]) if (args[1] == '1'): self.showFullScreen() else: self.showNormal() def cmdExit(self, args): self.print_('>> exit') self.socket.close() def cmdNext(self, args): self.timer.stop() self.timer.start(self.sites[self.siteId].time * 1000) self.siteId = (self.siteId + 1) % len(self.sites) print('>> next ' + self.sites[self.siteId].url.toString().encode()) self.backView.show() self.frontView, self.backView = self.backView, self.frontView self.backView.load(self.sites[self.siteId].url) self.backView.setZoomFactor(self.sites[self.siteId].zoom) self.backView.hide() def cmdRestart(self, args): self.print_('>> restart') self.close() def cmdUpgrade(self, args): self.print_('>> upgrade') update = urllib.urlopen('https://raw.githubusercontent.com/tjardick/monitoring_browser/master/webkit.py').read() script = file('webkit.py', 'w') script.write(update) script.close() self.close()
self.windowTitle() # generate exception app = QApplication([]) app.setQuitOnLastWindowClosed(False) lineEdit = LineEdit() lineEdit.deleteLater() if USE_SINGLESHOT: #QTimer.singleShot(1000, lineEdit.clear) #QTimer.singleShot(1000, lineEdit.purePythonMethod) QTimer.singleShot(1000, lineEdit.selectAll) # pure C++ method else: timer = QTimer(None) timer.setSingleShot(True) timer.setInterval(1000) timer.start() #timer.timeout.connect(lineEdit.clear) #timer.timeout.connect(lineEdit.purePythonMethod) timer.timeout.connect(lineEdit.selectAll) # pure C++ method QTimer.singleShot(2000, app.quit) app.exec_() print('~~~~ Application exited')
class Navigator(Singleton,object): instance = None logger = None PATH_SEPARATOR = '-' JUMP_SIGNAL_PAGE_LOAD_FINISH = 'PAGE LOAD FINISH' JUMP_SIGNAL_REQUEST_LOAD_FINISH = 'REQUEST LOAD FINISH' CROSS_AXIS_METHOD_AJAX = 'AJAX' CROSS_AXIS_METHOD_FULL_LOAD = 'FULL LOAD' def __init__(self): self.network_manager = CustomNetworkAccessManager.getInstance() self.axis = [] self.human_events = {} self.node_collected_data = {} self.last_url = None self.current_url = QUrl() self.current_axis_id = None self.crossed_axis_method = None self.crossed_axes = [] self.crossed_axes_string = '' self.cross_axis_delay = QTimer() self.cross_axis_delay.setSingleShot(True) self.cross_axis_delay.timeout.connect(self.crossedAxis) self.cross_axis_params = None self.loading_timeout = QTimer() self.loading_timeout.setSingleShot(True) self.loading_timeout.setInterval(30000) self.loading_timeout.timeout.connect(self.loadingTimeoutAction) self.inactivity_timeout = QTimer() self.inactivity_timeout.setSingleShot(True) self.inactivity_timeout.setInterval(10000) self.inactivity_timeout.timeout.connect(self.inactivityTimeoutAction) self.page_reloads = 0 Logger.getLoggerFor(self.__class__) def takeEntryPoint(self): g = GraphParser.getInstance() for ax in self.axis: self.logger.info('Axis : %s' % ax) if g.entry_point: self.logger.info('Taking the entry point %s' % g.entry_point) self.last_url = QUrl(g.entry_point) inst = Jaime.getInstance() inst.view.load(QUrl(g.entry_point)) return True self.logger.error("Can't take entry point, graph has't entry point") return False def matchesSelector(self,axis): inst = Jaime.getInstance() frame = inst.view.page().mainFrame() document = frame.documentElement() selector = axis.css_selector_condition # print 'document %s ' % document.evaluateJavaScript("""this.querySelector('%s')""" % selector) ret = frame.findAllElements(selector) print '%s axis %s matches css selector ? %s' % (selector,axis,bool(ret.count())) self.logger.debug('axis %s matches css selector ? %s' % (axis,bool(ret.count()))) # for i in range(ret.count()): # self.logger.error(ret.at(i).toInnerXml()) return ret.count() def matches(self,axis): m_from = False m_to = False m_method = True m_selector = True m_route = True if axis.from_url is not None : if isinstance(axis.from_url,str): m_from = axis.from_url == self.last_url.toString() elif axis.from_url.match(self.last_url.toString()): m_from = True else: m_from = True if axis.to_url is not None: if isinstance(axis.to_url,str): m_to = axis.to_url == self.current_url.toString() elif axis.to_url.match(self.current_url.toString()): m_to = True else: m_to = True # print 'el axis %s tiene method %s, %s ' % (axis, # axis.axis_method, # self.crossed_axis_method # ) if self.crossed_axis_method == RouteAxis.CROSS_AXIS_METHOD_AJAX or \ axis.axis_method : if self.crossed_axis_method != axis.axis_method: m_method = False if axis.css_selector_condition: m_selector = axis.not_css_selector ^ bool(self.matchesSelector(axis)) if len(axis.previous_axis)>0: m_route = False for path_index in range(len(axis.previous_axis)): path = string.join( axis.previous_axis[path_index], self.PATH_SEPARATOR) self.logger.debug( "matching axis (%s) with path %s" % (axis.id,path)) if self.crossed_axes_string.find(path) == 0: m_route = True break self.logger.debug("Matches (%s) %s\nfrom %s to %s method %s selector %s route %s" % (axis.id, axis.comment, m_from,m_to,m_method,m_selector,m_route)) return m_from and m_to and m_method and m_selector and m_route def crossedAxis(self,ajax=False): if self.cross_axis_params and self.cross_axis_params[0]: ajax = True self.cross_axis_params = None self.crossed_axes_string = string.join(self.crossed_axes,self.PATH_SEPARATOR) self.logger.info("crossedAxis, path: \n%s" % string.join(self.crossed_axes,"\n")) if ajax: self.crossed_axis_method = RouteAxis.CROSS_AXIS_METHOD_AJAX else: self.crossed_axis_method = RouteAxis.CROSS_AXIS_METHOD_FULL_LOAD print 'Cruse un eje por %s ' % self.crossed_axis_method for ax in self.axis: if self.matches(ax): self.current_axis_id = ax.id # print 'current axis %s' % ax.id self.logger.info('Axis matched %s' % ax) self.startSecuence(ax) return self.logger.warning("Can't match any axis") self.inactivity_timeout.start() def collectData(self,id,collector,params): if not self.getAxis(id): self.logger.error("Can't collect data from inexistent axis") return if id not in self.node_collected_data: self.node_collected_data[id] = [] self.node_collected_data[id].append((collector,params)) def setAxis(self,route_axis,human_events): self.logger.info('setting axis %s ' % route_axis) if route_axis.id in self.human_events: raise Exception('Axis repetido') self.human_events[route_axis.id] = human_events self.axis.append(route_axis) # QtCore.QObject.disconnect(self.human_events[route_axis.id], # QtCore.SIGNAL("finished()"), # self.secuenceFinished) QtCore.QObject.connect(self.human_events[route_axis.id], QtCore.SIGNAL("finished()"), self.secuenceFinished) def startSecuence(self,axis): inst = Jaime.getInstance() try : if axis.max_crosses > 0 and axis.max_crosses <= axis.cross_counter: self.logger.info('el axis supero la cantidad maxima de loops ') if axis.exit_point: exit_axis = self.getAxis(axis.exit_point) self.logger.info('Salto hacia el exit_axis %s' % exit_axis) self.startSecuence(exit_axis) else: self.logger.error('No hay exit axis muero %s' % axis.exit_point) inst = Jaime.getInstance() inst.finishWork() else: axis.cross_counter += 1 h_ev = self.human_events[axis.id] self.crossed_axes.insert(0,axis.id) if len(self.crossed_axes) >= 10: self.crossed_axes.pop() self.logger.info('Stopeo el inactivity timeout') self.inactivity_timeout.stop() if axis.id in self.node_collected_data: inst.route_node.doWork(self.node_collected_data[axis.id]) h_ev.fire() except Exception as e: # print 'Excepcion en startSecuence %s' % e self.logger.error(e) def getAxis(self,axis_id): for ax in self.axis: if ax.id == axis_id: return ax return None def secuenceFinished(self): self.logger.info('estarteo el inactivity timeout' ) self.inactivity_timeout.start() def inactivityTimeoutAction(self): inst = Jaime.getInstance() self.logger.info('inactivity timeout action fired after %s seconds' % (self.inactivity_timeout.interval()/1000)) if not len(self.crossed_axes): return last_axis = self.crossed_axes[0] ax = self.getAxis(last_axis) retry_axis_id = ax.retry_axis retry_axis = self.getAxis(retry_axis_id) if retry_axis: self.logger.info('El axis %s tiene como retry axis a %s lo estarteo' % (ax.id,ax.retry_axis)) self.startSecuence(retry_axis) def processLoadStarted(self): inst = Jaime.getInstance() self.logger.info('Page started load to %s' % inst.view.url().toString()) # print inst.page.mainFrame().requestedUrl() self.inactivity_timeout.stop() self.loading_timeout.stop() self.loading_timeout.start() self.logger.info('Starting loading timeout and stopping inactivity timeout') self.last_url = inst.view.url() def loadingTimeoutAction(self): self.logger.warning('Loading timeout fired') inst = Jaime.getInstance() if (not inst.view.url().isEmpty() and re.match('http.?://',inst.view.url().toString()) ) \ and not self.page_reloads: self.logger.info('Timeout fired, reloading last url %s' % inst.view.url().toString()) self.page_reloads += 1 self.loading_timeout.stop() inst.view.reload() else: self.logger.error("""Timeout fired, clossing jaime, there isn't last_url or max_reloads reatched""" ) inst.finishWork() def processPageLoadFinished(self,status): inst = Jaime.getInstance() self.logger.info('Page finished load to %s with status %s ' % (inst.view.url().toString(), status)) if status: self.current_url = inst.view.url() self.loading_timeout.stop() self.page_reloads = 0 self.logger.info('Stopping loading timeout') else: self.current_url = QUrl() self.testJumpRules(self.JUMP_SIGNAL_PAGE_LOAD_FINISH, status) def testJumpRules(self,signal,*args): # self.logger.info('Call to restJumpRules with signal %s' % signal) # print 'Call to restJumpRules %s' % ( signal) if signal == self.JUMP_SIGNAL_PAGE_LOAD_FINISH: print 'llamo a crosed axis' self.pushCrossedAxis() elif signal == self.JUMP_SIGNAL_REQUEST_LOAD_FINISH: if not self.current_axis_id: return req_headers = args[0] rep_headers = args[1] ax = self.getAxis(self.current_axis_id) # print '%s tiene exit_method %s' % (ax,ax.axis_exit_method) if ax.axis_exit_method and \ ax.axis_exit_method == RouteAxis.CROSS_AXIS_METHOD_AJAX: if 'X-Requested-With' in req_headers: if ax.axis_exit_method_toggled : ax.axis_exit_method = ax.axis_exit_method_toggled ax.axis_exit_method_toggled = None self.pushCrossedAxis(True) def pushCrossedAxis(self,*params): if self.cross_axis_delay.isActive() or \ self.cross_axis_params is not None: self.logger.warning("""Can't push crossAxis call, there is another call in process""") return self.cross_axis_params = params ax = self.getAxis(self.current_axis_id) if ax and ax.delay_node_test: delay = ax.delay_node_test else: delay = None if delay: self.cross_axis_delay.setInterval(delay) self.cross_axis_delay.start() self.logger.info('Delaying %s seconds crossedAxis call ' % int( int(delay) /1000) ) else: self.crossedAxis()
class CfdFaceSelectWidget: def __init__(self, parent_widget, obj, allow_face_sel, allow_solid_sel, allow_point_sel=False, allow_edge_sel=False): ui_path = os.path.join(os.path.dirname(__file__), "TaskPanelCfdListOfFaces.ui") self.parent_widget = parent_widget self.form = FreeCADGui.PySideUic.loadUi(ui_path, self.parent_widget) self.parent_widget.layout().addWidget(self.form) self.selecting_references = False self.recompute_timer = QTimer() self.recompute_timer.setSingleShot(True) self.recompute_timer.timeout.connect(self.recomputeDocument) self.obj = obj self.References = self.obj.References self.doc_name = self.obj.Document.Name self.view_object = self.obj.ViewObject self.allow_face_sel = allow_face_sel self.allow_solid_sel = allow_solid_sel self.allow_point_sel = allow_point_sel self.allow_edge_sel = allow_edge_sel self.selection_mode_solid = (not allow_face_sel) and allow_solid_sel sel_list = [] sel_rb_list = [] if allow_face_sel: sel_list.append("faces") sel_rb_list.append("Face") if allow_edge_sel: sel_list.append("edges") sel_rb_list.append("Edge") if allow_point_sel: sel_list.append("vertices") sel_rb_list.append("Vertex") sel_rb_text = ' / '.join(sel_rb_list) sel_msg = "" if len(sel_list) > 0: sel_msg = sel_list[0] if len(sel_list) > 1: for i in range(len(sel_list)-2): sel_msg += ", " + sel_list[i+1] sel_msg += " and " + sel_list[-1] self.form.rb_standard.setText(sel_rb_text) self.selection_mode_std_print_message = "Select {} by single-clicking " \ "on them.".format(sel_msg) self.selection_mode_solid_print_message = "Select solids by single-clicking on a face or edge which belongs " \ "to the solid." exclusive_sel = (not allow_solid_sel) or not (allow_face_sel or allow_edge_sel or allow_point_sel) self.form.labelSelection.setVisible(not exclusive_sel) self.form.rb_standard.setVisible(not exclusive_sel) self.form.rb_solid.setVisible(not exclusive_sel) self.form.rb_standard.toggled.connect(self.choose_selection_mode_standard) self.form.rb_solid.toggled.connect(self.choose_selection_mode_solid) self.form.listReferences.currentRowChanged.connect(self.setReferenceListSelection) self.form.buttonAddFace.clicked.connect(self.buttonAddFaceClicked) self.form.buttonAddFace.setCheckable(True) self.form.buttonRemoveFace.clicked.connect(self.buttonRemoveFaceClicked) # Face list selection self.form.faceList.clicked.connect(self.faceListSelection) self.form.shapeComboBox.currentIndexChanged.connect(self.faceListShapeChosen) self.form.faceListWidget.itemSelectionChanged.connect(self.faceHighlightChange) self.form.faceListWidget.itemChanged.connect(self.faceListItemChanged) self.form.selectAllButton.clicked.connect(self.selectAllButtonClicked) self.form.selectNoneButton.clicked.connect(self.selectNoneButtonClicked) self.form.doneButton.clicked.connect(self.closeFaceList) self.form.shapeComboBox.setToolTip("Choose a solid object from the drop down list and select one or more of " "the faces associated with the chosen solid.") self.solidsNames = ['None'] self.solidsLabels = ['None'] for i in FreeCADGui.ActiveDocument.Document.Objects: if "Shape" in i.PropertiesList: # Do not restrict to solids if not i.Name.startswith("CfdFluidBoundary"): self.solidsNames.append(i.Name) self.solidsLabels.append(i.Label) self.rebuildReferenceList() # First time, add any currently selected faces to list if len(self.References) == 0: self.addSelectionToRefList() self.scheduleRecompute() FreeCADGui.Selection.clearSelection() self.updateSelectionbuttonUI() def setReferenceListSelection(self, row): if row > -1: self.enableSelectingMode(False) docName = str(self.doc_name) doc = FreeCAD.getDocument(docName) ref = self.References[row] selection_object = doc.getObject(ref[0]) FreeCADGui.Selection.addSelection(selection_object, [str(ref[1])]) def addSelectionToRefList(self): """ Add currently selected objects to reference list. """ for sel in FreeCADGui.Selection.getSelectionEx(): if sel.HasSubObjects: for sub in sel.SubElementNames: print("{} {}".format(sel.ObjectName, sub)) self.addSelection(sel.DocumentName, sel.ObjectName, sub) self.scheduleRecompute() def enableSelectingMode(self, selecting): self.selecting_references = selecting FreeCADGui.Selection.clearSelection() # start SelectionObserver and parse the function to add the References to the widget if self.selecting_references: FreeCADGui.Selection.addObserver(self) else: FreeCADGui.Selection.removeObserver(self) self.scheduleRecompute() self.updateSelectionbuttonUI() def buttonAddFaceClicked(self): self.selecting_references = not self.selecting_references if self.selecting_references: # Add any currently selected objects if len(FreeCADGui.Selection.getSelectionEx()) >= 1: self.addSelectionToRefList() self.selecting_references = False self.enableSelectingMode(self.selecting_references) def buttonRemoveFaceClicked(self): if not self.References: return if not self.form.listReferences.currentItem(): return current_item_name = str(self.form.listReferences.currentItem().text()) for ref in self.References: idx = self.solidsNames.index(ref[0]) refname = self.solidsLabels[idx] + ':' + ref[1] if refname == current_item_name: self.References.remove(ref) self.rebuildReferenceList() self.scheduleRecompute() def choose_selection_mode_standard(self, state): self.selection_mode_solid = not state self.updateSelectionbuttonUI() def choose_selection_mode_solid(self, state): self.selection_mode_solid = state self.updateSelectionbuttonUI() def updateSelectionbuttonUI(self): self.form.buttonAddFace.setChecked(self.selecting_references) if self.selecting_references: if self.selection_mode_solid: # print message on button click print_message = self.selection_mode_solid_print_message else: print_message = self.selection_mode_std_print_message else: print_message = "" self.form.labelHelpText.setText(print_message) def addSelection(self, doc_name, obj_name, sub, selected_point=None, as_is=False): """ Add the selected sub-element (face) of the part to the Reference list. Prevent selection in other document. """ if FreeCADGui.activeDocument().Document.Name != self.doc_name: return selected_object = FreeCAD.getDocument(doc_name).getObject(obj_name) # On double click on a vertex of a solid sub is None and obj is the solid print('Selection: ' + selected_object.Shape.ShapeType + ' ' + selected_object.Name + ':' + sub + " @ " + str(selected_point)) if hasattr(selected_object, "Shape") and sub: if sub.startswith('Solid'): # getElement doesn't work for solids elt = selected_object.Shape.Solids[int(sub.lstrip('Solid')) - 1] else: elt = selected_object.Shape.getElement(sub) selection = None if as_is: selection = (selected_object.Name, sub) elif self.selection_mode_solid: # in solid selection mode use edges and faces for selection of a solid solid_to_add = None if elt.ShapeType == 'Edge': found_edge = False for i, s in enumerate(selected_object.Shape.Solids): for e in s.Edges: if elt.isSame(e): if not found_edge: solid_to_add = 'Solid' + str(i + 1) else: FreeCAD.Console.PrintMessage('Edge belongs to more than one solid\n') solid_to_add = None found_edge = True elif elt.ShapeType == 'Face': found_face = False for i, s in enumerate(selected_object.Shape.Solids): for e in s.Faces: if elt.isSame(e): if not found_face: solid_to_add = 'Solid' + str(i + 1) else: FreeCAD.Console.PrintMessage('Face belongs to more than one solid\n') solid_to_add = None found_face = True elif elt.ShapeType == 'Solid': solid_to_add = sub if solid_to_add: selection = (selected_object.Name, solid_to_add) print('Selection element changed to Solid: ' + selected_object.Shape.ShapeType + ' ' + selection[0] + ' ' + selection[1]) else: # Allow Vertex, Edge, Face or just Face selection if (elt.ShapeType == 'Face' and self.allow_face_sel) or \ (elt.ShapeType == 'Edge' and self.allow_edge_sel) or \ (elt.ShapeType == 'Vertex' and self.allow_point_sel): selection = (selected_object.Name, sub) if selection: if selection not in self.References: self.References.append(selection) else: FreeCAD.Console.PrintMessage( selection[0] + ':' + selection[1] + ' already in reference list\n') self.rebuildReferenceList() self.scheduleRecompute() self.updateSelectionbuttonUI() def rebuildReferenceList(self): self.form.listReferences.clear() items = [] remove_refs = [] for ref in self.References: try: idx = self.solidsNames.index(ref[0]) except ValueError: # If solid doesn't exist anymore remove_refs.append(ref) else: item_name = self.solidsLabels[idx] + ':' + ref[1] items.append(item_name) for ref in remove_refs: self.References.remove(ref) if remove_refs: self.scheduleRecompute() for listItemName in items: self.form.listReferences.addItem(listItemName) # At the moment we assume order in listbox is the same as order of references self.form.listReferences.setSortingEnabled(False) def faceListSelection(self): self.form.stackedWidget.setCurrentIndex(1) self.form.shapeComboBox.clear() self.form.faceListWidget.clear() self.form.shapeComboBox.insertItems(1, self.solidsLabels) def faceListShapeChosen(self): ind = self.form.shapeComboBox.currentIndex() objectName = self.solidsNames[ind] if objectName != 'None': # Disable change notifications while we add new items self.form.faceListWidget.itemChanged.disconnect(self.faceListItemChanged) self.shapeObj = FreeCADGui.ActiveDocument.Document.getObject(objectName) self.hideObjects() refs = list(self.References) self.form.faceListWidget.clear() FreeCADGui.showObject(self.shapeObj) if self.allow_face_sel: self.listOfShapeFaces = self.shapeObj.Shape.Faces selected_faces = [ref[1] for ref in refs if ref[0] == objectName] for i in range(len(self.listOfShapeFaces)): face_name = "Face" + str(i + 1) item = QtGui.QListWidgetItem(face_name, self.form.faceListWidget) item.setFlags(item.flags() | QtCore.Qt.ItemIsUserCheckable) checked = face_name in selected_faces if checked: item.setCheckState(QtCore.Qt.Checked) else: item.setCheckState(QtCore.Qt.Unchecked) self.form.faceListWidget.insertItem(i, item) if self.allow_solid_sel: self.listOfShapeSolids = self.shapeObj.Shape.Solids selected_solids = [ref[1] for ref in refs if ref[0] == objectName] for i in range(len(self.listOfShapeSolids)): face_name = "Solid" + str(i + 1) item = QtGui.QListWidgetItem(face_name, self.form.faceListWidget) item.setFlags(item.flags() | QtCore.Qt.ItemIsUserCheckable) checked = face_name in selected_solids if checked: item.setCheckState(QtCore.Qt.Checked) else: item.setCheckState(QtCore.Qt.Unchecked) self.form.faceListWidget.insertItem(i, item) if self.allow_edge_sel: self.listOfShapeEdges = self.shapeObj.Shape.Edges selected_edges = [ref[1] for ref in refs if ref[0] == objectName] for i in range(len(self.listOfShapeEdges)): face_name = "Edge" + str(i + 1) item = QtGui.QListWidgetItem(face_name, self.form.faceListWidget) item.setFlags(item.flags() | QtCore.Qt.ItemIsUserCheckable) checked = face_name in selected_edges if checked: item.setCheckState(QtCore.Qt.Checked) else: item.setCheckState(QtCore.Qt.Unchecked) self.form.faceListWidget.insertItem(i, item) if self.allow_point_sel: self.listOfShapeVertices = self.shapeObj.Shape.Vertexes selected_solids = [ref[1] for ref in refs if ref[0] == objectName] for i in range(len(self.listOfShapeVertices)): face_name = "Vertex" + str(i + 1) item = QtGui.QListWidgetItem(face_name, self.form.faceListWidget) item.setFlags(item.flags() | QtCore.Qt.ItemIsUserCheckable) checked = face_name in selected_solids if checked: item.setCheckState(QtCore.Qt.Checked) else: item.setCheckState(QtCore.Qt.Unchecked) self.form.faceListWidget.insertItem(i, item) self.form.faceListWidget.itemChanged.connect(self.faceListItemChanged) def hideObjects(self): for i in FreeCADGui.ActiveDocument.Document.Objects: if "Shape" in i.PropertiesList: FreeCADGui.hideObject(i) self.view_object.show() def faceHighlightChange(self): FreeCADGui.Selection.clearSelection() FreeCADGui.Selection.addSelection(self.shapeObj, self.form.faceListWidget.currentItem().text()) self.scheduleRecompute() def faceListItemChanged(self, item): object_name = self.solidsNames[self.form.shapeComboBox.currentIndex()] if object_name != 'None': face_name = item.text() if item.checkState() == QtCore.Qt.Checked: self.addSelection(self.doc_name, object_name, face_name, as_is=True) else: if not self.References: return for ref in self.References: if ref[0] == object_name and ref[1] == face_name: self.References.remove(ref) self.rebuildReferenceList() self.scheduleRecompute() def selectAllButtonClicked(self): for i in range(self.form.faceListWidget.count()): item = self.form.faceListWidget.item(i) item.setCheckState(QtCore.Qt.Checked) def selectNoneButtonClicked(self): for i in range(self.form.faceListWidget.count()): item = self.form.faceListWidget.item(i) item.setCheckState(QtCore.Qt.Unchecked) def closeFaceList(self): self.form.stackedWidget.setCurrentIndex(0) # self.obj.ViewObject.show() def scheduleRecompute(self): """ Only do one (costly) recompute when done processing - call this in preference to document.recompute() """ self.recompute_timer.start() def recomputeDocument(self): # Re-assign to force update of FreeCAD property self.obj.References = self.References FreeCAD.getDocument(self.doc_name).recompute() def closing(self): """ Call this on close to let the widget to its proper cleanup """ FreeCADGui.Selection.removeObserver(self) def __del__(self): # Just in case, make sure any stray selection observer is removed before object deleted FreeCADGui.Selection.removeObserver(self)
def resizeTimer(self): ret = QTimer(self.q) ret.setSingleShot(True) ret.setInterval(200) ret.timeout.connect(self.updateSize) return ret
class Plot2DDataWidget(QWidget): def __init__(self,parent=None,measdata=[[1,3,2],[3,5,7]],header=["index","prime numbers"],SymbolSize=10,linecolor='y',pointcolor='b',title='Plot Window'): # This class derivates from a Qt MainWindow so we have to call # the class builder ".__init__()" #QMainWindow.__init__(self) QWidget.__init__(self) # "self" is now a Qt Mainwindow, then we load the user interface # generated with QtDesigner and call it self.ui self.ui = Plot2DDataWidget_Ui.Ui_Plot2DData() # Now we have to feed the GUI building method of this object (self.ui) # with a Qt Mainwindow, but the widgets will actually be built as children # of this object (self.ui) self.ui.setupUi(self) self.setWindowTitle(title) self.x_index=0 self.y_index=0 self.curve=self.ui.plot_area.plot(pen=linecolor) self.curve.setSymbolBrush(pointcolor) self.curve.setSymbol('o') self.curve.setSymbolSize(SymbolSize) self.parent=parent self.measdata=measdata self.header=header self.update_dropdown_boxes(header) self.update_plot_timer = QTimer() self.update_plot_timer.setSingleShot(True) #The timer would not wait for the completion of the task otherwise self.update_plot_timer.timeout.connect(self.autoupdate) if self.ui.auto_upd.isChecked():self.autoupdate() def update_timer_timeout(self,msec): self.update_plot_timer.setInterval(msec) def updateX(self,value): self.x_index=value #self.update_plot() #that was the bug #This created a loop because update_plot called check_connection #which called update_dropdown_boxes, which cleared the x_axis_box #which triggered a call to updateX def updateY(self,value): self.y_index=value #self.update_plot() def change_line_color(self,color=None): if color==None:color=QColorDialog.getColor() if color.isValid(): self.curve.setPen(color) def change_point_color(self,color=None): if color==None:color=QColorDialog.getColor() if color.isValid(): self.curve.setSymbolBrush(color) def change_symbol_size(self,value): self.curve.setSymbolSize(value) def check_connection(self,state=1): """check if the pointer to the Master dataset to display (self.measdata in Main.py) has changed""" if state and hasattr(self.parent,"measdata"): if self.parent.measdata!=self.measdata:self.measdata=self.parent.measdata if self.parent.current_header!=self.header:self.update_dropdown_boxes(self.parent.current_header) #print "Reestablishing connection" def autoupdate(self,state=1): if state: self.update_plot() self.update_plot_timer.start(self.ui.refresh_rate.value()*1000)#The value must be converted to milliseconds else: self.update_plot_timer.stop() def update_plot(self): """plot the data columns selected in the drop-down menu boxes""" if self.ui.autoconnect.isChecked():self.check_connection() if self.x_index!=-1 and self.y_index!=-1 and self.measdata[self.x_index]!=[] and self.measdata[self.y_index]!=[]: self.curve.setData(self.measdata[self.x_index],self.measdata[self.y_index]) def update_dropdown_boxes(self,header): """Update the drop-down boxes that select the content of the plot""" self.ui.x_axis_box.clear() self.ui.x_axis_box.addItems(header) self.ui.y_axis_box.clear() self.ui.y_axis_box.addItems(header) self.header=header
class Recorder(QMainWindow): '''Main Window - menu toolbar to load and save session, open plotter and set server - dock widget holding session and run information - grid plot to stream current emg data of 16 channels - start, stop, progressbar and trigger for current run ''' def __init__(self, parent=None): super(Recorder, self).__init__(parent) self.ui = Ui_MainWindow() self.ui.setupUi(self) self.plotter = None self.session = None self.server = DelsysStation(parent=self) self.ui.dockWidget.setWidget(QWidget()) self.dockLayout = QGridLayout(self.ui.dockWidget.widget()) self.showSessionMeta = None self.showRunMeta = None self.plotWidget = None self.plots = [] self.startTime = datetime.now() self.pinger = QTimer(self) # timer to read data from server whenever available self.runPinger = QTimer(self) # timer to call stop when run duration times out self.runPinger.setSingleShot(True) self.runPinger.timeout.connect(self.stop) self.pinger.timeout.connect(self.ping) self.kinectRecorder=None self.newpath=None self.notSavedState = False def clearDock(self): ''' clear session (dock widget elements) - remove showSessionMeta - remove showRunMeta - kill kinectRecorder - remove session if changes are not saved, ask ''' if self.showSessionMeta is not None: reply = QMessageBox.question(self, 'QMessageBox.question()', 'Do you want to first save the current session?', QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel) if reply == QMessageBox.Yes: self.save() elif reply == QMessageBox.Cancel: return self.session = None self.dockLayout.removeWidget(self.showSessionMeta) self.dockLayout.removeWidget(self.showRunMeta) self.showSessionMeta.deleteLater() self.showRunMeta.deleteLater() self.showSessionMeta = None self.showRunMeta = None self.kinectRecorder.killRecorder() def preparePlots(self): '''arange 2x8 plots for each channel and set parameters ''' if self.ui.frame.layout() is None: layout = QGridLayout() self.ui.frame.setLayout(layout) else: ######### # reset # ######### layout = self.ui.frame.layout() layout.removeWidget(self.plotWidget) self.plotWidget = None self.plots = [] self.plotWidget = pg.GraphicsLayoutWidget(border=(100,100,100)) layout.addWidget(self.plotWidget) for i in range(8): for k in range(2): self.plots.append(self.plotWidget.addPlot(title="EMG " + str(k+2*i+1))) self.plots[-1].plot(np.linspace(0,2,1000)) self.plots[-1].setYRange(-0.0002, 0.0002) self.plotWidget.nextRow() self.plotWidget.show() def setServer(self): ''' open dialog to set server address e.g. localhost, 192.168.1.5 ''' text, ok = QInputDialog.getText(self, "Set server", 'Enter server adress') if ok: self.server.host = text def newSession(self): ''' create new session: - clear dock - open dialog to set session parameters - create showSessionMeta, showRunMeta, kinectRecorder ''' self.clearDock() sessionDialog = SessionDialog(self) if sessionDialog.exec_(): newpath = os.path.join(sessionDialog.ui.leDir.text(), sessionDialog.ui.leName.text()) if not os.path.isdir(newpath): os.makedirs(newpath) else: print('reusing folder') QMessageBox.information(self, 'Warning!', '''You\'re reusing the subject folder''', QMessageBox.Ok) self.session = Session(sessionDialog.ui.leName.text(), sessionDialog.ui.teBemerkung.toPlainText(), newpath) self.showSessionMeta = sessionView(self.session, self) self.showRunMeta = RunWidget(self) self.showRunMeta.ui.leCurrentRun.setText(str(len(self.session.runs))) try: self.kinectRecorder=KinectRecorder() except: print "no Kinect recording" self.kinectRecorder = None self.showSessionMeta.ui.showBemerkung.textChanged.connect(self.pendingSave) self.showRunMeta.ui.lwRuns.itemDoubleClicked.connect(self.openPlotter) self.dockLayout.addWidget(self.showSessionMeta) self.dockLayout.addWidget(self.showRunMeta) self.showRunMeta.show() self.showSessionMeta.show() self.preparePlots() self.ui.tbStart.setEnabled(True) def pendingSave(self): self.notSavedState = True self.setWindowTitle("PyTrigno(*)") def save(self): self.notSavedState = False self.setWindowTitle("PyTrigno") self.session.remarks = self.showSessionMeta.ui.showBemerkung.toPlainText() self.session.dump("ReadMe.txt") def startRun(self): ''' start a recording: - setup server - setup timer (if no eternity is toggled) - setup progress bar - setup buttons - setup session - start recorder ''' #setup server self.server.exitFlag = False try: self.startTime = datetime.now() self.server.start() except: print("something went wrong") self.server.exitFlag = True raise socket.timeout("Could not connect to Delsys Station") else: if self.showRunMeta.ui.cbEternity.checkState() == 0: duration = self.showRunMeta.ui.timeEdit.time() d = duration.second() + duration.minute()*60 self.runPinger.start(d*1000) self.ui.elapsedTime.setRange(0,d) elif self.showRunMeta.ui.cbEternity.checkState() == 2: self.ui.elapsedTime.setRange(0,0) self.pinger.start() self.ui.tbStop.setEnabled(True) self.ui.tbTrigger.setEnabled(True) self.ui.tbStart.setEnabled(False) name = self.showRunMeta.ui.leCurrentRun.text() self.session.addRun(name) if self.kinectRecorder is not None: self.kinectRecorder.startRecording(self.newpath+'\\'+name+'.oni') self.ui.elapsedTime.setRange(0,d) def stop(self): ''' stop recording due to button press or timeout - setup buttons - stop timers - stop server - kill kinectRecorder - add item to list of runs ''' self.ui.tbStop.setEnabled(False) self.ui.tbTrigger.setEnabled(False) self.ui.tbStart.setEnabled(True) self.ui.elapsedTime.reset() QListWidgetItem(self.showRunMeta.ui.leCurrentRun.text(), self.showRunMeta.ui.lwRuns) self.showRunMeta.ui.leCurrentRun.setText(str(len(self.session.runs))) self.server.exitFlag = True self.server.stop() self.runPinger.stop() self.pinger.stop() self.session.stopRun(self.server.buffer) self.server.buffer = None if self.kinectRecorder is not None: self.kinectRecorder.stopRecording() self.server.flush() def trigger(self): ''' add a trigger ''' print("trigger") trigger = self.server.buffer[0].shape[1] self.session.addTrigger(trigger) def ping(self): ''' update progress bar and plots everytime new data is available ''' elapsed = int((datetime.now()-self.startTime).total_seconds()) self.ui.elapsedTime.setValue(elapsed) for p in range(len(self.plots)): if self.server.buffer[0].shape[1] < 5000: self.plots[p].plot(self.server.buffer[0][p], clear=True) else: self.plots[p].plot(self.server.buffer[0][p,-5000:], clear=True) def openPlotter(self, item=None): if self.plotter is None: self.plotter = Plotter() if item is not None: self.plotter.load([os.path.join(self.session.dir, item.text()) + ".pk"]) self.plotter.show() def closeEvent(self, event): if self.notSavedState: reply = QMessageBox.question(self, 'QMessageBox.question()', 'Do you want to first save the current session?', QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel) else: reply = QMessageBox.No if reply == QMessageBox.Yes: self.save() elif reply == QMessageBox.Cancel: event.ignore() return if not self.server.exitFlag: self.stop() event.accept()
class Quiz(QFrame): def __init__(self, parent=None): super(Quiz, self).__init__(parent) """Session Info""" self.status = QFrame() ##session message self.status.message = QLabel(u'') self.status.layout = QHBoxLayout() self.status.layout.addWidget(self.status.message) self.status.setLayout(self.status.layout) ##mouse event filter #self.status.filter = StatusFilter() self.status.setAttribute(Qt.WA_Hover, True) #self.status.installEventFilter(self.status.filter) """Items Info""" self.info = QFrame() self.info.reading = QLabel(u'') self.info.item = QLabel(u'') self.info.components = QLabel(u'') self.info.translation = QLabel(u'') self.info.translation.setAlignment(Qt.AlignCenter) self.info.translation.setWordWrap(True) # separator_one = QFrame() # separator_one.setFrameShape(QFrame.HLine) # separator_one.setFrameShadow(QFrame.Sunken) # # separator_two = QFrame() # separator_two.setFrameShape(QFrame.HLine) # separator_two.setFrameShadow(QFrame.Sunken) self.info.layout = QVBoxLayout() self.info.layout.addWidget(self.info.translation) # self.info.layout.addWidget(self.info.reading) # self.info.layout.addWidget(separator_one) # self.info.layout.addWidget(self.info.item) # self.info.layout.addWidget(separator_two) # self.info.layout.addWidget(self.info.components) self.info.setLayout(self.info.layout) """Verbose Info""" self.allInfo = QFrame() self.allInfo.layout = QGridLayout() self.allInfo.setLayout(self.allInfo.layout) #the rest is (should be) generated on the fly """Global Flags""" #self.correct = False """Quiz Dialog""" self.filter = Filter() #### visual components ### self.countdown = QProgressBar() self.sentence = QLabel(u'') # self.var_1st = QPushButton(u'') # self.var_2nd = QPushButton(u'') # self.var_3rd = QPushButton(u'') # self.var_4th = QPushButton(u'') self.isKnown = QPushButton(u'Good') self.isNotKnown = QPushButton(u'Again') self.answered = QPushButton(u'') self.answered.hide() ### layouts #### self.layout_vertical = QVBoxLayout() #main self.layout_horizontal = QHBoxLayout() #buttons # self.layout_horizontal.addWidget(self.var_1st) # self.layout_horizontal.addWidget(self.var_2nd) # self.layout_horizontal.addWidget(self.var_3rd) # self.layout_horizontal.addWidget(self.var_4th) self.layout_horizontal.addWidget(self.isKnown) self.layout_horizontal.addWidget(self.isNotKnown) self.layout_vertical.addWidget(self.countdown) self.layout_vertical.addWidget(self.sentence) self.layout_vertical.addLayout(self.layout_horizontal) self.layout_horizontal.addWidget(self.answered) self.setLayout(self.layout_vertical) ### utility components ### self.trayIcon = QSystemTrayIcon(self) self.trayMenu = QMenu() self.gifLoading = QMovie('../res/cube.gif') self.gifLoading.frameChanged.connect(self.updateTrayIcon) self.nextQuizTimer = QTimer() self.nextQuizTimer.setSingleShot(True) self.nextQuizTimer.timeout.connect(self.showQuiz) self.countdownTimer = QTimer() self.countdownTimer.setSingleShot(True) self.countdownTimer.timeout.connect(self.timeIsOut) ### initializing ### self.initializeResources() """Start!""" if self.options.isQuizStartingAtLaunch(): self.waitUntilNextTimeslot() self.trayIcon.setToolTip('Quiz has started automatically!') self.pauseAction.setText('&Pause') self.trayIcon.showMessage( 'Loading complete! (took ~' + str(self.loadingTime.seconds) + ' seconds) Quiz underway.', 'Lo! Quiz already in progress!', QSystemTrayIcon.MessageIcon.Warning, 10000) else: self.trayIcon.setToolTip('Quiz is not initiated!') self.trayIcon.showMessage( 'Loading complete! (took ~' + str(self.loadingTime.seconds) + ' seconds) Standing by.', 'Quiz has not started yet! If you wish, you could start it manually or enable autostart by default.', QSystemTrayIcon.MessageIcon.Information, 10000) """Test calls here:""" ### ... ### #self.connect(self.hooker, SIGNAL('noQdict'), self.noQdict) def noQdict(self): self.showSessionMessage( 'Nope, cannot show quick dictionary during actual quiz.') #################################### # Initialization procedures # #################################### def initializeResources(self): """Pre-initialization""" self.animationTimer = () self.progressTimer = () self.grid_layout = () """Initialize Options""" self.options = Options() """Initialize Statistics""" self.stats = Stats() """Config Here""" self.initializeComposition() self.initializeComponents() self.setMenus() self.trayIcon.show() #self.startTrayLoading() """"Initialize Dictionaries (will take a some time!)""" time_start = datetime.now() self.dict = EdictParser() self.dict.loadDict() self.morphy = get_morph(PATH_TO_RES + DICT_EN) self.trayIcon.showMessage( 'Loading...', 'Initializing dictionaries', QSystemTrayIcon.MessageIcon.Information, 20000) #TODO: change into loading dialog... or not """Initializing srs system""" self.trayIcon.showMessage('Loading...', 'Initializing databases', QSystemTrayIcon.MessageIcon.Information, 20000) self.srs = srsScheduler() self.srs.initializeAll() self.srs.initializeCurrentSession(self.options.getSessionSize()) """Global hotkeys hook""" #TODO: add multiple hotkeys and fix stop() #self.hooker = GlobalHotkeyManager(toggleQDictFlag, 'Q') # self.hooker = GlobalHotkeyManager(toggleWidgetFlag(self.qdict), 'Q') # self.hooker.setDaemon(True) #temporarily, should work using stop() # self.hooker.start() time_end = datetime.now() self.loadingTime = time_end - time_start #################################### # Composition and appearance # #################################### def initializeComposition(self): """Main Dialog""" self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint) self.setFocusPolicy(Qt.StrongFocus) self.setFrameStyle(QFrame.StyledPanel | QFrame.Raised) #Font will appear in buttons self.setFont( QFont(self.options.getQuizFont(), self.options.getQuizFontSize())) desktop = QApplication.desktop().screenGeometry() self.setGeometry( QRect(desktop.width() - H_INDENT, desktop.height() - V_INDENT, D_WIDTH, D_HEIGHT)) self.setStyleSheet("QWidget { background-color: rgb(255, 255, 255); }") """Info dialog""" self.info.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint) self.info.setFrameStyle(QFrame.StyledPanel | QFrame.Raised) self.info.setGeometry( QRect(desktop.width() - H_INDENT - I_WIDTH - I_INDENT, desktop.height() - V_INDENT, I_WIDTH, I_HEIGHT)) self.info.setFixedSize(I_WIDTH, I_HEIGHT) self.info.setStyleSheet( "QWidget { background-color: rgb(255, 255, 255); }") """Verbose info dialog""" self.allInfo.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint) self.allInfo.setFrameStyle(QFrame.StyledPanel | QFrame.Raised) self.allInfo.setGeometry( QRect(desktop.width() - H_INDENT - I_WIDTH - I_INDENT, desktop.height() - V_INDENT, I_WIDTH, I_HEIGHT)) self.allInfo.setStyleSheet( "QWidget { background-color: rgb(255, 255, 255); }") """Session message""" self.status.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint) self.status.setFrameStyle(QFrame.StyledPanel | QFrame.Raised) self.status.setGeometry( QRect( desktop.width() - H_INDENT, desktop.height() - V_INDENT - S_HEIGHT - S_INDENT - S_CORRECTION, S_WIDTH, S_HEIGHT)) self.status.setStyleSheet( "QWidget { background-color: rgb(255, 255, 255); }") self.setMask(roundCorners(self.rect(), 5)) self.status.setMask(roundCorners(self.status.rect(), 5)) #self.info.setMask(roundCorners(self.info.rect(),5)) #self.allInfo.setMask(roundCorners(self.allInfo.rect(),5)) def initializeComponents(self): self.countdown.setMaximumHeight(6) self.countdown.setRange(0, self.options.getCountdownInterval() * 100) self.countdown.setTextVisible(False) self.countdown.setStyleSheet( "QProgressbar { background-color: rgb(255, 255, 255); }") #self.setFont(QFont(Fonts.SyoutyouProEl, 40))#self.options.getQuizFontSize())) self.sentence.setAlignment(Qt.AlignmentFlag.AlignCenter) #self.sentence.setFont(QFont(Fonts.HiragiNoMarugotoProW4, self.options.getSentenceFontSize())) self.sentence.setFont( QFont(self.options.getSentenceFont(), self.options.getSentenceFontSize())) self.sentence.setWordWrap(True) self.trayIcon.setIcon(QIcon(PATH_TO_RES + TRAY + 'active.png')) self.status.message.setFont( QFont('Cambria', self.options.getMessageFontSize())) self.status.layout.setAlignment(Qt.AlignCenter) self.status.message.setWordWrap(False) self.status.layout.setMargin(0) self.info.item.setFont(QFont(Fonts.HiragiNoMyoutyouProW3, 36)) self.info.reading.setFont(QFont(Fonts.HiragiNoMyoutyouProW3, 16)) self.info.components.setFont((QFont(Fonts.HiragiNoMyoutyouProW3, 14))) #self.info.item.setWordWrap(True) self.info.components.setWordWrap(True) #self.info.layout.setAlignment(Qt.AlignCenter) self.info.layout.setMargin(0) #self.info.layout.setSizeConstraint(self.info.layout.SetFixedSize) #NB: would work nice, if the anchor point was in right corner self.info.reading.setAlignment(Qt.AlignCenter) self.info.item.setAlignment(Qt.AlignCenter) self.info.components.setAlignment(Qt.AlignCenter) #self.info.setLayoutDirection(Qt.RightToLeft) self.info.reading.setStyleSheet( "QLabel { color: rgb(155, 155, 155); }") self.info.components.setStyleSheet( "QLabel { color: rgb(100, 100, 100); }") self.info.gem = self.info.saveGeometry() #################################### # Updating content # #################################### def updateContent(self): """Resetting multi-label sentence""" if self.grid_layout != (): for i in range(0, self.grid_layout.count()): self.grid_layout.itemAt(i).widget().hide() self.layout_vertical.removeItem(self.grid_layout) self.grid_layout.setParent(None) self.update() if self.sentence.isHidden(): self.sentence.show() self.showButtonsQuiz() """Getting actual content""" self.srs.getNextItem() start = datetime.now() #testing #example = self.srs.getCurrentExample().replace(self.srs.getWordFromExample(), u"<font color='blue'>" + self.srs.getWordFromExample() + u"</font>") example = self.srs.getCurrentExample().replace( self.srs.getCurrentItem(), u"<font color='blue'>" + self.srs.getCurrentItem() + u"</font>") print datetime.now() - start #testing self.sentence.setText(example) # start = datetime.now() #testing # readings = self.srs.getQuizVariants() # print datetime.now() - start #testing ''' changeFont = False for item in readings: if len(item) > 5 : changeFont = True if changeFont: self.setStyleSheet('QWidget { font-size: 11pt; }') else: self.setStyleSheet('QWidget { font-size: %spt; }' % self.options.getQuizFontSize()) ''' ''' if len(readings) == 4: #NB: HERE LIES THE GREAT ERROR self.var_1st.setText(readings[0]) self.var_2nd.setText(readings[1]) self.var_3rd.setText(readings[2]) self.var_4th.setText(readings[3]) ''' # try: # for i in range(0, self.layout_horizontal.count()): # if i > 3: break # self.layout_horizontal.itemAt(i).widget().setText(u'') # #self.layout_horizontal.itemAt(i).setStyleSheet('QPushButton { font-size: 11pt; }') # self.layout_horizontal.itemAt(i).widget().setText(readings[i]) # except: # print 'Not enough quiz variants' #TODO: log this def getReadyPostLayout(self): self.sentence.hide() self.update() self.grid_layout = QGridLayout() self.grid_layout.setSpacing(0) self.labels = [] columns_mod = 0 if len(self.srs.currentExample['eng']) > SENTENCE_MAX: font = QFont(self.options.getSentenceFont(), MIN_FONT_SIZE) columns_mod = 6 else: font = QFont(self.options.getSentenceFont(), self.options.getSentenceFontSize()) #row, column, rows span, columns span, max columns i = 0 j = 0 r = 1 c = 1 n = COLUMNS_MAX + columns_mod for word in self.srs.parseCurrentExample(): label = QLabel(word) # label.setFont(QFont(self.options.getSentenceFont(), self.options.getSentenceFontSize())) label.setFont(font) label.setAttribute(Qt.WA_Hover, True) label.installEventFilter(self.filter) self.labels.append(label) if len(label.text()) > 1: c = len(label.text()) else: c = 1 #Don't ask, really if j + c > n: i = i + 1 j = 0 self.grid_layout.addWidget(self.labels.pop(), i, j, r, c) if j <= n: j = j + c else: j = 0 i = i + 1 self.grid_layout.setAlignment(Qt.AlignCenter) self.layout_vertical.insertLayout(1, self.grid_layout) self.update() def hideButtonsQuiz(self): self.isKnown.hide() self.isNotKnown.hide() self.answered.clicked.connect(self.hideQuizAndWaitForNext) self.answered.show() def showButtonsQuiz(self): self.isKnown.show() self.isNotKnown.show() self.answered.hide() self.answered.disconnect() #################################### # Timers and animations # #################################### def waitUntilNextTimeslot(self): #if self.nextQuizTimer.isActive(): self.nextQuizTimer.stop() self.nextQuizTimer.start( self.options.getRepetitionInterval() * 60 * 1000 ) #options are in minutes NB: how do neatly I convert minutes to ms? def beginCountdown(self): self.trayIcon.setToolTip('Quiz in progress!') self.pauseAction.setText('&Pause') self.pauseAction.setShortcut('P') self.countdownTimer.start(self.options.getCountdownInterval() * 1000) self.progressTimer = RepeatTimer( 0.01, self.updateCountdownBar, self.options.getCountdownInterval() * 100) self.progressTimer.start() def updateCountdownBar(self): self.countdown.setValue(self.countdown.value() - 1) #print self.countdown.value() self.countdown.update( ) #NB: without .update() recursive repaint crushes qt def fade(self): if self.windowOpacity() == 1: self.animationTimer = RepeatTimer(0.025, self.fadeOut, 40) self.animationTimer.start() else: self.animationTimer = RepeatTimer(0.025, self.fadeIn, 40) self.animationTimer.start() def fadeIn(self): self.setWindowOpacity(self.windowOpacity() + 0.1) def fadeOut(self): self.setWindowOpacity(self.windowOpacity() - 0.1) def stopCountdown(self): self.progressTimer.cancel() self.countdownTimer.stop() self.countdown.setValue(0) #################################### # Actions and events # #################################### def setMenus(self): self.trayMenu.addAction( QAction('&Quiz me now!', self, shortcut="Q", triggered=self.showQuiz)) self.pauseAction = QAction('&Start quiz!', self, shortcut="S", triggered=self.pauseQuiz) self.trayMenu.addAction(self.pauseAction) self.trayMenu.addSeparator() self.trayMenu.addAction( QAction('Quick &dictionary', self, shortcut="D", triggered=self.showQuickDict)) self.trayMenu.addAction( QAction('&Global &statistics', self, shortcut="G", triggered=self.showGlobalStatistics)) self.trayMenu.addAction( QAction('&Options', self, shortcut="O", triggered=self.showOptions)) self.trayMenu.addAction( QAction('&About', self, shortcut="A", triggered=self.showAbout)) self.trayMenu.addSeparator() self.trayMenu.addAction( QAction('&Exit', self, shortcut="E", triggered=self.saveAndExit)) self.trayIcon.setContextMenu(self.trayMenu) self.trayIcon.activated.connect(self.onTrayIconActivated) #TODO: show session statistics def onTrayIconActivated(self, reason): ''' if reason == QSystemTrayIcon.DoubleClick: print 'tray icon double clicked' ''' if reason == QSystemTrayIcon.Trigger: if self.isHidden(): self.trayIcon.showMessage( 'Current session statistics:', 'Running time:\t\t' + self.stats.getRunningTime() + '\nItems seen:\t\t' + str(self.stats.totalItemSeen) + '\nCorrect answers:\t\t' + str(self.stats.answeredCorrect) + '\nWrong answers:\t\t' + self.stats.getIncorrectAnswersCount() + '\nCorrect ratio:\t\t' + self.stats.getCorrectRatioPercent() + #'\nQuiz total time:\t\t' + self.stats.getQuizActive() + '\nQuiz paused time:\t\t' + self.stats.getPausedTime() + '\nTotal pondering time:\t' + self.stats.getMusingsTime() + '\nTotal post-quiz time:\t' + self.stats.getQuizTime() + '\nAverage pondering:\t' + self.stats.getAverageMusingTime() + '\nAverage post-quiz:\t' + self.stats.getAveragePostQuizTime(), QSystemTrayIcon.MessageIcon.Information, 20000) def setButtonsActions(self): self.isKnown.clicked.connect(self.correctAnswer) self.isNotKnown.clicked.connect(self.wrongAnswer) def resetButtonsActions(self): self.isKnown.disconnect() self.isNotKnown.disconnect() def postAnswerActions(self): self.stats.musingsStopped() self.stats.postQuizStarted() self.stopCountdown() self.hideButtonsQuiz() self.getReadyPostLayout() def checkTranslationSize(self, translation): if len(translation) > TRANSLATION_CHARS_LIMIT: self.answered.setStyleSheet('QPushButton { font-size: 9pt; }') space_indices = [ i for i, value in enumerate(translation) if value == ' ' ] find_nearest_index = lambda value, list: min( list, key=lambda x: abs(x - value)) nearest_index = find_nearest_index(TRANSLATION_CHARS_LIMIT, space_indices) translation = translation[:nearest_index] + '\n' + translation[ nearest_index + 1:] else: self.answered.setStyleSheet('QPushButton { font-size: 11pt; }') self.answered.setText(translation) def correctAnswer(self): ''' self.stats.musingsStopped() self.stats.postQuizStarted() self.stopCountdown() self.hideButtonsQuiz() self.getReadyPostLayout() ''' self.postAnswerActions() self.srs.answeredCorrect() self.stats.quizAnsweredCorrect() #self.answered.setText(u"<font='Cambria'>" + self.srs.getCurrentSentenceTranslation() + "</font>") # self.answered.setText(self.srs.getCurrentSentenceTranslation()) self.checkTranslationSize(self.srs.getCurrentSentenceTranslation()) #self.answered.setFont(QFont('Calibri', 11)) self.showSessionMessage( u'<font color=green>Correct: OK</font>\t|\tNext quiz: ' + self.srs.getNextQuizTime() + '\t|\t<font color=' + self.srs.getLeitnerGradeAndColor()['color'] + '>Grade: ' + self.srs.getLeitnerGradeAndColor()['grade'] + ' (' + self.srs.getLeitnerGradeAndColor()['name'] + ')<font>') #self.answered.setShortcut('5') #self.setFocus() def wrongAnswer(self): ''' self.stats.musingsStopped() self.stats.postQuizStarted() self.stopCountdown() self.hideButtonsQuiz() self.getReadyPostLayout() ''' self.postAnswerActions() self.srs.answeredWrong() self.stats.quizAnsweredWrong() # self.answered.setText(self.srs.getCurrentSentenceTranslation()) self.checkTranslationSize(self.srs.getCurrentSentenceTranslation()) #self.answered.setFont(QFont('Calibri', 11)) #self.showSessionMessage(u"Wrong! Should be: <font style='font-family:" + Fonts.MSMyoutyou + "'>" #+ self.srs.getCorrectAnswer() + "</font> - Next quiz: " + self.srs.getNextQuizTime()) self.showSessionMessage( u'<font color=tomato>Bad</font>\t|\tNext quiz: ' + self.srs.getNextQuizTime() + '\t|\t<font color=' + self.srs.getLeitnerGradeAndColor()['color'] + '>Grade: ' + self.srs.getLeitnerGradeAndColor()['grade'] + ' (' + self.srs.getLeitnerGradeAndColor()['name'] + ')<font>') def timeIsOut(self): self.stats.musingsStopped() self.stats.postQuizStarted() QTimer.singleShot( 50, self.hideButtonsQuiz ) #NB: slight artificial lag to prevent recursive repaint crush, when mouse is suddenly over appearing button self.getReadyPostLayout() self.srs.answeredWrong() self.stats.quizAnsweredWrong() #self.showSessionMessage(u'Time is out! Correct answer is:' + self.srs.getCorrectAnswer()) # self.answered.setFont(QFont('Calibri', 11)) # self.answered.setText(self.srs.getCurrentSentenceTranslation()) self.checkTranslationSize(self.srs.getCurrentSentenceTranslation()) self.showSessionMessage( u'<font color=tomato>Timeout!</font>\t|\tNext quiz: ' + self.srs.getNextQuizTime() + '\t|\t<font color=' + self.srs.getLeitnerGradeAndColor()['color'] + '>Grade: ' + self.srs.getLeitnerGradeAndColor()['grade'] + ' (' + self.srs.getLeitnerGradeAndColor()['name'] + ')<font>') def hideQuizAndWaitForNext(self): self.stats.postQuizEnded() self.status.hide() self.info.hide() self.allInfo.hide() self.resetButtonsActions() self.setWindowOpacity(1) self.fade() QTimer.singleShot(1000, self.hide) self.waitUntilNextTimeslot() #self.updater.mayUpdate = True def pauseQuiz(self): if self.isHidden(): if self.pauseAction.text() == '&Pause': self.nextQuizTimer.stop() self.pauseAction.setText('&Unpause') self.pauseAction.setShortcut('U') self.trayIcon.setToolTip('Quiz paused!') self.trayIcon.setIcon( QIcon(PATH_TO_RES + TRAY + 'inactive.png')) self.stats.pauseStarted() #self.updater.mayUpdate = True elif self.pauseAction.text() == '&Start quiz!': self.waitUntilNextTimeslot() self.pauseAction.setText('&Pause') self.pauseAction.setShortcut('P') self.trayIcon.setToolTip('Quiz in progress!') self.trayIcon.setIcon(QIcon(PATH_TO_RES + TRAY + 'active.png')) else: self.waitUntilNextTimeslot() self.pauseAction.setText('&Pause') self.pauseAction.setShortcut('P') self.trayIcon.setToolTip('Quiz in progress!') self.trayIcon.setIcon(QIcon(PATH_TO_RES + TRAY + 'active.png')) self.stats.pauseEnded() #self.updater.mayUpdate = False else: self.showSessionMessage( u'Sorry, cannot pause while quiz in progress!') def showQuiz(self): if self.isHidden(): self.updateContent() self.setButtonsActions() self.show() self.setWindowOpacity(0) self.fade() self.countdown.setValue(self.options.getCountdownInterval() * 100) self.beginCountdown() self.stats.musingsStarted() if self.nextQuizTimer.isActive(): self.nextQuizTimer.stop() #self.updater.mayUpdate = False else: self.showSessionMessage(u'Quiz is already underway!') def showOptions(self): self.optionsDialog.show() def showAbout(self): self.about.show() def showQuickDict(self): self.qdict.showQDict = True def showGlobalStatistics(self): print '...' def startTrayLoading(self): self.gifLoading.start() #self.iconTimer = QTimer() #self.iconTimer.timeout.connect(self.updateTrayIcon) #self.iconTimer.start(100) def stopTrayLoading(self): self.gifLoading.stop() def updateTrayIcon(self): self.trayIcon.setIcon(self.gifLoading.currentPixmap()) def showSessionMessage(self, message): """Shows info message""" self.status.message.setText(message) self.status.show() #self.setFocus() #NB: does not work def saveAndExit(self): self.hide() self.status.hide() self.allInfo.hide() self.trayIcon.showMessage('Shutting down...', 'Saving session', QSystemTrayIcon.MessageIcon.Information, 20000) #self.startTrayLoading() if self.countdownTimer.isActive(): self.countdownTimer.stop() if self.nextQuizTimer.isActive(): self.nextQuizTimer.stop() if self.progressTimer != () and self.progressTimer.isAlive(): self.progressTimer.cancel() self.srs.endCurrentSession() self.trayIcon.hide() # self.hooker.stop() #self.updater.stop() self.optionsDialog.close() self.about.close() #self.qdict.close() self.close() def addReferences(self, about, options, qdict, updater): self.about = about self.optionsDialog = options self.qdict = qdict self.updater = updater def initGlobalHotkeys(self): def toggleWidgetFlag(): self.qdict.showQDict = True self.hooker = GlobalHotkeyManager(toggleWidgetFlag, 'Q') self.hooker.setDaemon(True) self.hooker.start()
class MediaView(QObject): started_signal = Signal() finished_signal = Signal() def __init__(self, media, parent): super(MediaView, self).__init__(parent) self._parent = parent self._id = media['id'] self._type = media['type'] self._duration = media['duration'] self._render = media['render'] self._options = media['options'] self._raws = media['raws'] self._layout_id = media['_layout_id'] self._schedule_id = media['_schedule_id'] self._region_id = media['_region_id'] self._save_dir = media['_save_dir'] self._widget = None self._play_timer = QTimer(self) self._started = 0 self._finished = 0 self._errors = None # self.setObjectName('Media-%s-%s' % (self._type, self._id)) # self._play_timer.setObjectName('%s-timer' % self.objectName()) self._connect_signals() def _connect_signals(self): self.started_signal.connect(self.mark_started) self.finished_signal.connect(self.mark_finished) self._play_timer.setSingleShot(True) self.connect(self._play_timer, SIGNAL("timeout()"), self.stop) @staticmethod def make(media, parent): if 'type' not in media: return None if 'image' == media['type']: view = ImageMediaView(media, parent) elif 'video' == media['type']: view = VideoMediaView(media, parent) else: view = WebMediaView(media, parent) return view @Slot() def play(self): pass @Slot() def stop(self, delete_widget=False): if self.is_finished(): return False if self._widget: tries = 10 while tries > 0 and not self._widget.close(): tries -= 1 time.sleep(0.05) if delete_widget: del self._widget self._widget = None self.finished_signal.emit() return True @Slot() def mark_started(self): self._started = time.time() @Slot() def mark_finished(self): if not self.is_finished(): self._finished = time.time() self._parent.queue_stats('media', self._started, self._finished, self._schedule_id, self._layout_id, self._id) def is_started(self): return self._started > 0 def is_finished(self): return self._finished > 0 def is_playing(self): return self.is_started() and not self.is_finished() def set_default_widget_prop(self): if self._widget is not None: self._widget.setAttribute(Qt.WA_DeleteOnClose, False) self._widget.setFocusPolicy(Qt.NoFocus) self._widget.setContextMenuPolicy(Qt.NoContextMenu) self._widget.setObjectName('%s-widget' % self.objectName())
class MainView(QGraphicsView): """Graphic representation of a user created circuit schematic.""" def __init__(self, parent): super(MainView, self).__init__(parent) self.setAcceptDrops(True) # Accept dragged items. self.setMouseTracking(True) # Allow mouseover effects. self.setDragMode(QGraphicsView.RubberBandDrag) # rubber select self.setScene(QGraphicsScene(parent)) self.isDrawing = False # user currently not drawing self.mainCircuit = Circuit("Main", None) self.timer = QTimer() """A timer for forcing redraws.""" self.timer.setInterval(200) self.timer.setSingleShot(True) self.timer.timeout.connect(self.setItemsInGrid) self.copyBuffer = None """A buffer for ctrl-c, ctrl-v copy operations.""" self.clockPlug = None def batchRename(self): """Experimental function to rename multiple items at once.""" sel = self.scene().selectedItems() if ( not all([isinstance(i, sel[0].__class__) for i in sel]) or (isinstance(sel[0].data, Plug) and not all([ i.data.isInput == sel[0].data.isInput for i in sel]))): print("Error") return ret = QInputDialog.getText(self, "Set prefix", "Prefix :") if ret[1] and len(ret[0]): for i in self.scene().selectedItems(): i.data.generate_name(None, ret[0]) i.setupPaint() self.parent().optionsDock.widget().updateOptions() def clearCircuit(self): """Clears every item from the circuit designer.""" for i in self.scene().items(): # https://bugreports.qt-project.org/browse/PYSIDE-252 if not isinstance(i, QGraphicsSimpleTextItem): self.scene().removeItem(i) if self.bgClockThread: self.bgClockThread.stop() self.mainCircuit.clear() def clockUpdate(self): """Updates the view at each clock tick.""" for item in self.scene().items(): if isinstance(item, PlugItem) or isinstance(item, WireItem): item.setupPaint() def closeEvent(self, e): """Overload in order to kill the clock thread.""" if self.bgClockThread is not None: self.bgClockThread.exit() def contextMenuEvent(self, e): """Pops a contextual menu up on right-clicks""" item = self.itemAt(e.pos()) if item: menu = QMenu(self) if isinstance(item, CircuitItem): pos = item.mapFromScene(self.mapToScene(e.pos())) plug = item.handleAtPos(pos) item = plug if plug else item menu.addAction(self.str_setName, lambda: self.getNewName(item)) elif isinstance(item, PlugItem): if isinstance(item.data, Clock): thread = item.data.clkThread if thread.paused: menu.addAction( self.str_startClock, lambda: thread.unpause()) else: menu.addAction( self.str_pauseClock, lambda: thread.pause()) menu.addAction(self.str_setName, lambda: self.getNewName(item)) if item.data.isInput: menu.addAction( str(item.data.value), item.setAndUpdate) elif isinstance(item, WireItem): pos = item.mapFromScene(self.mapToScene(e.pos())) if item.handleAtPos(pos): menu.addAction( self.str_removeLast, lambda: item.removeLast()) menu.popup(e.globalPos()) def dragEnterEvent(self, e): """Accept drag events coming from ToolBox.""" if isinstance(e.source(), ToolBox): e.accept() else: # Refuse all other drags. e.ignore() def dragLeaveEvent(self, e): """Fixes bug: items are half dragged over self, then away. (#16)""" e.ignore() def dragMoveEvent(self, e): """Accept drag move events.""" e.accept() def dropEvent(self, e): """Accept drop events.""" model = QStandardItemModel() model.dropMimeData( e.mimeData(), Qt.CopyAction, 0, 0, QModelIndex()) name = model.item(0).text() item = None if name in ['And', 'Or', 'Nand', 'Nor', 'Not', 'Xor', 'Xnor']: item = CircuitItem( getattr(engine.gates, name + 'Gate')(None, self.mainCircuit)) elif name == 'RSFlipFlop': item = CircuitItem(RSFlipFlop(None, self.mainCircuit)) elif name == 'JKFlipFlop': item = CircuitItem(JKFlipFlop(None, self.mainCircuit)) elif name == self.str_I: item = PlugItem(Plug(True, None, self.mainCircuit)) elif name == self.str_O: item = PlugItem(Plug(False, None, self.mainCircuit)) elif name == self.str_Clock: if not self.clockPlug: self.clockPlug = Clock(self.mainCircuit) self.clockPlug.clkThread.set_extern(self.clockUpdate) item = PlugItem(self.clockPlug) else: self.write(self.str_onlyOneClock) elif model.item(0, 1).text() == 'user': c = Circuit(None, self.mainCircuit) f = open(filePath('user/') + name + '.crc', 'rb') children = pickle.load(f) f.close() for child in children: if isinstance(child[0], Plug): child[0].owner = c if child[0].isInput: c.inputList.append(child[0]) else: c.outputList.append(child[0]) elif isinstance(child[0], Circuit): child[0].owner = c c.circuitList.append(child[0]) c.category = name item = CircuitItem(c) if item: # Fixes the default behaviour of centering the first # item added to scene. if not len(self.scene().items()): self.scene().setSceneRect(0, 0, 1, 1) else: self.scene().setSceneRect(0, 0, 0, 0) self.scene().addItem(item) item.setupPaint() item.setPos( closestGridPoint(item.mapFromScene(self.mapToScene(e.pos())))) for i in self.scene().selectedItems(): i.setSelected(False) item.setSelected(True) self.timer.start() def fillIO(self): """Experimental function to add as many global I/Os as still needed by the main circuit. """ for item in self.scene().items(): if isinstance(item, CircuitItem): pos = item.pos() rot = item.rotation() circuit = item.data off = 0 for input in circuit.inputList: if not input.sourcePlug: i = PlugItem(Plug(True, None, self.mainCircuit)) self.scene().addItem(i) x = ( (pos.x() if not rot % 180 else pos.y()) - 4 * GRIDSIZE * (1 if not rot % 360 else -1)) y = (pos.y() if not rot % 180 else pos.x()) + off i.setPos(x, y) i.setRotation(rot) off += 2 * GRIDSIZE off = 0 for output in circuit.outputList: if not len(output.destinationPlugs): i = PlugItem(Plug(False, None, self.mainCircuit)) self.scene().addItem(i) i.setPos(pos.x() + 100, pos.y() + off) off += 30 def getNewName(self, item): """Shows a dialog, and sets item name to user input.""" # ret = tuple string, bool ret = QInputDialog.getText(self, self.str_setName, self.str_name) if ret[1] and item.data.setName(ret[0]): # Not canceled. item.update() self.parent().optionsDock.widget().updateOptions() def keyPressEvent(self, e): """Manages keyboard events.""" scene = self.scene() selection = scene.selectedItems() # ESC, unselect all items if e.key() == Qt.Key_Escape: for item in selection: item.setSelected(False) # Del, suppression elif e.key() == Qt.Key_Delete: for item in selection: if isinstance(item, CircuitItem): self.mainCircuit.remove(item.data) elif isinstance(item, Plug): self.mainCircuit.remove(item.data) elif ( isinstance(item, WireItem) and item.data['endIO'] is not None): item.data['startIO'].disconnect(item.data['endIO']) scene.removeItem(item) # <- ->, item rotation. elif e.key() == Qt.Key_Left or e.key() == Qt.Key_Right: self.rotateItems(90 if e.key() == Qt.Key_Right else -90) # L, left align and even spacing elif e.key() == Qt.Key_L: left = min([item.scenePos().x() for item in selection]) sel = sorted(selection, key=lambda i: i.scenePos().y()) sel[0].setPos(left, sel[0].scenePos().y()) for i in range(1, len(sel)): sel[i].setPos( left, sel[i - 1].sceneBoundingRect().bottom() + 2 * GRIDSIZE) # R, right align and even spacing elif e.key() == Qt.Key_R: right = max([item.scenePos().x() for item in selection]) sel = sorted(selection, key=lambda i: i.scenePos().y()) sel[0].setPos(right, sel[0].scenePos().y()) for i in range(1, len(sel)): sel[i].setPos( right, sel[i - 1].sceneBoundingRect().bottom() + 2 * GRIDSIZE) # T, top align and even spacing elif e.key() == Qt.Key_T: top = min([item.scenePos().y() for item in selection]) sel = sorted(selection, key=lambda i: i.scenePos().x()) sel[0].setPos(sel[0].scenePos().x(), top) for i in range(1, len(sel)): sel[i].setPos( sel[i - 1].sceneBoundingRect().right() + 2 * GRIDSIZE, top) # B, bottom align and even spacing elif e.key() == Qt.Key_B: bottom = max([item.scenePos().y() for item in selection]) sel = sorted(selection, key=lambda i: i.scenePos().x()) sel[0].setPos(sel[0].scenePos().x(), bottom) # Ctrl-C, copy elif e.key() == Qt.Key_C and e.nativeModifiers() == 4: self.copyBuffer = copy(selection) # Ctrl-V, paste elif e.key() == Qt.Key_V and e.nativeModifiers() == 4: memo = {} for item in self.copyBuffer: if isinstance(item, PlugItem): dc = deepcopy(item.data, memo) self.mainCircuit.add(dc) dc.generate_name(None) i = PlugItem(dc) elif isinstance(item, CircuitItem): dc = deepcopy(item.data, memo) self.mainCircuit.add(dc) dc.generate_name() i = CircuitItem(dc) elif isinstance(item, WireItem): dc = deepcopy(item.data, memo) i = WireItem(dc['startIO'], dc['points'], dc['endIO']) self.scene().addItem(i) # TODO : +100 et pourquoi 100 et pas pi ou 5000? i.setPos(item.pos().x() + 100, item.pos().y() + 100) i.setRotation(item.rotation()) i.setupPaint() for item in selection: item.setupPaint() def mouseMoveEvent(self, e): """Redraw CurrentWire; change cursor on mouseOver handles.""" if self.isDrawing: self.currentWire.moveLastPoint( self.currentWire.mapFromScene(self.mapToScene(e.pos()))) item = self.itemAt(e.pos()) if item: pos = item.mapFromScene(self.mapToScene(e.pos())) isCircuit = isinstance(item, CircuitItem) if isCircuit or isinstance(item, PlugItem): handle = item.handleAtPos(pos) if handle: if isCircuit: item.setToolTip(handle.name) self.setCursor(Qt.CursorShape.UpArrowCursor) return elif isCircuit: item.setToolTip(item.data.name) elif (isinstance(item, WireItem) and item.handleAtPos(pos) and not self.isDrawing): self.setCursor(Qt.CursorShape.UpArrowCursor) return self.setCursor(Qt.CursorShape.ArrowCursor) super(MainView, self).mouseMoveEvent(e) def mousePressEvent(self, e): """Start Wire creation/extension.""" # Reserve right-clicks for contextual menus. if e.buttons() == Qt.RightButton: super(MainView, self).mousePressEvent(e) return self.mouseEventStart = None item = self.itemAt(e.pos()) if item: pos = item.mapFromScene(self.mapToScene(e.pos())) if isinstance(item, CircuitItem) or isinstance(item, PlugItem): plug = item.handleAtPos(pos) if plug: self.isDrawing = True p = closestGridPoint(self.mapToScene(e.pos())) self.currentWire = WireItem(plug, [p, p]) self.scene().addItem(self.currentWire) return # no super(), prevents dragging/selecting elif ( isinstance(item, PlugItem) and item.data.isInput and e.modifiers() & Qt.AltModifier): item.setAndUpdate() return elif isinstance(item, WireItem): if item.handleAtPos(pos): self.currentWire = item self.isDrawing = True return # no super(), prevents dragging/selecting # Didn't click a handle? We wanted to drag or select super(MainView, self).mousePressEvent(e) def mouseReleaseEvent(self, e): """Complete Wire segments. Connect Plugs.""" # Ignore right-clicks. if e.buttons() == Qt.RightButton: super(MainView, self).mousePressEvent(e) return if self.isDrawing: self.currentWire.addPoint() self.isDrawing = False old = self.currentWire.zValue() self.currentWire.setZValue(-500000) # to detect other wires item = self.itemAt(e.pos()) self.currentWire.setZValue(old) if item: pos = item.mapFromScene(self.mapToScene(e.pos())) if isinstance(item, CircuitItem) or isinstance(item, PlugItem): plug = item.handleAtPos(pos) if (plug and self.currentWire.handleAtPos( self.currentWire.mapFromScene( self.mapToScene(e.pos())))): if not self.currentWire.connect(plug): self.currentWire.revert() return elif ( isinstance(item, WireItem) and item != self.currentWire and not item.complete): if not self.currentWire.connect(item.data['startIO']): self.currentWire.revert() else: p = self.currentWire.data['points'][-1] points = item.data['points'] for i in range(len(points)): a = points[i] b = points[i + 1] if ( distance(a, p) + distance(p, b) == distance(a, b)): points = points[0:i + 1] points.reverse() break self.currentWire.data['points'].extend( points) self.scene().removeItem(item) self.currentWire.setupPaint() return elif isinstance(item, WireItem) and item.complete: p = item.data['startIO'] p1 = item.data['endIO'] source = p if p1.sourcePlug == p else p1 if not self.currentWire.connect(source): self.currentWire.revert() #~ else: # test code #~ print(self.currentWire.data) # Ugly hack to solve selection problems with multiple wireitems # on the same spot : the smallest area is selected. wires = sorted( [i for i in self.scene().items() if isinstance(i, WireItem)], key= lambda i: i.boundingRect().width() * i.boundingRect().height()) for i in range(len(wires)): wires[i].setZValue(-i - 1) self.currentWire = None super(MainView, self).mouseReleaseEvent(e) def rotateItems(self, angle): """Rotates the current selection around its gravity center.""" grp = self.scene().createItemGroup(self.scene().selectedItems()) br = grp.sceneBoundingRect() grp.setTransformOriginPoint( br.left() + br.width() / 2, br.top() + br.height() / 2) grp.setRotation(grp.rotation() + angle) self.scene().destroyItemGroup(grp) # The item group doesn't transfer its rotation to its children # .setRotation() sets it correctly but rotates the item # .rotate() rotates the item back without setting it # rotate is deprecated, so this is far from ideal for item in self.scene().selectedItems(): item.setRotation((item.rotation() + angle) % 360) item.rotate(-angle) def setItemsInGrid(self): """Correcting items pos to fit on the grid.""" for item in self.scene().items(): item.setPos(closestGridPoint(item.pos())) def write(self, message): """Briefly display a log WARNING.""" scene = self.scene() msg = scene.addText(message) QTimer.singleShot(1500, lambda: scene.removeItem(msg))
class Panel(QWidget): def __init__(self,parent=None,instr=None,lock=None,title='Instrument Panel'): # This class derivates from a Qt Widget so we have to call # the class builder ".__init__()" QWidget.__init__(self) # "self" is now a Qt Widget, then we load the user interface # generated with QtDesigner and call it self.ui self.ui = Lakeshore340_Ui.Ui_Panel() # Now we have to feed the GUI building method of this object (self.ui) # with the current Qt Widget 'self', but the widgets from the design will actually be built as children # of the object self.ui self.ui.setupUi(self) self.setWindowTitle(title) self.reserved_access_to_instr=lock self.temp_controller=instr self.check_T_timer = QTimer() self.check_T_timer.setSingleShot(True)#The timer would not wait for the completion of the task otherwise self.check_T_timer.timeout.connect(self.autocheckT) if self.ui.checkTbox.isChecked():self.autocheckT() def checkT(self): with self.reserved_access_to_instr: T=self.temp_controller.query_temp(self.ui.temp_controller_channel.currentText()) self.ui.T_display.setText(str(T)+" K") def autocheckT(self,state=1): if state: self.checkT() self.check_T_timer.start(self.ui.refresh_rate.value()*1000)#The value must be converted to milliseconds else: self.check_T_timer.stop() def update_timer_timeout(self,secs): self.check_T_timer.setInterval(int(secs*1000)) def set_heater_range(self,value=0): if value in range(6): #check compatibility with self.reserved_access_to_instr: self.temp_controller.set_heater_range(value) def set_setpoint(self): loop=self.ui.temp_controller_loop.value() setpoint=self.ui.temp_controller_setpoint.value() with self.reserved_access_to_instr: #self.temp_controller.switch_ramp(loop,'off') self.temp_controller.set_setpoint(loop,setpoint) def stop_ramp(self): loop=self.ui.temp_controller_loop.value() with self.reserved_access_to_instr: self.temp_controller.switch_ramp(loop,'off') def init_ramp(self): """After this function is called, the ramp will start the next time the setpoint is changed """ loop=self.ui.temp_controller_loop.value() #setpoint=self.ui.temp_controller_setpoint.value() rate=self.ui.temp_controller_ramprate.value() with self.reserved_access_to_instr: #self.temp_controller.set_setpoint(loop,setpoint) self.temp_controller.conf_ramp(loop,rate,'on') def PID_P_update(self,new_P_value): with self.reserved_access_to_instr: P,I,D=self.temp_controller.query_PID(1) self.temp_controller.set_PID(1,new_P_value,I,D) def PID_I_update(self,new_I_value): with self.reserved_access_to_instr: P,I,D=self.temp_controller.query_PID(1) self.temp_controller.set_PID(1,P,new_I_value,D) def PID_D_update(self,new_D_value): with self.reserved_access_to_instr: P,I,D=self.temp_controller.query_PID(1) self.temp_controller.set_PID(1,P,I,new_D_value)
class Thimblerig(QObject): """Класс для игры в наперстки в мосваре.""" def __init__(self, mw): super().__init__() self._mw = mw # Таймер для отсчета раундов игры self._timer_round_thimble = QTimer() self._timer_round_thimble.setInterval(1000) self._timer_round_thimble.setSingleShot(True) self._timer_round_thimble.timeout.connect(self.nine_thimble) # Таймер для выбора наперстков self._timer_thimble = QTimer() self._timer_thimble.setInterval(1000) self._timer_thimble.timeout.connect(self.select_thimbles) # Варианты порядка открытия наперстков self._variants = [ # С первого по третий ряд [0, 1, 2], [3, 4, 5], [6, 7, 8], # Диагонально [0, 4, 8], [6, 4, 2], ] # Номера наперстков, которые будет открывать бот self._thimble1, self._thimble2, self._thimble3 = 0, 0, 0 self._ruda_count = 0 self._thimble_round_count = 0 # Сумма ниже которой играть не будем self.min_money = 3000 finished_thimble_game = Signal() def _get_ruda_count(self): return self._ruda_count def _get_thimble_round_count(self): return self._thimble_round_count ruda_count = property(_get_ruda_count) thimble_round_count = property(_get_thimble_round_count) def run(self): """Игра в наперстки.""" try: # Если в текущий бот в текущий момент занят и это не игра в Наперстки if self._mw._used and 'thimble' not in self._mw.current_url(): logger.warn('Бот в данный момент занят процессом "%s". Выхожу из функции.', self._mw._used_process) return self._mw._used_process = "Игра в Наперстки" logger.debug('Выполняю задание "%s".', self._mw._used_process) self._mw.metro() if 'metro' in self._mw.current_url(): # TODO: временное решение проблемы с закончившимися билетами, лучше через окно сделать holders = self._mw.doc.findFirst('.metro-thimble .holders') holders = holders.toPlainText().replace('Встреч с Моней на сегодня: ', '') if int(holders) == 0: logger.warn('Закончились билеты для игры в Наперстки.') # TODO: добавить is_ready # TODO: is_ready указывает на следующий день return self._mw._used = True t = time.clock() if 'thimble' in self._mw.current_url(): logger.info('Игра в Наперстки уже была начала, продолжу играть.') else: # Эмулируем клик на кнопку "Начать играть" self._mw.click_tag('.metro-thimble .button .c') # # TODO: не работает, т.к. окно не сразу появляется # for el in self._mw.doc.findAll('.alert'): # text = el.findFirst('#alert-text') # # print(el, text.toPlainText()) # if not text.isNull() and 'Вы сегодня уже играли в наперстки с Моней Шацом' in text.toPlainText(): # # TODO: добавить is_ready # # TODO: is_ready указывает на следующий день # logger.warn('Закончились билеты для игры в наперстки.') # self._mw._used = False # return self._ruda_count = 0 self._thimble_round_count = 0 self._timer_round_thimble.start() # Ждем пока закончится игра loop = QEventLoop() self.finished_thimble_game.connect(loop.quit) loop.exec_() logger.debug('Длительность игры {:.1f} секунд'.format(time.clock() - t)) logger.debug('Игра в наперстки закончилась за {} раундов'.format(self._thimble_round_count)) logger.debug('Угадано {} руды. Потрачено {} тугриков.'.format(self._ruda_count, self._thimble_round_count * 1500)) logger.debug('Удача {:.2f}%'.format(self._ruda_count / (self._thimble_round_count * 3) * 100)) # Эмулируем клик на кнопку "Я наигрался, хватит" self._mw.go('thimble/leave') except MoswarClosedError: raise except Exception as e: raise MoswarBotError(e) finally: self._mw._used = False def nine_thimble(self): """Функция для начала игры в девять наперстков.""" if self._mw.money() < self.min_money: self._timer_thimble.stop() logger.debug("Заканчиваю игру.") self.finished_thimble_game.emit() return self._thimble_round_count += 1 # Выбираем кнопку для игры в 9 наперстков css_path = "#thimble-controls-buttons div[data-count='9']" self._mw.click_tag(css_path) # Выбираем случайный ряд наперстков numbers_thimble = random.choice(self._variants) # Порядок кликания на наперстки. Если True, то справа на лево right_to_left = random.choice([True, False]) if right_to_left: numbers_thimble.sort(reverse=True) self._thimble1, self._thimble2, self._thimble3 = ["#thimble{}".format(i) for i in numbers_thimble] logger.info('Порядок открытия наперстков: {}, {}, {}'.format(self._thimble1, self._thimble2, self._thimble3)) self._timer_thimble.start() def _check_click_thimble(self, css_path): """Функция, вернет True, если на Наперсток уже кликали.""" tag = self._mw.doc.findFirst(css_path) attr = tag.attribute('class') # Проверяем, что на наперсток кликнули check = 'guessed' in attr or 'empty' in attr logger.info('Проверяем наперсток "%s". Атрибут элемента: "%s". -> "%s".', css_path, attr, check) return check def select_thimbles(self): """Функция для клика на наперстки.""" doc = self._mw.doc # Количество оставшихся попыток. Для девяти наперсток их максимум будет 3 left = doc.findFirst('#naperstki-left').toInnerXml() # Если количество попыток равно 0, то останавливаем таймер клика на наперстки if left == '0': self._timer_thimble.stop() # Получаем количество угаданной за раунд руды ruda = doc.findFirst('#naperstki-ruda').toPlainText() self._ruda_count += int(ruda) logger.info("Раунд {}. Угадано {} руды.".format(self._thimble_round_count, ruda)) # Запускаем следующий раунд self._timer_round_thimble.start() return # Проверяем, что на наперсток не кликали и кликаем if not self._check_click_thimble(self._thimble1): self._mw.click_tag(self._thimble1) elif not self._check_click_thimble(self._thimble2): self._mw.click_tag(self._thimble2) elif not self._check_click_thimble(self._thimble3): self._mw.click_tag(self._thimble3)
def hoverTimer(self): ret = QTimer(self.q) ret.setInterval(400) ret.setSingleShot(False) ret.timeout.connect(self._hover) return ret
class QtReactor(posixbase.PosixReactorBase): implements(IReactorFDSet) def __init__(self): self._reads = {} self._writes = {} self._notifiers = {} self._timer = QTimer() self._timer.setSingleShot(True) QObject.connect(self._timer, SIGNAL("timeout()"), self.iterate) if QCoreApplication.instance() is None: # Application Object has not been started yet self.qApp = QCoreApplication([]) self._ownApp = True else: self.qApp = QCoreApplication.instance() self._ownApp = False self._blockApp = None posixbase.PosixReactorBase.__init__(self) def _add(self, xer, primary, type): """ Private method for adding a descriptor from the event loop. It takes care of adding it if new or modifying it if already added for another state (read -> read/write for example). """ if xer not in primary: primary[xer] = TwistedSocketNotifier(None, self, xer, type) def addReader(self, reader): """ Add a FileDescriptor for notification of data available to read. """ self._add(reader, self._reads, QSocketNotifier.Read) def addWriter(self, writer): """ Add a FileDescriptor for notification of data available to write. """ self._add(writer, self._writes, QSocketNotifier.Write) def _remove(self, xer, primary): """ Private method for removing a descriptor from the event loop. It does the inverse job of _add, and also add a check in case of the fd has gone away. """ if xer in primary: notifier = primary.pop(xer) notifier.shutdown() def removeReader(self, reader): """ Remove a Selectable for notification of data available to read. """ self._remove(reader, self._reads) def removeWriter(self, writer): """ Remove a Selectable for notification of data available to write. """ self._remove(writer, self._writes) def removeAll(self): """ Remove all selectables, and return a list of them. """ rv = self._removeAll(self._reads, self._writes) return rv def getReaders(self): return self._reads.keys() def getWriters(self): return self._writes.keys() def callLater(self, howlong, *args, **kargs): rval = super(QtReactor, self).callLater(howlong, *args, **kargs) self.reactorInvocation() return rval def reactorInvocation(self): self._timer.stop() self._timer.setInterval(0) self._timer.start() def _iterate(self, delay=None, fromqt=False): """See twisted.internet.interfaces.IReactorCore.iterate. """ self.runUntilCurrent() self.doIteration(delay, fromqt) iterate = _iterate def doIteration(self, delay=None, fromqt=False): 'This method is called by a Qt timer or by network activity on a file descriptor' if not self.running and self._blockApp: self._blockApp.quit() self._timer.stop() delay = max(delay, 1) if not fromqt: self.qApp.processEvents(QEventLoop.AllEvents, delay * 1000) if self.timeout() is None: timeout = 0.1 elif self.timeout() == 0: timeout = 0 else: timeout = self.timeout() self._timer.setInterval(timeout * 1000) self._timer.start() def runReturn(self, installSignalHandlers=True): self.startRunning(installSignalHandlers=installSignalHandlers) self.reactorInvocation() def run(self, installSignalHandlers=True): if self._ownApp: self._blockApp = self.qApp else: self._blockApp = QEventLoop() self.runReturn() self._blockApp.exec_()
class TextEditor(RWWidget, Ui_Form): """ Contains many features of popular text editors adapted for use with Ontologies such as syntax highlighting, and autocompletion. One column on the left of the text editor contains line numbers and another contains other contextual information such as whether a block of code has been hidden/collapsed and can be displayed/expanded later. It also contains an incremental search and an interface to pySUMO's settings so font size and family can be changed at will. Variables: - syntax_highlighter: The syntax highlighter object for the text editor. Methods: - __init__: Initalizes the Object and the QPlainTextEdit - commit: Notifies other Widgets of changes. - show_autocomplete: Returns autocompletion choices. - getWidget: returns the QPlainTextEdit - numberbarPaint: Paints the numberbar - searchCompletion: Asks QCompleter if a whole word exists starting with user input - hideFrom: Starts hides all lines from the ()-block started by line - insertCompletion: Puts the selected Completion into the TextEditor """ def __init__(self, mainwindow, settings=None): """ Initializes the text editor widget. """ super(TextEditor, self).__init__(mainwindow) self.setupUi(self.mw) self.plainTextEdit.clear() self.plainTextEdit.setEnabled(False) self.plainTextEdit.show() self.highlighter = SyntaxHighlighter(self.plainTextEdit.document(), settings) self.initAutocomplete() self._initNumberBar() self.hidden = {} self.printer = QPrinter(QPrinterInfo.defaultPrinter()) self.plainTextEdit.setTextCursor( self.plainTextEdit.cursorForPosition(QPoint(0, 0))) self.canUndo = False self.canRedo = False self.ontologySelector.setCurrentIndex(-1) self.timer = QTimer(self) self.timer.setSingleShot(True) self.timer.timeout.connect(self.commit) #Connects self.getWidget().textChanged.connect(self.searchCompletion) self.plainTextEdit.undoAvailable.connect(self.setCanUndo) self.plainTextEdit.redoAvailable.connect(self.setCanRedo) self.ontologySelector.currentIndexChanged[int].connect( self.showOtherOntology) self.plainTextEdit.textChanged.connect(self.expandIfBracketRemoved) self.plainTextEdit.textChanged.connect(self.setTextChanged) self._updateOntologySelector() #must be after connects @Slot() def setTextChanged(self): """Is called if the text changed signal is thrown and sets a timer of 3 seconds to reparse the ontology. """ self.timer.stop() self.timer.start(3000) def setCanUndo(self, b): self.canUndo = b def setCanRedo(self, b): self.canRedo = b def _print_(self): """ Creates a print dialog with the latest text""" dialog = QPrintDialog() if dialog.exec_() == QDialog.Accepted: doc = self.plainTextEdit.document() doc.print_(dialog.printer()) def _quickPrint_(self): """ No dialog, just print""" if self.printer is None: return doc = self.plainTextEdit.document() doc.print_(self.printer) def _printPreview_(self): """ Create a print preview""" dialog = QPrintPreviewDialog() dialog.paintRequested.connect(self.plainTextEdit.print_) dialog.exec_() def saveOntology(self): """ Save the ontology to disk""" idx = self.ontologySelector.currentIndex() ontology = self.ontologySelector.itemData(idx) if ontology is None: return if type(ontology) is Ontology: ontology.save() def getActiveOntology(self): idx = self.ontologySelector.currentIndex() return self.ontologySelector.itemData(idx) def undo(self): if self.canUndo: self.plainTextEdit.undo() try: self.SyntaxController.add_ontology( self.getActiveOntology(), self.plainTextEdit.toPlainText()) except ParseError: return self.commit() else: super(TextEditor, self).undo() def redo(self): if self.canRedo: self.plainTextEdit.redo() try: self.SyntaxController.add_ontology( self.getActiveOntology(), self.plainTextEdit.toPlainText()) except ParseError: return self.commit() else: super(TextEditor, self).redo() def _initNumberBar(self): """ Init the number bar""" self.number_bar = NumberBar(self) self.number_bar.setMinimumSize(QSize(30, 0)) self.number_bar.setObjectName("number_bar") self.gridLayout.addWidget(self.number_bar, 1, 0, 1, 1) self.plainTextEdit.blockCountChanged.connect( self.number_bar.adjustWidth) self.plainTextEdit.updateRequest.connect( self.number_bar.updateContents) @Slot(int) def jumpToLocation(self, location, ontology): if ontology == str(self.getActiveOntology()): textBlock = self.plainTextEdit.document().findBlockByNumber( location) pos = textBlock.position() textCursor = self.plainTextEdit.textCursor() textCursor.setPosition(pos) self.plainTextEdit.setTextCursor(textCursor) self.plainTextEdit.centerCursor() def _updateOntologySelector(self): """ Update the ontology selector where you can select which Ontology to show in the editor""" current = self.ontologySelector.currentText() self.ontologySelector.currentIndexChanged[int].disconnect( self.showOtherOntology) self.ontologySelector.clear() index = -1 count = 0 for i in self.getIndexAbstractor().ontologies: if current == i.name: index = count self.ontologySelector.addItem(i.name, i) count = count + 1 self.ontologySelector.setCurrentIndex(index) # if index == -1 : # the ontology was removed. # self.showOtherOntology(index) if index == -1: self.plainTextEdit.setEnabled(False) self.plainTextEdit.clear() self.ontologySelector.currentIndexChanged[int].connect( self.showOtherOntology) def setActiveOntology(self, ontology): index = -1 count = 0 for i in self.getIndexAbstractor().ontologies: if ontology.name == i.name: index = count break count = count + 1 self.ontologySelector.setCurrentIndex(index) @Slot(int) def showOtherOntology(self, idx): """ Show other ontology in the plaintextedit Arguments: - idx: The id of the current Ontologyselector """ dced = False try: self.plainTextEdit.textChanged.disconnect(self.setTextChanged) except RuntimeError: dced = True idx = self.ontologySelector.currentIndex() if idx == -1: self.plainTextEdit.setEnabled(False) self.plainTextEdit.clear() return ontologyname = self.ontologySelector.currentText() for i in self.getIndexAbstractor().ontologies: if i.name == ontologyname: self.plainTextEdit.setEnabled(True) self.getWidget().setPlainText( self.getIndexAbstractor().get_ontology_file(i).getvalue()) if not dced: self.plainTextEdit.textChanged.connect(self.setTextChanged) return self.plainTextEdit.textChanged.connect(self.commit) assert False @Slot() def expandIfBracketRemoved(self): """ Check if a line with ( or ) was changed and expand the possible hidden lines """ current_line = self.getWidget().document().findBlock( self.getWidget().textCursor().position()).blockNumber() + 1 if current_line in self.hidden: self.toggleVisibility(current_line) @Slot() def zoomIn(self): """ Increase the size of the font in the TextEditor """ doc = self.getWidget().document() font = doc.defaultFont() font.setPointSize(font.pointSize() + 1) font = QFont(font) doc.setDefaultFont(font) @Slot() def zoomOut(self): """ Decrease the size of the font in the TextEditor""" doc = self.getWidget().document() font = doc.defaultFont() font.setPointSize(font.pointSize() - 1) font = QFont(font) doc.setDefaultFont(font) @Slot() def expandAll(self): """ Expands all hidden code blocks""" for see in list(self.hidden.keys()): self.toggleVisibility(see) @Slot() def collapseAll(self): """ Collapse all code blocks (where possible)""" block = self.getWidget().document().firstBlock() while block.isValid(): if block.isVisible(): if block.text().count("(") > block.text().count(")"): self.toggleVisibility(block.blockNumber() + 1) block = block.next() def _hideLines(self, lines): for line in lines: if line == 0: continue block = self.getWidget().document().findBlockByNumber(line - 1) assert block.isVisible() block.setVisible(False) assert not block.isVisible(), "Problem with line %r" % (line) def _showLines(self, lines): """ Show the lines not visible starting by lines Arguments: - lines: The first line followed by an unvisible block """ for line in lines: block = self.getWidget().document().findBlockByNumber(line - 1) block.setVisible(True) def getLayoutWidget(self): """ Returns the layout widget""" return self.widget def numberbarPaint(self, number_bar, event): """Paints the line numbers of the code file""" self.number_bar.link = [] font_metrics = self.getWidget().fontMetrics() current_line = self.getWidget().document().findBlock( self.getWidget().textCursor().position()).blockNumber() + 1 block = self.getWidget().firstVisibleBlock() line_count = block.blockNumber() painter = QPainter(self.number_bar) # TODO: second argument is color -> to settings painter.fillRect(self.number_bar.rect(), self.getWidget().palette().base()) # Iterate over all visible text blocks in the document. while block.isValid(): line_count += 1 text = str(line_count) block_top = self.getWidget().blockBoundingGeometry( block).translated(self.getWidget().contentOffset()).top() if not block.isVisible(): block = block.next() while not block.isVisible(): line_count += 1 block = block.next() continue self.number_bar.link.append((block_top, line_count)) # Check if the position of the block is out side of the visible # area. if block_top >= event.rect().bottom(): break # We want the line number for the selected line to be bold. if line_count == current_line: font = painter.font() font.setBold(True) else: font = painter.font() font.setBold(False) # line opens a block if line_count in self.hidden: text += "+" font.setUnderline(True) elif block.text().count("(") > block.text().count(")"): text += "-" font.setUnderline(True) else: font.setUnderline(False) painter.setFont(font) # Draw the line number right justified at the position of the # line. paint_rect = QRect(0, block_top, self.number_bar.width(), font_metrics.height()) painter.drawText(paint_rect, Qt.AlignLeft, text) block = block.next() painter.end() def initAutocomplete(self): """Inits the QCompleter and gives him a list of words""" self.completer = QCompleter( list( OrderedDict.fromkeys( re.split("\\W", self.plainTextEdit.toPlainText())))) self.completer.setCaseSensitivity(Qt.CaseInsensitive) self.completer.setWidget(self.getWidget()) self.completer.activated.connect(self.insertCompletion) def searchCompletion(self): """Searches for possible completion from QCompleter to the current text position""" tc = self.getWidget().textCursor() tc.movePosition(QTextCursor.PreviousCharacter, QTextCursor.KeepAnchor) if tc.selectedText() in string.whitespace: self.completer.popup().hide() return tc.movePosition(QTextCursor.StartOfWord, QTextCursor.KeepAnchor) beginning = tc.selectedText() if len(beginning) >= 3: self.completer.setCompletionPrefix(beginning) self.completer.complete() shortcut = QShortcut(QKeySequence("Ctrl+Enter"), self.getWidget(), self.insertCompletion) def toggleVisibility(self, line): """ Shows or hides a line """ if line in self.hidden: self._showLines(self.hidden[line]) del self.hidden[line] else: self.hideFrom(line) # update views self.getWidget().hide() self.getWidget().show() self.number_bar.update() def hideFrom(self, line): """ Hides a block starting by line. Do nothing if not hidable""" block = self.getWidget().document().findBlockByNumber(line - 1) openB = block.text().count("(") closeB = block.text().count(")") startline = line # go to line >= line: block starts counting by 0 block = self.getWidget().document().findBlockByNumber(line - 1) hidden = [] assert block.isValid() while openB > closeB and block.isValid(): assert block.isValid() block = block.next() line = block.blockNumber() + 1 if block.isVisible(): hidden.append(line) openB += block.text().count("(") closeB += block.text().count(")") if hidden == []: return self._hideLines(hidden) self.hidden[startline] = hidden # set current line in viewable area current_line = self.getWidget().document().findBlock( self.getWidget().textCursor().position()).blockNumber() + 1 if (startline < current_line and current_line <= line): block = block.next() cursor = QTextCursor(block) self.getWidget().setTextCursor(cursor) @Slot(str) def insertCompletion(self, completion): """ Adds the completion to current text""" tc = self.getWidget().textCursor() tc.movePosition(QTextCursor.StartOfWord, QTextCursor.KeepAnchor) tc.removeSelectedText() tc.insertText(completion) def getWidget(self): """ Return the QPlainTextEdit Widget""" return self.plainTextEdit @Slot() def commit(self): """ Overrides commit from RWWidget. """ idx = self.ontologySelector.currentIndex() if idx == -1: return ontology = self.ontologySelector.itemData(idx) if ontology is None: return try: QApplication.setOverrideCursor(Qt.BusyCursor) self.SyntaxController.add_ontology( ontology, self.plainTextEdit.toPlainText()) QApplication.setOverrideCursor(Qt.ArrowCursor) except ParseError: return super(TextEditor, self).commit() @Slot() def refresh(self): """ Refreshes the content of the TextEditor (syncing with other widgets)""" textCursorPos = self.plainTextEdit.textCursor().position() super(TextEditor, self).refresh() dced = False try: self.plainTextEdit.textChanged.disconnect(self.setTextChanged) except RuntimeError: dced = True idx = self.ontologySelector.currentIndex() ontology = self.ontologySelector.itemData(idx) if ontology in self.IA.ontologies: f = self.IA.get_ontology_file(ontology) self.plainTextEdit.setPlainText(f.getvalue()) if not dced: self.plainTextEdit.textChanged.connect(self.setTextChanged) cursor = self.plainTextEdit.textCursor() cursor.setPosition(textCursorPos) self.plainTextEdit.setTextCursor(cursor) self.plainTextEdit.centerCursor()