How to add a custom graphic to the Word document header - apache-poi

I want to add a horizontal line to the header, which is drawn with word custom graphics.
1.For example, the following effect:
No custom graphics were added to the header:
enter image description here
Add a horizontal line to the header:
enter image description here
2.Unzip them separately and get the .xml file for the header:
There are no custom graphics in the header
<w:p w:rsidR="00212733" w:rsidRPr="00212733" w:rsidRDefault="00212733" w:rsidP="00212733">
<w:pPr>
<w:pStyle w:val="af"/>
<w:ind w:firstLine="800"/>
<w:jc w:val="center"/>
<w:rPr>
<w:sz w:val="40"/>
<w:szCs w:val="40"/>
</w:rPr>
</w:pPr>
<w:r w:rsidRPr="00212733">
<w:rPr>
<w:sz w:val="40"/>
<w:szCs w:val="40"/>
</w:rPr>
<w:t>This is a header</w:t>
</w:r>
</w:p>
There are custom graphics in the header
<w:p w:rsidR="00EB798F" w:rsidRDefault="00EB798F" w:rsidP="00EB798F">
<w:pPr>
<w:pStyle w:val="af"/>
<w:ind w:firstLine="800"/>
<w:jc w:val="center"/>
<w:rPr>
<w:sz w:val="40"/>
<w:szCs w:val="40"/>
</w:rPr>
</w:pPr>
<w:r w:rsidRPr="00EB798F">
<w:rPr>
<w:rFonts w:hint="eastAsia"/>
<w:sz w:val="40"/>
<w:szCs w:val="40"/>
</w:rPr>
<w:t>T</w:t>
</w:r>
<w:r w:rsidRPr="00EB798F">
<w:rPr>
<w:sz w:val="40"/>
<w:szCs w:val="40"/>
</w:rPr>
<w:t>his is a header</w:t>
</w:r>
</w:p>
<w:p w:rsidR="0039120D" w:rsidRPr="00EB798F" w:rsidRDefault="0039120D" w:rsidP="00EB798F">
<w:pPr>
<w:pStyle w:val="af"/>
<w:ind w:firstLine="400"/>
<w:jc w:val="center"/>
<w:rPr>
<w:sz w:val="40"/>
<w:szCs w:val="40"/>
</w:rPr>
</w:pPr>
<w:bookmarkStart w:id="0" w:name="_GoBack"/>
<w:r>
<w:rPr>
<w:rFonts w:ascii="仿宋_GB2312"/>
<w:noProof/>
<w:color w:val="FF0000"/>
<w:sz w:val="20"/>
</w:rPr>
<mc:AlternateContent>
<mc:Choice Requires="wps">
<w:drawing>
<wp:anchor distT="0" distB="0" distL="114300" distR="114300" simplePos="0" relativeHeight="251659264" behindDoc="0" locked="0" layoutInCell="1" allowOverlap="1" wp14:anchorId="07DD5BF2" wp14:editId="521D0E7B">
<wp:simplePos x="0" y="0"/>
<wp:positionH relativeFrom="column">
<wp:posOffset>0</wp:posOffset>
</wp:positionH>
<wp:positionV relativeFrom="paragraph">
<wp:posOffset>10795</wp:posOffset>
</wp:positionV>
<wp:extent cx="5963920" cy="1905"/>
<wp:effectExtent l="5715" t="10795" r="12065" b="6350"/>
<wp:wrapNone/>
<wp:docPr id="5" name="直线 15"/>
<wp:cNvGraphicFramePr><a:graphicFrameLocks xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"/>
</wp:cNvGraphicFramePr>
<a:graphic xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main">
<a:graphicData uri="http://schemas.microsoft.com/office/word/2010/wordprocessingShape">
<wps:wsp>
<wps:cNvCnPr>
<a:cxnSpLocks noChangeShapeType="1"/>
</wps:cNvCnPr>
<wps:spPr bwMode="auto">
<a:xfrm>
<a:off x="0" y="0"/>
<a:ext cx="5963920" cy="1905"/>
</a:xfrm>
<a:prstGeom prst="line">
<a:avLst/>
</a:prstGeom>
<a:noFill/>
<a:ln w="9525">
<a:solidFill>
<a:srgbClr val="000000"/>
</a:solidFill>
<a:round/>
<a:headEnd/>
<a:tailEnd/>
</a:ln>
<a:extLst>
<a:ext uri="{909E8E84-426E-40DD-AFC4-6F175D3DCCD1}">
<a14:hiddenFill xmlns:a14="http://schemas.microsoft.com/office/drawing/2010/main">
<a:noFill/>
</a14:hiddenFill>
</a:ext>
</a:extLst>
</wps:spPr>
<wps:bodyPr/>
</wps:wsp>
</a:graphicData>
</a:graphic>
<wp14:sizeRelH relativeFrom="page">
<wp14:pctWidth>0</wp14:pctWidth>
</wp14:sizeRelH>
<wp14:sizeRelV relativeFrom="page">
<wp14:pctHeight>0</wp14:pctHeight>
</wp14:sizeRelV>
</wp:anchor>
</w:drawing>
</mc:Choice>
<mc:Fallback>
<w:pict>
<v:line w14:anchorId="0A64B4CD" id="直线 15" o:spid="_x0000_s1026" style="position:absolute;left:0;text-align:left;z-index:251659264;visibility:visible;mso-wrap-style:square;mso-width-percent:0;mso-height-percent:0;mso-wrap-distance-left:9pt;mso-wrap-distance-top:0;mso-wrap-distance-right:9pt;mso-wrap-distance-bottom:0;mso-position-horizontal:absolute;mso-position-horizontal-relative:text;mso-position-vertical:absolute;mso-position-vertical-relative:text;mso-width-percent:0;mso-height-percent:0;mso-width-relative:page;mso-height-relative:page" from="0,.85pt" to="469.6pt,1pt" o:gfxdata="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
"/>
</w:pict>
</mc:Fallback>
</mc:AlternateContent>
</w:r>
<w:bookmarkEnd w:id="0"/>
</w:p>
I did some common sense, but I found that I always can't solve the id problem of custom graphics, I hope you can help me, thank you!

