Implement file drag drop
This commit is contained in:
@@ -34,7 +34,7 @@ from PyQt5 import QtWidgets, QtGui, QtCore
|
||||
from lector import database
|
||||
from lector import sorter
|
||||
from lector.toolbars import LibraryToolBar, BookToolBar
|
||||
from lector.widgets import Tab
|
||||
from lector.widgets import Tab, DragDropListView, DragDropTableView
|
||||
from lector.delegates import LibraryDelegate
|
||||
from lector.threaded import BackGroundTabUpdate, BackGroundBookAddition, BackGroundBookDeletion
|
||||
from lector.library import Library
|
||||
@@ -62,6 +62,13 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
|
||||
# Initialize translation function
|
||||
self._translate = QtCore.QCoreApplication.translate
|
||||
|
||||
# Create library widgets
|
||||
self.listView = DragDropListView(self, self.listPage)
|
||||
self.gridLayout_4.addWidget(self.listView, 0, 0, 1, 1)
|
||||
|
||||
self.tableView = DragDropTableView(self, self.tablePage)
|
||||
self.gridLayout_3.addWidget(self.tableView, 0, 0, 1, 1)
|
||||
|
||||
# Empty variables that will be infested soon
|
||||
self.settings = {}
|
||||
self.thread = None # Background Thread
|
||||
@@ -318,24 +325,104 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
|
||||
my_args = cl_parser.positionalArguments()
|
||||
if my_args:
|
||||
file_list = [QtCore.QFileInfo(i).absoluteFilePath() for i in my_args]
|
||||
books = sorter.BookSorter(
|
||||
file_list,
|
||||
('addition', 'manual'),
|
||||
self.database_path,
|
||||
self.settings['auto_tags'],
|
||||
self.temp_dir.path())
|
||||
self.process_post_hoc_files(file_list, True)
|
||||
|
||||
parsed_books = books.initiate_threads()
|
||||
if not parsed_books:
|
||||
return
|
||||
def process_post_hoc_files(self, file_list, open_files_after_processing):
|
||||
# Takes care of both dragged and dropped files
|
||||
# As well as files sent as command line arguments
|
||||
books = sorter.BookSorter(
|
||||
file_list,
|
||||
('addition', 'manual'),
|
||||
self.database_path,
|
||||
self.settings['auto_tags'],
|
||||
self.temp_dir.path())
|
||||
|
||||
database.DatabaseFunctions(self.database_path).add_to_database(parsed_books)
|
||||
self.lib_ref.generate_model('addition', parsed_books, True)
|
||||
parsed_books = books.initiate_threads()
|
||||
if not parsed_books:
|
||||
return
|
||||
|
||||
file_dict = {QtCore.QFileInfo(i).absoluteFilePath(): None for i in my_args}
|
||||
database.DatabaseFunctions(self.database_path).add_to_database(parsed_books)
|
||||
self.lib_ref.generate_model('addition', parsed_books, True)
|
||||
|
||||
file_dict = {i: None for i in file_list}
|
||||
if open_files_after_processing:
|
||||
self.open_files(file_dict)
|
||||
|
||||
self.move_on()
|
||||
self.move_on()
|
||||
|
||||
def open_files(self, path_hash_dictionary):
|
||||
# file_paths is expected to be a dictionary
|
||||
# This allows for threading file opening
|
||||
# Which should speed up multiple file opening
|
||||
# especially @ application start
|
||||
|
||||
file_paths = [i for i in path_hash_dictionary]
|
||||
|
||||
for filename in path_hash_dictionary.items():
|
||||
|
||||
file_md5 = filename[1]
|
||||
if not file_md5:
|
||||
try:
|
||||
with open(filename[0], 'rb') as current_book:
|
||||
first_bytes = current_book.read(1024 * 32) # First 32KB of the file
|
||||
file_md5 = hashlib.md5(first_bytes).hexdigest()
|
||||
except FileNotFoundError:
|
||||
return
|
||||
|
||||
# Remove any already open files
|
||||
# Set focus to last file in case only one is open
|
||||
for i in range(1, self.tabWidget.count()):
|
||||
tab_metadata = self.tabWidget.widget(i).metadata
|
||||
if tab_metadata['hash'] == file_md5:
|
||||
file_paths.remove(filename[0])
|
||||
if not file_paths:
|
||||
self.tabWidget.setCurrentIndex(i)
|
||||
return
|
||||
|
||||
if not file_paths:
|
||||
return
|
||||
|
||||
def finishing_touches():
|
||||
self.profile_functions.format_contentView()
|
||||
self.start_culling_timer()
|
||||
|
||||
print('Attempting to open: ' + ', '.join(file_paths))
|
||||
|
||||
contents = sorter.BookSorter(
|
||||
file_paths,
|
||||
('reading', None),
|
||||
self.database_path,
|
||||
True,
|
||||
self.temp_dir.path()).initiate_threads()
|
||||
|
||||
# TODO
|
||||
# Notification feedback in case all books return nothing
|
||||
|
||||
if not contents:
|
||||
return
|
||||
|
||||
for i in contents:
|
||||
# New tabs are created here
|
||||
# Initial position adjustment is carried out by the tab itself
|
||||
file_data = contents[i]
|
||||
Tab(file_data, self)
|
||||
|
||||
if self.settings['last_open_tab'] == 'library':
|
||||
self.tabWidget.setCurrentIndex(0)
|
||||
self.listView.setFocus()
|
||||
self.settings['last_open_tab'] = None
|
||||
return
|
||||
|
||||
for i in range(1, self.tabWidget.count()):
|
||||
this_path = self.tabWidget.widget(i).metadata['path']
|
||||
if self.settings['last_open_tab'] == this_path:
|
||||
self.tabWidget.setCurrentIndex(i)
|
||||
self.settings['last_open_tab'] = None
|
||||
finishing_touches()
|
||||
return
|
||||
|
||||
self.tabWidget.setCurrentIndex(self.tabWidget.count() - 1)
|
||||
finishing_touches()
|
||||
|
||||
def start_culling_timer(self):
|
||||
if self.settings['perform_culling']:
|
||||
@@ -626,80 +713,6 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
|
||||
|
||||
self.open_files(path)
|
||||
|
||||
def open_files(self, path_hash_dictionary):
|
||||
# file_paths is expected to be a dictionary
|
||||
# This allows for threading file opening
|
||||
# Which should speed up multiple file opening
|
||||
# especially @ application start
|
||||
|
||||
file_paths = [i for i in path_hash_dictionary]
|
||||
|
||||
for filename in path_hash_dictionary.items():
|
||||
|
||||
file_md5 = filename[1]
|
||||
if not file_md5:
|
||||
try:
|
||||
with open(filename[0], 'rb') as current_book:
|
||||
first_bytes = current_book.read(1024 * 32) # First 32KB of the file
|
||||
file_md5 = hashlib.md5(first_bytes).hexdigest()
|
||||
except FileNotFoundError:
|
||||
return
|
||||
|
||||
# Remove any already open files
|
||||
# Set focus to last file in case only one is open
|
||||
for i in range(1, self.tabWidget.count()):
|
||||
tab_metadata = self.tabWidget.widget(i).metadata
|
||||
if tab_metadata['hash'] == file_md5:
|
||||
file_paths.remove(filename[0])
|
||||
if not file_paths:
|
||||
self.tabWidget.setCurrentIndex(i)
|
||||
return
|
||||
|
||||
if not file_paths:
|
||||
return
|
||||
|
||||
def finishing_touches():
|
||||
self.profile_functions.format_contentView()
|
||||
self.start_culling_timer()
|
||||
|
||||
print('Attempting to open: ' + ', '.join(file_paths))
|
||||
|
||||
contents = sorter.BookSorter(
|
||||
file_paths,
|
||||
('reading', None),
|
||||
self.database_path,
|
||||
True,
|
||||
self.temp_dir.path()).initiate_threads()
|
||||
|
||||
# TODO
|
||||
# Notification feedback in case all books return nothing
|
||||
|
||||
if not contents:
|
||||
return
|
||||
|
||||
for i in contents:
|
||||
# New tabs are created here
|
||||
# Initial position adjustment is carried out by the tab itself
|
||||
file_data = contents[i]
|
||||
Tab(file_data, self)
|
||||
|
||||
if self.settings['last_open_tab'] == 'library':
|
||||
self.tabWidget.setCurrentIndex(0)
|
||||
self.listView.setFocus()
|
||||
self.settings['last_open_tab'] = None
|
||||
return
|
||||
|
||||
for i in range(1, self.tabWidget.count()):
|
||||
this_path = self.tabWidget.widget(i).metadata['path']
|
||||
if self.settings['last_open_tab'] == this_path:
|
||||
self.tabWidget.setCurrentIndex(i)
|
||||
self.settings['last_open_tab'] = None
|
||||
finishing_touches()
|
||||
return
|
||||
|
||||
self.tabWidget.setCurrentIndex(self.tabWidget.count() - 1)
|
||||
finishing_touches()
|
||||
|
||||
def statusbar_visibility(self):
|
||||
if self.sender() == self.libraryToolBar.searchBar:
|
||||
if self.libraryToolBar.searchBar.text() == '':
|
||||
|
@@ -2,7 +2,7 @@
|
||||
|
||||
# Form implementation generated from reading ui file 'raw/main.ui'
|
||||
#
|
||||
# Created by: PyQt5 UI code generator 5.9.2
|
||||
# Created by: PyQt5 UI code generator 5.10.1
|
||||
#
|
||||
# WARNING! All changes made in this file will be lost!
|
||||
|
||||
@@ -35,20 +35,6 @@ class Ui_MainWindow(object):
|
||||
self.gridLayout_4.setContentsMargins(0, 0, 0, 0)
|
||||
self.gridLayout_4.setSpacing(0)
|
||||
self.gridLayout_4.setObjectName("gridLayout_4")
|
||||
self.listView = QtWidgets.QListView(self.listPage)
|
||||
self.listView.setFrameShape(QtWidgets.QFrame.NoFrame)
|
||||
self.listView.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
|
||||
self.listView.setProperty("showDropIndicator", False)
|
||||
self.listView.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
|
||||
self.listView.setMovement(QtWidgets.QListView.Static)
|
||||
self.listView.setProperty("isWrapping", True)
|
||||
self.listView.setResizeMode(QtWidgets.QListView.Fixed)
|
||||
self.listView.setLayoutMode(QtWidgets.QListView.SinglePass)
|
||||
self.listView.setViewMode(QtWidgets.QListView.IconMode)
|
||||
self.listView.setUniformItemSizes(True)
|
||||
self.listView.setWordWrap(True)
|
||||
self.listView.setObjectName("listView")
|
||||
self.gridLayout_4.addWidget(self.listView, 0, 0, 1, 1)
|
||||
self.stackedWidget.addWidget(self.listPage)
|
||||
self.tablePage = QtWidgets.QWidget()
|
||||
self.tablePage.setObjectName("tablePage")
|
||||
@@ -56,20 +42,6 @@ class Ui_MainWindow(object):
|
||||
self.gridLayout_3.setContentsMargins(0, 0, 0, 0)
|
||||
self.gridLayout_3.setSpacing(0)
|
||||
self.gridLayout_3.setObjectName("gridLayout_3")
|
||||
self.tableView = QtWidgets.QTableView(self.tablePage)
|
||||
self.tableView.setFrameShape(QtWidgets.QFrame.Box)
|
||||
self.tableView.setFrameShadow(QtWidgets.QFrame.Plain)
|
||||
self.tableView.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustToContentsOnFirstShow)
|
||||
self.tableView.setEditTriggers(QtWidgets.QAbstractItemView.DoubleClicked|QtWidgets.QAbstractItemView.EditKeyPressed|QtWidgets.QAbstractItemView.SelectedClicked)
|
||||
self.tableView.setAlternatingRowColors(True)
|
||||
self.tableView.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
|
||||
self.tableView.setGridStyle(QtCore.Qt.NoPen)
|
||||
self.tableView.setSortingEnabled(True)
|
||||
self.tableView.setWordWrap(False)
|
||||
self.tableView.setObjectName("tableView")
|
||||
self.tableView.horizontalHeader().setVisible(True)
|
||||
self.tableView.verticalHeader().setVisible(False)
|
||||
self.gridLayout_3.addWidget(self.tableView, 0, 0, 1, 1)
|
||||
self.stackedWidget.addWidget(self.tablePage)
|
||||
self.gridLayout_2.addWidget(self.stackedWidget, 0, 0, 1, 1)
|
||||
self.tabWidget.addTab(self.tab, "")
|
||||
|
@@ -55,46 +55,6 @@
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="QListView" name="listView">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
<property name="showDropIndicator" stdset="0">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::ExtendedSelection</enum>
|
||||
</property>
|
||||
<property name="movement">
|
||||
<enum>QListView::Static</enum>
|
||||
</property>
|
||||
<property name="isWrapping" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="resizeMode">
|
||||
<enum>QListView::Fixed</enum>
|
||||
</property>
|
||||
<property name="layoutMode">
|
||||
<enum>QListView::SinglePass</enum>
|
||||
</property>
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="viewMode">
|
||||
<enum>QListView::IconMode</enum>
|
||||
</property>
|
||||
<property name="uniformItemSizes">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tablePage">
|
||||
@@ -114,43 +74,6 @@
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="QTableView" name="tableView">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::Box</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Plain</enum>
|
||||
</property>
|
||||
<property name="sizeAdjustPolicy">
|
||||
<enum>QAbstractScrollArea::AdjustToContentsOnFirstShow</enum>
|
||||
</property>
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::DoubleClicked|QAbstractItemView::EditKeyPressed|QAbstractItemView::SelectedClicked</set>
|
||||
</property>
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="selectionBehavior">
|
||||
<enum>QAbstractItemView::SelectRows</enum>
|
||||
</property>
|
||||
<property name="gridStyle">
|
||||
<enum>Qt::NoPen</enum>
|
||||
</property>
|
||||
<property name="sortingEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<attribute name="horizontalHeaderVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
|
@@ -666,3 +666,81 @@ class PliantQGraphicsScene(QtWidgets.QGraphicsScene):
|
||||
|
||||
self.parent.load_cover(cover_pixmap, True)
|
||||
self.parent.show()
|
||||
|
||||
|
||||
class DragDropListView(QtWidgets.QListView):
|
||||
# This is the library listview
|
||||
def __init__(self, main_window, parent):
|
||||
super(DragDropListView, self).__init__(parent)
|
||||
self.main_window = main_window
|
||||
self.setAcceptDrops(True)
|
||||
self.setDragDropMode(QtWidgets.QAbstractItemView.InternalMove)
|
||||
self.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
|
||||
self.setResizeMode(QtWidgets.QListView.Fixed)
|
||||
self.setLayoutMode(QtWidgets.QListView.SinglePass)
|
||||
self.setViewMode(QtWidgets.QListView.IconMode)
|
||||
self.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
|
||||
self.setProperty("showDropIndicator", False)
|
||||
self.setProperty("isWrapping", True)
|
||||
self.setFrameShape(QtWidgets.QFrame.NoFrame)
|
||||
self.setUniformItemSizes(True)
|
||||
self.setWordWrap(True)
|
||||
self.setObjectName("listView")
|
||||
|
||||
def dragEnterEvent(self, event):
|
||||
if event.mimeData().hasUrls():
|
||||
event.acceptProposedAction()
|
||||
else:
|
||||
super(DragDropListView, self).dragEnterEvent(event)
|
||||
|
||||
def dragMoveEvent(self, event):
|
||||
super(DragDropListView, self).dragMoveEvent(event)
|
||||
|
||||
def dropEvent(self, event):
|
||||
if event.mimeData().hasUrls():
|
||||
file_list = [url.path() for url in event.mimeData().urls()]
|
||||
self.main_window.process_post_hoc_files(file_list, False)
|
||||
event.acceptProposedAction()
|
||||
else:
|
||||
super(DragDropListView, self).dropEvent(event)
|
||||
|
||||
|
||||
class DragDropTableView(QtWidgets.QTableView):
|
||||
# This is the library tableview
|
||||
def __init__(self, main_window, parent):
|
||||
super(DragDropTableView, self).__init__(parent)
|
||||
self.main_window = main_window
|
||||
self.setAcceptDrops(True)
|
||||
self.setDragDropMode(QtWidgets.QAbstractItemView.InternalMove)
|
||||
self.setFrameShape(QtWidgets.QFrame.Box)
|
||||
self.setFrameShadow(QtWidgets.QFrame.Plain)
|
||||
self.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustToContentsOnFirstShow)
|
||||
self.setEditTriggers(
|
||||
QtWidgets.QAbstractItemView.DoubleClicked |
|
||||
QtWidgets.QAbstractItemView.EditKeyPressed |
|
||||
QtWidgets.QAbstractItemView.SelectedClicked)
|
||||
self.setAlternatingRowColors(True)
|
||||
self.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
|
||||
self.setGridStyle(QtCore.Qt.NoPen)
|
||||
self.setSortingEnabled(True)
|
||||
self.setWordWrap(False)
|
||||
self.setObjectName("tableView")
|
||||
self.horizontalHeader().setVisible(True)
|
||||
self.verticalHeader().setVisible(False)
|
||||
|
||||
def dragEnterEvent(self, event):
|
||||
if event.mimeData().hasUrls():
|
||||
event.acceptProposedAction()
|
||||
else:
|
||||
super(DragDropTableView, self).dragEnterEvent(event)
|
||||
|
||||
def dragMoveEvent(self, event):
|
||||
super(DragDropTableView, self).dragMoveEvent(event)
|
||||
|
||||
def dropEvent(self, event):
|
||||
if event.mimeData().hasUrls():
|
||||
file_list = [url.path() for url in event.mimeData().urls()]
|
||||
self.main_window.process_post_hoc_files(file_list, False)
|
||||
event.acceptProposedAction()
|
||||
else:
|
||||
super(DragDropTableView, self).dropEvent(event)
|
||||
|
Reference in New Issue
Block a user