12-14-2011, 06:20 PM | #1 |
Calibre Plugins Developer
Posts: 4,637
Karma: 2162064
Join Date: Oct 2010
Location: Australia
Device: Kindle Oasis
|
Working with DateTime custom columns programmatically?
I'm working on a new version of my Goodreads Sync plugin, and I need to do something I haven't done before of working with datetime custom columns.
1. Assume I get the Metadata object for a book, using mi = db.get_metadata(book_id, index_is_id=True). And lets say I know the custom column is called '#dateread'. When I query the value using this: dateread = mi.get('#dateread') I end up with this text value in my variable: datetime.datetime(2011, 12, 14, 22, 50, 50, tzinfo=SafeLocalTimeZone()) So that presumes there has to be some sort of further manipulation of this to get it into an actual date time value. And presumably this may also need to take into account the format associated with the custom column? Which leads on to the next question... 2. The point of retrieving this date, is to allow it to be edited as a cell within my QTableWidget. Is there either a cell widget I can reuse for editing dates or any other pointers on an alternative? Depending on whether there is, and what input it requires (a datetime object or a string) will perhaps help dictate the answer to question 1 in terms of what to retrieve the value of that custom column as. Thanks... |
12-14-2011, 10:05 PM | #2 |
creator of calibre
Posts: 43,860
Karma: 22666666
Join Date: Oct 2006
Location: Mumbai, India
Device: Various
|
1. get() should be returning an actual datetime object. Try printing type(dateread)
2. See the delegates in library/delegates.py (these are used in the main calibre book list to enable editing of published date) |
Advert | |
|
12-15-2011, 06:09 AM | #3 |
Grand Sorcerer
Posts: 11,742
Karma: 6997045
Join Date: Jan 2010
Location: Notts, England
Device: Kobo Libra 2
|
The following program demonstrates that mi.get() returns a datetime object for a datetime custom column.
Code:
from calibre.ebooks.metadata.book.base import Metadata from calibre.library.database2 import LibraryDatabase2 from calibre.utils.config import prefs src = prefs['library_path'] db = LibraryDatabase2(src) mi = db.get_metadata(1353, index_is_id=True) dt = mi.get('#mydate') print dt print type(dt) print dt.timetuple() Code:
2011-11-23 09:01:10+01:00 <type 'datetime.datetime'> time.struct_time(tm_year=2011, tm_mon=11, tm_mday=23, tm_hour=9, tm_min=1, tm_sec=10, tm_wday=2, tm_yday=327, tm_isdst=0) Last edited by chaley; 12-15-2011 at 06:13 AM. Reason: Add delegate info |
12-15-2011, 07:25 AM | #4 |
creator of calibre
Posts: 43,860
Karma: 22666666
Join Date: Oct 2006
Location: Mumbai, India
Device: Various
|
@charles: FYI, a nicer way to init the db object for the current library is
from calibre.library.db import db db = db() |
12-15-2011, 08:29 AM | #5 |
Calibre Plugins Developer
Posts: 4,637
Karma: 2162064
Join Date: Oct 2010
Location: Australia
Device: Kindle Oasis
|
Thanks Charles/Kovid,
I stupidly did not realise it actually was a datetime object I had - unfamiliar with what print() produces for python for this unfortunately. So that side of things is fine now, thanks. I am looking into the delegates stuff. One thing that I have never properly got my head around, is the Qt way of doing models/views. I made an attempt with my Plugin Updater plugin, but in every other situation for my relatively simple plugin usage I have just found life to be an awful lot easier to directly populate the QTableWidget. Which now possibly leaves me with a potential problem with trying to use CCDateDelegate (and I will want to use RatingDelegate as well) as I have no "model" of my own. Am I correct in thinking I am going to have to create a model and do it that way to use those delegates? |
Advert | |
|
12-15-2011, 08:35 AM | #6 |
creator of calibre
Posts: 43,860
Karma: 22666666
Join Date: Oct 2006
Location: Mumbai, India
Device: Various
|
You should be able to set a delegate on a qtablewidget (it is a subcalss of QTableView), though I have never tried it. And yeah, Qt model/view sucks.
|
12-15-2011, 08:44 AM | #7 |
Grand Sorcerer
Posts: 11,742
Karma: 6997045
Join Date: Jan 2010
Location: Notts, England
Device: Kobo Libra 2
|
You can set an "QItemDelegate" for a QTableWidget. Unfortunately, the GUI delegates are subclasses of QStyledItemDelegate. Both QItemDelegate and QStyledItemDelegate inherit from QAbstractItemDelegate, and they both seem to have the same API.
First thing I would try is to set the delegate for the date column to an instance of CcDateDelegate using setItemDelegate, and then see what happens. Making a model would imply also making a view. I don't see any way to get one's hands on the view in QTableWidget, and it is the view that sets the delegate. |
12-15-2011, 09:05 AM | #8 |
Calibre Plugins Developer
Posts: 4,637
Karma: 2162064
Join Date: Oct 2010
Location: Australia
Device: Kindle Oasis
|
Ahhh, not just me who hates the Qt model/view then
I'll have an experiment to see what happens (to see if I can reuse QTableWidget). I guess what isn't obvious to me (in my utter ignorance) is where the delegate is going to get its value from. QTableWidget does still have a default "model", but you don't normally do anything with it, just setting cell widgets directly. So where would the model get the value from when asked by the delegate? |
12-15-2011, 09:18 AM | #9 |
Grand Sorcerer
Posts: 11,742
Karma: 6997045
Join Date: Jan 2010
Location: Notts, England
Device: Kobo Libra 2
|
IIRC, you don't set the widgets. Instead you provide an "item" for each cell. I think that the "setItem" call is speaking to the internal model & view. The view creates the widgets using default delegates.
The default view (built in to QTableWidget) will use the default delegate for a type. SetItemDelegateForColumn replaces that delegate with yours. The view will invoke the delegate passing the internal model. When it is time to dosplay something, something in the view will call the delegate's setEditorData(), which in turn will ask the supplied model for the data using model.data(). Alternatively, and if the delgate has the QTableWidget instance (and it can if passed to the constructor), it can get the data using tablewidget_instance.itemAt(). Have fun experimenting. |
12-15-2011, 09:43 AM | #10 |
Calibre Plugins Developer
Posts: 4,637
Karma: 2162064
Join Date: Oct 2010
Location: Australia
Device: Kindle Oasis
|
Ahhh, think I might be getting somewhere. I have the rating delegate working anyway. This is what I did...
Code:
delegate = RatingDelegate(self) self.setItemDelegateForColumn(self.rating_column_index, delegate) ... # Inside a loop adding rows to the QTableWidget item = QTableWidgetItem() item.setData(Qt.DisplayRole, my_rating_value) self.setItem(row, self.rating_column_index, item) Code:
Traceback (most recent call last): File "D:\CalibreDev\latest\calibre\src\calibre\gui2\library\delegates.py", line 267, in createEditor AttributeError: 'QLineEdit' object has no attribute 'setDisplayFormat' |
12-15-2011, 10:01 AM | #11 |
Calibre Plugins Developer
Posts: 4,637
Karma: 2162064
Join Date: Oct 2010
Location: Australia
Device: Kindle Oasis
|
Ahhh, hang on - maybe if I convert the date into a QDate, then it will figure out the right widget for it...
|
12-15-2011, 10:37 AM | #12 |
Calibre Plugins Developer
Posts: 4,637
Karma: 2162064
Join Date: Oct 2010
Location: Australia
Device: Kindle Oasis
|
Ok, one step forward, two backwards it seems. I found that converting my python date into a QDateTime meant that QTableWidget creates the right sort of editor. However, the next error I get is this:
Traceback (most recent call last): File "D:\CalibreDev\latest\calibre\src\calibre\gui2\lib rary\delegates.py", line 277, in setEditorData AttributeError: 'QAbstractTableModel' object has no attribute 'db' I thought I could fudge this by doing something like this when I populate the table: self.model().db = self.db But that ain't doing it for me either. Wondering if I should just write my own CCDateDelegate that takes a db in, so it doesn't have to try to get it from the model... EDIT - that is definitely what I am going to do. Because I can now see the next problem will be how the editor tries to get its value, since the delegate does some mapping column lookups, none of which I want to have to replicate. Still, the Calibre class gave me something I can copy/paste from - thanks guys Last edited by kiwidude; 12-15-2011 at 10:44 AM. |
12-15-2011, 10:45 AM | #13 |
creator of calibre
Posts: 43,860
Karma: 22666666
Join Date: Oct 2006
Location: Mumbai, India
Device: Various
|
You will not be able to use the Cc delegate directly, instead model your own delegate based on it.
Use index.data(Qt.DisplayRole) and index.data(Qt.EditRole) etc. to get the data from the model of the QTableWidget in your delegate. |
12-15-2011, 11:09 AM | #14 |
Calibre Plugins Developer
Posts: 4,637
Karma: 2162064
Join Date: Oct 2010
Location: Australia
Device: Kindle Oasis
|
Yeah thx Kovid, that was the "doh" moment I realised once I got past the initial hurdles of figuring out whether I had to use a model or not. I've got it all working now, marvellous... thx to both of you for the help.
|
|
Similar Threads | ||||
Thread | Thread Starter | Forum | Replies | Last Post |
Custom Columns - How are you using yours? | nynaevelan | Library Management | 19 | 04-18-2011 12:42 AM |
Converting text for a datetime custom column | kiwidude | Development | 2 | 02-26-2011 10:47 AM |
0.7.46 and custom columns | meme | Library Management | 4 | 02-21-2011 04:21 AM |
Managing Custom Columns | ddjohn | Library Management | 3 | 02-19-2011 10:42 AM |
Custom Columns - the Future? | Starson17 | Calibre | 2 | 07-13-2010 09:56 AM |