[PyKDE] scrollview
Andrew Dalke
adalke at mindspring.com
Wed May 8 12:04:00 BST 2002
Hey all,
I'm trying to use the QScrollView and I'm having some problems.
The problem is in my fundamental understanding of how that widget
works, and how Qt works, so I'll start be describing what I want to
do. I've attached the code I'm working with if you want to go ahead
and look at it. I've also attached the image showing what's wrong
(it's only 30K so I figured it was okay to include it in this mail
rather than pointing to an external URL).
I'm writing a sequence alignment program, for biological sequences
like DNA. These look like
Name1 AATGCA--ACGTTT----AT--
Name2 --TG-A--ACGTTTAACTATGC
Name3 AATTGATTACGTTTGGGGAT--
The sequences can be very long, even into the tens of thousands of
bases (each letter is a DNA "base"). There are a couple of ways to
handle that. One is to use a horizontal scrollbar to slide things
back and forth, as in
_________________
Name1 |AATGCA--ACGTTT---| ]
Name2 |--TG-A--ACGTTTAAC| ]-- viewport doesn't show the last 5
characters
Name3 |AATTGATTACGTTTGGG| ]
|[=============--]| <--- scrollbar all the way to the left
_________________
Name1 |A--ACGTTT----AT--| ]
Name2 |A--ACGTTTAACTATGC| ]-- viewport doesn't show the first 5
characters
Name3 |ATTACGTTTGGGGAT--| ]
|[--=============]| <--- scrollbar all the way to the right
Each base (letter) might be drawn differently (eg, fg/bg color, italic,
underline) but for now the font name and size is the same for everything.
These would indicate the base name (eg, "G" is drawn with a yellow
background) or amount of consensus (eg, if >90% of the letters in a given
column are the same then draw it in bold).
To implement this I made an AlignmentView, which derives from QScrollView.
This draws the viewport as shown above.
The AlignmentView contains a list of SequenceView instances. A SequenceView
mostly works to define the widget size, break the display up into
base-sized segments, and (eventually) allow selection and other sorts of
interaction.
A SequenceView contains a SequenceBaseView. The SequenceBaseView is
in charge of painting a base in a cell, and can set colors, etc. as
appropriate. The SequenceView also understands paint events so that
only visible bases are painted, to help with performance.
But what I did doesn't work. Here are the problems I see:
- only a few of the bases are drawn -- the paint event isn't as wide
as the viewport, so only a few characters are shown. For the first
line it's enough, but for the other lines it only shows the first
three characters.
- the scrollbar size is too large. The effective width of the scroll
area is somewhat larger than what I told it.
- the paint height for the first line is 35 pixels while for the
other lines is 30 pixels. That leaves gaps between the lines when
there should be no gaps.
I don't know how to fix these. I looked in Boudewijn Rempt's book and
in Dalheimer's book but couldn't find relevant pointers.
Suggestions?
Andrew
dalke at dalkescientific.com
-------------- next part --------------
from qt import *
import sys, operator
class SequenceBaseView:
def __init__(self, seq):
self.seq = seq
def __len__(self):
return len(self.seq)
def paintBase(self, paint, index, rect, ascent):
assert index >= 0
try:
c = self.seq[index]
except IndexError:
return
paint.drawText(rect, Qt.AlignHCenter, c)
# paint.drawRect(rect)
class SequenceView(QWidget):
def __init__(self, baseview, *args):
QWidget.__init__(self, *args)
self._baseView = baseview
def sizeHint(self):
metrics = self.fontMetrics()
h = metrics.height()
w = metrics.maxWidth()
return QSize(w * 40, h)
def minimumSizeHint(self):
metrics = self.fontMetrics()
h = metrics.height()
w = metrics.maxWidth()
return QSize(w * 10, h)
def maximumSizeHint(self):
metrics = self.fontMetrics()
h = metrics.height()
w = metrics.maxWidth()
return QSize(w * len(self._baseView), h)
def setFont(self, font):
QWidget.setFont(self, font)
self.update()
def setBaseView(self, obj):
self._baseView = obj
def baseView(self):
return self._baseView
def paintEvent(self, event):
paint = QPainter(self)
metrics = paint.fontMetrics()
ascent = metrics.ascent()
char_w = metrics.maxWidth()
char_h = metrics.height()
rect = event.rect()
x = rect.x()
y = rect.y()
width = rect.width()
height = rect.height()
if y > char_h or y + height < 0:
return
i = x / char_w
j = (x + width) / char_w + 1
text_x = i * char_w
text_y = ascent
for index in range(i, j):
self._baseView.paintBase(paint, index,
QRect(text_x, 0, char_w, char_h),
text_y)
text_x += char_w
class AlignmentView(QScrollView):
def __init__(self, *args):
QScrollView.__init__(self, *args)
self._sequences = []
f = QFont("Helvetica", 48, QFont.Black)
self.setFont(f)
def addSequenceBaseView(self, baseview):
v = SequenceView(baseview, self.viewport())
font = self.font()
v.setFont(font)
hintsizes = [obj.maximumSizeHint() for obj in self._sequences]
widths = [size.width() for size in hintsizes]
heights = [size.height() for size in hintsizes]
height = reduce(operator.add, [size.height() for size in hintsizes], 0)
self.addChild(v, 0, height)
size = v.maximumSizeHint()
widths.append(size.width())
width = max(widths)
height += size.height()
self._sequences.append(v)
self.resizeContents(width, height)
def setFont(self, font):
QVBox.setFont(self, font)
# Move children!!!
for x in self._sequences:
x.setFont(font)
class MainWindow(QWidget):
def __init__(self, alignment, *args):
QWidget.__init__(self, *args)
filemenu = self._filemenu = QPopupMenu()
filemenu.insertItem("&Open")
filemenu.insertItem("&Save")
filemenu.insertSeparator()
filemenu.insertItem("&Quit", qApp, SLOT("quit()"))
editmenu = self._editmenu = QPopupMenu()
editmenu.insertItem("Delete")
alignmenu = self._alignment = QPopupMenu()
alignmenu.insertItem("Everything")
menubar = self._menubar = QMenuBar(self)
menubar.insertItem("&File", filemenu)
menubar.insertItem("&Edit", editmenu)
menubar.insertItem("&Align", alignmenu)
av = self._alignment_view = AlignmentView(self)
av.addSequenceBaseView(SequenceBaseView("ANDREW"))
av.addSequenceBaseView(SequenceBaseView("PETER"))
av.addSequenceBaseView(SequenceBaseView("DALKE"))
av.addSequenceBaseView(SequenceBaseView("Four"))
av.addSequenceBaseView(SequenceBaseView("Five"))
av.addSequenceBaseView(SequenceBaseView("Six"))
av.addSequenceBaseView(SequenceBaseView(
("".join([chr(i) for i in range(32, 128)])) * 5))
av.setGeometry(0, menubar.height(),
self.width(), self.height() - menubar.height())
def resizeEvent(self, event):
QWidget.resizeEvent(self, event)
av = self._alignment_view
menubar = self._menubar
av.setGeometry(0, menubar.height(),
self.width(), self.height() - menubar.height())
def main():
app = QApplication(sys.argv)
mywidget = MainWindow([])
mywidget.setGeometry(50, 50, 400, 400)
app.setMainWidget(mywidget)
mywidget.show()
app.exec_loop()
if __name__ == "__main__":
main()
-------------- next part --------------
A non-text attachment was scrubbed...
Name: align.jpg
Type: image/jpeg
Size: 29921 bytes
Desc: not available
Url : http://www.riverbankcomputing.com/pipermail/pyqt/attachments/20020508/66ed00e7/align.jpg
More information about the PyQt
mailing list