Skip to content

Commit 8650a43

Browse files
committed
Unify serial config for serial and qtserial.
closes #13
1 parent a0a2409 commit 8650a43

File tree

6 files changed

+104
-177
lines changed

6 files changed

+104
-177
lines changed

inkcut/device/extensions.py

+9-3
Original file line numberDiff line numberDiff line change
@@ -110,12 +110,18 @@ def get_job_config(self):
110110
"""
111111
return self.default_config.get('job', {}).copy()
112112

113-
def get_connection_config(self, id):
113+
def get_connection_config(self, id, fallback=None):
114114
""" Pull the connection config params from the default_config
115115
for the given transport id.
116116
"""
117-
cfg = self.default_config.get('connection', {}).copy()
118-
return cfg.get(id, {})
117+
cfg = self.default_config.get('connection', {})
118+
if not cfg:
119+
return {}
120+
if id in cfg:
121+
return cfg.get(id, {}).copy()
122+
if fallback:
123+
return cfg.get(fallback, {}).copy()
124+
return {}
119125

120126
def get_protocol_config(self, id):
121127
""" Pull the protocol config from the default_config """

inkcut/device/transports/qtserialport/manifest.enaml

+3-3
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,14 @@ from inkcut.device.extensions import DeviceTransport, DEVICE_TRANSPORT_POINT
1111

1212
def transport_factory(driver, declaration, protocol):
1313
from .plugin import QtSerialTransport, QtSerialConfig
14-
config = QtSerialConfig(**driver.get_connection_config('qtserial'))
14+
config = QtSerialConfig(**driver.get_connection_config('qtserial', 'serial'))
1515
return QtSerialTransport(declaration=declaration, protocol=protocol, config=config)
1616

1717

1818
def config_view_factory():
1919
with enaml.imports():
20-
from .settings import SerialPortSettingsView
21-
return SerialPortSettingsView
20+
from .settings import QtSerialPortSettingsView
21+
return QtSerialPortSettingsView
2222

2323

2424
def plugin_factory():

inkcut/device/transports/qtserialport/plugin.py

+46-77
Original file line numberDiff line numberDiff line change
@@ -15,81 +15,50 @@
1515
from atom.atom import set_default
1616
from atom.api import List, Instance, Enum, Bool, Int, Str
1717
from inkcut.core.api import Plugin, Model, log
18+
from inkcut.device.transports.serialport.plugin import SerialConfigBase, SerialPortInfo
1819
from inkcut.device.plugin import DeviceTransport
19-
from PyQt5.QtSerialPort import QSerialPort
20-
from PyQt5.QtSerialPort import QSerialPortInfo
20+
from qtpy.QtSerialPort import QSerialPort, QSerialPortInfo
21+
import serial
2122

22-
23-
24-
class IdNameItem:
25-
def __init__(self, id, name):
26-
self.id :int = id
27-
self.name = name
28-
29-
30-
class QtSerialConfig(Model):
31-
device_path = Str()
32-
33-
#: Available serial ports
34-
ports = List()
35-
#: Serial port config
36-
port = Str().tag(config=True)
37-
38-
flowcontrols = []
39-
# Available FlowControls
40-
flowcontrols.append( IdNameItem(QSerialPort.NoFlowControl, 'No flow control') )
41-
flowcontrols.append( IdNameItem(QSerialPort.HardwareControl,'Hardware flow control (RTS/CTS)') )
42-
flowcontrols.append( IdNameItem(QSerialPort.SoftwareControl,'Software flow control (XON/XOFF)') )
43-
flowcontrols.append( IdNameItem(QSerialPort.UnknownFlowControl,'Unknown flow control (obsolete value)') )
44-
#: FlowControl config
45-
flowcontrol=Int(0).tag(config=True)
46-
47-
#: Available BaudRates
48-
baudrates = Enum(110, 300, 600, 1200, 2400, 4800, 9600, 14400, 19200, 38400, 57600, 115200, 128000, 256000)
49-
#: BaudRate config
50-
baudrate = Int(9600).tag(config=True)
51-
52-
parities = []
53-
# Available Parities
54-
parities.append( IdNameItem(QSerialPort.NoParity, 'No Parity') )
55-
parities.append( IdNameItem(QSerialPort.EvenParity, 'Even') )
56-
parities.append( IdNameItem(QSerialPort.OddParity, 'Odd') )
57-
parities.append( IdNameItem(QSerialPort.SpaceParity, 'Space') )
58-
parities.append( IdNameItem(QSerialPort.MarkParity, 'Mark') )
59-
parities.append( IdNameItem(QSerialPort.UnknownParity, 'Unknown') )
60-
#: Parity config
61-
parity=Int(0).tag(config=True)
62-
63-
list_stopbits = []
64-
# Available Stopbits
65-
list_stopbits.append( IdNameItem(QSerialPort.OneStop, '1 stop bit') )
66-
list_stopbits.append( IdNameItem(QSerialPort.OneAndHalfStop, '1.5 stop bits (Windows only)') )
67-
list_stopbits.append( IdNameItem(QSerialPort.TwoStop, '2 stop bits') )
68-
#: Stopbits config
69-
stopbits=Int(1).tag(config=True)
70-
71-
bytesize = Enum(8, 7, 6, 5).tag(config=True)
72-
73-
74-
# -------------------------------------------------------------------------
75-
# Defaults
76-
# -------------------------------------------------------------------------
23+
class QtSerialConfig(SerialConfigBase):
7724
def _default_ports(self):
78-
return self.get_serial_ports()
79-
80-
def _default_port(self):
81-
if self.ports:
82-
return self.ports[0].portName()
83-
return ""
84-
85-
def refresh(self):
86-
self.ports = self._default_ports()
87-
88-
def get_serial_ports(self):
89-
info_list = QSerialPortInfo()
90-
serial_list = info_list.availablePorts()
91-
return serial_list
92-
25+
result = []
26+
for port in QSerialPortInfo().availablePorts():
27+
info = SerialPortInfo()
28+
info.device_path = port.portName()
29+
info.description = "{} {}".format(port.portName(), port.description())
30+
if port.hasProductIdentifier() and port.hasVendorIdentifier():
31+
info.usb_pid = port.productIdentifier()
32+
info.usb_vid = port.vendorIdentifier()
33+
result.append(info)
34+
return result
35+
36+
def map_flow_control(self):
37+
if self.rtscts:
38+
return QSerialPort.HardwareControl
39+
elif self.xonxoff:
40+
return QSerialPort.SoftwareControl
41+
return QSerialPort.NoFlowControl
42+
43+
STOP_BIT_MAPPING = {
44+
serial.STOPBITS_ONE: QSerialPort.StopBits.OneStop,
45+
serial.STOPBITS_ONE_POINT_FIVE: QSerialPort.StopBits.OneAndHalfStop,
46+
serial.STOPBITS_TWO: QSerialPort.StopBits.TwoStop,
47+
}
48+
49+
def map_stop_bits(self):
50+
return QtSerialConfig.STOP_BIT_MAPPING[self.stopbits]
51+
52+
PARITY_BIT_MAPPING = {
53+
serial.PARITY_NONE: QSerialPort.Parity.NoParity,
54+
serial.PARITY_EVEN: QSerialPort.Parity.EvenParity,
55+
serial.PARITY_ODD: QSerialPort.Parity.OddParity,
56+
serial.PARITY_MARK: QSerialPort.Parity.SpaceParity,
57+
serial.PARITY_SPACE: QSerialPort.Parity.MarkParity,
58+
}
59+
60+
def map_parity(self):
61+
return QtSerialConfig.PARITY_BIT_MAPPING[self.parity]
9362

9463
class QtSerialTransport(DeviceTransport):
9564

@@ -102,17 +71,17 @@ class QtSerialTransport(DeviceTransport):
10271

10372
#: Whether a serial connection spools depends on the device (configuration)
10473
always_spools = set_default(False)
105-
74+
10675
def open_serial_port(self, config):
10776
try:
10877
serial_port = QSerialPort()
109-
serial_port.setPortName(config.port)
78+
serial_port.setPortName(config.device_path)
11079
#Setting the AllDirections flag is supported on all platforms. Windows supports only this mode.
11180
serial_port.setBaudRate(config.baudrate, QSerialPort.AllDirections)
112-
serial_port.setParity(config.parity)
113-
serial_port.setStopBits(config.stopbits)
81+
serial_port.setParity(config.map_parity())
82+
serial_port.setStopBits(config.map_stop_bits())
11483
serial_port.setDataBits(config.bytesize)
115-
serial_port.setFlowControl(config.flowcontrol)
84+
serial_port.setFlowControl(config.map_flow_control())
11685
serial_port.open(QSerialPort.ReadWrite)
11786
return serial_port
11887
except Exception as e:

inkcut/device/transports/qtserialport/settings.enaml

+4-87
Original file line numberDiff line numberDiff line change
@@ -15,93 +15,10 @@ from inkcut.core.utils import load_icon
1515
from enaml.layout.api import hbox, align, spacer
1616
from enaml.qt.QtWidgets import QApplication
1717
from enaml.widgets.api import Container, Form, Label, ObjectCombo, SpinBox, CheckBox, PushButton, ComboBox
18+
from inkcut.device.transports.serialport.settings import SerialPortSettingsView
1819

1920

20-
enamldef SerialPortSettingsView(Container):
21-
attr model
22-
padding = 0
23-
24-
func selected_port(port, ports):
25-
matches = [p for p in ports if p.portName() == port]
26-
return matches[0] if matches else None
27-
28-
func selected_fc(flowcontrol, flowcontrols):
29-
matches = [p for p in flowcontrols if p.id == flowcontrol]
30-
return matches[0] if matches else None
31-
32-
func selected_parity(parity, parities):
33-
matches = [p for p in parities if p.id == parity]
34-
return matches[0] if matches else None
35-
36-
func selected_stopbits(stopbits, list_stopbits):
37-
matches = [p for p in list_stopbits if p.id == stopbits]
38-
return matches[0] if matches else None
39-
40-
Form:
41-
Label:
42-
text = QApplication.translate("serialport", "Port")
43-
Container:
44-
padding = 0
45-
constraints = [
46-
hbox(cb, pb),
47-
align('v_center', cb, pb)
48-
]
49-
ObjectCombo: cb:
50-
items << model.ports
51-
to_string = lambda obj: obj.portName() + " - " + obj.description()
52-
selected << selected_port(model.port, model.ports)
53-
selected ::
54-
port = change['value']
55-
if port:
56-
model.port = port.portName()
57-
tool_tip = textwrap.dedent("""
58-
List of serial ports detected by the system. If nothing is here you
59-
must install the device driver for your machine.
60-
""").strip()
61-
PushButton: pb:
62-
text = QApplication.translate("serialport", "Refresh")
63-
icon = load_icon("arrow_refresh")
64-
clicked :: model.refresh()
65-
Label:
66-
text = QApplication.translate("serialport", "Baudrate")
67-
ObjectCombo:
68-
items = list(model.get_member('baudrates').items)
69-
selected := model.baudrate
70-
Label:
71-
text = QApplication.translate("serialport", "Bytesize")
72-
ObjectCombo:
73-
items = list(model.get_member('bytesize').items)
74-
selected := model.bytesize
75-
Label:
76-
text = QApplication.translate("serialport", "Parity")
77-
ObjectCombo:
78-
items << model.parities
79-
to_string = lambda obj: obj.name
80-
selected << selected_parity(model.parity, model.parities)
81-
selected ::
82-
it = change['value']
83-
if it:
84-
model.parity = int(it.id)
85-
Label:
86-
text = QApplication.translate("serialport", "Stopbits")
87-
ObjectCombo:
88-
items << model.list_stopbits
89-
to_string = lambda obj: obj.name
90-
selected << selected_stopbits(model.stopbits, model.list_stopbits)
91-
selected ::
92-
it = change['value']
93-
if it:
94-
model.stopbits = int(it.id)
95-
Label:
96-
text = QApplication.translate("serialport", "Flow control")
97-
ObjectCombo:
98-
items << model.flowcontrols
99-
to_string = lambda obj: obj.name
100-
selected << selected_fc(model.flowcontrol, model.flowcontrols)
101-
selected ::
102-
fc = change['value']
103-
if fc:
104-
model.flowcontrol = int(fc.id)
105-
106-
21+
enamldef QtSerialPortSettingsView(SerialPortSettingsView):
22+
dsrdtr.visible = False
23+
exclusive = True
10724

inkcut/device/transports/serialport/plugin.py

+26-5
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,21 @@
2323

2424
from inkcut.device.transports.raw.plugin import RawFdTransport, RawFdProtocol
2525

26-
2726
#: Reverse key values
2827
SERIAL_PARITIES = {v: k for k, v in serial.PARITY_NAMES.items()}
2928

3029

31-
class SerialConfig(Model):
30+
class SerialPortInfo(Model):
31+
device_path = Str()
32+
description = Str()
33+
usb_pid = Int()
34+
usb_vid = Int()
35+
36+
def __str__(self):
37+
return self.description
38+
39+
40+
class SerialConfigBase(Model):
3241
#: Available serial ports
3342
ports = List()
3443

@@ -48,22 +57,34 @@ class SerialConfig(Model):
4857
# Defaults
4958
# -------------------------------------------------------------------------
5059
def _default_ports(self):
51-
return comports()
60+
return []
5261

5362
def _default_parity(self):
5463
return 'None'
5564

5665
def _default_port(self):
5766
if self.ports:
58-
return self.ports[0].device
67+
return self.ports[0].device_path
5968
return ""
6069

6170
def refresh(self):
6271
self.ports = self._default_ports()
6372

6473

65-
class SerialTransport(RawFdTransport):
74+
class SerialConfig(SerialConfigBase):
75+
def _default_ports(self):
76+
result = []
77+
for port in comports():
78+
info = SerialPortInfo()
79+
info.device_path = port.device
80+
info.description = str(port)
81+
info.usb_pid = port.pid if port.pid else 0
82+
info.usb_vid = port.vid if port.vid else 0
83+
result.append(info)
84+
return result
6685

86+
87+
class SerialTransport(RawFdTransport):
6788
#: Default config
6889
config = Instance(SerialConfig, ()).tag(config=True)
6990

inkcut/device/transports/serialport/settings.enaml

+16-2
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,12 @@ from enaml.widgets.api import Container, Form, Label, ObjectCombo, SpinBox, Chec
1919

2020
enamldef SerialPortSettingsView(Container):
2121
attr model
22+
attr exclusive = False
2223
padding = 0
2324

25+
alias dsrdtr
2426
func selected_port(port, ports):
25-
matches = [p for p in ports if p.device == port]
27+
matches = [p for p in ports if p.device_path == port]
2628
return matches[0] if matches else None
2729

2830
Form:
@@ -40,7 +42,7 @@ enamldef SerialPortSettingsView(Container):
4042
selected ::
4143
port = change['value']
4244
if port:
43-
model.port = port.device
45+
model.port = port.device_path
4446
tool_tip = textwrap.dedent("""
4547
List of serial ports detected by the system. If nothing is here you
4648
must install the device driver for your machine.
@@ -81,12 +83,24 @@ enamldef SerialPortSettingsView(Container):
8183
CheckBox: rtscts:
8284
text = QApplication.translate("serialport", "RTS/CTS")
8385
checked := model.rtscts
86+
checked ::
87+
if change['value'] and exclusive:
88+
model.xonxoff = False
89+
model.dsrdtr = False
8490
tool_tip = QApplication.translate("serialport", "Enable hardware (RTS/CTS) flow control")
8591
CheckBox: dsrdtr:
8692
text = QApplication.translate("serialport", "DSR/DTR")
8793
checked := model.dsrdtr
94+
checked ::
95+
if change['value'] and exclusive:
96+
model.xonxoff = False
97+
model.rtscts = False
8898
tool_tip = QApplication.translate("serialport", "Enable hardware (DSR/DTR) flow control")
8999
CheckBox: xonxoff:
90100
text = QApplication.translate("serialport", "XON/XOFF")
91101
checked := model.xonxoff
102+
checked ::
103+
if change['value'] and exclusive:
104+
model.dsrdtr = False
105+
model.rtscts = False
92106
tool_tip = QApplication.translate("serialport", "Enable software flow control")

0 commit comments

Comments
 (0)