Until now (apache poi 5.0.0) creation of shapes in Word is not supported by apache poi. Only inserting of pictures is supported. There is not even a possibility to handle the namespace markup-compatibility (mc). So also using the underlying low level org.openxmlformats.schemas.wordprocessingml.x2006.main.* classes it is not possible. Only directly manipulating the XML wold be possible. But this is too less straight forward.
What one could do is using the old shape definitions of com.microsoft.schemas.vml.*. These are currently still supported, even if they get replaced by the new drawing layer shapes.
Using com.microsoft.schemas.vml.* shapes are created using shape types which describe the shape by VML paths. Those shapes are placed in pict elements in text runs of document and/or header/footer.
A line shape in a header could be created as so:
import java.io.FileOutputStream;
import org.apache.poi.xwpf.usermodel.*;
import org.apache.poi.wp.usermodel.HeaderFooterType;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTR;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTPicture;
import com.microsoft.schemas.vml.CTGroup;
import com.microsoft.schemas.vml.CTShape;
import com.microsoft.schemas.vml.CTShapetype;
import org.w3c.dom.Node;
public class CreateWordLineInHeader {
public static void main(String[] args) throws Exception {
XWPFDocument doc= new XWPFDocument();
// the body content
XWPFParagraph paragraph = doc.createParagraph();
XWPFRun run=paragraph.createRun();
run.setText("The Body:");
paragraph = doc.createParagraph();
run=paragraph.createRun();
run.setText("Lorem ipsum....");
// create header start
XWPFHeader header = doc.createHeader(HeaderFooterType.DEFAULT);
paragraph = header.getParagraphArray(0);
if (paragraph == null) paragraph = header.createParagraph();
paragraph.setAlignment(ParagraphAlignment.CENTER);
run = paragraph.createRun();
run.setText("This is a header");
// paragraph for the line
paragraph = header.createParagraph();
paragraph.setAlignment(ParagraphAlignment.CENTER);
run = paragraph.createRun();
// create line shape in run
CTGroup ctGroup = CTGroup.Factory.newInstance();
String shapeTypeId = "_x0000_t123";
CTShapetype shapetype = ctGroup.addNewShapetype();
shapetype.setId(shapeTypeId);
shapetype.setCoordsize("21600,21600");
shapetype.setSpt(123);
shapetype.setPath2("m,l21600,21600e");
CTShape ctShape = ctGroup.addNewShape();
ctShape.setType("#"+shapeTypeId);
ctShape.setStyle("position:absolute;left:100pt;width:280pt;height:0");
Node ctGroupNode = ctGroup.getDomNode();
CTPicture ctPicture = CTPicture.Factory.parse(ctGroupNode);
CTR cTR = run.getCTR();
cTR.addNewPict();
cTR.setPictArray(0, ctPicture);
// create header end
// create footer start
XWPFFooter footer = doc.createFooter(HeaderFooterType.DEFAULT);
paragraph = footer.getParagraphArray(0);
if (paragraph == null) paragraph = footer.createParagraph();
paragraph.setAlignment(ParagraphAlignment.CENTER);
run = paragraph.createRun();
run.setText("The Footer");
// create footer end
FileOutputStream out = new FileOutputStream("CreateWordLineInHeader.docx");
doc.write(out);
out.close();
doc.close();
}
}
But is the line shape really necessary? If only a horizontal rule (like a HR in HTML) is needed, then a paragraph having a bottom border line would be much more straigt forward. See Word autoformat with Apache-POI.

