I'd like to make a simple GUI that offers buttons which I can drag and drop into other Windows applications such that this other applications receives a certain string depending on the button chosen.
What would be the easiest GUI framework for Python that allows this drag and drop?
Any of the UI Libraries are likely to have support for this in some fashion. In wxPython we allow List Items to be moved between various lists. Things might look like this:
class JobList(VirtualList):
def __init__(self, parent, colref = 'job_columns'):
VirtualList.__init__(self, parent, colref)
def _bind(self):
VirtualList._bind(self)
self.Bind(wx.EVT_LIST_BEGIN_DRAG, self._startDrag)
def _startDrag(self, evt):
# Create a data object to pass around.
data = wx.CustomDataObject('JobIdList')
data.SetData(str([self.data[idx]['Id'] for idx in self.selected]))
# Create the dropSource and begin the drag-and-drop.
dropSource = wx.DropSource(self)
dropSource.SetData(data)
dropSource.DoDragDrop(flags = wx.Drag_DefaultMove)
Then, there's a ListDrop class that facilitates the dropping of things onto the lists:
class ListDrop(wx.PyDropTarget):
"""
Utility class - Required to support List Drag & Drop.
Using example code from http://wiki.wxpython.org/ListControls.
"""
def __init__(self, setFn, dataType, acceptFiles = False):
wx.PyDropTarget.__init__(self)
self.setFn = setFn
# Data type to accept.
self.data = wx.CustomDataObject(dataType)
self.comp = wx.DataObjectComposite()
self.comp.Add(self.data)
if acceptFiles:
self.data2 = wx.FileDataObject()
self.comp.Add(self.data2)
self.SetDataObject(self.comp)
def OnData(self, x, y, d):
if self.GetData():
if self.comp.GetReceivedFormat().GetType() == wx.DF_FILENAME:
self.setFn(x, y, self.data2.GetFilenames())
else:
self.setFn(x, y, self.data.GetData())
return d
And finally, a List where things can be dropped:
class QueueList(VirtualList):
def __init__(self, parent, colref = 'queue_columns'):
VirtualList.__init__(self, parent, colref)
self.SetDropTarget(ListDrop(self.onJobDrop, 'JobIdList', True))
def onJobDrop(self, x, y, data):
idx, flags = self.HitTest((x, y)) ##UnusedVariable
if idx == -1: # Not dropped on a list item in the target list.
return
# Code here to handle incoming data.
PyQt to the rescue.
Theoretically, you can use any library for which a graphical drag-and-drop designer exists. Such tools often generate markup language which the library parses, and sometimes they generate code directly. The latter is language-dependent whilst the former shouldn't be. Either way, you'll find a way of doing it with Python.
Practically, some libraries have better visual designing tools that others. WinForms designer was really elegant and seamless when I used it, so maybe IronPython? How about PyGTK, with Glade, or the aforementioned PyQt? Maybe Jython using Swing designed in NetBeans?
EDIT: Whoops, I didn't read the question properly. What you're looking for is a framework with drag-and-drop capabilities, i.e. most of them. There's lots of things to consider though, for example are the destination and source windows going to be from the same process? Will they be written with the same framework? These things could be relevant or not, depending on how it's all written.
Related
I am searching for a way to have a QTreeView that contains hierarchical items which themselfs have a layout that is propperly drawn.
I tried to inherit from both QStandartItem and QWidget (to have a layout) but the second i set the layout on the widget part of this class the programm is shutting down when it tries to render.
class modPackItem(qtg.QStandardItem,qtw.QWidget):
def __init__(self,txt:str='',image_path:str='./assets/defaultModPack.jpg'):
super().__init__()
fnt = qtg.QFont('Calibri',12)
fnt.setBold(True)
self.setEditable(False)
self.setForeground(qtg.QColor(0,0,0))
self.setFont(fnt)
self.setText(txt)
self.horLayout = qtw.QHBoxLayout()
self.horLayout.addWidget(qtw.QLabel("test"))
#self.setLayout(self.horLayout) #this breaks the rendering
modPack_image = qtg.QImage(image_path)
self.setData(modPack_image.scaled(64,64,qtc.Qt.AspectRatioMode.KeepAspectRatioByExpanding),qtc.Qt.ItemDataRole.DecorationRole)
Is there a possible way to have all items in the QTreeView contain layouts (For example with multiple texts[description,tag-words,etc]).
Note: I also considered switching to a simple List of widgets which have children containing the hierarchical items. But that would increase complexity of my app-structure a lot and therefore i would like to avoid that.
Edit: To clearify what i want to do:
I want to build a mod(pack) manager in the style of the technic-launcher for minecraft mods but instead for any kind of game in any kind of infrastructure(steam, local instal,etc). By clicking different buttons i add new "modpacks" or "mods" (optimally custom QStandartItem with Layout for all the data) in an hierarchical fashion (therefore treeview). Adding the items and the steam-subsrciption or filecopy logic is no problem but i would like to see all infos (Name,descritpion, custom tags) on the overview (like in the example pic). I know i could bind the QStandartItem selection method to a new popup showing all infos but that would be inconvinient.
Edit2: On terms of implementation i just add the QStandartItem-object as an additional row to the root-node before setting the model. I allready tested adding new objects to the rootnode by clicking on a button and that worked fine. Just setting the layout in the object crashes the application at start.
class SteamModManager_Dialog(qtw.QDialog):
window: Ui_SteamModManagerFrame
treeModel: qtg.QStandardItemModel
rootNode: qtg.QStandardItem
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.window = Ui_SteamModManagerFrame()
self.window.setupUi(self)
self.window.label_footer.setText("")
self.treeModel = qtg.QStandardItemModel()
self.rootNode = self.treeModel.invisibleRootItem()
modPack = modPackItem('Dont Starve Together')
testMod = modItem("TestMod")
modPack.appendRow(testMod)
self.rootNode.appendRow(modPack)
self.window.tView_modPacks.setModel(self.treeModel)
self.window.tView_modPacks.expandAll()
On the behalf of #musicamente here the solution that worked out for me:
I created a widget in the designer (as usual, not posting the full ui code here).
Then i implemented the following code into the Dialog:
self.treeModel = qtg.QStandardItemModel()
self.rootNode = self.treeModel.invisibleRootItem()
modPack = modPackItem('Dont Starve Together')
testMod = modItem("TestMod")
modPack.appendRow(testMod)
self.rootNode.appendRow(modPack)
self.window.tView_modPacks.setModel(self.treeModel)
self.window.tView_modPacks.expandAll()
modPackWidget = qtw.QWidget()
ui = Ui_modPackWidget()
ui.setupUi(modPackWidget)
self.window.tView_modPacks.setIndexWidget(self.treeModel.index(0,0),modPackWidget)
This code resulted setting the custom widget to the treeview item. Here the final look:
I have a task of migrating a multi-userframe VBA project with a lot od database interaction into something different - as this must be something that cannot demand installing software (so JRE and .NET are out of the question) I believe this can be done with Python - wxPython covers frames and different controls (I'm using boa-constructor for it's frame designer), I also managed to connect via adodbapi with the current database VBA is using. I just suck at putting it all together properly. Consider this skeleton:
myApp.py
#!/usr/bin/env python
#Boa:App:BoaApp
import wx
import myFrame
modules ={u'myFrame': [1, 'Main frame of Application', u'myFrame.py']}
class BoaApp(wx.App):
def OnInit(self):
# here I think I'd see something like, say:
# self.main.cnnObject = adodbapi.connect ( some proper connection string )
self.main = myFrame.create(None)
self.main.Show()
self.SetTopWindow(self.main)
return True
def main():
application = BoaApp(0)
application.MainLoop()
if __name__ == '__main__':
main()
myFrame.py
#Boa:Frame:myFrame
import wx
def create(parent):
return myFrame(parent)
[wxID_MYFRAME, wxID_MYFRAMEBUTTON1,
] = [wx.NewId() for _init_ctrls in range(2)]
class myFrame(wx.Frame):
def _init_ctrls(self, prnt):
# generated method, don't edit
wx.Frame.__init__(self, id=wxID_MYFRAME, name='myFrame', parent=prnt,
pos=wx.Point(710, 329), size=wx.Size(400, 250),
style=wx.DEFAULT_FRAME_STYLE, title='MainFrame')
self.SetClientSize(wx.Size(392, 223))
self.button1 = wx.Button(id=wxID_MYFRAMEBUTTON1,
label='FETCH cnnObject', name='button1', parent=self,
pos=wx.Point(0, 144), size=wx.Size(392, 79), style=0)
self.button1.Bind(wx.EVT_BUTTON, self.OnButton1,
id=wxID_MYFRAMEBUTTON1)
def __init__(self, parent):
self._init_ctrls(parent)
def OnButton1(self, event):
event.Skip()
# here and in other events in other frames I would like to retrieve
# that cnnObject to use for queries
Adding tons of controls, events, opening consecutive frames from this one and next to come seems to work. However to not have to copy/paste the whole database connection stuff over and over again I wanted to have it all in one place and just access that code from frames.
My general idea is that since there's only one myApp object, it could contain the connection object, especially since the connection string will be available as sys.argv[1]
Is this possible ? And if so, how would I reference the application object from inside the OnButton1 method ? There's also a chance I have this figured out all wrong, in which case I'd like to hear an outline of the 'right way'.
What I feel I may be missing is maybe a proper class wrapper for those database operations, but even If I make one, I'd still like to have only one instance of that class available in all my future frames, yet I can't even manage to do that with the application instance - I import myApp inside myFrame (which itself seems weird as myApp already imports myFrame, so both ways ?) but no matter what type of assignment to a local variable I try, I mostly get a " 'module' object has no attribute " ... (which makes me think I probably don't get how scoping / modules work in Python)
I wrote a little about this, but I was using SQLAlchemy. This was my first attempt:
http://www.blog.pythonlibrary.org/2011/11/10/wxpython-and-sqlalchemy-an-intro-to-mvc-and-crud/
Then I received some comments and help from friend in the wxPython community and SQLAlchemy's devs and updated the app a bit:
http://www.blog.pythonlibrary.org/2011/11/30/improving-medialocker-wxpython-sqlalchemy-and-mvc/
This second article shows how to create a database session and pass it about. I think that's the approach you are looking for. I would do the database connection in your top level frame's init method:
def __init__(self):
"""Constructor"""
wx.Frame.__init__(self, None, title="Databases!")
self.data_connection = self.create_connection()
Then when you create your other frames, you can pass that connection to them:
def create_new_frame(self):
""""""
new_frame = MyOtherFrame(self.data_connection)
new_frame.Show()
Here's one way you could set up your frame class:
########################################################################
class MyOtherFrame(wx.Frame):
""""""
#----------------------------------------------------------------------
def __init__(self, data_connection):
"""Constructor"""
wx.Frame.__init__(self, None, title="Other frame")
self.data_connection = data_connection
Just make sure you do not close the data connection in your other frame as that will also close it in the original top level frame.
This solution also applies to your OnButton1 call. Just change it so it's like this:
def OnButton1(self, event):
cursor = self.data_connection.cursor()
You'll notice that you can access self.data_connection anywhere in your main frame because it was defined as a class level variable.
You might also be interested in the Dabo project, which is a wrapper around wxPython that is supposed to make it easier to work with databases: http://www.dabodev.com/
I'm writing a text-based hex viewer for fun and usefulness(I intend to add syntax highlighting for many different filetypes), and am wondering if there are any curses toolkits I could use.
I will probably write something myself anyway as to familiarize myself with the way gui toolkits work, but it would be nice to know of useful libraries for future reference for myself and others.
Urwid is the best library to work with curses and python that I know.
Altenatively, you might find also interesting snack (newt-based library).
For more information, please have a look at this question.
npyscreen
Npyscreen is a Python widget library and application framework for programming terminal or console applications. It is built on top of ncurses, which is part of the standard library.
The focus of this library is to provide a rapid way to develop console applications. In general, adding a control to the screen requires only one line of code.
This framework should be powerful enough to create everything from quick, simple programs to complex, multi-screen applications.
#!/usr/bin/env python
# encoding: utf-8
import npyscreen
class TestApp(npyscreen.NPSApp):
def main(self):
# These lines create the form and populate it with widgets.
# A fairly complex screen in only 8 or so lines of code - a line for each control.
F = npyscreen.Form(name = "Welcome to Npyscreen",)
t = F.add(npyscreen.TitleText, name = "Text:",)
fn = F.add(npyscreen.TitleFilename, name = "Filename:")
fn2 = F.add(npyscreen.TitleFilenameCombo, name="Filename2:")
dt = F.add(npyscreen.TitleDateCombo, name = "Date:")
s = F.add(npyscreen.TitleSlider, out_of=12, name = "Slider")
ml = F.add(npyscreen.MultiLineEdit,
value = """try typing here!\nMutiline text, press ^R to reformat.\n""",
max_height=5, rely=9)
ms = F.add(npyscreen.TitleSelectOne, max_height=4, value = [1,], name="Pick One",
values = ["Option1","Option2","Option3"], scroll_exit=True)
ms2= F.add(npyscreen.TitleMultiSelect, max_height =-2, value = [1,], name="Pick Several",
values = ["Option1","Option2","Option3"], scroll_exit=True)
# This lets the user interact with the Form.
F.edit()
print(ms.get_selected_objects())
if __name__ == "__main__":
App = TestApp()
App.run()
On GitHub there is a free to use, study, modify and re-distribute High Level GUI library, at "https://github.com/rigordo959/tsWxGTUI_PyVx_Repository".
It is implemented in Python 2x & 3x using the "curses" Low Level GUI package.
Your application programs can be programmed using a character-mode subset of the pixel-mode "wxPython" High Level GUI API. It supports displays with keyboard and mouse input and various terminal emulators including the color xterms (8-color with 64-color pairs and 16-color with 256-color pairs) and non-color vt100/vt220.
In PyQT, how can I plot small "Nodes" at given points and connect them with edges? All of the PyQT tutorials I find are "plot a button! plot a checkbox!"
It has been a pain to find a good explanation for this (as of by the end of 2014 already), and since this question asks exactely what I was looking for, I'll post a transcription (from C++ to Python) of what I found in this post.
The code is below, and here is the rationale:
QGrahpicsItem, QPainterPath and QPainterPath.Element are the classes you are looking for. Specifically, QPainterPath implements the kind of vector functionality you expect in applications such as CorelDraw, Adobe Illustrator, or Inkscape.
The example below benefits from the pre-existing QGraphicsEllipseItem (for rendering nodes) and QGraphicsPathItem (for rendering the path itself), which inherit from QGraphicsItem.
The Path constructor iterates over the QPainterPath elements, creating Node items for each one; Each of them, in turn, send updates to the parent Path object, which updates its path property accordingly.
I found much, much easier to study the C++ Qt4 Docs than the rather less structured PyQt docs found elsewhere. Once you get used to mentally translate between C++ and Python, the docs themselves are a powerful way to learn how to use each class.
#!/usr/bin/env python
# coding: utf-8
from PyQt4.QtGui import *
from PyQt4.QtCore import *
rad = 5
class Node(QGraphicsEllipseItem):
def __init__(self, path, index):
super(Node, self).__init__(-rad, -rad, 2*rad, 2*rad)
self.rad = rad
self.path = path
self.index = index
self.setZValue(1)
self.setFlag(QGraphicsItem.ItemIsMovable)
self.setFlag(QGraphicsItem.ItemSendsGeometryChanges)
self.setBrush(Qt.green)
def itemChange(self, change, value):
if change == QGraphicsItem.ItemPositionChange:
self.path.updateElement(self.index, value.toPointF())
return QGraphicsEllipseItem.itemChange(self, change, value)
class Path(QGraphicsPathItem):
def __init__(self, path, scene):
super(Path, self).__init__(path)
for i in xrange(path.elementCount()):
node = Node(self, i)
node.setPos(QPointF(path.elementAt(i)))
scene.addItem(node)
self.setPen(QPen(Qt.red, 1.75))
def updateElement(self, index, pos):
path.setElementPositionAt(index, pos.x(), pos.y())
self.setPath(path)
if __name__ == "__main__":
app = QApplication([])
path = QPainterPath()
path.moveTo(0,0)
path.cubicTo(-30, 70, 35, 115, 100, 100);
path.lineTo(200, 100);
path.cubicTo(200, 30, 150, -35, 60, -30);
scene = QGraphicsScene()
scene.addItem(Path(path, scene))
view = QGraphicsView(scene)
view.setRenderHint(QPainter.Antialiasing)
view.resize(600, 400)
view.show()
app.exec_()
If you want to be able to interact with the objects displayed in the plot, you will be better off using a QGraphicsScene. It handles zooming and panning and can contain other QGraphicsItem objects that can handle their own interactions.
It's very easy to use, but there is a bit of overhead involved, especially if you plan to make thousands of objects.
You can find a PyQt tutorial here. This and the API docs should get you started.
I have wxPython app which is running on MS Windows and I'd like it to support drag&drop between its instances (so the user opens my app 3 times and drags data from one instance to another).
The simple drag&drop in wxPython works that way:
User initiates drag: The source window packs necessary data in wx.DataObject(), creates new wx.DropSource, sets its data and calls dropSource.DoDragDrop()
User drops data onto target window: The drop target calls library function GetData() which transfers actual data to its wx.DataObject instance and finally - dataObject.GetData() unpacks the actual data.
I'd like to have some more sophisticated drag&drop which would allow user to choose what data is dragged after he drops.
Scenario of my dreams:
User initiates drag: Only some pointer to the source window is packed (some function or object).
User drops data onto target window: Nice dialog is displayed which asks user which drag&drop mode he chooses (like - dragging only song title, or song title and the artists name or whole album of the dragged artist).
Users chooses drag&drop mode: Drop target calls some function on the dragged data object, which then retrieves data from the drag source and transfers it to the drop target.
The scenario of my dreams seems doable in MS Windows, but the docs for wxWidgets and wxPython are pretty complex and ambigious. Not all wx.DataObject classes are available in wxPython (only wx.PySimpleDataObject), so I'd like someone to share his experience with such approach. Can such behaviour be implemented in wxPython without having to code it directly in winAPI?
EDIT:
Toni Ruža gave an answer with working drag&drop example, but that's not exactly the scenario of my dreams. His code manipulates data when it's dropped (the HandleDrop() shows popup menu), but data is prepared when drag is initiated (in On_ElementDrag()). In my application there should be three different drag&drop modes, and some of them require time-consuming data preparation. That's why I want to postpone data retrieval to the moment user drops data and chooses (potentially costly) d&d mode.
And for memory protection issue - I want to use OLE mechanisms for inter-process communication, like MS Office does. You can copy Excel diagram and paste it into MS-Word where it will behave like an image (well, sort of). Since it works I believe it can be done in winAPI. I just don't know if I can code it in wxPython.
Since you can't use one of the standard data formats to store references to python objects I would recommend you use a text data format for storing the parameters you need for your method calls rather than making a new data format. And anyway, it would be no good to pass a reference to an object from one app to another as the object in question would not be accessible (remember memory protection?).
Here is a simple example for your requirements:
import wx
class TestDropTarget(wx.TextDropTarget):
def OnDropText(self, x, y, text):
wx.GetApp().TopWindow.HandleDrop(text)
def OnDragOver(self, x, y, d):
return wx.DragCopy
class Test(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None)
self.numbers = wx.ListCtrl(self, style = wx.LC_ICON | wx.LC_AUTOARRANGE)
self.field = wx.TextCtrl(self)
sizer = wx.FlexGridSizer(2, 2, 5, 5)
sizer.AddGrowableCol(1)
sizer.AddGrowableRow(0)
self.SetSizer(sizer)
sizer.Add(wx.StaticText(self, label="Drag from:"))
sizer.Add(self.numbers, flag=wx.EXPAND)
sizer.Add(wx.StaticText(self, label="Drag to:"), flag=wx.ALIGN_CENTER_VERTICAL)
sizer.Add(self.field)
for i in range(100):
self.numbers.InsertStringItem(self.numbers.GetItemCount(), str(i))
self.numbers.Bind(wx.EVT_LIST_BEGIN_DRAG, self.On_ElementDrag)
self.field.SetDropTarget(TestDropTarget())
menu_id1 = wx.NewId()
menu_id2 = wx.NewId()
self.menu = wx.Menu()
self.menu.AppendItem(wx.MenuItem(self.menu, menu_id1, "Simple copy"))
self.menu.AppendItem(wx.MenuItem(self.menu, menu_id2, "Mess with it"))
self.Bind(wx.EVT_MENU, self.On_SimpleCopy, id=menu_id1)
self.Bind(wx.EVT_MENU, self.On_MessWithIt, id=menu_id2)
def On_ElementDrag(self, event):
data = wx.TextDataObject(self.numbers.GetItemText(event.Index))
source = wx.DropSource(self.numbers)
source.SetData(data)
source.DoDragDrop()
def HandleDrop(self, text):
self._text = text
self.PopupMenu(self.menu)
def On_SimpleCopy(self, event):
self.field.Value = self._text
def On_MessWithIt(self, event):
self.field.Value = "<-%s->" % "".join([int(c)*c for c in self._text])
app = wx.PySimpleApp()
app.TopWindow = Test()
app.TopWindow.Show()
app.MainLoop()
Methods like On_SimpleCopy and On_MessWithIt get executed after the drop so any lengthy operations you might want to do you can do there based on the textual or some other standard type of data you transfered with the drag (self._text in my case), and look... no OLE :)
Ok, it seems that it can't be done the way I wanted it.
Possible solutions are:
Pass some parameters in d&d and do some inter-process communication on your own, after user drops data in target processes window.
Use DataObjectComposite to support multiple drag&drop formats and keyboard modifiers to choose current format. Scenario:
User initiates drag. State of CTRL, ALT and SHIFT is checked, and depending on it the d&d format is selected. DataObjectComposite is created, and has set data in chosen format.
User drops data in target window. Drop target asks dropped DataObject for supported format and retrieves data, knowing what format it is in.
I'm choosing the solution 2., because it doesn't require hand crafting communication between processes and it allows me to avoid unnecessary data retrieval when user wants to drag only the simplest data.
Anyway - Toni, thanks for your answer! Played with it a little and it made me think of d&d and of changing my approach to the problem.