python3+PyQt5 使用自定義委託控制資料項的展示和 編輯
阿新 • • 發佈:2019-01-10
委託可以純粹用來控制外觀或者提供自定義編輯器用來完成控制編輯操作,又或者用於這兩方面。本文用python3+pyQt5改寫實現了python Qt Gui快速程式設計這本書的14章的例子。通過委託,實現了owner和country域和組合框組合在一起,TEU與微調框組合在一起。還增添了一個export按鈕,用於將資料匯出來成指定的txt格式的文字。
/home/yrd/eric_workspace/chap14/ships_delegate_ans/richtextlineedit.py
#!/usr/bin/env python3
import platform
import sys
import html
from PyQt5.QtCore import QSize, Qt,pyqtSignal
from PyQt5.QtGui import QColor, QFont,QFontMetrics, QIcon, QKeySequence, QPixmap,QTextCharFormat
from PyQt5.QtWidgets import QAction,QApplication,QMenu,QTextEdit
class RichTextLineEdit(QTextEdit):
returnPressed=pyqtSignal()
(Bold, Italic, Underline, StrikeOut, Monospaced, Sans, Serif,
NoSuperOrSubscript, Subscript, Superscript) = range(10 )
def __init__(self, parent=None):
super(RichTextLineEdit, self).__init__(parent)
self.monofamily = "courier"
self.sansfamily = "helvetica"
self.seriffamily = "times"
self.setLineWrapMode(QTextEdit.NoWrap)
self.setTabChangesFocus(True)
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
fm = QFontMetrics(self.font())
h = int(fm.height() * (1.4 if platform.system() == "Windows"
else 1.2))
self.setMinimumHeight(h)
self.setMaximumHeight(int(h * 1.2))
self.setToolTip("Press <b>Ctrl+M</b> for the text effects "
"menu and <b>Ctrl+K</b> for the color menu")
def toggleItalic(self):
self.setFontItalic(not self.fontItalic())
def toggleUnderline(self):
self.setFontUnderline(not self.fontUnderline())
def toggleBold(self):
self.setFontWeight(QFont.Normal
if self.fontWeight() > QFont.Normal else QFont.Bold)
def sizeHint(self):
return QSize(self.document().idealWidth() + 5,
self.maximumHeight())
def minimumSizeHint(self):
fm = QFontMetrics(self.font())
return QSize(fm.width("WWWW"), self.minimumHeight())
def contextMenuEvent(self, event):
self.textEffectMenu()
def keyPressEvent(self, event):
if event.modifiers() & Qt.ControlModifier:
handled = False
if event.key() == Qt.Key_B:
self.toggleBold()
handled = True
elif event.key() == Qt.Key_I:
self.toggleItalic()
handled = True
elif event.key() == Qt.Key_K:
self.colorMenu()
handled = True
elif event.key() == Qt.Key_M:
self.textEffectMenu()
handled = True
elif event.key() == Qt.Key_U:
self.toggleUnderline()
handled = True
if handled:
event.accept()
return
if event.key() in (Qt.Key_Enter, Qt.Key_Return):
self.returnPressed.emit()
event.accept()
else:
QTextEdit.keyPressEvent(self, event)
def colorMenu(self):
pixmap = QPixmap(22, 22)
menu = QMenu("Colour")
for text, color in (
("&Black", Qt.black),
("B&lue", Qt.blue),
("Dark Bl&ue", Qt.darkBlue),
("&Cyan", Qt.cyan),
("Dar&k Cyan", Qt.darkCyan),
("&Green", Qt.green),
("Dark Gr&een", Qt.darkGreen),
("M&agenta", Qt.magenta),
("Dark Mage&nta", Qt.darkMagenta),
("&Red", Qt.red),
("&Dark Red", Qt.darkRed)):
color = QColor(color)
pixmap.fill(color)
action = menu.addAction(QIcon(pixmap), text, self.setColor)
action.setData(color)
self.ensureCursorVisible()
menu.exec_(self.viewport().mapToGlobal(
self.cursorRect().center()))
def setColor(self):
action = self.sender()
if action is not None and isinstance(action, QAction):
color = QColor(action.data())
if color.isValid():
self.setTextColor(color)
def textEffectMenu(self):
format = self.currentCharFormat()
menu = QMenu("Text Effect")
for text, shortcut, data, checked in (
("&Bold", "Ctrl+B", RichTextLineEdit.Bold,
self.fontWeight() > QFont.Normal),
("&Italic", "Ctrl+I", RichTextLineEdit.Italic,
self.fontItalic()),
("Strike &out", None, RichTextLineEdit.StrikeOut,
format.fontStrikeOut()),
("&Underline", "Ctrl+U", RichTextLineEdit.Underline,
self.fontUnderline()),
("&Monospaced", None, RichTextLineEdit.Monospaced,
format.fontFamily() == self.monofamily),
("&Serifed", None, RichTextLineEdit.Serif,
format.fontFamily() == self.seriffamily),
("S&ans Serif", None, RichTextLineEdit.Sans,
format.fontFamily() == self.sansfamily),
("&No super or subscript", None,
RichTextLineEdit.NoSuperOrSubscript,
format.verticalAlignment() ==
QTextCharFormat.AlignNormal),
("Su&perscript", None, RichTextLineEdit.Superscript,
format.verticalAlignment() ==
QTextCharFormat.AlignSuperScript),
("Subs&cript", None, RichTextLineEdit.Subscript,
format.verticalAlignment() ==
QTextCharFormat.AlignSubScript)):
action = menu.addAction(text, self.setTextEffect)
if shortcut is not None:
action.setShortcut(QKeySequence(shortcut))
action.setData(data)
action.setCheckable(True)
action.setChecked(checked)
self.ensureCursorVisible()
menu.exec_(self.viewport().mapToGlobal(
self.cursorRect().center()))
def setTextEffect(self):
action = self.sender()
if action is not None and isinstance(action, QAction):
what = action.data()
if what == RichTextLineEdit.Bold:
self.toggleBold()
return
if what == RichTextLineEdit.Italic:
self.toggleItalic()
return
if what == RichTextLineEdit.Underline:
self.toggleUnderline()
return
format = self.currentCharFormat()
if what == RichTextLineEdit.Monospaced:
format.setFontFamily(self.monofamily)
elif what == RichTextLineEdit.Serif:
format.setFontFamily(self.seriffamily)
elif what == RichTextLineEdit.Sans:
format.setFontFamily(self.sansfamily)
if what == RichTextLineEdit.StrikeOut:
format.setFontStrikeOut(not format.fontStrikeOut())
if what == RichTextLineEdit.NoSuperOrSubscript:
format.setVerticalAlignment(
QTextCharFormat.AlignNormal)
elif what == RichTextLineEdit.Superscript:
format.setVerticalAlignment(
QTextCharFormat.AlignSuperScript)
elif what == RichTextLineEdit.Subscript:
format.setVerticalAlignment(
QTextCharFormat.AlignSubScript)
self.mergeCurrentCharFormat(format)
def toSimpleHtml(self):
htmltext = ""
black = QColor(Qt.black)
block = self.document().begin()
while block.isValid():
iterator = block.begin()
while iterator != block.end():
fragment = iterator.fragment()
if fragment.isValid():
format = fragment.charFormat()
family = format.fontFamily()
color = format.foreground().color()
text=html.escape(fragment.text())
if (format.verticalAlignment() ==
QTextCharFormat.AlignSubScript):
text = "<sub>{0}</sub>".format(text)
elif (format.verticalAlignment() ==
QTextCharFormat.AlignSuperScript):
text = "<sup>{0}</sup>".format(text)
if format.fontUnderline():
text = "<u>{0}</u>".format(text)
if format.fontItalic():
text = "<i>{0}</i>".format(text)
if format.fontWeight() > QFont.Normal:
text = "<b>{0}</b>".format(text)
if format.fontStrikeOut():
text = "<s>{0}</s>".format(text)
if color != black or family:
attribs = ""
if color != black:
attribs += ' color="{0}"'.format(color.name())
if family:
attribs += ' face="{0}"'.format(family)
text = "<font{0}>{1}</font>".format(attribs,text)
htmltext += text
iterator += 1
block = block.next()
return htmltext
if __name__ == "__main__":
def printout(lineedit):
print(str(lineedit.toHtml()))
print(str(lineedit.toPlainText()))
print(str(lineedit.toSimpleHtml()))
app = QApplication(sys.argv)
lineedit = RichTextLineEdit()
lineedit.returnPressed.connect(lambda:printout(lineedit))
lineedit.show()
lineedit.setWindowTitle("RichTextEdit")
app.exec_()
/home/yrd/eric_workspace/chap14/ships_delegate_ans/ships_ans.py
#!/usr/bin/env python3
import platform
import re
from PyQt5.QtCore import (QAbstractTableModel, QDataStream, QFile,
QIODevice, QModelIndex,QRegExp, QSize,QVariant, Qt,pyqtSignal)
from PyQt5.QtGui import QColor,QTextDocument
from PyQt5.QtWidgets import QApplication, QWidget,QComboBox, QLineEdit,QSpinBox, QStyle,QStyledItemDelegate, QTextEdit
import richtextlineedit
NAME, OWNER, COUNTRY, DESCRIPTION, TEU = range(5)
MAGIC_NUMBER = 0x570C4
FILE_VERSION = 1
class Ship(object):
def __init__(self, name, owner, country, teu=0, description=""):
self.name = name
self.owner = owner
self.country = country
self.teu = teu
self.description = description
def __hash__(self):
return super(Ship, self).__hash__()
def __lt__(self, other):
return bool(self.name.lower()<other.name.lower())
def __eq__(self, other):
return bool(self.name.lower()==other.name.lower())
class ShipTableModel(QAbstractTableModel):
dataChanged = pyqtSignal(QModelIndex,QModelIndex)
def __init__(self, filename=""):
super(ShipTableModel, self).__init__()
self.filename = filename
self.dirty = False
self.ships = []
self.owners = set()
self.countries = set()
def sortByName(self):
self.beginResetModel()
self.ships = sorted(self.ships)
self.endResetModel()
def sortByTEU(self):
self.beginResetModel()
ships = [(ship.teu, ship) for ship in self.ships]
ships.sort()
self.ships = [ship for teu, ship in ships]
self.endResetModel()
def sortByCountryOwner(self):
self.beginResetModel()
self.ships = sorted(self.ships,
key=lambda x: (x.country, x.owner, x.name))
self.endResetModel()
def flags(self, index):
if not index.isValid():
return Qt.ItemIsEnabled
return Qt.ItemFlags(QAbstractTableModel.flags(self, index)|
Qt.ItemIsEditable)
def data(self, index, role=Qt.DisplayRole):
if (not index.isValid() or
not (0 <= index.row() < len(self.ships))):
return QVariant()
ship = self.ships[index.row()]
column = index.column()
if role == Qt.DisplayRole:
if column == NAME:
return ship.name
elif column == OWNER:
return ship.owner
elif column == COUNTRY:
return ship.country
elif column == DESCRIPTION:
return ship.description
elif column == TEU:
return "{0}".format(ship.teu)
elif role == Qt.TextAlignmentRole:
if column == TEU:
return QVariant(int(Qt.AlignRight|Qt.AlignVCenter))
return QVariant(int(Qt.AlignLeft|Qt.AlignVCenter))
elif role == Qt.TextColorRole and column == TEU:
if ship.teu < 80000:
return QVariant(QColor(Qt.black))
elif ship.teu < 100000:
return QVariant(QColor(Qt.darkBlue))
elif ship.teu < 120000:
return QVariant(QColor(Qt.blue))
else:
return QVariant(QColor(Qt.red))
elif role == Qt.BackgroundColorRole:
if ship.country in ("Bahamas", "Cyprus", "Denmark",
"France", "Germany", "Greece"):
return QVariant(QColor(250, 230, 250))
elif ship.country in ("Hong Kong", "Japan", "Taiwan"):
return QVariant(QColor(250, 250, 230))
elif ship.country in ("Marshall Islands",):
return QVariant(QColor(230, 250, 250))
else:
return QVariant(QColor(210, 230, 230))
elif role == Qt.ToolTipRole:
msg = "<br>(minimum of 3 characters)"
if column == NAME:
return ship.name + msg
elif column == OWNER:
return ship.owner + msg
elif column == COUNTRY:
return ship.country + msg
elif column == DESCRIPTION:
return ship.description
elif column == TEU:
return "{0} twenty foot equivalents".format(ship.teu)
return QVariant()
def headerData(self, section, orientation, role=Qt.DisplayRole):
if role == Qt.TextAlignmentRole:
if orientation == Qt.Horizontal:
return QVariant(int(Qt.AlignLeft|Qt.AlignVCenter))
return QVariant(int(Qt.AlignRight|Qt.AlignVCenter))
if role != Qt.DisplayRole:
return QVariant()
if orientation == Qt.Horizontal:
if section == NAME:
return "Name"
elif section == OWNER:
return "Owner"
elif section == COUNTRY:
return "Country"
elif section == DESCRIPTION:
return "Description"
elif section == TEU:
return "TEU"
return QVariant(int(section + 1))
def rowCount(self, index=QModelIndex()):
return len(self.ships)
def columnCount(self, index=QModelIndex()):
return 5
def setData(self, index, value, role=Qt.EditRole):
if index.isValid() and 0 <= index.row() < len(self.ships):
ship = self.ships[index.row()]
column = index.column()
if column == NAME:
ship.name = value
elif column == OWNER:
ship.owner = value
elif column == COUNTRY:
ship.country = value
elif column == DESCRIPTION:
ship.description = value
elif column == TEU:
if str(value).isdecimal():
ship.teu=int(value)
self.dirty = True
self.dataChanged[QModelIndex,QModelIndex].emit(index,index)
return True
return False
def insertRows(self, position, rows=1, index=QModelIndex()):
self.beginInsertRows(QModelIndex(), position, position + rows - 1)
for row in range(rows):
self.ships.insert(position + row,
Ship(" Unknown", " Unknown", " Unknown"))
self.endInsertRows()
self.dirty = True
return True
def removeRows(self, position, rows=1, index=QModelIndex()):
self.beginRemoveRows(QModelIndex(), position, position + rows - 1)
self.ships = (self.ships[:position] + self.ships[position + rows:])
self.endRemoveRows()
self.dirty = True
return True
def load(self):
exception = None
fh = None
try:
if not self.filename:
raise IOError("no filename specified for loading")
fh = QFile(self.filename)
if not fh.open(QIODevice.ReadOnly):
raise IOError(str(fh.errorString()))
stream = QDataStream(fh)
magic = stream.readInt32()
if magic != MAGIC_NUMBER:
raise IOError("unrecognized file type")
fileVersion = stream.readInt16()
if fileVersion != FILE_VERSION:
raise IOError("unrecognized file type version")
self.ships = []
while not stream.atEnd():
name = ""
owner = ""
country = ""
description = ""
name=stream.readQString()
owner=stream.readQString()
country=stream.readQString()
description=stream.readQString()
teu = stream.readInt32()
self.ships.append(Ship(name, owner, country, teu,
description))
self.owners.add(str(owner))
self.countries.add(str(country))
self.dirty = False
except IOError as e:
exception = e
finally:
if fh is not None:
fh.close()
if exception is not None:
raise exception
def save(self):
exception = None
fh = None
try:
if not self.filename:
raise IOError("no filename specified for saving")
fh = QFile(self.filename)
if not fh.open(QIODevice.WriteOnly):
raise IOError(str(fh.errorString()))
stream = QDataStream(fh)
stream.writeInt32(MAGIC_NUMBER)
stream.writeInt16(FILE_VERSION)
stream.setVersion(QDataStream.Qt_5_7)
for ship in self.ships:
stream.writeQString(ship.name)
stream.writeQString(ship.owner)
stream.writeQString(ship.country)
stream.writeQString(ship.description)
stream.writeInt32(ship.teu)
self.dirty = False
except IOError as e:
exception = e
finally:
if fh is not None:
fh.close()
if exception is not None:
raise exception
class ShipDelegate(QStyledItemDelegate):
commitData = pyqtSignal(QWidget)
closeEditor = pyqtSignal(QWidget)
def __init__(self, parent=None):
super(ShipDelegate, self).__init__(parent)
def paint(self, painter, option, index):
if index.column() == DESCRIPTION:
text = str(index.model().data(index))
palette = QApplication.palette()
document = QTextDocument()
document.setDefaultFont(option.font)
if option.state & QStyle.State_Selected:
#document.setHtml("<font color={0}>{1}</font>".format("#FF0000",text))
document.setHtml("<font color={0}>{1}</font>".format(palette.highlightedText().color().name(),text))
else:
document.setHtml(text)
color = (palette.highlight().color()
if option.state & QStyle.State_Selected
else QColor(index.model().data(index,
Qt.BackgroundColorRole)))
#print(palette.highlight().color().name())
painter.save()
painter.fillRect(option.rect, color)
painter.translate(option.rect.x(), option.rect.y())
document.drawContents(painter)
painter.restore()
else:
QStyledItemDelegate.paint(self, painter, option, index)
def sizeHint(self, option, index):
fm = option.fontMetrics
if index.column() == TEU:
return QSize(fm.width("9,999,999"), fm.height())
if index.column() == DESCRIPTION:
text = str(index.model().data(index))
document = QTextDocument()
document.setDefaultFont(option.font)
document.setHtml(text)
return QSize(document.idealWidth() + 5, fm.height())
return QStyledItemDelegate.sizeHint(self, option, index)
def createEditor(self, parent, option, index):
if index.column() == TEU:
spinbox = QSpinBox(parent)
spinbox.setRange(0, 200000)
spinbox.setSingleStep(1000)
spinbox.setAlignment(Qt.AlignRight|Qt.AlignVCenter)
#add by yrd
spinbox.valueChanged.connect(self.commitAndCloseEditor)
return spinbox
elif index.column() == OWNER:
combobox = QComboBox(parent)
combobox.addItems(sorted(index.model().owners))
combobox.setEditable(True)
#add by yrd
#combobox.currentTextChanged.connect(self.commitAndCloseEditor)
combobox.editTextChanged.connect(self.commitAndCloseEditor)
return combobox
elif index.column() == COUNTRY:
combobox = QComboBox(parent)
combobox.addItems(sorted(index.model().countries))
combobox.setEditable(True)
#add by yrd
combobox.editTextChanged.connect(self.commitAndCloseEditor)
return combobox
elif index.column() == NAME:
editor = QLineEdit(parent)
editor.returnPressed.connect(self.commitAndCloseEditor)
return editor
elif index.column() == DESCRIPTION:
editor = richtextlineedit.RichTextLineEdit(parent)
editor.returnPressed.connect(self.commitAndCloseEditor)
return editor
else:
return QStyledItemDelegate.createEditor(self, parent, option,
index)
def commitAndCloseEditor(self):
editor = self.sender()
if isinstance(editor, (QTextEdit, QLineEdit,QSpinBox,QComboBox)):
self.commitData[QWidget].emit(editor)
self.closeEditor[QWidget].emit(editor)
def setEditorData(self, editor, index):
text = index.model().data(index, Qt.DisplayRole)
if index.column() == TEU:
value=int(re.sub("[., ]","",text))
editor.setValue(value)
elif index.column() in (OWNER, COUNTRY):
i = editor.findText(text)
if i == -1:
i = 0
editor.setCurrentIndex(i)
elif index.column() == NAME:
editor.setText(text)
elif index.column() == DESCRIPTION:
editor.setHtml(text)
else:
QStyledItemDelegate.setEditorData(self, editor, index)
def setModelData(self, editor, model, index):
if index.column() == TEU:
model.setData(index, editor.value())
elif index.column() in (OWNER, COUNTRY):
text = editor.currentText()
if len(text) >= 3:
model.setData(index, text)
elif index.column() == NAME:
text = editor.text()
if len(text) >= 3:
model.setData(index, text)
elif index.column() == DESCRIPTION:
model.setData(index, editor.toSimpleHtml())
else:
QStyledItemDelegate.setModelData(self, editor, model, index)
def generateFakeShips():
for name, owner, country, teu, description in (
("Emma M\u00E6rsk", "M\u00E6rsk Line", "Denmark", 151687,
"<b>W\u00E4rtsil\u00E4-Sulzer RTA96-C</b> main engine,"
"<font color=green>109,000 hp</font>"),
("MSC Pamela", "MSC", "Liberia", 90449,
"Draft <font color=green>15m</font>"),
("Colombo Express", "Hapag-Lloyd", "Germany", 93750,
"Main engine, <font color=green>93,500 hp</font>"),
("Houston Express", "Norddeutsche Reederei", "Germany", 95000,
"Features a <u>twisted leading edge full spade rudder</u>. "
"Sister of <i>Savannah Express</i>"),
("Savannah Express", "Norddeutsche Reederei", "Germany", 95000,
"Sister of <i>Houston Express</i>"),
("MSC Susanna", "MSC", "Liberia", 90449, ""),
("Eleonora M\u00E6rsk", "M\u00E6rsk Line", "Denmark", 151687,
"Captain <i>Hallam</i>"),
("Estelle M\u00E6rsk", "M\u00E6rsk Line", "Denmark", 151687,
"Captain <i>Wells</i>"),
("Evelyn M\u00E6rsk", "M\u00E6rsk Line", "Denmark", 151687,
"Captain <i>Byrne</i>"),
("Georg M\u00E6rsk", "M\u00E6rsk Line", "Denmark", 97933, ""),
("Gerd M\u00E6rsk", "M\u00E6rsk Line", "Denmark", 97933, ""),
("Gjertrud M\u00E6rsk", "M\u00E6rsk Line", "Denmark", 97933, ""),
("Grete M\u00E6rsk", "M\u00E6rsk Line", "Denmark", 97933, ""),
("Gudrun M\u00E6rsk", "M\u00E6rsk Line", "Denmark", 97933, ""),
("Gunvor M\u00E6rsk", "M\u00E6rsk Line", "Denmark", 97933, ""),
("CSCL Le Havre", "Danaos Shipping", "Cyprus", 107200, ""),
("CSCL Pusan", "Danaos Shipping", "Cyprus", 107200,
"Captain <i>Watts</i>"),
("Xin Los Angeles", "China Shipping Container Lines (CSCL)",
"Hong Kong", 107200, ""),
("Xin Shanghai", "China Shipping Container Lines (CSCL)", "Hong Kong",
107200, ""),
("Cosco Beijing", "Costamare Shipping", "Greece", 99833, ""),
("Cosco Hellas", "Costamare Shipping", "Greece", 99833, ""),
("Cosco Guangzho", "Costamare Shipping", "Greece", 99833, ""),
("Cosco Ningbo", "Costamare Shipping", "Greece", 99833, ""),
("Cosco Yantian", "Costamare Shipping", "Greece", 99833, ""),
("CMA CGM Fidelio", "CMA CGM", "France", 99500, ""),
("CMA CGM Medea", "CMA CGM", "France", 95000, ""),
("CMA CGM Norma", "CMA CGM", "Bahamas", 95000, ""),
("CMA CGM Rigoletto", "CMA CGM", "France", 99500, ""),
("Arnold M\u00E6rsk", "M\u00E6rsk Line", "Denmark", 93496,
"Captain <i>Morrell</i>"),
("Anna M\u00E6rsk", "M\u00E6rsk Line", "Denmark", 93496,
"Captain <i>Lockhart</i>"),
("Albert M\u00E6rsk", "M\u00E6rsk Line", "Denmark", 93496,
"Captain <i>Tallow</i>"),
("Adrian M\u00E6rsk", "M\u00E6rsk Line", "Denmark", 93496,
"Captain <i>G. E. Ericson</i>"),
("Arthur M\u00E6rsk", "M\u00E6rsk Line", "Denmark", 93496, ""),
("Axel M\u00E6rsk", "M\u00E6rsk Line", "Denmark", 93496, ""),
("NYK Vega", "Nippon Yusen Kaisha", "Panama", 97825, ""),
("MSC Esthi", "MSC", "Liberia", 99500, ""),
("MSC Chicago", "Offen Claus-Peter", "Liberia", 90449, ""),
("MSC Bruxelles", "Offen Claus-Peter", "Liberia", 90449, ""),
("MSC Roma", "Offen Claus-Peter", "Liberia", 99500, ""),
("MSC Madeleine", "MSC", "Liberia", 107551, ""),
("MSC Ines", "MSC", "Liberia", 107551, ""),
("Hannover Bridge", "Kawasaki Kisen Kaisha", "Japan", 99500, ""),
("Charlotte M\u00E6rsk", "M\u00E6rsk Line", "Denmark", 91690, ""),
("Clementine M\u00E6rsk", "M\u00E6rsk Line", "Denmark", 91690, ""),
("Columbine M\u00E6rsk", "M\u00E6rsk Line", "Denmark", 91690, ""),
("Cornelia M\u00E6rsk", "M\u00E6rsk Line", "Denmark", 91690, ""),
("Chicago Express", "Hapag-Lloyd", "Germany", 93750, ""),
("Kyoto Express", "Hapag-Lloyd", "Germany", 93750, ""),
("Clifford M\u00E6rsk", "M\u00E6rsk Line", "Denmark", 91690, ""),
("Sally M\u00E6rsk", "M\u00E6rsk Line", "Denmark", 91690, ""),
("Sine M\u00E6rsk", "M\u00E6rsk Line", "Denmark", 91690, ""),
("Skagen M\u00E6rsk", "M\u00E6rsk Line", "Denmark", 91690, ""),
("Sofie M\u00E6rsk", "M\u00E6rsk Line", "Denmark", 91690, ""),
("Sor\u00F8 M\u00E6rsk", "M\u00E6rsk Line", "Denmark", 91690, ""),
("Sovereing M\u00E6rsk", "M\u00E6rsk Line", "Denmark", 91690, ""),
("Susan M\u00E6rsk", "M\u00E6rsk Line", "Denmark", 91690, ""),
("Svend M\u00E6rsk", "M\u00E6rsk Line", "Denmark", 91690, ""),
("Svendborg M\u00E6rsk", "M\u00E6rsk Line", "Denmark", 91690, ""),
("A.P. M\u00F8ller", "M\u00E6rsk Line", "Denmark", 91690,
"Captain <i>Ferraby</i>"),
("Caroline M\u00E6rsk", "M\u00E6rsk Line", "Denmark", 91690, ""),
("Carsten M\u00E6rsk", "M\u00E6rsk Line", "Denmark", 91690, ""),
("Chastine M\u00E6rsk", "M\u00E6rsk Line", "Denmark", 91690, ""),
("Cornelius M\u00E6rsk", "M\u00E6rsk Line", "Denmark", 91690, ""),
("CMA CGM Otello", "CMA CGM", "France", 91400, ""),
("CMA CGM Tosca", "CMA CGM", "France", 91400, ""),
("CMA CGM Nabucco", "CMA CGM", "France", 91400, ""),
("CMA CGM La Traviata", "CMA CGM", "France", 91400, ""),
("CSCL Europe", "Danaos Shipping", "Cyprus", 90645, ""),
("CSCL Africa", "Seaspan Container Line", "Cyprus", 90645, ""),
("CSCL America", "Danaos Shipping ", "Cyprus", 90645, ""),
("CSCL Asia", "Seaspan Container Line", "Hong Kong", 90645, ""),
("CSCL Oceania", "Seaspan Container Line", "Hong Kong", 90645,
"Captain <i>Baker</i>"),
("M\u00E6rsk Seville", "Blue Star GmbH", "Liberia", 94724, ""),
("M\u00E6rsk Santana", "Blue Star GmbH", "Liberia", 94724, ""),
("M\u00E6rsk Sheerness", "Blue Star GmbH", "Liberia", 94724, ""),
("M\u00E6rsk Sarnia", "Blue Star GmbH", "Liberia", 94724, ""),
("M\u00E6rsk Sydney", "Blue Star GmbH", "Liberia", 94724, ""),
("MSC Heidi", "MSC", "Panama", 95000, ""),
("MSC Rania", "MSC", "Panama", 95000, ""),
("MSC Silvana", "MSC", "Panama", 95000, ""),
("M\u00E6rsk Stralsund", "Blue Star GmbH", "Liberia", 95000, ""),
("M\u00E6rsk Saigon", "Blue Star GmbH", "Liberia", 95000, ""),
("M\u00E6rsk Seoul", "Blue Star Ship Managment GmbH", "Germany",
95000, ""),
("M\u00E6rsk Surabaya", "Offen Claus-Peter", "Germany", 98400, ""),
("CMA CGM Hugo", "NSB Niederelbe", "Germany", 90745, ""