class UIWellPlotPG(QtCore.QObject): def setupUI(self): self.mainWidget = WellPlotWidget() vBox = QtGui.QVBoxLayout() self.splitter = QSplitter(QtCore.Qt.Vertical) self.headerScrollArea = QScrollArea() self.headerScrollArea.setVerticalScrollBarPolicy( QtCore.Qt.ScrollBarAlwaysOn) self.headerScrollArea.setHorizontalScrollBarPolicy( QtCore.Qt.ScrollBarAlwaysOff) self.headerScrollArea.setWidgetResizable(False) self.scrollArea = QScrollArea() self.scrollArea.setVerticalScrollBarPolicy( QtCore.Qt.ScrollBarAlwaysOff) self.scrollArea.setHorizontalScrollBarPolicy( QtCore.Qt.ScrollBarAlwaysOn) #Needs to be true to allow widget in scroll area size to change self.scrollArea.setWidgetResizable(True) #self.scrollArea.setWidget(self.dataWidget) #see http://stackoverflow.com/questions/29583927/pyqt-qscrollarea-within-qscrollarea/29584939#29584939 self.scrollArea.horizontalScrollBar().valueChanged.connect( self.headerScrollArea.horizontalScrollBar().setValue) self.splitter.addWidget(self.headerScrollArea) self.splitter.addWidget(self.scrollArea) #test hBox = QtGui.QHBoxLayout() #set parent so can access it for widget sizing self.scaleWidget = QWidget() self.scaleWidgetLayout = QtGui.QVBoxLayout() self.scaleWidget.setLayout(self.scaleWidgetLayout) self.scaleWidget.setMinimumWidth(30) hBox.addWidget(self.splitter, 1) hBox.addWidget(self.scaleWidget) self.mainWidget.setLayout(hBox) #end test '''
def selectFile(): global path path = QFileDialog.getOpenFileName() ui.fileUploadText.setText(path + ' uploaded.') file = open(path) counts = defaultdict(int) for line in file: list = line.split(',') counts[list[0]] += 1 counts = OrderedDict(sorted(counts.items())) #Create the plot plot.bar(range(len(counts)), counts.values(), align='center') plot.xticks(range(len(counts)), counts.keys()) fig = plot.gcf() fig.set_size_inches(24, 8) fig.suptitle('Age distribution', fontsize=18) plot.xlabel('Age', fontsize=18) plot.ylabel('Number of records', fontsize=18) fig.savefig(str(path) + 'plot.png', dpi=60) outputGraph = str(path) + 'plot.png' pixMap = QtGui.QPixmap(outputGraph) ui.inputDataLabel.setPixmap(pixMap) scrollArea = QScrollArea() scrollArea.setWidgetResizable(True) scrollArea.setWidget(ui.inputDataLabel) scrollArea.setFixedHeight(500) scrollArea.horizontalScrollBar().setValue( scrollArea.horizontalScrollBar().value() + 100) hLayout = QtGui.QHBoxLayout() hLayout.addWidget(scrollArea) ui.uploadTab.setLayout(hLayout)
def selectFile(): global path path = QFileDialog.getOpenFileName() ui.fileUploadText.setText(path + ' uploaded.') file = open(path) counts = defaultdict(int) for line in file: list = line.split(',') counts[list[0]] += 1 counts = OrderedDict(sorted(counts.items())) #Create the plot plot.bar(range(len(counts)), counts.values(), align='center') plot.xticks(range(len(counts)), counts.keys()) fig = plot.gcf() fig.set_size_inches(24, 8) fig.suptitle('Age distribution', fontsize=18) plot.xlabel('Age', fontsize=18) plot.ylabel('Number of records', fontsize=18) fig.savefig(str(path) + 'plot.png', dpi=60) outputGraph = str(path) + 'plot.png' pixMap = QtGui.QPixmap(outputGraph) ui.inputDataLabel.setPixmap(pixMap) scrollArea = QScrollArea() scrollArea.setWidgetResizable(True) scrollArea.setWidget(ui.inputDataLabel) scrollArea.setFixedHeight(500) scrollArea.horizontalScrollBar().setValue(scrollArea.horizontalScrollBar().value() + 100); hLayout = QtGui.QHBoxLayout() hLayout.addWidget(scrollArea) ui.uploadTab.setLayout(hLayout)
class ScrollArea(QFrame): def __init__(self, parent=None, horizontal=True, vertical=True, color=None): QFrame.__init__(self, parent) self.scrollarea = QScrollArea(self) self.scrollarea.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.scrollarea.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.v_pad = scrollbar.ScrollPad(self) self.v_pad.setOrientation(Qt.Vertical) self.v_pad.setMinimumWidth(30) if color: self.v_pad.set_color(color) self.h_pad = scrollbar.ScrollPad(self) self.h_pad.setOrientation(Qt.Horizontal) self.h_pad.setMinimumHeight(30) if color: self.h_pad.set_color(color) QObject.connect(self.v_pad, SIGNAL('valueChanged(int)'), self.v_scroll) QObject.connect(self.h_pad, SIGNAL('valueChanged(int)'), self.h_scroll) for name in ('setWidgetResizable',): setattr(self, name, getattr(self.scrollarea, name)) self.horizontal(horizontal) self.vertical(vertical) Layout1 = QVBoxLayout(self) Layout1.setSpacing(0) Layout1.setMargin(0) Layout2 = QHBoxLayout() Layout2.setSpacing(0) Layout2.setMargin(0) Layout2.addWidget(self.scrollarea) Layout2.addWidget(self.v_pad) Layout1.addLayout(Layout2) Layout1.addWidget(self.h_pad) def setWidget(self, widget): self.scrollarea.setWidget(widget) def horizontal(self, on): if on: self.h_pad.show() else: self.h_pad.hide() def vertical(self, on): if on: self.v_pad.show() else: self.v_pad.hide() def v_scroll(self, value): sb = self.v_pad target_sb = self.scrollarea.verticalScrollBar() percent = sb.value() / float(sb.maximum()) target_sb.setValue(int(percent * target_sb.maximum())) def h_scroll(self, value): sb = self.h_pad target_sb = self.scrollarea.horizontalScrollBar() percent = sb.value() / float(sb.maximum()) target_sb.setValue(int(percent * target_sb.maximum()))
def runDecisionTree(): #Todo: try not to use global variables global path global alteredPath fileName = '' if (alteredPath): fileName = alteredPath elif (path): fileName = path else: print('Must upload file first') return x = [] y = [] file = open(fileName) for line in file: line = line.rstrip() features = [] classification = [] list = line.split(',') features = list[0:-1] if (features and features[0].strip()): x.append(features) classification = [list[-1]] if (classification and classification[0].strip()): y.append(classification) ui.progressBar.setValue(25) samples = [dict(enumerate(sample)) for sample in x] # turn list of dicts into a numpy array vect = DictVectorizer(sparse=False) x = vect.fit_transform(samples) ui.progressBar.setValue(50) clf = tree.DecisionTreeClassifier() clf = clf.fit(x, y) with open(fileName + '.dot', 'w') as f: f = tree.export_graphviz(clf, out_file=f) graph = pydot.graph_from_dot_file(fileName + '.dot') graph.write_png(fileName + '.png') global outputGraph outputGraph = fileName + '.png' ui.progressBar.setValue(75) pixMap = QtGui.QPixmap(outputGraph) #ui.outputLabel.setPixmap(pixMap.scaled(ui.outputTab.size(), QtCore.Qt.KeepAspectRatio)) ui.outputLabel.setPixmap(pixMap) #ui.outputLabel.setScaledContents(True) scrollArea = QScrollArea() scrollArea.setWidgetResizable(True) scrollArea.setWidget(ui.outputLabel) scrollArea.setFixedHeight(525) scrollArea.horizontalScrollBar().setValue( scrollArea.horizontalScrollBar().value() + 3400) hLayout = QtGui.QHBoxLayout() hLayout.addWidget(scrollArea) ui.outputTab.setLayout(hLayout) ui.progressBar.setValue(100) ui.algorithmText.setText('Built decision tree')
def runDecisionTree(): #Todo: try not to use global variables global path global alteredPath fileName = '' if (alteredPath): fileName = alteredPath elif (path): fileName = path else: print('Must upload file first') return x = [] y = [] file = open(fileName) for line in file: line = line.rstrip() features = [] classification = [] list = line.split(',') features = list[0:-1] if (features and features[0].strip()): x.append(features) classification = [list[-1]] if (classification and classification[0].strip()): y.append(classification) ui.progressBar.setValue(25) samples = [dict(enumerate(sample)) for sample in x] # turn list of dicts into a numpy array vect = DictVectorizer(sparse=False) x = vect.fit_transform(samples) ui.progressBar.setValue(50) clf = tree.DecisionTreeClassifier() clf = clf.fit(x, y) with open(fileName + '.dot', 'w') as f: f = tree.export_graphviz(clf, out_file=f) graph = pydot.graph_from_dot_file(fileName + '.dot') graph.write_png(fileName + '.png') global outputGraph outputGraph = fileName + '.png' ui.progressBar.setValue(75) pixMap = QtGui.QPixmap(outputGraph) #ui.outputLabel.setPixmap(pixMap.scaled(ui.outputTab.size(), QtCore.Qt.KeepAspectRatio)) ui.outputLabel.setPixmap(pixMap) #ui.outputLabel.setScaledContents(True) scrollArea = QScrollArea() scrollArea.setWidgetResizable(True) scrollArea.setWidget(ui.outputLabel) scrollArea.setFixedHeight(525) scrollArea.horizontalScrollBar().setValue(scrollArea.horizontalScrollBar().value() + 3400); hLayout = QtGui.QHBoxLayout() hLayout.addWidget(scrollArea) ui.outputTab.setLayout(hLayout) ui.progressBar.setValue(100) ui.algorithmText.setText('Built decision tree')
class XNavigationEdit(XLineEdit): """ """ navigationChanged = Signal() __designer_icon__ = projexui.resources.find('img/ui/navigate.png') def __init__( self, parent = None ): super(XNavigationEdit, self).__init__( parent ) # define custom properties self._separator = '/' self._partsEditingEnabled = True self._originalText = '' self._scrollWidget = QScrollArea(self) self._partsWidget = QWidget(self._scrollWidget) self._buttonGroup = QButtonGroup(self) self._scrollAmount = 0 self._navigationModel = None # create the completer tree palette = self.palette() palette.setColor(palette.Base, palette.color(palette.Window)) palette.setColor(palette.Text, palette.color(palette.WindowText)) bg = palette.color(palette.Highlight) abg = bg.darker(115) fg = palette.color(palette.HighlightedText) sbg = 'rgb(%s, %s, %s)' % (bg.red(), bg.green(), bg.blue()) sabg = 'rgb(%s, %s, %s)' % (abg.red(), abg.green(), abg.blue()) sfg = 'rgb(%s, %s, %s)' % (fg.red(), fg.green(), fg.blue()) style = 'QTreeView::item:hover { '\ ' color: %s;'\ ' background: qlineargradient(x1:0,'\ ' y1:0,'\ ' x2:0,'\ ' y2:1,'\ ' stop: 0 %s,'\ ' stop: 1 %s);'\ '}' % (sfg, sbg, sabg) self._completerTree = QTreeView(self) self._completerTree.setStyleSheet(style) self._completerTree.header().hide() self._completerTree.setFrameShape(QTreeView.Box) self._completerTree.setFrameShadow(QTreeView.Plain) self._completerTree.setPalette(palette) self._completerTree.setEditTriggers(QTreeView.NoEditTriggers) self._completerTree.setWindowFlags(Qt.Popup) self._completerTree.installEventFilter(self) self._completerTree.setRootIsDecorated(False) self._completerTree.setItemsExpandable(False) # create the editing widget layout = QHBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) layout.addStretch() self._scrollWidget.setFrameShape( QScrollArea.NoFrame ) self._scrollWidget.setFocusPolicy(Qt.NoFocus) self._scrollWidget.setWidget(self._partsWidget) self._scrollWidget.setWidgetResizable(True) self._scrollWidget.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self._scrollWidget.setAlignment(Qt.AlignTop | Qt.AlignRight) self._scrollWidget.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self._scrollWidget.setContentsMargins(0, 0, 0, 0) self._scrollWidget.setViewportMargins(0, 0, 0, 0) self._scrollWidget.move(2, 2) self._partsWidget.setLayout(layout) self._partsWidget.setCursor(Qt.ArrowCursor) self._partsWidget.setAutoFillBackground(True) self._partsWidget.setFixedHeight(self.height() - 12) palette = self._partsWidget.palette() palette.setColor(palette.Background, palette.color(palette.Base)) self._partsWidget.setPalette(palette) # create connections self._completerTree.clicked.connect( self.navigateToIndex ) self._buttonGroup.buttonClicked.connect( self.handleButtonClick ) self._scrollWidget.horizontalScrollBar().valueChanged.connect( self.scrollParts ) def acceptEdit( self ): """ Accepts the current text and rebuilds the parts widget. """ if ( self._partsWidget.isVisible() ): return False use_completion = self.completer().popup().isVisible() completion = self.completer().currentCompletion() self._completerTree.hide() self.completer().popup().hide() if ( use_completion ): self.setText(completion) else: self.rebuild() return True def cancelEdit( self ): """ Rejects the current edit and shows the parts widget. """ if ( self._partsWidget.isVisible() ): return False self._completerTree.hide() self.completer().popup().hide() self.setText(self._originalText) return True def currentItem( self ): """ Returns the current navigation item from the current path. :return <XNavigationItem> || None """ model = self.navigationModel() if ( not model ): return None return model.itemByPath(self.text()) def eventFilter( self, object, event ): """ Filters the events for the inputed object through this edit. :param object | <QObject> event | <QEvent> :return <bool> | consumed """ if ( event.type() == event.KeyPress ): if ( event.key() == Qt.Key_Escape ): self._completerTree.hide() self.completer().popup().hide() self.cancelEdit() elif ( event.key() in (Qt.Key_Return, Qt.Key_Enter) ): self.acceptEdit() return True elif ( event.key() == Qt.Key_Tab ): if ( self.completer().popup().isVisible() ): text = str(self.completer().currentCompletion()) super(XNavigationEdit, self).setText(text) return True else: self.acceptEdit() return False elif ( event.type() == event.MouseButtonPress ): if ( not self._completerTree.rect().contains(event.pos()) ): self._completerTree.hide() self.completer().popup().hide() self.cancelEdit() return False def focusOutEvent( self, event ): """ Overloads the focus out event to cancel editing when the widget loses focus. :param event | <QFocusEvent> """ super(XNavigationEdit, self).focusOutEvent(event) self.cancelEdit() def handleButtonClick( self, button ): """ Handle the event when a user clicks on one of the part buttons. :param button | <QToolButton> """ path = button.property('path') is_completer = button.property('is_completer') # popup a completion menu if ( unwrapVariant(is_completer) ): model = self.navigationModel() if ( not model ): return sep = self.separator() path = str(unwrapVariant(path)) item = model.itemByPath(path, includeRoot = True) if ( not item ): return curr_path = str(self.text()).strip(self.separator()) curr_path = curr_path.replace(path, '').strip(self.separator()) child_name = '' if ( curr_path ): child_name = curr_path.split(self.separator())[0] index = model.indexFromItem(item) self._completerTree.move(QCursor.pos()) self._completerTree.setRootIndex(index) self._completerTree.verticalScrollBar().setValue(0) if ( child_name ): child_item = None for i in range(item.rowCount()): child = item.child(i) if ( child.text() == child_name ): child_item = child break if ( child_item ): child_index = model.indexFromItem(child_item) self._completerTree.setCurrentIndex(child_index) self._completerTree.scrollTo(child_index) self._completerTree.show() self._completerTree.setUpdatesEnabled(True) else: self.setText(unwrapVariant(path)) def keyPressEvent( self, event ): """ Overloads the key press event to listen for escape calls to cancel the parts editing. :param event | <QKeyPressEvent> """ if ( self.scrollWidget().isHidden() ): if ( event.key() == Qt.Key_Escape ): self.cancelEdit() return elif ( event.key() in (Qt.Key_Return, Qt.Key_Enter) ): self.acceptEdit() return elif ( event.key() == Qt.Key_A and event.modifiers() == Qt.ControlModifier ): self.startEdit() super(XNavigationEdit, self).keyPressEvent(event) def mouseDoubleClickEvent( self, event ): """ Overloads the system to enable editing when a user double clicks. :param event | <QMouseEvent> """ super(XNavigationEdit, self).mouseDoubleClickEvent(event) self.startEdit() def navigationModel( self ): """ Returns the navigation model linked with this edit. :return <XNavigationModel> || None """ return self._navigationModel def navigateToIndex( self, index ): """ Navigates to the inputed action's path. :param action | <QAction> """ self._completerTree.hide() item = self._navigationModel.itemFromIndex(index) self.setText(self._navigationModel.itemPath(item)) def parts( self ): """ Returns the parts that are used for this system. :return [<str>, ..] """ path = str(self.text()).strip(self.separator()) if ( not path ): return [] return path.split(self.separator()) def partsWidget( self ): """ Returns the widget that contains the parts system. :return <QScrollArea> """ return self._partsWidget def startEdit( self ): """ Rebuilds the pathing based on the parts. """ self._originalText = self.text() self.scrollWidget().hide() self.setFocus() self.selectAll() def rebuild( self ): """ Rebuilds the parts widget with the latest text. """ navitem = self.currentItem() if ( navitem ): navitem.initialize() self.setUpdatesEnabled(False) self.scrollWidget().show() self._originalText = '' partsw = self.partsWidget() for button in self._buttonGroup.buttons(): self._buttonGroup.removeButton(button) button.close() button.setParent(None) button.deleteLater() # create the root button layout = partsw.layout() parts = self.parts() button = QToolButton(partsw) button.setAutoRaise(True) button.setMaximumWidth(12) button.setArrowType(Qt.RightArrow) button.setProperty('path', wrapVariant('')) button.setProperty('is_completer', wrapVariant(True)) last_button = button self._buttonGroup.addButton(button) layout.insertWidget(0, button) # check to see if we have a navigation model setup if ( self._navigationModel ): last_item = self._navigationModel.itemByPath(self.text()) show_last = last_item and last_item.rowCount() > 0 else: show_last = False # load the navigation system count = len(parts) for i, part in enumerate(parts): path = self.separator().join(parts[:i+1]) button = QToolButton(partsw) button.setAutoRaise(True) button.setText(part) if ( self._navigationModel ): item = self._navigationModel.itemByPath(path) if ( item ): button.setIcon(item.icon()) button.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) button.setProperty('path', wrapVariant(path)) button.setProperty('is_completer', wrapVariant(False)) self._buttonGroup.addButton(button) layout.insertWidget((i * 2) + 1, button) # determine if we should show the final button if ( show_last or i < (count - 1) ): button = QToolButton(partsw) button.setAutoRaise(True) button.setMaximumWidth(12) button.setArrowType(Qt.RightArrow) button.setProperty('path', wrapVariant(path)) button.setProperty('is_completer', wrapVariant(True)) self._buttonGroup.addButton(button) layout.insertWidget((i * 2) + 2, button) last_button = button if ( self.scrollWidget().width() < partsw.width() ): self.scrollParts(partsw.width() - self.scrollWidget().width()) self.setUpdatesEnabled(True) self.navigationChanged.emit() def resizeEvent( self, event ): """ Resizes the current widget and its parts widget. :param event | <QResizeEvent> """ super(XNavigationEdit, self).resizeEvent(event) w = self.width() h = self.height() self._scrollWidget.resize(w - 4, h - 4) if ( self._scrollWidget.width() < self._partsWidget.width() ): self.scrollParts( self._partsWidget.width() - self._scrollWidget.width() ) def scrollParts( self, amount ): """ Scrolls the parts to offset the scrolling amount. :param amount | <int> """ change = self._scrollAmount - amount self._partsWidget.scroll(change, 0) self._scrollAmount = amount def scrollWidget( self ): """ Returns the scrolling widget. :return <QScrollArea> """ return self._scrollWidget def separator( self ): """ Returns the separation character that is used for this edit. :return <str> """ return self._separator def setTopLevelItems( self, items ): """ Initializes the navigation system to start with the inputed root \ item. :param item | <XNavigationItem> """ if ( not self._navigationModel ): self.setNavigationModel(XNavigationModel(self)) self._navigationModel.setTopLevelItems(items) def setNavigationModel( self, model ): """ Sets the navigation model for this edit. :param model | <XNavigationModel> """ self._navigationModel = model self._completerTree.setModel(model) if ( model ): model.setSeparator(self.separator()) completer = XNavigationCompleter(model, self) self.setCompleter(completer) completer.popup().installEventFilter(self) else: self.setCompleter(None) self.rebuild() def setParts( self, parts ): """ Sets the path for this edit widget by providing the parts to the path. :param parts | [<str>, ..] """ self.setText(self.separator().join(map(str, parts))) def setSeparator( self, separator ): """ Sets the separator to the inputed character. :param separator | <str> """ self._separator = separator if ( self._navigationModel ): self._navigationModel.setSeparator(separator) self.rebuild() def setText( self, text ): """ Sets the text for this edit to the inputed text. :param text | <str> """ super(XNavigationEdit, self).setText(text) self.scrollWidget().show() if ( text == '' or self._originalText != text ): self.rebuild()
class WellPlotMPL(QtCore.QObject): def __init__(self, logs, well, logSet=None, parent=None): super(WellPlotMPL, self).__init__(parent) self._well = well self._logSet = logSet self._logs = logs self.plots = [] self.canvas = None self.depthPlot = None self.headerPlot = None centralWidget = centraltabwidget.CentralTabWidget() #id(self) returns the 'hash' of this object self.uid = (centralWidget.count(), id(self)) self.wellPlotSignals = WellPlotSignals() self.setupUI() self.createTabView() self.connectSlots() self.plotMultiLogs() self.setSplitterStretch() self.createToolWidget() def setupUI(self): self.mainWidget = WellPlotWidget() vBox = QtGui.QVBoxLayout() self.mainWidget.setLayout(vBox) self.headerWidget = QWidget() self.headerLayout = QtGui.QHBoxLayout() self.headerWidget.setLayout(self.headerLayout) self.dataWidget = QWidget() self.dataLayout = QtGui.QHBoxLayout() self.dataWidget.setLayout(self.dataLayout) #if don't set a minimum, get matplotlib error when is very small self.dataWidget.setMinimumHeight(self.getMinimumVerticalHeight()) self.splitter = QSplitter(QtCore.Qt.Vertical) self.headerScrollArea = QScrollArea() self.headerScrollArea.setVerticalScrollBarPolicy( QtCore.Qt.ScrollBarAlwaysOn) self.headerScrollArea.setHorizontalScrollBarPolicy( QtCore.Qt.ScrollBarAlwaysOff) self.headerScrollArea.setWidgetResizable(False) self.headerScrollArea.setWidget(self.headerWidget) self.scrollArea = QScrollArea() self.scrollArea.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn) self.scrollArea.setHorizontalScrollBarPolicy( QtCore.Qt.ScrollBarAlwaysOn) self.scrollArea.setWidgetResizable(False) self.scrollArea.setWidget(self.dataWidget) #see http://stackoverflow.com/questions/29583927/pyqt-qscrollarea-within-qscrollarea/29584939#29584939 self.scrollArea.horizontalScrollBar().valueChanged.connect( self.headerScrollArea.horizontalScrollBar().setValue) self.splitter.addWidget(self.headerScrollArea) self.splitter.addWidget(self.scrollArea) self.splitter.setStretchFactor(1, 10) vBox.addWidget(self.splitter) def getMinimumVerticalHeight(self): screenRect = QtGui.QDesktopWidget().screenGeometry() #need to set a minimum size otherwise get matplotlib error when rezizing to too small twentythOfScreen = int(round(screenRect.width() / 20)) return twentythOfScreen def plotMultiLogs(self): logPlotModelAccess = WellPlotModelAccess() logPlotData = logPlotModelAccess.createWellPlotData(self._logs) self.createCanvas(logPlotData) self.plotHeaderFields(logPlotData) def createCanvas(self, logPlotData): logger.debug(">>createCanvas()") #test for subPlotData in logPlotData.sub_plots: logger.debug( "--createCanvas() plot_index:{0} track_width:{1} track_gap:{2}" .format(subPlotData.plot_index, subPlotData.track_width, subPlotData.track_gap)) for log in subPlotData._logs: logger.debug("--createCanvas id:{0}, name:{1}".format( log.id, log.name)) #end test if len(logPlotData.sub_plots) > 0: WidgetUtils.removeWidgets(self.dataLayout) #test #time.sleep(1) # delays for 1 second #end test #There may be a better way to link plots with the toolbar self.mainWidget.setLogPlotData(logPlotData) self.depthPlot = DepthAxis(logPlotData, self.dataWidget) self.dataLayout.addWidget(self.depthPlot) self.canvas = MultiLogCanvas(logPlotData, self.dataWidget) self.canvas.setAutoFillBackground(True) self.dataLayout.addWidget(self.canvas) spacerItem = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) self.dataLayout.addItem(spacerItem) else: logger.error("--plotMultiLogs() Error: no logs to plot") if AppSettings.isDebugMode: raise ValueError def plotHeaderFields(self, logPlotData): logger.debug(">>plotMultiLogs()") WidgetUtils.removeWidgets(self.headerLayout) self.headerPlot = HeaderPlotMPL(depthPlot=self.depthPlot, mainPlot=self.canvas, logPlotData=logPlotData) self.headerLayout.addWidget(self.headerPlot) def setSplitterStretch(self): #Minimum size is required for the QScrollArea.setWidgetResizable(False) setting''' headerW = self.headerPlot.width() headerH = self.headerPlot.height() self.headerWidget.setMinimumSize(headerW, headerH) #test (totalW, dataH) = MplUtils.calcFigCanvasWidthHeight(self.canvas.figure) #end test dWidth, dHeight = self.canvas.figure.canvas.get_width_height() self.dataWidget.setMinimumSize(dWidth, dHeight) def connectSlots(self): logger.debug(">>connectSlots") self.wellPlotSignals.logPlotSettingsModified.connect(self.replotLogs) @pyqtSlot(WellPlotData) def replotLogs(self, logPlotData): logger.debug(">>replotLogs len(logPlotData.sub_plots): " + str(len(logPlotData.sub_plots))) #check uid's before accessing them, where uid is a (number widgets in central widget, id) tuple logger.debug( "--replotLogs() len(self.uid):{0}, len(logPlotData.uid):{1}". format(len(self.uid), len(logPlotData.uid))) if (len(self.uid) == 2) and (len(logPlotData.uid) == 2): #ensure this object is associated with the plot object if self.uid[0] == logPlotData.uid[0] and self.uid[ 1] == logPlotData.uid[1]: logger.debug("--replotLogs() match found uid: " + str(self.uid[0])) self.createCanvas(logPlotData) self.plotHeaderFields(logPlotData) def spacePlots(self, bottomLayout): rightSpacer = QtGui.QWidget() rightSpacer.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding) #topLayout.addWidget(rightSpacer) bottomLayout.addWidget(rightSpacer) #self.topWidget.setLayout(topLayout) self.dataWidget.setLayout(bottomLayout) def createTabView(self): centralWidget = centraltabwidget.CentralTabWidget() self.mainWidget.setData(self.uid) #centralWidget.addTab(self.scrollArea, "Well plot "+str(self.uid[0])) centralWidget.addTab(self.mainWidget, "Well plot " + str(self.uid[0])) def createToolWidget(self): if len(self._logs) > 0: toolbar = logsettingstoolbar.LogSettingsToolbar() toolbar.setData(self._well, self._logSet, self.canvas, self.depthPlot, self.headerPlot) toolbar.emitShowToolbarSignal() logger.debug("<<createToolWidget() toolbar created")
class pngDisplay(QWidget): def __init__(self, parent=None): super(pngDisplay, self).__init__(parent) #self.profiler = cProfile.Profile() # create the label that displays the image - cribbing from the Image # Viewer example in the Qt docs. self.imLabel = QLabel() self.imLabel.setBackgroundRole(QPalette.Base) self.imLabel.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored) self.imLabel.setScaledContents(True) # Create a gray field to use when no png is available self.defaultPM = QPixmap(700,900) self.defaultPM.fill(QColor("gray")) # Create a scroll area within which to display our imLabel, this # enables the creation of horizontal and vertical scroll bars, when # the imLabel exceeds the size of the scroll area. self.scarea = QScrollArea() # The following two lines make sure that page up/dn gets through # the scrollarea widget and up to us. self.setFocusPolicy(Qt.ClickFocus) self.scarea.setFocusProxy(self) self.scarea.setBackgroundRole(QPalette.Dark) #self.scarea.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOn) #self.scarea.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn) self.scarea.setWidget(self.imLabel) # create the text label that will have the page number in it self.txLabel = QLabel(u"No image") self.txLabel.setAlignment(Qt.AlignBottom | Qt.AlignHCenter) self.txLabel.setFrameStyle(QFrame.Sunken | QFrame.StyledPanel) # Create a spinbox to set the zoom from 15 to 200 with a label: # (originally a slider, hence the name) self.minZoom = 0.15 self.maxZoom = 2.00 self.zlider = QSpinBox() self.zlider.setRange(int(100*self.minZoom),int(100*self.maxZoom)) # connect the value change signal to a slot to handle it self.connect(self.zlider, SIGNAL("valueChanged(int)"), self.newZoomFactor) # create the to-width and to-height zoom buttons zoomWidthButton = QPushButton(u'to Width') self.connect(zoomWidthButton, SIGNAL("clicked()"), self.zoomToWidth) zoomHeightButton = QPushButton(u'to Height') self.connect(zoomHeightButton, SIGNAL("clicked()"), self.zoomToHeight) # Make an hbox to contain the spinbox and two pushbuttons, with # stretch on left and right to center the group. zlabel = QLabel( u'&Zoom ' + str(self.zlider.minimum()) + '-' + str(self.zlider.maximum()) + '%') zlabel.setBuddy(self.zlider) zhbox = QHBoxLayout() zhbox.addStretch(1) zhbox.addWidget(zlabel,0,Qt.AlignLeft) zhbox.addWidget(self.zlider,0) zhbox.addStretch(1) zhbox.addWidget(zoomWidthButton) zhbox.addWidget(zoomHeightButton) zhbox.addStretch(1) # With all the pieces in hand, create our layout basically a # vertical stack: scroll area, label, slider box. vbox = QVBoxLayout() # the image gets a high stretch and default alignment, the text # label hugs the bottom and doesn't stretch at all. vbox.addWidget(self.txLabel,0,Qt.AlignBottom) vbox.addWidget(self.scarea,10) vbox.addLayout(zhbox,0) self.setLayout(vbox) # Initialize assuming no book is open. self.ready = False # nothing to display # Recover the last-set zoom factor from the settings object, default 1.0 qv = IMC.settings.value("pngs/zoomFactor",QVariant(1.0)) self.zoomFactor = qv.toFloat()[0] # The following causes entry into newZoomFactor, below, which tests # self.ready, hence the latter has to be assigned-to first. self.zlider.setValue(int(self.zoomFactor*100)) self.clear() # local subroutine to initialize our contents for an empty edit. # called from _init_ and from newPosition when we discover the file # has been cleared on us. Don't reset the zoomFactor, leave it as # the user last set it. def clear(self): # Clear the page name, used by pqNotes IMC.currentImageNumber = None # will be name of last png file e.g. "002" # Clear the page filename, used in our caption label self.lastPage = QString() # last file name e.g. "002.png" # Clear the path to the pngs folder, used to fetch image files self.pngPath = QString() # Clear the index of the last-shown page in the page table # -1 means no page is being displayed. self.lastIndex = -1 # Clear the index of the next page to display, normally same as last self.nextIndex = -1 # Set not-ready to indicate no pngs directory available. self.ready = False # Clear out & release storage of our QImage and QPixmaps self.pixmap = QPixmap() # null pixmap self.image = QImage() self.noImage() # show gray image # Display a blank gray frame and "No Image" below. # Called from clear() above and from showPage when no valid image. def noImage(self) : self.imLabel.setPixmap(self.defaultPM) self.txLabel.setText(u"No image") self.lastIndex = -1 # didn't see a prior page self.nextIndex = -1 # This slot gets the main window's signal shuttingDown. # We save our current zoom factor into IMC.settings. def shuttingDown(self): IMC.settings.setValue("pngs/zoomFactor",QVariant(self.zoomFactor)) # This slot gets pqMain's signal docHasChanged(QString), telling # us that a different document has been loaded. This could be for # a successful File>Open, or a failed File>Open or File>New. # The bookPath is a null QString for File>New, or the full bookPath. # If the latter, we convert that into the path to the pngs folder, # and see if bookPath/pngs is a directory. If so, we set self.ready # to true, indicating it is worthwhile to try opening image files. # At this point the gray image is displayed and previously would remain displayed # until the user moved the cursor in some way, generating cursorPositionChanged. # That's a minor annoyance, to avoid it we will fake that signal now. def newFile(self, bookPath): if not bookPath.isNull(): # this was successful File>Open finf = QFileInfo(bookPath) self.pngPath = finf.absolutePath().append(u"/pngs/") finf = QFileInfo(self.pngPath) if finf.exists() and finf.isDir(): # looking good self.ready = True self.newPosition() else: # We could inform the user we couldn't find a pngs folder, # but you know -- the user is probably already aware of that. self.clear() # just put up the gray default image else: # It was a File>New self.clear() # This function is the slot that is connected to the editor's # cursorPositionChanged signal. Its input is cursor position and # the page table. Its output is to set self.nextIndex to the # desired next image table row, and to call showPage. def newPosition(self): if self.ready : # We have a book and some pngs. Find the position of the higher end # of the current selection. pos = IMC.editWidget.textCursor().selectionEnd() # Get the page table index that matches this position, or -1 # if that is above the first psep, or there is no page data self.nextIndex = IMC.pageTable.getIndex(pos) else :# No file loaded or no pngs folder found. self.nextIndex = -1 if self.nextIndex != self.lastIndex : self.showPage() # Display the page indexed by self.nextIndex. This is called when the cursor # moves to a new page (newPosition, above), or when the PageUp/Dn keys are used, # (keyPressEvent, below) or when the zoom factor changes in any of several ways. def showPage(self): # If self.lastIndex is different from self.nextIndex, the page has # changed, and we need to load a new image. if self.lastIndex != self.nextIndex : self.lastIndex = self.nextIndex # don't come here again until it changes. if self.lastIndex > -1 : # Form the image filename as a Qstring, e.g. "025" and save that for # use by pqNotes: IMC.currentImageNumber = IMC.pageTable.getScan(self.lastIndex) # dbg = unicode(IMC.currentImageNumber) # Form the complete filename by appending ".png" and save as # self.lastPage for use in forming our caption label. self.lastPage = QString(IMC.currentImageNumber).append(QString(u".png")) # dbg = unicode(self.lastPage) # Form the full path to the image. Try to load it as a QImage. pngName = QString(self.pngPath).append(self.lastPage) self.image = QImage(pngName,'PNG') # dbg = unicode(self.image) # dbg = self.image.isNull() # If that successfully loaded an image, make sure it is one byte/pixel. if not self.image.isNull() \ and self.image.format() != QImage.Format_Indexed8 : # It might be Format_Mono (1 bit/pixel) or even Format_RGB32. self.image = self.image.convertToFormat(QImage.Format_Indexed8,Qt.ColorOnly) # Convert the image to a pixmap. If it's null, so is the pixmap. self.pixmap = QPixmap.fromImage(self.image,Qt.ColorOnly) else : IMC.currentImageNumber = QString(u"n.a.") self.lastPage = QString() self.image = QImage() self.pixmap = QPixmap() if not self.pixmap.isNull(): # We successfully found and loaded an image and converted it to pixmap. # Load it in our label for display, set the zoom factor, and the caption. # We do this every time through (even if lastIndex equalled nextIndex) # because the zoomfactor might have changed. self.imLabel.setPixmap(self.pixmap) self.imLabel.resize( self.zoomFactor * self.pixmap.size() ) folio = IMC.pageTable.getDisplay(self.lastIndex) self.txLabel.setText(u"image {0} (folio {1})".format(self.lastPage,folio)) else: # no file was loaded. It's ok if pages are missing self.noImage() # display the gray image. # Catch the signal from the Zoom spinbox with a new value. # Store the new value as a float, and if we have a page, repaint it. def newZoomFactor(self,new_value): self.zoomFactor = new_value / 100 if self.ready : self.showPage() # Catch the click on zoom-to-width and zoom-to height. The job is basically # the same for both. 1: Using the QImage that should be in self.image, # scan the pixels to find the width (height) of the nonwhite area. # 2. Get the ratio of that to our image label's viewport width (height). # 4. Set that ratio as the zoom factor and redraw the image. And finally # 5. Set the scroll position(s) of our scroll area to left-justify the text. # # We get access to the pixels using QImage:bits() which gives us a PyQt4 # "voidptr" that we can index to get byte values. # def zoomToWidth(self): if (not self.ready) or (self.image.isNull()) : return # nothing to do here #self.profiler.enable() #dbg # Query the Color look-up table and build a list of the Green values # corresponding to each possible pixel value. Probably there are just # two colors so colortab is [0,255] but there could be more, depending # on how the PNG was defined, 16 or 32 or even 255 grayscale. colortab = [ int((self.image.color(c) >> 4) & 255) for c in range(self.image.colorCount()) ] ncols = self.image.width() # number of logical pixels across stride = (ncols + 3) & (-4) # number of bytes per scanline nrows = self.image.height() # number of pixels high vptr = self.image.bits() # uchar * bunch-o-pixel-bytes vptr.setsize(stride * nrows) # make the pointer indexable # Scan in from left and right to find the outermost dark spots. # Looking for single pixels yeilds too many false positives, so we # look for three adjacent pixels that sum to less than 32. # Most pages start with many lines of white pixels so in hopes of # establishing the outer edge early, we start at the middle, go to # the end, then do the top half. left_side = int(ncols/2) # leftmost dark spot seen so far # scan from the middle down for r in xrange(int(nrows/2)*stride, (nrows-1)*stride, stride) : pa, pb = 255, 255 # virtual white outside border for c in xrange(left_side): pc = colortab[ ord(vptr[c + r]) ] if (pa + pb + pc) < 32 : # black or dark gray pair left_side = c # new, further-left, left margin break # no need to look further on this line pa = pb pb = pc # scan from the top to the middle, hopefully left_side is small now for r in xrange(0, int(nrows/2)*stride, stride) : pa, pb = 255, 255 # virtual white outside border for c in xrange(left_side): pc = colortab[ ord(vptr[c + r]) ] if (pa + pb + pc) < 32 : # black or dark gray pair left_side = c # new, further-left, left margin break # no need to look further on this line pa = pb pb = pc # Now do the same for the right margin. right_side = int(ncols/2) # rightmost dark spot seen so far for r in xrange(int(nrows/2)*stride, (nrows-1)*stride, stride) : pa, pb = 255, 255 # virtual white outside border for c in xrange(ncols-1,right_side,-1) : pc = colortab[ ord(vptr[c + r]) ] if (pa + pb + pc) < 32 : # black or dark gray pair right_side = c # new, further-right, right margin break pa = pb pb = pc for r in xrange(0, int(nrows/2)*stride, stride) : pa, pb = 255, 255 # virtual white outside border for c in xrange(ncols-1,right_side,-1) : pc = colortab[ ord(vptr[c + r]) ] if (pa + pb + pc) < 32 : # black or dark gray pair right_side = c # new, further-right, right margin break pa = pb pb = pc # The area with color runs from left_side to right_side. How does # that compare to the size of our viewport? Scale to that and redraw. #print('ls {0} rs {1} vp {2}'.format(left_side,right_side,self.scarea.viewport().width())) text_size = right_side - left_side + 2 port_width = self.scarea.viewport().width() self.zoomFactor = max( self.minZoom, min( self.maxZoom, port_width / text_size ) ) # the next line signals newZoomFactor, which calls showPage. self.zlider.setValue(int(100*self.zoomFactor)) # Set the scrollbar to show the page from its left margin. self.scarea.horizontalScrollBar().setValue(int( left_side * self.zoomFactor) ) #self.profiler.disable() #dbg #pstats.Stats(self.profiler).print_stats() # dbg def zoomToHeight(self): if (not self.ready) or (self.image.isNull()) : return # nothing to do here # Query the Color look-up table and build a list of the Green values # corresponding to each possible pixel value. Probably there are just # two colors so colortab is [0,255] but there could be more, depending # on how the PNG was defined, 16 or 32 or even 255 grayscale. colortab = [ int((self.image.color(c) >> 4) & 255) for c in range(self.image.colorCount()) ] ncols = self.image.width() # number of logical pixels across stride = (ncols + 3) & (-4) # number of bytes per scanline nrows = self.image.height() # number of pixels high vptr = self.image.bits() # uchar * bunch-o-pixel-bytes vptr.setsize(stride * nrows) # make the pointer indexable # Scan in from top and bottom to find the outermost rows with # significant pixels. top_side = -1 # The uppermost row with a significant spot of black offset = 0 # vptr index to the first/next pixel row for r in range(nrows) : pa, pb = 255, 255 # virtual white outside border for c in range(ncols) : pc = colortab[ ord(vptr[offset + c]) ] if (pa + pb + pc) < 32 : # black or dark gray triplet top_side = r # that's the row, break # ..so stop scanning pa, pb = pb, pc if top_side >= 0 : # we hit break # ..so don't scan down any further offset += stride # continue to next row # top_side indexes the first row with a significant blot if top_side == -1 : # never found one: an all-white page. bug out. return bottom_side = nrows # The lowest row with a significant blot offset = stride * nrows # vptr index to last/next row of pixels for r in range(nrows,top_side,-1) : offset -= stride pa, pb = 255, 255 # virtual white outside border for c in range(ncols) : pc = colortab[ ord(vptr[offset + c]) ] if (pa + pb + pc) < 32 : # black or dark gray triplet bottom_side = r break pa, pb = pb, pc if bottom_side < nrows : # we hit break # bottom_side is the lowest row with significant pixels. It must be # < nrows, there is at least one row (top_side) with a dot in it. # However if the page is mostly white, don't zoom to that extent. if bottom_side < (top_side+100) : return # seems to be a mostly-white page, give up # The text area runs from scanline top_side to bottom_side. text_height = bottom_side - top_side + 1 port_height = self.scarea.viewport().height() self.zoomFactor = max( self.minZoom, min( self.maxZoom, port_height / text_height ) ) self.zlider.setValue(int(100*self.zoomFactor)) # signals newZoomFactor->showPage # Set the scrollbar to show the page from its top margin. self.scarea.verticalScrollBar().setValue(int( top_side * self.zoomFactor) ) # Re-implement the parent's keyPressEvent in order to provide zoom: # ctrl-plus increases the image size by 1.25 # ctrl-minus decreases the image size by 0.8 # Also trap pageup/dn and use to walk through images. # At this point we do not reposition the editor to match the page viewed. # we page up/dn but as soon as focus returns to the editor and the cursor # moves, this display will snap back to the edited page. As a user that # seems best, come over to Pngs and page ahead to see what's coming, then # back to the editor to read or type. def keyPressEvent(self, event): # assume we will not handle this key and clear its accepted flag event.ignore() # If we are initialized and have displayed some page, look at the key if self.ready: kkey = int( int(event.modifiers()) & IMC.keypadDeModifier) | int(event.key()) if kkey in IMC.zoomKeys : # ctl/cmd + or -, do the zoom event.accept() fac = (0.8) if (kkey == IMC.ctl_minus) else (1.25) fac *= self.zoomFactor # target zoom factor if (fac >= self.minZoom) and (fac <= self.maxZoom): # keep in bounds self.zoomFactor = fac self.zlider.setValue(int(100*fac)) self.showPage() elif (event.key() == Qt.Key_PageUp) or (event.key() == Qt.Key_PageDown) : event.accept() # real pgUp or pgDn, we do it fac = 1 if (event.key() == Qt.Key_PageDown) else -1 fac += self.lastIndex if (fac >= 0) and (fac < IMC.pageTable.size()) : # not off either end of the book, so, self.nextIndex = fac self.showPage() if not event.isAccepted() : # we don't do those, pass them on super(pngDisplay, self).keyPressEvent(event)
class WTestPng(Document.WDocument): """ Test png widget """ def __init__(self, parent=None, path=None, filename=None, extension=None, nonameId=None, remoteFile=True, repoDest=None, project=0): """ Constructs WScript widget @param parent: @type parent: @param path: @type path: @param filename: @type filename: @param extension: @type extension: @param nonameId: @type nonameId: """ Document.WDocument.__init__(self, parent, path, filename, extension, nonameId, remoteFile, repoDest, project) self.scaleFactor = 0.0 self.rawContent = '' self.createWidgets() self.createActions() self.createToolbar() self.createConnections() def createActions(self): """ Create qt actions """ self.zoomInAct = QtHelper.createAction(self, "&Zoom &In (25%.", self.zoomIn, icon=QIcon(":/zoom-in.png")) self.zoomOutAct = QtHelper.createAction(self, "Zoom &Out (25%.", self.zoomOut, icon=QIcon(":/zoom-out.png")) self.normalSizeAct = QtHelper.createAction( self, "&Normal Size", self.normalSize, icon=QIcon(":/zoom-normal.png")) def createToolbar(self): """ Toolbar creation ||------|------||| || Open | Save ||| ||------|------||| """ self.dockToolbar.setObjectName("Test Config toolbar") self.dockToolbar.addAction(self.zoomInAct) self.dockToolbar.addAction(self.zoomOutAct) self.dockToolbar.addAction(self.normalSizeAct) self.dockToolbar.addSeparator() self.dockToolbar.setIconSize(QSize(16, 16)) def createWidgets(self): """ QtWidgets creation """ self.dockToolbar = QToolBar(self) self.dockToolbar.setStyleSheet( "QToolBar { border: 0px }") # remove 3D border self.imageLabel = QLabel(self) self.imageLabel.setBackgroundRole(QPalette.Base) self.imageLabel.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored) self.imageLabel.setScaledContents(True) self.scrollArea = QScrollArea() self.scrollArea.setBackgroundRole(QPalette.Dark) self.scrollArea.setWidget(self.imageLabel) title = QLabel("Image:") title.setStyleSheet("QLabel { padding-left: 2px; padding-top: 2px }") font = QFont() font.setBold(True) title.setFont(font) layout = QVBoxLayout() layout.addWidget(title) layout.addWidget(self.dockToolbar) layout.addWidget(self.scrollArea) layout.setContentsMargins(2, 2, 2, 2) self.setLayout(layout) def zoomIn(self): """ Zoom in """ self.scaleImage(1.25) def zoomOut(self): """ Zoom out """ self.scaleImage(0.8) def normalSize(self): """ Normal size """ self.imageLabel.adjustSize() self.scaleFactor = 1.0 def scaleImage(self, factor): """ Scale image """ self.scaleFactor *= factor self.imageLabel.resize(self.scaleFactor * self.imageLabel.pixmap().size()) self.adjustScrollBar(self.scrollArea.horizontalScrollBar(), factor) self.adjustScrollBar(self.scrollArea.verticalScrollBar(), factor) def adjustScrollBar(self, scrollBar, factor): """ Adjust scrollbar """ scrollBar.setValue( int(factor * scrollBar.value() + ((factor - 1) * scrollBar.pageStep() / 2))) def createConnections(self): """ QtSignals connection """ pass def write(self, force=False): """ Save """ absPath = '%s/%s.%s' % (self.path, self.filename, self.extension) try: with open(absPath, mode='wb') as myfile: myfile.write(self.rawContent) except Exception as e: self.error("unable to write png file: %s" % e) return None else: self.setUnmodify() return True def load(self, content=None): """ Open file """ if content is None: absPath = '%s/%s.%s' % (self.path, self.filename, self.extension) file = QFile(absPath) if not file.open(QIODevice.ReadOnly): self.error("Error opening image file: %s" % absPath) return False else: content = file.readAll() self.rawContent = content image = QImage() image.loadFromData(content) if image.isNull(): self.error("cannot load image") return False else: self.imageLabel.setPixmap(QPixmap.fromImage(QImage(image))) self.scaleFactor = 1.0 self.imageLabel.adjustSize() return True def getraw_encoded(self): """ Returns raw data encoded """ encoded = '' try: encoded = base64.b64encode(self.rawContent) except Exception as e: self.error('unable to encode raw image: %s' % str(e)) return encoded