Related

Trying to get cursor with coordinate display within a pyqtgraph plotwidget in PyQt5

I'm trying to add a readout of the cursor position in a pqytplot plotwidget in PyQt5. I found this code which does what I want, but in a stand-alone window all within one program file:
import pyqtgraph as pg
from pyqtgraph.Qt import QtGui, QtCore
#generate layout
app = QtGui.QApplication([])
win = pg.GraphicsWindow()
label = pg.LabelItem(justify='right')
win.addItem(label)
p1 = win.addPlot(row=1, col=0)
data1 = [n**2 for n in range(100)]
p1.plot(data1, pen="r")
#cross hair
vLine = pg.InfiniteLine(angle=90, movable=False)
hLine = pg.InfiniteLine(angle=0, movable=False)
p1.addItem(vLine, ignoreBounds=True)
p1.addItem(hLine, ignoreBounds=True)
def mouseMoved(evt):
pos = evt[0] ## using signal proxy turns original arguments into a tuple
if p1.sceneBoundingRect().contains(pos):
mousePoint = p1.vb.mapSceneToView(pos)
index = int(mousePoint.x())
if index > 0 and index < len(data1):
label.setText("<span style='font-size: 12pt'>x=%0.1f, <span style='color: red'>y1=%0.1f</span>" % (mousePoint.x(), data1[index]))
vLine.setPos(mousePoint.x())
hLine.setPos(mousePoint.y())
proxy = pg.SignalProxy(p1.scene().sigMouseMoved, rateLimit=60, slot=mouseMoved)
## Start Qt event loop unless running in interactive mode or using pyside.
if __name__ == '__main__':
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()
The problem I'm running in to is figuring out how to implement something like this with my GUI - where I will have to pass reference to the plotwidget to the mouseMoved function. In the example above, the mousemoved function has access to hline, vline and p1, but in my code it won't - I need to be able to pass those through. But I have no idea how to do that.
I've tried to replicate this issue with the smallest amount of code possible. First here's a simple UI file for the GUI, called CursorLayout.ui
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1167</width>
<height>443</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout_6">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_16">
<property name="sizeConstraint">
<enum>QLayout::SetFixedSize</enum>
</property>
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QPushButton" name="startbutton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Plot</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<widget class="PlotWidget" name="plotWidget" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>300</height>
</size>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3"/>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_17">
<property name="spacing">
<number>0</number>
</property>
<item>
<widget class="QPushButton" name="exitbutton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Exit</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
</widget>
<customwidgets>
<customwidget>
<class>PlotWidget</class>
<extends>QWidget</extends>
<header location="global">pyqtgraph</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
The main program is this:
from PyQt5 import uic
from PyQt5.QtWidgets import QApplication, QMainWindow
from initGUI import connecttolayout, setinitialview
class UI(QMainWindow):
def __init__(self):
super(UI, self).__init__()
uic.loadUi("CursorLayout.ui", self) #load GUI layout file created with QtDesigner
connecttolayout(self) # connect code to elements in UI file
setinitialview(self) # set initial view (button/label visibility, default values, etc)
self.show()
def clickedstartButton(self): #action if start button clicked
self.plotWidget.clear()
plotx = range(100)
ploty = [number**2 for number in plotx]
thisline = self.plotWidget.plot(plotx, ploty, pen='r')
QApplication.processEvents()
def clickedexitButton(self):
self.close()
app=QApplication([])
UIWindow=UI()
app.exec()
with file containing code to set up the gui, initGUI.py (not necessarily how you would do this, but this is to mimic the file structure of my larger program):
from PyQt5.QtWidgets import QPushButton
import pyqtgraph as pg
def connecttolayout(self): #connect GUI elements to elements in UI file
self.startButton = self.findChild(QPushButton, "startbutton")
self.exitButton = self.findChild(QPushButton, "exitbutton")
self.startButton.clicked.connect(self.clickedstartButton)
self.exitButton.clicked.connect(self.clickedexitButton)
def mouseMoved(evt):
pos = evt[0] ## using signal proxy turns original arguments into a tuple
if self.plotWidget.sceneBoundingRect().contains(pos):
mousePoint = self.plotWidget.vb.mapSceneToView(pos)
index = int(mousePoint.x())
#if index > 0 and index < len(data1):
if index > 0 and index < self.MFmax:
self.cursorlabel.setText("<span style='font-size: 12pt'>x=%0.1f, <span style='color: red'>y=%0.1f</span>" % (
mousePoint.x(), mousePoint.y()))
self.vLine.setPos(mousePoint.x())
self.hLine.setPos(mousePoint.y())
def setinitialview(self): #set initial view to pvst view and clear plot window
#set plot initial configuration
self.plotWidget.setBackground('w')
self.plotWidget.setLabels(left=('Pressure', 'Torr'))
self.plotWidget.setLabel('left',color='black',size=30)
self.plotWidget.setLabels(bottom=('Time', 's'))
self.plotWidget.setLabel('bottom',color='black',size=30)
self.plotWidget.clear()
# cross hair
self.vLine = pg.InfiniteLine(angle=90, movable=False)
self.hLine = pg.InfiniteLine(angle=0, movable=False)
self.plotWidget.addItem(self.vLine, ignoreBounds=True)
self.plotWidget.addItem(self.hLine, ignoreBounds=True)
self.cursorlabel = pg.LabelItem(justify='right')
proxy = pg.SignalProxy(self.plotWidget.scene().sigMouseMoved, rateLimit=60, slot=mouseMoved)
I'm actually surprised my attempt doesn't cause an error - pressing the plot button does create a plot, but it definitely doesn't create the cursor in the graph in the GUI.
How do I get the necessary info passed to the mouseMoved function?
There are a few little errors that will make your program fail:
The mouseMoved() function has to be inside your widget class because it needs the evt argument, which is generated in the widget.
The self.MFmax variable/constant is not created anywhere
In this line:
mousePoint = self.plotWidget.vb.mapSceneToView(pos)
The PlotWidget object doesn't have the vb attribute. It is a PlotItem's attribute, then you should change that line to this:
mousePoint = self.plotWidget.plotItem.vb.mapSceneToView(pos)
Pyqtgraph recommends here to use TextItem instead of LabelItem, to display text inside a scaled view, because of its scaling size.
Now, with that said and reorganizing your code to be more legible, here is my solution to your code (you only need the UI file and this script):
import sys
import pyqtgraph as pg
from pyqtgraph.Qt import QtGui, uic
ui_file = uic.loadUiType("CursorLayout.ui")[0]
class UI(QtGui.QMainWindow, ui_file):
def __init__(self):
## Inherit the QMainWindow and ui_file classes
QtGui.QMainWindow.__init__(self)
ui_file.__init__(self)
self.setupUi(self)
## Create aditional widgets
self.plot_item = self.plotWidget.plot()
self.vLine = pg.InfiniteLine(angle=90, movable=False)
self.hLine = pg.InfiniteLine(angle=0, movable=False)
self.cursorlabel = pg.TextItem(anchor=(-1,10))
## Build the rest of the GUI
self.format_plot()
## data
self.plotx = range(100)
self.ploty = [number**2 for number in self.plotx]
## Connect signals to actions
self.startbutton.clicked.connect(self.clickedstartButton)
self.exitbutton.clicked.connect(self.clickedexitButton)
self.plotWidget.scene().sigMouseMoved.connect(self.mouseMoved)
## OVERWRITE the mouseMoved action:
def mouseMoved(self, evt):
pos = evt
if self.plotWidget.sceneBoundingRect().contains(pos):
mousePoint = self.plotWidget.plotItem.vb.mapSceneToView(pos)
index = int(mousePoint.x())
if index > 0 and index < len(self.plotx):
# if index > 0 and index < self.MFmax:
self.cursorlabel.setHtml(
"<span style='font-size: 12pt'>x={:0.1f}, \
<span style='color: red'>y={:0.1f}</span>".format(
mousePoint.x(), mousePoint.y()))
self.vLine.setPos(mousePoint.x())
self.hLine.setPos(mousePoint.y())
def clickedstartButton(self): #action if start button clicked
self.plot_item.setData(self.plotx, self.ploty, pen='r')
self.plotWidget.addItem(self.cursorlabel)
def clickedexitButton(self):
self.close()
def format_plot(self):
self.plotWidget.setBackground('w')
self.plotWidget.setLabels(left=('Pressure', 'Torr'))
self.plotWidget.setLabel('left',color='black',size=30)
self.plotWidget.setLabels(bottom=('Time', 's'))
self.plotWidget.setLabel('bottom',color='black',size=30)
self.plotWidget.addItem(self.vLine, ignoreBounds=True)
self.plotWidget.addItem(self.hLine, ignoreBounds=True)
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
window = UI()
window.show()
sys.exit(app.exec_())
The code above will make the "crosshair" (the hline and vline) to follow your mouse and displaying the coordinates of that position, like this:
If you want the "crosshair" to track the points in the curve based on the x-axis position of your cursor, you can change the mouseMoved() function to this:
def mouseMoved(self, evt):
pos = evt
if self.plotWidget.sceneBoundingRect().contains(pos):
mousePoint = self.plotWidget.plotItem.vb.mapSceneToView(pos)
mx = np.array([abs(i-mousePoint.x()) for i in self.plotx])
index = mx.argmin()
if index >= 0 and index < len(self.plotx):
self.cursorlabel.setHtml(
"<span style='font-size: 12pt'>x={:0.1f}, \
<span style='color: red'>y={:0.1f}</span>".format(
self.plotx[index], self.ploty[index])
)
self.vLine.setPos(self.plotx[index])
self.hLine.setPos(self.ploty[index])
And this will be the result:
Performance of mx calculation in mouseMoved can be largly improved in order to get a faster response of the cursor:
def mouseMoved(self, evt):
pos = evt
if self.plotWidget.sceneBoundingRect().contains(pos):
mousePoint = self.plotWidget.plotItem.vb.mapSceneToView(pos)
mx = abs(np.ones(len(self.plotx))*mousePoint.x() - self.plotx)
index = mx.argmin()
if index >= 0 and index < len(self.plotx):
self.cursorlabel.setHtml(
"<span style='font-size: 12pt'>x={:0.1f}, \
<span style='color: red'>y={:0.1f}</span>".format(
self.plotx[index], self.ploty[index])
)
self.vLine.setPos(self.plotx[index])
self.hLine.setPos(self.ploty[index])

