def __init__(self, circuit): super(CircuitItem, self).__init__() self.setFlag(QGraphicsItem.ItemIsMovable) self.setFlag(QGraphicsItem.ItemIsSelectable) self.setFlag(QGraphicsItem.ItemSendsGeometryChanges) imgDir = filePath('icons/') self.data = circuit """The real info. The class CircuitItem is just a graphical container around it. data is saved / loaded to / from file. """ self.image = QImage(imgDir + circuit.__class__.__name__ + '.png') """The graphical representation of our item on screen.""" if not self.image: self.image = QImage(imgDir + 'Default.png') self.showCategory = True self.showName = True """Is the item's name shown on screen?""" self.showCategory = False """Is the item's category (circuit class) shown on screen?""" self.name = QGraphicsSimpleTextItem(self) # that won't rotate when the PlugItem is rotated by the user. self.name.setFlag(QGraphicsItem.ItemIgnoresTransformations) self.name.setText(self.data.name) self.category = QGraphicsSimpleTextItem(self) self.category.setFlag(QGraphicsItem.ItemIgnoresTransformations) self.category.setText(self.data.category) self.setupPaint()
def __init__(self, plug): super(PlugItem, self).__init__() self.data = plug """The real info. The class PlugItem is just a graphical container around it. data is saved / loaded to / from file. """ self.showName = False """Is the name of the item shown on screen?""" self.setFlag(QGraphicsItem.ItemIsMovable) self.setFlag(QGraphicsItem.ItemIsSelectable) self.setFlag(QGraphicsItem.ItemSendsGeometryChanges) self.setAcceptsHoverEvents(True) self.setPen(QPen(QBrush(QColor(QColor('black'))), 2)) # This path is needed at each mouse over event, to check if # the mouse is over a pin. We save it as an instance field, # rather than recreate it at each event. self.pinPath = QPainterPath() if self.data.isInput: self.pinPath.addEllipse( self.bodyW + self.pinW / 2, self.bodyW / 2, self.pinW, self.pinW) else: self.pinPath.addEllipse( self.pinW / 2, self.bodyW / 2, self.pinW, self.pinW) f = QFont('Times', 12, 75) # Name and value text labels. self.name = QGraphicsSimpleTextItem(self) # that won't rotate when the PlugItem is rotated by the user. self.name.setFlag(QGraphicsItem.ItemIgnoresTransformations) self.name.setText(self.data.name) self.name.setFont(f) self.value = QGraphicsSimpleTextItem(self) self.value.setFlag(QGraphicsItem.ItemIgnoresTransformations) # Or else value would get the clicks, instead of the PlugItem. self.value.setFlag(QGraphicsItem.ItemStacksBehindParent) self.value.setFont(f) self.setupPaint()
def reload(self, order_overview_widget, all_ops, all_operations, sort=1): # mainlog.debug("reload...") progress = QProgressDialog(_("Collecting data..."), None, 0, len(all_ops) + 3, order_overview_widget) progress.setWindowTitle("Horse") progress.setMinimumDuration(0) progress.setWindowModality(Qt.WindowModal) progress.setValue(progress.value() + 1) progress.show() for i in self.items(): self.removeItem(i) self.posts_offsets = dict() self.drawn_operations_data = dict() self.cursor = QGraphicsRectItem(0, 0, 50, 300) self.cursor.setBrush(QBrush(QColor(208, 208, 255, 255))) self.cursor.setPen(QPen(Qt.transparent)) self.addItem(self.cursor) bar_width = 8 bar_height = int(bar_width * 60.0 / 8.0) ascent = QFontMetrics(self.base_font).ascent() ascent_big = QFontMetrics(self.base_font_big).ascent() post_ops = {} # mainlog.debug("reload...2") # z = 0 # for op,order_part,parts in all_operations: # z = op.planned_hours # z = order_part.deadline # z = order_part.qty # z = order_part.human_identifier # all_operations = map(lambda i:i[0],all_operations) y = 0 for opdef in all_ops: progress.setValue(progress.value() + 1) operations = filter( lambda op: op.operation_definition_id == opdef. operation_definition_id, all_operations) # We're only interested in the effort/time that remains # to be put on an operation. We're only interested in # the future. # We want the oeprations that are either # - ongoing # - ready to start. # In all cases we're only interested in operations # that are "active" if sort == 1: operations = sorted( operations, key=lambda op: op.deadline or date(3000, 1, 1)) elif sort == 2: operations = sorted( operations, key=lambda op: op.planned_hours * op.qty - op.done_hours) else: # Don't sort pass maximum = 16.0 #float ! small_hours = 0 op_ndx = 0 current_x = 50 bar_drawn = False total_done_hours = total_estimated = 0 # -------------------------------------------------------------- # Started operations bars_line = BarsLine(16, bar_width, bar_height, current_x, y, self, order_overview_widget) total_hours_to_do = 0 for op in filter(lambda op: op.done_hours > 0, operations): hours_to_do = max( 0, op.planned_hours * op.qty - op.done_hours) # max protects against reporting errors total_hours_to_do += hours_to_do total_estimated += op.planned_hours * op.qty total_done_hours += op.done_hours bars_line.add_bar(hours_to_do, QBrush(Qt.green), self._operation_hoover_description(op), False, None) # op.production_file.order_part) # -------------------------------------------------------------- bars_line_unstarted_operations = BarsLine(16, bar_width, bar_height, current_x + 30, y, self, order_overview_widget) total_hours_to_do_on_unstarted_operations = 0 for op in filter(lambda op: op.done_hours == 0, operations): hours_to_do = op.planned_hours * op.qty total_hours_to_do_on_unstarted_operations += hours_to_do total_estimated += hours_to_do bars_line_unstarted_operations.add_bar( hours_to_do, QBrush(Qt.yellow), self._operation_hoover_description(op), False, None) #op.production_file.order_part) y_start = y total = total_hours_to_do + total_hours_to_do_on_unstarted_operations if total > 0: self.drawn_operations_data[ opdef.operation_definition_id] = "{}h".format( int(round(total_estimated))) gi = QGraphicsSimpleTextItem( _("{} - Estimated to do : {}h; done : {}h").format( opdef.description, int(round(total)), int(round(total_done_hours)))) gi.setFont(self.base_font_big) gi.setPos(0, y - gi.boundingRect().height()) self.addItem(gi) th = gi.boundingRect().height() gi = QGraphicsLineItem(-ascent_big, y, 1024 + 2 * ascent_big, y) gi.setPen(QPen(Qt.black)) self.addItem(gi) y += th else: continue y_bars = y if total_hours_to_do > 0: # There's something to draw head = QGraphicsSimpleTextItem(_("Started")) head.setFont(self.base_font) head.setPos(current_x, y) self.addItem(head) y += head.boundingRect().height() y += bar_height bars_line.set_start_pos(current_x, y) bars_line.finish_bar() foot = QGraphicsSimpleTextItem( _("{}h").format(int(total_hours_to_do + 0.5))) foot.setFont(self.base_font) foot.setPos(current_x, y) self.addItem(foot) y += foot.boundingRect().height() current_x = max(current_x + bars_line.estimate_width(), head.boundingRect().right(), foot.boundingRect().right()) bar_drawn = True if total_hours_to_do_on_unstarted_operations > 0: if bars_line_unstarted_operations.estimate_width( ) + current_x > 1200: x = 50 y += ascent_big else: y = y_bars x = current_x + 50 head = QGraphicsSimpleTextItem(_("Not started yet")) head.setFont(self.base_font) head.setPos(x, y) self.addItem(head) y += head.boundingRect().height() y += bar_height bars_line_unstarted_operations.set_start_pos(x, y) bars_line_unstarted_operations.finish_bar() foot = QGraphicsSimpleTextItem( _("{}h").format( int(total_hours_to_do_on_unstarted_operations + 0.5))) foot.setFont(self.base_font) foot.setPos(x, y) self.addItem(foot) y += foot.boundingRect().height() bar_drawn = True y += 3 * ascent_big r = self.sceneRect() self.posts_offsets[opdef.operation_definition_id] = \ QRectF(r.x() - 2*ascent_big, y_start - 1.5*ascent_big, r.width() + 4*ascent_big, (y - ascent_big) - (y_start - 1.5*ascent_big) ) y += ascent_big # mainlog.debug("reload...3") import functools max_width = functools.reduce(lambda acc, po: max(acc, po.width()), self.posts_offsets.values(), 0) map(lambda po: po.setWidth(max_width), self.posts_offsets.values()) # for r in self.posts_offsets.values(): # gi = QGraphicsLineItem(r.x(),r.y(),r.x()+r.width(),r.y()) # gi.setPen(QPen(Qt.lightGray)) # self.addItem(gi) progress.close()
class CircuitItem(QGraphicsItem): """Graphical wrapper around the engine Circuit class.""" textH = 12 """Height of text.""" ioH = 20 """Height between to I/O pins.""" ioW = 15 """Length of I/O pins.""" radius = 10 """Radius of I/O pin heads.""" def __init__(self, circuit): super(CircuitItem, self).__init__() self.setFlag(QGraphicsItem.ItemIsMovable) self.setFlag(QGraphicsItem.ItemIsSelectable) self.setFlag(QGraphicsItem.ItemSendsGeometryChanges) imgDir = filePath('icons/') self.data = circuit """The real info. The class CircuitItem is just a graphical container around it. data is saved / loaded to / from file. """ self.image = QImage(imgDir + circuit.__class__.__name__ + '.png') """The graphical representation of our item on screen.""" if not self.image: self.image = QImage(imgDir + 'Default.png') self.showCategory = True self.showName = True """Is the item's name shown on screen?""" self.showCategory = False """Is the item's category (circuit class) shown on screen?""" self.name = QGraphicsSimpleTextItem(self) # that won't rotate when the PlugItem is rotated by the user. self.name.setFlag(QGraphicsItem.ItemIgnoresTransformations) self.name.setText(self.data.name) self.category = QGraphicsSimpleTextItem(self) self.category.setFlag(QGraphicsItem.ItemIgnoresTransformations) self.category.setText(self.data.category) self.setupPaint() def boundingRect(self): """Qt requires overloading this when overloading QGraphicsItem.""" W = 2 * self.radius + 2 * self.ioW + self.imgW ni = self.data.nb_inputs() no = self.data.nb_outputs() t = (1 - int(max(ni, no) / 2)) * self.ioH - self.radius / 2 b = (1 + int(max(ni, no) / 2)) * self.ioH + self.radius / 2 return ( QRectF(-self.ioW - self.radius, t, W, b - t) if max(ni, no) > 1 else QRectF(-self.ioW - self.radius, 0, W, self.image.height())) def handleAtPos(self, pos): """Is there an interactive handle where the mouse is? Return it.""" for i in range(self.nIn): if self.inputPaths[i].contains(pos): return self.data.inputList[i] for i in range(self.nOut): if self.outputPaths[i].contains(pos): return self.data.outputList[i] def itemChange(self, change, value): """Warning view it will soon have to correct pos.""" if change == QGraphicsItem.ItemPositionHasChanged: # Restart till we stop moving. self.scene().views()[0].timer.start() return QGraphicsItem.itemChange(self, change, value) def paint(self, painter, option, widget): """Draws the item.""" painter.setPen(QPen(QColor('black'), 2)) ni = self.data.nb_inputs() no = self.data.nb_outputs() for i in range(1 - int(ni / 2), 2 + int(ni / 2)): if i != 1 or ni % 2: painter.drawLine(-self.ioW, i * self.ioH, 0, i * self.ioH) for i in range(1 - int(no / 2), 2 + int(no / 2)): if i != 1 or no % 2: painter.drawLine( self.imgW, i * self.ioH, self.imgW + self.ioW, i * self.ioH) painter.drawImage(QRectF(0, 0, self.imgW, self.imgH), self.image) for i in range(ni): painter.drawPath(self.inputPaths[i]) painter.drawLine( 0, (1 - int(ni / 2)) * self.ioH, 0, (1 + int(ni / 2)) * self.ioH) for i in range(no): painter.drawPath(self.outputPaths[i]) painter.drawLine( self.imgW, (1 - int(no / 2)) * self.ioH, self.imgW, (1 + int(no / 2)) * self.ioH) # Default selection box doesn't work; simple reimplementation. if option.state & QStyle.State_Selected: pen = QPen(Qt.black, 1, Qt.DashLine) painter.setPen(pen) painter.drawRect(self.boundingRect()) def setCategoryVisibility(self, isVisible): """Show/Hide circuit category (mostly useful for user circuits).""" self.showCategory = isVisible self.setupPaint() def setNameVisibility(self, isVisible): """Shows/Hide the item name in the graphical view.""" self.showName = isVisible self.setupPaint() def setNbInputs(self, nb): """Add/Remove inputs (for logical gates).""" if nb > self.data.nb_inputs(): for x in range(nb - self.data.nb_inputs()): Plug(True, None, self.data) elif nb < self.data.nb_inputs(): for x in range(self.data.nb_inputs() - nb): self.data.remove_input(self.data.inputList[0]) self.setupPaint() def setupPaint(self): """Offscreen rather than onscreen redraw (few changes).""" self.nIn = self.data.nb_inputs() self.nOut = self.data.nb_outputs() # 3 sections with different heights must be aligned : self.imgH = self.image.size().height() # central (png image) self.imgW = self.image.size().width() self.inH = (self.nIn - 1) * self.ioH + 2 * self.radius # inputs self.outH = (self.nOut - 1) * self.ioH + 2 * self.radius # outputs # therefore we calculate a vertical offset for each section : self.maxH = max(self.imgH, self.inH, self.outH) self.imgOff = ( 0 if self.maxH == self.imgH else (self.maxH - self.imgH) / 2.) self.inOff = ( 0 if self.maxH == self.inH else (self.maxH - self.inH) / 2.) self.outOff = ( 0 if self.maxH == self.outH else (self.maxH - self.outH) / 2.) # i/o mouseover detection. Create once, use on each mouseMoveEvent. self.inputPaths = [] self.outputPaths = [] ni = self.data.nb_inputs() no = self.data.nb_outputs() for i in range(1 - int(ni / 2), 2 + int(ni / 2)): if i != 1 or ni % 2: path = QPainterPath() path.addEllipse( -self.ioW - self.radius, i * self.ioH - self.radius / 2, self.radius, self.radius) self.inputPaths.append(path) for i in range(1 - int(no / 2), 2 + int(no / 2)): if i != 1 or no % 2: path = QPainterPath() path.addEllipse( self.imgW + self.ioW, i * self.ioH - self.radius / 2, self.radius, self.radius) self.outputPaths.append(path) self.name.setVisible(self.showName) self.category.setVisible(self.showCategory) if self.showName or self.showCategory: br = self.mapToScene(self.boundingRect()) w = self.boundingRect().width() h = self.boundingRect().height() realX = min([i.x() for i in br]) realY = min([i.y() for i in br]) firstY = realY + (w if self.rotation() % 180 else h) + 1 secondY = firstY + self.textH if self.showName: self.name.setBrush(QColor('red')) self.name.setText(self.data.name) self.name.setPos(self.mapFromScene(realX, firstY)) if self.showCategory: self.category.setBrush(QColor('green')) self.category.setText( self.data.category if self.data.category else self.data.__class__.__name__) self.category.setPos(self.mapFromScene( realX, secondY if self.showName else firstY)) self.prepareGeometryChange() # Must be called (cf Qt doc) self.update() # Force onscreen redraw after changes.
class PlugItem(QGraphicsPathItem): """Graphical wrapper around the engine Plug class.""" bodyW = 30 """The width of the body of plugs.""" pinW = 10 """The width of the pin part of plugs.""" def __init__(self, plug): super(PlugItem, self).__init__() self.data = plug """The real info. The class PlugItem is just a graphical container around it. data is saved / loaded to / from file. """ self.showName = False """Is the name of the item shown on screen?""" self.setFlag(QGraphicsItem.ItemIsMovable) self.setFlag(QGraphicsItem.ItemIsSelectable) self.setFlag(QGraphicsItem.ItemSendsGeometryChanges) self.setAcceptsHoverEvents(True) self.setPen(QPen(QBrush(QColor(QColor('black'))), 2)) # This path is needed at each mouse over event, to check if # the mouse is over a pin. We save it as an instance field, # rather than recreate it at each event. self.pinPath = QPainterPath() if self.data.isInput: self.pinPath.addEllipse( self.bodyW + self.pinW / 2, self.bodyW / 2, self.pinW, self.pinW) else: self.pinPath.addEllipse( self.pinW / 2, self.bodyW / 2, self.pinW, self.pinW) f = QFont('Times', 12, 75) # Name and value text labels. self.name = QGraphicsSimpleTextItem(self) # that won't rotate when the PlugItem is rotated by the user. self.name.setFlag(QGraphicsItem.ItemIgnoresTransformations) self.name.setText(self.data.name) self.name.setFont(f) self.value = QGraphicsSimpleTextItem(self) self.value.setFlag(QGraphicsItem.ItemIgnoresTransformations) # Or else value would get the clicks, instead of the PlugItem. self.value.setFlag(QGraphicsItem.ItemStacksBehindParent) self.value.setFont(f) self.setupPaint() def handleAtPos(self, pos): """Is there an interactive handle where the mouse is? Also return the Plug under this handle. """ return self.data if self.pinPath.contains(pos) else None def itemChange(self, change, value): """Warning view it will soon have to correct pos.""" if change == QGraphicsItem.ItemPositionHasChanged: # Restart till we stop moving. self.scene().views()[0].timer.start() return QGraphicsItem.itemChange(self, change, value) def setAndUpdate(self): """Change the undelying plug's value, and force updates items.""" self.data.set(not self.data.value) for i in self.scene().items(): if isinstance(i, PlugItem) or isinstance(i, WireItem): i.setupPaint() def setNameVisibility(self, isVisible): """Shows/Hide the item name in the graphical view.""" self.showName = isVisible self.setupPaint() def setupPaint(self): """Offscreen rather than onscreen redraw (few changes).""" path = QPainterPath() if self.data.isInput: path.addEllipse( self.pinW / 2, self.pinW / 2, self.bodyW, self.bodyW) else: path.addRect( 3 * self.pinW / 2 + 1, self.pinW / 2, self.bodyW, self.bodyW) path.addPath(self.pinPath) self.setPath(path) self.name.setVisible(self.showName) self.name.setText(self.data.name) br = self.mapToScene(self.boundingRect()) w = self.boundingRect().width() h = self.boundingRect().height() realX = min([i.x() for i in br]) realY = min([i.y() for i in br]) self.name.setPos(self.mapFromScene( realX, realY + (w if self.rotation() % 180 else h) + 1)) self.value.setText( str(int(self.data.value)) if self.data.value is not None else 'E') self.value.setPos(self.mapFromScene(realX + w / 3, realY + h / 3)) self.value.setBrush(QColor('green' if self.data.value else 'red')) self.update() # Force onscreen redraw after changes.
def _drawNodes(self,scene,selected_state,initial_state): for i in scene.items(): scene.removeItem(i) pos = dict() sx = 1 sy = 1 if configuration.font_select: sx = sx * 2 sy = sy pos[OrderStatusType.preorder_definition] = (200*sx,200*sy) pos[OrderStatusType.order_definition] = (10*sx,300*sy) pos[OrderStatusType.order_ready_for_production] = (300*sx,300*sy) pos[OrderStatusType.order_production_paused] = (200*sx,550*sy) pos[OrderStatusType.order_completed] = (400*sx,600*sy) pos[OrderStatusType.order_aborted] = (10*sx,600*sy) mainlog.debug("_drawNodes : selected_state {}".format(selected_state)) mainlog.debug("_drawNodes : initial_state {}".format(initial_state)) for org in OrderStatusType.symbols(): x,y = pos[org] item = QGraphicsSimpleTextItem(org.description) if (selected_state and org in OrderStatusType.next_states(selected_state)) or org == initial_state or org == selected_state: item = MyQGraphicsSimpleTextItem(org, self) item.setFlags( item.flags() | QGraphicsItem.ItemIsSelectable) if org == selected_state: mainlog.debug("Preselcting {}".format(org)) item.setSelected(True) item.setFont(self.thinfont) item.setPos(x,y) scene.addItem(item) pos[org] = item.boundingRegion( item.sceneTransform()).boundingRect() # scene.addRect(pos[org]) g_orgs = dict() for org in OrderStatusType.symbols(): g_orgs[org] = OrderStatusType.next_states(org) # Draw all arrows which don't end up or leave the selected_state drawn = [] for org,dests in g_orgs.iteritems(): for dest in dests: if selected_state != org and ((org,dest) not in drawn) and ((dest,org) not in drawn): # If an arrow must have two directions, we draw it # like that directly. item = self._makeArrow( pos[org],pos[dest],dest in g_orgs and org in g_orgs[dest]) item.setPen(self.grey_pen) scene.addItem( item) drawn.append((org,dest)) if initial_state: scene.addRect(pos[initial_state]) if selected_state: for dest in OrderStatusType.next_states(selected_state): item = self._makeArrow( pos[selected_state],pos[dest],False) item.setBrush(QBrush(Qt.green)) scene.addItem( item) drawn.append((org,dest))