Note: marked as community wiki.
Is there a good analysis of why visual programming languages still haven't taken off? We're still coding these days 'linearly' in a 80x25 text window; while the concepts we represent (data structures, algorithms) seem like they can be more intuitively represented visually.
Two approaches to programming that aren't just simple text come to mind:
Structured Editing (which I know from Kirill Osenkov's blog).
LabView.
I think Structured Editing is pretty interesting, because it takes the 'braces with idententation' convention, which has proven really useful for keeping code organized, to its logical extreme. I think it could really be something, if someone were to make a brilliant (from a usability perspective) implementation of it.
The LabView approach, on the other hand, doesn't excite me so much. The visual idioms don't seem powerful and obvious enough, compared to text. I haven't used LabView much though, so it's probably better than I think.
Dont forget with VS 2010 (.NET 4), its now multi monitor supported, which mean you can now allow editors, designers and tool-windows to be moved outside the top-level window and positioned anywhere you want across to any monitor on your system.
An 80x25 text window? Really? Not to compare sizes, but my text window is quite a bit bigger than that. But regardless of that, I personally can't imagine a visual programming language that would satisfy me. For technical information, text is far more information-dense than video. I would much rather skim an article about a technical subject than watch a video about that subject in five times as much time (seriously guys, knock it off with the videos already).
In a similar way, I would much rather spend a few seconds typing a few lines of code, than a few minutes dragging and dropping things around to accomplish the same thing. It's about conciseness, and expressiveness. Visual programming languages just don't have it, in my experience. Good for teaching fundamentals of programming? Sure. Alice is pretty neat. But not for day-to-day work.
On a somewhat-related note, Code Bubbles is an interesting take on improving the "80x25 text window".
There is quite some mixing and matching.
For instance, people do use GUI editors like NetBeans Matisse or VS.Net because some things are easier to draw than to code. Some people use GUI data model editors: it's much easier, faster and (I would argue) produces a better result than writing DDL. Even when you write code, you have all sorts of graphical tools to help you understand what you're doing (the eclipse hierarchy view, for example).
On the other hand, we do still use similar text editors to the ones people used 30 years ago for a lot of our work. :) It's obvious that there is value to be had from both.
simulink is part of matlab and works great for engineering problems
Visual programming languages have never taken off because no one has done it right yet. In the same way that C++ / visual studio were the right technology for people at the time of their advent.
However, the age of talking to our machines (Alex voice service), and programming with nicer tools than text editors is upon us.
Here's the start of one I'm working on. I'm trying to bootstrap my project since if you're making a cool programming tool, why wouldn't the tool itself eventually be written in the tool's input language. I did start out at first by rendering my own graphs with PyQt5 / QGraphicsScene but debugging a 2D scene is actually extremely hard - that is unless you have a visual graph to program with instead! So rendering my own graph and writing the graph editor comes after I can get basic graphs to run. My favorite general purpose graph editor is yEd. It outputs .graphml which is good because the networkx library for python can already read in .graphml (Only problem is loading in the graph colors + other properties than position; so feature will wait til we are doing our own graph drawings).
Here is an example input graph:
Here's some basic code to run it:
import networkx as nx
from PyQt5.QtCore import QThread, QObject, pyqtSignal
from PyQt5.QtGui import QColor
from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton
import re
import sys
EDAT = 2
NDAT = 1
class CodeGraphThread(QThread):
ifRgx = r'^if\s+(.+)\s*$'
elseRgx = r'\s+|^$'
def __init__(self, graph, parent=None):
super(CodeGraphThread, self).__init__(parent)
self._nodes = {}
self.setGraph(graph)
self._retVal = None
self._locals = []
def setGraph(self, graph):
self._graph = graph
G = graph.G()
nodes = [x for x in G.nodes(data=True) if x[NDAT]['label'] == 'start']
if nodes: self._setStart(nodes[0][0])
def _setStart(self, nstr):
self._nodes['start'] = nstr
def start(self):
self._running = True
self._nodes['current'] = self._nodes['start']
QThread.start(self)
def _exec(self, codeText):
try:
exec('self._retVal=' + codeText)
except:
try:
exec(codeText)
except:
self.codeGraph().errorMessage.emit('Coudln\'t execute code: "' + codeText + '"')
def returnVal(self):
return self._retVal
def run(self):
while self._running:
cg = self.codeGraph()
G = cg.G()
current = self._nodes['current']
#TODO transfer over to regex system
data = [d for x,d in G.nodes(data=True) if x == current and 'label' in d and d['label'] not in ['start']]
if data:
codeText = data[0]['label']
self._exec(codeText)
rgx = self.ifRgx
edges = cg.edgesFr(current, rgx)
if edges:
e= edges[0]
ifArg = cg.matches(rgx).group(1)
self._exec(ifArg)
if self.returnVal():
self._nodes['current'] = e[1]
continue
rgx = self.elseRgx
edges = cg.edgesFr(current, rgx)
edges += cg.edgesFr(current, None)
if edges:
e = edges[0]
self._nodes['current'] = e[1]
continue
break
def codeGraph(self):
return self._graph
class CodeGraph(QObject):
errorMessage = pyqtSignal(str)
statusMessage = pyqtSignal(str)
_rgxMemo = {}
def __init__(self, gmlpath=None):
QObject.__init__(self)
if gmlpath != None:
self.loadGraphML(gmlpath)
else:
self._gmlpath = None
self._G = nx.MultiDiGraph()
self._thread = CodeGraphThread(self)
def G(self):
return self._G
def loadGraphML(self, gmlpath):
self._gmlpath = gmlpath
self._G = nx.read_graphml(gmlpath)
def saveGraphML(self, gmlpath):
self._gmlpath = gmlpath
nx.write_graphml(self._G, gmlpath)
def debugPrintNodes(self):
print(self._G.nodes(data=True))
def debugPrintEdges(self):
print(self._G.edges(data=True))
def matches(self, rgx):
if rgx in self._rgxMemo:
return self._rgxMemo[rgx][1]
return None
def rgx(self, rgx):
if rgx not in self._rgxMemo:
self._rgxMemo[rgx] = [re.compile(rgx), None]
return self._rgxMemo[rgx][0]
def rgxMatch(self, rgx, string):
if rgx not in self._rgxMemo:
rgx_ = self.rgx(rgx)
else:
rgx_ = self._rgxMemo[rgx][0]
match = self._rgxMemo[rgx][1] = rgx_.match(string)
return match
def edgesFr(self, n0, rgx):
if rgx != None:
return [(u,v,d) for u,v,d in self.G().edges(data=True) if u == n0 and 'label' in d and self.rgxMatch(rgx, d['label'])]
else:
return [(u,v,d) for u,v,d in self.G().edges(data=True) if u == n0 and 'label' not in d]
if __name__ == '__main__':
cg = CodeGraph('unnamed0.graphml')
cgthread = CodeGraphThread(cg)
def printError(errorMsg):
print(errorMsg)
cg.errorMessage.connect(printError)
# Qt application reqd for QThread testing
app = QApplication(sys.argv)
win = QMainWindow()
win.setWindowTitle('PyGraphML Practice 0')
button0 = QPushButton('Start thread running')
button0.clicked.connect(cgthread.start)
win.setCentralWidget(button0)
win.show()
sys.exit(app.exec_())
Current issues: Python 3 doesn't handle exec / locals() well (hence use of self.x instead of just x), so thinking of using a 3rd-party python interpreter or just statically modifying the code.
Not saying that my tool does anything right yet. For things to be done right, there also must be auto-refactoring tools, debugging, etc.
Related
it is appearing in some big modules like matplotlib. For example expression :
import importlib
obj = importlib.import_module('matplotlib')
obj_entries = obj.__dict__
Between runs len of obj_entries can vary. From 108 to 157 (expected) entries. Especially pyplot can be ignored like some another submodules.
it can work stable during manual debug mode with len computing statement after dict extraction. But in auto it dont work well.
such error occures:
RuntimeError: dictionary changed size during iteration
python-BaseException
using clear python 3.10 on windows. Version swap change nothing at all
during some attempts some interesting features was found.
use of repr is helpfull before dict initiation.
But if module transported between classes like variable more likely lazy-import happening? For now there is evidence that not all names showing when command line interpriter doing opposite - returning what expected. So this junk of code help bypass this bechavior...
Note: using pkgutil.iter_modules(some_path) to observe modules im internal for pkgutil ModuleInfo form.
import pkgutil, importlib
module_info : pkgutil.ModuleInfo
name = module_info.name
founder = module_info.module_finder
spec = founder.find_spec(name)
module_obj = importlib.util.module_from_spec(spec)
loader = module_obj.__loader__
loader.exec_module(module_obj)
still unfamilliar with interior of import mechanics so it will be helpfull to recive some links to more detail explanation (spot on)
I am trying to put together some code in Python 3.6 to help test the computer hardware that passes through my hands as an IT tech.
I'd like to have a script that plays a simple sine wave tone on the left speaker, then the right and then the both speakers together.
I have found a potentially helpful script over at Pyaudio How to get sound on only one speaker but some of the code to actually run it is missing - chiefly the code for making sin wave tones. I have looked around online and have tried reverse-engineering this back into the code on that page but the maths is a little high-level for me! Sorry.
Thanks,
Will
Update:
I think I have found a partial (albeit long-winded solution) with 'sounddevice' for python 3
#!/usr/bin/env python3
import argparse
import logging
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("filename", help="audio file to be played back")
parser.add_argument("-d", "--device", type=int, help="device ID")
args = parser.parse_args()
try:
import sounddevice as sd
import soundfile as sf
data, fs = sf.read(args.filename, dtype='float32')
sd.play(data, fs, device=args.device, blocking=True, mapping=[1])
status = sd.get_status()
if status:
logging.warning(str(status))
except BaseException as e:
# This avoids printing the traceback, especially if Ctrl-C is used.
raise SystemExit(str(e))
The main chunk of code is repeat twice more but with "mapping = [1]" changed to "mapping = [2]" to test the right speaker and finally with "mapping = [?]" removed in the final block to test both speakers.
I found this over at https://python-sounddevice.readthedocs.io/en/0.2.1/examples.html.
Of course, if anyone knows a quicker and graceful way of getting this done, please share!
You could generate the sine tone directly in Python instead of loading it from a file. I've written some tutorials about creating simple sine tones:
https://nbviewer.jupyter.org/github/mgeier/python-audio/blob/master/simple-signals.ipynb
http://nbviewer.jupyter.org/github/spatialaudio/communication-acoustics-exercises/blob/master/intro.ipynb
Those tutorials use NumPy, because it makes manipulating the audio buffers very easy. But you can of course also do it in pure Python, if you prefer.
Here's an example:
#!/usr/bin/env python3
import math
import sounddevice as sd
sd.default.device = None
sd.default.samplerate = samplerate = 48000
duration = 1.5
volume = 0.3
frequency = 440
# fade time in seconds:
fade_in = 0.01
fade_out = 0.3
buffer = memoryview(bytearray(int(duration * samplerate) * 4)).cast('f')
for i in range(len(buffer)):
buffer[i] = volume * math.cos(2 * math.pi * frequency * i / samplerate)
fade_in_samples = int(fade_in * samplerate)
for i in range(fade_in_samples):
buffer[i] *= i / fade_in_samples
fade_out_samples = int(fade_out * samplerate)
for i in range(fade_out_samples):
buffer[-(i + 1)] *= i / fade_out_samples
for mapping in ([1], [2], [1, 2]):
sd.play(buffer, blocking=True, mapping=mapping)
sd.sleep(500)
Note that this code is using 32-bit floating point numbers (each one using 4 bytes), that's why we reserve 4 times more bytes in our bytearray than the required number of samples.
I am working on a program that will allow someone to enter details in order to write a CV. I am using the Tkinter module (as extra practice) but am already stuck on the menu!
At the moment I have three different options the user can choose: Write CV, Review CV and Exit. I have created a button for each option and when the user presses the button it'll open, however the menu window remains open (there is a different subroutine for each option).
I understand that you need to do something like window.destroy(), however I'm not sure how to give a button two commands without doing something too fiddly like create more subroutines etc.?
The other option I think I'd prefer is is I could clear the menu screen?
Here is the programming I have at the moment:
def Main_Menu():
import tkinter
main_menu = tkinter.Tk()
main_menu.title("CV Writer")
main_menu.geometry("300x300")
main_menu.wm_iconbitmap('cv_icon.ico')
title = tkinter.Label(main_menu, text = "Main Menu", font=("Helvetica",25))
title.pack()
gap = tkinter.Label(main_menu, text = "")
gap.pack()
write_cv = tkinter.Button(main_menu, text = "1) Write CV", font=("Helvetica"), command=Write_CV)
write_cv.pack()
review_cv = tkinter.Button(main_menu, text = "2) Review CV", font=("Helvetica"), command=Review_CV)
review_cv.pack()
leave = tkinter.Button(main_menu, text = "3) Exit", font=("Helvetica"), command=Exit)
leave.pack()
main_menu.mainloop()
def Write_CV():
import tkinter
write_cv = tkinter.Tk()
write_cv.geometry("300x300")
write_cv.title("Write CV")
def Review_CV():
import tkinter
review_cv = tkinter.Tk()
review_cv.geometry("300x300")
review_cv.title("Review CV")
def Exit():
import tkinter
leave = tkinter.Tk()
leave.geometry("300x300")
leave.title("Exit")
Main_Menu()
Running the program should help make this question make more sense!
I am so sorry for the wordy question, but any kind of help would be appreciated! Please bear in mind I am only a GCSE student so simple language would also be so nice! Thank you!
I don't know why are you importing tkinter under each method, it's completely useless. Simply import it once at the beginning of your file with a syntax like this:
import tkinter as tk
So that you can refer to the widgets simply with the duo tk:
btn = tk.Button(None, text='I can simply refer to a widget with tk')
Apart from this, the structure of your program is really bad. In my opinion, you should not instantiate Tk inside your function Main_Menu, because it will only be visible inside it. If you want to refer to the master or root or whatever you want to call the instance of Tk, you can't, because it's a local instance, as I said above.
I usually instantiate Tk in the main function of my program, or in the following if __name__ == '__main__': construct:
if __name__ == '__main__':
master = tk.Tk() # note I am using "tk"
# create your objects or call your functions here
master.mainloop()
Your are creating an instance of Tkin each of your function, that is really a bad practice, never do that. You should only create one instance of Tk for each Tkinter application.
You should use the object-oriented paradigm or make all your widgets global to structure your application.
Except these details, you can simply call master.destroy() when you want to destroy your main window and all its children widgets, where master is the Tk instance.
In general, you have a lot of errors and bad practices. My advice is:
Read a tutorial on Python first and then on Tkinter, before
proceeding.
The PySide docs include this section on QCompleter with tree models:
PySide.QtGui.QCompleter can look for completions in tree models, assuming that any item (or sub-item or sub-sub-item) can be unambiguously represented as a string by specifying the path to the item. The completion is then performed one level at a time.
Let’s take the example of a user typing in a file system path. The model is a (hierarchical) PySide.QtGui.QFileSystemModel . The completion occurs for every element in the path. For example, if the current text is C:\Wind , PySide.QtGui.QCompleter might suggest Windows to complete the current path element. Similarly, if the current text is C:\Windows\Sy , PySide.QtGui.QCompleter might suggest System .
For this kind of completion to work, PySide.QtGui.QCompleter needs to be able to split the path into a list of strings that are matched at each level. For C:\Windows\Sy , it needs to be split as “C:”, “Windows” and “Sy”. The default implementation of PySide.QtGui.QCompleter.splitPath() , splits the PySide.QtGui.QCompleter.completionPrefix() using QDir.separator() if the model is a PySide.QtGui.QFileSystemModel .
To provide completions, PySide.QtGui.QCompleter needs to know the path from an index. This is provided by PySide.QtGui.QCompleter.pathFromIndex() . The default implementation of PySide.QtGui.QCompleter.pathFromIndex() , returns the data for the edit role for list models and the absolute file path if the mode is a PySide.QtGui.QFileSystemModel.
But I can't seem to find an example showing how to do this. Can anyone point me at an example I can use as a starting point? (In my investigation it looks like maybe the hard part is the tree model rather than the QCompleter)
It looks like you would need to provide these functions:
ability to split a string into segments (for the example given, C:\Windows\Sy to ['C:','Windows','Sy']
the ability to specify the list of items that include the last segment (e.g. all the items included in ['C:','Windows']
I found an example for the basic functionality of QCompleter and have been able to tweak the basics fine (see below), I just don't know how to go about implementing a tree model type application.
'''based on
http://codeprogress.com/python/libraries/pyqt/showPyQTExample.php?index=403&key=QCompleterQLineEdit'''
from PySide.QtGui import *
from PySide.QtCore import *
import sys
def main():
app = QApplication(sys.argv)
edit = QLineEdit()
strList = '''
Germany;Russia;France;
french fries;frizzy hair;fennel;fuzzball
frayed;fickle;Frobozz;fear;framing;frames
Franco-American;Frames;fancy;fire;frozen yogurt
football;fnord;foul;fowl;foo;bar;baz;quux
family;Fozzie Bear;flinch;fizzy;famous;fellow
friend;fog;foil;far;flower;flour;Florida
'''.replace('\n',';').split(";")
strList.sort(key=lambda s: s.lower())
completer = QCompleter(strList,edit)
completer.setCaseSensitivity(Qt.CaseInsensitive)
edit.setWindowTitle("PySide QLineEdit Auto Complete")
edit.setCompleter(completer)
edit.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
I couldn't find a good example for what I wanted, but I figured out how to adapt the Qt TreeModel example to using a QCompleter:
https://gist.github.com/jason-s/9dcef741288b6509d362
The QCompleter is the easy part, you just have to tell it how to split a path into segments, and then how to get from a particular entry in the model back to a path:
class MyCompleter(QtGui.QCompleter):
def splitPath(self, path):
return path.split('/')
def pathFromIndex(self, index):
result = []
while index.isValid():
result = [self.model().data(index, QtCore.Qt.DisplayRole)] + result
index = index.parent()
r = '/'.join(result)
return r
Aside from that, you have to configure the QCompleter properly, telling it how to get from a model item to a text string. Here I set it up to use the DisplayRole and to use column 0.
edit = QtGui.QLineEdit()
completer = MyCompleter(edit)
completer.setModel(model)
completer.setCompletionColumn(0)
completer.setCompletionRole(QtCore.Qt.DisplayRole)
completer.setCaseSensitivity(QtCore.Qt.CaseInsensitive)
As the documentation for QCompleter says you can provide two models: a list model or a tree model.
Example for list model, after your example:
from PySide import QtGui
app = QtGui.QApplication([])
edit = QtGui.QLineEdit()
strList = "Germany;Russia;France;Norway".split(";")
completer = QtGui.QCompleter(strList)
edit.setCompleter(completer)
edit.show()
app.exec_()
works:
And as a tree model:
from PySide import QtGui, QtCore
app = QtGui.QApplication([])
edit = QtGui.QLineEdit()
model = QtGui.QFileSystemModel()
model.setFilter(QtCore.QDir.AllDirs | QtCore.QDir.Drives)
model.setRootPath('')
completer = QtGui.QCompleter(model, edit)
edit.setCompleter(completer)
edit.show()
app.exec_()
for some strange reason nothing is displayed here. Will investigate later.
I have some code creating a QTabWidget from Python using PyQt4. I want to get a 'throbber' animated gif in the tab. The /only way/ I have found how to do this is the following convoluted method.
tabBar = self.tabReports.tabBar()
lbl = QtGui.QLabel(self.tabReports)
movie = QtGui.QMovie(os.path.join(self.basedir, "images\\throbber.gif"))
lbl.setMovie(movie)
QtCore.QObject.connect(movie, QtCore.SIGNAL("frameChanged(int)"), lambda i: movie.jumpToFrame(i))
movie.start()
log.debug("valid = %s"%(movie.isValid()))
tabBar.setTabButton(idxtab, QtGui.QTabBar.LeftSide, lbl)
The debugging call always returns true, but the throbber sometimes works, sometimes is blank, and sometimes has a large ugly delay between frames. In particular, I can't help but think connecting the frameChanged signal from the movie to a function that simply calls jumpToFrame on the same movie is not correct.
Even more distressing, if I simply drop the lambda (That is, make the line say QtCore.QObject.connect(movie, QtCore.SIGNAL("frameChanged(int)"), movie.jumpToFrame) it never renders even the first frame.
So, what am I doing wrong?
PS: I realize .tabBar() is a protected member, but I assumed (apparently correctly) that PyQt unprotects protected members :). I'm new to Qt, and i'd rather not subclass QTabWidget if I can help it.
I believe the problem with the code I initially posted was that the QMovie didn't have a parent, and thus scoping issues allowed the underlying C++ issue to be destroyed. It is also possible I had had threading issues - threading.thread and QThread do not play nice together. The working code I have now is below - no messing with signals nor slots needed.
def animateTab(self, tab_widget, enable):
tw = tab_widget
tabBar = tw.tabBar()
if enable:
lbl = QtGui.QLabel(tw)
movie = QtGui.QMovie("images\\throbber.gif"), parent=lbl)
movie.setScaledSize(QtCore.QSize(16, 16))
lbl.setMovie(movie)
movie.start()
else:
lbl = QtGui.QLabel(tw)
lbl.setMinimumSize(QtCore.QSize(16, 16))
tabBar.setTabButton(tab_section.index, QtGui.QTabBar.LeftSide, lbl)
I faced the same problem and this posting helped to make it work:
http://www.daniweb.com/forums/printthread.php?t=191210&pp=40
For me this seems to make the difference: QMovie("image.gif", QByteArray(), self)