def send_object(self, name, obj): id = next(self._id_gen) value = self._objects[id] = dict(id=id, name=name, obj=obj) # Wait till JS is connected to receive objects while not self.receivers(self.objectChanged): qApp.processEvents(QEventLoop.ExcludeUserInputEvents) self.objectChanged.emit(value)
def test_plot_types_combinations(self): """Check that the widget doesn't crash at any plot combination""" # pylint: disable=too-many-nested-blocks from AnyQt.QtWidgets import qApp widget = self.widget c = widget.controls set_chk = self._set_check self.send_signal(widget.Inputs.data, self.iris) cont = self.iris.domain[0] disc = self.iris.domain.class_var for var in (cont, disc): for cvar in (disc, None): for fitter in [0, 2]: for cumulative in [False, True]: for stack in [False, True]: for show_probs in [False, True]: for sort_by_freq in [False, True]: self._set_var(var) self._set_cvar(cvar) self._set_fitter(fitter) set_chk(c.cumulative_distr, cumulative) set_chk(c.stacked_columns, stack) set_chk(c.show_probs, show_probs) set_chk(c.sort_by_freq, sort_by_freq) qApp.processEvents()
def _evalJS(self, code): while not self._jsobject_channel.is_all_exposed(): qApp.processEvents(QEventLoop.ExcludeUserInputEvents) self.runJavaScript(code, lambda result: setattr(self, "_result", result)) while self._result is _NOTSET: qApp.processEvents(QEventLoop.ExcludeUserInputEvents) result, self._result = self._result, _NOTSET return result
def _evalJS(self, code): while not self._jsobject_channel.is_all_exposed(): qApp.processEvents(QEventLoop.ExcludeUserInputEvents) self.runJavaScript(code, lambda result: setattr(self, '_result', result)) while self._result is _NOTSET: qApp.processEvents(QEventLoop.ExcludeUserInputEvents) result, self._result = self._result, _NOTSET return result
def _evalJS(self, code): while not self._jsobject_channel.is_all_exposed(): qApp.processEvents(QEventLoop.ExcludeUserInputEvents) if sip.isdeleted(self): return result = self._results.create() self.runJavaScript(code, lambda x: self._results.store(result)) while result not in self._results: qApp.processEvents(QEventLoop.ExcludeUserInputEvents) self._results.remove(result)
def send_object(self, name, obj): if isinstance(obj, QObject): raise ValueError( "QWebChannel doesn't transmit QObject instances. If you " "need a QObject available in JavaScript, pass it as a " "bridge in WebviewWidget constructor.") id = next(self._id_gen) value = self._objects[id] = dict(id=id, name=name, obj=obj) # Wait till JS is connected to receive objects while not self.receivers(self.objectChanged): qApp.processEvents(QEventLoop.ExcludeUserInputEvents) self.objectChanged.emit(value)
def wait(until: callable, timeout=5000): """Process events until condition is satisfied Parameters ---------- until: callable that returns True when condition is satisfied timeout: number of milliseconds to wait until TimeoutError is raised """ started = time.clock() while not until(): qApp.processEvents(QEventLoop.ExcludeUserInputEvents) if (time.clock() - started) * 1000 > timeout: raise TimeoutError()
def progressBarSet(self, value, processEvents=None): """ Set the current progress bar to `value`. Parameters ---------- value : float Progress value. processEvents : Optional[QEventLoop.ProcessEventsFlags] If present then `QApplication.processEvents(processEvents)` will be called. Passing any value here is highly discouraged. It is up to the client to handle the consequences of such action. .. versionchanged:: 3.4.2 Deprecated and changed default `processEvents` value. """ old = self.__progressBarValue self.__progressBarValue = value if value > 0: if self.__progressState != 1: warnings.warn( "progressBarSet() called without a " "preceding progressBarInit()", stacklevel=2, ) self.__progressState = 1 self.processingStateChanged.emit(1) usedTime = max(1, time.time() - self.startTime) totalTime = 100.0 * usedTime / value remainingTime = max(0, int(totalTime - usedTime)) hrs = remainingTime // 3600 mins = (remainingTime % 3600) // 60 secs = remainingTime % 60 if hrs > 0: text = "{}:{:02}:{:02}".format(hrs, mins, secs) else: text = "{}:{}:{:02}".format(hrs, mins, secs) self.setWindowTitle("{} ({:d}%, ETA: {})".format( self.captionTitle, int(value), text)) else: self.setWindowTitle(self.captionTitle + " (0% complete)") if old != value: self.progressBarValueChanged.emit(value) if processEvents is not None and processEvents is not False: qApp.processEvents(processEvents)
def wait(until: callable, timeout=5000): """Process events until condition is satisfied Parameters ---------- until: callable Returns True when condition is satisfied. timeout: int Milliseconds to wait until TimeoutError is raised. """ started = time.perf_counter() while not until(): qApp.processEvents(QEventLoop.ExcludeUserInputEvents) if (time.perf_counter() - started) * 1000 > timeout: raise TimeoutError()
def test_selection(self): data = Table('iris') selected_indices = [] def selection_callback(indices): nonlocal selected_indices selected_indices = indices scatter = SelectionScatter(selection_callback) scatter.chart(options=dict(series=[dict(data=data.X[:, :2])])) scatter.show() while scatter.isHidden() or not scatter.geometry().isValid(): qApp.processEvents() time.sleep(.05) time.sleep(1) # add some time for WM to place window or whatever topleft = scatter.geometry().topLeft() bottomright = scatter.geometry().bottomRight() startpos = topleft + QPoint(100, 100) endpos = bottomright - QPoint(300, 300) # Simulate selection QTest.mousePress(scatter, Qt.LeftButton, Qt.NoModifier, startpos, 1000) qApp.processEvents() QTest.mouseMove(scatter, endpos) qApp.processEvents() QTest.mouseRelease(scatter, Qt.LeftButton, Qt.NoModifier, endpos, 100) while not selected_indices: qApp.processEvents() time.sleep(.05) self.assertEqual(len(selected_indices), 1) self.assertGreater(len(selected_indices[0]), 0) # Simulate deselection QTest.mouseClick(scatter, Qt.LeftButton, Qt.NoModifier, startpos - QPoint(10, 10)) while selected_indices: qApp.processEvents() time.sleep(.05) self.assertFalse(len(selected_indices)) QTest.keyClick(scatter, Qt.Key_Escape) self.assertTrue(scatter.isHidden())
def progressBarSet(self, value, processEvents=None): """ Set the current progress bar to `value`. Parameters ---------- value : float Progress value. processEvents : Optional[QEventLoop.ProcessEventsFlags] If present then `QApplication.processEvents(processEvents)` will be called. Passing any value here is highly discouraged. It is up to the client to handle the consequences of such action. .. versionchanged:: 3.4.2 Deprecated and changed default `processEvents` value. """ old = self.__progressBarValue self.__progressBarValue = value if value > 0: if self.__progressState != 1: warnings.warn("progressBarSet() called without a " "preceding progressBarInit()", stacklevel=2) self.__progressState = 1 self.processingStateChanged.emit(1) usedTime = max(1, time.time() - self.startTime) totalTime = 100.0 * usedTime / value remainingTime = max(0, int(totalTime - usedTime)) hrs = remainingTime // 3600 mins = (remainingTime % 3600) // 60 secs = remainingTime % 60 if hrs > 0: text = "{}:{:02}:{:02}".format(hrs, mins, secs) else: text = "{}:{}:{:02}".format(hrs, mins, secs) self.setWindowTitle("{} ({:d}%, ETA: {})" .format(self.captionTitle, int(value), text)) else: self.setWindowTitle(self.captionTitle + " (0% complete)") if old != value: self.progressBarValueChanged.emit(value) if processEvents is not None and processEvents is not False: qApp.processEvents(processEvents)
def test_base(self): w = WebviewWidget() w.evalJS('document.write("foo");') SVG = '<svg xmlns:dc="...">asd</svg>' w.onloadJS('''document.write('{}');'''.format(SVG)) w.setUrl(SOME_URL) svg = None while svg is None: try: svg = w.svg() break except ValueError: qApp.processEvents() self.assertEqual(svg, SVG) self.assertEqual( w.html(), '<html><head></head><body>foo<svg xmlns:dc="...">asd</svg></body></html>')
def progressBarSet(self, value, processEvents=QEventLoop.AllEvents): """ Set the current progress bar to `value`. .. note:: This method will by default call `QApplication.processEvents` with `processEvents`. To suppress this behavior pass ``processEvents=None``. :param float value: Progress value :param processEvents: Process events flag :type processEvents: `QEventLoop.ProcessEventsFlags` or `None` """ old = self.__progressBarValue self.__progressBarValue = value if value > 0: if self.__progressState != 1: warnings.warn( "progressBarSet() called without a " "preceding progressBarInit()", stacklevel=2) self.__progressState = 1 self.processingStateChanged.emit(1) usedTime = max(1, time.time() - self.startTime) totalTime = 100.0 * usedTime / value remainingTime = max(0, int(totalTime - usedTime)) hrs = remainingTime // 3600 mins = (remainingTime % 3600) // 60 secs = remainingTime % 60 if hrs > 0: text = "{}:{:02}:{:02}".format(hrs, mins, secs) else: text = "{}:{}:{:02}".format(hrs, mins, secs) self.setWindowTitle("{} ({:d}%, ETA: {})".format( self.captionTitle, int(value), text)) else: self.setWindowTitle(self.captionTitle + " (0% complete)") if old != value: self.progressBarValueChanged.emit(value) if processEvents is not None and processEvents is not False: qApp.processEvents(processEvents)
def test_plot_types_combinations(self): """Check that the widget doesn't crash at any plot combination""" from AnyQt.QtWidgets import qApp widget = self.widget c = widget.controls self.send_signal(widget.Inputs.data, self.iris) cont = self.iris.domain[0] disc = self.iris.domain.class_var for var in (cont, disc): for cvar in (disc, None): for b in [0, 1]: self._set_var(var) self._set_cvar(cvar) self._set_fitter(2 * b) self._set_check(c.stacked_columns, b) self._set_check(c.show_probs, b) qApp.processEvents()
def progressBarSet(self, value, processEvents=QEventLoop.AllEvents): """ Set the current progress bar to `value`. .. note:: This method will by default call `QApplication.processEvents` with `processEvents`. To suppress this behavior pass ``processEvents=None``. :param float value: Progress value :param processEvents: Process events flag :type processEvents: `QEventLoop.ProcessEventsFlags` or `None` """ old = self.__progressBarValue self.__progressBarValue = value if value > 0: if self.__progressState != 1: warnings.warn("progressBarSet() called without a " "preceding progressBarInit()", stacklevel=2) self.__progressState = 1 self.processingStateChanged.emit(1) usedTime = max(1, time.time() - self.startTime) totalTime = 100.0 * usedTime / value remainingTime = max(0, int(totalTime - usedTime)) hrs = remainingTime // 3600 mins = (remainingTime % 3600) // 60 secs = remainingTime % 60 if hrs > 0: text = "{}:{:02}:{:02}".format(hrs, mins, secs) else: text = "{}:{}:{:02}".format(hrs, mins, secs) self.setWindowTitle("{} ({:d}%, ETA: {})" .format(self.captionTitle, int(value), text)) else: self.setWindowTitle(self.captionTitle + " (0% complete)") if old != value: self.progressBarValueChanged.emit(value) if processEvents is not None and processEvents is not False: qApp.processEvents(processEvents)
def progressBarFinished(self, processEvents=QEventLoop.AllEvents): """ Stop the widget's progress (i.e hide the progress bar). .. note:: This method will by default call `QApplication.processEvents` with `processEvents`. To suppress this behavior pass ``processEvents=None``. :param processEvents: Process events flag :type processEvents: `QEventLoop.ProcessEventsFlags` or `None` """ self.setWindowTitle(self.captionTitle) if self.__progressState != 0: self.__progressState = 0 self.processingStateChanged.emit(0) if processEvents is not None and processEvents is not False: qApp.processEvents(processEvents)
def test_exposeObject(self): test = self OBJ = dict(a=[1, 2], b='c') class Bridge(QObject): @pyqtSlot('QVariantMap') def check_object(self, obj): nonlocal test, done, OBJ done = True test.assertEqual(obj, OBJ) w = WebviewWidget(bridge=Bridge()) w.setUrl(SOME_URL) w.exposeObject('obj', OBJ) w.evalJS('''pybridge.check_object(window.obj);''') done = False while not done: qApp.processEvents() self.assertRaises(ValueError, w.exposeObject, 'obj', QDialog())
def progressBarFinished(self, processEvents=None): """ Stop the widget's progress (i.e hide the progress bar). Parameters ---------- value : float Progress value increment. processEvents : Optional[QEventLoop.ProcessEventsFlags] If present then `QApplication.processEvents(processEvents)` will be called. Passing any value here is highly discouraged. It is up to the client to handle the consequences of such action. .. versionchanged:: 3.4.2 Deprecated and changed default `processEvents` value. """ self.setWindowTitle(self.captionTitle) if self.__progressState != 0: self.__progressState = 0 self.processingStateChanged.emit(0) if processEvents is not None and processEvents is not False: qApp.processEvents(processEvents)
def progressBarSet(self, value, processEvents=None): self.setWindowTitle(self.captionTitle + " Evaluated {} permutations".format(value)) if processEvents is not None and processEvents is not False: qApp.processEvents(processEvents)
def html(self): self.page().toHtml(lambda html: setattr(self, '_html', html)) while self._html is _NOTSET: qApp.processEvents(QEventLoop.ExcludeUserInputEvents) html, self._html = self._html, _NOTSET return html
def wait(until: callable, timeout=5000): started = time.clock() while not until(): qApp.processEvents(QEventLoop.ExcludeUserInputEvents) if (time.clock() - started) * 1000 > timeout: raise TimeoutError()
def _update_positions(self, positions, _): for node, pos in zip(self.nodes, positions * self.scaleFactor): node.setPos(*pos) qApp.processEvents()
def find_rules(self): if self.data is None or not len(self.data): return if self._is_running: self._is_running = False return self.button.button.setText('Cancel') self._is_running = True data = self.data self.table.model().clear() n_examples = len(data) NumericItem = self.NumericItem StandardItem = self.StandardItem filterSearch = self.filterSearch itemsetMin = self.filterAntecedentMin + self.filterConsequentMin itemsetMax = self.filterAntecedentMax + self.filterConsequentMax isSizeMatch = self.isSizeMatch isRegexMatch = self.isRegexMatch X, mapping = OneHot.encode(data, self.classify) self.Error.need_discrete_data.clear() if X is None: self.Error.need_discrete_data() self.onehot_mapping = mapping ITEM_FMT = '{}' if issparse(data.X) else '{}={}' names = { item: ('{}={}' if var is data.domain.class_var else ITEM_FMT).format( var.name, val) for item, var, val in OneHot.decode(mapping, data, mapping) } # Items that consequent must include if classifying class_items = { item for item, var, val in OneHot.decode(mapping, data, mapping) if var is data.domain.class_var } if self.classify else set() assert bool(class_items) == bool(self.classify) model = QStandardItemModel(self.table) for col, (label, tooltip) in enumerate([ ("Supp", "Support"), ("Conf", "Confidence (support / antecedent support)"), ("Covr", "Coverage (antecedent support / number of examples)"), ("Strg", "Strength (consequent support / antecedent support)"), ("Lift", "Lift (number of examples * confidence / consequent support)"), ("Levr", "Leverage ((support * number of examples - antecedent support * consequent support) / (number of examples)²)" ), ("Antecedent", None), ("", None), ("Consequent", None) ]): item = QStandardItem(label) item.setToolTip(tooltip) model.setHorizontalHeaderItem(col, item) #~ # Aggregate rules by common (support,confidence) for scatterplot #~ scatter_agg = defaultdict(list) # Find itemsets nRules = 0 itemsets = {} ARROW_ITEM = StandardItem('→') ARROW_ITEM.setTextAlignment(Qt.AlignCenter) with self.progressBar(self.maxRules + 1) as progress: for itemset, support in frequent_itemsets(X, self.minSupport / 100): itemsets[itemset] = support if class_items and not class_items & itemset: continue # Filter itemset by joined filters before descending into it itemset_str = ' '.join(names[i] for i in itemset) if (filterSearch and (len(itemset) < itemsetMin or itemsetMax < len(itemset) or not isRegexMatch(itemset_str, itemset_str))): continue for rule in association_rules(itemsets, self.minConfidence / 100, itemset): left, right, support, confidence = rule if class_items and right - class_items: continue if filterSearch and not isSizeMatch(len(left), len(right)): continue left_str = ', '.join(names[i] for i in sorted(left)) right_str = ', '.join(names[i] for i in sorted(right)) if filterSearch and not isRegexMatch(left_str, right_str): continue # All filters matched, calculate stats and add table row _, _, _, _, coverage, strength, lift, leverage = next( rules_stats((rule, ), itemsets, n_examples)) support_item = NumericItem(support / n_examples) # Set row data on first column support_item.setData( (itemset - class_items, class_items and (class_items & itemset).pop()), self.ROW_DATA_ROLE) left_item = StandardItem(left_str, len(left)) left_item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter) model.appendRow([ support_item, NumericItem(confidence), NumericItem(coverage), NumericItem(strength), NumericItem(lift), NumericItem(leverage), left_item, ARROW_ITEM.clone(), StandardItem(right_str, len(right)) ]) #~ scatter_agg[(round(support / n_examples, 2), round(confidence, 2))].append((left, right)) nRules += 1 progress.advance() if not self._is_running or nRules >= self.maxRules: break qApp.processEvents() if not self._is_running or nRules >= self.maxRules: break # Populate the TableView table = self.table table.setHidden(True) table.setSortingEnabled(False) proxy_model = self.proxy_model proxy_model.setSourceModel(model) table.setModel(proxy_model) for i in range(model.columnCount()): table.resizeColumnToContents(i) table.setSortingEnabled(True) table.setHidden(False) self.button.button.setText('Find Rules') self.nRules = nRules self.nFilteredRules = proxy_model.rowCount( ) # TODO: continue; also add in owitemsets self.nSelectedRules = 0 self.nSelectedExamples = 0 self._is_running = False
def _update_positions(self, positions): for node, pos in zip(self.nodes, positions * 300): node.setPos(*pos) qApp.processEvents()
def html(self): self.page().toHtml(lambda html: setattr(self, '_html', html)) while self._html is _NOTSET and not sip.isdeleted(self): qApp.processEvents(QEventLoop.ExcludeUserInputEvents) html, self._html = self._html, _NOTSET return html
def find_itemsets(self): if self.data is None or not len(self.data): return if self._is_running: self._is_running = False return self._is_running = True self.button.button.setText('Cancel') data = self.data self.tree.clear() self.tree.setUpdatesEnabled(False) self.tree.blockSignals(True) class ItemDict(dict): def __init__(self, item): self.item = item top = ItemDict(self.tree.invisibleRootItem()) X, mapping = OneHot.encode(data) self.Error.need_discrete_data.clear() if X is None: self.Error.need_discrete_data() self.onehot_mapping = mapping ITEM_FMT = '{}' if issparse(data.X) else '{}={}' names = { item: ITEM_FMT.format(var.name, val) for item, var, val in OneHot.decode(mapping.keys(), data, mapping) } nItemsets = 0 filterSearch = self.filterSearch filterMinItems, filterMaxItems = self.filterMinItems, self.filterMaxItems isRegexMatch = self.isRegexMatch # Find itemsets and populate the TreeView with self.progressBar(self.maxItemsets + 1) as progress: for itemset, support in frequent_itemsets(X, self.minSupport / 100): if filterSearch and not filterMinItems <= len( itemset) <= filterMaxItems: continue parent = top first_new_item = None itemset_matches_filter = False for item in sorted(itemset): name = names[item] if filterSearch and not itemset_matches_filter: itemset_matches_filter = isRegexMatch(name) child = parent.get(name) if child is None: try: wi = self.TreeWidgetItem(parent.item, [ name, str(support), '{:.4g}'.format( 100 * support / len(data)) ]) except RuntimeError: # FIXME: When autoFind was in effect and the support # slider was moved, this line excepted with: # RuntimeError: wrapped C/C++ object of type # TreeWidgetItem has been deleted return wi.setData(0, self.ITEM_DATA_ROLE, item) child = parent[name] = ItemDict(wi) if first_new_item is None: first_new_item = (parent, name) parent = child if filterSearch and not itemset_matches_filter: parent, name = first_new_item parent.item.removeChild(parent[name].item) del parent[name].item del parent[name] else: nItemsets += 1 progress.advance() if not self._is_running or nItemsets >= self.maxItemsets: break qApp.processEvents() if not filterSearch: self.filter_change() self.nItemsets = nItemsets self.nSelectedItemsets = 0 self.nSelectedExamples = 0 self.tree.expandAll() for i in range(self.tree.columnCount()): self.tree.resizeColumnToContents(i) self.tree.setUpdatesEnabled(True) self.tree.blockSignals(False) self._is_running = False self.button.button.setText('Find Itemsets')