Beautiful Soup 4 Custom Attribute Order Output

I want to create a custom output formatter in BS4 that will rearrange the order of attributes of tags in an XML in a specific way this is not alphabetical order.
For instance, I want to output the following tag:
<word form="συ" head="2610" id="2357" lemma="συ" postag="p-s----n-" relation="ExD_AP"/>
as:
<word id="2357" head="2610" postag="p-s----n-" form="συ" lemma="συ" relation="ExD_AP"/>
BS4's documentation offers a clue as to where to begin. They give the following example:
from bs4.formatter import HTMLFormatter
class UnsortedAttributes(HTMLFormatter):
def attributes(self, tag):
for k, v in tag.attrs.items():
if k == 'm':
continue
yield k, v
print(attr_soup.p.encode(formatter=UnsortedAttributes()))
This will make a custom HTML output formatter that will leave attributes in the order they were input and also ignore certain tags, but I don't know how to alter this so that it will output in whatever order I would like. Can anyone help me out?
How about this?
from simplified_scrapy.simplified_doc import SimplifiedDoc
html ='''
<word form="συ" head="2610" id="2357" lemma="συ" postag="p-s----n-" relation="ExD_AP"/>
'''
def toString(ele):
order = ['id','head','postag','from','lemma','relation']
result = '<'+ele.tag
for p in order:
result+=' {}="{}"'.format(p,ele[p])
return result+'/>'
doc = SimplifiedDoc(html)
ele = doc.word
print (toString(ele))
Result:
<word id="2357" head="2610" postag="p-s----n-" from="None" lemma="συ" relation="ExD_AP"/>
Strictly speaking, I have an answer to my own question, but it's going to take more work to actually implement it in a way I'd like. Here's how to do it.
Make a sub-class of the XMLFormatter (or HTMLFormatter if you're working with HTML), name it what you want. I chose "SortAttributes." Write the function "attributes" so that it will return a list of tuples: [(attribute1, value1), (attribute2, value2), etc.] in the order you want. Mine may look verbose, but I do it this way because I work with very inconsistent XML.
from bs4 import BeautifulSoup
from bs4.formatter import XMLFormatter
class SortAttributes(XMLFormatter):
def attributes(self, tag):
"""Reorder a tag's attributes however you want."""
attrib_order = ['id', 'head', 'postag', 'relation', 'form', 'lemma']
new_order = []
for element in attrib_order:
if element in tag.attrs:
new_order.append((element, tag[element]))
for pair in tag.attrs.items():
if pair not in new_order:
new_order.append(pair)
return new_order
xml_string = '''
<word form="συ" head="2610" id="2357" lemma="συ" postag="p-s----n-" relation="ExD_AP"/>
'''
soup = BeautifulSoup(xml_string, 'xml')
print(soup.encode(formatter=SortAttributes()))
This will output what I want:
<word id="2357" head="2610" postag="p-s----n-" relation="ExD_AP" form="συ" lemma="συ"/>
Conveniently, I can do this for an entire document with the same encode method. But if I write that to a file as a string, then all the tags are just strung together end to end. A sample would be like such:
<sentence id="783"><word id="2357" head="2610" postag="p-s----n-" relation="ExD_AP" form="συ" lemma="συ"/><word id="2358" head="2610" postag="p-s----n-" relation="ExD_AP" form="συ" lemma="συ"/><word id="2359" head="2610" postag="p-s----n-" relation="ExD_AP" form="συ" lemma="συ"/></sentence>
Instead of something I'd prefer:
<sentence id="783">
<word id="2357" head="2610" postag="p-s----n-" relation="ExD_AP" form="συ" lemma="συ"/>
<word id="2358" head="2610" postag="p-s----n-" relation="ExD_AP" form="συ" lemma="συ"/>
<word id="2359" head="2610" postag="p-s----n-" relation="ExD_AP" form="συ" lemma="συ"/>
</sentence>
To fix that, I can't just .prettify it because prettify rearranges the attributes back to alphabetical order. I'll have to go into more details with the XMLFormatter subclass instead. I hope someone finds this helpful in the future!

How to extract values inside an XML tag-python 3

Nelow is a sample XML file that i want to parse through and get the value between the year tags(2008)
<?xml version="1.0"?>
<data>
<country name="Liechtenstein">
<rank>1</rank>
<year>2008</year>
<gdppc>141100</gdppc>
<neighbor name="Austria" direction="E"/>
<neighbor name="Switzerland" direction="W"/>
</country>
<country name="Singapore">
<rank>4</rank>
<year>2011</year>
<gdppc>59900</gdppc>
<neighbor name="Malaysia" direction="N"/>
</country>
<country name="Panama">
<rank>68</rank>
<year>2011</year>
<gdppc>13600</gdppc>
<neighbor name="Costa Rica" direction="W"/>
<neighbor name="Colombia" direction="E"/>
</country>
</data>
Is there any way to extract the data between the year tags (2008.2011,etc) and print it using python?
Here is the code so far:
import xml.etree.ElementTree as ET
tree = ET.parse('country_data.xml')
root = tree.getroot()
for year in root.iter('year'):
print(year.attrib)
But when i try that code, nothing prints. Any ideas/suggestions?
It's fairly simple to do it using lxml:
from lxml import etree
tree = etree.parse("country_data.xml")
tree.xpath('//year/text()')
Output:
['2008', '2011', '2011']
You can use BeatifulSoup for this.
from bs4 import BeautifulSoup
years = []
with open('country_data.xml') as fp:
soup = BeautifulSoup(fp, 'lxml')
for country in soup.findAll('country'):
years_data = country.find('year')
years.append(years_data.contents[0])
print('Years: {}'.format(years))
Output:
Years: ['2008', '2011', '2011']

Python Gtk+3 Tutorial 23 Application not displaying what's expected?

I am developing a UI for a desktop app. I've seen that "Menus" are deprecated and hence decided to use the Gtk.Application class for the first time ever.
In the following link https://python-gtk-3-tutorial.readthedocs.io/en/latest/application.html#example
they provide the following code:
import sys
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import GLib, Gio, Gtk
# This would typically be its own file
MENU_XML="""
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<menu id="app-menu">
<section>
<attribute name="label" translatable="yes">Change label</attribute>
<item>
<attribute name="action">win.change_label</attribute>
<attribute name="target">String 1</attribute>
<attribute name="label" translatable="yes">String 1</attribute>
</item>
<item>
<attribute name="action">win.change_label</attribute>
<attribute name="target">String 2</attribute>
<attribute name="label" translatable="yes">String 2</attribute>
</item>
<item>
<attribute name="action">win.change_label</attribute>
<attribute name="target">String 3</attribute>
<attribute name="label" translatable="yes">String 3</attribute>
</item>
</section>
<section>
<item>
<attribute name="action">win.maximize</attribute>
<attribute name="label" translatable="yes">Maximize</attribute>
</item>
</section>
<section>
<item>
<attribute name="action">app.about</attribute>
<attribute name="label" translatable="yes">_About</attribute>
</item>
<item>
<attribute name="action">app.quit</attribute>
<attribute name="label" translatable="yes">_Quit</attribute>
<attribute name="accel"><Primary>q</attribute>
</item>
</section>
</menu>
</interface>
"""
class AppWindow(Gtk.ApplicationWindow):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# This will be in the windows group and have the "win" prefix
max_action = Gio.SimpleAction.new_stateful("maximize", None,
GLib.Variant.new_boolean(False))
max_action.connect("change-state", self.on_maximize_toggle)
self.add_action(max_action)
# Keep it in sync with the actual state
self.connect("notify::is-maximized",
lambda obj, pspec: max_action.set_state(
GLib.Variant.new_boolean(obj.props.is_maximized)))
lbl_variant = GLib.Variant.new_string("String 1")
lbl_action = Gio.SimpleAction.new_stateful("change_label", lbl_variant.get_type(),
lbl_variant)
lbl_action.connect("change-state", self.on_change_label_state)
self.add_action(lbl_action)
self.label = Gtk.Label(label=lbl_variant.get_string(),
margin=30)
self.add(self.label)
self.label.show()
def on_change_label_state(self, action, value):
action.set_state(value)
self.label.set_text(value.get_string())
def on_maximize_toggle(self, action, value):
action.set_state(value)
if value.get_boolean():
self.maximize()
else:
self.unmaximize()
class Application(Gtk.Application):
def __init__(self, *args, **kwargs):
super().__init__(*args, application_id="org.example.myapp",
flags=Gio.ApplicationFlags.HANDLES_COMMAND_LINE,
**kwargs)
self.window = None
self.add_main_option("test", ord("t"), GLib.OptionFlags.NONE,
GLib.OptionArg.NONE, "Command line test", None)
def do_startup(self):
Gtk.Application.do_startup(self)
action = Gio.SimpleAction.new("about", None)
action.connect("activate", self.on_about)
self.add_action(action)
action = Gio.SimpleAction.new("quit", None)
action.connect("activate", self.on_quit)
self.add_action(action)
builder = Gtk.Builder.new_from_string(MENU_XML, -1)
self.set_app_menu(builder.get_object("app-menu"))
def do_activate(self):
# We only allow a single window and raise any existing ones
if not self.window:
# Windows are associated with the application
# when the last one is closed the application shuts down
self.window = AppWindow(application=self, title="Main Window")
self.window.present()
def do_command_line(self, command_line):
options = command_line.get_options_dict()
# convert GVariantDict -> GVariant -> dict
options = options.end().unpack()
if "test" in options:
# This is printed on the main instance
print("Test argument recieved: %s" % options["test"])
self.activate()
return 0
def on_about(self, action, param):
about_dialog = Gtk.AboutDialog(transient_for=self.window, modal=True)
about_dialog.present()
def on_quit(self, action, param):
self.quit()
if __name__ == "__main__":
app = Application()
app.run(sys.argv)
Where they show the following image as supossed output:
Yet I'm running it and I'm getting this window displayed:
I'm just bashing "python3 application.py"
I guess I'd like to know if either I'm dumb or the code isn't what should be anymore.
It must be a problem with your distro and/or window manager, because in Linux Mint 19 I get this window:

Retrieve Combobox Name in Tkinter From Bind Event

I am trying to create a settings window in tkinter python, I am trying to create same callback function for all comboboxes, but I can't figure how to identify which combobox is the caller. example code:
def Open_settings_Form():
def callbackFunc(event):
print(event.widget.current())
global SETTINGS
Settings_Form = Toplevel(Main_Form)
Settings_Form.title("LOG Settings")
Settings_Form.geometry('900x300')
labelTop = Label(Settings_Form, text = "Log Parsing Order")
labelTop.grid(column=0, row=0)
combo0 = ttk.Combobox(Settings_Form, state="readonly", values=Log_Fields)
combo0.bind("<<ComboboxSelected>>", callbackFunc)
combo0.current(SETTINGS['LOG_Order'][0])
combo0.grid(column=0, row=1)
combo1 = ttk.Combobox(Settings_Form, state="readonly", values=Log_Fields)
combo1.bind("<<ComboboxSelected>>", callbackFunc)
combo1.current(SETTINGS['LOG_Order'][1])
combo1.grid(column=1, row=1)
combo2 = ttk.Combobox(Settings_Form, state="readonly", values=Log_Fields)
combo2.bind("<<ComboboxSelected>>", callbackFunc)
combo2.current(SETTINGS['LOG_Order'][2])
combo2.grid(column=2, row=1)
combo3 = ttk.Combobox(Settings_Form, state="readonly", values=Log_Fields)
combo3.bind("<<ComboboxSelected>>", callbackFunc)
combo3.current(SETTINGS['LOG_Order'][3])
combo3.grid(column=3, row=1)
In my callbackFunc I can get the selected item of combobox, but not able to find which combobox fire the event. Is there any other bind method than ComboboxSelected which I am using ?
Following are the methods / Params for event.widget object:
['_Misc__winfo_getint', '_Misc__winfo_parseitem', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_bind', '_configure', '_displayof', '_do', '_getboolean', '_getconfigure', '_getconfigure1', '_getdoubles', '_getints', '_grid_configure', '_gridconvvalue', '_last_child_ids', '_name', '_nametowidget', '_noarg_', '_options', '_register', '_report_exception', '_root', '_setup', '_subst_format', '_subst_format_str', '_substitute', '_tclCommands', '_w', '_windowingsystem', 'after', 'after_cancel', 'after_idle', 'anchor', 'bbox', 'bell', 'bind', 'bind_all', 'bind_class', 'bindtags', 'cget', 'children', 'clipboard_append', 'clipboard_clear', 'clipboard_get', 'columnconfigure', 'config', 'configure', 'current', 'delete', 'deletecommand', 'destroy', 'event_add', 'event_delete', 'event_generate', 'event_info', 'focus', 'focus_displayof', 'focus_force', 'focus_get', 'focus_lastfor', 'focus_set', 'forget', 'get', 'getboolean', 'getdouble', 'getint', 'getvar', 'grab_current', 'grab_release', 'grab_set', 'grab_set_global', 'grab_status', 'grid', 'grid_anchor', 'grid_bbox', 'grid_columnconfigure', 'grid_configure', 'grid_forget', 'grid_info', 'grid_location', 'grid_propagate', 'grid_remove', 'grid_rowconfigure', 'grid_size', 'grid_slaves', 'icursor', 'identify', 'image_names', 'image_types', 'index', 'info', 'insert', 'instate', 'keys', 'lift', 'location', 'lower', 'mainloop', 'master', 'nametowidget', 'option_add', 'option_clear', 'option_get', 'option_readfile', 'pack', 'pack_configure', 'pack_forget', 'pack_info', 'pack_propagate', 'pack_slaves', 'place', 'place_configure', 'place_forget', 'place_info', 'place_slaves', 'propagate', 'quit', 'register', 'rowconfigure', 'scan_dragto', 'scan_mark', 'select_adjust', 'select_clear', 'select_from', 'select_present', 'select_range', 'select_to', 'selection_adjust', 'selection_clear', 'selection_from', 'selection_get', 'selection_handle', 'selection_own', 'selection_own_get', 'selection_present', 'selection_range', 'selection_to', 'send', 'set', 'setvar', 'size', 'slaves', 'state', 'tk', 'tk_bisque', 'tk_focusFollowsMouse', 'tk_focusNext', 'tk_focusPrev', 'tk_setPalette', 'tk_strictMotif', 'tkraise', 'unbind', 'unbind_all', 'unbind_class', 'update', 'update_idletasks', 'validate', 'wait_variable', 'wait_visibility', 'wait_window', 'waitvar', 'widgetName', 'winfo_atom', 'winfo_atomname', 'winfo_cells', 'winfo_children', 'winfo_class', 'winfo_colormapfull', 'winfo_containing', 'winfo_depth', 'winfo_exists', 'winfo_fpixels', 'winfo_geometry', 'winfo_height', 'winfo_id', 'winfo_interps', 'winfo_ismapped', 'winfo_manager', 'winfo_name', 'winfo_parent', 'winfo_pathname', 'winfo_pixels', 'winfo_pointerx', 'winfo_pointerxy', 'winfo_pointery', 'winfo_reqheight', 'winfo_reqwidth', 'winfo_rgb', 'winfo_rootx', 'winfo_rooty', 'winfo_screen', 'winfo_screencells', 'winfo_screendepth', 'winfo_screenheight', 'winfo_screenmmheight', 'winfo_screenmmwidth', 'winfo_screenvisual', 'winfo_screenwidth', 'winfo_server', 'winfo_toplevel', 'winfo_viewable', 'winfo_visual', 'winfo_visualid', 'winfo_visualsavailable', 'winfo_vrootheight', 'winfo_vrootwidth', 'winfo_vrootx', 'winfo_vrooty', 'winfo_width', 'winfo_x', 'winfo_y', 'xview', 'xview_moveto', 'xview_scroll']
Thanks a lot
You can pass a name to your combobox widget and retrieve it through event.widget callback.
import tkinter as tk
from tkinter import ttk
root = tk.Tk()
def details(event):
print (event.widget)
combo1 = ttk.Combobox(root,name="box1")
combo1["values"] = ("A","B","C")
combo1.pack()
combo1.bind("<<ComboboxSelected>>",details)
combo2 = ttk.Combobox(root,name="box2")
combo2["values"] = ("D","E","F")
combo2.pack()
combo2.bind("<<ComboboxSelected>>",details)
root.mainloop()

Resources